From 557f1d042ac21bfd491374d36da9934eae17b342 Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Thu, 29 Oct 2026 20:04:24 +0800 Subject: [PATCH] =?UTF-8?q?docs(=E8=AE=A1=E6=95=B0):=20=E5=BD=95=E6=B3=A2?= =?UTF-8?q?=E8=90=BD=E5=9C=B0=E5=90=8E=E7=9A=84=E5=85=A8=E4=BB=93=E8=AE=A1?= =?UTF-8?q?=E6=95=B0=E5=90=8C=E6=AD=A5=EF=BC=88=E5=A5=97=E6=95=B0/?= =?UTF-8?q?=E9=92=A9=E5=AD=90/=E9=BB=84=E9=87=91/=E8=8F=9C=E5=8D=95/?= =?UTF-8?q?=E5=AD=97=E5=BA=93=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 机器面先定真值,再逐处对齐文档;带日期戳的取数行只加「当时」不改数。 六个数(每条两个以上独立机器源互证) - 主机单测套数 41 → 44(find test/host -name run_unit_test.py / 覆盖率总闸 --list / 基线 modules 条目三源一致;目录数 46 = 44 + common + __pycache__,两个数分开写) - 有专属钩子 22 根(YAML 解析 -unit-test 后缀)⇒ 无专属钩子 19 → 22 套,并把 wave_ring / wave_store / wave_trig 逐名列进门禁台账 §6 第 4 条,18 / 19 / 22 三个数 改标「裁定当时 / 2026-10-28 / 2026-10-29 现值」 - 内容页黄金参考 60 → 66 张(simulator/tests/ref/shot_*.bin 实数 / PAGES 条目 66, 其中 2 个分页 → 页面 64);钩子显示名与门禁索引表成对改 - 菜单 42 → 43 行(9 菜单 + 34 叶)、已绑 28 → 30、未绑 4(ui_menu_tab.h 宏 + g_menu_tab[] 末行 / ui_page_registry.h 的 UI_PAGE_MAP_COUNT + #page= 计数剔示例行) - 中文字库 218 → 224 唯一字、313 → 319 字形(清单唯一码点 / .bitmap_index 条目数 含 notdef;224 + 95 = 319 成立) - 钩子口径 52 声明 / 49 提交期实跑:只核不改,核对 AGENTS 与 YAML 解析一致 Lead 追加四处(wave-algo 按「AGENTS 只核不改」的指令交回我裁的部分) - AGENTS.md 提交门禁节:41 套 → 44 套(两处)、19 套 → 22 套(两处,并把「与录波 三套」写进括注)、ui-golden 条目 60 张 → 66 张;52 / 49 的口径叙述与历史括号原样 - scripts/board_ui_nav.py 的自述 docstring:60 张 → 66 张 - test/host/sys_log_mcu/sys_log_mcu_test.c 注释:全量门禁 41 套 → 44 套(纯注释) - 产品需求 FR-UI-2:需求原文「42 节点」保留不动,在实现行注「2026-10-29 实到 43 节点,42 是 V0.1 立项数」——需求基线不由实现方回改 刻意不动的(冻结面) - 带日期戳的实测行(自动化测试流程、字库差集门禁、覆盖率口径里的 67 s / 25 s / 45 s / ~105 s 四个耗时)、保留变更记录的文档历史行、页面补齐规划里自写「完成即 冻结」的立项快照表、2026-10-28 那份分工说明、级联框总览.svg(42 节点时代所画, 无生成器,重画另开一笔)、三处伪命中(字体数据行、芯片手册、m2/m3 数据目录) 自查(真实退出码):check_doc_links.py --all RC=0(215 个文档、章节引用可判定 80 %, 合并后第一次全库零红);check_generated_fresh.py RC=0(22 张图 17 一致 / 5 条既有 BASELINE 豁免,生成物零改动);编号门禁 add 后复扫 RC=0;17 + 4 个改动文件字节体检 全绿(无 BOM / LF / 无 U+FFFD / 无尾随空白 / 单个尾换行)。 --- .dsh/skills/font-regen/SKILL.md | 2 +- .dsh/skills/lvgl-simulator/SKILL.md | 2 +- .dsh/skills/stale-doc-count-sweep/SKILL.md | 16 ++++++------ .pre-commit-config.yaml | 8 +++--- AGENTS.md | 2 +- docs/UI设计/UI_设计架构.md | 4 +-- docs/UI设计/菜单页面设计.md | 29 ++++++++++++---------- docs/UI设计/页面扩充详细方案.md | 2 +- docs/UI设计/页面补齐规划.md | 7 +++--- docs/产品需求/产品需求设计文档.md | 11 +++++--- docs/代码规范/pre-commit使用说明.md | 4 +-- docs/测试说明/主机单测覆盖率口径.md | 8 +++--- docs/编号规范.md | 2 +- docs/门禁说明/README.md | 2 +- docs/门禁说明/host-unit-tests.md | 18 ++++++++------ scripts/README.md | 2 +- scripts/board_ui_nav.py | 2 +- simulator/README.md | 2 +- simulator/tests/check_content_pages.py | 4 +-- test/host/sys_log_mcu/sys_log_mcu_test.c | 2 +- 20 files changed, 71 insertions(+), 58 deletions(-) diff --git a/.dsh/skills/font-regen/SKILL.md b/.dsh/skills/font-regen/SKILL.md index bac33887..7ace06c2 100644 --- a/.dsh/skills/font-regen/SKILL.md +++ b/.dsh/skills/font-regen/SKILL.md @@ -34,5 +34,5 @@ python tools/font_gen/extract_cn.py # 扫描 UI 源码字符串 → - `requirements.txt` 必须**纯 ASCII**(Windows 下 pip 按 GBK 解码,中文注释 → UnicodeDecodeError) - 生僻字(宋体可能无字形)→ **写码前先手动验证字形**,避免"代码写完才发现是空白" - 生成成功后跑 `ui-verify` skill 全回归;黄金参考因字形变化而 FAIL 属预期 → `check_content_pages.py --update` 刷新 -- 字数变化时同步更新文档中的字符集计数(当前 218 汉字/符号 + ASCII 0x20-0x7E,共 313 字形;2026-10-28 实测。历史上 119/214 → 179/274 → 185/280 均为当时快照):`UI_中文字体生成与配置.md`、`UI_界面制作流程.md` C3、`UI_设计架构.md`、`simulator/README.md` 已知坑 #4 +- 字数变化时同步更新文档中的字符集计数(当前 **224** 汉字/符号 + ASCII 0x20-0x7E,共 **319** 字形;2026-10-29 实测:`ui_cn_chars.txt` 唯一码点 224、`ui_font_cn_12.c` 的 `.bitmap_index` 320 条含 notdef → 319 字形。历史上 119/214 → 179/274 → 185/280 → 218/313 均为当时快照):`UI_中文字体生成与配置.md`、`UI_界面制作流程.md` C3、`UI_设计架构.md`、`simulator/README.md` 已知坑 #4 - **新增字低于既有最小码位**(如 Ⅰ U+2160)会下探稀疏 cmap 起点并吞掉单码位补回条目(实测 ▶ 箭头全变方框而旧版 coverage 全绿)——`make_font.py`(2026-10-25 起)按 LVGL 首命中语义插入/守卫/校验,扩字后模拟器像素回归**必跑** diff --git a/.dsh/skills/lvgl-simulator/SKILL.md b/.dsh/skills/lvgl-simulator/SKILL.md index f40734fa..b703fefb 100644 --- a/.dsh/skills/lvgl-simulator/SKILL.md +++ b/.dsh/skills/lvgl-simulator/SKILL.md @@ -33,7 +33,7 @@ PC 上编译运行与 MCU **同一份** UI 源码(CMake + MSVC + SDL2,x64, ## UI 改动回归 → 用 `ui-verify` skill(勿在此零散手跑) -5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 60 张黄金参考 + 往返池)→ `@build` → `coding-standard`。 +5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 66 张黄金参考 + 往返池)→ `@build` → `coding-standard`。 RemoDispBus 推流回归单独跑:`.venv\Scripts\python.exe simulator/tests/check_remo_sink.py`。 黄金参考 `tests/ref/*.bin` **随仓库入库**(`.gitignore` 在全仓 *.bin 兜底之后又显式反选,2026-10-22 起;缺基准 = 新克隆跑 `ui-golden` 必 FAIL):不入库的只是派生目检图 `ref/*.png`,换机后先 `check_content_pages.py --update` 生成,再 `regen_ref_png.py` 出目检 PNG。 diff --git a/.dsh/skills/stale-doc-count-sweep/SKILL.md b/.dsh/skills/stale-doc-count-sweep/SKILL.md index b95b2879..faee5ccb 100644 --- a/.dsh/skills/stale-doc-count-sweep/SKILL.md +++ b/.dsh/skills/stale-doc-count-sweep/SKILL.md @@ -6,20 +6,20 @@ whenToUse: 某个计数/数字变了要跨 md/yaml/py/txt 同步、用户说「 # 陈旧计数全仓同步 -适用:**一个事实数变了**(黄金参考 27→40→47→53→60、已绑叶数 22→28、字库 185/280→218/313、钩子 51→52→51…),仓库里多处引用它的当前态表述要跟着变,而历史记录、同数异义、带日期戳的测量**不能**跟着变。 +适用:**一个事实数变了**(黄金参考 27→40→47→53→60→66、已绑叶数 22→28→30、主机单测套数 28→33→41→44、无专属钩子套数 18→19→22、字库 185/280→218/313→224/319、菜单节点行数 42→43、钩子 51→52→51→52…),仓库里多处引用它的当前态表述要跟着变,而历史记录、同数异义、带日期戳的测量**不能**跟着变。 -本仓已跑过四轮,照序列复跑、别临场重新发明分类:`b24b2b5f`(27→40,13 文件)、`31ca4a1f`(40→47,11 文件)、UI 补齐批(`94b0d790` 落地 → `cbb4fdb8` 产品需求同步 → `11d60057` 架构总览补漏 → `6daa01ae` 字体**口径**更正)、钩子一增一减(`1668842d`,52→51)。 +本仓已跑过四轮,照序列复跑、别临场重新发明分类:`b24b2b5f`(27→40,13 文件)、`31ca4a1f`(40→47,11 文件)、UI 补齐批(`94b0d790` 落地 → `cbb4fdb8` 产品需求同步 → `11d60057` 架构总览补漏 → `6daa01ae` 字体**口径**更正)、钩子一增一减(`1668842d`,52→51)、**第五轮 2026-10-29 录波落地**(套数 41→44、无专属钩子 19→22、黄金 60→66、菜单 42→43 节点 / 33→34 叶、已绑 28→30、字库 218/313→224/319;改动面含 `.pre-commit-config.yaml` 显示名与 `docs/门禁说明/README.md` 成对、三个 skill 正文)。 ## 各计数的权威口径速查(本仓实测值,动手前先对表) | 计数 | 真值来源(**至少两处互证**) | 已知伪命中 / 换算 | |---|---|---| -| 内容页黄金参考**张数** | `simulator/tests/ref/` 下 `shot_*.bin` 实数(实测 60)= `simulator/tests/check_content_pages.py` 的 `PAGES` 条目数(实测 60) | 分页页要**单独再拍一张**(id 加 `_p2`,如 `runreport_p2`)→ **张数 = 页面个数 + 分页多出的张数**;两个数分开报,不许互相冒充(53 张时代的一个 12 行分页页会让 55 页变 56 张这类差 1) | -| 已绑内容页**叶数** | `Drivers/BSP/UI/ui_page_registry.h` 的 `#define UI_PAGE_MAP_COUNT 28u`(由 `tools/menu_gen/gen_menu.py` 生成) | `grep -c '#page=' ui_menu_tree.txt` 得 **29**:第 5 行是菜单文件**头部注释里的示例行** → 剔掉注释/模板行后的 28 才是真值 | +| 内容页黄金参考**张数** | `simulator/tests/ref/` 下 `shot_*.bin` 实数(2026-10-29 实测 **66**)= `simulator/tests/check_content_pages.py` 的 `PAGES` 条目数(实测 **66**,其中 2 个 `_p2` 分页 → 页面 64) | 分页页要**单独再拍一张**(id 加 `_p2`,如 `runreport_p2`)→ **张数 = 页面个数 + 分页多出的张数**;两个数分开报,不许互相冒充(53 张时代的一个 12 行分页页会让 55 页变 56 张这类差 1) | +| 已绑内容页**叶数** | `Drivers/BSP/UI/ui_page_registry.h` 的 `#define UI_PAGE_MAP_COUNT 30u`(由 `tools/menu_gen/gen_menu.py` 生成;2026-10-29 录波道绑两叶后 28→30) | `grep -c '#page=' ui_menu_tree.txt` 得 **31**:第 5 行是菜单文件**头部注释里的示例行** → 剔掉注释/模板行后的 30 才是真值 | | 提交**钩子数** | YAML 解析后的条目数(实测 **51**):自查命令见 `docs/门禁说明/README.md` §2 首条;与 AGENTS.md「共 N 个钩子」、该目录 §2 标题**三数同源** | `grep -c` 配置里的声明行 = 52(多算一条**被注释掉的** clang-format 候选);在配置**注释里复述那个 grep 字面量**会让文本计数再虚高 1 | -| 字库**唯一字数** | `ui_cn_chars.txt` 唯一码点数(实测 218,其中 ASCII 区 **0** 个) | `make_font.py` 的 `-r 0x20-0x7E` 是 ASCII 子集参数,**不算进字数** | -| **字形数** | `Drivers/BSP/UI/ui_font_cn_12.c` 里 `.bitmap_index` 出现次数 **314 条含 notdef → 313 字形**;换算式 = 218 汉字/符号 + ASCII 95 = 313 | 直接报 314 就是把 notdef 当字形;文档必须写出「不含 ASCII」「含 notdef 共 314 条」这类口径句 | -| 菜单**节点数 / 叶子数** | `ui_menu_tree.txt` 头部规则:缩进即层级(每级 2 空格)、`#` 开头为注释、空行忽略;叶子 = **不存在更深缩进后继行**的行 | 42 是节点行数、33 才是叶子;`未绑 = 待做 + 不做` 要与分类表逐项闭合 | +| 字库**唯一字数** | `ui_cn_chars.txt` 唯一码点数(2026-10-29 实测 **224**,其中 ASCII 区 **0** 个) | `make_font.py` 的 `-r 0x20-0x7E` 是 ASCII 子集参数,**不算进字数** | +| **字形数** | `Drivers/BSP/UI/ui_font_cn_12.c` 里 `.bitmap_index` 出现次数 **320 条含 notdef → 319 字形**;换算式 = 224 汉字/符号 + ASCII 95 = 319 | 直接报 314 就是把 notdef 当字形;文档必须写出「不含 ASCII」「含 notdef 共 314 条」这类口径句 | +| 菜单**节点数 / 叶子数** | `ui_menu_tree.txt` 头部规则:缩进即层级(每级 2 空格)、`#` 开头为注释、空行忽略;叶子 = **不存在更深缩进后继行**的行 | 43 是节点行数、34 才是叶子(2026-10-29 现值;42 / 33 是此前时点);`未绑 = 待做 + 不做` 要与分类表逐项闭合 | ## Steps @@ -61,7 +61,7 @@ whenToUse: 某个计数/数字变了要跨 md/yaml/py/txt 同步、用户说「 提交前逐条打勾;回报时**逐处列文件 + 命中数断言**,不要笼统说「已全部更新」。 - [ ] 每个数都写了**两个独立来源**且数值一致(`ref/shot_*.bin` 实数 = `PAGES` 条目数;`UI_PAGE_MAP_COUNT` = 剔伪命中后的 `#page=` 计数),并在结论里贴出该数字与伪命中取舍理由 -- [ ] 字形数说明**是否剔除 notdef**并给出条目数→字形数的换算(314 − 1 = 313);字数说明**是否含 ASCII**,且该口径句与实测一致 +- [ ] 字形数说明**是否剔除 notdef**并给出条目数→字形数的换算(条目数 − 1 = 字形数,2026-10-29 现值 320 − 1 = 319);字数说明**是否含 ASCII**,且该口径句与实测一致 - [ ] 分类表逐条列出:改了哪些当前态、哪些历史行保留、哪些误报不动(附理由)、哪些只加「当时」、哪些属**口径错**并已另起一笔 - [ ] 替换脚本打印了每处 `old -> new xN` 且每处 `assert` 通过(无半落地;报错时已 `git diff --stat` 确认零写入) - [ ] 命中面覆盖 `.pre-commit-config.yaml` 钩子 `name:`、AGENTS.md、`docs/**`、`scripts/**`、`.dsh/skills/*/SKILL.md`,逐条在改动清单里出现 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3261493a..0bad2054 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -649,7 +649,7 @@ repos: # 疑似 MSBuild 节点争用;此后 3 次干净树连跑均绿。判据:单次失败先复跑一次再看, # 连续两次失败才当真(不许直接 --no-verify 绕过)。 - id: ui-golden - name: UI 像素回归(菜单 8 状态 + 内容页 60 张 黄金参考) + name: UI 像素回归(菜单 8 状态 + 内容页 66 张 黄金参考) entry: python scripts/check_ui_golden.py language: system files: ^(Drivers/BSP/UI/|App/task/ui_task\.(c|h)$|simulator/(tests|sim|src)/|simulator/main\.c$|simulator/CMakeLists\.txt$|simulator/lv_conf\.h$|scripts/check_ui_golden\.py$|\.pre-commit-config\.yaml$) @@ -735,7 +735,8 @@ repos: # P1 新增的 12 套不在任何提交钩子里。原先"再加一份全量插桩等于同一件事做两遍" # 这条理由到现在已经**反了**(现状是有一遍没做),所以必须挂;缺口此前如实登记在 # docs/测试说明/主机单测覆盖率口径.md §8。 - # 为什么挂这一根而不是补 12 个 per-module 钩子:本钩子跑**全部 28 套**,并且守 §5 的 + # 为什么挂这一根而不是补 12 个 per-module 钩子:本钩子当时跑**全部 28 套**(现值 44 套,台账见 + # docs/门禁说明/host-unit-tests.md §6 第 4 条),并且守 §5 的 # 回退台账(某套被删、或某文件的已覆盖行变少 → [REGRESS] 直接红)。这是 per-module # 钩子做不到的——删掉一套测试,它那根钩子只会因为"没有文件要检查"而静默跳过。 # ⚠ FAIL 语义(用户裁定 2026-10-24):找不到 mscoverage 时脚本退出码 2 → 本钩子**失败**, @@ -745,7 +746,8 @@ repos: # 口径 / 分母 / 基线 / 盲区详解都在 docs/测试说明/主机单测覆盖率口径.md,本处只留接线事实。 # 触发面**含门禁自身**(.pre-commit-config.yaml):不带自身就等于谁改坏这根闸的 files: # 模式,它就再也不跑且无人察觉(同 lv-conf-sync / bsp-err 的既有做法)。 - # ⚠ stages: [manual](2026-10-28 用户裁定):本钩子跑光全部 41 套单测(实测 ~105 s, + # ⚠ stages: [manual](2026-10-28 用户裁定):本钩子跑光全部 44 套单测(41 套时实测 ~105 s,套件数 + # 涨了耗时就涨,44 套这轮未重测耗时; # 占一次带代码提交耗时的三分之二)→ 不压在提交期,由**远端 CI** 强制 # (.gitea/workflows/ci.yml 的「手动阶段门禁(覆盖率总闸 + 全量 cppcheck)」步),本地等价入口 = # scripts/ci_local.py(manual 段);手工复跑: diff --git a/AGENTS.md b/AGENTS.md index e37f9f11..cd4a3859 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -93,7 +93,7 @@ ## 提交门禁(pre-commit) -- 任一失败即拦截,共 **52 个钩子**(**口径 = YAML 解析后的 hook 条目数**:自查命令与「声明 / 文档 / 实跑」三数同源核对见 `docs/门禁说明/README.md` §2 首条——⚠ 直接 grep 配置里的声明行会多算一条**注释掉的**候选钩子(比口径多 1),不是口径;**配置注释里也别复述那个 grep 字面量**,它会让文本计数再虚高 1(2026-10-27 实测踩过);加一条钩子就同批改 AGENTS 与门禁索引两处计数。2026-10-25 道E 连加 `bom-guard` / `setview-check` / `uinav-selftest` 三条,2026-10-26 加 `generated-fresh` 一条,2026-10-27 加 `nested-comment` 一条;2026-10-28 加 `id-check-new` 一条并同批退役 `id-check`(口径 52 → 51:编号门禁换牙——新的只查新增行、零语料,旧的八项全库校验连语料一起移入 `scripts/archive/id_gate/`,路线见 `docs/编号规范.md` §8.5);**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check-new`(**编号门禁**:只查 `git diff` 新增行里有没有未登记形态的「字母+数字」标识,零语料、零维护;白名单是前缀级两张表,见 `docs/门禁说明/id-check-new.md`。旧的八项全库校验 `id-check` 已于 2026-10-28 退役,脚本与两份语料移入 `scripts/archive/id_gate/`,只在手工复查存量时才跑;退役路线见 `docs/编号规范.md` §8.5)+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + `bom-guard` / `setview-check` / `uinav-selftest`(2026-10-25 道E 那三条:前半句的加钩流水里有它们、逐项枚举里漏了,补上后本行各项相加才等于 52)+ 主机侧单测 **22 根专属钩子**(`-unit-test` 后缀 = 有独立 `files:` 触发面、需要秒级归因的那个子集;2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六根)——**这是口径不是缺口**:`test/host/` 实测 41 套带 `run_unit_test.py`,其余 **19 套**(含 `test/host/ui_sink`)没有专属钩子,但每次 CI / `scripts/ci_local.py` 都被 `test-coverage` 总闸全量真跑并守基线回退,**失败照样拦**,只是归因落在总闸输出上;**没有专属钩子 ≠ 没被跑**,19 套台账与补钩子的判据 → `docs/门禁说明/host-unit-tests.md` §6 第 4 条。**Lead 裁定 2026-10-27:不补第 23 根**——读「这套跑没跑」去看总闸输出,别数钩子(原写「23 个 / 含 ui-sink 七个」是双重错:既把 22 说成 23,又宣称 ui-sink 有钩子——配置历史里 `ui-sink-unit-test` 从未存在)+ `test-coverage`(**覆盖率总闸**:跑全部 41 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,登记簿条目「rs485_task 白名单缺口」)+ `generated-fresh`(**生成器幂等性**:把 `tools/menu_gen/gen_menu.py`、中文字库链、`tools/arch_diagram/gen_*.py` 拷进**系统临时目录**重跑,与工作区**逐字节**必须一致——钩子只读、不写仓库也不 stash 别人在飞的改动;实证 `0f7b5a87` 改了模板文字没重跑生成物,接线前实测另有 5 张 `docs/images/` 配图已漂 → 钉成只减不增基线,**业务生成物零豁免面** → `docs/门禁说明/generated-fresh.md`)+ `nested-comment`(**块注释嵌套**:注释体内再出现 /* 会被 ARMCC 报 9-D,且**按包含者数量刷屏**、只在重编译该 TU 时才暴露;本仓诱因就是「在注释里写作用域/通配路径」,改法是写全目录或用尖括号 ;接线当日实扫 403 个 .c/.h,新发 0、存量 2 处(`test/host/common/` 两份假件头)钉只减不增基线 → `docs/门禁说明/nested-comment.md`)+ `skill-size`(**SKILL.md 不超插件写侧上限 65536 字节**:dsh-mnemon 只在写侧校验、加载侧不查,超限技能照常可读却每次「生成草案」被拒、永久卡在「生成中」;另查 `reference/` 指针悬空与 BOM → `docs/门禁说明/skill-size.md`)+ `mem-budget`(RAM 92% 预算门禁,登记簿条目「缩堆迁 CCM 后半(暂缓)」)+ `font-charset`(UI 字符集差集检查,防缺字白框)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 60 张黄金参考)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3))+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**))。**提交期实跑 49 根**(2026-10-28 用户裁定):`cppcheck` / `test-cppcheck` / `test-coverage` 三根带 `stages: [manual]`——实测合计 ~165 s = 一次带代码提交耗时的三分之二,提交期只跑快的那些;这三根由**远端 CI** 强制(`.gitea/workflows/ci.yml` 的「手动阶段门禁」步,逐根 `--hook-stage manual`)与 `scripts/ci_local.py` 的 manual 段,本地手工复跑 `python -m pre_commit run --all-files --hook-stage manual `(一次只能一个 id) +- 任一失败即拦截,共 **52 个钩子**(**口径 = YAML 解析后的 hook 条目数**:自查命令与「声明 / 文档 / 实跑」三数同源核对见 `docs/门禁说明/README.md` §2 首条——⚠ 直接 grep 配置里的声明行会多算一条**注释掉的**候选钩子(比口径多 1),不是口径;**配置注释里也别复述那个 grep 字面量**,它会让文本计数再虚高 1(2026-10-27 实测踩过);加一条钩子就同批改 AGENTS 与门禁索引两处计数。2026-10-25 道E 连加 `bom-guard` / `setview-check` / `uinav-selftest` 三条,2026-10-26 加 `generated-fresh` 一条,2026-10-27 加 `nested-comment` 一条;2026-10-28 加 `id-check-new` 一条并同批退役 `id-check`(口径 52 → 51:编号门禁换牙——新的只查新增行、零语料,旧的八项全库校验连语料一起移入 `scripts/archive/id_gate/`,路线见 `docs/编号规范.md` §8.5);**每种门禁一篇详解(查什么 / 拦下过什么真事 / 限制与盲区 / 基线口径)→ `docs/门禁说明/README.md` 索引**;通用机制与整体口径 → `docs/代码规范/pre-commit使用说明.md`):通用 5 + `gitlint` + `cppcheck` / `test-cppcheck` + `no-mojibake` + `coding-standard` + 文档三件套 `doc-links` / `task-table` / `mem-numbers` + `id-check-new`(**编号门禁**:只查 `git diff` 新增行里有没有未登记形态的「字母+数字」标识,零语料、零维护;白名单是前缀级两张表,见 `docs/门禁说明/id-check-new.md`。旧的八项全库校验 `id-check` 已于 2026-10-28 退役,脚本与两份语料移入 `scripts/archive/id_gate/`,只在手工复查存量时才跑;退役路线见 `docs/编号规范.md` §8.5)+ `lv-conf-sync`(两份 `lv_conf.h` 特性子集一致)+ `cubemx-survival` + `bom-guard` / `setview-check` / `uinav-selftest`(2026-10-25 道E 那三条:前半句的加钩流水里有它们、逐项枚举里漏了,补上后本行各项相加才等于 52)+ 主机侧单测 **22 根专属钩子**(`-unit-test` 后缀 = 有独立 `files:` 触发面、需要秒级归因的那个子集;2026-10-25 增补 adc-cal / prot-dim / vol-idle-sync / ui-data / meas / run-par 六根)——**这是口径不是缺口**:`test/host/` 实测 44 套带 `run_unit_test.py`,其余 **22 套**(含 `test/host/ui_sink` 与录波三套)没有专属钩子,但每次 CI / `scripts/ci_local.py` 都被 `test-coverage` 总闸全量真跑并守基线回退,**失败照样拦**,只是归因落在总闸输出上;**没有专属钩子 ≠ 没被跑**,22 套台账与补钩子的判据 → `docs/门禁说明/host-unit-tests.md` §6 第 4 条。**Lead 裁定 2026-10-27:不补第 23 根**——读「这套跑没跑」去看总闸输出,别数钩子(原写「23 个 / 含 ui-sink 七个」是双重错:既把 22 说成 23,又宣称 ui-sink 有钩子——配置历史里 `ui-sink-unit-test` 从未存在)+ `test-coverage`(**覆盖率总闸**:跑全部 44 套并守基线回退;缺 mscoverage 组件时按用户裁定 **FAIL**、不 skip,2026-10-24)+ `test-suite-switch` + `registration-check`(双端注册反向校验 + UI 页面描述符表接线,登记簿条目「rs485_task 白名单缺口」)+ `generated-fresh`(**生成器幂等性**:把 `tools/menu_gen/gen_menu.py`、中文字库链、`tools/arch_diagram/gen_*.py` 拷进**系统临时目录**重跑,与工作区**逐字节**必须一致——钩子只读、不写仓库也不 stash 别人在飞的改动;实证 `0f7b5a87` 改了模板文字没重跑生成物,接线前实测另有 5 张 `docs/images/` 配图已漂 → 钉成只减不增基线,**业务生成物零豁免面** → `docs/门禁说明/generated-fresh.md`)+ `nested-comment`(**块注释嵌套**:注释体内再出现 /* 会被 ARMCC 报 9-D,且**按包含者数量刷屏**、只在重编译该 TU 时才暴露;本仓诱因就是「在注释里写作用域/通配路径」,改法是写全目录或用尖括号 ;接线当日实扫 403 个 .c/.h,新发 0、存量 2 处(`test/host/common/` 两份假件头)钉只减不增基线 → `docs/门禁说明/nested-comment.md`)+ `skill-size`(**SKILL.md 不超插件写侧上限 65536 字节**:dsh-mnemon 只在写侧校验、加载侧不查,超限技能照常可读却每次「生成草案」被拒、永久卡在「生成中」;另查 `reference/` 指针悬空与 BOM → `docs/门禁说明/skill-size.md`)+ `mem-budget`(RAM 92% 预算门禁,登记簿条目「缩堆迁 CCM 后半(暂缓)」)+ `font-charset`(UI 字符集差集检查,防缺字白框)+ `ui-golden`(UI 像素回归,菜单 8 状态 + 内容页 66 张黄金参考)+ `layer-deps`(**分层方向**:`Drivers/BSP/**` 不得 include `App/**` 的头;存量 3 条基线只减不增(2026-10-25 记录 reader 注入削掉 UI 记录 3 条边,6→3))+ `bsp-err`(**BSP 错误码与超时命名约定**:公开 API 不得拿 `HAL_StatusTypeDef`/`bool` 当错误通道、`*_TIMEOUT` 宏不得取负值、非 void 须写「返回值:」、**返回错误码的函数不得裸丢弃返回值**(须接住或显式 `(void)`;实测编译器与 cppcheck 都不报这类,只有这条会响);约定正文 `Drivers/BSP/bsp_err.h`,存量基线只减不增(2026-10-23 首清点 19 条 → 已还 18;`consume-result` 上线又暴露 37 条存量丢弃 → **现 38 条**))。**提交期实跑 49 根**(2026-10-28 用户裁定):`cppcheck` / `test-cppcheck` / `test-coverage` 三根带 `stages: [manual]`——实测合计 ~165 s = 一次带代码提交耗时的三分之二,提交期只跑快的那些;这三根由**远端 CI** 强制(`.gitea/workflows/ci.yml` 的「手动阶段门禁」步,逐根 `--hook-stage manual`)与 `scripts/ci_local.py` 的 manual 段,本地手工复跑 `python -m pre_commit run --all-files --hook-stage manual `(一次只能一个 id) - **范围 = 项目代码**:第三方(`Lib/`、`Middlewares/`、CMSIS、HAL、`lftpd`、`dhara`、`MDK-ARM`)排除;`cppcheck`/`coding-standard` 另排除 `test/`,规范检查再排除 CubeMX 生成的 `Src/`/`Inc/`。**`simulator/` 已纳入全部钩子**(2026-10-11)——生成物只靠 `.gitignore` 挡,**不在 `exclude` 里写目录名**(教训见 `docs/代码规范/pre-commit使用说明.md` §5.5) - ⚠ **pre-commit 4.x 已移除 `always: true`**(实测对 `local` 钩子只告警 `Unexpected key(s)` 并跳过)→ 想"总是跑"的钩子**必须让 `files:` 覆盖到所有会触发它的文件**,没有兜底开关 - **改写型钩子(`trailing-whitespace` + `end-of-file-fixer`)豁免 CubeMX/Keil 生成文件**(配置里 YAML 锚点 `&cubemx-generated`);⚠ **只读检查不受影响**:`cppcheck` / 乱码 / `cubemx-survival` / `task-table` / `mem-numbers` 仍覆盖 `Src/`、`Inc/` diff --git a/docs/UI设计/UI_设计架构.md b/docs/UI设计/UI_设计架构.md index 71cf28e7..8926145f 100644 --- a/docs/UI设计/UI_设计架构.md +++ b/docs/UI设计/UI_设计架构.md @@ -164,7 +164,7 @@ SDL 键盘 → LVGL SDL 驱动内部 5ms 定时器(sdl_event_handler) ### 4.2 数据:父子索引表 -- `g_menu_tab[]`:42 节点,每项 = 名称 + parent 下标(`MENU_ROOT`=0xFF 为根);树序即表序 +- `g_menu_tab[]`:43 节点,每项 = 名称 + parent 下标(`MENU_ROOT`=0xFF 为根);树序即表序 - "页" = 对表的一次过滤:节点 N 的页条目 = 全部 `parent == N` 的行(按表序) - 菜单 vs 叶子由"是否有子项"自动推导:`has_children(N)` 非空 → 菜单行(带 ▶),为空 → 叶子(无箭头,ENTER 进空白终点框) @@ -236,7 +236,7 @@ ENTER/→ 进子框: ### 5.3 框按需建/销 -- LVGL 池仅 16KB(`LV_MEM_SIZE`,2026-10-15 由 40KB 收缩),全量预建 42 个节点的所有页面必然爆内存 +- LVGL 池仅 16KB(`LV_MEM_SIZE`,2026-10-15 由 40KB 收缩),全量预建 43 个节点的所有页面必然爆内存 - 只建"当前路径"上的框:行自绘无对象,池内存 = 屏上框体(实测根框 used=5,368 B、最深四级级联 used=6,040 B、网口设置保存态峰值 used=6,568 B = 池的 40%) - 返回即 `lv_obj_delete` 释放,内存随时回落到根框水位 diff --git a/docs/UI设计/菜单页面设计.md b/docs/UI设计/菜单页面设计.md index a3a65be6..887d05e0 100644 --- a/docs/UI设计/菜单页面设计.md +++ b/docs/UI设计/菜单页面设计.md @@ -1,6 +1,6 @@ # 菜单页面设计 -> **用途**:定义 DTU 主菜单的**内容(42 节点层级树)、视觉规范、三层代码架构**(数据层 / 显示层 / 逻辑层),以及叶子节点进入后的**内容页模块**(§九)。 +> **用途**:定义 DTU 主菜单的**内容(43 节点层级树)、视觉规范、三层代码架构**(数据层 / 显示层 / 逻辑层),以及叶子节点进入后的**内容页模块**(§九)。 > **范围**:菜单层级(节点/父子/层级/进出导航)+ 三个已实现的内容页(装置信息/交流量/直流量,§九);其余叶子的内容页后续按同模式扩展。 > **来源**:用户提供的主菜单截图 7 屏(根页 + 实时数据/参数定值/三遥设置/装置维护/通讯参数/记录查询各一级子页 + 参数定值·整定二级)。 > **导航模型**:树形**逐级进入**——`ENTER`/`→` 进入子页,`ESC`/`←` 返回上一级;最多 **4 级(主界面 → 三级菜单)**。 @@ -37,7 +37,7 @@ flowchart TD K(["按键(远端键 / 模拟器键)"]) --> L["逻辑层 ui_menu.c
唯一 17 B 导航状态(path/选择/深度/滚动)
键语义:移动 / 进入 / 退出"] L -- "单向驱动: frame_build / frame_destroy / frame_refresh / focus" --> V["显示层 ui_menu_view.c/h
持全部 LVGL 对象,纯渲染,零导航状态"] - L -- "只读查询: children / valid" --> T["数据层 ui_menu_tab.c/h(生成物,只读)
g_menu_tab[42] 父子索引表 + 4 个查询 API"] + L -- "只读查询: children / valid" --> T["数据层 ui_menu_tab.c/h(生成物,只读)
g_menu_tab[43] 父子索引表 + 4 个查询 API"] V -- "只读查询: children / has_children / name" --> T ``` @@ -127,7 +127,7 @@ sequenceDiagram --- -## 二、菜单内容:42 节点层级树 +## 二、菜单内容:43 节点层级树 ### 2.1 层级总览 @@ -193,10 +193,11 @@ sequenceDiagram ├─ 遥控记录 [叶子] ├─ 自检记录 [叶子] ├─ 运行记录 [叶子] - └─ 运行报告 [叶子] + ├─ 运行报告 [叶子] + └─ 录波回放 [叶子] ``` -**统计**:菜单节点 **9** 个(含根),叶子节点 **33** 个,共 **42** 个节点;最大深度 **4 级**。 +**统计**:菜单节点 **9** 个(含根),叶子节点 **34** 个,共 **43** 个节点;最大深度 **4 级**。 ### 2.3 节点定义表(逻辑层级定义) @@ -236,7 +237,7 @@ sequenceDiagram | 29 | 通讯设置 | 二级菜单 | 叶子 | 28 | 0 | | 30 | 网口设置 | 二级菜单 | 叶子 | 28 | 0 | | 31 | SNTP设置 | 二级菜单 | 叶子 | 28 | 0 | -| 32 | 记录查询 | 一级菜单 | 菜单 | 0 | 9 | +| 32 | 记录查询 | 一级菜单 | 菜单 | 0 | 10 | | 33 | SOE记录 | 二级菜单 | 叶子 | 32 | 0 | | 34 | 事故记录 | 二级菜单 | 叶子 | 32 | 0 | | 35 | 操作记录 | 二级菜单 | 叶子 | 32 | 0 | @@ -246,12 +247,13 @@ sequenceDiagram | 39 | 自检记录 | 二级菜单 | 叶子 | 32 | 0 | | 40 | 运行记录 | 二级菜单 | 叶子 | 32 | 0 | | 41 | 运行报告 | 二级菜单 | 叶子 | 32 | 0 | +| 42 | 录波回放 | 二级菜单 | 叶子 | 32 | 0 | --- ## 三、数据层:ui_menu_tab(生成物,只读) -> **一句话职责**:提供"有什么条目"——一张扁平父子索引表(42 节点,`const` 存 Flash、只读)+ 4 个查询 API,供逻辑层 / 显示层共用。 +> **一句话职责**:提供"有什么条目"——一张扁平父子索引表(43 节点,`const` 存 Flash、只读)+ 4 个查询 API,供逻辑层 / 显示层共用。 > **绝不做**:持有任何状态、依赖 LVGL、被手改(改菜单走 §3.6 生成器)。 ### 3.1 选型:扁平"父子索引表"(邻接表 / 关系表模型) @@ -288,7 +290,7 @@ typedef struct { > 实现落在生成器产物 `Drivers/BSP/UI/ui_menu_tab.h`(见 §3.6);字段/语义与此一致。 -### 3.3 完整 42 节点表(按 §2.3 节点定义表落地) +### 3.3 完整 43 节点表(按 §2.3 节点定义表落地) > 产物 `ui_menu_tab.c`(§3.6 生成):`const ui_menu_node_t g_menu_tab[UI_MENU_TAB_COUNT]`,内容与下表一致。 @@ -337,10 +339,11 @@ const ui_menu_node_t g_menu_tab[UI_MENU_TAB_COUNT] = { { 32, "自检记录" }, /* 39 二级菜单叶子 */ { 32, "运行记录" }, /* 40 二级菜单叶子 */ { 32, "运行报告" }, /* 41 二级菜单叶子 */ + { 32, "录波回放" }, /* 42 二级菜单叶子 */ }; ``` -> `UI_MENU_TAB_COUNT`(=42)与 `MENU_ROOT` 在 `ui_menu_tab.h` 定义。 +> `UI_MENU_TAB_COUNT`(=43)与 `MENU_ROOT` 在 `ui_menu_tab.h` 定义。 ### 3.4 查询 API(逻辑层 / 显示层共用) @@ -392,7 +395,7 @@ for (uint8_t i = 0; i < cnt; i++) { | `ui_menu_tree.txt` | **人工编辑源**(缩进即层级);改菜单改这里 | | `tools/menu_gen/gen_menu.py` | 生成器(同 `make_font.py` 模式):解析缩进→推导 parent→产出 C 表;含根唯一/缩进对齐/层级上限自检 | | `Drivers/BSP/UI/ui_menu_tab.h` | 产物:`ui_menu_node_t` 定义 + `MENU_ROOT` + `UI_MENU_TAB_COUNT` + `extern g_menu_tab[]` | -| `Drivers/BSP/UI/ui_menu_tab.c` | 产物:`g_menu_tab[UI_MENU_TAB_COUNT]` 完整 42 节点表 | +| `Drivers/BSP/UI/ui_menu_tab.c` | 产物:`g_menu_tab[UI_MENU_TAB_COUNT]` 完整 43 节点表 | 重新生成: ``` @@ -421,9 +424,9 @@ python tools/menu_gen/gen_menu.py | 符号 | 定义处 | 内容 | 占用 | |---|---|---|---| | `ui_menu_node_t` | `ui_menu_tab.h` | `{ uint8_t parent; uint8_t pwd; const char *name; }`(32 位 8 B/项) | 类型定义 | -| `g_menu_tab[UI_MENU_TAB_COUNT]` | `ui_menu_tab.c`(生成) | 42 节点扁平父子表 | ≈ 42×8 B ≈ 336 B | -| 名字字面量 | 同上 | 42 个 `const char[]` | ≈ 400 B | -| `UI_MENU_TAB_COUNT` / `MENU_ROOT` | `ui_menu_tab.h` | 节点数 42 / 根哨兵 0xFF(8 位,上限 255 项) | 宏 | +| `g_menu_tab[UI_MENU_TAB_COUNT]` | `ui_menu_tab.c`(生成) | 43 节点扁平父子表 | ≈ 43×8 B ≈ 344 B | +| 名字字面量 | 同上 | 43 个 `const char[]` | ≈ 400 B | +| `UI_MENU_TAB_COUNT` / `MENU_ROOT` | `ui_menu_tab.h` | 节点数 43 / 根哨兵 0xFF(8 位,上限 255 项) | 宏 | > 逻辑层/显示层**只读**它(§3.4 的 API 都是对它的过滤/查询),从不修改。合计 ≈ **0.7 KB Flash**。 diff --git a/docs/UI设计/页面扩充详细方案.md b/docs/UI设计/页面扩充详细方案.md index 6070dea5..1224b17a 100644 --- a/docs/UI设计/页面扩充详细方案.md +++ b/docs/UI设计/页面扩充详细方案.md @@ -542,7 +542,7 @@ void ui_w_soe_refresh(ui_w_soe_t *inst, uint16_t row, char *name, char *valu ## 5. 菜单树改动 -**本批不改菜单树**——27 个空叶子全在现有 42 节点里,`#page=` 注记逐个补上即可(改 `ui_menu_tree.txt` 后重跑 `python tools/menu_gen/gen_menu.py`)。 +**本批不改菜单树**——当时 27 个空叶子全在现有 42 节点里(节点数现值 43,2026-10-29 录波道加「录波回放」叶),`#page=` 注记逐个补上即可(改 `ui_menu_tree.txt` 后重跑 `python tools/menu_gen/gen_menu.py`)。 > 【2026-10-26 已改一笔】整定 4 叶(参数 / 定值 / 控制字 / 软压板)已补 `#page=`, > 绑的是与查看列**同一批模块**(一个模块被两个叶子绑定是生成器允许的,`validate_pages()` 按 diff --git a/docs/UI设计/页面补齐规划.md b/docs/UI设计/页面补齐规划.md index af741912..9e055205 100644 --- a/docs/UI设计/页面补齐规划.md +++ b/docs/UI设计/页面补齐规划.md @@ -1,6 +1,7 @@ # UI 页面补齐规划(2026-10-23) -> **定位**:盘清 42 节点菜单里**还没有内容页的 27 个叶子**,按"数据是否就绪 + 页面形态"归并成可施工的包,给出分批顺序与每页规格。 +> **定位**:盘清当时 42 节点菜单里**还没有内容页的 27 个叶子**(节点数现值 43 = 9 菜单 + 34 叶, +> 2026-10-29 录波道加「录波回放」叶后升级;本文其余数字是 2026-10-23 立项快照,按 §1 的「完成即冻结」不回改),按"数据是否就绪 + 页面形态"归并成可施工的包,给出分批顺序与每页规格。 > **前置阅读**:`docs/UI设计/内容页架构.md`(框架机制与新增页面六步)、`docs/UI设计/菜单页面设计.md`(菜单树与几何)、`docs/UI设计/UI_自动化测试流程.md`(像素回归门禁)。 > **不重复登记**:页面级待办落地时按语义标识进 `docs/任务规划/登记簿.md`;本文只出规划与规格。 > **逐页细化**:每页显示哪些行、数据从哪来、要改哪些代码,见 `docs/UI设计/页面扩充详细方案.md`(本文的落地细化版)。 @@ -254,7 +255,7 @@ ui_page_ac / ui_page_yx / ui_page_setlist ... > **虚拟注入的价值**:当前送检等标准源(B10),而虚拟注入能让"改一个假值 → 看 UI 是否变 → 看 104 是否上送 → 看 SOE 是否记录"这条链在没有硬件激励的情况下闭环。它是**测试基础设施**,不是产品功能,建议放在厂家菜单并加明显标识(标题后缀"调试")。 > -> **代价**:菜单树从 42 节点扩到约 48~50 节点,`ui_menu_tree.txt` 改动会触发菜单 8 状态像素回归与 `task-table` 式索引文档同步。**这是需要拍板的项**——见 §8。 +> **代价**:菜单树从当时 42 节点扩到约 48~50 节点(节点数现值 43,2026-10-29 录波道加「录波回放」叶),`ui_menu_tree.txt` 改动会触发菜单 8 状态像素回归与 `task-table` 式索引文档同步。**这是需要拍板的项**——见 §8。 --- @@ -262,7 +263,7 @@ ui_page_ac / ui_page_yx / ui_page_setlist ... | # | 问题 | 影响 | |---|---|---| -| 1 | 菜单是否扩到 48~50 节点(加厂家设置支)? | 42 是产品需求里的硬数字;扩了要改 `产品需求设计文档.md` §8.2 与菜单像素回归 | +| 1 | 菜单是否扩到 48~50 节点(加厂家设置支)? | 节点数以 `产品需求设计文档.md` §8.2 为准(2026-10-29 现值 43,此前 42);扩了要改该节与菜单像素回归 | | 2 | **直流量页怎么办?**(V0.1 没有直流采样通道)三选一:改成"温度 + 备用"页 / 从菜单删掉 / 保留空页显示 `--` | 决定行表与菜单节点数 | | 3 | 定值名用全称还是缩写?(字体子集大小 vs 可读性) | 40 条名 × 平均字数,直接影响字库与 `font-charset` | | 4 | 事故/操作/告警/启动/遥控 5 类记录,是否本轮就要? | 每类 = 一个写入方 + 一个 `syslog_id_t` + 一个页面,是本规划里最大的一块后端工作 | diff --git a/docs/产品需求/产品需求设计文档.md b/docs/产品需求/产品需求设计文档.md index 2b1090c3..8ff0f2b3 100644 --- a/docs/产品需求/产品需求设计文档.md +++ b/docs/产品需求/产品需求设计文档.md @@ -660,7 +660,7 @@ sequenceDiagram **FR-UI-2 菜单与内容页** - 需求:42 节点层级菜单(最大 4 级)+ 内容页(装置信息/交流量/直流量等)。 -- 实现:三层拆分(数据层 `ui_menu_tab` 生成物 / 逻辑层 `ui_menu` 17 B 导航状态 / 显示层 `ui_menu_view` 级联框);内容页框架 + 页面插件描述符。 +- 实现:三层拆分(数据层 `ui_menu_tab` 生成物 / 逻辑层 `ui_menu` 17 B 导航状态 / 显示层 `ui_menu_view` 级联框);内容页框架 + 页面插件描述符。2026-10-29 实到 43 节点(新增「录波回放」叶,需求原文的 42 是 V0.1 立项数)。 - 判据:`UI menu init: OK`;菜单像素回归全 PASS。 **FR-UI-3 待机屏** @@ -1802,10 +1802,13 @@ pre-commit run --all-files | 刷新 | `uiTask` 10 ms 循环 | | 绘制缓冲 | I1 draw buffer 需 +8 B palette 头 | -### 8.2 菜单树(42 节点) +### 8.2 菜单树(43 节点) ![级联框总览](../images/级联框总览.svg) +> 该图仍是 42 节点时代所画(2026-10-29 新增的「录波回放」叶未进图):`tools/arch_diagram/` 里没有这张图的生成器, +> 重画要另开一笔并走 `generated-fresh`;**本节下面的文字与代码生成物是真值**。 + **规模**:菜单节点 **9** 个(含根)+ 叶子节点 **34** 个 = **43** 节点;最大深度 **4 级(主界面 / 一级 / 二级 / 三级菜单)**。 **绑定现状**(2026-10-29 复扫):**已绑内容页 30 叶 / 未绑 4 叶**(页面注册表 `UI_PAGE_MAP_COUNT` = 30, 与已绑叶数一致);未绑 4 叶 = 无开入硬件 3 + SNTP 1(取舍与重开条件见 @@ -1828,10 +1831,10 @@ docs/任务规划/UI补齐.md §5);原「当前执行 6 项」(记录 5 ├─ 装置维护 ▶(时钟设置 / 强制复归 / 手动录波 / 清除记录) ├─ 通讯参数 ▶(通讯设置 / 网口设置 / SNTP设置) └─ 记录查询 ▶(SOE记录 / 事故记录 / 操作记录 / 保护告警 / 保护启动 / - 遥控记录 / 自检记录 / 运行记录 / 运行报告) + 遥控记录 / 自检记录 / 运行记录 / 运行报告 / 录波回放) ``` -**三层代码架构**:数据层 `ui_menu_tab`(生成物,`g_menu_tab[42]` 扁平父子索引表,≈0.7 KB Flash) +**三层代码架构**:数据层 `ui_menu_tab`(生成物,`g_menu_tab[43]` 扁平父子索引表,≈0.7 KB Flash) + 逻辑层 `ui_menu`(唯一 **17 B** 导航状态)+ 显示层 `ui_menu_view`(持全部 LVGL 对象,零导航状态)。 **导航模型**:树形逐级进入——`ENTER`/`→` 进入子页,`ESC`/`←` 返回上一级;页面 = 对表的一次 diff --git a/docs/代码规范/pre-commit使用说明.md b/docs/代码规范/pre-commit使用说明.md index eaac9609..4a166c2f 100644 --- a/docs/代码规范/pre-commit使用说明.md +++ b/docs/代码规范/pre-commit使用说明.md @@ -32,7 +32,7 @@ git commit └─ manual(pre-commit --hook-stage manual;不随 git commit 跑) ├─ cppcheck 全量 C 静态分析(自有代码) ├─ test-cppcheck test/ 侧静态分析 - └─ test-coverage 主机单测覆盖率总闸(41 套 + 回退台账) + └─ test-coverage 主机单测覆盖率总闸(44 套 + 回退台账) ``` **阶段**:`pre-commit` 阶段拦截代码问题、`commit-msg` 阶段拦截格式问题,任一失败 → commit 中止;另有 `manual` 阶段(`stages: [manual]` 的慢钩子)**不随 `git commit` 跑**,由远端 CI 与 `scripts/ci_local.py` 的 manual 段承担(见下)。一次 `git commit` 实跑 **48** 根 = `pre-commit` 阶段 47 根 + `gitlint` 1 根。 @@ -332,7 +332,7 @@ $env:PATH = [Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [Env | 文件 | 位置 | 说明 | |---|---|---| -| `.pre-commit-config.yaml` | 仓库根 | 钩子配置(**52 个钩子**声明数,一次 `git commit` 实跑 49 根;口径 = YAML 解析后的 hook 条目数,自查命令 → `docs/门禁说明/README.md` §2。声明数与一次提交的实跑数分列,勿混写;计数改动的完整台账只记在 AGENTS.md 提交门禁节与本目录 README §2,本行不重述)。逐项详解在 `docs/门禁说明/`(索引 `docs/门禁说明/README.md`;主机侧单测 **22 根**合一篇,含各套件来历与「19 套无专属钩子」台账 `docs/门禁说明/host-unit-tests.md` §6;UI 侧另有 `font-charset` 与 `ui-golden` 两篇) | +| `.pre-commit-config.yaml` | 仓库根 | 钩子配置(**52 个钩子**声明数,一次 `git commit` 实跑 49 根;口径 = YAML 解析后的 hook 条目数,自查命令 → `docs/门禁说明/README.md` §2。声明数与一次提交的实跑数分列,勿混写;计数改动的完整台账只记在 AGENTS.md 提交门禁节与本目录 README §2,本行不重述)。逐项详解在 `docs/门禁说明/`(索引 `docs/门禁说明/README.md`;主机侧单测 **22 根**合一篇,含各套件来历与「22 套无专属钩子」台账(现值 2026-10-29) `docs/门禁说明/host-unit-tests.md` §6;UI 侧另有 `font-charset` 与 `ui-golden` 两篇) | | `.gitlint` | 仓库根 | gitlint 规则(宽松,兼容中文) | | `.cubemx_survival_baseline.json` | 仓库根 | CubeMX 易失定制基线(哈希;`--write` 重建) | | `scripts/check_mojibake.py` | scripts/ | U+FFFD 检测脚本 | diff --git a/docs/测试说明/主机单测覆盖率口径.md b/docs/测试说明/主机单测覆盖率口径.md index 11aa59c2..aaaa34af 100644 --- a/docs/测试说明/主机单测覆盖率口径.md +++ b/docs/测试说明/主机单测覆盖率口径.md @@ -56,7 +56,7 @@ ## 3. 怎么跑 ```powershell -# 全量:33 套单测逐套"编译(带 PDB)→ 插桩运行 → 解析 Cobertura",最后并集 + 比基线 +# 全量:44 套单测逐套"编译(带 PDB)→ 插桩运行 → 解析 Cobertura",最后并集 + 比基线 & '.venv\Scripts\python.exe' scripts/check_test_coverage.py # 只跑一套(调试用;子集会跳过"在测文件消失"类回退判定,见 §5) @@ -77,7 +77,9 @@ 退出码:`0` 全过且无回退 / `1` 有单测失败或覆盖率回退 / `2` 环境错误(无 cl、无 mscoverage、 编译失败、XML 缺失)/ `3` 用法错误。全量实测约 **67 s**(33 套 × 编译 + 插桩运行; 2026-10-25 实测;16 套时约 25 s、28 套时约 45 s,套件数涨了耗时就涨,别拿旧数当预算)。最新一次实测: -2026-10-28 = 41 套 **~105 s**(该钩子同日移入 `stages: [manual]`,由远端 CI 与 `ci_local.py` 的 manual 段强制)。 +2026-10-28 = 41 套 **~105 s**(该钩子同日移入 `stages: [manual]`,由远端 CI 与 `ci_local.py` 的 manual 段强制); +套件数现值 **44 套**(2026-10-29 实测,台账见 `docs/门禁说明/host-unit-tests.md` §6 第 4 条),**44 套这一轮的总闸耗时未重测**, +上面那三个耗时都是当时值,别当现值预算。 套件数的现值与逐个来历见 `docs/门禁说明/host-unit-tests.md` §6 第 4 条。 中间产物(exe / PDB / Cobertura XML)一律落 `%TEMP%\dtu_host\<工作树哈希>\coverage\<模块>` @@ -154,7 +156,7 @@ 宁可红,也不给可疑的数字。 6. **只覆盖跑到的进程树。** 一次 `collect` 只看一次运行;没跑到的套件不进分母 (全量跑时以"模块未跑到"判 REGRESS),子集运行明确降级(§5)。 -7. **头文件里的代码归 `.h`。** 统计面含 `.h`(静态内联与被展开的行落在头文件上);当前 33 套 +7. **头文件里的代码归 `.h`。** 统计面含 `.h`(静态内联与被展开的行落在头文件上);当前 44 套 几乎没触及 `.h`,表里看不到它们——不要以为头文件"天生 100%"。 8. **依赖 `Microsoft.CodeCoverage.Console.exe`。** 探测顺序:`DTU_MS_COVERAGE`(设了就必须 有效、**不静默回退**)→ 各 VS 安装根的 `Common7\IDE\Extensions\Microsoft\CodeCoverage.Console` → diff --git a/docs/编号规范.md b/docs/编号规范.md index f35df347..8b91180d 100644 --- a/docs/编号规范.md +++ b/docs/编号规范.md @@ -85,7 +85,7 @@ > - ❌「不做 = 维持原状 = 还能发」——**错**。新用例一律用**语义化测试函数名 + 文档用例表的行名**(§3.3), > 不再配 TC 号;指认**既有**用例仍可引用现存号(旧号只读,不是许可新配)。 > - ❌「停发 = 这族要废了,顺手清掉、把号腾出来复用」——**也错**。现存号与它在 `scripts/archive/id_gate/id_registry.json` -> 里的登记,是 `check_test_traceability.py` 与 41 套 `run_unit_test.py` **按字面量读的追溯把手**, +> 里的登记,是 `check_test_traceability.py` 与 44 套 `run_unit_test.py` **按字面量读的追溯把手**, > 清它等于换机制(`编号治理历史.md` §9.1);号不回收、不复用是 §1 的硬规矩。 > - ❌「registry 里 TC 族还带着活游标,所以门禁允许我发」——**不成立:这条缺口已于 2026-10-27 同日关掉**。 > 37 个 TC 类族现在**全部** `frozen: true`,其中 **34 个 `TC-HOST-*` 子族的 `cursor` 冻结在裁定日该套件存量 diff --git a/docs/门禁说明/README.md b/docs/门禁说明/README.md index 1e16e96b..19d72abb 100644 --- a/docs/门禁说明/README.md +++ b/docs/门禁说明/README.md @@ -75,7 +75,7 @@ | [`setview-check.md`](setview-check.md) | `setview-check` | 定值查看行表与 `prot_set` 枚举序 / `g_prot_set_meta` 上下限是否同一份口径 | `tools/setview_check/**`、`ui_w_setview.*` | 已写 | | (专篇待补,档位与限度正文在 [`../UI设计/UI_自动化测试流程.md`](../UI设计/UI_自动化测试流程.md) §7.3) | `uinav-selftest` | 板端 UI 导航器认屏快检(黄金参考帧分类;完整闭环档=合入前/上板前手工跑,快检不互替) | `scripts/board_ui_nav.py`、`simulator/tests/ref/*.bin` | 指针 | | [`host-unit-tests.md`](host-unit-tests.md) | 22 个 `*-unit-test` | 主机侧单测(同一机制 22 个实例):逻辑层在无硬件的 PC 上编译并跑断言 | 各自被测模块与其测试目录 | 已写 | -| 详解在 [`../测试说明/主机单测覆盖率口径.md`](../测试说明/主机单测覆盖率口径.md)(口径 / 分母 / 基线 / 盲区都在那篇,本目录不重复) | `test-coverage` | **覆盖率总闸**:跑全部 41 套主机单测 + 已覆盖行只增不减的回退台账 | `App/`、`Drivers/BSP/`、`test/host/`、脚本与基线 | 已写(在 `docs/测试说明/`;`stages: [manual]`) | +| 详解在 [`../测试说明/主机单测覆盖率口径.md`](../测试说明/主机单测覆盖率口径.md)(口径 / 分母 / 基线 / 盲区都在那篇,本目录不重复) | `test-coverage` | **覆盖率总闸**:跑全部 44 套主机单测 + 已覆盖行只增不减的回退台账 | `App/`、`Drivers/BSP/`、`test/host/`、脚本与基线 | 已写(在 `docs/测试说明/`;`stages: [manual]`) | | [`trailing-whitespace.md`](trailing-whitespace.md) | `trailing-whitespace` | 行尾空白(改写型;CubeMX/Keil 生成文件豁免) | 全部 | 已写 | | [`end-of-file-fixer.md`](end-of-file-fixer.md) | `end-of-file-fixer` | 文件末尾必须恰好一个换行(改写型;同豁免) | 全部 | 已写 | | [`check-added-large-files.md`](check-added-large-files.md) | `check-added-large-files` | 别把大文件(日志/镜像/二进制)误提进仓库 | 全部 | 已写 | diff --git a/docs/门禁说明/host-unit-tests.md b/docs/门禁说明/host-unit-tests.md index add1161c..4c6c0665 100644 --- a/docs/门禁说明/host-unit-tests.md +++ b/docs/门禁说明/host-unit-tests.md @@ -88,30 +88,32 @@ 3. `include_dirs` 与源文件清单是**手写**的:新依赖一个目录就会假失败(见 §4 第 2 类),反过来, 漏了某个真实依赖可能让测试**编过却少编了一个翻译单元**(少测而不自知)。 4. **专属钩子不是唯一防线,而「没有专属钩子」也不等于「没被跑」**。2026-10-26 按 git 历史与实测重新 - 清点(2026-10-28 复点:下列数字已按复点值更新;上一版写「16 个模块有专属钩子、另有 12 套、总闸跑 28 套」——三个数都随 2026-10-25 的 + 清点(2026-10-28 复点、2026-10-29 录波三套入库后再复点:下列数字已按复点值更新;上一版写「16 个模块有专属钩子、另有 12 套、总闸跑 28 套」——三个数都随 2026-10-25 的 六根新钩子与后续新增套件过期了,正是本目录反复在防的"文档与机器面不同码"): - **22** 根专属钩子 = 有独立 `files:` 触发面、需要**秒级归因**的那个子集; - - `test/host/` 下带 `run_unit_test.py` 的套件实测 **41** 套(43 个目录,除 `common` 与 - `__pycache__`)⇒ **没有专属钩子的是 19 套**:`boot_stats`、`cfg_file_store_mcu`、`console_log`、 + - `test/host/` 下带 `run_unit_test.py` 的套件实测 **44** 套(46 个目录,除 `common` 与 + `__pycache__`;两个数分别来自 `check_test_coverage.py --list` 与 `ls -d test/host/*/`,2026-10-29 实测) + ⇒ **没有专属钩子的是 22 套**:`boot_stats`、`cfg_file_store_mcu`、`console_log`、 `ftp_cfg`、`iec104_serv`、`lftpd`、`lftpd_inet`、`lftpd_io`、`net_cfg`、`net_events`、 `net_listener`、`net_pending`、`net_select`、`net_socket`、`prot_set_store`、`soe_store`、 - `sys_log_mcu`、`task_prof`、`ui_sink`; - - 这 19 套每次 CI 与 `scripts/ci_local.py` 都由 `test-coverage` **总闸**真跑(该钩子带 `stages: [manual]`: + `sys_log_mcu`、`task_prof`、`ui_sink`、`wave_ring`、`wave_store`、`wave_trig`(末三套 2026-10-29 随 + 故障录波入库,按同一条 Lead 裁定不补专属钩子;22 = 44 套 − 22 根专属钩子,逐点数与两处机器源互证); + - 这 22 套每次 CI 与 `scripts/ci_local.py` 都由 `test-coverage` **总闸**真跑(该钩子带 `stages: [manual]`: `git commit` 阶段不跑它,这一根与 `cppcheck` / `test-cppcheck` 同属 CI 与 `ci_local.py` 的 manual 段); 发现规则是 `HOST_TEST_DIR.rglob("run_unit_test.py")`(与 `scripts/ci_local.py` 的主机侧纯逻辑单测同一口径), 退出码语义写死「0 = 全部单测通过且覆盖率相对基线没回退 / 1 = 有单测失败或覆盖率回退」⇒ - **失败照样拦(CI 与 `ci_local.py` 的 manual 段)**。差的只是归因粒度(要在总闸输出里认出是哪套)与耗时(41 套全量比单根慢得多); + **失败照样拦(CI 与 `ci_local.py` 的 manual 段)**。差的只是归因粒度(要在总闸输出里认出是哪套)与耗时(44 套全量比单根慢得多); - 要不要给某套补第 23 根,判据现成:pre-commit 4.x 没有 `always: true`,**该模块有没有自己的 `files:` 触发面**决定补了会不会真跑;只为凑数加一根,等于把「快归因面」和「全量把关」两件事混成 一件,还要让钩子计数再改一次; - - **裁定(2026-10-27,Lead):这批无专属钩子套件(裁定当时 18 套,2026-10-28 现值 19 套;含 `test/host/ui_sink`)不补专属钩子,差异按设计说明记录。** + - **裁定(2026-10-27,Lead):这批无专属钩子套件(裁定当时 18 套,2026-10-28 为 19 套,2026-10-29 现值 22 套;含 `test/host/ui_sink` 与新增的 `wave_ring` / `wave_store` / `wave_trig`)不补专属钩子,差异按设计说明记录。** 理由不是省事:22 根专属钩子存在的唯一意义是**秒级归因**;而 `ui_sink` 改动面小,它的失败面全部 落在 `test-coverage` 总闸的必跑路径里,坏了照样在 CI / `ci_local.py` 拦下。再加一根只是多一个必须「声明数 / 文档数 / 实跑数」三处同源同步的计数点——本仓 2026-10-26 到 27 一天之内就为 40 / 50 / 51 这三个数不同源 返工过两轮。**要读「这套跑没跑」,读总闸输出,不要来数钩子。** 两个方向的空头账一起堵住: ① 别把没接线的套件算进钩子账(原写「23 个 / 含 ui-sink」正是这么错的;2026-10-27 复核的准数是 - 22 根 / 40 套 / 18 套无专属钩子——第 41 套 `boot_stats` 当日 11:29 才入库,此后两数是 41 / 19,以本条上面的实测行为准);② 也别把总闸的绿读成「每套都被单独归因过」——它只保证 + 22 根 / 40 套 / 18 套无专属钩子(三个数都是当时值)——第 41 套 `boot_stats` 当日 11:29 才入库,此后两数当时是 41 / 19,现值 44 / 22 以本条上面的实测行为准);② 也别把总闸的绿读成「每套都被单独归因过」——它只保证 「全跑过 + 覆盖率没回退」。将来确实要给某套补专属钩子,先过上一条判据,并在**同一笔**里改 AGENTS 与门禁索引的计数(口径 = YAML 解析条目数,见 `docs/门禁说明/README.md` §2 首条)。 - 总闸的触发面是 `App/`、`Drivers/BSP/`、`test/host/`、覆盖率脚本与基线,**外加门禁自身** diff --git a/scripts/README.md b/scripts/README.md index 20f811a2..82b2122b 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -44,7 +44,7 @@ | `ci_local.py` | **本地跑与 CI 等价的门禁**(静态门禁 pre-commit 47 根 + **manual 段**(`cppcheck` / `test-cppcheck` / `test-coverage` 三根逐根 `--hook-stage manual`,默认跑)+ 提交信息门禁 + **体积门禁** + 主机侧纯逻辑单测):自动选用装了 pre-commit 的解释器;无 `.map` 时自适应跳过 `mem-numbers` / `mem-budget`(⚠ 那是 SKIP 不是通过,缺口由体积门禁兜住);退出码 0/1/2 与 CI 对齐。CI 定义见 `../.gitea/workflows/ci.yml`,覆盖边界见 `docs/测试说明/测试开发标准流程.md` §9 | | `check_size_gate.py` | **体积门禁的强制路径**:`.map` 只在本地有,钩子无它时自适应 SKIP,于是"没测=通过"。本脚本用**已提交的同码凭证** `scripts/size_attestation.json` 把这条判据变成无需 Keil 也能跑:`--stamp` 只在"`.map` 存在、比全部受管源码新、且未超 92%"时才盖章(已挂在 `ram_baseline.py --update` 里,无新增人工步骤);`--local` 供 ci_local 的体积门禁步;`--ci` 供 runner(同码性 + 按凭证数字复核红线,主干 push 拦红、其余事件 `--warn-only`);`--show` 排障。**口径与边界的唯一正文在本脚本模块 docstring** | | `size_attestation.json` | (被 `check_size_gate.py` 读写)体积同码凭证:受管源码指纹(`git ls-files` 范围内逐文件 sha256 聚合)+ 测量数字与占比 + `.map` 出处。改固件源码后不重测 → 指纹对不上 → 体积门禁与 CI 同时拦红 | -| `check_test_coverage.py` | 主机单测**覆盖率**(2026-10-23):mscoverage 动态插桩跑 `test/host/**` 全部 41 套,统计自研生产源码行/函数覆盖(并集)+ 与 `test_coverage_baseline.json` 比回退;`--module/--update-baseline/--md/--json/--list`;挂在 `test-coverage` 钩子上(`stages: [manual]`,**`git commit` 不跑**),入口 = 远端 CI「手动阶段门禁」步 / `scripts/ci_local.py` 的 manual 段(`--coverage` 旗标保留只为兼容旧命令;口径见 `docs/测试说明/主机单测覆盖率口径.md`) | +| `check_test_coverage.py` | 主机单测**覆盖率**(2026-10-23):mscoverage 动态插桩跑 `test/host/**` 全部 44 套,统计自研生产源码行/函数覆盖(并集)+ 与 `test_coverage_baseline.json` 比回退;`--module/--update-baseline/--md/--json/--list`;挂在 `test-coverage` 钩子上(`stages: [manual]`,**`git commit` 不跑**),入口 = 远端 CI「手动阶段门禁」步 / `scripts/ci_local.py` 的 manual 段(`--coverage` 旗标保留只为兼容旧命令;口径见 `docs/测试说明/主机单测覆盖率口径.md`) | | `ci_gitlint_range.ps1` | CI 侧**提交信息门禁**:逐条校验本次 push/PR 引入的提交(`--all-files` 跑不到 `commit-msg` stage,故必须单独守) | | `ci_find_python.ps1` | CI 侧 Python 探测(候选顺序须与 `ci_local.py` 同序同步,否则 CI 与本地判定会分叉) | | `ci_runner_bootstrap.ps1` | **本机 runner 运维脚本**(体检 `-Status` / 干跑 `-DryRun` / 部署 `-SetEnv -StartNow`):装"等 Gitea 就绪 + 崩溃自拉起"的看门狗 + 开机计划任务 + 机器级 `DTU_*` | diff --git a/scripts/board_ui_nav.py b/scripts/board_ui_nav.py index 61ad6357..c4bb7ae8 100644 --- a/scripts/board_ui_nav.py +++ b/scripts/board_ui_nav.py @@ -993,7 +993,7 @@ def selftest_board_frames(clf): def selftest_refs(clf): - """仓库黄金参考(60 张)逐帧分类自检:每张图的期望值 = 它的文件名。 + """仓库黄金参考(66 张)逐帧分类自检:每张图的期望值 = 它的文件名。 这一组是**语义正确的基线**(模拟器与板端同一份 UI 源码),用来把"分类器错了"与 "留档帧本来就是走歪的"分开——两类失败含义完全不同,混在一起就只能靠猜。 diff --git a/simulator/README.md b/simulator/README.md index b9674c28..066906ba 100644 --- a/simulator/README.md +++ b/simulator/README.md @@ -222,7 +222,7 @@ UI 改动的自动化门禁脚本(完整流程见 `docs/UI设计/UI_自动化 | 脚本 | 原理 | 失败输出 | |------|------|----------| | `check_menu_rows.py` | 键序注入(u/d/e/s)跑 8 个菜单状态,断言选中行白带、非选行黑底白字、框体边框、往返状态逐像素一致;MENU-STATE-00 池水位须等于脚本内 `MENU_POOL_BASELINE` | 未过断言清单 + 保留临时截图 | -| `check_content_pages.py` | ① 文本断言(解析字体字形,在截图 y 带内精确匹配期望串,不依赖 `ref/`)② **反向断言**(`ABSENT` 表:某串**不该**出现,如查看页/只读态不得出现「未标定」徽标与「保存」行——像素参考会被 `--update` 刷成"自证正确",反向断言不依赖参考图)③ **60 张**无头截图与 `tests/ref/shot_*.bin` **逐字节**比对(含 2026-10-26 新增的整定 9 态:进页 / 保存行 / 只读态 / 编辑态 / 越界 / 保存 OK / 落定后可读 / 二态回绕);另校验开页-关页往返池水位 = `POOL_AFTER_ROUNDTRIP` | 文本未命中项 / 反向断言被命中 / 差异像素数 + 双方 ink runs 对照 | +| `check_content_pages.py` | ① 文本断言(解析字体字形,在截图 y 带内精确匹配期望串,不依赖 `ref/`)② **反向断言**(`ABSENT` 表:某串**不该**出现,如查看页/只读态不得出现「未标定」徽标与「保存」行——像素参考会被 `--update` 刷成"自证正确",反向断言不依赖参考图)③ **66 张**无头截图与 `tests/ref/shot_*.bin` **逐字节**比对(含 2026-10-26 新增的整定 9 态:进页 / 保存行 / 只读态 / 编辑态 / 越界 / 保存 OK / 落定后可读 / 二态回绕);另校验开页-关页往返池水位 = `POOL_AFTER_ROUNDTRIP` | 文本未命中项 / 反向断言被命中 / 差异像素数 + 双方 ink runs 对照 | | `check_remo_sink.py` | 启动**窗口**模拟器(非无头,`CREATE_NEW_PROCESS_GROUP` 防管道阻塞),经 TCP 7003 发 LCDMEM 取 1bpp 帧;断言首帧=LCDMEM(无 INIT 污染)/位图 3200 字节/非平凡;复用 `i1_to_png` 出目检 PNG;与无头参考(`lvgl_sim --screenshot`)逐位比对 <2% 判 MATCH。守护"INIT 污染 / 快照卡死"两个历史缺陷 | 各判据失败原因 + `sim_err.txt` 尾部 + 保留 PNG(默认不删,`--clean` 才删) | - **黄金参考与目检 PNG**(`ref/*.bin` **入库**、`ref/*.png` 本地派生不入库):换机或想目检时—— diff --git a/simulator/tests/check_content_pages.py b/simulator/tests/check_content_pages.py index 0f88f0bf..de68d980 100644 --- a/simulator/tests/check_content_pages.py +++ b/simulator/tests/check_content_pages.py @@ -149,7 +149,7 @@ PAGES = [ # 计数范围/复位原因/主站接入 + 范围标签"本次开机",第 2 页钉 104 另外三枚(发送帧数 # 128 / 发送失败 0 / 接收字节 4096)与页脚真实页码 2/2。四枚假数据值互不相同是刻意的: # 行错位/串行只有"像素 + 逐行文本断言"两把尺子同时才照得出来(Lead 裁定 2026-10-28, - # 因此用例是 53+7=60 张,不是 53+6=59)。 + # 因此当时用例是 53+7=60 张,不是 53+6=59;现集合 66 张,见 PAGES 表)。 ("runreport", "dddddde dddddddd e"), ("runreport_p2", "dddddde dddddddd e dddddddd"), # 页内再 8 下 → 第 2 页(选中落 r0=发送帧数) # ── 故障录波 UI 道(2026-10-29):手动录波三态 + 回放三态(真实波形/空态/坏段)── @@ -880,7 +880,7 @@ EXTRA = { # sim_oper.log 已经攒了 3 条(192 B = 3×64 B)→ "操作记录"页读到的是真记录, # 空态判据(第 0 行"无记录")根本拍不到。**判据本身没错,错在跑它的环境被前面的写路径 # 污染了**,所以给这 5 页各开一个空目录 = 现场"这些日志文件还不存在"的确定态; -# 其余 55 页环境一字不变(对当时存量 53 张黄金零影响,实测逐字节零漂移;当前集合 60 张)。 +# 其余 55 页环境一字不变(对当时存量 53 张黄金零影响,实测逐字节零漂移;当时集合 60 张,现集合 66 张)。 FRESH_CWD = ("accident", "oper", "alarm", "start", "yk") diff --git a/test/host/sys_log_mcu/sys_log_mcu_test.c b/test/host/sys_log_mcu/sys_log_mcu_test.c index 10ef4f76..4568d644 100644 --- a/test/host/sys_log_mcu/sys_log_mcu_test.c +++ b/test/host/sys_log_mcu/sys_log_mcu_test.c @@ -193,7 +193,7 @@ * (s_entry_fill / s_put / syslog_post_quiet / syslog_post_dropped)与 26 行 * 可执行代码**全部**有断言跑到:覆盖行 +26、函数 +4,未覆盖仍是同一批 3 行老账 * (一条死分支 + sys_log_evidence 里 snprintf<=0),**一条没涨**。 - * ⚠ 全量门禁(41 套并集 + 基线只增不减)由提交前那一次统一跑,本行只是自量记录。 + * ⚠ 全量门禁(44 套并集 + 基线只增不减)由提交前那一次统一跑,本行只是自量记录。 * L 族变异实测(2026-10-26,三处同时注入后逐字节还原): * ① s_paths[] 里 accid 与 alarm 两格**对调**(就是"表序错"那个静默错法); * ② 给 quiet 的丢弃分支补一条 `DBG_ERROR`(= 悄悄退化成 syslog_post);