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.
43 KiB
页面扩充详细方案(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 对页面设计的硬约束
- 未标定必须可见:
eng=0时页面要显式标"未标定",否则码值被当成伏特(现场误读)。 - 无源点显示
--,不显示0.000。 - 不在 UI 层算业务量:功率、电度、方和根一律等 App 侧给出。
- 中文名 ≤ 7 字:左列 x=7、值列 x=101 → 名称可用宽度约 94 px,12 px 字宽 ≈ 7 个汉字。
- 日志描述 ≤ 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 两页)。四处要点:
- 两处命名不照抄本表简写:idx0
3 本表写「YK14 位置」,屏上只写「位置14」——遥信页 出现 YK 前缀会被读成遥控对象(那是另一页的事),与遥控的对应关系留在代码注释里;idx30 本表写「闭锁1」,屏上写「保护闭锁」(写侧13 与 idx31~32 点表未定义功能名,屏上如实写点号占位(FEED_YX_BIT_LOCK的语义即此,用编号反而丢信息)。 idx4YX5…、YX32),不编造功能名。- 值映射判定次序是「先无效、再单双点、最后位值」:无效位上的位值不代表任何事实,排在 后面就会把垃圾位画成「复归」。单/双点由快照
yx_dp决定,不在页面复制点表事实(理由见docs/UI设计/UI_数据供给设计说明.md§4.1 的yx_dp条目)。- 本表「双点遥信用
is_dp()决定是否显示分/合」落地为 合位/分位 两词,但该支路 V0.1 恒 无效(无开入硬件),像素与主机单测都覆盖不到 → 已登记为开入落地后的台架判据,不假装测过。- 落地时踩到一条对所有名表页通用的陷阱:把既有的
*_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组件)。三处口径落地时要说清楚,都写进了代码注释与提交正文,不留在这儿等人猜:
- 返回系数按 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钩子)。- 单位挂在值串尾(
10.00A/500ms),不塞进显示名:值列只有数字会让60000既是 ms 又是 A;A/V/ms/Hz 全 ASCII,不占中文字库、不触发 font-charset。为此给ui_w_setlist_row_t加了unit字段(NULL = 不挂;只挂在非编辑态——编辑中是逐位输入串,挂单位会算错位光标)。- 上下限不自己拟定:行表 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_setlistM(约 250 行),ui_w_editorM(约 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_parM(表驱动 + 单测),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)(FatFsf_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_cfgRAM 副本(与网口设置页同一个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_COUNT12 → 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 |
为什么有些数据现在没有(标定/遥测链/运行参数/录波) |