diff --git a/DLPilot/release/DLPilot.exe b/DLPilot/release/DLPilot.exe index 5ccde8b..11a019d 100644 Binary files a/DLPilot/release/DLPilot.exe and b/DLPilot/release/DLPilot.exe differ diff --git a/DLPilot/src/dlpc_rs485.py b/DLPilot/src/dlpc_rs485.py index c22c087..7668204 100644 --- a/DLPilot/src/dlpc_rs485.py +++ b/DLPilot/src/dlpc_rs485.py @@ -185,15 +185,29 @@ class FrameParser: del self._buf[:1] +def _clean(s): + """控制字符 -> '·': 二进制载荷按 ASCII 解出的 NUL 等会污染日志行, + 且 Windows 剪贴板遇 NUL 直接截断复制内容""" + return ''.join(c if ord(c) >= 0x20 and ord(c) != 0x7F else '·' for c in s) + + def describe(fr): - """响应帧 -> 人读文本: TYPE 译码 + payload, err:N 追加 DLPC 错误释义""" + """响应帧 -> 人读文本: TYPE 译码 + payload, err:N 追加 DLPC 错误释义。 + + 二进制载荷(M303/M304/M305 电源实测)不以 ASCII 展示 —— 改显 hex, + 避免乱码与 NUL 截断剪贴板(值由 GUI 读数框/CLI format_power 译码)。""" + pl = fr.payload + if any(ord(c) < 0x20 or ord(c) == 0x7F for c in pl): + pl = f'[二进制载荷 {fr.raw[3:-1].hex(" ").upper()}]' + else: + pl = _clean(pl) if fr.type_code == 0xF0: - text = fr.payload or 'ok' + text = pl or 'ok' else: text = TYPE_NAMES.get(fr.type_code, f'TYPE=0x{fr.type_code:02X}') - if fr.payload: - text += f' {fr.payload}' - m = re.search(r'err:(\d+)', fr.payload) + if pl: + text += f' {pl}' + m = re.search(r'err:(\d+)', pl) if m: text += f'({DLPC_ERR_NAMES.get(int(m.group(1)), "未知错误码")})' return text diff --git a/DLPilot/src/selftest_rs485.py b/DLPilot/src/selftest_rs485.py index 13ea406..28b1bf4 100644 --- a/DLPilot/src/selftest_rs485.py +++ b/DLPilot/src/selftest_rs485.py @@ -97,5 +97,11 @@ check('M305 功率(0x8CA0=36000mW)', d.format_power('M305', frames[0]), check('M305 拒绝双字节载荷', d.format_power( 'M305', d.FrameParser().feed(bytes.fromhex('B5 F0 02 8C A0 1E'))[0]), None) +# 10) 二进制载荷的日志行安全: 无控制字符(NUL 会截断 Windows 剪贴板), 以 hex 展示 +frames = d.FrameParser().feed(bytes.fromhex('B5 F0 02 00 34 26')) # M304 应答(含 NUL) +desc = d.describe(frames[0]) +check('二进制载荷 hex 展示', desc, '[二进制载荷 00 34]') +check('日志行无控制字符', any(ord(c) < 0x20 for c in desc), False) + print('RESULT:', 'FAIL:' + ','.join(fails) if fails else 'ALL PASS') sys.exit(1 if fails else 0)