generated from hulk/gd32f4xx_template_cmake_vscode
Add PA11 power-hold output (PMOS self-latch keep-alive)
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""filmforge_rs485 协议自检(无硬件): 对照 ../protocols.md 的权威帧向量做回归
|
||||
|
||||
用法: python selftest_rs485.py (全部通过退出码 0)
|
||||
"""
|
||||
import sys
|
||||
|
||||
import filmforge_rs485 as ff
|
||||
|
||||
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 命令表全部 12 条权威 hex 向量
|
||||
for cmd, hex_want in (
|
||||
('M1', 'D5 01 02 4D 31 81'),
|
||||
('M2', 'D5 01 02 4D 32 82'),
|
||||
('M2S500', 'D5 01 06 4D 32 53 35 30 30 6E'),
|
||||
('M2S0', 'D5 01 04 4D 32 53 30 07'),
|
||||
('M3', 'D5 01 02 4D 33 83'),
|
||||
('M3S1', 'D5 01 04 4D 33 53 31 09'),
|
||||
('M3S0', 'D5 01 04 4D 33 53 30 08'),
|
||||
('M4', 'D5 01 02 4D 34 84'),
|
||||
('M4S120', 'D5 01 06 4D 34 53 31 32 30 6E'),
|
||||
('M888', 'D5 01 04 4D 38 38 38 FA'),
|
||||
('M999', 'D5 01 04 4D 39 39 39 FD'),
|
||||
('M9999', 'D5 01 05 4D 39 39 39 39 37'),
|
||||
):
|
||||
check(f'build_frame({cmd})', ff.build_frame(cmd).hex(' ').upper(), hex_want)
|
||||
|
||||
# 2) 增量解析: ok 应答跨两个半包喂入
|
||||
p = ff.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)', ff.describe(fr), 'ok')
|
||||
|
||||
# 3) 温度上报帧(protocols.md 实例: 24.5C)与前导噪声
|
||||
body = bytes.fromhex('F0 04 32 34 2E 35') # F0 + "24.5"
|
||||
good = bytes([0xB5]) + body + bytes([ff.sum_crc(body)])
|
||||
frames = ff.FrameParser().feed(b'\x01\x02' + good)
|
||||
check('noise+report count', len(frames), 1)
|
||||
check('report payload', frames[0].payload, '24.5')
|
||||
check('parse_temperature', ff.parse_temperature(frames[0].payload), (24.5, None))
|
||||
check('describe(成功)', ff.describe(frames[0]), '24.5')
|
||||
|
||||
# 4) 传感器故障态 OPEN/SHRT 译码
|
||||
for s in (ff.SENSOR_OPEN, ff.SENSOR_SHORT):
|
||||
b = bytes.fromhex('F0 04') + s.encode()
|
||||
f = ff.FrameParser().feed(bytes([0xB5]) + b + bytes([ff.sum_crc(b)]))
|
||||
check(f'parse_temperature({s})', ff.parse_temperature(f[0].payload), (None, s))
|
||||
|
||||
# 5) 错误 TYPE 译码(FD 参数越界 / FE 未知命令)
|
||||
for tc, name in ((0xFD, '参数越界'), (0xFE, '未知命令'), (0xF1, 'CRC 校验错'),
|
||||
(0xF3, '板类型错'), (0xF4, '命令格式错')):
|
||||
b = bytes([tc, 0x03]) + b'err'
|
||||
f = ff.FrameParser().feed(bytes([0xB5]) + b + bytes([ff.sum_crc(b)]))
|
||||
check(f'describe(TYPE={tc:02X})', ff.describe(f[0]), f'{name} err')
|
||||
|
||||
# 6) CRC 坏帧被丢弃后继续同步到下一帧
|
||||
bad = bytearray(good)
|
||||
bad[-1] ^= 0xFF
|
||||
frames = ff.FrameParser().feed(bytes(bad) + good)
|
||||
check('bad-crc then good', len(frames), 1)
|
||||
check('recovered payload', frames[0].payload, '24.5')
|
||||
|
||||
# 7) M2 状态与 M3 开关译码
|
||||
check("parse_report_state('ON 500')", ff.parse_report_state('ON 500'), ('ON', 500))
|
||||
check("parse_report_state('ON 60000')", ff.parse_report_state('ON 60000'), ('ON', 60000))
|
||||
check("parse_report_state('OFF')", ff.parse_report_state('OFF'), ('OFF', None))
|
||||
check("parse_report_state('ok')", ff.parse_report_state('ok'), (None, None))
|
||||
check("parse_onoff('ON')", ff.parse_onoff('ON'), 'ON')
|
||||
check("parse_onoff('OFF')", ff.parse_onoff('OFF'), 'OFF')
|
||||
check("parse_onoff('24.5')", ff.parse_onoff('24.5'), None)
|
||||
check("parse_temperature('120.0')", ff.parse_temperature('120.0'), (120.0, None))
|
||||
check("parse_temperature('ok')", ff.parse_temperature('ok'), (None, None))
|
||||
|
||||
# 8) 参数校验: 上报间隔(100~60000, 10ms 步进)与目标温度(0~200 整数)
|
||||
check('interval 500 ok', ff.validate_report_interval('500'), 500)
|
||||
check('interval 60000 ok', ff.validate_report_interval('60000'), 60000)
|
||||
for bad_v in ('99', '60010', '505', 'abc', '0'):
|
||||
try:
|
||||
ff.validate_report_interval(bad_v)
|
||||
fails.append(f'interval {bad_v} not rejected')
|
||||
except ValueError:
|
||||
pass
|
||||
check('temp 120 ok', ff.validate_target_temp('120'), 120)
|
||||
check('temp 0 ok', ff.validate_target_temp('0'), 0)
|
||||
check('temp 200.0 ok', ff.validate_target_temp('200.0'), 200)
|
||||
for bad_v in ('-1', '201', '120.5', 'abc'):
|
||||
try:
|
||||
ff.validate_target_temp(bad_v)
|
||||
fails.append(f'temp {bad_v} not rejected')
|
||||
except ValueError:
|
||||
pass
|
||||
print('参数越界/非法输入拒绝: PASS')
|
||||
|
||||
# 9) 非法输入拒绝
|
||||
for bad_cmd in ('', ' '):
|
||||
try:
|
||||
ff.build_frame(bad_cmd)
|
||||
fails.append('empty-cmd-not-rejected')
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
ff.build_frame('M' * 40)
|
||||
fails.append('long-cmd-not-rejected')
|
||||
except ValueError:
|
||||
pass
|
||||
print('空/超长命令拒绝: PASS')
|
||||
|
||||
# 10) 预设命令全部可组帧
|
||||
for _name, cmd, _note in ff.PRESETS:
|
||||
ff.build_frame(cmd)
|
||||
print(f'预设 {len(ff.PRESETS)} 条全部可组帧: PASS')
|
||||
|
||||
print('RESULT:', 'FAIL:' + ','.join(fails) if fails else 'ALL PASS')
|
||||
sys.exit(1 if fails else 0)
|
||||
Reference in New Issue
Block a user