dafc18f5ed
启动 docs/任务规划/README.md 的 W6 UI 扩展簇(用户裁定,覆盖 2026-10-11
的"本轮不排期")。四个工作包,全部验证通过。
## U1 设计令牌层(零行为变化)
新建 Drivers/BSP/UI/ui_tokens.h(20 个宏),框架 + 7 个页面 + App/task/
ui_task.c 全部接入。屏幕几何**复用 ui_port.h 既有 UI_SCREEN_W/H**,不重复
定义;令牌头自身只 include ui_port.h,不把 LVGL 拖进全部消费者。
补收了报告没列但代码里真重复的量:UI_TEXT_BAND_H 15(7 次)、列表左右边界
与每页行数(各 4 个页面逐字复制)、推流协议几何与显示几何同源(此前硬编码
3200 且注释写着"须与 UI_FB_SIZE 一致"——典型漂移隐患)、UI_TASK_TICK_MS
(ui_port_step 与 osDelay 必须同值,原先靠注释维系)。
故意不收页面私有几何与"同形不同义"的值(device_info 的 pitch=15 与列表
UI_ROW_PITCH=14 合并即改像素)。
## U3 页面注册表与生成器(删手工分发)
ui_page_registry.{c,h} 由生成器产出,**手工 menu_content_page_for() 已删
除**(仓库内仅存 2 处历史说明注释)。密码页按代码实况**单独出口**
(ui_page_registry_password())——它非节点绑定,任何 #P 叶子进同一页。
顺带落地此前判定"U1 阶段做不了"的共享 flat 样式 ui_style.{c,h}。
生成器除产注册表外**自动把新 .c 写进 uvprojx 与 CMakeLists.txt**,且
**双向对账**:加页打印 registered、删页打印 pruned,不留死条目;并双向
校验(#page 找不到实例报错、实例未被任何 #page 引用也报错)。
## U4 门禁补强(35 个钩子)
新增 font-charset 钩子(scripts/check_font_charset.py):复用
tools/font_gen 的同一套提取与 cmap 解析规则(importlib 直载,零第二套
规则),A 段查源码在用 vs 字符集、B 段查字符集 vs 字体 cmap。
registration-check 补第 ④ 类:实测原规则对"进了两端构建但没接描述符表"
的页面**全绿**——desc 被链接器剥离、编译链接全过、菜单永远打不开的死代码。
仍照不出的两项已如实写进文档(枚举漏加归 @build、菜单树漏挂归 golden)。
## 字体子集瘦身(D 案,4 留 3 删)
ui_cn_chars.txt 122→119 字(恰删 上/电/重,零新增)。异/常/片/芯 进
KEEP_EXTRA 带理由白名单:它们曾在 9eb1bd63 被写进中文自检描述落盘
/log/selftest.log(定长 64B 记录跨固件持久),删字形会使历史故障记录
渲染空白且不可逆。保留字按**必需**处理,反向有牙——谁只跑 extract_cn
把它们静默洗掉,钩子直接 FAIL 并逐字打印理由。
--strict 口径为"未知冗余 = 0"而非"冗余 = 0"。
## 验证
@build 0 错 0 警;ER_IROM1 232,052 B(较 HEAD −228 B)、RW_IRAM1
75,232 B **零增量**、RW_CCM 87.9% 未变。golden:菜单 8 状态全像素一致 +
内容页 11 页逐字节一致,**参考图 sha256 差异 0 条**(未用 --update 刷
基线);池水位菜单基线 5368 B、开页-关页往返 5448 B 与 U1 时相同。
38 个在途修改文件逐个核对 BOM/CRLF/U+FFFD 全部与 HEAD 一致。
L0 33 钩子 + L0.5 16 runner / 1104 断言全绿;check_doc_links --all
142 篇、无法判定 0;mem-budget / mem-numbers 已回填。
## 单位成本实测(U5 排期依据)
临时页 zzprobe 绑到"遥信量"叶子 → 只跑生成器 → 带页 @build 0/0 →
注册表出现 {5u, &ui_page_zzprobe_desc} → 回滚后 6 个产出文件 zzprobe
残留计数均 0、重编回基线。
**结论:新增一页 = 3 处人工(新 .c + 新 .h + 菜单树那一行加
#page=<名>)+ 1 条生成器命令,手工编辑注册文件 0 处。**
据此修正报告原口径"8 处降到 1 处"——那个 1 只在"1 处对既有文件的修改"
意义下成立,页面自身 2 个文件本就要写,真实处数是 3。
## 文档同步与两处自我修正
§0.1 UI 行 B → B+;§6.1「内容页」「设计令牌」两行按实况回填;§6.5 U1/U3
状态更新;§6 顶部加"建议栏不可照抄"总警示(本轮四次证伪:ui_port.h 已有
同名宏、LV_COLOR_BLACK/WHITE 不存在且极性反了、27 页实为目标页数现存 7
个、生成器草图漏密码页非节点绑定)。
两处改口:① §6.5 U4 从我上一版**误写的 3/3 改回 2/3**——"golden 脚本早已
存在"不等于"已进自动门禁",两个 golden 脚本至今未挂 pre-commit,该缺口
单列为待办;② 原计划"分三笔提交"**作废**,U1 与 U3 改同一批文件、文件内
交叠,硬切会产生不可编译的中间态,故整波一笔。
103 lines
3.6 KiB
C
103 lines
3.6 KiB
C
/*
|
||
* 模块名称:UI Page SelfTest (自检记录内容页)
|
||
* 模块功能:自检记录叶子节点(node 39)的内容页数据——动态列表,每条记录 2 行
|
||
* (偶数行 = 时间戳、奇数行 = 故障描述),可移选分页;数据源 = sys_log API
|
||
* (/log/selftest.log,开机自检故障,仅故障才写);空日志显示单条伪行"无记录";
|
||
* 向框架 ui_content_page 提供页面描述符(零 LVGL 对象)
|
||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||
* 作者:王建锋
|
||
* 创建日期:2026-09-14
|
||
* 修改记录:
|
||
* v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.4)
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#include <string.h>
|
||
|
||
#include "ui_page_selftest.h"
|
||
|
||
#include "ui_tokens.h"
|
||
|
||
#include "sys_log.h"
|
||
|
||
/* 静态变量定义区 */
|
||
|
||
/* 最近读取的记录缓存(偶数行读盘后,奇数行复用;避免同记录重复 I/O) */
|
||
static uint8_t s_cache_valid; /* 缓存有效标志(0 = 无缓存) */
|
||
static uint32_t s_last_rec; /* 已缓存的记录序号 */
|
||
static char s_ts[SYSLOG_TS_LEN + 1u];
|
||
static char s_desc[SYSLOG_DESC_MAX + 1u];
|
||
|
||
/* 函数定义区 */
|
||
|
||
/*
|
||
* 函数功能:总行数(动态;每条记录 2 行,空日志 = 单条伪行"无记录")
|
||
* 入口参数:无
|
||
* 返回值:总行数 uint16_t
|
||
*/
|
||
static uint16_t st_count_cb(void) {
|
||
uint32_t n = syslog_count(SYSLOG_SELFTEST);
|
||
|
||
if (n == 0u) {
|
||
return 1u; /* 空态伪行(页脚"共 1 页",避免"共 0 页") */
|
||
}
|
||
return (uint16_t)(n * 2u);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:逐行取数(偶数行 = 时间戳、奇数行 = 故障描述;值列恒空)
|
||
* 入口参数:row - 全局行下标 uint16_t
|
||
* name - 名输出缓冲(框架栈缓冲 UI_BUF_STRLEN=32,含 NUL) char *
|
||
* value - 值输出缓冲(同上;本页恒写空串) char *
|
||
* 限定条件:页面打开中(框架绘制/按键上下文,uiTask)
|
||
*/
|
||
static void st_refresh(uint16_t row, char *name, char *value) {
|
||
uint32_t n = syslog_count(SYSLOG_SELFTEST);
|
||
|
||
value[0] = '\0';
|
||
if (n == 0u) {
|
||
(void)memcpy(name, "无记录", sizeof("无记录"));
|
||
return;
|
||
}
|
||
|
||
{
|
||
uint32_t rec = row / 2u;
|
||
|
||
if (!s_cache_valid || rec != s_last_rec) {
|
||
if (syslog_get_record(SYSLOG_SELFTEST, rec, s_ts, sizeof(s_ts), s_desc, sizeof(s_desc)) != 0) {
|
||
name[0] = '\0'; /* 读失败(越界/存储异常):空行防御 */
|
||
return;
|
||
}
|
||
s_last_rec = rec;
|
||
s_cache_valid = 1u;
|
||
}
|
||
if ((row % 2u) == 0u) {
|
||
(void)memcpy(name, s_ts, sizeof(s_ts));
|
||
}
|
||
else {
|
||
(void)memcpy(name, s_desc, sizeof(s_desc));
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=selftest 绑定引用):
|
||
* 动态列表页——每记录 2 行、UI_LIST_ROWS 行/页 = 4 条/页;band_h=UI_ROW_BAND_H(同 AC/DC
|
||
* 约定:selectable 页选中行黑字必须配白带,band_h=0 会致选中行不可见);
|
||
* 时间戳 19 ASCII ≈114px < 屏宽可容
|
||
*/
|
||
const content_page_def_t ui_page_selftest_desc = {
|
||
.id = "selftest",
|
||
.title = "自检记录",
|
||
.selectable = 1u,
|
||
.top_y = UI_LIST_TOP,
|
||
.pitch = UI_ROW_PITCH,
|
||
.band_h = UI_ROW_BAND_H,
|
||
.text_dy = 0,
|
||
.left_x = UI_LIST_LEFT_X,
|
||
.right_x = UI_LIST_RIGHT_X,
|
||
.rows_per_page = UI_LIST_ROWS,
|
||
.count_cb = st_count_cb,
|
||
.refresh = st_refresh,
|
||
};
|