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
+150 -1
View File
@@ -24,6 +24,10 @@
* (定值写路径出队 → 闭锁/越界预检 → set_sealed,
* 见 s_param_ops);② 参数帧尾填 cal_block(未标定
* 闭锁徽标,三路口径同 protect_task.c,不用 eng)
* v1.3 2026-10-27 DTU 项目组 道F task-7:遥测整定注入表(ui_data_yt_ops_t)
* 装配层实现 + 首轮懒绑(时序与通道四绑定等价,
* 见 feed_yt_bind_once 函数头);行数等值由编译期
* 守卫钉,量纲分档与合法域分档同源同序
* v1.2 2026-10-26 DTU 项目组 道F task-16:删除本地镜像 feed_cal_block(),
* 两个调用点(填帧 / 门控比较)改调
* adc_cal_prot_blocked()——同一份三路判定此前在
@@ -55,7 +59,8 @@
/* 同层(App)数据源 */
#include "adc_input.h" /* ADC_SEM_COUNT / adc_input_raw(TEMP 码值) */
#include "adc_cal.h" /* adc_cal_flags:VALID/CLIPPED 三态可用性;adc_cal_prot_blocked:未标定闭锁单点 */
#include "adc_cal.h" /* adc_cal_flags:VALID/CLIPPED 三态可用性;adc_cal_prot_blocked:未标定闭锁单点;adc_cal_k_uv:系数当前生效值(与 104 回显同视图) */
#include "run_par.h" /* 道F task-7 遥测整定注入表:死区/合法域/系数 setter/脏位(真源全在本模块与 adc_cal,feed 只转达) */
#include "phasor.h" /* 基波/谐波有效值(fAmSQ) */
#include "freq.h" /* 测频读数与新鲜度 */
#include "prot_elm.h" /* 元件动作态(遥信位图来源) */
@@ -476,6 +481,143 @@ static const ui_data_param_ops_t s_param_ops = {
feed_param_apply
};
/*
* ======================= 道F task-7:遥测整定注入表(ui_data_yt_ops_t)装配
*
* 两页唯一的执行侧转达处。四条装配纪律:
* ① 数值与合法域**都**现给执行侧(run_par_deadband / run_par_db_range /
* adc_cal_k_uv / RUN_PAR_K_MAX_UV)——本文件不出现任何范围数字字面量;
* 系数真带(µV/码执行段裁)没有公开访问器,也**不在此补一份**(task-43 收口),
* 真带拒的值经 set 原样返 -1,页面反显 + 保存行 ERR,两层守卫互不掩盖。
* ② 写走语义 setter(写值与置脏同一函数内发生,run_par_cal_k_set 文件头契约);
* 本层不存在"只置脏不写值"或"只写值不置脏"的组合,UI 永远够不到裸脏位。
* ③ 跨任务写口径:真机默认档 = iec104Task 的 C_SE;本文件的调用在 uiTask,
* 这**正是** Lead 裁定(task-7 派发②)批准的"装配层转达"整定路径——与 104
* 走同一批 setter、同一 int32 单字原子契约,不新开工;run_par.h 里死区旧
* 接口"iec104Task 上下文"的限定条件语写于该裁定之前,语义已被裁定覆盖
* (系数两支的限定条件已明写装配层入口,措辞同步归算法层下一笔)。
* ④ 落盘不靠本表:写成功置脏 → defaultTask 每轮无条件 run_par_store_save()
* (查实:唯一 cfg_file_save 调用者,首行"无脏立即返回";104 的 peek→事件位
* 只是加速器不是必经链)——所以"OK = 盘上真落了"在真机与模拟器(pump 清脏)
* 两端口径一致,黄金参考拍的 WAIT/OK 不是表演。
*/
/* 跨层行数等值守卫:BSP 侧契约值 ↔ 执行侧真值。本文件是全仓第一处两份头
* 同时可见的编译单元——BSP 物理上无法自证(看不到 App 头),守卫落这里 */
typedef char feed_yt_db_n_check_t[(UI_DATA_YT_DB_N == (uint8_t)RUN_PAR_DB_N) ? 1 : -1];
typedef char feed_yt_ch_n_check_t[(UI_DATA_YT_CH_N == (uint8_t)RUN_PAR_CH_N) ? 1 : -1];
/*
* 函数功能:死区点数(执行侧真值直通)
* 入口参数:无
* 返回值:RUN_PAR_DB_N uint8_t
* 限定条件:uiTask(注入表回调)
* 函数说明:行数从这一头出、BSP 契约常量在文件头编译期守卫对表——两边不等
* 编不过,等值不靠注释维持
*/
static uint8_t feed_yt_db_n(void) {
return (uint8_t)RUN_PAR_DB_N;
}
/*
* 函数功能:读第 idx 行死区现状(值 + 粗守域现给;量纲按点型分档)
* 入口参数:idx - 0..8(0..7 电气语义通道序,8 频率) uint8_t
* a_out - 行现状 ui_data_yt_row_t *
* 返回值:0 - 已写出;-1 - 越界 / 指针空(run_par_db_range 自判,本层不重复判) int
* 函数说明:unit 的分档(前 4 电压、中 4 电流、末 1 频率)**必须**与
* run_par_db_range 的档位分档同序——两处真源都没导出量纲,唯一防线是
* run_par.h 地址映射表(0x8030~0x8038 = UA..IN/FREQ)与本表同序这条
* 写死的对表纪律 + 黄金像素(档位漂了屏上会呈现"0~50"配 "A" 的肉眼矛盾);
* decimals 取 3 = 死区在引擎里的存储分辨率(毫单位 int32,换算单点
* s_milli_to_eng 在 run_par.c,本层不重除)
*/
static int feed_yt_db_row(uint8_t a_idx, ui_data_yt_row_t *a_out) {
float lo;
float hi;
if (run_par_db_range(a_idx, &lo, &hi) != 0) {
return -1;
}
a_out->val = run_par_deadband(a_idx);
a_out->lo = lo;
a_out->hi = hi;
a_out->decimals = 3u;
a_out->unit = (a_idx < 4u) ? UI_DATA_YT_UNIT_V
: ((a_idx < 8u) ? UI_DATA_YT_UNIT_A : UI_DATA_YT_UNIT_HZ);
return 0;
}
/* 函数功能:写第 idx 行死区(直通语义 setter;返回值原样上抛,页面据此记账) int */
static int feed_yt_db_set(uint8_t a_idx, float a_eng) {
return run_par_deadband_set(a_idx, a_eng);
}
static uint8_t feed_yt_cal_n(void) {
return (uint8_t)RUN_PAR_CH_N;
}
/*
* 函数功能:读第 idx 路系数现状(k = adc_cal **当前生效值**,µV/码)
* 返回值:0 - 已写出;-1 - idx 越界或指针空 int
* 函数说明:显示值与 104 C_RD 回显同视图(run_par_104_read 用的就是 adc_cal_k_uv:
* 文件坏值被真带回退 ROM 时,屏与主站看到同一份 ROM——"页面值按比例
* 变化"判据因此量的就是执行段真正吃的数);
* lo/hi 只有粗守一层(0..RUN_PAR_K_MAX_UV,上限来自 run_par 唯一出口),
* 真带在执行段裁——两层守卫的注释纪律见 ui_w_ytview 头注;
* decimals 取 0 是**显示域容量约束**不是精度偏好:粗守上限 1e9 在
* ui_w_setlist 的 int32 显示域里只够零位小数(带 2 位则上限装不下),
* 屏上输入按整 µV 量化、写口仍收 float;要小数位得收紧粗守或改组件
* 值通道,都不在本批,也不许在此抄一份更小的假上限
*/
static int feed_yt_cal_row(uint8_t a_idx, ui_data_yt_row_t *a_out) {
if ((a_out == NULL) || (a_idx >= (uint8_t)RUN_PAR_CH_N)) {
return -1;
}
a_out->val = adc_cal_k_uv((adc_semantic_t)a_idx);
a_out->lo = 0.0f; /* 下限 0 由写口拒 k<=0 兜住,这里只给输入框边界 */
a_out->hi = RUN_PAR_K_MAX_UV;
a_out->decimals = 0u;
a_out->unit = UI_DATA_YT_UNIT_UV;
return 0;
}
/* 函数功能:写第 idx 路系数 k(越界防御后再进语义 setter——setter 形参是枚举,
* 编译器帮不上越界忙;返回码直通) int */
static int feed_yt_cal_k_set(uint8_t a_idx, float a_uv) {
if (a_idx >= (uint8_t)RUN_PAR_CH_N) {
return -1;
}
return run_par_cal_k_set((adc_semantic_t)a_idx, a_uv);
}
/* 函数功能:未标定闭锁徽标源(task-16 单点访问器,与判据停走同一个判定) uint8_t */
static uint8_t feed_yt_cal_blocked(void) {
return (adc_cal_prot_blocked() != 0) ? 1u : 0u;
}
/* 函数功能:整档落盘脏位(保存行三态判据;与 104 留痕同一个 peek,不另发明) uint8_t */
static uint8_t feed_yt_pending(void) {
return run_par_dirty_peek();
}
/* 遥测整定注入表(八件齐全;ui_data_yt_bind 拒残表,缺件就是装配错) */
static const ui_data_yt_ops_t s_yt_ops = {
feed_yt_db_n,
feed_yt_db_row,
feed_yt_db_set,
feed_yt_cal_n,
feed_yt_cal_row,
feed_yt_cal_k_set,
feed_yt_cal_blocked,
feed_yt_pending
};
/* 首轮懒绑一次:ui_data_feed_tick() 的首个调用点在 App/task/ui_task.c 里先于
* ui_menu_init()(首帧发布那次),与通道四 ui_data_maint_bind 的绑定时序等价;
* 不在 ui_task.c 直接绑是为了本批写域收在派发单列出的文件里。残表是真故障,
* 必须能在启动日志里看见,不能静默(通道四同口径) */
static uint8_t s_yt_bound;
/*
* 函数功能:组装参数快照(40 项定值 Q10 + 校验和 + 封存态 + 越界位图 + 未标定闭锁)
@@ -558,6 +700,13 @@ void ui_data_feed_tick(void) {
uint32_t sum;
uint8_t due_rt;
if (s_yt_bound == 0u) {
s_yt_bound = 1u; /* 先置位再绑:失败也不每轮重试刷日志(90 B/条) */
if (ui_data_yt_bind(&s_yt_ops) != 0) {
DBG_ERROR("UI yt bind: FAIL");
}
}
now = osKernelGetTickCount();
if (s_map_done == 0u) {