1

Мое приложение использует отдельный поток для обработки полученных последовательных данных асинхронно. ПК попадает в приемник-получатель, как ожидалось, но оттуда все странно.Последовательный порт Получение темы не работает как ожидается - C++

Это моя функция потока:

// Create event for OVERLAPPED structure. 
s_ov.hEvent = ::CreateEvent(
    NULL,       // No security 
    TRUE,       // Create a manual-reset event object 
    FALSE,       // Initial state is non-signaled 
    NULL       // No name specified 
    ); 

// Load event handles. 
pHandles[0] = s_hSerialPortRxThreadExitEvent; 

while (bContinue) 
{ 
    if (!::WaitCommEvent(s_hSerialPort, &dwEventMask, &s_ov)) 
    { 
     if (::GetLastError() != ERROR_IO_PENDING) 
     { 
      TRACE(_T("SerialPortRxThreadFn : Call to WaitCommEvent failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
      return ::GetLastError(); 
     } 
    } 

    pHandles[1] = s_ov.hEvent; 

    dwObjectWaitState = ::WaitForMultipleObjects(2, pHandles, FALSE, INFINITE); 

    switch (dwObjectWaitState) 
    { 
    case WAIT_ABANDONED: 
     TRACE(_T("SerialPortRxThreadFn : Owner thread terminated prematurely.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ERROR_ARENA_TRASHED, __WFILE__, __LINE__); 
     return ERROR_ARENA_TRASHED; 
     break; 

    case WAIT_TIMEOUT: 
     TRACE(_T("SerialPortRxThreadFn : The timeout is set to INFINITE; there should be no timeout. State is nonsignaled.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), WAIT_TIMEOUT, __WFILE__, __LINE__); 
     return WAIT_TIMEOUT; 
     break; 

    case WAIT_FAILED: 
     TRACE(_T("SerialPortRxThreadFn : Call to WaitCommEvent failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
     return ::GetLastError(); 
     break; 

    case WAIT_OBJECT_0:    // thread exit event signalled 
     bContinue = FALSE; 

     if (!::ResetEvent(pHandles[0])) 
     { 
      TRACE(_T("SerialPortRxThreadFn : Failed to reset the serial port thread exit event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
      return ::GetLastError(); 
     } 
     break; 

    case WAIT_OBJECT_0 + 1:   // OVERLAPPED structure event signalled 
     // Read data from serial port. 
     if (!::ReadFile(s_hSerialPort, pBuf, RX_BUF_SIZE, &dwWritten, &s_ov)) // <- Set breakpoint here 
     { 
      TRACE(_T("SerialPortRxThreadFn : Call to ReadFile filed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
      return ::GetLastError(); 
     } 

     // Discontinue thread operation if there are no more bytes in the serial port receive buffer. 
     if (dwWritten == 0) // <- Or, set breakpoint here 
     { 
      bContinue = FALSE; 
     } 
     // Copy the received bytes to the thread-safe buffer. 
     else if (!s_pobjRxRingBuffer->Add(pBuf, dwWritten, TRUE)) 
     { 
      TRACE(_T("SerialPortRxThreadFn : Failed to add bytes to ring buffer.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ERROR_INSUFFICIENT_BUFFER, __WFILE__, __LINE__); 
      return ERROR_INSUFFICIENT_BUFFER; 
     } 
     else if (s_SpCallbackFn != NULL) 
     { 
      // Notify application of received data. 
      if ((dwRetVal = s_SpCallbackFn(s_pobjRxRingBuffer->ItemsInBuffer())) != ERROR_SUCCESS) 
      { 
       TRACE(_T("SerialPortRxThreadFn : Serial port callback function failed.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwRetVal, __WFILE__, __LINE__); 
       return dwRetVal; 
      } 
     } 

     if (!::ResetEvent(pHandles[1])) 
     { 
      TRACE(_T("SerialPortRxThreadFn : Failed to reset the OVERLAPPED structure event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__); 
      return ::GetLastError(); 
     } 
     break; 

    default: 
     // Do nothing. 
     break; 
    } 
} 

::CloseHandle(s_ov.hEvent); 

return ERROR_SUCCESS; 

Если я могу установить точку останова на линии вызывающего ReadFile все работает, как я ожидал, и компьютер получает в функцию обратного вызова. Однако, если я установил свою точку останова на следующей строке, где dwWritten оценивается на ноль, она равна нулю, выражение оценивается как ИСТИНА, и цикл завершается; ПК никогда не попадает на обратный вызов. Что я делаю не так? Благодарю.

+1

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

+1

Также вы пропустите ручку, вы не можете вернуться из функции, не закрывая ее, но вы делаете это при ошибках. – doron

+1

Перед вызовом ReadFile вам необходимо сбросить событие pHandles [1]. –

ответ

2

Я не эксперт по API Win32, но это, безусловно, звучит как проблема времени (что является общей причиной heisenbugs.) Предположим, к тому времени, когда вы доберетесь до ReadFile, нет данных для чтения. Включение в отладчик может дать ему достаточно паузы для поступления данных, поэтому, когда вы возобновляете/перешагиваете через ReadFile, это удается.

Есть много вещей, кроме прибытия данных, которые могут вызвать событие. Вы можете проверить свой dwEventMask, чтобы убедиться, что моя гипотеза верна.

+0

Все, что вы сказали, правда, но предложение плохое. Использование 'dwEventMask' - неправильный способ узнать, получены ли данные. Правильный способ - вызвать «SetCommTimeouts» и «ReadFile», чтобы чтение было завершено, как только данные находятся в буфере приема ядра. 'WaitCommEvent' сообщает вам другие вещи, такие как состояние контактов управления потоком. –

+0

@Ben Возможно, я был неясно. Я не имел в виду, что обработка должна основываться на 'dwEventMask'. Я имел в виду, что 'dwEventMask' должен быть проверен, чтобы установить, истинна ли моя гипотеза. – NPE

+0

До начала потока 'EV_RXCHAR' - единственный флаг, который я установил в своем вызове' SetCommMask'.Существуют ли еще другие флаги, которые 'WaitCommEvent' мог бы получить? Я думал, что он будет извлекать только флаги, установленные в маске. –

0

В документации WaiCommEvent указано, что после использования функции ожидания (например, WaitForMultipleEObjects (...)) вы используете функцию GetOverlappedResult (...) для получения результатов вашей операции. Не нужно читать Read \ Write-File (...).

+0

Вы не можете получить полученные байты без вызова 'ReadFile'. На самом деле это 'ReadFile', который необходим, а' WaitCommEvent' - нет. –

+0

@Ben Voigt: Я думаю, вы ошибаетесь. Если вы прочитали документацию на WaitCommEvent, вы можете прочитать, что для параметра EventMask имеется флаг EV_RXCHAR. И ReadFile выбор не нужен. –

+0

Я использовал эти функции раньше. Поверьте мне, вы можете читать в режиме перекрытия, используя 'ReadFile' (или' ReadFileEx', который я предпочитаю) и вообще не 'WaitCommEvent'. 'ReadFile' использует событие ядра в своем параметре OVERLAPPED, который вы можете ждать. 'WaitCommEvent' может сообщить вам, когда есть активность на последовательном порту, но он не даст вам фактических данных. –

1

Довольно больно смотреть этот код, написав некоторые из них. Многословие этого, ну, лучше всего застряло в чужой библиотеке классов. Пара красных флагов. Вы предполагаете, что завершение WaitCommEvent() означает, что вы можете вызвать ReadFile(). Как правило, маска события, которую вы использовали, не отображается, но есть много других причин, по которым последовательный порт хочет что-то сказать вам. Другая проблема заключается в том, что WaitCommEvent может завершить работу сразу. Это не редкость, что-то доступно в буфере приема.

Украсть этот код откуда-нибудь, это жесткий код. Это было сделано.

+0

До начала потока 'EV_RXCHAR' - единственный флаг, который я установил в своем вызове' SetCommMask'. К сожалению, код заимствования извне компании не является вариантом в этом случае. Я могу посмотреть на примеры и т. Д., Но весь код должен быть написан мной или кем-то еще в компании. –

0

Вам не нужны комм-события для асинхронного чтения данных. Просто позвоните ReadFile, вы получите «ошибку» ERROR_IO_PENDING, и когда данные поступят, событие будет сигнализировано, и вы сможете получить количество байтов GetOverlappedResult, данные будут в буфере, который вы первоначально поставили на ReadFile.

Смежные вопросы