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