Main MRPT website > C++ reference for MRPT 1.5.7
CSerialPort.cpp
Go to the documentation of this file.
1 /* +---------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +---------------------------------------------------------------------------+ */
9 
10 #include "hwdrivers-precomp.h" // Precompiled headers
11 
13 #include <mrpt/system/os.h>
14 #include <mrpt/utils/utils_defs.h>
15 #include <mrpt/system/threads.h>
16 
17 #if defined(MRPT_OS_LINUX) || defined(MRPT_OS_APPLE)
18 // Linux implementation: refer to
19 // http://www.easysw.com/~mike/serial/serial.html
20 
21 #include <stdio.h> /* Standard input/output definitions */
22 #include <string.h> /* String function definitions */
23 #include <unistd.h> /* UNIX standard function definitions */
24 #include <fcntl.h> /* File control definitions */
25 #include <errno.h> /* Error number definitions */
26 #include <sys/time.h> // gettimeofday
27 
28 #include <termios.h> /* POSIX terminal control definitions */
29 
30 #include <sys/ioctl.h> // FIONREAD,...
31 #include <signal.h>
32 
33 #ifdef HAVE_LINUX_SERIAL_H
34  #include <linux/serial.h>
35 #endif
36 
37 #include <map>
38 
39 #endif // Linux | Apple
40 
41 #ifdef MRPT_OS_WINDOWS
42 #include <windows.h>
43 #endif
44 
45 using namespace mrpt;
46 using namespace mrpt::utils;
47 using namespace mrpt::hwdrivers;
48 using namespace std;
49 
50 // ctor
51 CSerialPort::CSerialPort( const string &portName, bool openNow ) :
52  m_serialName(portName),
53  m_totalTimeout_ms(0),
54  m_interBytesTimeout_ms(0),
55 #ifdef MRPT_OS_WINDOWS
56  hCOM(NULL)
57 #else
58  hCOM(-1) // Not connected
59 #endif
60 {
61  if (openNow) open();
62 }
63 
64 // Default ctor
66  m_serialName(),
67  m_totalTimeout_ms(0),
68  m_interBytesTimeout_ms(0),
69 #ifdef MRPT_OS_WINDOWS
70  hCOM(NULL)
71 #else
72  hCOM(-1) // Not connected
73 #endif
74 {
75 }
76 
77 // Dtor:
79 {
80  if ( isOpen() )
81  close();
82 }
83 
84 /* -----------------------------------------------------
85  Open
86  ----------------------------------------------------- */
88 {
90 #ifdef MRPT_OS_WINDOWS
91  // Check name:
92  if (!m_serialName.size()) THROW_EXCEPTION("Serial port name is empty!!")
93 
94  // Is it COMX, X>4? -> "\\.\COMX"
95  if (tolower(m_serialName[0]) == 'c' && tolower(m_serialName[1]) == 'o' && tolower(m_serialName[2]) == 'm')
96  {
97  // Need to add "\\.\"?
98  if (m_serialName.size()>4 || m_serialName[3]>'4')
99  m_serialName = std::string("\\\\.\\") + m_serialName;
100  }
101 
102 
103  // Open the serial port:
104  if (INVALID_HANDLE_VALUE == (
105  hCOM = CreateFileA(
106  m_serialName.c_str(), // Serial Port name
107  GENERIC_READ | GENERIC_WRITE,
108  0,
109  NULL,
110  OPEN_EXISTING,
111  0,
112  0)))
113  {
114  hCOM = NULL;
115  THROW_EXCEPTION_FMT("Error trying to open serial port: %s", m_serialName.c_str());
116  }
117 
118  // Set recommended buffer sizes:
119  SetupComm(hCOM, 4096, 4096);
120 
121 #else
122  // Check name:
123  if (!m_serialName.size()) THROW_EXCEPTION("Serial port name is empty!!")
124  if (m_serialName[0]!='/') m_serialName = string("/dev/") + m_serialName;
125 
126  // Open the serial port:
127  // The O_NOCTTY flag tells UNIX that this program doesn't want to be the "controlling terminal" for that port.
128  // The O_NDELAY flag tells UNIX that this program doesn't care what state the DCD signal line is in - whether the other end of the port is up and running.
129  if ( -1==( hCOM= ::open( m_serialName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY ) ) )
130  THROW_EXCEPTION_FMT("Error trying to open the serial port %s!!",m_serialName.c_str());
131 
132  // Clear flags:
133  fcntl( hCOM, F_SETFL, 0 );
134 
135  //
136  // Start assembling the new port settings.
137  //
138  termios port_settings;
139  bzero( &port_settings,sizeof( port_settings ) ) ;
140 
141  //
142  // Enable the receiver (CREAD) and ignore modem control lines
143  // (CLOCAL).
144  //
145  port_settings.c_cflag |= CREAD | CLOCAL ;
146 
147  //
148  // Set the VMIN and VTIME parameters to zero by default. VMIN is
149  // the minimum number of characters for non-canonical read and
150  // VTIME is the timeout in deciseconds for non-canonical
151  // read. Setting both of these parameters to zero implies that a
152  // read will return immediately only giving the currently
153  // available characters.
154  //
155  port_settings.c_cc[ VMIN ] = 0 ;
156  port_settings.c_cc[ VTIME ] = 0 ;
157 
158  /*
159  * Flush the input buffer associated with the port.
160  */
161  if ( tcflush( hCOM,TCIFLUSH ) < 0 )
162  THROW_EXCEPTION_FMT("Cannot flush serial port: %s",strerror(errno) );
163 
164  /*
165  * Write the new settings to the port.
166  */
167  if ( tcsetattr( hCOM,TCSANOW,&port_settings ) < 0 )
168  THROW_EXCEPTION_FMT("Cannot set the new config to the serial port: %s",strerror(errno) ) ;
169 
170 
171  // Do NOT block on read.
172  fcntl(hCOM, F_SETFL, FNDELAY);
173 
174  // Success!
175 #endif
176  MRPT_END
177 }
178 
179 
180 /* -----------------------------------------------------
181  isOpen
182  ----------------------------------------------------- */
184 {
185 #ifdef MRPT_OS_WINDOWS
186  return hCOM != NULL;
187 #else
188  return hCOM != -1;
189 #endif
190 }
191 
192 /* -----------------------------------------------------
193  setConfig
194  ----------------------------------------------------- */
196  int baudRate,
197  int parity,
198  int bits,
199  int nStopBits,
200  bool enableFlowControl )
201 {
202  MRPT_START
203 #ifdef MRPT_OS_WINDOWS
204 
205  DCB dcb_conf;
206  dcb_conf.DCBlength = sizeof(DCB);
207 
208  // Port must be open!
209  if (!isOpen()) THROW_EXCEPTION("The serial port is not open");
210 
211  if (!GetCommState(hCOM, &dcb_conf)) THROW_EXCEPTION("Error retrieving COM state");
212 
213  //
214  // Apply baud rate
215  //
216  int BR;
217  switch (baudRate)
218  {
219  case 300: BR = CBR_300; break;
220  case 600: BR = CBR_600; break;
221  case 1200: BR = CBR_1200; break;
222  case 2400: BR = CBR_2400; break;
223  case 4800: BR = CBR_4800; break;
224  case 9600: BR = CBR_9600; break;
225  case 19200: BR = CBR_19200; break;
226  case 38400: BR = CBR_38400; break;
227  case 57600: BR = CBR_57600; break;
228  case 115200: BR = CBR_115200; break;
229  default:
230  BR = baudRate;
231  //THROW_EXCEPTION_FMT("Invalid desired baud rate value: %i",baudRate ) ;
232  break;
233  }
234 
235  dcb_conf.BaudRate = BR;
236 
237 
238  dcb_conf.ByteSize = (BYTE)bits;
239  dcb_conf.Parity = (BYTE)parity;
240 
241  // stop bits:
242  switch (nStopBits)
243  {
244  case 1:
245  dcb_conf.StopBits = ONESTOPBIT;
246  break;
247  case 2:
248  dcb_conf.StopBits = TWOSTOPBITS;
249  break;
250  default:
251  THROW_EXCEPTION_FMT("Invalid number of stop bits: %i", nStopBits);
252  break;
253  }
254 
255  dcb_conf.fBinary = true;
256  dcb_conf.fParity = parity != 0;
257 
258  dcb_conf.fRtsControl = enableFlowControl ? RTS_CONTROL_HANDSHAKE : RTS_CONTROL_DISABLE;
259  dcb_conf.fOutxCtsFlow = enableFlowControl;
260 
261  // Apply:
262  if (!SetCommState(hCOM, &dcb_conf))
263  THROW_EXCEPTION("Error changing COM state");
264 
265  // Assure:
266  if (!GetCommState(hCOM, &dcb_conf))
267  THROW_EXCEPTION("Error retrieving COM state");
268  if (((int)dcb_conf.BaudRate) != baudRate)
269  THROW_EXCEPTION("COM state verification after writing failed");
270 
271  m_baudRate = baudRate;
272 
273 #else
274  // Port must be open!
275  if (!isOpen()) THROW_EXCEPTION("The serial port is not open!");
276 
277  ASSERT_(baudRate>0)
278 
279  //
280  // Apply baud rate
281  //
282  int BR;
283  bool special_rate = false;
284  switch (baudRate)
285  {
286  case 50: BR = B50; break;
287  case 75: BR = B75; break;
288  case 110: BR = B110; break;
289  case 134: BR = B134; break;
290  case 200: BR = B200; break;
291  case 300: BR = B300; break;
292  case 600: BR = B600; break;
293  case 1200: BR = B1200; break;
294  case 2400: BR = B2400; break;
295  case 4800: BR = B4800; break;
296  case 9600: BR = B9600; break;
297  case 19200: BR = B19200; break;
298  case 38400: BR = B38400; break;
299  case 57600: BR = B57600; break;
300  case 115200: BR = B115200; break;
301  case 230400: BR = B230400; break;
302 #ifdef B460800
303  case 460800: BR = B460800; break;
304 #endif
305 #ifdef B500000
306  case 500000: BR = B500000; break;
307 #endif
308 #ifdef B4000000
309  case 576000: BR = B576000; break;
310  case 921600: BR = B921600; break;
311  case 1000000: BR = B1000000; break;
312  case 1152000: BR = B1152000; break;
313  case 1500000: BR = B1500000; break;
314  case 2000000: BR = B2000000; break;
315  case 2500000: BR = B2500000; break;
316  case 3000000: BR = B3000000; break;
317  case 3500000: BR = B3500000; break;
318  case 4000000: BR = B4000000; break;
319 #endif
320  default:
321 #ifdef HAVE_LINUX_SERIAL_H
322  special_rate = true;
323 #else
324  BR = baudRate; // This is all we can try in that case...
325 #endif
326  break;
327  }
328 
329  if (special_rate)
330  {
331 #ifdef HAVE_LINUX_SERIAL_H
332  struct serial_struct serial;
333  if (ioctl(hCOM, TIOCGSERIAL, &serial) < 0)
334  THROW_EXCEPTION("error on TIOCGSERIAL ioctl");
335 
336  serial.custom_divisor = serial.baud_base / baudRate;
337  if (!serial.custom_divisor) serial.custom_divisor = 1;
338  const int actual_rate = serial.baud_base / serial.custom_divisor;
339 
340  serial.flags &= ~ASYNC_SPD_MASK;
341  serial.flags |= ASYNC_SPD_CUST; // We want to use our CUSTOM divisor.
342 
343  if (ioctl(hCOM, TIOCSSERIAL, &serial) < 0)
344  THROW_EXCEPTION("error on TIOCSSERIAL ioctl");
345 
346  BR = B38400; // It seems that 38400 means to the driver here to use our custom divisor
347 
348  if (actual_rate!=baudRate)
349  cout << "[CSerialPort::setConfig] Setting custom baud rate to " << actual_rate << ", the closer I can make to " << baudRate << endl;
350 #else
351  THROW_EXCEPTION("Custom serial port baud rates require linux/serial.h");
352 #endif
353  } // end specialRate
354  else
355  {
356  // Normal baudrate: Just in case, undo possible custom divisors:
357 //#ifdef HAVE_LINUX_SERIAL_H
358 // struct serial_struct serial;
359 // if (ioctl(hCOM, TIOCGSERIAL, &serial) < 0)
360 // THROW_EXCEPTION("error on TIOCGSERIAL ioctl");
361 //
362 // serial.flags &= ~ASYNC_SPD_MASK;
363 //
364 // if (ioctl(hCOM, TIOCSSERIAL, &serial) < 0)
365 // THROW_EXCEPTION("error on TIOCSSERIAL ioctl");
366 //#endif
367  }
368 
369  termios port_settings;
370  if ( tcgetattr( hCOM, & port_settings ) < 0 )
371  THROW_EXCEPTION_FMT("Cannot get the current settings: %s",strerror(errno) ) ;
372 
373  if ( ( cfsetispeed( &port_settings,BR ) < 0 ) ||
374  ( cfsetospeed( &port_settings,BR) < 0 ) )
375  THROW_EXCEPTION_FMT("Cannot change baudRate in setting structure: %s",strerror(errno) ) ;
376 
377  //
378  // Set the character size.
379  //
380  port_settings.c_cflag &= ~CSIZE ;
381  switch (bits)
382  {
383  case 5:
384  port_settings.c_cflag |= CS5;
385  break;
386  case 6:
387  port_settings.c_cflag |= CS6;
388  break;
389  case 7:
390  port_settings.c_cflag |= CS7;
391  break;
392  case 8:
393  port_settings.c_cflag |= CS8;
394  break;
395  default:
396  THROW_EXCEPTION_FMT("Invalid character size: %i",bits ) ;
397  break;
398  }
399 
400  // parity 0:No parity, 1:Odd, 2:Even
401  switch ( parity )
402  {
403  case 2:
404  port_settings.c_cflag |= PARENB ;
405  port_settings.c_cflag &= ~PARODD ;
406  port_settings.c_iflag |= INPCK ;
407  break ;
408  case 1:
409  port_settings.c_cflag |= ( PARENB | PARODD );
410  port_settings.c_iflag |= INPCK;
411  break ;
412  case 0:
413  port_settings.c_cflag &= ~(PARENB);
414  port_settings.c_iflag |= IGNPAR;
415  break ;
416  default:
417  THROW_EXCEPTION_FMT("Invalid parity selection: %i",parity) ;
418  break;
419  }
420 
421  // stop bits:
422  switch ( nStopBits )
423  {
424  case 1:
425  port_settings.c_cflag &= ~(CSTOPB) ;
426  break ;
427  case 2:
428  port_settings.c_cflag |= CSTOPB ;
429  break ;
430  default:
431  THROW_EXCEPTION_FMT("Invalid number of stop bits: %i",nStopBits) ;
432  break;
433  }
434 
435  //
436  // Set the flow control.
437  //
438  if (enableFlowControl)
439  {
440  // RTS/CTS ON:
441  port_settings.c_cflag |= CRTSCTS ;
442  }
443  else
444  {
445  // none
446  port_settings.c_cflag &= ~(CRTSCTS) ;
447  }
448 
449  /* Write the new settings to the port.
450  */
451  if ( tcsetattr( hCOM,TCSANOW,&port_settings ) < 0 )
452  THROW_EXCEPTION_FMT("Cannot set the new settings: %s",strerror(errno) );
453 
454  // Check:
455  termios port_settings_verif;
456  if ( tcgetattr( hCOM, & port_settings_verif) < 0 )
457  THROW_EXCEPTION_FMT("Cannot get the settings to verify: %s",strerror(errno) ) ;
458 
459 #if 0
460  if (!special_rate)
461  {
462  if (port_settings_verif.c_ispeed != port_settings.c_ispeed)
463  THROW_EXCEPTION("Verification of changed baudrate(i) failed");
464  if (port_settings_verif.c_ospeed != port_settings.c_ospeed)
465  THROW_EXCEPTION("Verification of changed baudrate(i) failed");
466  }
467 
468  if (port_settings_verif.c_cflag != port_settings.c_cflag)
469  THROW_EXCEPTION("Verification of serial port flags failed");
470 #endif
471 
472  m_baudRate = baudRate;
473 #endif
474  MRPT_END
475 }
476 
478  int ReadIntervalTimeout,
479  int ReadTotalTimeoutMultiplier,
480  int ReadTotalTimeoutConstant,
481  int WriteTotalTimeoutMultiplier,
482  int WriteTotalTimeoutConstant )
483 {
484  MRPT_START
485 #ifdef MRPT_OS_WINDOWS
486  COMMTIMEOUTS timeouts;
487 
488  // Port must be open!
489  if (!isOpen())
490  THROW_EXCEPTION("The COM port is not open");
491 
492  // Config:
493  timeouts.ReadIntervalTimeout = ReadIntervalTimeout; // Milisegundos entre dos bytes recibidos
494  timeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier; // Milisegundos de espera por cada byte a recibir
495  timeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant; // Milisegundos de espera en cada operacion de recepcion
496  timeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier; // Timeout de escritura no usado
497  timeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant; // Timeout de escritura no usado
498 
499  if (!SetCommTimeouts(hCOM, &timeouts))
500  THROW_EXCEPTION("Error changing COM port timeout config");
501 
502  // Success
503 #else
504  // Port must be open!
505  if (!isOpen()) THROW_EXCEPTION("The serial port is not open!");
506 
507  // Save variables which are used in other methods:
508  m_totalTimeout_ms = ReadTotalTimeoutConstant;
509  m_interBytesTimeout_ms = ReadIntervalTimeout;
510 
511  // http://www.unixwiz.net/techtips/termios-vmin-vtime.html
512  // VMIN & VTIME
513  termios port_settings;
514  if (tcgetattr(hCOM, &port_settings) < 0)
515  THROW_EXCEPTION_FMT("Cannot get the current settings: %s", strerror(errno));
516 
517  // We set VMIN=0 and VTIME=ReadIntervalTimeout (in thenth of seconds)
518  //
519  port_settings.c_cc[VMIN] = 0;
520  port_settings.c_cc[VTIME] = max(1, ReadTotalTimeoutConstant / 100);
521 
522  /* Write the new settings to the port.
523  */
524  if (tcsetattr(hCOM, TCSANOW, &port_settings) < 0)
525  THROW_EXCEPTION_FMT("Cannot set the new settings: %s", strerror(errno));
526 #endif
527  MRPT_END
528 }
529 
530 /* -----------------------------------------------------
531  Close
532  ----------------------------------------------------- */
534 {
535  MRPT_START
536 #ifdef MRPT_OS_WINDOWS
537  if (hCOM)
538  CloseHandle(hCOM);
539  hCOM = NULL;
540 #else
541  if (hCOM<0) return; // Already closed
542 
543 // PosixSignalDispatcher& signal_dispatcher = PosixSignalDispatcher::Instance() ;
544 // signal_dispatcher.DetachHandler( SIGIO, *this ) ;
545  // Close the serial port file descriptor.
546  ::close(hCOM);
547 
548  hCOM=-1; // Means the port is closed
549 #endif
550  MRPT_END
551 }
552 
553 /* -----------------------------------------------------
554  read
555  ----------------------------------------------------- */
556 size_t CSerialPort::Read(void *Buffer, size_t Count)
557 {
558  MRPT_START
559 #ifdef MRPT_OS_WINDOWS
560  // Port must be open!
561  if (!isOpen())
562  THROW_EXCEPTION("The port is not open yet!");
563 
564  DWORD actuallyRead;
565 
566  if (!ReadFile(
567  hCOM, // Handle,
568  Buffer, // Buffer
569  (DWORD)Count, // Max expected bytes
570  &actuallyRead, // Actually read bytes
571  NULL))
572  THROW_EXCEPTION("Error reading from port!");
573 
574  return actuallyRead;
575 #else
576 
577  // Port must be open!
578  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
579 
580  if (!Count) return 0;
581 
582  // Use the "m_totalTimeout_ms" global timeout
583  // and the "m_interBytesTimeout_ms" for inter-bytes:
584  m_timer.Tic();
585 
586  size_t alreadyRead = 0;
587  int leftTime = m_totalTimeout_ms - (int)(m_timer.Tac()*1000);
588 
589  while ( alreadyRead<Count && leftTime>=0 )
590  {
591  // Bytes waiting in the queue?
592  // Check if we are still connected or there is an error...
593  int waiting_bytes=0;
594  if ( ioctl(hCOM, FIONREAD, &waiting_bytes) < 0)
595  {
596  if (errno==EIO)
597  {
598  // The port has been disconnect (for USB ports)
599  this->close();
600  return alreadyRead;
601  }
602  }
603 
604  // Are there any bytes??
605  int nRead=0;
606 
607  if (waiting_bytes>0)
608  {
609  int nToRead = min( (size_t)waiting_bytes, Count-alreadyRead );
610 
611  if ( ( nRead=::read(hCOM, ((char*)Buffer)+alreadyRead, nToRead ) ) <0 )
612  {
613  cerr << "[CSerialPort] Error reading from port..." << endl;
614  }
615 
616  alreadyRead+= nRead;
617  }
618  else
619  {
620  // Nope...
621  }
622 
623  if (alreadyRead<Count)
624  {
625  // Wait 1 more ms for new data to arrive.
626  mrpt::system::sleep( 1 );
627  }
628 
629  // Reset interbytes timer:
630  leftTime = m_totalTimeout_ms - (int)(m_timer.Tac()*1000);
631  if (nRead>0)
632  leftTime = max(leftTime, m_interBytesTimeout_ms);
633  }
634 
635 // cout << "READ DONE: "<< alreadyRead << endl;
636  return alreadyRead;
637 #endif
638  MRPT_END
639 }
640 
641 /** Reads one text line from the serial port in POSIX "canonical mode".
642  * This method reads from the serial port until one of the characters in \a eol are found.
643  */
645  const int total_timeout_ms,
646  bool *out_timeout,
647  const char *eol_chars)
648 {
650  // Calling ::ReadBuffer() many times would be even worse, so replicate its code here:
651 
652  ASSERT_(eol_chars != NULL)
653 
654  // Port must be open!
655  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
656 
657  if (out_timeout) *out_timeout = false; // Will be set to true on timeout
658 
659  m_timer.Tic();
660  string receivedStr; // Rx buffer
661 
662  while (total_timeout_ms<0 || (m_timer.Tac()*1e3 < total_timeout_ms))
663  {
664 #ifdef MRPT_OS_WINDOWS
665  // Read just 1 byte:
666  char buf[1];
667 
668  DWORD actuallyRead;
669  if (!ReadFile(
670  hCOM, // Handle,
671  buf, // Buffer
672  1, // Max expected bytes
673  &actuallyRead, // Actually read bytes
674  NULL))
675  THROW_EXCEPTION("Error reading from port!");
676 
677  if (actuallyRead)
678  { // Append to string, if it's not a control char:
679  if (!strchr(eol_chars, buf[0]))
680  receivedStr.push_back(buf[0]);
681  else
682  { // end of string!
683  return receivedStr;
684  }
685  }
686  // If we are still here, string is not finished:
687  mrpt::system::sleep(1); // Wait 1 more ms for new data to arrive.
688 #else
689  // Bytes waiting in the queue?
690  // Check if we are still connected or there is an error...
691  int waiting_bytes = 0;
692  if (ioctl(hCOM, FIONREAD, &waiting_bytes) < 0)
693  {
694  if (errno == EIO)
695  { // The port has been disconnect (for USB ports)
696  this->close();
697  THROW_EXCEPTION("Error reading port before end of line")
698  }
699  }
700 
701  // Are there any bytes??
702  int nRead = 0;
703  if (waiting_bytes>0)
704  {
705  // Read just 1 byte:
706  char buf[1];
707  if ((nRead = ::read(hCOM, buf, 1)) <0)
708  {
709  cerr << "[CSerialPort] Error reading from port..." << endl;
710  }
711  if (nRead)
712  { // Append to string, if it's not a control char:
713  if (!strchr(eol_chars, buf[0]))
714  receivedStr.push_back(buf[0]);
715  else
716  { // end of string!
717  return receivedStr;
718  }
719  }
720  }
721  else
722  {
723  // we decide to move the sleep here to satisfy realtime requirement in the case where we are waiting a n-length string at a frequency
724  // greater than 1/n...
725  mrpt::system::sleep(1); // Wait 1 more ms for new data to arrive.
726  }
727  // If we are still here, string is not finished:
728 #endif
729  }
730 
731  // Timeout:
732  if (out_timeout) *out_timeout = true;
733  return receivedStr;
735 }
736 
737 size_t CSerialPort::Write(const void *Buffer, size_t Count)
738 {
739  MRPT_START
740  // Port must be open!
741  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
742 
743 #ifdef MRPT_OS_WINDOWS
744  DWORD actuallyWritten;
745  if (!WriteFile(
746  hCOM,
747  Buffer,
748  (DWORD)Count,
749  &actuallyWritten,
750  NULL))
751  THROW_EXCEPTION("Error writing to port!");
752  return actuallyWritten;
753 #else
754  // Write the data to the serial port. Keep retrying if EAGAIN
755  // error is received.
756 
757  /** \todo Add support for write timeout here */
758  struct timeval start, end;
759  int num_of_bytes_written = -1 ;
760  size_t total_bytes_written = 0;
761  do
762  {
763  gettimeofday(&start, NULL);
764  num_of_bytes_written = write( hCOM,reinterpret_cast<const char*>(Buffer)+total_bytes_written, Count-total_bytes_written );
765  //cout << "wr: " << num_of_bytes_written << " tot: " << total_bytes_written << " of " << Count << " err: " << errno << endl;
766  if (num_of_bytes_written>0)
767  total_bytes_written+=num_of_bytes_written;
768 
769  if (num_of_bytes_written<(int)Count)
770  {
771  // JL: These few lines are from the Player/Stage project:
772 
773  // need to do this sort of busy wait to ensure the right timing
774  // although I've noticed you will get some anamolies that are
775  // in the ms range; this could be a problem...
776  int usecs;
777  do {
778  gettimeofday(&end, NULL);
779  usecs= (end.tv_sec - start.tv_sec)*1000000 +
780  (end.tv_usec - start.tv_usec);
781  } while (usecs < 60);
782  //mrpt::system::sleep(1); // we'll continue writting is a ms.
783  }
784  }
785  while ( ( total_bytes_written<Count) &&
786  ( !errno || EAGAIN == errno ) ) ;
787  //
788  if ( num_of_bytes_written < 0 ) // This means we exit the loop due to a bad "errno".
789  THROW_EXCEPTION_FMT("Error writing data to the serial port: %s",strerror(errno) ) ;
790 
791  // Make sure the queue is drained
792  // Synchronous IO doesnt always work
793  ::tcdrain(hCOM);
794 
795  // OK:
796  return total_bytes_written;
797 #endif
798 
799  MRPT_END
800 }
801 
803 {
804  MRPT_START
805 
806  // Port must be open!
807  if (!isOpen()) THROW_EXCEPTION("The port is not open yet!");
808 
809 #ifdef MRPT_OS_WINDOWS
810  if (!PurgeComm(hCOM, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR))
811  THROW_EXCEPTION("Error during COM port purge");
812 #else
813  /* Flush the input buffer associated with the port. */
814  if ( tcflush( hCOM,TCIFLUSH ) < 0 )
815  THROW_EXCEPTION_FMT("Cannot flush serial port: %s",strerror(errno) ) ;
816 #endif
817 
818  MRPT_END
819 }
820 
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
Definition: zip.h:16
bool isOpen() const
Returns if port has been correctly open.
#define min(a, b)
void setTimeouts(int ReadIntervalTimeout, int ReadTotalTimeoutMultiplier, int ReadTotalTimeoutConstant, int WriteTotalTimeoutMultiplier, int WriteTotalTimeoutConstant)
Changes the timeouts of the port, in milliseconds.
#define THROW_EXCEPTION(msg)
#define THROW_EXCEPTION_FMT(_FORMAT_STRING,...)
Contains classes for various device interfaces.
size_t Write(const void *Buffer, size_t Count)
Implements the virtual method responsible for writing to the stream.
STL namespace.
void Tic()
Starts the stopwatch.
Definition: CTicTac.cpp:77
#define MRPT_TRY_END
#define MRPT_END
void BASE_IMPEXP sleep(int time_ms) MRPT_NO_THROWS
An OS-independent method for sending the current thread to "sleep" for a given period of time...
Definition: threads.cpp:57
void setConfig(int baudRate, int parity=0, int bits=8, int nStopBits=1, bool enableFlowControl=false)
Changes the configuration of the port.
GLuint GLuint end
Definition: glext.h:3512
mrpt::utils::CTicTac m_timer
Used only in ReadString.
Definition: CSerialPort.h:193
GLsizei const GLchar ** string
Definition: glext.h:3919
void close()
Close the port.
CSerialPort()
Default constructor: it does not open any port - later you must call "setSerialPortName" and then "op...
Definition: CSerialPort.cpp:65
#define MRPT_START
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
int hCOM
The file handle (-1: Not open)
Definition: CSerialPort.h:202
void purgeBuffers()
Purge tx and rx buffers.
std::string ReadString(const int total_timeout_ms=-1, bool *out_timeout=NULL, const char *eol_chars="\")
Reads one text line from the serial port in POSIX "canonical mode".
void open()
Open the port.
Definition: CSerialPort.cpp:87
double Tac()
Stops the stopwatch.
Definition: CTicTac.cpp:92
#define MRPT_TRY_START
#define ASSERT_(f)
std::string m_serialName
The complete name of the serial port device (i.e.
Definition: CSerialPort.h:189
virtual ~CSerialPort()
Destructor.
Definition: CSerialPort.cpp:78
GLuint start
Definition: glext.h:3512
size_t Read(void *Buffer, size_t Count)
Implements the virtual method responsible for reading from the stream - Unlike CStream::ReadBuffer, this method will not raise an exception on zero bytes read, as long as there is not any fatal error in the communications.



Page generated by Doxygen 1.8.14 for MRPT 1.5.7 Git: 5902e14cc Wed Apr 24 15:04:01 2019 +0200 at lun oct 28 01:39:17 CET 2019