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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 8fe78517f Sun Jul 14 19:43:28 2019 +0200 at lun oct 28 02:10:00 CET 2019