Commit 2e35cebd53a35d3059c893d6d2a35b1e5b29daa4
1 parent
9bfbe366
Working on basic read/write unit test using two virtual serial ports.
Showing
10 changed files
with
327 additions
and
80 deletions
.gitignore
.idea/CppLinuxSerial.iml
0 → 100644
.idea/misc.xml
0 → 100644
.idea/modules.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<project version="4"> | |
| 3 | + <component name="ProjectModuleManager"> | |
| 4 | + <modules> | |
| 5 | + <module fileurl="file://$PROJECT_DIR$/.idea/CppLinuxSerial.iml" filepath="$PROJECT_DIR$/.idea/CppLinuxSerial.iml" /> | |
| 6 | + </modules> | |
| 7 | + </component> | |
| 8 | +</project> | |
| 0 | 9 | \ No newline at end of file | ... | ... |
.idea/vcs.xml
0 → 100644
CMakeLists.txt
include/CppLinuxSerial/SerialPort.hpp
| ... | ... | @@ -19,85 +19,92 @@ |
| 19 | 19 | |
| 20 | 20 | // User headers |
| 21 | 21 | |
| 22 | -namespace mn | |
| 23 | -{ | |
| 24 | -namespace CppLinuxSerial | |
| 25 | -{ | |
| 22 | +namespace mn { | |
| 23 | + namespace CppLinuxSerial { | |
| 26 | 24 | |
| 27 | 25 | /// \brief Strongly-typed enumeration of baud rates for use with the SerialPort class |
| 28 | -enum class BaudRate | |
| 29 | -{ | |
| 30 | - none, | |
| 31 | - b9600, | |
| 32 | - b57600 | |
| 33 | -}; | |
| 26 | + enum class BaudRate { | |
| 27 | + none, | |
| 28 | + b9600, | |
| 29 | + b57600 | |
| 30 | + }; | |
| 31 | + | |
| 32 | + enum class State { | |
| 33 | + UNCONFIGURED, | |
| 34 | + CLOSED, | |
| 35 | + OPEN | |
| 36 | + }; | |
| 34 | 37 | |
| 35 | 38 | /// \brief SerialPort object is used to perform rx/tx serial communication. |
| 36 | -class SerialPort | |
| 37 | -{ | |
| 39 | + class SerialPort { | |
| 38 | 40 | |
| 39 | - public: | |
| 40 | - /// \brief Default constructor. | |
| 41 | - SerialPort(); | |
| 41 | + public: | |
| 42 | + /// \brief Default constructor. | |
| 43 | + SerialPort(); | |
| 42 | 44 | |
| 43 | - /// \brief Constructor that sets up serial port with all required parameters. | |
| 44 | - SerialPort(const std::string& device, BaudRate baudRate); | |
| 45 | + /// \brief Constructor that sets up serial port with all required parameters. | |
| 46 | + SerialPort(const std::string &device, BaudRate baudRate); | |
| 45 | 47 | |
| 46 | - //! @brief Destructor | |
| 47 | - virtual ~SerialPort(); | |
| 48 | + //! @brief Destructor. Closes serial port if still open. | |
| 49 | + virtual ~SerialPort(); | |
| 48 | 50 | |
| 49 | - //! @brief Sets the file path to use for communications. The file path must be set before Open() is called, otherwise Open() will return an error. | |
| 50 | - void SetDevice(const std::string& device); | |
| 51 | + //! @brief Sets the file path to use for communications. The file path must be set before Open() is called, otherwise Open() will return an error. | |
| 52 | + void SetDevice(const std::string &device); | |
| 51 | 53 | |
| 52 | - void SetBaudRate(BaudRate baudRate); | |
| 54 | + void SetBaudRate(BaudRate baudRate); | |
| 53 | 55 | |
| 54 | - //! @brief Controls what happens when Read() is called. | |
| 55 | - //! @param numOfCharToWait Minimum number of characters to wait for before returning. Set to 0 for non-blocking mode. | |
| 56 | - void SetNumCharsToWait(uint32_t numCharsToWait); | |
| 56 | + //! @brief Controls what happens when Read() is called. | |
| 57 | + //! @param numOfCharToWait Minimum number of characters to wait for before returning. Set to 0 for non-blocking mode. | |
| 58 | + void SetNumCharsToWait(uint32_t numCharsToWait); | |
| 57 | 59 | |
| 58 | - //! @brief Enables/disables echo. | |
| 59 | - //! param echoOn Pass in true to enable echo, false to disable echo. | |
| 60 | - void EnableEcho(bool echoOn); | |
| 60 | + //! @brief Enables/disables echo. | |
| 61 | + //! param echoOn Pass in true to enable echo, false to disable echo. | |
| 62 | + void EnableEcho(bool echoOn); | |
| 61 | 63 | |
| 62 | - //! @brief Opens the COM port for use. | |
| 63 | - //! @throws {std::runtime_error} if filename has not been set. | |
| 64 | - //! {std::system_error} if system open() operation fails. | |
| 65 | - //! @note Must call this before you can configure the COM port. | |
| 66 | - void Open(); | |
| 64 | + //! @brief Opens the COM port for use. | |
| 65 | + //! @throws {std::runtime_error} if filename has not been set. | |
| 66 | + //! {std::system_error} if system open() operation fails. | |
| 67 | + //! @note Must call this before you can configure the COM port. | |
| 68 | + void Open(); | |
| 67 | 69 | |
| 68 | - /// \brief Configures the tty device as a serial port. | |
| 69 | - void ConfigureDeviceAsSerialPort(); | |
| 70 | + /// \brief Configures the tty device as a serial port. | |
| 71 | + /// \warning Device must be open (valid file descriptor) when this is called. | |
| 72 | + void ConfigureDeviceAsSerialPort(); | |
| 70 | 73 | |
| 71 | - //! @brief Closes the COM port. | |
| 72 | - void Close(); | |
| 74 | + //! @brief Closes the COM port. | |
| 75 | + void Close(); | |
| 73 | 76 | |
| 74 | - //! @brief Sends a message over the com port. | |
| 75 | - //! @param str Reference to an string containing the characters to write to the COM port. | |
| 76 | - //! @throws {std::runtime_error} if filename has not been set. | |
| 77 | - //! {std::system_error} if system write() operation fails. | |
| 78 | - void Write(std::string *str); | |
| 77 | + //! @brief Sends a message over the com port. | |
| 78 | + //! @param str Reference to an string containing the characters to write to the COM port. | |
| 79 | + //! @throws {std::runtime_error} if filename has not been set. | |
| 80 | + //! {std::system_error} if system write() operation fails. | |
| 81 | + void Write(std::string *str); | |
| 79 | 82 | |
| 80 | - //! @brief Use to read from the COM port. | |
| 81 | - //! @param str Reference to a string that the read characters from the COM port will be saved to. | |
| 82 | - //! @throws {std::runtime_error} if filename has not been set. | |
| 83 | - //! {std::system_error} if system read() operation fails. | |
| 84 | - void Read(std::string *str); | |
| 83 | + //! @brief Use to read from the COM port. | |
| 84 | + //! @param str Reference to a string that the read characters from the COM port will be saved to. | |
| 85 | + //! @throws {std::runtime_error} if filename has not been set. | |
| 86 | + //! {std::system_error} if system read() operation fails. | |
| 87 | + void Read(std::string *str); | |
| 85 | 88 | |
| 86 | - private: | |
| 87 | - std::string device_; | |
| 89 | + private: | |
| 88 | 90 | |
| 89 | - BaudRate baudRate_; | |
| 91 | + /// \brief Keeps track of the serial port's state. | |
| 92 | + State state_; | |
| 90 | 93 | |
| 91 | - //! @brief The file descriptor for the open file. This gets written to when Open() is called. | |
| 92 | - int fileDesc; | |
| 94 | + std::string device_; | |
| 93 | 95 | |
| 94 | - //! @brief Returns a populated termios structure for the passed in file descriptor. | |
| 95 | - termios GetTermios(); | |
| 96 | + BaudRate baudRate_; | |
| 96 | 97 | |
| 97 | - void SetTermios(termios myTermios); | |
| 98 | -}; | |
| 98 | + //! @brief The file descriptor for the open file. This gets written to when Open() is called. | |
| 99 | + int fileDesc_; | |
| 99 | 100 | |
| 100 | -} // namespace CppLinuxSerial | |
| 101 | + //! @brief Returns a populated termios structure for the passed in file descriptor. | |
| 102 | + termios GetTermios(); | |
| 103 | + | |
| 104 | + void SetTermios(termios myTermios); | |
| 105 | + }; | |
| 106 | + | |
| 107 | + } // namespace CppLinuxSerial | |
| 101 | 108 | } // namespace mn |
| 102 | 109 | |
| 103 | 110 | #endif // #ifndef SERIAL_PORT_SERIAL_PORT_H | ... | ... |
src/SerialPort.cpp
| 1 | 1 | //! |
| 2 | 2 | //! @file SerialPort.cpp |
| 3 | -//! @author Geoffrey Hunter <gbmhunter@gmail.com> () | |
| 3 | +//! @author Geoffrey Hunter <gbmhunter@gmail.com> (www.mbedded.ninja) | |
| 4 | 4 | //! @created 2014-01-07 |
| 5 | 5 | //! @last-modified 2017-11-23 |
| 6 | 6 | //! @brief The main serial port class. |
| ... | ... | @@ -22,9 +22,7 @@ |
| 22 | 22 | namespace mn { |
| 23 | 23 | namespace CppLinuxSerial { |
| 24 | 24 | |
| 25 | - SerialPort::SerialPort() : | |
| 26 | - SerialPort("", BaudRate::none) | |
| 27 | - { | |
| 25 | + SerialPort::SerialPort() { | |
| 28 | 26 | } |
| 29 | 27 | |
| 30 | 28 | SerialPort::SerialPort(const std::string& device, BaudRate baudRate) { |
| ... | ... | @@ -32,14 +30,18 @@ namespace CppLinuxSerial { |
| 32 | 30 | baudRate_ = baudRate; |
| 33 | 31 | } |
| 34 | 32 | |
| 35 | - SerialPort::~SerialPort() | |
| 36 | - { | |
| 37 | - | |
| 33 | + SerialPort::~SerialPort() { | |
| 34 | + try { | |
| 35 | + Close(); | |
| 36 | + } catch(...) { | |
| 37 | + // We can't do anything about this! | |
| 38 | + } | |
| 38 | 39 | } |
| 39 | 40 | |
| 40 | 41 | void SerialPort::SetDevice(const std::string& device) |
| 41 | 42 | { |
| 42 | 43 | device_ = device; |
| 44 | + ConfigureDeviceAsSerialPort(); | |
| 43 | 45 | } |
| 44 | 46 | |
| 45 | 47 | void SerialPort::SetBaudRate(BaudRate baudRate) |
| ... | ... | @@ -89,18 +91,19 @@ namespace CppLinuxSerial { |
| 89 | 91 | |
| 90 | 92 | // O_RDONLY for read-only, O_WRONLY for write only, O_RDWR for both read/write access |
| 91 | 93 | // 3rd, optional parameter is mode_t mode |
| 92 | - this->fileDesc = open(device_.c_str(), O_RDWR); | |
| 94 | + fileDesc_ = open(device_.c_str(), O_RDWR); | |
| 93 | 95 | |
| 94 | 96 | // Check status |
| 95 | - if (this->fileDesc == -1) | |
| 96 | - { | |
| 97 | + if(fileDesc_ == -1) { | |
| 97 | 98 | // Could not open COM port |
| 98 | 99 | //this->sp->PrintError(SmartPrint::Ss() << "Unable to open " << this->filePath << " - " << strerror(errno)); |
| 99 | 100 | //return false; |
| 100 | 101 | |
| 101 | - throw std::system_error(EFAULT, std::system_category()); | |
| 102 | + throw std::runtime_error("Could not open device " + device_ + ". Is the device name correct and do you have read/write permission?"); | |
| 102 | 103 | } |
| 103 | 104 | |
| 105 | + ConfigureDeviceAsSerialPort(); | |
| 106 | + | |
| 104 | 107 | std::cout << "COM port opened successfully." << std::endl; |
| 105 | 108 | |
| 106 | 109 | // If code reaches here, open and config must of been successful |
| ... | ... | @@ -231,7 +234,7 @@ namespace CppLinuxSerial { |
| 231 | 234 | |
| 232 | 235 | void SerialPort::Write(std::string* str) |
| 233 | 236 | { |
| 234 | - if(this->fileDesc == 0) | |
| 237 | + if(this->fileDesc_ == 0) | |
| 235 | 238 | { |
| 236 | 239 | //this->sp->PrintError(SmartPrint::Ss() << ); |
| 237 | 240 | //return false; |
| ... | ... | @@ -239,7 +242,7 @@ namespace CppLinuxSerial { |
| 239 | 242 | throw std::runtime_error("SendMsg called but file descriptor (fileDesc) was 0, indicating file has not been opened."); |
| 240 | 243 | } |
| 241 | 244 | |
| 242 | - int writeResult = write(this->fileDesc, str->c_str(), str->size()); | |
| 245 | + int writeResult = write(this->fileDesc_, str->c_str(), str->size()); | |
| 243 | 246 | |
| 244 | 247 | // Check status |
| 245 | 248 | if (writeResult == -1) |
| ... | ... | @@ -256,7 +259,7 @@ namespace CppLinuxSerial { |
| 256 | 259 | |
| 257 | 260 | void SerialPort::Read(std::string* str) |
| 258 | 261 | { |
| 259 | - if(this->fileDesc == 0) | |
| 262 | + if(this->fileDesc_ == 0) | |
| 260 | 263 | { |
| 261 | 264 | //this->sp->PrintError(SmartPrint::Ss() << "Read() was called but file descriptor (fileDesc) was 0, indicating file has not been opened."); |
| 262 | 265 | //return false; |
| ... | ... | @@ -268,7 +271,7 @@ namespace CppLinuxSerial { |
| 268 | 271 | memset (&buf, '\0', sizeof buf); |
| 269 | 272 | |
| 270 | 273 | // Read from file |
| 271 | - int n = read(this->fileDesc, &buf, sizeof(buf)); | |
| 274 | + int n = read(this->fileDesc_, &buf, sizeof(buf)); | |
| 272 | 275 | |
| 273 | 276 | // Error Handling |
| 274 | 277 | if(n < 0) |
| ... | ... | @@ -295,11 +298,14 @@ namespace CppLinuxSerial { |
| 295 | 298 | |
| 296 | 299 | termios SerialPort::GetTermios() |
| 297 | 300 | { |
| 301 | + if(fileDesc_ == -1) | |
| 302 | + throw std::runtime_error("GetTermios() called but file descriptor was not valid."); | |
| 303 | + | |
| 298 | 304 | struct termios tty; |
| 299 | 305 | memset(&tty, 0, sizeof(tty)); |
| 300 | 306 | |
| 301 | 307 | // Get current settings (will be stored in termios structure) |
| 302 | - if(tcgetattr(this->fileDesc, &tty) != 0) | |
| 308 | + if(tcgetattr(fileDesc_, &tty) != 0) | |
| 303 | 309 | { |
| 304 | 310 | // Error occurred |
| 305 | 311 | std::cout << "Could not get terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl; |
| ... | ... | @@ -313,9 +319,9 @@ namespace CppLinuxSerial { |
| 313 | 319 | void SerialPort::SetTermios(termios myTermios) |
| 314 | 320 | { |
| 315 | 321 | // Flush port, then apply attributes |
| 316 | - tcflush(this->fileDesc, TCIFLUSH); | |
| 322 | + tcflush(this->fileDesc_, TCIFLUSH); | |
| 317 | 323 | |
| 318 | - if(tcsetattr(this->fileDesc, TCSANOW, &myTermios) != 0) | |
| 324 | + if(tcsetattr(this->fileDesc_, TCSANOW, &myTermios) != 0) | |
| 319 | 325 | { |
| 320 | 326 | // Error occurred |
| 321 | 327 | std::cout << "Could not apply terminal attributes for \"" << device_ << "\" - " << strerror(errno) << std::endl; |
| ... | ... | @@ -326,5 +332,17 @@ namespace CppLinuxSerial { |
| 326 | 332 | // Successful! |
| 327 | 333 | } |
| 328 | 334 | |
| 335 | + void SerialPort::Close() { | |
| 336 | + if(fileDesc_ != -1) { | |
| 337 | + auto retVal = close(fileDesc_); | |
| 338 | + if(retVal != 0) | |
| 339 | + throw std::runtime_error("Tried to close serial port " + device_ + ", but close() failed."); | |
| 340 | + | |
| 341 | + fileDesc_ = -1; | |
| 342 | + } | |
| 343 | + | |
| 344 | + state_ = State::CLOSED; | |
| 345 | + } | |
| 346 | + | |
| 329 | 347 | } // namespace CppLinuxSerial |
| 330 | 348 | } // namespace mn | ... | ... |
test/unit/BasicTests.cpp
| 1 | +/// | |
| 2 | +/// \file BasicTests.cpp | |
| 3 | +/// \author Geoffrey Hunter <gbmhunter@gmail.com> (www.mbedded.ninja) | |
| 4 | +/// \created 2017-11-24 | |
| 5 | +/// \last-modified 2017-11-24 | |
| 6 | +/// \brief Basic tests for the SerialPort class. | |
| 7 | +/// \details | |
| 8 | +/// See README.rst in repo root dir for more info. | |
| 9 | + | |
| 10 | +// System includes | |
| 1 | 11 | #include "gtest/gtest.h" |
| 2 | 12 | |
| 13 | +// 3rd party includes | |
| 3 | 14 | #include "CppLinuxSerial/SerialPort.hpp" |
| 4 | 15 | |
| 16 | +// User includes | |
| 17 | +#include "TestUtil.hpp" | |
| 18 | + | |
| 5 | 19 | using namespace mn::CppLinuxSerial; |
| 6 | 20 | |
| 7 | 21 | namespace { |
| ... | ... | @@ -9,11 +23,29 @@ namespace { |
| 9 | 23 | class BasicTests : public ::testing::Test { |
| 10 | 24 | protected: |
| 11 | 25 | |
| 26 | + static void SetUpTestCase() { | |
| 27 | + GetTestUtil().CreateVirtualSerialPortPair(); | |
| 28 | + } | |
| 29 | + | |
| 30 | + static void TearDownTestCase() { | |
| 31 | + std::cout << "Destroying virtual serial ports..." << std::endl; | |
| 32 | + GetTestUtil().CloseSerialPorts(); | |
| 33 | + } | |
| 34 | + | |
| 35 | + static TestUtil& GetTestUtil() { | |
| 36 | + static TestUtil testUtil; | |
| 37 | + return testUtil; | |
| 38 | + } | |
| 39 | + | |
| 40 | + | |
| 12 | 41 | BasicTests() { |
| 13 | 42 | } |
| 14 | 43 | |
| 15 | 44 | virtual ~BasicTests() { |
| 16 | 45 | } |
| 46 | + | |
| 47 | + std::string device0_ = "/dev/ttyS10"; | |
| 48 | + std::string device1_ = "/dev/ttyS11"; | |
| 17 | 49 | }; |
| 18 | 50 | |
| 19 | 51 | TEST_F(BasicTests, CanBeConstructed) { |
| ... | ... | @@ -22,9 +54,17 @@ namespace { |
| 22 | 54 | } |
| 23 | 55 | |
| 24 | 56 | TEST_F(BasicTests, CanOpen) { |
| 25 | - SerialPort serialPort; | |
| 26 | - serialPort.Open(); | |
| 27 | - EXPECT_EQ(true, true); | |
| 57 | + SerialPort serialPort0(device0_, BaudRate::b57600); | |
| 58 | + serialPort0.Open(); | |
| 59 | + } | |
| 60 | + | |
| 61 | + TEST_F(BasicTests, ReadWrite) { | |
| 62 | + SerialPort serialPort0(device0_, BaudRate::b57600); | |
| 63 | + serialPort0.Open(); | |
| 64 | + | |
| 65 | + SerialPort serialPort1(device1_, BaudRate::b57600); | |
| 66 | + serialPort1.Open(); | |
| 67 | + | |
| 28 | 68 | } |
| 29 | 69 | |
| 30 | 70 | } // namespace |
| 31 | 71 | \ No newline at end of file | ... | ... |
test/unit/TestUtil.hpp
0 → 100644
| 1 | +/// | |
| 2 | +/// \file TestUtil.hpp | |
| 3 | +/// \author Geoffrey Hunter <gbmhunter@gmail.com> (www.mbedded.ninja) | |
| 4 | +/// \created 2017-11-24 | |
| 5 | +/// \last-modified 2017-11-24 | |
| 6 | +/// \brief Contains utility methods to help with testing. | |
| 7 | +/// \details | |
| 8 | +/// See README.rst in repo root dir for more info. | |
| 9 | + | |
| 10 | +#ifndef MN_CPP_LINUX_SERIAL_TEST_UTIL_H_ | |
| 11 | +#define MN_CPP_LINUX_SERIAL_TEST_UTIL_H_ | |
| 12 | + | |
| 13 | +// System includes | |
| 14 | +#include <string> | |
| 15 | +#include <array> | |
| 16 | +#include <memory> | |
| 17 | +#include <iostream> | |
| 18 | +#include <thread> | |
| 19 | +#include <chrono> | |
| 20 | + | |
| 21 | +// 3rd party includes | |
| 22 | + | |
| 23 | + | |
| 24 | +using namespace std::literals; | |
| 25 | + | |
| 26 | +#define READ 0 | |
| 27 | +#define WRITE 1 | |
| 28 | +FILE * popen2(std::string command, std::string type, int & pid) | |
| 29 | +{ | |
| 30 | + pid_t child_pid; | |
| 31 | + int fd[2]; | |
| 32 | + pipe(fd); | |
| 33 | + | |
| 34 | + if((child_pid = fork()) == -1) | |
| 35 | + { | |
| 36 | + perror("fork"); | |
| 37 | + exit(1); | |
| 38 | + } | |
| 39 | + | |
| 40 | + /* child process */ | |
| 41 | + if (child_pid == 0) | |
| 42 | + { | |
| 43 | + if (type == "r") | |
| 44 | + { | |
| 45 | + close(fd[READ]); //Close the READ end of the pipe since the child's fd is write-only | |
| 46 | + dup2(fd[WRITE], 1); //Redirect stdout to pipe | |
| 47 | + } | |
| 48 | + else | |
| 49 | + { | |
| 50 | + close(fd[WRITE]); //Close the WRITE end of the pipe since the child's fd is read-only | |
| 51 | + dup2(fd[READ], 0); //Redirect stdin to pipe | |
| 52 | + } | |
| 53 | + | |
| 54 | + setpgid(child_pid, child_pid); //Needed so negative PIDs can kill children of /bin/sh | |
| 55 | + execl("/bin/sh", "/bin/sh", "-c", command.c_str(), NULL); | |
| 56 | + exit(0); | |
| 57 | + } | |
| 58 | + else | |
| 59 | + { | |
| 60 | + if (type == "r") | |
| 61 | + { | |
| 62 | + close(fd[WRITE]); //Close the WRITE end of the pipe since parent's fd is read-only | |
| 63 | + } | |
| 64 | + else | |
| 65 | + { | |
| 66 | + close(fd[READ]); //Close the READ end of the pipe since parent's fd is write-only | |
| 67 | + } | |
| 68 | + } | |
| 69 | + | |
| 70 | + pid = child_pid; | |
| 71 | + | |
| 72 | + if (type == "r") | |
| 73 | + { | |
| 74 | + return fdopen(fd[READ], "r"); | |
| 75 | + } | |
| 76 | + | |
| 77 | + return fdopen(fd[WRITE], "w"); | |
| 78 | +} | |
| 79 | + | |
| 80 | +int pclose2(FILE * fp, pid_t pid) | |
| 81 | +{ | |
| 82 | + int stat; | |
| 83 | + | |
| 84 | + fclose(fp); | |
| 85 | + while (waitpid(pid, &stat, 0) == -1) | |
| 86 | + { | |
| 87 | + if (errno != EINTR) | |
| 88 | + { | |
| 89 | + stat = -1; | |
| 90 | + break; | |
| 91 | + } | |
| 92 | + } | |
| 93 | + | |
| 94 | + return stat; | |
| 95 | +} | |
| 96 | + | |
| 97 | +struct ProcessInfo { | |
| 98 | + FILE* fp; | |
| 99 | + pid_t pid; | |
| 100 | +}; | |
| 101 | + | |
| 102 | + | |
| 103 | +namespace mn { | |
| 104 | + namespace CppLinuxSerial { | |
| 105 | + | |
| 106 | + class TestUtil { | |
| 107 | + | |
| 108 | + public: | |
| 109 | + /// \brief Executes a command on the Linux command-line. | |
| 110 | + /// \details Blocks until command is complete. | |
| 111 | + /// \throws std::runtime_error is popen() fails. | |
| 112 | + static std::string Exec(const std::string &cmd) { | |
| 113 | + std::array<char, 128> buffer; | |
| 114 | + std::string result; | |
| 115 | + std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose); | |
| 116 | + if (!pipe) throw std::runtime_error("popen() failed!"); | |
| 117 | + | |
| 118 | + while (!feof(pipe.get())) { | |
| 119 | + if (fgets(buffer.data(), 128, pipe.get()) != nullptr) | |
| 120 | + result += buffer.data(); | |
| 121 | + } | |
| 122 | + | |
| 123 | + return result; | |
| 124 | + } | |
| 125 | + | |
| 126 | + void StartProcess(const std::string &cmd) { | |
| 127 | + std::array<char, 128> buffer; | |
| 128 | + std::string result; | |
| 129 | + int pid; | |
| 130 | + FILE * fp = popen2(cmd, "r", pid); | |
| 131 | + ProcessInfo processInfo; | |
| 132 | + processInfo.fp = fp; | |
| 133 | + processInfo.pid = pid; | |
| 134 | + processes_.push_back(processInfo); | |
| 135 | + } | |
| 136 | + | |
| 137 | + void CreateVirtualSerialPortPair() { | |
| 138 | + std::cout << "Creating virtual serial port pair..." << std::endl; | |
| 139 | + StartProcess("sudo socat -d -d pty,raw,echo=0,link=/dev/ttyS10 pty,raw,echo=0,link=/dev/ttyS11"); | |
| 140 | + std::this_thread::sleep_for(1s); | |
| 141 | + StartProcess("sudo chmod a+rw /dev/ttyS10"); | |
| 142 | + StartProcess("sudo chmod a+rw /dev/ttyS11"); | |
| 143 | + std::this_thread::sleep_for(1s); | |
| 144 | + std::cout << "Finished creating virtual serial port pair." << std::endl; | |
| 145 | + } | |
| 146 | + | |
| 147 | + void CloseSerialPorts() { | |
| 148 | + for(const auto& filePointer : processes_) { | |
| 149 | + kill(filePointer.pid, SIGKILL); | |
| 150 | + pclose2(filePointer.fp, filePointer.pid); | |
| 151 | + } | |
| 152 | + } | |
| 153 | + | |
| 154 | + std::vector<ProcessInfo> processes_; | |
| 155 | + | |
| 156 | + }; | |
| 157 | + } // namespace CppLinuxSerial | |
| 158 | +} // namespace mn | |
| 159 | + | |
| 160 | +#endif // #ifndef MN_CPP_LINUX_SERIAL_TEST_UTIL_H_ | ... | ... |