Files
STM32F4-Base/test/host/cal_write.py
T
wandering1 1355dbe1b1 fix(cfg): 配置热重载吞段缺陷修复 + 两点标定工具链与台架实测落档
修复运行期改配置被打回出厂值的缺陷:cfg_file_poll() 按段循环复用同一
份读缓冲,而 s_parse() 通过 s_split_line()/s_key_value() 就地改写缓冲
(把换行与等号换成 NUL)。第一个段解析完,缓冲里已没有键值行,后续 7
个段静默保持出厂值;随后 run_par_store_save() 又把这份出厂值写回
config.ini,用户的标定值被永久覆盖。开机路径 cfg_file_load() 只解析一
轮,所以该缺陷长期不可见,只在运行中改配置时发作。

- s_parse() 改为非破坏性:入参 const char *,逐行 memcpy 到栈缓冲
  (CFG_LINE_TEST_MAX + 1)后再交给会破坏缓冲的解析子函数;超长行跳
  过(保存侧已拒绝落盘超长受管键行)。
- 新增 TC-CONFIG-063:同时改两段内容,断言第二个注册段也重载成功、
  含负值 INT 键存活,把「热重载吞段」钉成机器面。

新增两点标定台架工具链(test/host/):
- cal_two_point.py:整数算术镜像 adc_cal_two_point(),支持单路模式与
  从执行卡 CALDATA 表批量求解(--from-doc),带合法带与零点自检拦截。
- cal_write.py:复用 iec104_master 作库,经 104 定值段写 r 后写 b 并
  读回校验,是所有写入的唯一入口。

adc_task 的 meas: 心跳行精度提到 4 位小数,标定读数需要。

文档:新增《采样数据校正全流程》(端到端导航)与《ADC 两点标定执行
流程》(台架执行卡,含 CALDATA 可填表);ADC通道映射说明新增 §7.3 台
架实测结果(UA 554.687914、IA 29.634259,出厂默认值保持不变);配置
文件设计说明新增解析器契约 v1.19;采集与算法层设计说明 §13 系列与
README 索引同步。

台架实测:UA r=554.687914(出厂 1000.0 偏大 1.80 倍,根因是接线分压
使 PT 一次侧只拿到一半电压,实测与参数算定 606.06 只差 8.5%)、IA
r=29.634259(与出厂 29.4118 只差 0.76%,反证链路未算错)。UN 按用户
裁定本轮不标定,保护闭锁(vl=0x11)是预期行为,未伪造 VALID。

同时入库 .dsh/roles 角色定义(原被 .gitignore 挡住)。
2026-11-13 01:35:58 +08:00

93 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""两点标定写入器:C_SE_NC_1 两段写下发 r/b,C_RD 读回复核。
复用台架已验收主站库 test/host/iec104_master.py(不重写协议层)。
地址(App/algo/run_par.c run_par_104_write):r = 0x8050+slot,
b = 0x8048+slot;slot UA0/UN3/IA4。执行卡:
docs/测试说明/专项验证/ADC两点标定执行流程.md §4。
用法:
python test/host/cal_write.py --r 0x8050=995.99 --b 0x8048=-1 \
--read 0x8050,0x8048 --expect 995.99,-1
python test/host/cal_write.py --read 0x8050,0x8054 --expect 995.99,29.52
写入顺序刻意**先 r 后 b**:b 走 adc_cal_set() 置 VALID 位,而 VALID 要求
r 已在带——先让 r 就位,语义干净。--b 必须是整数码值。
PN(否定确认)= 执行段拒绝:看串口 `[ERR] set ch=... rejected` 行。
输出纯 ASCII,可直接重定向归档为台架证据;exit 0 全过 / 1 有失败 / 2 连接或协议错误。
"""
import argparse
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import bench_defaults # noqa: E402
import iec104_master as m104 # noqa: E402
def parse_pair(text):
addr_s, val_s = text.split("=", 1)
return int(addr_s, 0), float(val_s)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--ip", default=bench_defaults.DEVICE_IP)
ap.add_argument("--port", type=int, default=2404)
ap.add_argument("--r", action="append", default=[],
help="0xADDR=FLOAT ratio write (repeatable, issued first)")
ap.add_argument("--b", action="append", default=[],
help="0xADDR=INT zero-code write (repeatable, sets VALID)")
ap.add_argument("--read", default="",
help="comma-separated IOA list, C_RD readback")
ap.add_argument("--expect", default="",
help="comma-separated expected values, same order as --read")
a = ap.parse_args()
reads = [x.strip() for x in a.read.split(",") if x.strip()]
expects = [float(x) for x in a.expect.split(",")] if a.expect else []
if not a.r and not a.b and not reads:
ap.error("need at least one --r / --b / --read")
ck = m104.Checks()
try:
m = m104.Master(a.ip, a.port)
except Exception as exc:
print("CONNECT FAIL: %r" % (exc,))
return 2
try:
m._send(m104.u_frame(m104.U_STARTDT_ACT), "U STARTDT act")
ck.add("startdt_con", m.wait_event(
lambda e: e[0] == "startdt_con", 5.0) is not None)
for text in a.r: # 1) ratio first
addr, val = parse_pair(text)
m104.se_write(m, ck, addr, val, "r_%04X" % addr)
for text in a.b: # 2) zero + VALID
addr, val = parse_pair(text)
if abs(val - round(val)) > 1e-6:
print("WARN b=%s not an integer code, truncated to %.0f"
% (text, round(val)))
val = float(round(val))
m104.se_write(m, ck, addr, val, "b_%04X" % addr)
for i, addr_s in enumerate(reads):
want = expects[i] if i < len(expects) else 0.0
m104.rd_set(m, ck, int(addr_s, 0), want, "rd_%s" % addr_s)
finally:
m.close()
bad = [n for n, ok, _ in ck.items if not ok]
print("SUMMARY %s total=%d fail=%d" %
("PASS" if not bad else "FAIL", len(ck.items), len(bad)))
if bad:
print("FAILED: %s" % ", ".join(bad))
return 0 if not bad else 1
if __name__ == "__main__":
sys.exit(main())