From 34df9ec5fa0b8050d3e9568dfbb0a7d8cea03659 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Apr 2026 20:47:35 +0000 Subject: [PATCH] fix(parser): resolve BAD CHK for BW frames caused by SESSION_RESET bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SESSION_RESET (41 03) is sent before each POLL frame to wake monitoring units. The ETX lookahead in parse_bw only checked for ACK+STX directly after ETX, so when 41 03 followed a frame's ETX, the check failed and the ETX was swallowed into the body as a payload byte — giving a 19-byte body instead of 17 for POLL frames and failing checksum validation. Fix: scan past any SESSION_RESET (41 03) sequences when looking for the next frame start, so the real ACK+STX boundary is found correctly. Also adds SUM8 checksum validation to parse_s3, which previously left checksum_valid=None for all S3 frames. https://claude.ai/code/session_014NczSHUz9uTzCAf4cVASTJ --- parsers/s3_parser.py | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/parsers/s3_parser.py b/parsers/s3_parser.py index bfec36b..2f32933 100644 --- a/parsers/s3_parser.py +++ b/parsers/s3_parser.py @@ -33,7 +33,7 @@ STX = 0x02 ETX = 0x03 ACK = 0x41 -__version__ = "0.2.2" +__version__ = "0.2.3" @dataclass @@ -227,17 +227,32 @@ def parse_s3(blob: bytes, trailer_len: int) -> List[Frame]: trailer_end = trailer_start + trailer_len trailer = blob[trailer_start:trailer_end] - # For S3 mode we don't assume checksum type here yet. + chk_valid = None + chk_type = None + chk_hex = None + payload = bytes(body) + + if len(body) >= 1: + received_chk = body[-1] + computed_chk = checksum8_sum(bytes(body[:-1])) + if computed_chk == received_chk: + chk_valid = True + chk_type = "SUM8" + chk_hex = f"{received_chk:02x}" + payload = bytes(body[:-1]) + else: + chk_valid = False + frames.append(Frame( index=idx, start_offset=start_offset, end_offset=end_offset, payload_raw=bytes(body), - payload=bytes(body), + payload=payload, trailer=trailer, - checksum_valid=None, - checksum_type=None, - checksum_hex=None + checksum_valid=chk_valid, + checksum_type=chk_type, + checksum_hex=chk_hex )) idx += 1 @@ -298,10 +313,13 @@ def parse_bw(blob: bytes, trailer_len: int, validate_checksum: bool) -> List[Fra if b == ETX: # Candidate end-of-frame. - # Accept ETX if the next bytes look like a real next-frame start (ACK+STX), - # or we're at EOF. This prevents chopping on in-payload 0x03. - next_is_start = (i + 2 < n and blob[i + 1] == ACK and blob[i + 2] == STX) - at_eof = (i == n - 1) + # Skip any SESSION_RESET (41 03) sequences — sent before POLL to wake + # monitoring units — to find the real next frame start (ACK+STX). + j = i + 1 + while j + 1 < n and blob[j] == ACK and blob[j + 1] == ETX: + j += 2 + next_is_start = (j + 1 < n and blob[j] == ACK and blob[j + 1] == STX) + at_eof = (i == n - 1) or (j >= n) if not (next_is_start or at_eof): # Not a real boundary -> payload byte -- 2.52.0