generated from hulk/gd32f4xx_template_cmake_vscode
112 lines
3.6 KiB
Python
112 lines
3.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""FilmForge 温控闭环自动测试(COM5,RS485)。
|
||
|
||
固定测试序列:M999(版本) -> M2S500(0.5s 上报) -> M4S50(目标) -> M3S1(加热)
|
||
-> 采样 DURATION 秒 -> M3S0/M2S0 收尾。
|
||
|
||
不设温度上限(用户明确:真机靠物理温度开关保护,测试环境上限由用户控制)。
|
||
|
||
判定标准(脚本自动输出,不掺人工解释):
|
||
J1 上报流:>2 帧/s 连续; J2 稳态窗(最后 300s):|均值-目标|<=0.5C
|
||
J3 稳态窗极值:[目标-0.5, 目标+0.5](用户规格 ±0.5C)
|
||
J4 无 OPEN/SHRT 故障值
|
||
输出:test_data.csv(t,temp 全序列)+ test_events.log(全部收发)+ 终端摘要。
|
||
"""
|
||
|
||
import sys
|
||
import time
|
||
|
||
sys.path.insert(0, "GUI")
|
||
import filmforge_rs485 as ff
|
||
|
||
PORT = "COM5"
|
||
TARGET = 50.0
|
||
DURATION = float(sys.argv[1]) if len(sys.argv) > 1 else 600.0
|
||
|
||
rows = [] # (t, temp or None)
|
||
events = []
|
||
faults = []
|
||
version = None
|
||
t0 = time.time()
|
||
|
||
|
||
def on_event(line):
|
||
events.append((time.time() - t0, line))
|
||
with open("test_events.log", "a", encoding="utf-8") as f:
|
||
f.write(f"{time.time():.3f} {line}\n")
|
||
|
||
|
||
def on_status(msg):
|
||
on_event(f"[STATUS] {msg}")
|
||
|
||
|
||
def on_frame(frame, t_rx):
|
||
payload = frame.payload if isinstance(frame.payload, str) else \
|
||
frame.payload.decode("ascii", errors="replace")
|
||
try:
|
||
val = float(payload)
|
||
except ValueError:
|
||
val = None
|
||
if val is not None and val > -100.0: # 正常温度
|
||
rows.append((time.time() - t0, val))
|
||
elif val is not None: # OPEN=-273.15 / SHORT=-274.15
|
||
faults.append((time.time() - t0, val))
|
||
on_event(f"[FAULT] {payload}")
|
||
else:
|
||
if payload.startswith("v"):
|
||
global version
|
||
version = payload
|
||
on_event(f"[FRAME] {payload!r}")
|
||
|
||
|
||
link = ff.FF485(on_event=on_event, on_status=on_status, on_frame=on_frame)
|
||
link.open(PORT)
|
||
time.sleep(0.5)
|
||
if not link.is_connected():
|
||
print("OPEN COM5 FAILED")
|
||
sys.exit(1)
|
||
|
||
# ---- 序列 ----
|
||
link.send("M999")
|
||
time.sleep(1.0)
|
||
link.send("M2S500")
|
||
time.sleep(1.0)
|
||
link.send("M4S50")
|
||
time.sleep(1.0)
|
||
link.send("M3S1")
|
||
on_event("[SEQ] heat on, target 50")
|
||
|
||
t_end = time.time() + DURATION
|
||
while time.time() < t_end:
|
||
time.sleep(1.0)
|
||
|
||
link.send("M3S0")
|
||
time.sleep(0.5)
|
||
link.send("M2S0")
|
||
time.sleep(1.0)
|
||
link.close()
|
||
|
||
# ---- 判定输出 ----
|
||
with open("test_data.csv", "w", encoding="utf-8") as f:
|
||
f.write("t,temp\n")
|
||
for t, v in rows:
|
||
f.write(f"{t:.2f},{v:.2f}\n")
|
||
|
||
print(f"version={version} frames={len(rows)} faults={len(faults)}")
|
||
if rows:
|
||
temps = [v for _, v in rows]
|
||
print(f"info global_max={max(temps):.2f} global_min={min(temps):.2f}")
|
||
settle = [v for t, v in rows if t >= rows[-1][0] - 300.0] or temps
|
||
mean = sum(settle) / len(settle)
|
||
print(f"J2 settle_mean={mean:.2f} (|{-TARGET + mean:.2f}|<=0.5? {'PASS' if abs(mean - TARGET) <= 0.5 else 'FAIL'})")
|
||
ok3 = max(settle) <= TARGET + 0.5 and min(settle) >= TARGET - 0.5
|
||
print(f"J3 settle_min={min(settle):.2f} settle_max={max(settle):.2f} "
|
||
f"p2p={max(settle) - min(settle):.2f} ([{TARGET - 0.5:.1f},{TARGET + 0.5:.1f}]? {'PASS' if ok3 else 'FAIL'})")
|
||
cross = next((t for t, v in rows if abs(v - TARGET) <= 0.3), None)
|
||
if cross:
|
||
post = [(t, v) for t, v in rows if t >= cross]
|
||
print(f"reach={cross:.0f}s post_max={max(v for _, v in post):.2f} "
|
||
f"(overshoot {max(v for _, v in post) - TARGET:+.2f})")
|
||
print(f"J4 faults={'PASS' if not faults else 'FAIL(' + str(len(faults)) + ')'}")
|
||
print(f"J1 report_rate={'PASS' if len(rows) >= DURATION * 1.6 else 'FAIL'}")
|