#pragma once #include #include #include #include #include #include // HSMS wire-format primitives (SEMI E37): SType + status enums, the 10-byte // Header, the Frame (header + body, length-prefixed on the wire), and the // protocol timer defaults. One header keeps everything that touches the wire // format in one place. namespace secsgem::hsms { // ---- Session type + status / reason enums -------------------------------- enum class SType : uint8_t { Data = 0, SelectReq = 1, SelectRsp = 2, DeselectReq = 3, DeselectRsp = 4, LinktestReq = 5, LinktestRsp = 6, RejectReq = 7, SeparateReq = 9, }; const char* stype_name(SType s); enum class SelectStatus : uint8_t { Ok = 0, AlreadyActive = 1, NotReady = 2, ConnectExhaust = 3, }; enum class DeselectStatus : uint8_t { Ok = 0, NotEstablished = 1, Busy = 2, }; enum class RejectReason : uint8_t { StypeNotSupported = 1, PtypeNotSupported = 2, TransactionNotOpen = 3, EntityNotSelected = 4, }; inline constexpr uint8_t kPTypeSecsII = 0; inline constexpr uint16_t kControlSessionId = 0xFFFF; // HSMS protocol timer defaults (SEMI E37 ยง10). struct Timers { std::chrono::milliseconds t3{45000}; // reply std::chrono::milliseconds t5{10000}; // connect separation std::chrono::milliseconds t6{5000}; // control transaction std::chrono::milliseconds t7{10000}; // not-selected std::chrono::milliseconds t8{5000}; // intercharacter std::chrono::milliseconds linktest{0}; // 0 disables }; // ---- Header (10 bytes) --------------------------------------------------- struct Header { uint16_t session_id = kControlSessionId; uint8_t byte2 = 0; uint8_t byte3 = 0; uint8_t ptype = kPTypeSecsII; SType stype = SType::Data; uint32_t system_bytes = 0; bool w_bit() const { return (byte2 & 0x80) != 0; } uint8_t stream() const { return byte2 & 0x7F; } uint8_t function() const { return byte3; } static Header data_message(uint16_t session_id, uint8_t stream, uint8_t function, bool reply_expected, uint32_t system_bytes) { Header h; h.session_id = session_id; h.byte2 = static_cast((reply_expected ? 0x80 : 0x00) | (stream & 0x7F)); h.byte3 = function; h.ptype = kPTypeSecsII; h.stype = SType::Data; h.system_bytes = system_bytes; return h; } static Header control(SType stype, uint32_t system_bytes, uint16_t session_id = kControlSessionId, uint8_t byte2 = 0, uint8_t byte3 = 0) { Header h; h.session_id = session_id; h.byte2 = byte2; h.byte3 = byte3; h.ptype = kPTypeSecsII; h.stype = stype; h.system_bytes = system_bytes; return h; } std::array encode() const; static Header decode(const uint8_t* data); // reads exactly 10 bytes std::string describe() const; bool operator==(const Header&) const = default; }; // ---- Frame (header + body, length-prefixed) ------------------------------ class FrameError : public std::runtime_error { public: using std::runtime_error::runtime_error; }; inline constexpr std::size_t kLengthPrefixSize = 4; inline constexpr std::size_t kHeaderSize = 10; struct Frame { Header header; std::vector body; Frame() = default; explicit Frame(Header h, std::vector b = {}) : header(h), body(std::move(b)) {} // Full wire bytes including the 4-byte length prefix. std::vector encode() const; // Decode a message from its payload (header + body, i.e. the bytes that // follow the length prefix). `len` must be >= kHeaderSize. static Frame decode(const uint8_t* payload, std::size_t len); }; } // namespace secsgem::hsms