00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00036
00037
00038
00039
00040
00041
00042
00043
00044 #ifndef ASYNCSERIAL_H
00045 #define ASYNCSERIAL_H
00046
00047 #include <vector>
00048 #include <boost/asio.hpp>
00049 #include <boost/bind.hpp>
00050 #include <boost/thread.hpp>
00051 #include <boost/utility.hpp>
00052 #include <boost/function.hpp>
00053 #include <boost/shared_array.hpp>
00054
00055 namespace serialcom
00056 {
00057 const unsigned int read_buffer_size=512;
00058 }
00059
00060 #ifndef __APPLE__
00061
00066 class AsyncSerialImpl:private boost::noncopyable
00067 {
00068 public:
00069 AsyncSerialImpl():
00070 io ( ),
00071 port ( io ),
00072 backgroundThread ( ),
00073 open ( false ),
00074 error ( false )
00075 {
00076 }
00077
00078 boost::asio::io_service io;
00079
00080 boost::asio::serial_port port;
00081 boost::thread backgroundThread;
00082 bool open;
00083
00084 bool error;
00085
00086 mutable boost::mutex errorMutex;
00087
00089 std::vector <char> writeQueue;
00090
00091 boost::shared_array <char> writeBuffer;
00092
00093 size_t writeBufferSize;
00094
00095 boost::mutex writeQueueMutex;
00096
00097 char readBuffer[serialcom::read_buffer_size];
00098
00100 boost::function < void ( const char *, size_t ) > callback;
00101 };
00102
00108 class AsyncSerial:private boost::noncopyable
00109 {
00110 public:
00111
00112 AsyncSerial()
00113 :pimpl ( new AsyncSerialImpl )
00114 {
00115 }
00116
00128 AsyncSerial ( const std::string & devname, unsigned int baud_rate,
00129 boost::asio::serial_port_base::parity opt_parity =
00130 boost::asio::serial_port_base::parity ( boost::asio::
00131 serial_port_base::parity::none ),
00132 boost::asio::serial_port_base::character_size opt_csize =
00133 boost::asio::serial_port_base::character_size ( 8 ),
00134 boost::asio::serial_port_base::flow_control opt_flow =
00135 boost::asio::serial_port_base::flow_control ( boost::
00136 asio::serial_port_base::flow_control::
00137 none ),
00138 boost::asio::serial_port_base::stop_bits opt_stop =
00139 boost::asio::serial_port_base::stop_bits ( boost::asio::
00140 serial_port_base::stop_bits::
00141 one ) )
00142 :pimpl ( new AsyncSerialImpl )
00143 {
00144 open ( devname, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop );
00145 }
00146
00158 void open ( const std::string & devname, unsigned int baud_rate,
00159 boost::asio::serial_port_base::parity opt_parity =
00160 boost::asio::serial_port_base::parity ( boost::asio::serial_port_base::
00161 parity::none ),
00162 boost::asio::serial_port_base::character_size opt_csize =
00163 boost::asio::serial_port_base::character_size ( 8 ),
00164 boost::asio::serial_port_base::flow_control opt_flow =
00165 boost::asio::serial_port_base::flow_control ( boost::asio::serial_port_base::flow_control::none ),
00166 boost::asio::serial_port_base::stop_bits opt_stop =
00167 boost::asio::serial_port_base::stop_bits ( boost::asio::serial_port_base::stop_bits::one ) )
00168 {
00169 if ( isOpen ( ) )
00170 close ( );
00171
00172 setErrorStatus ( true );
00173 pimpl->port.open ( devname );
00174 pimpl->port.set_option ( boost::asio::serial_port_base::baud_rate ( baud_rate ) );
00175 pimpl->port.set_option ( opt_parity );
00176 pimpl->port.set_option ( opt_csize );
00177 pimpl->port.set_option ( opt_flow );
00178 pimpl->port.set_option ( opt_stop );
00179
00180
00181 pimpl->io.post ( boost::bind ( &AsyncSerial::doRead, this ) );
00182
00183 boost::thread t ( boost::bind ( &boost::asio::io_service::run, &pimpl->io ) );
00184
00185 pimpl->backgroundThread.swap ( t );
00186 setErrorStatus ( false );
00187 pimpl->open = true;
00188 }
00189
00193 bool isOpen ( ) const
00194 {
00195 return pimpl->open;
00196 }
00197
00201 bool errorStatus ( ) const
00202 {
00203 boost::lock_guard < boost::mutex > l ( pimpl->errorMutex );
00204 return pimpl->error;
00205 }
00206
00211 void close()
00212 {
00213 if ( !isOpen ( ) )
00214 return;
00215
00216 pimpl->open = false;
00217 pimpl->io.post ( boost::bind ( &AsyncSerial::doClose, this ) );
00218 pimpl->backgroundThread.join ( );
00219 pimpl->io.reset ( );
00220 if ( errorStatus ( ) )
00221 {
00222 throw ( boost::system::system_error ( boost::system::error_code ( ),"Error while closing the device" ) );
00223 }
00224 }
00225
00231 void write ( const char *data, size_t size )
00232 {
00233 {
00234 boost::lock_guard < boost::mutex > l ( pimpl->writeQueueMutex );
00235 pimpl->writeQueue.insert ( pimpl->writeQueue.end ( ), data, data + size );
00236 }
00237
00238 pimpl->io.post ( boost::bind ( &AsyncSerial::doWrite, this ) );
00239 }
00240
00245 void write ( const std::vector < char >&data )
00246 {
00247 {
00248 boost::lock_guard < boost::mutex > l ( pimpl->writeQueueMutex );
00249 pimpl->writeQueue.insert ( pimpl->writeQueue.end ( ), data.begin ( ),data.end ( ) );
00250 }
00251
00252 pimpl->io.post ( boost::bind ( &AsyncSerial::doWrite, this ) );
00253 }
00254
00261 void writeString ( const std::string & s )
00262 {
00263 {
00264 boost::lock_guard < boost::mutex > l ( pimpl->writeQueueMutex );
00265 pimpl->writeQueue.insert ( pimpl->writeQueue.end ( ), s.begin ( ), s.end ( ) );
00266 }
00267
00268 pimpl->io.post ( boost::bind ( &AsyncSerial::doWrite, this ) );
00269 }
00270
00271 virtual ~AsyncSerial ( )
00272 {
00273 if ( isOpen ( ) )
00274 {
00275 try
00276 {
00277 close ( );
00278 }
00279 catch ( ... )
00280 {
00281
00282 }
00283 }
00284 }
00288 static const int readBufferSize = 512;
00289
00290 private:
00291
00296 void doRead ( )
00297 {
00298 pimpl->port.async_read_some ( boost::asio::buffer ( pimpl->readBuffer, readBufferSize ),
00299 boost::bind ( &AsyncSerial::readEnd,
00300 this,
00301 boost::asio::placeholders::error,
00302 boost::asio::placeholders::bytes_transferred ) );
00303 }
00304
00309 void readEnd ( const boost::system::error_code & error, size_t bytes_transferred )
00310 {
00311 if ( error )
00312 {
00313 #ifdef __APPLE__
00314 if ( error.value ( ) == 45 )
00315 {
00316
00317
00318 doRead ( );
00319 return;
00320 }
00321 #endif //__APPLE__
00322
00323
00324
00325 if ( isOpen ( ) )
00326 {
00327 doClose ( );
00328 setErrorStatus ( true );
00329 }
00330 } else
00331 {
00332 if ( pimpl->callback )
00333 pimpl->callback ( pimpl->readBuffer, bytes_transferred );
00334 doRead ( );
00335 }
00336 }
00337
00343 void doWrite ( )
00344 {
00345
00346 if ( pimpl->writeBuffer == 0 )
00347 {
00348 boost::lock_guard < boost::mutex > l ( pimpl->writeQueueMutex );
00349 pimpl->writeBufferSize = pimpl->writeQueue.size ( );
00350 pimpl->writeBuffer.reset ( new char[pimpl->writeQueue.size ( )] );
00351
00352 copy ( pimpl->writeQueue.begin ( ), pimpl->writeQueue.end ( ),
00353 pimpl->writeBuffer.get ( ) );
00354 pimpl->writeQueue.clear ( );
00355 async_write ( pimpl->port, boost::asio::buffer ( pimpl->writeBuffer.get ( ),
00356 pimpl->writeBufferSize ),
00357 boost::bind ( &AsyncSerial::writeEnd, this,
00358 boost::asio::placeholders::error ) );
00359 }
00360 }
00361
00367 void writeEnd ( const boost::system::error_code & error )
00368 {
00369 if ( !error )
00370 {
00371 boost::lock_guard < boost::mutex > l ( pimpl->writeQueueMutex );
00372 if ( pimpl->writeQueue.empty ( ) )
00373 {
00374 pimpl->writeBuffer.reset ( );
00375 pimpl->writeBufferSize = 0;
00376
00377 return;
00378 }
00379 pimpl->writeBufferSize = pimpl->writeQueue.size ( );
00380 pimpl->writeBuffer.reset ( new char[pimpl->writeQueue.size ( )] );
00381 copy ( pimpl->writeQueue.begin ( ), pimpl->writeQueue.end ( ),
00382 pimpl->writeBuffer.get ( ) );
00383 pimpl->writeQueue.clear ( );
00384 async_write ( pimpl->port, boost::asio::buffer ( pimpl->writeBuffer.get ( ),
00385 pimpl->writeBufferSize ),
00386 boost::bind ( &AsyncSerial::writeEnd, this,
00387 boost::asio::placeholders::error ) );
00388 } else
00389 {
00390 setErrorStatus ( true );
00391 doClose ( );
00392 }
00393 }
00394
00398 void doClose ( )
00399 {
00400 boost::system::error_code ec;
00401 pimpl->port.cancel ( ec );
00402 if ( ec )
00403 setErrorStatus ( true );
00404 pimpl->port.close ( ec );
00405 if ( ec )
00406 setErrorStatus ( true );
00407 }
00408
00409 boost::shared_ptr <AsyncSerialImpl > pimpl;
00410
00411 protected:
00412
00417 void setErrorStatus ( bool e )
00418 {
00419 boost::lock_guard < boost::mutex > l ( pimpl->errorMutex );
00420 pimpl->error = e;
00421 }
00422
00426 void setReadCallback ( const boost::function < void ( const char *, size_t ) > &callback )
00427 {
00428 pimpl->callback = callback;
00429 }
00430
00431
00437 void clearReadCallback ( )
00438 {
00439 pimpl->callback.clear ( );
00440 }
00441
00442 };
00443
00444 #else //__APPLE__
00445
00446 #include <sys/types.h>
00447 #include <sys/stat.h>
00448 #include <fcntl.h>
00449 #include <termios.h>
00450 #include <unistd.h>
00451
00452 class AsyncSerialImpl:private boost::noncopyable
00453 {
00454 public:
00455 AsyncSerialImpl():
00456 backgroundThread ( ),
00457 open ( false ),
00458 error ( false )
00459 {
00460 }
00461
00462 boost::thread backgroundThread;
00463
00464 bool open;
00465
00466 bool error;
00467
00468 mutable boost::mutex errorMutex;
00469
00470 int fd;
00471
00472 char readBuffer[AsyncSerial::readBufferSize];
00473
00475 boost::function < void ( const char *, size_t ) >callback;
00476 };
00477
00483 class AsyncSerial:private boost::noncopyable
00484 {
00485 public:
00486
00487 AsyncSerial()
00488 :pimpl ( new AsyncSerialImpl )
00489 {
00490 }
00491
00503 AsyncSerial ( const std::string & devname, unsigned int baud_rate,
00504 boost::asio::serial_port_base::parity opt_parity =
00505 boost::asio::serial_port_base::parity ( boost::asio::
00506 serial_port_base::parity::none ),
00507 boost::asio::serial_port_base::character_size opt_csize =
00508 boost::asio::serial_port_base::character_size ( 8 ),
00509 boost::asio::serial_port_base::flow_control opt_flow =
00510 boost::asio::serial_port_base::flow_control ( boost::
00511 asio::serial_port_base::flow_control::
00512 none ),
00513 boost::asio::serial_port_base::stop_bits opt_stop =
00514 boost::asio::serial_port_base::stop_bits ( boost::asio::
00515 serial_port_base::stop_bits::
00516 one ) )
00517 :pimpl ( new AsyncSerialImpl )
00518 {
00519 open ( devname, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop );
00520 }
00521
00522
00534 void open ( const std::string & devname, unsigned int baud_rate,
00535 boost::asio::serial_port_base::parity opt_parity =
00536 boost::asio::serial_port_base::parity ( boost::asio::serial_port_base::
00537 parity::none ),
00538 boost::asio::serial_port_base::character_size opt_csize =
00539 boost::asio::serial_port_base::character_size ( 8 ),
00540 boost::asio::serial_port_base::flow_control opt_flow =
00541 boost::asio::serial_port_base::flow_control ( boost::asio::
00542 serial_port_base::flow_control::
00543 none ),
00544 boost::asio::serial_port_base::stop_bits opt_stop =
00545 boost::asio::serial_port_base::stop_bits ( boost::asio::
00546 serial_port_base::stop_bits::
00547 one ) )
00548 {
00549 if ( isOpen ( ) )
00550 close ( );
00551
00552 setErrorStatus ( true );
00553
00554 struct termios new_attributes;
00555
00556 speed_t speed;
00557
00558 int status;
00559
00560
00561 pimpl->fd =::open ( devname.c_str ( ), O_RDWR | O_NOCTTY | O_NONBLOCK );
00562 if ( pimpl->fd < 0 )
00563 throw ( boost::system::system_error ( boost::system::error_code ( ),"Failed to open port" ) );
00564
00565
00566 status = tcgetattr ( pimpl->fd, &new_attributes );
00567 if ( status < 0 || !isatty ( pimpl->fd ) )
00568 {
00569 ::close ( pimpl->fd );
00570 throw ( boost::system::system_error ( boost::system::error_code ( ),"Device is not a tty" ) );
00571 }
00572 new_attributes.c_iflag = IGNBRK;
00573 new_attributes.c_oflag = 0;
00574 new_attributes.c_lflag = 0;
00575 new_attributes.c_cflag = ( CS8 | CREAD | CLOCAL );
00576
00577
00578
00579
00580
00581 new_attributes.c_cc[VMIN] = 1;
00582 new_attributes.c_cc[VTIME] = 1;
00583
00584
00585 switch ( baud_rate )
00586 {
00587 case 50:
00588 speed = B50;
00589 break;
00590 case 75:
00591 speed = B75;
00592 break;
00593 case 110:
00594 speed = B110;
00595 break;
00596 case 134:
00597 speed = B134;
00598 break;
00599 case 150:
00600 speed = B150;
00601 break;
00602 case 200:
00603 speed = B200;
00604 break;
00605 case 300:
00606 speed = B300;
00607 break;
00608 case 600:
00609 speed = B600;
00610 break;
00611 case 1200:
00612 speed = B1200;
00613 break;
00614 case 1800:
00615 speed = B1800;
00616 break;
00617 case 2400:
00618 speed = B2400;
00619 break;
00620 case 4800:
00621 speed = B4800;
00622 break;
00623 case 9600:
00624 speed = B9600;
00625 break;
00626 case 19200:
00627 speed = B19200;
00628 break;
00629 case 38400:
00630 speed = B38400;
00631 break;
00632 case 57600:
00633 speed = B57600;
00634 break;
00635 case 115200:
00636 speed = B115200;
00637 break;
00638 case 230400:
00639 speed = B230400;
00640 break;
00641 default:
00642 {
00643 ::close ( pimpl->fd );
00644 throw ( boost::system::system_error ( boost::system::error_code ( ),"Unsupported baud rate" ) );
00645 }
00646 }
00647
00648 cfsetospeed ( &new_attributes, speed );
00649 cfsetispeed ( &new_attributes, speed );
00650
00651
00652 status = tcsetattr ( pimpl->fd, TCSANOW, &new_attributes );
00653 if ( status < 0 )
00654 {
00655 ::close ( pimpl->fd );
00656 throw ( boost::system::system_error ( boost::system::error_code ( ),"Can't set port attributes" ) );
00657 }
00658
00659 status = fcntl ( pimpl->fd, F_GETFL, 0 );
00660 if ( status != -1 )
00661 fcntl ( pimpl->fd, F_SETFL, status & ~O_NONBLOCK );
00662
00663 setErrorStatus ( false );
00664 pimpl->open = true;
00665
00666 boost::thread t ( bind ( &AsyncSerial::doRead, this ) );
00667
00668 pimpl->backgroundThread.swap ( t );
00669 }
00670
00674 bool isOpen ( ) const
00675 {
00676 return pimpl->open;
00677 }
00678
00682 bool errorStatus ( ) const
00683 {
00684 boost::lock_guard < boost::mutex > l ( pimpl->errorMutex );
00685 return pimpl->error;
00686 }
00687
00692 void close()
00693 {
00694 if ( !isOpen ( ) )
00695 return;
00696
00697 pimpl->open = false;
00698
00699 ::close ( pimpl->fd );
00700
00701 pimpl->backgroundThread.join ( );
00702 if ( errorStatus ( ) )
00703 {
00704 throw ( boost::system::system_error ( boost::system::error_code ( ),"Error while closing the device" ) );
00705 }
00706 }
00707
00708
00714 void write ( const char *data, size_t size )
00715 {
00716 if ( ::write ( pimpl->fd, data, size ) != size )
00717 setErrorStatus ( true );
00718 }
00719
00724 void write ( const std::vector < char >&data )
00725 {
00726 if ( ::write ( pimpl->fd, &data[0], data.size ( ) ) != data.size ( ) )
00727 setErrorStatus ( true );
00728 }
00729
00736 void writeString ( const std::string & s )
00737 {
00738 if ( ::write ( pimpl->fd, &s[0], s.size ( ) ) != s.size ( ) )
00739 setErrorStatus ( true );
00740 }
00741
00742 virtual ~AsyncSerial ( )
00743 {
00744 if ( isOpen ( ) )
00745 {
00746 try
00747 {
00748 close ( );
00749 }
00750 catch ( ... )
00751 {
00752
00753 }
00754 }
00755 }
00756
00760 static const int readBufferSize = 512;
00761
00762 private:
00763
00768 void doRead ( )
00769 {
00770
00771 for ( ;; )
00772 {
00773 int
00774 received =::read ( pimpl->fd, pimpl->readBuffer, readBufferSize );
00775
00776 if ( received < 0 )
00777 {
00778 if ( isOpen ( ) == false )
00779 return;
00780 else
00781 {
00782 setErrorStatus ( true );
00783 continue;
00784 }
00785 }
00786
00787 if ( pimpl->callback )
00788 pimpl->callback ( pimpl->readBuffer, received );
00789 }
00790 }
00791
00796 void readEnd ( const boost::system::error_code & error, size_t bytes_transferred )
00797 {
00798
00799 }
00800
00806 void doWrite ( )
00807 {
00808
00809 }
00810
00816 void writeEnd ( const boost::system::error_code & error )
00817 {
00818
00819 }
00820
00824 void doClose ( )
00825 {
00826
00827 }
00828
00829 boost::shared_ptr <AsyncSerialImpl > pimpl;
00830
00831 protected:
00832
00837 void setErrorStatus ( bool e )
00838 {
00839 boost::lock_guard < boost::mutex > l ( pimpl->errorMutex );
00840 pimpl->error = e;
00841 }
00842
00846 void setReadCallback ( const boost::function < void ( const char *, size_t ) > &callback )
00847 {
00848 pimpl->callback = callback;
00849 }
00850
00851
00857 void clearReadCallback ( )
00858 {
00859 pimpl->callback.clear ( );
00860 }
00861
00862 };
00863
00864 #endif //__APPLE__
00865
00872 class CallbackAsyncSerial: public AsyncSerial
00873 {
00874 public:
00875 CallbackAsyncSerial ( )
00876 :AsyncSerial ( )
00877 {
00878
00879 }
00880
00892 CallbackAsyncSerial ( const std::string & devname, unsigned int baud_rate,
00893 boost::asio::serial_port_base::parity opt_parity =
00894 boost::asio::serial_port_base::parity ( boost::asio::serial_port_base::parity::
00895 none ),
00896 boost::asio::serial_port_base::character_size opt_csize =
00897 boost::asio::serial_port_base::character_size ( 8 ),
00898 boost::asio::serial_port_base::flow_control opt_flow =
00899 boost::asio::serial_port_base::flow_control ( boost::asio::serial_port_base::flow_control::
00900 none ),
00901 boost::asio::serial_port_base::stop_bits opt_stop =
00902 boost::asio::serial_port_base::stop_bits ( boost::asio::serial_port_base::stop_bits::
00903 one ) )
00904 :AsyncSerial ( devname, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop )
00905 {
00906
00907 }
00908
00915 void setCallback ( const boost::function < void ( const char *, size_t ) > &callback )
00916 {
00917 setReadCallback ( callback );
00918 }
00919
00924 void clearCallback ( )
00925 {
00926 clearReadCallback ( );
00927 }
00928
00929 virtual ~ CallbackAsyncSerial ( )
00930 {
00931 clearReadCallback ( );
00932 }
00933 };
00934
00935 #endif //ASYNCSERIAL_H