diff --git a/Drivers/BSP/UI/ui_content_page.c b/Drivers/BSP/UI/ui_content_page.c index 2a49f153..7bfc062a 100644 --- a/Drivers/BSP/UI/ui_content_page.c +++ b/Drivers/BSP/UI/ui_content_page.c @@ -34,6 +34,8 @@ * 描述符指针、s_open 变指针(NULL = 未打开);7 个页面头包含移交 * ui_page_registry.c;日志页身份改打描述符 id(ASCII 页名,比原 * id=%u 更可定位);绘制/按键/几何一律未动 + * 2026-11-02 DTU 项目组 新增 ui_content_page_current_desc(#69 板端导航身份查询: + * 页 id 从"只有串口开/关沿可见"升级为可按帧查询) */ /* 头文件包含区 */ @@ -599,3 +601,15 @@ uint16_t ui_content_page_selected_row(void) { } return s_sel; } + +/* + * 函数功能:查询当前打开页面的描述符(契约见 ui_content_page.h,Issue #69) + * 入口参数:无 + * 返回值:当前页面描述符指针(Flash 常量,只读);无页打开 = NULL + * 限定条件:同上下文(uiTask)读取才有一致性语义;本函数零副作用、不打日志 + * 函数说明:与 ui_content_page_is_open 的区别只在于顺带交出身份(desc->id), + * 推流 STATUS 用它把"开着哪一页"随帧带回,不再只有串口开/关沿可见 + */ +const content_page_def_t *ui_content_page_current_desc(void) { + return s_open; +} diff --git a/Drivers/BSP/UI/ui_content_page.h b/Drivers/BSP/UI/ui_content_page.h index 5a936200..6b566633 100644 --- a/Drivers/BSP/UI/ui_content_page.h +++ b/Drivers/BSP/UI/ui_content_page.h @@ -124,6 +124,15 @@ void ui_content_page_invalidate(void); */ bool ui_content_page_is_open(void); +/* + * 函数功能:查询当前打开页面的描述符(身份查询/调试消费,Issue #69) + * 入口参数:无 + * 返回值:当前页面描述符指针(Flash 常量,只读);无页打开 = NULL + * 限定条件:与框架同上下文(uiTask)读取才有一致性语义 + * 函数说明:消费方(推流 STATUS)据 desc->id 回答"开着哪一页";零副作用、不打日志 + */ +const content_page_def_t *ui_content_page_current_desc(void); + /* * 函数功能:查询当前打开的页面是否可编辑 * 入口参数:无 diff --git a/Drivers/BSP/UI/ui_menu.c b/Drivers/BSP/UI/ui_menu.c index 1d691431..776340e7 100644 --- a/Drivers/BSP/UI/ui_menu.c +++ b/Drivers/BSP/UI/ui_menu.c @@ -283,3 +283,23 @@ int ui_menu_init(void) { (unsigned)(mon.total_size - mon.free_size)); return 0; } + +/* + * 函数功能:读取菜单导航状态(契约见 ui_menu.h,Issue #69) + * 入口参数:p_depth / p_path / p_sel - 三个只读出参,任一可为 NULL 表示不取 + * 返回值:无 + * 限定条件:同上下文(uiTask)读取;本函数零副作用、不打日志(每帧查询不刷日志) + * 函数说明:直接交还静态数组指针(导航状态本身仅 17 B,拷贝反而是第二份要同步的 + * 事实);消费方按"深度+1"项数读取,越界由消费方的深度钳位负责 + */ +void ui_menu_get_nav_state(uint8_t *p_depth, const uint8_t **p_path, const uint8_t **p_sel) { + if (p_depth != NULL) { + *p_depth = s_depth; + } + if (p_path != NULL) { + *p_path = s_path; + } + if (p_sel != NULL) { + *p_sel = s_sel; + } +} diff --git a/Drivers/BSP/UI/ui_menu.h b/Drivers/BSP/UI/ui_menu.h index 3a4e5eb7..c730c289 100644 --- a/Drivers/BSP/UI/ui_menu.h +++ b/Drivers/BSP/UI/ui_menu.h @@ -11,10 +11,14 @@ * 2026-09-02 王建锋 弃用已废弃的 lv_menu 控件,改为基元控件页面导航 * 2026-09-04 王建锋 重构为"横向级联框"模型(菜单页面设计.md):拆出显示层 * ui_menu_view 与数据层 ui_menu_tab;本层只留导航状态机 + * 2026-11-02 DTU 项目组 新增 ui_menu_get_nav_state(#69 板端导航身份查询: + * 主机侧不再只靠像素认屏猜"在哪一列选中哪一项") */ #ifndef UI_MENU_H #define UI_MENU_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -31,6 +35,20 @@ extern "C" { */ int ui_menu_init(void); +/* + * 函数功能:读取菜单导航状态(身份查询/调试消费的只读快照,Issue #69) + * 入口参数:p_depth - 输出当前深度(0 = 根框) uint8_t *,可为 NULL + * p_path - 输出路径数组指针(节点下标) const uint8_t **,可为 NULL + * p_sel - 输出各层选中下标数组指针 const uint8_t **,可为 NULL + * 返回值:无 + * 限定条件:与导航同上下文(uiTask/模拟器主循环)调用才有一致性语义; + * ui_menu_init() 前调用返回初始零值,不崩 + * 函数说明:输出指针指向本模块静态数组(共 17 B 导航状态,见设计 §7.2), + * 调用方只读、最多读"深度+1"项(上限 MENU_PAGE_MAX)、不得改写; + * 弹框被屏裁时像素认屏看不见选带,本接口是主机侧唯一的菜单真值出口 + */ +void ui_menu_get_nav_state(uint8_t *p_depth, const uint8_t **p_path, const uint8_t **p_sel); + #ifdef __cplusplus } #endif diff --git a/Drivers/BSP/UI/ui_net_sink.c b/Drivers/BSP/UI/ui_net_sink.c index c213035f..72ca5d33 100644 --- a/Drivers/BSP/UI/ui_net_sink.c +++ b/Drivers/BSP/UI/ui_net_sink.c @@ -39,6 +39,9 @@ * 在往返边界续命。钩子未注册 = 零行为变化(模拟器/黄金 * 回归不注册)。判读与设计口径见 * docs/UI设计/UI_设计架构.md §5.8 + * 2026-11-02 DTU 项目组 #69:新增 STATUS(4) 身份查询应答(kind+菜单路径/选中+页 id, + * v1 契约在 ui_net_sink.h)与 GOTO(5) 直开页(台架/模拟器构建, + * DTU_BENCH / UI_REMO_ALLOW_GOTO 门控;产品构建应答 NOSUPP) */ #include "ui_net_sink.h" @@ -50,6 +53,12 @@ #include "ui_tokens.h" /* 屏幕几何令牌(UI_SCREEN_W/H、UI_FB_SIZE 权威在 ui_port.h) */ #include "test_config.h" /* TEST_LATENCY_PROBE(链路时延探针编译开关) */ #include "net_latency_test.h" /* M3 探针:prod_cycle_mcu(LCDMEM 处理净成本) */ +#include "ui_menu.h" /* 菜单导航状态只读快照(STATUS 身份查询,#69) */ +#include "ui_menu_view.h" /* MENU_PAGE_MAX(STATUS 深度钳位上界) */ +#include "ui_content_page.h" /* 当前内容页描述符(STATUS 页 id,#69) */ +#include "ui_standby.h" /* 待机判定(STATUS)/ 待机拒开(GOTO) */ +#include "ui_page_registry.h" /* 叶节点→页注册表(GOTO 按 id 直开,#69) */ +#include "ui_pwd_def.h" /* UI_PERM_VIEW(GOTO 授只读权限,#69) */ /* (2026-10-02):发送短写/失败告警(MCU=控制台日志,模拟器=printf 桩) */ #define DBG_TAG "[UI_NET_SINK]" @@ -62,6 +71,8 @@ #define CMD_INIT UI_REMO_CMD_INIT #define CMD_KEY UI_REMO_CMD_KEY #define CMD_LCDMEM UI_REMO_CMD_LCDMEM +#define CMD_STATUS UI_REMO_CMD_STATUS +#define CMD_GOTO UI_REMO_CMD_GOTO /* 屏幕尺寸与帧大小取 ui_tokens.h / ui_port.h 的屏幕令牌(160x160 1bpp = 3200 字节), * 本模块不再自带同值字面量——显示几何与协议几何必须是同一个事实 */ @@ -71,6 +82,7 @@ static uint8_t g_tx[4 + 4 + UI_FB_SIZE + 1]; /* 发送组帧缓冲:[TAG][CMD][LEN][DATA][CRC] */ static uint8_t g_lcdmem_data[4 + UI_FB_SIZE]; /* LCDMEM 应答缓冲:4B 偏移 + 1bpp 位图 */ +static uint8_t g_status_data[32]; /* STATUS 应答缓冲:最大 28 B,见应答注释 */ static uint8_t g_rx[RM_RX_BUF]; /* 接收暂存区:整块 recv 后逐字节喂入状态机 */ static int g_listen = -1; /* 监听 Socket fd(未启动或已停止时为 -1) */ @@ -343,6 +355,122 @@ static void remo_handle(const uint8_t *f, int n) { } break; } + case CMD_STATUS: { + /* 身份查询 v1(#69,2026-11-02):一帧回答"现在在哪个画面"—— + * kind + 菜单路径/选中 + 页 id。本模块由 uiTask 轮询驱动(宿主契约 + * 见 ui_net_sink.h poll 文档),同上下文读静态导航状态,免锁、只读、 + * 零画面副作用。布局:[ver][kind][depth][n][id_len][path n B][sel n B] + * [页 id ASCII id_len B];上界 5 + 2n(≤8) + id_len(≤15) = 28 ≤ 32 */ + uint8_t depth = 0u; + const uint8_t *path = NULL; + const uint8_t *sel = NULL; + const content_page_def_t *desc; + uint8_t npath; /* 帧内 [n] 字段:path/sel 公共长度 */ + uint8_t id_len; + uint8_t kind; + uint16_t i; + uint16_t p; + + ui_menu_get_nav_state(&depth, &path, &sel); + if (depth >= MENU_PAGE_MAX) { + depth = (uint8_t)(MENU_PAGE_MAX - 1u); /* 防御钳位(正常不可达) */ + } + npath = (uint8_t)(depth + 1u); + desc = ui_content_page_current_desc(); + id_len = 0u; + if (ui_standby_is_active()) { + kind = UI_REMO_ST_STANDBY; /* 待机优先:path 仍为底层菜单态,页 id 不附 */ + } else if (desc != NULL) { + kind = UI_REMO_ST_PAGE; + if (desc->id != NULL) { + size_t l = strlen(desc->id); + id_len = (l > UI_REMO_MAX_ID) ? (uint8_t)UI_REMO_MAX_ID : (uint8_t)l; + } + } else { + kind = UI_REMO_ST_MENU; + } + p = 0u; + g_status_data[p++] = UI_REMO_STATUS_VER; + g_status_data[p++] = kind; + g_status_data[p++] = depth; + g_status_data[p++] = npath; + g_status_data[p++] = id_len; + for (i = 0u; i < npath; i++) { + g_status_data[p++] = path[i]; + } + for (i = 0u; i < npath; i++) { + g_status_data[p++] = sel[i]; + } + if (id_len > 0u) { + memcpy(g_status_data + p, desc->id, id_len); + } + (void)remo_send(CMD_STATUS, g_status_data, (uint16_t)(p + id_len)); + break; + } +#if defined(DTU_BENCH) || defined(UI_REMO_ALLOW_GOTO) + case CMD_GOTO: { + /* 直开页(#69,2026-11-02):按页 id 一步开内容页,替代多步按键盲走 + * (主机侧 scripts/board_ui_nav.py --goto)。仅台架构建(DTU_BENCH) + * 与模拟器(UI_REMO_ALLOW_GOTO)存在;产品构建走下方 NOSUPP 分支。 + * 契约:只读授权——与"过查看密码"的进入态同档;执行页(clear/reset) + * 拒(清单与主机 EXEC_PAGE_IDS 同一事实,动作前拒);待机中先拒 + * (STANDBY),主机按键唤醒后再来——不在此处双份判屏。 + * 应答 [ver][status][node],node = 目标叶节点下标(成功有意义,余 0xFF)。 */ + uint8_t rsp[3]; + char want[UI_REMO_MAX_ID + 1u]; + uint8_t hit; + uint8_t i; + + rsp[0] = UI_REMO_STATUS_VER; + rsp[2] = 0xFFu; + if ((len == 0u) || (len > UI_REMO_MAX_ID)) { + rsp[1] = UI_REMO_GOTO_NO_ID; + (void)remo_send(CMD_GOTO, rsp, 3u); + break; + } + memcpy(want, data, len); + want[len] = '\0'; + hit = 0xFFu; /* 哨兵 = 未命中 */ + for (i = 0u; i < (uint8_t)UI_PAGE_MAP_COUNT; i++) { + if (strcmp(g_ui_page_map[i].desc->id, want) == 0) { + hit = i; + break; + } + } + if (hit == 0xFFu) { + rsp[1] = UI_REMO_GOTO_NO_ID; + } else if ((strcmp(want, "clear") == 0) || (strcmp(want, "reset") == 0)) { + rsp[1] = UI_REMO_GOTO_EXEC; /* 执行页拒(清单与主机 EXEC_PAGE_IDS 一致) */ + } else if (ui_standby_is_active()) { + rsp[1] = UI_REMO_GOTO_STANDBY; /* 先按键唤醒再查(主机侧自动处理) */ + } else { + if (ui_content_page_is_open()) { + ui_content_page_close(); /* 先关已开页(open 守卫:同时只开一页) */ + } + if (ui_content_page_open(g_ui_page_map[hit].desc, (uint8_t)UI_PERM_VIEW)) { + rsp[1] = UI_REMO_GOTO_OK; + rsp[2] = g_ui_page_map[hit].node; + /* 身份留痕:开页本身有框架自带 "page open: id=" 边沿日志, + * 这里只补 goto 来源一条,便于台架日志对账 */ + DBG_INFO("ui jump ok id=%s node=%u", want, (unsigned)rsp[2]); + } else { + rsp[1] = UI_REMO_GOTO_FAIL; + } + } + (void)remo_send(CMD_GOTO, rsp, 3u); + break; + } +#else + case CMD_GOTO: { + /* 产品构建:GOTO 编译期剔除,应答 NOSUPP(主机侧据此回退按键走法) */ + uint8_t rsp[3]; + rsp[0] = UI_REMO_STATUS_VER; + rsp[1] = UI_REMO_GOTO_NOSUPP; + rsp[2] = 0xFFu; + (void)remo_send(CMD_GOTO, rsp, 3u); + break; + } +#endif default: /* 未知命令码:静默丢弃,保持连接等待后续帧 */ break; diff --git a/Drivers/BSP/UI/ui_net_sink.h b/Drivers/BSP/UI/ui_net_sink.h index 1b031d56..4520c738 100644 --- a/Drivers/BSP/UI/ui_net_sink.h +++ b/Drivers/BSP/UI/ui_net_sink.h @@ -9,6 +9,9 @@ * 2026-08-29 王建锋 修正监听端口为 7003,与 DTU-RemoteLCD 客户端默认端口对齐 * 2026-10-25 DTU 项目组 F3:新增 ui_net_sink_set_beat_cb()(看门狗心跳 * 回调注入,往返返回点喂位;未注册 = 零行为变化) + * 2026-11-02 DTU 项目组 #69:新增 STATUS(4) 身份查询与 GOTO(5) 直开页命令 + * (协议契约 UI_REMO_STATUS_VER / UI_REMO_ST_* / + * UI_REMO_GOTO_*,v1;主机侧解析器 = scripts/board_ui_nav.py) */ #ifndef UI_NET_SINK_H #define UI_NET_SINK_H @@ -26,6 +29,27 @@ extern "C" { #define UI_REMO_CMD_INIT 1 #define UI_REMO_CMD_KEY 2 #define UI_REMO_CMD_LCDMEM 3 +#define UI_REMO_CMD_STATUS 4 /* 身份查询:应答"现在在哪个画面"(v1,2026-11-02) */ +#define UI_REMO_CMD_GOTO 5 /* 直开页:按页 id 一步开内容页(台架/模拟器构建,2026-11-02) */ + +/* STATUS/GOTO 协议契约 v1(#69,2026-11-02;主机侧解析器 = scripts/board_ui_nav.py, + * 两端共用本组常量,禁止各自复制字面量) + * STATUS 应答布局:[ver][kind][depth][n][id_len][path n B][sel n B][页 id ASCII id_len B] + * kind 取值:MENU(菜单屏,path/sel 有效)/ PAGE(内容页开,页 id 尾附)/ STANDBY(待机屏) + * GOTO payload:页 id ASCII(≤ UI_REMO_MAX_ID 字节);应答 [ver][status][node] + * status 取值:OK(node=目标叶节点)/ NO_ID / EXEC(clear、reset 拒)/ FAIL / + * NOSUPP(产品构建,编译期剔除)/ STANDBY(先唤醒再查) */ +#define UI_REMO_STATUS_VER 1u +#define UI_REMO_MAX_ID 15u /* 页 id 字符串上限(现最长 wave_manual 10 字符,留余量) */ +#define UI_REMO_ST_MENU 0u +#define UI_REMO_ST_PAGE 1u +#define UI_REMO_ST_STANDBY 2u +#define UI_REMO_GOTO_OK 0u +#define UI_REMO_GOTO_NO_ID 1u +#define UI_REMO_GOTO_EXEC 2u +#define UI_REMO_GOTO_FAIL 3u +#define UI_REMO_GOTO_NOSUPP 4u +#define UI_REMO_GOTO_STANDBY 5u /* UI 推流监听端口(须与 DTU-RemoteLCD 客户端默认端口 7003 一致) */ #define UI_NET_SINK_PORT 7003 @@ -48,7 +72,7 @@ int ui_net_sink_start(uint16_t port); * 限定条件:ui_net_sink_start() 已成功调用 * 函数说明:1. 无客户端时先门控(net_listener_armed,纯 RAM 读)再尝试接受连接 * 2. 有客户端时先门控(net_can_read)再用 net_recv_nb 非阻塞收帧并解析 - * (KEEPLIVE/INIT/KEY/LCDMEM);门控判为无数据时仍每 32 轮兜底收一次 + * (KEEPLIVE/INIT/KEY/LCDMEM/STATUS/GOTO);门控判为无数据时仍每 32 轮兜底收一次 * (DONTWAIT 已转正为具名 API;uiTask 受 IWDG 监控, * 非阻塞是契约义务——见 net_socket.h net_recv 文档契约) * 3. 对端关闭或异常时复位客户端,等待下次重监听 diff --git a/docs/内存管理/RAM使用分析与优化方案.md b/docs/内存管理/RAM使用分析与优化方案.md index 384ca921..474e7877 100644 --- a/docs/内存管理/RAM使用分析与优化方案.md +++ b/docs/内存管理/RAM使用分析与优化方案.md @@ -47,10 +47,10 @@ dhara 失败类 +84;RO 另 +208,全是该失败类名表)→ `ER_IROM1` | 区域 | 用量 | 容量 | 占比 | |---|---|---|---| -| ER_IROM1 | 277,124 B (270.63 KB) | 1,048,576 B (1024 KB) | 26.4% | -| RW_IRAM1 | 94,336 B (92.12 KB) | 131,072 B (128 KB) | 72.0% | +| ER_IROM1 | 277,384 B (270.88 KB) | 1,048,576 B (1024 KB) | 26.5% | +| RW_IRAM1 | 94,368 B (92.16 KB) | 131,072 B (128 KB) | 72.0% | | RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% | -| **Flash (ROM 合计)** | **279,176 B (272.6 KB)** | 1,048,576 B (1024 KB) | **26.6%** | +| **Flash (ROM 合计)** | **279,436 B (272.9 KB)** | 1,048,576 B (1024 KB) | **26.6%** | 各区的容量与常量来源: diff --git a/docs/内存管理/RAM预算台账.md b/docs/内存管理/RAM预算台账.md index 88460cfc..4a015430 100644 --- a/docs/内存管理/RAM预算台账.md +++ b/docs/内存管理/RAM预算台账.md @@ -15,16 +15,16 @@ | 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) | |---|---|---|---|---| -| ER_IROM1 (Flash) | 277,124 B | 1,048,576 B | 26.4% | 参考 | +| ER_IROM1 (Flash) | 277,384 B | 1,048,576 B | 26.5% | 参考 | | RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS | | UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 | -| RW_IRAM1 | 94,336 B | 131,072 B | 72.0% | PASS | +| RW_IRAM1 | 94,368 B | 131,072 B | 72.0% | PASS | | 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 | |---|---|---|---| | 网络 | 13,702 B | 12 KB | ⚠ 超 1.4 KB | | 存储 | 7,072 B | 12 KB | OK | -| UI | 36,441 B | 16 KB | ⚠ 超 19.6 KB | +| UI | 36,473 B | 16 KB | ⚠ 超 19.6 KB | | RTOS 堆 | 57,376 B | 56 KB | 堆=配额 | | 应用 | 26,333 B | 8 KB | ⚠ 超 17.7 KB | | 其它 | 10,780 B | — | 参考 | diff --git a/scripts/ram_baseline.tsv b/scripts/ram_baseline.tsv index 9a098a17..a2d7ecc3 100644 --- a/scripts/ram_baseline.tsv +++ b/scripts/ram_baseline.tsv @@ -14,4 +14,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi 2026-10-29 238936 29140 1932 141396 268076 25.6 85904 65.5 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;sb_link.o=4364;ch395f.o=4162;console_log.o=4132;sys_log_mcu.o=4055 2026-10-29 243552 28784 1972 145388 272336 26.0 89936 68.6 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6420;nand_ftl.o=6216;wave_ring.o=5296;ch395f.o=4162;console_log.o=4132;sys_log_mcu.o=4055 2026-10-30 246720 29836 2052 149708 276556 26.4 94336 72.0 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6432;nand_ftl.o=6216;wave_ring.o=5296;sb_link.o=4376;ch395f.o=4162;console_log.o=4132 -2026-11-02 247288 29836 2052 149708 277124 26.4 94336 72.0 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6770;ui_port.o=6432;nand_ftl.o=6216;wave_ring.o=5296;sb_link.o=4376;ch395f.o=4162;console_log.o=4132 +2026-11-02 247548 29836 2052 149740 277384 26.5 94368 72.0 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6802;ui_port.o=6432;nand_ftl.o=6216;wave_ring.o=5296;sb_link.o=4376;ch395f.o=4162;console_log.o=4132 diff --git a/scripts/size_attestation.json b/scripts/size_attestation.json index 730deffa..0fcadeba 100644 --- a/scripts/size_attestation.json +++ b/scripts/size_attestation.json @@ -1,25 +1,25 @@ { - "built_at": "2026-11-02T18:21:27+08:00", - "fingerprint": "a4435f138bc6dc5fe18779977ec5a2515e15e72ceeecb9653ce2930b640e71c2", + "built_at": "2026-11-02T22:19:57+08:00", + "fingerprint": "19f80087a70f1c74afb1d71fb6d754b4b647321da90b312068144a8f31a9a935", "fingerprint_files": 1466, "fingerprint_source": "git-index", "gated_regions": [ "RW_IRAM1", "RW_CCM" ], - "git_head": "d6253aa974f4357779d0a94ff24ba66a58abf094", + "git_head": "b8e1de5f2e2515b528b0af21bcb8b423a406b8f2", "map": "MDK-ARM/STM32F407-Demo/STM32F407-Demo.map", - "map_mtime": 1793614877, + "map_mtime": 1793629184, "pct": { - "ER_IROM1": 26.43, + "ER_IROM1": 26.45, "RW_CCM": 87.89, - "RW_IRAM1": 71.97, + "RW_IRAM1": 72.0, "UNINIT_CCM": 18.75 }, "regions": { "ER_IROM1": { "max": 1048576, - "size": 277124 + "size": 277384 }, "RW_CCM": { "max": 65280, @@ -27,7 +27,7 @@ }, "RW_IRAM1": { "max": 131072, - "size": 94336 + "size": 94368 }, "UNINIT_CCM": { "max": 256, @@ -37,10 +37,10 @@ "schema": 2, "scope_note": "\u540c\u7801\u51ed\u8bc1\uff1a\u6307\u7eb9\u8986\u76d6\u56fa\u4ef6\u4f53\u79ef\u76f8\u5173\u6e90\u7801\uff0c\u53e3\u5f84\u89c1\u672c\u811a\u672c docstring", "sizes": { - "code": 247288, + "code": 247548, "ro_data": 29836, "rw_data": 2052, - "zi_data": 149708 + "zi_data": 149740 }, "threshold_pct": 92.0, "worktree_dirt_at_stamp": 0, diff --git a/simulator/CMakeLists.txt b/simulator/CMakeLists.txt index 873b3a0e..e226f970 100644 --- a/simulator/CMakeLists.txt +++ b/simulator/CMakeLists.txt @@ -179,6 +179,7 @@ target_compile_definitions(lvgl_sim PRIVATE LV_LVGL_H_INCLUDE_SIMPLE # ui_font_cn_12.c 内部 include 简化 WIN32_LEAN_AND_MEAN # 防止 Windows.h 拉入 winsock.h(与 winsock2.h 冲突) NET_NO_OS # 剥离 net_socket.h 的 FreeRTOS 相关接口(纯 BSD Socket) + UI_REMO_ALLOW_GOTO # 模拟器构建启用 GOTO(5) 直开页(#69,2026-11-02;台架 MCU 门控 DTU_BENCH,见 ui_net_sink.c 注释) ) target_include_directories(lvgl_sim PRIVATE diff --git a/test/host/ui_sink/stub/lvgl.h b/test/host/ui_sink/stub/lvgl.h new file mode 100644 index 00000000..8f9079c3 --- /dev/null +++ b/test/host/ui_sink/stub/lvgl.h @@ -0,0 +1,56 @@ +#ifndef TEST_UI_SINK_STUB_LVGL_H +#define TEST_UI_SINK_STUB_LVGL_H + +/* + * 测试存根 lvgl.h(#69,2026-11-02 新增) + * + * 为什么存在:ui_net_sink.c 新增 STATUS/GOTO 处理(#69 板端导航身份查询)后, + * 间接引用 ui_menu_view.h / ui_content_page.h,两者 include 真实 "lvgl.h"。 + * 本单测只覆盖成帧 / 心跳 / 状态机纯逻辑(见 run_unit_test.py 文件头), + * 运行期不触任何 LVGL,故此处仅提供上述头文件声明处用到的最小类型; + * 不模拟任何 LVGL 行为,也不参与 MCU 构建(stub 目录仅本测试使用)。 + * + * 维护约定:ui_content_page.h / ui_menu_view.h 的声明若新增 lv_* 类型依赖, + * 在此按需补最小 typedef;禁止在此写真实 LVGL 语义来"凑过"编译。 + */ + +#include + +typedef struct _lv_obj_t lv_obj_t; +typedef struct _lv_area_t lv_area_t; +typedef struct _lv_layer_t lv_layer_t; +typedef uint32_t lv_key_t; + +typedef struct { + uint8_t a; + uint8_t r; + uint8_t g; + uint8_t b; +} lv_color_t; + +/* ui_tokens.h 的 UI_COL_* 宏用到;本测试不断言其值,仅需可编译 */ +static inline lv_color_t lv_color_white(void) +{ + lv_color_t c; + c.a = 0xFFu; + c.r = 0xFFu; + c.g = 0xFFu; + c.b = 0xFFu; + return c; +} + +static inline lv_color_t lv_color_black(void) +{ + lv_color_t c; + c.a = 0xFFu; + c.r = 0x00u; + c.g = 0x00u; + c.b = 0x00u; + return c; +} + +/* ui_content_page.h 注释提及;为防后续声明引用,按真实 LVGL 键码给出 */ +#define LV_KEY_LEFT 0x14u +#define LV_KEY_ESC 0x1Bu + +#endif /* TEST_UI_SINK_STUB_LVGL_H */ diff --git a/test/host/ui_sink/ui_sink_test.c b/test/host/ui_sink/ui_sink_test.c index 6331a485..840be4b1 100644 --- a/test/host/ui_sink/ui_sink_test.c +++ b/test/host/ui_sink/ui_sink_test.c @@ -93,6 +93,38 @@ void ui_net_sink_inject_key(uint8_t k) { if (s_key_n < 16) { s_keys[s_key_n++] = k; } } +/* ============ 假件:导航侧(#69 STATUS/GOTO,2026-11-02 新增) ============ + * 本单测不发 STATUS/GOTO 帧,以下仅为链接占位:STATUS 恒为根菜单 + * depth=0、无页、非待机。若后续要断言 STATUS/GOTO 语义,把这些假件 + * 改成可控状态并新增用例,不要动生产分支。 */ +#include "ui_menu.h" +#include "ui_content_page.h" +#include "ui_page_registry.h" +#include "ui_standby.h" + +static const uint8_t s_nav_zero[4] = { 0u, 0u, 0u, 0u }; /* 根菜单 path/sel */ + +void ui_menu_get_nav_state(uint8_t *p_depth, const uint8_t **p_path, + const uint8_t **p_sel) +{ + if (p_depth != NULL) { *p_depth = 0u; } + if (p_path != NULL) { *p_path = s_nav_zero; } + if (p_sel != NULL) { *p_sel = s_nav_zero; } +} +const content_page_def_t *ui_content_page_current_desc(void) { return NULL; } +bool ui_content_page_open(const content_page_def_t *desc, uint8_t perm) +{ + (void)desc; (void)perm; return false; +} +void ui_content_page_close(void) { } +bool ui_standby_is_active(void) { return false; } + +/* 生成表占位:尺寸必须按 ui_page_registry.h 的固定条数(本头文件 extern + * 了 g_ui_page_map[UI_PAGE_MAP_COUNT])。静态零初始化 = 全表 {node=0, + * desc=NULL}:本单测从不发 GOTO 帧,生产的 desc 扫描不会被触达 */ +const ui_page_entry_t g_ui_page_map[UI_PAGE_MAP_COUNT] = { { 0u, + (const content_page_def_t *)0 } }; + /* ===================== 被测代码 ===================== */ #include "ui_net_sink.c"