Форум: "WinAPI";
Текущий архив: 2010.03.28;
Скачать: [xml.tar.bz2];
ВнизCOM порт, асинхронные чтение запись Найти похожие ветки
← →
REA (2008-05-16 10:41) [0]Помогите с вопросом. Уже мозг сломал.
Сделал чтение COM порта и периодически теряются байтики. В результате получился такой код:
FillChar(IO, SizeOf(IO), 0);
IO.hEvent := FIOEvent; // Overlapped
ToRead := 2; // Допустим 2
RdRes := ReadFile(idComm, Buf[1], ToRead, Bytes, @IO);
If (Not RdRes) And (GetLastError = ERROR_IO_PENDING) Then
RdRes := WaitForSingleObject(FIOEvent, 500) = 0; // Limit frame reading by 500 msec
If (RdRes) Then
RdRes := GetOverlappedResult(idComm, IO, Bytes, False);
If RdRes And (Bytes = ToRead) Then
и вот тут при RdRes = True число байт (Bytes) = 0.
Что я неправильно делаю?
← →
Сергей М. © (2008-05-16 10:50) [1]
> RdRes := ReadFile(idComm, Buf[1], ToRead, Bytes, @IO);
>
> If (Not RdRes) And (GetLastError = ERROR_IO_PENDING) Then
А где обработка случая RdRes = True ?
> WaitForSingleObject(FIOEvent, 500)
> If (RdRes) Then
Почему 500 ? Откуда ты знаешь, что данные будут доступны не позднее пол-секунды ?
Где обработка случая RdRes = False ?
← →
МистерТ (2008-05-16 10:51) [2]Может проще будет проверять сколько байт накопилось ?
Вот код (правда на С++)size_t byte_in_port()
{
if (!m_pSerialPort)
return 0;
DWORD l_dwErrors;
COMSTAT l_stStat;
size_t Result;
::ClearCommError(m_pSerialPort->m_port,
&l_dwErrors,
&l_stStat);
Result = static_cast <size_t> (l_stStat.cbInQue) ;
return Result;
};
У меня это проверяется в цикле, и если за некоторое время ни чего не нападало - считаю, что передача закончена (не совсем правильно конечно).
А сам цикл, примерно такой:...
if (m_state == state::eWrite)
{
/* write */
if (!byte_in_port())
{
flush();
write_data();
if (m_pSerialPort->m_sending_messages.empty())
m_state = state::eRead;
next();
return;
}
};
/* read */
if (byte_in_port())
{
read_data();
}
else
flush();
...
← →
REA (2008-05-16 11:13) [3]>А где обработка случая RdRes = True ?
Если ReadFile = True, то WaitForSingleObject как я понимаю уже ни к чему. Или нет?
Напоминаю, что ошибка замечена при RdRes = True. Т.е. из файла читается, потом отрабатывает WaitForSingleObject (таймаут не возникает) и GetOverlappedResult и считано 0 байт. Как будто какой то таймаут COM порта возник... При чем при синхронном чтении все работало достаточно стабильно.
>Откуда ты знаешь, что данные будут доступны не позднее пол-секунды ?
Должны быть практически сразу они. Там читается 2 байта на скорости 57600 и передатчик отвечает быстро.
>Может проще будет проверять сколько байт накопилось ?
Может... но и так работало в синхронном режиме
← →
Сергей М. © (2008-05-16 11:22) [4]
> Если ReadFile = True, то WaitForSingleObject как я понимаю
> уже ни к чему
Именно так.
> из файла читается, потом отрабатывает WaitForSingleObject
Зачем ожидание-то, если ReadFile = True ?
Сразу забирай данные из буфера в кол-ве lpNumberOfBytesRead
> Должны быть практически сразу они
Не надо делать никаких предположений.
Поставь INFINITE, если ожидание у тебя не в гуёвом потоке - гарантированно дождешься данные.
Или вызывай ф-цию ожидания в цикле с фиксированным сравнительно небольшим таймаутом, пока ф-ция не вернет True.
← →
REA (2008-05-16 11:27) [5]>Не надо делать никаких предположений
почему? за 500 мсек короткий кадр 102 процента должен дойти. А если не дошел, то транспортный протокол все восстановит (что он собственно и делает). INFINITE не люблю с детства.
>Зачем ожидание-то, если ReadFile = True ?
А и нету ожидания. Если ReadFile = True, то Wait не вызывается. Или GetOverlappedResult тоже в этом случае ни к чему? Сейчас проверю.
← →
Сергей М. © (2008-05-16 11:32) [6]
> Или GetOverlappedResult тоже в этом случае ни к чему?
Конечно ни к чему.
В этом и главная ошибка
← →
REA (2008-05-16 11:37) [7]Проверил. Может и ни к чему, но ошибка видимо не в этом. ReadFile = False, Wait = True, Overlapped = True, 0 байт из ожидаемых 2х принятно.
← →
Сергей М. © (2008-05-16 11:46) [8]
> Wait = True
Ф-ция ожидания вообще-то возвращает не булев результат, а код завершения.
Ты попросту не дожидаешься данных (о чем я тебе и сказал, спросив откуда взялось 500), потому как ф-ция вернула не нулевой результат, в то время как именно 0-й результат (см. константу WAIT_OBJECT_0) означает успешное завершение ожидания в течение указанного тобой периода таймаута.
← →
Сергей М. © (2008-05-16 11:47) [9]Кстати, а каково состояние ивента на момент перед вызовом ф-ции ожидания ?
Ты вообще как создавал ивент, с какими параметрами ?
← →
REA (2008-05-16 14:40) [10]Не, там все как в исходниках написано. Т.е. проверяется на 0.
Таймаут там не возникает.
Event ручной, сбрасывается судя по MSDN сам при ReadFile.
← →
Сергей М. © (2008-05-16 14:58) [11]
> Т.е. проверяется на 0
А, да, вижу, есть такая проверка у тебя.
Что-то я не вижу про автосброс ивента ридфайлом ...
Ткни носом ?
← →
REA (2008-05-16 15:22) [12]ReadFile resets the event specified by the hEvent member of the OVERLAPPED structure to a nonsignaled state when it begins the I/O operation. Therefore, the caller does not need to do that.
Посмотрел COM монитором - байтики эти приходят, но уже потом, когда я решил, что это ошибка:
0.00000073 Geoscape.exe IOCTL_SERIAL_SET_WAIT_MASK Serial0 SUCCESS Mask: TXEMPTY - ждать пока последний байт ушел
0.00000312 Geoscape.exe IOCTL_SERIAL_SET_DTR Serial0 SUCCESS махнуть DTR
0.00001523 Geoscape.exe IRP_MJ_WRITE Serial0 SUCCESS Length 3: 51 00 4A записать
0.00000152 Geoscape.exe IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS подождать
0.00000071 Geoscape.exe IOCTL_SERIAL_SET_WAIT_MASK Serial0 SUCCESS Mask: сбросить маску
0.00000447 Geoscape.exe IOCTL_SERIAL_CLR_DTR Serial0 SUCCESS снять DTR
0.00000279 Geoscape.exe IRP_MJ_READ Serial0 SUCCESS Length 2: 31 00 читать 2 байта
0.00000201 Geoscape.exe IRP_MJ_READ Serial0 SUCCESS Length 1: 52 читать все остальное - тут порядок
0.00000148 Geoscape.exe IOCTL_SERIAL_SET_WAIT_MASK
Serial0 SUCCESS Mask: TXEMPTY далее по кргу
0.00000381 Geoscape.exe IOCTL_SERIAL_SET_DTR Serial0 SUCCESS
0.00002319 Geoscape.exe IRP_MJ_WRITE Serial0 SUCCESS Length 3: 51 00 4A
0.00000156 Geoscape.exe IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
0.00000077 Geoscape.exe IOCTL_SERIAL_SET_WAIT_MASK Serial0 SUCCESS Mask:
0.00000518 Geoscape.exe IOCTL_SERIAL_CLR_DTR Serial0 SUCCESS
0.00000362 Geoscape.exe IRP_MJ_READ Serial0 SUCCESS Length 2: 31 00
0.00000195 Geoscape.exe IRP_MJ_READ Serial0 SUCCESS Length 1: 52
0.00000162 Geoscape.exe IOCTL_SERIAL_SET_WAIT_MASK Serial0 SUCCESS Mask: TXEMPTY
0.00000386 Geoscape.exe IOCTL_SERIAL_SET_DTR Serial0 SUCCESS
0.00002064 Geoscape.exe IRP_MJ_WRITE Serial0 SUCCESS Length 3: 51 00 4A
0.00000173 Geoscape.exe IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
0.00000069 Geoscape.exe IOCTL_SERIAL_SET_WAIT_MASK Serial0 SUCCESS Mask:
0.00000441 Geoscape.exe IOCTL_SERIAL_CLR_DTR Serial0 SUCCESS
181.90626013 Geoscape.exe IRP_MJ_READ Serial0 SUCCESS Length 2: 34 28 - а вот тут я не дождался
0.00000424 Geoscape.exe IOCTL_SERIAL_PURGE Serial0 SUCCESS Purge: RXCLEAR тут все забыть и по кругу
← →
Сергей М. © (2008-05-16 15:29) [13]
> байтики эти приходят, но уже потом
Что значит "потом" ?
← →
REA (2008-05-16 15:35) [14]Ну значит я ставлю точку остановки в отладчике где ошибка и туда попадает, байтов на этот момент еще нет. А потом они в мониторе видны - значит не потерялись в проводах.
← →
Сергей М. © (2008-05-16 15:53) [15]А почему бы не воспользоваться ReadFileEx и колбэк-механизмом нотификации вместо овелэпа ?
← →
REA (2008-05-16 16:10) [16]Если не разберусь, то буду пытаться этот механизм задействовать. Что то мне подсказывает, что это не проще :)
Изменил код на такой:
RdRes := ReadFile(idComm, Buf[1], ToRead, Bytes, @IO);
If (Not RdRes) Then
Begin
If (GetLastError = ERROR_IO_PENDING) Then
Begin
RdRes := WaitForSingleObject(FIOEvent, 800) = 0;
If (RdRes) Then
RdRes := GetOverlappedResult(idComm, IO, Bytes, False)
Else
Begin
OutputDebugString(PChar("Wait bug: " + SysErrorMessage(GetLastError)));
End;
End Else OutputDebugString(PChar("ReadFile bug"));
End;
и выдает он чаще всего вот что:
ODS: Wait bug: Протекает наложенное событие ввода/вывода
и кто бы сомневался, что оно протекает... только почему то не вытекает
← →
Сергей М. © (2008-05-16 16:57) [17]ПРобуй так:
RdRes := ReadFile(idComm, Buf[1], ToRead, Bytes, @IO);
If not RdRes Then
Begin
If GetLastError = ERROR_IO_PENDING Then
Begin
while not (WaitForSingleObject(FIOEvent, 500) = WAIT_OBJECT_0) do Sleep(0);
Win32Check(GetOverlappedResult(idComm, IO, Bytes, False));
End Else
RaiseLastWin32Error;
End
← →
REA (2008-05-16 17:04) [18]Оно еще может возвратить WAIT_TIMEOUT, но мысль я понял. Тоже хотел так попробовать.
← →
Сергей М. © (2008-05-16 17:08) [19]
> Оно еще может возвратить WAIT_TIMEOUT
Может. Но выход из цикла только по WAIT_OBJECT_0
← →
REA (2008-05-16 17:28) [20]Выяснил что это все таки таймаут, но таймаут вне зависимости от его размера - т.е. хоть 20 секунд поставь. Переписал так:
If (GetLastError = ERROR_IO_PENDING) Then
Begin
RdRes := GetOverlappedResult(idComm, IO, Bytes, True); // А вдруг уже все?
If Not RdRes Then
Begin
WaitRes := WaitForSingleObject(IO.hEvent, 200);
RdRes := WaitRes = 0;
If (RdRes) Then
RdRes := GetOverlappedResult(idComm, IO, Bytes, True)
Else
Begin
OutputDebugString(PChar("Wait bug: " + SysErrorMessage(GetLastError)));
End
End Else OutputDebugString("Wow");
End Else OutputDebugString(PChar("ReadFile bug"));
ээфект от этого заклинания получился странный: ошибок кажется не возникает, но и сообщение "Wow" не выводит... Теперь придется проверить на одноядерных процессорах и на Win98 - сдается мне, что там оно себя может вести иначе...
← →
REA (2008-05-16 17:38) [21]А нет, все на месте :)
Если заменить
RdRes := GetOverlappedResult(idComm, IO, Bytes, True); на False, то выдает вот что:
ODS: Wait bug: Наложенное событие ввода/вывода не находится в сигнальном состоянии
Будем ломать дальше...
← →
REA (2008-05-16 18:08) [22]Все страньше и страньше. Таймаут теперь не возникает, но зато все как сначала: все отрабатывает Ok, но число байт = 0
Интересно еще вот что, если программа обменивается "пустыми" кадрами (там только заголовки без информации), то ошибок не возникает. Как только идет обмен "длинными" информационными кадрами возникает ошибка.
← →
Сергей М. © (2008-05-16 19:30) [23]Дай определение "пустого" и "длинного" информационного кадра ..
← →
Германн © (2008-05-17 01:20) [24]
> REA (16.05.08 10:41)
Могу переслать тебе книгу Павла Агурова. Самое, имхо лучшее пособие по работе с СОМ-портом. Скажи только куда.
← →
REA (2008-05-17 20:06) [25]>Дай определение "пустого" и "длинного" информационного кадра ..
ну пустой это байт 6, а длинный может 10 и более, но тоже не больше 16 наверно. Точнее не помню, но думаю это и не нужно.
>Могу переслать тебе книгу Павла Агурова. Самое, имхо лучшее пособие по работе с СОМ-портом. Скажи только куда.
Было бы здорово. Шлите на rea999<>hotmail.com
Только мне уже самому можно скоро будет книгу писать :)
← →
Германн © (2008-05-18 15:00) [26]
> REA (17.05.08 20:06) [25]
Выслал.
← →
Сергей М. © (2008-05-19 10:04) [27]
> пустой это байт 6, а длинный может 10 и более
6 байт - это отнюдь не "пустой кадр".
Даже 1 байт, посланный дивайсом по прикл.протоколу, уже не есть "пустой кадр".
Может ты порт не должным образом инициализируешь ?
← →
REA (2008-05-19 10:25) [28]>6 байт - это отнюдь не "пустой кадр".
С точки зрения протокола кадрового уровня я имел ввиду. Поэтому в кавычках.
В синхронном режиме все работало. С появлением многоядерных компьютеров работать перестало (связано с записью и маханием DTR).
Ну может и неправильно инициализирую. Хотя 12 лет отработало уже...
Все равно непонятно где байты пропадают.
← →
REA (2008-05-19 10:26) [29]>Германн
Спасибо!
← →
REA (2008-05-19 10:44) [30]Поиграл с настройками порта - действительно при одних значениях таймаутов порта возникает именно таймаут при WaitForSingleObject, а при других таймаут не возникает, но число байт = 0
Сейчас сделал чтобы возникал таймаут (все таймауты порта установлены в 0, кроме межбайтового на чтение = 4мс). Ищем дальше...
← →
Сергей М. © (2008-05-19 10:53) [31]А так ли уж необходим асинхронный оверлэп-режим ?
← →
REA (2008-05-19 11:25) [32]>А так ли уж необходим асинхронный оверлэп-режим ?
Дело в следующем: мне нужно дождаться ухода последнего байта, чтобы махнуть по протоколу DTR. Это можно сделать при помощи WaitCommEvent, но сама по себе эта функция таймаута не имеет и может зависнуть, если вызвать ее тогда, когда последний байт уже ушел. Что на многоядерных компьютерах и происходит. В Overlapped режиме можно результат проверить позже с таймаутом. Запись в порт в таком виде вроде работает.
← →
Сергей М. © (2008-05-19 11:34) [33]Думаю, у тебя как минимум 2 верных варианта избавления от геморроя:
1. Работай с портом проверенным тобой способом, т.е. в синхронном блок-режиме, но в доп.потоке, если это непозволительно для основного.
2. Работай в оверлэпе (или с колбэками), но исключи возможность выполнения неконтролируемых тобой потоков своего процесса разными ядрами (см. SetProcess/ThreadAffinitiMask)
← →
REA (2008-05-19 11:57) [34]>Работай с портом проверенным тобой способом, т.е. в синхронном блок-режиме, но в доп.потоке, если это непозволительно для основного
там это и так в доп. потоке
>Работай в оверлэпе (или с колбэками)
пробую с колбэками. Насчет ThreadAffinitiMask это как то совсем уж сурово.
да в принципе оно и так работает - протокол кадры перезапрашивает, но не люблю такие фокусы - не известно как оно себя поведет на других компьютерах
← →
Сергей М. © (2008-05-19 12:02) [35]
> там это и так в доп. потоке
А зачем тогда асинхронка ? Или этот доп.поток у тебя кроме собственно транспорта еще чем-то важным озадачен ?
> Насчет ThreadAffinitiMask это как то совсем уж сурово
Ну а как ты хотел ?
На то и AffinitiMask, чтобы явно ткнуть систему носом в упомянутый запрет
← →
REA (2008-05-19 12:24) [36]>А зачем тогда асинхронка ?
См. [32]
ReadFileEx под 98 не работает. Попробовал под XP (+SleepEx) - где то виснет переодически...
← →
Сергей М. © (2008-05-19 12:30) [37]
> ReadFileEx под 98 не работает
Да, не работает.
Но и упомянутых проблем с мультипроцессорностью там тоже быть не может, потому что оная поддерживается лишь в NT-линейке.
← →
REA (2008-05-19 12:51) [38]GetOverlappedResult
Windows Me/98/95: This function works only on communications devices or on files opened using the DeviceIoControl function.
уфф... засада за засадой
← →
Сергей М. © (2008-05-19 12:58) [39]Да тут-то вроде бы нет засады - ты же как раз с коммуникационным дивайсом работаешь ..
← →
REA (2008-05-19 14:36) [40]Так я же его открываю как CreateFile. А как тогда узнать, сколько байт реально прочиталось?
Страницы: 1 2 вся ветка
Форум: "WinAPI";
Текущий архив: 2010.03.28;
Скачать: [xml.tar.bz2];
Память: 0.56 MB
Время: 0.006 c