Конференция "WinAPI" » COM порт, асинхронные чтение запись
 
  • REA (16.05.08 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.
    Что я неправильно делаю?
  • Сергей М. © (16.05.08 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 ?
  • МистерТ (16.05.08 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 (16.05.08 11:13) [3]
    >А где обработка случая RdRes = True ?

    Если ReadFile = True, то WaitForSingleObject как я понимаю уже ни к чему. Или нет?
    Напоминаю, что ошибка замечена при RdRes = True. Т.е. из файла читается, потом отрабатывает WaitForSingleObject (таймаут не возникает) и GetOverlappedResult и считано 0 байт. Как будто какой то таймаут COM порта возник... При чем при синхронном чтении все работало достаточно стабильно.

    >Откуда ты знаешь, что данные будут доступны не позднее пол-секунды ?
    Должны быть практически сразу они. Там читается 2 байта на скорости 57600 и передатчик отвечает быстро.

    >Может проще будет проверять сколько байт накопилось ?
    Может... но и так работало в синхронном режиме
  • Сергей М. © (16.05.08 11:22) [4]

    > Если ReadFile = True, то WaitForSingleObject как я понимаю
    > уже ни к чему


    Именно так.


    > из файла читается, потом отрабатывает WaitForSingleObject


    Зачем ожидание-то, если  ReadFile = True ?
    Сразу забирай данные из буфера в кол-ве lpNumberOfBytesRead


    > Должны быть практически сразу они


    Не надо делать никаких предположений.

    Поставь INFINITE, если ожидание у тебя не в гуёвом потоке - гарантированно дождешься данные.

    Или вызывай ф-цию ожидания в цикле с фиксированным сравнительно небольшим таймаутом, пока ф-ция не вернет True.
  • REA (16.05.08 11:27) [5]
    >Не надо делать никаких предположений

    почему? за 500 мсек короткий кадр 102 процента должен дойти. А если не дошел, то транспортный протокол все восстановит (что он собственно и делает). INFINITE не люблю с детства.

    >Зачем ожидание-то, если  ReadFile = True ?
    А и нету ожидания. Если ReadFile = True, то Wait не вызывается. Или GetOverlappedResult тоже в этом случае ни к чему? Сейчас проверю.
  • Сергей М. © (16.05.08 11:32) [6]

    > Или GetOverlappedResult тоже в этом случае ни к чему?


    Конечно ни к чему.
    В этом и главная ошибка
  • REA (16.05.08 11:37) [7]
    Проверил. Может и ни к чему, но ошибка видимо не в этом. ReadFile = False, Wait = True, Overlapped = True, 0 байт из ожидаемых 2х принятно.
  • Сергей М. © (16.05.08 11:46) [8]

    > Wait = True


    Ф-ция ожидания вообще-то возвращает не булев результат, а код завершения.
    Ты попросту не дожидаешься данных (о чем я тебе и сказал, спросив откуда взялось 500), потому как ф-ция вернула не нулевой результат, в то время как именно 0-й результат (см. константу WAIT_OBJECT_0) означает успешное завершение ожидания в течение указанного тобой периода таймаута.
  • Сергей М. © (16.05.08 11:47) [9]
    Кстати, а каково состояние ивента на момент перед вызовом ф-ции ожидания ?
    Ты вообще как создавал ивент, с какими параметрами ?
  • REA (16.05.08 14:40) [10]
    Не, там все как в исходниках написано. Т.е. проверяется на 0.
    Таймаут там не возникает.
    Event ручной, сбрасывается судя по MSDN сам при ReadFile.
  • Сергей М. © (16.05.08 14:58) [11]

    > Т.е. проверяется на 0


    А, да, вижу, есть такая проверка у тебя.

    Что-то я не вижу про автосброс ивента ридфайлом ...
    Ткни носом ?
  • REA (16.05.08 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 тут все забыть и по кругу
  • Сергей М. © (16.05.08 15:29) [13]

    > байтики эти приходят, но уже потом


    Что значит "потом" ?
  • REA (16.05.08 15:35) [14]
    Ну значит я ставлю точку остановки в отладчике где ошибка и туда попадает, байтов на этот момент еще нет. А потом они в мониторе видны - значит не потерялись в проводах.
  • Сергей М. © (16.05.08 15:53) [15]
    А почему бы не воспользоваться ReadFileEx и колбэк-механизмом нотификации вместо овелэпа ?
  • REA (16.05.08 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: Протекает наложенное событие ввода/вывода

    и кто бы сомневался, что оно протекает... только почему то не вытекает
  • Сергей М. © (16.05.08 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 (16.05.08 17:04) [18]
    Оно еще может возвратить WAIT_TIMEOUT, но мысль я понял. Тоже хотел так попробовать.
  • Сергей М. © (16.05.08 17:08) [19]

    > Оно еще может возвратить WAIT_TIMEOUT


    Может. Но выход из цикла только по WAIT_OBJECT_0
 
Конференция "WinAPI" » COM порт, асинхронные чтение запись
Есть новые Нет новых   [134435   +34][b:0][p:0.002]