diff --git a/include/rs232.hpp b/include/rs232.hpp index a026e14..469f6d9 100644 --- a/include/rs232.hpp +++ b/include/rs232.hpp @@ -3,8 +3,7 @@ #include "rs232_native.hpp" -#include -#include +#include namespace sakurajin { @@ -38,6 +37,30 @@ namespace sakurajin { */ std::atomic currentDevice = 0; + std::shared_mutex deviceMutex; + + /** + * @brief The work thread that actually does most of the work + */ + std::future workThread; + std::atomic stopThread = false; + + std::string readBuffer; + std::shared_mutex readBufferMutex; + std::atomic readBufferHasData = false; + + std::string writeBuffer; + std::shared_mutex writeBufferMutex; + std::atomic writeBufferHasData = false; + + /** + * @brief The function that is executed by the work thread + * It performs the actual read/write operations constantly in the background. + * This function only does one loop iteration and then returns. + * It should be called in a loop to keep the query system running. + */ + void work(); + public: /** * @brief Construct a new RS232 object with a single device @@ -79,153 +102,121 @@ namespace sakurajin { ~RS232(); /** - * @brief Get a pointer to a native device - * If the index is not a valid index the current device will be returned. - * - * @param index the index of the device that should be returned - */ - [[nodiscard]] [[maybe_unused]] - std::shared_ptr getNativeDevice(size_t index = -1) const; - - /** - * @brief Get the number of devices that are added to this class - * @return The size of the list of devices - */ - [[nodiscard]] [[maybe_unused]] - size_t getDeviceCount() const; - - /** - * @brief Checks if the connection was started successfully + * @brief connect to the first available device * - * @return true the connection is established as expected - * @return false there was an error while initializing the connection or some of the settings are not valid + * @return true if the connection was established successfully + * @return false if the connection could not be established */ - [[nodiscard]] [[maybe_unused]] - bool IsAvailable(size_t index = -1) const; + [[maybe_unused]] + bool Connect(); /** - * @brief Get the name of the port used for this RS232 connection - * If the index is not a valid index the current device will be checked. - * - * @param index the index of the device that should be used + * @brief disconnect from all devices + * If no device is connected this will exit early otherwise it will disconnect all devices. */ - [[nodiscard]] [[maybe_unused]] - std::string_view GetDeviceName(size_t index = -1) const; + [[maybe_unused]] + void DisconnectAll(); /** - * @brief reads until the next character is received + * @brief Get a pointer to a native device + * If the index is not a valid index the current device will be returned. * - * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong - * The return value is >= 0 if everything is okay and < 0 if something went wrong + * @param index the index of the device that should be returned */ [[nodiscard]] [[maybe_unused]] - std::tuple ReadNextChar(); + std::shared_ptr getNativeDevice(size_t index) const; /** - * @brief reads until the next character is received or the waitTaime is over - * - * @param waitTime the duration that should be waited for a signal before stopping the function. - * @param ignoreTime true if the duration value should be ignored (the same as no parameter) - * - * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong - * The return value is >= 0 if everything is okay and < 0 if something went wrong + * @brief Get a pointer to the current native device + * This is the same as calling getNativeDevice(currentDevice) but is more convenient since its used more often. */ - template > [[nodiscard]] [[maybe_unused]] - std::tuple ReadNextChar(std::chrono::duration waitTime, - bool ignoreTime = false, - std::shared_ptr transferDevice = nullptr); + std::shared_ptr getCurrentDevice() const; /** - * @brief reads the interface until a newline (\n) is received - * - * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + * @brief check if the current device is available + * @return bool true if the current device is connected and ready to use */ [[nodiscard]] [[maybe_unused]] - std::tuple ReadNextMessage(); + bool IsAvailable() const; /** - * @brief reads the interface until a newline (\n) is received or the waitTime is over - * The waitTime is the time the code will wait for each next character. If the delay between the - * characters is too long the function will return an error. - * - * @param waitTime the duration that should be waited for a signal before stopping the function. - * @param ignoreTime true if the duration value should be ignored (the same as no parameter) - * - * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + * @brief Get the number of devices that are added to this class + * @return The size of the list of devices */ - template > [[nodiscard]] [[maybe_unused]] - std::tuple ReadNextMessage(std::chrono::duration waitTime, bool ignoreTime = false); + size_t getDeviceCount() const; /** - * @brief read the interface until one of the stop conditions is reached - * - * @param conditions a vector containing all the stop conditions. - * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + * @brief Empty the read buffer and return its content + * @warning the read buffer will be cleared after this function is called. + * @return std::string the content of the read buffer */ [[nodiscard]] [[maybe_unused]] - std::tuple ReadUntil(const std::vector& conditions); + std::string&& retrieveReadBuffer(); /** - * @brief read the interface until one of the stop conditions is reached or the waitTaime is over - * The waitTime is the time the code will wait for each next character. If the delay between the - * characters is too long the function will return an error. - * - * @param waitTime the duration that should be waited for a signal before stopping the function. - * @param ignoreTime true if the duration value should be ignored (the same as no parameter) - * - * @param conditions a vector containing all the stop conditions. - * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + * @brief load the read buffer and return the first match with a regex + * This function uses the std::regex_search function to find the first match of the read buffer. + * If no match is found an empty string is returned. + * @note this function clears the read buffer until the end of the match. + * If the buffer contains "Hello World!" and the pattern is "World" the buffer will be cleared until the end of the match. + * The buffer will then contain "!". Everything in front of the match will be discarded. + * @param pattern the regex pattern that should be used + * @return std::string the first match of the read buffer */ - template > [[nodiscard]] [[maybe_unused]] - std::tuple - ReadUntil(const std::vector& conditions, std::chrono::duration waitTime, bool ignoreTime = false); + std::string&& retrieveFirstMatch(const std::regex& pattern); /** * @brief print a string to the currently connected device - * @param text the test to send + * This function adds the string to the write buffer and then returns. + * The write buffer is then written to the device in the background by the work thread. + * Because of this the actual write operation might be delayed. + * For a more immediate write operation use the native device directly. + * + * @param text the text to send */ [[maybe_unused]] - void Print(const std::string& text, std::ostream& errorStream = std::cerr); + void Print(const std::string& text); + /** - * @brief close the connection to the device. - * @warning this disables all following transactions to the device. + * @brief check if the clear to send flag is set * + * @return true if the flag is set + * @return false if the flag is not set */ - [[deprecated("use DisconnectAll() instead")]] - void Close(); + [[nodiscard]] [[maybe_unused]] [[deprecated("get the native device and retrieve the information from there")]] + bool IsCTSEnabled() const; /** - * @brief connect to the first available device + * @brief Checks if the connection was started successfully * - * @return true if the connection was established successfully - * @return false if the connection could not be established + * @return true the connection is established as expected + * @return false there was an error while initializing the connection or some of the settings are not valid */ - [[maybe_unused]] - bool Connect(); + [[nodiscard]] [[maybe_unused]] [[deprecated("get the native device and retrieve the information from there")]] + bool IsAvailable(size_t index) const; /** - * @brief disconnect from all devices - * If no device is connected this will exit early otherwise it will disconnect all devices. + * @brief Get the name of the port used for this RS232 connection + * If the index is not a valid index the current device will be checked. + * + * @param index the index of the device that should be used */ - [[maybe_unused]] - void DisconnectAll(); + [[nodiscard]] [[maybe_unused]] [[deprecated("use getNativeDevice(index)->getDeviceName() instead")]] + std::string_view GetDeviceName(size_t index) const; /** - * @brief check if the clear to send flag is set + * @brief close the connection to the device. + * @warning this disables all following transactions to the device. * - * @return true if the flag is set - * @return false if the flag is not set */ - [[nodiscard]] [[maybe_unused]] [[deprecated("get the native device and retrieve the information from there")]] - bool IsCTSEnabled() const; + [[deprecated("use DisconnectAll() instead")]] + void Close(); }; } // namespace sakurajin -#include "rs232_template_implementations.hpp" - #endif // SAKURAJIN_RS232_HPP_INCLUDED diff --git a/include/rs232_native.hpp b/include/rs232_native.hpp index 1ee3438..09f8adb 100644 --- a/include/rs232_native.hpp +++ b/include/rs232_native.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,8 @@ #include #include #include +#include +#include #include namespace sakurajin { @@ -251,6 +254,167 @@ namespace sakurajin { int64_t retrieveFlags(bool block = true) noexcept; }; + /** + * @brief This namespace contains some external functions that operate on the RS232_native class + * They are not directly part of the class since they are abstractions in a way but they are still useful. + * Use these functions for convenient direct access to the RS232_native class. + * + * In version 1.x these were part of the RS232 wrapper class but they were moved here. + * This is because the wrapper class uses a new design where these functions no longer fit. + */ + namespace native { + + /** + * @brief reads until the next character is received or the waitTaime is over + * + * @param waitTime the duration that should be waited for a signal before stopping the function. + * @param ignoreTime true if the duration value should be ignored (the same as no parameter) + * + * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + * The return value is >= 0 if everything is okay and < 0 if something went wrong + */ + template > + [[nodiscard]] [[maybe_unused]] + std::tuple ReadNextChar(const std::shared_ptr& transferDevice, + std::chrono::duration waitTime, + bool ignoreTime = false) { + if (transferDevice == nullptr) { + return {'\0', -1}; + } + + if (transferDevice->getConnectionStatus() != sakurajin::connectionStatus::connected) { + return {'\0', -2}; + } + + auto startTime = std::chrono::high_resolution_clock::now(); + + char IOBuf = '\0'; + int64_t readLength; + + do { + readLength = transferDevice->readRawData(&IOBuf, 1, false); + + if (ignoreTime) { + std::this_thread::sleep_for(std::chrono::nanoseconds(1)); + continue; + } + + if (std::chrono::high_resolution_clock::now() - startTime > waitTime) { + return {'\0', -3}; + } + } while (readLength < 1); + + return {IOBuf, 0}; + } + + /** + * @brief read the interface until one of the stop conditions is reached or the waitTaime is over + * The waitTime is the time the code will wait for each next character. If the delay between the + * characters is too long the function will return an error. + * + * @param waitTime the duration that should be waited for a signal before stopping the function. + * @param ignoreTime true if the duration value should be ignored (the same as no parameter) + * + * @param conditions a vector containing all the stop conditions. + * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + */ + template > + [[nodiscard]] [[maybe_unused]] + std::tuple ReadUntil(const std::shared_ptr& transferDevice, + const std::vector& conditions, + std::chrono::duration waitTime, + bool ignoreTime = false) { + if (transferDevice == nullptr) { + return {"", -1}; + } + + if (transferDevice->getConnectionStatus() != sakurajin::connectionStatus::connected) { + return {"", -2}; + } + + std::string message; + int64_t errCode = 0; + char nextChar = '\n'; + bool stop = false; + + while (!stop) { + // read the next char and append if there was no error + std::tie(nextChar, errCode) = ReadNextChar(transferDevice, waitTime, ignoreTime); + + if (errCode < 0) { + return {"", -3}; + } + message += nextChar; + + // check if a stop condition is met + for (auto cond : conditions) { + if (cond == nextChar) { + stop = true; + break; + } + } + } + + return {message, 0}; + } + + /** + * @brief reads the interface until a newline (\n) is received or the waitTime is over + * The waitTime is the time the code will wait for each next character. If the delay between the + * characters is too long the function will return an error. + * + * @param waitTime the duration that should be waited for a signal before stopping the function. + * @param ignoreTime true if the duration value should be ignored (the same as no parameter) + * + * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + */ + template > + [[nodiscard]] [[maybe_unused]] + std::tuple ReadNextMessage(const std::shared_ptr& transferDevice, + std::chrono::duration waitTime, + bool ignoreTime = false) { + return ReadUntil(transferDevice, {'\n'}, waitTime, ignoreTime); + } + + /** + * @brief Directly output a string to the device. + * The string is written to the device and the function returns immediately after that. + * @note this function is blocking and will wait until a write can actually be performed. + * @param transferDevice The device that should be used for the transfer + * @param text The text that should be written to the device + * @return negative if an error occurred + */ + [[maybe_unused]] + RS232_EXPORT_MACRO int Print(const std::shared_ptr& transferDevice, const std::string& text); + + /** + * @brief reads until the next character is received + * + * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + * The return value is >= 0 if everything is okay and < 0 if something went wrong + */ + [[nodiscard]] [[maybe_unused]] + RS232_EXPORT_MACRO std::tuple ReadNextChar(const std::shared_ptr& transferDevice); + + /** + * @brief reads the interface until a newline (\n) is received + * + * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + */ + [[nodiscard]] [[maybe_unused]] + RS232_EXPORT_MACRO std::tuple ReadNextMessage(const std::shared_ptr& transferDevice); + + /** + * @brief read the interface until one of the stop conditions is reached + * + * @param conditions a vector containing all the stop conditions. + * @return std::tuple this tuple contains the wanted return data and an error code in case something went wrong + */ + [[nodiscard]] [[maybe_unused]] + RS232_EXPORT_MACRO std::tuple ReadUntil(const std::shared_ptr& transferDevice, + const std::vector& conditions); + } // namespace native + } // namespace sakurajin #endif // SAKURAJIN_RS232_NATIVE_HPP_INCLUDED diff --git a/include/rs232_template_implementations.hpp b/include/rs232_template_implementations.hpp deleted file mode 100644 index b7d07b4..0000000 --- a/include/rs232_template_implementations.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#ifdef SAKURAJIN_RS232_HPP_INCLUDED - -template -std::tuple -sakurajin::RS232::ReadNextChar(std::chrono::duration waitTime, bool ignoreTime, std::shared_ptr transferDevice) { - if (transferDevice == nullptr) { - transferDevice = rs232Devices[currentDevice]; - } - - if (transferDevice->getConnectionStatus() != sakurajin::connectionStatus::connected) { - return {'\0', -1}; - } - - auto startTime = std::chrono::high_resolution_clock::now(); - - char IOBuf = '\0'; - int errCode = 0; - - do { - errCode = transferDevice->readRawData(&IOBuf, 1); - - if (ignoreTime) { - continue; - } - - if (std::chrono::high_resolution_clock::now() - startTime > waitTime) { - return {'\0', -2}; - } - } while (errCode != 1); - - return {IOBuf, 0}; -} - - -template -std::tuple sakurajin::RS232::ReadNextMessage(std::chrono::duration waitTime, bool ignoreTime) { - return ReadUntil({'\n'}, waitTime, ignoreTime); -} - -template -std::tuple -sakurajin::RS232::ReadUntil(const std::vector& conditions, std::chrono::duration waitTime, bool ignoreTime) { - auto transferDevice = rs232Devices[currentDevice]; - if (transferDevice->getConnectionStatus() != sakurajin::connectionStatus::connected) { - return {"", -1}; - } - - std::string message = ""; - int errCode = 0; - char nextChar = '\n'; - bool stop = false; - - while (!stop) { - // read the next char and append if there was no error - std::tie(nextChar, errCode) = ReadNextChar(waitTime, ignoreTime, transferDevice); - - if (errCode < 0) { - return {"", -1}; - } - message += nextChar; - - // check if a stop condition is met - for (auto cond : conditions) { - if (cond == nextChar) { - stop = true; - break; - } - } - } - - return {message, 0}; -} - -#else - - #error "do not directly include this header!" - -#endif diff --git a/samples/readInfinite.cpp b/samples/readInfinite.cpp index f47f31c..4650376 100644 --- a/samples/readInfinite.cpp +++ b/samples/readInfinite.cpp @@ -1,36 +1,65 @@ #include "rs232.hpp" -#include + +constexpr const bool regexExample = true; /** * @brief This sample shows how to read from a serial port until the end of the buffer is reached. * * It first tries to get a list of available ports and then tries to connect to the first one. */ - int main (int argc, char **argv) { - std::vector ports = sakurajin::getAvailablePorts(); - - sakurajin::RS232 rs232_interface{ports,sakurajin::baud9600}; + //try to connect to all potentially available ports with the baudrate 9600 + sakurajin::RS232 rs232_interface{sakurajin::baud9600}; if(!rs232_interface.IsAvailable()){ std::cerr << "No serial port is available!" << std::endl; return -1; } + //iterate over all available ports and print all connected to the console std::cout << "Available serial ports:" << std::endl; auto devCount = rs232_interface.getDeviceCount(); for(size_t i = 0; i < devCount; i++){ - if(rs232_interface.IsAvailable(i)){ - std::cout << rs232_interface.GetDeviceName(i) << std::endl; + auto deviceI = rs232_interface.getNativeDevice(i); + if(deviceI == nullptr){ + continue; + } + + if(deviceI->getConnectionStatus() == sakurajin::connectionStatus::connected) { + std::cout << deviceI->getDeviceName() << " (connected)" << std::endl; } } - while (true){ - auto [nextC, error] = rs232_interface.ReadNextChar(); - if(error < 0){ - std::cerr << "Error while reading the buffer" << std::endl; - break; + if(regexExample) { + //setup the regex pattern + //here the pattern is X followed by 7 non whitespace characters followed by Y + std::regex pattern{"X\\S{7}Y"}; + + while (true) { + //do a small delay to prevent too much locking + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + //get the first match of the pattern in the read buffer + auto result = rs232_interface.retrieveFirstMatch(pattern); + + //don't output anything if there is no match + if (result.empty()) { + continue; + } + + //output the result to the console + std::cout << result << std::endl; + } + }else{ + while(true){ + //do a small delay to prevent too much locking + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + + //read the string and save the result in a variable + auto readString = rs232_interface.retrieveReadBuffer(); + + //output the string to the console + std::cout << readString << std::flush; } - std::cout << nextC << std::flush; } } diff --git a/src/rs232.cpp b/src/rs232.cpp index 47d26a4..bf524f6 100644 --- a/src/rs232.cpp +++ b/src/rs232.cpp @@ -1,50 +1,8 @@ #include "rs232.hpp" -void sakurajin::RS232::Print(const std::string& text, std::ostream& errorStream) { - if (rs232Devices.empty()) { - return; - } - auto transferDevice = rs232Devices[currentDevice]; - if (transferDevice->getConnectionStatus() != sakurajin::connectionStatus::connected) { - errorStream << "device is not connected"; - return; - } - - for (auto c : text) { - if (transferDevice->writeRawData(&c, 1) < 1) { - errorStream << "error writing [" << c << "] to serial port"; - } - } -} - -std::shared_ptr sakurajin::RS232::getNativeDevice(size_t index) const { - if (rs232Devices.empty()) { - return nullptr; - } - if (index < rs232Devices.size()) { - return rs232Devices[index]; - } - return rs232Devices[currentDevice]; -} - -size_t sakurajin::RS232::getDeviceCount() const { - return rs232Devices.size(); -} - -bool sakurajin::RS232::IsAvailable(size_t index) const { - auto dev = getNativeDevice(index); - if (dev == nullptr) { - return false; - } - - return dev->getConnectionStatus() == sakurajin::connectionStatus::connected; -} - -std::string_view sakurajin::RS232::GetDeviceName(size_t index) const { - auto dev = getNativeDevice(index); - return dev == nullptr ? "" : dev->getDeviceName(); -} +using namespace std::literals; +// constructors and destructors sakurajin::RS232::RS232(const std::vector& deviceNames, sakurajin::Baudrate baudrate, std::ostream& errorStream) { if (deviceNames.empty()) { errorStream << "No device name was given. Creating empty RS232 object."; @@ -66,6 +24,13 @@ sakurajin::RS232::RS232(const std::vector& deviceNames, sakurajin:: } Connect(); + + // start the work thread + workThread = std::async(std::launch::async, [this]() { + while (!stopThread) { + work(); + } + }); } sakurajin::RS232::RS232(const std::string& deviceName, sakurajin::Baudrate Rate, std::ostream& errorStream) @@ -76,20 +41,23 @@ sakurajin::RS232::RS232(sakurajin::Baudrate Rate, std::ostream& errorStream) : RS232(sakurajin::getAvailablePorts(), Rate, errorStream) {} sakurajin::RS232::~RS232() { - DisconnectAll(); -} + // correctly stop the work thread before disconnecting everything + stopThread = true; + if (workThread.valid()) { + workThread.wait(); + } -void sakurajin::RS232::Close() { DisconnectAll(); } +// connection functions bool sakurajin::RS232::Connect() { if (rs232Devices.empty()) { return false; } // return early if the current device is already connected - if (rs232Devices[currentDevice]->getConnectionStatus() == sakurajin::connectionStatus::connected) { + if (getCurrentDevice()->getConnectionStatus() == sakurajin::connectionStatus::connected) { return true; } @@ -124,22 +92,142 @@ void sakurajin::RS232::DisconnectAll() { } } -std::tuple sakurajin::RS232::ReadNextChar() { - return ReadNextChar(std::chrono::microseconds(1), true); +// the work function +void sakurajin::RS232::work() { + if (rs232Devices.empty()) { + // if there are no devices, wait for 100ms + // the delay is to prevent the thread from spinning + // since it is unlikely that a device will be added the delay is higher than for the other cases + std::this_thread::sleep_for(100ms); + return; + } + + auto transferDevice = getCurrentDevice(); + if (transferDevice->getConnectionStatus() != sakurajin::connectionStatus::connected) { + // it is more likely that a device will be connected than that a device will be added + // because of this the sleep duration is lower + std::this_thread::sleep_for(1ms); + return; + } + + // if there is something to write to the device, write it + if (writeBufferHasData) { + // lock the mutex to prevent the buffer from being changed while it is being moved + writeBufferMutex.lock(); + auto localWriteBuffer = std::move(writeBuffer); + writeBufferHasData = false; + writeBufferMutex.unlock(); + + // write the data + for (auto c : localWriteBuffer) { + if (transferDevice->writeRawData(&c, 1) < 1) { + std::cerr << "error writing [" << c << "] to serial port"; + } + } + } + + // read the data + char IOBuf = '\0'; + int64_t dataSize = transferDevice->readRawData(&IOBuf, 1); + if (dataSize > 0) { + // if there is data, add it to the buffer and set the hasData flag + readBufferMutex.lock(); + if (readBufferHasData) { + readBuffer.append(1, IOBuf); + } else { + readBuffer = std::string(1, IOBuf); + readBuffer.reserve(10); + readBufferHasData = true; + } + readBufferMutex.unlock(); + } +} + +// io functions +void sakurajin::RS232::Print(const std::string& text) { + std::scoped_lock lock(writeBufferMutex); + + if (writeBufferHasData) { + writeBuffer.append(text); + } else { + writeBuffer = text; + writeBufferHasData = true; + } +} + +std::string&& sakurajin::RS232::retrieveReadBuffer() { + if (!readBufferHasData) { + return std::string(); + } + + std::scoped_lock lock(readBufferMutex); + + readBufferHasData = false; + return std::move(readBuffer); +} + +std::string&& sakurajin::RS232::retrieveFirstMatch(const std::regex& pattern) { + if (!readBufferHasData) { + return std::string(); + } + + std::scoped_lock lock(readBufferMutex); + std::smatch s_match_result; + std::regex_search(readBuffer, s_match_result, pattern); + if (s_match_result.empty()) { + return std::string(); + } + + auto result = s_match_result.str(); + readBuffer = s_match_result.suffix(); + return std::move(result); +} + +// device access functions +std::shared_ptr sakurajin::RS232::getNativeDevice(size_t index) const { + if (rs232Devices.empty()) { + return nullptr; + } + if (index < rs232Devices.size()) { + return rs232Devices[index]; + } + return rs232Devices[currentDevice]; +} + +std::shared_ptr sakurajin::RS232::getCurrentDevice() const { + return getNativeDevice(currentDevice); +} +bool sakurajin::RS232::IsAvailable() const { + return getCurrentDevice()->getConnectionStatus() == sakurajin::connectionStatus::connected; +} + +size_t sakurajin::RS232::getDeviceCount() const { + return rs232Devices.size(); } -std::tuple sakurajin::RS232::ReadNextMessage() { - return ReadNextMessage(std::chrono::microseconds(1), true); +// deprecated functions +void sakurajin::RS232::Close() { + DisconnectAll(); } -std::tuple sakurajin::RS232::ReadUntil(const std::vector& conditions) { - return ReadUntil(conditions, std::chrono::microseconds(1), true); +std::string_view sakurajin::RS232::GetDeviceName(size_t index) const { + auto dev = getNativeDevice(index); + return dev == nullptr ? "" : dev->getDeviceName(); } bool sakurajin::RS232::IsCTSEnabled() const { - auto nativeDevice = getNativeDevice(); + auto nativeDevice = getCurrentDevice(); if (nativeDevice == nullptr) { return false; } return nativeDevice->checkForFlag(REQUEST_TO_SEND); } + +bool sakurajin::RS232::IsAvailable(size_t index) const { + auto dev = getNativeDevice(index); + if (dev == nullptr) { + return false; + } + + return dev->getConnectionStatus() == sakurajin::connectionStatus::connected; +} diff --git a/src/rs232_native_common.cpp b/src/rs232_native_common.cpp index 663f0d0..39a9109 100644 --- a/src/rs232_native_common.cpp +++ b/src/rs232_native_common.cpp @@ -3,6 +3,8 @@ #include "rs232_native.hpp" +using namespace std::literals; + sakurajin::RS232_native::RS232_native(std::string deviceName, Baudrate _baudrate, std::ostream& error_stream) : devname(std::move(deviceName)) { baudrate = _baudrate; @@ -72,3 +74,42 @@ std::vector sakurajin::getAvailablePorts() noexcept { return allPorts; } + +std::tuple sakurajin::native::ReadNextChar(const std::shared_ptr& transferDevice) { + return sakurajin::native::ReadNextChar(transferDevice, std::chrono::microseconds(1), true); +} + +std::tuple sakurajin::native::ReadNextMessage(const std::shared_ptr& transferDevice) { + return sakurajin::native::ReadNextMessage(transferDevice, 1us, true); +} + +std::tuple sakurajin::native::ReadUntil(const std::shared_ptr& transferDevice, + const std::vector& conditions) { + return sakurajin::native::ReadUntil(transferDevice, conditions, 1us, true); +} + +int sakurajin::native::Print(const std::shared_ptr& transferDevice, const std::string& text) { + + if (transferDevice == nullptr) { + return -1; + } + + if (transferDevice->getConnectionStatus() != sakurajin::connectionStatus::connected) { + return -2; + } + + for (auto c : text) { + // retry each character until it is written + int64_t writeRes; + do { + writeRes = transferDevice->writeRawData(&c, 1); + + // if the connection was lost while writing, return + if (transferDevice->getConnectionStatus() != sakurajin::connectionStatus::connected) { + return -3; + } + } while (writeRes < 1); + } + + return 0; +}