LINUX.ORG.RU

[ttyUSB0] вызов read()


0

0

ПК соединен с контроллером через переходник USB->serial. По определенному сигналу контроллер швыряет на последовательный порт около 200 байтов (но каждый раз разное количество). Пытаюсь их получить следующим образом:

/* ..здесь какой-то код... */
if (!portOpen()) return 0;
if ((count=portScan(points, maxlen)) <= 0) {
	fprintf(stderr, "getData: Таймаут ожидания данных\n");
	portClose();
	return 0;
}

инициализация выглядит вот так:

#define DEVNAME "/dev/ttyUSB0"

static int _openPort(void)
{
	int fd;

	fd = open(DEVNAME, O_RDWR | O_NOCTTY | O_NDELAY);
	if (fd == -1) {
		fprintf(stderr, "_openPort: can't open %s\n",DEVNAME);
	}
	else
		fcntl(fd, F_SETFL,0);
	
	return fd;
}

static struct termios _initPort(int fd)
{
	struct termios options, oldopt;
	tcgetattr(fd, &oldopt);

	bzero(&options, sizeof(options));
	
	options.c_cflag |= (CLOCAL | CREAD | B9600);
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8;

	options.c_iflag = 0;
	options.c_lflag = 0;
	options.c_oflag = 0;

	options.c_cc[VTIME] = 10; /* 1 second */
	options.c_cc[VMIN] = 0;
	
	tcflush(fd, TCIFLUSH);
	tcsetattr(fd, TCSANOW, &options);

	/* этот участок необходим по схемотехническим требованиям */
	int rts = TIOCM_RTS;
	int dtr = TIOCM_DTR;

	ioctl(fd, TIOCMBIC, &rts);
	ioctl(fd, TIOCMBIS, &dtr);

	return oldopt;
}

static void _closePort(int fd)
{
	close(fd);
}

static int serial;
static struct termios oldopt;

int portOpen(void)
{
	if ((serial=_openPort())!=-1) {
		oldopt = _initPort(serial);
		return 1;
	} 
	return 0;
}

void portClose(void)
{
	if (serial != -1)
		tcsetattr(serial,TCSANOW,&oldopt);
		_closePort(serial);
}


int portScan(void *buf, int count)
{
	if (serial == -1) { 
		fprintf(stderr, "portScan: port is not opened!\n");
		return 0;
	}

	return read(serial, buf, count);
}

Проблема в том, что вызов portScan(points, maxlen) возвращает всегда только 4 байта. Сказать, первые ли это 4 байта, я не могу. Но если после обращения read(...) сразу же вызвать read еще раз, то успешно прочитываются еще 4 байта.

Подскажите пожалуйста, с чем это может быть связано? (Аппаратное/программное управление событиями не требуется)


Попробуйте читать кусками побольше, или до тех пор, пока read не вернёт 0, или -1 и EAGAIN.

ArtSh ★★★ ()
Ответ на: комментарий от ArtSh

Попробуйте читать кусками побольше, или до тех пор, пока read не вернёт 0, или -1 и EAGAIN.

Дело в том, что побольше кусок прочитать не получается. В этом-то и загвоздка. В вызове read(serial, buf, count) значение count равняется 1024. Тем не менее, возвращается стабильно 4.

Попробовал выставить:

options.c_cc[VTIME] = 10; /* 1 second */ 
options.c_cc[VMIN] = 1;
Наблюдается такое же странное поведение.

Corey ()
Ответ на: комментарий от Corey

$ man 2 read

It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal),

vga ★★ ()
Ответ на: комментарий от vga

Сорри, если и это и так понятно. Я просто два раза перечитал пост, и никак не могу понять, в чем собственно проблема? Ну присылает переходник по 4 байта, ну и что такого?

vga ★★ ()
Ответ на: комментарий от vga

>Сорри, если и это и так понятно. Я просто два раза перечитал пост, и никак не могу понять, в чем собственно проблема? Ну присылает переходник по 4 байта, ну и что такого?

Я просто, наверное, не совсем точно изложил суть проблемы. Присылает-то он по 4. А передается на него по 190..200 байтов. Мне хотелось бы увидеть их все.

Corey ()
Ответ на: комментарий от Corey

Читай побайтно

options.c_cc[VTIME] = 10; /* 1 second */  
options.c_cc[VMIN] = 1; 
....

int portScan(void *buf, int count) 
{ 
   int i = 0;

   if (serial == -1) {  
      fprintf(stderr, "portScan: port is not opened!\n"); 
      return 0; 
   } 

   while((i < count) && (read(serial, &buf[i], 1))
      i++;
 
   return i; 
}
anonymous ()

Может дурацкий вопрос. Настройки скорости совпадают? Может шлешь данные на 115200, а принимаешь на 9600. Принятые 4 байта соответствуют первым 4 байтам посылки?

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Хотя скорее всего это драйвер/устройство такое. Ну перебрасывает оно кусками по 4 байта наверх. Что-то такого. Вызывай read столько раз, сколько нужно чтоб достать все 200 байт.

pathfinder ★★★★ ()
Ответ на: комментарий от Eddy_Em

>Может, попробовать использовать select при чтении?

O_o Не понял. Как select может на что-то повлиять? Я так понимаю код read в драйвере никак не поменяется от того будет пользователь вызывать функцию read или select. Или я не прав?

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

select не будет подвешивать интерфейс в случае отсутствия данных (если вы откроете его в неблокирующем режиме).

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

Кстати, не факт, что

   options.c_cc[VTIME] = 10; /* 1 second */ 
   options.c_cc[VMIN] = 0; 
работает (у меня с этими параметрами возникали проблемы, количество байт данных возвращалось неправильно, решил проблему select'ом. Хотя, может быть, в вашем варианте это и не поможет).

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от pathfinder

Посмотрел сейчас ман: O_NDELAY - то же самое, что и O_NONBLOCK. Так что вам только select, иначе часть данных будете терять.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

Побайтно самый надежный метод - может не так красиво но железно работает и вычитывает все что есть. Я так делал на опросе устройств - промзона, rs-485, длина линии на пределе для данной скорости по спекам, причем обычная телефонная пара в кабеле и кабель шел по лотку рядом с высоковольтным.

anonymous ()
Ответ на: комментарий от Eddy_Em

>Так что вам только select, иначе часть данных будете терять.

Почему терять??? Цикл с read и по хорошему циклический буфер на стороне приложения. Правда процесс будет блокироваться до прихода данных, но сами данные при этом потеряны не будут. Циклический буфер будет полезен, если нельзя исключить возможность захвата байтов от следующего пакета, т.е. скажем за один read считали 1 последний байт от старого пакета и 3 начальных байта от нового пакета. Так как мы получили полный старый пакет и немного нового, то обрабатываем старый и ждем оставшихся байт нового.

pathfinder ★★★★ ()
Ответ на: комментарий от anonymous

>Побайтно самый надежный метод - может не так красиво но железно работает и вычитывает все что есть.

Ужасный способ. Годится, только при малых скоростях и малых размеров пакета. По хорошему надо делать буферизацию на стороне приложения и читать полными блоками, как нам передает драйвер.

pathfinder ★★★★ ()
Ответ на: комментарий от anonymous

>rs-485, длина линии на пределе для данной скорости по спекам, причем обычная телефонная пара в кабеле и кабель шел по лотку рядом с высоковольтным.

Как побайтное чтение защитит от помех в линии ?!!!!!1111

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Для ламеров программирующих по книжкам все ужастно кажется :)

options.c_cflag |= (CLOCAL | CREAD | >>>B9600<<<);

обычно такие как ты обламываются на реальных задачах.

anonymous ()
Ответ на: комментарий от pathfinder

У вас read ждет 1 секунду (если, конечно, таймер работает) и ему без разницы, сколько придет байт. Попробуйте сделать ненулевое значение c_cc[VMIN] - тогда при приходе очередной порции данных таймер будет возобновлять работу, и будет меньше вероятности данные потерять.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от pathfinder

>Как побайтное чтение защитит от помех в линии ?!!!!!1111

Ты идиот ? При обмене на таких линиях всегда бывают задежки и требуется коррекция.

anonymous ()
Ответ на: комментарий от anonymous

>Для ламеров программирующих по книжкам все ужастно кажется :)

options.c_cflag |= (CLOCAL | CREAD | >>>B9600<<<);
обычно такие как ты обламываются на реальных задачах.

Не знаю как ты, но я занимался решением реальных задач. Конкретно я занимаюсь реализацией протоколов телемеханики для энергетики. И я знаю что такое «неожиданное изменение требований к продукту», сегодня устройство должно работать на скорости 9600, а завтра «неожиданно» понадобится 10Мбит/с. Пример конечно искусственный, но надо научится писать чтоб меньше надо было потом переделывать.

pathfinder ★★★★ ()
Ответ на: комментарий от anonymous

>Ты идиот ? При обмене на таких линиях всегда бывают задежки и требуется коррекция.

О каких задержках идет речь? Повторная посылка пакета из-за того, что прошлый оказался сбойным? Если так, то тогда все равно не понятно чем поможет побайтное чтение.

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Не ври - не бывает ничего внезапно. Как там - в ОДУ все еще гранитом пользуются или что-то поинтересней придумали ?

anonymous ()
Ответ на: комментарий от anonymous

>Не ври - не бывает ничего внезапно. Как там - в ОДУ все еще гранитом пользуются или что-то поинтересней придумали ?

Эээ.... МЭК 60870-5-101 Не?

Гранит реализовывал. Читал блоками через read (не побайтно), правда драйвер интерфейса был свой, для модуля синхронных интерфейсов. Битовую обработку уже делал в считанном буфере (захват маркера и все такое).

pathfinder ★★★★ ()
Ответ на: комментарий от anonymous

>Не ври - не бывает ничего внезапно...

Не вру. Жизнь непредсказуема. Нельзя все учесть на начальном этапе.

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

>МЭК 60870-5-101 Не?

Ишь ты - современный :) Я уже не работал к этому времени когда стали национальные стандарты вводить, в мое время телемеханика соотвествовала названию - буквально системы на электромеханических реле были :)

anonymous ()
Ответ на: комментарий от anonymous

От злого анонимуса я так и не понял по части проблем с плохой линией, «задержками», «коррекцией» и как это все увязано на побайтное чтение. Может более подробно разъясните. Я ведь тоже не все могу правильно понимать. Хотел бы знать. А вдруг я где-то ошибаюсь?

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Почему сразу злой ? Я не злой. Есть протоколы в которых длина посылки передается в теле самой посылки, если получил не все и чтение по таймауту закончилось снова нужно запрашивать данные.

anonymous ()
Ответ на: комментарий от anonymous

>>Ты идиот ? При обмене на таких линиях всегда бывают задежки и требуется коррекция.

Почему сразу злой ? Я не злой.

Ну меня все таки идиотом обозвали. :)

Есть протоколы в которых длина посылки передается в теле самой посылки...

Есть. Таких много. Хотя можно решить указанную проблему и при блочном чтении. Так же соглашусь что до побайтное чтение легче реализовать, правда не намного, но тогда будем насиловать OS системными вызовами.

Ладно соглашусь, что для медленной телемеханики это вполне нормальный путь, но не соглашусь, что единственно нормальный.

pathfinder ★★★★ ()
Ответ на: комментарий от anonymous

На медленных скоростях есть перспектива, что все равно будешь по байту из драйвера извлекать, даже если твоя программа может это делать блоками. :(

В итоге разницы не будет.

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Заметь - я не говорил что это норманый метод, это самый надежный, не более того, а на скорость порта я сразу внимание обратил. 9600 кстати для локальной телемеханики выше крыши хватает :)

anonymous ()
Ответ на: комментарий от pathfinder

pathfinder, а у вас работало вообще (с какой-нибудь другой железякой) чтение из последовательного порта в неблокирующем режиме без select с такими же параметрами линии? У меня со встроенными контроллерами RS-232 постоянно возникали проблемы (чихать хотел контроллер на параметры c_cc[...]), не знаю, правда, как обстоят дела с USBшным RS-232.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

>pathfinder, а у вас работало вообще (с какой-нибудь другой железякой) чтение из последовательного порта в неблокирующем режиме без select с такими же параметрами линии?

Ну... *краснеет* я пользовался в своих программах poll, но это только для того, чтобы можно было писать однопотоковые программы. Если без select/poll, то подойдет блокирующее чтение и многопотоковое приложение (один поток чисто для чтения, другие рабочие). Если мы хотим именно неблокирующее чтение и при этом обойтись без select/poll, то надо как-то ловить момент прихода данных. Единственное что приходит на ум, это периодический вызов read через определенные интервалы времени. Я не думаю, что использовать именно неблокирущее чтение без select/poll хорошая идея.

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Я не думаю, что использовать именно неблокирущее чтение без select/poll хорошая идея.

А O_NDELAY как раз указывает на неблокирующий режим.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

>А O_NDELAY как раз указывает на неблокирующий режим.

Я понял. Ещё раз пересмотрел код топикстартера. Наличие неблокируемого режима радует. Совершенно непонятно КАК он хотел осуществлять чтение в своем коде. Возможно перед вызовом count=portScan(points, maxlen)) он ждал некоторое время, за которое он думал будет получен весь пакет. Сделал бы хотя бы чтение в цикле до полного извлечения данных. Драйвер не обязан предоставлять за одну операцию read весь объем данных приемного буфера. Более того, он не знает точно размер этого буфера. А вдруг этот размер равен 128 байт (я думаю все понимают что память не резиновая и размер буфера конечен), тогда он никак по такой схеме не сможет извлечь залпом все 200 байт.

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Только сейчас заметил, что вы - не топикстартер :)

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

>options.c_cc[VTIME] = 10; /* 1 second */

Вообще странно. Зачем настраивать эту опцию и включать неблокирующий режим?!! Все равно ждать никто не будет, так как режим неблокирующий.

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Теперь я понял. Топикстартер большой молодец. Он наверное понадеялся на VTIME. При этом САМ указал опцией O_NDELAY на то, что никакого ожидания ни в коем случае нельзя делать. Наверное он успевает получить только 4 байта к моменту вызова portScan().

pathfinder ★★★★ ()
Ответ на: комментарий от pathfinder

Вот про это я и говорил. Здесь либо блокировать, либо использовать отдельный поток на чтение (или читать достаточно часто) с select'ом.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

Да, я сам все напутал. Спасибо за помощь. Заработало с O_NDELAY, c_cc[VMIN]=200 и чтением через select (хотя, насколько я позже понял, если указан флаг O_NDELAY, то элементы VMIN и VTIME игнорируются).

Полагаю, должно еще заработать просто чтение read без флага O_NDELAY, с указанием достаточно большого VTIME и c_cc[VMIN]=0?

А в чем в этом случае преимущество использования select, если я использую всего один открытый дескриптор? Просто лишь в том, что чтение начинается только тогда, когда в буфере появляются доступные для чтения байты?

Corey ()
Ответ на: комментарий от Corey

Если в буфере ничего не было, read без select'а в случае неблокирующего чтения вернет 0. Тем временем в буфере может что-нибудь накопиться, и он может успеть переполниться. А вот select даст возможность подождать появления данных. Если же вы будете использовать блокирующее чтение в каноническом режиме, вам нужно будет процедуру чтения выносить в отдельный поток, т.к. она будет блокировать выполнение до тех пор, пока не считает хотя бы один байт (или пока не выполнятся условия c_cc, если режим не канонический).

Быть может, man termios прольет вам свет на режимы работы RS232.

Eddy_Em ☆☆☆☆☆ ()
Ответ на: комментарий от Eddy_Em

Спасибо за объяснение. Теперь уже выстроилась более-менее ясная картина всего этого.

Corey ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.