371 lines
14 KiB
C
371 lines
14 KiB
C
/*
|
||
* 模块名称:UI Page Net Cfg (网口设置内容页)
|
||
* 模块功能:网口设置叶子节点的内容页数据 + 行为——5 行地址列表(IP 地址/子网
|
||
* 掩码/默认网关/MAC 地址 + 保存行,5 行每页共 1 页);
|
||
* 行槽顶 = 24+14i、选中白带 x=2..156 高 13、名 x=4 / 值 x=54;
|
||
* 值取自 net_cfg RAM 副本(defaultTask 上电自 Flash 加载;
|
||
* netTask 回写实际 MAC 影子);
|
||
* 编辑模式:ENTER 进选中地址行(字节光标 = 活动字节,反显标记),
|
||
* ↑↓调字节 0..255(环绕、值列实时刷新)、←→移字节光标(首↔末环绕)、
|
||
* ENTER 确认退出编辑;
|
||
* 权限:edit_capable=1——查看密码 → 只读(编辑/保存键无效),
|
||
* 修改密码 → 可编辑;保存行 ENTER → 投递保存请求
|
||
* (net_cfg_save_request;属主任务 defaultTask 写盘并立即应用
|
||
* 重建网络,无需重启);保存行值随交接状态显示
|
||
* ENTER/WAIT/OK/ERR;
|
||
* 向框架 ui_content_page 提供页面描述符(零 LVGL 对象)
|
||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||
* 作者:王建锋
|
||
* 创建日期:2026-09-10
|
||
* 修改记录:
|
||
* v1.0 2026-09-10 王建锋 创建初始版本
|
||
* v1.1 2026-09-15 王建锋 保存改异步交接:投递请求 → 属主任务写盘/应用;
|
||
* 保存行随交接状态显示 ENTER/WAIT/OK/ERR
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#include <string.h>
|
||
|
||
#include "ui_page_net_cfg.h"
|
||
|
||
#include "net_cfg.h"
|
||
|
||
/* 私有宏定义区 */
|
||
|
||
/*
|
||
* 布局常量(设备像素;1bpp 屏 160x160):
|
||
* 5 行共 1 页(行槽顶 y = 24+14i,末行止于 y=93,与页脚 y=143 无冲突);
|
||
* 名 x=4 / 值 x=54(值列须 ≥54:4 汉字名占 48px + 间隙;值列宽——
|
||
* IP/掩码/网关 11 ASCII 字符 = 66px 止于 119,MAC 17 字符(冒号分隔)= 102px 止于 155,
|
||
* 均在选中白带 x=2..156 内)
|
||
*/
|
||
#define NC_ROW_COUNT 5u /* 4 地址行 + 1 保存行 */
|
||
#define NC_ADDR_ROW_MAX 4u /* 地址行范围 0..3(IP/掩码/网关/MAC) */
|
||
#define NC_SAVE_ROW 4u /* 保存行下标 */
|
||
#define NC_TOP_Y 24 /* 首行槽顶 y */
|
||
#define NC_PITCH 14 /* 行距 */
|
||
#define NC_BAND_H 13 /* 选中白带高 */
|
||
#define NC_LEFT_X 4 /* 左列 x(名) */
|
||
#define NC_RIGHT_X 54 /* 右列 x(值) */
|
||
#define NC_ROW_BUF_LEN 32u /* 行缓冲长度(与框架 CP_ROW_BUF_LEN 一致) */
|
||
|
||
/* 静态变量定义区 */
|
||
|
||
/* 页面运行态(每次开页经 init_cb 重置;约 2 B) */
|
||
static uint8_t s_editing; /* 1 = 编辑中(被编辑行 = 当前选中行) */
|
||
static uint8_t s_cursor; /* 编辑模式活动字节(IP/掩码/网关 0..3,MAC 0..5) */
|
||
|
||
/* 函数定义区 */
|
||
|
||
/*
|
||
* 函数功能:判断两区域是否相交(与 ui_content_page.c / ui_page_password.c 同实现)
|
||
* 入口参数:a - 区域 1 const lv_area_t *
|
||
* b - 区域 2 const lv_area_t *
|
||
* 返回值:true - 相交;false - 不相交
|
||
* 限定条件:无
|
||
*/
|
||
static bool nc_areas_intersect(const lv_area_t *a, const lv_area_t *b) {
|
||
int32_t x1 = (a->x1 > b->x1) ? a->x1 : b->x1;
|
||
int32_t y1 = (a->y1 > b->y1) ? a->y1 : b->y1;
|
||
int32_t x2 = (a->x2 < b->x2) ? a->x2 : b->x2;
|
||
int32_t y2 = (a->y2 < b->y2) ? a->y2 : b->y2;
|
||
|
||
return (x1 <= x2 && y1 <= y2);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:取地址行字节数
|
||
* 入口参数:row - 地址行下标(0..3) uint16_t
|
||
* 返回值:字节数(IP/掩码/网关 = 4,MAC = 6) uint8_t
|
||
* 限定条件:row < NC_ADDR_ROW_MAX
|
||
*/
|
||
static uint8_t nc_octet_count(uint16_t row) {
|
||
return (row == 3u) ? 6u : 4u;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:生成指定地址行的值串(与 refresh 值列同源,编辑光标定位与行文本一致)
|
||
* 入口参数:row - 地址行下标(0..3) uint16_t
|
||
* p_buf - 输出缓冲(≥ NC_ROW_BUF_LEN) char *
|
||
* 返回值:无
|
||
* 限定条件:p_buf 非空
|
||
* 函数说明:IP/掩码/网关 = "192.168.001.100"(每段 3 位补零定长,字节 i 恒在
|
||
* 字符 4i 起);MAC = "A4:94:B6:12:34:56"(冒号分隔,字节 i 恒在字符 3i 起)
|
||
*/
|
||
static void nc_fmt_value(uint16_t row, char *p_buf) {
|
||
const net_cfg_t *cfg = net_cfg_get();
|
||
|
||
if (row == 3u) {
|
||
uint8_t mac[6];
|
||
|
||
net_cfg_get_mac(mac);
|
||
(void)lv_snprintf(p_buf, NC_ROW_BUF_LEN, "%02X:%02X:%02X:%02X:%02X:%02X",
|
||
(unsigned)mac[0], (unsigned)mac[1], (unsigned)mac[2],
|
||
(unsigned)mac[3], (unsigned)mac[4], (unsigned)mac[5]);
|
||
return;
|
||
}
|
||
|
||
{
|
||
const uint8_t *field = (row == 0u) ? cfg->ip
|
||
: ((row == 1u) ? cfg->mask : cfg->gw);
|
||
|
||
net_cfg_fmt_ipv4(field, p_buf, NC_ROW_BUF_LEN);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:动态行刷新(逐行取名/值)
|
||
* 入口参数:row - 行下标(0..4) uint16_t
|
||
* name - 名缓冲(≥ NC_ROW_BUF_LEN,框架栈缓冲) char *
|
||
* value - 值缓冲(≥ NC_ROW_BUF_LEN,框架栈缓冲) char *
|
||
* 返回值:无
|
||
* 限定条件:页面打开(框架绘制链内逐行调用)
|
||
* 函数说明:地址行值取 net_cfg RAM 副本(MAC 全零时显示芯片实际 MAC 影子);
|
||
* 保存行值随保存交接状态(net_cfg_save_state)显示
|
||
* ENTER/WAIT/OK/ERR
|
||
*/
|
||
static void nc_refresh(uint16_t row, char *name, char *value) {
|
||
const net_cfg_t *cfg = net_cfg_get();
|
||
|
||
name[0] = '\0';
|
||
value[0] = '\0';
|
||
|
||
switch (row) {
|
||
case 0u:
|
||
(void)memcpy(name, "IP地址", sizeof("IP地址"));
|
||
net_cfg_fmt_ipv4(cfg->ip, value, NC_ROW_BUF_LEN);
|
||
break;
|
||
case 1u:
|
||
(void)memcpy(name, "子网掩码", sizeof("子网掩码"));
|
||
net_cfg_fmt_ipv4(cfg->mask, value, NC_ROW_BUF_LEN);
|
||
break;
|
||
case 2u:
|
||
(void)memcpy(name, "默认网关", sizeof("默认网关"));
|
||
net_cfg_fmt_ipv4(cfg->gw, value, NC_ROW_BUF_LEN);
|
||
break;
|
||
case 3u:
|
||
(void)memcpy(name, "MAC地址", sizeof("MAC地址"));
|
||
nc_fmt_value(row, value);
|
||
break;
|
||
case 4u:
|
||
(void)memcpy(name, "保存", sizeof("保存"));
|
||
switch (net_cfg_save_state()) {
|
||
case NET_CFG_SAVE_ST_WAIT:
|
||
(void)memcpy(value, "WAIT", sizeof("WAIT"));
|
||
break;
|
||
case NET_CFG_SAVE_ST_OK:
|
||
(void)memcpy(value, "OK", sizeof("OK"));
|
||
break;
|
||
case NET_CFG_SAVE_ST_FAIL:
|
||
(void)memcpy(value, "ERR", sizeof("ERR"));
|
||
break;
|
||
default:
|
||
(void)memcpy(value, "ENTER", sizeof("ENTER"));
|
||
break;
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:调整被编辑地址行的活动字节(0..255 环绕,实时落 RAM 副本)
|
||
* 入口参数:row - 地址行下标(0..3) uint16_t
|
||
* delta - 调整方向(>0 = +1,<0 = -1) int8_t
|
||
* 返回值:无
|
||
* 限定条件:编辑模式(s_cursor 有效)
|
||
* 函数说明:只改 RAM 副本;写盘须显式经保存行投递 net_cfg_save_request
|
||
*/
|
||
static void nc_adjust_octet(uint16_t row, int8_t delta) {
|
||
net_cfg_t cfg = *net_cfg_get();
|
||
uint8_t *field = (row == 0u) ? cfg.ip
|
||
: ((row == 1u) ? cfg.mask : ((row == 2u) ? cfg.gw : cfg.mac));
|
||
|
||
field[s_cursor] = (uint8_t)((field[s_cursor] + ((delta > 0) ? 1u : 255u)) % 256u);
|
||
net_cfg_set(&cfg);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:页面自绘(编辑模式:选中行值内活动字节反显)
|
||
* 入口参数:layer - 绘制层 lv_layer_t *
|
||
* clip - 重绘区 const lv_area_t *
|
||
* 返回值:无
|
||
* 限定条件:页面打开(框架绘制链末尾调用)
|
||
* 函数说明:非编辑模式无附加绘制(行/带/页脚全由框架);编辑模式在选中行白带
|
||
* 内对活动字节区域反显(1bpp:white=暗底 black=亮字),位置按文本
|
||
* 步进宽实测定位(ASCII 步进 6px/字符,与行文本布局一致)
|
||
*/
|
||
static void nc_draw_cb(lv_layer_t *layer, const lv_area_t *clip) {
|
||
char value[NC_ROW_BUF_LEN];
|
||
lv_area_t box;
|
||
uint32_t start;
|
||
uint32_t seg_len;
|
||
int32_t slot_y;
|
||
int32_t x0;
|
||
int32_t w;
|
||
uint16_t sel;
|
||
|
||
if (s_editing == 0u || ui_content_page_is_editable() == false) {
|
||
return;
|
||
}
|
||
|
||
sel = ui_content_page_selected_row();
|
||
if (sel >= NC_ADDR_ROW_MAX) {
|
||
return; /* 防御:保存行无编辑模式 */
|
||
}
|
||
|
||
nc_fmt_value(sel, value);
|
||
start = (uint32_t)s_cursor * ((sel == 3u) ? 3u : 4u); /* 字节 i 字符偏移:IP/掩码/网关 4i,MAC 3i(冒号分隔) */
|
||
seg_len = (sel == 3u) ? 2u : 3u;
|
||
|
||
slot_y = NC_TOP_Y + (int32_t)sel * NC_PITCH;
|
||
x0 = NC_RIGHT_X + (int32_t)lv_text_get_width(value, start, LV_FONT_DEFAULT, 0);
|
||
w = (int32_t)lv_text_get_width(value + start, seg_len, LV_FONT_DEFAULT, 0);
|
||
|
||
box.x1 = x0 - 1;
|
||
box.x2 = x0 + w;
|
||
box.y1 = slot_y;
|
||
box.y2 = slot_y + NC_BAND_H - 1;
|
||
if (!nc_areas_intersect(&box, clip)) {
|
||
return; /* 编辑光标不在重绘区:跳过(culling) */
|
||
}
|
||
|
||
/* 反显:黑底白字(与选中行白带黑字相反,方能在带内凸显活动字节;
|
||
* 1bpp:black=暗底 white=亮字) */
|
||
lv_draw_rect_dsc_t fd;
|
||
|
||
lv_draw_rect_dsc_init(&fd);
|
||
fd.base.layer = layer;
|
||
fd.bg_color = lv_color_black();
|
||
fd.bg_opa = LV_OPA_COVER;
|
||
lv_draw_rect(layer, &fd, &box);
|
||
|
||
{
|
||
lv_draw_label_dsc_t ld;
|
||
char seg[4]; /* 段文本:IP/掩码/网关 3 字符、MAC 2 字符,+ '\0' */
|
||
|
||
/* 只绘制"段文本"本身:若传整串(value + start),LVGL 在未置 EXPAND 标志时
|
||
* 按 coords 宽度(此处为段宽)折行,会逐行向盒下方绘制、溢出到其它行/空白区。
|
||
* 传段文本(宽度 ≤ 盒宽)则单行绘制,恰好落在光标盒内。 */
|
||
memcpy(seg, value + start, seg_len);
|
||
seg[seg_len] = '\0';
|
||
|
||
lv_draw_label_dsc_init(&ld);
|
||
ld.base.layer = layer;
|
||
ld.text = seg;
|
||
ld.text_static = 0u; /* 栈缓冲(同帧有效) */
|
||
ld.color = lv_color_white();
|
||
ld.ofs_x = 1; /* box.x1 = x0-1,+1 = x0(与框架行文本原点一致) */
|
||
ld.ofs_y = 0; /* 与行文本垂直对齐(text_dy = 0) */
|
||
lv_draw_label(layer, &ld, &box);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:页面按键处理(列表页 + 编辑模式)
|
||
* 入口参数:key - LVGL 键值 lv_key_t
|
||
* 返回值:true - 页已处理(框架跳过内置语义);false - 未处理
|
||
* 限定条件:页面打开;经框架键分发调用(uiTask 上下文)
|
||
* 函数说明: 1. 编辑模式:↑↓调活动字节(0..255 环绕),←→移字节光标(首↔末
|
||
* 环绕;← 键经框架拦截标志转入本函数),ENTER 确认退出编辑
|
||
* (值已实时落 RAM 副本);ESC 恒关页(框架保证,未保存调整留在
|
||
* RAM,下次开页重显、不写盘、重启失效)
|
||
* 2. 正常模式:ENTER 地址行 → 进编辑模式(需可编辑权限);ENTER
|
||
* 保存行 → 投递保存请求 net_cfg_save_request(需可编辑权限;
|
||
* 写盘与"立即应用"由属主任务 defaultTask 执行,保存行随交接
|
||
* 状态显示 WAIT/OK/ERR);↑↓ 落框架移选、← 落框架关页;
|
||
* 只读态 ENTER 一律忽略
|
||
*/
|
||
static bool nc_key_cb(lv_key_t key) {
|
||
uint16_t sel = ui_content_page_selected_row();
|
||
|
||
if (s_editing != 0u) {
|
||
uint8_t n;
|
||
|
||
if (sel >= NC_ADDR_ROW_MAX) {
|
||
s_editing = 0u; /* 防御:编辑中选中行越界(编辑态移选被拦截,不应发生) */
|
||
return false;
|
||
}
|
||
|
||
n = nc_octet_count(sel);
|
||
switch (key) {
|
||
case LV_KEY_UP:
|
||
nc_adjust_octet(sel, 1);
|
||
ui_content_page_invalidate();
|
||
return true;
|
||
case LV_KEY_DOWN:
|
||
nc_adjust_octet(sel, -1);
|
||
ui_content_page_invalidate();
|
||
return true;
|
||
case LV_KEY_LEFT:
|
||
s_cursor = (uint8_t)((s_cursor + n - 1u) % n); /* 光标左移,首绕末 */
|
||
ui_content_page_invalidate();
|
||
return true;
|
||
case LV_KEY_RIGHT:
|
||
s_cursor = (uint8_t)((s_cursor + 1u) % n); /* 光标右移,末绕首 */
|
||
ui_content_page_invalidate();
|
||
return true;
|
||
case LV_KEY_ENTER:
|
||
s_editing = 0u; /* 确认(值已在 RAM 副本);退出编辑 */
|
||
ui_content_page_invalidate();
|
||
return true;
|
||
default:
|
||
return false; /* ESC 框架恒关页;其余键落内置忽略 */
|
||
}
|
||
}
|
||
|
||
if (key == LV_KEY_ENTER && ui_content_page_is_editable() != false) {
|
||
if (sel < NC_ADDR_ROW_MAX) {
|
||
s_editing = 1u;
|
||
s_cursor = 0u;
|
||
ui_content_page_invalidate();
|
||
return true;
|
||
}
|
||
if (sel == NC_SAVE_ROW) {
|
||
(void)net_cfg_save_request(); /* 投递保存请求:属主任务写盘并立即应用 */
|
||
ui_content_page_invalidate();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:重置页面运行态
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:框架 ui_content_page_open 内调用(开页后一次)
|
||
* 函数说明:页面模块静态态不随屏/容器建毁自动复位,须自重置
|
||
* (编辑态/光标/保存提示)
|
||
*/
|
||
static void nc_init_cb(void) {
|
||
s_editing = 0u;
|
||
s_cursor = 0u;
|
||
}
|
||
|
||
/*
|
||
* 页面描述符(外部链接:框架描述符表 s_defs[] 直接引用):
|
||
* 列表页(5 行 1 页,动态值)+ 可编辑(edit_capable,还须修改权限)
|
||
* + ← 拦截(编辑模式移字节光标;ESC 恒不可拦截)
|
||
*/
|
||
const content_page_def_t ui_page_net_cfg_desc = {
|
||
.title = "网口设置",
|
||
.selectable = 1u,
|
||
.key_intercept = 1u,
|
||
.edit_capable = 1u,
|
||
.top_y = NC_TOP_Y,
|
||
.pitch = NC_PITCH,
|
||
.band_h = NC_BAND_H,
|
||
.text_dy = 0,
|
||
.left_x = NC_LEFT_X,
|
||
.right_x = NC_RIGHT_X,
|
||
.rows_per_page = NC_ROW_COUNT,
|
||
.row_count = NC_ROW_COUNT,
|
||
.refresh = nc_refresh,
|
||
.init_cb = nc_init_cb,
|
||
.draw_cb = nc_draw_cb,
|
||
.key_cb = nc_key_cb,
|
||
};
|