Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2010.03.28;
Скачать: CL | DM;

Вниз

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 вся ветка

Текущий архив: 2010.03.28;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.012 c
15-1263159025
Юрий
2010-01-11 00:30
2010.03.28
С днем рождения ! 11 января 2010 понедельник


1-1247504656
Дмитрий Белькевич
2009-07-13 21:04
2010.03.28
Особенности при работе с com портом в dll.


15-1263046404
Алекс555
2010-01-09 17:13
2010.03.28
Кто-то очень много ест .... памяти


15-1263034579
palva
2010-01-09 13:56
2010.03.28
Опять про Аватар


2-1264622951
Чайник
2010-01-27 23:09
2010.03.28
I/O error 998