From 50be6410fedc203b5b30bf36af68bbb94ab04784 Mon Sep 17 00:00:00 2001 From: serversdwn Date: Tue, 3 Mar 2026 00:30:03 -0500 Subject: [PATCH] fix: parser frame logic now tracks DLE state. --- parsers/s3_parser.py | 145 ++++++++++++++++++++++++++++--------------- 1 file changed, 95 insertions(+), 50 deletions(-) diff --git a/parsers/s3_parser.py b/parsers/s3_parser.py index e768fec..5316c93 100644 --- a/parsers/s3_parser.py +++ b/parsers/s3_parser.py @@ -102,68 +102,113 @@ CRC_FUNCS = { def parse_frames(blob: bytes, trailer_len: int, etx_mode: str = ETX_MODE_AUTO) -> List[Frame]: frames: List[Frame] = [] - i = 0 - idx = 0 n = len(blob) - def is_dle_seq(pos: int, second: int) -> bool: - return pos + 1 < n and blob[pos] == DLE and blob[pos + 1] == second + STATE_IDLE = 0 + STATE_IN_FRAME = 1 + STATE_AFTER_DLE = 2 - # Auto-detect whether ETX is bare (logger-stripped) or DLE+ETX (raw wire). + state = STATE_IDLE + payload_raw = bytearray() + start_offset = 0 + idx = 0 + i = 0 + + # Auto-detect ETX style once if etx_mode == ETX_MODE_AUTO: - raw_etx = sum(1 for p in range(n - 1) if is_dle_seq(p, ETX)) - stx_count = sum(1 for p in range(n - 1) if is_dle_seq(p, STX)) - # Heuristic: the logger-stripped .bin files have plenty of STX but - # almost no DLE+ETX. If ETX count is far below STX count, assume stripped. + raw_etx = sum(1 for p in range(n - 1) if blob[p] == DLE and blob[p + 1] == ETX) + stx_count = sum(1 for p in range(n - 1) if blob[p] == DLE and blob[p + 1] == STX) if raw_etx and raw_etx >= max(1, int(0.8 * stx_count)): etx_mode = ETX_MODE_RAW else: etx_mode = ETX_MODE_STRIPPED - while i < n - 1: - if is_dle_seq(i, STX): - start = i - i += 2 # move past DLE STX - payload_start = i + while i < n: + b = blob[i] - # find end-of-frame marker - while i < n: - if etx_mode == ETX_MODE_RAW and is_dle_seq(i, ETX): - payload_end = i # up to (but not including) DLE ETX - i += 2 # skip DLE ETX - end = i - break - if etx_mode == ETX_MODE_STRIPPED and blob[i] == ETX: - payload_end = i # up to (but not including) bare ETX - i += 1 # skip ETX - end = i - break - i += 1 - - else: - # Ran off the end without finding ETX - break - - payload_raw = blob[payload_start:payload_end] - payload = unescape_dle(payload_raw) - - trailer = blob[i:i + trailer_len] if trailer_len > 0 else b"" - i += trailer_len - - frames.append(Frame( - index=idx, - start_offset=start, - end_offset=end, - payload_raw=payload_raw, - payload=payload, - trailer=trailer, - crc_match=None - )) - idx += 1 + if state == STATE_IDLE: + if b == DLE and i + 1 < n and blob[i + 1] == STX: + start_offset = i + payload_raw = bytearray() + i += 2 + state = STATE_IN_FRAME + continue + i += 1 continue - # optional: you can also detect DLE EOT boundaries if useful later - i += 1 + elif state == STATE_IN_FRAME: + + # RAW mode: look for DLE+ETX + if etx_mode == ETX_MODE_RAW and b == DLE: + state = STATE_AFTER_DLE + i += 1 + continue + + # STRIPPED mode: bare ETX ends frame + if etx_mode == ETX_MODE_STRIPPED and b == ETX: + payload_end = i + i += 1 + end_offset = i + + payload = unescape_dle(bytes(payload_raw)) + trailer = blob[i:i + trailer_len] if trailer_len > 0 else b"" + i += trailer_len + + frames.append(Frame( + index=idx, + start_offset=start_offset, + end_offset=end_offset, + payload_raw=bytes(payload_raw), + payload=payload, + trailer=trailer, + crc_match=None + )) + idx += 1 + state = STATE_IDLE + continue + + payload_raw.append(b) + i += 1 + continue + + elif state == STATE_AFTER_DLE: + + if etx_mode == ETX_MODE_RAW and b == ETX: + # Proper DLE ETX terminator + end_offset = i + 1 + i += 1 + + payload = unescape_dle(bytes(payload_raw)) + trailer = blob[i:i + trailer_len] if trailer_len > 0 else b"" + i += trailer_len + + frames.append(Frame( + index=idx, + start_offset=start_offset, + end_offset=end_offset, + payload_raw=bytes(payload_raw), + payload=payload, + trailer=trailer, + crc_match=None + )) + idx += 1 + state = STATE_IDLE + continue + + elif b == DLE: + # Escaped DLE (10 10) + payload_raw.append(DLE) + state = STATE_IN_FRAME + i += 1 + continue + + else: + # False alarm — previous 10 was data + payload_raw.append(DLE) + payload_raw.append(b) + state = STATE_IN_FRAME + i += 1 + continue return frames