Files
kwa.fr/sdi_toolBox_1.0.x/toolBox/sdi_toolBox/comm/uBinaryFrame.h
2026-03-12 16:31:18 +01:00

785 lines
27 KiB
C++

//{{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