merge: dsh-95f12db9-ba22 合入 master(待机页六路实时值 + 实时页自动刷新)
包含三笔提交: - 待机显示页接入 UA/UB/UC/IA/IB/IC 六路实时值(v1.1) - 内容页框架新增自动失效机制,交流量页 300ms 启用 - 其余五个实时页按内容变化节拍接入(dc/yx/clock/ device_info/runreport,info 页切通道二新鲜源) 验证:双端构建 0 错 0 警;菜单 8 态 + 内容页 74 张 + rtlive 断言 PASS;三笔提交 52 钩子全绿;台账与体积 凭证随笔回填。 合并后在主树复跑全量门禁补齐合并提交期钩子盲区。
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: lvgl-simulator
|
||||
description: LVGL PC 模拟器构建/运行/自检入口:build_sim.bat 自动探测本机 VS、lvgl_sim.exe 无头截图与自检开关(--screenshot/--keys/--standby/--cfgtest/--logtest/--idle)、三套回归脚本、两份 lv_conf.h 同步门禁、构建报错速查(SDL2/lv_conf 找不到、全黑、键盘无响应)。
|
||||
description: LVGL PC 模拟器构建/运行/自检入口:build_sim.bat 自动探测本机 VS、lvgl_sim.exe 无头截图与自检开关(--screenshot/--keys/--standby/--cfgtest/--logtest/--idle/--rtlive)、三套回归脚本、两份 lv_conf.h 同步门禁、构建报错速查(SDL2/lv_conf 找不到、全黑、键盘无响应)。
|
||||
whenToUse: 需要构建或运行模拟器、无头截图出图、跑模拟器自检或 UI 回归、修改 simulator/ 目录代码或 simulator/lv_conf.h、排查模拟器构建/运行异常时。UI 改动后的完整回归走 ui-verify skill。
|
||||
---
|
||||
|
||||
@@ -28,12 +28,13 @@ PC 上编译运行与 MCU **同一份** UI 源码(CMake + MSVC + SDL2,x64,
|
||||
| `lvgl_sim.exe --standby shot.bin` | 待机页截图 |
|
||||
| `lvgl_sim.exe --cfgtest` | 配置引擎闭环:加载→轮询→外部改文件重载→外科式保存回验。临时用当前 CWD 的 `sim_config.ini`,**跑完无论成败一律删除**(旧行为连 PASS 路径也会留一份 4000 B 伪造档 → 之后每次运行都被加载 → 引擎按设计拒写超长文档 → 网口设置页保存行变 `ERR`,把像素回归带偏) |
|
||||
| `lvgl_sim.exe --logtest` | syslog 后端闭环(定长记录/时间戳/轮转) |
|
||||
| `lvgl_sim.exe --rtlive "dee" a.bin b.bin` | 框架自动失效实证:开交流量页截 A → 发布 1 帧确定性斜坡值 → 直接调 `ui_content_page_auto_poll()` → 截 B;退出码 0 且 A≠B = PASS(零按键新帧落屏;无头黄金参考从不走此路径——无头不跑 `lv_timer_handler`) |
|
||||
|
||||
⚠ **模拟器按当前工作目录读写 `sim_config.ini`**:目录里残留一份配置就会改变页面内容。`simulator/tests/` 两套回归因此一律在**空临时目录**里跑(`cwd=tmp`);手工跑 `--keys`/`--screenshot` 做对比时,也要先确认当前目录没有 `sim_config.ini`,否则"复现不了别人的 FAIL"。
|
||||
|
||||
## UI 改动回归 → 用 `ui-verify` skill(勿在此零散手跑)
|
||||
|
||||
5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 66 张黄金参考 + 往返池)→ `/keil-build` → `coding-standard`。
|
||||
5 步门禁 = 构建 → `check_menu_rows.py`(菜单 8 状态像素 + 池基线)→ `check_content_pages.py`(文本断言 + 74 张黄金参考 + 往返池 + rtlive)→ `/keil-build` → `coding-standard`。
|
||||
RemoDispBus 推流回归单独跑:`.venv\Scripts\python.exe simulator/tests/check_remo_sink.py`。
|
||||
黄金参考 `tests/ref/*.bin` **随仓库入库**(`.gitignore` 在全仓 *.bin 兜底之后又显式反选,2026-10-22 起;缺基准 = 新克隆跑 `ui-golden` 必 FAIL):不入库的只是派生目检图 `ref/*.png`,换机后先 `check_content_pages.py --update` 生成,再 `regen_ref_png.py` 出目检 PNG。
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
* id=%u 更可定位);绘制/按键/几何一律未动
|
||||
* 2026-11-02 DTU 项目组 新增 ui_content_page_current_desc(#69 板端导航身份查询:
|
||||
* 页 id 从"只有串口开/关沿可见"升级为可按帧查询)
|
||||
* 2026-11-13 DTU 项目组 实时页自动刷新:描述符 auto_ms>0 的页在打开期间由框架
|
||||
* 持一只 lv_timer 轮询发布序号(默认通道一 ui_data_rt_seq),
|
||||
* 序号变过才整页失效;新 API ui_content_page_auto_poll
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
@@ -55,6 +58,8 @@
|
||||
|
||||
#include "ui_tokens.h"
|
||||
|
||||
#include "ui_data.h"
|
||||
|
||||
#include "lvgl.h"
|
||||
|
||||
#define DBG_TAG "[UI_CONTENT_PAGE]"
|
||||
@@ -85,6 +90,8 @@ static uint16_t s_sel; /* 列表页选中行(
|
||||
static uint8_t s_perm; /* 本次授予权限(ui_perm_t;开页时装载,关页重置) */
|
||||
static lv_obj_t *s_scr; /* 页面屏幕(进入时建、返回时毁) */
|
||||
static lv_obj_t *s_cont; /* 页面全屏容器(焦点/键入口 + 自绘载体) */
|
||||
static lv_timer_t *s_auto_timer; /* 自动失效轮询定时器(仅 auto_ms>0 的页打开期间存在;关页即销) */
|
||||
static uint32_t s_auto_seq; /* 上一拍见到的发布序号(开页时装基准,开页本身不多刷一帧) */
|
||||
|
||||
/* 容器样式:flat(透明 + 无边框/圆角/内边距)取自共享样式表 ui_style.c——
|
||||
* 与 ui_menu_view / ui_standby 共用同一 const 对象(原先三处各定义一份逐字相同
|
||||
@@ -469,6 +476,42 @@ static void cp_key_cb(lv_event_t *e) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:自动刷新轮询一拍(契约见 ui_content_page.h;判定与序号基准只在这里一份)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:uiTask 上下文;无页打开 / 本页未订阅(auto_ms=0)时空操作
|
||||
* 函数说明:不在此拷帧——失效后由框架重绘走页面 refresh 的 seq 门控取新帧(§5.4
|
||||
* 页面私有缓存纪律不变),框架对数据内容零感知,只认序号
|
||||
*/
|
||||
void ui_content_page_auto_poll(void) {
|
||||
const content_page_def_t *d = s_open;
|
||||
uint32_t now;
|
||||
|
||||
if (d == NULL || d->auto_ms == 0u) {
|
||||
return;
|
||||
}
|
||||
now = (d->auto_seq != NULL) ? d->auto_seq() : ui_data_rt_seq();
|
||||
if (now == s_auto_seq) {
|
||||
return; /* 数据没变:零失效(省电屏带宽纪律) */
|
||||
}
|
||||
s_auto_seq = now;
|
||||
ui_content_page_invalidate(); /* 序号变过才整页失效 */
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:自动失效轮询定时器回调(薄封装;一切判定在 ui_content_page_auto_poll)
|
||||
* 入口参数:t - 定时器对象(未用) lv_timer_t *
|
||||
* 返回值:无
|
||||
* 限定条件:lv_timer_handler 内(uiTask 上下文)——与其他 LVGL 调用同线程同串行点
|
||||
* 函数说明:standby 期间内容页对象仍在(待机盖屏不关页),此时失效后台容器只是
|
||||
* 多一次脏区合并,待机屏自身重绘节律不受影响(实测待机像素回归绿)
|
||||
*/
|
||||
static void cp_auto_timer_cb(lv_timer_t *t) {
|
||||
(void)t;
|
||||
ui_content_page_auto_poll();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:打开指定内容页(建屏 + 切屏 + 焦点置页面容器)
|
||||
* 入口参数:desc - 页面描述符(取自 ui_page_registry_for / ui_page_registry_password)
|
||||
@@ -517,6 +560,19 @@ bool ui_content_page_open(const content_page_def_t *desc, uint8_t perm) {
|
||||
desc->init_cb(); /* 页面模块重置运行态(编辑模式/光标/保存标志等) */
|
||||
}
|
||||
|
||||
/* 自动失效轮询(实时页自动刷新):定时器随页建销,页面模块零定时器。基准取
|
||||
* "开页瞬间"的序号——开页首帧绘制本就已取过数,不该再为基线多补一次重绘;
|
||||
* 之后每拍只在序号变过时失效。创建失败(LVGL 池耗尽)只降级回"按键才重绘"
|
||||
* 旧行为并留痕,不挡开页(同 ui_w_setview 轮询定时器的降级口径)。 */
|
||||
s_auto_timer = NULL;
|
||||
if (desc->auto_ms != 0u) {
|
||||
s_auto_seq = (desc->auto_seq != NULL) ? desc->auto_seq() : ui_data_rt_seq();
|
||||
s_auto_timer = lv_timer_create(cp_auto_timer_cb, (uint32_t)desc->auto_ms, NULL);
|
||||
if (s_auto_timer == NULL) {
|
||||
DBG_ERROR("auto timer create failed (id=%s): key-only refresh fallback", desc->id);
|
||||
}
|
||||
}
|
||||
|
||||
/* 焦点/键入口:入默认输入组并获焦(先于切屏,保证按键立即由本模块接管) */
|
||||
lv_group_t *g = lv_group_get_default();
|
||||
if (g != NULL) {
|
||||
@@ -550,6 +606,13 @@ void ui_content_page_close(void) {
|
||||
|
||||
DBG_INFO("page close: id=%s", s_open->id);
|
||||
|
||||
/* 自动失效定时器随页销毁:留着它,下一拍就会去失效即将释放的 s_cont
|
||||
* (回调与关页同在 uiTask 串行点,此处删表即绝后患) */
|
||||
if (s_auto_timer != NULL) {
|
||||
lv_timer_delete(s_auto_timer);
|
||||
s_auto_timer = NULL;
|
||||
}
|
||||
|
||||
lv_obj_delete(s_cont); /* 先删容器 */
|
||||
lv_obj_delete(s_scr); /* 再删屏 */
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
* ui_page_registry.c(菜单树 #page= 注记为唯一真源),
|
||||
* 逻辑层手写 menu_content_page_for() 随之删除;
|
||||
* 描述符新增 id 字段(ASCII 页名,供日志/诊断定位是哪一页)
|
||||
* 2026-11-13 DTU 项目组 实时页自动刷新:描述符新增 auto_ms/auto_seq 两字段,
|
||||
* 新 API ui_content_page_auto_poll——框架在页面打开期间持
|
||||
* 一只轮询定时器,发布序号变过才整页失效,数据没变零重绘;
|
||||
* 页面模块不再各自造定时器(周期账见 ui_page_ac.c 的
|
||||
* AC_AUTO_MS 注与 UI_数据供给设计说明.md §5.4)
|
||||
*/
|
||||
#ifndef UI_CONTENT_PAGE_H
|
||||
#define UI_CONTENT_PAGE_H
|
||||
@@ -80,6 +85,15 @@ typedef struct {
|
||||
uint16_t (*count_cb)(void); /* 动态行数(记录页) */
|
||||
void (*draw_cb)(lv_layer_t *layer, const lv_area_t *clip); /* 表单/操作页:自定义内容 */
|
||||
bool (*key_cb)(lv_key_t key); /* 页面自有键;true = 页已处理 */
|
||||
/* —— 自动失效轮询(全可选;auto_ms=0 = 只在按键重绘,未订阅页行为逐位不变)——
|
||||
* auto_ms>0:本页打开期间框架持一只 lv_timer(周期 auto_ms ms,uiTask 上下文随
|
||||
* lv_timer_handler 走),每拍取发布序号(auto_seq(),NULL = ui_data_rt_seq()
|
||||
* 即通道一),序号与上一拍不同才整页失效——取数门控仍是页面 refresh 里的
|
||||
* seq 判断(UI_数据供给设计说明.md §5.4 的页面私有缓存约定原样有效)。
|
||||
* 关页即销定时器;页面模块禁止自造定时器。周期取值账见 ui_page_ac.c 的
|
||||
* AC_AUTO_MS 注释(发布者 rt 每 200ms 一帧,见同文 §5.2)。 */
|
||||
uint16_t auto_ms; /* 轮询周期(ms);0 = 不启用自动刷新 */
|
||||
uint32_t (*auto_seq)(void); /* 新鲜度序号源;NULL = ui_data_rt_seq() */
|
||||
} content_page_def_t;
|
||||
|
||||
/*
|
||||
@@ -154,4 +168,16 @@ bool ui_content_page_is_editable(void);
|
||||
*/
|
||||
uint16_t ui_content_page_selected_row(void);
|
||||
|
||||
/*
|
||||
* 函数功能:自动刷新轮询一拍(生产由框架定时器回调调用;无头模拟器 --rtlive 自检
|
||||
* 没有定时器节拍、直调本函数顶替——两个入口共用同一份判定,规则不会两套)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:uiTask 上下文(lv_timer_handler 内或其等价串行点)
|
||||
* 函数说明:1. 无页打开 / 本页 auto_ms=0:空操作;
|
||||
* 2. 序号与上一拍相同:不失效——数据没变的一次整屏重绘是纯支出(屏带宽/功耗);
|
||||
* 3. 序号变了:记下新基准并整页失效,真正取数仍走页面 refresh 的 seq 门控
|
||||
*/
|
||||
void ui_content_page_auto_poll(void);
|
||||
|
||||
#endif /* UI_CONTENT_PAGE_H */
|
||||
|
||||
@@ -50,6 +50,19 @@
|
||||
#define AC_Q_BAD (UI_DATA_Q_IV | UI_DATA_Q_NT | UI_DATA_Q_RAW | UI_DATA_Q_SB | \
|
||||
UI_DATA_Q_BL | UI_DATA_Q_OV)
|
||||
|
||||
/* 自动刷新轮询周期(ms;框架级机制,字段语义见 ui_content_page.h 的 auto_ms 注)。
|
||||
* 取 300 的账(允许区间 250~500;发布者 rt 通道每 200ms 一帧,见
|
||||
* UI_数据供给设计说明.md §5.2):
|
||||
* ① 必须 ≥ 发布节拍 200ms——轮得比发得快只是空转,白耗 uiTask;
|
||||
* ② 避开 400ms(恰为 2×200):采样拍与发布拍锁相,感知滞后最坏 800ms;
|
||||
* ③ 300 = 15 × LVGL 刷新周期 20ms(LV_DEF_REFR_PERIOD 两端同值):失效恰好落进
|
||||
* 下一次整刷合并,不多触发一帧;最坏屏上滞后 = 200 + 300 = 500ms,对一周波
|
||||
* 相量级的电气量读数足够,读表不觉得卡;
|
||||
* ④ 250 只比 300 早至多 50ms(新帧首先受发布节拍约束),代价是多 20% 空转拍;
|
||||
* 500 最坏滞后升到 700ms,数值跳变看着像"翻页"。
|
||||
* 稳态开销为零:序号没变不失效不重绘(ui_content_page_auto_poll 纪律)。 */
|
||||
#define AC_AUTO_MS 300u
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
/* 行名表(Flash) */
|
||||
@@ -228,4 +241,7 @@ const content_page_def_t ui_page_ac_desc = {
|
||||
.rows = NULL,
|
||||
.init_cb = ac_init,
|
||||
.refresh = ac_refresh,
|
||||
/* 自动刷新(task-2):框架轮询通道一发布序号,来新帧才整页失效——本页不自造
|
||||
* 定时器;auto_seq 缺省 = ui_data_rt_seq(),与 ac_frame_ready 的取数门控同源。 */
|
||||
.auto_ms = AC_AUTO_MS,
|
||||
};
|
||||
|
||||
@@ -385,4 +385,11 @@ const content_page_def_t ui_page_clock_desc = {
|
||||
.refresh = pg_refresh,
|
||||
.draw_cb = pg_draw,
|
||||
.key_cb = pg_key,
|
||||
/* 自动失效轮询(机制见 ui_content_page.h 的 auto_ms 注;口径见供给说明 §5.5):
|
||||
* 本页快变项只有墙钟秒尾——rt 帧 200ms 一发,但秒每 1s 才变,轮询快于 1s
|
||||
* 只会把同一秒多画几遍(seq 随帧走,每拍都触发整页重绘)。取 1000ms:
|
||||
* 1000 = 5 × 发布节拍 = 50 × LVGL 整刷周期 20ms;最坏屏上滞后 200 + 1000 =
|
||||
* 1200ms,屏显是墙钟字符串(非自走计数),迟到的是展示不是时刻,不累积漂移。
|
||||
* 编辑态无碍:失效只按当前叠层状态重绘,不动编辑数据 */
|
||||
.auto_ms = 1000u,
|
||||
};
|
||||
|
||||
@@ -149,4 +149,9 @@ const content_page_def_t ui_page_dc_desc = {
|
||||
.rows = NULL,
|
||||
.init_cb = dc_init,
|
||||
.refresh = dc_refresh,
|
||||
/* 自动失效轮询(框架级机制见 ui_content_page.h 的 auto_ms 注;口径见供给说明 §5.5):
|
||||
* 直流/站温/温度行随 rt 帧每 200ms 推进,可见内容与电气量同拍——周期账目与
|
||||
* ac 页同构(300 = 15 × LVGL 整刷周期 20ms,最坏屏上滞后 200 + 300 = 500ms),
|
||||
* 四点账全文在 ui_page_ac.c 的 AC_AUTO_MS 注释,此处不复述 */
|
||||
.auto_ms = 300u,
|
||||
};
|
||||
|
||||
@@ -259,4 +259,13 @@ const content_page_def_t ui_page_device_info_desc = {
|
||||
.fixed_total_pages = 1u,
|
||||
.init_cb = di_init,
|
||||
.refresh = di_refresh,
|
||||
/* 自动失效轮询——通道二(新鲜度源换 info_seq;机制见 ui_content_page.h 的
|
||||
* auto_ms/auto_seq 注,口径见供给说明 §5.5):info 与 rt 同拍发布(每 200ms
|
||||
* 两帧连发,见 App/app/ui_data_feed.c 的 ui_data_feed_tick;App 侧节拍值,
|
||||
* 本文件不 include App 头,口径引用文档);本页可见内容近静态(型号/版本/
|
||||
* 身份),只有 IP、端口等网络事件级变化——取 1000ms 大档:事件上屏最坏
|
||||
* 200 + 1000 = 1200ms,对配置生效核对足够;seq 恒随帧走,周期即稳态重绘频率,
|
||||
* 近静态页没必要快 */
|
||||
.auto_ms = 1000u,
|
||||
.auto_seq = ui_data_info_seq,
|
||||
};
|
||||
|
||||
@@ -276,4 +276,11 @@ const content_page_def_t ui_page_runreport_desc = {
|
||||
.count_cb = rr_count,
|
||||
.init_cb = rr_init,
|
||||
.refresh = rr_refresh,
|
||||
/* 自动失效轮询——通道二(.auto_seq = ui_data_info_seq;机制见 ui_content_page.h
|
||||
* 的 auto_ms/auto_seq 注,口径见供给说明 §5.5):本页是上电计数、复位原因、
|
||||
* 104 观测计数与记录条数的汇总,活跃链路(104 轮询、记录追加)下计数秒级
|
||||
* 走动,1000ms 档既让读数"看起来在活",又不在稳态多刷(seq 恒随 info 帧
|
||||
* 前进,周期即重绘频率;info 与 rt 同拍 200ms,见供给说明 §5.2) */
|
||||
.auto_ms = 1000u,
|
||||
.auto_seq = ui_data_info_seq,
|
||||
};
|
||||
|
||||
@@ -202,4 +202,10 @@ const content_page_def_t ui_page_yx_desc = {
|
||||
.init_cb = pg_init,
|
||||
.count_cb = pg_count,
|
||||
.refresh = pg_refresh,
|
||||
/* 自动失效轮询(机制见 ui_content_page.h 的 auto_ms 注;口径见供给说明 §5.5):
|
||||
* 遥信变位是低频事件,屏上可见变化只在 rt 帧(200ms 拍)落地时发生;seq 恒
|
||||
* 随帧前进,所以轮询周期就是稳态整页重绘频率——取 500ms 的账:变位"1s 内
|
||||
* 上屏"够用(最坏 200 + 500 = 700ms 到位),比电气量档 300ms 少 40% 无内容
|
||||
* 变化的重绘;500 = 25 × LVGL 整刷周期 20ms,落整刷边界 */
|
||||
.auto_ms = 500u,
|
||||
};
|
||||
|
||||
+119
-12
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* 模块名称:UI Standby (待机显示页)
|
||||
* 模块功能:长时间无按键操作时自动切入的默认界面(6 路电流值 + 分隔线 + 底部时钟);
|
||||
* 模块功能:长时间无按键操作时自动切入的默认界面(UA/UB/UC/IA/IB/IC 六路实时值 + 分隔线 + 底部时钟);
|
||||
* 数值经 ui_data 通道一实时帧 yc[](App 层装配,本文件零 App include),口径同 ui_page_ac.c;
|
||||
* 屏幕覆盖层模式——进入保存前屏与焦点、ENTER(确认键)还原,其余键忽略;
|
||||
* 空闲检测 = lv_timer(1s 周期),活动通知由菜单/内容页键事件挂钩
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||||
@@ -8,6 +9,9 @@
|
||||
* 创建日期:2026-09-14
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.2)
|
||||
* v1.1 2026-11-13 DTU 项目组 v1 占位(6 行电流恒 0.00A)→ UA/UB/UC/IA/IB/IC 实时值:
|
||||
* ui_data 通道一按 seq 门控取帧;无首帧/坏质显 "--";定点
|
||||
* 格式化复用 ui_w_editor_fmt_scaled;单位随 eng(引脚档不挂)
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
@@ -16,9 +20,11 @@
|
||||
#include "ui_standby.h"
|
||||
|
||||
#include "lvgl.h"
|
||||
#include "ui_data.h"
|
||||
#include "ui_menu_view.h"
|
||||
#include "ui_style.h"
|
||||
#include "ui_tokens.h"
|
||||
#include "ui_w_editor.h"
|
||||
|
||||
#define DBG_TAG "[UI_STANDBY]"
|
||||
#include "dbg_log.h"
|
||||
@@ -27,24 +33,40 @@
|
||||
|
||||
/*
|
||||
* 布局常量(设备像素;参考 mockups/待机显示页面.jpg):
|
||||
* 屏幕宽高取令牌 UI_SCREEN_W/H;6 路电流值行自 y=28 起、行距 18
|
||||
* 屏幕宽高取令牌 UI_SCREEN_W/H;六路实时值行自 y=28 起、行距 18
|
||||
* (末行 y=118..130);分隔线 y=132;底部时钟 y=140 居中
|
||||
*/
|
||||
#define SB_CH_COUNT 6u /* 电流通道数 */
|
||||
#define SB_CH_COUNT 6u /* 显示通道数(六路主要电力量;yc 下标见 s_ch_yc) */
|
||||
#define SB_ROW_Y0 28 /* 首数据行顶 y */
|
||||
#define SB_PITCH 18 /* 行距(相邻行槽顶间距) */
|
||||
#define SB_LABEL_X 24 /* 通道名 x(Ib/Ic/...) */
|
||||
#define SB_LABEL_X 24 /* 通道名 x(UA/UB/UC/IA/IB/IC) */
|
||||
#define SB_EQ_X 72 /* "=" 号 x */
|
||||
#define SB_VALUE_X 96 /* 数值 x(0.00A;v1 占位,待交流量标定管线接入实时值) */
|
||||
#define SB_VALUE_X 96 /* 数值 x(数字+单位 或 "--") */
|
||||
|
||||
/* 值串缓冲长度:最坏 "2147483.647V" = 12 + NUL,取 16 留余量 */
|
||||
#define SB_VAL_BUF_LEN 16u
|
||||
#define SB_INVALID "--" /* 空值串(同交流量页 AC_INVALID) */
|
||||
#define SB_DECIMALS 3u /* 电压/电流小数位(同交流量页 AC_IA_DECIMALS) */
|
||||
/* 不可信品质合集:无效 / 未攒满一周波 / 透传码值 / 自检异常 / 闭锁 / 越程
|
||||
* (逐字同 ui_page_ac.c 的 AC_Q_BAD——两处判据不可能漂移) */
|
||||
#define SB_Q_BAD (UI_DATA_Q_IV | UI_DATA_Q_NT | UI_DATA_Q_RAW | UI_DATA_Q_SB | \
|
||||
UI_DATA_Q_BL | UI_DATA_Q_OV)
|
||||
|
||||
#define SB_LINE_Y 132 /* 分隔线 y(高 1px) */
|
||||
#define SB_TIME_Y 140 /* 底部时钟放置 y(居中) */
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
/* 通道名与占位数值(Flash;全 ASCII,无需中文字形) */
|
||||
static const char *const s_ch_name[SB_CH_COUNT] = { "Ib", "Ic", "Io", "Ia2", "Ib2", "Ic2" };
|
||||
static const char *const s_ch_value[SB_CH_COUNT] = { "0.00A", "0.00A", "0.00A", "0.00A", "0.00A", "0.00A" };
|
||||
/* 通道名表 + yc[] 语义下标映射(Flash;全 ASCII,无需中文字形)。
|
||||
* 下标 = adc_semantic_t 序(UA/UB/UC/UN/IA/IB/IC/IN/TEMP):跳过 UN(3)/IN(7)——
|
||||
* 用户点名显示六路(2026-11-13),且 UN 本台架永久无数据,摆一排恒 "--" 的假通道
|
||||
* 不如不摆(取舍逻辑同交流量页只显 Ua/U0/Ia,docs/UI设计/页面扩充详细方案.md §3.1.1) */
|
||||
static const char *const s_ch_name[SB_CH_COUNT] = { "UA", "UB", "UC", "IA", "IB", "IC" };
|
||||
static const uint8_t s_ch_yc[SB_CH_COUNT] = { 0u, 1u, 2u, 4u, 5u, 6u };
|
||||
|
||||
/* 帧缓存:发布序号变化才整帧拷一次(页面侧约定 = ui_data.h 头注 / UI_数据供给设计说明.md §5.4) */
|
||||
static ui_data_rt_t s_rt;
|
||||
static uint32_t s_seq;
|
||||
|
||||
static lv_obj_t *s_scr; /* 待机屏(init 时建、常驻不销毁) */
|
||||
static lv_obj_t *s_cont; /* 全屏容器(焦点/键入口 + 自绘载体,无子对象) */
|
||||
@@ -75,6 +97,85 @@ static bool sb_areas_intersect(const lv_area_t *a, const lv_area_t *b) {
|
||||
return (x1 <= x2 && y1 <= y2);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把常量值串安全搬进显示缓冲(替代 strcpy,规范 7.5;同 ui_page_ac.c 的 ac_put)
|
||||
* 入口参数:a_dst - 目标缓冲(SB_VAL_BUF_LEN) char *
|
||||
* a_lit - 源字面量(编译期串,长度由宏用 sizeof 算好) const char *
|
||||
* a_len - 需拷贝字节数(不含 NUL) uint32_t
|
||||
* 返回值:无
|
||||
* 限定条件:a_dst/a_lit 非 NULL;a_len 超缓冲时截断而不是越界
|
||||
*/
|
||||
static void sb_put(char *a_dst, const char *a_lit, uint32_t a_len) {
|
||||
if (a_len > (SB_VAL_BUF_LEN - 1u)) {
|
||||
a_len = SB_VAL_BUF_LEN - 1u;
|
||||
}
|
||||
(void)memcpy(a_dst, a_lit, a_len);
|
||||
a_dst[a_len] = (char)0;
|
||||
}
|
||||
|
||||
/* 值串落屏统一走这里:长度由 sizeof 在调用点算,改串不会漏改长度 */
|
||||
#define SB_PUT(a_dst, a_lit) sb_put((a_dst), (a_lit), (uint32_t)(sizeof(a_lit) - 1u))
|
||||
|
||||
/*
|
||||
* 函数功能:取用最新实时帧(只在发布序号变化时整帧拷)——读法同 ui_page_ac.c 的 ac_frame_ready
|
||||
* 入口参数:无
|
||||
* 返回值:1 - 有可用帧;0 - 首帧未到或本帧无效 uint8_t
|
||||
* 限定条件:UI 任务绘制上下文
|
||||
* 函数说明:读失败(首帧未到)时清缓存并记住本次序号,避免每次重绘重复尝试;下一帧序号
|
||||
* 必然变化 → 自动恢复。待机 1s 定时器整屏 invalidate 就是数值刷新节拍,不新增定时器
|
||||
*/
|
||||
static uint8_t sb_frame_ready(void) {
|
||||
uint32_t now = ui_data_rt_seq();
|
||||
|
||||
if (now != s_seq) {
|
||||
s_seq = now;
|
||||
if (ui_data_rt(&s_rt) != 0) {
|
||||
(void)memset(&s_rt, 0, sizeof(s_rt));
|
||||
return 0u;
|
||||
}
|
||||
}
|
||||
return (s_rt.valid != 0u) ? 1u : 0u;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:拼一行值串(数字 + 单位,或 "--")
|
||||
* 入口参数:a_idx - 显示行下标 0..5 uint32_t
|
||||
* a_buf - 输出缓冲 char *
|
||||
* a_len - 缓冲容量(含 NUL) uint32_t
|
||||
* 返回值:无
|
||||
* 限定条件:UI 任务绘制上下文
|
||||
* 函数说明:口径同 ui_page_ac.c——无首帧或品质带 SB_Q_BAD 一律 "--"("有数但不可信"与
|
||||
* "没有数"同形);数字走共享定点格式化 ui_w_editor_fmt_scaled(3 位小数 =
|
||||
* 10^3 定点,负值钳 0 交给品质位解释),页面侧不引 %f 软浮点打印。单位随 eng:
|
||||
* 二次/一次值档 UA/UB/UC 挂 "V"、IA/IB/IC 挂 "A";ENG_PIN 档不挂单位——引脚档
|
||||
* 数字是 ADC 引脚电压而非端子电流量,挂单位就是替用户完成没做的换算(交流量页
|
||||
* 同档另设量纲口径行标注,待机屏无第 7 行可标,故数字照显、单位缺席)
|
||||
*/
|
||||
static void sb_value_str(uint32_t a_idx, char *a_buf, uint32_t a_len) {
|
||||
const ui_data_yc_t *p;
|
||||
uint32_t n;
|
||||
|
||||
if ((a_idx >= SB_CH_COUNT) || (sb_frame_ready() == 0u) ||
|
||||
(s_ch_yc[a_idx] >= (uint8_t)UI_DATA_YC_N)) {
|
||||
SB_PUT(a_buf, SB_INVALID);
|
||||
return;
|
||||
}
|
||||
p = &s_rt.yc[s_ch_yc[a_idx]];
|
||||
if ((p->qds & (uint8_t)SB_Q_BAD) != 0u) {
|
||||
SB_PUT(a_buf, SB_INVALID);
|
||||
return;
|
||||
}
|
||||
ui_w_editor_fmt_scaled((p->val > 0.0f) ? (int32_t)((p->val * 1000.0f) + 0.5f) : 0,
|
||||
SB_DECIMALS, a_buf, a_len);
|
||||
if (s_rt.eng != (uint8_t)UI_DATA_ENG_PIN) {
|
||||
n = (uint32_t)strlen(a_buf);
|
||||
if ((n + 1u) < a_len) {
|
||||
a_buf[n] = (a_idx < 3u) ? 'V' : 'A';
|
||||
a_buf[n + 1u] = (char)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:绘制居中单行文本(底部时钟)
|
||||
* 入口参数:layer - 绘制层 lv_layer_t *
|
||||
@@ -100,11 +201,12 @@ static void sb_draw_centered_text(lv_layer_t *layer, const char *text, int32_t y
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:待机屏自绘(6 路电流值 + 分隔线 + 底部时钟;逐行 culling)
|
||||
* 函数功能:待机屏自绘(六路实时值 + 分隔线 + 底部时钟;逐行 culling)
|
||||
* 入口参数:e - 事件指针 lv_event_t *
|
||||
* 返回值:无
|
||||
* 限定条件:事件为 LV_EVENT_DRAW_MAIN,待机模块已初始化
|
||||
* 函数说明:1bpp 极性 white=暗墨——文本/分隔线均白色;数值 v1 占位 0.00A
|
||||
* 函数说明:1bpp 极性 white=暗墨——文本/分隔线均白色;数值逐行现拼(sb_value_str),
|
||||
* 待机 1s invalidate 即数值刷新节拍
|
||||
*/
|
||||
static void sb_draw_cb(lv_event_t *e) {
|
||||
lv_layer_t *layer = lv_event_get_layer(e);
|
||||
@@ -113,6 +215,7 @@ static void sb_draw_cb(lv_event_t *e) {
|
||||
int32_t row_y = SB_ROW_Y0 + (int32_t)i * SB_PITCH;
|
||||
lv_area_t strip;
|
||||
lv_draw_label_dsc_t ld;
|
||||
char vb[SB_VAL_BUF_LEN];
|
||||
|
||||
strip.x1 = 0;
|
||||
strip.x2 = UI_SCREEN_W - 1;
|
||||
@@ -142,8 +245,8 @@ static void sb_draw_cb(lv_event_t *e) {
|
||||
|
||||
lv_draw_label_dsc_init(&ld);
|
||||
ld.base.layer = layer;
|
||||
ld.text = s_ch_value[i];
|
||||
ld.text_static = 1;
|
||||
sb_value_str(i, vb, (uint32_t)sizeof(vb)); /* 栈缓冲同帧有效(同底部时钟 ts 模式) */
|
||||
ld.text = vb;
|
||||
ld.color = UI_COL_FG;
|
||||
ld.ofs_x = SB_VALUE_X;
|
||||
ld.ofs_y = 0;
|
||||
@@ -197,6 +300,10 @@ static void sb_enter(void) {
|
||||
if (s_active || s_scr == NULL) {
|
||||
return;
|
||||
}
|
||||
/* 进入即复位帧缓存(同交流量页 ac_init 开页复位理由:不沿用上次的陈旧帧号,
|
||||
* 强制下一次绘制重取最新帧) */
|
||||
s_seq = 0u;
|
||||
(void)memset(&s_rt, 0, sizeof(s_rt));
|
||||
s_prev_scr = lv_screen_active();
|
||||
g = lv_group_get_default();
|
||||
s_prev_focus = (g != NULL) ? lv_group_get_focused(g) : NULL;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* 模块名称:UI Standby (待机显示页)
|
||||
* 模块功能:长时间无按键操作时自动切入的默认界面(6 路电流值 + 分隔线 + 底部时钟);
|
||||
* 模块功能:长时间无按键操作时自动切入的默认界面(UA/UB/UC/IA/IB/IC 六路实时值 + 分隔线 + 底部时钟);
|
||||
* 数值经 ui_data 通道一实时帧 yc[](口径同 ui_page_ac.c);
|
||||
* 屏幕覆盖层模式——进入保存前屏与焦点、ENTER(确认键)还原,其余键忽略;
|
||||
* 空闲检测 = lv_timer(1s 周期),活动通知由菜单/内容页键事件挂钩
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
|
||||
@@ -8,6 +9,7 @@
|
||||
* 创建日期:2026-09-14
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本(设计见 docs/子系统设计/运行与自检记录设计说明.md §5.2)
|
||||
* v1.1 2026-11-13 DTU 项目组 6 路电流占位 → UA/UB/UC/IA/IB/IC 实时值(口径同 ui_page_ac.c)
|
||||
*/
|
||||
|
||||
#ifndef UI_STANDBY_H
|
||||
|
||||
@@ -340,6 +340,25 @@ static void pg_refresh(uint16_t row, char *name, char *value) {
|
||||
|
||||
> 用 `ui_data_rt_seq()` 门控而不是"row==0 才刷新",是为了避免依赖"框架总从第 0 行开始绘制"这一隐式时序假设——框架换绘制顺序时页面不会静默出错。
|
||||
|
||||
### 5.5 框架级自动失效(实时页不靠按键也有新数)
|
||||
|
||||
§5.4 的 seq 门解决"取数不冗余",不解决"什么时候该重绘":框架旧行为是**按键触发绘制才走 `refresh()`**——`rt` 帧每 200 ms 在发,人停在「交流量」页不动按键,屏上数值就是冻结的。
|
||||
|
||||
新机制(task-2):`content_page_def_t` 尾部新增两个可选字段,定时器由**框架 `ui_content_page` 统一持有**(开页创建、关页销毁,同时最多一只),页面模块禁止自造轮询定时器:
|
||||
|
||||
| 字段 | 语义 |
|
||||
|---|---|
|
||||
| `uint16_t auto_ms` | 0 = 关闭(保持旧行为;指定初始化未写即 0);>0 = 该页打开期间的轮询周期 |
|
||||
| `uint32_t (*auto_seq)(void)` | 新鲜度来源;NULL(缺省)= `ui_data_rt_seq()`;读 `info` 通道的页面可填 `ui_data_info_seq()` |
|
||||
|
||||
每拍框架调 `ui_content_page_auto_poll()`(回调经 uiTask 主循环 `lv_timer_handler` 触发,与绘制钩子同上下文):**序号变了才整页失效,没变零动作**——不拷帧、不失效、不重绘,稳态开销与旧行为一致;取数门控仍走 §5.4 的页面私有 seq 缓存,失效只是把"下次按键才有机会读新帧"变成"到点就有机会读新帧"。
|
||||
|
||||
当前口径:
|
||||
|
||||
- **六个实时页全部接入(用户裁定 2026-11-13)**。seq 恒随帧前进,所以轮询周期 = 稳态整页重绘频率——可见内容变化越慢取档越大:`ac`/`dc` 300 ms(电气量随 rt 帧帧帧推进;取值账目:轮询不得快于 200 ms 发布节拍、避开 400 ms 与其锁相、300 = 15 × LVGL 整刷周期 20 ms(两端 `LV_DEF_REFR_PERIOD` 同值),最坏屏上滞后 200 + 300 = 500 ms;全文在 `ui_page_ac.c` 的 `AC_AUTO_MS` 注释);`yx` 500 ms(变位低频事件,按"1 s 内上屏"到位——最坏 700 ms,较 300 档少 40% 稳态重绘);`clock` 1000 ms(快变项只有墙钟秒尾,快于 1 s 的轮询只会把同一秒多画几遍);`device_info`/`runreport` 1000 ms 并显式 `.auto_seq = ui_data_info_seq`(通道二;`info` 与 `rt` 同拍——`ui_data_feed_tick` 每 200 ms 拍两帧连发,见 §5.2)。各页取值账目在该页描述符注释里,本文不复述。
|
||||
- **不迁移**:待机屏 `ui_standby.c`(自持 1 s 整屏定时器 + seq 门控)、整定视图 `ui_w_setview.c`(`sv_poll` 定时器盯 `par` 通道)——两处各自已有节拍,纳入框架只会多一只定时器。
|
||||
|
||||
**回归确定性**:无头模式(`--keys`/74 张黄金参考)从不跑 `lv_timer_handler`,定时器不可能在截屏前触发,且仿真仍是"一次发布冻结帧"——黄金参考逐字节不变(`check_content_pages.py` 实证)。自动失效路径的验证走新开关 `--rtlive <键序> <A.bin> <B.bin>`:开页截 A → 发布第 1 号确定性斜坡帧 → **直接调用** `ui_content_page_auto_poll()`(等价定时器到点)→ 截 B;A≠B 即证明"零按键、新帧落屏"。窗口模式(真机行为仿真)每 200 ms 发一帧斜坡值,屏上数值自己会走。
|
||||
---
|
||||
|
||||
## 6. 写路径(定值整定)
|
||||
|
||||
@@ -26,7 +26,7 @@ cmake --build simulator/build --config Debug # 0) 构建模拟器(须 0 警
|
||||
| 步 | 脚本/命令 | 门禁内容 | 预期输出 |
|
||||
|---|---|---|---|
|
||||
| 1 | `simulator/tests/check_menu_rows.py` | 菜单 8 状态:选中行白带 / 非选行黑底白字 / 框体 1px 边框 / 往返像素一致(MENU-STATE-03==MENU-STATE-01、MENU-STATE-06==MENU-STATE-01、MENU-STATE-07==MENU-STATE-02)+ MENU-STATE-00 池基线 | `PASS: 8 状态全部断言通过(8 图,池基线 5368 B)` |
|
||||
| 2 | `simulator/tests/check_content_pages.py` | ① 文本级断言(字形模板匹配,正向 484 条 + 反向 45 条,独立于 `ref/`)→ ② 74 张截图与 `ref/` 黄金参考**逐字节**比对 + 开页-关页往返池水位 | `PASS: 74 内容页 + 往返池水位全部通过`(文本断言先于像素比对,失败即中止) |
|
||||
| 2 | `simulator/tests/check_content_pages.py` | ① 文本级断言(字形模板匹配,正向 484 条 + 反向 45 条,独立于 `ref/`)→ ② 74 张截图与 `ref/` 黄金参考**逐字节**比对 + 开页-关页往返池水位 → ③ `--rtlive` 框架自动失效实证(开交流量页截 A → 发布 1 帧确定性斜坡值 → 直调 `ui_content_page_auto_poll()` → 截 B,A≠B 即 PASS;机制见 `UI_数据供给设计说明.md` §5.5) | `PASS: 74 内容页 + 往返池水位 + rtlive 全部通过`(文本断言先于像素比对,失败即中止) |
|
||||
| 3 | `keil-build` skill(`keil_build.py`) | MCU 端编译(UI 源码双端共用,改一端必须过另一端) | 退出码 0 = BUILD_OK |
|
||||
| 4 | `scripts/check_coding_standard.py` | 代码规范 V1.0 机械检查 | PASS |
|
||||
|
||||
|
||||
+19
-5
@@ -96,15 +96,19 @@ flowchart TD
|
||||
| `bool ui_content_page_open(id)` | 开页 | ① 查 `s_defs[id]`:NULL → 打 `ERROR` 日志并返回 false(枚举/表漂移防护)② `lv_obj_create(NULL)` 建屏 + 全屏容器(flat 常量样式 + `IGNORE_LAYOUT` + `CLICK_FOCUSABLE`)③ 容器**先**入默认输入组并获焦 ⓸ `lv_screen_load` 切屏 |
|
||||
| `void ui_content_page_close()` | 关页 | ① 焦点**先**回菜单持久容器(防输入组焦点悬空)② 切回菜单屏(菜单屏对象未动,原样还原)③ 再删容器+屏,池内存回落 |
|
||||
| `void ui_content_page_invalidate()` | 整页重绘 | 失效页面容器;供表单/操作页自身状态变化后调用(无页打开时空操作) |
|
||||
| `bool ui_content_page_is_open()` | 查询 | `s_open != CONTENT_PAGE_COUNT` |
|
||||
| `void ui_content_page_auto_poll()` | 自动失效轮询 | 供框架定时器(`auto_ms>0` 的页打开期间才存在)每拍调用:发布序号(`auto_seq()`,NULL = `ui_data_rt_seq()`)与上一拍不同才整页失效,没变零动作;无页打开 / `auto_ms=0` 时空操作。模拟器 `--rtlive` 直接调它做确定性验证(机制见 `UI_数据供给设计说明.md` §5.5) |
|
||||
| `bool ui_content_page_is_open()` | 查询 | `s_open != NULL` |
|
||||
|
||||
框架持有的运行状态(全部 4 个变量,约 20 B):
|
||||
框架持有的运行状态(基础 5 个 + 自动失效 2 个,与 ui_content_page.c 静态块一致):
|
||||
|
||||
```c
|
||||
static content_page_id_t s_open; /* 当前打开的页(CONTENT_PAGE_COUNT = 未打开) */
|
||||
static const content_page_def_t *s_open; /* 当前打开页的描述符(NULL = 未打开) */
|
||||
static uint16_t s_sel; /* 列表页选中行(全局下标,跨页连续;每次进入清零) */
|
||||
static uint8_t s_perm; /* 本次开页授予的权限(ui_perm_t;开页时装载,关页重置) */
|
||||
static lv_obj_t *s_scr; /* 页面屏幕(进入时建、返回时毁) */
|
||||
static lv_obj_t *s_cont; /* 页面全屏容器(焦点/键入口 + 自绘载体) */
|
||||
static lv_timer_t *s_auto_timer; /* 自动失效轮询定时器(仅 auto_ms>0 的页打开期间存在;关页即销) */
|
||||
static uint32_t s_auto_seq; /* 上一拍见到的发布序号(开页时装基准,开页本身不多刷一帧) */
|
||||
```
|
||||
|
||||
### 3.2 页面生命周期:一次 ENTER → 一次 ESC
|
||||
@@ -249,6 +253,9 @@ typedef struct {
|
||||
uint16_t (*count_cb)(void); /* 动态行数(记录页) */
|
||||
void (*draw_cb)(lv_layer_t *layer, const lv_area_t *clip); /* 表单/操作页:自定义内容 */
|
||||
bool (*key_cb)(lv_key_t key); /* 页面自有键;true = 页已处理 */
|
||||
/* —— 自动失效轮询(auto_ms=0 = 只在按键重绘,未订阅页行为逐位不变;见 UI_数据供给设计说明.md §5.5)—— */
|
||||
uint16_t auto_ms; /* 轮询周期(ms);0 = 不启用自动刷新 */
|
||||
uint32_t (*auto_seq)(void); /* 新鲜度序号源;NULL = ui_data_rt_seq() */
|
||||
} content_page_def_t;
|
||||
```
|
||||
|
||||
@@ -273,6 +280,9 @@ typedef struct {
|
||||
| `rows` | `const cp_row_t *` | 静态数据表(Flash) | 6 行 | 24 行 | 3 行 | NULL |
|
||||
| `fixed_cur_page` / `fixed_total_pages` | `uint8_t` | 信息页页脚固定值 | 10 / 1 | — | — | — |
|
||||
| `refresh` / `count_cb` / `draw_cb` / `key_cb` | 函数指针 | 扩展钩子 | 全 NULL | 全 NULL | 全 NULL | draw_cb + key_cb(余 NULL) |
|
||||
| `auto_ms` / `auto_seq` | `uint16_t` / 函数指针 | 自动失效轮询周期 / 新鲜度源(`UI_数据供给设计说明.md` §5.5) | 1000 / `ui_data_info_seq` | 300 / NULL | 300 / NULL | 0 / NULL |
|
||||
|
||||
> 上表仅列这四页;未列出的实时页也已接入:`yx` 500 ms、`clock` 1000 ms、`runreport` 1000 ms(`ui_data_info_seq`)。六个实时页的取值账与"不迁移"清单见 `UI_数据供给设计说明.md` §5.5。
|
||||
|
||||
扩展钩子(机制已实现;密码页用 `draw_cb` + `key_cb`,其余 3 页未用):
|
||||
|
||||
@@ -283,6 +293,8 @@ typedef struct {
|
||||
| `draw_cb` | `void (lv_layer_t *layer, const lv_area_t *clip)` | 绘制链末尾(内置区块之后) | 表单/操作页自定义内容 |
|
||||
| `key_cb` | `bool (lv_key_t key)` | 键分发时(ESC 之后、内置 ↑↓ 之前) | 页面自有键(ENTER 确认等);返回 true = 页已处理 |
|
||||
|
||||
> 自动失效(`auto_ms`/`auto_seq`)**不是页面钩子**:定时器、轮询节拍与序号比较全在框架(`ui_content_page_auto_poll`),页面只声明周期与可选序号源;口径见 `UI_数据供给设计说明.md` §5.5。
|
||||
|
||||
### 4.1 表单/操作页首个应用:密码页(已落地)
|
||||
|
||||
实机(淮源电力 F30)已有"用户密钥"页:进入某些子叶面前须先输入密码(实机照片见
|
||||
@@ -423,8 +435,8 @@ extern const content_page_def_t ui_page_device_info_desc; /* 描述符(Flash
|
||||
|
||||
| 项 | 值 | 说明 |
|
||||
|---|---|---|
|
||||
| 框架常驻 RAM | ~20 B | `s_open/s_sel/s_scr/s_cont` 4 变量 |
|
||||
| 开页峰值 | +776 B | 屏 + 容器 + 事件列表 + 常量样式引用 |
|
||||
| 框架常驻 RAM | ~28 B | `s_open/s_sel/s_perm/s_scr/s_cont/s_auto_timer/s_auto_seq` 7 变量(后 2 个为自动失效) |
|
||||
| 开页峰值 | +776 B | 屏 + 容器 + 事件列表 + 常量样式引用;启用自动失效的页另占一只 lv_timer 对象(数十字节,关页归还池,2026-11-13 起) |
|
||||
| 关页回落 | 基线 +80 B 一次性残留 | screens 数组高水位 +8、`lv_screen_load` 菜单屏局部 X/Y 样式 +72(LVGL 核心行为,非泄漏;分解见 `菜单页面设计.md` §9.5) |
|
||||
| 池水位门禁 | 5368 B / 5448 B | 菜单基线 / 开页-关页往返(回归脚本常量,2026-10-11 随 LVGL 控件裁剪由 4784/4864 上移;**与池总大小无关**,16 KB 池下实测值不变) |
|
||||
| 描述符/行表/标题 | Flash const | 不进 LVGL 池;每页增量 = 描述符 ~40 B + 行表 + 代码 |
|
||||
@@ -492,4 +504,6 @@ extern const content_page_def_t ui_page_device_info_desc; /* 描述符(Flash
|
||||
| 2026-10-27 | 内容页黄金参考 **47 → 53 页**(通讯设置页落地并绑 `#page=comm`;判据 = `simulator/tests/ref/` 入库 `shot_*.bin` 实数 53 张,`git diff --name-status 31ca4a1f HEAD -- simulator/tests/ref` 实测只有 6 个 `A`、零 `M` ⇒ 存量 47 张逐字节零漂移);本文件的当前态计数已同步 |
|
||||
| 2026-10-26 | 验证与回归段的页数口径修正为 **27 页**黄金参考(`simulator/tests/ref/` 入库 27 个 `shot_*.bin`,含整定 9 态);上方变更记录里的「11 页」是当时事实不动 |
|
||||
| 2026-10-26 | 门禁常量修正为 5368 B / 5448 B(= 两个回归脚本头部现值);本文件正文表早已是 5368/5448,只这一处残留 2026-10-11 裁剪 LVGL 控件类之前的 4784/4864 |
|
||||
| 2026-11-13 | 五个实时页接入框架自动失效(task-3,用户裁定全接):`dc` 300 / `yx` 500 / `clock` 1000(rt 通道)与 `device_info` / `runreport` 1000 + `.auto_seq = ui_data_info_seq`(info 通道,`info` 与 `rt` 同拍 200 ms)描述符加 `.auto_ms`,取值账在各页注释;§三 字段表与 §六 内存表同步;实测黄金参考 74 张、往返水位 5448 B、菜单基线 5368 B 均不动(无头从不跑 `lv_timer_handler`) |
|
||||
| 2026-10-26 | 验证与回归段的黄金参考页数 **27 → 40 页**(UI 三页落地后 `simulator/tests/ref/` 实数 40 张);上方变更记录里的 11 / 27 是各当时下限,保留 |
|
||||
| 2026-11-13 | 框架级自动失效(task-2):描述符尾部新增 `auto_ms`/`auto_seq`(当前仅 ac 页启用 300 ms),新增框架 API `ui_content_page_auto_poll()`(§3.1);§3.1 静态块与代码同步(补 `s_perm` 与自动失效 2 变量);回归脚本新增 `--rtlive` 零按键落屏检查(74 张黄金参考逐字节不变,实测 PASS);机制详述在 `UI_数据供给设计说明.md` §5.5 |
|
||||
|
||||
@@ -626,7 +626,7 @@ SNTP 客户端 ──────→ 批4 SNTP设置
|
||||
| 4 | 无源点显示 `--` | 遥信量无效点、直流量直流通道 |
|
||||
| 5 | 定值整定闭环 | 改 → 保存 → 重启 → 值保持 + 校验和自检通过 + 主站 C_RD 一致。**2026-10-26 半收口**:模拟器侧「改 → 保存 → `OK` → 回首屏新值已在帧里」与「越界被拒、值不落盘」已由像素 + 文本断言覆盖(`sparam_save` / `sparam_landed` / `sparam_oob`);**重启保持与 C_RD 一致只能上板**(模拟器无 NAND)。另注意 40 项里 **13 项 `comm_addr = 0`**(投退、返回系数、过频/低频段、全局软压板)主站不可寻址,不能拿 C_RD 当它们的判据 |
|
||||
| 6 | 分层门禁无新增 | `check_layer_deps.py` 退出码 0,基线 6 → 3。**现状:3**(记录 reader 已削 3 条;写路径走注入式 ops 函数表,未新增 include,见 `docs/UI设计/UI_数据供给设计说明.md` §6.2) |
|
||||
| 7 | 像素回归稳定 | `ui-golden` 连续两次零差异。**已验(2026-10-26)**:27 页连跑两次逐字节一致——**该下限此后已扩至 66 张**(新增 clock / reset / clear 系列 13 张,其后 47→53→60→66,2026-10-29 录波 UI 道 +6 张),66 张上的双次逐字节一致**已验(2026-10-29:任务树重建模拟器后同一批 66 张连跑三次逐字节一致,页面池水位 5,448 B;菜单侧 `check_menu_rows.py` 8 状态连跑三次亦逐字节一致,5,368 B;三次之间未用更新模式写过任何参考图,故这是同口径的传递性证据)** |
|
||||
| 7 | 像素回归稳定 | `ui-golden` 连续两次零差异。**已验(2026-10-26)**:27 页连跑两次逐字节一致——**该下限此后已扩至 66 张**(新增 clock / reset / clear 系列 13 张,其后 47→53→60→66,2026-10-29 录波 UI 道 +6 张),66 张上的双次逐字节一致**已验(2026-10-29:任务树重建模拟器后同一批 66 张连跑三次逐字节一致,页面池水位 5,448 B;菜单侧 `check_menu_rows.py` 8 状态连跑三次亦逐字节一致,5,368 B;三次之间未用更新模式写过任何参考图,故这是同口径的传递性证据)**。集合其后扩至 74 张——2026-11-13 按同口径跑过 74 张零差异(task-3 五页接自动刷新,无头不跑 `lv_timer_handler`,往返水位 5,448 B / 菜单 5,368 B 均不动) |
|
||||
| 8 | 每个新页有 golden 参考 | `simulator/tests/ref/` 新增对应 `.bin`。**整定 4 页已有 9 张**(含保存/越界/落定/回绕各态);参考图入库,改它 = 改判据,须逐页过目 |
|
||||
|
||||
---
|
||||
|
||||
@@ -47,10 +47,10 @@ dhara 失败类 +84;RO 另 +208,全是该失败类名表)→ `ER_IROM1`
|
||||
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 |
|
||||
|---|---|---|---|
|
||||
| ER_IROM1 | 287,964 B (281.21 KB) | 1,048,576 B (1024 KB) | 27.5% |
|
||||
| RW_IRAM1 | 97,024 B (94.75 KB) | 131,072 B (128 KB) | 74.0% |
|
||||
| ER_IROM1 | 288,624 B (281.86 KB) | 1,048,576 B (1024 KB) | 27.5% |
|
||||
| RW_IRAM1 | 97,248 B (94.97 KB) | 131,072 B (128 KB) | 74.2% |
|
||||
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
|
||||
| **Flash (ROM 合计)** | **290,044 B (283.2 KB)** | 1,048,576 B (1024 KB) | **27.7%** |
|
||||
| **Flash (ROM 合计)** | **290,712 B (283.9 KB)** | 1,048,576 B (1024 KB) | **27.7%** |
|
||||
<!-- MEM_SNAPSHOT:END -->
|
||||
|
||||
各区的容量与常量来源:
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
|
||||
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|
||||
|---|---|---|---|---|
|
||||
| ER_IROM1 (Flash) | 287,964 B | 1,048,576 B | 27.5% | 参考 |
|
||||
| ER_IROM1 (Flash) | 288,624 B | 1,048,576 B | 27.5% | 参考 |
|
||||
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
|
||||
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
|
||||
| RW_IRAM1 | 97,024 B | 131,072 B | 74.0% | PASS |
|
||||
| RW_IRAM1 | 97,248 B | 131,072 B | 74.2% | PASS |
|
||||
|
||||
| 子系统(静态 RW+ZI;除 RTOS 堆在 CCM 外均在 SRAM1) | 现值 | 配额 | 状态 |
|
||||
|---|---|---|---|
|
||||
| 网络 | 13,958 B | 12 KB | ⚠ 超 1.6 KB |
|
||||
| 存储 | 9,144 B | 12 KB | OK |
|
||||
| UI | 36,511 B | 16 KB | ⚠ 超 19.7 KB |
|
||||
| UI | 36,731 B | 16 KB | ⚠ 超 19.9 KB |
|
||||
| RTOS 堆 | 57,376 B | 56 KB | 堆=配额 |
|
||||
| 应用 | 26,609 B | 8 KB | ⚠ 超 18.0 KB |
|
||||
| 其它 | 10,792 B | — | 参考 |
|
||||
|
||||
+17
-11
@@ -1,7 +1,7 @@
|
||||
# 运行/自检记录与待机显示设计说明
|
||||
|
||||
> **版本**:v1.3(§4.4 新增"卷锁 fail-open 边沿补投自检记录"一条,道G task-40;v1.2 为 §4.3 随七类记录更新,v1.1 的原始设计口径不变)
|
||||
> **日期**:2026-09-14(v1.2 更新 2026-10-26)
|
||||
> **版本**:v1.4(2026-11-13:§5.2 待机屏从 6 路占位电流改为 UA/UB/UC/IA/IB/IC 六路实时值,§1 R2 按新口径改写、不再引 mockup,§9 对应开放问题销项删行;v1.3 为 §4.4 新增"卷锁 fail-open 边沿补投自检记录"一条,道G task-40;v1.2 为 §4.3 随七类记录更新,v1.1 的原始设计口径不变)
|
||||
> **日期**:2026-09-14(v1.2 更新 2026-10-26,v1.4 更新 2026-11-13)
|
||||
> **状态**:步骤 1–9 全部完成——双端构建 0/0、`--logtest` PASS、ui-verify 五门禁全绿;真机 /serial-boot-test 待上电回归(§8.2)
|
||||
> **关联文档**:`项目架构总览.md` · `STM32F4-Base存储架构说明.md` · `系统墙钟时间维护说明.md` · `UI设计/内容页架构.md`(如存在)· `docs/UI设计/mockups/`(待机显示页面.jpg / 运行记录页.jpg / 自检记录页.jpg / 密码页面实际机器的示例图片.jpg)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
| # | 需求 | 说明 |
|
||||
|---|------|------|
|
||||
| R1 | 密码错误提示 | 用户密钥页输错密码时提示"密码输入错误",并允许重新输入(现状:仅清空重输、无提示) |
|
||||
| R2 | 待机显示页 | 长时间无操作时默认显示的界面(参考 `mockups/待机显示页面.jpg`:6 路电流值 + 底部时钟);**只有按确认键才进入菜单** |
|
||||
| R2 | 待机显示页 | 长时间无操作时默认显示的界面:UA/UB/UC/IA/IB/IC 六路电量的实时值 + 底部时钟;**只有按确认键才进入菜单** |
|
||||
| R3 | 运行记录页 | 记录装置上电时间、重启时间(参考 `mockups/运行记录页.jpg`:标题 + 逐条"时间戳 + 事件" + 页脚分页) |
|
||||
| R4 | 自检记录页 | 记录开机自检结果,有问题才记录(参考 `mockups/自检记录页.jpg`) |
|
||||
| R5 | FTP 可读存储 | 记录以**纯文本文件**落盘,FTP(根目录 = FatFS 卷根)可直接下载读取 |
|
||||
@@ -225,19 +225,26 @@ int syslog_get_record(syslog_id_t id, uint32_t idx, char *ts, size_t ts_len
|
||||
- 空闲检测 = `lv_timer`(1s 周期,MCU/模拟器均经 `lv_timer_handler` 驱动,无 OS 依赖);
|
||||
- 活动通知挂钩:`menu_key_cb`、`cp_key_cb` 每收到一个键调 `ui_standby_notify_activity()`(重置计时)。
|
||||
|
||||
**待机屏内容**(参考 mockup,全 ASCII 无需新字体字):
|
||||
**待机屏内容**(v1.1 起显示实时值,2026-11-13;全 ASCII 无需新字体字):
|
||||
|
||||
```
|
||||
Ib = 0.00A ← 6 行电流值(v1 占位 0.00A,与 AC/DC 页现状一致;
|
||||
Ic = 0.00A 待交流量标定管线落地后接 TPAFE5160 实时值)
|
||||
Io = 0.00A
|
||||
Ia2 = 0.00A
|
||||
Ib2 = 0.00A
|
||||
Ic2 = 0.00A
|
||||
UA = 0.341 ← 6 行实时值 = ui_data 通道一 yc[](下标 = adc_semantic_t 语义序:
|
||||
UB = 0.338 UA/UB/UC = 0..2,IA/IB/IC = 4..6;UN/IN 不在待机屏——用户点名
|
||||
UC = 0.339 六路,且 UN 本台架永久无数据,摆一排假通道不如不摆)
|
||||
IA = 0.021 ← 示例为现状 ENG_PIN 引脚电压档:数字照显、不挂单位(挂单位 =
|
||||
IB = 0.020 替用户完成没做的标定换算,同交流量页引脚档不造第二口径)
|
||||
IC = 0.021 ← 升二次/一次值档后单位随 eng:UA/UB/UC 挂 V、IA/IB/IC 挂 A
|
||||
────────────────────── ← 分隔线(y≈132)
|
||||
YYYY-MM-DD HH:MM:SS ← 底部实时时钟(每秒重绘,lv_timer 驱动 invalidate)
|
||||
```
|
||||
|
||||
- 空值/坏质口径与格式化的唯一真源 = 交流量页(`ui_page_ac.c`):无首帧或品质带
|
||||
`IV|NT|RAW|SB|BL|OV` → `--`;3 位小数定点格式化复用 `ui_w_editor_fmt_scaled`(不引 %f)。
|
||||
- 帧读取按 `ui_data_rt_seq()` 门控整帧拷(`ui_data.h` 头注);待机 1s 定时器整屏
|
||||
invalidate 即数值刷新节拍,未新增定时器。模拟器 `main.c` 自带固定仿真 rt 帧:待机截图
|
||||
(`--standby`)即双分支实证——UA/IA 显引脚档数字(0.812 / 0.046,不挂单位)、
|
||||
UB/UC/IB/IC 带 `Q_IV|Q_OV` 坏质显 `--`。
|
||||
|
||||
**键语义(用户定案)**:待机屏上**只有 ENTER(确认键)进入菜单**;其余按键忽略(同时重置空闲计时)。
|
||||
- 实现为**屏幕覆盖层**:进入时保存当前活动屏 + 组内焦点对象,加载待机屏并聚焦其容器;ENTER 时还原前屏与焦点。
|
||||
- 内容页打开期间超时 → 同样切待机(页面对象保留在底层,ENTER 返回后原样恢复,编辑态不丢)。
|
||||
@@ -324,7 +331,6 @@ Forbidden:`FAIL`/`ERROR`/`HardFault`。
|
||||
| O1 | 断电 <10min 的"上电/重启"歧义 | §5.3 阈值折中,文档声明;如需精确可后续加掉电标志(PVD) |
|
||||
| O2 | 故障"已复归"/持续监测(mockup 语义) | v2:周期自检 + 活动故障状态机 + 恢复记录 |
|
||||
| O3 | ~~"清除记录"菜单项~~ **已实施(2026-10-26 道F task-4)** | 数据面 = `syslog_request_clear(id)` + `syslog_clear_state(id)`,落地在 defaultTask(唯一写者)的 `syslog_drain()` 尾部;UI 侧页面归记录/维护页批次。细口径见 `App/log/README.md` |
|
||||
| O4 | 待机页电流值接实时数据 | 待交流量标定管线(AC 页占位数据替换)落地后接入 `tpafe5160_snapshot()`(原子快照;旧 `tpafe5160_get_buf()` 已于 2026-10-10 删除,见 `AGENTS.md`) |
|
||||
| — | FAT32 小追加簇开销 | §3 调研结论:写频率极低可接受;容量上限约束增长 |
|
||||
| — | UI 读与落盘并发 | 只读既有内容 + EOF 追加 + FatFS 系统锁,无互斥需求(§4.4) |
|
||||
|
||||
|
||||
@@ -21,4 +21,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
|
||||
2026-11-09 255160 31204 2080 152152 286364 27.3 96808 73.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;nand_ftl.o=8288;lftpd.o=7884;ui_net_sink.o=6802;ui_port.o=6432;wave_ring.o=5296;sb_link.o=4376;ch395f.o=4162;console_log.o=4132
|
||||
2026-11-10 255160 31204 2080 152152 286364 27.3 96808 73.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;nand_ftl.o=8288;lftpd.o=7884;ui_net_sink.o=6802;ui_port.o=6432;wave_ring.o=5296;sb_link.o=4376;ch395f.o=4162;console_log.o=4132
|
||||
2026-11-11 256024 31760 2080 152368 287784 27.4 97024 74.0 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;nand_ftl.o=8288;lftpd.o=7884;ui_net_sink.o=6802;ui_port.o=6432;wave_ring.o=5332;sb_link.o=4376;ch395f.o=4162;console_log.o=4132
|
||||
2026-11-13 256200 31764 2080 152368 287964 27.5 97024 74.0 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;nand_ftl.o=8288;lftpd.o=7884;ui_net_sink.o=6802;ui_port.o=6432;wave_ring.o=5332;sb_link.o=4376;ch395f.o=4162;console_log.o=4132
|
||||
2026-11-13 256588 32036 2088 152584 288624 27.5 97248 74.2 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;nand_ftl.o=8288;lftpd.o=7884;ui_net_sink.o=6802;ui_port.o=6432;wave_ring.o=5332;sb_link.o=4376;ch395f.o=4162;console_log.o=4132
|
||||
|
||||
|
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"built_at": "2026-11-13T11:25:00+08:00",
|
||||
"fingerprint": "39f4f69a673f97c33cc59b301e723ec8f1e03686f4556ac6abb1a5a6cd438796",
|
||||
"built_at": "2026-11-13T14:51:49+08:00",
|
||||
"fingerprint": "7bca71501bd2582cd221f9cfea5d4db5d196d3d40a583ff119dfd26841355379",
|
||||
"fingerprint_files": 1479,
|
||||
"fingerprint_source": "git-index",
|
||||
"gated_regions": [
|
||||
"RW_IRAM1",
|
||||
"RW_CCM"
|
||||
],
|
||||
"git_head": "7c505b2457d977adf7750abbb841b0453fd6bfb9",
|
||||
"git_head": "a5142c6d9d11b751d5ff1390bd815f5202bcdabb",
|
||||
"map": "MDK-ARM/STM32F407-Demo/STM32F407-Demo.map",
|
||||
"map_mtime": 1794540294,
|
||||
"map_mtime": 1794551787,
|
||||
"pct": {
|
||||
"ER_IROM1": 27.46,
|
||||
"ER_IROM1": 27.53,
|
||||
"RW_CCM": 87.89,
|
||||
"RW_IRAM1": 74.02,
|
||||
"RW_IRAM1": 74.19,
|
||||
"UNINIT_CCM": 18.75
|
||||
},
|
||||
"regions": {
|
||||
"ER_IROM1": {
|
||||
"max": 1048576,
|
||||
"size": 287964
|
||||
"size": 288624
|
||||
},
|
||||
"RW_CCM": {
|
||||
"max": 65280,
|
||||
@@ -27,7 +27,7 @@
|
||||
},
|
||||
"RW_IRAM1": {
|
||||
"max": 131072,
|
||||
"size": 97024
|
||||
"size": 97248
|
||||
},
|
||||
"UNINIT_CCM": {
|
||||
"max": 256,
|
||||
@@ -37,10 +37,10 @@
|
||||
"schema": 2,
|
||||
"scope_note": "\u540c\u7801\u51ed\u8bc1\uff1a\u6307\u7eb9\u8986\u76d6\u56fa\u4ef6\u4f53\u79ef\u76f8\u5173\u6e90\u7801\uff0c\u53e3\u5f84\u89c1\u672c\u811a\u672c docstring",
|
||||
"sizes": {
|
||||
"code": 256200,
|
||||
"ro_data": 31764,
|
||||
"rw_data": 2080,
|
||||
"zi_data": 152368
|
||||
"code": 256588,
|
||||
"ro_data": 32036,
|
||||
"rw_data": 2088,
|
||||
"zi_data": 152584
|
||||
},
|
||||
"threshold_pct": 92.0,
|
||||
"worktree_dirt_at_stamp": 0,
|
||||
|
||||
+200
-84
@@ -6,6 +6,8 @@
|
||||
* 创建日期:2026-08-31
|
||||
* 修改记录:
|
||||
* v1.0 2026-08-31 王建锋 创建:基于 lv_port_pc_vscode 适配
|
||||
* v1.1 2026-11-13 DTU 项目组 task-2:rt 帧装配收口成 sim_fill_rt_frame 唯一装配口 +
|
||||
* --rtlive 两态自检 + 窗口模式 200ms 节拍重发 live 帧
|
||||
*
|
||||
* @file main.c
|
||||
* @brief LVGL 模拟器入口 — 160x160 1bpp 单色菜单 UI
|
||||
@@ -26,6 +28,13 @@
|
||||
* seq 字符:u=↑ d=↓ l=← r=→ e=Enter s=Esc(其余字符忽略)
|
||||
* 每键经 lv_group_send_data 注入(与 keypad 输入设备同路径),驱动 5 帧后取快照
|
||||
*
|
||||
* 实时页自动刷新自检模式(无窗口,两态比对,退出码 0=两枪都出图):
|
||||
* lvgl_sim.exe --rtlive <seq> shotA.bin shotB.bin
|
||||
* 先注键开到目标页拍 A(冻结帧 n=0);随后发布一帧确定性斜坡帧(仿真设备还在
|
||||
* 采集,发布序号前进)并直调生产判定体 ui_content_page_auto_poll(无头无定时器
|
||||
* 节拍,直调顶替),再拍 B。A 不等于 B 逐字节成立 = 不按任何键,新数据照样刷屏;
|
||||
* 像素差异断言在 simulator/tests/check_content_pages.py 里做
|
||||
*
|
||||
* 配置轮询自检模式(无窗口、进程内闭环、退出码 0=PASS / 非0=FAIL):
|
||||
* lvgl_sim.exe --cfgtest
|
||||
* 验证链:net_cfg_load(只读 INI;旧 NCFG 档迁移已于 2026-10-10 删除)
|
||||
@@ -64,6 +73,7 @@
|
||||
#include "sim_port.h"
|
||||
#include "ui_menu.h"
|
||||
#include "ui_standby.h"
|
||||
#include "ui_content_page.h" /* task-2:--rtlive 直调框架自动刷新判定体 */
|
||||
#include "ui_net_sink.h"
|
||||
#include "net_cfg.h"
|
||||
#include "cfg_file.h"
|
||||
@@ -972,6 +982,123 @@ static void sim_publish_par_frame(void) {
|
||||
s_par_pub_sum = par.checksum;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:组装一帧 rt 快照——黄金冻结帧(n=0)与 live 斜坡帧(n>=1)共用这唯一
|
||||
* 装配口,仿真假数据不存在第二份
|
||||
* 入口参数:p_rt - 目标帧(调用方持有,本函数先整帧清零) ui_data_rt_t *
|
||||
* n - 帧号;0 = 黄金冻结帧(逐字节等于重构前,74 张黄金参考建立在
|
||||
* 它上面),>0 = 确定性斜坡帧(三角波 0..5..0 周期 11 帧,无随机,
|
||||
* 仅 --rtlive 自检与窗口模式节拍使用) uint32_t
|
||||
* 返回值:无
|
||||
* 限定条件:--noclock / --epoch 形态旗标已就位(时钟段读它们)
|
||||
* 函数说明:随 n 变的只有显示相关字段(相量/遥测/tick/墙钟秒),品质位与遥信位图
|
||||
* 原样冻结——模拟的是设备还在采集,不是设备自己变坏了
|
||||
*/
|
||||
static void sim_fill_rt_frame(ui_data_rt_t *p_rt, uint32_t n) {
|
||||
uint8_t i;
|
||||
|
||||
(void)memset(p_rt, 0, sizeof(*p_rt));
|
||||
p_rt->valid = 1u;
|
||||
p_rt->eng = UI_DATA_ENG_PIN;
|
||||
p_rt->tick_ms = 0u;
|
||||
/* 遥测下标 = adc_semantic_t 语义序(UA/UB/UC/UN/IA/IB/IC/IN/TEMP)。
|
||||
* 模拟器不 include adc_input.h,故这里用语义序注释对照,不写符号名 */
|
||||
p_rt->yc[0].val = 0.8123f; /* UA 基波(引脚电压域) */
|
||||
p_rt->yc[0].qds = 0u;
|
||||
p_rt->yc[1].val = 0.0f; /* UB:V0.1 硬件削顶无采样源 */
|
||||
p_rt->yc[1].qds = UI_DATA_Q_IV | UI_DATA_Q_OV;
|
||||
p_rt->yc[2].qds = UI_DATA_Q_IV | UI_DATA_Q_OV; /* UC 同上 */
|
||||
p_rt->yc[3].val = 0.0054f; /* UN 零序电压基波 */
|
||||
p_rt->yc[3].qds = 0u;
|
||||
p_rt->yc[4].val = 0.0456f; /* IA 基波 */
|
||||
p_rt->yc[4].qds = 0u;
|
||||
p_rt->yc[5].qds = UI_DATA_Q_IV | UI_DATA_Q_OV; /* IB */
|
||||
p_rt->yc[6].qds = UI_DATA_Q_IV | UI_DATA_Q_OV; /* IC */
|
||||
p_rt->yc[7].qds = UI_DATA_Q_IV | UI_DATA_Q_OV; /* IN */
|
||||
p_rt->yc[8].val = 2048.0f; /* TEMP:码值(不参与线性标定) */
|
||||
p_rt->yc[8].qds = UI_DATA_Q_RAW;
|
||||
/* 相量实例槽(序 = phasor k_inst[]:IA-F1/UA-F1/UN-F1/IA-F2/IA-F5) */
|
||||
p_rt->ph_n = 5u;
|
||||
p_rt->ph[0].val = 0.0456f;
|
||||
p_rt->ph[1].val = 0.8123f;
|
||||
p_rt->ph[2].val = 0.0054f;
|
||||
p_rt->ph[3].val = 0.0017f; /* IA 二次谐波(涌流制动监视量) */
|
||||
p_rt->ph[4].val = 0.0008f; /* IA 五次谐波(选线用) */
|
||||
for (i = 0u; (i < UI_DATA_PH_MAX) && (i < p_rt->ph_n); i++) {
|
||||
p_rt->ph[i].qds = 0u;
|
||||
}
|
||||
for (; i < UI_DATA_PH_MAX; i++) {
|
||||
p_rt->ph[i].qds = UI_DATA_Q_IV; /* 未发布的槽:无效,页面显示 "--" */
|
||||
}
|
||||
p_rt->yx_bits = 0u; /* 固定帧无动作态 */
|
||||
p_rt->yx_valid = 0x4043C000ULL; /* YX15~18/23/31 有效位图(与点表同集合) */
|
||||
/* 双点位图照写侧口径给(App/protocol/iec104/iec104_pts.c 的 YX_DP_N = 4 → bit0~3)。
|
||||
* 这 4 点**不在**上面的有效位图里(V0.1 无开入硬件),所以遥信页对它们打 "--"——
|
||||
* 刻意不把 bit0 塞进 yx_valid 让画面多两个汉字好看:那会让黄金参考显示成"这台设备
|
||||
* 有开关位置遥信",而真机上没有(屏上的话必须与本机一致,同页脚页码那条裁定) */
|
||||
p_rt->yx_dp = 0x0FULL;
|
||||
p_rt->freq_valid = 0u; /* eng=0 期间幅值门恒失效 → 无新鲜读数 */
|
||||
p_rt->freq_hz = 0.0f;
|
||||
/* 墙钟两态(道F task-3):--noclock 把这台机器装成"RTC 异常/未对时"——只有
|
||||
* 这样才照得到时钟设置页的 "--" + "无时钟"徽标形态;面板设过时以面板值为准,
|
||||
* 这就是页面上"回读证据"的唯一来源(真机那边是 SD2506 回读 + 快照层重发) */
|
||||
p_rt->clock_ok = (s_noclock == 0) ? 1u : 0u;
|
||||
if (s_noclock != 0) {
|
||||
p_rt->clock[0] = 0; /* 无效时标 = 空串(页面据此显 "--",绝不显 1970) */
|
||||
} else if (s_sim_epoch != 0u) {
|
||||
ui_data_clock_t ck;
|
||||
|
||||
if (ui_data_clock_from_epoch(s_sim_epoch, &ck) == 0) {
|
||||
(void)ui_data_clock_format(&ck, p_rt->clock, sizeof(p_rt->clock));
|
||||
} else {
|
||||
p_rt->clock[0] = 0;
|
||||
p_rt->clock_ok = 0u;
|
||||
}
|
||||
} else {
|
||||
(void)snprintf(p_rt->clock, sizeof(p_rt->clock), "%s", "2026-10-25 08:00:00");
|
||||
}
|
||||
|
||||
/* live 斜坡(n=0 完全不进本段:黄金像素逐位不变)。三角波避免单调漂移——窗口
|
||||
* 模式跑多久数值域都有界;相量实例与对应遥测写同值(镜像 App/app/ui_data_feed.c
|
||||
* 的同源纪律)。freq 保持 freq_valid=0:eng=0 期间幅值门恒失效,不为了让 Hz 行
|
||||
* 看着在动就伪造新鲜读数 */
|
||||
if (n != 0u) {
|
||||
uint32_t k = n % 11u;
|
||||
uint32_t s = (k <= 5u) ? k : (10u - k);
|
||||
float f = (float)s;
|
||||
|
||||
p_rt->tick_ms = n * 200u;
|
||||
p_rt->yc[0].val = 0.8123f + (0.0020f * f);
|
||||
p_rt->yc[3].val = 0.0054f + (0.0002f * f);
|
||||
p_rt->yc[4].val = 0.0456f + (0.0004f * f);
|
||||
p_rt->ph[0].val = p_rt->yc[4].val;
|
||||
p_rt->ph[1].val = p_rt->yc[0].val;
|
||||
p_rt->ph[2].val = p_rt->yc[3].val;
|
||||
p_rt->ph[3].val = 0.0017f + (0.0001f * f);
|
||||
p_rt->ph[4].val = 0.0008f + (0.0001f * f);
|
||||
if ((p_rt->clock_ok != 0u) && (p_rt->clock[0] != (char)0) && (s_sim_epoch == 0u)) {
|
||||
(void)snprintf(p_rt->clock, sizeof(p_rt->clock), "2026-10-25 08:00:%02u",
|
||||
(unsigned int)(n % 60u));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:发布一帧 live 斜坡快照(--rtlive 自检与窗口模式节拍共用;真机对应
|
||||
* App/app/ui_data_feed.c 的 200ms 发布拍)
|
||||
* 入口参数:n - 帧号(>=1;0 是冻结帧,只由 sim_publish_data_frames 开局发一次) uint32_t
|
||||
* 返回值:无
|
||||
* 限定条件:ui_data_init() 之后
|
||||
* 函数说明:ui_data_publish_rt 自带发布序号前进——这正是内容页框架自动失效的触发
|
||||
* 源(序号变过才整页失效,见 ui_content_page_auto_poll)
|
||||
*/
|
||||
static void sim_publish_rt_live(uint32_t n) {
|
||||
ui_data_rt_t rt;
|
||||
|
||||
sim_fill_rt_frame(&rt, n);
|
||||
(void)ui_data_publish_rt(&rt);
|
||||
}
|
||||
|
||||
static void sim_publish_data_frames(void) {
|
||||
ui_data_rt_t rt;
|
||||
ui_data_info_t info;
|
||||
@@ -979,68 +1106,10 @@ static void sim_publish_data_frames(void) {
|
||||
|
||||
ui_data_init();
|
||||
|
||||
(void)memset(&rt, 0, sizeof(rt));
|
||||
rt.valid = 1u;
|
||||
rt.eng = UI_DATA_ENG_PIN;
|
||||
rt.tick_ms = 0u;
|
||||
/* 遥测下标 = adc_semantic_t 语义序(UA/UB/UC/UN/IA/IB/IC/IN/TEMP)。
|
||||
* 模拟器不 include adc_input.h,故这里用语义序注释对照,不写符号名 */
|
||||
rt.yc[0].val = 0.8123f; /* UA 基波(引脚电压域) */
|
||||
rt.yc[0].qds = 0u;
|
||||
rt.yc[1].val = 0.0f; /* UB:V0.1 硬件削顶无采样源 */
|
||||
rt.yc[1].qds = UI_DATA_Q_IV | UI_DATA_Q_OV;
|
||||
rt.yc[2].qds = UI_DATA_Q_IV | UI_DATA_Q_OV; /* UC 同上 */
|
||||
rt.yc[3].val = 0.0054f; /* UN 零序电压基波 */
|
||||
rt.yc[3].qds = 0u;
|
||||
rt.yc[4].val = 0.0456f; /* IA 基波 */
|
||||
rt.yc[4].qds = 0u;
|
||||
rt.yc[5].qds = UI_DATA_Q_IV | UI_DATA_Q_OV; /* IB */
|
||||
rt.yc[6].qds = UI_DATA_Q_IV | UI_DATA_Q_OV; /* IC */
|
||||
rt.yc[7].qds = UI_DATA_Q_IV | UI_DATA_Q_OV; /* IN */
|
||||
rt.yc[8].val = 2048.0f; /* TEMP:码值(不参与线性标定) */
|
||||
rt.yc[8].qds = UI_DATA_Q_RAW;
|
||||
/* 相量实例槽(序 = phasor k_inst[]:IA-F1/UA-F1/UN-F1/IA-F2/IA-F5) */
|
||||
rt.ph_n = 5u;
|
||||
rt.ph[0].val = 0.0456f;
|
||||
rt.ph[1].val = 0.8123f;
|
||||
rt.ph[2].val = 0.0054f;
|
||||
rt.ph[3].val = 0.0017f; /* IA 二次谐波(涌流制动监视量) */
|
||||
rt.ph[4].val = 0.0008f; /* IA 五次谐波(选线用) */
|
||||
for (i = 0u; (i < UI_DATA_PH_MAX) && (i < rt.ph_n); i++) {
|
||||
rt.ph[i].qds = 0u;
|
||||
}
|
||||
for (; i < UI_DATA_PH_MAX; i++) {
|
||||
rt.ph[i].qds = UI_DATA_Q_IV; /* 未发布的槽:无效,页面显示 "--" */
|
||||
}
|
||||
rt.yx_bits = 0u; /* 固定帧无动作态 */
|
||||
rt.yx_valid = 0x4043C000ULL; /* YX15~18/23/31 有效位图(与点表同集合) */
|
||||
/* 双点位图照写侧口径给(App/protocol/iec104/iec104_pts.c 的 YX_DP_N = 4 → bit0~3)。
|
||||
* 这 4 点**不在**上面的有效位图里(V0.1 无开入硬件),所以遥信页对它们打 "--"——
|
||||
* 刻意不把 bit0 塞进 yx_valid 让画面多两个汉字好看:那会让黄金参考显示成"这台设备
|
||||
* 有开关位置遥信",而真机上没有(屏上的话必须与本机一致,同页脚页码那条裁定) */
|
||||
rt.yx_dp = 0x0FULL;
|
||||
rt.freq_valid = 0u; /* eng=0 期间幅值门恒失效 → 无新鲜读数 */
|
||||
rt.freq_hz = 0.0f;
|
||||
/* 墙钟两态(道F task-3):--noclock 把这台机器装成"RTC 异常/未对时"——只有
|
||||
* 这样才照得到时钟设置页的 "--" + "无时钟"徽标形态;面板设过时以面板值为准,
|
||||
* 这就是页面上"回读证据"的唯一来源(真机那边是 SD2506 回读 + 快照层重发) */
|
||||
rt.clock_ok = (s_noclock == 0) ? 1u : 0u;
|
||||
if (s_noclock != 0) {
|
||||
rt.clock[0] = 0; /* 无效时标 = 空串(页面据此显 "--",绝不显 1970) */
|
||||
} else if (s_sim_epoch != 0u) {
|
||||
ui_data_clock_t ck;
|
||||
|
||||
if (ui_data_clock_from_epoch(s_sim_epoch, &ck) == 0) {
|
||||
(void)ui_data_clock_format(&ck, rt.clock, sizeof(rt.clock));
|
||||
} else {
|
||||
rt.clock[0] = 0;
|
||||
rt.clock_ok = 0u;
|
||||
}
|
||||
} else {
|
||||
(void)snprintf(rt.clock, sizeof(rt.clock), "%s", "2026-10-25 08:00:00");
|
||||
}
|
||||
sim_fill_rt_frame(&rt, 0u); /* 冻结帧:黄金基准,装配与斜坡逻辑见 sim_fill_rt_frame */
|
||||
(void)ui_data_publish_rt(&rt);
|
||||
|
||||
|
||||
/* 定值帧:出厂默认 + 封存(幂等),再按 prot_set 现状发一帧(见上面两个函数) */
|
||||
(void)prot_set_init();
|
||||
sim_publish_par_frame();
|
||||
@@ -1153,14 +1222,49 @@ static void sim_param_pump(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把当前屏快照(I1)写成像素文件(run_headless 首枪与 --rtlive 第二枪共用)
|
||||
* 入口参数:path - 输出文件路径(写入 160x160 I1 原始像素) const char *
|
||||
* 返回值:0 - 成功;1 - 快照失败或文件打不开 int
|
||||
* 限定条件:离屏显示与目标屏已就绪(无头上下文内调用)
|
||||
* 函数说明:I1 缓冲区布局 [palette 8B][pixel data],跳调色板只写像素;快照即整屏
|
||||
* 同步重渲,不依赖 lv_timer_handler(无头禁驱动帧循环的纪律不变)
|
||||
*/
|
||||
static int snap_to_file(const char *path) {
|
||||
lv_draw_buf_t *snap = lv_snapshot_take(lv_screen_active(), LV_COLOR_FORMAT_I1);
|
||||
if (snap == NULL) {
|
||||
fprintf(stderr, "[shot] snapshot failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t snap_w = snap->header.w;
|
||||
uint32_t snap_h = snap->header.h;
|
||||
uint32_t pal_bytes = LV_COLOR_INDEXED_PALETTE_SIZE(LV_COLOR_FORMAT_I1) * (uint32_t)sizeof(lv_color32_t);
|
||||
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) {
|
||||
lv_draw_buf_destroy(snap);
|
||||
fprintf(stderr, "fopen failed: %s\n", path);
|
||||
return 1;
|
||||
}
|
||||
size_t wn = fwrite(px, 1, px_bytes, fp);
|
||||
fclose(fp);
|
||||
lv_draw_buf_destroy(snap);
|
||||
printf("screenshot: %s (%zu bytes, %ux%u I1)\n", path, wn, snap_w, snap_h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:无窗口离屏渲染(可注入键序列)并导出末帧像素
|
||||
* 入口参数:keys - 键序列(u=上 d=下 l=左 r=右 e=确认 s=返回;空 = 不注键) const char *
|
||||
* path - 输出文件(写入 160x160 I1 原始像素) const char *
|
||||
* standby - 非 0 = 注键后强制进入待机屏再截图(--standby 模式) int
|
||||
* path_b - 非 NULL = --rtlive 模式:A 枪出图后发布一帧 live 斜坡帧并直调
|
||||
* 框架自动刷新判定体,再拍 B 写到本路径 const char *
|
||||
* 返回值: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, const char * path_b) {
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
lv_init();
|
||||
lv_delay_set_cb(shot_delay_cb);
|
||||
@@ -1256,28 +1360,24 @@ static int run_headless(const char * keys, const char * path, int standby) {
|
||||
(unsigned)(mon.total_size - mon.free_size), (unsigned)mon.free_size);
|
||||
|
||||
/* 用 snapshot 把整屏渲进独立 I1 draw_buf(避开 I1+FULL 的渲染死循环) */
|
||||
lv_draw_buf_t *snap = lv_snapshot_take(lv_screen_active(), LV_COLOR_FORMAT_I1);
|
||||
if (snap == NULL) {
|
||||
fprintf(stderr, "[shot] snapshot failed\n");
|
||||
return 1;
|
||||
int rc = snap_to_file(path);
|
||||
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint32_t snap_w = snap->header.w;
|
||||
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);
|
||||
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) {
|
||||
lv_draw_buf_destroy(snap);
|
||||
fprintf(stderr, "fopen failed: %s\n", path);
|
||||
return 1;
|
||||
/* --rtlive 第二枪:发布前进过序号的 live 斜坡帧后,无头没有定时器节拍,
|
||||
* 直调生产判定体 ui_content_page_auto_poll 顶替——它就是板端定时器回调里
|
||||
* 跑的同一份代码;"数据变过才失效"的判定与真机逐句相同 */
|
||||
if (path_b != NULL) {
|
||||
sim_publish_rt_live(1u);
|
||||
ui_content_page_auto_poll();
|
||||
rc = snap_to_file(path_b);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
printf("rtlive: frames written (A=%s B=%s)\n", path, path_b);
|
||||
}
|
||||
size_t n = fwrite(px, 1, px_bytes, fp);
|
||||
fclose(fp);
|
||||
lv_draw_buf_destroy(snap);
|
||||
printf("screenshot: %s (%zu bytes, %ux%u I1)\n", path, n, snap_w, snap_h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1624,7 +1724,7 @@ static int run_logtest(void) {
|
||||
* 函数功能:模拟器入口(解析模式开关 → 窗口模式或无头自检)
|
||||
* 入口参数:argc - 参数个数 int
|
||||
* argv - 参数数组(--screenshot / --keys / --standby / --cfgtest /
|
||||
* --logtest / --idle / --noclock) char **
|
||||
* --logtest / --idle / --noclock / --rtlive) char **
|
||||
* 返回值:0 - 正常退出或自检 PASS;非 0 - 失败 int
|
||||
*/
|
||||
int main(int argc, char **argv) {
|
||||
@@ -1652,13 +1752,16 @@ 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) {
|
||||
return run_headless("", argv[i + 1], 0);
|
||||
return run_headless("", argv[i + 1], 0, NULL);
|
||||
}
|
||||
if (strcmp(argv[i], "--keys") == 0 && (i + 2) < argc) {
|
||||
return run_headless(argv[i + 1], argv[i + 2], 0);
|
||||
return run_headless(argv[i + 1], argv[i + 2], 0, NULL);
|
||||
}
|
||||
if (strcmp(argv[i], "--standby") == 0 && (i + 1) < argc) {
|
||||
return run_headless("", argv[i + 1], 1);
|
||||
return run_headless("", argv[i + 1], 1, NULL);
|
||||
}
|
||||
if (strcmp(argv[i], "--rtlive") == 0 && (i + 3) < argc) {
|
||||
return run_headless(argv[i + 1], argv[i + 2], 0, argv[i + 3]);
|
||||
}
|
||||
if (strcmp(argv[i], "--cfgtest") == 0) {
|
||||
return run_cfgtest();
|
||||
@@ -1759,6 +1862,11 @@ int main(int argc, char **argv) {
|
||||
/* 配置文件轮询起点(2s 周期,与 MCU netTask 一致) */
|
||||
uint32_t cfg_last_poll = (uint32_t)SDL_GetTicks();
|
||||
|
||||
/* rt live 节拍(窗口模式实时演示):镜像板端 ui_data_feed 的 200ms 发布拍;
|
||||
* 无头回归模式根本不走本循环——黄金参考永远建立在冻结帧 n=0 上 */
|
||||
uint32_t rt_last_pub = cfg_last_poll;
|
||||
uint32_t rt_live_n = 0u;
|
||||
|
||||
/* 主循环:驱动 LVGL 定时器与刷新
|
||||
* 注意:SDL 事件(键盘/窗口/退出)由 LVGL SDL 驱动内部 5ms 定时器
|
||||
* (lv_sdl_window.c 的 sdl_event_handler)统一轮询分发,
|
||||
@@ -1791,6 +1899,14 @@ int main(int argc, char **argv) {
|
||||
cfg_file_poll();
|
||||
}
|
||||
|
||||
/* rt 帧按拍重发(窗口实时演示,板端 feed 同款 200ms 拍):确定性斜坡帧、
|
||||
* 无随机;发布序号前进后,实时页由内容页框架自动失效定时器刷屏——
|
||||
* 打开交流量页不按键即可看到数值自己走 */
|
||||
if ((uint32_t)(now_ms - rt_last_pub) >= 200u) {
|
||||
rt_last_pub = now_ms;
|
||||
sim_publish_rt_live(++rt_live_n);
|
||||
}
|
||||
|
||||
/* 驱动 LVGL 定时器与刷新 */
|
||||
uint32_t sleep_ms = lv_timer_handler();
|
||||
if (sleep_ms == LV_NO_TIMER_READY) {
|
||||
|
||||
@@ -17,6 +17,10 @@ check_content_pages.py — 叶子内容页像素黄金参考回归 + 文本级
|
||||
另做开页-关页往返池水位门禁:`es`(info)与 `ddeeesll`(密码页,ESC 后 ←← 弹出路上压的
|
||||
2 个菜单框回根框,完全还原初始态)后 used 须等于 POOL_AFTER_ROUNDTRIP。
|
||||
|
||||
另跑 --rtlive 两态比对,实证「不按任何键,新数据照样刷屏」(task-2 框架自动失效链:
|
||||
开页拍 A → 仿真发布一帧斜坡帧并直调 ui_content_page_auto_poll → 拍 B,A≠B 才 PASS;
|
||||
黄金参考本身仍建立在冻结帧上,与本检查互不影响)。
|
||||
|
||||
黄金参考更新流程(有意改视觉时):先肉眼看图确认新效果正确,再 --update 刷新,
|
||||
刷新即改基准 → `git diff --stat simulator/tests/ref` 必须逐页过目并在提交正文说明;
|
||||
最后 regen_ref_png.py 重生成目检 PNG;误报排查见 docs/UI设计/UI_自动化测试流程.md。
|
||||
@@ -1220,13 +1224,36 @@ def main():
|
||||
else:
|
||||
print("[rt:%s] PASS 开页-关页往返池水位 %d B" % (rt_name, used))
|
||||
|
||||
# --rtlive 两态比对(task-2):开交流量页拍 A,发布 live 斜坡帧 + 框架判定体再拍 B。
|
||||
# A≠B = 「数据在变、页面没被按键过,屏上内容照样更新」成立;黄金参考不受影响。
|
||||
rtA = os.path.join(tmp, "rtlive_a.bin")
|
||||
rtB = os.path.join(tmp, "rtlive_b.bin")
|
||||
proc = subprocess.run([str(EXE), "--rtlive", "dee", rtA, rtB],
|
||||
cwd=tmp, capture_output=True, text=True)
|
||||
if proc.returncode != 0:
|
||||
ok = False
|
||||
print("[rtlive] FAIL 退出码 %d stderr=%s" % (proc.returncode, proc.stderr.strip()))
|
||||
elif "rtlive: frames written" not in proc.stdout:
|
||||
ok = False
|
||||
print("[rtlive] FAIL 输出缺标记行:\n%s" % proc.stdout)
|
||||
else:
|
||||
a = Path(rtA).read_bytes()
|
||||
b = Path(rtB).read_bytes()
|
||||
if len(a) != len(b) or a == b:
|
||||
ok = False
|
||||
print("[rtlive] FAIL A/B 异常(lenA=%d lenB=%d equal=%s)——自动失效链断了或斜坡帧没动显示值"
|
||||
% (len(a), len(b), a == b))
|
||||
else:
|
||||
diff = sum(1 for x, y in zip(a, b) if x != y)
|
||||
print("[rtlive] PASS 未按键实时数据落屏(A≠B,字节差异 %d)" % diff)
|
||||
|
||||
if update:
|
||||
print("PASS: 黄金参考已刷新(%d 页)" % len(PAGES))
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
return 0
|
||||
|
||||
if ok:
|
||||
print("PASS: %d 内容页 + 往返池水位全部通过" % len(PAGES))
|
||||
print("PASS: %d 内容页 + 往返池水位 + rtlive 全部通过" % len(PAGES))
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
return 0
|
||||
# 失败时保留临时截图便于排查
|
||||
|
||||
Reference in New Issue
Block a user