Index: lib/MsgUtils/src/AgentMessage.cpp =================================================================== diff -u -rf8c0a0b1c19dc386b2f88484d53db022270690a7 -r088513e6ea7bad08b4fb7862127c726eabad18fd --- lib/MsgUtils/src/AgentMessage.cpp (.../AgentMessage.cpp) (revision f8c0a0b1c19dc386b2f88484d53db022270690a7) +++ lib/MsgUtils/src/AgentMessage.cpp (.../AgentMessage.cpp) (revision 088513e6ea7bad08b4fb7862127c726eabad18fd) @@ -1,10 +1,31 @@ +/*! + * + * Copyright (c) 2024-2026 Diality Inc. - All Rights Reserved. + * \copyright + * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN + * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. + * + * \file AgentMessage.cpp + * \author (original) Stephen Quong + * \date (original) 24-May-2026 + * + */ #include "AgentMessage.h" #include // --------------------------------------------------------------------------- // Outbound // --------------------------------------------------------------------------- + +/*! + * \brief AgentMessage::build + * \details Builds a complete wire-ready frame with header and optional payload CRCs. + * \param msgId - message identifier for Agent MQTT routing + * \param sequence - caller-managed sequence number + * \param payload - optional payload; pass empty for zero-length frames (e.g. Ack) + * \return complete frame ready to write to the transport + */ QByteArray AgentMessage::build(MsgId msgId, quint16 sequence, const QByteArray &payload) { const quint32 payloadLen = static_cast(payload.size()); @@ -22,39 +43,30 @@ const quint16 hCrc = crc16ccitt(header, HEADER_SIZE - HEADER_CRC_SIZE); qToBigEndian(hCrc, header + HEADER_SIZE - HEADER_CRC_SIZE); - if (payloadLen == 0) { - return msg; + if (payloadLen > 0) { + msg.append(payload); + const quint32 crc = crc32isohdlc(reinterpret_cast(payload.constData()), payload.size()); + QByteArray crcBytes(PAYLOAD_CRC_SIZE, Qt::Uninitialized); + qToBigEndian(crc, reinterpret_cast(crcBytes.data())); + msg.append(crcBytes); } - msg.append(payload); - - const quint32 crc = crc32isohdlc(reinterpret_cast(payload.constData()), payload.size()); - QByteArray crcBytes(PAYLOAD_CRC_SIZE, Qt::Uninitialized); - qToBigEndian(crc, reinterpret_cast(crcBytes.data())); - msg.append(crcBytes); - return msg; } // --------------------------------------------------------------------------- // Inbound -// \details 1. Bytes are consumed when: scanning for the sync word, a complete header is parsed, -// or a full frame (header + payload) is parsed. -// 2. Header and payload will try to be read in the same feed() call when possible, but they -// may also be parsed across multiple feed() calls if the input buffer is fragmented. -// The caller is responsible for accumulating incoming bytes into the buffer and invoking feed() -// each time new bytes arrive. -// 3. If a header fails CRC validation, only the sync word is discarded. The payload section is -// skipped and the remaining bytes stay in the buffer to be re-scanned on the next feed() call. -// 4. If a payload fails CRC validation, only the header is discarded (pos is not advanced past -// the payload). The payload bytes stay in the buffer to be re-scanned on the next feed() call. -// 5. bytes parameter is modified in-place to remove the consumed bytes. The caller is responsible for -// appending new incoming bytes to the buffer and invoking feed() again. -// 6. If PayloadError is returned, the caller can immediately call feed() again in order to try -// to search for a header in the remaining payload bytes. -// If HeaderError is returned, the caller can call feed() again if there is any data remaining in the -// buffer to search for a header in the remaining bytes. // --------------------------------------------------------------------------- + +/*! + * \brief AgentMessage::feed + * \details Feeds raw bytes into the inbound parser state machine. + * Consumed bytes are removed from the front of the buffer. + * On HeaderError or PayloadError the caller may call feed() again + * immediately if the buffer is non-empty. + * \param bytes - raw bytes from the transport; modified in-place + * \return FeedResult indicating the parser outcome + */ AgentMessage::FeedResult AgentMessage::feed(QByteArray &bytes) { int pos = 0; @@ -122,6 +134,11 @@ return result; } +/*! + * \brief AgentMessage::reset + * \details Resets the inbound parser to its initial sync-scanning state. + * Must be called after consuming a Complete frame. + */ void AgentMessage::reset() { _headerBuf.clear(); @@ -131,10 +148,14 @@ _rxPayload.clear(); } -// --------------------------------------------------------------------------- -// CRC-16/CCITT (poly 0x1021, init 0xFFFF, no reflection, no final XOR) -// Check value for "123456789": 0x29B1 -// --------------------------------------------------------------------------- +/*! + * \brief AgentMessage::crc16ccitt + * \details CRC-16/CCITT: poly 0x1021, init 0xFFFF, no reflection, no final XOR. + * Check value: crc16ccitt("123456789", 9) == 0x29B1. + * \param data - input data bytes + * \param len - number of bytes to process + * \return 16-bit CRC value + */ quint16 AgentMessage::crc16ccitt(const quint8 *data, int len) { quint16 crc = 0xFFFF; @@ -152,11 +173,15 @@ return crc; } -// --------------------------------------------------------------------------- -// CRC-32/ISO-HDLC (IEEE 802.3, reflected poly 0xEDB88320, -// init 0xFFFFFFFF, input/output reflected, final XOR 0xFFFFFFFF) -// Check value for "123456789": 0xCBF43926 -// --------------------------------------------------------------------------- +/*! + * \brief AgentMessage::crc32isohdlc + * \details CRC-32/ISO-HDLC: reflected poly 0xEDB88320 (IEEE 802.3), init 0xFFFFFFFF, + * input and output reflected, final XOR 0xFFFFFFFF. + * Check value: crc32isohdlc("123456789", 9) == 0xCBF43926. + * \param data - input data bytes + * \param len - number of bytes to process + * \return 32-bit CRC value + */ quint32 AgentMessage::crc32isohdlc(const quint8 *data, int len) { quint32 crc = 0xFFFFFFFF;