Pregunta

Estoy intentando usar boost :: asio para leer y escribir desde un dispositivo en un puerto serie. Tanto boost :: asio: read () como boost :: asio :: serial_port :: read_some () se bloquean cuando no hay nada que leer. En cambio, me gustaría detectar esta condición y escribir un comando en el puerto para iniciar el dispositivo.

¿Cómo puedo detectar que no hay datos disponibles?

Si es necesario, puedo hacer todo de forma asincrónica, prefiero evitar la complejidad adicional si puedo.

¿Fue útil?

Solución

Tienes un par de opciones, en realidad. Puede usar la función async_read_some integrada del puerto serie, o puede usar la función independiente boost :: asio :: async_read (o async_read_some ).

Todavía se encontrará con la situación en la que efectivamente está "bloqueado", ya que ninguno de estos llamará a la devolución de llamada a menos que (1) se hayan leído los datos o (2) se produzca un error. Para evitar esto, querrá usar un objeto deadline_timer para establecer un tiempo de espera. Si el tiempo de espera se dispara primero, no hay datos disponibles. De lo contrario, habrá leído los datos.

La complejidad añadida no es realmente tan mala. Terminarás con dos devoluciones de llamada con un comportamiento similar. Si ya sea " leer " o el "tiempo de espera" la devolución de llamada se dispara con un error, sabes que es el perdedor de la carrera. Si cualquiera de los dos dispara sin error, entonces sabe que es el ganador de la carrera (y debe cancelar la otra llamada). En el lugar donde hubiera tenido su llamada de bloqueo a read_some , ahora tendrá una llamada a io_svc.run () . Su función seguirá bloqueándose como antes cuando llama a ejecutar , pero esta vez usted controla la duración.

Aquí hay un ejemplo:

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
}

Eso debería comenzar con solo unos pocos ajustes aquí y allá para satisfacer sus necesidades específicas. ¡Espero que esto ayude!

Otra nota: no se necesitaron subprocesos adicionales para manejar las devoluciones de llamada. Todo se maneja dentro de la llamada a run () . No estoy seguro si ya lo sabía ...

Otros consejos

En realidad es mucho más simple de lo que las respuestas aquí han implicado, y puedes hacerlo sincrónicamente:

Suponga que su lectura de bloqueo es algo como esto:

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

Luego lo reemplaza con

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);

Utiliza la forma alternativa sobrecargada de recibir_de que tienen casi todos los métodos de envío / recepción. Desafortunadamente toman un argumento de banderas, pero 0 parece funcionar bien.

Debe usar la función libre asio :: async_read.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top