feat(UI): 遥测死区与系数 2 页——注入表第四张、三态用脏位、WAIT 与 OK 各出一张黄金

这两页是「UI补齐」里唯一带**持久化语义**的一档,所以整笔的设计重心不在画面上,而在**页面如何诚实表达
「已受理」与「已落盘」**——这一点由 setpage-scout 在开工前的只读查实钉下来:cfg_file_save() 全仓唯一调用
者是 run_par_store_save(),而 default_task.c:246 **每轮无条件**调它、函数首行 s_dirty==0 就直接返回
⇒
落盘动力本来就在 defaultTask,不在 104 会话、也不在事件位。因此本笔走 A2(UI 不当授权写者),不需要
新事件位、不需要新同步口。

页面三态判据 = run_par_dirty_peek():ENTER / WAIT(脏位仍在 = 已受理未落盘)/ OK(脏位归零 = 整档已外科
保存成功)/ 写被拒 = ERR。两条限制写在页面注释里没藏:脏位是**整档全局**的(一趟落 [run_par] 9 键 +
[adc_cal] 两段,归零只证明「这一档落盘了」,不证明「我这一项单独落盘」);他人随后再改会重新置脏 ⇒ 沿用
clear/clock 页的结果锁存,把「我这次动作的结果」与「档位此刻的状态」分开。

**为什么黄金必须出两张**(这条是本轮最有价值的防假证据规则):模拟器假件**保留脏位**、全仓唯一的
s_yt_dirty=0 写点是 sim_yt_pump()(setter 只置不清),于是 WAIT 与 OK 各出一帧——两组同键序、唯一差别是
EXTRA 里的 --yt-nopump。若假件同步清脏位,真机的 WAIT 态永远不会被像素回归覆盖,黄金就成了「只钉住最
好看那一帧」。TC-UIDATA-028o 另钉住 ui_data 层零缓存。

形状:新增 ui_data 第四张注入表(八字段整表校验、拒残表),实现落在装配层 App/app/ui_data_feed.c,
页面零 App include(分层方向 + 模拟器不链 run_par 两条老理由都成立);共用视图模型 ui_w_ytview 承载两页。
合法域一律由执行侧出口给(run_par_db_range / RUN_PAR_K_MAX_UV),页面不抄第二真源——系数页 v1 只用粗守
并在注释写明两层守卫互不掩盖,µV/码 ×[0.5,2.0] 那条真带的出口器归 task-43,**没有**在页面里抄常量。

取舍与遗留(如实):系数行 d=0 整 µV(1e9 装不下两位小数,注释写了「要小数改哪、不收紧假上限」);b 覆盖
无口未做;徽标只画系数页;行名因「率」缺字改为 Hz死区(font-charset 机器先抓到的,不是我目测判的)。

实测:@build 0 错误 0 警告(Code=233,900 / RW-data=1,536);ui_data 单测 277/277、run_par
67/67 零回归;
内容页黄金 47 页全过、**存量 40 张逐字节零漂移**(git 里只有 7 张新增);menu_rows / 池水位 / font-charset
/
bom / mojibake / nested-comment / cppcheck / coding-standard / layer-deps /
bsp-err 全 0 新增;编号五项式
refs 8,778 / dec 7,622 / und **1,156**(上限 1,166)/ 比值 86.83%(基线 86.0% 之上)/ 错误差值
0,注释零裸形
字母+数字。generated-fresh 与 registration-check 本地 PASS;新增 6 只源文件按规矩**双端注册同笔**(叶子
+
registry 19→21 + uvprojx + CMakeLists),uvprojx 这次属「真的增删源文件」才入库。

移交三条不混进本笔:① 全量 --selftest 现 exit 1 是 task-41 硬闸在模拟器上也拒 reset/clear(既有红,快检档
不受影响),归台架道定夺;② 内容页 40→47 的全仓文档计数改写由 Lead 派;③ run_par_deadband_set 的限定
条件语措辞(「iec104Task 上下文」已被装配层裁定覆盖)归算法层下一笔。
This commit is contained in:
2026-10-27 05:07:18 +08:00
parent 1d39571114
commit 48a8ef0564
27 changed files with 1682 additions and 11 deletions
+3
View File
@@ -105,6 +105,7 @@ set(UI_SOURCES
${UI_DIR}/ui_data.c # 数据快照存侧(道E task-14;写侧 App/app/ui_data_feed.c 不进模拟器,见 scripts/check_registration.py)
${UI_DIR}/ui_w_soe.c # SOE 记录组件(道E task-14;reader 已在批 2 两端绑定)
${UI_DIR}/ui_w_setview.c # 定值查看视图模型(道E task-15 批 3:四张视图表 + Q10 换算 + 越界反显)
${UI_DIR}/ui_w_ytview.c # 遥测整定视图模型(道F task-7:死区/系数两页共用注入表视图 + 落盘确认状态机)
${UI_DIR}/ui_page_soe.c
${UI_DIR}/ui_page_view_param.c
${UI_DIR}/ui_page_view_plate.c
@@ -114,6 +115,8 @@ set(UI_SOURCES
${UI_DIR}/ui_page_clear.c
${UI_DIR}/ui_page_clock.c
${UI_DIR}/ui_page_reset.c
${UI_DIR}/ui_page_calib.c
${UI_DIR}/ui_page_deadband.c
)
# 运行/自检记录引擎(双端共享核心 + 平台存储后端;MCU 端编 sys_log_mcu.c,见 Keil 工程)
+148
View File
@@ -267,6 +267,143 @@ static const ui_data_maint_ops_t s_maint_ops = {
sim_maint_clear_req,
sim_maint_clear_state
};
/*
* ======================= 道F task-7:遥测整定注入表假件(镜像 ui_data_feed.c 形状)
*
* 初值/合法域 = App/algo/run_par.c 工厂 ROM 与 RUN_PAR_DB_MAX_* 档位的镜像
* (模拟器不链 run_par,真源抄不来只能镜像;变更纪律同 s_sim_log_map 那条:
* 执行侧改档不改这里,屏上会呈现"档位与量纲自相矛盾"的肉眼可见错,黄金像素兜底)。
* ⚠ 脏位口径是这一家的存在理由:**写成功只置脏、绝不同步清**,清脏的唯一去处是
* sim_yt_pump()(镜像 defaultTask 每轮无条件 run_par_store_save())。同步清 =
* WAIT 态在模拟器里永不出现,"落盘确认"三态就只剩最好看那一帧被黄金钉住
* (Lead 裁定④原文的理由,别顺手"优化"成同步)。
*/
static float s_yt_db[UI_DATA_YT_DB_N] = {
0.5f, 0.5f, 0.5f, 0.5f, /* 电压死区 0.5 V(工厂额定档) */
0.005f, 0.005f, 0.005f, 0.005f, /* 电流死区 0.005 A */
0.5f /* 频率死区 0.5 Hz(绝对档) */
};
static float s_yt_k[UI_DATA_YT_CH_N] = {
152.587890625f, 152.587890625f, 152.587890625f, 152.587890625f,
152.587890625f, 152.587890625f, 152.587890625f, 152.587890625f
}; /* = adc_cal ROM 的 TPAFE 系数(µV/码);显示小数位 0 的量化后果见真机侧装配注 */
static uint8_t s_yt_dirty; /* 镜像 run_par 的整档脏位(页面上 WAIT 的唯一来源) */
static int s_yt_nopump; /* --yt-nopump:拍 WAIT 态黄金专用(截图前不清脏) */
/* 死区粗守上限(工程量):镜像 RUN_PAR_DB_MAX_U/I/F 三档;分档与 feed 的 unit
* 分档必须同序(同一条对表纪律) */
/* 函数功能:假死区粗守上限(工程量档,镜像 RUN_PAR_DB_MAX_U/I/F) 返回值:上限 float */
static float sim_yt_db_hi(uint8_t a_idx) {
if (a_idx < 4u) {
return 50.0f;
}
if (a_idx < (UI_DATA_YT_DB_N - 1u)) {
return 1.0f;
}
if (a_idx == (UI_DATA_YT_DB_N - 1u)) {
return 5.0f;
}
return 0.0f;
}
/* 函数功能:假死区点数(镜像 RUN_PAR_DB_N) 返回值:点数 uint8_t */
static uint8_t sim_yt_db_n(void) {
return (uint8_t)UI_DATA_YT_DB_N;
}
/* 函数功能:假死区行现状(值现取自家数组、档位分档与 feed 同序) 返回值:0 / -1 int */
static int sim_yt_db_row(uint8_t a_idx, ui_data_yt_row_t *a_out) {
if ((a_out == NULL) || (a_idx >= UI_DATA_YT_DB_N)) {
return -1;
}
a_out->val = s_yt_db[a_idx];
a_out->lo = 0.0f;
a_out->hi = sim_yt_db_hi(a_idx);
a_out->decimals = 3u;
a_out->unit = (a_idx < 4u) ? UI_DATA_YT_UNIT_V
: ((a_idx < (UI_DATA_YT_DB_N - 1u)) ? UI_DATA_YT_UNIT_A : UI_DATA_YT_UNIT_HZ);
return 0;
}
/* 函数功能:假写死区——写守卫与执行侧同款语义:越界拒 = 值不动、**不置脏**
* (置了脏就是"屏上没改、却要落盘"的空承诺);接受 = 落值 + 置脏,
* 同一函数内发生。 返回值:0 / -1 int */
static int sim_yt_db_set(uint8_t a_idx, float a_eng) {
if ((a_idx >= UI_DATA_YT_DB_N) || (a_eng < 0.0f) || (a_eng > sim_yt_db_hi(a_idx))) {
return -1;
}
s_yt_db[a_idx] = a_eng;
s_yt_dirty = 1u;
return 0;
}
/* 函数功能:假系数点数(镜像 RUN_PAR_CH_N) 返回值:点数 uint8_t */
static uint8_t sim_yt_cal_n(void) {
return (uint8_t)UI_DATA_YT_CH_N;
}
/* 函数功能:假系数行现状(hi = RUN_PAR_K_MAX_UV 的镜像;见上注变更纪律) 返回值:0 / -1 int */
static int sim_yt_cal_row(uint8_t a_idx, ui_data_yt_row_t *a_out) {
if ((a_out == NULL) || (a_idx >= UI_DATA_YT_CH_N)) {
return -1;
}
a_out->val = s_yt_k[a_idx];
a_out->lo = 0.0f;
a_out->hi = 1000000000.0f; /* = RUN_PAR_K_MAX_UV 的镜像(粗守上限) */
a_out->decimals = 0u;
a_out->unit = UI_DATA_YT_UNIT_UV;
return 0;
}
/* 函数功能:假写系数 k(同 db_set 的"写值与置脏成对"语义) 返回值:0 / -1 int */
static int sim_yt_cal_k_set(uint8_t a_idx, float a_uv) {
if ((a_idx >= UI_DATA_YT_CH_N) || !(a_uv > 0.0f) || (a_uv > 1000000000.0f)) {
return -1; /* k<=0 与超粗守都拒;真带这一层模拟器不复刻(执行段的事) */
}
s_yt_k[a_idx] = a_uv;
s_yt_dirty = 1u;
return 0;
}
/* 函数功能:假未标定徽标源(恒已标定,理由见函数体注) 返回值:0 uint8_t */
static uint8_t sim_yt_cal_blocked(void) {
/* 模拟器恒"已标定":徽标腿的像素证据由整定四页黄金覆盖(同一注入式画法);
* 要拍"未标定 + 系数页"的组合另立带旗标的黄金态,本批不扩表 */
return 0u;
}
/* 函数功能:假整档脏位(页面上 WAIT 的唯一来源) 返回值:脏位 uint8_t */
static uint8_t sim_yt_pending(void) {
return s_yt_dirty;
}
static const ui_data_yt_ops_t s_yt_ops = {
sim_yt_db_n,
sim_yt_db_row,
sim_yt_db_set,
sim_yt_cal_n,
sim_yt_cal_row,
sim_yt_cal_k_set,
sim_yt_cal_blocked,
sim_yt_pending
};
/*
* 函数功能:落盘脏位的唯一清去处(镜像 defaultTask 每轮无条件 run_par_store_save)
* 入口参数:无
* 返回值:无
* 限定条件:主循环 / 无头注键尾各一处;--yt-nopump(拍 WAIT 黄金)时整函数空转
* 函数说明:真机"保存成功"的语义 = cfg_file_save 返 0 才清脏;模拟器无失败面,
* pump 即落盘——ERR 腿不复刻(拍不出来也别假拍,屏上 ERR 由写被拒触发,
* 那条模拟器能真拍:见黄金 cal_reject)
*/
static void sim_yt_pump(void) {
if (s_yt_nopump != 0) {
return;
}
s_yt_dirty = 0u;
}
/*
* 函数功能:SOE 记录假源——固定 3 条(镜像 App/task/ui_task.c 真机绑定的形态)
* 入口参数:无
@@ -584,6 +721,9 @@ static int run_headless(const char * keys, const char * path, int standby) {
printf("[sim] UI maint bind: FAIL\n");
}
ui_w_soe_bind(&s_soe_reader); /* task-15:SOE 记录页假源(形态镜像真机绑定) */
if (ui_data_yt_bind(&s_yt_ops) != 0) {
printf("[sim] UI yt bind: FAIL\n");
}
/* task-14 数据快照:镜像 ui_task.c 的 ui_data_init + 首帧发布(先于建页,
* 页面任何时刻打开都拿得到帧,不必等装配节拍) */
sim_publish_data_frames();
@@ -617,6 +757,7 @@ static int run_headless(const char * keys, const char * path, int standby) {
* 无头模式没有那个节拍,不在此当场排空,截图就会停在 WAIT(终态拍不到,
* OK/ERR/LOCK 三条判据的像素证据也就全都不成立) */
sim_param_pump();
sim_yt_pump(); /* 道F task-7:截图前清脏 = 拍 OK 黄金;--yt-nopump 停转 = 拍 WAIT 黄金(Lead 裁定④:两态都要有像素证据) */
/* --standby:注键后强制进入待机屏(截图前) */
if (standby != 0) {
@@ -1009,6 +1150,9 @@ int main(int argc, char **argv) {
if (strcmp(argv[i], "--noclock") == 0) {
s_noclock = 1; /* 把这台机器装成"墙钟无效"(RTC 异常/未对时) */
}
if (strcmp(argv[i], "--yt-nopump") == 0) {
s_yt_nopump = 1; /* 落盘 pump 停转:拍"已受理未落盘"(WAIT)黄金专用 */
}
}
/* 截图/自检模式:--screenshot / --keys / --standby / --cfgtest / --logtest */
@@ -1080,6 +1224,9 @@ int main(int argc, char **argv) {
printf("[sim] UI maint bind: FAIL\n");
}
ui_w_soe_bind(&s_soe_reader); /* task-15:SOE 记录页假源(形态镜像真机绑定) */
if (ui_data_yt_bind(&s_yt_ops) != 0) {
printf("[sim] UI yt bind: FAIL\n");
}
/* task-14 数据快照:镜像 ui_task.c 的 ui_data_init + 首帧发布(先于建页,
* 页面任何时刻打开都拿得到帧,不必等装配节拍) */
sim_publish_data_frames();
@@ -1128,6 +1275,7 @@ int main(int argc, char **argv) {
/* 落定定值整定请求(镜像真机 ui_data_feed_tick 里的 ui_data_param_drain:
* 出队 → prot_set 落值 → 重发参数帧);队列空时只是一次计数判断 */
sim_param_pump();
sim_yt_pump(); /* 道F task-7:窗口模式同样每轮无条件清脏(真机 50 ms 落盘的镜像) */
/* 配置文件轮询(2s 周期:外部文件变更 4s 沉降后自动重载) */
uint32_t now_ms = (uint32_t)SDL_GetTicks();
+69
View File
@@ -102,6 +102,25 @@ PAGES = [
("clear_2nd", "dddd eddde uruuruuue e"),
("clear_done", "dddd eddde uruuruuue ee"),
("clear_ro", "dddd eddde e"),
# ---- 道F task-7 遥测整定两页(根菜单第 4 项 = 3 下;子项 0/1 = 死区/系数)
# 键序读法与 task-3 三页同源:e 进子菜单后再 e 开叶子(#P → 先落密码页),
# "uruuruuue" = 修改口令 123(可编辑)。
# 死区 9 行 + 保存 = 10 行(2 页);系数 8 行 + 保存 = 9 行(2 页,保存行独在第 2 页)。
# ⚠ wait/ok 两张**必须成对存在**:脏位在模拟器由 sim_yt_pump 清(镜像真机
# defaultTask 每轮无条件落盘),wait 态靠 --yt-nopump 停 pump 拍下来——
# 只钉 OK 就是"只钉最好看那一帧"(Lead 裁定 task-7 派发④原文),
# 真机 WAIT 窗口毫秒级、人工永远补拍不到,只有这里替它留了像素证据。
("deadband", "ddd ee uruuruuue"), # 首屏(选中首行 UA死区 0.500V)
("deadband_wait", "ddd ee uruuruuue e uu e ddddddddd e"), # UA→0.502 提交 → 保存行 ENTER:WAIT(+--yt-nopump)
("deadband_ok", "ddd ee uruuruuue e uu e ddddddddd e"), # 同键序、pump 正常 → OK(脏位真被清)
("calib", "ddd e de uruuruuue"), # 首屏 8 行系数(153uV = ROM TPAFE 整 µV)
("calib_wait", "ddd e de uruuruuue e u e dddddddd e"), # UA→154 提交 → 保存行:WAIT(+--yt-nopump)
("calib_ok", "ddd e de uruuruuue e u e dddddddd e"), # 同键序、pump 正常 → OK
# 拒后**再**移到保存行(不按保存):一张图同时钉"粘滞 ERR 词"与"越界行反显"
# (r0 反显要等光标移开才单独可见——与选中白条重叠时像素分不出来,这是一开始
# 拍在首屏的图目检后改出来的键序);不按保存是因为按键会重新评估、ERR 会被
# 新一轮 WAIT/OK 覆盖——ERR 的像素证据只有这条窗口是真的
("calib_reject", "ddd e de uruuruuue e ddd l ddddd l d e dddddddd"),
]
# 文本级断言表:页名 -> [(名称, 期望串, y 带, 是否反显)]
@@ -192,6 +211,52 @@ TEXTS = {
("r0名", "运行记录", (24, 38), True),
("r6名", "遥控记录", (108, 122), False),
],
# ---- 道F task-7 遥测整定两页。行槽沿用列表页口径(24 + 14i),单位串由
# ui_w_setlist 直接贴数值尾("0.500V"无空格),µV 在屏上是 ASCII "uV"
"deadband": [
("标题", "遥测死区", (0, 24), False),
("r0名", "UA死区", (24, 38), True), ("r0值", "0.500V", (24, 38), True),
("r4名", "IA死区", (80, 94), False), ("r4值", "0.005A", (80, 94), False),
("r7名", "IN死区", (122, 136), False), ("r7值", "0.005A", (122, 136), False),
("页脚", "第 1 页,共 2 页", (140, 160), False),
],
"deadband_wait": [
("标题", "遥测死区", (0, 24), False),
# 第 2 页窗口:槽 0 = Hz死区行(跟随真源、本轮未动),槽 1 = 保存行(选中反显)
("r0名", "Hz死区", (24, 38), False), ("r0值", "0.500Hz", (24, 38), False),
("保存名", "保存", (38, 52), True), ("保存值", "WAIT", (38, 52), True),
("页脚", "第 2 页,共 2 页", (140, 160), False),
],
"deadband_ok": [
("标题", "遥测死区", (0, 24), False),
("r0名", "Hz死区", (24, 38), False),
("保存名", "保存", (38, 52), True), ("保存值", "OK", (38, 52), True),
("页脚", "第 2 页,共 2 页", (140, 160), False),
],
"calib": [
("标题", "遥测系数", (0, 24), False),
("r0名", "UA系数", (24, 38), True), ("r0值", "153uV", (24, 38), True),
("r7名", "IN系数", (122, 136), False), ("r7值", "153uV", (122, 136), False),
("页脚", "第 1 页,共 2 页", (140, 160), False),
],
"calib_wait": [
("标题", "遥测系数", (0, 24), False),
("保存名", "保存", (24, 38), True), ("保存值", "WAIT", (24, 38), True),
("页脚", "第 2 页,共 2 页", (140, 160), False),
],
"calib_ok": [
("标题", "遥测系数", (0, 24), False),
("保存名", "保存", (24, 38), True), ("保存值", "OK", (24, 38), True),
("页脚", "第 2 页,共 2 页", (140, 160), False),
],
"calib_reject": [
("标题", "遥测系数", (0, 24), False),
# 第 2 页(光标移到保存行、未按键):k=0 被执行侧拒 → **值纹丝不动**
# (副本未动、不置脏——"写值与置脏成对"的另一半判据)+ 保存行粘滞 ERR;
# r0 的越界反显在第 1 页像素里(文本断言不猜反显形状,逐字节比对钉住)
("保存名", "保存", (24, 38), True), ("保存值", "ERR", (24, 38), True),
("页脚", "第 2 页,共 2 页", (140, 160), False),
],
# v2.0(道E task-15 回炉):6 行 Flash 字面量 -> 8 行真值;标签去全角冒号(要给值列
# 让出 MAC 的 102 px),行槽 = 38 + 13i。值全部来自模拟器固定帧(simulator/main.c 的
# sim_publish_data_frames)与 net_cfg 出厂副本 —— 逐行断言等于把这两处口径钉在像素侧。
@@ -569,6 +634,10 @@ POOL_AFTER_ROUNDTRIP = 5448
# 不为它另造一套假数据:假的是"时钟有没有"这件事,页面与显示链全是真的。
EXTRA = {
"clock_noclock": ["--noclock"],
# 道F task-7:WAIT 态黄金 = 落盘 pump 停转(假件保留脏位,Lead 裁定④);
# OK 态同键序不加旗标,两图唯一差别就是"脏有没有被清"——对照即凭证
"deadband_wait": ["--yt-nopump"],
"calib_wait": ["--yt-nopump"],
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.