summaryrefslogtreecommitdiff
path: root/src/MTS_IO_SerialConnection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/MTS_IO_SerialConnection.cpp')
-rw-r--r--src/MTS_IO_SerialConnection.cpp545
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);
+ }
+}