Files
STM32F4-Base/Drivers/BSP/UI/ui_page_action.h
T
wandering1 52e28885cb feat(UI): 装置维护 3 页落地(时钟 / 强制复归 / 清除记录),内容页黄金 27→40
三个薄页面模块 ui_page_clock / ui_page_reset / ui_page_clear,页面注册由生成器
自动写双端(裁定 A-1:「三叶各绑一页」与「零新增 .c」不可兼得,选薄页面加生成器,
不写手写 node switch——那种分发在增删叶子时会静默换功能,且没有任何门禁照得到)。

数据侧是注入式函数表:UI 不 include App 层的头。两条硬理由都实测过——分层方向
(layer-deps)之外,模拟器根本不链 sys_clock,ALLOW 豁免挡得住 layer-deps,
挡不住模拟器链接期。面板对时的唯一真判据是回读 RTC:sys_clock_set() 恒返 0
(RTC 写失败只打日志),所以真机侧写 1 后回读 1,最坏约 200 ms 落在一次「保存」
按键上,IWDG 800 ms 窗有余;上界与依据都写进注释。

「无时钟」徽标位置有两个像素级实测坑:放标题带右侧会被 4 字标题连成「时钟设置
无时钟」一句话;放页脚带左侧 x=7 会压住页脚——页脚从 x=6 起排,不是居中。
最终落在清单末行与页脚之间的空带 y=124。clear_done 那张顺带给出像素证据:
清 run.log 之后「操作记录」那一行从 2 变 3,页面上没缓存、没撒谎。

ui_w_records 的 UI_W_REC_LOG_* 顺序必须等于 syslog_id_t 顺序(7 类,只准尾追加),
已加编译期顺序守卫;执行侧 7 条映射表同样带编译期守卫。

测试与真值:内容页黄金 27 → 40 页(+13),文本断言 +68、反向断言 ABSENT +19;
27 张旧参考 git diff 为空 = 逐字节未变。往返池 5448 B 与菜单常驻堆 5368 B 均未变;
字库未扩(源码在用 171 并 KEEP_EXTRA 39 = 清单 183),font-charset 与 cn-chars、
cn-font 字节一致。@build 0 错误 0 警告;uiTask 静态最坏链 1,336 B / 16,384
(余 91%)与基线相同,页面钩子没进最坏链。

一条测试学结论单独留痕:反向断言不能当「位置不重叠」的证据——本脚本的字形匹配
把 y 带判得比字面宽(徽标在 125..136、页脚在 144..155,仍被 (140,160) 判命中)。
该防线改由正向断言「页脚未被徽标压住」承担(压字那一版实测过不了它)。

两条自我更正一并记档(作者自报,按真值而非原口径):
① 先前报「文案零新增汉字」是错的——观测行拟用「动作元件」,「元」不在子集,
cn-chars 与 font-charset 如实红;最终改「保护动作 N」才让「零新增」成为事实。
顺序是:先按机器红,再按真值写结论。
② 先前报「6 个文件 BOM 逐个核过」被 bom-guard 证伪:丢 BOM 发生在上一次编译之后,
正是 AGENTS 那句「历史 0 警告不代表安全」的活例;此后一律以退出码为准。

未覆盖项不假装:台架「N>0 → N=0」在产品构建的未标定闭锁期拿不到(闭锁期 N 恒 0),
判据显式退化为「页面受理 + 落一条操作记录」,写在 rs_watch 注释里。

生成物入库例外照用:uvprojx 只加 3 个 ui_page_ 的 FilePath(真增删源文件),
CMakeLists 同批;两处都由 gen_menu.py 自己写,没手写。

本笔先走了一次真钩子:20 条钩子实跑通过(ui-golden 菜单 8 状态加内容页 40 页、
generated-fresh、registration-check、font-charset、ui-data 单测、cppcheck、
coding-standard、bom、mojibake、id-check、doc-links 等),只有 mem-numbers 与
mem-budget 两条红——磁盘上的 .map 出自 20:39 那次构建、含三条在飞道的代码,
与 HEAD 台账必然不同码。按仓库既有约定(这两条等台账回填时允许 --no-verify 并
正文留证)落这一笔;回填由我统一在收尾一笔做。

三条在飞道(task-26 崩溃证据路径、task-16 与 task-17 谓词收敛和卷锁观测、
task-36 台架工具)的未暂存改动被 autostash 暂时收走,钩子看到的是 HEAD 加本笔;
过程中曾把 comm 道未跟踪的 iec104_comm.c 临时移出(它不在 stash 覆盖面内),
那次移出反而让 ui-golden 撞见 CMake 缓存里的悬空引用而红——已改用「先落 comm
那笔、再落本笔」的顺序消掉这个中间态。ui_data_maint_ready() 待删(只有测试在读
的自检口),排在本笔之后单独一笔。
2026-10-26 22:43:54 +08:00

121 lines
7.7 KiB
C
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 动作确认页(操作页共享实现,ui_page_action)
* 模块功能:可复用的"确认 -> 二次确认 -> 执行回报"操作页**钩子三件套**——
* 强制复归 / 手动录波等破坏性叶子共用同一份确认链实现,各自持一份
* 实例(标题 / 问句 / 结果行 / 执行回调 / 可选观测行);
* 执行回调只投递请求不阻塞(跨任务交接由执行侧落定),
* 成功经 done_text 回报后再 ENTER 返回,done_text=NULL 成功即关页;
* 投递失败显示 ERR,下一次 ENTER 重新走二次确认;
* 只读权限(查看密码进入)可整页拒执行并显式说"没权限"(need_modify)
* 适用范围:Drivers/BSP/UI(表单页形态:无浅 V 装饰、无页脚——用户密钥页
* 跟随实机先例;ESC 恒关页 = 取消,LEFT 亦关页 = 取消)
* 说明:本组件**不建描述符也不开页**(v1.1 起的形态):内容页框架按描述符
* **指针**开页,而描述符只能由"一叶一模块"的页面文件导出(真源 =
* ui_menu_tree.txt 的 #page 注记 + 生成物 ui_page_registry.c)——
* 旧形态"组件自己 open()"决定了它永远不可能被注册表绑定(构建器不是
* 描述符,全仓零调用者即其化石证据)。改成 init/draw/key 三个收实例的
* 钩子后:确认链仍只有一份实现,而页面跳板薄到只剩"一份实例 + 一枚
* 描述符"(见 ui_page_reset.c),多个操作页各自持实例、互不串态
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道B task-5 三组件之一;规格 =
* 页面扩充详细方案.md §3.4 装置维护操作页 +
* §4.2 ui_page_action bind(title, prompt, ok_cb)
* 二次确认、ok_cb 只投递不阻塞)
* v1.1 2026-10-26 DTU 项目组 道F task-3(装置维护 3 页):改成钩子三件套
* 形态(init/draw/key 收实例指针),删掉模块单例
* 与 ui_page_action_open()——构建器开不了注册表
* 绑定的页(框架按描述符指针开页),旧形态全仓
* 零调用者;新增 need_modify(只读拒绝执行并显
* LOCK)与可选观测行(watch_label + watch_cb,
* 复归页用它把"动作位复归"画在同一屏上);
* 错误通道由 bsp_err_t 改为无返回 + uint8_t 分发标志
*/
#ifndef UI_PAGE_ACTION_H
#define UI_PAGE_ACTION_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include "lvgl.h" /* lv_key_t / lv_layer_t:钩子三件套的形参类型(本组件不建对象) */
/* 执行回调:只投递请求(事件位/消息队列),立即返回;
* 返回值:true = 投递被接受(页面转成功回报),false = 拒收(页面显示 ERR) */
typedef bool (*ui_page_action_ok_fn)(void);
/* 操作页实例(配置字段 = Flash 指针与只读回调,运行态 = 本模块写的两个字节);
* 用法:页面跳板 static 一份、花括号初始化配置,把 init/draw/key 三个钩子直接
* 转进本组件(v1.1 起本组件不再自己开页——内容页框架按描述符指针开页,
* 跳板就是那枚描述符的属主,见 ui_page_reset.c) */
typedef struct {
/* 页标题不在本结构里:描述符(连 title)由页面跳板 own —— 见 ui_page_reset.c;
* 两处各留一份标题就是两个真源,改一处屏上另一处不变那种 */
const char *prompt; /* Flash:确认问句(如"确认复归保护?"——
问句用字须已入字体子集,否则显示空白方块) */
const char *done_text; /* Flash:成功后结果行(如"已请求复归");
NULL = 成功后直接关页回菜单 */
ui_page_action_ok_fn ok_cb; /* 必填:执行投递(只投递,不等待执行结果) */
uint8_t need_modify; /* 1 = 只读权限(查看密码进入)时拒绝执行:
两次 ENTER 都不投递、态标位改显 LOCK。
破坏性动作必须置 1——只靠"屏上看着像能按"
拦不住人,必须落在键路上 */
const char *watch_label; /* Flash:可选观测行的前缀(NULL = 不画该行),
如"保护动作"。配合 watch_cb 让"复归前 N>0、
复归后 N=0"在同一屏上看得见 */
uint32_t (*watch_cb)(void); /* 可选:观测行取值(NULL = 不画该行;与
watch_label 要么都给要么都不给) */
uint8_t stage; /* 运行态:0 一次确认 / 1 二次确认 / 2 完成回报 */
uint8_t err; /* 运行态:1 = 上轮投递被拒(显示 ERR) */
} ui_page_action_t;
/*
* 函数功能:开页复位确认链(接框架的 init_cb;v1.1 起本组件不再自己开页)
* 入口参数:inst - 操作页实例(ok_cb 不得为 NULL;配置字段由跳板在编译期填好)
* ui_page_action_t *
* 返回值:无
* 限定条件:内容页框架开页链内调用(uiTask);实例为 NULL 时空操作(防御)
* 函数说明:每次进入固定从一次确认开始——"上一次按到一半"跨次保留下来就是
* 第二次进页一按 ENTER 直接执行,那是操作票事故的形状,不是效率优化
*/
void ui_page_action_init(ui_page_action_t *a_inst);
/*
* 函数功能:自绘(接框架的 draw_cb:问句 / 态标 / 可选观测行 / 键提示,四行居中)
* 入口参数:inst - 操作页实例 ui_page_action_t *
* layer - 绘制层 lv_layer_t *
* clip - 重绘区 const lv_area_t *
* 返回值:无
* 限定条件:框架绘制链末尾调用(本组件只画自己那几行,标题/装饰/页脚归框架);
* 实例或 prompt 为 NULL 时空操作
* 函数说明:态标行优先级——只读且需修改权限 > 二次确认 > 投递失败 ERR;
* 只读那条排最前是刻意的:现场最常见的误读是"我按了没反应",
* 把"没权限"显出来才不会让人反复按
*/
void ui_page_action_draw(ui_page_action_t *a_inst, lv_layer_t *a_layer,
const lv_area_t *a_clip);
/*
* 函数功能:按键处理(接框架的 key_cb:确认链状态机)
* 入口参数:inst - 操作页实例 ui_page_action_t *
* key - LVGL 键值 lv_key_t
* 返回值:1 = 已消费(框架跳过内置语义);0 = 未消费(交回框架)
* (键分发标志非错误通道,取 uint8_t 不用 bool——
* bsp-err/err-type 口径,同 ui_w_editor_key)
* 限定条件:经描述符 key_cb 挂接;LEFT/ESC 不进本函数(框架恒关页 = 取消)
* 函数说明:ENTER——stage0 转二次确认;stage1 调 ok_cb 投递:受理且有 done_text
* 转 stage2 回报、受理且无 done_text 直接关页、拒收置 err 留在 stage1;
* stage2 关页返回。只读且 need_modify 时任何 ENTER 都不推进也不投递
*/
uint8_t ui_page_action_key(ui_page_action_t *a_inst, lv_key_t a_key);
#ifdef __cplusplus
}
#endif
#endif /* UI_PAGE_ACTION_H */