00001 #include <stdio.h>
00002 #include <string.h>
00003 #include <errno.h>
00004 #include <termios.h>
00005 #include <math.h>
00006 #include <poll.h>
00007 #include <signal.h>
00008 #include <fcntl.h>
00009 #include <iostream>
00010 #include <fstream>
00011 #include <stdexcept>
00012
00013 #include "md49_serialport/md49_serialport.h"
00014
00016 #define CEREAL_EXCEPT(except, msg, ...) \
00017 { \
00018 char buf[1000]; \
00019 snprintf(buf, 1000, msg " (in cereal::CerealPort::%s)" , ##__VA_ARGS__, __FUNCTION__); \
00020 throw except(buf); \
00021 }
00022
00023 cereal::CerealPort::CerealPort() : fd_(-1)
00024 {
00025 stream_thread_ = NULL;
00026 }
00027
00028 cereal::CerealPort::~CerealPort()
00029 {
00030 if(portOpen()) close();
00031 }
00032
00033 void cereal::CerealPort::open(const char * port_name, int baud_rate)
00034 {
00035 if(portOpen()) close();
00036
00037
00038
00039
00040
00041 fd_ = ::open(port_name, O_RDWR | O_NONBLOCK | O_NOCTTY);
00042
00043 if(fd_ == -1)
00044 {
00045 const char *extra_msg = "";
00046 switch(errno)
00047 {
00048 case EACCES:
00049 extra_msg = "You probably don't have premission to open the port for reading and writing.";
00050 break;
00051
00052 case ENOENT:
00053 extra_msg = "The requested port does not exist. Is the hokuyo connected? Was the port name misspelled?";
00054 break;
00055 }
00056 CEREAL_EXCEPT(cereal::Exception, "Failed to open port: %s. %s (errno = %d). %s", port_name, strerror(errno), errno, extra_msg);
00057 }
00058
00059 try
00060 {
00061 struct flock fl;
00062 fl.l_type = F_WRLCK;
00063 fl.l_whence = SEEK_SET;
00064 fl.l_start = 0;
00065 fl.l_len = 0;
00066 fl.l_pid = getpid();
00067
00068 if(fcntl(fd_, F_SETLK, &fl) != 0)
00069 CEREAL_EXCEPT(cereal::Exception, "Device %s is already locked. Try 'lsof | grep %s' to find other processes that currently have the port open.", port_name, port_name);
00070
00071
00072 struct termios newtio;
00073 tcgetattr(fd_, &newtio);
00074 memset (&newtio.c_cc, 0, sizeof (newtio.c_cc));
00075 newtio.c_cflag = CS8 | CLOCAL | CREAD;
00076 newtio.c_iflag = IGNPAR;
00077 newtio.c_oflag = 0;
00078 newtio.c_lflag = 0;
00079 cfsetspeed(&newtio, baud_rate);
00080 baud_ = baud_rate;
00081
00082
00083 tcflush(fd_, TCIFLUSH);
00084 if(tcsetattr(fd_, TCSANOW, &newtio) < 0)
00085 CEREAL_EXCEPT(cereal::Exception, "Unable to set serial port attributes. The port you specified (%s) may not be a serial port.", port_name);
00086 usleep (200000);
00087 }
00088 catch(cereal::Exception& e)
00089 {
00090
00091 if(fd_ != -1) ::close(fd_);
00092 fd_ = -1;
00093 throw e;
00094 }
00095 }
00096
00097 void cereal::CerealPort::close()
00098 {
00099 int retval = 0;
00100
00101 retval = ::close(fd_);
00102
00103 fd_ = -1;
00104
00105 if(retval != 0)
00106 CEREAL_EXCEPT(cereal::Exception, "Failed to close port properly -- error = %d: %s\n", errno, strerror(errno));
00107 }
00108
00109 int cereal::CerealPort::write(const char * data, int length)
00110
00111 {
00112 int len = length==-1 ? strlen(data) : length;
00113
00114
00115 int origflags = fcntl(fd_, F_GETFL, 0);
00116 fcntl(fd_, F_SETFL, origflags & ~O_NONBLOCK);
00117 int retval = ::write(fd_, data, len);
00118 fcntl(fd_, F_SETFL, origflags | O_NONBLOCK);
00119
00120 if(retval == len) return retval;
00121 else CEREAL_EXCEPT(cereal::Exception, "write failed");
00122 }
00123
00124 int cereal::CerealPort::read(char * buffer, int max_length, int timeout)
00125 {
00126 int ret;
00127
00128 struct pollfd ufd[1];
00129 int retval;
00130 ufd[0].fd = fd_;
00131 ufd[0].events = POLLIN;
00132
00133 if(timeout == 0) timeout = -1;
00134
00135 if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
00136
00137 if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
00138
00139 if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
00140
00141 ret = ::read(fd_, buffer, max_length);
00142
00143 if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
00144
00145 return ret;
00146 }
00147
00148 int cereal::CerealPort::readBytes(char * buffer, int length, int timeout)
00149 {
00150 int ret;
00151 int current = 0;
00152
00153 struct pollfd ufd[1];
00154 int retval;
00155 ufd[0].fd = fd_;
00156 ufd[0].events = POLLIN;
00157
00158 if(timeout == 0) timeout = -1;
00159
00160 while(current < length)
00161 {
00162 if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
00163
00164 if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
00165
00166 if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
00167
00168 ret = ::read(fd_, &buffer[current], length-current);
00169
00170 if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
00171
00172 current += ret;
00173 }
00174 return current;
00175 }
00176
00177 int cereal::CerealPort::readLine(char * buffer, int length, int timeout)
00178 {
00179 int ret;
00180 int current = 0;
00181
00182 struct pollfd ufd[1];
00183 int retval;
00184 ufd[0].fd = fd_;
00185 ufd[0].events = POLLIN;
00186
00187 if(timeout == 0) timeout = -1;
00188
00189 while(current < length-1)
00190 {
00191 if(current > 0)
00192 if(buffer[current-1] == '\n')
00193 return current;
00194
00195 if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
00196
00197 if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
00198
00199 if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
00200
00201 ret = ::read(fd_, &buffer[current], length-current);
00202
00203 if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
00204
00205 current += ret;
00206 }
00207 CEREAL_EXCEPT(cereal::Exception, "buffer filled without end of line being found");
00208 }
00209
00210 bool cereal::CerealPort::readLine(std::string * buffer, int timeout)
00211 {
00212 int ret;
00213
00214 struct pollfd ufd[1];
00215 int retval;
00216 ufd[0].fd = fd_;
00217 ufd[0].events = POLLIN;
00218
00219 if(timeout == 0) timeout = -1;
00220
00221 buffer->clear();
00222 while(buffer->size() < buffer->max_size()/2)
00223 {
00224
00225 ret = buffer->find_first_of('\n');
00226 if(ret > 0)
00227 {
00228
00229 buffer->erase(ret+1, buffer->size()-ret-1);
00230 return true;
00231 }
00232
00233 if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
00234
00235 if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
00236
00237 if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
00238
00239 char temp_buffer[128];
00240 ret = ::read(fd_, temp_buffer, 128);
00241
00242 if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
00243
00244
00245 try{ buffer->append(temp_buffer, ret); }
00246 catch(std::length_error& le)
00247 {
00248 CEREAL_EXCEPT(cereal::Exception, "buffer filled without reaching end of data stream");
00249 }
00250 }
00251 CEREAL_EXCEPT(cereal::Exception, "buffer filled without end of line being found");
00252 }
00253
00254 bool cereal::CerealPort::readBetween(std::string * buffer, char start, char end, int timeout)
00255 {
00256 int ret;
00257
00258 struct pollfd ufd[1];
00259 static std::string erased;
00260 int retval;
00261 ufd[0].fd = fd_;
00262 ufd[0].events = POLLIN;
00263
00264 if(timeout == 0) timeout = -1;
00265
00266
00267 buffer->clear();
00268 while(buffer->size() < buffer->max_size()/2)
00269 {
00270 if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
00271
00272 if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
00273
00274 if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
00275
00276
00277 if(!erased.empty())
00278 {
00279 try
00280 {
00281 buffer->append(erased);
00282 erased.clear();
00283 }
00284 catch(std::length_error& le)
00285 {
00286 CEREAL_EXCEPT(cereal::Exception, "failed to append erased to buffer");
00287 }
00288 }
00289
00290 char temp_buffer[3];
00291 ret = ::read(fd_, temp_buffer, 3);
00292
00293 if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
00294
00295
00296 try{ buffer->append(temp_buffer, ret); }
00297 catch(std::length_error& le)
00298 {
00299 CEREAL_EXCEPT(cereal::Exception, "buffer filled without reaching end of data stream");
00300 }
00301
00302
00303 ret = buffer->find_first_of(start);
00304
00305 if(ret == -1) buffer->clear();
00306
00307 else if(ret > 0) buffer->erase(0, ret);
00308
00309
00310 ret = buffer->find_first_of(end);
00311 if(ret > 0)
00312 {
00313
00314 erased = buffer->substr(ret+1, buffer->size()-ret-1);
00315
00316 buffer->erase(ret+1, buffer->size()-ret-1);
00317 return true;
00318 }
00319 }
00320 CEREAL_EXCEPT(cereal::Exception, "buffer filled without reaching end of data stream");
00321 }
00322
00323 int cereal::CerealPort::flush()
00324 {
00325 int retval = tcflush(fd_, TCIOFLUSH);
00326 if(retval != 0) CEREAL_EXCEPT(cereal::Exception, "tcflush failed");
00327
00328 return retval;
00329 }
00330
00331 bool cereal::CerealPort::startReadStream(boost::function<void(char*, int)> f)
00332 {
00333 if(stream_thread_ != NULL) return false;
00334
00335 stream_stopped_ = false;
00336 stream_paused_ = false;
00337
00338 readCallback = f;
00339
00340 stream_thread_ = new boost::thread(boost::bind(&cereal::CerealPort::readThread, this));
00341 return true;
00342 }
00343
00344 void cereal::CerealPort::readThread()
00345 {
00346 char data[MAX_LENGTH];
00347 int ret;
00348
00349 struct pollfd ufd[1];
00350 ufd[0].fd = fd_;
00351 ufd[0].events = POLLIN;
00352
00353 while(!stream_stopped_)
00354 {
00355 if(!stream_paused_)
00356 {
00357 if(poll(ufd, 1, 10) > 0)
00358 {
00359 if(!(ufd[0].revents & POLLERR))
00360 {
00361 ret = ::read(fd_, data, MAX_LENGTH);
00362 if(ret>0)
00363 {
00364 readCallback(data, ret);
00365 }
00366 }
00367 }
00368 }
00369 }
00370 }
00371
00372 bool cereal::CerealPort::startReadLineStream(boost::function<void(std::string*)> f)
00373 {
00374 if(stream_thread_ != NULL) return false;
00375
00376 stream_stopped_ = false;
00377 stream_paused_ = false;
00378
00379 readLineCallback = f;
00380
00381 stream_thread_ = new boost::thread(boost::bind(&cereal::CerealPort::readLineThread, this));
00382 return true;
00383 }
00384
00385 void cereal::CerealPort::readLineThread()
00386 {
00387 std::string data;
00388 bool error = false;
00389
00390 while(!stream_stopped_)
00391 {
00392 if(!stream_paused_)
00393 {
00394 error = false;
00395 try{ readLine(&data, 100); }
00396 catch(cereal::Exception& e)
00397 {
00398 error = true;
00399 }
00400
00401 if(!error && data.size()>0) readLineCallback(&data);
00402 }
00403 }
00404 }
00405
00406 bool cereal::CerealPort::startReadBetweenStream(boost::function<void(std::string*)> f, char start, char end)
00407 {
00408 if(stream_thread_ != NULL) return false;
00409
00410 stream_stopped_ = false;
00411 stream_paused_ = false;
00412
00413 readBetweenCallback = f;
00414
00415 stream_thread_ = new boost::thread(boost::bind(&cereal::CerealPort::readBetweenThread, this, start, end));
00416 return true;
00417 }
00418
00419 void cereal::CerealPort::readBetweenThread(char start, char end)
00420 {
00421 std::string data;
00422 bool error = false;
00423
00424 while(!stream_stopped_)
00425 {
00426 if(!stream_paused_)
00427 {
00428 error = false;
00429 try{ readBetween(&data, start, end, 100); }
00430 catch(cereal::Exception& e)
00431 {
00432 error = true;
00433 }
00434
00435 if(!error && data.size()>0) readBetweenCallback(&data);
00436 }
00437 }
00438 }
00439
00440 void cereal::CerealPort::stopStream()
00441 {
00442 stream_stopped_ = true;
00443 stream_thread_->join();
00444
00445 delete stream_thread_;
00446 stream_thread_ = NULL;
00447 }
00448
00449 void cereal::CerealPort::pauseStream()
00450 {
00451 stream_paused_ = true;
00452 }
00453
00454 void cereal::CerealPort::resumeStream()
00455 {
00456 stream_paused_ = false;
00457 }