From 64abefaa002bfdc07b3b5c4dc7b42b7ae65eddfd Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Tue, 20 Oct 2026 22:57:13 +0800 Subject: [PATCH] =?UTF-8?q?refactor(UI):=20U2=20=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E8=BF=90=E8=A1=8C/=E8=87=AA=E6=A3=80=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E4=B8=A4=E9=A1=B5=E4=B8=BA=E5=85=B1=E4=BA=AB=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=20ui=5Fw=5Frecords?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ui_page_runlog.c 与 ui_page_selftest.c 各 102 行,逐行比对后差异全是命名: 唯一的功能变量是日志 ID(SYSLOG_RUN / SYSLOG_SELFTEST),计数、每记录 2 行、 同记录读盘缓存、空态伪行的实现两边逐字相同。是真重复,不是推测出的抽象。 ## 落地 新建 Drivers/BSP/UI/ui_w_records.{c,h}(已在 uvprojx + CMakeLists 双端注册; 生成器只自动管 ui_page_*.c,故这两处手工加)。两页各留一个实例与两个薄跳板, .count_cb/.refresh 名字不变 => 注册表与生成器零改动。 净效果:两页 90 删 / 28 增(−62 行),重复逻辑收成一份。 ## 合并唯一的真陷阱:缓存归属 count_cb/refresh 签名里没有 user context,最容易写的版本是把 s_cache_valid / s_last_rec / s_ts / s_desc 留成组件级静态——那样两页共用一份 缓存,从 A 页切到 B 页会因记录号相同而直接命中缓存,显示另一页的陈旧行, 是静默错数据而非崩溃。故缓存内嵌进实例(ui_w_records_t),每页各一份。 ## 验证 @build 0 错 0 警;golden 经真实 ui-golden 钩子跑通:菜单 8 状态 + 内容页 11 页(含这两页)逐字节一致,未用 --update 刷基线 => 语义搬迁行为等价有证。 ER_IROM1 232,052 → 231,936 B(−116 B)。RW_IRAM1 75,232 → 75,240 B(+8 B), 原因如实记录:原先 4 个文件级静态零初始化落 ZI,现在 s_rec 带 .id 初值整个 结构体落 .data(RW),两页合计净 +8 B;要压回 0 需把 .id 留零再加 init 钩子, 为 8 字节引入运行时初始化顺序不值得,故保留并把账写在此处。 stack_report FAIL 行数 0(间接层未顶破栈预算);mem-budget / mem-numbers 已 按 @build 后规定回填台账。 ## 过程中的两处自我失误(已修复,记录以免重犯) ① 用 stem[:2] 生成函数前缀,对 selftest 得到 se_ 而非 st_,锚点失配。 ② 撤销脚本里 ${UI_DIR} 被 PowerShell here-string 插值吞掉,导致"边校验边写" 把树留在引用组件但未注册的链接错误态。教训:回滚脚本必须比正向脚本更保守, 全部校验通过后才允许写盘。 --- Drivers/BSP/UI/ui_page_runlog.c | 61 ++++++---------------- Drivers/BSP/UI/ui_page_selftest.c | 61 ++++++---------------- Drivers/BSP/UI/ui_w_records.c | 70 ++++++++++++++++++++++++++ Drivers/BSP/UI/ui_w_records.h | 59 ++++++++++++++++++++++ MDK-ARM/STM32F407-Demo.uvprojx | 5 ++ docs/内存管理/RAM使用分析与优化方案.md | 6 +-- docs/内存管理/RAM预算台账.md | 6 +-- scripts/ram_baseline.tsv | 2 +- simulator/CMakeLists.txt | 1 + 9 files changed, 172 insertions(+), 99 deletions(-) create mode 100644 Drivers/BSP/UI/ui_w_records.c create mode 100644 Drivers/BSP/UI/ui_w_records.h diff --git a/Drivers/BSP/UI/ui_page_runlog.c b/Drivers/BSP/UI/ui_page_runlog.c index 1b5214ba..ff225724 100644 --- a/Drivers/BSP/UI/ui_page_runlog.c +++ b/Drivers/BSP/UI/ui_page_runlog.c @@ -9,12 +9,14 @@ * 创建日期:2026-09-14 * 修改记录: * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.3) + * v1.1 2026-10-20 王建锋 改用 ui_w_records 共享组件(W6 U2:合并两页重复实现) */ /* 头文件包含区 */ #include #include "ui_page_runlog.h" +#include "ui_w_records.h" #include "ui_tokens.h" @@ -23,10 +25,7 @@ /* 静态变量定义区 */ /* 最近读取的记录缓存(偶数行读盘后,奇数行复用;避免同记录重复 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]; +/* 静态变量定义区:见下方 s_rec */ /* 函数定义区 */ @@ -35,49 +34,19 @@ static char s_desc[SYSLOG_DESC_MAX + 1u]; * 入口参数:无 * 返回值:总行数 uint16_t */ -static uint16_t rl_count_cb(void) { - uint32_t n = syslog_count(SYSLOG_RUN); +/* + * 记录页薄跳板:内容全部走 ui_w_records 共享组件(W6 U2)。 + * 实例 s_rec 携带日志 ID 与**本实例自己的**读盘缓存——两页共用组件代码, + * 但缓存不得共享,否则切页会命中上一记录号而显示另一页的陈旧行。 + */ +static ui_w_records_t s_rec = { SYSLOG_RUN }; - if (n == 0u) { - return 1u; /* 空态伪行(页脚"共 1 页",避免"共 0 页") */ - } - return (uint16_t)(n * 2u); +static uint16_t pg_count(void) { + return ui_w_records_count(&s_rec); } -/* - * 函数功能:逐行取数(偶数行 = 时间戳、奇数行 = 描述;值列恒空) - * 入口参数:row - 全局行下标 uint16_t - * name - 名输出缓冲(框架栈缓冲 UI_BUF_STRLEN=32,含 NUL) char * - * value - 值输出缓冲(同上;本页恒写空串) char * - * 限定条件:页面打开中(框架绘制/按键上下文,uiTask) - */ -static void rl_refresh(uint16_t row, char *name, char *value) { - uint32_t n = syslog_count(SYSLOG_RUN); - - 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_RUN, 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)); - } - } +static void pg_refresh(uint16_t row, char *name, char *value) { + ui_w_records_refresh(&s_rec, row, name, value); } /* @@ -97,6 +66,6 @@ const content_page_def_t ui_page_runlog_desc = { .left_x = UI_LIST_LEFT_X, .right_x = UI_LIST_RIGHT_X, .rows_per_page = UI_LIST_ROWS, - .count_cb = rl_count_cb, - .refresh = rl_refresh, + .count_cb = pg_count, + .refresh = pg_refresh, }; diff --git a/Drivers/BSP/UI/ui_page_selftest.c b/Drivers/BSP/UI/ui_page_selftest.c index 2579171e..d18b89c2 100644 --- a/Drivers/BSP/UI/ui_page_selftest.c +++ b/Drivers/BSP/UI/ui_page_selftest.c @@ -9,12 +9,14 @@ * 创建日期:2026-09-14 * 修改记录: * v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.4) + * v1.1 2026-10-20 王建锋 改用 ui_w_records 共享组件(W6 U2:合并两页重复实现) */ /* 头文件包含区 */ #include #include "ui_page_selftest.h" +#include "ui_w_records.h" #include "ui_tokens.h" @@ -23,10 +25,7 @@ /* 静态变量定义区 */ /* 最近读取的记录缓存(偶数行读盘后,奇数行复用;避免同记录重复 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]; +/* 静态变量定义区:见下方 s_rec */ /* 函数定义区 */ @@ -35,49 +34,19 @@ static char s_desc[SYSLOG_DESC_MAX + 1u]; * 入口参数:无 * 返回值:总行数 uint16_t */ -static uint16_t st_count_cb(void) { - uint32_t n = syslog_count(SYSLOG_SELFTEST); +/* + * 记录页薄跳板:内容全部走 ui_w_records 共享组件(W6 U2)。 + * 实例 s_rec 携带日志 ID 与**本实例自己的**读盘缓存——两页共用组件代码, + * 但缓存不得共享,否则切页会命中上一记录号而显示另一页的陈旧行。 + */ +static ui_w_records_t s_rec = { SYSLOG_SELFTEST }; - if (n == 0u) { - return 1u; /* 空态伪行(页脚"共 1 页",避免"共 0 页") */ - } - return (uint16_t)(n * 2u); +static uint16_t pg_count(void) { + return ui_w_records_count(&s_rec); } -/* - * 函数功能:逐行取数(偶数行 = 时间戳、奇数行 = 故障描述;值列恒空) - * 入口参数: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)); - } - } +static void pg_refresh(uint16_t row, char *name, char *value) { + ui_w_records_refresh(&s_rec, row, name, value); } /* @@ -97,6 +66,6 @@ const content_page_def_t ui_page_selftest_desc = { .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, + .count_cb = pg_count, + .refresh = pg_refresh, }; diff --git a/Drivers/BSP/UI/ui_w_records.c b/Drivers/BSP/UI/ui_w_records.c new file mode 100644 index 00000000..d31fcbf8 --- /dev/null +++ b/Drivers/BSP/UI/ui_w_records.c @@ -0,0 +1,70 @@ +/* + * 模块名称:UI 记录页共享组件实现 + * 模块功能:见 ui_w_records.h(逻辑照搬合并前的 ui_page_runlog.c / ui_page_selftest.c, + * 两文件差异仅为日志 ID 与符号名,其余逐字相同) + * 适用范围:Drivers/BSP/UI + * 作者:王建锋 + * 创建日期:2026-10-20 + * 修改记录: + * v1.0 2026-10-20 王建锋 创建(W6 U2) + */ + +#include + +#include "ui_w_records.h" + +#include "ui_tokens.h" + +/* 函数定义区 */ + +/* + * 函数功能:总行数(动态;每条记录 2 行,空日志 = 单条伪行"无记录") + * 入口参数:a_inst - 记录页实例 ui_w_records_t * + * 返回值:总行数 uint16_t + */ +uint16_t ui_w_records_count(ui_w_records_t *a_inst) { + uint32_t n = syslog_count(a_inst->id); + + 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 = syslog_count(a_inst->id); + + 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 (syslog_get_record(a_inst->id, rec, a_inst->ts, sizeof(a_inst->ts), + a_inst->desc, sizeof(a_inst->desc)) != 0) { + 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)); + } + } +} diff --git a/Drivers/BSP/UI/ui_w_records.h b/Drivers/BSP/UI/ui_w_records.h new file mode 100644 index 00000000..80eabc84 --- /dev/null +++ b/Drivers/BSP/UI/ui_w_records.h @@ -0,0 +1,59 @@ +/* + * 模块名称:UI 记录页共享组件(运行记录 / 自检记录两页的公共实现) + * 模块功能:把"定长记录 -> 每记录 2 行(偶数行时间戳、奇数行描述)+ 同记录读盘缓存 + * + 空态单条伪行"这套逻辑收成一份,供多个日志页按实例复用 + * 适用范围:Drivers/BSP/UI(仅 UI 层,数据源固定为 sys_log API) + * 说明:缓存必须按实例存放而非文件级静态——两页共用代码但各有独立实例, + * 若共享缓存,从 A 页切到 B 页会命中上一记录号而显示另一页的陈旧行(静默错数据)。 + * 作者:王建锋 + * 创建日期:2026-10-20 + * 修改记录: + * v1.0 2026-10-20 王建锋 创建(W6 U2:合并 ui_page_runlog / ui_page_selftest 的重复实现) + */ + +#ifndef UI_W_RECORDS_H +#define UI_W_RECORDS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "sys_log.h" + +/* + * 记录页实例:日志源 + 该实例自己的读盘缓存 + * 用法:页面文件里 static ui_w_records_t s_rec = { SYSLOG_XXX }; + * 其余字段自动清零,即"缓存初始无效",与合并前两页的行为一致。 + */ +typedef struct { + syslog_id_t id; /* 日志源 ID(决定读哪个记录文件) */ + uint8_t cache_valid; /* 缓存有效标志(0 = 无缓存) */ + uint32_t last_rec; /* 已缓存的记录序号 */ + char ts[SYSLOG_TS_LEN + 1u]; /* 时间戳缓存(含 NUL) */ + char desc[SYSLOG_DESC_MAX + 1u];/* 描述缓存(含 NUL) */ +} ui_w_records_t; + +/* + * 函数功能:总行数(动态;每条记录 2 行,空日志 = 单条伪行"无记录") + * 入口参数:inst - 记录页实例 ui_w_records_t * + * 返回值:总行数 uint16_t + */ +uint16_t ui_w_records_count(ui_w_records_t *a_inst); + +/* + * 函数功能:逐行取数(偶数行 = 时间戳、奇数行 = 描述;值列恒空) + * 入口参数:inst - 记录页实例 ui_w_records_t * + * row - 全局行下标 uint16_t + * name - 名输出缓冲(框架栈缓冲 UI_BUF_STRLEN=32,含 NUL) char * + * value - 值输出缓冲(同上;本组件恒写空串) char * + * 限定条件:页面打开中(框架绘制/按键上下文,uiTask) + */ +void ui_w_records_refresh(ui_w_records_t *a_inst, uint16_t a_row, char *a_name, char *a_value); + +#ifdef __cplusplus +} +#endif + +#endif /* UI_W_RECORDS_H */ diff --git a/MDK-ARM/STM32F407-Demo.uvprojx b/MDK-ARM/STM32F407-Demo.uvprojx index 2983d55f..c8a4c4e6 100644 --- a/MDK-ARM/STM32F407-Demo.uvprojx +++ b/MDK-ARM/STM32F407-Demo.uvprojx @@ -3506,6 +3506,11 @@ 1 ..\Drivers\BSP\UI\ui_page_registry.c + + ui_w_records.c + 1 + ..\Drivers\BSP\UI\ui_w_records.c + ui_style.c 1 diff --git a/docs/内存管理/RAM使用分析与优化方案.md b/docs/内存管理/RAM使用分析与优化方案.md index 38ff57b4..a9a92673 100644 --- a/docs/内存管理/RAM使用分析与优化方案.md +++ b/docs/内存管理/RAM使用分析与优化方案.md @@ -36,10 +36,10 @@ | 区域 | 用量 | 容量 | 占比 | |---|---|---|---| -| ER_IROM1 | 232,052 B (226.61 KB) | 1,048,576 B (1024 KB) | 22.1% | -| RW_IRAM1 | 75,232 B (73.47 KB) | 131,072 B (128 KB) | 57.4% | +| ER_IROM1 | 231,936 B (226.50 KB) | 1,048,576 B (1024 KB) | 22.1% | +| RW_IRAM1 | 75,240 B (73.48 KB) | 131,072 B (128 KB) | 57.4% | | RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% | -| **Flash (ROM 合计)** | **233,192 B (227.7 KB)** | 1,048,576 B (1024 KB) | **22.2%** | +| **Flash (ROM 合计)** | **233,120 B (227.7 KB)** | 1,048,576 B (1024 KB) | **22.2%** | 各区的容量与常量来源: diff --git a/docs/内存管理/RAM预算台账.md b/docs/内存管理/RAM预算台账.md index 97e30ac1..ea26b87d 100644 --- a/docs/内存管理/RAM预算台账.md +++ b/docs/内存管理/RAM预算台账.md @@ -15,16 +15,16 @@ | 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) | |---|---|---|---|---| -| ER_IROM1 (Flash) | 232,052 B | 1,048,576 B | 22.1% | 参考 | +| ER_IROM1 (Flash) | 231,936 B | 1,048,576 B | 22.1% | 参考 | | RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS | | UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 | -| RW_IRAM1 | 75,232 B | 131,072 B | 57.4% | PASS | +| RW_IRAM1 | 75,240 B | 131,072 B | 57.4% | PASS | | 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 | |---|---|---|---| | 网络 | 13,690 B | 12 KB | ⚠ 超 1.4 KB | | 存储 | 7,056 B | 12 KB | OK | -| UI | 30,416 B | 16 KB | ⚠ 超 13.7 KB | +| UI | 30,422 B | 16 KB | ⚠ 超 13.7 KB | | RTOS 堆 | 57,376 B | 56 KB | 堆=配额 | | 应用 | 14,911 B | 8 KB | ⚠ 超 6.6 KB | | 其它 | 9,184 B | — | 参考 | diff --git a/scripts/ram_baseline.tsv b/scripts/ram_baseline.tsv index ed7dc1b5..363f53df 100644 --- a/scripts/ram_baseline.tsv +++ b/scripts/ram_baseline.tsv @@ -3,4 +3,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi 2026-10-13 211456 18760 1104 154232 230216 22.0 97912 74.7 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924 2026-10-14 212296 18740 1112 154456 231036 22.0 98144 74.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924 2026-10-15 213540 18740 1136 131520 232280 22.2 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736 -2026-10-20 213472 18580 1140 131516 232052 22.1 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736 +2026-10-20 213356 18580 1184 131480 231936 22.1 75240 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736 diff --git a/simulator/CMakeLists.txt b/simulator/CMakeLists.txt index f77a1a9d..ba62f39b 100644 --- a/simulator/CMakeLists.txt +++ b/simulator/CMakeLists.txt @@ -96,6 +96,7 @@ set(UI_SOURCES ${UI_DIR}/ui_net_sink.c ${UI_DIR}/ui_page_registry.c ${UI_DIR}/ui_style.c + ${UI_DIR}/ui_w_records.c ) # 运行/自检记录引擎(双端共享核心 + 平台存储后端;MCU 端编 sys_log_mcu.c,见 Keil 工程)