When reading information from a serial_port, it seems like I'm required to poll the port for data. Is it possible to receive asynchronous events when data is available, kind of like how TCP/IP works? --------- Robert Dailey
Robert Dailey wrote:
When reading information from a serial_port, it seems like I'm required to poll the port for data. Is it possible to receive asynchronous events when data is available, kind of like how TCP/IP works?
--------- Robert Dailey
Robert - Just use the serial_port like you would a TCP/IP socket. For example, I do this in one of my classes reading streams from hardware: serial_port.async_read_some( boost::asio::buffer( in_message, MAX_MESSAGE_SIZE ), boost::bind( &SerialClient::read_done, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred ) ); Why are you having to poll? Michael -- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
On Tue, Aug 25, 2009 at 6:36 PM, Michael Caisse < boost@objectmodelingdesigns.com> wrote:
Robert Dailey wrote:
When reading information from a serial_port, it seems like I'm required to poll the port for data. Is it possible to receive asynchronous events when data is available, kind of like how TCP/IP works?
--------- Robert Dailey
Robert -
Just use the serial_port like you would a TCP/IP socket. For example, I do this in one of my classes reading streams from hardware:
serial_port.async_read_some( boost::asio::buffer( in_message, MAX_MESSAGE_SIZE ), boost::bind( &SerialClient::read_done, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred ) );
Why are you having to poll?
What I'm really asking is: How do you know when to call async_read_some()? Ideally I only want to call into the socket to read information when I get a callback saying that there is actually something in the read buffer to get. A short sequence of what I want to happen: 1. Send a message over the COM port 2. Wait for the device to reply (The device would send a message back) 3. Receive a callback saying that a new message (new data) is available and ready to be read 4. Call async_read() to obtain that data.
Robert Dailey wrote:
On Tue, Aug 25, 2009 at 6:36 PM, Michael Caisse
mailto:boost@objectmodelingdesigns.com> wrote: Why are you having to poll?
What I'm really asking is: How do you know when to call async_read_some()? Ideally I only want to call into the socket to read information when I get a callback saying that there is actually something in the read buffer to get. A short sequence of what I want to happen:
1. Send a message over the COM port 2. Wait for the device to reply (The device would send a message back) 3. Receive a callback saying that a new message (new data) is available and ready to be read 4. Call async_read() to obtain that data.
Robert - It looks like you might be thinking about the problem in a synchronous manner. Let me suggest a different way to think about the problem. Let us pretend that your messages across the COM port are delimited in some manner (like a standard STX/ETX). You now have some object that invokes an async_read_some for the port and just waits. I wont digress on all the reasons I think async_read_some is the right choice... for now lets just assume that my way works (o; The completion handler for async_read_some will look for the STX and then collect bytes until it finds an ETX. When the completion handler is called it may very well not have an entire message in the buffer. That is fine. It will just start another async_read_some. Eventually a message will be formed. This is a good point to receive your callback saying that a new message is available. Not only available... but here it is. That is the basic idea. When do you call async_read? Right after you open the port. The reads are just going on in the background putting together useful atoms of data and then alerting you that something worthwhile is available to look at. So... you might have some thing like this: (I'm just now making this up for illustration ... not for efficiency and may not even work) void my_message_maker::read_start() { serial_port.async_read_some( boost::asio::buffer( in_message, MAX_MESSAGE_SIZE ), boost::bind( &my_message_maker::read_done, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred ) ); } void my_message_maker::read_done( const boost::system::error_code& error, int bytes_transferred ) { if( !error ) { for( int i=0; i < bytes_transferred; ++i ) { if( in_message[ i ] == STX ) { my_message.clear(); } else if( in_message[ i ] == ETX ) { make_callback( my_message ); } else { my_message += in_message[ i ]; } } } else { close_port( error ); } } I hope this helps some. Obviously the example has issues but it might give you another way to think about the problem. Michael -- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
Whoops ... forgot to start the read again in the completion handler. "Fixed" below. Michael Caisse wrote:
void my_message_maker::read_start() { serial_port.async_read_some( boost::asio::buffer( in_message, MAX_MESSAGE_SIZE ), boost::bind( &my_message_maker::read_done, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred ) ); }
void my_message_maker::read_done( const boost::system::error_code& error, int bytes_transferred ) { if( !error ) { for( int i=0; i < bytes_transferred; ++i ) { if( in_message[ i ] == STX ) { my_message.clear(); } else if( in_message[ i ] == ETX ) { make_callback( my_message ); } else { my_message += in_message[ i ]; }
read_start(); }
} else { close_port( error ); } }
-- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
On Tue, Aug 25, 2009 at 9:04 PM, Michael Caisse < boost@objectmodelingdesigns.com> wrote:
Whoops ... forgot to start the read again in the completion handler. "Fixed" below.
Michael Caisse wrote:
void my_message_maker::read_start() { serial_port.async_read_some( boost::asio::buffer( in_message, MAX_MESSAGE_SIZE ), boost::bind( &my_message_maker::read_done, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred ) ); }
void my_message_maker::read_done( const boost::system::error_code& error, int bytes_transferred ) { if( !error ) { for( int i=0; i < bytes_transferred; ++i ) { if( in_message[ i ] == STX ) { my_message.clear(); } else if( in_message[ i ] == ETX ) { make_callback( my_message ); } else { my_message += in_message[ i ]; }
read_start(); }
}
else { close_port( error ); } }
Ah, i see what you're saying now. I was thinking from a lower level (i.e. that I would not call any read methods until I was notified from the socket/serial port layer that information was ready). It's actually a good idea to take it up to a higher level like this and validate data completion and defragment the buffers. Would you recommend calling read_start() from its own thread and have it loop indefinitely? I believe this is what you originally suggested. However, I am curious about the issues with calling read_some() and write_some() from separate threads without using any external synchronization techniques (i.e. mutexes). Do I have to synchronize these two calls manually or is it done internally? I'll re-review the documentation, but I did not see anything regarding this earlier. Thanks for your help.
Robert Dailey wrote:
Would you recommend calling read_start() from its own thread and have it loop indefinitely? I believe this is what you originally suggested. However, I am curious about the issues with calling read_some() and write_some() from separate threads without using any external synchronization techniques (i.e. mutexes). Do I have to synchronize these two calls manually or is it done internally? I'll re-review the documentation, but I did not see anything regarding this earlier.
Thanks for your help.
The io_service takes care of the "threading" issues for you. Just call read_start once from (lets say) main to get the processes going and then it all happens in the background... asynchronously. The call to read_start doesn't block. If you want different threads to operate on the io (though I recommend not doing this at first) then you can use a thread pool to call the io_service.run method from multiple threads. Have a look at some of the examples to see how it is all put together. Reading/writing to serial ports is the same as reading/writing to sockets using asio. I like to recommend the chat server/client example. It pulls together a lot of asio concepts in an easy to understand application. Michael -- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
On Wed, Aug 26, 2009 at 11:16 AM, Michael Caisse < boost@objectmodelingdesigns.com> wrote:
Robert Dailey wrote:
Would you recommend calling read_start() from its own thread and have it loop indefinitely? I believe this is what you originally suggested. However, I am curious about the issues with calling read_some() and write_some() from separate threads without using any external synchronization techniques (i.e. mutexes). Do I have to synchronize these two calls manually or is it done internally? I'll re-review the documentation, but I did not see anything regarding this earlier.
Thanks for your help.
The io_service takes care of the "threading" issues for you. Just call read_start once from (lets say) main to get the processes going and then it all happens in the background... asynchronously. The call to read_start doesn't block. If you want different threads to operate on the io (though I recommend not doing this at first) then you can use a thread pool to call the io_service.run method from multiple threads.
Have a look at some of the examples to see how it is all put together. Reading/writing to serial ports is the same as reading/writing to sockets using asio. I like to recommend the chat server/client example. It pulls together a lot of asio concepts in an easy to understand application.
Thanks, I'll have a look at that. I understand what you are saying. I actually figured that out shortly after I posted. So I'm calling read_start() at the beginning and letting it take over, however I have one concern. I have a thread that loops, continuously calling io_service::run(). Will run() block while waiting for data to read? If so, this could potentially block any writes that occur after it. For example, suppose I do an async_read_some(), and then shortly after that I call boost::asio::async_write(). Will the write be blocked by the read? I'm probably going to be sending a lot more data than receiving it, and if this is the case it would really deadlock my application. Thanks again for all of the great support.
participants (2)
-
Michael Caisse
-
Robert Dailey