2009-01-18 11 views
11

Estoy intentando utilizar boost :: asio para leer y escribir desde un dispositivo en un puerto serie. Both boost :: asio: read() y boost :: asio :: serial_port :: read_some() bloquean cuando no hay nada que leer. En cambio, me gustaría detectar esta condición y escribir un comando en el puerto para poner en marcha el dispositivo.¿Cómo realizo una lectura sin bloqueo usando asio?

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

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

Respuesta

17

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 ...

+0

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

+3

aparentemente tuve que llamar a io_svc.reset() después de io_svc.run(). – Schiavini

+0

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

1

Debes utilizar la función libre asio :: async_read.

1

En realidad es mucho más simple que las respuestas aquí han implicado, y puede hacerlo de forma sincrónica:

Suponga que su lectura bloqueo fue algo como esto:

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

Luego que se sustituya por

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

se utiliza la versión sobrecargada alternativa de receive_from la que casi todo el envío/recepción métodos tienen. Desafortunadamente toman un argumento de banderas pero 0 parece funcionar bien.

+0

Su ejemplo cubre el ejemplo del socket, mientras que la pregunta era sobre el puerto serie. –

Cuestiones relacionadas