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  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 
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 {
922  MRPT_START
923  MRPT_UNUSED_PARAM(Origin);
924  MRPT_UNUSED_PARAM(Offset);
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 
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
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.
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
~CSerialPort() override
Destructor.
Definition: CSerialPort.cpp:61
unsigned __int64 uint64_t
Definition: glext.h:3457
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.
GLuint start
Definition: glext.h:3532
void Tic() noexcept
Starts the stopwatch.
Definition: CTicTac.cpp:75
__int64 int64_t
Definition: glext.h:3456
#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: 9b18308f3 Mon Nov 18 23:39:25 2019 +0100 at lun nov 18 23:45:12 CET 2019