#pragma once #include #include #include #include #include #include "secsgem/secs2/message.hpp" #include "secsgem/secsi/header.hpp" // SECS-I block framing. Each block on the wire is: // // 1 byte, 10..254 (header size + body size) //
10 bytes (see secsi/header.hpp) // 0..244 bytes // 2 bytes, big-endian sum of
mod 65536 // // Long SECS-II messages are split into multiple blocks; only the final // block has the E-bit set in its header (E4 ยง7.2.3). The block number // is 1-based and increments monotonically across the message. namespace secsgem::secsi { class BlockError : public std::runtime_error { public: using std::runtime_error::runtime_error; }; struct Block { Header header; std::vector body; // Full wire encoding: length byte + 10-byte header + body + 2-byte // checksum. std::vector encode() const; // Parse a block whose first byte is the length. Returns the consumed // byte count via `bytes_consumed`; throws BlockError on a short read, // a bad length, or a checksum mismatch. static Block decode(const uint8_t* data, std::size_t len, std::size_t& bytes_consumed); // Compute the 16-bit checksum of the header + body bytes (everything // between the length byte and the checksum on the wire). static uint16_t checksum(const uint8_t* data, std::size_t len); }; // Split a SECS-II message into one or more SECS-I blocks. Each block // carries up to kMaxBlockBody payload bytes; only the final block has // the E-bit set. The header template is duplicated across all blocks // (block_number / end_block are filled in by the splitter). std::vector split_message(const secs2::Message& msg, const Header& header_template); // Reassemble a SECS-II message from a sequence of blocks. Blocks must // be contiguous (block_number 1..N) and the last must have end_block=true; // the function returns nullopt if either invariant is violated. std::optional assemble_message(const std::vector& blocks); } // namespace secsgem::secsi