fix(SerialMate): v0.1.2 解析模式不再吞数据——不成帧字节透传显示

背景: 用户接非 DLPilot 协议设备(COM25)开解析模式接收区一片空白——_dispatch_rx 在解析模式只上抛完整合法帧, 不成帧字节(纯文本日志/别的协议/波特率不符乱码)被 FrameParser 重同步时静默丢弃。用户明确定位: 首先是基础串口调试工具, 自定义协议解析是附加项, 附加能力不得影响本体功能。

修复: protocol.py FrameParser 丢弃字节累积进 garbage(新增 drain_garbage(), feed() 返回值不变); link.py 新增 on_garbage(data, ts) 回调, 每批无条件 drain(未接回调也不累积); main.py 把 on_garbage 并入 raw 事件与透传行同格式显示, 解析协议帧开关补 tooltip, 版本 0.1.2。半帧前缀扣留语义有意保留(可能补全成真帧, 至多扣一个前缀长度)。

自检: selftest 8b +10 项(纯文本全透出/噪声+帧+尾字节/坏CRC帧字节全透出/半帧头扣留补全), loop 3b +3 项, gui 5b +2 项; 全量 67+21+68 项 PASS; exe 已重建。README/AGENTS.md(不变量 5a) 同步。
This commit is contained in:
2026-10-16 19:08:55 +08:00
parent ac76c10132
commit 8e91e661f1
8 changed files with 118 additions and 11 deletions
+19 -3
View File
@@ -331,6 +331,9 @@ class FrameParser:
"""增量字节流 -> 完整帧: 前缀令牌逐位对齐(帧头/字面量, ID 为通配) + LEN 定长
+ CRC 校验; 任一处失配丢 1 字节重新同步。LEN 值经口径换算后即 payload 长度,
负值视为伪帧头逐出 —— 伪帧头最多滞留一帧即被逐出重同步, 对调试工具可接受。
丢弃字节不销毁: 累积进 self.garbage, 由上层 drain_garbage() 取走透出 ——
本工具首先是串口助手, 协议解析是附加层, 不成帧的字节也必须可见。
(仅匹配上前缀、还在等后续字节的"半帧"缓冲不算垃圾, 可能补全成真帧)
"""
def __init__(self, cfg):
@@ -349,9 +352,22 @@ class FrameParser:
self._len_pos = lay.len_pos
self._extra = lay.len_extra(cfg.len_sem, cs)
self._buf = bytearray()
self.garbage = bytearray() # 重同步丢弃的非帧字节(调用方定期取走)
def clear(self):
self._buf.clear()
self.garbage.clear()
def drain_garbage(self):
"""取走累积的非帧字节(bytes)并清空; 解析模式不吞数据的取数入口"""
out = bytes(self.garbage)
self.garbage.clear()
return out
def _drop_head(self):
"""丢缓冲首字节重同步; 丢弃字节记入 garbage(可能正是用户要看的输出)"""
self.garbage.append(self._buf[0])
del self._buf[:1]
def _aligned(self):
"""把缓冲对齐到前缀匹配; 字节不足且已匹配部分则保持等待(返回 True),
@@ -365,7 +381,7 @@ class FrameParser:
break # 失配 -> 丢 1 字节
else:
return True
del buf[:1]
self._drop_head()
if not buf:
return False
@@ -378,7 +394,7 @@ class FrameParser:
break # LEN 字节未到齐
plen = self._buf[self._len_pos] - self._extra
if plen < 0: # LEN 为负 -> 伪帧头, 逐出重同步
del self._buf[:1]
self._drop_head()
continue
total = cfg.layout.total_size(plen, cfg.crc_size)
if len(self._buf) < total:
@@ -388,7 +404,7 @@ class FrameParser:
frames.append(cfg.parse_frame(raw))
del self._buf[:total]
else:
del self._buf[:1] # CRC 失配 -> 丢帧头重同步
self._drop_head() # CRC 失配 -> 丢帧头重同步
return frames