feat(ui/remo): 7003 总线新增 STATUS 身份查询与 GOTO 直达页(#69)

STATUS(命令 4)一帧回答"现在在哪个画面":kind(MENU/PAGE/STANDBY)
+ 菜单路径/选中 + 页 id,布局契约与字节序见 ui_net_sink.h。由 uiTask
轮询驱动,同上下文读静态导航状态:零画面副作用、免锁、不发日志。
新增只读接口 ui_menu_get_nav_state / ui_content_page_current_desc。

GOTO(命令 5)按页 id 查生成表直开内容页,以 VIEW 权限进入(与
"已过查看密码"同档,不代输任何密码)。编译期门控
DTU_BENCH || UI_REMO_ALLOW_GOTO:产品构建恒拒(status=4),模拟器
构建默认开启便于无板验证;执行类页(clear/reset)固件侧同样拒绝
(status=2,与主机 EXEC_PAGE_IDS 同一事实源);待机中拒绝(status=5),
由主机按键唤醒后再来。成功应答带回目标叶节点下标便于对账。

ui_sink 主机单测补导航侧链接假件 + 最小 lvgl 桩(仅本测试目录)。
产品构建 Keil 0 Error 0 Warning;ui_sink 单测 17/17。
This commit is contained in:
2026-11-02 22:19:58 +08:00
parent b8e1de5f2e
commit 493fa6ba0e
13 changed files with 320 additions and 18 deletions
+56
View File
@@ -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 <stdint.h>
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 */
+32
View File
@@ -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"