Adds the sdi_toolBox library (temporary version)
This commit is contained in:
430
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/comm/curlHttpClient.h
Normal file
430
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/comm/curlHttpClient.h
Normal file
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <sdi_toolBox/logs/reLog.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sdi_ToolBox::communication
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class CurlGlobalInitializer
|
||||
{
|
||||
public:
|
||||
static void init() // Initialize libcurl globally
|
||||
{
|
||||
static auto instance = std::unique_ptr<CurlGlobalInitializer>(new CurlGlobalInitializer());
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~CurlGlobalInitializer() // Default destructor
|
||||
{
|
||||
curl_global_cleanup();
|
||||
}
|
||||
CurlGlobalInitializer(const CurlGlobalInitializer &obj) = delete; // Copy constructor
|
||||
CurlGlobalInitializer(CurlGlobalInitializer &&obj) noexcept = delete; // Move constructor
|
||||
CurlGlobalInitializer &operator=(const CurlGlobalInitializer &obj) = delete; // Copy assignment operator
|
||||
CurlGlobalInitializer &operator=(CurlGlobalInitializer &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
protected:
|
||||
CurlGlobalInitializer() // Default constructor
|
||||
{
|
||||
if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK)
|
||||
throw std::runtime_error("ERROR: Failed to initialize cURL");
|
||||
}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
class CurlHttpClient
|
||||
{
|
||||
public:
|
||||
enum class Method : uint8_t
|
||||
{
|
||||
http_GET,
|
||||
http_POST,
|
||||
http_PUT,
|
||||
http_DELETE,
|
||||
};
|
||||
|
||||
struct Settings
|
||||
{
|
||||
bool enableSSL = true;
|
||||
bool ssl_verifyPeer = true;
|
||||
bool ssl_verifyHost = true;
|
||||
std::filesystem::path ssl_caInfo;
|
||||
std::string ssl_certBlob;
|
||||
std::chrono::seconds timeout{ 10 };
|
||||
|
||||
bool operator==(const Settings &other) const
|
||||
{
|
||||
// Compare all fields against the other object
|
||||
return (enableSSL == other.enableSSL &&
|
||||
ssl_verifyPeer == other.ssl_verifyPeer &&
|
||||
ssl_verifyHost == other.ssl_verifyHost &&
|
||||
ssl_caInfo == other.ssl_caInfo &&
|
||||
ssl_certBlob == other.ssl_certBlob);
|
||||
}
|
||||
};
|
||||
|
||||
using Headers = std::unordered_map<std::string, std::string>;
|
||||
|
||||
struct Request
|
||||
{
|
||||
std::string url;
|
||||
Method method = Method::http_GET;
|
||||
std::string body;
|
||||
Headers headers;
|
||||
Settings settings;
|
||||
|
||||
bool operator==(const Request &other) const
|
||||
{
|
||||
// Compare all fields against the other object
|
||||
return (url == other.url &&
|
||||
method == other.method &&
|
||||
body == other.body &&
|
||||
headers == other.headers &&
|
||||
settings == other.settings);
|
||||
}
|
||||
};
|
||||
struct Response
|
||||
{
|
||||
int returnCode = -1;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
public:
|
||||
CurlHttpClient(); // Default constructor
|
||||
virtual ~CurlHttpClient() = default; // Default destructor
|
||||
CurlHttpClient(const CurlHttpClient &obj) = delete; // Copy constructor
|
||||
CurlHttpClient(CurlHttpClient &&obj) noexcept = delete; // Move constructor
|
||||
CurlHttpClient &operator=(const CurlHttpClient &obj) = delete; // Copy assignment operator
|
||||
CurlHttpClient &operator=(CurlHttpClient &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
const Response &performRequest(const Request &request = {}); // Perform HTTP request
|
||||
const Response &performRequest_nothrow(const Request &request = {}); // Perform HTTP request
|
||||
|
||||
const Request &getRequest() const; // Retrieves curl request
|
||||
const Response &getResponse() const; // Retrieves curl response
|
||||
|
||||
protected:
|
||||
mutable std::mutex m_mtx;
|
||||
|
||||
CURL *m_curl = nullptr;
|
||||
curl_slist *m_headerList = nullptr;
|
||||
Request m_request;
|
||||
Response m_response;
|
||||
|
||||
private:
|
||||
CURLcode setRequestOptions(); // Set cURL request options
|
||||
|
||||
static size_t write_callback(void *contents, // Callback for writing received data
|
||||
size_t size,
|
||||
size_t itemCount,
|
||||
void *userData);
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Default constructor */
|
||||
inline CurlHttpClient::CurlHttpClient()
|
||||
{
|
||||
CurlGlobalInitializer::init();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Perform HTTP request */
|
||||
inline const CurlHttpClient::Response &CurlHttpClient::performRequest(const Request &request)
|
||||
{
|
||||
std::lock_guard lock(m_mtx);
|
||||
|
||||
if (const Request default_req = {}; request != default_req)
|
||||
m_request = request;
|
||||
|
||||
try
|
||||
{
|
||||
m_curl = curl_easy_init();
|
||||
if (!m_curl)
|
||||
throw std::runtime_error("unable to initialize curl library");
|
||||
|
||||
// Set request options
|
||||
if (const auto res = setRequestOptions(); res != CURLE_OK)
|
||||
throw std::runtime_error(std::format("ERROR: Failed to set up CURL options: {}", curl_easy_strerror(res)));
|
||||
|
||||
// Perform request
|
||||
if (const auto res = curl_easy_perform(m_curl); res != CURLE_OK)
|
||||
throw std::runtime_error(std::format("ERROR: curl_easy_perform() failed: {}", curl_easy_strerror(res)));
|
||||
if (const auto res = curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &m_response.returnCode); res != CURLE_OK)
|
||||
throw std::runtime_error(std::format("ERROR: Failed to get content code: {}", curl_easy_strerror(res)));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
m_response.returnCode = -1;
|
||||
m_response.content = e.what();
|
||||
}
|
||||
|
||||
// Free resources
|
||||
if (m_curl)
|
||||
{
|
||||
curl_easy_cleanup(m_curl); // free the easy curl handle
|
||||
m_curl = nullptr;
|
||||
}
|
||||
if (m_headerList)
|
||||
{
|
||||
curl_slist_free_all(m_headerList); // free the custom headers
|
||||
m_curl = nullptr;
|
||||
}
|
||||
|
||||
if (m_response.returnCode == -1)
|
||||
throw std::runtime_error(m_response.content);
|
||||
|
||||
return m_response;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Perform HTTP request */
|
||||
inline const CurlHttpClient::Response &CurlHttpClient::performRequest_nothrow(const Request &request)
|
||||
{
|
||||
try
|
||||
{
|
||||
performRequest(request);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LogError() << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return m_response;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Retrieves curl request */
|
||||
inline const CurlHttpClient::Request &CurlHttpClient::getRequest() const
|
||||
{
|
||||
std::lock_guard lock(m_mtx);
|
||||
|
||||
return m_request;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Retrieves curl response */
|
||||
inline const CurlHttpClient::Response &CurlHttpClient::getResponse() const
|
||||
{
|
||||
std::lock_guard lock(m_mtx);
|
||||
|
||||
return m_response;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Set cURL request options */
|
||||
inline CURLcode CurlHttpClient::setRequestOptions()
|
||||
{
|
||||
// --- Common Options ---
|
||||
// Set the target URL
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_URL, m_request.url.c_str()); res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
// Set the write callback function
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, write_callback); res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
// Set the user data pointer for the callback (our content buffer)
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &m_response.content); res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
// Set custom headers if any
|
||||
if (m_headerList)
|
||||
{
|
||||
// Prepare header list
|
||||
for (const auto &[key, value] : m_request.headers)
|
||||
{
|
||||
const auto header = format("{0:}: {1:}", key, value);
|
||||
m_headerList = curl_slist_append(m_headerList, header.c_str());
|
||||
}
|
||||
|
||||
// Append headers to the request
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_headerList); res != CURLE_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
// Set the max time in seconds for the entire operation (including connection and transfer)
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_TIMEOUT, m_request.settings.timeout.count()); res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
// --- SSL/TLS Options ---
|
||||
// curl documentation recommends setting both for full control
|
||||
if (m_request.settings.enableSSL)
|
||||
{
|
||||
// Enable peer certificate verification
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 1L); res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
// Enable host name verification (prevents 'Man-in-the-middle' attacks)
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYHOST, 2L); res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
if (!m_request.settings.ssl_caInfo.empty())
|
||||
{
|
||||
// Optionally, set the path to a CA bundle file (often not needed
|
||||
// if curl is built with a system-wide CA store)
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_CAINFO, m_request.settings.ssl_caInfo.c_str()); res != CURLE_OK)
|
||||
return res;
|
||||
}
|
||||
else if (!m_request.settings.ssl_certBlob.empty())
|
||||
{
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_SSLKEY_BLOB, &m_request.settings.ssl_certBlob); res != CURLE_OK)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Disable verification (USE WITH CAUTION: Insecure!)
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, 0L); res != CURLE_OK)
|
||||
return res;
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYHOST, 0L); res != CURLE_OK)
|
||||
return res;
|
||||
}
|
||||
|
||||
// --- Method-Specific Options ---
|
||||
switch (m_request.method)
|
||||
{
|
||||
using enum Method;
|
||||
case http_GET:
|
||||
{
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_HTTPGET, 1L); res != CURLE_OK)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
case http_POST:
|
||||
{
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_POST, 1L); res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
// Set the data to be sent
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_request.body.c_str()); res != CURLE_OK)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
case http_PUT:
|
||||
{
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_PUT, 1L); res != CURLE_OK)
|
||||
return res;
|
||||
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, "PUT"); res != CURLE_OK)
|
||||
return res;
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_request.body.c_str()); res != CURLE_OK)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
case http_DELETE:
|
||||
{
|
||||
if (const auto res = curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, "DELETE"); res != CURLE_OK)
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Callback for writing received data */
|
||||
inline size_t CurlHttpClient::write_callback(void *contents, const size_t size, const size_t itemCount, void *userData)
|
||||
{
|
||||
// Total size of the incoming buffer
|
||||
const auto realSize = size * itemCount;
|
||||
|
||||
// Append the received data to the content string
|
||||
const auto &response = static_cast<std::string *>(userData);
|
||||
response->append(static_cast<char *>(contents), realSize);
|
||||
|
||||
return realSize;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
class CurlInfo
|
||||
{
|
||||
public:
|
||||
CurlInfo(); // Default constructor
|
||||
virtual ~CurlInfo() = default; // Default destructor
|
||||
CurlInfo(const CurlInfo &obj) = delete; // Copy constructor
|
||||
CurlInfo(CurlInfo &&obj) noexcept = delete; // Move constructor
|
||||
CurlInfo &operator=(const CurlInfo &obj) = delete; // Copy assignment operator
|
||||
CurlInfo &operator=(CurlInfo &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
void toStream(std::ostream &stream); // Print cURL information to stream object
|
||||
void print(); // Print cURL information to cout
|
||||
|
||||
std::string version;
|
||||
uint32_t features;
|
||||
|
||||
bool ssl_support = false;
|
||||
std::string ssl_version;
|
||||
|
||||
std::vector<std::string> availableProtocols;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Default constructor */
|
||||
inline CurlInfo::CurlInfo()
|
||||
{
|
||||
CurlGlobalInitializer::init();
|
||||
|
||||
// Retrieves information from the library
|
||||
const auto info = curl_version_info(CURLVERSION_NOW);
|
||||
|
||||
version = info->version;
|
||||
features = info->features;
|
||||
|
||||
ssl_support = info->features & CURL_VERSION_SSL;
|
||||
ssl_version = info->ssl_version;
|
||||
|
||||
if (info->protocols)
|
||||
{
|
||||
for (const char *const *proto = info->protocols; *proto; ++proto)
|
||||
availableProtocols.emplace_back(*proto);
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Print cURL information to stream object */
|
||||
inline void CurlInfo::toStream(std::ostream &stream)
|
||||
{
|
||||
stream << "--- libcurl Version Info ---" << "\n";
|
||||
stream << "Version : " << version << "\n";
|
||||
stream << "Features (bitmask) : " << std::format("0x{0:X}", features) << "\n";
|
||||
stream << "SSL/TLS support : " << ((ssl_support) ? std::format("**Yes** [{0:}]", ssl_version) : "No") << "\n";
|
||||
if (!availableProtocols.empty())
|
||||
{
|
||||
stream << "Available protocols: ";
|
||||
bool first = true;
|
||||
for (const auto protocol : availableProtocols)
|
||||
{
|
||||
if (!first)
|
||||
stream << ", ";
|
||||
stream << protocol;
|
||||
first = false;
|
||||
}
|
||||
stream << "\n";
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Print cURL information to cout */
|
||||
inline void CurlInfo::print()
|
||||
{
|
||||
toStream(std::cout);
|
||||
std::cout << std::flush;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_ToolBox::communication
|
||||
6
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/comm/namespace.h
Normal file
6
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/comm/namespace.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @namespace sdi_ToolBox::comm
|
||||
* @brief Communication protocols and binary handling.
|
||||
*/
|
||||
784
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/comm/uBinaryFrame.h
Normal file
784
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/comm/uBinaryFrame.h
Normal file
@@ -0,0 +1,784 @@
|
||||
//{{copyright}}
|
||||
|
||||
//{{version}}
|
||||
|
||||
//{{license}}
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../generic/crc.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @namespace sdi_ToolBox::comm::uBinaryFrame
|
||||
* @brief Utilities for robust binary frame serialization, deserialization, and protocol handling.
|
||||
*
|
||||
* This namespace provides classes and constants to manage binary communication frames with automatic
|
||||
* escaping, CRC calculation, and protocol validation. The supported frame format is:
|
||||
* [SOF1][SOF2][SIZE][PAYLOAD][CRC16][EOF]
|
||||
* where special bytes are escaped as needed.
|
||||
*
|
||||
* Main components:
|
||||
* - @ref FrameReceiver : Receives and extracts a complete binary frame from a byte stream.
|
||||
* - @ref MessageParser : Parses and validates a binary frame, providing typed access to the payload.
|
||||
* - @ref MessageBuilder : Builds a binary message with CRC calculation and escaping.
|
||||
* - @ref FramePacker : Packs a message into a complete frame ready for transmission (header, CRC, escaping, etc.).
|
||||
*/
|
||||
namespace sdi_ToolBox::comm::uBinaryFrame
|
||||
{
|
||||
constexpr uint8_t SOF1_ = 0xAA; // Start of Frame byte (0b10101010 - useful for bus synchronisation)
|
||||
constexpr uint8_t SOF2_ = 0x55; // Start of Frame byte (0b01010101 - useful for bus synchronisation)
|
||||
constexpr uint8_t EOF_ = 0x03; // End of Frame byte (End of Text code in ASCII table)
|
||||
constexpr uint8_t ESC_ = 0x1B; // Escape byte (Escape code in ASCII table)
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class FrameReceiver
|
||||
* @brief Stateful receiver for extracting a complete binary frame from a byte stream.
|
||||
*
|
||||
* Handles frame synchronization, byte un-escaping, and buffering.
|
||||
* The frame is considered complete when the protocol's end-of-frame byte is detected.
|
||||
*
|
||||
* @tparam BUFFER_SIZE Maximum size of the internal receive buffer.
|
||||
*
|
||||
* Usage:
|
||||
* - Call push(byte) for each received byte.
|
||||
* - When push() returns true or isFrameAvailable() is true, use getFrame() to access the frame data.
|
||||
*
|
||||
* @code
|
||||
* FrameReceiver<128> receiver;
|
||||
* for (uint8_t byte : incomingBytes) {
|
||||
* if (receiver.push(byte)) {
|
||||
* auto frame = receiver.getFrame();
|
||||
* // Process frame...
|
||||
*
|
||||
* receiver.reset();
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template<size_t BUFFER_SIZE>
|
||||
class FrameReceiver
|
||||
{
|
||||
enum class State : uint8_t
|
||||
{
|
||||
WAIT_SOF1,
|
||||
WAIT_SOF2,
|
||||
RECEIVE_DATA,
|
||||
WAIT_EOF,
|
||||
ESCAPE_NEXT,
|
||||
END,
|
||||
};
|
||||
|
||||
public:
|
||||
FrameReceiver() = default; // Default constructor
|
||||
~FrameReceiver() = default; // Default destructor
|
||||
FrameReceiver(const FrameReceiver &obj) = default; // Copy constructor
|
||||
FrameReceiver(FrameReceiver &&obj) noexcept = default; // Move constructor
|
||||
FrameReceiver &operator=(const FrameReceiver &obj) = default; // Copy assignment operator
|
||||
FrameReceiver &operator=(FrameReceiver &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
/**
|
||||
* @brief Resets the receiver state and clears the internal buffer.
|
||||
*
|
||||
* After calling this method, the receiver is ready to process a new frame.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* @brief Pushes a byte into the receiver buffer and updates the state machine.
|
||||
* @param byte The received byte to process.
|
||||
* @return true if a complete frame has been received and is available, false otherwise.
|
||||
*
|
||||
* @note If a frame is available, use getFrame() to access its content.
|
||||
*/
|
||||
[[nodiscard]] bool push(uint8_t byte);
|
||||
|
||||
/**
|
||||
* @brief Checks if a complete frame is available in the buffer.
|
||||
* @return true if a frame is available, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool isFrameAvailable() const;
|
||||
|
||||
/**
|
||||
* @brief Returns a span to the complete frame data.
|
||||
* @return A span containing the frame bytes, or an empty span if no frame is available.
|
||||
*
|
||||
* @note The returned span is only valid until the next reset() or push().
|
||||
*/
|
||||
[[nodiscard]] std::span<const uint8_t> getFrame() const;
|
||||
|
||||
protected:
|
||||
std::array<uint8_t, BUFFER_SIZE> m_buffer{}; // Receiver buffer
|
||||
size_t m_index = 0; // Current index in the buffer
|
||||
State m_state = State::WAIT_SOF1; // Current state of the receiver
|
||||
|
||||
private:
|
||||
void appendByte(uint8_t byte); // Append a byte to the buffer
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline void FrameReceiver<BUFFER_SIZE>::reset()
|
||||
{
|
||||
m_index = 0;
|
||||
m_state = State::WAIT_SOF1;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline bool FrameReceiver<BUFFER_SIZE>::push(const uint8_t byte)
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
// Initial state: waiting for SOF1
|
||||
case State::WAIT_SOF1:
|
||||
{
|
||||
if (byte == SOF1_)
|
||||
{
|
||||
m_index = 0;
|
||||
m_state = State::WAIT_SOF2; // Next byte should be SOF2
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Waiting for SOF2
|
||||
case State::WAIT_SOF2:
|
||||
{
|
||||
if (byte == SOF2_)
|
||||
m_state = State::RECEIVE_DATA; // Start receiving data
|
||||
else
|
||||
m_state = State::WAIT_SOF1; // Abort frame reception
|
||||
break;
|
||||
}
|
||||
|
||||
// Receiving data
|
||||
case State::RECEIVE_DATA:
|
||||
{
|
||||
if (byte == ESC_)
|
||||
m_state = State::ESCAPE_NEXT; // Next byte is escaped
|
||||
else if (byte == SOF1_)
|
||||
m_state = State::WAIT_SOF2; // Restart frame reception
|
||||
else if (byte == SOF2_)
|
||||
m_state = State::WAIT_SOF1; // Abort frame reception
|
||||
else if (byte == EOF_)
|
||||
{
|
||||
m_state = State::END; // Frame complete
|
||||
return true;
|
||||
}
|
||||
else
|
||||
appendByte(byte);
|
||||
break;
|
||||
}
|
||||
|
||||
// Handling escaped byte
|
||||
case State::ESCAPE_NEXT:
|
||||
{
|
||||
appendByte(byte);
|
||||
m_state = State::RECEIVE_DATA; // Return to data reception
|
||||
break;
|
||||
}
|
||||
|
||||
// Frame complete
|
||||
case State::END:
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline bool FrameReceiver<BUFFER_SIZE>::isFrameAvailable() const
|
||||
{
|
||||
return m_state == State::END;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline std::span<const uint8_t> FrameReceiver<BUFFER_SIZE>::getFrame() const
|
||||
{
|
||||
if (m_state == State::END)
|
||||
return { m_buffer.data(), m_index };
|
||||
return {};
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline void FrameReceiver<BUFFER_SIZE>::appendByte(const uint8_t byte)
|
||||
{
|
||||
if (m_index < BUFFER_SIZE)
|
||||
m_buffer[m_index++] = byte;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class MessageParser
|
||||
* @brief Parses and validates a binary frame, providing typed access to the payload.
|
||||
*
|
||||
* Checks frame structure, size, CRC, and end-of-frame marker.
|
||||
* Allows extraction of typed values or raw data from the payload.
|
||||
*
|
||||
* Usage:
|
||||
* - Construct with a frame buffer.
|
||||
* - Check validity with isValid() or getStatus().
|
||||
* - Use get<T>(offset, value) to extract typed data from the payload.
|
||||
*
|
||||
* @code
|
||||
* MessageParser parser(frame);
|
||||
* if (parser.isValid()) {
|
||||
* uint16_t id;
|
||||
* parser.get(0, id);
|
||||
* std::array<uint8_t, 8> data;
|
||||
* parser.get(2, std::span<uint8_t>(data));
|
||||
* // Use id and data...
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
class MessageParser
|
||||
{
|
||||
public:
|
||||
enum class MessageStatus : uint8_t
|
||||
{
|
||||
VALID,
|
||||
INVALID,
|
||||
BAD_SIZE,
|
||||
BAD_CRC,
|
||||
};
|
||||
|
||||
public:
|
||||
MessageParser() = delete; // Default constructor
|
||||
~MessageParser() = default; // Default destructor
|
||||
MessageParser(const MessageParser &obj) = default; // Copy constructor
|
||||
MessageParser(MessageParser &&obj) noexcept = default; // Move constructor
|
||||
MessageParser &operator=(const MessageParser &obj) = default; // Copy assignment operator
|
||||
MessageParser &operator=(MessageParser &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
/**
|
||||
* @brief Constructs a MessageParser and checks the integrity of the provided frame.
|
||||
* @param buffer The buffer containing the frame to parse.
|
||||
*/
|
||||
explicit MessageParser(std::span<const uint8_t> buffer);
|
||||
|
||||
/**
|
||||
* @brief Checks if the parsed message is valid.
|
||||
* @return true if the message is valid, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool isValid() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the current message validity status.
|
||||
* @return The status as a MessageStatus enum value.
|
||||
*/
|
||||
[[nodiscard]] MessageStatus getStatus() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the message payload buffer.
|
||||
* @return A span containing the payload bytes.
|
||||
*/
|
||||
[[nodiscard]] std::span<const uint8_t> getBuffer() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the size of the message payload.
|
||||
* @return The size of the payload in bytes.
|
||||
*/
|
||||
[[nodiscard]] size_t size() const; // Get message payload size
|
||||
|
||||
/**
|
||||
* @brief Extracts a value of type T from the message payload at the specified offset.
|
||||
* @tparam T The type of the value to extract (POD type or std::span<uint8_t>).
|
||||
* @param offset The offset in the payload to read from.
|
||||
* @param value Reference to store the extracted value.
|
||||
* @return The new offset after reading the value, or the original offset if reading failed.
|
||||
*
|
||||
* @note No exception is thrown. If extraction fails, value is not modified.
|
||||
*/
|
||||
template<class T>
|
||||
[[nodiscard]] size_t get(size_t offset, T &value) const;
|
||||
|
||||
protected:
|
||||
std::span<const uint8_t> m_buffer; // Message buffer
|
||||
MessageStatus m_messageStatus = MessageStatus::INVALID; // Message validity status
|
||||
|
||||
private:
|
||||
void messageCheck(); // Check message integrity
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline MessageParser::MessageParser(const std::span<const uint8_t> buffer)
|
||||
{
|
||||
m_buffer = buffer;
|
||||
messageCheck();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline bool MessageParser::isValid() const
|
||||
{
|
||||
return m_messageStatus == MessageStatus::VALID;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get message validity status */
|
||||
inline MessageParser::MessageStatus MessageParser::getStatus() const
|
||||
{
|
||||
return m_messageStatus;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get message payload buffer */
|
||||
inline std::span<const uint8_t> MessageParser::getBuffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get message payload size */
|
||||
inline size_t MessageParser::size() const
|
||||
{
|
||||
return m_buffer.size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<class T>
|
||||
inline size_t MessageParser::get(size_t offset, T &value) const
|
||||
{
|
||||
static_assert((std::is_standard_layout_v<T> && std::is_trivial_v<T>) || // POD type
|
||||
std::is_same_v<T, std::span<uint8_t>> || // std::span<uint8_t> type
|
||||
std::is_same_v<T, std::string>); // std::string type
|
||||
|
||||
if constexpr (std::is_standard_layout_v<T> && std::is_trivial_v<T>)
|
||||
{
|
||||
// POD type
|
||||
constexpr size_t typeSize = sizeof(T);
|
||||
if (offset + typeSize <= m_buffer.size())
|
||||
{
|
||||
std::memcpy(&value, m_buffer.data() + offset, typeSize);
|
||||
return offset + typeSize; // Return new offset
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::span<uint8_t>>)
|
||||
{
|
||||
// std::span<uint8_t> type
|
||||
if (offset + value.size() <= m_buffer.size())
|
||||
{
|
||||
std::memcpy(value.data(), m_buffer.data() + offset, value.size());
|
||||
return offset + value.size(); // Return new offset
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
// std::string type
|
||||
uint8_t strSize;
|
||||
offset = get(offset, strSize);
|
||||
|
||||
value.resize(strSize);
|
||||
std::span<uint8_t> strSpan(reinterpret_cast<uint8_t *>(value.data()), strSize);
|
||||
offset = get(offset, strSpan);
|
||||
}
|
||||
|
||||
return offset; // Unable to read value, return original offset
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check message integrity */
|
||||
inline void MessageParser::messageCheck()
|
||||
{
|
||||
/* Message format : [SOF1(1)][SOF2(1)][SIZE(1)][PAYLOAD(n)][CRC16(2)][EOF(1)] */
|
||||
// Check SIZE
|
||||
const auto size = m_buffer[0];
|
||||
if (constexpr size_t CRCSize = 2; m_buffer.size() != CRCSize + size + sizeof(size))
|
||||
{
|
||||
m_messageStatus = MessageStatus::BAD_SIZE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check CRC16
|
||||
const auto loCRC = *(m_buffer.rbegin() + 1);
|
||||
const auto hiCRC = *m_buffer.rbegin();
|
||||
const uint16_t frameCRC = static_cast<uint16_t>(hiCRC << 8) | static_cast<uint16_t>(loCRC);
|
||||
const uint16_t computedCRC = generic::CRC16_modbus::computeCRC(m_buffer.subspan(1, m_buffer.size() - 3));
|
||||
if (frameCRC != computedCRC)
|
||||
{
|
||||
m_messageStatus = MessageStatus::BAD_CRC;
|
||||
return;
|
||||
}
|
||||
|
||||
m_buffer = m_buffer.subspan(1, size);
|
||||
m_messageStatus = MessageStatus::VALID;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class MessageBuilderHelper
|
||||
* @brief Utility class to simplify the usage of MessageBuilder templates.
|
||||
*
|
||||
* This helper provides convenient methods and type aliases to work with MessageBuilder
|
||||
* instances without exposing template parameters in function signatures.
|
||||
*/
|
||||
class MessageBuilderHelper
|
||||
{
|
||||
public:
|
||||
MessageBuilderHelper() = default; // Default constructor
|
||||
virtual ~MessageBuilderHelper() = default; // Default destructor
|
||||
MessageBuilderHelper(const MessageBuilderHelper &obj) = default; // Copy constructor
|
||||
MessageBuilderHelper(MessageBuilderHelper &&obj) noexcept = default; // Move constructor
|
||||
MessageBuilderHelper &operator=(const MessageBuilderHelper &obj) = default; // Copy assignment operator
|
||||
MessageBuilderHelper &operator=(MessageBuilderHelper &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
/**
|
||||
* @brief Gets the current message buffer as a span.
|
||||
* @return A span containing the current message bytes.
|
||||
*/
|
||||
[[nodiscard]] virtual std::span<const uint8_t> getBuffer() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Checks if the message buffer is full.
|
||||
* @return true if the buffer is full, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] virtual bool isFull() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Gets the current size of the message buffer.
|
||||
* @return The number of bytes currently in the message buffer.
|
||||
*/
|
||||
[[nodiscard]] virtual size_t getBufferSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Gets the current CRC16 value of the message.
|
||||
* @return The current CRC16 value.
|
||||
*/
|
||||
[[nodiscard]] virtual uint16_t getCRC() const = 0;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class MessageBuilder
|
||||
* @brief Builds a binary message payload with CRC calculation and escaping support.
|
||||
*
|
||||
* Supports appending POD types or byte spans, and computes CRC incrementally.
|
||||
* Provides methods to reset, append, and retrieve the current buffer and CRC.
|
||||
*
|
||||
* @tparam BUFFER_SIZE Maximum size of the internal message buffer.
|
||||
*
|
||||
* Usage:
|
||||
* - Use append() or operator<< to add data.
|
||||
* - Retrieve the buffer with getBuffer() and CRC with getCRC().
|
||||
*
|
||||
* @code
|
||||
* MessageBuilder<64> builder;
|
||||
* builder << uint16_t(0x1234) << std::span<const uint8_t>(data, dataLen);
|
||||
* auto buffer = builder.getBuffer();
|
||||
* auto crc = builder.getCRC();
|
||||
* @endcode
|
||||
*/
|
||||
template<size_t BUFFER_SIZE>
|
||||
class MessageBuilder : public MessageBuilderHelper
|
||||
{
|
||||
public:
|
||||
MessageBuilder(); // Default constructor
|
||||
virtual ~MessageBuilder() = default; // Default destructor
|
||||
MessageBuilder(const MessageBuilder &obj) = default; // Copy constructor
|
||||
MessageBuilder(MessageBuilder &&obj) noexcept = default; // Move constructor
|
||||
MessageBuilder &operator=(const MessageBuilder &obj) = default; // Copy assignment operator
|
||||
MessageBuilder &operator=(MessageBuilder &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
/**
|
||||
* @brief Resets the message builder to an empty state.
|
||||
* @return Reference to the MessageBuilder for chaining.
|
||||
*/
|
||||
MessageBuilder &reset(); // Reset the message builder
|
||||
|
||||
/**
|
||||
* @brief Appends a value of type T to the message buffer.
|
||||
* @tparam T The type of the value to append (POD type or std::span<uint8_t>).
|
||||
* @param value The value to append.
|
||||
* @return Reference to the MessageBuilder for chaining.
|
||||
*
|
||||
* @note If appending the value would exceed the buffer size, it is ignored.
|
||||
*/
|
||||
template<class T>
|
||||
MessageBuilder &append(const T &value); // Append value of type T to the message
|
||||
|
||||
/**
|
||||
* @brief Overloads the << operator to append a value of type T to the message buffer.
|
||||
* @tparam T The type of the value to append (POD type or std::span<uint8_t>).
|
||||
* @param value The value to append.
|
||||
* @return Reference to the MessageBuilder for chaining.
|
||||
*
|
||||
* @note If appending the value would exceed the buffer size, it is ignored.
|
||||
*/
|
||||
template<class T>
|
||||
MessageBuilder &operator<<(const T &value);
|
||||
|
||||
[[nodiscard]] std::span<const uint8_t> getBuffer() const override;
|
||||
[[nodiscard]] bool isFull() const override;
|
||||
[[nodiscard]] size_t getBufferSize() const override;
|
||||
[[nodiscard]] uint16_t getCRC() const override;
|
||||
|
||||
protected:
|
||||
std::array<uint8_t, BUFFER_SIZE> m_buffer{}; // Receiver buffer
|
||||
size_t m_index = 0; // Current index in the buffer
|
||||
|
||||
generic::CRC16_modbus m_CRC; // Current CRC value
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline MessageBuilder<BUFFER_SIZE>::MessageBuilder()
|
||||
{
|
||||
m_CRC.init();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline MessageBuilder<BUFFER_SIZE> &MessageBuilder<BUFFER_SIZE>::reset()
|
||||
{
|
||||
m_index = 0;
|
||||
m_CRC.init();
|
||||
return *this; // Return reference to self for chaining
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
template<class T>
|
||||
inline MessageBuilder<BUFFER_SIZE> &MessageBuilder<BUFFER_SIZE>::append(const T &value)
|
||||
{
|
||||
static_assert((std::is_standard_layout_v<T> && std::is_trivial_v<T>) || // POD type
|
||||
std::is_same_v<T, std::span<uint8_t>> || // std::span<uint8_t> type
|
||||
std::is_same_v<T, std::span<const uint8_t>> || // std::span<const uint8_t> type
|
||||
std::is_same_v<T, std::string> || // std::string type
|
||||
std::is_same_v<T, const std::string>); // const std::string type
|
||||
|
||||
if constexpr (std::is_standard_layout_v<T> && std::is_trivial_v<T>)
|
||||
{
|
||||
// POD type
|
||||
constexpr size_t typeSize = sizeof(T);
|
||||
if (m_index + typeSize <= BUFFER_SIZE)
|
||||
{
|
||||
std::array<uint8_t, typeSize> dataBuffer;
|
||||
std::memcpy(dataBuffer.data(), &value, typeSize);
|
||||
|
||||
std::memcpy(m_buffer.data() + m_index, dataBuffer.data(), dataBuffer.size());
|
||||
m_CRC.update(dataBuffer);
|
||||
m_index += typeSize;
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::span<uint8_t>> ||
|
||||
std::is_same_v<T, std::span<const uint8_t>>)
|
||||
{
|
||||
// std::span<uint8_t> type
|
||||
if (m_index + value.size() <= BUFFER_SIZE)
|
||||
{
|
||||
std::memcpy(m_buffer.data() + m_index, value.data(), value.size());
|
||||
m_CRC.update(value);
|
||||
m_index += value.size();
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, const std::string>)
|
||||
{
|
||||
// std::string type
|
||||
if (m_index + (value.size() + sizeof(uint8_t)) <= BUFFER_SIZE)
|
||||
{
|
||||
append(static_cast<uint8_t>(value.size()));
|
||||
|
||||
std::memcpy(m_buffer.data() + m_index, value.data(), value.size());
|
||||
m_CRC.update(std::span(reinterpret_cast<const uint8_t *>(value.data()), value.size()));
|
||||
m_index += value.size();
|
||||
}
|
||||
}
|
||||
|
||||
return *this; // Return reference to self for chaining
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
template<class T>
|
||||
inline MessageBuilder<BUFFER_SIZE> &MessageBuilder<BUFFER_SIZE>::operator<<(const T &value)
|
||||
{
|
||||
append(value);
|
||||
return *this; // Return reference to self for chaining
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline std::span<const uint8_t> MessageBuilder<BUFFER_SIZE>::getBuffer() const
|
||||
{
|
||||
return { m_buffer.data(), m_index };
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline bool MessageBuilder<BUFFER_SIZE>::isFull() const
|
||||
{
|
||||
return m_index >= BUFFER_SIZE;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline size_t MessageBuilder<BUFFER_SIZE>::getBufferSize() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline uint16_t MessageBuilder<BUFFER_SIZE>::getCRC() const
|
||||
{
|
||||
return m_CRC.getCRC();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class FramePacker
|
||||
* @brief Packs a message payload into a complete binary frame ready for transmission.
|
||||
*
|
||||
* Adds protocol headers, escapes special bytes, appends CRC and end-of-frame marker.
|
||||
* Ensures the packed frame fits in the provided buffer size.
|
||||
*
|
||||
* @tparam BUFFER_SIZE Maximum size of the output frame buffer.
|
||||
*
|
||||
* Usage:
|
||||
* - Call packMessage(payload, crc) to build the frame.
|
||||
* - Use isValid() to check for overflow.
|
||||
* - Retrieve the packed frame with getPackedFrame().
|
||||
*
|
||||
* @code
|
||||
* FramePacker<256> packer;
|
||||
* if (packer.packMessage(payload, crc)) {
|
||||
* auto frame = packer.getPackedFrame();
|
||||
* // Send frame...
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template<size_t BUFFER_SIZE>
|
||||
class FramePacker
|
||||
{
|
||||
static constexpr size_t CRC_SIZE = 4; // CRC16(2 x2) --> (CRC is 4 bytes to support escaped CRC)
|
||||
|
||||
public:
|
||||
FramePacker() = default; // Default constructor
|
||||
explicit FramePacker(std::span<const uint8_t> message, uint16_t crc); // Constructor
|
||||
~FramePacker() = default; // Default destructor
|
||||
FramePacker(const FramePacker &obj) = default; // Copy constructor
|
||||
FramePacker(FramePacker &&obj) noexcept = default; // Move constructor
|
||||
FramePacker &operator=(const FramePacker &obj) = default; // Copy assignment operator
|
||||
FramePacker &operator=(FramePacker &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
/**
|
||||
* @brief Packs the input message into a complete frame with headers, CRC, and escaping.
|
||||
* @param message The message payload to pack.
|
||||
* @param crc The CRC16 value to append to the frame.
|
||||
* @return true if packing was successful without overflow, false otherwise.
|
||||
*/
|
||||
bool packMessage(std::span<const uint8_t> message, uint16_t crc);
|
||||
|
||||
/**
|
||||
* @brief Checks if the packed frame is valid (no overflow occurred).
|
||||
* @return true if the packed frame is valid, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool isValid() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the packed frame buffer as a span.
|
||||
* @return A span containing the packed frame bytes.
|
||||
*/
|
||||
[[nodiscard]] std::span<const uint8_t> getPackedFrame() const;
|
||||
|
||||
protected:
|
||||
std::array<uint8_t, BUFFER_SIZE> m_frame{}; // Output buffer
|
||||
size_t m_index = 0; // Current index in output buffer
|
||||
|
||||
std::array<uint8_t, CRC_SIZE> m_CRCBuffer{}; // CRC buffer with escaping
|
||||
size_t m_CRCSize = 0; // Current size of CRC buffer with escaping
|
||||
|
||||
bool m_overflow = false; // Flag indicating if overflow occurred during packing
|
||||
|
||||
private:
|
||||
void appendByte(uint8_t byte); // Append byte with escaping to output buffer
|
||||
void appendRawByte(uint8_t byte); // Append raw byte to output buffer
|
||||
|
||||
void packCRC(uint16_t crc); // Pack CRC into CRC buffer with escaping
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline FramePacker<BUFFER_SIZE>::FramePacker(const std::span<const uint8_t> message, const uint16_t crc)
|
||||
{
|
||||
packMessage(message, crc);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline bool FramePacker<BUFFER_SIZE>::packMessage(const std::span<const uint8_t> message, const uint16_t crc)
|
||||
{
|
||||
// Reset state
|
||||
m_index = 0;
|
||||
m_overflow = false;
|
||||
|
||||
// Prepare CRC buffer with escaping
|
||||
packCRC(crc);
|
||||
|
||||
// Append header
|
||||
appendRawByte(SOF1_); // Append SOF1
|
||||
appendRawByte(SOF2_); // Append SOF2
|
||||
appendByte(static_cast<uint8_t>(message.size())); // Append payload size
|
||||
|
||||
// Append payload
|
||||
for (const auto &byte : message)
|
||||
appendByte(byte);
|
||||
|
||||
// Append CRC
|
||||
for (size_t i = 0; i < m_CRCSize; i++)
|
||||
appendRawByte(m_CRCBuffer[i]);
|
||||
|
||||
// Append EOF
|
||||
appendRawByte(EOF_);
|
||||
|
||||
return !m_overflow; // Return true if packing was successful
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline bool FramePacker<BUFFER_SIZE>::isValid() const
|
||||
{
|
||||
return !m_overflow;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline std::span<const uint8_t> FramePacker<BUFFER_SIZE>::getPackedFrame() const
|
||||
{
|
||||
return std::span<const uint8_t>(m_frame.data(), m_index);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline void FramePacker<BUFFER_SIZE>::appendByte(const uint8_t byte)
|
||||
{
|
||||
if (byte == SOF1_ || byte == SOF2_ || byte == EOF_ || byte == ESC_)
|
||||
{
|
||||
appendRawByte(ESC_); // Append escape byte
|
||||
}
|
||||
appendRawByte(byte); // Append actual byte
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline void FramePacker<BUFFER_SIZE>::appendRawByte(const uint8_t byte)
|
||||
{
|
||||
if (m_index < BUFFER_SIZE)
|
||||
m_frame[m_index++] = byte;
|
||||
else
|
||||
m_overflow = true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
template<size_t BUFFER_SIZE>
|
||||
inline void FramePacker<BUFFER_SIZE>::packCRC(const uint16_t crc)
|
||||
{
|
||||
m_CRCSize = 0;
|
||||
const uint8_t loCRC = static_cast<uint8_t>(crc & 0x00FF);
|
||||
const uint8_t hiCRC = static_cast<uint8_t>((crc >> 8) & 0x00FF);
|
||||
|
||||
if (loCRC == SOF1_ || loCRC == SOF2_ || loCRC == EOF_ || loCRC == ESC_)
|
||||
m_CRCBuffer[m_CRCSize++] = ESC_;
|
||||
m_CRCBuffer[m_CRCSize++] = loCRC;
|
||||
|
||||
if (hiCRC == SOF1_ || hiCRC == SOF2_ || hiCRC == EOF_ || hiCRC == ESC_)
|
||||
m_CRCBuffer[m_CRCSize++] = ESC_;
|
||||
m_CRCBuffer[m_CRCSize++] = hiCRC;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_ToolBox::comm::uBinaryFrame
|
||||
84
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/console/ansi.h
Normal file
84
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/console/ansi.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace sdi_toolBox::console::ANSI
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class EscapeCommand
|
||||
{
|
||||
public:
|
||||
enum class Color : uint8_t
|
||||
{
|
||||
Black = 0,
|
||||
Red,
|
||||
Green,
|
||||
Yellow,
|
||||
Blue,
|
||||
Magenta,
|
||||
Cyan,
|
||||
White,
|
||||
};
|
||||
|
||||
static constexpr uint8_t LIGHT = 0x01 << 0; // 0x01
|
||||
static constexpr uint8_t BRIGHT = 0x01 << 1; // 0x02
|
||||
static constexpr uint8_t DIM = 0x01 << 2; // 0x04
|
||||
static constexpr uint8_t UNDERLINE = 0x01 << 3; // 0x08
|
||||
static constexpr uint8_t BLINK = 0x01 << 4; // 0x10
|
||||
static constexpr uint8_t REVERSE = 0x01 << 5; // 0x20
|
||||
|
||||
public:
|
||||
static std::string clear();
|
||||
static std::string get(const Color foregroundColor,
|
||||
const uint8_t foregroundStyle = 0,
|
||||
const Color backgroundColor = Color::Black,
|
||||
const uint8_t backgroundStyle = 0);
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
inline std::string EscapeCommand::clear()
|
||||
{
|
||||
return "\x1b[0m";
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline std::string EscapeCommand::get(const Color foregroundColor, const uint8_t foregroundStyle, const Color backgroundColor, const uint8_t backgroundStyle)
|
||||
{
|
||||
std::string command = "\x1b[";
|
||||
int foregroundColorValue = static_cast<int>(foregroundColor) + 30;
|
||||
if (foregroundStyle & LIGHT)
|
||||
foregroundColorValue += 60;
|
||||
command += std::to_string(foregroundColorValue) + ";";
|
||||
|
||||
int backgroundColorValue = static_cast<int>(backgroundColor) + 40;
|
||||
if (backgroundStyle & LIGHT)
|
||||
backgroundColorValue += 60;
|
||||
command += std::to_string(backgroundColorValue);
|
||||
|
||||
if (foregroundStyle & BRIGHT)
|
||||
command += ";1";
|
||||
if (foregroundStyle & DIM)
|
||||
command += ";2";
|
||||
if (foregroundStyle & UNDERLINE)
|
||||
command += ";4";
|
||||
if (foregroundStyle & BLINK)
|
||||
command += ";5";
|
||||
if (foregroundStyle & REVERSE)
|
||||
command += ";7";
|
||||
|
||||
command += "m";
|
||||
return command;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::console::ANSI
|
||||
152
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/console/consoleTable.h
Normal file
152
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/console/consoleTable.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sdi_toolBox::console
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class ConsoleTable
|
||||
{
|
||||
public:
|
||||
using Row = std::vector<std::string>;
|
||||
using ColumnWidths = std::vector<size_t>;
|
||||
|
||||
public:
|
||||
ConsoleTable() = default; // Constructor
|
||||
virtual ~ConsoleTable() = default; // Destructor
|
||||
ConsoleTable(const ConsoleTable &other) = delete; // Copy constructor
|
||||
ConsoleTable(ConsoleTable &&other) noexcept = delete; // Move constructor
|
||||
ConsoleTable &operator=(const ConsoleTable &other) = delete; // Copy assignment
|
||||
ConsoleTable &operator=(ConsoleTable &&other) noexcept = delete; // Move assignment
|
||||
|
||||
void setHeaders(const Row &headers); // Set table headers
|
||||
void addRow(const Row &row); // Add a row to the table
|
||||
|
||||
[[nodiscard]] std::string render() const; // Render the table as a string
|
||||
|
||||
protected:
|
||||
Row m_headers; // Table headers
|
||||
std::vector<Row> m_rows; // Table rows
|
||||
size_t m_padding = 2; // Padding between columns
|
||||
|
||||
private:
|
||||
[[nodiscard]] ColumnWidths calculateColumnWidths() const; // Calculate column widths
|
||||
[[nodiscard]] static std::string drawSeparator(const ColumnWidths &columnWidths); // Draw a separator line (+---+---+---+)
|
||||
[[nodiscard]] static std::string drawRow(const Row &row, // Draw a single row
|
||||
const ColumnWidths &columnWidths);
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Set table headers */
|
||||
inline void ConsoleTable::setHeaders(const Row &headers)
|
||||
{
|
||||
m_headers = headers;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Add a row to the table */
|
||||
inline void ConsoleTable::addRow(const Row &row)
|
||||
{
|
||||
m_rows.push_back(row);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Render the table as a string */
|
||||
inline std::string ConsoleTable::render() const
|
||||
{
|
||||
// Calculate column widths
|
||||
const auto columnWidths = calculateColumnWidths();
|
||||
|
||||
// Initialize the output string
|
||||
std::string output;
|
||||
|
||||
// Draw the top separator
|
||||
output += drawSeparator(columnWidths);
|
||||
|
||||
// Draw the headers
|
||||
output += drawRow(m_headers, columnWidths);
|
||||
|
||||
// Draw the separator between headers and data
|
||||
output += drawSeparator(columnWidths);
|
||||
|
||||
// Draw the data rows
|
||||
for (const auto &row : m_rows)
|
||||
{
|
||||
output += drawRow(row, columnWidths);
|
||||
}
|
||||
|
||||
// Draw the bottom separator
|
||||
output += drawSeparator(columnWidths);
|
||||
|
||||
return output;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Calculate column widths */
|
||||
inline ConsoleTable::ColumnWidths ConsoleTable::calculateColumnWidths() const
|
||||
{
|
||||
// Determine the number of columns (based on headers or first row)
|
||||
const auto numColumns = m_headers.empty() ? (m_rows.empty() ? 0 : m_rows[0].size()) : m_headers.size();
|
||||
|
||||
// Initialize column widths
|
||||
ColumnWidths widths(numColumns, 0);
|
||||
|
||||
// Calculate widths based on headers
|
||||
for (size_t i = 0; i < m_headers.size() && i < numColumns; ++i)
|
||||
{
|
||||
widths[i] = std::max(widths[i], m_headers[i].length() + m_padding);
|
||||
}
|
||||
|
||||
// Calculate widths based on rows
|
||||
for (const auto &row : m_rows)
|
||||
{
|
||||
for (size_t i = 0; i < row.size() && i < numColumns; ++i)
|
||||
{
|
||||
widths[i] = std::max(widths[i], row[i].length() + m_padding);
|
||||
}
|
||||
}
|
||||
|
||||
return widths;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Draw a separator line (+---+---+---+) */
|
||||
inline std::string ConsoleTable::drawSeparator(const ColumnWidths &columnWidths)
|
||||
{
|
||||
std::string output;
|
||||
output += "+";
|
||||
for (const auto &width : columnWidths)
|
||||
{
|
||||
output += std::string(width, '-') + "+";
|
||||
}
|
||||
output += "\n";
|
||||
return output;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Draw a single row */
|
||||
inline std::string ConsoleTable::drawRow(const Row &row, const ColumnWidths &columnWidths)
|
||||
{
|
||||
std::string output;
|
||||
output += "|";
|
||||
for (size_t i = 0; i < columnWidths.size(); ++i)
|
||||
{
|
||||
const auto cellContent = (i < row.size()) ? row[i] : "";
|
||||
output += " " + cellContent;
|
||||
// Add padding spaces
|
||||
const size_t paddingSpaces = columnWidths[i] - cellContent.length() - 1;
|
||||
output += std::string(paddingSpaces, ' ');
|
||||
output += "|";
|
||||
}
|
||||
output += "\n";
|
||||
return output;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::console
|
||||
124
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/console/msw/win32Console.h
Normal file
124
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/console/msw/win32Console.h
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <Windows.h>
|
||||
#endif // defined WIN32
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace sdi_toolBox::logs
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Win32Console
|
||||
{
|
||||
public:
|
||||
static void initConsole(); // Init application console and attach debug console under MSW
|
||||
static void releaseConsole(); // Release application console
|
||||
|
||||
public:
|
||||
virtual ~Win32Console(); // Default destructor
|
||||
Win32Console(const Win32Console &obj) = delete; // Copy constructor
|
||||
Win32Console(Win32Console &&obj) noexcept = delete; // Move constructor
|
||||
Win32Console &operator=(const Win32Console &obj) = delete; // Copy assignment operator
|
||||
Win32Console &operator=(Win32Console &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
static bool hasAttachedConsole(); // Returns true if console is attached, false otherwise
|
||||
|
||||
protected:
|
||||
static inline std::unique_ptr<Win32Console> m_singleton;
|
||||
Win32Console(); // Default constructor
|
||||
|
||||
FILE *m_stdoutFile; // Reopened stdout file pointer
|
||||
FILE *m_stderrFile; // Reopened stderr file pointer
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Init application console and attach debug console under MSW */
|
||||
inline void Win32Console::initConsole()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
# error This calss is only available under Windows systems
|
||||
#endif
|
||||
|
||||
if (!m_singleton)
|
||||
m_singleton = std::unique_ptr<Win32Console>(new Win32Console());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Release application console */
|
||||
inline void Win32Console::releaseConsole()
|
||||
{
|
||||
if (m_singleton)
|
||||
m_singleton.reset();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default constructor */
|
||||
inline Win32Console::Win32Console()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
bool consoleIsCreated = false;
|
||||
|
||||
// Try to attach application to the current console
|
||||
AttachConsole(ATTACH_PARENT_PROCESS);
|
||||
|
||||
if (!GetConsoleWindow()) // No console was available
|
||||
{
|
||||
// Create console and attach application to it
|
||||
if (!AllocConsole())
|
||||
throw std::logic_error("Unable to attach application to debug console"); // Error during creating console
|
||||
consoleIsCreated = true;
|
||||
}
|
||||
|
||||
// Reopen stdout and stderr streams to console window
|
||||
if (freopen_s(&m_stdoutFile, "CONOUT$", "w", stdout) != 0)
|
||||
throw std::logic_error("Unable to reopen stdout"); // Error during reopen on stdout
|
||||
|
||||
if (freopen_s(&m_stderrFile, "CONOUT$", "w", stderr) != 0)
|
||||
throw std::logic_error("Unable to reopen stderr"); // Error during reopen on stderr
|
||||
|
||||
std::cout.clear();
|
||||
std::cerr.clear();
|
||||
|
||||
if (!consoleIsCreated)
|
||||
std::cout << std::endl; // Add a new line if console was already existing
|
||||
#endif
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
inline Win32Console::~Win32Console()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::cout.clear();
|
||||
std::cerr.clear();
|
||||
|
||||
// Free console
|
||||
FreeConsole();
|
||||
|
||||
// Close reopened stdout and stderr streams
|
||||
(void)fclose(m_stdoutFile);
|
||||
(void)fclose(m_stderrFile);
|
||||
#endif
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Returns true if console is attached, false otherwise */
|
||||
inline bool Win32Console::hasAttachedConsole()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (GetConsoleWindow())
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::logs
|
||||
81
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/base64.h
Normal file
81
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/base64.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace sdi_toolBox::crypto
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Base64
|
||||
{
|
||||
public:
|
||||
Base64() = default; // Default constructor
|
||||
virtual ~Base64() = default; // Default destructor
|
||||
Base64(const Base64 &obj) = delete; // Copy constructor
|
||||
Base64(Base64 &&obj) noexcept = delete; // Move constructor
|
||||
Base64 &operator=(const Base64 &obj) = delete; // Copy assignment operator
|
||||
Base64 &operator=(Base64 &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
static std::string encode(const std::span<uint8_t> &binary_data); // Encodes binary data into a Base64 string
|
||||
static std::vector<uint8_t> decode(const std::string &base64_string); // Decodes a Base64 string into binary data
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Encodes binary data into a Base64 string */
|
||||
inline std::string Base64::encode(const std::span<uint8_t> &binary_data)
|
||||
{
|
||||
if (binary_data.empty())
|
||||
return "";
|
||||
|
||||
// Calculate the length of the encoded data
|
||||
const size_t output_len_max = EVP_ENCODE_LENGTH(binary_data.size());
|
||||
|
||||
// Allocate output buffer (with null terminator)
|
||||
std::vector<char> output_buffer(output_len_max + 1, 0);
|
||||
|
||||
// OpenSSL encoding
|
||||
const auto final_len = EVP_EncodeBlock(reinterpret_cast<uint8_t *>(output_buffer.data()),
|
||||
binary_data.data(),
|
||||
binary_data.size());
|
||||
if (final_len < 0)
|
||||
throw std::runtime_error("Error: Base64 encoding failed");
|
||||
|
||||
return std::string(output_buffer.data());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Decodes a Base64 string into binary data */
|
||||
inline std::vector<uint8_t> Base64::decode(const std::string &base64_string)
|
||||
{
|
||||
if (base64_string.empty())
|
||||
return {};
|
||||
|
||||
// Calculate the length of the decoded data
|
||||
const auto output_len_max = EVP_DECODE_LENGTH(base64_string.size());
|
||||
|
||||
// Allocate output buffer
|
||||
std::vector<uint8_t> output_buffer(output_len_max);
|
||||
|
||||
// OpenSSL decoding
|
||||
const auto final_len = EVP_DecodeBlock(output_buffer.data(),
|
||||
reinterpret_cast<const unsigned char *>(base64_string.data()),
|
||||
static_cast<int>(base64_string.size()));
|
||||
if (final_len < 0)
|
||||
throw std::runtime_error("Error: Base64 decoding failed: Invalid data");
|
||||
output_buffer.resize(final_len);
|
||||
|
||||
return output_buffer;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::crypto
|
||||
71
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/data.h
Normal file
71
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/data.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sdi_toolBox::crypto
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Data8
|
||||
{
|
||||
public:
|
||||
Data8() = default; // Default constructor
|
||||
explicit Data8(const std::string &data); // Constructor from string
|
||||
explicit Data8(const std::span<const uint8_t> &data); // Constructor from byte span
|
||||
virtual ~Data8() = default; // Default destructor
|
||||
Data8(const Data8 &obj) = delete; // Copy constructor
|
||||
Data8(Data8 &&obj) noexcept = delete; // Move constructor
|
||||
Data8 &operator=(const Data8 &obj) = delete; // Copy assignment operator
|
||||
Data8 &operator=(Data8 &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
[[nodiscard]] std::string getString() const; // Get data as string
|
||||
[[nodiscard]] std::span<const uint8_t> getBytes() const; // Get data as byte span
|
||||
[[nodiscard]] size_t getSize() const; // Get data size
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> m_data; // Raw data storage
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor from string */
|
||||
inline Data8::Data8(const std::string &data)
|
||||
{
|
||||
m_data = std::vector<uint8_t>(data.begin(), data.end());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline Data8::Data8(const std::span<const uint8_t> &data)
|
||||
{
|
||||
m_data = std::vector<uint8_t>(data.begin(), data.end());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get data as string */
|
||||
inline std::string Data8::getString() const
|
||||
{
|
||||
return std::string(m_data.begin(), m_data.end());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get data as byte span */
|
||||
inline std::span<const uint8_t> Data8::getBytes() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get data size */
|
||||
inline size_t Data8::getSize() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::crypto
|
||||
78
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/digitalHash.h
Normal file
78
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/digitalHash.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <openssl/evp.h>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
namespace sdi_toolBox::crypto
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class DigitalHash
|
||||
{
|
||||
public:
|
||||
DigitalHash() = default; // Default constructor
|
||||
virtual ~DigitalHash() = default; // Default destructor
|
||||
DigitalHash(const DigitalHash &obj) = delete; // Copy constructor
|
||||
DigitalHash(DigitalHash &&obj) noexcept = delete; // Move constructor
|
||||
DigitalHash &operator=(const DigitalHash &obj) = delete; // Copy assignment operator
|
||||
DigitalHash &operator=(DigitalHash &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
static std::string calculateDataHash(const std::span<const uint8_t> &data, const EVP_MD *md_type); // Calculate digital hash from data buffer
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Calculate digital hash from data buffer */
|
||||
inline std::string DigitalHash::calculateDataHash(const std::span<const uint8_t> &data, const EVP_MD *md_type)
|
||||
{
|
||||
// Initialize OpenSSL digest context
|
||||
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
|
||||
if (!mdctx)
|
||||
throw std::runtime_error("Error: Failed to create EVP_MD_CTX");
|
||||
|
||||
// Initialize digest operation
|
||||
if (EVP_DigestInit_ex(mdctx, md_type, nullptr) != 1)
|
||||
{
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("Error: EVP_DigestInit_ex failed");
|
||||
}
|
||||
|
||||
// Update digest with data
|
||||
if (EVP_DigestUpdate(mdctx, data.data(), data.size()) != 1)
|
||||
{
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("Error: EVP_DigestUpdate failed");
|
||||
}
|
||||
|
||||
// Finalize digest computation
|
||||
std::vector<uint8_t> hash(EVP_MD_size(md_type));
|
||||
unsigned int hash_len = 0;
|
||||
if (EVP_DigestFinal_ex(mdctx, hash.data(), &hash_len) != 1)
|
||||
{
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
throw std::runtime_error("Error: EVP_DigestFinal_ex failed");
|
||||
}
|
||||
|
||||
// Clean up
|
||||
EVP_MD_CTX_free(mdctx);
|
||||
|
||||
// Convert hash to hexadecimal string
|
||||
std::string hash_hex;
|
||||
for (unsigned int i = 0; i < hash_len; ++i)
|
||||
hash_hex += std::format("{:02x}", hash[i]);
|
||||
|
||||
return hash_hex;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::crypto
|
||||
255
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/digitalSign.h
Normal file
255
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/crypto/digitalSign.h
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sdi_toolBox::crypto
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class DigitalSign
|
||||
{
|
||||
// Custom deleter for EVP_PKEY to ensure it's automatically freed
|
||||
struct EVP_PKEY_Deleter
|
||||
{
|
||||
void operator()(EVP_PKEY *p) const
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
EVP_PKEY_free(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
using pEVP_PKEY = std::unique_ptr<EVP_PKEY, EVP_PKEY_Deleter>;
|
||||
|
||||
struct BIO_Deleter
|
||||
{
|
||||
void operator()(BIO *b) const
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
BIO_free(b);
|
||||
}
|
||||
}
|
||||
};
|
||||
using pBIO = std::unique_ptr<BIO, BIO_Deleter>;
|
||||
|
||||
struct EVP_MD_CTX_Deleter
|
||||
{
|
||||
void operator()(EVP_MD_CTX *ctx) const
|
||||
{
|
||||
if (ctx)
|
||||
{
|
||||
EVP_MD_CTX_free(ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
using pEVP_MD_CTX = std::unique_ptr<EVP_MD_CTX, EVP_MD_CTX_Deleter>;
|
||||
|
||||
public:
|
||||
DigitalSign() = default; // Default constructor
|
||||
virtual ~DigitalSign() = default; // Default destructor
|
||||
DigitalSign(const DigitalSign &obj) = delete; // Copy constructor
|
||||
DigitalSign(DigitalSign &&obj) noexcept = delete; // Move constructor
|
||||
DigitalSign &operator=(const DigitalSign &obj) = delete; // Copy assignment operator
|
||||
DigitalSign &operator=(DigitalSign &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
void loadPEMKeyFromFile(const std::filesystem::path &filepath, bool is_private); // Load key from PEM file
|
||||
void loadPEMKeyFromMemory(const std::string &key, bool is_private); // Load key from memory
|
||||
|
||||
std::vector<uint8_t> sign(const Data8 &data, const EVP_MD *digest_type = EVP_sha256()) const; // Sign data with private key
|
||||
std::string signHex(const Data8 &data, const EVP_MD *digest_type = EVP_sha256()) const; // Sign data with private key
|
||||
|
||||
bool verify(const Data8 &data, const std::span<const uint8_t> &signature, const EVP_MD *digest_type = EVP_sha256()) const; // Verify signature with public key
|
||||
bool verify(const Data8 &data, const std::string &signature, const EVP_MD *digest_type = EVP_sha256()) const; // Verify signature with public key
|
||||
|
||||
protected:
|
||||
pEVP_PKEY m_privateKey;
|
||||
pEVP_PKEY m_publicKey;
|
||||
|
||||
private:
|
||||
static std::vector<uint8_t> hexToBytes(const std::string &hexString); // Converts a hexadecimal string into a vector of bytes
|
||||
static std::string bytesToHex(const std::span<const uint8_t> &bytes); // Converts a span of bytes into a hexadecimal string representation
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Load key from PEM file */
|
||||
inline void DigitalSign::loadPEMKeyFromFile(const std::filesystem::path &filepath, const bool is_private)
|
||||
{
|
||||
std::ifstream file(filepath, std::ifstream::binary);
|
||||
if (!file.is_open())
|
||||
throw std::runtime_error("Error: Could not open file " + filepath.string());
|
||||
|
||||
const std::string key((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
loadPEMKeyFromMemory(key, is_private);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Load key from memory */
|
||||
inline void DigitalSign::loadPEMKeyFromMemory(const std::string &key, const bool is_private)
|
||||
{
|
||||
if (key.empty())
|
||||
throw std::runtime_error("Error: Input stream is empty or could not be read");
|
||||
|
||||
// Create a memory BIO (Basic Input/Output) for reading the key
|
||||
const auto bio = static_cast<pBIO>(BIO_new_mem_buf(key.data(), static_cast<int>(key.size())));
|
||||
if (!bio)
|
||||
throw std::runtime_error("Error: Failed to create memory BIO");
|
||||
|
||||
// Use the BIO to read the key
|
||||
if (is_private)
|
||||
{
|
||||
m_privateKey = static_cast<pEVP_PKEY>(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
|
||||
if (!m_privateKey)
|
||||
throw std::runtime_error("Error: Failed to read private key from memory buffer");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_publicKey = static_cast<pEVP_PKEY>(PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr));
|
||||
if (!m_publicKey)
|
||||
throw std::runtime_error("Error: Failed to read private key from memory buffer");
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Sign data with private key */
|
||||
inline std::vector<uint8_t> DigitalSign::sign(const Data8 &data, const EVP_MD *digest_type) const
|
||||
{
|
||||
if (!m_privateKey)
|
||||
throw std::runtime_error("Error: Private key is not loaded for signing");
|
||||
|
||||
// Initialize the context for signing
|
||||
const auto md_ctx = static_cast<pEVP_MD_CTX>(EVP_MD_CTX_new());
|
||||
if (!md_ctx)
|
||||
throw std::runtime_error("Error: Failed to create EVP_MD_CTX");
|
||||
|
||||
// Set the context for signing with the private key and digest
|
||||
if (EVP_DigestSignInit(md_ctx.get(), nullptr, digest_type, nullptr, m_privateKey.get()) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestSignInit failed");
|
||||
|
||||
// Provide the data to be signed
|
||||
if (EVP_DigestSignUpdate(md_ctx.get(), data.getBytes().data(), data.getSize()) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestSignUpdate failed");
|
||||
|
||||
// Determine the required signature size
|
||||
size_t sigLen = 0;
|
||||
if (EVP_DigestSignFinal(md_ctx.get(), nullptr, &sigLen) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestSignFinal (size) failed");
|
||||
|
||||
// Allocate space and generate the signature
|
||||
std::vector<uint8_t> signature(sigLen);
|
||||
if (EVP_DigestSignFinal(md_ctx.get(), signature.data(), &sigLen) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestSignFinal (signature) failed");
|
||||
|
||||
// Resize signature to the actual length used
|
||||
signature.resize(sigLen);
|
||||
|
||||
return signature;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Sign data */
|
||||
inline std::string DigitalSign::signHex(const Data8 &data, const EVP_MD *digest_type) const
|
||||
{
|
||||
return bytesToHex(sign(data, digest_type));
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Verify signature with public key */
|
||||
inline bool DigitalSign::verify(const Data8 &data, const std::span<const uint8_t> &signature, const EVP_MD *digest_type) const
|
||||
{
|
||||
if (!m_publicKey)
|
||||
throw std::runtime_error("Error: Public key is not loaded for verifying");
|
||||
|
||||
// Initialize the context for verifying
|
||||
const auto md_ctx = static_cast<pEVP_MD_CTX>(EVP_MD_CTX_new());
|
||||
if (!md_ctx)
|
||||
throw std::runtime_error("Error: Failed to create EVP_MD_CTX");
|
||||
|
||||
// Set the context for verifying with the public key and digest
|
||||
if (EVP_DigestVerifyInit(md_ctx.get(), nullptr, digest_type, nullptr, m_publicKey.get()) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestVerifyInit failed");
|
||||
|
||||
// Provide the original data
|
||||
if (EVP_DigestVerifyUpdate(md_ctx.get(), data.getBytes().data(), data.getSize()) != 1)
|
||||
throw std::runtime_error("Error: EVP_DigestVerifyUpdate failed");
|
||||
|
||||
// Verify the signature
|
||||
// Returns 1 for a good signature, 0 for a bad signature, and -1 on error
|
||||
const auto result = EVP_DigestVerifyFinal(md_ctx.get(), signature.data(), signature.size());
|
||||
if (result == 1)
|
||||
return true; // Signature is valid
|
||||
if (result == 0)
|
||||
return false; // Signature is NOT valid
|
||||
|
||||
throw std::runtime_error("Error: EVP_DigestVerifyFinal failed during execution");
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Verify signature with public key */
|
||||
inline bool DigitalSign::verify(const Data8 &data, const std::string &signature, const EVP_MD *digest_type) const
|
||||
{
|
||||
return verify(data, hexToBytes(signature), digest_type);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Converts a hexadecimal string into a vector of bytes */
|
||||
inline std::vector<uint8_t> DigitalSign::hexToBytes(const std::string &hexString)
|
||||
{
|
||||
// A valid hex string must have an even length
|
||||
if (hexString.length() % 2 != 0)
|
||||
throw std::invalid_argument("Hex string must have an even length");
|
||||
|
||||
std::vector<uint8_t> bytes;
|
||||
bytes.reserve(hexString.length() / 2);
|
||||
|
||||
for (size_t i = 0; i < hexString.length(); i += 2)
|
||||
{
|
||||
std::string byteString = hexString.substr(i, 2);
|
||||
unsigned long byteVal;
|
||||
|
||||
try
|
||||
{
|
||||
byteVal = std::stoul(byteString, nullptr, 16);
|
||||
}
|
||||
catch (const std::exception & /*e*/)
|
||||
{
|
||||
throw std::invalid_argument("Invalid hexadecimal character sequence: " + byteString); // Non-hex characters ?
|
||||
}
|
||||
|
||||
// Check if the value fits in a uint8_t (should always be true for 2 chars)
|
||||
if (byteVal > 0xFF)
|
||||
throw std::invalid_argument("Internal error: Converted value exceeds 0xFF");
|
||||
|
||||
bytes.push_back(static_cast<uint8_t>(byteVal));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Converts a span of bytes into a hexadecimal string representation */
|
||||
inline std::string DigitalSign::bytesToHex(const std::span<const uint8_t> &bytes)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::uppercase << std::setfill('0');
|
||||
|
||||
for (const uint8_t byte : bytes)
|
||||
ss << std::format("{0:02X}", byte);
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::crypto
|
||||
175
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/age.h
Normal file
175
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/age.h
Normal file
@@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
class Age
|
||||
{
|
||||
public:
|
||||
Age() = default; // Default constructor
|
||||
virtual ~Age() = default; // Default destructor
|
||||
Age(const Age &obj) = default; // Copy constructor
|
||||
Age(Age &&obj) noexcept = default; // Move constructor
|
||||
Age &operator=(const Age &obj) = default; // Copy assignment operator
|
||||
Age &operator=(Age &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
explicit Age(std::chrono::nanoseconds age); // Constructor
|
||||
void set(std::chrono::nanoseconds age); // Convert age in microseconds to days, hours, minutes, seconds, milliseconds and microseconds
|
||||
|
||||
std::chrono::microseconds getAge() const; // Return age in microseconds
|
||||
std::chrono::days getDays() const; // Return days part in age
|
||||
std::chrono::hours getHours() const; // Return hours part in age
|
||||
std::chrono::minutes getMinutes() const; // Return minutes part in age
|
||||
std::chrono::seconds getSeconds() const; // Return seconds part in age
|
||||
std::chrono::milliseconds getMilliseconds() const; // Return milliseconds part in age
|
||||
std::chrono::microseconds getMicroseconds() const; // Return microseconds part in age
|
||||
std::chrono::nanoseconds getNanoseconds() const; // Return nanoseconds part in age
|
||||
|
||||
std::string toString(bool show_ms = true, bool show_us = false, bool show_ns = false) const; // Return age as a string in format "[d] H:m:s.ms.us.ns"
|
||||
|
||||
void testAge() const; // Test function to verify the correctness of the Age class implementation
|
||||
|
||||
protected:
|
||||
std::chrono::nanoseconds m_age = std::chrono::nanoseconds(0);
|
||||
|
||||
std::chrono::days m_days = std::chrono::days(0);
|
||||
std::chrono::hours m_hours = std::chrono::hours(0);
|
||||
std::chrono::minutes m_minutes = std::chrono::minutes(0);
|
||||
std::chrono::seconds m_seconds = std::chrono::seconds(0);
|
||||
std::chrono::milliseconds m_milliseconds = std::chrono::milliseconds(0);
|
||||
std::chrono::microseconds m_microseconds = std::chrono::microseconds(0);
|
||||
std::chrono::nanoseconds m_nanoseconds = std::chrono::nanoseconds(0);
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline Age::Age(const std::chrono::nanoseconds age)
|
||||
{
|
||||
set(age);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Convert age in microseconds to days, hours, minutes, seconds, milliseconds and microseconds */
|
||||
inline void Age::set(std::chrono::nanoseconds age)
|
||||
{
|
||||
constexpr auto max_ns = std::chrono::nanoseconds::max().count(); // For an int64_t storage, the maximum value corresponds to more than 106752 days
|
||||
constexpr auto min_ns = std::chrono::nanoseconds::min().count();
|
||||
if (age.count() > max_ns || age.count() < min_ns)
|
||||
throw std::overflow_error("Duration exceeds the limits of std::chrono::nanoseconds");
|
||||
|
||||
if (age < std::chrono::nanoseconds(0))
|
||||
throw std::invalid_argument("Age cannot be negative");
|
||||
|
||||
m_age = age;
|
||||
|
||||
m_days = std::chrono::duration_cast<std::chrono::days>(age);
|
||||
age -= m_days;
|
||||
|
||||
m_hours = std::chrono::duration_cast<std::chrono::hours>(age);
|
||||
age -= m_hours;
|
||||
|
||||
m_minutes = std::chrono::duration_cast<std::chrono::minutes>(age);
|
||||
age -= m_minutes;
|
||||
|
||||
m_seconds = std::chrono::duration_cast<std::chrono::seconds>(age);
|
||||
age -= m_seconds;
|
||||
|
||||
m_milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(age);
|
||||
age -= m_milliseconds;
|
||||
|
||||
m_microseconds = std::chrono::duration_cast<std::chrono::microseconds>(age);
|
||||
age -= m_microseconds;
|
||||
|
||||
m_nanoseconds = age;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return age in microseconds */
|
||||
inline std::chrono::microseconds Age::getAge() const
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(m_age);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return days part in age */
|
||||
inline std::chrono::days Age::getDays() const
|
||||
{
|
||||
return m_days;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return hours part in age */
|
||||
inline std::chrono::hours Age::getHours() const
|
||||
{
|
||||
return m_hours;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return minutes part in age */
|
||||
inline std::chrono::minutes Age::getMinutes() const
|
||||
{
|
||||
return m_minutes;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return seconds part in age */
|
||||
inline std::chrono::seconds Age::getSeconds() const
|
||||
{
|
||||
return m_seconds;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return milliseconds part in age */
|
||||
inline std::chrono::milliseconds Age::getMilliseconds() const
|
||||
{
|
||||
return m_milliseconds;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return microseconds part in age */
|
||||
inline std::chrono::microseconds Age::getMicroseconds() const
|
||||
{
|
||||
return m_microseconds;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return nanoseconds part in age */
|
||||
inline std::chrono::nanoseconds Age::getNanoseconds() const
|
||||
{
|
||||
return m_nanoseconds;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return age as a string in format "[d] H:m:s.ms.us.ns".
|
||||
* The show_ms, show_us and show_ns parameters control whether
|
||||
* to include milliseconds, microseconds and nanoseconds in the
|
||||
* output string. */
|
||||
inline std::string Age::toString(const bool show_ms, const bool show_us, const bool show_ns) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
// Add days if greater than 0
|
||||
if (m_days.count() > 0)
|
||||
oss << std::format("[{}] ", m_days.count());
|
||||
|
||||
// Add hours, minutes and seconds
|
||||
oss << std::format("{:02}:{:02}:{:02}", m_hours.count(), m_minutes.count(), m_seconds.count());
|
||||
|
||||
// Add milliseconds, microseconds and nanoseconds if requested
|
||||
if (show_ms || show_us || show_ns)
|
||||
oss << std::format(".{:03}", m_milliseconds.count());
|
||||
if (show_us || show_ns)
|
||||
oss << std::format("{:03}", m_microseconds.count());
|
||||
if (show_ns)
|
||||
oss << std::format("{:03}", m_nanoseconds.count());
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Test function to verify the correctness of the Age class implementation */
|
||||
inline void Age::testAge() const
|
||||
{
|
||||
const Age age(std::chrono::nanoseconds(90061001001)); // 1 day, 1 hour, 1 minute, 1 second, 1 millisecond, 1 microsecond, 1 nanosecond
|
||||
|
||||
assert(age.getDays().count() == 1);
|
||||
assert(age.getHours().count() == 1);
|
||||
assert(age.getMinutes().count() == 1);
|
||||
assert(age.getSeconds().count() == 1);
|
||||
assert(age.getMilliseconds().count() == 1);
|
||||
assert(age.getMicroseconds().count() == 1);
|
||||
assert(age.getNanoseconds().count() == 1);
|
||||
|
||||
std::cout << "All tests passed!" << std::endl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
49
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/iPause.h
Normal file
49
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/iPause.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace sdi_toolBox::dateTime
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Abstract interface for pause functionality.
|
||||
*
|
||||
* Provides a common interface for implementing pause or delay mechanisms.
|
||||
* Derived classes must implement the wait() method to pause execution
|
||||
* for a specified duration.
|
||||
*/
|
||||
class IPause
|
||||
{
|
||||
public:
|
||||
using Duration = std::chrono::milliseconds;
|
||||
|
||||
public:
|
||||
~IPause() = default; // Default destructor
|
||||
IPause(const IPause &obj) = default; // Copy constructor
|
||||
IPause(IPause &&obj) noexcept = default; // Move constructor
|
||||
IPause &operator=(const IPause &obj) = default; // Copy assignment operator
|
||||
IPause &operator=(IPause &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
/**
|
||||
* @brief Pause execution for the specified duration.
|
||||
* @param duration Duration of the pause.
|
||||
*/
|
||||
virtual void wait(const Duration &duration) const = 0;
|
||||
|
||||
protected:
|
||||
IPause() = default; // Default constructor
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::dateTime
|
||||
100
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/iTimer.h
Normal file
100
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/iTimer.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace sdi_toolBox::dateTime
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Abstract interface for timer functionality.
|
||||
*
|
||||
* Provides a common interface for timer implementations, allowing
|
||||
* measurement of elapsed time and checking for timeouts.
|
||||
* Derived classes must implement the now() method to provide the
|
||||
* current time point.
|
||||
*/
|
||||
class ITimer
|
||||
{
|
||||
public:
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
using TimePoint = Clock::time_point;
|
||||
using Duration = std::chrono::milliseconds;
|
||||
|
||||
public:
|
||||
virtual ~ITimer() = default; // Default destructor
|
||||
ITimer(const ITimer &obj) = default; // Copy constructor
|
||||
ITimer(ITimer &&obj) noexcept = default; // Move constructor
|
||||
ITimer &operator=(const ITimer &obj) = default; // Copy assignment operator
|
||||
ITimer &operator=(ITimer &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
/**
|
||||
* @brief Resets the reference time point to the current time.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* @brief Returns the current time point.
|
||||
* @return The current time point as defined by the derived class.
|
||||
*/
|
||||
[[nodiscard]] virtual TimePoint now() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the elapsed duration since the last reset.
|
||||
* @return Duration since the last reset.
|
||||
*/
|
||||
[[nodiscard]] Duration getElapsed() const;
|
||||
|
||||
/**
|
||||
* @brief Checks if the specified duration has elapsed since the last reset.
|
||||
* @param duration The duration to check.
|
||||
* @param autoReset If true, automatically resets the timer when the duration has elapsed.
|
||||
* @return True if the duration has elapsed, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool isElapsed(const Duration duration,
|
||||
bool autoReset = false);
|
||||
|
||||
protected:
|
||||
ITimer() = default; // Default constructor
|
||||
|
||||
TimePoint m_t0; // Timepoint t0 for elapsed time measurement
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Reset the timepoint t0 */
|
||||
inline void ITimer::reset()
|
||||
{
|
||||
m_t0 = now();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the elapsed time */
|
||||
inline ITimer::Duration ITimer::getElapsed() const
|
||||
{
|
||||
return std::chrono::duration_cast<Duration>(now() - m_t0);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the specified delay has elapsed */
|
||||
inline bool ITimer::isElapsed(const Duration duration, bool autoReset)
|
||||
{
|
||||
Duration elapsed = getElapsed();
|
||||
if (elapsed >= duration)
|
||||
{
|
||||
if (autoReset)
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::dateTime
|
||||
59
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/pause.h
Normal file
59
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/pause.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "iPause.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace sdi_toolBox::dateTime
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Standard pause implementation.
|
||||
*
|
||||
* Implements IPause to provide a blocking pause using standard C++ mechanisms.
|
||||
* The actual delay is performed using std::this_thread::sleep_for.
|
||||
*/
|
||||
class Pause : public IPause
|
||||
{
|
||||
public:
|
||||
Pause() = default; // Default constructor
|
||||
~Pause() = default; // Default destructor
|
||||
Pause(const Pause &obj) = default; // Copy constructor
|
||||
Pause(Pause &&obj) noexcept = default; // Move constructor
|
||||
Pause &operator=(const Pause &obj) = default; // Copy assignment operator
|
||||
Pause &operator=(Pause &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
explicit Pause(const Duration &duration); // Constructor
|
||||
|
||||
/**
|
||||
* @brief Pause execution for the specified duration.
|
||||
* @param duration Duration of the pause.
|
||||
*/
|
||||
void wait(const Duration &duration) const override;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline Pause::Pause(const Duration &duration)
|
||||
{
|
||||
wait(duration);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Pause execution for the specified duration */
|
||||
inline void Pause::wait(const Duration &duration) const
|
||||
{
|
||||
std::this_thread::sleep_for(duration);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::dateTime
|
||||
55
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/timer.h
Normal file
55
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/dateTime/timer.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "iTimer.h"
|
||||
|
||||
namespace sdi_toolBox::dateTime
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Standard timer implementation using std::chrono.
|
||||
*
|
||||
* Implements ITimer using std::chrono::high_resolution_clock for
|
||||
* high-precision timing in standard C++ environments.
|
||||
*/
|
||||
class Timer : public ITimer
|
||||
{
|
||||
public:
|
||||
Timer(); // Default constructor
|
||||
~Timer() = default; // Default destructor
|
||||
Timer(const Timer &obj) = default; // Copy constructor
|
||||
Timer(Timer &&obj) noexcept = default; // Move constructor
|
||||
Timer &operator=(const Timer &obj) = default; // Copy assignment operator
|
||||
Timer &operator=(Timer &&obj) noexcept = default; // Move assignment operator
|
||||
|
||||
/**
|
||||
* @brief Returns the current time point using std::chrono::high_resolution_clock.
|
||||
* @return The current time point.
|
||||
*/
|
||||
[[nodiscard]] TimePoint now() const override;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Default constructor */
|
||||
inline Timer::Timer()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the current timepoint */
|
||||
inline Timer::TimePoint Timer::now() const
|
||||
{
|
||||
return Clock::now();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::dateTime
|
||||
24765
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/external/json/nlohmann/json.hpp
vendored
Normal file
24765
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/external/json/nlohmann/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
176
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/external/json/nlohmann/json_fwd.hpp
vendored
Normal file
176
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/external/json/nlohmann/json_fwd.hpp
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
|
||||
#include <cstdint> // int64_t, uint64_t
|
||||
#include <map> // map
|
||||
#include <memory> // allocator
|
||||
#include <string> // string
|
||||
#include <vector> // vector
|
||||
|
||||
// #include <nlohmann/detail/abi_macros.hpp>
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.11.3
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
|
||||
// This file contains all macro definitions affecting or depending on the ABI
|
||||
|
||||
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
|
||||
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
|
||||
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3
|
||||
#warning "Already included a different version of the library!"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_MINOR 11 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_PATCH 3 // NOLINT(modernize-macro-to-enum)
|
||||
|
||||
#ifndef JSON_DIAGNOSTICS
|
||||
#define JSON_DIAGNOSTICS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
_v ## major ## _ ## minor ## _ ## patch
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
|
||||
|
||||
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION
|
||||
#else
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
|
||||
NLOHMANN_JSON_VERSION_MINOR, \
|
||||
NLOHMANN_JSON_VERSION_PATCH)
|
||||
#endif
|
||||
|
||||
// Combine namespace components
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
|
||||
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE
|
||||
#define NLOHMANN_JSON_NAMESPACE \
|
||||
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION)
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
|
||||
namespace nlohmann \
|
||||
{ \
|
||||
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION) \
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_END
|
||||
#define NLOHMANN_JSON_NAMESPACE_END \
|
||||
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
|
||||
} // namespace nlohmann
|
||||
#endif
|
||||
|
||||
|
||||
/*!
|
||||
@brief namespace for Niels Lohmann
|
||||
@see https://github.com/nlohmann
|
||||
@since version 1.0.0
|
||||
*/
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/*!
|
||||
@brief default JSONSerializer template argument
|
||||
|
||||
This serializer ignores the template arguments and uses ADL
|
||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
||||
for serialization.
|
||||
*/
|
||||
template<typename T = void, typename SFINAE = void>
|
||||
struct adl_serializer;
|
||||
|
||||
/// a class to store JSON values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||
template<template<typename U, typename V, typename... Args> class ObjectType =
|
||||
std::map,
|
||||
template<typename U, typename... Args> class ArrayType = std::vector,
|
||||
class StringType = std::string, class BooleanType = bool,
|
||||
class NumberIntegerType = std::int64_t,
|
||||
class NumberUnsignedType = std::uint64_t,
|
||||
class NumberFloatType = double,
|
||||
template<typename U> class AllocatorType = std::allocator,
|
||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||
adl_serializer,
|
||||
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||
class CustomBaseClass = void>
|
||||
class basic_json;
|
||||
|
||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template<typename RefStringType>
|
||||
class json_pointer;
|
||||
|
||||
/*!
|
||||
@brief default specialization
|
||||
@sa https://json.nlohmann.me/api/json/
|
||||
*/
|
||||
using json = basic_json<>;
|
||||
|
||||
/// @brief a minimal map-like container that preserves insertion order
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||
struct ordered_map;
|
||||
|
||||
/// @brief specialization that maintains the insertion order of object keys
|
||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||
using ordered_json = basic_json<nlohmann::ordered_map>;
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
105
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/generic/circularBuffer.h
Normal file
105
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/generic/circularBuffer.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace sdi_toolBox::generic
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
template<class T, size_t BUFFER_SIZE>
|
||||
class CircularBuffer final
|
||||
{
|
||||
public:
|
||||
CircularBuffer() = default; // Constructor
|
||||
~CircularBuffer() = default; // Destructor
|
||||
CircularBuffer(const CircularBuffer &other) = delete; // Copy constructor
|
||||
CircularBuffer(CircularBuffer &&other) noexcept = delete; // Move constructor
|
||||
CircularBuffer &operator=(const CircularBuffer &other) = delete; // Copy assignment
|
||||
CircularBuffer &operator=(CircularBuffer &&other) noexcept = delete; // Move assignment
|
||||
|
||||
bool push(const T &item); // Add an element to the buffer
|
||||
std::optional<T> pop(); // Retrieve an element from the buffer
|
||||
bool isEmpty() const; // Check if the buffer is empty
|
||||
bool isFull() const; // Check if the buffer is full
|
||||
size_t size() const; // Get the current number of elements
|
||||
|
||||
protected:
|
||||
std::array<T, BUFFER_SIZE> m_buffer;
|
||||
volatile size_t m_head = 0; // Index for the next write (push)
|
||||
volatile size_t m_tail = 0; // Index for the next read (pop)
|
||||
volatile size_t m_size = 0; // Current number of elements stored
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Add an element to the buffer */
|
||||
template<class T, size_t BUFFER_SIZE>
|
||||
inline bool CircularBuffer<T, BUFFER_SIZE>::push(const T &item)
|
||||
{
|
||||
if (isFull())
|
||||
return false; // Buffer is full
|
||||
|
||||
// Write the item at the current head position
|
||||
m_buffer[m_head] = item;
|
||||
|
||||
// Move head pointer, wrapping around using modulo
|
||||
m_head = (m_head + 1) % BUFFER_SIZE;
|
||||
|
||||
// Increase the current size of the data in the buffer
|
||||
m_size = m_size + 1;
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Retrieve an element from the buffer */
|
||||
template<class T, size_t BUFFER_SIZE>
|
||||
inline std::optional<T> CircularBuffer<T, BUFFER_SIZE>::pop()
|
||||
{
|
||||
if (isEmpty())
|
||||
return {}; // Buffer is empty, cannot pop
|
||||
|
||||
// Read (copy) the item at the current tail position
|
||||
T item = m_buffer[m_tail];
|
||||
|
||||
// Move tail pointer, wrapping around using modulo
|
||||
m_tail = (m_tail + 1) % BUFFER_SIZE;
|
||||
|
||||
// Decrease the current size of the data in the buffer
|
||||
m_size = m_size - 1;
|
||||
return item;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the buffer is empty */
|
||||
template<class T, size_t BUFFER_SIZE>
|
||||
inline bool CircularBuffer<T, BUFFER_SIZE>::isEmpty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the buffer is full */
|
||||
template<class T, size_t BUFFER_SIZE>
|
||||
inline bool CircularBuffer<T, BUFFER_SIZE>::isFull() const
|
||||
{
|
||||
return m_size == BUFFER_SIZE;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the current number of elements */
|
||||
template<class T, size_t BUFFER_SIZE>
|
||||
inline size_t CircularBuffer<T, BUFFER_SIZE>::size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::generic
|
||||
163
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/generic/crc.h
Normal file
163
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/generic/crc.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
namespace sdi_ToolBox::generic
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class CRC16_modbus
|
||||
* @brief CRC16 calculator for the Modbus protocol.
|
||||
*
|
||||
* Provides both static and incremental interfaces to compute the CRC16 checksum
|
||||
* as used in the Modbus protocol (polynomial 0xA001, initial value 0xFFFF).
|
||||
*
|
||||
* Usage examples:
|
||||
* @code
|
||||
* // One-shot CRC calculation
|
||||
* std::array<uint8_t, 8> data = { ... };
|
||||
* uint16_t crc = Crc16_modbus::computeCRC(data);
|
||||
*
|
||||
* // Incremental CRC calculation
|
||||
* Crc16_modbus crcHandle;
|
||||
* crcHandle.init();
|
||||
* for (uint8_t b : data) {
|
||||
* crcHandle.update(b);
|
||||
* }
|
||||
* uint16_t crc = crcHandle.finalize();
|
||||
* @endcode
|
||||
*/
|
||||
class CRC16_modbus
|
||||
{
|
||||
static constexpr uint16_t INITIAL_VALUE = 0xFFFF;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Computes the CRC16 of the given data in one call.
|
||||
* @param data The data buffer to compute the CRC for.
|
||||
* @return The computed CRC16 value.
|
||||
*/
|
||||
static uint16_t computeCRC(const std::span<const uint8_t> &data);
|
||||
|
||||
public:
|
||||
CRC16_modbus() = default; // Default constructor
|
||||
virtual ~CRC16_modbus() = default; // Default destructor
|
||||
CRC16_modbus(const CRC16_modbus &other) = default; // Copy constructor
|
||||
CRC16_modbus(CRC16_modbus &&other) noexcept = default; // Move constructor
|
||||
CRC16_modbus &operator=(const CRC16_modbus &other) = default; // Copy assignment
|
||||
CRC16_modbus &operator=(CRC16_modbus &&other) noexcept = default; // Move assignment
|
||||
|
||||
/**
|
||||
* @brief Constructs and initializes the CRC with the given data.
|
||||
* @param data The data buffer to initialize and update the CRC with.
|
||||
*/
|
||||
explicit CRC16_modbus(const std::span<const uint8_t> &data);
|
||||
|
||||
/**
|
||||
* @brief Initializes or re-initializes the CRC to the default value (0xFFFF).
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* @brief Updates the CRC with a single byte.
|
||||
* @param val The byte value to update the CRC with.
|
||||
*/
|
||||
void update(const uint8_t &val);
|
||||
|
||||
/**
|
||||
* @brief Updates the CRC with a data buffer.
|
||||
* @param data The data buffer to update the CRC with.
|
||||
*/
|
||||
void update(const std::span<const uint8_t> &data);
|
||||
|
||||
/**
|
||||
* @brief Finalizes the CRC calculation and returns the CRC value.
|
||||
* @return The finalized CRC16 value.
|
||||
*/
|
||||
uint16_t finalize() const;
|
||||
|
||||
/**
|
||||
* @brief Returns the current CRC value.
|
||||
* @return The current CRC16 value.
|
||||
*/
|
||||
[[nodiscard]] uint16_t getCRC() const;
|
||||
|
||||
protected:
|
||||
uint16_t m_crc = INITIAL_VALUE; // Current CRC value
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Compute CRC in one go */
|
||||
inline uint16_t CRC16_modbus::computeCRC(const std::span<const uint8_t> &data)
|
||||
{
|
||||
const CRC16_modbus crc(data);
|
||||
return crc.finalize();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline CRC16_modbus::CRC16_modbus(const std::span<const uint8_t> &data)
|
||||
{
|
||||
init();
|
||||
update(data);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Reinit crc handle */
|
||||
inline void CRC16_modbus::init()
|
||||
{
|
||||
m_crc = INITIAL_VALUE;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Update CRC */
|
||||
inline void CRC16_modbus::update(const uint8_t &val)
|
||||
{
|
||||
constexpr uint16_t PolynomialValue = 0xA001;
|
||||
|
||||
m_crc ^= static_cast<uint16_t>(val); // XOR byte into least sig. byte of crc
|
||||
for (int i = 8; i != 0; i--) // Loop over each bit
|
||||
{
|
||||
if ((m_crc & 0x0001) != 0) // If the LSB is set
|
||||
{
|
||||
m_crc >>= 1; // Shift right and XOR PolynomialValue
|
||||
m_crc ^= PolynomialValue;
|
||||
}
|
||||
else // Else LSB is not set
|
||||
{
|
||||
m_crc >>= 1; // Just shift right
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Update CRC */
|
||||
inline void CRC16_modbus::update(const std::span<const uint8_t> &data)
|
||||
{
|
||||
for (const auto &c : data)
|
||||
update(c);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Finalize and return CRC value */
|
||||
inline uint16_t CRC16_modbus::finalize() const
|
||||
{
|
||||
return getCRC();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return CRC value */
|
||||
inline uint16_t CRC16_modbus::getCRC() const
|
||||
{
|
||||
return m_crc;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_ToolBox::generic
|
||||
79
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/generic/uuid.h
Normal file
79
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/generic/uuid.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/uuid/uuid.hpp> // Pour boost::uuids::uuid
|
||||
#include <boost/uuid/uuid_generators.hpp> // Pour boost::uuids::random_generator
|
||||
#include <boost/uuid/uuid_io.hpp> // Pour boost::uuids::to_string
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
namespace sdi_toolBox::generic
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class UuidGenerator
|
||||
{
|
||||
public:
|
||||
static std::string uuid_v4(); // Generate a random UUID v4 string
|
||||
static std::string uniqid(bool moreEntropy = false); // Generate a unique identifier string (as in PHP)
|
||||
|
||||
protected:
|
||||
UuidGenerator() = default; // Default constructor
|
||||
~UuidGenerator() = default; // Default destructor
|
||||
UuidGenerator(const UuidGenerator &) = default; // Copy constructor
|
||||
UuidGenerator(UuidGenerator &&) noexcept = default; // Move constructor
|
||||
UuidGenerator &operator=(const UuidGenerator &) = default; // Copy assignment operator
|
||||
UuidGenerator &operator=(UuidGenerator &&) noexcept = default; // Move assignment operator
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Generate a random UUID v4 string */
|
||||
inline std::string UuidGenerator::uuid_v4()
|
||||
{
|
||||
boost::uuids::random_generator gen; // Create a random UUID generator
|
||||
boost::uuids::uuid u = gen(); // Generate a random UUID
|
||||
return boost::uuids::to_string(u); // Convert the UUID to a string and return it
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Generate a unique identifier string (as in PHP) */
|
||||
inline std::string UuidGenerator::uniqid(const bool moreEntropy)
|
||||
{
|
||||
const auto now = std::chrono::high_resolution_clock::now(); // Get current time point
|
||||
const auto epoch = now.time_since_epoch(); // Get duration since epoch
|
||||
const auto us_since_epoch = std::chrono::duration_cast<std::chrono::microseconds>(epoch).count();
|
||||
|
||||
// Format the time part into a hexadecimal string
|
||||
const auto time_part = us_since_epoch / 16;
|
||||
|
||||
std::string result = std::format("{0:x}", time_part);
|
||||
|
||||
// PHP's uniqid pads the result to 13 characters if needed. We replicate the core logic.
|
||||
// If the time part is shorter than 13 characters (highly unlikely today), it should be padded.
|
||||
if (result.length() < 13)
|
||||
result.insert(0, 13 - result.length(), '0');
|
||||
|
||||
// Add more entropy if requested (similar to PHP's second argument = true)
|
||||
if (moreEntropy)
|
||||
{
|
||||
// Generate 5 bytes of random data (10 characters hex)
|
||||
static std::random_device rd;
|
||||
static std::mt19937 generator(rd());
|
||||
unsigned int rand_val = generator(); // Generate a random 32-bit number and use a portion of a second random number
|
||||
|
||||
// Append the random data in hexadecimal format
|
||||
result = result + std::format(".{0:08x}", rand_val);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::generic
|
||||
531
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/logs/reLog.h
Normal file
531
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/logs/reLog.h
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../console/ansi.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN // Disable windows mess
|
||||
# endif
|
||||
# ifndef NOMINMAX
|
||||
# define NOMINMAX
|
||||
# endif
|
||||
# include <Windows.h>
|
||||
# undef GetMessage // To avoid conflict with other libraries
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <source_location>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
* Usage :
|
||||
*
|
||||
* {
|
||||
* int value = 42;
|
||||
* std::string user = "Alice";
|
||||
*
|
||||
* // Usage example:
|
||||
* LogMessage() << "System ready" << std::endl;
|
||||
* LogDebug() << "Checking configuration file..." << std::endl;
|
||||
* LogInfo() << "User " << user << " logged in. (Value: " << value << ")" << std::endl;
|
||||
* LogWarning() << "Device connection lost..." << std::endl;
|
||||
* LogError() << "Failed to connect to server on port " << 8080 << "!" << std::endl;
|
||||
* LogComm() << "<< 0123456789" << std::endl;
|
||||
* LogDev() << "<< 0123456789" << std::endl;
|
||||
* }
|
||||
*/
|
||||
|
||||
namespace sdi_toolBox::logs
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
enum class LogLevel : uint8_t
|
||||
{
|
||||
None,
|
||||
Dev,
|
||||
Comm,
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Message,
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
class ReLog
|
||||
{
|
||||
public:
|
||||
struct Log
|
||||
{
|
||||
LogLevel level;
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
using ColorLogMap = std::map<LogLevel, std::string>;
|
||||
using VisibleLogMap = std::map<LogLevel, bool>;
|
||||
static VisibleLogMap getVisibleLogMap(); // Return the map of visible log messages
|
||||
static void setVisibleLogMap(const VisibleLogMap &logMap); // Define the map of visible log messages
|
||||
|
||||
static ReLog &systemInit(const std::filesystem::path &filepath = ""); // Initialize log system
|
||||
static ReLog &getInstance(); // Gets singleton instance
|
||||
|
||||
public:
|
||||
virtual ~ReLog() = default; // Default destructor
|
||||
ReLog(const ReLog &obj) = delete; // Copy constructor
|
||||
ReLog(ReLog &&obj) noexcept = delete; // Move constructor
|
||||
ReLog &operator=(const ReLog &obj) = delete; // Copy assignment operator
|
||||
ReLog &operator=(ReLog &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
void openFile(const std::filesystem::path &filepath); // Open or create log file
|
||||
void closeFile(); // Close log file
|
||||
|
||||
void log(const Log &log); // Core log method
|
||||
|
||||
void printColorTest() const; // Print the different configurations and colors on the terminal
|
||||
|
||||
protected:
|
||||
ReLog(); // Default constructor
|
||||
|
||||
bool m_vtModeEnabled = false;
|
||||
|
||||
std::mutex m_mtx;
|
||||
std::ofstream m_file;
|
||||
|
||||
static inline ColorLogMap m_colorLogMap;
|
||||
static inline VisibleLogMap m_visibleLogMap;
|
||||
|
||||
private:
|
||||
void enableVTMode();
|
||||
|
||||
static void init(); // Settings initialization
|
||||
|
||||
static std::string createLogMessage(const Log &log, const bool isFile = false); // Print log messages
|
||||
void printDate();
|
||||
|
||||
void saveToFile(const std::string &message); // Save log messages in file
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Return the map of visible log messages */
|
||||
inline ReLog::VisibleLogMap ReLog::getVisibleLogMap()
|
||||
{
|
||||
return m_visibleLogMap;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Define the map of visible log messages */
|
||||
inline void ReLog::setVisibleLogMap(const VisibleLogMap &logMap)
|
||||
{
|
||||
m_visibleLogMap = logMap;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Initialize log system */
|
||||
inline ReLog &ReLog::systemInit(const std::filesystem::path &filepath)
|
||||
{
|
||||
auto &logSystem = getInstance();
|
||||
|
||||
// Post initialization
|
||||
if (!filepath.empty())
|
||||
logSystem.openFile(filepath);
|
||||
logSystem.printDate();
|
||||
return logSystem;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Gets singleton instance */
|
||||
inline ReLog &ReLog::getInstance()
|
||||
{
|
||||
static ReLog instance;
|
||||
return instance;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline ReLog::ReLog()
|
||||
{
|
||||
// Initialization
|
||||
init();
|
||||
enableVTMode();
|
||||
|
||||
// Test on debug mode
|
||||
#ifdef DEBUG
|
||||
// printColorTest();
|
||||
#endif
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Open or create log file */
|
||||
inline void ReLog::openFile(const std::filesystem::path &filepath)
|
||||
{
|
||||
std::lock_guard lock(m_mtx);
|
||||
|
||||
m_file = std::ofstream(filepath, std::ofstream::out | std::ofstream::app | std::ofstream::binary);
|
||||
if (!m_file)
|
||||
throw std::runtime_error("unable to open log file");
|
||||
m_file << "\n-----------------------------------------------------------------" << std::endl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Close log file */
|
||||
inline void ReLog::closeFile()
|
||||
{
|
||||
std::lock_guard lock(m_mtx);
|
||||
|
||||
if (m_file.is_open())
|
||||
m_file.close();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Core log method */
|
||||
inline void ReLog::log(const Log &log)
|
||||
{
|
||||
std::lock_guard lock(m_mtx);
|
||||
|
||||
const auto termMessage = createLogMessage(log);
|
||||
if (!termMessage.empty())
|
||||
{
|
||||
if (log.level == LogLevel::Error)
|
||||
std::cerr << termMessage << std::endl;
|
||||
else
|
||||
std::cout << termMessage << std::endl;
|
||||
}
|
||||
|
||||
const auto fileMessage = createLogMessage(log, true);
|
||||
if (!fileMessage.empty())
|
||||
saveToFile(fileMessage);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Print the different configurations and colors on the terminal */
|
||||
inline void ReLog::printColorTest() const
|
||||
{
|
||||
const auto coutTest = [&](const auto &color, const std::string &label)
|
||||
{
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, sdi_toolBox::console::ANSI::EscapeCommand::UNDERLINE) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, sdi_toolBox::console::ANSI::EscapeCommand::DIM | sdi_toolBox::console::ANSI::EscapeCommand::UNDERLINE) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT | sdi_toolBox::console::ANSI::EscapeCommand::UNDERLINE) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, sdi_toolBox::console::ANSI::EscapeCommand::BLINK) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, sdi_toolBox::console::ANSI::EscapeCommand::BRIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, sdi_toolBox::console::ANSI::EscapeCommand::DIM) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, sdi_toolBox::console::ANSI::EscapeCommand::REVERSE) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << " | ";
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Black) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Red) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Green) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Yellow) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Blue) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Magenta) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Cyan) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::White) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << " | ";
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Black, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Red, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Green, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Yellow, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Blue, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Magenta, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::Cyan, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << sdi_toolBox::console::ANSI::EscapeCommand::get(color, 0, sdi_toolBox::console::ANSI::EscapeCommand::Color::White, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT) << label << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
std::cout << std::endl;
|
||||
};
|
||||
coutTest(sdi_toolBox::console::ANSI::EscapeCommand::Color::Black, " B ");
|
||||
coutTest(sdi_toolBox::console::ANSI::EscapeCommand::Color::Red, " R ");
|
||||
coutTest(sdi_toolBox::console::ANSI::EscapeCommand::Color::Green, " G ");
|
||||
coutTest(sdi_toolBox::console::ANSI::EscapeCommand::Color::Yellow, " Y ");
|
||||
coutTest(sdi_toolBox::console::ANSI::EscapeCommand::Color::Blue, " B ");
|
||||
coutTest(sdi_toolBox::console::ANSI::EscapeCommand::Color::Magenta, " M ");
|
||||
coutTest(sdi_toolBox::console::ANSI::EscapeCommand::Color::Cyan, " C ");
|
||||
coutTest(sdi_toolBox::console::ANSI::EscapeCommand::Color::White, " W ");
|
||||
std::cout << std::endl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline void ReLog::enableVTMode()
|
||||
{
|
||||
if (m_vtModeEnabled)
|
||||
return;
|
||||
|
||||
m_vtModeEnabled = false;
|
||||
|
||||
// 1. Get the handle to the standard output
|
||||
const HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
// 2. Get the current console mode
|
||||
DWORD dwMode = 0;
|
||||
if (!GetConsoleMode(hOut, &dwMode))
|
||||
return;
|
||||
|
||||
// 3. Set the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag (using bitwise OR)
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
|
||||
// 4. Apply the new console mode
|
||||
if (!SetConsoleMode(hOut, dwMode)) // This is often the point of failure on older Windows versions (pre-Win10 v1511)
|
||||
return;
|
||||
|
||||
m_vtModeEnabled = true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Settings initialization */
|
||||
inline void ReLog::init()
|
||||
{
|
||||
m_colorLogMap[LogLevel::None] = sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
m_colorLogMap[LogLevel::Dev] = sdi_toolBox::console::ANSI::EscapeCommand::get(sdi_toolBox::console::ANSI::EscapeCommand::Color::Green, sdi_toolBox::console::ANSI::EscapeCommand::DIM);
|
||||
m_colorLogMap[LogLevel::Comm] = sdi_toolBox::console::ANSI::EscapeCommand::get(sdi_toolBox::console::ANSI::EscapeCommand::Color::Magenta);
|
||||
m_colorLogMap[LogLevel::Debug] = sdi_toolBox::console::ANSI::EscapeCommand::get(sdi_toolBox::console::ANSI::EscapeCommand::Color::Yellow, sdi_toolBox::console::ANSI::EscapeCommand::DIM);
|
||||
m_colorLogMap[LogLevel::Info] = sdi_toolBox::console::ANSI::EscapeCommand::get(sdi_toolBox::console::ANSI::EscapeCommand::Color::White);
|
||||
m_colorLogMap[LogLevel::Warning] = sdi_toolBox::console::ANSI::EscapeCommand::get(sdi_toolBox::console::ANSI::EscapeCommand::Color::Yellow);
|
||||
m_colorLogMap[LogLevel::Error] = sdi_toolBox::console::ANSI::EscapeCommand::get(sdi_toolBox::console::ANSI::EscapeCommand::Color::Red, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT | sdi_toolBox::console::ANSI::EscapeCommand::BRIGHT);
|
||||
m_colorLogMap[LogLevel::Message] = sdi_toolBox::console::ANSI::EscapeCommand::get(sdi_toolBox::console::ANSI::EscapeCommand::Color::White, sdi_toolBox::console::ANSI::EscapeCommand::LIGHT);
|
||||
|
||||
m_visibleLogMap[LogLevel::Dev] = true;
|
||||
m_visibleLogMap[LogLevel::Comm] = true;
|
||||
m_visibleLogMap[LogLevel::Debug] = true;
|
||||
m_visibleLogMap[LogLevel::Info] = true;
|
||||
m_visibleLogMap[LogLevel::Warning] = true;
|
||||
m_visibleLogMap[LogLevel::Error] = true;
|
||||
m_visibleLogMap[LogLevel::Message] = true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Print log messages */
|
||||
inline std::string ReLog::createLogMessage(const Log &log, const bool isFile)
|
||||
{
|
||||
// Check if the message should be visible or not
|
||||
if (m_visibleLogMap.contains(log.level) && m_visibleLogMap[log.level] == false)
|
||||
return "";
|
||||
|
||||
std::ostringstream stream;
|
||||
|
||||
// Prepare message color
|
||||
const auto levelStyle = (m_colorLogMap.contains(log.level)) ? m_colorLogMap[log.level] : sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
const auto messageStyle = (!isFile) ? levelStyle : "";
|
||||
|
||||
if (!log.message.empty())
|
||||
{
|
||||
switch (log.level)
|
||||
{
|
||||
case LogLevel::Error:
|
||||
stream << messageStyle << "[E] ";
|
||||
break;
|
||||
case LogLevel::Warning:
|
||||
stream << messageStyle << "[W] ";
|
||||
break;
|
||||
case LogLevel::Info:
|
||||
stream << messageStyle << "[I] ";
|
||||
break;
|
||||
case LogLevel::Debug:
|
||||
stream << messageStyle << "[D] ";
|
||||
break;
|
||||
case LogLevel::Comm:
|
||||
stream << messageStyle << "[C] ";
|
||||
break;
|
||||
case LogLevel::Dev:
|
||||
stream << messageStyle << "[-] ";
|
||||
break;
|
||||
case LogLevel::Message:
|
||||
default:;
|
||||
stream << messageStyle;
|
||||
break;
|
||||
}
|
||||
|
||||
if (log.level != LogLevel::Message)
|
||||
{
|
||||
const auto local_tz = std::chrono::current_zone();
|
||||
const auto local_time = std::chrono::zoned_time(local_tz, log.timestamp);
|
||||
stream << std::format("{0:%T} - ", local_time);
|
||||
}
|
||||
}
|
||||
|
||||
// Print message
|
||||
stream << log.message;
|
||||
|
||||
// Reset style
|
||||
if (!messageStyle.empty())
|
||||
stream << sdi_toolBox::console::ANSI::EscapeCommand::clear();
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline void ReLog::printDate()
|
||||
{
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto local_tz = std::chrono::current_zone();
|
||||
const auto local_time = std::chrono::zoned_time(local_tz, now);
|
||||
|
||||
const Log logMessage{ .level = LogLevel::Info,
|
||||
.timestamp = now,
|
||||
.message = std::format("current date: {0:%F}", local_time) };
|
||||
log(logMessage);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Save log messages in file */
|
||||
inline void ReLog::saveToFile(const std::string &message)
|
||||
{
|
||||
// Remove escape sequences
|
||||
std::string messageResult;
|
||||
bool in_escape_sequence = false;
|
||||
|
||||
constexpr char ANSI_ESCAPE_CHAR = '\x1b';
|
||||
for (const char c : message)
|
||||
{
|
||||
if (in_escape_sequence)
|
||||
{
|
||||
if (c == 'm')
|
||||
in_escape_sequence = false; // End of the escape sequence
|
||||
}
|
||||
else if (c == ANSI_ESCAPE_CHAR)
|
||||
in_escape_sequence = true; // Begin of the escape sequence
|
||||
else
|
||||
messageResult += c;
|
||||
}
|
||||
|
||||
if (m_file.is_open())
|
||||
m_file << messageResult << "\n";
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
|
||||
/* --- */
|
||||
|
||||
//--------------------------------------------------------------
|
||||
class ReLogStreamProxy
|
||||
{
|
||||
public:
|
||||
ReLogStreamProxy() = delete; // Constructor
|
||||
explicit ReLogStreamProxy(const LogLevel &level); // Constructor
|
||||
virtual ~ReLogStreamProxy(); // Destructor
|
||||
ReLogStreamProxy(ReLogStreamProxy &obj) = delete; // Copy constructor
|
||||
ReLogStreamProxy(ReLogStreamProxy &&obj) = delete; // Move constructor
|
||||
ReLogStreamProxy &operator=(const ReLogStreamProxy &obj) = delete; // Copy assignment operator
|
||||
ReLogStreamProxy &operator=(ReLogStreamProxy &&obj) = delete; // Move assignment operator
|
||||
|
||||
template<typename T>
|
||||
ReLogStreamProxy &operator<<(const T &data); // Overload for various data types (int, string, double, etc.)
|
||||
ReLogStreamProxy &operator<<(std::ostream &(*manip)(std::ostream &)); // Overload for std::endl and other manipulators
|
||||
|
||||
protected:
|
||||
LogLevel m_level;
|
||||
std::chrono::system_clock::time_point m_timestamp;
|
||||
std::stringstream m_buffer;
|
||||
|
||||
private:
|
||||
void flush(); // Pass the buffered content to the central Logger
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline ReLogStreamProxy::ReLogStreamProxy(const LogLevel &level)
|
||||
{
|
||||
// Initialization
|
||||
m_level = level;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Destructor */
|
||||
inline ReLogStreamProxy::~ReLogStreamProxy()
|
||||
{
|
||||
if (!m_buffer.str().empty())
|
||||
flush();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Overload for various data types (int, string, double, etc.) */
|
||||
template<typename T>
|
||||
inline ReLogStreamProxy &ReLogStreamProxy::operator<<(const T &data)
|
||||
{
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
|
||||
if (m_buffer.str().empty())
|
||||
m_timestamp = now;
|
||||
|
||||
m_buffer << data;
|
||||
return *this;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Overload for std::endl and other manipulators */
|
||||
inline ReLogStreamProxy &ReLogStreamProxy::operator<<(std::ostream &(*manip)(std::ostream &))
|
||||
{
|
||||
if (manip == static_cast<std::ostream &(*)(std::ostream &)>(std::endl))
|
||||
{
|
||||
flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle other manipulators if necessary (e.g., std::hex)
|
||||
m_buffer << manip;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Pass the buffered content to the central Logger */
|
||||
inline void ReLogStreamProxy::flush()
|
||||
{
|
||||
// Pass the buffered content to the central Logger
|
||||
ReLog::getInstance().log(ReLog::Log{ .level = m_level,
|
||||
.timestamp = m_timestamp,
|
||||
.message = m_buffer.str() });
|
||||
|
||||
// Reset stream content
|
||||
m_buffer.str("");
|
||||
m_buffer.clear();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::logs
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/*
|
||||
* User interface
|
||||
*/
|
||||
inline sdi_toolBox::logs::ReLogStreamProxy LogError()
|
||||
{
|
||||
return sdi_toolBox::logs::ReLogStreamProxy(sdi_toolBox::logs::LogLevel::Error);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline sdi_toolBox::logs::ReLogStreamProxy LogWarning()
|
||||
{
|
||||
return sdi_toolBox::logs::ReLogStreamProxy(sdi_toolBox::logs::LogLevel::Warning);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline sdi_toolBox::logs::ReLogStreamProxy LogInfo()
|
||||
{
|
||||
return sdi_toolBox::logs::ReLogStreamProxy(sdi_toolBox::logs::LogLevel::Info);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline sdi_toolBox::logs::ReLogStreamProxy LogDebug()
|
||||
{
|
||||
return sdi_toolBox::logs::ReLogStreamProxy(sdi_toolBox::logs::LogLevel::Debug);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline sdi_toolBox::logs::ReLogStreamProxy LogComm()
|
||||
{
|
||||
return sdi_toolBox::logs::ReLogStreamProxy(sdi_toolBox::logs::LogLevel::Comm);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline sdi_toolBox::logs::ReLogStreamProxy LogMessage()
|
||||
{
|
||||
return sdi_toolBox::logs::ReLogStreamProxy(sdi_toolBox::logs::LogLevel::Message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline sdi_toolBox::logs::ReLogStreamProxy LogDev()
|
||||
{
|
||||
return sdi_toolBox::logs::ReLogStreamProxy(sdi_toolBox::logs::LogLevel::Dev);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline void LogCode(const std::string &msg = "", const std::source_location &location = std::source_location::current())
|
||||
{
|
||||
LogDev() << msg << ((!msg.empty()) ? " - " : "") << "(" << location.file_name() << " l." << location.line() << ")" << std::endl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
6
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/namespace.h
Normal file
6
sdi_toolBox_1.0.x/toolBox/sdi_toolBox/namespace.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @namespace sdi_ToolBox
|
||||
* @brief Root namespace for SD-Innovation ToolBox utilities.
|
||||
*/
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wx/statline.h>
|
||||
#include <wx/wx.h>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
inline wxStaticText *createTitleCtrl(wxWindow *parentWindow, const wxString &title, const long style = 0)
|
||||
{
|
||||
const auto ctrl = new wxStaticText(parentWindow,
|
||||
wxID_ANY,
|
||||
title,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
style);
|
||||
ctrl->SetFont(ctrl->GetFont().Italic());
|
||||
return ctrl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline wxStaticText *createLabelCtrl(wxWindow *parentWindow, const wxString &title = wxEmptyString, const long style = 0)
|
||||
{
|
||||
const auto ctrl = new wxStaticText(parentWindow,
|
||||
wxID_ANY,
|
||||
title,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
style);
|
||||
return ctrl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline wxTextCtrl *createTextCtrl(wxWindow *parentWindow, const wxString &value = wxEmptyString, const long style = 0)
|
||||
{
|
||||
const auto ctrl = new wxTextCtrl(parentWindow,
|
||||
wxID_ANY,
|
||||
value,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
style);
|
||||
return ctrl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline wxButton *createButtonCtrl(wxWindow *parentWindow, const wxString &label, const long style = 0)
|
||||
{
|
||||
const auto ctrl = new wxButton(parentWindow,
|
||||
wxID_ANY,
|
||||
label,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
style);
|
||||
return ctrl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline wxBitmapButton *createBitmapButtonCtrl(wxWindow *parentWindow, const wxBitmap &bmp, const long style = 0)
|
||||
{
|
||||
const auto ctrl = new wxBitmapButton(parentWindow,
|
||||
wxID_ANY,
|
||||
bmp,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
style);
|
||||
return ctrl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline wxStaticLine *createVLineCtrl(wxWindow *parentWindow)
|
||||
{
|
||||
const auto ctrl = new wxStaticLine(parentWindow,
|
||||
wxID_ANY,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxVERTICAL);
|
||||
return ctrl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
inline wxStaticLine *createHLineCtrl(wxWindow *parentWindow)
|
||||
{
|
||||
const auto ctrl = new wxStaticLine(parentWindow,
|
||||
wxID_ANY,
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxHORIZONTAL);
|
||||
return ctrl;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
Reference in New Issue
Block a user