serial_pos.cpp
Go to the documentation of this file.
00001 
00009 /*****************************************************************************
00010  ** Platform Check
00011  *****************************************************************************/
00012 
00013 #include <ecl/config/ecl.hpp>
00014 #ifdef ECL_IS_POSIX
00015 
00016 /*****************************************************************************
00017  ** Includes
00018  *****************************************************************************/
00019 
00020 #include <fcntl.h>
00021 #include <sys/ioctl.h>
00022 #include <termios.h>
00023 #include <cstdlib> // div
00024 #include <unistd.h> // access
00025 #include <ecl/exceptions/standard_exception.hpp>
00026 #include <ecl/exceptions/macros.hpp>
00027 #include "../../include/ecl/devices/serial_pos.hpp"
00028 #include "../../include/ecl/devices/detail/error_handler.hpp"
00029 
00030 /*****************************************************************************
00031  ** Namespaces
00032  *****************************************************************************/
00033 
00034 namespace ecl
00035 {
00036 
00037 /*****************************************************************************
00038  ** Using
00039  *****************************************************************************/
00040 
00041 using std::string;
00042 using ecl::InvalidInputError;
00043 using ecl::OpenError;
00044 using ecl::StandardException;
00045 
00046 /*****************************************************************************
00047  ** Implementation [Serial][C&D]
00048  *****************************************************************************/
00049 
00050 Serial::Serial(const std::string& port_name, const BaudRate &baud_rate, const DataBits &data_bits,
00051                const StopBits &stop_bits, const Parity &parity) ecl_throw_decl(StandardException) :
00052     port(port_name), read_timeout_ms(5000), error_handler(NoError)
00053 {
00054   ecl_try
00055   {
00056     open(port_name, baud_rate, data_bits, stop_bits, parity);
00057   } ecl_catch( const StandardException &e ) {
00058     ecl_throw(StandardException(LOC,e));
00059   }
00060 }
00061 
00062 Serial::~Serial()
00063 {
00064   close();
00065 }
00066 
00067 /*****************************************************************************
00068  ** Implementation [Serial][Open]
00069  *****************************************************************************/
00070 
00071 bool Serial::open(const std::string& port_name, const BaudRate &baud_rate, const DataBits &data_bits,
00072                   const StopBits &stop_bits, const Parity &parity) ecl_throw_decl(StandardException)
00073 {
00074 
00075   /*********************
00076    ** Input Checks
00077    **********************/
00078   if (stop_bits == StopBits_15)
00079   {
00080     ecl_throw(
00081         StandardException(LOC,ConfigurationError,"Standard serial device does not accept StopBits_15 as valid (used in ftdi)."));
00082     error_handler = InvalidArgError;
00083     is_open = false;
00084     return false;
00085   }
00086 
00087   if (open())
00088   {
00089     close();
00090   }
00091   port = port_name;
00092   // One curious thing here is O_NONBLOCK. If you do not specify this, it will try
00093   // and wait till the line has an 'other' end. Which it can't possibly know, so
00094   // it will effectively block here forever.
00095   file_descriptor = ::open(port_name.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
00096   if (file_descriptor == -1)
00097   {
00098     ecl_throw(devices::open_exception(LOC,port_name));
00099     error_handler = devices::open_error();
00100     is_open = false;
00101     return false;
00102   }
00103 
00104   static const int baud_rate_flags[] = {B110, B300, B600, B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200,
00105                                         B921600};
00106   static const int data_bits_flags[] = {CS5, CS6, CS7, CS8};
00107 
00108   /*********************
00109    ** Current Settings
00110    **********************/
00111 //    tcgetattr(file_descriptor,&options); // Need?
00112   /******************************************
00113    ** Flow Control
00114    *******************************************/
00115   // Turn off the O_NONBLOCK we set back when opening (we want by default
00116   // blocking and timeouts etc to occur).
00117   // Using 0 as the argument here resets the file status flags to defaults.
00118   fcntl(file_descriptor, F_SETFL, 0);
00119   // this came from ros' hokoyu node code for setting a lock on the device file descriptor
00120   // useful if you want to make sure other devices dont mess with your device!
00121   struct flock file_lock;
00122   file_lock.l_type = F_WRLCK;
00123   file_lock.l_whence = SEEK_SET;
00124   file_lock.l_start = 0;
00125   file_lock.l_len = 0;
00126   file_lock.l_pid = getpid();
00127   if (fcntl(file_descriptor, F_SETLK, &file_lock) != 0)
00128   {
00129     ecl_throw_decl(
00130         StandardException(LOC,OpenError,std::string("Device is already locked. Try 'lsof | grep ") + port + std::string("' to find other processes that currently have the port open (if the device is a symbolic link you may need to replace the device name with the device that it is pointing to).")));
00131     error_handler = IsLockedError;
00132     is_open = false;
00133     return false;
00134   }
00135 
00136   /******************************************
00137    ** Initialise flags
00138    *******************************************/
00139   options.c_cflag = 0;
00140   options.c_iflag = 0;
00141   options.c_lflag = 0;
00142   options.c_oflag = 0;
00143 
00144   /*********************
00145    ** Baud rates
00146    **********************/
00147   cfsetispeed(&options, baud_rate_flags[baud_rate]);
00148 
00149   /*********************
00150    ** Ownership
00151    **********************/
00152   /*
00153    * CLOCAL and CREAD should always be set.
00154    * They make sure the owner of the port is
00155    * not changed by the program.
00156    */
00157   options.c_cflag |= CLOCAL;
00158   options.c_cflag |= CREAD;
00159 
00160   /*********************
00161    ** Disable Flow control
00162    **********************/
00163 #if defined(CRTSCTS)
00164   options.c_cflag &= ~CRTSCTS; // Disable hardware flow control (old)
00165 #elif defined (CNEW_RTSCTS)
00166   options.c_cflag &= ~CNEW_RTSCTS; // Disable hardware flow control (new)
00167 #endif
00168 
00169   /*********************
00170    ** StopBits
00171    **********************/
00172   if (stop_bits == StopBits_1)
00173   {
00174     options.c_cflag &= ~CSTOPB; // One stop bit only
00175   }
00176   else
00177   {
00178     options.c_cflag |= CSTOPB; // Two stop bits.
00179   }
00180 
00181   /******************************************
00182    ** DataBits
00183    *******************************************/
00184   options.c_cflag &= ~CSIZE; // Mask (i.e. reset) character size bits
00185   options.c_cflag |= data_bits_flags[data_bits]; // Now set the # data bits
00186 
00187   /******************************************
00188    ** Processing
00189    *******************************************/
00190   options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // raw, no echoing or waiting till a flush or signals
00191   // options.c_lflag |= (ICANON | ECHO | ECHOE); // like a chat session
00192 
00193   /******************************************
00194    ** Input Options
00195    *******************************************/
00196   options.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control
00197 
00198   /******************************************
00199    ** Output Options
00200    *******************************************/
00201 //    options.c_oflag |= OPOST; // Perform a little post processing
00202 //    options.c_oflag |= ONLCR; // Man NL to NL-CR
00203   /*********************
00204    ** Parity
00205    **********************/
00206   if (parity == NoParity)
00207   {
00208     options.c_cflag &= ~PARENB; // No Parity
00209   }
00210   else if (parity == EvenParity)
00211   {
00212     options.c_iflag |= (INPCK | ISTRIP); // Enable parity check and strip parity bit
00213     options.c_cflag |= PARENB;
00214     options.c_cflag &= ~PARODD;
00215   }
00216   else
00217   { // OddParity
00218     options.c_iflag |= (INPCK | ISTRIP); // Enable parity check and strip parity bit
00219     options.c_cflag |= PARENB;
00220     options.c_cflag |= PARODD;
00221   }
00222 
00223   /******************************************
00224    ** Enable
00225    *******************************************
00226    * TCSANOW - make changes immediately
00227    * TCSADRAIN - wait till everything is transmitted, don't do anything with unread data.
00228    * TCSAFLUSH - wait till everything is transmitted, also clear unread data.
00229    *******************************************/
00230   tcsetattr(file_descriptor, TCSAFLUSH, &options);
00231 
00232   /******************************************
00233   ** Reset Status
00234   *******************************************/
00235   if ( read_timeout_ms == NonBlocking ) {
00236     unblock();
00237   } else {
00238     block(read_timeout_ms);
00239   }
00240   clear();
00241 
00242   is_open = true;
00243   error_handler = NoError;
00244   return true;
00245 }
00246 
00247 void Serial::close()
00248 {
00249   if (is_open)
00250   {
00251     // should check return values here, it does have some, EBADF/EINTR/EIO
00252     ::close(file_descriptor);
00253     is_open = false;
00254   }
00255 }
00256 
00257 bool Serial::open()
00258 {
00259   if ( is_open ) {
00260     if ( access(port.c_str(), F_OK ) == -1 ) {
00261       close();
00262     }
00263   }
00264   return is_open;
00265 }
00266 
00267 /*****************************************************************************
00268  ** Implementation [Serial][Reading Modes]
00269  *****************************************************************************/
00270 
00271 void Serial::block(const unsigned long &timeout)
00272 {
00273   if (timeout < 100)
00274   {
00275     if (timeout < 5)
00276     {
00277       fake_snooze.period(ecl::Duration(0.001));
00278       fake_loop_count = timeout;
00279     }
00280     else if (timeout < 20)
00281     {
00282       fake_snooze.period(ecl::Duration(0.002));
00283       div_t d = div(timeout, 2);
00284       if (d.rem == 0)
00285       {
00286         fake_loop_count = d.quot;
00287       }
00288       else
00289       {
00290         fake_loop_count = d.quot + 1;
00291       }
00292     }
00293     else
00294     {
00295       fake_snooze.period(ecl::Duration(0.005));
00296       div_t d = div(timeout, 5);
00297       if (d.rem == 0)
00298       {
00299         fake_loop_count = d.quot;
00300       }
00301       else
00302       {
00303         fake_loop_count = d.quot + 1;
00304       }
00305     }
00306     this->unblock();
00307   }
00308   else
00309   {
00310     options.c_cc[VMIN] = 0;
00311     // VTIME is in tenths of a second
00312     if (timeout < 100)
00313     { // just in case we're not running in debug mode or exceptions are disabled.
00314       options.c_cc[VTIME] = static_cast<unsigned char>(1); // 100/100
00315     }
00316     else
00317     {
00318       options.c_cc[VTIME] = static_cast<unsigned char>(timeout / 100);
00319     }
00320     tcsetattr(file_descriptor, TCSAFLUSH, &options);
00321   }
00322   read_timeout_ms = timeout;
00323 }
00324 
00325 void Serial::unblock()
00326 {
00327   options.c_cc[VMIN] = 0;
00328   options.c_cc[VTIME] = 0;
00329   tcsetattr(file_descriptor, TCSAFLUSH, &options);
00330   read_timeout_ms = NonBlocking;
00331 }
00332 
00333 /*****************************************************************************
00334  ** Implementation [Serial][Reading]
00335  *****************************************************************************/
00336 long Serial::remaining()
00337 {
00338   long bytes = 0;
00339   ioctl(file_descriptor, FIONREAD, &bytes);
00340   return bytes;
00341 }
00342 
00343 } // namespace ecl
00344 #endif /* ECL_IS_POSIX */


ecl_devices
Author(s): Daniel Stonier
autogenerated on Sun Oct 5 2014 23:35:57