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