Medical Imaging Interaction Toolkit  2016.11.0
Medical Imaging Interaction Toolkit
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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