# 页面扩充详细方案(27 个空叶子 + 3 页回炉) > **定位**:`docs/UI设计/页面补齐规划.md` 回答"怎么归并、分几批";本文回答**每一页显示什么、数据从哪来、要改哪些代码**——可直接照着开工的粒度。 > **数据通道**:`docs/UI设计/UI_数据供给设计说明.md`(快照推送 + 写路径 + 记录 reader 注入)。 > **数据真实性**:`docs/问题记录/采集与数据处理短板评测.md`(标定、遥测链、运行参数段的缺口)。 > **统计口径**:本文所有节点/叶子数字均由 `ui_menu_tree.txt` 脚本统计 + `Drivers/BSP/UI/ui_page_registry.c` 交叉核对,不引用旧文档结论。 --- ## 1. 核对结论 ### 1.1 统计口径与结果 | 项 | 数 | 来源 | |---|---|---| | 菜单节点总数 | **42** | `ui_menu_tab.h` 的 `UI_MENU_TAB_COUNT` | | 叶子节点 | **33** | 无子项的节点 | | 已绑定内容页 | **6** | `ui_page_registry.c` 的 `g_ui_page_map[]` | | 密码页 | 1 | 非节点绑定(`@password=password`) | | **空叶子** | **27** | 33 − 6 | 已绑定 6 页:`装置信息`(node 1)、`交流量`(3)、`直流量`(4)、`网口设置`(30)、`自检记录`(39)、`运行记录`(40)。 > ⚠ **本表 = 2026-10-25 立项时快照,按历史冻结不回改。** 2026-10-28 复扫现值:**已绑 28 叶 / 未绑 5 叶**;**2026-10-29 再复扫:已绑 30 叶 / 未绑 4 叶**(手动录波与录波回放两叶已绑)(判据两处互证:`ui_menu_tree.txt` 剔掉文件头注释里的示例行后 `#page=` 实数 = 30,与生成物 `ui_page_registry.h` 的 `UI_PAGE_MAP_COUNT` 一致);未绑 5 叶 = 遥信类型 / 遥信防抖 / 双点遥信 / 手动录波 / SNTP 设置。现状口径见 `docs/任务规划/UI补齐.md` §1 与 `docs/产品需求/产品需求设计文档.md` §8.2 / §8.3。 ### 1.2 27 个空叶子清单(按菜单顺序) | # | 叶子 | 节点 | 密码 | 父节点 | |---|---|---|---|---| | 1 | 遥信量 | 5 | — | 实时数据 | | 2~5 | 参数 / 定值 / 控制字 / 软压板 | 8~11 | ✓ | 参数定值 → 整定 | | 6~9 | 参数 / 定值 / 控制字 / 软压板 | 13~16 | — | 参数定值 → 查看 | | 10~14 | 遥测死区 / 遥测系数 / 遥信类型 / 遥信防抖 / 双点遥信 | 18~22 | ✓ | 三遥设置 | | 15~18 | 时钟设置 / 强制复归 / 手动录波 / 清除记录 | 24~27 | ✓ | 装置维护 | | 19~20 | 通讯设置 / SNTP设置 | 29、31 | ✓ | 通讯参数(网口设置 30 已有页) | | 21~27 | SOE记录 / 事故记录 / 操作记录 / 保护告警 / 保护启动 / 遥控记录 / 运行报告 | 33~38、41 | — | 记录查询 | > 记录查询下的 9 个叶子是**平级**的(`SOE记录` 不是分组节点),因此 SOE 页与其余 8 页同级,不需要为它单独加一层菜单。 ### 1.3 已建 6 页的健康度 | 页 | 状态 | 证据 | |---|---|---| | 网口设置 | ✅ 真数据 | `net_cfg_get()` + 保存交接 `net_cfg_save_request/state` | | 自检记录 | ✅ 真数据 | `ui_w_records` → `syslog_count/get_record` | | 运行记录 | ✅ 真数据 | 同上 | | 装置信息 | ⚠ 静态字面量 | `s_rows[]` 6 行写死(`SV0.010` / `HW0.010` / `4454` / `2024.08.27`) | | 交流量 | ⚠ 静态字面量 | `s_rows[]` 24 行全是 Flash 字面量,**不会变**(第 1 页数值取自参考图实测,看着像真值) | | 直流量 | ⚠ 静态字面量 | `s_rows[]` 3 行写死(`ZL01/ZL02/Pa1`) | **结论**:真正接通的只有 3 页(网口 + 2 个记录页)。所以"要动"的页数是 **27 个空叶子 + 3 页回炉 = 30 页次**。 ### 1.4 与既有规划的关系 `docs/UI设计/页面补齐规划.md` 已把 27 个空叶子归并为 **7 个页面模块 + 1 个共享组件 + 2 项框架能力 + 1 个数据层**。本文沿用该归并,但**新增三项**(核对后才发现必需): | 新增项 | 为什么规划里没有 | |---|---| | `ui_w_soe`(二进制 SOE 解码组件) | 规划只写了"需新组件",没定接口 | | `ui_page_action`(确认操作页) | 强制复归 / 清除记录 / 手动录波三处共用,规划里散落在"操作页" | | `run_par`(运行参数模型) | 三遥设置 5 页的地基;规划里当成"数据现成",实际整段不存在 | --- ## 2. 数据可用性:V0.1 到底能显示什么 这一节决定每页的"天花板"。**页面写得再漂亮,没有数据源就是假值 2.0。** ### 2.1 现有真值清单(可被页面消费) | 数据 | API | 状态 | 说明 | |---|---|---|---| | 相量 Ua | `phasor_get(1, &p)` → `p.fAmSQ` | ✅ 真(**码值口径**) | 系数表只含 √2/N,未折算工程量 | | 相量 U0(UN 通道) | `phasor_get(2, &p)` | ✅ 真(码值) | 零序电压基波 | | 相量 Ia | `phasor_get(0, &p)` | ✅ 真(码值) | 过流/过负荷判据源 | | 相量 Ia-2 次 | `phasor_get(3, &p)` | ✅ 真(码值) | 涌流制动用,页面可显示 | | 相量 Ia-5 次 | `phasor_get(4, &p)` | ✅ 真(码值) | 选线用 | | 频率 | `freq_get(&f)` + `freq_valid()` | 🔶 有条件 | 等周波未闭环(`docs/算法/继电保护功能复现规划.md` 「等周波采样(软件测频 + 调频闭环)」),104 点 `0x401D` 仍标 IV | | 温度 | `adc_input_raw(ADC_SEM_TEMP, &raw)` | 🔶 仅码值 | 无换算 API | | 遥信 6 点 | `iec104_pts_yx_read(idx, &v)` | ✅ 真 | YX15/16/17/18/23/31(`YX_VALID_MASK`) | | 遥信 27 点 | 同上 | ❌ 恒无效 | 无硬件来源(开入未配置) | | 定值 40 项 | `prot_set_get_f/get_ctl/checksum/locked/bad_mask` | ✅ 真 | Q10 定点 | | 时钟 | `sys_clock_get_str(buf, len)` | ✅ 真 | 墙钟(可能慢 8 h,见台架记录) | | 网口 | `net_cfg_get()` | ✅ 真 | IP/掩码/网关/MAC | | 日志 | `syslog_count/get_record` | ✅ 真 | 仅运行、自检 2 类 | | SOE | **无读取 API**(只有 `soe_store_post/drain`) | ❌ 需新增 | `/log/soe.log` 二进制 64 B | | 元件状态 | `prot_elm_state/active_mask/name` | ✅ 真 | 当前态,无历史 | | 功率/电度 | — | ❌ 禁止 | 双转换器无共同采样触发:跨转换器不同步,V0.1 不得算功率 | | 直流通道 | — | ❌ 无硬件 | `ADC_SEM` 只有 8 电气 + 温度 | | 开入/开出 | — | ❌ 无代码 | 固件完全没有开入开出实现 | ### 2.2 三档可用性分类(27 个空叶子的分档) | 档 | 含义 | 页数 | 叶子 | |---|---|---|---| | **A 类** | 数据现成,只差页面 | **17** | 参数/定值/控制字/软压板 ×2(8)、遥信量、时钟设置、强制复归、清除记录、事故/操作/告警/启动/遥控记录(5) | | **B 类** | 需新数据层或新 API | **8** | 遥测死区/遥测系数/遥信类型/遥信防抖/双点遥信(5)、SOE记录、运行报告、通讯设置 | | **C 类** | 功能本身不存在 | **2** | 手动录波(无录波)、SNTP设置(无 SNTP 客户端) | > A 类 17 页里有 5 页(事故/操作/告警/启动/遥控)**页面是 S 但写入方是 M**——`sys_log` 只有 2 个 ID,5 类新记录要先有写入点。这是本方案最容易低估的地方。 > > C 类的 2 页与"直流量无通道"是同一个性质的问题:不是页面没做,是**被测对象不存在**。页面做得再好也只能显示"未配置"。 ### 2.3 对页面设计的硬约束 1. **未标定必须可见**:`eng=0` 时页面要显式标"未标定",否则码值被当成伏特(现场误读)。 2. **无源点显示 `--`**,不显示 `0.000`。 3. **不在 UI 层算业务量**:功率、电度、方和根一律等 App 侧给出。 4. **中文名 ≤ 7 字**:左列 x=7、值列 x=101 → 名称可用宽度约 94 px,12 px 字宽 ≈ 7 个汉字。 5. **日志描述 ≤ 9 汉字**:`SYSLOG_DESC_MAX=28` 字节,UTF-8 汉字 3 B/字。 --- ## 3. 逐页方案 ### 3.1 实时数据(3 页) #### 3.1.1 交流量(回炉,24 行 → 7 行) **为什么不是 24 行**:参考布局的 24 行是"双回线 + 三相 + 功率 + 极值"的产物。V0.1 只有 8 个电气通道,其中 5 个(UB/UC/IB/IC/IN)在 104 点表里明确标 `IV`(削顶),相量实例只有 5 个(IA-F1 / UA-F1 / UN-F1 / IA-F2 / IA-F5)。**照抄 24 行等于 17 行 `--`**。 | 行 | 显示名 | 数据源 | 小数位 | 单位 | |---|---|---|---|---| | 1 | `Ua` | `phasor_get(1)` → `fAmSQ` | 3 | — | | 2 | `U0` | `phasor_get(2)` | 3 | — | | 3 | `Ia` | `phasor_get(0)` | 3 | — | | 4 | `Ia2` | `phasor_get(3)`(二次谐波) | 3 | — | | 5 | `Ia5` | `phasor_get(4)`(五次谐波) | 3 | — | | 6 | `Hz` | `freq_get` + `freq_valid` | 2 | Hz | | 7 | `标定` | 快照 `eng` → `未标定`/`二次值`/`一次值` | — | — | - **第 7 行是诚实性行**:它不是测量值,是"上面 6 行的口径说明"。标定落地后自动变"二次值",无需改页面。 - 单页 7 行(8 行/页),不分页。 - 数据来源:`ui_data_rt_t` 的 `yc[0..5]`(下标语义见 `docs/UI设计/UI_数据供给设计说明.md` §9.4 一致性测试)。 - 规模:**S**(改行表 + `refresh` 钩子,约 80 行)。 **待拍板**:是否接受"7 行"而非参考的"24 行"。若产品要求保留 24 行骨架(便于将来扩通道),则改成 24 行固定表、当前 17 行显 `--`。 【2026-10-25 落地】按 7 行落了(`Drivers/BSP/UI/ui_page_ac.c`):品质位 IV/NT/RAW/SB/BL/OV 一律显 `--`, 第 7 行走快照 `eng` 真值语义(口径见 `docs/UI设计/UI_数据供给设计说明.md` §4.1)。24 行骨架并 没有"便于将来扩通道"的价值——真扩通道要连采样源一起动,那时页面改行表就是一行表的事。 #### 3.1.2 直流量(回炉,3 行 → 2 行) V0.1 **无直流采样通道**(`ADC_SEM` 只有 8 路电气 + 温度),点表 `0x401E/0x401F` 也注明"FTU 无"。 | 行 | 显示名 | 数据源 | 说明 | |---|---|---|---| | 1 | `温度` | `adc_input_raw(ADC_SEM_TEMP)` | 码值,未标定 | | 2 | `直流通道` | 常量 | 值列显示 `未配置` | - 规模:**S**。 - **待拍板**(三选一):① 按上表做(推荐,保留菜单语义与点表一致);② 菜单改名"温度量"(改产品语义);③ 删节点(与参考/点表不一致)。 - 【2026-10-25 落地】按方案①落了(`Drivers/BSP/UI/ui_page_dc.c`)。温度值列写成 `码2048`:那个"码"字把 "这是 12 位原始码、不是温度"写进屏上而不是写进注释(通道本身带 `UI_DATA_Q_RAW`,页面不挂单位)。 #### 3.1.3 遥信量(新建) 33 点表,但只有 6 点有硬件来源(`YX_VALID_MASK`:YX15/16/17/18/23/31)。 | 方案 | 行表 | 优点 | 缺点 | |---|---|---|---| | **A 固定 33 行(推荐)** | 33 行固定名表,无效点值显 `--` | 与点表一一对应,现场可对点;开入落地后**页面零改动**自动变真 | 5 页里 4 页多 `--` | | B 只显有效点 | `count_cb` 返回有效点数 | 紧凑 | 看不出缺哪些点;开入落地后行数变化,`ui-golden` 需重做 | 推荐 A。行表(节选,全 33 项按点表): | 行 | 名 | 有效 | 值 | |---|---|---|---| | 1~4 | `YK1~4 位置` | ❌ | `--`(双点,无硬件) | | 5~14 | `YX5~14` | ❌ | `--` | | 15 | `故障总` | ✅ | `动作`/`复归` | | 16 | `过流Ⅰ段` | ✅ | 同上 | | 17 | `过流Ⅱ段` | ✅ | 同上 | | 18 | `过流Ⅲ段` | ✅ | 同上 | | 19 | `零序Ⅰ段` | ❌ | `--` | | 20 | `零序Ⅱ段` | ❌ | `--` | | 21 | `接地故障` | ❌ | `--` | | 22 | `重载` | ❌ | `--`(定值源缺失,见 `docs/问题记录/采集与数据处理短板评测.md` §4.3 运行参数段) | | 23 | `过载` | ✅ | `动作`/`复归` | | 24~30 | `外部遥信1~7` | ❌ | `--` | | 31~33 | `闭锁1~3` | 31 ✅ | 其余 `--` | - 名表放页面侧(Flash const),**不进快照**——显示文案属表现层(`docs/UI设计/UI_数据供给设计说明.md` §8)。 - 双点遥信 4 点用 `iec104_pts_yx_is_dp()` 决定是否显示"分/合",当前恒无效。 - 规模:**M**(33 行名表 + `refresh` + 值映射,约 150 行)。 > **落地注(2026-10-25 道E task-15 批 4)**:已按方案 A 落地为 `Drivers/BSP/UI/ui_page_yx.c` > (菜单 `遥信量 #page=yx`,8 行/页 = 5 页,黄金参考钉了第 1、2 两页)。四处要点: > 1. **两处命名不照抄本表简写**:idx0~3 本表写「YK1~4 位置」,屏上只写「位置1~4」——遥信页 > 出现 YK 前缀会被读成遥控对象(那是另一页的事),与遥控的对应关系留在代码注释里;idx30 > 本表写「闭锁1」,屏上写「保护闭锁」(写侧 `FEED_YX_BIT_LOCK` 的语义即此,用编号反而丢信息)。 > idx4~13 与 idx31~32 点表未定义功能名,屏上如实写点号占位(`YX5`…、`YX32`),**不编造功能名**。 > 2. **值映射判定次序是「先无效、再单双点、最后位值」**:无效位上的位值不代表任何事实,排在 > 后面就会把垃圾位画成「复归」。单/双点由快照 `yx_dp` 决定,不在页面复制点表事实(理由见 > `docs/UI设计/UI_数据供给设计说明.md` §4.1 的 `yx_dp` 条目)。 > 3. 本表「双点遥信用 `is_dp()` 决定是否显示分/合」落地为 **合位/分位** 两词,但该支路 V0.1 恒 > 无效(无开入硬件),像素与主机单测都覆盖不到 → 已登记为开入落地后的台架判据,不假装测过。 > 4. 落地时踩到一条对所有名表页通用的陷阱:把既有的 `*_PUT(dst, 字面量)` 宏用在 > `const char *const` 表项上,`sizeof(源)` 就成了**指针宽度**,CJK 名被截成第一个汉字而短 > ASCII 名恰好完好——编译器 / cppcheck / 编码钩子全不响,只有像素文本断言报出「某行名字 > 匹配不到、值却在原位」。长度参数必须取**目标缓冲容量**(`UI_BUF_STRLEN`)。 ### 3.2 参数定值(8 叶子 → 2 个模块) 8 个叶子(整定 4 + 查看 4)共用**一份** `ui_w_setlist` 组件 + **4 张视图表**。查看与整定的差别只是 `edit_capable` 与权限。 > 【2026-10-26 落地】查看 4 页(道E task-15 批 3)与**整定 4 页(道F task-2)都已落**: 八个叶子绑的是**同一批模块**(`view_param` / `view_set` / `view_word` / `view_plate`), 两态共用**同一份页面描述符**——差别不写在描述符里,而在 `pg_init` 按 `ui_content_page_is_editable()` 选 `ui_w_setview_attach()`(只读)还是 `ui_w_setview_attach_edit()`(可写)。 下方 §3.2.6 那张按两种描述符写的表已照此更正。 #### 3.2.1 40 项四视图划分 | 视图 | 项数 | 判据 | 页数(8 行/页) | |---|---|---|---| | 参数 | 8 | PT/CT 额定 + 4 个返回系数 | 1 | | 定值 | 18 | 数值型(电流/电压/频率/延时) | 3 | | 控制字 | 4 | `*_TRIP`(0 告警 / 1 跳闸) | 1 | | 软压板 | 10 | `*_EN` + `PS_GLOBAL_EN` | 2 | | **合计** | **40** | 与 `PS_ID_COUNT` 相等 | 7 | > 划分只改**显示分组**,不改枚举序、不改校验和覆盖序(`prot_set` 的枚举序 = 数组序 = 校验和序,只增不删不改序)。 #### 3.2.2 参数视图(8 项) | 行 | 显示名 | 枚举 | 单位 | 小数位 | |---|---|---|---|---| | 1 | `PT一次` | `PS_PT_PRI` | V | 0 | | 2 | `PT二次` | `PS_PT_SEC` | V | 0 | | 3 | `CT一次` | `PS_CT_PRI` | A | 0 | | 4 | `CT二次` | `PS_CT_SEC` | A | 0 | | 5 | `过量返回` | `PS_RET_OVER` | — | 2 ⚠ | | 6 | `欠量返回` | `PS_RET_UNDER` | — | 2 ⚠ | | 7 | `过频返回` | `PS_RET_OF` | — | 2 ⚠ | | 8 | `低频返回` | `PS_RET_UF` | — | 2 ⚠ | > 【2026-10-25 落地注·四张视图表(道E task-15 批 3)】四个查看页已接(`ui_page_view_param` / > `_set` / `_word` / `_plate`,共用 `Drivers/BSP/UI/ui_w_setview.c` 的四张行表 + `ui_w_setlist` > 组件)。三处口径落地时要说清楚,都写进了代码注释与提交正文,不留在这儿等人猜: > 1. **返回系数按 2 位小数显示(本表原写 3 位,已按可分辨精度修正,⚠ 标在表里)**。这不是 > 口径让步而是原口径本身错了:3 位来自"把 Q10 当成十进制存储"这个误会。Q10 存不下 0.95 > (0.95×1024 截成 972 → 真存 0.94921875),按 3 位显示等于多打一位根本不存在的数字; > 整定单上写 0.95 的人看到屏上 0.949 会怀疑装置、甚至反复重输(重输还是 0.949)。2 位才是 > 这份存储在自身可分辨精度上的正确读数。上下限、枚举序、单位口径由 > `tools/setview_check/check_setview_limits.py` 连 meta 一起核(已挂 `setview-check` 钩子)。 > 2. **单位挂在值串尾**(`10.00A` / `500ms`),不塞进显示名:值列只有数字会让 `60000` 既是 ms > 又是 A;A/V/ms/Hz 全 ASCII,不占中文字库、不触发 font-charset。为此给 `ui_w_setlist_row_t` > 加了 `unit` 字段(NULL = 不挂;只挂在非编辑态——编辑中是逐位输入串,挂单位会算错位光标)。 > 3. **上下限不自己拟定**:行表 min/max = `g_prot_set_meta` 的 Q10 上下限按该行小数位换算的结果, > 事后由上述脚本核验(改了 meta 不改行表就会红)。 #### 3.2.3 定值视图(18 项) | 行 | 显示名 | 枚举 | 单位 | 小数位 | |---|---|---|---|---| | 1 | `Ⅰ段电流` | `PS_OC1_CUR` | A | 2 | | 2 | `Ⅰ段延时` | `PS_OC1_DLY` | ms | 0 | | 3 | `Ⅱ段电流` | `PS_OC2_CUR` | A | 2 | | 4 | `Ⅱ段延时` | `PS_OC2_DLY` | ms | 0 | | 5 | `Ⅲ段电流` | `PS_OC3_CUR` | A | 2 | | 6 | `Ⅲ段延时` | `PS_OC3_DLY` | ms | 0 | | 7 | `过负荷电流` | `PS_OL_CUR` | A | 2 | | 8 | `过负荷延时` | `PS_OL_DLY` | ms | 0 | | 9 | `过压定值` | `PS_OV_VOL` | V | 1 | | 10 | `过压延时` | `PS_OV_DLY` | ms | 0 | | 11 | `低压定值` | `PS_UV_VOL` | V | 1 | | 12 | `低压延时` | `PS_UV_DLY` | ms | 0 | | 13 | `零序定值` | `PS_3U0_VOL` | V | 1 | | 14 | `零序延时` | `PS_3U0_DLY` | ms | 0 | | 15 | `过频定值` | `PS_OF_HZ` | Hz | 2 | | 16 | `过频延时` | `PS_OF_DLY` | ms | 0 | | 17 | `低频定值` | `PS_UF_HZ` | Hz | 2 | | 18 | `低频延时` | `PS_UF_DLY` | ms | 0 | #### 3.2.4 控制字视图(4 项) | 行 | 显示名 | 枚举 | 值 | |---|---|---|---| | 1 | `Ⅰ段跳闸` | `PS_OC1_TRIP` | `告警`/`跳闸` | | 2 | `Ⅱ段跳闸` | `PS_OC2_TRIP` | 同上 | | 3 | `Ⅲ段跳闸` | `PS_OC3_TRIP` | 同上 | | 4 | `过负荷跳闸` | `PS_OL_TRIP` | 同上 | #### 3.2.5 软压板视图(10 项) | 行 | 显示名 | 枚举 | 值 | |---|---|---|---| | 1 | `Ⅰ段投入` | `PS_OC1_EN` | `投入`/`退出` | | 2 | `Ⅱ段投入` | `PS_OC2_EN` | 同上 | | 3 | `Ⅲ段投入` | `PS_OC3_EN` | 同上 | | 4 | `过负荷投入` | `PS_OL_EN` | 同上 | | 5 | `过压投入` | `PS_OV_EN` | 同上 | | 6 | `低压投入` | `PS_UV_EN` | 同上 | | 7 | `零序投入` | `PS_3U0_EN` | 同上 | | 8 | `过频投入` | `PS_OF_EN` | 同上 | | 9 | `低频投入` | `PS_UF_EN` | 同上 | | 10 | `保护总投入` | `PS_GLOBAL_EN` | 同上 | #### 3.2.6 查看与整定的差别 | 项 | 查看(4 页) | 整定(4 页) | |---|---|---| | 权限 | 无密码 → `UI_PERM_VIEW` | 密码 → 按所输密码授 `VIEW`/`MODIFY`(123 = 修改、000 = 查看) | | `edit_capable` | 描述符置 1,但 `is_editable()` 为假 → 走只读 attach | 同一份描述符,`is_editable()` 为真 → 走 `attach_edit` | | 组件回调 | `commit = state = NULL` → **不画保存行**、ENTER 不消费(绑了 `key_cb` 也等价于没绑) | 绑 `set/commit/state` → 画保存行、放开编辑 | | 页脚 | 只显示页码 | 额外一行"保存"(挂在**末行之后**,不是固定第 8 行) | | 提交 | — | 只提交**改动位图**内的 id(一次封存、一次校验和),经 `ui_data_param_request()` 投递,App 侧 `drain` 落定 → 见 `docs/UI设计/UI_数据供给设计说明.md` §6.2 | | 越界反显来源 | `par.bad_mask`(装载侧,且 `bad_valid=1` 才参与) | 同上 **+** `ui_data_param_err_mask()`(本次提交被整批预检挡下的项) | | 闭锁 | 无写通路,不需要表达 | `par.locked=1` 或投递回 `ERR_LOCKED` → 保存行显 `LOCK` **且 ENTER 一律不消费**(禁编辑,不是只换个词) | | 未标定 | 不画(查看页像素恒零变化) | 标题右侧「未标定」徽标(`par.cal_block`);与 `LOCK` 是两件事,两源两通道两词 | > 保存行占掉一个可见行 → 整定页行数 = 视图项数 + 1,分页随之增加:参数 9 行 2 页、定值 19 行 3 页、控制字 5 行 1 页、软压板 11 行 2 页(合计 8 页)。这是"保存必须整表封签"带来的必然代价,不是设计缺陷。 > 【2026-10-26 落地实测 + 两条必修缺陷】上述分页与黄金参考逐页对上(`sparam_p2` / > `sset` / `sword_cycle` / `splate` 四条用例的页脚分别是 > 「共 2 页 / 共 3 页 / 共 1 页 / 共 2 页」)。**「整表封签」指的是一次保存只封存一次、 > 只重算一次校验和,不是「把 40 项全写一遍」**——实际只写用户改过的项:Q10 存不下 0.95 这类 > 返回系数(真存 972/1024),从显示值回提全表会把用户没动的项顺手改掉。 > > 这一批暴露了两条「只有第一个消费者才会暴露」的缺陷(都已修;写路径口径见 > `docs/UI设计/UI_数据供给设计说明.md` §6.2/§6.3): > ① `ui_w_setlist` 的二态行 ENTER **不回绕**——落在最后一项时写出 opts 之外的 > 下标,屏上看着「按了没反应」,叠层里却已是越界值,随保存投出去必被预检判 `ERR_RANGE`; > 控制字 4 行与软压板 10 行的出厂值**全是**最后一项(跳闸 / 投入),所以是必现而不是边角。 > 本方案落地之前,没有任何页面按过这条键(查看页 `commit=NULL` 走不到)。 > ② 编辑态的「参数帧已就位」原先只在**绘制路径**里被置位,而按键门读它 → 无头回归里开页后 > 第一个 ENTER 永远被挡(真机开页 10 ms 内就重绘,症状只在测试侧出现)。修法是让 > 「数据就位」与「画过没有」解耦:判就位之前先按 seq 做一次 sync(帧未变时只是一次比较)。 > > 另记一条生成器侧的事实(不在本道修,已登记为待办「生成器幂等性检查」):菜单树重跑时 > `ui_menu_tab.h` 有一行注释跟着变了 —— 说明 HEAD 上生成器的模板文字与已提交的 > 生成物本来就**不同步**(改模板那笔没重跑生成器)。生成物是产物,以生成器输出为准即可, > 但这条滞后不补,任何人都可能在不知情的情况下把它 revert 掉。 #### 3.2.7 编辑交互(复用网口设置页的模型) | 键 | 正常模式 | 编辑模式 | |---|---|---| | ↑↓ | 移选(框架内置) | 当前位 +1 / −1(按项步进) | | ←→ | ← 返回上级(框架) | 移动数字位光标 | | ENTER | 进编辑 / 触发保存行(**闭锁态 `LOCK` 下一律不消费**) | 确认本项(越界则拒退并打提示,值不进叠层) | | ESC | 关页(框架恒可拦截除外) | 关页(未保存的调整留在 RAM,不落盘) | - 权限不足时 ENTER 一律忽略(`ui_content_page_is_editable()` 判);**权限够但装置写不进去时同样忽略**(`state()` 回 `LOCK` 或帧里 `locked=1`)——前者是「这人不能写」,后者是「这台装置现在写不进」,两件事都必须落在键路上,只改屏上词等于让用户白改一轮。 - 编辑中「←」是**移动光标**而不是关页:描述符要置 `key_intercept = 1u`,框架才先把 LEFT 交页面(`simulator/tests/` 的 `sparam_edit` / `sparam_oob` 两条用例走的就是这条路)。 - 越界项在保存失败后由 `bad_mask` 高亮(`draw_cb` 画反显)。 - 规模:`ui_w_setlist` **M**(约 250 行),`ui_w_editor` **M**(约 180 行)。 ### 3.3 三遥设置(5 页,阻塞于 `run_par`) **现状**:死区是**编译期常量**——`App/protocol/iec104/iec104_pts.c` 的 `k_yc[].deadband`(第 244 行 `if (d > k_yc[i].deadband)`)。其余四项(系数/类型/防抖/双点)在固件里**完全没有对应结构**。 **所以这 5 页不是页面工作,是一个参数模型工作。** | 页 | 项数 | 数据模型 | 消费方 | |---|---|---|---| | 遥测死区 | 9 | `run_par.deadband[9]`(float,工程量) | `iec104_pts_cov_check`(替换 `k_yc[].deadband`) | | 遥测系数 | 9 | `run_par.gain[9]`(Q10 微调,1.000 基准) | `iec104_pts_yc_read`(乘在 fAmSQ 上) | | 遥信类型 | 33 | `run_par.yx_type[33]`(单点/双点/常闭) | `iec104_pts` 的 `M_SP`/`M_DP` 选择 | | 遥信防抖 | 33 | `run_par.yx_debounce[33]`(ms) | 开入采集(尚未实现,**当前无消费方**) | | 双点遥信 | 4 | `run_par.dp_map[4]`(虚端子映射) | 开入采集(同上) | **关键判断**: - 死区/系数两项**有消费方**(104 层),做完立刻生效 → 值得先做。 - 遥信类型/防抖/双点三项的消费方是**开入采集**,而开入采集在固件里不存在 → 现在做页面 = 配置无处生效,属于"存了不用的参数"。**建议随开入量采集一起做**,不在本批。 - 因此本批只做 **2 页(死区、系数)**,其余 3 页等开入。 **`run_par` 模块要点**(与 `prot_set` 并列,不合并——运行参数是"可随时改的运行旋钮",定值有 sum32 封存/闭锁语义,混在一起会搅乱闭锁判断): ```c /* App/algo/run_par.h —— 运行参数(无封存语义,可随时改) */ #define RUN_PAR_YC_N 9u #define RUN_PAR_YX_N 33u int run_par_init(void); /* 出厂默认 */ float run_par_deadband(uint8_t yc_idx); int run_par_set_deadband(uint8_t yc_idx, float v); /* 范围校验 */ /* 落盘走 cfg_file 新段 "runpar";键名 ≤15 字符(CFG_FILE_MAX_NAME_LEN) */ ``` - 规模:`run_par` **M**(表驱动 + 单测),2 个页面 **M**。 - 104 侧改动:`k_yc[].deadband` 改为查 `run_par`(1 行 + 去常量表字段),`yc_read` 乘增益(3 行)。 ### 3.4 装置维护(4 页) | 页 | 形态 | 数据/接口 | 规模 | 阻塞 | |---|---|---|---|---| | 时钟设置 | 表单(6 字段:年月日时分秒) | `sys_clock_get_time()` 预填 → `sys_clock_set_unix()` 提交 | **M** | 无 | | 强制复归 | 操作页(确认框) | `prot_elm_request_reset()` | **S** | 无 | | 手动录波 | 操作页(确认框) | **无录波模块** | — | ⛔ 等录波 | | 清除记录 | 操作页(选记录类型 + 确认) | 需新增 `syslog_clear(id)` + `soe_clear()` | **M**(API S + 页 M) | 无(API 自建) | - **时钟设置**:字段级编辑(年/月/日/时/分/秒),提交后立即 `sys_clock_set_unix()`;时钟无效时(`clock_ok=0`)允许设置。注意 RTC 可能慢 8 h(台架记录),页面不修正,只如实写。 - **强制复归**:调 `prot_elm_request_reset()`(任意上下文安全,单字标志),页脚回"已请求复归"。 - **手动录波**:**本批不建页**。菜单节点保留空叶子(现状行为 = 进空白终点框)。录波落地后再补。 - **清除记录**:需要 `sys_log` 加删除 API(按 ID 删文件 + 重建 + 计数归零),以及 `soe_store` 加清空 API。页面列出 9 类记录供选择,二次确认。 ### 3.5 通讯参数(3 页) | 页 | 形态 | 现状 | 结论 | |---|---|---|---| | 网口设置 | 已有真页 | `net_cfg` 全链闭环 | ✅ 不动 | | 通讯设置 | 只读信息页 | **当时无配置模型**(104 端口与 CA 都是 `#define`;2026-10-30 核实:现已在 `/config.ini` `[comm]` 段可配、UI 通讯设置页可改) | 做只读页,值经快照下发 | | SNTP设置 | — | **无 SNTP 客户端** | ⛔ 本批不建 | **通讯设置 4 行**(值必须在 App 侧取、经快照下发,页面不得自己 include 协议头): | 行 | 名 | 值来源 | |---|---|---| | 1 | `104端口` | `IEC104_PORT`(App 侧常量) | | 2 | `公共地址` | `iec104_serv` 的 CA(当前固定 1) | | 3 | `显示端口` | RemoDispBus 端口常量 | | 4 | `FTP用户` | FTP 配置(`admin`,鉴权启用后) | - 需要在 `ui_data_rt_t` **尾部追加**一个 `comm` 子结构(`port`/`ca`/`disp_port` + 8 B 用户名),按 `docs/UI设计/UI_数据供给设计说明.md` §4.3 的扩展规则升 `UI_DATA_VER`。 - 规模:**S**(快照 +4 字段,页面 60 行)。 - **SNTP设置**:等 SNTP 客户端(`App/` 下无任何 SNTP 代码)。现在建页只能存"没人读的参数"。 ### 3.6 记录查询(9 页) 9 页 = **2 个组件 + 5 个新日志 ID + 1 个二进制解码器 + 1 个摘要页**。 | 页 | 组件 | 数据源 | 页面规模 | 写入方规模 | |---|---|---|---|---| | 自检记录 | `ui_w_records` | `SYSLOG_SELFTEST` | ✅ 已有 | — | | 运行记录 | `ui_w_records` | `SYSLOG_RUN` | ✅ 已有 | — | | 事故记录 | `ui_w_records` | **新** `SYSLOG_ACCIDENT` | S | **M** | | 操作记录 | `ui_w_records` | **新** `SYSLOG_OPER` | S | **M** | | 保护告警 | `ui_w_records` | **新** `SYSLOG_ALARM` | S | **M** | | 保护启动 | `ui_w_records` | **新** `SYSLOG_START` | S | **M** | | 遥控记录 | `ui_w_records` | **新** `SYSLOG_YK` | S | **M** | | SOE记录 | **新** `ui_w_soe` | `/log/soe.log`(二进制 64 B) | **M** | 已有(`soe_store_post`) | | 运行报告 | 专用摘要页 | 最近一条 SOE 动作事件 | S | 无 | **5 个新日志 ID 的写入点**(这是本批真正的成本): | 记录 | 写入点 | 判据 | |---|---|---| | 事故记录 | `App/task/protect_task.c` 的 `s_log_active_edge()` | `asserted` 上升沿 **且** 该元件 `*_TRIP` 控制字 = 1 | | 保护告警 | 同上 | `asserted` 上升沿 **且** `*_TRIP` = 0 | | 保护启动 | 同上(新增 `picked` 边沿检测) | `picked` 上升沿 | | 操作记录 | 定值保存成功 / 强制复归 / 时钟设置 / 清除记录(4 处) | 动作成功后写一条 | | 遥控记录 | `iec104_pts_yk_exec()` 返回处 | 每次执行结果(成功/失败/选择态) | > **本表只是页面侧的需求清单**(记录页要消费哪几类),**不是记录层的登记表**: > 5 个 ID 的定名、记录格式、条数与字节上限、写入时机、以及是否真的落地,一律以 > `App/log` 侧为准,权威口径 `docs/子系统设计/运行与自检记录设计说明.md`; > 实施进度由记录侧任务回报后回填本文(本道不代记,免得同一件事在两份文档里各说一遍—— > 同一事实只在一处详述,见 `docs/文档规范.md`)。 **`ui_w_soe` 要点**: ```c /* Drivers/BSP/UI/ui_w_soe.h —— 与 ui_w_records 同构,但数据是二进制 */ typedef struct { uint8_t cache_valid; uint32_t last_rec; char ts[20]; char line[24]; /* "过流Ⅰ段 动作" */ } ui_w_soe_t; uint16_t ui_w_soe_count(ui_w_soe_t *inst); void ui_w_soe_refresh(ui_w_soe_t *inst, uint16_t row, char *name, char *value); ``` - 数据经注入的 reader 取(不 include `soe_store.h`),与 `ui_rec_reader_t` 同一模式但返回 `soe_rec_t`。 【2026-10-25 落地】页已接:`Drivers/BSP/UI/ui_page_soe.c`(菜单树 `SOE记录 #page=soe`, `tools/menu_gen/gen_menu.py` 重生成 + 双端注册自动化),reader 在两端适配层绑定 (真机 `App/task/ui_task.c` / 模拟器 `simulator/main.c` 的固定 3 条假记录)。 只读 API **不带类型掩码**(Lead 裁定 2026-10-25):5 个记录页怎么分组是界面语义,不进 日志存储层;环容量小,页渲染全扫无成本问题,真变大了再加带 mask 的优化 API(YAGNI)。 显示名(含类型 7 未标定闭锁 / 8 标定复归)留在 UI 侧——与"屏上的话归 UI、数据归 App"一致。 - 需要 `soe_store` 新增只读 API:`soe_count()` / `soe_read(idx, &rec)`(FatFs `f_open` + `f_lseek`,与 `syslog_get_record` 同构)。 【2026-10-25 落地】实到为 `App/log/soe_store.c` 的 `soe_store_count()` / `soe_store_read()`,照 `syslog_get_record` 那套**窗口 + 代数 + try 锁**纪律实现(8 条 / 512 B 一窗,命中即零 I/O)。 可见性规则钉在 `App/log/soe_store.h` 头注:条数只数整条 64 B 槽、写者在卷锁区间内自增代数、 单条版本/校验和不过一律 `-1`——**读者不得看到半成品记录**;`count` 与 `read` 之间新落一条只 容忍"本轮少看一条"(200 ms 重绘自然追上),不容忍读到半条。 - 每条记录 2 行(时间戳 / "元件+类型"),与文本记录页一致 → 用户学习成本为零。 **运行报告(最小可用版)**:3 行——`最近动作`(元件名)、`动作时间`、`当前状态`。数据取最后一条 `SOE_T_ELM_ASSERT`。完整版(故障前后有效值 + 波形摘要)依赖录波,不在本批。 **记录容量**:新增 5 类日志需各自定上限(参考现有:运行 1024 条/64 KB、自检 512 条/32 KB)。按"事故/告警/启动各 512 条 + 操作/遥控各 256 条"计 ≈ **128 KB**(64 B/条),NAND 余量需按 `docs/子系统设计/运行与自检记录设计说明.md` 的容量口径核对。 ### 3.7 装置信息(回炉) 现在 6 行全是字面量,其中"软件校验 4454"是参考项目的值,与本机无关。 | 行 | 名 | 值来源 | |---|---|---| | 1 | `终端型号` | 编译期常量 `APP_MODEL`(`F30`) | | 2 | `软件版本` | 编译期常量 `APP_SW_VER` | | 3 | `硬件版本` | 编译期常量 `APP_HW_VER` | | 4 | `程序日期` | `__DATE__`(编译期) | | 5 | `软件校验` | 编译期常量(构建脚本生成,或先显示 `--`) | | 6 | `MAC地址` | `net_cfg_get_mac()` | | 7 | `IP地址` | `net_cfg_get()` + `net_cfg_fmt_ipv4()` | | 8 | `运行时长` | 由 `ui_data_feed` 算开机 tick 差值(页面只拿秒数,不碰 RTOS) | - 新增 `App/app/app_ver.h`(4 个宏 + `__DATE__`),页面经快照取值。 - 规模:**S**。 - 说明:第 5 行"软件校验"若没有构建期 CRC 生成步骤,**先显示 `--` 而不是抄参考值**。 - 【2026-10-25 落地】8 行全接真值(`Drivers/BSP/UI/ui_page_device_info.c`):型号/软件/硬件/校验/日期/ 运行时长取装置信息帧,MAC/IP 取 `net_cfg` RAM 副本(与网口设置页同一个 `net_cfg_fmt_ipv4`, 不另起第二套格式化)。两处顺带修正的口径:① 标签去全角冒号、值列左对齐 x=56——17 字符 MAC 要 102 px,参考图的 x=86 容不下;② 页脚从参考遗留的固定"第 10 页"改成真页码"第 1 页,共 1 页" (屏上不出现与本机无关的数字,Lead 裁定 2026-10-25,像素基线随之单独提交)。 --- ## 4. 组件与模块 ### 4.1 已有 vs 待建 | 组件 | 状态 | 用途 | |---|---|---| | `ui_content_page` | ✅ 已有 | 页面框架(4 钩子 + 权限) | | `ui_menu` / `ui_menu_view` / `ui_menu_tab` | ✅ 已有 | 导航 + 显示 + 生成表 | | `ui_w_records` | ✅ 已有(需改为 reader 注入) | 文本记录页 | | `ui_data` | ⛔ 待建(步骤1) | 快照存储 | | `ui_data_feed` | ⛔ 待建(步骤2) | 快照组装 | | `ui_w_setlist` | ⛔ 待建 | 定值四视图共用 | | `ui_w_editor` | ⛔ 待建 | 数值编辑器 | | `ui_w_soe` | ⛔ 待建 | SOE 二进制解码 | | `ui_page_action` | ⛔ 待建 | 确认操作页(复归/清除/录波) | | `run_par` | ⛔ 待建 | 运行参数模型 | ### 4.2 六个待建件的接口要点 | 组件 | 关键接口 | 关键约束 | |---|---|---| | `ui_data` | `publish_rt/par`、`rt/par`、`rt_seq`、`param_request/state` | 单写者同上下文;不 include 任何 App 头 | | `ui_data_feed` | `ui_data_feed_tick()` | 唯一填充者;不在绘制路径调用 | | `ui_w_setlist` | `bind(view_tbl, count, commit_cb)` | 视图表含 `{name, enum_id, dot, unit}`;不直接调 `prot_set` | | `ui_w_editor` | `edit_begin(row)` / `edit_key(key)` / `edit_commit()` | 只改 RAM 副本;提交由页面统一发起 | | `ui_w_soe` | 同 `ui_w_records` | reader 注入;不 include `soe_store.h` | | `ui_page_action` | `bind(title, prompt, ok_cb)` | 二次确认;`ok_cb` 只投递请求,不阻塞 | --- ## 5. 菜单树改动 **本批不改菜单树**——当时 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()` 按 > 集合去重判孤儿页)。重跑生成器后 `UI_PAGE_MAP_COUNT` 12 → 16, > `uvprojx` 与 `CMakeLists.txt` 未变(没新增文件)。 > 生成器幂等性的一条滞后见 §3.2.6 落地注末段。 **唯一建议改动**(可选,需产品拍板):补参考项目的"厂家设置"分支。参考侧该分支有 18 个叶子(元件配置、恢复默认 ×5、交流显示、装置调试 ×8、版本信息)。若采纳,精简为 3 个叶子 + 2 个分组: | 建议叶子 | 理由 | |---|---| | 恢复默认 → 全部恢复 / 仅定值 / 仅软压板 | 现场救砖刚需 | | 装置调试 → 交流虚遥测 / 虚拟遥信 | 无标准源时的唯一验证手段 | | 装置调试 → 动作出口 | 出口测试 | (版本信息不单列,并入装置信息。) → 42 + 9 = **51 节点**(`厂家设置` + `恢复默认` + `装置调试` 三个分组节点 + 6 个叶子)。未超 255 上限,但 `ui-golden` 的全部菜单屏参考图需重拍。 --- ## 6. 分批与依赖 ### 6.1 批 0:前置能力(不产出可见页面,但决定后面是否可信) | 步 | 内容 | 验证 | |---|---|---| | 0-1 | `ui_data` + 主机单测 | `test/host/ui_data` 全绿 | | 0-2 | `ui_data_feed` + `ui_task` 接线 | 首帧串口锚点;`/keil-build` 0/0 | | 0-3 | `ui_w_editor`(数值编辑器) | 模拟器可改值 | | 0-4 | `ui_page_action`(确认操作页) | 模拟器确认/取消 | ### 6.2 批 1~4 | 批 | 页面 | 页数 | 依赖 | |---|---|---|---| | **1** | 交流量(回炉)、直流量(回炉)、装置信息(回炉)、参数/定值/控制字/软压板**查看**(4)、遥信量 | **8** | 批 0 | | **2** | 整定 4 页、时钟设置、强制复归、清除记录、通讯设置 | **8** | 批 1 + 写路径 | | **3** | 事故/操作/告警/启动/遥控记录(5)、SOE记录、运行报告、遥测死区、遥测系数 | **9** | 第二轮 + 5 个写入方 + `run_par` + `soe` 读 API | | **4**(可选) | 遥信类型/遥信防抖/双点遥信、手动录波、SNTP设置 | **5** | 开入采集 / 录波 / SNTP 客户端 | ### 6.3 依赖关系 ``` 批0 ui_data ──┬─→ 批1 实时数据 3 页 + 查看 4 页 ├─→ 批2 整定 4 页(写路径) └─→ 批3 记录 5 页(reader 注入)+ run_par 2 页 开入采集 ─────────→ 批4 遥信类型/防抖/双点 录波 ─────────────→ 批4 手动录波 + 运行报告完整版 SNTP 客户端 ──────→ 批4 SNTP设置 ``` **关键路径**:批 0 → 批 1 → 第二轮。批 3 的 5 个记录页可以并行(写入方在 App 侧,页面在 BSP 侧,互不阻塞)。 --- ## 7. 预算 | 项 | 量 | 说明 | |---|---|---| | Flash(页面代码) | ≈ 12 KB | 8 个新页面文件(`yx`/`set_ro`/`set_rw`/`runpar`/`clock`/`comm`/`soe`/`report`)× ~1.2 KB + 3 个回炉页 | | Flash(组件) | ≈ 8 KB | 6 个组件(`ui_data`/`ui_data_feed`/`ui_w_setlist`/`ui_w_editor`/`ui_w_soe`/`ui_page_action`)× ~1.4 KB | | Flash(字库) | ≈ **+10 KB** | 新增约 25 字(段/投/延/零/频/荷/闸/跳/低/过/Ⅰ/Ⅱ/Ⅲ 等),12 px 1 bpp ≈ 370 B/字 | | ROM 总量 | 268 KB → ≈ 298 KB | 1 MB Flash,余量充足 | | RAM(`ui_data`) | ≈ 380 B | 见 `docs/UI设计/UI_数据供给设计说明.md` §10 | | RAM(页面缓存) | ≈ 130 B/数据页 | 6 个数据页 ≈ 800 B | | LVGL 池 | 0 增量 | 页面全自绘,无子对象 | | 记录存储 | +160 KB NAND | 5 类新日志,需核 NAND 余量 | **字库是唯一需要提前处理的硬依赖**:新增中文名不在 `ui_cn_chars.txt` 里,必须重跑 `tools/font_gen/make_font.py`,否则屏上显示方块。 --- ## 8. 验收判据 | # | 判据 | 方法 | |---|---|---| | 1 | 27 个空叶子全部有页(除批 4 延后的 5 个) | 遍历菜单树,进入每个叶子不出现空白框 | | 2 | 3 个静态页全部接真数据 | 改变输入信号 → 页面值变 | | 3 | 未标定态可见 | `eng=0` 时交流量第 7 行显示"未标定" | | 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 页连跑两次逐字节一致——**该下限此后已扩至 66 张**(新增 clock / reset / clear 系列 13 张,其后 47→53→60→66,2026-10-29 录波 UI 道 +6 张),66 张上的双次逐字节一致**已验(2026-10-29:任务树重建模拟器后同一批 66 张连跑三次逐字节一致,页面池水位 5,448 B;菜单侧 `check_menu_rows.py` 8 状态连跑三次亦逐字节一致,5,368 B;三次之间未用更新模式写过任何参考图,故这是同口径的传递性证据)** | | 8 | 每个新页有 golden 参考 | `simulator/tests/ref/` 新增对应 `.bin`。**整定 4 页已有 9 张**(含保存/越界/落定/回绕各态);参考图入库,改它 = 改判据,须逐页过目 | --- ## 9. 待拍板 | # | 问题 | 选项 | 建议 | |---|---|---|---| | 1 | 交流量 7 行 vs 参考 24 行骨架 | 7 行紧凑 / 24 行留位 | **7 行**(不显示假 `--`) | | 2 | 直流量页处置 | 温度+未配置 / 改名温度量 / 删节点 | **温度+未配置** | | 3 | 遥信量 33 行固定 vs 只显有效点 | 固定 33 / 动态 6 | **固定 33**(可对点,零维护) | | 4 | 三遥设置 5 页是否全做 | 全做 / 只做死区+系数 | **只做 2 页**(另 3 页配置无处生效) | | 5 | 是否补厂家设置分支 | 补 6 节点 / 不补 | 看送检要求 | | 6 | 5 类记录是否都在本批 | 全做 / 先事故+操作 | 全做(写入点集中在一处) | --- ## 附:本文与既有文档的分工 | 文档 | 回答什么 | |---|---| | `docs/UI设计/页面补齐规划.md` | 27 页怎么归并、分几批、内存预算 | | **本文** | 每一页显示哪些行、数据从哪来、要改哪些代码 | | `docs/UI设计/UI_数据供给设计说明.md` | 数据怎么从 App 到 UI(通道/契约/并发/写路径) | | `docs/UI设计/内容页架构.md` | 页面框架本身(描述符/钩子/权限) | | `docs/问题记录/采集与数据处理短板评测.md` | 为什么有些数据现在没有(标定/遥测链/运行参数/录波) |