Files
STM32F4-Base/docs/门禁说明/ui-golden.md
T
super_agent_bot ad7338365c
CI / 静态门禁 (pre-commit) (push) Failing after 6m14s
CI / 主机侧单测 (MSVC) (push) Successful in 54s
CI / 静态门禁 (pre-commit) (pull_request) Failing after 4m35s
CI / 主机侧单测 (MSVC) (pull_request) Successful in 47s
feat(sntp): 板端 SNTP 校时接入——coreSNTP 序列化器 + [sntp] 段 + 设置页(#73)
- vendored coreSNTP v2.0.0 序列化器(MIT,仅 serializer+LICENSE,零堆),
  48 字节报文编解码在库,节奏与准入判据在本方(RFC 4330 §10 调研口径)
- App/sync/time_sntp.c 挂 timeSyncTask 的 8888 事件循环,等待切片 ≤50 ms,
  不饿任务狗与 IWDG;懒建 UDP socket,代际变化/禁用/死错误码即关闭归还
- 防伪 = OriginTimestamp 逐位等于发出时 T1(含低 16 位随机小数位);
  mode/stratum/闰秒指示/KoD 按 RFC 拒收;DENY·RSTR 换服务器、RATE 只退避
- 主备轮换 + 失败指数退避(5 s 起、60 s 封顶)+ 首轮 1~5 分钟随机错峰;
  成功恢复 poll_min 周期(1~1440 分,出厂默认 60,默认不启用不发包)
- 钟面 = 本地时间:UTC+8 固定偏移落 sys_clock_set_unix(§1.1 时基裁定,
  第三个写入口,装置仍不存时区);|offset|<2 s 只记日志不步进,冷启动或
  更大偏差步进并回写 SD2506,留痕 [SNTP] step 与 syslog OPER
- 首个校验通过的同步置 SYS_EVT_TIMESYNC_READY(就绪位,消费方按需 NoClear)
- [sntp] 配置段(enable/server1/server2/poll_min)属主 sntp_cfg.c,
  注册在 net_cfg_load();UI 页 31「SNTP 设置」(密码叶):启用开关 +
  主/备服务器 octet 编辑 + 周期(分) + 保存,复用 net_cfg_save_request 链
- 测试与门禁:test/host/sntp 新套件(报文/防伪/KoD/退避周期/阶跃域/配置
  注册表)、net_cfg 与 time_sync 套件补 fake、黄金参考 66→74 张零漂移、
  字符集 228 字、Keil 0 错 0 警(RO +5,848 B 含字体)、覆盖率基线回填
- 文档:墙钟维护说明 §16、网络配置说明 [sntp] 节、配置文件设计说明变更记录、
  产品需求 FR-TIME-3 与 8.3/9.1.2 台账、UI 补齐规划/UI补齐 31 叶口径、
  登记簿销项并登记「SNTP 校时真机判据」等台架(PC 时间源 192.168.31.161/.13)
2026-11-04 16:20:22 +08:00

79 lines
5.0 KiB
Markdown
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.
# `ui-golden` — UI 像素回归(菜单 8 状态 + 内容页 74 张黄金参考)
> 一句话:改 UI 源码时,把模拟器增量重编 + 两条 golden 回归串成一次提交门禁,**像素对不上就拦**。
> 它是包装器:真正的判据在被包装的两条脚本与参考图里。
- 配置:`entry: python scripts/check_ui_golden.py`、`pass_filenames: false`、
`files:` 覆盖 `Drivers/BSP/UI/`、`App/task/ui_task.(c|h)`、模拟器等**能改变像素的面**。
- 包装器只做三件事:跑 `simulator/build_sim.bat` → 跑 `simulator/tests/check_menu_rows.py` →
跑 `simulator/tests/check_content_pages.py`,透传输出与退出码。
## 1. 它在查什么
- 菜单侧:整棵菜单树的行渲染结果(8 个状态)与参考图逐像素比对;
- 内容页侧:74 张黄金参考(= 74 个截图态条目,同一内容页的多个状态各一张:分页页多拍的第 2 屏与确认链/录波的各态都单独计条目),逐张与参考图比对;
- 参考图 `simulator/tests/ref/*.bin` **就是判据本身**。
## 2. 为什么要它(有牙的实测)
- 两条 golden 脚本早就存在且一直人工跑,但**从未进自动门禁**——审查报告里自认的这个缺口,
正是"**有基线 ≠ 有门禁**"的教科书案例。
- **注入实验(证明它真的会咬人)**:给 `ui_page_dc.c` 的 `.top_y` 注入 +3 像素位移 →
增量重编 → golden **FAIL 并点名** `[dc] FAIL 与参考页不一致(1366 px 差异)`、退出码 1;
还原后 diff 干净。
- 形态选型是**实测出来的**:改一个 UI 源文件后,增量重编 + 两条 golden 合计 2~4 秒 → 才决定
进自动门禁。项目原则写得很清楚:**若实测要几分钟,就应改为文档化的人工门禁——一个逼人
`--no-verify` 的门禁等于没有门禁**。
## 3. 触发面
- ⚠ pre-commit 4.x 没有 `always: true`(实测对 `local` 钩子只告警并跳过)→ 想"总能跑到",
`files:` 必须**穷举所有能改变像素的入口**:UI 源码、推流任务、字体生成物、模拟器源码/配置。
- 包装器**有意忽略** pre-commit 传进来的文件名参数:两条 golden 按"整棵树 + 全量参考图"工作,
不接受文件参数(所以它必须 `pass_filenames: false`)。
- 依赖本机可构建模拟器(需要 Visual Studio / CMake,见 `lvgl-simulator` skill)→ 换机第一次跑
可能因为构建环境缺失而 FAIL,那不是 UI 有问题。
## 4. 报错怎么读
```
[ui-golden] 增量构建模拟器 ...
[ui-golden] 跑 simulator\tests\check_content_pages.py ...
[dc] FAIL 与参考页不一致(1366 px 差异)
```
- 先分清两类失败:**构建失败**("模拟器构建失败,无法做像素回归"→ 环境问题,先修构建)与
**像素不一致**(真改了显示效果)。
- 确认显示改动是**有意的**,才允许按 `ui-verify` skill 的流程更新参考图;
**任何时候都不得用刷新基线的方式让钩子变绿。**
## 5. 好处
- 抓的是"看不出来的错位":白底白字、palette 头越界、位移 3 像素这类**编译与单测都不报**、
只有肉眼能发现的问题(两条实证坑见 `docs/UI设计/UI_设计架构.md` §1.4)。
- 让"改 UI 不用手点一遍"成为可能,配合 `ui-verify` skill 是 UI 改动的标准回归路径。
## 6. 限制与盲区
1. **参考图即判据**:刷新参考图 = 修改判据。评审时必须盯 `simulator/tests/ref/` 的 diff;
一旦"顺手更新",此后所有回归都只在和错的基线比。**门禁无法阻止这件事**,只能靠评审。
2. 只覆盖 8 个菜单状态 + 74 张内容页黄金参考:未列入的状态(密码错误态、超长文本、缺字渲染、
无 SD 卡 / 挂载失败的降级显示)**不会有任何回归**。
3. 跑的是**模拟器那份配置**,不是固件那份 —— 两份 `lv_conf.h` 漂移时,回归结论不能外推到固件
(所以 `lv-conf-sync` 是本门有效性的前提)。
4. 不覆盖真机显示:面板时序、灰度、对比度、SPI 刷屏速率一概不看;像素一致 ≠ 上机正确。
5. 动态内容(时间戳、日志页、SOE 记录)若被纳入参考图,会因数据变化误报;因此这类页面通常
在稳定数据下生成参考图 —— **数据源一变(例如月份进位)就可能假阳性**。
6. 与 `font-charset` 互补:那门管"有没有字形",本门管"像素对不对";两边都绿仍可能有布局问题
不在黄金参考集内。
## 7. 豁免与基线
- 基线 = 参考图二进制;更新流程属 `ui-verify` skill 的一步,且**必须与引起变化的 UI 改动同批提交**,
提交说明里写清"为什么像素变了"。
## 8. 改这个门禁本身
- 加新页面 / 新状态时,要同时扩参考图集合,否则新页面**完全没有回归**(不会报错,只是没覆盖);
- 触发面 `files:` 每加一类能改像素的入口就补一行,漏一类 = 那一类改动静默不跑回归。