Tienes un par de opciones, en realidad. Puede usar la función incorporada del puerto serie async_read_some
, 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 está efectivamente "bloqueado", ya que ninguno de estos llamará la devolución de llamada a menos que (1) los datos se hayan leído o (2) se haya producido 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á con dos devoluciones de llamada con un comportamiento similar. Si la devolución de llamada de "lectura" o "tiempo de espera" se activa con un error, sabrá que es el perdedor de la carrera. Si alguno de ellos se dispara sin un error, entonces sabrá que es el ganador de la carrera (y debe cancelar la otra llamada). En el lugar donde habría tenido su llamada de bloqueo al read_some
, ahora tendrá una llamada al io_svc.run()
. Su función todavía bloqueará como antes cuando llama al run
, pero esta vez usted controla la duración.
He aquí 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 empezar con sólo unos pocos ajustes aquí y allá para satisfacer sus necesidades específicas. ¡Espero que esto ayude!
Otra nota: No se necesitaron hilos adicionales para manejar las devoluciones de llamada. Todo se maneja dentro de la llamada al run()
. No estoy seguro si ya era consciente de ello ...
He utilizado este enfoque, y funciona bien, pero solo en la primera vez. La próxima llamada a io_svc.run() regresa inmediatamente. ¿Tendría que hacer algo extra? Gracias – Schiavini
aparentemente tuve que llamar a io_svc.reset() después de io_svc.run(). – Schiavini
excelente respuesta, muy útil. Sin embargo, estoy obteniendo un comportamiento extraño: tengo un programa escribiendo datos en el puerto serial cada 5 ms y otra lectura desde allí; sin embargo, si configuro el retraso asíncrono en 5 ms en la parte del lector, aún obtengo algunos "datos no disponibles" llamadas mezcladas con los valores de lectura.¿Esto es causado por una sobrecarga en el código o me falta algo? – joaocandre