门禁扩容:新增 lv-conf-sync、simulator/ 纳入全部钩子;含 L3 定值/元件框架落地

本轮把"自研代码落在门禁之外"的口子收掉,同时落地 LVGL 特性裁剪与
L3 保护定值/元件框架。

门禁(.pre-commit-config.yaml,26 个钩子)
- 新增 scripts/check_lv_conf_sync.py + 钩子 lv-conf-sync:比对两份
  lv_conf.h 的**生效值**(未定义则取 lv_conf_internal.h 兜底默认),
  抓 MISMATCH / LVGL 不认的宏名 / 同文件重复定义
- 顶层 exclude 删掉 simulator/:其下 21 个 tracked 文件全是本项目自研
  代码,生成物(build/、SDL2/、tests/ref/、shot*)已在 .gitignore;
  doc-links 的 exclude 同步删除
- lv-conf-sync 的 files: 覆盖两端配置 → 只改 simulator/lv_conf.h 也触发
  (pre-commit 4.x 已移除 always: true,实测只告警并跳过,无兜底开关)
- 新增 prot-set-unit-test / prot-elm-unit-test 两个主机侧单测钩子
- S11(2.3.5) 判定细化:禁的是"用路径找文件",尖括号系统头带子目录
  (如 <sys/stat.h>)不再误报;全仓库复核仅 2 处项目代码判定变化

模拟器代码整改(纳入钩子后暴露,全部修代码而非豁免)
- cppcheck 4 处:sim_syslog.c 判空分支内解引用 p_epoch(真实 bug)、
  2 处 struct tm 未初始化、1 处指针可加 const
- coding-standard 61 处:44 处函数左大括号并到右括号同行、8 处补函数
  注释、3 处补文件头注释块、2 处 __ 前缀保护宏改 XXX_H、2 处 sprintf
  改 snprintf(写入字节数与原来完全一致)

LVGL 特性裁剪(F-1/F-2/F-3)
- 两端 lv_conf.h 对齐同一特性子集:删 26 个 LVGL 9.3 不存在的选项名、
  3 处重复定义,10 项靠默认值隐式生效的改为显式固化
- 实测对镜像体积中性;价值在防漂移,且模拟器 golden 回归跑的正是固件
  实际编译的配置

L3 软件侧
- App/algo/prot_set.* Q10 定值模型(范围校验 / 整表 all-or-nothing /
  校验和封存-自检-闭锁)+ prot_elm.* 通用"滞回+定时限"元件框架 7 实例
- test/host/prot_set、test/host/prot_elm 主机侧单测;uvprojx 挂载两处

文档同步
- pre-commit使用说明:§2.8 S11 口径、§2.15 lv-conf-sync 详解、新增 §5.5
  复盘(教训:exclude 只写真正不该进钩子的路径)
- AGENTS.md、UI_设计架构 §1.4 第 10 条、RAM 分析、审查报告刷新与销项

验证
- @build BUILD_OK 0/0(Code=194276 RO=15872 RW=876 ZI=139892)
- 模拟器重建:菜单 8 状态像素逐字节一致(池 5368 B)、11 内容页黄金参考
  全过(往返 5448 B)、remo sink 0/25600 位差异、--logtest/--cfgtest PASS
- 全仓库 cppcheck / coding-standard / no-mojibake --all-files 全过
- check_doc_links --all 129/0;check_mem_numbers --check 内存数字一致
- 端到端:往 simulator/lv_conf.h 注入一行漂移 → 钩子 FAIL(exit 1)
This commit is contained in:
2026-10-11 08:48:49 +08:00
parent 13a4e61c27
commit c936441c83
36 changed files with 2220 additions and 565 deletions
+1 -1
View File
@@ -141,4 +141,4 @@ if (Test-Path $exe) {
}
Write-Host "[build_sim] 构建退出码 0 但未找到 $exe" -ForegroundColor Red
exit 1


+12 -3
View File
@@ -1,4 +1,13 @@
/*
* 模块名称:模拟器调试日志 stub(dbg_log.h)
* 模块功能:覆盖 MCU 工程的 Drivers/BSP/dbg_log.h,把 DBG_* 直接映射到宿主 printf
* (模拟器无 sys_clock/RTC,日志走 PC 控制台)
* 适用平台:Windows / Linux / macOS(PC 模拟器)
* 作者:王建锋
* 创建日期:2026-08-31
* 修改记录:
* v1.0 2026-08-31 王建锋 创建:模拟器侧 DBG_* stub
*
* dbg_log.h — 模拟器专用调试日志 stub(覆盖 MCU 工程的 Drivers/BSP/dbg_log.h)
*
* 模拟器不使用 sys_clock.h / RTC,因此把 DBG_* 宏直接映射到 printf,
@@ -6,8 +15,8 @@
* DBG_TAG 仍需各 .c 文件在 include 前定义,这里保留该约定。
*/
#ifndef __DBG_LOG_H
#define __DBG_LOG_H
#ifndef DBG_LOG_H
#define DBG_LOG_H
#ifdef __cplusplus
extern "C" {
@@ -35,4 +44,4 @@ extern "C" {
}
#endif
#endif /* __DBG_LOG_H */
#endif /* DBG_LOG_H */
+32 -23
View File
@@ -1,4 +1,16 @@
/**
* 模块名称:LVGL 配置 —— PC 模拟器侧(simulator/lv_conf.h)
* 模块功能:LVGL v9.3.0 编译期特性裁剪与 SDL2 适配(160x160 1bpp 单色)
* 适用平台:Windows(CMake + SDL2)
* 作者:王建锋
* 创建日期:2026-08-31
* 修改记录:
* v1.0 2026-08-31 王建锋 创建:基于 Lib/lv_conf.h 适配 PC 模拟器
*
* ⚠ 本文件与 `Lib/lv_conf.h`(MCU 端)必须保持**同一特性子集**,已由门禁校验:
* `python scripts/check_lv_conf_sync.py`(pre-commit 钩子 lv-conf-sync,
* 两端任一改动都会触发)。规则正文见 docs/UI设计/UI_设计架构.md §1.4 第 10 条。
*
* @file lv_conf.h
* @brief LVGL v9.3.0 配置 — Windows SDL2 模拟器(160x160 1bpp 单色)
* @note 基于项目 Lib/lv_conf.h 适配,用于 PC 模拟器 UI 开发
@@ -51,7 +63,8 @@
/** 复杂绘制:主界面顶部开口浅 V 装饰含斜线(lv_line 斜段依赖 LV_DRAW_SW_COMPLEX),须与 MCU 端一致保持开启,否则两腿不渲染 */
#define LV_DRAW_SW_COMPLEX 1
#define LV_DRAW_SW_COMPLEX_GRADIENTS 0
/* 注:9.3 没有 LV_DRAW_SW_COMPLEX_GRADIENTS / LV_DRAW_SW_YIELD / LV_DRAW_SW_ASYNC
* (9.4 才引入),原定义无效,已删 */
#define LV_DRAW_SW_SHADOW_CACHE_SIZE 0
#define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4 /* LV_DRAW_SW_COMPLEX 依赖非零(零长数组 C2466);与 MCU 端默认一致 */
#define LV_DRAW_LAYER_SIMPLE_BUF_SIZE 0
@@ -72,16 +85,13 @@
#define LV_DRAW_SW_SUPPORT_I1 1
#define LV_DRAW_SW_I1_LUM_THRESHOLD 127
/** 关闭 GPU/矢量绘制 */
/** 关闭 GPU/矢量绘制(注:9.3 无 LV_USE_DRAW_NEMA_GFX / PPA / EVE / NANOVG,
* 属更新版本 lv_conf_template 残留,原定义无效,已删) */
#define LV_USE_DRAW_VG_LITE 0
#define LV_USE_DRAW_DAVE2D 0
#define LV_USE_DRAW_NEMA_GFX 0
#define LV_USE_DRAW_PXP 0
#define LV_USE_DRAW_PPA 0
#define LV_USE_DRAW_DMA2D 0
#define LV_USE_DRAW_EVE 0
#define LV_USE_DRAW_G2D 0
#define LV_USE_DRAW_NANOVG 0
#define LV_USE_DRAW_OPENGLES 0
#define LV_USE_DRAW_SDL 0
#define LV_USE_THORVG 0
@@ -105,11 +115,10 @@
#define LV_OBJ_ID_AUTO_ASSIGN 1
#define LV_USE_OBJ_ID_BUILTIN 1
#define LV_USE_OBJ_PROPERTY 0
#define LV_USE_EXT_DATA 0
#define LV_USE_FLOAT 0
#define LV_USE_OBSERVER 0
#define LV_USE_TRANSLATION 0
#define LV_USE_COLOR_FILTER 0
/* 注:9.3 无 LV_USE_EXT_DATA / LV_USE_TRANSLATION(9.4 才有),原两行无效,已删 */
/*============================================================================
* LOGGING
@@ -126,7 +135,7 @@
/*============================================================================
* LAYOUTS
*============================================================================*/
#define LV_USE_FLEX 1
#define LV_USE_FLEX 0 /* 2026-10-11 与 MCU 端一致:全仓无 lv_obj_set_flex_* 调用 */
#define LV_USE_GRID 0
/*============================================================================
@@ -142,7 +151,6 @@
#define LV_USE_BMP 0
#define LV_USE_TJPGD 0
#define LV_USE_LIBJPEG_TURBO 0
#define LV_USE_LIBWEBP 0
#define LV_USE_SVG 0
/*============================================================================
@@ -190,7 +198,7 @@
#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(ui_font_cn_12)
#define LV_FONT_DEFAULT &ui_font_cn_12
#define LV_USE_CUSTOM_FONT_DEFAULT 0
/* 注:9.3 无 LV_USE_CUSTOM_FONT_DEFAULT,原行无效,已删 */
#define LV_FONT_FMT_TXT_LARGE 0
#define LV_USE_FONT_COMPRESSED 0
#define LV_USE_FONT_PLACEHOLDER 1
@@ -207,11 +215,10 @@
#define LV_USE_3DTEXTURE 0
#define LV_USE_ANIMIMG 0
#define LV_USE_ARC 0
#define LV_USE_ARCLABEL 0
#define LV_USE_BAR 0
#define LV_USE_BARCODE 0
#define LV_USE_BUTTON 1
#define LV_USE_BUTTONMATRIX 1
#define LV_USE_BUTTON 0
#define LV_USE_BUTTONMATRIX 0
#define LV_USE_CALENDAR 0
#define LV_USE_CANVAS 0
#define LV_USE_CHART 0
@@ -219,13 +226,16 @@
#define LV_USE_DROPDOWN 0
#define LV_USE_FFMPEG 0
#define LV_USE_GIF 0
#define LV_USE_GLTF 0
#define LV_USE_GSTREAMER 0
#define LV_USE_IMAGE 1
/* 注:9.3 无 LV_USE_GLTF / LV_USE_GSTREAMER / LV_USE_ARCLABEL(更新版本残留),已删 */
/* 2026-10-11(F-2/F-3 对齐 MCU 端 Lib/lv_conf.h):本项目 UI 全部自绘,
* 三处代码(Drivers/BSP/UI、App、simulator)零控件构造函数、零 flex/grid 调用。
* 关掉后模拟器与固件跑的是同一套特性子集,golden 回归才能真正验证 MCU 配置。
* 若将来引入控件型页面(U2 原型库),两端同步逐项打开并复跑 ui-verify。 */
#define LV_USE_IMAGE 0
#define LV_USE_IMAGEBUTTON 0
#define LV_USE_IME_PINYIN 0
#define LV_USE_KEYBOARD 1
#define LV_USE_LABEL 1
#define LV_USE_KEYBOARD 0
#define LV_USE_LABEL 0
#define LV_LABEL_TEXT_SELECTION 0
#define LV_LABEL_LONG_TXT_HINT 0
#define LV_LABEL_WAIT_CHAR_COUNT 0
@@ -244,7 +254,7 @@
#define LV_USE_SPINBOX 0
#define LV_USE_SPINNER 0
#define LV_USE_SWITCH 0
#define LV_USE_TEXTAREA 1
#define LV_USE_TEXTAREA 0
#define LV_USE_TABLE 0
#define LV_USE_TABVIEW 0
#define LV_USE_TILEVIEW 0
@@ -288,13 +298,12 @@
#define LV_USE_WINDOWS 0
/*============================================================================
* THEMES (补充)
* THEMES 子选项(LV_USE_THEME_DEFAULT / SIMPLE 本体已在上面 THEMES 块定义,
* 此处勿重复定义——同文件重复 #define 后者生效,极易误读)
*============================================================================*/
#define LV_USE_THEME_DEFAULT 0
#define LV_THEME_DEFAULT_DARK 0
#define LV_THEME_DEFAULT_GROW 0
#define LV_THEME_DEFAULT_TRANSITION_TIME 0
#define LV_USE_THEME_SIMPLE 0
/*============================================================================
* EXPORTS
+55 -38
View File
@@ -1,4 +1,12 @@
/**
* 模块名称:PC 模拟器入口(simulator/main.c)
* 模块功能:在 PC 上以 SDL2 跑**与固件同一套** UI 源码,提供窗口调试与多种无头自检模式
* 适用平台:Windows(CMake + SDL2)
* 作者:王建锋
* 创建日期:2026-08-31
* 修改记录:
* v1.0 2026-08-31 王建锋 创建:基于 lv_port_pc_vscode 适配
*
* @file main.c
* @brief LVGL 模拟器入口 — 160x160 1bpp 单色菜单 UI
* @note 基于 lv_port_pc_vscode 适配,用于 PC 上 UI 开发与调试
@@ -69,30 +77,35 @@
static uint8_t g_shot_buf[SIM_SCREEN_W * SIM_SCREEN_H / 8];
/*
* @brief 离屏截图用空 delay 回调
* @note 默认 lv_delay_ms 会忙等 tick 前进,但离屏渲染时 tick 不会在等待期间推进,
* 导致死循环挂死。window 模式经 SDL 设了真实 delay,离屏这里用空实现代替。
* 函数功能:离屏截图用的空 delay 回调
* 入口参数:ms - 请求延时的毫秒数(未使用) uint32_t
* 返回值:无
* 说明:默认 lv_delay_ms 会忙等 tick 前进,但离屏渲染时 tick 不会在等待期间推进,
* 导致死循环挂死。window 模式经 SDL 设了真实 delay,离屏这里用空实现代替。
*/
static void shot_delay_cb(uint32_t ms)
{
static void shot_delay_cb(uint32_t ms) {
(void)ms;
}
/*
* @brief 离屏截图 flush 回调(不写真实设备,直接标记就绪)
* 函数功能:离屏截图 flush 回调(不写真实设备,直接标记就绪)
* 入口参数:disp - LVGL 显示对象 lv_display_t *
* area - 刷新区域(未使用) const lv_area_t *
* color_p - 像素缓冲(未使用) uint8_t *
* 返回值:无
*/
static void shot_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p)
{
static void shot_flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p) {
(void)area;
(void)color_p;
lv_display_flush_ready(disp);
}
/*
* 键序列字符 → LVGL 键值(非键字符返回 0 = 忽略)
* 函数功能:键序列字符 → LVGL 键值映射(--keys 无头回归用)
* 入口参数:c - 键序列字符(u=↑ d=↓ l=← r=→ e=确认 s=返回) char
* 返回值:LVGL 键值;非键字符返回 0 = 忽略 uint32_t
*/
static uint32_t sim_map_key_char(char c)
{
static uint32_t sim_map_key_char(char c) {
switch (c) {
case 'u': return LV_KEY_UP;
case 'd': return LV_KEY_DOWN;
@@ -105,12 +118,14 @@ static uint32_t sim_map_key_char(char c)
}
/*
* @brief 待机屏底部时钟提供回调(平台差异:模拟器 = 宿主 time())
* 函数功能:待机屏底部时钟提供回调(平台差异:模拟器 = 宿主 time())
* 入口参数:buf - 输出缓冲(格式化时间串) char *
* len - 缓冲长度 size_t
* 返回值:无
*/
static void sim_time_cb(char *buf, size_t len)
{
static void sim_time_cb(char *buf, size_t len) {
time_t t = time(NULL);
struct tm tmv;
struct tm tmv = {0}; /* 显式清零:localtime_s/r 是"输出型"参数,静态分析按未初始化报 */
#if defined(_MSC_VER)
localtime_s(&tmv, &t);
@@ -123,14 +138,13 @@ static void sim_time_cb(char *buf, size_t len)
}
/*
* @brief 无窗口离屏渲染(可注入键序列)并导出末帧像素
* @param keys 键序列(u=上 d=下 l=左 r=右 e=确认 s=返回;空 = 不注键)
* @param path 输出文件(写入 160x160 I1 原始像素)
* @param standby 非 0 = 注键后强制进入待机屏再截图(--standby 模式)
* @return 0 成功,非 0 失败
* 函数功能:无窗口离屏渲染(可注入键序列)并导出末帧像素
* 入口参数:keys - 键序列(u=上 d=下 l=左 r=右 e=确认 s=返回;空 = 不注键) const char *
* path - 输出文件(写入 160x160 I1 原始像素) const char *
* standby - 非 0 = 注键后强制进入待机屏再截图(--standby 模式) int
* 返回值:0 - 成功;非 0 - 失败 int
*/
static int run_headless(const char * keys, const char * path, int standby)
{
static int run_headless(const char * keys, const char * path, int standby) {
setvbuf(stdout, NULL, _IONBF, 0);
lv_init();
lv_delay_set_cb(shot_delay_cb);
@@ -209,7 +223,7 @@ static int run_headless(const char * keys, const char * path, int standby)
uint32_t snap_h = snap->header.h;
/* I1 缓冲区布局:[palette 8B][pixel data],跳过 palette 只写像素 */
uint32_t pal_bytes = LV_COLOR_INDEXED_PALETTE_SIZE(LV_COLOR_FORMAT_I1) * (uint32_t)sizeof(lv_color32_t);
uint8_t * px = snap->data + pal_bytes;
const uint8_t * px = snap->data + pal_bytes; /* 只读:写文件时不再改动快照 */
uint32_t px_bytes = (uint32_t)snap->header.stride * (uint32_t)snap_h;
FILE *fp = fopen(path, "wb");
if (fp == NULL) {
@@ -239,13 +253,13 @@ static const char s_cfgtest_doc[] =
static const uint8_t s_cfgtest_ip[4] = { 10u, 0u, 0u, 1u };
/*
* @brief 配置轮询自检(无窗口、无需 LVGL/SDL,进程内闭环)
* @note 验证链:net_cfg_load(只读 INI)→ poll 建基线 → 伪造外部编辑
* → poll 两次(沉降去抖)→ 重载并更新 RAM 副本
* @return 0 PASS,非 0 FAIL
* 函数功能:配置轮询自检(无窗口、无需 LVGL/SDL,进程内闭环)
* 入口参数:无
* 返回值:0 - PASS;非 0 - FAIL int
* 说明:验证链 net_cfg_load(只读 INI)→ poll 建基线 → 伪造外部编辑
* → poll 两次(沉降去抖)→ 重载并更新 RAM 副本
*/
static int run_cfgtest(void)
{
static int run_cfgtest(void) {
int i;
const net_cfg_t *p_cfg;
@@ -367,13 +381,13 @@ static int run_cfgtest(void)
}
/*
* @brief 日志引擎自检(无窗口、进程内闭环)
* @note 验证链:清理旧档 → post N 条 → count/get 回读断言 + 宿主文件字节级校验
* (64B 定长、ISO 时间戳格式、epoch 字段 ≈ now、NUL 填充)
* @return 0 PASS,非 0 FAIL
* 函数功能:日志引擎自检(无窗口、进程内闭环)
* 入口参数:无
* 返回值:0 - PASS;非 0 - FAIL int
* 说明:验证链 清理旧档 → post N 条 → count/get 回读断言 + 宿主文件字节级校验
* (64B 定长、ISO 时间戳格式、epoch 字段 ≈ now、NUL 填充)
*/
static int run_logtest(void)
{
static int run_logtest(void) {
uint32_t ep;
char ts[SYSLOG_TS_LEN + 1u];
char desc[SYSLOG_DESC_MAX + 1u];
@@ -464,10 +478,13 @@ static int run_logtest(void)
}
/**
* @brief 程序入口
* 函数功能:模拟器入口(解析模式开关 → 窗口模式或无头自检)
* 入口参数:argc - 参数个数 int
* argv - 参数数组(--screenshot / --keys / --standby / --cfgtest /
* --logtest / --idle) char **
* 返回值:0 - 正常退出或自检 PASS;非 0 - 失败 int
*/
int main(int argc, char **argv)
{
int main(int argc, char **argv) {
/* 截图/自检模式:--screenshot / --keys / --standby / --cfgtest / --logtest */
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--screenshot") == 0 && (i + 1) < argc) {
+30 -60
View File
@@ -53,8 +53,7 @@ static int s_generation = 0; /* 网络代次(模拟器恒为 0
* 限定条件:无
* 函数说明:任何 Socket 操作前先调用;已初始化则直接返回
*/
static int ensure_wsa(void)
{
static int ensure_wsa(void) {
static int s_done = 0;
if (s_done == 0) {
@@ -75,8 +74,7 @@ static int ensure_wsa(void)
* 限定条件:s_socks 已定义
* 函数说明:线性扫描,返回首个 in_use == 0 的控制块索引
*/
static int alloc_fd(void)
{
static int alloc_fd(void) {
int i;
for (i = 0; i < NET_MAX_SOCKETS; i++) {
@@ -96,8 +94,7 @@ static int alloc_fd(void)
* 限定条件:两结构体布局一致(均 16 字节,字段偏移相同)
* 函数说明:字段偏移一致,按 sizeof 直接拷贝
*/
static void to_ws_addr(const struct net_sockaddr *addr, struct sockaddr_in *out)
{
static void to_ws_addr(const struct net_sockaddr *addr, struct sockaddr_in *out) {
memcpy(out, addr, sizeof(struct sockaddr_in));
}
@@ -110,8 +107,7 @@ static void to_ws_addr(const struct net_sockaddr *addr, struct sockaddr_in *out)
* 限定条件:两结构体布局一致(均 16 字节,字段偏移相同)
* 函数说明:字段偏移一致,按 sizeof 直接拷贝
*/
static void from_ws_addr(const struct sockaddr_in *in, struct net_sockaddr *out)
{
static void from_ws_addr(const struct sockaddr_in *in, struct net_sockaddr *out) {
memcpy(out, in, sizeof(struct sockaddr_in));
}
@@ -123,8 +119,7 @@ static void from_ws_addr(const struct sockaddr_in *in, struct net_sockaddr *out)
* 限定条件:无
* 函数说明:与 MCU 端 net_init 语义对齐,供上层统一调用
*/
int net_init(const char *ip, const char *mask, const char *gateway, const uint8_t *mac)
{
int net_init(const char *ip, const char *mask, const char *gateway, const uint8_t *mac) {
(void)ip;
(void)mask;
(void)gateway;
@@ -145,8 +140,7 @@ int net_init(const char *ip, const char *mask, const char *gateway, const uint8_
* 限定条件:无
* 函数说明:模拟器无网络重建,代次恒 0,使上层代次自愈逻辑空转
*/
uint32_t net_get_generation(void)
{
uint32_t net_get_generation(void) {
return (uint32_t)s_generation;
}
@@ -157,8 +151,7 @@ uint32_t net_get_generation(void)
* 限定条件:无
* 函数说明:模拟器不会触发,保留接口以对齐 API
*/
void net_generation_inc(void)
{
void net_generation_inc(void) {
s_generation++;
}
@@ -169,8 +162,7 @@ void net_generation_inc(void)
* 限定条件:无
* 函数说明:模拟器无 CH395F 中断,空操作
*/
int net_poll(void)
{
int net_poll(void) {
return 0;
}
@@ -183,8 +175,7 @@ int net_poll(void)
* 限定条件:Winsock 已就绪
* 函数说明:分配空闲控制块,创建 Winsock SOCKET 并置非阻塞
*/
int net_socket(int domain, int type)
{
int net_socket(int domain, int type) {
int idx;
int is_udp;
SOCKET ws;
@@ -238,8 +229,7 @@ int net_socket(int domain, int type)
* 限定条件:socket 已创建
* 函数说明:设置 SO_REUSEADDR 后 bind,便于断线后重监听复用端口
*/
int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen)
{
int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen) {
struct sockaddr_in wsaddr;
int yes = 1;
@@ -273,8 +263,7 @@ int net_bind(int sockfd, const struct net_sockaddr *addr, int addrlen)
* 限定条件:socket 已绑定
* 函数说明:backlog 为 1(单客户端),监听 Socket 保持非阻塞
*/
int net_listen(int sockfd)
{
int net_listen(int sockfd) {
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
s_net_errno = NET_ERR_BADF;
return -1;
@@ -301,8 +290,7 @@ int net_listen(int sockfd)
* 函数说明:非阻塞监听下无新连接时返回 -1 并置 NET_ERR_WOULDBLOCK;
* 与 MCU 端不同,Winsock 的 accept 返回独立于监听 Socket 的新 fd
*/
int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen)
{
int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen) {
struct sockaddr_in wscli;
int wslen;
SOCKET cws;
@@ -356,8 +344,7 @@ int net_accept(int sockfd, struct net_sockaddr *addr, int *addrlen)
* 而 accept 本就是非阻塞的、**没有 MCU 端的 RPC 成本** → 恒返回 1,
* 使上层门控在模拟器上等价于"改前行为"(逐位一致,不引入新语义)
*/
int net_can_accept(int sockfd)
{
int net_can_accept(int sockfd) {
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
return 0;
}
@@ -372,8 +359,7 @@ int net_can_accept(int sockfd)
* 函数说明:同 net_can_accept——Winsock recv 非阻塞、无 RPC 成本 → 恒返回 1,
* 上层门控在模拟器上不改变既有行为(含"每轮都 recv"的自愈特性)
*/
int net_can_read(int sockfd)
{
int net_can_read(int sockfd) {
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
return 0;
}
@@ -390,8 +376,7 @@ int net_can_read(int sockfd)
* 限定条件:socket 已创建
* 函数说明:非阻塞 Socket 上连接未完成时返回 -1 + NET_ERR_WOULDBLOCK
*/
int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen)
{
int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen) {
struct sockaddr_in wsaddr;
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
@@ -430,8 +415,7 @@ int net_connect(int sockfd, const struct net_sockaddr *addr, int addrlen)
* 函数说明:非阻塞单次发送(保持 UI 主循环不阻塞);协议帧小且对端主动
* 读取,实践中一次即发完
*/
int net_send(int sockfd, const void *buf, int len, int flags)
{
int net_send(int sockfd, const void *buf, int len, int flags) {
int r;
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
@@ -473,8 +457,7 @@ int net_send(int sockfd, const void *buf, int len, int flags)
* 函数说明:与 MCU 端语义一致——>0 有数据,0 对端关闭,<0 且 errno 区分
* "暂无数据"与"真错误"
*/
int net_recv(int sockfd, void *buf, int len, int flags)
{
int net_recv(int sockfd, void *buf, int len, int flags) {
int r;
(void)flags;
@@ -519,8 +502,7 @@ int net_recv(int sockfd, void *buf, int len, int flags)
* 限定条件:socket 已创建,buf/dest_addr 非空
*/
int net_sendto(int sockfd, const void *buf, int len, int flags,
const struct net_sockaddr *dest_addr, int addrlen)
{
const struct net_sockaddr *dest_addr, int addrlen) {
struct sockaddr_in wsaddr;
int r;
@@ -563,8 +545,7 @@ int net_sendto(int sockfd, const void *buf, int len, int flags,
* 限定条件:socket 已创建,buf 非空
*/
int net_recvfrom(int sockfd, void *buf, int len, int flags,
struct net_sockaddr *src_addr, int *addrlen)
{
struct net_sockaddr *src_addr, int *addrlen) {
struct sockaddr_in wsaddr;
int wslen;
int r;
@@ -606,8 +587,7 @@ int net_recvfrom(int sockfd, void *buf, int len, int flags,
* 限定条件:socket 已打开(in_use == 1)
* 函数说明:关闭 Winsock 句柄并释放控制块(in_use 清零,net_get_sock 转返回 NULL)
*/
int net_close(int sockfd)
{
int net_close(int sockfd) {
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
s_net_errno = NET_ERR_BADF;
return -1;
@@ -636,8 +616,7 @@ int net_close(int sockfd)
* 限定条件:socket 已创建
* 函数说明:模拟器以非阻塞 I/O 直接感知事件,回调仅存储不主动触发
*/
int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg)
{
int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg) {
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS || s_socks[sockfd].in_use == 0) {
s_net_errno = NET_ERR_BADF;
return -1;
@@ -656,8 +635,7 @@ int net_set_event_cb(int sockfd, net_event_cb_t cb, void *arg)
* 返回值:最近一次 net_* 操作设置的错误码(NET_OK / NET_ERR_xxx)
* 限定条件:无
*/
int net_get_errno(void)
{
int net_get_errno(void) {
return s_net_errno;
}
@@ -669,8 +647,7 @@ int net_get_errno(void)
* 限定条件:无
* 函数说明:逐段解析后按 NET_HTONL 转网络字节序(与 MCU 端实现一致)
*/
uint32_t net_inet_addr(const char *cp)
{
uint32_t net_inet_addr(const char *cp) {
uint32_t addr = 0;
int i;
@@ -701,8 +678,7 @@ uint32_t net_inet_addr(const char *cp)
* 返回值:buf 指针(buf 为 NULL 时返回 NULL)
* 限定条件:buf 非空且长度足够
*/
char *net_inet_ntoa(uint32_t addr, char *buf)
{
char *net_inet_ntoa(uint32_t addr, char *buf) {
uint32_t naddr = NET_NTOHL(addr);
if (buf == NULL) {
@@ -724,8 +700,7 @@ char *net_inet_ntoa(uint32_t addr, char *buf)
* 返回值:网络序端口
* 限定条件:无
*/
uint16_t net_htons(uint16_t hostshort)
{
uint16_t net_htons(uint16_t hostshort) {
return NET_HTONS(hostshort);
}
@@ -735,8 +710,7 @@ uint16_t net_htons(uint16_t hostshort)
* 返回值:主机序端口
* 限定条件:无
*/
uint16_t net_ntohs(uint16_t netshort)
{
uint16_t net_ntohs(uint16_t netshort) {
return NET_NTOHS(netshort);
}
@@ -746,8 +720,7 @@ uint16_t net_ntohs(uint16_t netshort)
* 返回值:网络序 IP
* 限定条件:无
*/
uint32_t net_htonl(uint32_t hostlong)
{
uint32_t net_htonl(uint32_t hostlong) {
return NET_HTONL(hostlong);
}
@@ -757,8 +730,7 @@ uint32_t net_htonl(uint32_t hostlong)
* 返回值:主机序 IP
* 限定条件:无
*/
uint32_t net_ntohl(uint32_t netlong)
{
uint32_t net_ntohl(uint32_t netlong) {
return NET_NTOHL(netlong);
}
@@ -769,8 +741,7 @@ uint32_t net_ntohl(uint32_t netlong)
* 限定条件:无
* 函数说明:上层据此判断"监听 Socket 是否随客户端一同失效"(单连接模型)
*/
net_sock_t *net_get_sock(int sockfd)
{
net_sock_t *net_get_sock(int sockfd) {
if (sockfd < 0 || sockfd >= NET_MAX_SOCKETS) {
return NULL;
}
@@ -790,8 +761,7 @@ net_sock_t *net_get_sock(int sockfd)
* 限定条件:socket 已创建
* 函数说明:经 Winsock getsockname 读取实际绑定地址
*/
int net_getsockname(int sockfd, struct net_sockaddr *addr, int *addrlen)
{
int net_getsockname(int sockfd, struct net_sockaddr *addr, int *addrlen) {
struct sockaddr_in wsaddr;
int wslen;
+3 -6
View File
@@ -25,8 +25,7 @@ static lv_indev_t *g_kbd_indev = NULL;
* 2. 创建键盘输入设备及默认输入组
* 3. 不创建鼠标/触摸设备(模拟器仅用键盘导航)
*/
int sim_port_init(int32_t hor_res, int32_t ver_res)
{
int sim_port_init(int32_t hor_res, int32_t ver_res) {
/* 创建默认输入组 */
lv_group_t *g = lv_group_create();
if (g == NULL) {
@@ -65,8 +64,7 @@ int sim_port_init(int32_t hor_res, int32_t ver_res)
* 返回值:显示设备指针 lv_display_t * 可能为 NULL
* 限定条件:sim_port_init() 已成功调用
*/
void *sim_port_get_display(void)
{
void *sim_port_get_display(void) {
return (void *)lv_display_get_default();
}
@@ -83,8 +81,7 @@ void *sim_port_get_display(void)
* 转换,直接读 get_buf_active 不可靠;读回屏幕呈现像素最稳,
* 且完全避开快照离屏重渲染(SDL 驱动下会卡派发循环)
*/
int sim_port_get_screen_mono(uint8_t *buf, uint32_t buf_size)
{
int sim_port_get_screen_mono(uint8_t *buf, uint32_t buf_size) {
if (buf == NULL) {
return -1;
}
+3 -3
View File
@@ -4,8 +4,8 @@
* 适用平台:Windows / Linux / macOS (SDL2)
* 说明:替代 MCU 端的 ui_port.c,用于 PC 上 UI 开发与调试
*/
#ifndef __SIM_PORT_H
#define __SIM_PORT_H
#ifndef SIM_PORT_H
#define SIM_PORT_H
#include <stdint.h>
@@ -47,4 +47,4 @@ int sim_port_get_screen_mono(uint8_t *buf, uint32_t buf_size);
}
#endif
#endif /* __SIM_PORT_H */
#endif /* SIM_PORT_H */
+13 -6
View File
@@ -32,16 +32,16 @@ static const uint32_t s_caps[SYSLOG_COUNT] = { SYSLOG_RUN_MAX, SYSLOG_SELFTEST_M
*/
static void s_now(char *p_ts, uint32_t *p_epoch) {
time_t t = time(NULL);
struct tm tmv;
struct tm tmv = {0}; /* 显式清零:localtime_s/r 是"输出型"参数,静态分析按未初始化报 */
#if defined(_WIN32)
localtime_s(&tmv, &t);
#else
localtime_r(&t, &tmv);
#endif
(void)sprintf(p_ts, "%04d-%02d-%02d %02d:%02d:%02d",
tmv.tm_year + 1900, tmv.tm_mon + 1, tmv.tm_mday,
tmv.tm_hour, tmv.tm_min, tmv.tm_sec);
(void)snprintf(p_ts, SYSLOG_TS_LEN + 1u, "%04d-%02d-%02d %02d:%02d:%02d",
tmv.tm_year + 1900, tmv.tm_mon + 1, tmv.tm_mday,
tmv.tm_hour, tmv.tm_min, tmv.tm_sec);
*p_epoch = (uint32_t)t;
}
@@ -68,7 +68,9 @@ static int s_append(syslog_id_t id, const char *desc) {
s_now(ts, &epoch);
memcpy(rec, ts, SYSLOG_TS_LEN);
rec[SYSLOG_TS_LEN] = ' ';
(void)sprintf((char *)(rec + SYSLOG_EPOCH_OFFS), "%010u", (unsigned int)epoch);
/* +1 = 容纳 NUL;字段本身固定 10 位数字,与旧 sprintf 写入字节数完全一致 */
(void)snprintf((char *)(rec + SYSLOG_EPOCH_OFFS), SYSLOG_EPOCH_LEN + 1u,
"%010u", (unsigned int)epoch);
rec[SYSLOG_DESC_OFFS - 1u] = ' ';
dl = strlen(desc);
if (dl > SYSLOG_DESC_MAX) {
@@ -185,7 +187,12 @@ int syslog_last_epoch(syslog_id_t id, uint32_t *p_epoch) {
uint32_t v = 0u;
uint32_t i;
if (p_epoch == NULL || syslog_count(id) == 0u) {
/* 先判空再写:旧写法 `if (p_epoch == NULL || ...)` 之后立刻 *p_epoch = 0,
* 一旦调用方真传 NULL 就是空指针解引用(cppcheck nullPointerRedundantCheck 实报) */
if (p_epoch == NULL) {
return -1;
}
if (syslog_count(id) == 0u) {
*p_epoch = 0u;
return -1;
}
+3 -6
View File
@@ -21,8 +21,7 @@
* 限定条件:无
* 函数说明:远端按键码与 LVGL 按键码一一对应
*/
static uint32_t sim_map_key(uint8_t remo_key)
{
static uint32_t sim_map_key(uint8_t remo_key) {
switch (remo_key) {
case UI_KEY_UP: return LV_KEY_UP;
case UI_KEY_DOWN: return LV_KEY_DOWN;
@@ -43,8 +42,7 @@ static uint32_t sim_map_key(uint8_t remo_key)
* 函数说明:窗口模式显示缓冲经 I1→ARGB8888 转换,不能直读,
* 故读回屏幕像素再按红通道阈值打包 1bpp(布局与 MCU 端一致)
*/
int ui_net_sink_get_lcdmem(uint8_t *out, uint32_t out_size)
{
int ui_net_sink_get_lcdmem(uint8_t *out, uint32_t out_size) {
if (out == NULL || out_size < UI_NET_SINK_MONO_SIZE) {
return -1;
}
@@ -58,8 +56,7 @@ int ui_net_sink_get_lcdmem(uint8_t *out, uint32_t out_size)
* 限定条件:默认输入组已建立
* 函数说明:映射为 LV_KEY_* 后经 lv_group_send_data 发向焦点对象
*/
void ui_net_sink_inject_key(uint8_t remo_key)
{
void ui_net_sink_inject_key(uint8_t remo_key) {
lv_group_t *g;
uint32_t key;
+3 -1
View File
@@ -106,7 +106,9 @@ STRIDE = W // 8 # I1:20 字节/行
# 页面结构变更导致漂移时,先确认新值合理再更新此常量(内存回归门禁)。
# 2026-09-14: 4864 → 5520(+656B)= run_headless 新增 ui_standby_init():待机模块
# 常驻屏 s_scr + 容器 s_cont 两个 lv_obj,随菜单基线同移(见 check_menu_rows.py)。
POOL_AFTER_ROUNDTRIP = 5520
# 2026-10-11: 5520 → 5448(−72B)= 裁剪 LVGL 控件类(F-2),随菜单基线 5440→5368 同移;
# **一次性残留不变,仍是 80 B**(5448 − 5368),说明泄漏特征未变,只是每对象样式表更小。
POOL_AFTER_ROUNDTRIP = 5448
def run_sim(seq, out):
+5 -1
View File
@@ -57,7 +57,11 @@ ROOT_X0, ROOT_Y0, ROOT_W, ROOT_H = 0, 44, 66, 107
# 先确认新值合理再更新此常量(内存回归门禁,见 docs/UI设计/UI_自动化测试流程.md)
# 2026-09-14: 4784 → 5440(+656B)= run_headless 新增 ui_standby_init():待机模块
# 常驻屏 s_scr + 容器 s_cont 两个 lv_obj(先于池测量创建),属有意结构变更。
MENU_POOL_BASELINE = 5440
# 2026-10-11: 5440 → 5368(−72B)= 裁剪 LVGL 控件类(F-2:label/button/buttonmatrix/
# bar/textarea 全关,simulator/lv_conf.h 与 Lib/lv_conf.h 对齐为同一特性子集)。
# 像素逐字节未变(11 页黄金参考在**裁剪前**生成、裁剪后全部一致);差值来自
# lv_theme_apply 不再为已不存在的控件类登记样式条目 → 每对象样式表更小。更优。
MENU_POOL_BASELINE = 5368
def load(path):