Index: lib/MsgUtils/include/AgentMessage.h =================================================================== diff -u -rf8c0a0b1c19dc386b2f88484d53db022270690a7 -r088513e6ea7bad08b4fb7862127c726eabad18fd --- lib/MsgUtils/include/AgentMessage.h (.../AgentMessage.h) (revision f8c0a0b1c19dc386b2f88484d53db022270690a7) +++ lib/MsgUtils/include/AgentMessage.h (.../AgentMessage.h) (revision 088513e6ea7bad08b4fb7862127c726eabad18fd) @@ -15,175 +15,101 @@ #include /*! - * \brief LeahiRt to Connectivity Agent message framing. - * \details Implements the binary message framing protocol - * between the LeahiRt application and the Agent application. + * \brief LeahiRt to Connectivity Agent message framing + * \details Transport-agnostic binary framing. build() makes a wire-ready frame; + * feed() parses inbound bytes (see FeedResult). On Complete, read + * msgId()/sequence()/payload(), then reset(). * - * The class is transport-agnostic — it has no dependency on any - * socket or I/O class and can be used with any byte-stream transport - * (QLocalSocket, QTcpSocket, CAN bus, test harness, etc.). + * Frame layout — header only (payload_length == 0): * - * \b Outbound: AgentMessage::build() constructs a complete wire-ready - * frame from a message ID, sequence number, and optional payload. - * - * \b Inbound: feed() accepts raw bytes from the transport. The caller - * invokes feed() each time bytes arrive and inspects the returned - * FeedResult. When FeedResult::Complete is returned the decoded - * message is available via msgId(), sequence(), and payload(). - * reset() must be called before feeding the next message. - * - * \b Frame \b layout \b — \b header \b only \b (payload_length = 0): - * \code * Byte: 0 1 2 3 4 5 6-9 10 11 * ┌─────────┬────────┬────────┬───────────┬────────┐ * │ AA 55 │ msg_id │ seq_num│ pay_length│hdr_crc │ * │ sync │uint16BE│uint16BE│ uint32 BE │uint16BE│ * └─────────┴────────┴────────┴───────────┴────────┘ - * \endcode * - * \b Frame \b layout \b — \b with \b payload \b (payload_length > 0): - * \code + * Frame layout — with payload (payload_length > 0): + * * ┌── 12-byte header ──┬── N bytes payload ──┬── pay_crc (4 B) ──┐ * │ (see above) │ uint8[] │ CRC-32/ISO-HDLC │ * └────────────────────┴─────────────────────┴───────────────────┘ - * \endcode * - * Header CRC uses CRC-16/CCITT (poly 0x1021, init 0xFFFF, no reflection). - * Payload CRC uses CRC-32/ISO-HDLC (IEEE 802.3, reflected poly 0xEDB88320). + * Header CRC: CRC-16/CCITT (poly 0x1021, init 0xFFFF, no reflection). + * Payload CRC: CRC-32/ISO-HDLC (IEEE 802.3, reflected poly 0xEDB88320). */ class AgentMessage { public: /*! - * \brief MQTT routing identifier carried in every frame header. - * \details The Connectivity Agent uses this value as a key into its - * routing table to determine the MQTT topic for the message. + * \brief MQTT routing identifier carried in every frame header + * \details The Connectivity Agent uses this value to determine the MQTT topic. */ enum class MsgId : quint16 { - ClinicalData = 0x0001, ///< Clinical data → MQTT topic: clinical - Diagnostic = 0x0002, ///< Diagnostic data → MQTT topic: service - Ack = 0x0003, ///< Acknowledgement → response to LeahiRt - Alarms = 0x0004, ///< Alarm data → MQTT topic: alarms - Audit = 0x0005, ///< Audit data → MQTT topic: audit - DeviceLogFile = 0x0006, ///< Device log file → MQTT topic: log - TreatmentLogFile = 0x0007, ///< Treatment log → MQTT topic: tx_log - CloudSyncLogFile = 0x0008, ///< CloudSync log → MQTT topic: cs_log + ClinicalData = 0x0001, + Diagnostic = 0x0002, + Ack = 0x0003, + Alarms = 0x0004, + Audit = 0x0005, + DeviceLogFile = 0x0006, + TreatmentLogFile = 0x0007, + CloudSyncLogFile = 0x0008, }; /*! - * \brief Result returned by feed() after processing each byte chunk. + * \brief Result returned by feed() after processing each byte chunk */ enum class FeedResult { - Incomplete, ///< More bytes needed — continue feeding. - Complete, ///< Full valid frame assembled — read accessors, then call reset(). - HeaderError, ///< Header CRC mismatch — frame dropped, state reset automatically. - PayloadError, ///< Payload CRC mismatch or oversized payload — frame dropped, state reset automatically. + Incomplete, ///< More bytes needed — continue feeding. + Complete, ///< Full valid frame assembled — read accessors, then call reset(). + HeaderError, ///< Header CRC mismatch — frame dropped, state reset automatically. + PayloadError, ///< Payload CRC mismatch or oversized payload — frame dropped, state reset automatically. }; - /*! - * \brief Build a complete wire-ready frame. - * \details Constructs the 12-byte header, computes the CRC-16/CCITT header - * integrity field, and when payload is non-empty appends the payload - * followed by its CRC-32/ISO-HDLC integrity field. - * \param msgId Message identifier used by the Agent for MQTT routing. - * \param sequence Caller-managed sequence number for message tracking and ACK correlation. - * \param payload Optional message payload. Pass an empty QByteArray for zero-length - * control frames (e.g. ACK). Payload must not exceed 64 KB. - * \return QByteArray containing the complete frame ready to write to the transport. - */ static QByteArray build(MsgId msgId, quint16 sequence, const QByteArray &payload = {}); - - /*! - * \brief Feed raw bytes from the transport into the inbound state machine. - * \details Consumes only the bytes needed to advance or complete the current - * frame, then returns. Any bytes beyond what the current frame - * requires are left in \p bytes so the caller can pass the same - * buffer back to feed() to begin parsing the next frame. - * - * The caller owns the buffer and is responsible for draining it: - * \code - * QByteArray buf = _socket->readAll(); - * AgentMessage::FeedResult result; - * do { - * result = _msg.feed(buf); - * if (result == AgentMessage::FeedResult::Complete) { - * handleMessage(_msg.msgId(), _msg.sequence(), _msg.payload()); - * _msg.reset(); - * } - * } while (result == AgentMessage::FeedResult::Complete && !buf.isEmpty()); - * \endcode - * - * \param bytes Buffer of raw bytes from the transport. Bytes consumed by - * this call are removed from the front of the buffer. - * \return FeedResult indicating the parser outcome for this call. - */ FeedResult feed(QByteArray &bytes); /*! - * \brief Message identifier of the last successfully parsed frame. - * \note Valid only after feed() returns FeedResult::Complete. + * \brief AgentMessage::msgId + * \details Message identifier of the last complete frame. + * Valid only after feed() returns FeedResult::Complete. + * \return MsgId of the last complete frame */ MsgId msgId() const { return _rxMsgId; } /*! - * \brief Sequence number of the last successfully parsed frame. - * \note Valid only after feed() returns FeedResult::Complete. + * \brief AgentMessage::sequence + * \details Sequence number of the last complete frame. + * Valid only after feed() returns FeedResult::Complete. + * \return sequence number of the last complete frame */ quint16 sequence() const { return _rxSequence; } /*! - * \brief Payload of the last successfully parsed frame. - * \details Returns an empty QByteArray for zero-length frames. - * \note Valid only after feed() returns FeedResult::Complete. + * \brief AgentMessage::payload + * \details Payload bytes of the last complete frame. Empty for zero-length frames. + * Valid only after feed() returns FeedResult::Complete. + * \return payload of the last complete frame */ QByteArray payload() const { return _rxPayload; } - /*! - * \brief Reset inbound parser state. - * \details Clears all accumulated header bytes and decoded fields, returning - * the parser to its initial sync-scanning state. Must be called by - * the caller after consuming a Complete frame. Error results call - * reset() internally. The caller's buffer is unaffected — bytes - * already removed from it by feed() are not restored. - */ void reset(); - - private: - /*! - * \brief Compute a CRC-16/CCITT integrity value. - * \details Parameters: poly 0x1021, init 0xFFFF, no input or output - * reflection, no final XOR. Used for header integrity. - * Verify implementation with check value: crc16ccitt("123456789", 9) == 0x29B1. - * \param data Pointer to the input data bytes. - * \param len Number of bytes to process. - * \return 16-bit CRC value. - */ +private: static quint16 crc16ccitt(const quint8 *data, int len); - - /*! - * \brief Compute a CRC-32/ISO-HDLC integrity value. - * \details Parameters: reflected poly 0xEDB88320 (IEEE 802.3), init 0xFFFFFFFF, - * input and output reflected, final XOR 0xFFFFFFFF. Used for payload integrity. - * Verify implementation with check value: crc32isohdlc("123456789", 9) == 0xCBF43926. - * \param data Pointer to the input data bytes. - * \param len Number of bytes to process. - * \return 32-bit CRC value. - */ static quint32 crc32isohdlc(const quint8 *data, int len); - static constexpr int SYNC_SIZE = 2; ///< Sync word length in bytes. - static constexpr quint8 SYNC[SYNC_SIZE] = {0xAA, 0x55}; ///< Sync word byte sequence. - static constexpr int HEADER_SIZE = 12; ///< Fixed header length in bytes. - static constexpr int MSGID_SIZE = 2; ///< msg_id field length in bytes. - static constexpr int SEQUENCE_SIZE = 2; ///< sequence_num field length in bytes. - static constexpr int HEADER_CRC_SIZE = 2; ///< Header CRC field length in bytes. - static constexpr int PAYLOAD_CRC_SIZE = 4; ///< Payload CRC field length in bytes. - static constexpr quint32 MAX_PAYLOAD_LEN = 64 * 1024; ///< Maximum permitted payload length. + static constexpr int SYNC_SIZE = 2; + static constexpr quint8 SYNC[SYNC_SIZE] = {0xAA, 0x55}; + static constexpr int HEADER_SIZE = 12; + static constexpr int MSGID_SIZE = 2; + static constexpr int SEQUENCE_SIZE = 2; + static constexpr int HEADER_CRC_SIZE = 2; + static constexpr int PAYLOAD_CRC_SIZE = 4; + static constexpr quint32 MAX_PAYLOAD_LEN = 64 * 1024; - QByteArray _headerBuf; ///< Partial header bytes accumulated across feed() calls. - MsgId _rxMsgId = MsgId::ClinicalData; ///< Parsed msg_id, valid after header is complete. - quint16 _rxSequence = 0; ///< Parsed sequence number, valid after header is complete. - quint32 _rxPayloadLen = 0; ///< Parsed payload_length, valid after header is complete. - QByteArray _rxPayload; ///< Decoded payload of the last complete frame. + QByteArray _headerBuf; + MsgId _rxMsgId = MsgId::ClinicalData; + quint16 _rxSequence = 0; + quint32 _rxPayloadLen = 0; + QByteArray _rxPayload; };