generated from hulk/gd32f4xx_template_cmake_vscode
Tools: PID debug toolchain from V0.1.x bring-up
test_run.py: judged closed-loop test over COM5 (fixed sequence, J1~J4
This commit is contained in:
+111
@@ -0,0 +1,111 @@
|
||||
# -*- 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'}")
|
||||
Reference in New Issue
Block a user