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


md49_serialport
Author(s): Fabian Prinzing
autogenerated on Mon Jun 10 2019 13:54:48