两件事都落在这份方案文档里,合一笔提交(同一文件的两处改动按文件切分提交不干净,故合并说明)。 一、四个返回系数:口径从"我偏离文档"改成"文档原口径错了"(Lead 裁定 2026-10-25) - §3.2.2 表里 4 行小数位 3 → 2 并加 ⚠;落地注改写;同步去掉 ui_w_setview.c 注释与 tools/setview_check/check_setview_limits.py 文档串里的"偏离"措辞。 - 原口径 3 位来自"把 Q10 当成十进制存储"这个误会:Q10 存不下 0.95(0.95×1024 截成 972,真存 0.94921875),按 3 位显示等于多打一位根本不存在的数字;整定单写 0.95 的人看到屏上 0.949 会 怀疑装置、甚至反复重输(重输还是 0.949)。2 位才是这份存储在自己可分辨精度上的正确读数。 - 不改行为,只改文档与注释:屏上显示与行表换算逻辑一笔未动。 二、§3.1.3 落地注(遥信量页落地后回填,含两条命名取舍与一条通用陷阱) - 命名:idx0~3 屏上写「位置1~4」不写「YK1~4 位置」;idx30 写「保护闭锁」不写「闭锁1」;点表未 定义名的行用点号占位,不编造功能名。 - 值映射次序(先无效 → 再单双点 → 后位值)与双点支路"当前无自动化证据"的诚实登记。 - 通用陷阱:把 *_PUT(dst, 字面量) 宏用在 const char *const 表项上,sizeof(源) 变成指针宽度, CJK 名被截而短 ASCII 名恰好完好——只有像素文本断言会报"名字匹配不到而值在原位"。 跳过的门禁(三条同族):mem-numbers / mem-budget / size-gate(L0c),根因是台账与体积凭证待回填。
36 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)。
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 L1-7),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 |
✅ 真 | 当前态,无历史 |
| 功率/电度 | — | ❌ 禁止 | N-18:跨转换器不同步,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 与权限。
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 |
edit_capable |
0 | 1 |
| 页脚 | 只显示页码 | 额外一行"保存"(第 8 行固定为保存行) |
| 提交 | — | 整表 prot_set_set* + prot_set_seal() + 置脏,走请求/状态交接 |
保存行占掉一个可见行 → 整定页行数 = 视图项数 + 1,分页随之增加:参数 9 行 2 页、定值 19 行 3 页、控制字 5 行 1 页、软压板 11 行 2 页(合计 8 页)。这是"保存必须整表封签"带来的必然代价,不是设计缺陷。
3.2.7 编辑交互(复用网口设置页的模型)
| 键 | 正常模式 | 编辑模式 |
|---|---|---|
| ↑↓ | 移选(框架内置) | 当前位 +1 / −1(按项步进) |
| ←→ | ← 返回上级(框架) | 移动数字位光标 |
| ENTER | 进编辑 / 触发保存行 | 确认本项(写 RAM 副本) |
| ESC | 关页(框架恒可拦截除外) | 关页(未保存的调整留在 RAM,不落盘) |
- 权限不足时 ENTER 一律忽略(
ui_content_page_is_editable()判)。 - 越界项在保存失败后由
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 端口 2404、CA=1 都是 #define) |
做只读页,值经快照下发 |
| SNTP设置 | — | 无 SNTP 客户端 | ⛔ 本批不建 |
通讯设置 4 行(值必须在 App 侧取、经快照下发,页面不得自己 include 协议头):
| 行 | 名 | 值来源 |
|---|---|---|
| 1 | 104端口 |
IEC104_PORT(App 侧常量) |
| 2 | 公共地址 |
iec104_serv 的 CA(当前固定 1) |
| 3 | 显示端口 |
RemoDispBus 端口常量 |
| 4 | FTP用户 |
FTP 配置(admin,N-19 鉴权后) |
- 需要在
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() 返回处 |
每次执行结果(成功/失败/选择态) |
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 节点里,#page= 注记逐个补上即可(改 ui_menu_tree.txt 后重跑 python tools/menu_gen/gen_menu.py)。
唯一建议改动(可选,需产品拍板):补参考项目的"厂家设置"分支。参考侧该分支有 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 接线 |
首帧串口锚点;@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 | 批 2 + 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 → 批 2。批 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 一致 |
| 6 | 分层门禁无新增 | check_layer_deps.py 退出码 0,基线 6 → 3 |
| 7 | 像素回归稳定 | ui-golden 连续两次零差异 |
| 8 | 每个新页有 golden 参考 | simulator/tests/ref/ 新增对应 .bin |
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 |
为什么有些数据现在没有(标定/遥测链/运行参数/录波) |