16

boost::asio を使用して、シリアル ポート上のデバイスから読み書きしようとしています。読み取るものが何もない場合は、boost::asio:read() と boost::asio::serial_port::read_some() の両方がブロックされます。代わりに、この状態を検出し、ポートにコマンドを書き込んでデバイスをキックスタートさせたいと考えています。

利用可能なデータがないことをどのように検出できますか?

必要に応じて、すべてを非同期で行うことができます。できれば、余分な複雑さを避けたいだけです。

4

3 に答える 3

19

実際には、いくつかのオプションがあります。シリアルポートの組み込みasync_read_some機能を使用することも、スタンドアロン機能boost::asio::async_read(またはasync_read_some) を使用することもできます。

(1) データが読み取られるか、(2) エラーが発生しない限り、どちらもコールバックを呼び出さないため、事実上「ブロックされた」状況に陥ります。これを回避するには、deadline_timerオブジェクトを使用してタイムアウトを設定します。タイムアウトが最初に発生した場合は、データが利用できませんでした。そうしないと、データを読み取ってしまいます。

追加された複雑さは、それほど悪いことではありません。同様の動作を持つ 2 つのコールバックが発生します。「読み取り」コールバックまたは「タイムアウト」コールバックのいずれかがエラーで発生した場合、それはレースの敗者であることがわかります。いずれかがエラーなしで発火した場合、それがレースの勝者であることがわかります (そして、もう一方の呼び出しをキャンセルする必要があります)。へのブロッキング コールがあった場所で、 へread_someのコールが行われio_svc.run()ます。関数は を呼び出すときに以前と同じようにブロックされますrunが、今回は期間を制御します。

次に例を示します。

void foo()
{
  io_service     io_svc;
  serial_port    ser_port(io_svc, "your string here");
  deadline_timer timeout(io_svc);
  unsigned char  my_buffer[1];
  bool           data_available = false;

  ser_port.async_read_some(boost::asio::buffer(my_buffer),
      boost::bind(&read_callback, boost::ref(data_available), boost::ref(timeout),
                  boost::asio::placeholders::error,
                  boost::asio::placeholders::bytes_transferred));
  timeout.expires_from_now(boost::posix_time::milliseconds(<<your_timeout_here>>));
  timeout.async_wait(boost::bind(&wait_callback, boost::ref(ser_port),
                  boost::asio::placeholders::error));

  io_svc.run();  // will block until async callbacks are finished

  if (!data_available)
  {
    kick_start_the_device();
  }
}

void read_callback(bool& data_available, deadline_timer& timeout, const boost::system::error_code& error, std::size_t bytes_transferred)
{
  if (error || !bytes_transferred)
  {
    // No data was read!
    data_available = false;
    return;
  }

  timeout.cancel();  // will cause wait_callback to fire with an error
  data_available = true;
}

void wait_callback(serial_port& ser_port, const boost::system::error_code& error)
{
  if (error)
  {
    // Data was read and this timeout was canceled
    return;
  }

  ser_port.cancel();  // will cause read_callback to fire with an error
}

これにより、特定のニーズに合わせてあちこちで微調整を行うだけで開始できるはずです。これが役立つことを願っています!

別の注意: コールバックを処理するために余分なスレッドは必要ありませんでした。すべてが への呼び出し内で処理されますrun()。あなたがすでにこれを知っていたかどうかはわかりません...

于 2009-11-17T18:42:31.840 に答える
7

実際には、ここでの回答が暗示しているよりもはるかに単純であり、同期的に実行できます。

ブロッキング読み取りが次のようなものであるとします。

size_t len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint);

次に、それを置き換えます

socket.non_blocking(true);
size_t len = 0;
error = boost::asio::error::would_block;
while (error == boost::asio::error::would_block)
    //do other things here like go and make coffee
    len = socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, error);
std::cout.write(recv_buf.data(), len);

ほとんどすべての送信/受信メソッドが持つ、代替のオーバーロードされた形式の receive_from を使用します。残念ながら flags 引数を取りますが、0 は問題なく動作するようです。

于 2017-01-06T01:26:07.820 に答える
1

フリー関数 asio::async_read を使用する必要があります。

于 2009-04-15T22:49:57.597 に答える