Menu

AsioSerialDriver.h

Go to the documentation of this file.
00001 /*******************************************************************************
00002  *
00003  * Copyright (c) 2010 Thomas Kiebel <kiebel@ivs.cs.uni-magdeburg.de>
00004  * All rights reserved.
00005  *
00006  *    Redistribution and use in source and binary forms, with or without
00007  *    modification, are permitted provided that the following conditions
00008  *    are met:
00009  *
00010  *    * Redistributions of source code must retain the above copyright
00011  *      notice, this list of conditions and the following disclaimer.
00012  *
00013  *    * Redistributions in binary form must reproduce the above copyright
00014  *      notice, this list of conditions and the following disclaimer in
00015  *      the documentation and/or other materials provided with the
00016  *      distribution.
00017  *
00018  *    * Neither the name of the copyright holders nor the names of
00019  *      contributors may be used to endorse or promote products derived
00020  *      from this software without specific prior written permission.
00021  *
00022  *
00023  *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
00024  *    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00025  *    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
00026  *    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00027  *    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00028  *    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00029  *    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00030  *    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00031  *    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00032  *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00033  *    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00034  *
00035  *
00036  * $Id$
00037  *
00038  ******************************************************************************/
00039 #ifndef __BOOST_ASIO_SERIAL_DRIVER_h__
00040 #define __BOOST_ASIO_SERIAL_DRIVER_h__
00041 
00042 #include <boost/bind.hpp>
00043 #include <boost/asio.hpp>
00044 #include <boost/asio/serial_port.hpp>
00045 #include <deque>
00046 #include <iostream>
00047 
00048 #include "case/Delegate.h"
00049 #include "devices/nic/serial/SerialFrameDelimiter.h"
00050 #include "util/CommandLineParameterGenerator.h"
00051 #include "util/ios.h"
00052 
00053 #include "debug.h"
00054 
00055 using namespace std;
00056 namespace device {
00057     namespace nic {
00058         namespace serial {
00059 
00060             CLP2(ASIOOptions,
00061                  "ASIO Serial Driver",
00062                  "serial,s",
00063                  "The device that is used and the baudrate in the form of device:baudrate"
00064                  "Values:\n"
00065                  "  Device:     \tdevice that has to be used\n"
00066                  "  Baudrate:   \tlegal values are\n"
00067                  "                 0 =    300 bps\n"
00068                  "                 1 =    600 bps\n"
00069                  "                 2 =   1200 bps\n"
00070                  "                 3 =   2400 bps\n"
00071                  "                 4 =   4800 bps\n"
00072                  "                 5 =   9600 bps\n"
00073                  "                 6 =  19200 bps\n"
00074                  "                 7 =  38400 bps\n"
00075                  "                 8 =  57600 bps\n"
00076                  "                 9 = 115200 bps\n"
00077                  "  Packetsize: \tmax size of a packet\n"
00078                  "(e.g. /dev/ttyS0:6: (default))",
00079                  std::string, device, "/dev/ttyS0",
00080                  int, baudrate, 6);
00081 
00090             template <
00091                         uint8_t PL  = 255,
00092                         class SFD = struct SerialFrameDelimiter
00093                      >
00094             class AsioSerialDriver : boost::noncopyable {
00095                 public:
00097                     typedef AsioSerialDriver< PL, SFD > type;
00099                     struct cfgInfo {
00100                         enum {
00101                             payload = PL
00102                         };
00103                     };
00105                     typedef struct __attribute__((packed)) {
00106                         uint8_t size;
00107                         uint8_t payload[cfgInfo::payload];
00108                     } mob_t;
00109 
00110                     AsioSerialDriver() :
00111                         _io_service(famouso::util::ios::instance()),
00112                         _serialPort(_io_service) {
00113                     }
00114                     ~AsioSerialDriver() {
00115                         _serialPort.close();
00116                     }
00117 
00121                     void close(const boost::system::error_code& error) {
00122                         // if this call is the result of a timer cancel() ignore it
00123                         // because the connection cancelled the timer
00124                         if (error == boost::asio::error::operation_aborted)
00125                             return;
00126                         if (error) {
00127                             ::logging::log::emit< ::logging::Error>()
00128                                 << error.message().c_str()
00129                                 << ::logging::log::endl;
00130                         }
00131                         _serialPort.close();
00132                     }
00133 
00139                     void doneRead(const boost::system::error_code& error, std::size_t size) {
00140                         char aChar;
00141 
00142                         if (!error) {
00143                             std::istream is(&trxBuffer);
00144                             while (size--) {
00145                                 driverState = ( _rmobj.size > cfgInfo::payload ) ? invalid : driverState;
00146                                 is.get( aChar );
00147                                 switch ( aChar ) {
00148                                     case (SFD::sofr): // frame start
00149                                         driverState = ( driverState == invalid ) ? regular : invalid;
00150                                         _rmobj.size  = 0;
00151                                         break;
00152                                     case (SFD::eofr): // frame end
00153                                         driverState = ( driverState == regular ) ? valid   : invalid;
00154                                         if ( (driverState == valid) && this->onReceive) this->onReceive();
00155                                         break;
00156                                     case (SFD::esc):  // modifier detected
00157                                         driverState = ( driverState == regular ) ? stuff   : invalid;
00158                                         break;
00159                                     default:          // everything else
00160                                         if ( driverState == regular || driverState == stuff ) {
00161                                             aChar = ( driverState == stuff && SFD::escmod != 0) ? (aChar ^ SFD::escmod) : aChar;
00162                                             _rmobj.payload[_rmobj.size++] = (uint8_t)aChar;
00163                                             driverState  = regular;
00164                                         } else driverState = invalid;
00165                                         break;
00166                                 }
00167                             }
00168                             boost::asio::async_read_until(
00169                                 _serialPort, trxBuffer, SFD::eofr,
00170                                 boost::bind(&type::doneRead, this,
00171                                             boost::asio::placeholders::error,
00172                                             boost::asio::placeholders::bytes_transferred));
00173                         } else close(error);
00174                     }
00175 
00181                     void doneWrite(const boost::system::error_code& error, std::size_t size) {
00182                         if ( !error ) {
00183                             trxBuffer.consume(size);
00184                             if ( this->onReady ) this->onReady();
00185                         } else close(error);
00186                     }
00187 
00195                     void init() {
00196                         TRACE_FUNCTION;
00197 
00198                         CLP::config::ASIOOptions::Parameter param;
00199                         CLP::config::ASIOOptions::instance().getParameter(param);
00200                         if ( (param.baudrate < 0) || (param.baudrate > 9) ) {
00201                             ::logging::log::emit< ::logging::Error>()
00202                                 << "Error: parameter baudrate out of Range"
00203                                 << ::logging::log::endl;
00204                             exit(0);
00205                         }
00206 
00207                         famouso::util::impl::start_ios();
00208                         // open serial port for given port
00209                         _serialPort.open( param.device.c_str() );
00210                         if ( !_serialPort.is_open() ) {
00211                             ::logging::log::emit< ::logging::Error>()
00212                                 << "can't open serial device "
00213                                 << param.device.c_str() << ::logging::log::endl;
00214                             exit(0);
00215                         }
00216                         // set defaults for serial port
00217                         _serialPort.set_option(
00218                             boost::asio::serial_port_base::character_size( 8 ) );
00219                         _serialPort.set_option(
00220                             boost::asio::serial_port_base::flow_control(
00221                                 boost::asio::serial_port_base::flow_control::none ) );
00222                         uint16_t baudrate = (0x01 << param.baudrate) * 300;
00223                         _serialPort.set_option(
00224                             boost::asio::serial_port_base::baud_rate( baudrate ) );
00225                         _serialPort.set_option(
00226                             boost::asio::serial_port_base::parity(
00227                                 boost::asio::serial_port_base::parity::none ) );
00228                         _serialPort.set_option(
00229                             boost::asio::serial_port_base::stop_bits(
00230                                 boost::asio::serial_port_base::stop_bits::one) );
00231                         // start reading data
00232                         boost::asio::async_read_until(
00233                             _serialPort, trxBuffer, SFD::eofr,
00234                             boost::bind(&type::doneRead, this,
00235                                         boost::asio::placeholders::error,
00236                                         boost::asio::placeholders::bytes_transferred));
00237                     }
00238 
00242                     void recv(mob_t& msg) {
00243                         TRACE_FUNCTION;
00244 
00245                         msg.size = 0;
00246                         while ( driverState != valid ); // message available
00247 
00248                         for (;msg.size < _rmobj.size; msg.size++) {
00249                             msg.payload[msg.size] = _rmobj.payload[msg.size];
00250                         }
00251                         driverState = invalid;
00252                     }
00253 
00257                     void send(const mob_t& message) {
00258                         TRACE_FUNCTION;
00259 
00260                         std::ostream os(&trxBuffer);
00261                         uint8_t aByte = 0;
00262 
00263                         // transfer data into the send buffer
00264                         os.put( (uint8_t)SFD::sofr );
00265                         for (uint16_t index = 0;index < message.size;index++) {
00266                             aByte = message.payload[index];
00267                             if (    aByte == SFD::esc ||
00268                                     aByte == SFD::eofr ||
00269                                     aByte == SFD::sofr    ) {
00270                                 os.put( (uint8_t)SFD::esc );
00271                                 os.put( ((SFD::escmod != 0) ? aByte ^ SFD::escmod : aByte));
00272                             } else {
00273                                 os.put( aByte );
00274                             }
00275                         }
00276                         os.put( (uint8_t)SFD::eofr );
00277                         // start ascynchronous send of message
00278                         boost::asio::async_write(
00279                             _serialPort,
00280                             trxBuffer,
00281                             boost::bind(&type::doneWrite, this,
00282                                         boost::asio::placeholders::error,
00283                                         boost::asio::placeholders::bytes_transferred));
00284                     }
00285 
00287                     famouso::util::Delegate<> onReceive;
00288 
00294                     famouso::util::Delegate<> onReady;
00295                 private:
00296                     boost::asio::io_service& _io_service;
00297                     boost::asio::serial_port _serialPort;
00298                     boost::asio::streambuf trxBuffer;
00299                     enum { valid, invalid, regular, stuff } driverState;
00300                     mob_t _rmobj;
00301             }; // class AsioDriver
00302         } /* namespace serial */
00303     } /* namesace nic */
00304 } /* namespace device */
00305 
00306 #endif  //__BOOST_ASIO_SERIAL_DRIVER_h__