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