diff options
Diffstat (limited to 'src/MTS_IO_SerialConnection.cpp')
-rw-r--r-- | src/MTS_IO_SerialConnection.cpp | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/src/MTS_IO_SerialConnection.cpp b/src/MTS_IO_SerialConnection.cpp new file mode 100644 index 0000000..8f0b8e3 --- /dev/null +++ b/src/MTS_IO_SerialConnection.cpp @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2015 by Multi-Tech Systems + * + * This file is part of libmts_io. + * + * libmts_io is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * libmts_io is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libmts_io. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/*! + \file MTS_IO_SerialConnection.cpp + \brief A brief description + \date Jan 15, 2013 + \author sgodinez + + A more elaborate description +*/ + +#include <mts/MTS_IO_SerialConnection.h> +#include <mts/MTS_Text.h> +#include <mts/MTS_Logger.h> +#include <unistd.h> + +#ifdef WIN32 + +#else +#include <fcntl.h> /* File control definitions */ +#include <errno.h> /* Error number definitions */ +#include <termios.h> /* POSIX terminal control definitions */ +#include <string.h> /* String function definitions */ +#endif + +using namespace MTS::IO; + +SerialConnection::Builder::Builder(const std::string& sPortName) +: m_sPortName(sPortName) +, m_iBaudRate(9600) +, m_eType(RS232) +, m_eParity(OFF) +, m_eDataBits(B8) +, m_eStopBits(B1) +, m_bBuilt(false) +{ + +} + +SerialConnection::Builder& SerialConnection::Builder::type(const ETYPE& eType) { + m_eType = eType; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::rs232() { + m_eType = RS232; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::rs422() { + m_eType = RS422; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::rs485() { + m_eType = RS485; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::parity(const EPARITY& eParity) { + m_eParity = eParity; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::parityOff() { + m_eParity = OFF; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::parityOdd() { + m_eParity = ODD; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::parityEven() { + m_eParity = EVEN; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::dataBits(const EDATABITS& eDataBits) { + m_eDataBits = eDataBits; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::fiveDataBits() { + m_eDataBits = B5; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::sixDataBits() { + m_eDataBits = B6; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::sevenDataBits() { + m_eDataBits = B7; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::eightDataBits() { + m_eDataBits = B8; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::stopBits(const ESTOPBITS& eStopBits) { + m_eStopBits = eStopBits; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::oneStopBit() { + m_eStopBits = B1; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::twoStopBits() { + m_eStopBits = B2; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::baudRate(uint32_t iBaud) { + m_iBaudRate = iBaud; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::useLockFile(bool bEnabled) { + m_bLockFile = bEnabled; + return *this; +} + +SerialConnection::Builder& SerialConnection::Builder::build() { + m_bBuilt = true; + return *this; +} + +const std::string& SerialConnection::Builder::getPortName() const { + return m_sPortName; +} + +const uint32_t& SerialConnection::Builder::getBaudRate() const { + return m_iBaudRate; +} + +SerialConnection::ETYPE SerialConnection::Builder::getType() const { + return m_eType; +} + +SerialConnection::EPARITY SerialConnection::Builder::getParity() const { + return m_eParity; +} + +SerialConnection::EDATABITS SerialConnection::Builder::getDataBits() const { + return m_eDataBits; +} + +SerialConnection::ESTOPBITS SerialConnection::Builder::getStopBits() const { + return m_eStopBits; +} + +bool SerialConnection::Builder::isUsingLockFile() const { + return m_bLockFile; +} + +std::string SerialConnection::Builder::getTypeAsString() const { + std::string sType; + switch(m_eType) { + case RS232: + sType = "RS232"; + break; + case RS422: + sType = "RS422"; + break; + case RS485: + sType = "RS485"; + break; + default: + break; + } + return sType; +} + +SerialConnection::Builder& SerialConnection::Builder::flowControl(const SerialConnection::FLOW_CONTROL& eFlowControl) { + m_eFlowControl = eFlowControl; + return *this; +} + +SerialConnection::FLOW_CONTROL SerialConnection::Builder::getFlowControl() const { + return m_eFlowControl; +} + +bool SerialConnection::Builder::isBuilt() const { + return m_bBuilt; +} + +SerialConnection::SerialConnection(const Builder& oBuilder) +: Connection(oBuilder.getTypeAsString()) +, m_sPortName(oBuilder.getPortName()) +, m_iBaudRate(oBuilder.getBaudRate()) +, m_eType(oBuilder.getType()) +, m_eParity(oBuilder.getParity()) +, m_eDataBits(oBuilder.getDataBits()) +, m_eStopBits(oBuilder.getStopBits()) +, m_eFlowControl(oBuilder.getFlowControl()) +{ + if(!oBuilder.isBuilt()) { + //Throw Exception? + } +#ifdef WIN32 + m_iHandle = INVALID_HANDLE_VALUE; +#else + m_iHandle = -1; +#endif + m_apHandleLock.reset(new Lock()); + if(oBuilder.isUsingLockFile()) { + std::vector<std::string> vParts = MTS::Text::split(m_sPortName, "/"); + m_apLockFile.reset(new LockFile("/var/lock/LCK.." + vParts[vParts.size()-1])); + } +} + +SerialConnection::~SerialConnection() { + doClose(); + m_apHandleLock.reset(); +} + +const std::string& SerialConnection::getPortName() const { + return m_sPortName; +} + +uint32_t SerialConnection::getBaudRate() const { + return m_iBaudRate; +} + +int SerialConnection::getFileDescriptor() { + m_apHandleLock->lock(); + int h = m_iHandle; + m_apHandleLock->unlock(); + return h; +} + +bool SerialConnection::doOpen(const int32_t& timeoutMillis) { + m_apHandleLock->lock(); + + if (m_iHandle != -1) { + m_apHandleLock->unlock(); + return true; + } + if(!m_apLockFile.isNull() && !m_apLockFile->lock(timeoutMillis)) { + printError("SERIAL| Failed to obtain file lock"); + m_apHandleLock->unlock(); + return false; + } + +#ifdef WIN32 + +#else + m_iHandle = ::open(m_sPortName.c_str(), O_RDWR | O_NOCTTY ); + if (m_iHandle == -1) { + printWarning("SERIAL| Failed to open port [%s] [%d]", m_sPortName.c_str(), errno); + if(!m_apLockFile.isNull()) { + m_apLockFile->unlock(); + } + m_apHandleLock->unlock(); + return false; + } + + int result = tcgetattr(m_iHandle, &m_stPrevOptions); + if (result == -1) { + printWarning("SERIAL| Failed to get attributes on port [%s] [%d]", m_sPortName.c_str(), errno); + } + + termios options = m_stPrevOptions; + + //Set to Raw Mode + cfmakeraw(&options); + + //Set the Baud Rate + speed_t speed = B9600; + switch (m_iBaudRate) { + case 300: + speed = B300; + break; + case 600: + speed = B600; + break; + case 1200: + speed = B1200; + break; + case 2400: + speed = B2400; + break; + case 4800: + speed = B4800; + break; + case 9600: + speed = B9600; + break; + case 19200: + speed = B19200; + break; + case 38400: + speed = B38400; + break; + case 57600: + speed = B57600; + break; + case 115200: + speed = B115200; + break; + case 230400: + speed = B230400; + break; + case 460800: + speed = B460800; + break; + case 921600: + speed = B921600; + break; + default: + printWarning("SERIAL| Baud Rate [%d] not supported. Defaulting to 9600.", m_iBaudRate); + m_iBaudRate = 9600; + speed = B9600; + break; + } + + result = cfsetispeed(&options, speed); + if (result == -1) { + printWarning("SERIAL| Failed to set input speed of [%d] on port [%s] [%d]", m_iBaudRate, m_sPortName.c_str(), errno); + goto CLEANUP; + } + result = cfsetospeed(&options, speed); + if (result == -1) { + printWarning("SERIAL| Failed to set output speed of [%d] on port [%s] [%d]", m_iBaudRate, m_sPortName.c_str(), errno); + goto CLEANUP; + } + + //Set Parity, Stop Bits, and Data Length + switch(m_eParity) { + case ODD: + options.c_cflag |= ( PARODD | PARENB ); + break; + + case EVEN: + options.c_cflag |= PARENB; + break; + + case OFF: + default: + options.c_cflag &= ~PARENB; + break; + } + + options.c_cflag &= ~CSIZE; + switch(m_eDataBits) { + case B5: + options.c_cflag |= CS5; + break; + + case B6: + options.c_cflag |= CS6; + break; + + case B7: + options.c_cflag |= CS7; + break; + + case B8: + default: + options.c_cflag |= CS8; + break; + } + + switch(m_eStopBits) { + case B2: + options.c_cflag &= CSTOPB; + break; + + case B1: + default: + options.c_cflag &= ~CSTOPB; + break; + } + + switch (m_eFlowControl) { + case RTS_CTS: + options.c_cflag &= CRTSCTS; + break; + case NONE: + default: + options.c_cflag &= ~(IXON | IXOFF | IXANY); + break; + } + + //Set Control Modes + options.c_cc[VMIN] = 0; + options.c_cc[VTIME] = 2; // tenths of seconds + + result = tcsetattr(m_iHandle, TCSANOW, &options); + if (result == -1) { + printWarning("SERIAL| Failed to set configurations on port [%s] [%d]", m_sPortName.c_str(), errno); + goto CLEANUP; + } + result = tcflush(m_iHandle, TCIOFLUSH); + if (result == -1) { + printWarning("SERIAL| Failed to flush port [%s] [%d]", m_sPortName.c_str(), errno); + goto CLEANUP; + } +#endif + m_apHandleLock->unlock(); + printInfo("SERIAL| %s connection opened on %s @ %d", getName().c_str(), m_sPortName.c_str(), m_iBaudRate); + return true; +CLEANUP: + cleanup(); + m_apHandleLock->unlock(); + return false; +} + +void SerialConnection::doClose() { + m_apHandleLock->lock(); + cleanup(); + m_apHandleLock->unlock(); +} + +int SerialConnection::doRead(char* pBuffer, const uint32_t& iSize, int32_t& timeoutMillis) { + int32_t result = -1; +#ifdef WIN32 + +#else + + int h = getFileDescriptor(); + + if (h == -1) { + printError("SERIAL| bad handle on port %s", m_sPortName.c_str()); + return result; + } + + if(timeoutMillis < 0) { + result = ::read(h, pBuffer, iSize); + } else { + fd_set readfs; + struct timeval stTimeout; + stTimeout.tv_usec = ((timeoutMillis%1000) * 1000); + stTimeout.tv_sec = timeoutMillis/1000; + + FD_ZERO(&readfs); + FD_SET(h, &readfs); + int res = select(h + 1, &readfs, NULL, NULL, &stTimeout); + if(res == -1) { + result = -1; + } else if (res == 0) { + //Timeout Occured + //printTrace("SERIAL| read timed out on port [%s]", m_sPortName.c_str()); + result = 0; + } else { + if (FD_ISSET(h, &readfs)) { + result = ::read(h, pBuffer, iSize); + } else { + //socket closed? + result = -1; + } + } + timeoutMillis = (stTimeout.tv_usec / 1000) + (stTimeout.tv_sec * 1000); + } +#endif + if (result < 0) { + printWarning("SERIAL| failed to read from %s [%d]", m_sPortName.c_str(), errno); + } + return result; +} + +int SerialConnection::doWrite(const char* pBuffer, const uint32_t& iSize, int32_t& timeoutMillis) { + int32_t result = -1; +#ifdef WIN32 + +#else + + int h = getFileDescriptor(); + + if (h == -1) { + printError("SERIAL| bad handle on port %s", m_sPortName.c_str()); + return result; + } + + if(timeoutMillis < 0) { + result = ::write(h, pBuffer, iSize); + } else { + //No difference between timeout and no timeout + result = ::write(h, pBuffer, iSize); + } + +#endif + if (result < 0) { + printWarning("SERIAL| failed to write to %s [%d]", m_sPortName.c_str(), errno); + } + return result; +} + +void SerialConnection::cleanup() { +#ifdef WIN32 + CloseHandle(m_iHandle); + m_iHandle = INVALID_HANDLE_VALUE; +#else + if(m_iHandle >= 0) { + int result = tcsetattr(m_iHandle, TCSANOW, &m_stPrevOptions); + if (result == -1) { + printWarning("SERIAL| Failed to revert configurations on port [%s] [%d]", m_sPortName.c_str(), errno); + } + ::close(m_iHandle); + m_iHandle = -1; + } +#endif + if(!m_apLockFile.isNull()) { + m_apLockFile->unlock(); + } +} + +void SerialConnection::drain() { + int result = tcdrain(m_iHandle); + if (result == -1) { + printWarning("SERIAL| Failed to drain port [%s] [%d]", m_sPortName.c_str(), errno); + } +} + +void SerialConnection::flush() { + int result = tcflush(m_iHandle, TCIOFLUSH); + if (result == -1) { + printWarning("SERIAL| Failed to flush port [%s] [%d]", m_sPortName.c_str(), errno); + } +} |