AsyncSerial.cpp
Go to the documentation of this file.
00001 /**************************************************************************************************
00002  Software License Agreement (BSD License)
00003 
00004  Copyright (c) 2011-2013, LAR toolkit developers - University of Aveiro - http://lars.mec.ua.pt
00005  All rights reserved.
00006 
00007  Redistribution and use in source and binary forms, with or without modification, are permitted
00008  provided that the following conditions are met:
00009 
00010   *Redistributions of source code must retain the above copyright notice, this list of
00011    conditions and the following disclaimer.
00012   *Redistributions in binary form must reproduce the above copyright notice, this list of
00013    conditions and the following disclaimer in the documentation and/or other materials provided
00014    with the distribution.
00015   *Neither the name of the University of Aveiro nor the names of its contributors may be used to
00016    endorse or promote products derived from this software without specific prior written permission.
00017  
00018  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
00019  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
00020  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
00021  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
00024  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00025  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00026 ***************************************************************************************************/
00055 // ORIGINAL BELOW
00056 /*
00057  * File:   AsyncSerial.cpp
00058  * Author: Terraneo Federico
00059  * Distributed under the Boost Software License, Version 1.0.
00060  * Created on September 7, 2009, 10:46 AM
00061  *
00062  * v1.02: Fixed a bug in BufferedAsyncSerial: Using the default constructor
00063  * the callback was not set up and reading didn't work.
00064  *
00065  * v1.01: Fixed a bug that did not allow to reopen a closed serial port.
00066  *
00067  * v1.00: First release.
00068  *
00069  * IMPORTANT:
00070  * On Mac OS X boost asio's serial ports have bugs, and the usual implementation
00071  * of this class does not work. So a workaround class was written temporarily,
00072  * until asio (hopefully) will fix Mac compatibility for serial ports.
00073  * 
00074  * Please note that unlike said in the documentation on OS X until asio will
00075  * be fixed serial port *writes* are *not* asynchronous, but at least
00076  * asynchronous *read* works.
00077  * In addition the serial port open ignores the following options: parity,
00078  * character size, flow, stop bits, and defaults to 8N1 format.
00079  * I know it is bad but at least it's better than nothing.
00080  *
00081  */
00082 
00083 #include <pressure_cells/AsyncSerial.h>
00084 
00085 #include <string>
00086 #include <algorithm>
00087 #include <iostream>
00088 #include <boost/bind.hpp>
00089 
00090 using namespace std;
00091 
00092 using namespace boost;
00093 
00094 //
00095 //Class AsyncSerial
00096 //
00097 
00098 #ifndef __APPLE__
00099 
00100 class AsyncSerialImpl:private
00101    boost::noncopyable {
00102  public:
00103    AsyncSerialImpl (  ):
00104    io (  ),
00105    port ( io ),
00106    backgroundThread (  ),
00107    open ( false ),
00108    error ( false ) {
00109    }
00110    boost::asio::io_service
00111       io;                       
00112 
00113    boost::asio::serial_port port;   
00114    boost::thread backgroundThread;  
00115    bool open;                   
00116 
00117    bool error;                  
00118 
00119    mutable boost::mutex errorMutex; 
00120 
00122    std::vector < char >
00123       writeQueue;
00124 
00125    boost::shared_array < char >
00126       writeBuffer;              
00127 
00128    size_t writeBufferSize;      
00129 
00130    boost::mutex writeQueueMutex;    
00131    char
00132      readBuffer[AsyncSerial::readBufferSize];   
00133 
00135    boost::function < void ( const char *, size_t ) >
00136       callback;
00137 };
00138 
00139 AsyncSerial::AsyncSerial (  ):pimpl ( new AsyncSerialImpl )
00140 {
00141 
00142 }
00143 
00144 AsyncSerial::AsyncSerial ( const std::string & devname, unsigned int baud_rate,
00145                            asio::serial_port_base::parity opt_parity,
00146                            asio::serial_port_base::character_size opt_csize,
00147                            asio::serial_port_base::flow_control opt_flow,
00148                            asio::serial_port_base::stop_bits opt_stop ):
00149 pimpl ( new AsyncSerialImpl )
00150 {
00151    open ( devname, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop );
00152 }
00153 
00154 void
00155 
00156 
00157 
00158 
00159 
00160 
00161 
00162 
00163  
00164    AsyncSerial::open ( const std::string & devname, unsigned int baud_rate,
00165                        asio::serial_port_base::parity opt_parity,
00166                        asio::serial_port_base::character_size opt_csize,
00167                        asio::serial_port_base::flow_control opt_flow,
00168                        asio::serial_port_base::stop_bits opt_stop )
00169 {
00170    if ( isOpen (  ) )
00171       close (  );
00172 
00173    setErrorStatus ( true );     //If an exception is thrown, error_ remains true
00174    pimpl->port.open ( devname );
00175    pimpl->port.set_option ( asio::serial_port_base::baud_rate ( baud_rate ) );
00176    pimpl->port.set_option ( opt_parity );
00177    pimpl->port.set_option ( opt_csize );
00178    pimpl->port.set_option ( opt_flow );
00179    pimpl->port.set_option ( opt_stop );
00180 
00181    //This gives some work to the io_service before it is started
00182    pimpl->io.post ( boost::bind ( &AsyncSerial::doRead, this ) );
00183 
00184    thread t ( boost::bind ( &asio::io_service::run, &pimpl->io ) );
00185 
00186    pimpl->backgroundThread.swap ( t );
00187    setErrorStatus ( false );    //If we get here, no error
00188    pimpl->open = true;          //Port is now open
00189 }
00190 
00191 bool AsyncSerial::isOpen (  ) const
00192 {
00193    return pimpl->open;
00194 }
00195 
00196 bool AsyncSerial::errorStatus (  ) const
00197 {
00198    lock_guard < mutex > l ( pimpl->errorMutex );
00199    return pimpl->error;
00200 }
00201 
00202 void
00203   AsyncSerial::close (  )
00204 {
00205    if ( !isOpen (  ) )
00206       return;
00207 
00208    pimpl->open = false;
00209    pimpl->io.post ( boost::bind ( &AsyncSerial::doClose, this ) );
00210    pimpl->backgroundThread.join (  );
00211    pimpl->io.reset (  );
00212    if ( errorStatus (  ) )
00213      {
00214         throw ( boost::system::system_error ( boost::system::error_code (  ),
00215                                               "Error while closing the device" ) );
00216      }
00217 }
00218 
00219 void
00220   AsyncSerial::write ( const char *data, size_t size )
00221 {
00222    {
00223       lock_guard < mutex > l ( pimpl->writeQueueMutex );
00224       pimpl->writeQueue.insert ( pimpl->writeQueue.end (  ), data, data + size );
00225    }
00226    pimpl->io.post ( boost::bind ( &AsyncSerial::doWrite, this ) );
00227 }
00228 
00229 void
00230   AsyncSerial::write ( const std::vector < char >&data )
00231 {
00232    {
00233       lock_guard < mutex > l ( pimpl->writeQueueMutex );
00234       pimpl->writeQueue.insert ( pimpl->writeQueue.end (  ), data.begin (  ),
00235                                  data.end (  ) );
00236    }
00237    pimpl->io.post ( boost::bind ( &AsyncSerial::doWrite, this ) );
00238 }
00239 
00240 void
00241   AsyncSerial::writeString ( const std::string & s )
00242 {
00243    {
00244       lock_guard < mutex > l ( pimpl->writeQueueMutex );
00245       pimpl->writeQueue.insert ( pimpl->writeQueue.end (  ), s.begin (  ), s.end (  ) );
00246    }
00247    pimpl->io.post ( boost::bind ( &AsyncSerial::doWrite, this ) );
00248 }
00249 
00250 AsyncSerial::~AsyncSerial (  )
00251 {
00252    if ( isOpen (  ) )
00253      {
00254         try
00255         {
00256            close (  );
00257         }
00258         catch ( ... )
00259         {
00260            //Don't throw from a destructor
00261         }
00262      }
00263 }
00264 
00265 void
00266   AsyncSerial::doRead (  )
00267 {
00268    pimpl->port.async_read_some ( asio::buffer ( pimpl->readBuffer, readBufferSize ),
00269                                  boost::bind ( &AsyncSerial::readEnd,
00270                                                this,
00271                                                asio::placeholders::error,
00272                                                asio::placeholders::bytes_transferred ) );
00273 }
00274 
00275 void
00276   AsyncSerial::readEnd ( const boost::system::error_code & error,
00277                           size_t bytes_transferred )
00278 {
00279    if ( error )
00280      {
00281 #ifdef __APPLE__
00282         if ( error.value (  ) == 45 )
00283           {
00284              //Bug on OS X, it might be necessary to repeat the setup
00285              //http://osdir.com/ml/lib.boost.asio.user/2008-08/msg00004.html
00286              doRead (  );
00287              return;
00288           }
00289 #endif //__APPLE__
00290         //error can be true even because the serial port was closed.
00291         //In this case it is not a real error, so ignore
00292         if ( isOpen (  ) )
00293           {
00294              doClose (  );
00295              setErrorStatus ( true );
00296           }
00297    } else
00298      {
00299         if ( pimpl->callback )
00300            pimpl->callback ( pimpl->readBuffer, bytes_transferred );
00301         doRead (  );
00302      }
00303 }
00304 
00305 void
00306   AsyncSerial::doWrite (  )
00307 {
00308    //If a write operation is already in progress, do nothing
00309    if ( pimpl->writeBuffer == 0 )
00310      {
00311         lock_guard < mutex > l ( pimpl->writeQueueMutex );
00312         pimpl->writeBufferSize = pimpl->writeQueue.size (  );
00313         pimpl->writeBuffer.reset ( new char[pimpl->writeQueue.size (  )] );
00314 
00315         copy ( pimpl->writeQueue.begin (  ), pimpl->writeQueue.end (  ),
00316                pimpl->writeBuffer.get (  ) );
00317         pimpl->writeQueue.clear (  );
00318         async_write ( pimpl->port, asio::buffer ( pimpl->writeBuffer.get (  ),
00319                                                   pimpl->writeBufferSize ),
00320                       boost::bind ( &AsyncSerial::writeEnd, this,
00321                                     asio::placeholders::error ) );
00322      }
00323 }
00324 
00325 void
00326   AsyncSerial::writeEnd ( const boost::system::error_code & error )
00327 {
00328    if ( !error )
00329      {
00330         lock_guard < mutex > l ( pimpl->writeQueueMutex );
00331         if ( pimpl->writeQueue.empty (  ) )
00332           {
00333              pimpl->writeBuffer.reset (  );
00334              pimpl->writeBufferSize = 0;
00335 
00336              return;
00337           }
00338         pimpl->writeBufferSize = pimpl->writeQueue.size (  );
00339         pimpl->writeBuffer.reset ( new char[pimpl->writeQueue.size (  )] );
00340         copy ( pimpl->writeQueue.begin (  ), pimpl->writeQueue.end (  ),
00341                pimpl->writeBuffer.get (  ) );
00342         pimpl->writeQueue.clear (  );
00343         async_write ( pimpl->port, asio::buffer ( pimpl->writeBuffer.get (  ),
00344                                                   pimpl->writeBufferSize ),
00345                       boost::bind ( &AsyncSerial::writeEnd, this,
00346                                     asio::placeholders::error ) );
00347    } else
00348      {
00349         setErrorStatus ( true );
00350         doClose (  );
00351      }
00352 }
00353 
00354 void
00355   AsyncSerial::doClose (  )
00356 {
00357    boost::system::error_code ec;
00358    pimpl->port.cancel ( ec );
00359    if ( ec )
00360       setErrorStatus ( true );
00361    pimpl->port.close ( ec );
00362    if ( ec )
00363       setErrorStatus ( true );
00364 }
00365 
00366 void
00367   AsyncSerial::setErrorStatus ( bool e )
00368 {
00369    lock_guard < mutex > l ( pimpl->errorMutex );
00370    pimpl->error = e;
00371 }
00372 
00373 void
00374    AsyncSerial::setReadCallback ( const boost::function < void ( const char *, size_t ) >
00375                                   &callback )
00376 {
00377    pimpl->callback = callback;
00378 }
00379 
00380 void
00381   AsyncSerial::clearReadCallback (  )
00382 {
00383    pimpl->callback.clear (  );
00384 }
00385 
00386 #else //__APPLE__
00387 
00388 #include <sys/types.h>
00389 #include <sys/stat.h>
00390 #include <fcntl.h>
00391 #include <termios.h>
00392 #include <unistd.h>
00393 
00394 class AsyncSerialImpl:private
00395    boost::noncopyable {
00396  public:
00397    AsyncSerialImpl (  ):
00398    backgroundThread (  ),
00399    open ( false ),
00400    error ( false ) {
00401    }
00402    boost::thread
00403       backgroundThread;         
00404 
00405    bool open;                   
00406 
00407    bool error;                  
00408 
00409    mutable boost::mutex errorMutex; 
00410 
00411    int
00412      fd;                        
00413 
00414    char
00415      readBuffer[AsyncSerial::readBufferSize];   
00416 
00418    boost::function < void ( const char *, size_t ) >
00419       callback;
00420 };
00421 
00422 AsyncSerial::AsyncSerial (  ):pimpl ( new AsyncSerialImpl )
00423 {
00424 
00425 }
00426 
00427 AsyncSerial::AsyncSerial ( const std::string & devname, unsigned int baud_rate,
00428                            asio::serial_port_base::parity opt_parity,
00429                            asio::serial_port_base::character_size opt_csize,
00430                            asio::serial_port_base::flow_control opt_flow,
00431                            asio::serial_port_base::stop_bits opt_stop ):
00432 pimpl ( new AsyncSerialImpl )
00433 {
00434    open ( devname, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop );
00435 }
00436 
00437 void
00438   AsyncSerial::open ( const std::string & devname, unsigned int baud_rate,
00439                        asio::serial_port_base::parity opt_parity,
00440                        asio::serial_port_base::character_size opt_csize,
00441                        asio::serial_port_base::flow_control opt_flow,
00442                        asio::serial_port_base::stop_bits opt_stop )
00443 {
00444    if ( isOpen (  ) )
00445       close (  );
00446 
00447    setErrorStatus ( true );     //If an exception is thrown, error remains true
00448 
00449    struct termios new_attributes;
00450 
00451    speed_t speed;
00452 
00453    int status;
00454 
00455    // Open port
00456    pimpl->fd =::open ( devname.c_str (  ), O_RDWR | O_NOCTTY | O_NONBLOCK );
00457    if ( pimpl->fd < 0 )
00458       throw ( boost::system::system_error ( boost::system::error_code (  ),
00459                                             "Failed to open port" ) );
00460 
00461    // Set Port parameters.
00462    status = tcgetattr ( pimpl->fd, &new_attributes );
00463    if ( status < 0 || !isatty ( pimpl->fd ) )
00464      {
00465         ::close ( pimpl->fd );
00466         throw ( boost::system::system_error ( boost::system::error_code (  ),
00467                                               "Device is not a tty" ) );
00468      }
00469    new_attributes.c_iflag = IGNBRK;
00470    new_attributes.c_oflag = 0;
00471    new_attributes.c_lflag = 0;
00472    new_attributes.c_cflag = ( CS8 | CREAD | CLOCAL );   //8 data bit,Enable receiver,Ignore modem
00473    /* In non canonical mode (Ctrl-C and other disabled, no echo,...) VMIN and VTIME work this way:
00474       if the function read() has'nt read at least VMIN chars it waits until has read at least VMIN
00475       chars (even if VTIME timeout expires); once it has read at least vmin chars, if subsequent
00476       chars do not arrive before VTIME expires, it returns error; if a char arrives, it resets the
00477       timeout, so the internal timer will again start from zero (for the nex char,if any) */
00478    new_attributes.c_cc[VMIN] = 1;   // Minimum number of characters to read before returning error
00479    new_attributes.c_cc[VTIME] = 1;  // Set timeouts in tenths of second
00480 
00481    // Set baud rate
00482    switch ( baud_rate )
00483      {
00484      case 50:
00485         speed = B50;
00486         break;
00487      case 75:
00488         speed = B75;
00489         break;
00490      case 110:
00491         speed = B110;
00492         break;
00493      case 134:
00494         speed = B134;
00495         break;
00496      case 150:
00497         speed = B150;
00498         break;
00499      case 200:
00500         speed = B200;
00501         break;
00502      case 300:
00503         speed = B300;
00504         break;
00505      case 600:
00506         speed = B600;
00507         break;
00508      case 1200:
00509         speed = B1200;
00510         break;
00511      case 1800:
00512         speed = B1800;
00513         break;
00514      case 2400:
00515         speed = B2400;
00516         break;
00517      case 4800:
00518         speed = B4800;
00519         break;
00520      case 9600:
00521         speed = B9600;
00522         break;
00523      case 19200:
00524         speed = B19200;
00525         break;
00526      case 38400:
00527         speed = B38400;
00528         break;
00529      case 57600:
00530         speed = B57600;
00531         break;
00532      case 115200:
00533         speed = B115200;
00534         break;
00535      case 230400:
00536         speed = B230400;
00537         break;
00538      default:
00539         {
00540            ::close ( pimpl->fd );
00541            throw ( boost::system::system_error ( boost::system::error_code (  ),
00542                                                  "Unsupported baud rate" ) );
00543         }
00544      }
00545 
00546    cfsetospeed ( &new_attributes, speed );
00547    cfsetispeed ( &new_attributes, speed );
00548 
00549    //Make changes effective
00550    status = tcsetattr ( pimpl->fd, TCSANOW, &new_attributes );
00551    if ( status < 0 )
00552      {
00553         ::close ( pimpl->fd );
00554         throw ( boost::system::system_error ( boost::system::error_code (  ),
00555                                               "Can't set port attributes" ) );
00556      }
00557    //These 3 lines clear the O_NONBLOCK flag
00558    status = fcntl ( pimpl->fd, F_GETFL, 0 );
00559    if ( status != -1 )
00560       fcntl ( pimpl->fd, F_SETFL, status & ~O_NONBLOCK );
00561 
00562    setErrorStatus ( false );    //If we get here, no error
00563    pimpl->open = true;          //Port is now open
00564 
00565    thread t ( bind ( &AsyncSerial::doRead, this ) );
00566 
00567    pimpl->backgroundThread.swap ( t );
00568 }
00569 
00570      bool AsyncSerial::isOpen (  ) const {
00571         return
00572            pimpl->
00573            open;
00574      }
00575      bool
00576      AsyncSerial::errorStatus (  )  const {
00577         lock_guard <
00578            mutex >
00579         l ( pimpl->errorMutex );
00580         return
00581            pimpl->
00582            error;
00583      }
00584      void
00585      AsyncSerial::close (  )
00586 {
00587    if ( !isOpen (  ) )
00588       return;
00589 
00590    pimpl->open = false;
00591 
00592    ::close ( pimpl->fd );       //The thread waiting on I/O should return
00593 
00594    pimpl->backgroundThread.join (  );
00595    if ( errorStatus (  ) )
00596      {
00597         throw ( boost::system::system_error ( boost::system::error_code (  ),
00598                                               "Error while closing the device" ) );
00599      }
00600 }
00601 
00602 void
00603   AsyncSerial::write ( const char *data, size_t size )
00604 {
00605    if ( ::write ( pimpl->fd, data, size ) != size )
00606       setErrorStatus ( true );
00607 }
00608 
00609 void
00610   AsyncSerial::write ( const std::vector < char >&data )
00611 {
00612    if ( ::write ( pimpl->fd, &data[0], data.size (  ) ) != data.size (  ) )
00613       setErrorStatus ( true );
00614 }
00615 
00616 void
00617   AsyncSerial::writeString ( const std::string & s )
00618 {
00619    if ( ::write ( pimpl->fd, &s[0], s.size (  ) ) != s.size (  ) )
00620       setErrorStatus ( true );
00621 }
00622 
00623 AsyncSerial::~AsyncSerial (  )
00624 {
00625    if ( isOpen (  ) )
00626      {
00627         try
00628         {
00629            close (  );
00630         }
00631         catch ( ... )
00632         {
00633            //Don't throw from a destructor
00634         }
00635      }
00636 }
00637 
00638 void
00639   AsyncSerial::doRead (  )
00640 {
00641    //Read loop in spawned thread
00642    for ( ;; )
00643      {
00644         int
00645           received =::read ( pimpl->fd, pimpl->readBuffer, readBufferSize );
00646 
00647         if ( received < 0 )
00648           {
00649              if ( isOpen (  ) == false )
00650                 return;         //Thread interrupted because port closed
00651              else
00652                {
00653                   setErrorStatus ( true );
00654                   continue;
00655                }
00656           }
00657         if ( pimpl->callback )
00658            pimpl->callback ( pimpl->readBuffer, received );
00659      }
00660 }
00661 
00662 void
00663 AsyncSerial::readEnd ( const boost::system::error_code & error,
00664                           size_t bytes_transferred )
00665 {
00666    //Not used
00667 }
00668 
00669 void
00670   AsyncSerial::doWrite (  )
00671 {
00672    //Not used
00673 }
00674 
00675 void
00676   AsyncSerial::writeEnd ( const boost::system::error_code & error )
00677 {
00678    //Not used
00679 }
00680 
00681 void
00682   AsyncSerial::doClose (  )
00683 {
00684    //Not used
00685 }
00686 
00687 void
00688   AsyncSerial::setErrorStatus ( bool e )
00689 {
00690    lock_guard < mutex > l ( pimpl->errorMutex );
00691    pimpl->error = e;
00692 }
00693 
00694 void
00695   AsyncSerial::setReadCallback ( const
00696                                  function < void ( const char *, size_t ) > &callback )
00697 {
00698    pimpl->callback = callback;
00699 }
00700 
00701 void
00702   AsyncSerial::clearReadCallback (  )
00703 {
00704    pimpl->callback.clear (  );
00705 }
00706 
00707 #endif //__APPLE__
00708 
00709 //
00710 //Class CallbackAsyncSerial
00711 //
00712 
00713 CallbackAsyncSerial::CallbackAsyncSerial (  ):AsyncSerial (  )
00714 {
00715 
00716 }
00717 
00718 CallbackAsyncSerial::CallbackAsyncSerial ( const std::string & devname,
00719                                            unsigned int baud_rate,
00720                                            asio::serial_port_base::parity opt_parity,
00721                                            asio::
00722                                            serial_port_base::character_size opt_csize,
00723                                            asio::serial_port_base::flow_control opt_flow,
00724                                            asio::serial_port_base::stop_bits opt_stop ):
00725 AsyncSerial ( devname, baud_rate, opt_parity, opt_csize, opt_flow, opt_stop )
00726 {
00727 
00728 }
00729 
00730 void
00731   CallbackAsyncSerial::setCallback ( const
00732                                      boost::function < void ( const char *,
00733                                                               size_t ) > &callback )
00734 {
00735    setReadCallback ( callback );
00736 }
00737 
00738 void
00739   CallbackAsyncSerial::clearCallback (  )
00740 {
00741    clearReadCallback (  );
00742 }
00743 
00744 CallbackAsyncSerial::~CallbackAsyncSerial (  )
00745 {
00746    clearReadCallback (  );
00747 }


pressure_cells
Author(s): Emilio Estrelinha
autogenerated on Thu Nov 20 2014 11:35:56