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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
/*
|
||||
* 函数功能:查询当前打开的页面是否可编辑
|
||||
* 入口参数:无
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <stdint.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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. 对端关闭或异常时复位客户端,等待下次重监听
|
||||
|
||||
Reference in New Issue
Block a user