78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""dlpc_rs485 协议自检(无硬件): 对照上游 protocols.md 的实测帧向量做回归
|
||
|
||
用法: python selftest_rs485.py (全部通过退出码 0)
|
||
"""
|
||
import sys
|
||
|
||
import dlpc_rs485 as d
|
||
|
||
fails = []
|
||
|
||
|
||
def check(name, got, want):
|
||
ok = got == want
|
||
print(('PASS' if ok else 'FAIL'), name, '->', got, '' if ok else f'(期望 {want})')
|
||
if not ok:
|
||
fails.append(name)
|
||
|
||
|
||
# 1) 帧构造: protocols.md 实测示例 M730S0T2000
|
||
f = d.build_frame('M730S0T2000')
|
||
check('build_frame(M730S0T2000)', f.hex(' ').upper(),
|
||
'D5 01 0B 4D 37 33 30 53 30 54 32 30 30 30 8C')
|
||
|
||
# 2) 增量解析: ok 应答跨两个半包喂入
|
||
p = d.FrameParser()
|
||
r1 = p.feed(bytes.fromhex('B5 F0'))
|
||
r2 = p.feed(bytes.fromhex('02 6F 6B CC'))
|
||
check('split-feed frames', len(r1) + len(r2), 1)
|
||
fr = (r1 + r2)[0]
|
||
check('type_code', fr.type_code, 0xF0)
|
||
check('payload', fr.payload, 'ok')
|
||
check('describe(ok)', d.describe(fr), 'ok')
|
||
|
||
# 3) 前导噪声 + 0xFD err:OL 译码(字符串标记, 不附 DLPC 释义)
|
||
body = bytes.fromhex('FD 06 65 72 72 3A 4F 4C') # "err:OL"
|
||
good = bytes([0xB5]) + body + bytes([d.sum_crc(body)])
|
||
frames = d.FrameParser().feed(b'\x01\x02' + good)
|
||
check('noise+err-frame count', len(frames), 1)
|
||
check('describe(err:OL)', d.describe(frames[0]), '参数越界/读回复核不一致 err:OL')
|
||
|
||
# 4) 0xFA + 数字错误码 err:3 追加 DLPC 释义
|
||
body = bytes.fromhex('FA 05') + b'err:3'
|
||
fa = bytes([0xB5]) + body + bytes([d.sum_crc(body)])
|
||
frames = d.FrameParser().feed(fa)
|
||
check('describe(err:3)', d.describe(frames[0]),
|
||
'开光机通信失败 err:3(I2C NACK(器件无应答))')
|
||
|
||
# 5) CRC 坏帧被丢弃后继续同步到下一帧
|
||
bad = bytearray(good)
|
||
bad[-1] ^= 0xFF
|
||
frames = d.FrameParser().feed(bytes(bad) + good)
|
||
check('bad-crc then good', len(frames), 1)
|
||
check('recovered type', frames[0].type_code, 0xFD)
|
||
|
||
# 6) 非法输入拒绝
|
||
for bad_cmd in ('', ' '):
|
||
try:
|
||
d.build_frame(bad_cmd)
|
||
fails.append('empty-cmd-not-rejected')
|
||
except ValueError:
|
||
pass
|
||
try:
|
||
d.build_frame('M' * 40)
|
||
fails.append('long-cmd-not-rejected')
|
||
except ValueError:
|
||
pass
|
||
print('空/超长命令拒绝: PASS')
|
||
|
||
# 7) 预设命令全部可组帧
|
||
for _name, cmd, _note in d.PRESETS:
|
||
d.build_frame(cmd)
|
||
print(f'预设 {len(d.PRESETS)} 条全部可组帧: PASS')
|
||
|
||
print('RESULT:', 'FAIL:' + ','.join(fails) if fails else 'ALL PASS')
|
||
sys.exit(1 if fails else 0)
|