34#include "libserial/SerialPort.h"
46#include <linux/serial.h>
76 const BaudRate& baudRate,
77 const CharacterSize& characterSize,
78 const FlowControl& flowControlType,
79 const Parity& parityType,
80 const StopBits& stopBits,
117 void Open(const std::
string& fileName,
118 const std::ios_base::openmode& openMode,
203 void SetParity(const Parity& parityType) ;
227 void SetVMin(const
short vmin) ;
252 void SetDTR(const
bool dtrState) ;
264 void SetRTS(const
bool rtsState) ;
302 std::vector<std::string> GetAvailableSerialPorts()
const ;
320 void Read(DataBuffer& dataBuffer,
321 size_t numberOfBytes = 0,
322 size_t msTimeout = 0) ;
337 void Read(std::string& dataString,
338 size_t numberOfBytes = 0,
339 size_t msTimeout = 0) ;
350 template <
typename ByteType,
351 typename = std::enable_if_t<(
sizeof(ByteType) == 1)>>
353 size_t msTimeout = 0) ;
369 void ReadLine(std::string& dataString,
370 char lineTerminator =
'\n',
371 size_t msTimeout = 0) ;
377 void Write(
const DataBuffer& dataBuffer) ;
383 void Write(
const std::string& dataString) ;
395 void WriteByte(
unsigned char charBuffer) ;
435 int GetBitRate(
const BaudRate& baudRate)
const ;
440 void SetDefaultLinuxSpecificModes() ;
445 void SetDefaultInputModes() ;
450 void SetDefaultOutputModes() ;
455 void SetDefaultControlModes() ;
460 void SetDefaultLocalModes() ;
465 int mFileDescriptor = -1 ;
471 int mByteArrivalTimeDelta = 1 ;
477 bool mExclusiveAccess =
true;
484 termios mOldPortSettings {} ;
494 const BaudRate& baudRate,
495 const CharacterSize& characterSize,
496 const FlowControl& flowControlType,
497 const Parity& parityType,
498 const StopBits& stopBits,
512 mImpl(std::move(otherSerialPort.mImpl))
519 mImpl = std::move(otherSerialPort.mImpl);
527 const std::ios_base::openmode& openMode,
530 mImpl->
Open(fileName,
544 mImpl->DrainWriteBuffer() ;
550 mImpl->FlushInputBuffer() ;
556 mImpl->FlushOutputBuffer() ;
562 mImpl->FlushIOBuffers() ;
568 return mImpl->IsDataAvailable() ;
574 return mImpl->IsOpen() ;
580 mImpl->SetDefaultSerialPortParameters() ;
586 mImpl->SetBaudRate(baudRate) ;
592 return mImpl->GetBaudRate() ;
598 mImpl->SetCharacterSize(characterSize) ;
604 return mImpl->GetCharacterSize() ;
610 mImpl->SetFlowControl(flowControlType) ;
616 return mImpl->GetFlowControl() ;
622 mImpl->SetParity(parityType) ;
628 return mImpl->GetParity() ;
634 mImpl->SetStopBits(stopBits) ;
640 return mImpl->GetStopBits() ;
646 mImpl->SetVMin(vmin) ;
652 return mImpl->GetVMin() ;
658 mImpl->SetVTime(vtime) ;
664 return mImpl->GetVTime() ;
670 mImpl->SetDTR(dtrState) ;
676 return mImpl->GetDTR() ;
682 mImpl->SetRTS(rtsState) ;
688 return mImpl->GetRTS() ;
694 return mImpl->GetCTS() ;
700 return mImpl->GetDSR() ;
706 return mImpl->GetFileDescriptor() ;
712 return mImpl->GetNumberOfBytesAvailable() ;
716 std::vector<std::string>
717 SerialPort::GetAvailableSerialPorts()
const
719 return mImpl->GetAvailableSerialPorts() ;
725 const size_t numberOfBytes,
726 const size_t msTimeout)
728 mImpl->Read(dataBuffer,
735 const size_t numberOfBytes,
736 const size_t msTimeout)
738 mImpl->Read(dataString,
745 const size_t msTimeout)
747 mImpl->ReadByte(charBuffer,
753 const size_t msTimeout)
755 mImpl->ReadByte(charBuffer,
761 const char lineTerminator,
762 const size_t msTimeout)
764 mImpl->ReadLine(dataString,
772 mImpl->Write(dataBuffer) ;
778 mImpl->Write(dataString) ;
784 mImpl->WriteByte(charBuffer) ;
790 mImpl->WriteByte(charBuffer) ;
796 mImpl->SetSerialPortBlockingStatus(blockingStatus) ;
802 return mImpl->GetSerialPortBlockingStatus() ;
807 const bool lineState)
809 mImpl->SetModemControlLine(modemLine, lineState) ;
815 return mImpl->GetModemControlLine(modemLine) ;
822 const BaudRate& baudRate,
823 const CharacterSize& characterSize,
824 const FlowControl& flowControlType,
825 const Parity& parityType,
826 const StopBits& stopBits,
829 this->
Open(fileName, std::ios_base::in | std::ios_base::out, exclusive) ;
861 const std::ios_base::openmode& openMode,
874 int flags = (O_NOCTTY | O_NONBLOCK) ;
876 if (openMode == (std::ios_base::in | std::ios_base::out))
880 else if (openMode == std::ios_base::in)
884 else if (openMode == std::ios_base::out)
890 throw OpenFailed {
"Invalid or unsupported open mode"} ;
895 mFileDescriptor = call_with_retry(open, fileName.c_str(), flags) ;
897 if (this->mFileDescriptor < 0)
904 mExclusiveAccess = exclusive;
905 if (mExclusiveAccess && call_with_retry(ioctl,
906 this->mFileDescriptor,
909 throw std::runtime_error(std::strerror(errno)) ;
914 if (tcgetattr(this->mFileDescriptor,
915 &mOldPortSettings) < 0)
934 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
947 std::string err_msg {} ;
948 if (tcsetattr(this->mFileDescriptor,
950 &mOldPortSettings) < 0)
952 err_msg = std::strerror(errno) ;
956 if (mExclusiveAccess)
959 if(call_with_retry(ioctl, this->mFileDescriptor, TIOCNXCL) == -1)
962 err_msg += std::strerror(errno);
968 bool is_failed = false ;
969 if (call_with_retry(close, this->mFileDescriptor) < 0)
973 err_msg += std::strerror(errno) ;
977 mFileDescriptor = -1 ;
984 throw std::runtime_error(err_msg) ;
995 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
998 if (call_with_retry(tcdrain, this->mFileDescriptor) < 0)
1000 throw std::runtime_error(std::strerror(errno)) ;
1011 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1014 if (tcflush(this->mFileDescriptor, TCIFLUSH) < 0)
1016 throw std::runtime_error(std::strerror(errno)) ;
1027 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1030 if (tcflush(this->mFileDescriptor, TCOFLUSH) < 0)
1032 throw std::runtime_error(std::strerror(errno)) ;
1043 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1046 if (tcflush(this->mFileDescriptor, TCIOFLUSH) < 0)
1048 throw std::runtime_error(std::strerror(errno)) ;
1056 return (this->mFileDescriptor != -1) ;
1066 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1069 int number_of_bytes_available = 0 ;
1070 bool is_data_available = false ;
1073 const auto ioctl_result = call_with_retry(ioctl,
1074 this->mFileDescriptor,
1076 &number_of_bytes_available) ;
1078 if ((ioctl_result >= 0) and
1079 (number_of_bytes_available > 0))
1081 is_data_available = true ;
1084 return is_data_available ;
1094 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1098 SetDefaultLinuxSpecificModes() ;
1101 SetDefaultInputModes() ;
1102 SetDefaultOutputModes() ;
1103 SetDefaultControlModes() ;
1104 SetDefaultLocalModes() ;
1122 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1126 termios port_settings {} ;
1127 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1129 if (tcgetattr(this->mFileDescriptor,
1130 &port_settings) < 0)
1132 throw std::runtime_error(std::strerror(errno)) ;
1136 if (0 != cfsetspeed(&port_settings,
static_cast<speed_t
>(baudRate)))
1139 throw std::runtime_error(ERR_MSG_INVALID_BAUD_RATE) ;
1143 if (tcsetattr(this->mFileDescriptor,
1145 &port_settings) < 0)
1148 throw std::runtime_error(std::strerror(errno)) ;
1152 mByteArrivalTimeDelta = (BITS_PER_BYTE * MICROSECONDS_PER_SEC) / GetBitRate(baudRate) ;
1162 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1166 termios port_settings {} ;
1167 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1169 if (tcgetattr(this->mFileDescriptor,
1170 &port_settings) < 0)
1172 throw std::runtime_error(std::strerror(errno)) ;
1176 const auto input_baud = cfgetispeed(&port_settings) ;
1177 const auto output_baud = cfgetospeed(&port_settings) ;
1181 if (input_baud != output_baud)
1183 throw std::runtime_error(ERR_MSG_INVALID_BAUD_RATE) ;
1188 return BaudRate(input_baud) ;
1193 SerialPort::Implementation::GetBitRate(
const BaudRate& baudRate)
const
1195 int baud_rate_as_int = 1 ;
1199 case BaudRate::BAUD_50:
1200 baud_rate_as_int = 50 ;
1203 case BaudRate::BAUD_75:
1204 baud_rate_as_int = 75 ;
1207 case BaudRate::BAUD_110:
1208 baud_rate_as_int = 110 ;
1211 case BaudRate::BAUD_134:
1212 baud_rate_as_int = 134 ;
1215 case BaudRate::BAUD_150:
1216 baud_rate_as_int = 150 ;
1219 case BaudRate::BAUD_200:
1220 baud_rate_as_int = 200 ;
1223 case BaudRate::BAUD_300:
1224 baud_rate_as_int = 300 ;
1227 case BaudRate::BAUD_600:
1228 baud_rate_as_int = 600 ;
1231 case BaudRate::BAUD_1200:
1232 baud_rate_as_int = 1200 ;
1235 case BaudRate::BAUD_1800:
1236 baud_rate_as_int = 1800 ;
1239 case BaudRate::BAUD_2400:
1240 baud_rate_as_int = 2400 ;
1243 case BaudRate::BAUD_4800:
1244 baud_rate_as_int = 4800 ;
1247 case BaudRate::BAUD_9600:
1248 baud_rate_as_int = 9600 ;
1251 case BaudRate::BAUD_19200:
1252 baud_rate_as_int = 19200 ;
1255 case BaudRate::BAUD_38400:
1256 baud_rate_as_int = 38400 ;
1259 case BaudRate::BAUD_57600:
1260 baud_rate_as_int = 57600 ;
1263 case BaudRate::BAUD_115200:
1264 baud_rate_as_int = 115200 ;
1267 case BaudRate::BAUD_230400:
1268 baud_rate_as_int = 230400 ;
1273 case BaudRate::BAUD_460800:
1274 baud_rate_as_int = 460800 ;
1277 case BaudRate::BAUD_500000:
1278 baud_rate_as_int = 500000 ;
1281 case BaudRate::BAUD_576000:
1282 baud_rate_as_int = 576000 ;
1285 case BaudRate::BAUD_921600:
1286 baud_rate_as_int = 921600 ;
1289 case BaudRate::BAUD_1000000:
1290 baud_rate_as_int = 1000000 ;
1293 case BaudRate::BAUD_1152000:
1294 baud_rate_as_int = 1152000 ;
1297 case BaudRate::BAUD_1500000:
1298 baud_rate_as_int = 1500000 ;
1301#if __MAX_BAUD > B2000000
1302 case BaudRate::BAUD_2000000:
1303 baud_rate_as_int = 2000000 ;
1306 case BaudRate::BAUD_2500000:
1307 baud_rate_as_int = 2500000 ;
1310 case BaudRate::BAUD_3000000:
1311 baud_rate_as_int = 3000000 ;
1314 case BaudRate::BAUD_3500000:
1315 baud_rate_as_int = 3500000 ;
1318 case BaudRate::BAUD_4000000:
1319 baud_rate_as_int = 4000000 ;
1325 throw std::runtime_error(ERR_MSG_INVALID_BAUD_RATE) ;
1328 return baud_rate_as_int ;
1338 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1342 termios port_settings {} ;
1343 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1345 if (tcgetattr(this->mFileDescriptor,
1346 &port_settings) < 0)
1348 throw std::runtime_error(ERR_MSG_INVALID_CHARACTER_SIZE) ;
1359 if (characterSize == CharacterSize::CHAR_SIZE_8)
1362 port_settings.c_iflag &= ~ISTRIP ;
1366 port_settings.c_iflag |= ISTRIP ;
1371 port_settings.c_cflag &= ~CSIZE ;
1372 port_settings.c_cflag |=
static_cast<tcflag_t
>(characterSize) ;
1375 if (tcsetattr(this->mFileDescriptor,
1377 &port_settings) < 0)
1379 throw std::runtime_error(std::strerror(errno)) ;
1390 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1394 termios port_settings {} ;
1395 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1397 if (tcgetattr(this->mFileDescriptor,
1398 &port_settings) < 0)
1400 throw std::runtime_error(std::strerror(errno)) ;
1405 return CharacterSize(port_settings.c_cflag & CSIZE) ;
1415 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1419 if (tcflush(this->mFileDescriptor,
1422 throw std::runtime_error(std::strerror(errno)) ;
1426 termios port_settings {} ;
1427 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1429 if (tcgetattr(this->mFileDescriptor,
1430 &port_settings) < 0)
1432 throw std::runtime_error(ERR_MSG_INVALID_FLOW_CONTROL) ;
1438 switch(flowControlType)
1440 case FlowControl::FLOW_CONTROL_HARDWARE:
1441 port_settings.c_iflag &= ~ (IXON|IXOFF) ;
1442 port_settings.c_cflag |= CRTSCTS ;
1443 port_settings.c_cc[VSTART] = _POSIX_VDISABLE ;
1444 port_settings.c_cc[VSTOP] = _POSIX_VDISABLE ;
1446 case FlowControl::FLOW_CONTROL_SOFTWARE:
1447 port_settings.c_iflag |= IXON|IXOFF ;
1448 port_settings.c_cflag &= ~CRTSCTS ;
1449 port_settings.c_cc[VSTART] = CTRL_Q ;
1450 port_settings.c_cc[VSTOP] = CTRL_S ;
1452 case FlowControl::FLOW_CONTROL_NONE:
1453 port_settings.c_iflag &= ~(IXON|IXOFF) ;
1454 port_settings.c_cflag &= ~CRTSCTS ;
1457 throw std::invalid_argument(ERR_MSG_INVALID_FLOW_CONTROL) ;
1462 if (tcsetattr(this->mFileDescriptor,
1464 &port_settings) < 0)
1466 throw std::runtime_error(std::strerror(errno)) ;
1477 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1481 termios port_settings {} ;
1482 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1484 if (tcgetattr(this->mFileDescriptor,
1485 &port_settings) < 0)
1487 throw std::runtime_error(std::strerror(errno)) ;
1493 if ((port_settings.c_iflag & IXON) and
1494 (port_settings.c_iflag & IXOFF) and
1495 (CTRL_Q == port_settings.c_cc[VSTART]) and
1496 (CTRL_S == port_settings.c_cc[VSTOP]))
1498 return FlowControl::FLOW_CONTROL_SOFTWARE ;
1501 if (not ((port_settings.c_iflag & IXON) or
1502 (port_settings.c_iflag & IXOFF)))
1504 if (0 != (port_settings.c_cflag & CRTSCTS))
1508 return FlowControl::FLOW_CONTROL_HARDWARE ;
1510 return FlowControl::FLOW_CONTROL_NONE ;
1515 return FlowControl::FLOW_CONTROL_INVALID ;
1525 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1529 termios port_settings {} ;
1530 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1532 if (tcgetattr(this->mFileDescriptor,
1533 &port_settings) < 0)
1535 throw std::runtime_error(std::strerror(errno)) ;
1541 case Parity::PARITY_EVEN:
1542 port_settings.c_cflag |= PARENB ;
1543 port_settings.c_cflag &= ~PARODD ;
1544 port_settings.c_iflag |= INPCK ;
1546 case Parity::PARITY_ODD:
1547 port_settings.c_cflag |= PARENB ;
1548 port_settings.c_cflag |= PARODD ;
1549 port_settings.c_iflag |= INPCK ;
1551 case Parity::PARITY_NONE:
1552 port_settings.c_cflag &= ~PARENB ;
1553 port_settings.c_iflag |= IGNPAR ;
1556 throw std::invalid_argument(ERR_MSG_INVALID_PARITY) ;
1561 if (tcsetattr(this->mFileDescriptor,
1563 &port_settings) < 0)
1565 throw std::runtime_error(std::strerror(errno)) ;
1576 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1580 termios port_settings {} ;
1581 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1583 if (tcgetattr(this->mFileDescriptor,
1584 &port_settings) < 0)
1586 throw std::runtime_error(std::strerror(errno)) ;
1590 if (0 != (port_settings.c_cflag & PARENB))
1593 if (port_settings.c_cflag & PARODD)
1595 return Parity::PARITY_ODD ;
1597 return Parity::PARITY_EVEN ;
1599 return Parity::PARITY_NONE ;
1609 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1613 termios port_settings {} ;
1614 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1616 if (tcgetattr(this->mFileDescriptor,
1617 &port_settings) < 0)
1619 throw std::runtime_error(std::strerror(errno)) ;
1625 case StopBits::STOP_BITS_1:
1626 port_settings.c_cflag &= ~CSTOPB ;
1628 case StopBits::STOP_BITS_2:
1629 port_settings.c_cflag |= CSTOPB ;
1632 throw std::invalid_argument(ERR_MSG_INVALID_STOP_BITS) ;
1637 if (tcsetattr(this->mFileDescriptor,
1639 &port_settings) < 0)
1641 throw std::runtime_error(std::strerror(errno)) ;
1652 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1656 termios port_settings {} ;
1657 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1659 if (tcgetattr(this->mFileDescriptor,
1660 &port_settings) < 0)
1662 throw std::runtime_error(std::strerror(errno)) ;
1667 if (port_settings.c_cflag & CSTOPB)
1669 return StopBits::STOP_BITS_2 ;
1671 return StopBits::STOP_BITS_1 ;
1681 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1684 if (vmin < 0 || vmin > 255)
1686 std::stringstream error_message ;
1687 error_message <<
"Invalid vmin value: " << vmin <<
". " ;
1688 error_message <<
"Vmin must be in the range [0, 255]." ;
1689 throw std::invalid_argument {error_message.str()} ;
1693 termios port_settings {} ;
1694 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1696 if (tcgetattr(this->mFileDescriptor,
1697 &port_settings) < 0)
1699 throw std::runtime_error(std::strerror(errno)) ;
1702 port_settings.c_cc[VMIN] =
static_cast<cc_t
>(vmin) ;
1705 if (tcsetattr(this->mFileDescriptor,
1707 &port_settings) < 0)
1709 throw std::runtime_error(std::strerror(errno)) ;
1720 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1724 termios port_settings {} ;
1725 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1727 if (tcgetattr(this->mFileDescriptor,
1728 &port_settings) < 0)
1730 throw std::runtime_error(std::strerror(errno)) ;
1733 return port_settings.c_cc[VMIN] ;
1743 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1746 if (vtime < 0 || vtime > 255)
1748 std::stringstream error_message ;
1749 error_message <<
"Invalid vtime value: " << vtime <<
". " ;
1750 error_message <<
"Vtime must be in the range [0, 255]." ;
1751 throw std::invalid_argument {error_message.str()} ;
1755 termios port_settings {} ;
1756 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1758 if (tcgetattr(this->mFileDescriptor,
1759 &port_settings) < 0)
1761 throw std::runtime_error(std::strerror(errno)) ;
1764 port_settings.c_cc[VTIME] =
static_cast<cc_t
>(vtime) ;
1767 if (tcsetattr(this->mFileDescriptor,
1769 &port_settings) < 0)
1771 throw std::runtime_error(std::strerror(errno)) ;
1782 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1786 termios port_settings {} ;
1787 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
1789 if (tcgetattr(this->mFileDescriptor,
1790 &port_settings) < 0)
1792 throw std::runtime_error(std::strerror(errno)) ;
1795 return port_settings.c_cc[VTIME] ;
1805 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1819 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1832 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1846 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1859 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1872 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1885 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1888 return this->mFileDescriptor ;
1898 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1901 int number_of_bytes_available = 0 ;
1904 if (call_with_retry(ioctl,
1905 this->mFileDescriptor,
1907 &number_of_bytes_available) < 0)
1909 throw std::runtime_error(std::strerror(errno)) ;
1912 return number_of_bytes_available ;
1917 std::vector<std::string>
1918 SerialPort::Implementation::GetAvailableSerialPorts()
const
1920 constexpr int array_size = 3 ;
1921 constexpr size_t max_port_number = 128 ;
1923 std::string serial_ports[array_size] = {
"/dev/ttyS",
1927 std::vector<std::string> serial_port_names {} ;
1928 for (
const auto& port_prefix: serial_ports)
1930 for (
size_t j = 0 ;j < max_port_number;j++)
1932 const auto file_name = port_prefix + std::to_string(j) ;
1935 const auto file_desc = call_with_retry(open,
1937 O_RDWR | O_NOCTTY | O_NONBLOCK) ;
1941 serial_struct serial_port_info {} ;
1943 if (call_with_retry(ioctl,
1946 &serial_port_info) == -1)
1948 throw std::runtime_error(std::strerror(errno)) ;
1951 serial_port_names.push_back(file_name) ;
1958 return serial_port_names ;
1965 const bool lineState)
1970 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1973 if (modemLine != TIOCM_LE &&
1974 modemLine != TIOCM_DTR &&
1975 modemLine != TIOCM_RTS &&
1976 modemLine != TIOCM_ST &&
1977 modemLine != TIOCM_SR &&
1978 modemLine != TIOCM_CTS &&
1979 modemLine != TIOCM_CAR &&
1980 modemLine != TIOCM_CD &&
1981 modemLine != TIOCM_RNG &&
1982 modemLine != TIOCM_RI &&
1983 modemLine != TIOCM_DSR)
1985 throw std::invalid_argument {ERR_MSG_INVALID_MODEM_LINE} ;
1989 int ioctl_result = -1 ;
1993 int set_line_mask = modemLine ;
1995 ioctl_result = call_with_retry(ioctl,
1996 this->mFileDescriptor,
2002 int reset_line_mask = modemLine ;
2004 ioctl_result = call_with_retry(ioctl,
2005 this->mFileDescriptor,
2011 if (ioctl_result < 0)
2013 throw std::runtime_error(std::strerror(errno)) ;
2024 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2027 if (modemLine != TIOCM_LE &&
2028 modemLine != TIOCM_DTR &&
2029 modemLine != TIOCM_RTS &&
2030 modemLine != TIOCM_ST &&
2031 modemLine != TIOCM_SR &&
2032 modemLine != TIOCM_CTS &&
2033 modemLine != TIOCM_CAR &&
2034 modemLine != TIOCM_CD &&
2035 modemLine != TIOCM_RNG &&
2036 modemLine != TIOCM_RI &&
2037 modemLine != TIOCM_DSR)
2039 throw std::invalid_argument {ERR_MSG_INVALID_MODEM_LINE} ;
2043 int serial_port_state = 0 ;
2046 if (call_with_retry(ioctl,
2047 this->mFileDescriptor,
2049 &serial_port_state) < 0)
2051 throw std::runtime_error(std::strerror(errno)) ;
2054 return (0 != (serial_port_state & modemLine)) ;
2064 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2067 int flags = fcntl(this->mFileDescriptor, F_GETFL, 0) ;
2072 if (fcntl(this->mFileDescriptor,
2074 flags &~ O_NONBLOCK) < 0)
2076 throw std::runtime_error(std::strerror(errno)) ;
2082 if (fcntl(this->mFileDescriptor,
2084 flags | O_NONBLOCK) < 0)
2086 throw std::runtime_error(std::strerror(errno)) ;
2098 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2101 bool blocking_status = false ;
2103 const int flags = fcntl(this->mFileDescriptor, F_GETFL, 0) ;
2107 throw std::runtime_error(std::strerror(errno)) ;
2110 if (flags == (flags | O_NONBLOCK))
2112 blocking_status = true ;
2115 return blocking_status ;
2120 SerialPort::Implementation::SetDefaultLinuxSpecificModes()
2125 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2129 termios port_settings {} ;
2130 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
2132 if (tcgetattr(this->mFileDescriptor,
2133 &port_settings) < 0)
2135 throw std::runtime_error(std::strerror(errno)) ;
2141 port_settings.c_line =
'\0' ;
2145 if (tcsetattr(this->mFileDescriptor,
2147 &port_settings) < 0)
2149 throw std::runtime_error(std::strerror(errno)) ;
2155 SerialPort::Implementation::SetDefaultInputModes()
2160 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2164 termios port_settings {} ;
2165 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
2167 if (tcgetattr(this->mFileDescriptor,
2168 &port_settings) < 0)
2170 throw std::runtime_error(std::strerror(errno)) ;
2174 port_settings.c_iflag = IGNBRK ;
2177 if (tcsetattr(this->mFileDescriptor,
2179 &port_settings) < 0)
2181 throw std::runtime_error(std::strerror(errno)) ;
2187 SerialPort::Implementation::SetDefaultOutputModes()
2192 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2196 termios port_settings {} ;
2197 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
2199 if (tcgetattr(this->mFileDescriptor,
2200 &port_settings) < 0)
2202 throw std::runtime_error(std::strerror(errno)) ;
2205 port_settings.c_oflag = 0 ;
2208 if (tcsetattr(this->mFileDescriptor,
2210 &port_settings) < 0)
2212 throw std::runtime_error(std::strerror(errno)) ;
2218 SerialPort::Implementation::SetDefaultControlModes()
2223 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2227 termios port_settings {} ;
2228 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
2230 if (tcgetattr(this->mFileDescriptor,
2231 &port_settings) < 0)
2233 throw std::runtime_error(std::strerror(errno)) ;
2237 port_settings.c_cflag |= CREAD | CLOCAL ;
2240 if (tcsetattr(this->mFileDescriptor,
2242 &port_settings) < 0)
2244 throw std::runtime_error(std::strerror(errno)) ;
2250 SerialPort::Implementation::SetDefaultLocalModes()
2255 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2259 termios port_settings {} ;
2260 std::memset(&port_settings, 0,
sizeof(port_settings)) ;
2262 if (tcgetattr(this->mFileDescriptor,
2263 &port_settings) < 0)
2265 throw std::runtime_error(std::strerror(errno)) ;
2268 port_settings.c_lflag = 0 ;
2271 if (tcsetattr(this->mFileDescriptor,
2273 &port_settings) < 0)
2275 throw std::runtime_error(std::strerror(errno)) ;
2282 const size_t numberOfBytes,
2283 const size_t msTimeout)
2288 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2291 if ((numberOfBytes == 0) and
2298 size_t number_of_bytes_read = 0 ;
2299 size_t number_of_bytes_remaining = std::max(numberOfBytes,
static_cast<size_t>(1)) ;
2300 size_t maximum_number_of_bytes = dataBuffer.max_size() ;
2303 dataBuffer.clear() ;
2304 dataBuffer.resize(number_of_bytes_remaining) ;
2307 const auto entry_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2309 while (number_of_bytes_remaining > 0)
2312 if (number_of_bytes_remaining >= maximum_number_of_bytes - number_of_bytes_read)
2317 if (numberOfBytes == 0)
2320 dataBuffer.resize(number_of_bytes_read + 1) ;
2323 const auto read_result = call_with_retry(read,
2324 this->mFileDescriptor,
2325 &dataBuffer[number_of_bytes_read],
2326 number_of_bytes_remaining) ;
2328 if (read_result > 0)
2330 number_of_bytes_read += read_result ;
2332 if (numberOfBytes != 0)
2334 number_of_bytes_remaining = numberOfBytes - number_of_bytes_read ;
2336 if (number_of_bytes_remaining == 0)
2342 else if ((read_result <= 0) and
2343 (errno != EWOULDBLOCK))
2345 throw std::runtime_error(std::strerror(errno)) ;
2349 const auto current_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2352 const auto elapsed_time = current_time - entry_time ;
2355 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() ;
2359 if ((msTimeout > 0) and
2360 (
static_cast<size_t>(elapsed_ms) > msTimeout))
2363 dataBuffer.resize(number_of_bytes_read) ;
2369 usleep(mByteArrivalTimeDelta) ;
2376 const size_t numberOfBytes,
2377 const size_t msTimeout)
2382 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2385 if ((numberOfBytes == 0) and
2392 size_t number_of_bytes_read = 0 ;
2393 size_t number_of_bytes_remaining = std::max(numberOfBytes,
static_cast<size_t>(1)) ;
2394 size_t maximum_number_of_bytes = dataString.max_size() ;
2397 dataString.clear() ;
2398 dataString.resize(number_of_bytes_remaining) ;
2401 const auto entry_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2403 while (number_of_bytes_remaining > 0)
2406 if (number_of_bytes_remaining >= maximum_number_of_bytes - number_of_bytes_read)
2411 if (numberOfBytes == 0)
2414 dataString.resize(number_of_bytes_read + 1) ;
2417 const auto read_result = call_with_retry(read,
2418 this->mFileDescriptor,
2419 &dataString[number_of_bytes_read],
2420 number_of_bytes_remaining) ;
2422 if (read_result > 0)
2424 number_of_bytes_read += read_result ;
2426 if (numberOfBytes != 0)
2428 number_of_bytes_remaining = numberOfBytes - number_of_bytes_read ;
2430 if (number_of_bytes_remaining == 0)
2436 else if (read_result <= 0 &&
2437 errno != EWOULDBLOCK)
2439 throw std::runtime_error(std::strerror(errno)) ;
2443 const auto current_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2446 const auto elapsed_time = current_time - entry_time ;
2449 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() ;
2453 if ((msTimeout > 0) and
2454 (
static_cast<size_t>(elapsed_ms) > msTimeout))
2457 dataString.resize(number_of_bytes_read) ;
2463 usleep(mByteArrivalTimeDelta) ;
2467 template <
typename ByteType,
typename >
2471 const size_t msTimeout)
2474 static_assert(
sizeof(ByteType) == 1,
2475 "ByteType must have a size of exactly one byte.") ;
2480 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2484 const auto entry_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2488 ssize_t read_result = 0 ;
2489 while (read_result < 1)
2491 read_result = call_with_retry(read,
2492 this->mFileDescriptor,
2497 if (read_result ==
sizeof(ByteType))
2502 if ((read_result <= 0) and
2503 (errno != EWOULDBLOCK))
2505 throw std::runtime_error(std::strerror(errno)) ;
2509 const auto current_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2512 const auto elapsed_time = current_time - entry_time ;
2515 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() ;
2519 if ((msTimeout > 0) and
2520 (
static_cast<size_t>(elapsed_ms) > msTimeout))
2526 usleep(mByteArrivalTimeDelta) ;
2533 const char lineTerminator,
2534 const size_t msTimeout)
2539 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2543 dataString.clear() ;
2545 unsigned char next_char = 0 ;
2547 size_t elapsed_ms = 0 ;
2549 ssize_t remaining_ms = 0 ;
2551 std::chrono::high_resolution_clock::duration entry_time ;
2552 std::chrono::high_resolution_clock::duration current_time ;
2553 std::chrono::high_resolution_clock::duration elapsed_time ;
2556 entry_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2558 while (next_char != lineTerminator)
2561 current_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2564 elapsed_time = current_time - entry_time ;
2567 elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() ;
2571 if (msTimeout > 0 &&
2572 elapsed_ms > msTimeout)
2577 remaining_ms = msTimeout - elapsed_ms ;
2582 dataString += next_char ;
2593 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2596 size_t number_of_bytes = dataBuffer.size() ;
2599 if (number_of_bytes <= 0)
2605 size_t number_of_bytes_written = 0 ;
2606 size_t number_of_bytes_remaining = number_of_bytes ;
2610 ssize_t write_result = 0 ;
2612 while (number_of_bytes_remaining > 0)
2614 write_result = call_with_retry(write,
2615 this->mFileDescriptor,
2616 &dataBuffer[number_of_bytes_written],
2617 number_of_bytes_remaining) ;
2619 if (write_result >= 0)
2621 number_of_bytes_written += write_result ;
2622 number_of_bytes_remaining = number_of_bytes - number_of_bytes_written ;
2624 if (number_of_bytes_remaining == 0)
2629 else if (write_result <= 0 &&
2630 errno != EWOULDBLOCK)
2632 throw std::runtime_error(std::strerror(errno)) ;
2644 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2647 size_t number_of_bytes = dataString.size() ;
2650 if (number_of_bytes <= 0)
2656 size_t number_of_bytes_written = 0 ;
2657 size_t number_of_bytes_remaining = number_of_bytes ;
2661 ssize_t write_result = 0 ;
2663 while (number_of_bytes_remaining > 0)
2665 write_result = call_with_retry(write,
2666 this->mFileDescriptor,
2667 &dataString[number_of_bytes_written],
2668 number_of_bytes_remaining) ;
2670 if (write_result >= 0)
2672 number_of_bytes_written += write_result ;
2673 number_of_bytes_remaining = number_of_bytes - number_of_bytes_written ;
2675 if (number_of_bytes_remaining == 0)
2680 else if (write_result <= 0 &&
2681 errno != EWOULDBLOCK)
2683 throw std::runtime_error(std::strerror(errno)) ;
2695 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2700 ssize_t write_result = 0 ;
2702 while (write_result <= 0)
2704 write_result = call_with_retry(write,
2705 this->mFileDescriptor,
2709 if (write_result == 1)
2714 if ((write_result <= 0) and
2715 (errno != EWOULDBLOCK))
2717 throw std::runtime_error(std::strerror(errno)) ;
2729 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2734 ssize_t write_result = 0 ;
2736 while (write_result <= 0)
2738 write_result = call_with_retry(write,
2739 this->mFileDescriptor,
2743 if (write_result == 1)
2748 if ((write_result <= 0) and
2749 (errno != EWOULDBLOCK))
2751 throw std::runtime_error(std::strerror(errno)) ;
Exception error thrown when the serial port is already open.
Exception error thrown when the serial port is not open.
Exception error thrown when the serial port could not be opened.
Exception error thrown when data could not be read from the serial port before the timeout had been e...
SerialPort::Implementation is the SerialPort implementation class.
void SetCharacterSize(const CharacterSize &characterSize)
Sets the character size for the serial port.
void SetSerialPortBlockingStatus(bool blockingStatus)
Sets the current state of the serial port blocking status.
~Implementation() noexcept
Default Destructor for a SerialPort object. Closes the serial port associated with mFileDescriptor if...
bool GetDTR()
Gets the serial port DTR line status.
void SetVMin(const short vmin)
Sets the minimum number of characters for non-canonical reads.
void WriteByte(char charBuffer)
Writes a single byte to the serial port.
void ReadLine(std::string &dataString, char lineTerminator='\n', size_t msTimeout=0)
Reads a line of characters from the serial port. The method will timeout if no data is received in th...
void SetDefaultSerialPortParameters()
Sets all serial port paramters to their default values.
void Close()
Closes the serial port. All settings of the serial port will be lost and no more I/O can be performed...
CharacterSize GetCharacterSize() const
Gets the character size being used for serial communication.
int GetFileDescriptor() const
Gets the serial port file descriptor.
bool GetCTS()
Gets the serial port CTS line status.
void DrainWriteBuffer()
Waits until the write buffer is drained and then returns.
void SetVTime(const short vtime)
Sets character buffer timeout for non-canonical reads in deciseconds.
Parity GetParity() const
Gets the parity type for the serial port.
bool GetSerialPortBlockingStatus() const
Gets the current state of the serial port blocking status.
FlowControl GetFlowControl() const
Get the current flow control setting.
bool GetRTS()
Gets the serial port RTS line status.
void SetParity(const Parity &parityType)
Sets the parity type for the serial port.
bool IsOpen() const
Determines if the serial port is open for I/O.
void SetStopBits(const StopBits &stopBits)
Sets the number of stop bits to be used with the serial port.
void Write(const DataBuffer &dataBuffer)
Writes a DataBuffer to the serial port.
StopBits GetStopBits() const
Gets the number of stop bits currently being used by the serial.
void SetBaudRate(const BaudRate &baudRate)
Sets the baud rate for the serial port to the specified value.
bool GetModemControlLine(int modemLine)
Get the current state of the specified modem control line.
void FlushOutputBuffer()
Flushes the serial port output buffer.
void SetRTS(const bool rtsState)
Sets the serial port RTS line status.
BaudRate GetBaudRate() const
Gets the current baud rate for the serial port.
void SetModemControlLine(int modemLine, bool lineState)
Set the specified modem control line to the specified value.
bool IsDataAvailable()
Determines if data is available at the serial port.
void FlushInputBuffer()
Flushes the serial port input buffer.
void SetDTR(const bool dtrState)
Sets the serial port DTR line status.
void Open(const std::string &fileName, const std::ios_base::openmode &openMode, bool exclusive)
Opens the serial port associated with the specified file name and the specified mode.
void ReadByte(ByteType &charBuffer, size_t msTimeout=0)
Reads a single byte from the serial port. If no data is available within the specified number of mill...
short GetVTime() const
Gets the current timeout value for non-canonical reads in deciseconds.
void Read(DataBuffer &dataBuffer, size_t numberOfBytes=0, size_t msTimeout=0)
Reads the specified number of bytes from the serial port. The method will timeout if no data is recei...
void SetFlowControl(const FlowControl &flowControlType)
Sets flow control for the serial port.
void FlushIOBuffers()
Flushes the serial port input and output buffers.
Implementation()=default
Default Constructor.
int GetNumberOfBytesAvailable()
Gets the number of bytes available in the read buffer.
short GetVMin() const
Gets the VMIN value for the device, which represents the minimum number of characters for non-canonic...
bool GetDSR()
Gets the serial port DSR line status.
SerialPort allows an object oriented approach to serial port communication. A serial port object can ...
void SetBaudRate(const BaudRate &baudRate)
Sets the baud rate for the serial port to the specified value.
void SetModemControlLine(int modemLine, bool lineState)
Set the specified modem control line to the specified value.
void SetParity(const Parity &parityType)
Sets the parity type for the serial port.
bool GetCTS()
Get the status of the CTS line.
bool GetSerialPortBlockingStatus() const
Gets the current state of the serial port blocking status.
void WriteByte(char charbuffer)
Writes a single byte to the serial port.
void FlushInputBuffer()
Flushes the serial port input buffer.
void SetFlowControl(const FlowControl &flowControlType)
Sets flow control for the serial port.
void SetDefaultSerialPortParameters()
Sets all serial port paramters to their default values.
SerialPort()
Default Constructor.
FlowControl GetFlowControl() const
Gets the current flow control setting.
CharacterSize GetCharacterSize() const
Gets the character size being used for serial communication.
void Write(const DataBuffer &dataBuffer)
Writes a DataBuffer to the serial port.
bool GetDTR() const
Gets the status of the DTR line.
void SetRTS(const bool rtsState=true)
Set the RTS line to the specified value.
void SetVMin(const short vmin)
Sets the minimum number of characters for non-canonical reads.
virtual ~SerialPort() noexcept
Default Destructor for a SerialPort object. Closes the serial port associated with mFileDescriptor if...
StopBits GetStopBits() const
Gets the number of stop bits currently being used by the serial.
bool GetRTS() const
Get the status of the RTS line.
void Close()
Closes the serial port. All settings of the serial port will be lost and no more I/O can be performed...
SerialPort & operator=(const SerialPort &otherSerialPort)=delete
Copy assignment is disallowed.
void SetCharacterSize(const CharacterSize &characterSize)
Sets the character size for the serial port.
bool IsDataAvailable()
Checks if data is available at the input of the serial port.
BaudRate GetBaudRate() const
Gets the current baud rate for the serial port.
void SetVTime(const short vtime)
Sets character buffer timeout for non-canonical reads in deciseconds.
void FlushIOBuffers()
Flushes the serial port input and output buffers.
void FlushOutputBuffer()
Flushes the serial port output buffer.
Parity GetParity() const
Gets the parity type for the serial port.
short GetVMin() const
Gets the VMIN value for the device, which represents the minimum number of characters for non-canonic...
bool GetModemControlLine(int modemLine)
Get the current state of the specified modem control line.
void Open(const std::string &fileName, const std::ios_base::openmode &openMode=std::ios_base::in|std::ios_base::out, bool exclusive=true)
Opens the serial port associated with the specified file name and the specified mode.
short GetVTime() const
Gets the current timeout value for non-canonical reads in deciseconds.
void Read(DataBuffer &dataBuffer, size_t numberOfBytes=0, size_t msTimeout=0)
Reads the specified number of bytes from the serial port. The method will timeout if no data is recei...
bool GetDSR()
Get the status of the DSR line.
void ReadByte(char &charBuffer, size_t msTimeout=0)
Reads a single byte from the serial port. If no data is available within the specified number of mill...
int GetNumberOfBytesAvailable()
Gets the number of bytes available in the read buffer.
int GetFileDescriptor() const
Gets the serial port file descriptor.
void SetDTR(const bool dtrState=true)
Sets the DTR line to the specified value.
void ReadLine(std::string &dataString, char lineTerminator='\n', size_t msTimeout=0)
Reads a line of characters from the serial port. The method will timeout if no data is received in th...
void DrainWriteBuffer()
Waits until the write buffer is drained and then returns.
bool IsOpen() const
Determines if the serial port is open for I/O.
void SetSerialPortBlockingStatus(bool blockingStatus)
Sets the current state of the serial port blocking status.
void SetStopBits(const StopBits &stopBits)
Sets the number of stop bits to be used with the serial port.