diff --git a/.dsh/skills/lvgl-simulator/SKILL.md b/.dsh/skills/lvgl-simulator/SKILL.md index 9bc70595..f40734fa 100644 --- a/.dsh/skills/lvgl-simulator/SKILL.md +++ b/.dsh/skills/lvgl-simulator/SKILL.md @@ -33,9 +33,9 @@ PC 上编译运行与 MCU **同一份** UI 源码(CMake + MSVC + SDL2,x64, ## UI 改动回归 → 用 `ui-verify` skill(勿在此零散手跑) -5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 53 页黄金参考 + 往返池)→ `@build` → `coding-standard`。 +5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 60 张黄金参考 + 往返池)→ `@build` → `coding-standard`。 RemoDispBus 推流回归单独跑:`.venv\Scripts\python.exe simulator/tests/check_remo_sink.py`。 -黄金参考 `tests/ref/` 是**本地生成物不入库**:换机后先 `check_content_pages.py --update` 生成,再 `regen_ref_png.py` 出目检 PNG。 +黄金参考 `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/.pre-commit-config.yaml b/.pre-commit-config.yaml index 525442a3..8ceb891b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -641,7 +641,7 @@ repos: # 疑似 MSBuild 节点争用;此后 3 次干净树连跑均绿。判据:单次失败先复跑一次再看, # 连续两次失败才当真(不许直接 --no-verify 绕过)。 - id: ui-golden - name: UI 像素回归(菜单 8 状态 + 内容页 60 页 黄金参考) + name: UI 像素回归(菜单 8 状态 + 内容页 60 张 黄金参考) 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$) @@ -677,6 +677,9 @@ repos: # 判据:看 git diff 的新增行里有没有**未登记形态**的「字母+数字」标识 # (自造前缀直接 FAIL;已停发族只 WARN);白名单在脚本里,按实测普查枚举、几乎不增长。 # 自检:`python scripts/check_ids_new.py --self-test`;范围自查用 `--range A..B`。 + # ⚠ 计数口径(2026-10-28,一增一减写成同日的两个阶段,净零):本条落地使 YAML 解析条目 + # 51 → 52,同日摘掉退役的旧语料审计钩子后 52 → **51**。口径与自查命令、「声明 / 文档 / + # 实跑」三数同源核对 → docs/门禁说明/README.md §2 首条(本文件注释与 AGENTS 必须同批改)。 - id: id-check-new name: 编号门禁·新增行(零语料,拦截新造编号) entry: python scripts/check_ids_new.py diff --git a/docs/UI设计/LVGL模拟器部署与使用说明.md b/docs/UI设计/LVGL模拟器部署与使用说明.md index c973768d..114819dd 100644 --- a/docs/UI设计/LVGL模拟器部署与使用说明.md +++ b/docs/UI设计/LVGL模拟器部署与使用说明.md @@ -186,7 +186,7 @@ MCU 端 `Drivers/BSP/dbg_log.h` 会引用 `sys_clock.h`(RTC 时间戳),PC - `LV_USE_THEME_MONO` 是否 = 1 且在 `sim_port_init` 中调了 `lv_theme_mono_init`(1bpp 无主题 = 黑底黑字) - 主循环是否调用 `lv_refr_now(NULL)` 强制首帧输出(PARTIAL 模式下保证) - 是否重复设置 tick:SDL 驱动内部 `lv_tick_set_cb(SDL_GetTicks)`,**不要**再 `SDL_AddTimer` + `lv_tick_inc` -2. **中文字体缺字**:`ui_font_cn_12` 只覆盖 83 个汉字(ASCII + 指定符号集),完整菜单需要 186 个唯一汉字。**当前菜单部分文字会显示为空白**,需重新生成字体(见下文 6.3)。 +2. **中文字体缺字**:`ui_font_cn_12` 是子集字体(当前 218 汉字/符号 + ASCII 0x20~0x7E,共 313 字形;2026-10-28 实测)。**缺字才会显示为空白**——新增汉字必须重生成字体(见下文 6.3)。早期只有 83 字、当时菜单需要 186 字,那是 2026-09-03 的历史快照,不是现状。 3. **`LV_SDL_RENDER_MODE` 必须为 `PARTIAL`**:1bpp + SDL 若非 PARTIAL,`lv_sdl_window.c` 会 `#error` 编译失败。 ### 6.3 字体生成(如需补全中文字符) @@ -234,7 +234,7 @@ A: `LV_CONF_DIR` 变量未在 `add_subdirectory(lvgl)` 前定义,检查 `simul A: 按 6.2 黑屏排查顺序检查:mono 主题 → `lv_refr_now` → tick 不重复。 ### Q4: 菜单某些中文显示空白? -A: 中文字体缺字(覆盖 83 字,需要 186 字),见 6.3 重新生成字体。 +A: 子集字体未含该字(当前字符集 218 汉字/符号 + ASCII 0x20~0x7E,共 313 字形;2026-10-28 实测),见 6.3 重新生成字体。 ### Q5: 修改了 UI 代码但模拟器没变化? A: 确认修改的是 `Drivers/BSP/UI/ui_menu.c` 而非 `simulator/` 内的副本(本工程无副本);然后重新 `cmake --build`。 diff --git a/docs/UI设计/UI_界面制作流程.md b/docs/UI设计/UI_界面制作流程.md index 26bd097b..5682c7dc 100644 --- a/docs/UI设计/UI_界面制作流程.md +++ b/docs/UI设计/UI_界面制作流程.md @@ -414,3 +414,4 @@ P4 [ ] UI_设计架构.md 变更记录更新 | 2026-09-14 | 运行记录/自检记录页 + 待机屏落地后回填:C3 字符集 111→122 字(217 字形,补输/入/错/误/上/电/重/芯/片/异/常——**故障描述串在非 UI 源码中 extract_cn 扫不到,须手动补字**);新文案走 P4 字体重生成 + `check_content_pages.py` pwc 态参考更新(密码错误提示);池基线 4784→5440 / 往返 4864→5520(+656B = 待机模块常驻屏+容器,见两测试脚本头部注记) | | 2026-10-25 | 字库扩充(页面扩充规划预置字):C3 字符集 122→179 字(217→274 字形;上次入表后 2026-10-15 字体瘦身实测 119 字/214 字形未回填本表,本次一并校正)。+60 字取自 `docs/UI设计/页面补齐规划.md`、`docs/UI设计/页面扩充详细方案.md` 的页面显示串(段/投/延/零/频/荷/闸/跳/低/过/Ⅰ/Ⅱ/Ⅲ 等 + 年/月/分/秒/分合/已请求/确认/取消/越界用字);源码未用期间 font-charset 记 [WARN] 属预期,批 2 页面接线后自然转"在用"。顺带修复 `make_font.py` 两处缺陷:补回字形 cmap 条目被宽稀疏 range 吞掉(Ⅰ 下探起点 0x2160 → ▶ 全变方框,A/B 同树实测)与 `verify_coverage()` 非首命中判定;菜单 8 状态 + 内容页 11 页黄金参考逐字节不变;ROM +1,528 B、RAM 0。详见 `docs/UI设计/UI_中文字体生成与配置.md` §4 与文末事故记录 | | 2026-10-28 | UI 补齐批接线(单写者):记录 5 页 + 运行报告六叶在 `ui_menu_tree.txt` 加 ` #page=` 并重跑 `tools/menu_gen/gen_menu.py`(绑定叶 22→28、`UI_PAGE_MAP_COUNT` 22→28,节点 42 / 叶 33 不变)、6 个页面 `.c` 双端注册;C3 字库链重跑 185→218 字(280→313 字形,新增 33 字取自六页新文案(含运行报告补「计数可信」行用的「可」字);本行旧数 183/278 系 2026-10-26 之后未回填,改前实测 185 字/280 字形);`check_content_pages.py` 内容页黄金 53→59 页;ROM/RAM 增量待 Lead `@build` 后按 `.map` 实测回填 | +| 2026-10-28 | **复扫认错 + 当前态改报「张」**:上一行的「内容页黄金 53→59」按**页面个数**口径写,与本仓判据本体不同源——判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 **60** = `check_content_pages.py` 的 `PAGES` **60** 项(两源一致、id 逐一对齐),53 → 60 = **+7 张**(记录 5 页 + 运行报告 2 张:超屏那一页的第 2 屏单独拍一张)。上一行原文**冻结不回改**;本文件当前态此后一律报「60 张黄金参考」,「页」只指内容页/菜单屏本身。全仓口径更正见 docs/门禁说明/ui-golden.md 同日行。 | diff --git a/docs/UI设计/UI_自动化测试流程.md b/docs/UI设计/UI_自动化测试流程.md index d18785ec..64d0b02f 100644 --- a/docs/UI设计/UI_自动化测试流程.md +++ b/docs/UI设计/UI_自动化测试流程.md @@ -26,7 +26,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 | 步 | 脚本/命令 | 门禁内容 | 预期输出 | |---|---|---|---| | 1 | `simulator/tests/check_menu_rows.py` | 菜单 8 状态:选中行白带 / 非选行黑底白字 / 框体 1px 边框 / 往返像素一致(MENU-STATE-03==MENU-STATE-01、MENU-STATE-06==MENU-STATE-01、MENU-STATE-07==MENU-STATE-02)+ MENU-STATE-00 池基线 | `PASS: 8 状态全部断言通过(8 图,池基线 5368 B)` | -| 2 | `simulator/tests/check_content_pages.py` | ① 文本级断言(字形模板匹配,正向 425 条 + 反向 36 条,独立于 `ref/`)→ ② 60 页截图与 `ref/` 黄金参考**逐字节**比对 + 开页-关页往返池水位 | `PASS: 60 内容页 + 往返池水位全部通过`(文本断言先于像素比对,失败即中止) | +| 2 | `simulator/tests/check_content_pages.py` | ① 文本级断言(字形模板匹配,正向 425 条 + 反向 36 条,独立于 `ref/`)→ ② 60 张截图与 `ref/` 黄金参考**逐字节**比对 + 开页-关页往返池水位 | `PASS: 60 内容页 + 往返池水位全部通过`(文本断言先于像素比对,失败即中止) | | 3 | `keil-build` skill(`keil_build.py`) | MCU 端编译(UI 源码双端共用,改一端必须过另一端) | 退出码 0 = BUILD_OK | | 4 | `scripts/check_coding_standard.py` | 代码规范 V1.0 机械检查 | PASS | @@ -37,8 +37,8 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 > **先于**像素比对运行,失败即中止(不误刷参考)。文本断言覆盖确定文案,像素参考仍负责几何/样式/ > 光标反显等其余视觉。规模与覆盖面(2026-10-26 实跑 `check_content_pages.py` 取数):文本断言 **310 条**、覆盖当时 40 页中的 > **37 页**——`pwd` / `pwe` / `pwc` 三张密码态只进像素比对、未登记文本断言,改这几页的文案没有文本层兜底。 -> 2026-10-28 再取数(接线道,`PAGES` 60 项时实测):正向 **425 条** / 反向 **36 条**、覆盖 60 页中的 -> **57 页**(仍缺那三张密码态)。更正留痕:上一句登记的是「2026-10-26 实跑取数 310 条」,而当时脚本 +> 2026-10-28 再取数(接线道,`PAGES` 60 项时实测):正向 **425 条** / 反向 **36 条**、覆盖 60 张中的 +> **57 张**(仍缺那三张密码态)。更正留痕:上一句登记的是「2026-10-26 实跑取数 310 条」,而当时脚本 > 的真值是**正向 380 / 反向 29**——历史行按当时冻结不改数,差值只在这里说明,避免下一个人再抄一遍旧数。 **退出码约定**:两个脚本均为 0=PASS / 1=断言失败 / 2=环境错误(exe 缺失、参考缺失)。 @@ -52,7 +52,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 | `POOL_AFTER_ROUNDTRIP` | check_content_pages.py | **5448 B** | 开页-关页往返后水位 = 基线 + 一次性残留 80 B | > 水位历史:4784 →(2026-09-14 待机模块常驻 +656)5440 / 5520 →(2026-10-11 裁剪 LVGL 控件类 −72)5368 / 5448。 -> 后一次漂移已核实为**有意且更优**:像素逐字节未变(**裁剪当时黄金参考只有 11 页**,在裁剪**前**生成、裁剪后全部一致;该集合现已扩至 60 页), +> 后一次漂移已核实为**有意且更优**:像素逐字节未变(**裁剪当时黄金参考只有 11 页**,在裁剪**前**生成、裁剪后全部一致;该集合现已扩至 60 张), > 且一次性残留仍是 80 B(5448 − 5368)→ 泄漏特征未变,只是 `lv_theme_apply` 不再为已不存在的控件类登记样式条目。 **漂移处理**:菜单/页面结构或样式变更导致门禁失败时,先确认新值合理(残留分解见 @@ -61,7 +61,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警 ## 四、黄金参考(入库)与目检 PNG(本地产物) -`simulator/tests/ref/*.bin`(像素黄金参考,60 页 × 3200 B)**随仓库入库**;派生的 `.png` +`simulator/tests/ref/*.bin`(像素黄金参考,60 张 × 3200 B)**随仓库入库**;派生的 `.png` 目检图仍是本地产物(`.gitignore`)。入库的理由:比对是**逐字节**的,基线不入库时任何新克隆 或新工作树跑 `ui-golden` 都必然 FAIL(缺基准),这条门禁就只在"有老副本的机器"上真实存在。 可复现性实测(2026-10-22):两份各自独立构建的 `lvgl_sim`(不同构建目录、相隔 11 天的代码) diff --git a/docs/UI设计/UI_设计架构.md b/docs/UI设计/UI_设计架构.md index cd24debf..0703fb3b 100644 --- a/docs/UI设计/UI_设计架构.md +++ b/docs/UI设计/UI_设计架构.md @@ -310,3 +310,4 @@ ENTER/→ 进子框: | 2026-10-25 | **F3 推流心跳边界(§5.8)**:`ui_net_sink_set_beat_cb()` 回调注入,accept/recv_nb/send/close/重建三件套返回处喂 UI 心跳(2026-10-25 大窗 bit4 8 连击实证:单轮多慢往返之和串成 800ms 缺席);往返语义不改(上界归 E-27/E-28);未注册 = 零行为变化(模拟器/黄金回归形态);新增 `test/host/ui_sink/` 主机套件 17 断言 | | 2026-10-25 | 字库扩充(页面扩充规划预置字 +60,含罗马数字 Ⅰ/Ⅱ/Ⅲ):默认字体升至 179 字符/274 字形,像素基线不变,ROM +1,528 B、RAM 0;修复生成器 `make_font.py` 的 cmap 插入顺序缺陷——LVGL `get_glyph_dsc_id()` 线性扫描首个包含即返回,补回字形(▶)单码位条目被下探后的宽稀疏 range 吞掉即全渲染方框,现按首命中语义插位/守卫/校验(详见 `docs/UI设计/UI_中文字体生成与配置.md` 文末事故记录) | | 2026-10-28 | UI 补齐批接线(单写者):事故/操作/保护告警/保护启动/遥控/运行报告六叶绑定内容页(生成物 `UI_PAGE_MAP_COUNT` 22→28,节点 42 / 叶 33 不变)+ 6 个页面 `.c` 双端注册;默认字体 185 字符/280 字形 → 218 字符/313 字形(+33 字,§1 概况表"默认字体"行同步,旧数 183/278 系未回填);内容页像素黄金参考 53 → 59 页;ROM/RAM 增量待 `@build` 后按 `.map` 实测回填 | +| 2026-10-28 | **全仓计数复扫(三数双源互证 + 口径更正)**:① `simulator/tests/ref/` 入库 `shot_*.bin` 实数 **60** = `check_content_pages.py` 的 `PAGES` 条目数 **60**(两源一致;其中 2 张是分页页多拍的第 2 页,所以**张数比页面个数多**。上一行「53 → 59 页」按**页面个数**口径、同日 `docs/门禁说明/ui-golden.md`「53 → 60」按**入库张数**口径——两个数各自成立,但**不许互相冒充**;本仓判据本体 = 入库张数,当前态一律报 **60 张**。② 已绑内容页 **28 叶**(生成物 `UI_PAGE_MAP_COUNT` = `ui_menu_tree.txt` 剔掉文件头注释示例行后的 `#page=` 实数 28;不剔是 29)/ 未绑 **5 叶** = 遥信类型 + 遥信防抖 + 双点遥信 + 手动录波 + SNTP 设置,与分类表逐项闭合。③ 中文字体 **218 唯一字 / 313 字形**:`ui_cn_chars.txt` 唯一码点 218(ASCII 区 **0** 个,ASCII 由生成器子集参数另打进去、不计入字数);`ui_font_cn_12.c` 的 `.bitmap_index` 条目 **314 条含 notdef** → 字形 = 314 − 1 = 313 = 218 + ASCII 95。④ 本次另笔补的漏改当前态:运行与自检记录设计说明 §2.1(122 字→218)、LVGL模拟器部署与使用说明 §6.2 与 Q4(83 / 186→218 / 313)、lvgl-simulator skill 正文(53 页→60 页);页面扩充详细方案 §1.1 的「已绑 6 叶」按立项快照冻结、只加现值指针 | diff --git a/docs/UI设计/mockups/01_主界面_说明.md b/docs/UI设计/mockups/01_主界面_说明.md index bf541673..37737e70 100644 --- a/docs/UI设计/mockups/01_主界面_说明.md +++ b/docs/UI设计/mockups/01_主界面_说明.md @@ -32,7 +32,7 @@ 说明:选中行(第 1 行)无 ▶ 箭头;其余行 ▶ 紧跟在 4 字文本后(非右对齐)。行内文字左对齐、左留白 8px。 -## 三、字符集核对(对照现有 ui_font_cn_12:78 汉字 + ASCII 0x20~0x7E) +## 三、字符集核对(对照当时(2026-09-03)的 ui_font_cn_12:78 汉字 + ASCII 0x20~0x7E;本 mockup 的核对结论只对该时点成立,现值 = 218 汉字/符号 + ASCII,共 313 字形,见 docs/UI设计/UI_中文字体生成与配置.md §7) - **菜单 7 项**共 22 个唯一汉字:命中 21(装 置 信 息 实 时 数 据 参 定 值 遥 设 维 护 通 讯 记 录 查 询),**新增 1(三)**。 - **状态行**"当前模式 : 无模式":**新增 5(当 前 模 式 无)** + **全角冒号 :(U+FF1A,新增)**;空格为 ASCII(已在字库)。 diff --git a/docs/UI设计/内容页架构.md b/docs/UI设计/内容页架构.md index b940cf12..d96398ca 100644 --- a/docs/UI设计/内容页架构.md +++ b/docs/UI设计/内容页架构.md @@ -453,7 +453,7 @@ extern const content_page_def_t ui_page_device_info_desc; /* 描述符(Flash ## 八、验证与回归 - **门禁**(`ui-verify` skill / `UI_自动化测试流程.md`): - 0) 模拟器构建 0 警告 → 1) 菜单 8 状态像素回归 + 池基线 → 2) 内容页文本断言 + 60 页黄金参考逐字节比对 + 0) 模拟器构建 0 警告 → 1) 菜单 8 状态像素回归 + 池基线 → 2) 内容页文本断言 + 60 张黄金参考逐字节比对 (网口设置 4 态 + 密码页 4 态等)+ 开页-关页往返水位 → 3) Keil 构建 0 错 0 警 → 4) 代码规范检查 - **门禁常量**(脚本头部):`MENU_POOL_BASELINE = 5368 B`、`POOL_AFTER_ROUNDTRIP = 5448 B` - **失败处置**:像素不一致 = 代码 bug(修代码,**禁止**刷参考掩盖);**有意**改视觉 → diff --git a/docs/UI设计/页面扩充详细方案.md b/docs/UI设计/页面扩充详细方案.md index bdd73f79..6fda7b70 100644 --- a/docs/UI设计/页面扩充详细方案.md +++ b/docs/UI设计/页面扩充详细方案.md @@ -21,6 +21,8 @@ 已绑定 6 页:`装置信息`(node 1)、`交流量`(3)、`直流量`(4)、`网口设置`(30)、`自检记录`(39)、`运行记录`(40)。 +> ⚠ **本表 = 2026-10-25 立项时快照,按历史冻结不回改。** 2026-10-28 复扫现值:**已绑 28 叶 / 未绑 5 叶**(判据两处互证:`ui_menu_tree.txt` 剔掉文件头注释里的示例行后 `#page=` 实数 = 28,与生成物 `ui_page_registry.h` 的 `UI_PAGE_MAP_COUNT` 一致);未绑 5 叶 = 遥信类型 / 遥信防抖 / 双点遥信 / 手动录波 / SNTP 设置。现状口径见 `docs/任务规划/UI补齐.md` §1 与 `docs/产品需求/产品需求设计文档.md` §8.2 / §8.3。 + ### 1.2 27 个空叶子清单(按菜单顺序) | # | 叶子 | 节点 | 密码 | 父节点 | @@ -624,7 +626,7 @@ SNTP 客户端 ──────→ 批4 SNTP设置 | 4 | 无源点显示 `--` | 遥信量无效点、直流量直流通道 | | 5 | 定值整定闭环 | 改 → 保存 → 重启 → 值保持 + 校验和自检通过 + 主站 C_RD 一致。**2026-10-26 半收口**:模拟器侧「改 → 保存 → `OK` → 回首屏新值已在帧里」与「越界被拒、值不落盘」已由像素 + 文本断言覆盖(`sparam_save` / `sparam_landed` / `sparam_oob`);**重启保持与 C_RD 一致只能上板**(模拟器无 NAND)。另注意 40 项里 **13 项 `comm_addr = 0`**(投退、返回系数、过频/低频段、全局软压板)主站不可寻址,不能拿 C_RD 当它们的判据 | | 6 | 分层门禁无新增 | `check_layer_deps.py` 退出码 0,基线 6 → 3。**现状:3**(记录 reader 已削 3 条;写路径走注入式 ops 函数表,未新增 include,见 `docs/UI设计/UI_数据供给设计说明.md` §6.2) | -| 7 | 像素回归稳定 | `ui-golden` 连续两次零差异。**已验(2026-10-26)**:27 页连跑两次逐字节一致——**该下限此后已扩至 60 页**(新增 clock / reset / clear 系列 13 张,其后 47→53→60),60 页上的双次逐字节一致需按同口径重跑一次再回填本行 | +| 7 | 像素回归稳定 | `ui-golden` 连续两次零差异。**已验(2026-10-26)**:27 页连跑两次逐字节一致——**该下限此后已扩至 60 张**(新增 clock / reset / clear 系列 13 张,其后 47→53→60),60 张上的双次逐字节一致需按同口径重跑一次再回填本行 | | 8 | 每个新页有 golden 参考 | `simulator/tests/ref/` 新增对应 `.bin`。**整定 4 页已有 9 张**(含保存/越界/落定/回绕各态);参考图入库,改它 = 改判据,须逐页过目 | --- diff --git a/docs/产品需求/产品需求设计文档.md b/docs/产品需求/产品需求设计文档.md index d756b0d4..4c458219 100644 --- a/docs/产品需求/产品需求设计文档.md +++ b/docs/产品需求/产品需求设计文档.md @@ -890,7 +890,7 @@ sequenceDiagram 上电次数 + 本次复位原因快照)与 iec104_serv 的四个只读观测计数,经 info 帧下发 (口径 docs/UI设计/UI_数据供给设计说明.md §4.4)。 - 判据:五类记录各有写入点、可翻最近 20 条、空记录明确显示"无记录"、共用 reader——**软件面已满足** - (空态与四个范围标签由 60 页像素黄金钉住;模拟器侧这些文件没有写入方,只拍到空态)。 + (空态与四个范围标签由 60 张像素黄金钉住;模拟器侧这些文件没有写入方,只拍到空态)。 真记录翻页、掉电后保持、清除记录后归零、boot_count 自增与四态判定**待台架复核** (docs/任务规划/登记簿.md §7)。 @@ -1878,7 +1878,7 @@ docs/任务规划/UI补齐.md §5);原「当前执行 6 项」(记录 5 - 菜单导航状态**不压层**,返回即原样还原;同一时刻至多一个内容页打开。 - 节点→页映射在逻辑层 `menu_content_page_for(node)`;未绑叶子进入空白终点框(不伪造数据)。 - 数据供给走**注入通道**(rt / par / info / 遥测死区与系数第四表),页面不直接触算法层; - 像素黄金用例当前 **60 页**(菜单 8 状态另计;2026-10-28 记录 5 页与运行报告 2 页落地,+7 张)。 + 像素黄金用例当前 **60 张**(菜单 8 状态另计;2026-10-28 记录 5 页与运行报告 2 页落地,+7 张)。 ### 8.4 密码与权限 @@ -2715,3 +2715,4 @@ docs/任务规划/UI补齐.md §5);原「当前执行 6 项」(记录 5 | 2026-10-28 | **v1.7 单色口径:删除多色协议,INIT 由 29 B 改 16 B** | 用户裁定"屏幕板都是单色的,多色协议可以删除、不必对齐"。**核查后确认这不是让步,而是删死字段**:① **三处源码证据**——`RemoDispBus.h:46` `CN_RDISP_TYPE_MONO = TRUE` → `CN_RDISP_LCD_TYPE = CN_MODE_GREY_MONO`、`CN_RDISP_MEMSIZE = (X>>3)*Y`(链路显存本就是 1 bpp);`RDisp_Form_InitInf` 里 `CN_RGB_RED/GREEN/BLUE` 是**编译期常量**而非逐帧数据;`RDisp_LcdColor_To_Mono()` 对 `CN_MODE_GREY_MONO` **直接 `return TRUE` 不转换**,真正干活的 `RDisp_Lcd16B_To_Mono()`(RGB565→单色)挂在 `CN_MODE_COLOR_16B` 下**本构建永不进入**(死代码);② **§5.16.5 INIT 应答表由 29 B 重写为 16 B**(宽 2 + 高 2 + 显存 4 + 8 键值表),删掉 1 B 颜色位 + 12 B RGB,并补"为什么是 16 B"的证据块;③ **§6.2 命令集 INIT 行**同步改 16 B 并加"单色口径"注(颜色位与 RGB 整段删除、`RDisp_LcdColor_To_Mono()` 不存在);④ **§5.16.6 第 6 条注**改为"该函数在参考单色构建下就是空转",避免误以为本产品漏了一步转换;⑤ **§5.16.9 第 1 行**由"补 21 B"改为"**补 8 B 键值表 → 16 B**,颜色 13 B 不对齐";"新增约束"补"单色"一条;末尾警示改为 **16 B vs 29 B 不可互换**(从偏移 4 起含义即不同,客户端不得复用参考 29 B 解析器截前 16 B);⑥ **§5.16.10 判据第 1 条**改 16 B 且"不得出现颜色位或 RGB";⑦ **§9.13 第 2 条**改"INIT 应答补足 **16** 字节 / 不加颜色位与 RGB"、第 9 条客户端按 16 B 解析、破坏性变更警示改 16 B(后 8 B 而非后 21 B)并补"不可与参考 29 B 互换";⑧ **§2.4.1 补"多色显示不在本表内"**——参考与本产品都是单色链路,删的是死字段,**不属于"决定不做"的能力**;§2.5.2 第 20 项、§9.1.2、§9.12、§10.3 第 13 项同步;⑨ **§8.1 色深行**补"无灰度、无彩色,7003 INIT 颜色字段已删",渲染行改为"串口屏板通道(主)+ TCP 7003(镜像)双出口"。**本次只改本文件** | | 2026-10-28 | **v1.8 端口重排:屏板占 USART1,日志迁 RS-232 通道** | 用户裁定"把现在的日志 UART 让给屏幕,RS-232 通道后面用来输出日志"。**这条裁定把 v1.6/v1.7 的头号阻塞直接消掉了**:① **§5.16.2 新增「端口裁定」块**——屏板改走 **USART1(PA9/PA10,MCU 直出 TTL、不经任何收发器)**,因此 `U15 = TP8485E-SR` 只有 250 kbps 这件事**对屏板链路不再构成约束**;日志迁 **RS-232 通道**(V0.1 板上 RS232#3 = USART2 PD5/PD6 为默认,RS232#1 = UART5 PC12/PD2 为备选,两者都经隔离器 → U13 TPT3232E → CN1);② **纠正分频算法**——旧文写"`42 000 000 / 700 000 = 60` → `USARTDIV = 60.0`、`BRR = 0x3C0`"**漏了 16 倍过采样**,照此写实际波特率只有 43 750;正确值:USART1(APB2 84 MHz)`USARTDIV = 7.5` → **`BRR = 0x0078`**,APB1 实例(42 MHz)`USARTDIV = 3.75` → **`BRR = 0x003C`**,两者误差均为 0;③ **§5.16.2「主机侧端口选型」整表重写为「主机侧端口重排」**,按 `docs/硬件平台/V0.1_网表详细分析报告.md` §4.2 的三对 UART 轮换列明 RS485#4 = UART4(DE = PA15)、RS232#1 = UART5、RS232#3 = USART2,并补两条实现约束(`console_log.c` 里 `huart1` 5 处 + 自愈函数直写 `USART1_IRQn` / `MX_USART1_UART_Init()` 需收口成一处端口宏;新日志口必须 `UART_MODE_TX`,否则重演 ORE 锁存);④ **DE 口径改为「TTL 直连无 DE」**,仅"经外部 485 模块接 A/B"时才需处理,且极性由该模块决定;⑤ **主机 RX 口径**由"吃下 4208 B"改为"只需 6 B(16 B 缓冲够)",并说明 TTL 直连无自发自收;⑥ **§4.2 外设接口表**:USART1 行补"端口重排后改作屏板链路 + PA10 目前未接线",新增 USART2(RS-232 #3,日志口)与 UART5(RS-232 #1,备选)两行,UART4 行注明 V0.1 轮换依据;⑦ **§5.16.9「新增约束」**与 **§9.12** 的 700000 行同步为"USART1 TTL 直连、无 DE";⑧ **§9.13 新增第 16 条「日志口迁移」**并置于第三批首位,批次表注明 **16 必须排在 10 之前**(两者共用 `USART1->DR`);第 10 条改为 `BRR = 0x0078`、无 DE、`DMA2_Stream7/2` 现成;**前置阻塞**补两条物理前置(PA10 接线、台架串口线从 USART1 挪到 CN1)。**本次只改本文件** | | 2026-10-28 | **v1.9 撤销"PA10 未接线",ORE 机理改正** | 用户反馈"PA10 这个端口我接线了,当时分析出这个问题来,我怀疑是错误的"。核查后确认:**`App/log/console_log.c`(文件头与 `con_uart_reinit()` 注释)与 `docs/子系统设计/控制台单写者日志设计说明.md` §12 里的"PA10 悬空 / 未接线"是 2026-10-02 的归因推断,不是实测**,且与用户确认的接线状态相反。改动:① **§4.2 外设接口表** USART1 行删掉"PA10 目前未接线、作屏板链路前必须接上",改为"PA10 双向均已接线";② **§5.16.2** 原"USART1 的 RX 目前没有接线"警示块整段重写为「旧结论已撤销 + ORE 真正机理」——ORE 的充分条件是**RX 使能却从不读 `DR`**(任意一个字节或噪声沿使 `RXNE` 置位后无人读,下一个事件即锁存 ORE,且只能靠"读 SR 再读 DR"清除 ⇒ **永不自愈**),与引脚悬空无关;据此补两条硬要求(屏板链路必须真的消费 `DR` 并在 `HAL_UART_ErrorCallback` 里清 ORE 后重挂接收;新日志口设 `UART_MODE_TX` 仍正确,因为那是从机制上摘掉 RX);③ **§9.13 前置阻塞**由"两条物理前置"改为"一条物理前置(台架串口线挪位)+ 一条实现纪律(RX 要真读、ORE 要真清)",并写明原"PA10 接线"结论已撤销;第 11 条的验收隐含"拔插 TX 线制造噪声后能自动恢复"。**当年那场 ORE 风暴的真因仍待定**——接线正常时更常见的是对端波特率不匹配或适配器 TX 在 USB 掉线时浮空,下次复现先量空闲电平与波特率,不要再默认"引脚悬空"。**本次只改本文件** | +| 2026-10-28 | **像素黄金口径更正(不 bump 版本号)** | 正文两处当前态由「60 页」改报「60 张」:判据本体 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 60 = `check_content_pages.py` 的 `PAGES` 条目数 60,其中 2 张是分页页多拍的第 2 屏 ⇒ 张数 ≠ 页数(同一句里「+7 张」与「60 页」并存就是口径混用的后果)。历史行 53 → 60 与更早的 27 / 40 / 47 一律按当日事实冻结,本行只追加。 | diff --git a/docs/任务规划/UI补齐.md b/docs/任务规划/UI补齐.md index efbcb085..5043f303 100644 --- a/docs/任务规划/UI补齐.md +++ b/docs/任务规划/UI补齐.md @@ -18,7 +18,7 @@ 现有基础:`ui_w_editor` / `ui_w_setlist` / `ui_page_action` 已就绪;`ui_data` 已扩到**四通道** (rt / par / info + 遥测死区与系数用的第四张注入表);像素黄金用例与字符集门禁已存在,内容页黄金 -现有 **60 页**(菜单 8 状态另计)。**装置维护 3 页、遥测死区/系数 2 页、通讯设置页、记录 5 页与 +现有 **60 张**(菜单 8 状态另计)。**装置维护 3 页、遥测死区/系数 2 页、通讯设置页、记录 5 页与 运行报告页都是把这套基础接上真实消费者做出来的,不另发明一套机制。** 逐页形态见 `docs/UI设计/页面补齐规划.md`;数据契约见 `docs/UI设计/UI_数据供给设计说明.md`。本文件保留范围、完成判据与不做的取舍口径(执行清单已于 2026-10-28 清空)。 diff --git a/docs/子系统设计/运行与自检记录设计说明.md b/docs/子系统设计/运行与自检记录设计说明.md index d6cffbd9..ef8c2b16 100644 --- a/docs/子系统设计/运行与自检记录设计说明.md +++ b/docs/子系统设计/运行与自检记录设计说明.md @@ -25,7 +25,7 @@ - **菜单树已含目标叶子**:`ui_menu_tree.txt` "记录查询"下已有 **自检记录 = node 39、运行记录 = node 40**(当前为空白终点框,无内容页映射);无需改树。 - **密码页** `ui_page_password.c`:3 位数字框 + ↑↓←→/ENTER;输错仅清空重输(R1 缺口)。 - **按键流**:物理/远端键 → LVGL keypad → 默认输入组 → 焦点对象事件回调(菜单容器 `menu_key_cb` / 内容页容器 `cp_key_cb`)——空闲检测可在此统一挂钩。 -- **字体**:子集位图字体 `ui_font_cn_12`,字符集 `ui_cn_chars.txt`(当前 122 汉字/符号 + ASCII)。 +- **字体**:子集位图字体 `ui_font_cn_12`,字符集 `ui_cn_chars.txt`(当前 218 汉字/符号 + ASCII 0x20~0x7E,共 313 字形;2026-10-28 实测。122/217 是 2026-09-14 的历史快照,见 §6.1)。 ### 2.2 存储与时间 diff --git a/docs/测试说明/主机单测覆盖率口径.md b/docs/测试说明/主机单测覆盖率口径.md index 410c6f20..df9ba8ff 100644 --- a/docs/测试说明/主机单测覆盖率口径.md +++ b/docs/测试说明/主机单测覆盖率口径.md @@ -704,7 +704,7 @@ uint16_t ch395f_link_change_count(void); | 类别 | 体量 | 为什么不做 | 那它靠什么把关 | |---|---|---|---| | 芯片驱动(寄存器/时序) | `ch395f.c`(801)、`gd5f2gq5ue.c`(689)、`sd2506.c`(264)、`tpafe5160.c`(157)、`rs485.c`(79) = **1,990 行** | 把 HAL 与寄存器假到底 ≈ **重写一遍驱动**,测的是自己的桩而不是硬件行为(SPI 分频必须 8、DMA 流、I2C 时序在桩里怎么编都"对") | 板级套件(`board-test-suite` skill)+ `@test` 真机日志 + 4 个 Fault 现场;陷阱逐条登记在 `docs/问题记录/CH395F_Trap_Records.md`、`docs/问题记录/GD5F2GQ5UE_Trap_Records.md`、`docs/问题记录/SD2506_Trap_Records.md` | -| UI 侧 | 17 个 `.c` = **1,942 行** | 与 PC 模拟器是**同一份源码**,行为回归已由 `ui-golden`(菜单 8 状态 + 内容页 60 页像素黄金参考)覆盖;再写单测是第三份重复投资 | `ui-verify` skill 五步 + `docs/UI设计/UI_自动化测试流程.md` | +| UI 侧 | 17 个 `.c` = **1,942 行** | 与 PC 模拟器是**同一份源码**,行为回归已由 `ui-golden`(菜单 8 状态 + 内容页 60 张像素黄金参考)覆盖;再写单测是第三份重复投资 | `ui-verify` skill 五步 + `docs/UI设计/UI_自动化测试流程.md` | | 任务编排 | `default_task.c`(281)、`adc_task.c`(171)、`net_task.c`(123)、`protect_task.c`(90)、`ui_task.c`(71)、`rs485_task.c`(11) = **747 行** | 每个只有 4~5 个函数,主体是"按顺序调别人";真逻辑在被调模块里(那些已测或在 P1/P2)。给编排层写单测最容易写成"断言我调了几次"的自证 | 真机启动锚点计数 + IWDG `required_mask`(`@test`)+ `docs/架构设计/项目启动顺序管理.md` | | 内核前装配 | `app_main.c`(135) | 被测对象本身就是"`osKernelStart` 之前不许调内核 API"的时序,主机侧无法忠实复现(PC 上没有那个约束) | 同上 + `cubemx-survival` 审计 | diff --git a/docs/测试说明/自动化测试流程.md b/docs/测试说明/自动化测试流程.md index e7cd72c3..613a7181 100644 --- a/docs/测试说明/自动化测试流程.md +++ b/docs/测试说明/自动化测试流程.md @@ -119,7 +119,7 @@ - **初始化回归**:`App/task/ui_task.c` 已有 `UI port init: OK` / `UI menu init: OK` / `UI net sink: started` 打印(见 `@test` 命令示例);将其加入 `-Required` 即可经串口自动回归 UI 初始化。 -- **屏幕内容回归**:由 LVGL 模拟器像素回归覆盖(文本级断言 + 60 页黄金参考 + 池水位门禁, +- **屏幕内容回归**:由 LVGL 模拟器像素回归覆盖(文本级断言 + 60 张黄金参考 + 池水位门禁, 见 `docs/UI设计/UI_自动化测试流程.md`);上机屏幕仍需人工目检(L3 层,见 `测试开发标准流程.md` §1)。 ## 扩展用法 diff --git a/docs/门禁说明/font-charset.md b/docs/门禁说明/font-charset.md index 3d45ede4..af4fca38 100644 --- a/docs/门禁说明/font-charset.md +++ b/docs/门禁说明/font-charset.md @@ -61,7 +61,7 @@ A 通过 + B 通过 ⟹ 字体覆盖"源码在用"与"刻意保留"的每一个 `tools/font_gen/extract_cn.py` 只认**当前源码里的字符串字面量**,照不出运行期数据里的字—— 典型是 2026-09 窗口固件写进 `/log/selftest.log`、`/log/run.log` 的定长 64 B 中文描述记录 (跨固件持久,自检/运行记录页直接渲染 `desc`)。实测口径(2026-10-25):extract 扫出 -**144 字**,而 `ui_cn_chars.txt` 现有 **183 字**,差的 39 字全部由 `KEEP_EXTRA` 兜住: +**144 字**,而 `ui_cn_chars.txt` **当时**现有 **183 字**(2026-10-28 实测已 218 唯一字),差的 39 字全部由 `KEEP_EXTRA` 兜住: - **逐字登记 4 字**(异/常/片/芯):出处可指到 `9eb1bd63`(2026-09-14 中文自检描述) 与 `6f51c62a`(2026-09-20 串口侧改 ASCII),脚本里逐字写了理由; @@ -105,3 +105,4 @@ A 通过 + B 通过 ⟹ 字体覆盖"源码在用"与"刻意保留"的每一个 |---|---| | 2026-10-23 | 首篇逐项文档(两段链条判定口径 + 生成期校验覆盖不到的那一环) | | 2026-10-25 | 新增 §6.1:实名登记「按 extract 输出重写字符集 = 洗字形」这条事故路径;把当时「未知冗余」WARN 的 35 字**成组**并入 `KEEP_EXTRA`(实测 extract 144 字 vs 清单 183 字,差 39 字 = 逐字 4 + 成组 35),并写清删除前置条件(道E task-15) | +| 2026-10-28 | 全仓计数复扫:§6.1 的「183 字」是 2026-10-25 实测口径,**只补「当时」锁口径、不臆改新数**;现值实测 = `ui_cn_chars.txt` 唯一码点 **218** 个(其中 ASCII 区 0 个,ASCII 由生成器的子集参数另计,不算进字数)。同一句实测在 `scripts/check_font_charset.py` 的成组保留注释里同步锁「当时」 | diff --git a/docs/门禁说明/ui-golden.md b/docs/门禁说明/ui-golden.md index dc7ea91b..7e072999 100644 --- a/docs/门禁说明/ui-golden.md +++ b/docs/门禁说明/ui-golden.md @@ -1,4 +1,4 @@ -# `ui-golden` — UI 像素回归(菜单 8 状态 + 内容页 60 页黄金参考) +# `ui-golden` — UI 像素回归(菜单 8 状态 + 内容页 60 张黄金参考) > 一句话:改 UI 源码时,把模拟器增量重编 + 两条 golden 回归串成一次提交门禁,**像素对不上就拦**。 > 它是包装器:真正的判据在被包装的两条脚本与参考图里。 @@ -11,7 +11,7 @@ ## 1. 它在查什么 - 菜单侧:整棵菜单树的行渲染结果(8 个状态)与参考图逐像素比对; -- 内容页侧:60 个内容页的渲染结果与参考图比对; +- 内容页侧:60 张黄金参考(= 60 个截图态条目,同一内容页的多个状态各一张,其中 2 张是分页页多拍的第 2 屏),逐张与参考图比对; - 参考图 `simulator/tests/ref/*.bin` **就是判据本身**。 ## 2. 为什么要它(有牙的实测) @@ -57,7 +57,7 @@ 1. **参考图即判据**:刷新参考图 = 修改判据。评审时必须盯 `simulator/tests/ref/` 的 diff; 一旦"顺手更新",此后所有回归都只在和错的基线比。**门禁无法阻止这件事**,只能靠评审。 -2. 只覆盖 8 个菜单状态 + 60 个内容页:未列入的状态(密码错误态、超长文本、缺字渲染、 +2. 只覆盖 8 个菜单状态 + 60 张内容页黄金参考:未列入的状态(密码错误态、超长文本、缺字渲染、 无 SD 卡 / 挂载失败的降级显示)**不会有任何回归**。 3. 跑的是**模拟器那份配置**,不是固件那份 —— 两份 `lv_conf.h` 漂移时,回归结论不能外推到固件 (所以 `lv-conf-sync` 是本门有效性的前提)。 @@ -86,3 +86,4 @@ | 2026-10-27 | 内容页黄金参考 **47 → 53 页**(通讯设置页落地并绑 `#page=comm`;判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 53 张,`git diff --name-status 31ca4a1f HEAD -- simulator/tests/ref` 实测只有 6 个 `A`、零 `M` ⇒ 存量 47 张逐字节零漂移);本文件的当前态计数已同步 | | 2026-10-28 | 内容页黄金参考 **53 → 60 页**(「UI 补齐」清单清空:记录 5 页 accident / oper / alarm / start / yk 与运行报告 runreport 六页落地并在菜单树绑 #page=。运行报告 13 行分 2 页(8 + 5),第 2 页单独拍一张 shot_runreport_p2.bin 钉「第 2 页,共 2 页」页脚与 104 三枚计数,故 60 而非 59。判据 = simulator/tests/ref/ 入库 shot_*.bin 实数 60 张(tracked 53 + 新增 7)+ 存量 53 张逐字节零漂移(本批字库净新增 33 字:185→218 字 / 280→313 字形,两轮 make_font 后对既有 58 页像素零影响);文本断言 425 正向 / 36 反向(2026-10-26 写的 310 条为当时数)| | 2026-10-26 | 内容页黄金参考下限 **27 → 40 页**(UI 三页落地;`simulator/tests/ref/` 入库 `shot_*.bin` 实测 40 个、`check_content_pages.py` 的 `PAGES` 40 项 / 文本断言 310 条覆盖 37 页 / 反向断言 29 条 16 页),标题与「拦不住什么」段同步;未列入状态那一段的覆盖面口径不变 | +| 2026-10-28 | **口径同步(数字一律不动)**:当前态计数单位「页」→「张」。判据本体 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 **60** = `check_content_pages.py` 的 `PAGES` 条目数 **60**(两份 id 列表逐一对齐、零孤儿),其中 2 张是分页页多拍的第 2 屏 ⇒ **张数 ≠ 页数**;§1 那句「60 个内容页」同批改写为「60 张 = 60 个截图态条目」。同批 16 文件 23 处(含本文件标题、`.pre-commit-config.yaml` 钩子显示名、`AGENTS.md` 钩子清单、`.dsh/skills/lvgl-simulator/SKILL.md` 正文、`scripts/board_ui_nav.py` docstring)。历史行(27 → 40 / 40 → 47 / 47 → 53 / 53 → 60)与本文件 2026-10-26 行按当日事实**原样冻结不回改**,本行只追加为末行。 | diff --git a/scripts/board_ui_nav.py b/scripts/board_ui_nav.py index d8c9d41f..61ad6357 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 页)逐帧分类自检:每张图的期望值 = 它的文件名。 + """仓库黄金参考(60 张)逐帧分类自检:每张图的期望值 = 它的文件名。 这一组是**语义正确的基线**(模拟器与板端同一份 UI 源码),用来把"分类器错了"与 "留档帧本来就是走歪的"分开——两类失败含义完全不同,混在一起就只能靠猜。 diff --git a/scripts/check_font_charset.py b/scripts/check_font_charset.py index c9d6dab6..c9a1ed03 100644 --- a/scripts/check_font_charset.py +++ b/scripts/check_font_charset.py @@ -88,7 +88,7 @@ KEEP_EXTRA = { # → 整组保留并在此**点名成组**,而不是给每个字编一条看起来可信的理由。 # 删除前置条件:确认设备上已无该窗口的落盘记录(或已接受历史记录部分渲染白框)。 # ⚠ 本组真正的价值是把一条路径钉死:**「按 extract_cn 输出重写 ui_cn_chars.txt」= 洗字形** -# (extract 只认当前源码字面量,实测 144 字,而 txt 现在 183 字)——谁按它的输出覆盖 +# (extract 只认当前源码字面量,实测 144 字,而 txt 当时 183 字(2026-10-28 实测已 218 字))——谁按它的输出覆盖 # txt,就会把这 35 字连同上面 4 字一起洗掉,且只在渲染历史日志时才暴露、无日志无报错。 # 成因与判据详述 → docs/门禁说明/font-charset.md §6.1 _KEEP_HIST_SELFTEST = ("历史落盘记录用字(2026-09 窗口固件写入 /log/selftest.log 与" diff --git a/simulator/README.md b/simulator/README.md index f79f32be..d6ec524a 100644 --- a/simulator/README.md +++ b/simulator/README.md @@ -53,7 +53,7 @@ simulator/ | `build_sim.bat` / `build_sim.ps1` | 一键构建入口:vswhere 探测本机 VS 大版本(16=2019 / 17=2022 / 18=2026,取已装最高)→ 映射 CMake 生成器;`build/` 缓存生成器与本机不符(换机/升级 VS)时自动删除重建(带重试,抗文件锁)。**构建入口一律走它,文档/命令中勿写死生成器版本**;`.bat` 仅负责绕过 PowerShell 执行策略并转交 `.ps1` | | `CMakeLists.txt` | 收集 LVGL 源码(`../Lib/lvgl`)+ 共享 UI 源码(`../Drivers/BSP/UI`)+ 通用配置文件引擎(`../App/cfg_file.c`、`../Drivers/BSP/NETCFG` 的 net_cfg;`net_cfg_legacy` 已于 2026-10-10 删除)+ `sim/` 平台层,链接预编译 SDL2,产出 `lvgl_sim.exe` | | `i1_to_png.py` | 把 `--screenshot` 产出的 I1(1bpp) 原始位图转成 PNG,便于离线比对设计稿。详见 §3.5 | -| `tests/` | 无头回归脚本(菜单 8 状态像素回归 + 内容页 60 页:文本断言 / 反向断言 / 黄金参考逐字节比对);`ref/shot_*.bin` **随仓库入库**(判据本体,改动等于改判据,评审须逐页过目),`ref/*.png` 是本地派生的目检图(`.gitignore` 挡掉)。详见 §3.6 | +| `tests/` | 无头回归脚本(菜单 8 状态像素回归 + 内容页 60 张:文本断言 / 反向断言 / 黄金参考逐字节比对);`ref/shot_*.bin` **随仓库入库**(判据本体,改动等于改判据,评审须逐页过目),`ref/*.png` 是本地派生的目检图(`.gitignore` 挡掉)。详见 §3.6 | > **共享代码不复制**:`ui_menu.c` / `ui_font_cn_12.c` / `ui_net_sink.c` 直接引用 `Drivers/BSP/UI/` 下的原件, > MCU 与 PC 两端永远同步。**不要**在本目录内复制 UI 源码。 @@ -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` 刷成"自证正确",反向断言不依赖参考图)③ **60 张**无头截图与 `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/README.md b/simulator/tests/README.md index 39c939c8..e6e9bf8b 100644 --- a/simulator/tests/README.md +++ b/simulator/tests/README.md @@ -11,7 +11,7 @@ | `check_content_pages.py` | **内容页回归**:各内容页渲染比对 | | `check_remo_sink.py` | **推流(RemoDispBus)回归**:接收端渲染比对 | | `regen_ref_png.py` | 由 `ref/*.bin` 批量出**目检 PNG**(不产生基准;基准由 `check_content_pages.py --update` 刷) | -| `ref/*.bin` | **像素黄金参考(入库)**:60 页 × 3200 B,160×160 单色打包 | +| `ref/*.bin` | **像素黄金参考(入库)**:60 张 × 3200 B,160×160 单色打包 | ## 使用方式 diff --git a/simulator/tests/check_content_pages.py b/simulator/tests/check_content_pages.py index 27d6feda..0ceded45 100644 --- a/simulator/tests/check_content_pages.py +++ b/simulator/tests/check_content_pages.py @@ -804,11 +804,11 @@ EXTRA = { # 接线道 task-6:记录 5 页在**各自独立的空目录**里跑(不共用回归的那个 tmp)。 # 诱因(实测,不是猜):模拟器把"人工操作留痕"镜像成了真记录——simulator/main.c 的 # sim_post_oper() 走 syslog_post(SYSLOG_OPER, ...) 落盘**当前工作目录**的 sim_oper.log; -# 而 60 页原本共用一个 tmp,clock_save / netcfg_save / sparam_save 这些"保存"态跑完之后 +# 而 60 张(每个截图态一条目)原本共用一个 tmp,clock_save / netcfg_save / sparam_save 这些"保存"态跑完之后 # sim_oper.log 已经攒了 3 条(192 B = 3×64 B)→ "操作记录"页读到的是真记录, # 空态判据(第 0 行"无记录")根本拍不到。**判据本身没错,错在跑它的环境被前面的写路径 # 污染了**,所以给这 5 页各开一个空目录 = 现场"这些日志文件还不存在"的确定态; -# 其余 55 页环境一字不变(对存量 53 张黄金零影响,实测逐字节零漂移)。 +# 其余 55 页环境一字不变(对当时存量 53 张黄金零影响,实测逐字节零漂移;当前集合 60 张)。 FRESH_CWD = ("accident", "oper", "alarm", "start", "yk")