Medical Imaging Interaction Toolkit  2018.4.99-389bf124
Medical Imaging Interaction Toolkit
mitkSerialCommunication.cpp
Go to the documentation of this file.
1 /*============================================================================
2 
3 The Medical Imaging Interaction Toolkit (MITK)
4 
5 Copyright (c) German Cancer Research Center (DKFZ)
6 All rights reserved.
7 
8 Use of this source code is governed by a 3-clause BSD license that can be
9 found in the LICENSE file.
10 
11 ============================================================================*/
12 
14 
15 #ifdef WIN32
16 //#include <atlstr.h>
17 #include <itksys/SystemTools.hxx>
18 #else // Posix
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <sys/ioctl.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <termios.h>
26 #include <cerrno>
27 
28 #define INVALID_HANDLE_VALUE -1
29 #endif
30 
31 #define OK 1
32 #define ERROR_VALUE 0
33 
35  m_DeviceName(""), m_PortNumber(COM1), m_BaudRate(BaudRate9600),
36  m_DataBits(DataBits8), m_Parity(None), m_StopBits(StopBits1),
37  m_HardwareHandshake(HardwareHandshakeOff),
38  m_ReceiveTimeout(500), m_SendTimeout(500), m_Connected(false)
39 {
40 #ifdef WIN32 // Windows
41  m_ComPortHandle = INVALID_HANDLE_VALUE;
42 #else // Posix
44 #endif
45 }
46 
48 {
50 }
51 
53 {
54  return m_Connected;
55 }
56 
58 {
59  if (m_Connected)
60  return ERROR_VALUE;
61 
62 #ifdef WIN32
63  std::stringstream ss;
64  if (m_DeviceName.empty())
65  ss << "\\\\.\\COM" << static_cast<unsigned int>(m_PortNumber); // use m_PortNumber
66  else
67  ss << "\\\\.\\" << m_DeviceName; // use m_DeviceName
68 
69  m_ComPortHandle = CreateFile(ss.str().c_str(), GENERIC_READ | GENERIC_WRITE,
70  0, // no sharing
71  0, // no security flags
72  OPEN_EXISTING, // open com port, don't create it
73  0, // no flags
74  0); // no template
75  if (m_ComPortHandle == INVALID_HANDLE_VALUE)
76  return ERROR_VALUE;
77 
78  GetCommState(m_ComPortHandle, &m_PreviousDeviceControlBlock);
79  GetCommTimeouts(m_ComPortHandle, &m_PreviousTimeout);
80  GetCommMask(m_ComPortHandle, &m_PreviousMask);
81 
82  if (this->ApplyConfiguration() != OK) // set interface parameters
83  {
84  CloseHandle(m_ComPortHandle);
85  m_ComPortHandle = INVALID_HANDLE_VALUE;
86  return ERROR_VALUE;
87  }
88  m_Connected = true;
89  return OK;
90 
91 #else // Posix
92  std::stringstream ss;
93  if (m_DeviceName.empty())
94  ss << "/dev/ttyS" << static_cast<unsigned int>(m_PortNumber) - 1; // use m_PortNumber, COM1 = ttyS0
95  else
96  ss << m_DeviceName; // use m_DeviceName
97 
98  //m_FileDescriptor = open(ss.str().c_str(), O_RDWR | O_NONBLOCK | O_NDELAY | O_NOCTTY | O_EXCL); // open device file
99  m_FileDescriptor = open(ss.str().c_str(), O_RDWR|O_NONBLOCK|O_EXCL); // open device file
100  if (m_FileDescriptor < 0)
101  return ERROR_VALUE;
102 
103  fcntl(m_FileDescriptor, F_SETFL, 0); // change to blocking mode
104  tcflush(m_FileDescriptor, TCIOFLUSH); // flush buffers
105  if (this->ApplyConfiguration() != OK) // set interface parameters
106  {
107  close(m_FileDescriptor);
109  return ERROR_VALUE;
110  }
111  m_Connected = true;
112  return OK;
113 #endif
114 }
115 
117 {
118 #ifdef WIN32
119  if (m_ComPortHandle == INVALID_HANDLE_VALUE)
120  return;
122  ClearSendBuffer();
123  SetCommState(m_ComPortHandle, &m_PreviousDeviceControlBlock); // restore previous settings
124  SetCommTimeouts(m_ComPortHandle, &m_PreviousTimeout); // restore previous timeout values
125  SetCommMask(m_ComPortHandle, m_PreviousMask); // restore previous mask value
126  PurgeComm(m_ComPortHandle, PURGE_TXCLEAR | PURGE_RXCLEAR); // empty buffers
127  CloseHandle(m_ComPortHandle); // close handle
128  m_ComPortHandle = INVALID_HANDLE_VALUE;
129  m_Connected = false;
130  return;
131 
132 #else // Posix
134  return;
136  ClearSendBuffer();
137  close(m_FileDescriptor);
139  m_Connected = false;
140  return;
141 #endif
142 }
143 
144 int mitk::SerialCommunication::Receive(std::string& answer, unsigned int numberOfBytes, const char *eol)
145 {
146  if (numberOfBytes == 0)
147  return OK;
148  if (m_Connected == false)
149  return ERROR_VALUE;
150 
151 #ifdef WIN32
152  if (m_ComPortHandle == INVALID_HANDLE_VALUE)
153  return ERROR_VALUE;
154 
155  DWORD numberOfBytesRead = 0;
156  char* buffer = new char[numberOfBytes];
157  if (ReadFile(m_ComPortHandle, buffer, numberOfBytes, &numberOfBytesRead, nullptr) != 0)
158  {
159  if (numberOfBytesRead > 0) // data read
160  {
161  answer.assign(buffer, numberOfBytesRead); // copy buffer to answer
162  delete buffer;
163  if (numberOfBytesRead == numberOfBytes)
164  {
165  return OK; // everything was received
166  }
167  else
168  {
169  return ERROR_VALUE; // some data was received, but not as much as expected
170  }
171  }
172  else // error
173  {
174  answer = "";
175  delete buffer;
176  return ERROR_VALUE;
177  }
178  }
179  delete buffer;
180  return OK;
181 
182 #else // Posix
184  return ERROR_VALUE;
185 
186  unsigned long bytesRead = 0;
187  unsigned long bytesLeft = numberOfBytes;
188  auto buffer = new char[numberOfBytes];
189 
190  while ((bytesLeft > 0) && (bytesRead < numberOfBytes))
191  {
192  int num = read(m_FileDescriptor, &buffer[bytesRead], 1); // read one byte
193  if (num == -1) // ERROR_VALUE
194  {
195  if (errno == EAGAIN) // nonblocking, no byte there right now, but maybe next time
196  continue;
197  else
198  break; // ERROR_VALUE, stop trying to read
199  }
200  if (num == 0) // timeout or eof(?)
201  break;
202 
203  bytesLeft -= num; // n is number of chars left to read
204  bytesRead += num; // i is the number of chars read
205 
206  if (eol && *eol == buffer[bytesRead-1]) // end of line char reached
207  break;
208  }
209  if (bytesRead > 0)
210  answer.assign(buffer, bytesRead); // copy buffer to answer
211 
212  delete[] buffer;
213 
214  if ( bytesRead == numberOfBytes || // everything was received
215  (eol && answer.size() > 0 && *eol == answer.at(answer.size()-1)) ) // end of line char reached
216  return OK;
217  else
218  return ERROR_VALUE; // some data was received, but not as much as expected
219 #endif
220 }
221 
222 int mitk::SerialCommunication::Send(const std::string& input, bool block)
223 {
224  //long retval = E2ERR_OPENFAILED;
225  if (input.empty())
226  return OK;
227  if (m_Connected == false)
228  return ERROR_VALUE;
229 
230 #ifdef WIN32
231  if (m_ComPortHandle == INVALID_HANDLE_VALUE)
232  return ERROR_VALUE;
233 
234  DWORD bytesWritten = 0;
235  if (WriteFile(m_ComPortHandle, input.data(), static_cast<DWORD>(input.size()), &bytesWritten, nullptr) == TRUE)
236  return OK;
237  else
238  return ERROR_VALUE;
239 
240 #else // Posix
242  return ERROR_VALUE;
243 
244  long bytesWritten = 0;
245  long bytesLeft = input.size();
246 
247  while (bytesLeft > 0)
248  {
249  bytesWritten = write(m_FileDescriptor, input.data() + bytesWritten, bytesLeft);
250  if (bytesWritten <= 0)
251  return ERROR_VALUE; //return ERROR_VALUE
252  bytesLeft -= bytesWritten;
253  }
254  if (block)
255  {
256  // wait for output to be physically sent
257  if (tcdrain(m_FileDescriptor) == -1)
258  return ERROR_VALUE;
259  }
260  return OK;
261 #endif
262 }
263 
265 {
266 #ifdef WIN32 // Windows implementation
267  return ApplyConfigurationWin();
268 #else // Posix
269  return ApplyConfigurationUnix();
270 #endif
271 }
272 
277 #ifdef WIN32
278 int mitk::SerialCommunication::ApplyConfigurationWin()
279 {
280  if (m_ComPortHandle == INVALID_HANDLE_VALUE)
281  return ERROR_VALUE;
282 
283  DCB controlSettings;
284  if (GetCommState(m_ComPortHandle, &controlSettings) == 0)
285  {
286  return ERROR_VALUE;
287  }
288 
289  std::ostringstream o;
290  o << "baud=" << m_BaudRate << " parity=" << static_cast<char>(m_Parity) << " data=" << m_DataBits << " stop=" << m_StopBits;
291  if (BuildCommDCBA(o.str().c_str(), &controlSettings) == 0) // Build device-control block
292  return ERROR_VALUE;
293 
294  if (m_HardwareHandshake == HardwareHandshakeOn) // Modify hardware handshake values
295  {
296  controlSettings.fDtrControl = DTR_CONTROL_ENABLE;
297  controlSettings.fRtsControl = RTS_CONTROL_ENABLE;
298  controlSettings.fOutxCtsFlow = TRUE;
299  controlSettings.fRtsControl = RTS_CONTROL_HANDSHAKE;
300  }
301  else
302  {
303  controlSettings.fDtrControl = DTR_CONTROL_DISABLE;
304  controlSettings.fRtsControl = RTS_CONTROL_DISABLE;
305  controlSettings.fOutxCtsFlow = FALSE;
306  controlSettings.fRtsControl = RTS_CONTROL_DISABLE;
307  }
308  if (SetCommState(m_ComPortHandle, &controlSettings) == FALSE) // Configure com port
309  return ERROR_VALUE;
310 
311  COMMTIMEOUTS timeouts;
312 
313  timeouts.ReadIntervalTimeout = m_ReceiveTimeout;
314  timeouts.ReadTotalTimeoutMultiplier = 0;
315  timeouts.ReadTotalTimeoutConstant = m_ReceiveTimeout;
316  timeouts.WriteTotalTimeoutMultiplier = 0;
317  timeouts.WriteTotalTimeoutConstant = m_SendTimeout;
318  if (SetCommTimeouts(m_ComPortHandle, &timeouts) == FALSE) // set timeout values
319  return ERROR_VALUE;
320 
321  PurgeComm(m_ComPortHandle, PURGE_TXCLEAR | PURGE_RXCLEAR); // clear read and write buffers
322  return OK;
323 }
324 
325 #else
326 
330 {
332  return ERROR_VALUE;
333 
334  struct termios termIOStructure;
335  if ( tcgetattr(m_FileDescriptor, &termIOStructure) != 0 ) // retrieve parameters from com port
336  return ERROR_VALUE;
337 
338  cfmakeraw(&termIOStructure); // set flags to raw mode
339  termIOStructure.c_cflag |= CLOCAL;
341  { // enable
342  termIOStructure.c_cflag |= CRTSCTS;
343  termIOStructure.c_iflag &= ~(IXON|IXOFF);
344  }
345  else
346  { // disable
347  termIOStructure.c_cflag &= ~CRTSCTS;
348  termIOStructure.c_iflag &= ~(IXON|IXOFF);
349  }
350  termIOStructure.c_cflag &= ~CSIZE; // set number of data bits
351  switch (m_DataBits)
352  {
353  case DataBits7:
354  termIOStructure.c_cflag |= CS7;
355  break;
356  case DataBits8:
357  default:
358  termIOStructure.c_cflag |= CS8;
359  }
360  switch (m_StopBits) // set number of stop bits
361  {
362  case StopBits2:
363  termIOStructure.c_cflag |= CSTOPB;
364  break;
365  case StopBits1:
366  default:
367  termIOStructure.c_cflag &= ~CSTOPB;
368  }
369  switch (m_Parity) // set parity
370  {
371  case Odd:
372  termIOStructure.c_cflag |= (PARENB|PARODD);
373  break;
374  case Even:
375  termIOStructure.c_cflag |= PARENB;
376  termIOStructure.c_cflag &= ~PARODD;
377  // TODO: check if this is intended
378  // FALLTHRU
379  case None:
380  // FALLTHRU
381  default:
382  termIOStructure.c_cflag &= ~PARENB;
383  break;
384  }
385  speed_t baudrate; // set baudrate
386  switch (m_BaudRate)
387  {
388  case BaudRate9600:
389  baudrate = B9600;
390  break;
391  case BaudRate14400:
392  baudrate = B9600; //14400 is not defined for posix, use 9600 instead
393  break;
394  case BaudRate19200:
395  baudrate = B19200;
396  break;
397  case BaudRate38400:
398  baudrate = B38400;
399  break;
400  case BaudRate57600:
401  baudrate = B57600;
402  break;
403  case BaudRate115200:
404  baudrate = B115200;
405  break;
406  case BaudRate230400:
407  baudrate = B230400;
408  break;
409  // the following baud rates do not work for apple
410  #ifndef __APPLE__
411  case BaudRate460800:
412  baudrate = B460800;
413  break;
414  case BaudRate500000:
415  baudrate = B500000;
416  break;
417  case BaudRate576000:
418  baudrate = B576000;
419  break;
420  case BaudRate921600:
421  baudrate = B921600;
422  break;
423  case BaudRate1000000:
424  baudrate = B1000000;
425  break;
426  case BaudRate1152000:
427  baudrate = B1152000;
428  break;
429  //case BaudRate1228739:
430  //baudrate = B1228739;
431  //break;
432  case BaudRate1500000:
433  baudrate = B1500000;
434  break;
435  case BaudRate2000000:
436  baudrate = B2000000;
437  break;
438  case BaudRate2500000:
439  baudrate = B2500000;
440  break;
441  case BaudRate3000000:
442  baudrate = B3000000;
443  break;
444  case BaudRate3500000:
445  baudrate = B3500000;
446  break;
447  case BaudRate4000000:
448  baudrate = B4000000;
449  break;
450  #endif
451  default:
452  MITK_WARN("mitk::SerialCommunication") << "Baud rate not recognized, using default of 9600 Baud.";
453  baudrate = B9600;
454  break;
455  }
456  cfsetispeed(&termIOStructure, baudrate);
457  cfsetospeed(&termIOStructure, baudrate);
458 
459  termIOStructure.c_cc[VMIN] = 0;
460  termIOStructure.c_cc[VTIME] = m_ReceiveTimeout / 100; // timeout in 1/10 sec, not in ms. Rounded down.
461 
462  if (tcsetattr(m_FileDescriptor, TCSANOW, &termIOStructure) == 0)
463  return OK;
464  else
465  return ERROR_VALUE;
466 }
467 
468 #endif
469 
470 
472 {
473 #ifdef WIN32
474  if (m_ComPortHandle == INVALID_HANDLE_VALUE)
475  return;
476  SetCommBreak(m_ComPortHandle);
477  itksys::SystemTools::Delay(ms);
478  ClearCommBreak(m_ComPortHandle);
479  return;
480 
481 #else // Posix
482 
484  return;
485  tcsendbreak(m_FileDescriptor, ms);
486  return;
487 #endif
488 }
489 
491 {
492 #ifdef WIN32
493  if (m_ComPortHandle != INVALID_HANDLE_VALUE)
494  PurgeComm(m_ComPortHandle, PURGE_RXCLEAR);
495 #else // Posix
497  tcflush(m_FileDescriptor, TCIFLUSH);
498 #endif
499 }
500 
502 {
503 #ifdef WIN32
504  if ( m_ComPortHandle != INVALID_HANDLE_VALUE )
505  PurgeComm(m_ComPortHandle, PURGE_TXCLEAR);
506 #else // Posix
508  tcflush(m_FileDescriptor, TCOFLUSH);
509 #endif
510 }
std::string m_DeviceName
device name that is used to connect to the serial interface (will be used if != "") ...
int ApplyConfigurationUnix()
Applies the configuration for Linux.
void ClearReceiveBuffer()
erase the receive buffer of the serial interface
BaudRate m_BaudRate
baud rate of the serial interface connection
int ApplyConfiguration()
configures the serial interface with all parameters
unsigned int m_SendTimeout
timeout for sending data to the serial interface in milliseconds
int Send(const std::string &input, bool block=false)
Send the string input.
void CloseConnection()
Closes the connection.
#define OK
StopBits m_StopBits
number of stop bits per symbol
int Receive(std::string &answer, unsigned int numberOfBytes, const char *eol=nullptr)
Read numberOfBytes characters from the serial interface.
unsigned int m_ReceiveTimeout
timeout for receiving data from the serial interface in milliseconds
#define MITK_WARN
Definition: mitkLogMacros.h:19
HardwareHandshake m_HardwareHandshake
whether to use hardware handshake for the connection
bool IsConnected()
Returns m_Connected.
PortNumber m_PortNumber
port number of the device
void ClearSendBuffer()
erase the send buffer of the serial interface
void SendBreak(unsigned int ms=400)
Send the break signal for ms milliseconds.
DataBits m_DataBits
number of data bits per symbol
#define ERROR_VALUE
int OpenConnection()
Opens connection to the COM port with port number m_PortNumber or the device name m_DeviceName and al...
bool m_Connected
is set to true if a connection currently established
#define INVALID_HANDLE_VALUE