CerealPort.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 *
3 * Software License Agreement (BSD License)
4 *
5 * Copyright (c) 2010, ISR University of Coimbra.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * * Neither the name of the ISR University of Coimbra nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 * Author: Gonçalo Cabrita on 01/10/2010
36 *********************************************************************/
37 #include <stdio.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <termios.h>
41 #include <math.h>
42 #include <poll.h>
43 #include <signal.h>
44 #include <fcntl.h>
45 #include <iostream>
46 #include <fstream>
47 #include <stdexcept>
48 
49 #include "cereal_port/CerealPort.h"
50 
52 #define CEREAL_EXCEPT(except, msg, ...) \
53 { \
54  char buf[1000]; \
55  snprintf(buf, 1000, msg " (in cereal::CerealPort::%s)" , ##__VA_ARGS__, __FUNCTION__); \
56  throw except(buf); \
57 }
58 
60 {
61  stream_thread_ = NULL;
62 }
63 
65 {
66  if(portOpen()) close();
67 }
68 
69 void cereal::CerealPort::open(const char * port_name, int baud_rate)
70 {
71  if(portOpen()) close();
72 
73  // Make IO non blocking. This way there are no race conditions that
74  // cause blocking when a badly behaving process does a read at the same
75  // time as us. Will need to switch to blocking for writes or errors
76  // occur just after a replug event.
77  fd_ = ::open(port_name, O_RDWR | O_NONBLOCK | O_NOCTTY);
78 
79  if(fd_ == -1)
80  {
81  const char *extra_msg = "";
82  switch(errno)
83  {
84  case EACCES:
85  extra_msg = "You probably don't have premission to open the port for reading and writing.";
86  break;
87 
88  case ENOENT:
89  extra_msg = "The requested port does not exist. Is the hokuyo connected? Was the port name misspelled?";
90  break;
91  }
92  CEREAL_EXCEPT(cereal::Exception, "Failed to open port: %s. %s (errno = %d). %s", port_name, strerror(errno), errno, extra_msg);
93  }
94 
95  try
96  {
97  struct flock fl;
98  fl.l_type = F_WRLCK;
99  fl.l_whence = SEEK_SET;
100  fl.l_start = 0;
101  fl.l_len = 0;
102  fl.l_pid = getpid();
103 
104  if(fcntl(fd_, F_SETLK, &fl) != 0)
105  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);
106 
107  // Settings for USB?
108  struct termios newtio;
109  tcgetattr(fd_, &newtio);
110  memset (&newtio.c_cc, 0, sizeof (newtio.c_cc));
111  newtio.c_cflag = CS8 | CLOCAL | CREAD;
112  newtio.c_iflag = IGNPAR;
113  newtio.c_oflag = 0;
114  newtio.c_lflag = 0;
115  cfsetspeed(&newtio, baud_rate);
116  baud_ = baud_rate;
117 
118  // Activate new settings
119  tcflush(fd_, TCIFLUSH);
120  if(tcsetattr(fd_, TCSANOW, &newtio) < 0)
121  CEREAL_EXCEPT(cereal::Exception, "Unable to set serial port attributes. The port you specified (%s) may not be a serial port.", port_name);
122  usleep (200000);
123  }
124  catch(cereal::Exception& e)
125  {
126  // These exceptions mean something failed on open and we should close
127  if(fd_ != -1) ::close(fd_);
128  fd_ = -1;
129  throw e;
130  }
131 }
132 
134 {
135  int retval = 0;
136 
137  retval = ::close(fd_);
138 
139  fd_ = -1;
140 
141  if(retval != 0)
142  CEREAL_EXCEPT(cereal::Exception, "Failed to close port properly -- error = %d: %s\n", errno, strerror(errno));
143 }
144 
145 int cereal::CerealPort::write(const char * data, int length)
146 {
147  int len = length==-1 ? strlen(data) : length;
148 
149  // IO is currently non-blocking. This is what we want for the more cerealon read case.
150  int origflags = fcntl(fd_, F_GETFL, 0);
151  fcntl(fd_, F_SETFL, origflags & ~O_NONBLOCK); // TODO: @todo can we make this all work in non-blocking?
152  int retval = ::write(fd_, data, len);
153  fcntl(fd_, F_SETFL, origflags | O_NONBLOCK);
154 
155  if(retval == len) return retval;
156  else CEREAL_EXCEPT(cereal::Exception, "write failed");
157 }
158 
159 int cereal::CerealPort::read(char * buffer, int max_length, int timeout)
160 {
161  int ret;
162 
163  struct pollfd ufd[1];
164  int retval;
165  ufd[0].fd = fd_;
166  ufd[0].events = POLLIN;
167 
168  if(timeout == 0) timeout = -1; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout.
169 
170  if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
171 
172  if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
173 
174  if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
175 
176  ret = ::read(fd_, buffer, max_length);
177 
178  if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
179 
180  return ret;
181 }
182 
183 int cereal::CerealPort::readBytes(char * buffer, int length, int timeout)
184 {
185  int ret;
186  int current = 0;
187 
188  struct pollfd ufd[1];
189  int retval;
190  ufd[0].fd = fd_;
191  ufd[0].events = POLLIN;
192 
193  if(timeout == 0) timeout = -1; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout.
194 
195  while(current < length)
196  {
197  if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
198 
199  if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
200 
201  if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
202 
203  ret = ::read(fd_, &buffer[current], length-current);
204 
205  if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
206 
207  current += ret;
208  }
209  return current;
210 }
211 
212 int cereal::CerealPort::readLine(char * buffer, int length, int timeout)
213 {
214  int ret;
215  int current = 0;
216 
217  struct pollfd ufd[1];
218  int retval;
219  ufd[0].fd = fd_;
220  ufd[0].events = POLLIN;
221 
222  if(timeout == 0) timeout = -1; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout.
223 
224  while(current < length-1)
225  {
226  if(current > 0)
227  if(buffer[current-1] == '\n')
228  return current;
229 
230  if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
231 
232  if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
233 
234  if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
235 
236  ret = ::read(fd_, &buffer[current], length-current);
237 
238  if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
239 
240  current += ret;
241  }
242  CEREAL_EXCEPT(cereal::Exception, "buffer filled without end of line being found");
243 }
244 
245 bool cereal::CerealPort::readLine(std::string * buffer, int timeout)
246 {
247  int ret;
248 
249  struct pollfd ufd[1];
250  int retval;
251  ufd[0].fd = fd_;
252  ufd[0].events = POLLIN;
253 
254  if(timeout == 0) timeout = -1; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout.
255 
256  buffer->clear();
257  while(buffer->size() < buffer->max_size()/2)
258  {
259  // Look for the end char
260  ret = buffer->find_first_of('\n');
261  if(ret > 0)
262  {
263  // If it is there clear everything after it and return
264  buffer->erase(ret+1, buffer->size()-ret-1);
265  return true;
266  }
267 
268  if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
269 
270  if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
271 
272  if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
273 
274  char temp_buffer[128];
275  ret = ::read(fd_, temp_buffer, 128);
276 
277  if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
278 
279  // Append the new data to the buffer
280  try{ buffer->append(temp_buffer, ret); }
281  catch(std::length_error& le)
282  {
283  CEREAL_EXCEPT(cereal::Exception, "buffer filled without reaching end of data stream");
284  }
285  }
286  CEREAL_EXCEPT(cereal::Exception, "buffer filled without end of line being found");
287 }
288 
289 bool cereal::CerealPort::readBetween(std::string * buffer, char start, char end, int timeout)
290 {
291  int ret;
292 
293  struct pollfd ufd[1];
294  int retval;
295  ufd[0].fd = fd_;
296  ufd[0].events = POLLIN;
297 
298  if(timeout == 0) timeout = -1; // For compatibility with former behavior, 0 means no timeout. For poll, negative means no timeout.
299 
300  // Clear the buffer before we start
301  buffer->clear();
302  while(buffer->size() < buffer->max_size()/2)
303  {
304  if((retval = poll(ufd, 1, timeout)) < 0) CEREAL_EXCEPT(cereal::Exception, "poll failed -- error = %d: %s", errno, strerror(errno));
305 
306  if(retval == 0) CEREAL_EXCEPT(cereal::TimeoutException, "timeout reached");
307 
308  if(ufd[0].revents & POLLERR) CEREAL_EXCEPT(cereal::Exception, "error on socket, possibly unplugged");
309 
310  char temp_buffer[128];
311  ret = ::read(fd_, temp_buffer, 128);
312 
313  if(ret == -1 && errno != EAGAIN && errno != EWOULDBLOCK) CEREAL_EXCEPT(cereal::Exception, "read failed");
314 
315  // Append the new data to the buffer
316  try{ buffer->append(temp_buffer, ret); }
317  catch(std::length_error& le)
318  {
319  CEREAL_EXCEPT(cereal::Exception, "buffer filled without reaching end of data stream");
320  }
321 
322  // Look for the start char
323  ret = buffer->find_first_of(start);
324  // If it is not on the buffer, clear it
325  if(ret == -1) buffer->clear();
326  // If it is there, but not on the first position clear everything behind it
327  else if(ret > 0) buffer->erase(0, ret);
328 
329  // Look for the end char
330  ret = buffer->find_first_of(end);
331  if(ret > 0)
332  {
333  // If it is there clear everything after it and return
334  buffer->erase(ret+1, buffer->size()-ret-1);
335  return true;
336  }
337  }
338  CEREAL_EXCEPT(cereal::Exception, "buffer filled without reaching end of data stream");
339 }
340 
342 {
343  int retval = tcflush(fd_, TCIOFLUSH);
344  if(retval != 0) CEREAL_EXCEPT(cereal::Exception, "tcflush failed");
345 
346  return retval;
347 }
348 
349 bool cereal::CerealPort::startReadStream(boost::function<void(char*, int)> f)
350 {
351  if(stream_thread_ != NULL) return false;
352 
353  stream_stopped_ = false;
354  stream_paused_ = false;
355 
356  readCallback = f;
357 
358  stream_thread_ = new boost::thread(boost::bind(&cereal::CerealPort::readThread, this));
359  return true;
360 }
361 
363 {
364  char data[MAX_LENGTH];
365  int ret;
366 
367  struct pollfd ufd[1];
368  ufd[0].fd = fd_;
369  ufd[0].events = POLLIN;
370 
371  while(!stream_stopped_)
372  {
373  if(!stream_paused_)
374  {
375  if(poll(ufd, 1, 10) > 0)
376  {
377  if(!(ufd[0].revents & POLLERR))
378  {
379  ret = ::read(fd_, data, MAX_LENGTH);
380  if(ret>0)
381  {
382  readCallback(data, ret);
383  }
384  }
385  }
386  }
387  }
388 }
389 
390 bool cereal::CerealPort::startReadLineStream(boost::function<void(std::string*)> f)
391 {
392  if(stream_thread_ != NULL) return false;
393 
394  stream_stopped_ = false;
395  stream_paused_ = false;
396 
397  readLineCallback = f;
398 
399  stream_thread_ = new boost::thread(boost::bind(&cereal::CerealPort::readLineThread, this));
400  return true;
401 }
402 
404 {
405  std::string data;
406  bool error = false;
407 
408  while(!stream_stopped_)
409  {
410  if(!stream_paused_)
411  {
412  error = false;
413  try{ readLine(&data, 100); }
414  catch(cereal::Exception& e)
415  {
416  error = true;
417  }
418 
419  if(!error && data.size()>0) readLineCallback(&data);
420  }
421  }
422 }
423 
424 bool cereal::CerealPort::startReadBetweenStream(boost::function<void(std::string*)> f, char start, char end)
425 {
426  if(stream_thread_ != NULL) return false;
427 
428  stream_stopped_ = false;
429  stream_paused_ = false;
430 
431  readBetweenCallback = f;
432 
433  stream_thread_ = new boost::thread(boost::bind(&cereal::CerealPort::readBetweenThread, this, start, end));
434  return true;
435 }
436 
437 void cereal::CerealPort::readBetweenThread(char start, char end)
438 {
439  std::string data;
440  bool error = false;
441 
442  while(!stream_stopped_)
443  {
444  if(!stream_paused_)
445  {
446  error = false;
447  try{ readBetween(&data, start, end, 100); }
448  catch(cereal::Exception& e)
449  {
450  error = true;
451  }
452 
453  if(!error && data.size()>0) readBetweenCallback(&data);
454  }
455  }
456 }
457 
459 {
460  stream_stopped_ = true;
461  stream_thread_->join();
462 
463  delete stream_thread_;
464  stream_thread_ = NULL;
465 }
466 
468 {
469  stream_paused_ = true;
470 }
471 
473 {
474  stream_paused_ = false;
475 }
476 
477 // EOF
bool startReadBetweenStream(boost::function< void(std::string *)> f, char start, char end)
Start a stream of readBetween()
Definition: CerealPort.cpp:424
void stopStream()
Stop streaming.
Definition: CerealPort.cpp:458
#define CEREAL_EXCEPT(except, msg,...)
Macro for throwing an exception with a message, passing args.
Definition: CerealPort.cpp:52
void readBetweenThread(char start, char end)
Thread for a stream of readBetween()
Definition: CerealPort.cpp:437
int readBytes(char *data, int length, int timeout=-1)
Read a fixed number of bytes from the serial port.
Definition: CerealPort.cpp:183
void open(const char *port_name, int baud_rate=115200)
Open the serial port.
Definition: CerealPort.cpp:69
int write(const char *data, int length=-1)
Write to the port.
Definition: CerealPort.cpp:145
#define MAX_LENGTH
Definition: CerealPort.h:46
bool readBetween(std::string *data, char start, char end, int timeout=-1)
Read from the serial port between a start char and an end char.
Definition: CerealPort.cpp:289
bool startReadStream(boost::function< void(char *, int)> f)
Start a stream of read()
Definition: CerealPort.cpp:349
void close()
Close the serial port.
Definition: CerealPort.cpp:133
int read(char *data, int max_length, int timeout=-1)
Read from the port.
Definition: CerealPort.cpp:159
void readThread()
Thread for a stream of read()
Definition: CerealPort.cpp:362
~CerealPort()
Destructor.
Definition: CerealPort.cpp:64
void resumeStream()
Resume streaming.
Definition: CerealPort.cpp:472
int flush()
Wrapper around tcflush.
Definition: CerealPort.cpp:341
bool startReadLineStream(boost::function< void(std::string *)> f)
Start a stream of readLine(std::string*, int)
Definition: CerealPort.cpp:390
void readLineThread()
Thread for a stream of readLine(std::string*, int)
Definition: CerealPort.cpp:403
CerealPort()
Constructor.
Definition: CerealPort.cpp:59
boost::thread * stream_thread_
Stream thread.
Definition: CerealPort.h:259
int readLine(char *data, int length, int timeout=-1)
Read a line from the serial port.
Definition: CerealPort.cpp:212
void pauseStream()
Pause streaming.
Definition: CerealPort.cpp:467


cereal_port
Author(s): Gonçalo Cabrita and Pedro Sousa
autogenerated on Mon Mar 2 2015 01:31:33