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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: c7a3bec24 Sun Mar 29 18:33:13 2020 +0200 at dom mar 29 18:50:38 CEST 2020