52e28885cb
三个薄页面模块 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() 待删(只有测试在读 的自检口),排在本笔之后单独一笔。
108 lines
4.1 KiB
C
108 lines
4.1 KiB
C
/*
|
||
* 模块名称:UI 记录页共享组件实现
|
||
* 模块功能:见 ui_w_records.h(逻辑照搬合并前的 ui_page_runlog.c / ui_page_selftest.c,
|
||
* 两文件差异仅为记录源编号与符号名,其余逐字相同;数据经注入 reader 取)
|
||
* 适用范围:Drivers/BSP/UI
|
||
* 作者:王建锋
|
||
* 创建日期:2026-10-20
|
||
* 修改记录:
|
||
* v1.0 2026-10-20 王建锋 创建(W6)
|
||
* v2.0 2026-10-25 王建锋 reader 注入(道B task-6):syslog_count/syslog_get_record
|
||
* 直调改为 s_reader 函数表调用;未注入按空态渲染
|
||
*/
|
||
|
||
#include <string.h>
|
||
|
||
#include "ui_w_records.h"
|
||
|
||
#include "ui_tokens.h"
|
||
|
||
/* 静态变量定义区 */
|
||
|
||
/* 注入的记录 reader(模块级单例,理由见头注;NULL = 未注入 → 空态) */
|
||
static const ui_rec_reader_t *s_reader;
|
||
|
||
/* 函数定义区 */
|
||
|
||
/*
|
||
* 函数功能:注入记录 reader
|
||
*/
|
||
void ui_w_records_bind(const ui_rec_reader_t *a_reader) {
|
||
s_reader = a_reader;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:按记录源编号查条数(不经行缓存的裸计数)
|
||
* 入口参数:log_id - 记录源编号(UI_W_REC_LOG_*) uint8_t
|
||
* 返回值:条数;未注入 reader 或编号未知一律 0,即空态,调用方按"无记录"显示
|
||
* uint16_t
|
||
* 限定条件:uiTask 上下文(记录侧绘制/按键链)
|
||
* 函数说明:为什么开这个口子——清除记录页要在 7 行上分行显示各类条数(点完
|
||
* "清除"之后条数归零就是它唯一的真反馈),而 reader 是本组件私有
|
||
* 的注入表;不开放一个只读入口,页面就只能自己去注第二份 reader,
|
||
* 那正好把 task-6 刚削掉的分层反向边又接回来(口径见
|
||
* docs/UI设计/UI_数据供给设计说明.md §7.2/§7.3)。
|
||
* 本函数不带行缓存:缓存属于"某一条记录的字段",与条数无关
|
||
*/
|
||
uint16_t ui_w_records_count_of(uint8_t a_log_id) {
|
||
if (s_reader == NULL) {
|
||
return 0u;
|
||
}
|
||
return s_reader->count(a_log_id);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:总行数(动态;每条记录 2 行,空日志 = 单条伪行"无记录")
|
||
* 入口参数:a_inst - 记录页实例 ui_w_records_t *
|
||
* 返回值:总行数 uint16_t
|
||
* 函数说明:未注入 reader 视同 0 条(空态伪行),两端启动期绑定前的开页窗口
|
||
* 也被覆盖(防御不崩)
|
||
*/
|
||
uint16_t ui_w_records_count(ui_w_records_t *a_inst) {
|
||
uint32_t n = (s_reader != NULL) ? s_reader->count(a_inst->log_id) : 0u;
|
||
|
||
if (n == 0u) {
|
||
return 1u; /* 空态伪行(页脚"共 1 页",避免"共 0 页") */
|
||
}
|
||
return (uint16_t)(n * 2u);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:逐行取数(偶数行 = 时间戳、奇数行 = 描述;值列恒空)
|
||
* 入口参数:a_inst - 记录页实例 ui_w_records_t *
|
||
* a_row - 全局行下标 uint16_t
|
||
* a_name - 名输出缓冲 char *
|
||
* a_value- 值输出缓冲(恒写空串) char *
|
||
* 限定条件:页面打开中(框架绘制/按键上下文,uiTask)
|
||
*/
|
||
void ui_w_records_refresh(ui_w_records_t *a_inst, uint16_t a_row, char *a_name, char *a_value) {
|
||
uint32_t n = (s_reader != NULL) ? s_reader->count(a_inst->log_id) : 0u;
|
||
|
||
a_value[0] = '\0';
|
||
if (n == 0u) {
|
||
(void)memcpy(a_name, "无记录", sizeof("无记录"));
|
||
return;
|
||
}
|
||
|
||
{
|
||
uint32_t rec = a_row / 2u;
|
||
|
||
if (!a_inst->cache_valid || rec != a_inst->last_rec) {
|
||
if ((s_reader->get)(a_inst->log_id, rec, a_inst->ts, sizeof(a_inst->ts),
|
||
a_inst->desc, sizeof(a_inst->desc)) != 0) {
|
||
a_inst->cache_valid = 0u;
|
||
a_name[0] = '\0'; /* 读失败(越界/存储异常):空行防御 */
|
||
return;
|
||
}
|
||
a_inst->last_rec = rec;
|
||
a_inst->cache_valid = 1u;
|
||
}
|
||
if ((a_row % 2u) == 0u) {
|
||
(void)memcpy(a_name, a_inst->ts, sizeof(a_inst->ts));
|
||
}
|
||
else {
|
||
(void)memcpy(a_name, a_inst->desc, sizeof(a_inst->desc));
|
||
}
|
||
}
|
||
}
|