Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "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.005 c
2-1264626549
kalexi
2010-01-28 00:09
2010.03.28
Недопустимый дескриптор окна при закрытии приложения


2-1264099254
lamer6666
2010-01-21 21:40
2010.03.28
SMS центр


3-1237289266
Yurikon
2009-03-17 14:27
2010.03.28
Количество записей в TADOTable


3-1237104687
joomler
2009-03-15 11:11
2010.03.28
Как сделать поиск по нескольким полям в базе?


15-1262890350
AlexDan
2010-01-07 21:52
2010.03.28
подключение через проксисервер..





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский