Skip to content

Commit

Permalink
implementing new read and write methods.
Browse files Browse the repository at this point in the history
 Version 2 uses a read/write worker in the RS232 wrapper class.
 Because of this the old methods are now split out.
 They still exist in case immediate read/write is wanted or for
 the implementation of other custom wrapper classes.

 All preexsiting read functions were unified into a retrieveFirstMatch
 method. This one takes a regex pattern and performs a regex_search
 on the buffer. The first result is returned and the buffer is set
 to whatever is left after the match (result.suffix()). This drastically
 simplifies the code and the other functions will likely get updated the
 same way.

 There is also a function to get the full buffer without needing to
 perform a regex search.
  • Loading branch information
noah1510 committed Dec 14, 2023
1 parent bc0ce67 commit a08c49b
Show file tree
Hide file tree
Showing 6 changed files with 478 additions and 245 deletions.
185 changes: 88 additions & 97 deletions include/rs232.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@

#include "rs232_native.hpp"

#include <chrono>
#include <tuple>
#include <future>

namespace sakurajin {

Expand Down Expand Up @@ -38,6 +37,30 @@ namespace sakurajin {
*/
std::atomic<size_t> currentDevice = 0;

std::shared_mutex deviceMutex;

/**
* @brief The work thread that actually does most of the work
*/
std::future<void> workThread;
std::atomic<bool> stopThread = false;

std::string readBuffer;
std::shared_mutex readBufferMutex;
std::atomic<bool> readBufferHasData = false;

std::string writeBuffer;
std::shared_mutex writeBufferMutex;
std::atomic<bool> 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
Expand Down Expand Up @@ -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<RS232_native> 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<unsigned char, int> 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<unsigned char, int> ReadNextChar();
std::shared_ptr<RS232_native> 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<unsigned char, int> 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 <class Rep = int64_t, class Period = std::ratio<1>>
[[nodiscard]] [[maybe_unused]]
std::tuple<unsigned char, int> ReadNextChar(std::chrono::duration<Rep, Period> waitTime,
bool ignoreTime = false,
std::shared_ptr<RS232_native> transferDevice = nullptr);
std::shared_ptr<RS232_native> getCurrentDevice() const;

/**
* @brief reads the interface until a newline (\n) is received
*
* @return std::tuple<std::string, int> 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<std::string, int> 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<std::string, int> 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 <class Rep = int64_t, class Period = std::ratio<1>>
[[nodiscard]] [[maybe_unused]]
std::tuple<std::string, int> ReadNextMessage(std::chrono::duration<Rep, Period> 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<std::string, int> 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<std::string, int> ReadUntil(const std::vector<unsigned char>& 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<std::string, int> 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 <class Rep = int64_t, class Period = std::ratio<1>>
[[nodiscard]] [[maybe_unused]]
std::tuple<std::string, int>
ReadUntil(const std::vector<unsigned char>& conditions, std::chrono::duration<Rep, Period> 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
Loading

0 comments on commit a08c49b

Please sign in to comment.