diff --git a/Modules/IGT/IGTTrackingDevices/mitkSerialCommunication.cpp b/Modules/IGT/IGTTrackingDevices/mitkSerialCommunication.cpp index b481658a06..44a9b3b1ea 100644 --- a/Modules/IGT/IGTTrackingDevices/mitkSerialCommunication.cpp +++ b/Modules/IGT/IGTTrackingDevices/mitkSerialCommunication.cpp @@ -1,445 +1,451 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSerialCommunication.h" #ifdef WIN32 //#include #include #else // Posix #include #include #include #include #include #include #include #include #define INVALID_HANDLE_VALUE -1 #endif #define OK 1 #define ERROR_VALUE 0 mitk::SerialCommunication::SerialCommunication() : itk::Object(), m_DeviceName(""), m_PortNumber(COM1), m_BaudRate(BaudRate9600), m_DataBits(DataBits8), m_Parity(None), m_StopBits(StopBits1), m_HardwareHandshake(HardwareHandshakeOff), m_ReceiveTimeout(500), m_SendTimeout(500), m_Connected(false) { #ifdef WIN32 // Windows m_ComPortHandle = INVALID_HANDLE_VALUE; #else // Posix m_FileDescriptor = INVALID_HANDLE_VALUE; #endif } mitk::SerialCommunication::~SerialCommunication() { CloseConnection(); } int mitk::SerialCommunication::OpenConnection() { if (m_Connected) return ERROR_VALUE; #ifdef WIN32 std::stringstream ss; if (m_DeviceName.empty()) ss << "\\\\.\\COM" << static_cast(m_PortNumber); // use m_PortNumber else ss << "\\\\.\\" << m_DeviceName; // use m_DeviceName m_ComPortHandle = CreateFile(ss.str().c_str(), GENERIC_READ | GENERIC_WRITE, NULL, /* no sharing */ NULL, /* no security flags */ OPEN_EXISTING, /* open com port, don't create it */ NULL, /* no flags */ NULL); /* no template */ if (m_ComPortHandle == INVALID_HANDLE_VALUE) return ERROR_VALUE; GetCommState(m_ComPortHandle, &m_PreviousDeviceControlBlock); GetCommTimeouts(m_ComPortHandle, &m_PreviousTimeout); GetCommMask(m_ComPortHandle, &m_PreviousMask); if (this->ApplyConfiguration() != OK) // set interface parameters { CloseHandle(m_ComPortHandle); m_ComPortHandle = INVALID_HANDLE_VALUE; return ERROR_VALUE; } m_Connected = true; return OK; #else // Posix std::stringstream ss; if (m_DeviceName.empty()) ss << "/dev/ttyS" << static_cast(m_PortNumber) - 1; // use m_PortNumber, COM1 = ttyS0 else ss << m_DeviceName; // use m_DeviceName //m_FileDescriptor = open(ss.str().c_str(), O_RDWR | O_NONBLOCK | O_NDELAY | O_NOCTTY | O_EXCL); // open device file m_FileDescriptor = open(ss.str().c_str(), O_RDWR|O_NONBLOCK|O_EXCL); // open device file if (m_FileDescriptor < 0) return ERROR_VALUE; fcntl(m_FileDescriptor, F_SETFL, 0); // change to blocking mode tcflush(m_FileDescriptor, TCIOFLUSH); // flush buffers if (this->ApplyConfiguration() != OK) // set interface parameters { close(m_FileDescriptor); m_FileDescriptor = INVALID_HANDLE_VALUE; return ERROR_VALUE; } m_Connected = true; return OK; #endif } void mitk::SerialCommunication::CloseConnection() { #ifdef WIN32 if (m_ComPortHandle == INVALID_HANDLE_VALUE) return; ClearReceiveBuffer(); ClearSendBuffer(); SetCommState(m_ComPortHandle, &m_PreviousDeviceControlBlock); // restore previous settings SetCommTimeouts(m_ComPortHandle, &m_PreviousTimeout); // restore previous timeout values SetCommMask(m_ComPortHandle, m_PreviousMask); // restore previous mask value PurgeComm(m_ComPortHandle, PURGE_TXCLEAR | PURGE_RXCLEAR); // empty buffers CloseHandle(m_ComPortHandle); // close handle m_ComPortHandle = INVALID_HANDLE_VALUE; m_Connected = false; return; #else // Posix if (m_FileDescriptor == INVALID_HANDLE_VALUE) return; ClearReceiveBuffer(); ClearSendBuffer(); close(m_FileDescriptor); m_FileDescriptor = INVALID_HANDLE_VALUE; m_Connected = false; return; #endif } int mitk::SerialCommunication::Receive(std::string& answer, unsigned int numberOfBytes, const char *eol) { if (numberOfBytes == 0) return OK; if (m_Connected == false) return ERROR_VALUE; #ifdef WIN32 if (m_ComPortHandle == INVALID_HANDLE_VALUE) return ERROR_VALUE; DWORD numberOfBytesRead = 0; char* buffer = new char[numberOfBytes]; if (ReadFile(m_ComPortHandle, buffer, numberOfBytes, &numberOfBytesRead, NULL) != 0) { if (numberOfBytesRead > 0) // data read { answer.assign(buffer, numberOfBytesRead); // copy buffer to answer delete buffer; if (numberOfBytesRead == numberOfBytes) { return OK; // everything was received } else { return ERROR_VALUE; // some data was received, but not as much as expected } } else // error { answer = ""; delete buffer; return ERROR_VALUE; } } delete buffer; return OK; #else // Posix if (m_FileDescriptor == INVALID_HANDLE_VALUE) return ERROR_VALUE; unsigned long bytesRead = 0; unsigned long bytesLeft = numberOfBytes; char* buffer = new char[numberOfBytes]; while ((bytesLeft > 0) && (bytesRead < numberOfBytes)) { int num = read(m_FileDescriptor, &buffer[bytesRead], 1); // read one byte if (num == -1) // ERROR_VALUE { if (errno == EAGAIN) // nonblocking, no byte there right now, but maybe next time continue; else break; // ERROR_VALUE, stop trying to read } if (num == 0) // timeout or eof(?) break; bytesLeft -= num; // n is number of chars left to read bytesRead += num; // i is the number of chars read if (eol && *eol == buffer[bytesRead-1]) // end of line char reached break; } if (bytesRead > 0) answer.assign(buffer, bytesRead); // copy buffer to answer delete buffer; if ( bytesRead == numberOfBytes || // everything was received (eol && answer.size() > 0 && *eol == answer.at(answer.size()-1)) ) // end of line char reached return OK; else return ERROR_VALUE; // some data was received, but not as much as expected #endif } -int mitk::SerialCommunication::Send(const std::string& input) +int mitk::SerialCommunication::Send(const std::string& input, bool block) { //long retval = E2ERR_OPENFAILED; if (input.empty()) return OK; if (m_Connected == false) return ERROR_VALUE; #ifdef WIN32 if (m_ComPortHandle == INVALID_HANDLE_VALUE) return ERROR_VALUE; DWORD bytesWritten = 0; if (WriteFile(m_ComPortHandle, input.data(), static_cast(input.size()), &bytesWritten, NULL) == TRUE) return OK; else return GetLastError(); #else // Posix if (m_FileDescriptor == INVALID_HANDLE_VALUE) return ERROR_VALUE; long bytesWritten = 0; long bytesLeft = input.size(); while (bytesLeft > 0) { bytesWritten = write(m_FileDescriptor, input.data() + bytesWritten, bytesLeft); if (bytesWritten <= 0) return ERROR_VALUE; //return ERROR_VALUE bytesLeft -= bytesWritten; } + if (block) + { + // wait for output to be physically sent + if (tcdrain(m_FileDescriptor) == -1) + return ERROR_VALUE; + } return OK; #endif } int mitk::SerialCommunication::ApplyConfiguration() { #ifdef WIN32 // Windows implementation if (m_ComPortHandle == INVALID_HANDLE_VALUE) return ERROR_VALUE; DCB controlSettings; if (GetCommState(m_ComPortHandle, &controlSettings) == 0) { return ERROR_VALUE; } std::ostringstream o; o << "baud=" << m_BaudRate << " parity=" << static_cast(m_Parity) << " data=" << m_DataBits << " stop=" << m_StopBits; if (BuildCommDCBA(o.str().c_str(), &controlSettings) == 0) // Build device-control block return ERROR_VALUE; if (m_HardwareHandshake == HardwareHandshakeOn) // Modify hardware handshake values { controlSettings.fDtrControl = DTR_CONTROL_ENABLE; controlSettings.fRtsControl = RTS_CONTROL_ENABLE; controlSettings.fOutxCtsFlow = TRUE; controlSettings.fRtsControl = RTS_CONTROL_HANDSHAKE; } else { controlSettings.fDtrControl = DTR_CONTROL_DISABLE; controlSettings.fRtsControl = RTS_CONTROL_DISABLE; controlSettings.fOutxCtsFlow = FALSE; controlSettings.fRtsControl = RTS_CONTROL_DISABLE; } if (SetCommState(m_ComPortHandle, &controlSettings) == FALSE) // Configure com port return GetLastError(); COMMTIMEOUTS timeouts; timeouts.ReadIntervalTimeout = m_ReceiveTimeout; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = m_ReceiveTimeout; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = m_SendTimeout; if (SetCommTimeouts(m_ComPortHandle, &timeouts) == FALSE) // set timeout values return GetLastError(); PurgeComm(m_ComPortHandle, PURGE_TXCLEAR | PURGE_RXCLEAR); // clear read and write buffers return OK; #else // Posix if ( m_FileDescriptor == INVALID_HANDLE_VALUE ) return ERROR_VALUE; struct termios termIOStructure; if ( tcgetattr(m_FileDescriptor, &termIOStructure) != 0 ) // retrieve parameters from com port return ERROR_VALUE; cfmakeraw(&termIOStructure); // set flags to raw mode termIOStructure.c_cflag |= CLOCAL; if (m_HardwareHandshake == HardwareHandshakeOn) { // enable termIOStructure.c_cflag |= CRTSCTS; termIOStructure.c_iflag &= ~(IXON|IXOFF); } else { // disable termIOStructure.c_cflag &= ~CRTSCTS; termIOStructure.c_iflag &= ~(IXON|IXOFF); } termIOStructure.c_cflag &= ~CSIZE; // set number of data bits switch (m_DataBits) { case DataBits7: termIOStructure.c_cflag |= CS7; break; case DataBits8: default: termIOStructure.c_cflag |= CS8; } switch (m_StopBits) // set number of stop bits { case StopBits2: termIOStructure.c_cflag |= CSTOPB; break; case StopBits1: default: termIOStructure.c_cflag &= ~CSTOPB; } switch (m_Parity) // set parity { case Odd: termIOStructure.c_cflag |= (PARENB|PARODD); break; case Even: termIOStructure.c_cflag |= PARENB; termIOStructure.c_cflag &= ~PARODD; case None: default: termIOStructure.c_cflag &= ~PARENB; break; } speed_t baudrate; // set baudrate switch (m_BaudRate) { case BaudRate9600: baudrate = B9600; break; case BaudRate14400: baudrate = B9600; //14400 is not defined for posix, use 9600 instead break; case BaudRate19200: baudrate = B19200; break; case BaudRate38400: baudrate = B38400; break; case BaudRate57600: baudrate = B57600; break; case BaudRate115200: baudrate = B115200; break; default: baudrate = B9600; break; } cfsetispeed(&termIOStructure, baudrate); cfsetospeed(&termIOStructure, baudrate); termIOStructure.c_cc[VMIN] = 0; termIOStructure.c_cc[VTIME] = m_ReceiveTimeout / 100; // timeout in 1/10 sec, not in ms. Rounded down. if (tcsetattr(m_FileDescriptor, TCSANOW, &termIOStructure) == 0) return OK; else return ERROR_VALUE; #endif } void mitk::SerialCommunication::SendBreak(unsigned int ms) { #ifdef WIN32 if (m_ComPortHandle == INVALID_HANDLE_VALUE) return; SetCommBreak(m_ComPortHandle); itksys::SystemTools::Delay(ms); ClearCommBreak(m_ComPortHandle); return; #else // Posix if (m_FileDescriptor == INVALID_HANDLE_VALUE) return; tcsendbreak(m_FileDescriptor, ms); return; #endif } void mitk::SerialCommunication::ClearReceiveBuffer() { #ifdef WIN32 if (m_ComPortHandle != INVALID_HANDLE_VALUE) PurgeComm(m_ComPortHandle, PURGE_RXCLEAR); #else // Posix if (m_FileDescriptor != INVALID_HANDLE_VALUE) tcflush(m_FileDescriptor, TCIFLUSH); #endif } void mitk::SerialCommunication::ClearSendBuffer() { #ifdef WIN32 if ( m_ComPortHandle != INVALID_HANDLE_VALUE ) PurgeComm(m_ComPortHandle, PURGE_TXCLEAR); #else // Posix if ( m_FileDescriptor != INVALID_HANDLE_VALUE ) tcflush(m_FileDescriptor, TCOFLUSH); #endif } diff --git a/Modules/IGT/IGTTrackingDevices/mitkSerialCommunication.h b/Modules/IGT/IGTTrackingDevices/mitkSerialCommunication.h index 33cd214901..7a25a1da3e 100644 --- a/Modules/IGT/IGTTrackingDevices/mitkSerialCommunication.h +++ b/Modules/IGT/IGTTrackingDevices/mitkSerialCommunication.h @@ -1,310 +1,313 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKSERIALCOMMUNICATION_H_HEADER_INCLUDED_ #define MITKSERIALCOMMUNICATION_H_HEADER_INCLUDED_ #include #include "mitkCommon.h" #include #include #ifdef WIN32 #include #else // Posix #include #endif namespace mitk { /**Documentation * \brief serial communication interface * * This class allows to send and receive data over a serial communication interface (COM Port). * Define the serial interface that should be used either with SetPortNumber() or SetDeviceName() * Next, define communication parameters: baud rate, number of data bits, number of stop bits, * parity mode, usage of hardware handshake and timeout values (in ms). * Use OpenConnection() to establish a connection on the serial interface with the selected * parameters. While the connection is established, changes to the parameters will not take * effect. You have to close the connection using CloseConnection() and then reopen it with * the new parameters with OpenConnection(). * * \ingroup IGT */ class MitkIGT_EXPORT SerialCommunication : public itk::Object { public: mitkClassMacro(SerialCommunication, itk::Object); itkNewMacro(Self); enum PortNumber { COM1 = 1, COM2 = 2, COM3 = 3, COM4 = 4, COM5 = 5, COM6 = 6, COM7 = 7, COM8 = 8, COM9 = 9, COM10 = 10, COM11 = 11, COM12 = 12, COM13 = 13 }; enum BaudRate { BaudRate9600 = 9600, BaudRate14400 = 14400, BaudRate19200 = 19200, BaudRate38400 = 38400, BaudRate57600 = 57600, BaudRate115200 = 115200 }; enum DataBits { DataBits8 = 8, DataBits7 = 7 }; enum Parity { None = 'N', Odd = 'O', Even = 'E' }; enum StopBits { StopBits1 = 1, StopBits2 = 2 }; enum HardwareHandshake { HardwareHandshakeOn = 1, HardwareHandshakeOff = 0 }; /** * \brief Opens connection to the COM port with port number m_PortNumber * or the device name m_DeviceName and all port settings. * */ int OpenConnection(); /** * \brief Closes the connection * */ void CloseConnection(); /** * \brief Read numberOfBytes characters from the serial interface * * This method tries to read numberOfBytes characters from the serial * interface or until an eol byte is received, whichever comes first. If * The ReceiveTimeout is set to 0, the Receive() method will wait * indefinetly until all characters are received or an eol character is * received. If the ReceiveTimeout is set to another value, it will return * after m_ReceiveTimeout milliseconds (or after all characters are read or * an eol character is received). * * \param[out] answer String that stores the received characters. Note * that this will overwrite the content of answer! * \param[in] numberOfBytes The number of bytes to read. When an eol * character is used this is interpretted as the * maximum number of bytes to read. * \param[in] eol Pointer to an End-of-Line character. If this is NULL * (the default) then no End-of-Line character is used. */ int Receive(std::string& answer, unsigned int numberOfBytes, const char *eol=0); /** * \brief Send the string input * - * This method will send the string input to the serial interface. - * It does not send the string termination character \\0. + * \param[in] input The string to send to the serial interface. The string + * termination character \\0 is not sent. + * \param[in] block If false, the this method will return immediately. If + * true, this method will block until all bytes have been + * physically transmitted over the serial interface. */ - int Send(const std::string& input); + int Send(const std::string& input, bool block = false); /** * \brief Send the break signal for ms milliseconds */ void SendBreak(unsigned int ms = 400); /** * \brief erase the receive buffer of the serial interface */ void ClearReceiveBuffer(); /** * \brief erase the send buffer of the serial interface */ void ClearSendBuffer(); /** * \brief Get the port number of the serial interface * * Returns the port number that will be used in the connection. * The port number is only used if the m_DeviceName is empty (""). */ itkGetConstMacro(PortNumber, PortNumber); /** * \brief Set the port number of the serial interface * * SerialCommunication can either use PortNumber to create serial interface device names * COM1 to COM9 for windows and /dev/ttyS0 to /dev/ttyS8 on linux * (SetPortNumber(COM1) is mapped to /dev/ttyS0 and so on). Alternatively, use SetDeviceName() * to set the device name directly (e.g. "CNCA0" for a com0com virtual com port or * "/dev/ttyUSB0" for a USB to serial adapter on linux. If a device name is set (m_DeviceName != "") * then OpenConnection() will try to open that device. Otherwise, it will build the device * name using the port number */ itkSetMacro(PortNumber, PortNumber); /** * \brief Get the device name * * SerialCommunication can either use m_PortNumber to create serial interface device names * or use m_DeviceName directly. This method allows to set an arbitrary device name * that will be used to connect to the device. Common names are COM1, CNCA0, CNCB9 * on windows and /dev/ttyS0 or /dev/ttyUSB0 on linux. */ itkGetStringMacro(DeviceName); /** * \brief Set the device name * * if the device name is set (!=""), OpenConnection() will try to open the * serial device on that device name. Normally, the serial interfaces are named COM1-COM9 * on windows and /dev/ttyS0 to /dev/ttyS9 on linux, but other names are possible too * (e.g. /dev/ttyUSB0). */ itkSetStringMacro(DeviceName); /** * \brief Get the baud rate of the serial interface */ itkGetConstMacro(BaudRate, BaudRate); /** * \brief Set the baud rate of the serial interface */ itkSetMacro(BaudRate, BaudRate); /** * \brief Get the number of data bits of the serial interface */ itkGetConstMacro(DataBits, DataBits); /** * \brief Set the number of data bits of the serial interface */ itkSetMacro(DataBits, DataBits); /** * \brief Get the parity mode of the serial interface */ itkGetConstMacro(Parity, Parity); /** * \brief Set the parity mode of the serial interface */ itkSetMacro(Parity, Parity); /** * \brief Get number of stop bits of the serial interface */ itkGetConstMacro(StopBits, StopBits); /** * \brief Set number of stop bits of the serial interface */ itkSetMacro(StopBits, StopBits); /** * \brief returns true if hardware handshake should is used */ itkGetConstMacro(HardwareHandshake, HardwareHandshake); /** * \brief Set if hardware handshake should be used */ itkSetMacro(HardwareHandshake, HardwareHandshake); /** * \brief returns the send timeout in milliseconds */ itkGetConstMacro(SendTimeout, unsigned int); /** * \brief set the send timeout in milliseconds */ itkSetMacro(SendTimeout, unsigned int); /** * \brief returns the receive timeout in milliseconds */ itkGetConstMacro(ReceiveTimeout, unsigned int); /** * \brief set the send timeout in milliseconds * * Specify the receive timeout in milliseconds. * Setting this value to 0 will cause the Receive() * method to wait until all expected characters are received. */ itkSetMacro(ReceiveTimeout, unsigned int); protected: SerialCommunication(); virtual ~SerialCommunication(); /** * \brief configures the serial interface with all parameters */ int ApplyConfiguration(); std::string m_DeviceName; ///< device name that is used to connect to the serial interface (will be used if != "") PortNumber m_PortNumber; ///< port number of the device BaudRate m_BaudRate; ///< baud rate of the serial interface connection DataBits m_DataBits; ///< number of data bits per symbol Parity m_Parity; ///< parity mode StopBits m_StopBits; ///< number of stop bits per symbol HardwareHandshake m_HardwareHandshake; ///< whether to use hardware handshake for the connection unsigned int m_ReceiveTimeout; ///< timeout for receiving data from the serial interface in milliseconds unsigned int m_SendTimeout; ///< timeout for sending data to the serial interface in milliseconds bool m_Connected; ///< is set to true if a connection currently established #ifdef WIN32 HANDLE m_ComPortHandle; DWORD m_PreviousMask; COMMTIMEOUTS m_PreviousTimeout; DCB m_PreviousDeviceControlBlock; #else int m_FileDescriptor; #endif }; } // namespace mitk #endif /* MITKSERIALCOMMUNICATION_H_HEADER_INCLUDED_ */