UI 代码:补全 remo_handle/cp_draw 注释,拆分 cp_draw_cb 绘制函数
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
* 2026-09-10 王建锋 网口设置页(node 30):新增 CONTENT_PAGE_NET_CFG 入描述符表;
|
||||
* 新 API ui_content_page_selected_row(页面钩子感知选中行)+
|
||||
* 描述符 init_cb(开页后调一次,页面模块重置运行态)
|
||||
* 2026-09-13 王建锋 cp_draw_cb 按区块拆为四个绘制子函数(cp_draw_deco/header/rows/footer),
|
||||
* 主函数仅编排(行数取一次供行窗口/页脚共用),行为零变化
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
@@ -181,32 +183,34 @@ static void cp_draw_centered_text(lv_layer_t *layer, const char *text, int32_t y
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:页面自绘(浅 V 装饰 + 标题/副标题 + 行数据 + 页脚页码 + 页面钩子)
|
||||
* 入口参数:e - 事件指针 lv_event_t *
|
||||
* 函数功能:绘制"浅 V"装饰(共享 ui_deco 模块,与菜单根框同几何)
|
||||
* 入口参数:layer - 绘制层 lv_layer_t *
|
||||
* d - 页面描述符 const content_page_def_t *
|
||||
* 返回值:无
|
||||
* 限定条件:事件为 LV_EVENT_DRAW_MAIN,页面已打开
|
||||
* 函数说明: 1. 浅 V 装饰:共享 ui_deco 模块(与菜单根框同几何)
|
||||
* 2. 行数:静态页取 row_count,动态页取 count_cb();
|
||||
* 列表页窗口 = 选中行所在页(首可见行 = (sel/每页数)*每页数),
|
||||
* 行槽位置固定、数据随页切换;信息页全量静态绘制
|
||||
* 3. 行文本:静态页直绘 Flash 表(不拷内存);动态页经 refresh()
|
||||
* 逐行取入栈缓冲(同帧同步光栅化,安全)
|
||||
* 4. 选中行整行白带(x=2..156)+ 黑字,非选中白字
|
||||
* 5. 各区块按 culling 区跳过不在重绘区的部分;末尾调 draw_cb(页自定义内容)
|
||||
* 限定条件:页面已打开
|
||||
* 函数说明:每页可关(hide_deco;密码页跟随实机:无装饰)
|
||||
*/
|
||||
static void cp_draw_cb(lv_event_t *e) {
|
||||
const content_page_def_t *d = s_defs[s_open];
|
||||
lv_layer_t *layer = lv_event_get_layer(e);
|
||||
|
||||
/* "浅 V"装饰:共享 ui_deco 模块(与菜单根框同几何);每页可关(密码页跟随实机:无装饰) */
|
||||
static void cp_draw_deco(lv_layer_t *layer, const content_page_def_t *d) {
|
||||
if (d->hide_deco == 0u) {
|
||||
ui_deco_draw(layer, &layer->_clip_area);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:绘制标题/副标题(居中;整宽带 + culling)
|
||||
* 入口参数:layer - 绘制层 lv_layer_t *
|
||||
* d - 页面描述符 const content_page_def_t *
|
||||
* 返回值:无
|
||||
* 限定条件:页面已打开
|
||||
* 函数说明:标题放置 y 每页可配(title_y,0 = 框架默认 CP_TITLE_Y);
|
||||
* 副标题仅信息页(subtitle 非空);重绘区不相交则跳过区块
|
||||
*/
|
||||
static void cp_draw_header(lv_layer_t *layer, const content_page_def_t *d) {
|
||||
lv_area_t t;
|
||||
|
||||
/* 标题(居中;放置 y 每页可配,0 = 框架默认) */
|
||||
{
|
||||
int32_t ty = (d->title_y != 0) ? d->title_y : CP_TITLE_Y;
|
||||
lv_area_t t;
|
||||
|
||||
t.x1 = 0;
|
||||
t.x2 = CP_SCREEN_W - 1;
|
||||
@@ -219,8 +223,6 @@ static void cp_draw_cb(lv_event_t *e) {
|
||||
|
||||
/* 副标题(仅信息页) */
|
||||
if (d->subtitle != NULL) {
|
||||
lv_area_t t;
|
||||
|
||||
t.x1 = 0;
|
||||
t.x2 = CP_SCREEN_W - 1;
|
||||
t.y1 = d->subtitle_y;
|
||||
@@ -229,9 +231,22 @@ static void cp_draw_cb(lv_event_t *e) {
|
||||
cp_draw_centered_text(layer, d->subtitle, d->subtitle_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 行数据:列表页窗口跟随选中行所在页,信息页全量 */
|
||||
int32_t nrows = (int32_t)cp_row_total(d);
|
||||
/*
|
||||
* 函数功能:绘制行数据(列表页窗口跟随选中行所在页,信息页全量;逐行 culling)
|
||||
* 入口参数:layer - 绘制层 lv_layer_t *
|
||||
* d - 页面描述符 const content_page_def_t *
|
||||
* nrows - 总行数 int32_t(调用方取一次,与页脚同帧共用)
|
||||
* 返回值:无
|
||||
* 限定条件:页面已打开
|
||||
* 函数说明: 1. 列表页窗口顶行 = (sel/每页行数)*每页行数,末页不足整页截断
|
||||
* 2. 行条带(整宽)不在重绘区则跳过(culling)
|
||||
* 3. 选中行:整行白带(x=2..156,高 band_h)+ 黑字,非选中白字
|
||||
* 4. 行文本:静态 = Flash 表(不拷内存);动态 = refresh() 逐行取
|
||||
* 入栈缓冲(同帧同步光栅化,安全)
|
||||
*/
|
||||
static void cp_draw_rows(lv_layer_t *layer, const content_page_def_t *d, int32_t nrows) {
|
||||
int32_t first = 0;
|
||||
int32_t cnt = nrows;
|
||||
|
||||
@@ -316,43 +331,84 @@ static void cp_draw_cb(lv_event_t *e) {
|
||||
lv_draw_label(layer, &ld, &strip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 页脚"第 X 页,共 Y 页":列表页随选中行现算,信息页按参考图固定值;每页可关(密码页跟随实机) */
|
||||
{
|
||||
char buf[32];
|
||||
uint8_t cur;
|
||||
uint8_t pages;
|
||||
lv_area_t t;
|
||||
/*
|
||||
* 函数功能:绘制页脚"第 X 页,共 Y 页"
|
||||
* 入口参数:layer - 绘制层 lv_layer_t *
|
||||
* d - 页面描述符 const content_page_def_t *
|
||||
* nrows - 总行数 int32_t(调用方取一次,与行窗口同帧同值)
|
||||
* 返回值:无
|
||||
* 限定条件:页面已打开
|
||||
* 函数说明:列表页随选中行现算,信息页按参考图固定值;每页可关
|
||||
* (hide_footer;密码页跟随实机)
|
||||
*/
|
||||
static void cp_draw_footer(lv_layer_t *layer, const content_page_def_t *d, int32_t nrows) {
|
||||
char buf[32]; /* 页行文本缓冲(栈):"第 %u 页,共 %u 页" 最长远不足 32B */
|
||||
uint8_t cur; /* 当前页码(1 基) */
|
||||
uint8_t pages; /* 总页数 */
|
||||
lv_area_t t; /* 页脚条带区域(整宽,放置于 CP_PAGE_LINE_Y) */
|
||||
|
||||
if (d->selectable != 0u) {
|
||||
cur = (uint8_t)(s_sel / d->rows_per_page + 1u);
|
||||
pages = (uint8_t)((nrows + (int32_t)d->rows_per_page - 1) / d->rows_per_page);
|
||||
}
|
||||
else {
|
||||
cur = d->fixed_cur_page;
|
||||
pages = d->fixed_total_pages;
|
||||
}
|
||||
|
||||
t.x1 = 0;
|
||||
t.x2 = CP_SCREEN_W - 1;
|
||||
t.y1 = CP_PAGE_LINE_Y;
|
||||
t.y2 = CP_PAGE_LINE_Y + 15;
|
||||
if (d->hide_footer == 0u && cp_areas_intersect(&t, &layer->_clip_area)) {
|
||||
/* lv_snprintf 而非 snprintf:MSVC 对 printf 族做格式串扫描,中文字面量会误报 C4819 */
|
||||
lv_snprintf(buf, sizeof(buf), "第 %u 页,共 %u 页", (unsigned)cur, (unsigned)pages);
|
||||
|
||||
lv_draw_label_dsc_t ld;
|
||||
|
||||
/* ofs 相对 coords 原点(t.y1=CP_PAGE_LINE_Y),垂直偏移取 0 */
|
||||
lv_draw_label_dsc_init(&ld);
|
||||
ld.base.layer = layer;
|
||||
ld.text = buf; /* 栈缓冲:本页码每次现算 */
|
||||
ld.color = lv_color_white();
|
||||
ld.ofs_x = 6;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &t);
|
||||
}
|
||||
/* 页码计算,两种来源:
|
||||
* - 列表页(selectable):随选中行现算——当前页 = 选中行/每页行数 + 1
|
||||
* (选中行所在页);总页数 = ceil(nrows/每页行数),
|
||||
* 写成经典向上取整 (n + p - 1) / p(nrows 恰为整页倍数时不多算一页);
|
||||
* nrows 由调用方取一次、与行窗口共用,保证同帧页码与窗口一致
|
||||
* - 信息/表单页(非 selectable):取参考图固定值(fixed_cur_page / fixed_total_pages) */
|
||||
if (d->selectable != 0u) {
|
||||
cur = (uint8_t)(s_sel / d->rows_per_page + 1u);
|
||||
pages = (uint8_t)((nrows + (int32_t)d->rows_per_page - 1) / d->rows_per_page);
|
||||
}
|
||||
else {
|
||||
cur = d->fixed_cur_page;
|
||||
pages = d->fixed_total_pages;
|
||||
}
|
||||
|
||||
/* 页脚条带:整宽,放置于 CP_PAGE_LINE_Y(143,字形顶距已计入放置 y,
|
||||
* 墨迹与参考图对齐);高 15 = 字体行高 13 + 余量,整行文字一次覆盖 */
|
||||
t.x1 = 0;
|
||||
t.x2 = CP_SCREEN_W - 1;
|
||||
t.y1 = CP_PAGE_LINE_Y;
|
||||
t.y2 = CP_PAGE_LINE_Y + 15;
|
||||
|
||||
/* 双重门控:① 本页开启页脚(hide_footer;密码页跟随实机:无页码)
|
||||
* ② 页脚条带与重绘区相交(culling)——两者皆成立才格式化并绘制 */
|
||||
if (d->hide_footer == 0u && cp_areas_intersect(&t, &layer->_clip_area)) {
|
||||
/* lv_snprintf 而非 snprintf:MSVC 对 printf 族做格式串扫描,中文字面量会误报 C4819 */
|
||||
lv_snprintf(buf, sizeof(buf), "第 %u 页,共 %u 页", (unsigned)cur, (unsigned)pages);
|
||||
|
||||
lv_draw_label_dsc_t ld;
|
||||
|
||||
/* ofs 相对 coords 原点(t.x1=0,t.y1=CP_PAGE_LINE_Y):
|
||||
* 水平 6 = 左对齐、左留白 6px(与实机一致);垂直 0(顶距已计入放置 y) */
|
||||
lv_draw_label_dsc_init(&ld);
|
||||
ld.base.layer = layer;
|
||||
ld.text = buf; /* 栈缓冲:本页码每次现算,同帧内有效(同步光栅化) */
|
||||
ld.color = lv_color_white(); /* 页码恒白字(不像选中行那样反色) */
|
||||
ld.ofs_x = 6;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(layer, &ld, &t);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:页面自绘(编排:浅 V 装饰 + 标题/副标题 + 行数据 + 页脚页码 + 页面钩子)
|
||||
* 入口参数:e - 事件指针 lv_event_t *
|
||||
* 返回值:无
|
||||
* 限定条件:事件为 LV_EVENT_DRAW_MAIN,页面已打开
|
||||
* 函数说明:行数取一次(cp_row_total)供行窗口与页脚页码共用(同帧同值);
|
||||
* 各区块由子函数绘制(deco/header/rows/footer),均按 culling 区
|
||||
* 跳过不在重绘区的部分;末尾调 draw_cb(页自定义内容,在内置区块之后)
|
||||
*/
|
||||
static void cp_draw_cb(lv_event_t *e) {
|
||||
const content_page_def_t *d = s_defs[s_open];
|
||||
lv_layer_t *layer = lv_event_get_layer(e);
|
||||
int32_t nrows = (int32_t)cp_row_total(d); /* 行数取一次:供行窗口与页脚页码共用 */
|
||||
|
||||
cp_draw_deco(layer, d);
|
||||
cp_draw_header(layer, d);
|
||||
cp_draw_rows(layer, d, nrows);
|
||||
cp_draw_footer(layer, d, nrows);
|
||||
|
||||
/* 页面钩子:自定义内容(表单/操作页;在内置区块之后绘制) */
|
||||
if (d->draw_cb != NULL) {
|
||||
|
||||
@@ -139,12 +139,24 @@ static void remo_send_init(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:处理一帧完整的远端命令(客户端→设备)
|
||||
* 入口参数:f - 完整帧 [TAG][CMD][LEN][DATA][CRC];n - 帧总长度
|
||||
* 函数功能:处理一帧完整的远端命令(客户端→设备),校验通过后按命令分发执行
|
||||
* 入口参数:f - 完整帧 [TAG][CMD][LEN][DATA][CRC](数据段自偏移 4 起)
|
||||
* n - 帧总长度(字节)
|
||||
* 返回值:无
|
||||
* 限定条件:调用前帧已通过 CRC 与 TAG 校验
|
||||
* 函数说明:按命令分发:KEEPLIVE 回心跳 / INIT 回尺寸 / KEY 注入按键 /
|
||||
* LCDMEM 回送 1bpp 帧缓冲(经平台后端取帧);非法情况静默丢弃
|
||||
* 限定条件:f 指向至少 n 字节可读空间;由解析状态机 rm_parser_feed 在 CRC 通过后调用
|
||||
* 函数说明:1. 前置校验(任一失败即静默丢弃:不应答、不断连接):
|
||||
* a) n >= 5:最短合法帧 5 字节(LEN=0 时 TAG/CMD/LEN_H/LEN_LO/CRC)
|
||||
* b) 4+len+1 <= n:长度字段与帧长一致,防长度损坏引发越界读
|
||||
* c) CRC 与数据段逐字节异或一致(仅覆盖数据段,不含头与 CRC 自身)
|
||||
* d) TAG 必须为 TAG_CLIENT(0xAA):服务端只收客户端方向帧
|
||||
* 状态机已做过等价校验,此处复核使本函数自包含(可独立调用)
|
||||
* 2. 按命令分发:
|
||||
* KEEPLIVE(0) - 原样回显空心跳帧,维持连接保活
|
||||
* INIT(1) - 回送屏幕几何 [宽u16][高u16][帧字节数u32](大端,6 字节)
|
||||
* KEY(2) - 数据段首字节按键码经平台后端注入 UI,回空帧作确认
|
||||
* LCDMEM(3) - 数据段为 4B 大端偏移;偏移 0=整帧,经平台后端取 1bpp
|
||||
* 位图后回送 [偏移u32][位图 3200B],非 0 偏移暂不支持
|
||||
* 3. 未知命令码静默丢弃
|
||||
*/
|
||||
static void remo_handle(const uint8_t *f, int n)
|
||||
{
|
||||
@@ -154,49 +166,68 @@ static void remo_handle(const uint8_t *f, int n)
|
||||
const uint8_t *data;
|
||||
uint8_t crc;
|
||||
|
||||
/* a) 最小帧长:TAG+CMD+LEN_H+LEN_LO+CRC 共 5 字节,不足必是残帧/噪声 */
|
||||
if (n < 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 解析帧头:TAG=方向(0xAA 客户端帧 / 0xBB 服务端帧),CMD=命令码,
|
||||
* LEN=数据段长度(16 位大端),数据段自偏移 4 起 */
|
||||
tag = f[0];
|
||||
cmd = f[1];
|
||||
len = (uint16_t)(((uint16_t)f[2] << 8) | (uint16_t)f[3]);
|
||||
data = f + 4;
|
||||
|
||||
/* b) 长度一致性:整帧长须恰为 4+len+1,防长度字段损坏导致越界读帧缓冲 */
|
||||
if ((int)(4 + len + 1) > n) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* c) CRC 校验:数据段逐字节异或(LEN=0 时恒 0)须与帧尾一致,坏帧静默丢弃 */
|
||||
crc = f[4 + len];
|
||||
if (crc != remo_crc(data, len)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* d) 方向校验:只接受客户端帧 0xAA;0xBB 为服务端→客户端方向,出现在收包路径即非法 */
|
||||
if (tag != TAG_CLIENT) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_KEEPLIVE:
|
||||
/* 心跳保活:原样回显一个空 KEEPLIVE 帧,维持连接活性 */
|
||||
remo_send(CMD_KEEPLIVE, NULL, 0);
|
||||
break;
|
||||
case CMD_INIT:
|
||||
/* 查询屏幕参数:回送 [宽u16][高u16][帧字节数u32](大端,共 6 字节) */
|
||||
remo_send_init();
|
||||
break;
|
||||
case CMD_KEY:
|
||||
/* 远端导航按键:数据段首字节为按键码,经平台后端注入 UI 按键队列 */
|
||||
if (len >= 1) {
|
||||
ui_net_sink_inject_key(data[0]);
|
||||
}
|
||||
/* 回空帧作“已接收”确认(LEN=0,CRC=0) */
|
||||
remo_send(CMD_KEY, NULL, 0);
|
||||
break;
|
||||
case CMD_LCDMEM: {
|
||||
/* 读取显存:数据段为 4 字节大端起始偏移,不足 4 字节判非法丢弃 */
|
||||
if (len < 4) {
|
||||
break;
|
||||
}
|
||||
{
|
||||
/* 偏移按大端 32 位拼装(协议约定多字节字段大端) */
|
||||
uint32_t off = ((uint32_t)data[0] << 24) |
|
||||
((uint32_t)data[1] << 16) |
|
||||
((uint32_t)data[2] << 8) |
|
||||
((uint32_t)data[3]);
|
||||
/* 目前仅支持偏移 0(整帧)请求;非 0 偏移暂不支持,静默忽略 */
|
||||
if (off == 0) {
|
||||
/* 整帧请求:经平台后端取 1bpp 位图后回送(失败则静默丢弃) */
|
||||
/* 整帧请求:经平台后端取 1bpp 位图(MCU: ui_port_get_framebuffer /
|
||||
* 模拟器: sim_port_get_screen_mono)写入应答缓冲尾部,失败则静默丢弃 */
|
||||
if (ui_net_sink_get_lcdmem(g_lcdmem_data + 4, UI_MEMSIZE) == 0) {
|
||||
/* 应答数据段 = [偏移u32(回填请求原值)] + [1bpp 位图 3200B] */
|
||||
memcpy(g_lcdmem_data, data, 4);
|
||||
remo_send(CMD_LCDMEM, g_lcdmem_data, (uint16_t)(4 + UI_MEMSIZE));
|
||||
}
|
||||
@@ -205,6 +236,7 @@ static void remo_handle(const uint8_t *f, int n)
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* 未知命令码:静默丢弃,保持连接等待后续帧 */
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -374,7 +406,7 @@ int ui_net_sink_start(uint16_t port)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:ui_net_sink_start() 已成功调用
|
||||
* 函数说明:1. 网络代次变化(重建)时关闭旧监听/客户端并重建
|
||||
* 函数说明: 1. 网络代次变化(重建)时关闭旧监听/客户端并重建
|
||||
* 2. 无监听(启动失败)时重试启动
|
||||
* 3. 无客户端时尝试 accept,成功则复位状态机
|
||||
* 4. 有客户端时非阻塞 recv 逐字节喂入状态机
|
||||
|
||||
Reference in New Issue
Block a user