票面:task-20 合入后 README 索引上挂着我名的两篇(Lead 指定挂批 4 MR)。按本目录八段结构写, 「拦下过什么真事」一栏用同日实测,作者自己的也算。 - bom-guard.md:立项依据是 2026-10-25 一天内 5 次(道E 4 次 + 道B 1 次);症状 #870-D / #8 / #274 一类**假语法错**,位置不指向真因;MSVC 与既有钩子全照不出来;"人工逐文件比 HEAD"只防得 住记得做的人。两条刻意不报的边界(新文件无 BOM、反向加 BOM)+ 一条刻意不做(不自动补,整文件 重写会把可修的丢 BOM 变成不可修的 U+FFFD)+ 三处互指防漂移,各写清理由。也记了本门禁自己的 立项提交被 gitlint 拦过(正文一行 92>80,量出来仍按了提交)——这一栏只挑别人的事写就等于暗示 写门禁的人不需要门禁。 - setview-check.md:立论点是"分层边界制造的物理盲区"——BSP 不得 include App 头,行表 id 只能是 裸数字 + 注名,编译期无法自证;像素断言又只看可见 8 行,40 项分 5 页,错位一行时第 1 页可能 看着完全正常。另收两条脚本自身实测坑:\w+ 吃不下带引号的单位导致**整行静默跳过**(故加行数 硬闸)、打印中文时 CP936 半路崩(门禁脚本崩在半路 = 一条都没检查)。退出码 1 与 2 分开:把 "我没读懂"报成"你写错了"是检查类脚本最坏的一种失败。 - README.md:索引加两行;"那 46 个钩子" → 48、"40 个 hook → 23 篇" → 42 → 25 篇(不同步就是在 让索引说谎)。AGENTS 里"两篇待补"那句已过期,但 AGENTS 是单写者文件,留给 Lead 收。 跳过的门禁(三条同族):mem-numbers / mem-budget / size-gate(L0c),与本笔无关,等台账回填。
6.9 KiB
setview-check — 定值查看行表与 prot_set 枚举序 / 上下限一致性
一句话:UI 侧四张定值视图表(
Drivers/BSP/UI/ui_w_setview.c)与算法侧的定值定义 (App/algo/prot_set.h枚举序 +App/algo/prot_set.c的g_prot_set_meta)之间有一条 编译期照不到的对齐关系——本门禁从外面把它钉住。
1. 它在查什么
脚本用正则读出三份事实,再逐条比对(tools/setview_check/check_setview_limits.py):
- 四张视图表的
id合起来恰好覆盖 0..39 各一次(不多、不少、不重); - 每个数值行的
min/max等于g_prot_set_meta的 Q10 上下限按该行小数位换算后的整数; - 行
kind(数值 / 二态串表)与g_prot_set_meta[].is_ctl一致; - 单位串只允许 ASCII(
V/A/ms/Hz/NULL)——中文字不进子集字体,见font-charset.md; - 小数位查表
s_dec[]与行表decimals对同一个id取值一致(组件get只收到id, 两处不一致就会显示错位数字)。
退出码:0 一致;1 不一致(打印具体行与期望值);2 解析锚点丢失——这是结构漂移, 说明 C 侧写法变了、脚本要跟着改,不是"数据错了"。1 与 2 必须分开:把"我没读懂"报成"你写错了" 是检查类脚本最坏的一种失败。
2. 为什么要它(这不是"多一道保险")
- 分层边界制造的物理盲区:
layer-deps钩子禁止Drivers/BSP/**includeApp/**的头, 所以视图表里的id只能是裸数字 + 行尾注名(/* PS_OC1_EN */)。这条对应关系在编译期 无法自证:写错一个数字、少写一行、上下限抄错一位小数,编译器与 cppcheck 全都不会响。 - 像素断言只覆盖可见部分:一页只有 8 行进入黄金参考,40 项定值分 5 页;行表整体错位一行时, 第 1 页可能看起来完全正常(名字和值都是"某个真定值"),要翻到第 2、3 页才可能被人看出来, 而人真去翻的时候往往已经太晚——那时基线已经按错的图刷新过了。
- 也就是说:分层是对的,代价必须有人补。这条门禁就是补的那一步(同类补法见
registration-check.md用反向校验补"新文件双端注册",ui-golden.md补"改了就画错")。
3. 触发面
^(tools/setview_check/|Drivers/BSP/UI/ui_w_setview\.(c|h)$):改脚本自己、或改视图表所在
文件时触发。⚠ files 不命中时是 Skipped,Skipped ≠ Passed。
本钩子与实现文件是跨分支落地的(钩子配置在 task/bom-guard 分支,脚本在 task/pages-ui
分支):在脚本还没合入的树上,files 永不命中 → 钩子自然 skip,合入后自动生效,不需要任何人
再动配置。这一条写进配置注释,是为了避免下一个人以为"钩子没跑 = 配置坏了"。
4. 单独跑
# 仓库根执行(脚本按仓库根的相对路径读三个源文件,别在子目录跑)
& '.venv/Scripts/python.exe' tools/setview_check/check_setview_limits.py
正常输出只有一行:行表 40 行 / dec 40 项 / meta 40 项 -> OK。行数必须等于 40 是硬闸:
解析器少吞一行不算通过(见 §6.1)。
5. 好处
- 把一条"只有真机装载真定值、且人翻到第 2 页之后"才可能暴露的静默错位,提前到提交点;
- 让整定 4 页能安全复用同一批表:界内界外只有一份定义,不会出现"查看说合法、整定说越界"
两套边界(复用是当初把表放在
ui_w_setview.c而不是各页面里的原因); - 口径变更留下痕迹:
RATIO_IDS与文档 §3.2.2 的表注互为引用,改动必须同时解释。
6. 限制、盲区与写脚本时踩到的两个坑
脚本用正则读 C 源码,天然脆弱:换一种写法(宏生成表、初始化器拆行、结构体加字段)就可能 解析不到。以下是同日实测的两个真坑,都已固化进脚本:
6.1 \w+ 吃不下带引号的单位 → 整行静默跳过
行表项形如 { "过流Ⅰ段", 0, 1, 0, 0, "A", NULL }。最初的行正则用 \w+ 匹配"最后一个字段",
带引号的 "A" 匹配不上 → 整行不匹配 → 一条错误都不报,脚本照样打 OK。少检一行比检查错
更难发现(后者有人会去查,前者看起来像已经查过)。修法有两半:字段改用 \S+ 承接,以及加
len(rows) == 40 的硬闸——解析条数本身也要被检查,否则"没解析到"就等价于"没问题"。
6.2 打印中文 → Windows 控制台按 CP936 写 stdout 会崩在半路
门禁脚本崩在半路等于一条都没检查,而现场看到的是一串 Python traceback 而不是 FAIL。脚本开头
把 stdout/stderr 锁到 UTF-8(errors="replace")。同一条课在本仓一天里踩了两次,第二次才
写成固定动作——所以两个脚本里都有这段,且都写了为什么。
6.3 不检查的东西(别把它当万能闸)
- 名字对不对不查:
"过流Ⅰ段"该不该叫这个名字是语义判断,脚本只核结构一致;名称口径 的真源是docs/UI设计/页面扩充详细方案.md§3.2.2~§3.2.5 四张表; - 上下限本身合不合理不查:脚本只核"行表等于 meta 换算结果",如果 meta 自己就写错, 这条门禁会忠实地把错的值判成一致(真机定值核对在台架大窗里做);
- Q10 → 显示小数位的换算函数本身由主机单测与像素断言覆盖,不在本脚本内(脚本重算一遍 换算是为了独立见证,不复用 C 侧代码)。
7. 豁免与基线
无存量豁免、无基线文件、无 --write。定值项数改变(PS_ID_COUNT ≠ 40)时必须同步:脚本里的
期望行数与 id 上界、ui_data.h 的 UI_DATA_SET_N、以及 docs/UI设计/UI_数据供给设计说明.md
的参数通道说明。
一条已修正的原口径记在这里免得被"改回去":四个返回系数按 2 位小数显示。原方案文档写 3 位, 而 Q10 存不下 0.95(0.95×1024 截成 972 → 真存 0.94921875),3 位会多打一位根本不存在的数字; §3.2.2 的表已同步改成 2 位并加 ⚠ 指向代码注释。
8. 改这个门禁本身
- 行表写法一改(新增字段、换初始化风格),先跑脚本:退出码 2 就是"脚本要先跟上";
- 新增校验项时同步新增一条"如果这行解析不到就 FAIL"的期望,不要让新校验继承 §6.1 的静默;
- 想把它扩成通用"表 vs 元数据"检查器之前,先数一下真有几个这样的对象(现在只有一个)。
9. 变更记录
| 日期 | 变更 |
|---|---|
| 2026-10-25 | 首版(道E task-20 追加批;脚本与实现来自 task-15 批 3)。素材:\w+ 静默跳行、CP936 半路崩、满屏横杠与 4 字符位两条像素教训 |