$search
00001 /********************************************************************* 00002 * 00003 * Software License Agreement (BSD License) 00004 * 00005 * Copyright (c) 2010, ISR University of Coimbra. 00006 * All rights reserved. 00007 * 00008 * Redistribution and use in source and binary forms, with or without 00009 * modification, are permitted provided that the following conditions 00010 * are met: 00011 * 00012 * * Redistributions of source code must retain the above copyright 00013 * notice, this list of conditions and the following disclaimer. 00014 * * Redistributions in binary form must reproduce the above 00015 * copyright notice, this list of conditions and the following 00016 * disclaimer in the documentation and/or other materials provided 00017 * with the distribution. 00018 * * Neither the name of the ISR University of Coimbra nor the names of its 00019 * contributors may be used to endorse or promote products derived 00020 * from this software without specific prior written permission. 00021 * 00022 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00023 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00024 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00025 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00026 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00027 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00028 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00029 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00030 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00032 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00033 * POSSIBILITY OF SUCH DAMAGE. 00034 * 00035 * Author: Gonçalo Cabrita on 01/10/2010 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 // Make IO non blocking. This way there are no race conditions that 00074 // cause blocking when a badly behaving process does a read at the same 00075 // time as us. Will need to switch to blocking for writes or errors 00076 // occur just after a replug event. 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 // Settings for USB? 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 // Activate new settings 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 // These exceptions mean something failed on open and we should close 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 // IO is currently non-blocking. This is what we want for the more cerealon read case. 00150 int origflags = fcntl(fd_, F_GETFL, 0); 00151 fcntl(fd_, F_SETFL, origflags & ~O_NONBLOCK); // TODO: @todo can we make this all work in non-blocking? 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; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout. 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; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout. 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; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout. 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; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout. 00255 00256 buffer->clear(); 00257 while(buffer->size() < buffer->max_size()/2) 00258 { 00259 // Look for the end char 00260 ret = buffer->find_first_of('\n'); 00261 if(ret > 0) 00262 { 00263 // If it is there clear everything after it and return 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 // Append the new data to the buffer 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; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout. 00299 00300 // Clear the buffer before we start 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 // Append the new data to the buffer 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 // Look for the start char 00323 ret = buffer->find_first_of(start); 00324 // If it is not on the buffer, clear it 00325 if(ret == -1) buffer->clear(); 00326 // If it is there, but not on the first position clear everything behind it 00327 else if(ret > 0) buffer->erase(0, ret); 00328 00329 // Look for the end char 00330 ret = buffer->find_first_of(end); 00331 if(ret > 0) 00332 { 00333 // If it is there clear everything after it and return 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 // EOF