Files
STM32F4-Base/docs/UI设计/页面扩充详细方案.md
T
super_agent_bot 72b4b0943c docs: clear task and layer shorthand IDs
Replace task/batch shorthand IDs and residual layer labels in
human-facing docs with semantic phrases. Add the new cleanup
policy to docs/编号规范.md and update the entry docs.

Machine keys (TC/CODE-RULE/MENU-STATE/TRAP/FR), hardware
designators/pins/MAC, protocol/version numbers and frozen
historical evidence are intentionally kept.

Docs only; no code behavior change. Verified with
check_doc_links.py --all and git diff --check.
2026-11-02 03:33:51 +08:00

43 KiB
Raw Blame History

页面扩充详细方案(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. 两处命名不照抄本表简写:idx03 本表写「YK14 位置」,屏上只写「位置14」——遥信页 出现 YK 前缀会被读成遥控对象(那是另一页的事),与遥控的对应关系留在代码注释里;idx30 本表写「闭锁1」,屏上写「保护闭锁」(写侧 FEED_YX_BIT_LOCK 的语义即此,用编号反而丢信息)。 idx413 与 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 封存/闭锁语义,混在一起会搅乱闭锁判断):

/* 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 要点:

/* 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 为什么有些数据现在没有(标定/遥测链/运行参数/录波)