LibSerial 1.0.0
LibSerial provides a convenient, object oriented approach to accessing serial ports on POSIX systems.
Loading...
Searching...
No Matches
SerialPort.cpp
1/******************************************************************************
2 * @file SerialPort.cpp *
3 * @copyright (C) 2004-2018 LibSerial Development Team. All rights reserved. *
4 * crayzeewulf@gmail.com *
5 * *
6 * Redistribution and use in source and binary forms, with or without *
7 * modification, are permitted provided that the following conditions *
8 * are met: *
9 * *
10 * 1. Redistributions of source code must retain the above copyright *
11 * notice, this list of conditions and the following disclaimer. *
12 * 2. Redistributions in binary form must reproduce the above copyright *
13 * notice, this list of conditions and the following disclaimer in *
14 * the documentation and/or other materials provided with the *
15 * distribution. *
16 * 3. Neither the name PX4 nor the names of its contributors may be *
17 * used to endorse or promote products derived from this software *
18 * without specific prior written permission. *
19 * *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
24 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS *
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED *
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
31 * POSSIBILITY OF SUCH DAMAGE. *
32 *****************************************************************************/
33
34#include "libserial/SerialPort.h"
35
36#include <chrono>
37#include <cstdlib>
38#include <cstring>
39#include <fcntl.h>
40#include <sstream>
41#include <sys/ioctl.h>
42#include <type_traits>
43#include <unistd.h>
44
45#ifdef __linux__
46#include <linux/serial.h>
47#endif
48
49namespace LibSerial
50{
55 {
56 public:
60 Implementation() = default ;
61
74 Implementation(const std::string& fileName,
75 const BaudRate& baudRate,
76 const CharacterSize& characterSize,
77 const FlowControl& flowControlType,
78 const Parity& parityType,
79 const StopBits& stopBits) ;
80
85 ~Implementation() noexcept ;
86
90 Implementation(const Implementation& otherImplementation) = delete ;
91
95 Implementation(const Implementation&& otherImplementation) = delete ;
96
100 Implementation& operator=(const Implementation& otherImplementation) = delete ;
101
105 Implementation& operator=(const Implementation&& otherImplementation) = delete ;
106
114 void Open(const std::string& fileName,
115 const std::ios_base::openmode& openMode) ;
116
121 void Close() ;
122
126 void DrainWriteBuffer() ;
127
131 void FlushInputBuffer() ;
132
136 void FlushOutputBuffer() ;
137
141 void FlushIOBuffers() ;
142
146 bool IsDataAvailable() ;
147
152 bool IsOpen() const ;
153
158
163 void SetBaudRate(const BaudRate& baudRate) ;
164
169 BaudRate GetBaudRate() const ;
170
175 void SetCharacterSize(const CharacterSize& characterSize) ;
176
181 CharacterSize GetCharacterSize() const ;
182
187 void SetFlowControl(const FlowControl& flowControlType) ;
188
193 FlowControl GetFlowControl() const ;
194
199 void SetParity(const Parity& parityType) ;
200
205 Parity GetParity() const ;
206
211 void SetStopBits(const StopBits& stopBits) ;
212
217 StopBits GetStopBits() const ;
218
223 void SetVMin(const short vmin) ;
224
230 short GetVMin() const ;
231
236 void SetVTime(const short vtime) ;
237
242 short GetVTime() const ;
243
248 void SetDTR(const bool dtrState) ;
249
254 bool GetDTR() ;
255
260 void SetRTS(const bool rtsState) ;
261
266 bool GetRTS() ;
267
272 bool GetCTS() ;
273
278 bool GetDSR() ;
279
284 int GetFileDescriptor() const ;
285
291
292#ifdef __linux__
298 std::vector<std::string> GetAvailableSerialPorts() const ;
299#endif
300
316 void Read(DataBuffer& dataBuffer,
317 size_t numberOfBytes = 0,
318 size_t msTimeout = 0) ;
319
333 void Read(std::string& dataString,
334 size_t numberOfBytes = 0,
335 size_t msTimeout = 0) ;
336
346 template <typename ByteType,
347 typename = std::enable_if_t<(sizeof(ByteType) == 1)>>
348 void ReadByte(ByteType& charBuffer,
349 size_t msTimeout = 0) ;
350
365 void ReadLine(std::string& dataString,
366 char lineTerminator = '\n',
367 size_t msTimeout = 0) ;
368
373 void Write(const DataBuffer& dataBuffer) ;
374
379 void Write(const std::string& dataString) ;
380
385 void WriteByte(char charBuffer) ;
386
391 void WriteByte(unsigned char charBuffer) ;
392
398 void SetSerialPortBlockingStatus(bool blockingStatus) ;
399
404 bool GetSerialPortBlockingStatus() const ;
405
413 void SetModemControlLine(int modemLine,
414 bool lineState) ;
415
423 bool GetModemControlLine(int modemLine) ;
424
425 private:
426
431 int GetBitRate(const BaudRate& baudRate) const ;
432
436 void SetDefaultLinuxSpecificModes() ;
437
441 void SetDefaultInputModes() ;
442
446 void SetDefaultOutputModes() ;
447
451 void SetDefaultControlModes() ;
452
456 void SetDefaultLocalModes() ;
457
461 int mFileDescriptor = -1 ;
462
467 int mByteArrivalTimeDelta = 1 ;
468
474 termios mOldPortSettings {} ;
475 } ;
476
478 : mImpl(new Implementation())
479 {
480 /* Empty */
481 }
482
483 SerialPort::SerialPort(const std::string& fileName,
484 const BaudRate& baudRate,
485 const CharacterSize& characterSize,
486 const FlowControl& flowControlType,
487 const Parity& parityType,
488 const StopBits& stopBits)
489 : mImpl(new Implementation(fileName,
490 baudRate,
491 characterSize,
492 flowControlType,
493 parityType,
494 stopBits))
495 {
496 /* Empty */
497 }
498
499 SerialPort::SerialPort(SerialPort&& otherSerialPort) :
500 mImpl(std::move(otherSerialPort.mImpl))
501 {
502 // empty
503 }
504
506 {
507 mImpl = std::move(otherSerialPort.mImpl);
508 return *this;
509 }
510
511 SerialPort::~SerialPort() noexcept = default ;
512
513 void
514 SerialPort::Open(const std::string& fileName,
515 const std::ios_base::openmode& openMode)
516 {
517 mImpl->Open(fileName,
518 openMode) ;
519 }
520
521 void
523 {
524 mImpl->Close() ;
525 }
526
527 void
529 {
530 mImpl->DrainWriteBuffer() ;
531 }
532
533 void
535 {
536 mImpl->FlushInputBuffer() ;
537 }
538
539 void
541 {
542 mImpl->FlushOutputBuffer() ;
543 }
544
545 void
547 {
548 mImpl->FlushIOBuffers() ;
549 }
550
551 bool
553 {
554 return mImpl->IsDataAvailable() ;
555 }
556
557 bool
559 {
560 return mImpl->IsOpen() ;
561 }
562
563 void
565 {
566 mImpl->SetDefaultSerialPortParameters() ;
567 }
568
569 void
570 SerialPort::SetBaudRate(const BaudRate& baudRate)
571 {
572 mImpl->SetBaudRate(baudRate) ;
573 }
574
575 BaudRate
577 {
578 return mImpl->GetBaudRate() ;
579 }
580
581 void
582 SerialPort::SetCharacterSize(const CharacterSize& characterSize)
583 {
584 mImpl->SetCharacterSize(characterSize) ;
585 }
586
587 CharacterSize
589 {
590 return mImpl->GetCharacterSize() ;
591 }
592
593 void
594 SerialPort::SetFlowControl(const FlowControl& flowControlType)
595 {
596 mImpl->SetFlowControl(flowControlType) ;
597 }
598
599 FlowControl
601 {
602 return mImpl->GetFlowControl() ;
603 }
604
605 void
606 SerialPort::SetParity(const Parity& parityType)
607 {
608 mImpl->SetParity(parityType) ;
609 }
610
611 Parity
613 {
614 return mImpl->GetParity() ;
615 }
616
617 void
618 SerialPort::SetStopBits(const StopBits& stopBits)
619 {
620 mImpl->SetStopBits(stopBits) ;
621 }
622
623 StopBits
625 {
626 return mImpl->GetStopBits() ;
627 }
628
629 void
630 SerialPort::SetVMin(const short vmin)
631 {
632 mImpl->SetVMin(vmin) ;
633 }
634
635 short
637 {
638 return mImpl->GetVMin() ;
639 }
640
641 void
642 SerialPort::SetVTime(const short vtime)
643 {
644 mImpl->SetVTime(vtime) ;
645 }
646
647 short
649 {
650 return mImpl->GetVTime() ;
651 }
652
653 void
654 SerialPort::SetDTR(const bool dtrState)
655 {
656 mImpl->SetDTR(dtrState) ;
657 }
658
659 bool
661 {
662 return mImpl->GetDTR() ;
663 }
664
665 void
666 SerialPort::SetRTS(const bool rtsState)
667 {
668 mImpl->SetRTS(rtsState) ;
669 }
670
671 bool
673 {
674 return mImpl->GetRTS() ;
675 }
676
677 bool
679 {
680 return mImpl->GetCTS() ;
681 }
682
683 bool
685 {
686 return mImpl->GetDSR() ;
687 }
688
689 int
691 {
692 return mImpl->GetFileDescriptor() ;
693 }
694
695 int
697 {
698 return mImpl->GetNumberOfBytesAvailable() ;
699 }
700
701#ifdef __linux__
702 std::vector<std::string>
703 SerialPort::GetAvailableSerialPorts() const
704 {
705 return mImpl->GetAvailableSerialPorts() ;
706 }
707#endif
708
709 void
710 SerialPort::Read(DataBuffer& dataBuffer,
711 const size_t numberOfBytes,
712 const size_t msTimeout)
713 {
714 mImpl->Read(dataBuffer,
715 numberOfBytes,
716 msTimeout) ;
717 }
718
719 void
720 SerialPort::Read(std::string& dataString,
721 const size_t numberOfBytes,
722 const size_t msTimeout)
723 {
724 mImpl->Read(dataString,
725 numberOfBytes,
726 msTimeout) ;
727 }
728
729 void
730 SerialPort::ReadByte(char& charBuffer,
731 const size_t msTimeout)
732 {
733 mImpl->ReadByte(charBuffer,
734 msTimeout) ;
735 }
736
737 void
738 SerialPort::ReadByte(unsigned char& charBuffer,
739 const size_t msTimeout)
740 {
741 mImpl->ReadByte(charBuffer,
742 msTimeout) ;
743 }
744
745 void
746 SerialPort::ReadLine(std::string& dataString,
747 const char lineTerminator,
748 const size_t msTimeout)
749 {
750 mImpl->ReadLine(dataString,
751 lineTerminator,
752 msTimeout) ;
753 }
754
755 void
756 SerialPort::Write(const DataBuffer& dataBuffer)
757 {
758 mImpl->Write(dataBuffer) ;
759 }
760
761 void
762 SerialPort::Write(const std::string& dataString)
763 {
764 mImpl->Write(dataString) ;
765 }
766
767 void
768 SerialPort::WriteByte(const char charBuffer)
769 {
770 mImpl->WriteByte(charBuffer) ;
771 }
772
773 void
774 SerialPort::WriteByte(const unsigned char charBuffer)
775 {
776 mImpl->WriteByte(charBuffer) ;
777 }
778
779 void
781 {
782 mImpl->SetSerialPortBlockingStatus(blockingStatus) ;
783 }
784
785 bool
787 {
788 return mImpl->GetSerialPortBlockingStatus() ;
789 }
790
791 void
793 const bool lineState)
794 {
795 mImpl->SetModemControlLine(modemLine, lineState) ;
796 }
797
798 bool
800 {
801 return mImpl->GetModemControlLine(modemLine) ;
802 }
803
806 inline
808 const BaudRate& baudRate,
809 const CharacterSize& characterSize,
810 const FlowControl& flowControlType,
811 const Parity& parityType,
812 const StopBits& stopBits)
813 {
814 this->Open(fileName, std::ios_base::in | std::ios_base::out) ;
815 this->SetBaudRate(baudRate) ;
816 this->SetCharacterSize(characterSize) ;
817 this->SetFlowControl(flowControlType) ;
818 this->SetParity(parityType) ;
819 this->SetStopBits(stopBits) ;
820 }
821
822 inline
824 try
825 {
826 // Close the serial port if it is open.
827 if (this->IsOpen())
828 {
829 this->Close() ;
830 }
831 }
832 catch(...)
833 {
834 //
835 // :IMPORTANT: We do not let any exceptions escape the destructor.
836 // (see https://isocpp.org/wiki/faq/exceptions#dtors-shouldnt-throw)
837 //
838 // :TODO: Once we add logging to LibSerial, we should issue a warning
839 // if we reach here.
840 //
841 }
842
843 inline
844 void
845 SerialPort::Implementation::Open(const std::string& fileName,
846 const std::ios_base::openmode& openMode)
847 {
848 // Throw an exception if the port is already open.
849 if (this->IsOpen())
850 {
851 throw AlreadyOpen(ERR_MSG_PORT_ALREADY_OPEN) ;
852 }
853
854 // We only allow three different combinations of ios_base::openmode so
855 // we can use a switch here to construct the flags to be used with the
856 // open() system call. Since we are dealing with the serial port we
857 // need to use the O_NOCTTY option.
858 int flags = (O_NOCTTY | O_NONBLOCK) ; // NOLINT (hicpp-signed-bitwise)
859
860 if (openMode == (std::ios_base::in | std::ios_base::out))
861 {
862 flags |= O_RDWR ;
863 }
864 else if (openMode == std::ios_base::in)
865 {
866 flags |= O_RDONLY ;
867 }
868 else if (openMode == std::ios_base::out)
869 {
870 flags |= O_WRONLY ;
871 }
872 else
873 {
874 throw OpenFailed {"Invalid or unsupported open mode"} ;
875 }
876
877 // Try to open the serial port.
878 // NOLINTNEXTLINE (cppcoreguidelines-pro-type-vararg)
879 mFileDescriptor = call_with_retry(open, fileName.c_str(), flags) ;
880
881 if (this->mFileDescriptor < 0)
882 {
883 throw OpenFailed(std::strerror(errno)) ;
884 }
885
886 // Set the serial port to exclusive access to this process.
887 // NOLINTNEXTLINE (cppcoreguidelines-pro-type-vararg)
888 if (call_with_retry(ioctl,
889 this->mFileDescriptor,
890 TIOCEXCL) == -1)
891 {
892 throw std::runtime_error(std::strerror(errno)) ;
893 }
894
895 // Save the current settings of the serial port so they can be
896 // restored when the serial port is closed.
897 if (tcgetattr(this->mFileDescriptor,
898 &mOldPortSettings) < 0)
899 {
900 throw OpenFailed(std::strerror(errno)) ;
901 }
902
903 // Set up the default configuration for the serial port.
905
906 // Flush the input and output buffers associated with the port.
907 this->FlushIOBuffers() ;
908 }
909
910 inline
911 void
913 {
914 // Throw an exception if the serial port is not open.
915 if (not this->IsOpen())
916 {
917 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
918 }
919
920 // Restore the old settings of the port.
921 //
922 // :IMPORTANT: If there is an error while attempting to restore the old
923 // settings, do not throw an exception here and attempt to close the
924 // serial port anyways. See issue #135 for the reason for this. The
925 // serial port device may have been removed when this method is called.
926 // In such a case we will not be able to restore the old settings. But
927 // we should still close the serial port file descriptor. Otherwise,
928 // the user has no way to cleanly recover from this state.
929 //
930 std::string err_msg {} ;
931 if (tcsetattr(this->mFileDescriptor,
932 TCSANOW,
933 &mOldPortSettings) < 0)
934 {
935 err_msg = std::strerror(errno) ;
936 }
937
938 // Otherwise, close the serial port and set the file descriptor
939 // to an invalid value.
940 bool is_failed = false ;
941 if (call_with_retry(close, this->mFileDescriptor) < 0)
942 {
943 is_failed = true ;
944 err_msg += ", " ;
945 err_msg += std::strerror(errno) ;
946 }
947
948 // Set the file descriptor to an invalid value, -1.
949 mFileDescriptor = -1 ;
950
951 //
952 // Throw an exception if close() failed
953 //
954 if (is_failed)
955 {
956 throw std::runtime_error(err_msg) ;
957 }
958 }
959
960 inline
961 void
963 {
964 // Throw an exception if the serial port is not open.
965 if (not this->IsOpen())
966 {
967 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
968 }
969
970 if (call_with_retry(tcdrain, this->mFileDescriptor) < 0)
971 {
972 throw std::runtime_error(std::strerror(errno)) ;
973 }
974 }
975
976 inline
977 void
979 {
980 // Throw an exception if the serial port is not open.
981 if (not this->IsOpen())
982 {
983 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
984 }
985
986 if (tcflush(this->mFileDescriptor, TCIFLUSH) < 0)
987 {
988 throw std::runtime_error(std::strerror(errno)) ;
989 }
990 }
991
992 inline
993 void
995 {
996 // Throw an exception if the serial port is not open.
997 if (not this->IsOpen())
998 {
999 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1000 }
1001
1002 if (tcflush(this->mFileDescriptor, TCOFLUSH) < 0)
1003 {
1004 throw std::runtime_error(std::strerror(errno)) ;
1005 }
1006 }
1007
1008 inline
1009 void
1011 {
1012 // Throw an exception if the serial port is not open.
1013 if (not this->IsOpen())
1014 {
1015 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1016 }
1017
1018 if (tcflush(this->mFileDescriptor, TCIOFLUSH) < 0)
1019 {
1020 throw std::runtime_error(std::strerror(errno)) ;
1021 }
1022 }
1023
1024 inline
1025 bool
1027 {
1028 return (this->mFileDescriptor != -1) ;
1029 }
1030
1031 inline
1032 bool
1034 {
1035 // Throw an exception if the serial port is not open.
1036 if (not this->IsOpen())
1037 {
1038 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1039 }
1040
1041 int number_of_bytes_available = 0 ;
1042 bool is_data_available = false ;
1043
1044 // NOLINTNEXTLINE (cppcoreguidelines-pro-type-vararg)
1045 const auto ioctl_result = call_with_retry(ioctl,
1046 this->mFileDescriptor,
1047 FIONREAD,
1048 &number_of_bytes_available) ;
1049
1050 if ((ioctl_result >= 0) and
1051 (number_of_bytes_available > 0))
1052 {
1053 is_data_available = true ;
1054 }
1055
1056 return is_data_available ;
1057 }
1058
1059 inline
1060 void
1062 {
1063 // Make sure that the serial port is open.
1064 if (not this->IsOpen())
1065 {
1066 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1067 }
1068
1069 #ifdef __linux__
1070 SetDefaultLinuxSpecificModes() ;
1071 #endif
1072
1073 SetDefaultInputModes() ;
1074 SetDefaultOutputModes() ;
1075 SetDefaultControlModes() ;
1076 SetDefaultLocalModes() ;
1077
1078 SetBaudRate(BaudRate::BAUD_DEFAULT) ;
1079 SetCharacterSize(CharacterSize::CHAR_SIZE_DEFAULT) ;
1080 SetFlowControl(FlowControl::FLOW_CONTROL_DEFAULT) ;
1081 SetParity(Parity::PARITY_DEFAULT) ;
1082 SetStopBits(StopBits::STOP_BITS_DEFAULT) ;
1083 SetVMin(VMIN_DEFAULT) ;
1084 SetVTime(VTIME_DEFAULT) ;
1085 }
1086
1087 inline
1088 void
1090 {
1091 // Throw an exception if the serial port is not open.
1092 if (not this->IsOpen())
1093 {
1094 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1095 }
1096
1097 // Get the current serial port settings.
1098 termios port_settings {} ;
1099 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1100
1101 if (tcgetattr(this->mFileDescriptor,
1102 &port_settings) < 0)
1103 {
1104 throw std::runtime_error(std::strerror(errno)) ;
1105 }
1106
1107 // Set the baud rate for both input and output.
1108 if (0 != cfsetspeed(&port_settings, static_cast<speed_t>(baudRate)))
1109 {
1110 // If applying the baud rate settings fail, throw an exception.
1111 throw std::runtime_error(ERR_MSG_INVALID_BAUD_RATE) ;
1112 }
1113
1114 // Apply the modified settings.
1115 if (tcsetattr(this->mFileDescriptor,
1116 TCSANOW,
1117 &port_settings) < 0)
1118 {
1119 // If applying the settings fails, throw an exception.
1120 throw std::runtime_error(std::strerror(errno)) ;
1121 }
1122
1123 // Set the time interval value (us) required for one byte of data to arrive.
1124 mByteArrivalTimeDelta = (BITS_PER_BYTE * MICROSECONDS_PER_SEC) / GetBitRate(baudRate) ;
1125 }
1126
1127 inline
1128 BaudRate
1130 {
1131 // Throw an exception if the serial port is not open.
1132 if (not this->IsOpen())
1133 {
1134 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1135 }
1136
1137 // Get the current serial port settings.
1138 termios port_settings {} ;
1139 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1140
1141 if (tcgetattr(this->mFileDescriptor,
1142 &port_settings) < 0)
1143 {
1144 throw std::runtime_error(std::strerror(errno)) ;
1145 }
1146
1147 // Read the input and output baud rates.
1148 const auto input_baud = cfgetispeed(&port_settings) ;
1149 const auto output_baud = cfgetospeed(&port_settings) ;
1150
1151 // Make sure that the input and output baud rates are
1152 // equal. Otherwise, we do not know which one to return.
1153 if (input_baud != output_baud)
1154 {
1155 throw std::runtime_error(ERR_MSG_INVALID_BAUD_RATE) ;
1156 // return BaudRate::BAUD_INVALID ;
1157 }
1158
1159 // Obtain the input baud rate from the current settings.
1160 return BaudRate(input_baud) ;
1161 }
1162
1163 inline
1164 int
1165 SerialPort::Implementation::GetBitRate(const BaudRate& baudRate) const
1166 {
1167 int baud_rate_as_int = 1 ;
1168
1169 switch (baudRate)
1170 {
1171 case BaudRate::BAUD_50:
1172 baud_rate_as_int = 50 ;
1173 break ;
1174
1175 case BaudRate::BAUD_75:
1176 baud_rate_as_int = 75 ;
1177 break ;
1178
1179 case BaudRate::BAUD_110:
1180 baud_rate_as_int = 110 ;
1181 break ;
1182
1183 case BaudRate::BAUD_134:
1184 baud_rate_as_int = 134 ;
1185 break ;
1186
1187 case BaudRate::BAUD_150:
1188 baud_rate_as_int = 150 ;
1189 break ;
1190
1191 case BaudRate::BAUD_200:
1192 baud_rate_as_int = 200 ;
1193 break ;
1194
1195 case BaudRate::BAUD_300:
1196 baud_rate_as_int = 300 ;
1197 break ;
1198
1199 case BaudRate::BAUD_600:
1200 baud_rate_as_int = 600 ;
1201 break ;
1202
1203 case BaudRate::BAUD_1200:
1204 baud_rate_as_int = 1200 ;
1205 break ;
1206
1207 case BaudRate::BAUD_1800:
1208 baud_rate_as_int = 1800 ;
1209 break ;
1210
1211 case BaudRate::BAUD_2400:
1212 baud_rate_as_int = 2400 ;
1213 break ;
1214
1215 case BaudRate::BAUD_4800:
1216 baud_rate_as_int = 4800 ;
1217 break ;
1218
1219 case BaudRate::BAUD_9600:
1220 baud_rate_as_int = 9600 ;
1221 break ;
1222
1223 case BaudRate::BAUD_19200:
1224 baud_rate_as_int = 19200 ;
1225 break ;
1226
1227 case BaudRate::BAUD_38400:
1228 baud_rate_as_int = 38400 ;
1229 break ;
1230
1231 case BaudRate::BAUD_57600:
1232 baud_rate_as_int = 57600 ;
1233 break ;
1234
1235 case BaudRate::BAUD_115200:
1236 baud_rate_as_int = 115200 ;
1237 break ;
1238
1239 case BaudRate::BAUD_230400:
1240 baud_rate_as_int = 230400 ;
1241 break ;
1242
1243// @note: >B230400 are defined in Linux but not other POSIX systems, (e.g. Mac OS X).
1244#ifdef __linux__
1245 case BaudRate::BAUD_460800:
1246 baud_rate_as_int = 460800 ;
1247 break ;
1248
1249 case BaudRate::BAUD_500000:
1250 baud_rate_as_int = 500000 ;
1251 break ;
1252
1253 case BaudRate::BAUD_576000:
1254 baud_rate_as_int = 576000 ;
1255 break ;
1256
1257 case BaudRate::BAUD_921600:
1258 baud_rate_as_int = 921600 ;
1259 break ;
1260
1261 case BaudRate::BAUD_1000000:
1262 baud_rate_as_int = 1000000 ;
1263 break ;
1264
1265 case BaudRate::BAUD_1152000:
1266 baud_rate_as_int = 1152000 ;
1267 break ;
1268
1269 case BaudRate::BAUD_1500000:
1270 baud_rate_as_int = 1500000 ;
1271 break ;
1272
1273#if __MAX_BAUD > B2000000
1274 case BaudRate::BAUD_2000000:
1275 baud_rate_as_int = 2000000 ;
1276 break ;
1277
1278 case BaudRate::BAUD_2500000:
1279 baud_rate_as_int = 2500000 ;
1280 break ;
1281
1282 case BaudRate::BAUD_3000000:
1283 baud_rate_as_int = 3000000 ;
1284 break ;
1285
1286 case BaudRate::BAUD_3500000:
1287 baud_rate_as_int = 3500000 ;
1288 break ;
1289
1290 case BaudRate::BAUD_4000000:
1291 baud_rate_as_int = 4000000 ;
1292 break ;
1293#endif // __MAX_BAUD
1294#endif // __linux__
1295 default:
1296 // If an incorrect baud rate was specified, throw an exception.
1297 throw std::runtime_error(ERR_MSG_INVALID_BAUD_RATE) ;
1298 }
1299
1300 return baud_rate_as_int ;
1301 }
1302
1303 inline
1304 void
1305 SerialPort::Implementation::SetCharacterSize(const CharacterSize& characterSize)
1306 {
1307 // Throw an exception if the serial port is not open.
1308 if (not this->IsOpen())
1309 {
1310 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1311 }
1312
1313 // Get the current serial port settings.
1314 termios port_settings {} ;
1315 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1316
1317 if (tcgetattr(this->mFileDescriptor,
1318 &port_settings) < 0)
1319 {
1320 throw std::runtime_error(ERR_MSG_INVALID_CHARACTER_SIZE) ;
1321 }
1322
1323 // Set the character size to the specified value. If the character
1324 // size is not 8 then it is also important to set ISTRIP. Setting
1325 // ISTRIP causes all but the 7 low-order bits to be set to
1326 // zero. Otherwise they are set to unspecified values and may
1327 // cause problems. At the same time, we should clear the ISTRIP
1328 // flag when the character size is 8 otherwise the MSB will always
1329 // be set to zero (ISTRIP does not check the character size
1330 // setting;it just sets every bit above the low 7 bits to zero).
1331 if (characterSize == CharacterSize::CHAR_SIZE_8)
1332 {
1333 // NOLINTNEXTLINE (hicpp-signed-bitwise)
1334 port_settings.c_iflag &= ~ISTRIP ; // Clear the ISTRIP flag.
1335 }
1336 else
1337 {
1338 port_settings.c_iflag |= ISTRIP ; // Set the ISTRIP flag.
1339 }
1340
1341 // Set the character size.
1342 // NOLINTNEXTLINE (hicpp-signed-bitwise)
1343 port_settings.c_cflag &= ~CSIZE ; // Clear all CSIZE bits.
1344 port_settings.c_cflag |= static_cast<tcflag_t>(characterSize) ; // Set the character size.
1345
1346 // Apply the modified settings.
1347 if (tcsetattr(this->mFileDescriptor,
1348 TCSANOW,
1349 &port_settings) < 0)
1350 {
1351 throw std::runtime_error(std::strerror(errno)) ;
1352 }
1353 }
1354
1355 inline
1356 CharacterSize
1358 {
1359 // Throw an exception if the serial port is not open.
1360 if (not this->IsOpen())
1361 {
1362 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1363 }
1364
1365 // Get the current serial port settings.
1366 termios port_settings {} ;
1367 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1368
1369 if (tcgetattr(this->mFileDescriptor,
1370 &port_settings) < 0)
1371 {
1372 throw std::runtime_error(std::strerror(errno)) ;
1373 }
1374
1375 // Read the character size from the setttings.
1376 // NOLINTNEXTLINE (hicpp-signed-bitwise)
1377 return CharacterSize(port_settings.c_cflag & CSIZE) ;
1378 }
1379
1380 inline
1381 void
1382 SerialPort::Implementation::SetFlowControl(const FlowControl& flowControlType)
1383 {
1384 // Throw an exception if the serial port is not open.
1385 if (not this->IsOpen())
1386 {
1387 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1388 }
1389
1390 // Flush the input and output buffers associated with the port.
1391 if (tcflush(this->mFileDescriptor,
1392 TCIOFLUSH) < 0)
1393 {
1394 throw std::runtime_error(std::strerror(errno)) ;
1395 }
1396
1397 // Get the current serial port settings.
1398 termios port_settings {} ;
1399 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1400
1401 if (tcgetattr(this->mFileDescriptor,
1402 &port_settings) < 0)
1403 {
1404 throw std::runtime_error(ERR_MSG_INVALID_FLOW_CONTROL) ;
1405 }
1406
1407 // Set the flow control. Hardware flow control uses the RTS (Ready
1408 // To Send) and CTS (clear to Send) lines. Software flow control
1409 // uses IXON|IXOFF
1410 switch(flowControlType)
1411 {
1412 case FlowControl::FLOW_CONTROL_HARDWARE:
1413 port_settings.c_iflag &= ~ (IXON|IXOFF) ; // NOLINT (hicpp-signed-bitwise)
1414 port_settings.c_cflag |= CRTSCTS ;
1415 port_settings.c_cc[VSTART] = _POSIX_VDISABLE ;
1416 port_settings.c_cc[VSTOP] = _POSIX_VDISABLE ;
1417 break ;
1418 case FlowControl::FLOW_CONTROL_SOFTWARE:
1419 port_settings.c_iflag |= IXON|IXOFF ; // NOLINT(hicpp-signed-bitwise)
1420 port_settings.c_cflag &= ~CRTSCTS ;
1421 port_settings.c_cc[VSTART] = CTRL_Q ; // 0x11 (021) ^q
1422 port_settings.c_cc[VSTOP] = CTRL_S ; // 0x13 (023) ^s
1423 break ;
1424 case FlowControl::FLOW_CONTROL_NONE:
1425 port_settings.c_iflag &= ~(IXON|IXOFF) ; // NOLINT(hicpp-signed-bitwise)
1426 port_settings.c_cflag &= ~CRTSCTS ;
1427 break ;
1428 default:
1429 throw std::invalid_argument(ERR_MSG_INVALID_FLOW_CONTROL) ;
1430 // break ; break not needed after a throw
1431 }
1432
1433 // Apply the modified settings.
1434 if (tcsetattr(this->mFileDescriptor,
1435 TCSANOW,
1436 &port_settings) < 0)
1437 {
1438 throw std::runtime_error(std::strerror(errno)) ;
1439 }
1440 }
1441
1442 inline
1443 FlowControl
1445 {
1446 // Throw an exception if the serial port is not open.
1447 if (not this->IsOpen())
1448 {
1449 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1450 }
1451
1452 // Get the current serial port settings.
1453 termios port_settings {} ;
1454 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1455
1456 if (tcgetattr(this->mFileDescriptor,
1457 &port_settings) < 0)
1458 {
1459 throw std::runtime_error(std::strerror(errno)) ;
1460 }
1461
1462 // Check if IXON and IXOFF are set in c_iflag. If both are set and
1463 // VSTART and VSTOP are set to 0x11 (^Q) and 0x13 (^S) respectively,
1464 // then we are using software flow control.
1465 if ((port_settings.c_iflag & IXON) and // NOLINT (hicpp-signed-bitwise)
1466 (port_settings.c_iflag & IXOFF) and // NOLINT (hicpp-signed-bitwise)
1467 (CTRL_Q == port_settings.c_cc[VSTART]) and
1468 (CTRL_S == port_settings.c_cc[VSTOP]))
1469 {
1470 return FlowControl::FLOW_CONTROL_SOFTWARE ;
1471 }
1472
1473 if (not ((port_settings.c_iflag & IXON) or // NOLINT (hicpp-signed-bitwise)
1474 (port_settings.c_iflag & IXOFF))) // NOLINT (hicpp-signed-bitwise)
1475 {
1476 if (0 != (port_settings.c_cflag & CRTSCTS))
1477 {
1478 // If neither IXON or IXOFF is set then we must have hardware flow
1479 // control.
1480 return FlowControl::FLOW_CONTROL_HARDWARE ;
1481 }
1482 return FlowControl::FLOW_CONTROL_NONE ;
1483 }
1484
1485 // If none of the above conditions are satisfied then the serial port
1486 // is using a flow control setup which we do not support at present.
1487 return FlowControl::FLOW_CONTROL_INVALID ;
1488 }
1489
1490 inline
1491 void
1493 {
1494 // Throw an exception if the serial port is not open.
1495 if (not this->IsOpen())
1496 {
1497 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1498 }
1499
1500 // Get the current serial port settings.
1501 termios port_settings {} ;
1502 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1503
1504 if (tcgetattr(this->mFileDescriptor,
1505 &port_settings) < 0)
1506 {
1507 throw std::runtime_error(std::strerror(errno)) ;
1508 }
1509
1510 // Set the parity type
1511 switch(parityType)
1512 {
1513 case Parity::PARITY_EVEN:
1514 port_settings.c_cflag |= PARENB ;
1515 port_settings.c_cflag &= ~PARODD ; // NOLINT (hicpp-signed-bitwise)
1516 port_settings.c_iflag |= INPCK ;
1517 break ;
1518 case Parity::PARITY_ODD:
1519 port_settings.c_cflag |= PARENB ;
1520 port_settings.c_cflag |= PARODD ;
1521 port_settings.c_iflag |= INPCK ;
1522 break ;
1523 case Parity::PARITY_NONE:
1524 port_settings.c_cflag &= ~PARENB ; // NOLINT (hicpp-signed-bitwise)
1525 port_settings.c_iflag |= IGNPAR ;
1526 break ;
1527 default:
1528 throw std::invalid_argument(ERR_MSG_INVALID_PARITY) ;
1529 // break ; break not needed after a throw
1530 }
1531
1532 // Apply the modified port settings.
1533 if (tcsetattr(this->mFileDescriptor,
1534 TCSANOW,
1535 &port_settings) < 0)
1536 {
1537 throw std::runtime_error(std::strerror(errno)) ;
1538 }
1539 }
1540
1541 inline
1542 Parity
1544 {
1545 // Throw an exception if the serial port is not open.
1546 if (not this->IsOpen())
1547 {
1548 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1549 }
1550
1551 // Get the current serial port settings.
1552 termios port_settings {} ;
1553 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1554
1555 if (tcgetattr(this->mFileDescriptor,
1556 &port_settings) < 0)
1557 {
1558 throw std::runtime_error(std::strerror(errno)) ;
1559 }
1560
1561 // Get the parity setting from the termios structure.
1562 if (0 != (port_settings.c_cflag & PARENB)) // NOLINT (hicpp-signed-bitwise)
1563 {
1564 // parity is enabled.
1565 if (port_settings.c_cflag & PARODD) // NOLINT (hicpp-signed-bitwise)
1566 {
1567 return Parity::PARITY_ODD ; // odd parity
1568 }
1569 return Parity::PARITY_EVEN ; // even parity
1570 }
1571 return Parity::PARITY_NONE ; // no parity.
1572 }
1573
1574 inline
1575 void
1577 {
1578 // Throw an exception if the serial port is not open.
1579 if (not this->IsOpen())
1580 {
1581 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1582 }
1583
1584 // Get the current serial port settings.
1585 termios port_settings {} ;
1586 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1587
1588 if (tcgetattr(this->mFileDescriptor,
1589 &port_settings) < 0)
1590 {
1591 throw std::runtime_error(std::strerror(errno)) ;
1592 }
1593
1594 // Set the number of stop bits.
1595 switch(stopBits)
1596 {
1597 case StopBits::STOP_BITS_1:
1598 port_settings.c_cflag &= ~CSTOPB ; // NOLINT (hicpp-signed-bitwise)
1599 break ;
1600 case StopBits::STOP_BITS_2:
1601 port_settings.c_cflag |= CSTOPB ;
1602 break ;
1603 default:
1604 throw std::invalid_argument(ERR_MSG_INVALID_STOP_BITS) ;
1605 // break ; break not needed after a throw
1606 }
1607
1608 // Apply the modified settings.
1609 if (tcsetattr(this->mFileDescriptor,
1610 TCSANOW,
1611 &port_settings) < 0)
1612 {
1613 throw std::runtime_error(std::strerror(errno)) ;
1614 }
1615 }
1616
1617 inline
1618 StopBits
1620 {
1621 // Throw an exception if the serial port is not open.
1622 if (not this->IsOpen())
1623 {
1624 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1625 }
1626
1627 // Get the current serial port settings.
1628 termios port_settings {} ;
1629 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1630
1631 if (tcgetattr(this->mFileDescriptor,
1632 &port_settings) < 0)
1633 {
1634 throw std::runtime_error(std::strerror(errno)) ;
1635 }
1636
1637 // If CSTOPB is set then we are using two stop bits, otherwise we
1638 // are using 1 stop bit.
1639 if (port_settings.c_cflag & CSTOPB) // NOLINT (hicpp-signed-bitwise)
1640 {
1641 return StopBits::STOP_BITS_2 ;
1642 }
1643 return StopBits::STOP_BITS_1 ;
1644 }
1645
1646 inline
1647 void
1649 {
1650 // Throw an exception if the serial port is not open.
1651 if (not this->IsOpen())
1652 {
1653 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1654 }
1655
1656 if (vmin < 0 || vmin > 255)
1657 {
1658 std::stringstream error_message ;
1659 error_message << "Invalid vmin value: " << vmin << ". " ;
1660 error_message << "Vmin must be in the range [0, 255]." ;
1661 throw std::invalid_argument {error_message.str()} ;
1662 }
1663
1664 // Get the current serial port settings.
1665 termios port_settings {} ;
1666 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1667
1668 if (tcgetattr(this->mFileDescriptor,
1669 &port_settings) < 0)
1670 {
1671 throw std::runtime_error(std::strerror(errno)) ;
1672 }
1673
1674 port_settings.c_cc[VMIN] = static_cast<cc_t>(vmin) ;
1675
1676 // Apply the modified settings.
1677 if (tcsetattr(this->mFileDescriptor,
1678 TCSANOW,
1679 &port_settings) < 0)
1680 {
1681 throw std::runtime_error(std::strerror(errno)) ;
1682 }
1683 }
1684
1685 inline
1686 short
1688 {
1689 // Throw an exception if the serial port is not open.
1690 if (not this->IsOpen())
1691 {
1692 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1693 }
1694
1695 // Get the current serial port settings.
1696 termios port_settings {} ;
1697 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1698
1699 if (tcgetattr(this->mFileDescriptor,
1700 &port_settings) < 0)
1701 {
1702 throw std::runtime_error(std::strerror(errno)) ;
1703 }
1704
1705 return port_settings.c_cc[VMIN] ;
1706 }
1707
1708 inline
1709 void
1711 {
1712 // Throw an exception if the serial port is not open.
1713 if (not this->IsOpen())
1714 {
1715 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1716 }
1717
1718 if (vtime < 0 || vtime > 255)
1719 {
1720 std::stringstream error_message ;
1721 error_message << "Invalid vtime value: " << vtime << ". " ;
1722 error_message << "Vtime must be in the range [0, 255]." ;
1723 throw std::invalid_argument {error_message.str()} ;
1724 }
1725
1726 // Get the current serial port settings.
1727 termios port_settings {} ;
1728 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1729
1730 if (tcgetattr(this->mFileDescriptor,
1731 &port_settings) < 0)
1732 {
1733 throw std::runtime_error(std::strerror(errno)) ;
1734 }
1735
1736 port_settings.c_cc[VTIME] = static_cast<cc_t>(vtime) ;
1737
1738 // Apply the modified settings.
1739 if (tcsetattr(this->mFileDescriptor,
1740 TCSANOW,
1741 &port_settings) < 0)
1742 {
1743 throw std::runtime_error(std::strerror(errno)) ;
1744 }
1745 }
1746
1747 inline
1748 short
1750 {
1751 // Throw an exception if the serial port is not open.
1752 if (not this->IsOpen())
1753 {
1754 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1755 }
1756
1757 // Get the current serial port settings.
1758 termios port_settings {} ;
1759 std::memset(&port_settings, 0, sizeof(port_settings)) ;
1760
1761 if (tcgetattr(this->mFileDescriptor,
1762 &port_settings) < 0)
1763 {
1764 throw std::runtime_error(std::strerror(errno)) ;
1765 }
1766
1767 return port_settings.c_cc[VTIME] ;
1768 }
1769
1770 inline
1771 void
1773 {
1774 // Throw an exception if the serial port is not open.
1775 if (not this->IsOpen())
1776 {
1777 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1778 }
1779
1780 this->SetModemControlLine(TIOCM_DTR,
1781 dtrState) ;
1782 }
1783
1784 inline
1785 bool
1787 {
1788 // Throw an exception if the serial port is not open.
1789 if (not this->IsOpen())
1790 {
1791 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1792 }
1793
1794 return this->GetModemControlLine(TIOCM_DTR) ;
1795 }
1796
1797 inline
1798 void
1800 {
1801 // Throw an exception if the serial port is not open.
1802 if (not this->IsOpen())
1803 {
1804 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1805 }
1806
1807 this->SetModemControlLine(TIOCM_RTS,
1808 rtsState) ;
1809 }
1810
1811 inline
1812 bool
1814 {
1815 // Throw an exception if the serial port is not open.
1816 if (not this->IsOpen())
1817 {
1818 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1819 }
1820
1821 return this->GetModemControlLine(TIOCM_RTS) ;
1822 }
1823
1824 inline
1825 bool
1827 {
1828 // Throw an exception if the serial port is not open.
1829 if (not this->IsOpen())
1830 {
1831 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1832 }
1833
1834 return this->GetModemControlLine(TIOCM_CTS) ;
1835 }
1836
1837 inline
1838 bool
1840 {
1841 // Throw an exception if the serial port is not open.
1842 if (not this->IsOpen())
1843 {
1844 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1845 }
1846
1847 return this->GetModemControlLine(TIOCM_DSR) ;
1848 }
1849
1850 inline
1851 int
1853 {
1854 // Throw an exception if the serial port is not open.
1855 if (not this->IsOpen())
1856 {
1857 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1858 }
1859
1860 return this->mFileDescriptor ;
1861 }
1862
1863 inline
1864 int
1866 {
1867 // Throw an exception if the serial port is not open.
1868 if (not this->IsOpen())
1869 {
1870 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1871 }
1872
1873 int number_of_bytes_available = 0 ;
1874
1875 // NOLINTNEXTLINE (cppcoreguidelines-pro-type-vararg)
1876 if (call_with_retry(ioctl,
1877 this->mFileDescriptor,
1878 FIONREAD,
1879 &number_of_bytes_available) < 0)
1880 {
1881 throw std::runtime_error(std::strerror(errno)) ;
1882 }
1883
1884 return number_of_bytes_available ;
1885 }
1886
1887#ifdef __linux__
1888 inline
1889 std::vector<std::string>
1890 SerialPort::Implementation::GetAvailableSerialPorts() const
1891 {
1892 constexpr int array_size = 3 ;
1893 constexpr size_t max_port_number = 128 ;
1894
1895 std::string serial_ports[array_size] = {"/dev/ttyS",
1896 "/dev/ttyACM",
1897 "/dev/ttyUSB"} ;
1898
1899 std::vector<std::string> serial_port_names {} ;
1900 for (const auto& port_prefix: serial_ports)
1901 {
1902 for (size_t j = 0 ;j < max_port_number;j++)
1903 {
1904 const auto file_name = port_prefix + std::to_string(j) ;
1905
1906 // Try to open the serial port.
1907 const auto file_desc = call_with_retry(open,
1908 file_name.c_str(),
1909 O_RDWR | O_NOCTTY | O_NONBLOCK) ; // NOLINT (hicpp-vararg)
1910
1911 if (file_desc > 0)
1912 {
1913 serial_struct serial_port_info {} ;
1914 // NOLINTNEXTLINE (hicpp-vararg)
1915 if (call_with_retry(ioctl,
1916 file_desc,
1917 TIOCGSERIAL,
1918 &serial_port_info) == -1)
1919 {
1920 throw std::runtime_error(std::strerror(errno)) ;
1921 }
1922
1923 serial_port_names.push_back(file_name) ;
1924
1925 close(file_desc) ;
1926 }
1927 }
1928 }
1929
1930 return serial_port_names ;
1931 }
1932#endif
1933
1934 inline
1935 void
1937 const bool lineState)
1938 {
1939 // Throw an exception if the serial port is not open.
1940 if (not this->IsOpen())
1941 {
1942 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1943 }
1944
1945 if (modemLine != TIOCM_LE &&
1946 modemLine != TIOCM_DTR &&
1947 modemLine != TIOCM_RTS &&
1948 modemLine != TIOCM_ST &&
1949 modemLine != TIOCM_SR &&
1950 modemLine != TIOCM_CTS &&
1951 modemLine != TIOCM_CAR &&
1952 modemLine != TIOCM_CD &&
1953 modemLine != TIOCM_RNG &&
1954 modemLine != TIOCM_RI &&
1955 modemLine != TIOCM_DSR)
1956 {
1957 throw std::invalid_argument {ERR_MSG_INVALID_MODEM_LINE} ;
1958 }
1959
1960 // Set or unset the specified bit according to the value of lineState.
1961 int ioctl_result = -1 ;
1962
1963 if (lineState)
1964 {
1965 int set_line_mask = modemLine ;
1966 // NOLINTNEXTLINE (cppcoreguidelines-pro-type-vararg)
1967 ioctl_result = call_with_retry(ioctl,
1968 this->mFileDescriptor,
1969 TIOCMBIS,
1970 &set_line_mask) ;
1971 }
1972 else
1973 {
1974 int reset_line_mask = modemLine ;
1975 // NOLINTNEXTLINE (cppcoreguidelines-pro-type-vararg)
1976 ioctl_result = call_with_retry(ioctl,
1977 this->mFileDescriptor,
1978 TIOCMBIC,
1979 &reset_line_mask) ;
1980 }
1981
1982 // Check for errors.
1983 if (ioctl_result < 0)
1984 {
1985 throw std::runtime_error(std::strerror(errno)) ;
1986 }
1987 }
1988
1989 inline
1990 bool
1992 {
1993 // Throw an exception if the serial port is not open.
1994 if (not this->IsOpen())
1995 {
1996 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
1997 }
1998
1999 if (modemLine != TIOCM_LE &&
2000 modemLine != TIOCM_DTR &&
2001 modemLine != TIOCM_RTS &&
2002 modemLine != TIOCM_ST &&
2003 modemLine != TIOCM_SR &&
2004 modemLine != TIOCM_CTS &&
2005 modemLine != TIOCM_CAR &&
2006 modemLine != TIOCM_CD &&
2007 modemLine != TIOCM_RNG &&
2008 modemLine != TIOCM_RI &&
2009 modemLine != TIOCM_DSR)
2010 {
2011 throw std::invalid_argument {ERR_MSG_INVALID_MODEM_LINE} ;
2012 }
2013
2014 // Use an ioctl() call to get the state of the line.
2015 int serial_port_state = 0 ;
2016
2017 // NOLINTNEXTLINE (cppcoreguidelines-pro-type-vararg)
2018 if (call_with_retry(ioctl,
2019 this->mFileDescriptor,
2020 TIOCMGET,
2021 &serial_port_state) < 0)
2022 {
2023 throw std::runtime_error(std::strerror(errno)) ;
2024 }
2025
2026 return (0 != (serial_port_state & modemLine)) ; // NOLINT(hicpp-signed-bitwise)
2027 }
2028
2029 inline
2030 void
2032 {
2033 // Throw an exception if the serial port is not open.
2034 if (not this->IsOpen())
2035 {
2036 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2037 }
2038
2039 int flags = fcntl(this->mFileDescriptor, F_GETFL, 0) ;
2040
2041 if (blockingStatus)
2042 {
2043 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
2044 if (fcntl(this->mFileDescriptor,
2045 F_SETFL,
2046 flags &~ O_NONBLOCK) < 0) // NOLINT (hicpp-signed-bitwise)
2047 {
2048 throw std::runtime_error(std::strerror(errno)) ;
2049 }
2050 }
2051 else
2052 {
2053 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg)
2054 if (fcntl(this->mFileDescriptor,
2055 F_SETFL,
2056 flags | O_NONBLOCK) < 0) // NOLINT (hicpp-signed-bitwise)
2057 {
2058 throw std::runtime_error(std::strerror(errno)) ;
2059 }
2060 }
2061 }
2062
2063 inline
2064 bool
2066 {
2067 // Throw an exception if the serial port is not open.
2068 if (not this->IsOpen())
2069 {
2070 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2071 }
2072
2073 bool blocking_status = false ;
2074
2075 const int flags = fcntl(this->mFileDescriptor, F_GETFL, 0) ;
2076
2077 if (flags == -1)
2078 {
2079 throw std::runtime_error(std::strerror(errno)) ;
2080 }
2081
2082 if (flags == (flags | O_NONBLOCK)) // NOLINT (hicpp-signed-bitwise)
2083 {
2084 blocking_status = true ;
2085 }
2086
2087 return blocking_status ;
2088 }
2089
2090 inline
2091 void
2092 SerialPort::Implementation::SetDefaultLinuxSpecificModes()
2093 {
2094 // Make sure that the serial port is open.
2095 if (not this->IsOpen())
2096 {
2097 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2098 }
2099
2100 // Get the current serial port settings.
2101 termios port_settings {} ;
2102 std::memset(&port_settings, 0, sizeof(port_settings)) ;
2103
2104 if (tcgetattr(this->mFileDescriptor,
2105 &port_settings) < 0)
2106 {
2107 throw std::runtime_error(std::strerror(errno)) ;
2108 }
2109
2110 // @NOTE - termios.c_line is not a standard element of the termios
2111 // structure, (as per the Single Unix Specification 3).
2112 #ifdef __linux__
2113 port_settings.c_line = '\0' ;
2114 #endif
2115
2116 // Apply the modified settings.
2117 if (tcsetattr(this->mFileDescriptor,
2118 TCSANOW,
2119 &port_settings) < 0)
2120 {
2121 throw std::runtime_error(std::strerror(errno)) ;
2122 }
2123 }
2124
2125 inline
2126 void
2127 SerialPort::Implementation::SetDefaultInputModes()
2128 {
2129 // Make sure that the serial port is open.
2130 if (not this->IsOpen())
2131 {
2132 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2133 }
2134
2135 // Get the current serial port settings.
2136 termios port_settings {} ;
2137 std::memset(&port_settings, 0, sizeof(port_settings)) ;
2138
2139 if (tcgetattr(this->mFileDescriptor,
2140 &port_settings) < 0)
2141 {
2142 throw std::runtime_error(std::strerror(errno)) ;
2143 }
2144
2145 // Ignore Break conditions on input.
2146 port_settings.c_iflag = IGNBRK ;
2147
2148 // Apply the modified settings.
2149 if (tcsetattr(this->mFileDescriptor,
2150 TCSANOW,
2151 &port_settings) < 0)
2152 {
2153 throw std::runtime_error(std::strerror(errno)) ;
2154 }
2155 }
2156
2157 inline
2158 void
2159 SerialPort::Implementation::SetDefaultOutputModes()
2160 {
2161 // Make sure that the serial port is open.
2162 if (not this->IsOpen())
2163 {
2164 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2165 }
2166
2167 // Get the current serial port settings.
2168 termios port_settings {} ;
2169 std::memset(&port_settings, 0, sizeof(port_settings)) ;
2170
2171 if (tcgetattr(this->mFileDescriptor,
2172 &port_settings) < 0)
2173 {
2174 throw std::runtime_error(std::strerror(errno)) ;
2175 }
2176
2177 port_settings.c_oflag = 0 ;
2178
2179 // Apply the modified settings.
2180 if (tcsetattr(this->mFileDescriptor,
2181 TCSANOW,
2182 &port_settings) < 0)
2183 {
2184 throw std::runtime_error(std::strerror(errno)) ;
2185 }
2186 }
2187
2188 inline
2189 void
2190 SerialPort::Implementation::SetDefaultControlModes()
2191 {
2192 // Make sure that the serial port is open.
2193 if (not this->IsOpen())
2194 {
2195 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2196 }
2197
2198 // Get the current serial port settings.
2199 termios port_settings {} ;
2200 std::memset(&port_settings, 0, sizeof(port_settings)) ;
2201
2202 if (tcgetattr(this->mFileDescriptor,
2203 &port_settings) < 0)
2204 {
2205 throw std::runtime_error(std::strerror(errno)) ;
2206 }
2207
2208 // Enable the receiver (CREAD) and ignore modem control lines (CLOCAL).
2209 port_settings.c_cflag |= CREAD | CLOCAL ; // NOLINT (hicpp-signed-bitwise)
2210
2211 // Apply the modified settings.
2212 if (tcsetattr(this->mFileDescriptor,
2213 TCSANOW,
2214 &port_settings) < 0)
2215 {
2216 throw std::runtime_error(std::strerror(errno)) ;
2217 }
2218 }
2219
2220 inline
2221 void
2222 SerialPort::Implementation::SetDefaultLocalModes()
2223 {
2224 // Make sure that the serial port is open.
2225 if (not this->IsOpen())
2226 {
2227 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2228 }
2229
2230 // Get the current serial port settings.
2231 termios port_settings {} ;
2232 std::memset(&port_settings, 0, sizeof(port_settings)) ;
2233
2234 if (tcgetattr(this->mFileDescriptor,
2235 &port_settings) < 0)
2236 {
2237 throw std::runtime_error(std::strerror(errno)) ;
2238 }
2239
2240 port_settings.c_lflag = 0 ;
2241
2242 // Apply the modified settings.
2243 if (tcsetattr(this->mFileDescriptor,
2244 TCSANOW,
2245 &port_settings) < 0)
2246 {
2247 throw std::runtime_error(std::strerror(errno)) ;
2248 }
2249 }
2250
2251 inline
2252 void
2253 SerialPort::Implementation::Read(DataBuffer& dataBuffer,
2254 const size_t numberOfBytes,
2255 const size_t msTimeout)
2256 {
2257 // Throw an exception if the serial port is not open.
2258 if (not this->IsOpen())
2259 {
2260 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2261 }
2262
2263 if ((numberOfBytes == 0) and
2264 (msTimeout == 0))
2265 {
2266 return ;
2267 }
2268
2269 // Local variables.
2270 size_t number_of_bytes_read = 0 ;
2271 size_t number_of_bytes_remaining = std::max(numberOfBytes, static_cast<size_t>(1)) ;
2272 size_t maximum_number_of_bytes = dataBuffer.max_size() ;
2273
2274 // Clear the data buffer and reserve enough space in the buffer to store the incoming data.
2275 dataBuffer.clear() ;
2276 dataBuffer.resize(number_of_bytes_remaining) ;
2277
2278 // Obtain the entry time.
2279 const auto entry_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2280
2281 while (number_of_bytes_remaining > 0)
2282 {
2283 // If insufficient space remains in the buffer, exit the loop and return .
2284 if (number_of_bytes_remaining >= maximum_number_of_bytes - number_of_bytes_read)
2285 {
2286 break ;
2287 }
2288
2289 if (numberOfBytes == 0)
2290 {
2291 // Add an additional element for the read() call.
2292 dataBuffer.resize(number_of_bytes_read + 1) ;
2293 }
2294
2295 const auto read_result = call_with_retry(read,
2296 this->mFileDescriptor,
2297 &dataBuffer[number_of_bytes_read],
2298 number_of_bytes_remaining) ;
2299
2300 if (read_result > 0)
2301 {
2302 number_of_bytes_read += read_result ;
2303
2304 if (numberOfBytes != 0)
2305 {
2306 number_of_bytes_remaining = numberOfBytes - number_of_bytes_read ;
2307
2308 if (number_of_bytes_remaining == 0)
2309 {
2310 break ;
2311 }
2312 }
2313 }
2314 else if ((read_result <= 0) and
2315 (errno != EWOULDBLOCK))
2316 {
2317 throw std::runtime_error(std::strerror(errno)) ;
2318 }
2319
2320 // Obtain the current time.
2321 const auto current_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2322
2323 // Calculate the time delta.
2324 const auto elapsed_time = current_time - entry_time ;
2325
2326 // Calculate the elapsed number of milliseconds.
2327 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() ;
2328
2329 // If more than msTimeout milliseconds have elapsed while
2330 // waiting for data, then we throw a ReadTimeout exception.
2331 if ((msTimeout > 0) and
2332 (static_cast<size_t>(elapsed_ms) > msTimeout))
2333 {
2334 // Resize the data buffer.
2335 dataBuffer.resize(number_of_bytes_read) ;
2336
2337 throw ReadTimeout(ERR_MSG_READ_TIMEOUT) ;
2338 }
2339
2340 // Allow sufficient time for an additional byte to arrive.
2341 usleep(mByteArrivalTimeDelta) ;
2342 }
2343 }
2344
2345 inline
2346 void
2347 SerialPort::Implementation::Read(std::string& dataString,
2348 const size_t numberOfBytes,
2349 const size_t msTimeout)
2350 {
2351 // Throw an exception if the serial port is not open.
2352 if (not this->IsOpen())
2353 {
2354 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2355 }
2356
2357 if ((numberOfBytes == 0) and
2358 (msTimeout == 0))
2359 {
2360 return ;
2361 }
2362
2363 // Local variables.
2364 size_t number_of_bytes_read = 0 ;
2365 size_t number_of_bytes_remaining = std::max(numberOfBytes, static_cast<size_t>(1)) ;
2366 size_t maximum_number_of_bytes = dataString.max_size() ;
2367
2368 // Clear the data buffer and reserve enough space in the buffer to store the incoming data.
2369 dataString.clear() ;
2370 dataString.resize(number_of_bytes_remaining) ;
2371
2372 // Obtain the entry time.
2373 const auto entry_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2374
2375 while (number_of_bytes_remaining > 0)
2376 {
2377 // If insufficient space remains in the buffer, exit the loop and return .
2378 if (number_of_bytes_remaining >= maximum_number_of_bytes - number_of_bytes_read)
2379 {
2380 break ;
2381 }
2382
2383 if (numberOfBytes == 0)
2384 {
2385 // Add an additional element for the read() call.
2386 dataString.resize(number_of_bytes_read + 1) ;
2387 }
2388
2389 const auto read_result = call_with_retry(read,
2390 this->mFileDescriptor,
2391 &dataString[number_of_bytes_read],
2392 number_of_bytes_remaining) ;
2393
2394 if (read_result > 0)
2395 {
2396 number_of_bytes_read += read_result ;
2397
2398 if (numberOfBytes != 0)
2399 {
2400 number_of_bytes_remaining = numberOfBytes - number_of_bytes_read ;
2401
2402 if (number_of_bytes_remaining == 0)
2403 {
2404 break ;
2405 }
2406 }
2407 }
2408 else if (read_result <= 0 &&
2409 errno != EWOULDBLOCK)
2410 {
2411 throw std::runtime_error(std::strerror(errno)) ;
2412 }
2413
2414 // Obtain the current time.
2415 const auto current_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2416
2417 // Calculate the time delta.
2418 const auto elapsed_time = current_time - entry_time ;
2419
2420 // Calculate the elapsed number of milliseconds.
2421 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() ;
2422
2423 // If more than msTimeout milliseconds have elapsed while
2424 // waiting for data, then we throw a ReadTimeout exception.
2425 if ((msTimeout > 0) and
2426 (static_cast<size_t>(elapsed_ms) > msTimeout))
2427 {
2428 // Resize the data string.
2429 dataString.resize(number_of_bytes_read) ;
2430
2431 throw ReadTimeout(ERR_MSG_READ_TIMEOUT) ;
2432 }
2433
2434 // Allow sufficient time for an additional byte to arrive.
2435 usleep(mByteArrivalTimeDelta) ;
2436 }
2437 }
2438
2439 template <typename ByteType, typename /* unused */>
2440 inline
2441 void
2443 const size_t msTimeout)
2444 {
2445 // Double check to make sure that ByteType is exactly one byte long.
2446 static_assert(sizeof(ByteType) == 1,
2447 "ByteType must have a size of exactly one byte.") ;
2448
2449 // Throw an exception if the serial port is not open.
2450 if (not this->IsOpen())
2451 {
2452 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2453 }
2454
2455 // Obtain the entry time.
2456 const auto entry_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2457
2458 // Loop until the number of bytes requested have been read or the
2459 // timeout has elapsed.
2460 ssize_t read_result = 0 ;
2461 while (read_result < 1)
2462 {
2463 read_result = call_with_retry(read,
2464 this->mFileDescriptor,
2465 &charBuffer,
2466 sizeof(ByteType)) ;
2467
2468 // If the byte has been successfully read, exit the loop and return.
2469 if (read_result == sizeof(ByteType))
2470 {
2471 break ;
2472 }
2473
2474 if ((read_result <= 0) and
2475 (errno != EWOULDBLOCK))
2476 {
2477 throw std::runtime_error(std::strerror(errno)) ;
2478 }
2479
2480 // Obtain the current time.
2481 const auto current_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2482
2483 // Calculate the time delta.
2484 const auto elapsed_time = current_time - entry_time ;
2485
2486 // Calculate the elapsed number of milliseconds.
2487 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() ;
2488
2489 // Throw a ReadTimeout exception if more than msTimeout milliseconds
2490 // have elapsed while waiting for data.
2491 if ((msTimeout > 0) and
2492 (static_cast<size_t>(elapsed_ms) > msTimeout))
2493 {
2494 throw ReadTimeout(ERR_MSG_READ_TIMEOUT) ;
2495 }
2496
2497 // Allow sufficient time for an additional byte to arrive.
2498 usleep(mByteArrivalTimeDelta) ;
2499 }
2500 }
2501
2502 inline
2503 void
2505 const char lineTerminator,
2506 const size_t msTimeout)
2507 {
2508 // Throw an exception if the serial port is not open.
2509 if (not this->IsOpen())
2510 {
2511 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2512 }
2513
2514 // Clear the data string.
2515 dataString.clear() ;
2516
2517 unsigned char next_char = 0 ;
2518
2519 size_t elapsed_ms = 0 ;
2520
2521 ssize_t remaining_ms = 0 ;
2522
2523 std::chrono::high_resolution_clock::duration entry_time ;
2524 std::chrono::high_resolution_clock::duration current_time ;
2525 std::chrono::high_resolution_clock::duration elapsed_time ;
2526
2527 // Obtain the entry time.
2528 entry_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2529
2530 while (next_char != lineTerminator)
2531 {
2532 // Obtain the current time.
2533 current_time = std::chrono::high_resolution_clock::now().time_since_epoch() ;
2534
2535 // Calculate the time delta.
2536 elapsed_time = current_time - entry_time ;
2537
2538 // Calculate the elapsed number of milliseconds.
2539 elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time).count() ;
2540
2541 // If more than msTimeout milliseconds have elapsed while
2542 // waiting for data, then we throw a ReadTimeout exception.
2543 if (msTimeout > 0 &&
2544 elapsed_ms > msTimeout)
2545 {
2546 throw ReadTimeout(ERR_MSG_READ_TIMEOUT) ;
2547 }
2548
2549 remaining_ms = msTimeout - elapsed_ms ;
2550
2551 this->ReadByte(next_char,
2552 remaining_ms) ;
2553
2554 dataString += next_char ;
2555 }
2556 }
2557
2558 inline
2559 void
2560 SerialPort::Implementation::Write(const DataBuffer& dataBuffer)
2561 {
2562 // Throw an exception if the serial port is not open.
2563 if (not this->IsOpen())
2564 {
2565 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2566 }
2567
2568 size_t number_of_bytes = dataBuffer.size() ;
2569
2570 // Nothing needs to be done if there is no data in the string.
2571 if (number_of_bytes <= 0)
2572 {
2573 return ;
2574 }
2575
2576 // Local variables.
2577 size_t number_of_bytes_written = 0 ;
2578 size_t number_of_bytes_remaining = number_of_bytes ;
2579
2580 // Write the data to the serial port. Keep retrying if EAGAIN
2581 // error is received and EWOULDBLOCK is not received.
2582 ssize_t write_result = 0 ;
2583
2584 while (number_of_bytes_remaining > 0)
2585 {
2586 write_result = call_with_retry(write,
2587 this->mFileDescriptor,
2588 &dataBuffer[number_of_bytes_written],
2589 number_of_bytes_remaining) ;
2590
2591 if (write_result >= 0)
2592 {
2593 number_of_bytes_written += write_result ;
2594 number_of_bytes_remaining = number_of_bytes - number_of_bytes_written ;
2595
2596 if (number_of_bytes_remaining == 0)
2597 {
2598 break ;
2599 }
2600 }
2601 else if (write_result <= 0 &&
2602 errno != EWOULDBLOCK)
2603 {
2604 throw std::runtime_error(std::strerror(errno)) ;
2605 }
2606 }
2607 }
2608
2609 inline
2610 void
2611 SerialPort::Implementation::Write(const std::string& dataString)
2612 {
2613 // Throw an exception if the serial port is not open.
2614 if (not this->IsOpen())
2615 {
2616 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2617 }
2618
2619 size_t number_of_bytes = dataString.size() ;
2620
2621 // Nothing needs to be done if there is no data in the string.
2622 if (number_of_bytes <= 0)
2623 {
2624 return ;
2625 }
2626
2627 // Local variables.
2628 size_t number_of_bytes_written = 0 ;
2629 size_t number_of_bytes_remaining = number_of_bytes ;
2630
2631 // Write the data to the serial port. Keep retrying if EAGAIN
2632 // error is received and EWOULDBLOCK is not received.
2633 ssize_t write_result = 0 ;
2634
2635 while (number_of_bytes_remaining > 0)
2636 {
2637 write_result = call_with_retry(write,
2638 this->mFileDescriptor,
2639 &dataString[number_of_bytes_written],
2640 number_of_bytes_remaining) ;
2641
2642 if (write_result >= 0)
2643 {
2644 number_of_bytes_written += write_result ;
2645 number_of_bytes_remaining = number_of_bytes - number_of_bytes_written ;
2646
2647 if (number_of_bytes_remaining == 0)
2648 {
2649 break ;
2650 }
2651 }
2652 else if (write_result <= 0 &&
2653 errno != EWOULDBLOCK)
2654 {
2655 throw std::runtime_error(std::strerror(errno)) ;
2656 }
2657 }
2658 }
2659
2660 inline
2661 void
2663 {
2664 // Throw an exception if the serial port is not open.
2665 if (not this->IsOpen())
2666 {
2667 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2668 }
2669
2670 // Write the data to the serial port. Keep retrying if EAGAIN
2671 // error is received and EWOULDBLOCK is not received.
2672 ssize_t write_result = 0 ;
2673
2674 while (write_result <= 0)
2675 {
2676 write_result = call_with_retry(write,
2677 this->mFileDescriptor,
2678 &charBuffer,
2679 1) ;
2680
2681 if (write_result == 1)
2682 {
2683 break ;
2684 }
2685
2686 if ((write_result <= 0) and
2687 (errno != EWOULDBLOCK))
2688 {
2689 throw std::runtime_error(std::strerror(errno)) ;
2690 }
2691 }
2692 }
2693
2694 inline
2695 void
2696 SerialPort::Implementation::WriteByte(const unsigned char charBuffer)
2697 {
2698 // Throw an exception if the serial port is not open.
2699 if (not this->IsOpen())
2700 {
2701 throw NotOpen(ERR_MSG_PORT_NOT_OPEN) ;
2702 }
2703
2704 // Write the data to the serial port. Keep retrying if EAGAIN
2705 // error is received and EWOULDBLOCK is not received.
2706 ssize_t write_result = 0 ;
2707
2708 while (write_result <= 0)
2709 {
2710 write_result = call_with_retry(write,
2711 this->mFileDescriptor,
2712 &charBuffer,
2713 1) ;
2714
2715 if (write_result == 1)
2716 {
2717 break ;
2718 }
2719
2720 if ((write_result <= 0) and
2721 (errno != EWOULDBLOCK))
2722 {
2723 throw std::runtime_error(std::strerror(errno)) ;
2724 }
2725 }
2726 }
2727} // namespace LibSerial
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.
void Open(const std::string &fileName, const std::ios_base::openmode &openMode)
Opens the serial port associated with the specified file name and the specified mode.
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 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 ...
Definition SerialPort.h:56
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.
void Open(const std::string &fileName, const std::ios_base::openmode &openMode=std::ios_base::in|std::ios_base::out)
Opens the serial port associated with the specified file name and the specified mode.
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.
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.