2016-04-13 14 views
1

async_read操作がシリアルポートで正常に完了し、直ちに別のasync_read操作を開始してnバイトを読み取ると、2番目のasync_read操作成功するとすぐに突然終了し、0バイトが転送されます。boost :: asio :: async_readが完了条件を満たさずに終了する

  • async_read操作後、第三のasync_read操作がnバイトを読み取るために開始された場合、それは成功して完了し、1ミリ秒のスリープが実行される場合nバイトは

    // where buffer_size(buffer) and n are both greater than 1 
    async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { 
        // completes with error=success and bytes_transferred=n 
        async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { 
        // complete with error=success and bytes_transferred=0 
        async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { 
         // completes with error=success and bytes_transferred=n 
        }); 
        }); 
    }); 
    
  • 転送しました第1の動作と第2の動作との間で、第2の動作は成功し、nバイトが転送される。

    // where buffer_size(buffer) and n are both greater than 1 
    async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { 
        // completes with error=success and bytes_transferred=n 
        sleep_for(milliseconds(1)); 
        async_read(serial, buffer, transfer_exactly(n), [](error, bytes_transferred) { 
        // completes with error=success and bytes_transferred=n 
        }); 
    }); 
    

なぜこのようなことが起こりますか?どのように回避できますか?詳細については


、私はATXMEGA-192A3UでエミュレートRS232経由でマイクロコントローラと通信するには、Windows上でBoost.Asioを使用しています。私はコントローラに開始コマンドを送り、タイムアウトで出力を読みます。コードを以下に示すReadPort関数を呼び出すことによって出力を読み取りました。プログラムは、次の読み取りタスクを連続して実行します。

  1. マイクロコントローラの応答を開始コマンドで確認します。この読み込みは、私が期待している3文字を読み取るのに成功しました。R\r\n
  2. コントローラからの出力は、数百msの間でnバイトです。

ステップ2の操作は、要求されたバイト数を読み取っていないにもかかわらず、正常に完了します。

class BoostBasedCommunication 
{ 
public: 
    BoostBasedCommunication(); 
    ~BoostBasedCommunication(void); 
    /*...*/ 
    virtual int ReadPort(
     int const numberOfCharacters, // maximum number of characters to be read 
     unsigned long const globalTimeout, // maximum time the operation is allowed to take in ms 
     unsigned long const intermediateTimeout, // maximum time allowed between two consequtive characters in ms 
     int& numberOfCharactersRead 
     ); 
    /*...*/ 

private: 
    /*...*/ 
    std::vector<unsigned char> inputBuffer; ///< buffer to save data to that is received 
    size_t numberOfBytesRead; ///< Number of bytes read 
    int lastErrorCode; ///< last error code 
    io_service my_io_service; ///< boost io service class 
    serial_port port; ///< boost serial port class 
    /*...*/ 
}; 

// Reads from the port until numberOfCharacters have been read, or the 
// deadline_timer has expired, or the time between two consecutive calls of 
// the completion condition is larger than intermediateTimeoutMS 

int BoostBasedCommunication::ReadPort(
    int const numberOfCharacters, // maximum number of characters to be read 
    unsigned long const globalTimeoutMS, // maximum time the operation is allowed to take in ms 
    unsigned long const intermediateTimeoutMS, // maximum time allowed between two consecutive characters in ms 
    int& numberOfCharactersRead // Actual number of characters read 
    ) 
{ 
    try 
    { 
     OutputDebugStringA("ReadPort called\r\n"); 
     my_io_service.reset(); 
     deadline_timer gloabalTimeout(my_io_service); 
     inputBuffer.resize(numberOfCharacters); 
     timeoutHandler myGlobalTimeoutHandler(&port); 

     completion_handler_2 myHandler(&gloabalTimeout, numberOfBytesRead); 
     completion_condition_2 my_completion_condition(intermediateTimeoutMS, numberOfCharacters); 

     // Set the timer 
     gloabalTimeout.expires_from_now(boost::posix_time::milliseconds(globalTimeoutMS)); 
     gloabalTimeout.async_wait(myGlobalTimeoutHandler); 

     async_read(port, boost::asio::buffer(inputBuffer, numberOfCharacters), my_completion_condition, myHandler); 

     my_io_service.run(); // run the io service 
     numberOfCharactersRead = numberOfBytesRead; 
    } 
    catch (std::exception&) 
    { 
     return COMMUNICATIONFAILED; 
    } 
    return NOERROR; 
} 

class completion_condition_2 
{ 
public: 
    completion_condition_2(
     long intermediateTimeOutTime, 
     size_t numberOfCharactersTobeRead 
     ) :intermediateTimeOutTime(intermediateTimeOutTime), 
     numberOfCharactersTobeRead(numberOfCharactersTobeRead) 
    {} 

    std::size_t operator()(
     const boost::system::error_code& error, // Result of latest async_read_some operation. 
     std::size_t bytes_transferred // Number of bytes transferred so far. 
     ) 
    { 
     if (error) 
     { 
      OutputDebugStringA(("completion_condition received error code: " + error.message() + "\r\n").c_str()); 

      if (error.value() == ERROR_OPERATION_ABORTED) 
      { 
       return 0; 
      } 
     } 

     /* ...Code concerning the intermediate timeout, which is commented out...*/ 

     if (numberOfCharactersTobeRead <= bytes_transferred) // Enough data has been read 
     { 
      std::stringstream message; 
      message << "completion_condition: bytes transferred: " << bytes_transferred << " of " << numberOfCharactersTobeRead << " => done!" << std::endl; 
      OutputDebugStringA(message.str().c_str()); 
      return 0; 
     } 
     else // More data should be read. 
     { 
      std::stringstream message; 
      message << "completion_condition: bytes transferred: " << bytes_transferred << " of " << numberOfCharactersTobeRead << " => continue!" << std::endl; 
      OutputDebugStringA(message.str().c_str()); 
      return numberOfCharactersTobeRead - bytes_transferred; 
     } 
    } 

private: 
    size_t numberOfCharactersTobeRead; ///< Number of characters to be read 
}; 

class completion_handler_2 { 
public: 
    completion_handler_2(
     deadline_timer* _globalTimeout, 
     size_t& numberOfBytesRead 
     ) :_globalTimeout(_globalTimeout), 
     numberOfBytesRead(numberOfBytesRead) 
    { 
    } 

    void operator()(
     const boost::system::error_code& error, // Result of operation. 
     std::size_t bytes_transferred   // Number of bytes read. 
     ) 
    { 
     OutputDebugStringA(("completion handler called with error code: " + error.message() + "\r\n").c_str()); 
     if (error) 
     { 
      if (error.value() == ERROR_OPERATION_ABORTED) 
      { 
       numberOfBytesRead = bytes_transferred; 
       return; 
      } 
      else 
      { 
       BOOST_THROW_EXCEPTION(std::exception("Communication failed")); 
      } 
     } 

     OutputDebugStringA("completion handler: timeout cancelation.\r\n"); 

     _globalTimeout->cancel(); 
     numberOfBytesRead = bytes_transferred; 
    } 

private: 
    deadline_timer* _globalTimeout; ///< global timeout deadline timer 
    size_t& numberOfBytesRead; ///< number of bytes read 
}; 

私が期待どおりに動作している最初の読み取りを実行すると、私は次のような出力を受け取ります。

ReadPort called 
completion_condition: bytes transferred: 0 of 3 => continue! 
completion_condition: bytes transferred: 3 of 3 => done! 
completion handler called with error code: success 
completion handler timeout cancelation. 
timeoutHandler received error code: The I/O operation has been aborted because of either a thread exit or an application request 

最初が完了した後、私はすぐに別の読み取りを行う場合は、操作は2ミリ秒後に完了次の出力で:

ReadPort called 
completion_condition: bytes transferred: 0 of 1024 => continue! 
completion handler called with error code: success // Why is the completion handler called here, although the completion condition did not return 0? 
completion handler timeout cancelation. 
timeoutHandler received error code: The I/O operation has been aborted because of either a thread exit or an application request 

3回目のリード、すぐに最後の1以下の期待通りに動作します:

要するに
ReadPort called 
completion_condition: bytes transferred: 0 of 1024 => continue! 
completion_condition: bytes transferred: 8 of 1024 => continue! 
... 
completion_condition: bytes transferred: 88 of 1024 => continue! 
completion_condition: bytes transferred: 96 of 1024 => continue! 
timeoutHandler called cancel of seriel port. 
completion_condition received error code: The I/O operation has been aborted because of either a thread exit or an application request 
completion handler called with error code: The I/O operation has been aborted because of either a thread exit or an application request 

答えて

1

、根本的な問題はいずれかです:タイムアウトとReadFile API契約の

  • ASIOの解釈が間違っている
  • 通信ドライバが

ReadFile API契約に違反しています最も簡単な解決策は、アプリケーションコード内のこの動作を考慮して、前のコードが成功して0バイトが読み込まれた場合は別のasync_readオペレーションを発行することです。通信ドライバの実装によっては、読み取り間に1ミリ秒のスリープが発生するが動作する可能性があります。


COMMTIMEOUTSドキュメントは述べている読ん間隔のタイムアウトのために:

ミリ秒単位で、通信回線上の次のバイトの到着前に経過することが許容される最大時間。が2バイトの到着間隔の間にの間隔を超えると、ReadFileの操作が完了し、バッファされたデータが返されます。ドキュメント、すなわち強調したテキスト、の

ASIOの解釈がためReadFile操作与えられたということである[...]、読ん間隔タイムアウトが最初のバイトが読み取られた後に開始されます。暗黙のことは、ReadFileが0バイト以上の読み込みを要求された場合、AsioはReadFileオペレーションが0バイトを同期または非同期に正常に読み取ったことを示すステータスを返すとは期待していません。この解釈では、Asioのimplementationは、1ミリ秒の読み取り間隔タイムアウトでシリアルポートを設定しますです。

// Set up timeouts so that the serial port will behave similarly to a 
// network socket. Reads wait for at least one byte, then return with 
// whatever they have. Writes return once everything is out the door. 
::COMMTIMEOUTS timeouts; 
timeouts.ReadIntervalTimeout = 1; 
timeouts.ReadTotalTimeoutMultiplier = 0; 
timeouts.ReadTotalTimeoutConstant = 0; 

async_read中間async_read_some操作にゼロまたはそれ以上の呼び出しで実装なる操作です。 async_readオペレーションは、成功して完了し、0バイトを次のように転送された中間のasync_read_someオペレーションを解釈します。作成されたオペレーションがそれ以上進行しないため、オペレーションは完了します(async_read)。この解釈は、ReadFileの基になるシステムコールが、予期せず成功と同期して完了し、0バイトが読み込まれたときに問題になります。

  • 操作は、少なくとも単一バイト
  • パッチASIOを読んだ後、タイムアウト間隔だけReadFile動作に始まるように通信ドライバをパッチ:この詳細を

    、代替ソリューションのいずれかになっています。観察された動作がReadFileであると詳述し、ReadFileオペレーションが同期して完了した場合にのみ発生することがわかった場合、async_read_some()オペレーションをwin_iocp_handle_service::start_read_opにパッチすることができます。そうでなければ、さまざまなread_op特殊化にパッチを適用して、0バイトが読み込まれたが0以上が要求された場合にはcompletion predicateが終了しないようにすることができます。


1.通信ドライバの実装は、その後の間眠って、読ん間隔タイムアウト間隔以内に開始されReadFilen+1動作に影響を与えるためにReadFilen操作のために読んで最後のバイトで開始読ま間隔のタイムアウトを許可された場合ReadFilenReadFilen+1を読み込み間隔のタイムアウトの期間使用すると、Asioの読み込み操作が成功しなくなり、指定されたバッファのサイズが0バイトより大きい場合は0バイトになります。

+0

これを念頭に置いて、私はコンピュータとマイクロコントローラ間の安定した通信を確立することができました。 しかし、私は1msのタイムアウトの効果を理解していないと思います。マイクロコントローラは、いくつかの点で話し手として機能し、約7.5msごとに8バイトを出力する。私はタイムアウトを打つことなく数百ミリ秒間ポートを読み取ることができます。最初の8バイトの後に通信が中断しないのはなぜですか?次の8バイトが到着するまで1ミリ秒以上かかりますか? –

+1

@PaulR。 'ReadFile'は、1ミリ秒の読み取り間隔タイムアウト設定のために8バイト後にタイムアウトします。あなたの出力では、 'async_read'合成操作で1024バイトを読み込もうとすると、中間の' async_read_some'演算ではそれぞれ8バイトが読み込まれます。 –

+0

私は理解します。したがって、8バイトの後に、私の完了条件コールバックが呼び出されます。これは私が観察しているものです。 –

関連する問題