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:
@@ -879,3 +879,150 @@ int ui_data_clock_format(const ui_data_clock_t *p_in, char *a_buf, uint32_t a_le
|
||||
*p = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── 通道五:遥测整定注入(死区 / 系数)──────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* 注入的遥测整定执行侧函数表(NULL = 未注入)。
|
||||
* 与 s_maint 同一条生命周期约定:注入方(App/app/ui_data_feed.c 与
|
||||
* simulator/main.c)给的都文件级 static const 表,本层只存指针不拷内容;
|
||||
* 撤销窗口只在宿主测试里存在。
|
||||
*
|
||||
* ⚠ 本层**不持有任何遥测整定的值**:九条死区与八路系数的真源在
|
||||
* App/algo/run_par.c 与 App/algo/adc_cal.c,落盘动力在 defaultTask 的每轮
|
||||
* 无条件 run_par_store_save()(首行 if (s_dirty == 0u) return 0;,道F task-7
|
||||
* 查实结论:落盘链不靠 104 会话、也不靠事件位)。本层若自己存一份,就正是
|
||||
* 卡片判据"不新增第二份真源"要拦的那件事。页面能问的只有两样:现状(*_row)
|
||||
* 与"我这次动作落没落"(pending)。
|
||||
*/
|
||||
static const ui_data_yt_ops_t *s_yt;
|
||||
|
||||
/*
|
||||
* 函数功能:注入遥测整定执行侧函数表(启动期一次)
|
||||
* 入口参数:ops - 函数表;八个成员指针都不得为 NULL;NULL = 撤销注入
|
||||
* 返回值:0 - 已注入(或已撤销);-1 - ops 非空但某成员为 NULL(存量不动) int
|
||||
* 限定条件:uiTask 启动期,先于 ui_menu_init();本层无锁,与快照层同一并发前提
|
||||
* 函数说明:整表校验的理由与 ui_data_maint_bind() 逐字相同——半途接错一条会让
|
||||
* 页面在"按下才崩"的位置崩,装配期就拒掉残表才是省事。残表被拒后**存量
|
||||
* 不动**(TC-UIDATA-027 钉住这条:好表在用时被残表覆盖会静默把已通的口
|
||||
* 打断)
|
||||
*/
|
||||
int ui_data_yt_bind(const ui_data_yt_ops_t *a_ops) {
|
||||
if (a_ops == NULL) {
|
||||
s_yt = NULL; /* 撤销注入:页面随即按"整定能力不可用"渲染 */
|
||||
return 0;
|
||||
}
|
||||
if ((a_ops->db_n == NULL) || (a_ops->db_row == NULL) ||
|
||||
(a_ops->db_set == NULL) || (a_ops->cal_n == NULL) ||
|
||||
(a_ops->cal_row == NULL) || (a_ops->cal_k_set == NULL) ||
|
||||
(a_ops->cal_blocked == NULL) || (a_ops->pending == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
s_yt = a_ops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:死区点数
|
||||
* 返回值:点数;未注入返回 0(页面据此渲染空表,而不是拿编译期常量当"真源说
|
||||
* 有几行"——行数归执行侧,见 ui_data.h 通道五表注) uint8_t
|
||||
* 限定条件:页面绘制路径可直调(一次函数指针转发,零 I/O、零锁)
|
||||
*/
|
||||
uint8_t ui_data_yt_db_n(void) {
|
||||
if (s_yt == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
return s_yt->db_n();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:读第 idx 行死区现状(工程量 + 粗守域 + 量纲 + 显示小数位)
|
||||
* 入口参数:idx - 0..8(0..7 = 电气语义通道序,8 = 频率) uint8_t
|
||||
* a_out - 行现状 ui_data_yt_row_t *
|
||||
* 返回值:0 - 已写出;-1 - 未注入 / 参数空 / 执行侧无此行(页面这行退化显示,
|
||||
* 不猜、不拿上一行数值顶替) int
|
||||
* 限定条件:页面绘制与按键链内(uiTask)
|
||||
* 函数说明:出参在本层不预清——执行侧契约是"返 0 才整份写",页面返非 0 时
|
||||
* 不得读该结构,这条口径由 TC-UIDATA-028 钉
|
||||
*/
|
||||
int ui_data_yt_db_row(uint8_t a_idx, ui_data_yt_row_t *a_out) {
|
||||
if ((s_yt == NULL) || (a_out == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
return s_yt->db_row(a_idx, a_out);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:写第 idx 行死区(工程量)——两页唯一的死区写出口
|
||||
* 返回值:0 - 副本已写且已置脏;-1 - 未注入或执行侧拒(副本未动、不置脏) int
|
||||
* 限定条件:uiTask 按键链内(编辑提交)
|
||||
* 函数说明:未注入与执行侧拒绝**同码返回**是刻意的:两条腿屏上都是"值没变 +
|
||||
* 保存行 ERR",页面不需要、也不该分辨"是没接上还是被拒"——那是装配期
|
||||
* 故障与工况故障的区分,归日志不归屏幕
|
||||
*/
|
||||
int ui_data_yt_db_set(uint8_t a_idx, float a_eng) {
|
||||
if (s_yt == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return s_yt->db_set(a_idx, a_eng);
|
||||
}
|
||||
|
||||
/* 函数功能:系数覆盖点数;未注入返回 0(理由同 ui_data_yt_db_n) uint8_t */
|
||||
uint8_t ui_data_yt_cal_n(void) {
|
||||
if (s_yt == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
return s_yt->cal_n();
|
||||
}
|
||||
|
||||
/* 函数功能:读第 idx 路系数现状;返回值与不猜口径同 ui_data_yt_db_row int */
|
||||
int ui_data_yt_cal_row(uint8_t a_idx, ui_data_yt_row_t *a_out) {
|
||||
if ((s_yt == NULL) || (a_out == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
return s_yt->cal_row(a_idx, a_out);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:写第 idx 路系数 k(µV/码)——两页唯一的系数写出口
|
||||
* 返回值:0 - 已写并置脏;-1 - 未注入或拒(副本未动、不置脏) int
|
||||
* 限定条件:uiTask 按键链内
|
||||
* 函数说明:粗守(上限 RUN_PAR_K_MAX_UV)与真带(执行段 ×[0.5,2.0])两层守卫
|
||||
* **互不掩盖**:屏上输入框只能挡误写,真带裁在 adc_cal;本层不加第三层
|
||||
*/
|
||||
int ui_data_yt_cal_k_set(uint8_t a_idx, float a_uv) {
|
||||
if (s_yt == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return s_yt->cal_k_set(a_idx, a_uv);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:未标定闭锁徽标源
|
||||
* 返回值:1 - 闭锁中;0 - 正常**或未注入** uint8_t
|
||||
* 限定条件:页面绘制路径可直调
|
||||
* 函数说明:未注入按 0 是"宁可不画"而不是"报个徽标保平安"——徽标是断言,
|
||||
* 没有真源时的断言就是假断言;此时页面上写值本来也全被拒(写口返 -1),
|
||||
* 信息不缺这条
|
||||
*/
|
||||
uint8_t ui_data_yt_cal_blocked(void) {
|
||||
if (s_yt == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
return s_yt->cal_blocked();
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:整档落盘脏位探查(保存行三态的唯一判据源)
|
||||
* 返回值:1 - 有已整定未落盘改动;0 - 干净或未注入 uint8_t
|
||||
* 限定条件:页面绘制路径可直调(每帧一次转发,零 I/O)
|
||||
* 函数说明:本层**不加任何边沿记忆**——"我这次动作的结果"要锁存是页面的事
|
||||
* (脏位整档全局、他人再改会重新置脏,两条口径见 ui_data.h pending
|
||||
* 成员注);在这里做锁存会把两页的状态耦合成一份
|
||||
*/
|
||||
uint8_t ui_data_yt_pending(void) {
|
||||
if (s_yt == NULL) {
|
||||
return 0u;
|
||||
}
|
||||
return s_yt->pending();
|
||||
}
|
||||
|
||||
@@ -679,6 +679,161 @@ int ui_data_clock_parse(const char *a_ts, ui_data_clock_t *p_out);
|
||||
*/
|
||||
int ui_data_clock_format(const ui_data_clock_t *p_in, char *a_buf, uint32_t a_len);
|
||||
|
||||
/* ── 通道五:遥测整定注入(死区 / 系数,道F task-7)────────────────────────
|
||||
*
|
||||
* 为什么又是一张注入表而不是页面直调 run_par / adc_cal:与通道四完全同一组理由
|
||||
* ——① 分层方向(Drivers/BSP 不得反向 include App 头,layer-deps 拦提交);
|
||||
* ② 模拟器既不链 App/algo/run_par.c 也不链 App/algo/adc_cal.c,页面里用一次
|
||||
* 真源函数就是"MCU 编得过、模拟器链接期爆"(通道四表注已记过一次的同族坑);
|
||||
* ③ 合法域与"写值 + 置脏"必须原子发生在执行侧(run_par_cal_k_set 的文件头契约:
|
||||
* 拆开等于"屏上已改、盘上永不落、且没有任何错误返回"),本表只转达。
|
||||
* ⚠ 本表刻意不含任何范围数值的拷贝:lo/hi 由执行侧逐行现给(合法域唯一出口
|
||||
* run_par_db_range,系数粗守上限唯一出口 RUN_PAR_K_MAX_UV),页面抄一份数字
|
||||
* 就是第二真源——µV 真带(执行段裁)没有公开访问器,也**不许**页面自己补一份。
|
||||
*/
|
||||
|
||||
/* 死区点数:下标 0..7 = 电气语义通道序(UA..IN),8 = 频率(与 104 地址映射表
|
||||
* 0x8030~0x8038 同序);系数覆盖点数:电气 8 路(语义枚举前缀段)。
|
||||
* 与 App/algo/run_par.h 的 RUN_PAR_DB_N / RUN_PAR_CH_N 等值——BSP 看不到 App 头,
|
||||
* 等值由装配层编译期守卫(App/app/ui_data_feed.c)与 TC-UIDATA-027 同时钉住 */
|
||||
#define UI_DATA_YT_DB_N 9u
|
||||
#define UI_DATA_YT_CH_N 8u
|
||||
|
||||
/* 量纲代码:unit 是"这行数值本身的物理量纲"(诚实性字段,同 rt 帧 eng 的思路),
|
||||
* 归数据源管;把它显示成什么字符串("V"/"Hz"/"uV")才是表现层的事 */
|
||||
#define UI_DATA_YT_UNIT_V 0u /* 电压死区,V */
|
||||
#define UI_DATA_YT_UNIT_A 1u /* 电流死区,A */
|
||||
#define UI_DATA_YT_UNIT_HZ 2u /* 频率死区,Hz */
|
||||
#define UI_DATA_YT_UNIT_UV 3u /* 系数 k,µV/码(屏上串用 ASCII "uV") */
|
||||
|
||||
/* 一行遥测整定项的完整现状(执行侧现给,页面只读) */
|
||||
typedef struct {
|
||||
float val; /* 当前工程量:死区 V/A/Hz;系数 = adc_cal 当前生效值(µV/码,
|
||||
与 104 回显同视图:文件坏值被合法带哨兵回退 ROM 时显 ROM) */
|
||||
float lo; /* 粗守下限(含)——合法域唯一出口给,页面不得自造 */
|
||||
float hi; /* 粗守上限(含)——同上;真带在执行段另裁,两层互不掩盖 */
|
||||
uint8_t unit; /* UI_DATA_YT_UNIT_* */
|
||||
uint8_t decimals; /* 显示小数位(**显示域选择**,不是数据源精度,归装配层定) */
|
||||
} ui_data_yt_row_t;
|
||||
|
||||
/*
|
||||
* 执行侧函数表(常驻注入;形状与 ui_data_maint_ops_t 同款:整表校验、拒残表)
|
||||
*
|
||||
* 与定值整定(通道 param 的位图批量 + 工作叠层)**故意不同形**:死区/系数写的是
|
||||
* 运行参数档([run_par]/[adc_cal]),语义 setter 本身就是"写 RAM 副本 + 置脏"
|
||||
* 的原子出口、单行即提交;落盘确认是独立一问(pending),所以本表没有 commit
|
||||
* 一口——保存行按键读的就是 pending()。
|
||||
*/
|
||||
typedef struct {
|
||||
/*
|
||||
* 函数功能:死区点数(执行侧真值;未注入时本层返回 0)
|
||||
* 返回值:点数 uint8_t
|
||||
*/
|
||||
uint8_t (*db_n)(void);
|
||||
/*
|
||||
* 函数功能:读第 idx 行死区现状(工程量 + 粗守域 + 量纲 + 显示小数位)
|
||||
* 返回值:0 - 已写出;-1 - idx 越界 / 指针空 / 执行侧无此行 int
|
||||
*/
|
||||
int (*db_row)(uint8_t a_idx, ui_data_yt_row_t *a_out);
|
||||
/*
|
||||
* 函数功能:写第 idx 行死区(工程量)——真机 = run_par_deadband_set()
|
||||
* 返回值:0 - 副本已写且已置脏;-1 - 粗守或执行侧拒(副本未动、不置脏) int
|
||||
*/
|
||||
int (*db_set)(uint8_t a_idx, float a_eng);
|
||||
/* 函数功能:系数覆盖点数(执行侧真值) 返回值:点数 uint8_t */
|
||||
uint8_t (*cal_n)(void);
|
||||
/* 函数功能:读第 idx 路系数现状(同 db_row,工程量域为 µV/码) int */
|
||||
int (*cal_row)(uint8_t a_idx, ui_data_yt_row_t *a_out);
|
||||
/*
|
||||
* 函数功能:写第 idx 路系数 k(µV/码)——真机 = run_par_cal_k_set()
|
||||
* 返回值:0 - 已写并置脏;-1 - 粗守或执行段真带拒(副本未动、不置脏) int
|
||||
*/
|
||||
int (*cal_k_set)(uint8_t a_idx, float a_uv);
|
||||
/*
|
||||
* 函数功能:未标定闭锁徽标源——真机 = adc_cal_prot_blocked()(task-16 单点
|
||||
* 访问器,与判据停走同一个判定;这里**不**用 rt 帧 eng 反推)
|
||||
* 返回值:1 - 未标定闭锁中;0 - 正常 uint8_t
|
||||
*/
|
||||
uint8_t (*cal_blocked)(void);
|
||||
/*
|
||||
* 函数功能:整档落盘脏位探查——真机 = run_par_dirty_peek()
|
||||
* 返回值:1 - 有已整定未落盘改动;0 - 干净 uint8_t
|
||||
* ⚠ 两条口径必须与页面注释同步(道F task-7 Lead 裁定):
|
||||
* ① 脏位是**整档全局**的(一趟落 [run_par] 九键 + [adc_cal] 两段),归零
|
||||
* 证明"这一档落盘了",不证明"我这一项单独落盘";
|
||||
* ② 他人(104 C_SE)随后再改会重新置脏 ⇒ 页面必须结果锁存,把"我这次动作
|
||||
* 的结果"与"档位此刻的状态"分开(clear/clock 页同一条先例)。
|
||||
*/
|
||||
uint8_t (*pending)(void);
|
||||
} ui_data_yt_ops_t;
|
||||
|
||||
/*
|
||||
* 函数功能:注入遥测整定执行侧函数表(启动期一次;形状/语义逐字对齐 ui_data_maint_bind)
|
||||
* 入口参数:ops - 函数表(八个成员指针都不得为 NULL);NULL = 撤销注入(只在宿主测试存在)
|
||||
* 返回值:0 - 已注入(或已撤销);-1 - ops 非空但成员残缺(存量不动,页面按
|
||||
* "整定能力不可用"渲染,不崩不猜) int
|
||||
* 限定条件:uiTask 启动期、先于 ui_menu_init()(真机在装配层 ui_data_feed_tick()
|
||||
* 首轮懒绑一次——该调用点在 ui_menu_init 之前,时序与通道四绑定等价)
|
||||
*/
|
||||
int ui_data_yt_bind(const ui_data_yt_ops_t *a_ops);
|
||||
|
||||
/*
|
||||
* 函数功能:死区点数
|
||||
* 入口参数:无
|
||||
* 返回值:点数;未注入返回 0 uint8_t
|
||||
*/
|
||||
uint8_t ui_data_yt_db_n(void);
|
||||
|
||||
/*
|
||||
* 函数功能:读第 idx 行死区现状
|
||||
* 入口参数:idx - 行下标 uint8_t;a_out - 行现状(返 0 才被整份写出) ui_data_yt_row_t *
|
||||
* 返回值:0 - 已写出;-1 - 未注入 / 参数空 / 执行侧无此行 int
|
||||
*/
|
||||
int ui_data_yt_db_row(uint8_t a_idx, ui_data_yt_row_t *a_out);
|
||||
|
||||
/*
|
||||
* 函数功能:写第 idx 行死区(工程量)
|
||||
* 入口参数:idx - 行下标 uint8_t;a_eng - 工程量(V/A/Hz) float
|
||||
* 返回值:0 - 已写并置脏;-1 - 未注入或执行侧拒(未注入与拒绝同码:页面都按
|
||||
* ERR 显示,装配故障与工况故障的区分归日志不归屏幕) int
|
||||
*/
|
||||
int ui_data_yt_db_set(uint8_t a_idx, float a_eng);
|
||||
|
||||
/*
|
||||
* 函数功能:系数覆盖点数
|
||||
* 入口参数:无
|
||||
* 返回值:点数;未注入返回 0 uint8_t
|
||||
*/
|
||||
uint8_t ui_data_yt_cal_n(void);
|
||||
|
||||
/*
|
||||
* 函数功能:读第 idx 路系数现状(µV/码域)
|
||||
* 入口参数:idx - 电气语义通道序 uint8_t;a_out - 行现状 ui_data_yt_row_t *
|
||||
* 返回值:0 - 已写出;-1 - 未注入 / 参数空 / 执行侧无此行 int
|
||||
*/
|
||||
int ui_data_yt_cal_row(uint8_t a_idx, ui_data_yt_row_t *a_out);
|
||||
|
||||
/*
|
||||
* 函数功能:写第 idx 路系数 k(µV/码)
|
||||
* 入口参数:idx - 电气语义通道序 uint8_t;a_uv - 新系数(µV/码) float
|
||||
* 返回值:0 - 已写并置脏;-1 - 未注入或拒(粗守/真带,副本未动) int
|
||||
*/
|
||||
int ui_data_yt_cal_k_set(uint8_t a_idx, float a_uv);
|
||||
|
||||
/*
|
||||
* 函数功能:未标定闭锁徽标源
|
||||
* 入口参数:无
|
||||
* 返回值:1 - 闭锁中;0 - 正常或未注入(宁可不画,不假报) uint8_t
|
||||
*/
|
||||
uint8_t ui_data_yt_cal_blocked(void);
|
||||
|
||||
/*
|
||||
* 函数功能:整档落盘脏位探查(保存行三态判据)
|
||||
* 入口参数:无
|
||||
* 返回值:1 - 有已整定未落盘改动;0 - 干净或未注入 uint8_t
|
||||
*/
|
||||
uint8_t ui_data_yt_pending(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 模块名称:UI Page calib (遥测系数内容页)
|
||||
* 模块功能:三遥设置 → 遥测系数 叶子(#P 受密码保护)的内容页——8 行数值表,
|
||||
* 每页 8 行自动分页;本文件零 App include:行表现状(值/粗守域/量纲/
|
||||
* 小数位)逐行来自 ui_data.h 通道五注入表,写与落盘确认全经
|
||||
* ui_w_ytview 视图模型(状态机/反显/徽标在那一侧,两页共用一份)——
|
||||
* 本文件只是标准骨架几何令牌接进框架的薄跳板,改一次口径两页一致
|
||||
* 两态一页:123 修改权限 → 可编辑(编辑器 + 保存行 = 落盘确认);000 查看权限
|
||||
* → 只读(attach 内不绑 commit → 无保存行、key_cb 整页交回框架,
|
||||
* 行为与"没有 key_cb"逐字等价,整定四页同一条分派纪律)
|
||||
* 徽标说明:只读/可编辑两态都画"未标定"(cal_blocked 经注入表现读)——徽标说的
|
||||
* 是数据可信度,与本次权限无关;它**不**禁任何编辑(与定值 LOCK 分工:
|
||||
* LOCK 才写不进去,未标定只是"改了也不会动作",ui_w_setlist.h 三词三源)
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-27 DTU 项目组 创建(道F task-7;判据 docs/任务规划/UI补齐.md
|
||||
* §2 第 4 行「改死区能改变 104 上送门限 / 改 k 后
|
||||
* 页面值按比例变化 / 只走既有落盘链不加第二真源」)
|
||||
*/
|
||||
/* 头文件包含区 */
|
||||
#include "ui_page_calib.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
#include "ui_w_setlist.h"
|
||||
#include "ui_w_ytview.h"
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
/* 本页定值表实例(组件状态独占一份,勿跨页共用——ui_w_setlist 实例化约定) */
|
||||
static ui_w_setlist_t s_sl;
|
||||
|
||||
/* 函数定义区 */
|
||||
|
||||
/*
|
||||
* 函数功能:开页绑视图(行表现状逐行现取 + 两态分派都在视图模型内;接 init_cb)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:框架开页链内(本次进入权限已装载,时序依据见 ui_page_view_param.c
|
||||
* 同位置注释:框架先装权限再调 init_cb)
|
||||
*/
|
||||
static void pg_init(void) {
|
||||
ui_w_ytview_attach_cal(&s_sl);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:总行数(数据行现给 + 可编辑态才有保存行;接 count_cb)
|
||||
* 入口参数:无
|
||||
* 返回值:总行数 uint16_t
|
||||
* 限定条件:框架绘制/翻页链内
|
||||
*/
|
||||
static uint16_t pg_count(void) {
|
||||
return ui_w_setlist_count(&s_sl);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:逐行取数(接 refresh;行现状在视图模型里现取,本层无缓存)
|
||||
* 入口参数:a_row / a_name / a_value - 契约见 ui_content_page.h
|
||||
* 返回值:无
|
||||
* 限定条件:框架绘制链内
|
||||
*/
|
||||
static void pg_refresh(uint16_t a_row, char *a_name, char *a_value) {
|
||||
ui_w_setlist_refresh(&s_sl, a_row, a_name, a_value);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:自绘层(编辑光标反显 + 写被拒行反显 + 未标定徽标)
|
||||
* 入口参数:a_layer / a_clip - 契约见 ui_content_page.h
|
||||
* 返回值:无
|
||||
* 限定条件:框架绘制链内,内置区块画完之后
|
||||
*/
|
||||
static void pg_draw(lv_layer_t *a_layer, const lv_area_t *a_clip) {
|
||||
ui_w_setlist_draw(&s_sl, a_layer, a_clip);
|
||||
ui_w_ytview_draw_cal_badge(a_layer, a_clip);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:按键转发(接 key_cb)
|
||||
* 入口参数:a_key - LVGL 键值 lv_key_t
|
||||
* 返回值:true - 本页已消费;false - 交回框架(↑↓ 移选、← 关页)
|
||||
* 限定条件:描述符 edit_capable=1 + key_intercept=1(编辑中 ←→ 是移数字光标,
|
||||
* 必须先给页面,否则框架把 ← 当关页——整定四页同款实证)
|
||||
* 函数说明:第一条门 = 只读进入(未绑 commit)整页交回框架,与"本页没有
|
||||
* key_cb"逐字等价(黄金参考只读态为证);其余分发规则全在组件
|
||||
*/
|
||||
static bool pg_key(lv_key_t a_key) {
|
||||
if (s_sl.commit == NULL) {
|
||||
return false;
|
||||
}
|
||||
return (ui_w_setlist_key(&s_sl, a_key) != 0u);
|
||||
}
|
||||
|
||||
/*
|
||||
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=calib 绑定引用)
|
||||
* 列表页骨架 + 可编辑 + ← 拦截;band_h 必须 >0(选中行黑字白底反显,同四整定页);
|
||||
* row_count 给静态上限(8 数据行 + 保存行),行数运行时以注入表现报为准
|
||||
*/
|
||||
const content_page_def_t ui_page_calib_desc = {
|
||||
.id = "calib",
|
||||
.title = "遥测系数",
|
||||
.selectable = 1u,
|
||||
.edit_capable = 1u,
|
||||
.key_intercept = 1u,
|
||||
.top_y = UI_LIST_TOP,
|
||||
.pitch = UI_ROW_PITCH,
|
||||
.band_h = UI_ROW_BAND_H,
|
||||
.text_dy = 0,
|
||||
.left_x = UI_LIST_LEFT_X,
|
||||
.right_x = UI_LIST_RIGHT_X,
|
||||
.rows_per_page = UI_LIST_ROWS,
|
||||
.row_count = 9u,
|
||||
.init_cb = pg_init,
|
||||
.count_cb = pg_count,
|
||||
.refresh = pg_refresh,
|
||||
.draw_cb = pg_draw,
|
||||
.key_cb = pg_key,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 模块名称:UI Page calib (遥测系数内容页对外声明)
|
||||
* 模块功能:供生成物 ui_page_registry.c 引用的页面描述符声明
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-27 DTU 项目组 创建(道F task-7)
|
||||
*/
|
||||
#ifndef UI_PAGE_CALIB_H
|
||||
#define UI_PAGE_CALIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ui_content_page.h"
|
||||
|
||||
extern const content_page_def_t ui_page_calib_desc;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* UI_PAGE_CALIB_H */
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 模块名称:UI Page deadband (遥测死区内容页)
|
||||
* 模块功能:三遥设置 → 遥测死区 叶子(#P 受密码保护)的内容页——9 行数值表,
|
||||
* 每页 8 行自动分页;本文件零 App include:行表现状(值/粗守域/量纲/
|
||||
* 小数位)逐行来自 ui_data.h 通道五注入表,写与落盘确认全经
|
||||
* ui_w_ytview 视图模型(状态机/反显/徽标在那一侧,两页共用一份)——
|
||||
* 本文件只是标准骨架几何令牌接进框架的薄跳板,改一次口径两页一致
|
||||
* 两态一页:123 修改权限 → 可编辑(编辑器 + 保存行 = 落盘确认);000 查看权限
|
||||
* → 只读(attach 内不绑 commit → 无保存行、key_cb 整页交回框架,
|
||||
* 行为与"没有 key_cb"逐字等价,整定四页同一条分派纪律)
|
||||
* 徽标说明:本页**不画**"未标定"——死区数值来自 [run_par] 档、与两点标定无关,
|
||||
* 挂不相干的告警等于稀释真告警(ui_w_ytview.h 同注)
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-27 DTU 项目组 创建(道F task-7;判据 docs/任务规划/UI补齐.md
|
||||
* §2 第 4 行「改死区能改变 104 上送门限 / 改 k 后
|
||||
* 页面值按比例变化 / 只走既有落盘链不加第二真源」)
|
||||
*/
|
||||
/* 头文件包含区 */
|
||||
#include "ui_page_deadband.h"
|
||||
|
||||
#include "ui_tokens.h"
|
||||
#include "ui_w_setlist.h"
|
||||
#include "ui_w_ytview.h"
|
||||
|
||||
/* 静态变量定义区 */
|
||||
|
||||
/* 本页定值表实例(组件状态独占一份,勿跨页共用——ui_w_setlist 实例化约定) */
|
||||
static ui_w_setlist_t s_sl;
|
||||
|
||||
/* 函数定义区 */
|
||||
|
||||
/*
|
||||
* 函数功能:开页绑视图(行表现状逐行现取 + 两态分派都在视图模型内;接 init_cb)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:框架开页链内(本次进入权限已装载,时序依据见 ui_page_view_param.c
|
||||
* 同位置注释:框架先装权限再调 init_cb)
|
||||
*/
|
||||
static void pg_init(void) {
|
||||
ui_w_ytview_attach_db(&s_sl);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:总行数(数据行现给 + 可编辑态才有保存行;接 count_cb)
|
||||
* 入口参数:无
|
||||
* 返回值:总行数 uint16_t
|
||||
* 限定条件:框架绘制/翻页链内
|
||||
*/
|
||||
static uint16_t pg_count(void) {
|
||||
return ui_w_setlist_count(&s_sl);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:逐行取数(接 refresh;行现状在视图模型里现取,本层无缓存)
|
||||
* 入口参数:a_row / a_name / a_value - 契约见 ui_content_page.h
|
||||
* 返回值:无
|
||||
* 限定条件:框架绘制链内
|
||||
*/
|
||||
static void pg_refresh(uint16_t a_row, char *a_name, char *a_value) {
|
||||
ui_w_setlist_refresh(&s_sl, a_row, a_name, a_value);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:自绘层(编辑光标反显 + 写被拒行反显)
|
||||
* 入口参数:a_layer / a_clip - 契约见 ui_content_page.h
|
||||
* 返回值:无
|
||||
* 限定条件:框架绘制链内,内置区块画完之后
|
||||
*/
|
||||
static void pg_draw(lv_layer_t *a_layer, const lv_area_t *a_clip) {
|
||||
ui_w_setlist_draw(&s_sl, a_layer, a_clip);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:按键转发(接 key_cb)
|
||||
* 入口参数:a_key - LVGL 键值 lv_key_t
|
||||
* 返回值:true - 本页已消费;false - 交回框架(↑↓ 移选、← 关页)
|
||||
* 限定条件:描述符 edit_capable=1 + key_intercept=1(编辑中 ←→ 是移数字光标,
|
||||
* 必须先给页面,否则框架把 ← 当关页——整定四页同款实证)
|
||||
* 函数说明:第一条门 = 只读进入(未绑 commit)整页交回框架,与"本页没有
|
||||
* key_cb"逐字等价(黄金参考只读态为证);其余分发规则全在组件
|
||||
*/
|
||||
static bool pg_key(lv_key_t a_key) {
|
||||
if (s_sl.commit == NULL) {
|
||||
return false;
|
||||
}
|
||||
return (ui_w_setlist_key(&s_sl, a_key) != 0u);
|
||||
}
|
||||
|
||||
/*
|
||||
* 页面描述符(外部链接:生成物 ui_page_registry.c 按菜单树 #page=deadband 绑定引用)
|
||||
* 列表页骨架 + 可编辑 + ← 拦截;band_h 必须 >0(选中行黑字白底反显,同四整定页);
|
||||
* row_count 给静态上限(9 数据行 + 保存行),行数运行时以注入表现报为准
|
||||
*/
|
||||
const content_page_def_t ui_page_deadband_desc = {
|
||||
.id = "deadband",
|
||||
.title = "遥测死区",
|
||||
.selectable = 1u,
|
||||
.edit_capable = 1u,
|
||||
.key_intercept = 1u,
|
||||
.top_y = UI_LIST_TOP,
|
||||
.pitch = UI_ROW_PITCH,
|
||||
.band_h = UI_ROW_BAND_H,
|
||||
.text_dy = 0,
|
||||
.left_x = UI_LIST_LEFT_X,
|
||||
.right_x = UI_LIST_RIGHT_X,
|
||||
.rows_per_page = UI_LIST_ROWS,
|
||||
.row_count = 10u,
|
||||
.init_cb = pg_init,
|
||||
.count_cb = pg_count,
|
||||
.refresh = pg_refresh,
|
||||
.draw_cb = pg_draw,
|
||||
.key_cb = pg_key,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 模块名称:UI Page deadband (遥测死区内容页对外声明)
|
||||
* 模块功能:供生成物 ui_page_registry.c 引用的页面描述符声明
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-27 DTU 项目组 创建(道F task-7)
|
||||
*/
|
||||
#ifndef UI_PAGE_DEADBAND_H
|
||||
#define UI_PAGE_DEADBAND_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ui_content_page.h"
|
||||
|
||||
extern const content_page_def_t ui_page_deadband_desc;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* UI_PAGE_DEADBAND_H */
|
||||
@@ -10,9 +10,11 @@
|
||||
#include "ui_page_registry.h"
|
||||
|
||||
#include "ui_page_ac.h"
|
||||
#include "ui_page_calib.h"
|
||||
#include "ui_page_clear.h"
|
||||
#include "ui_page_clock.h"
|
||||
#include "ui_page_dc.h"
|
||||
#include "ui_page_deadband.h"
|
||||
#include "ui_page_device_info.h"
|
||||
#include "ui_page_net_cfg.h"
|
||||
#include "ui_page_password.h"
|
||||
@@ -39,6 +41,8 @@ const ui_page_entry_t g_ui_page_map[UI_PAGE_MAP_COUNT] = {
|
||||
{ 14u, &ui_page_view_set_desc }, /* node 14 定值 */
|
||||
{ 15u, &ui_page_view_word_desc }, /* node 15 控制字 */
|
||||
{ 16u, &ui_page_view_plate_desc }, /* node 16 软压板 */
|
||||
{ 18u, &ui_page_deadband_desc }, /* node 18 遥测死区 */
|
||||
{ 19u, &ui_page_calib_desc }, /* node 19 遥测系数 */
|
||||
{ 24u, &ui_page_clock_desc }, /* node 24 时钟设置 */
|
||||
{ 25u, &ui_page_reset_desc }, /* node 25 强制复归 */
|
||||
{ 27u, &ui_page_clear_desc }, /* node 27 清除记录 */
|
||||
|
||||
@@ -25,7 +25,7 @@ typedef struct {
|
||||
const content_page_def_t *desc; /* 页面描述符(Flash const) */
|
||||
} ui_page_entry_t;
|
||||
|
||||
#define UI_PAGE_MAP_COUNT 19u /* 绑定了内容页的叶子数 */
|
||||
#define UI_PAGE_MAP_COUNT 21u /* 绑定了内容页的叶子数 */
|
||||
|
||||
extern const ui_page_entry_t g_ui_page_map[UI_PAGE_MAP_COUNT];
|
||||
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* 模块名称:UI 遥测整定视图模型实现(ui_w_ytview)
|
||||
* 模块功能:死区/系数两张页的行表填充(注入表现状 → 显示域)、逐行写转发、
|
||||
* 落盘确认状态机、越界行反显与未标定徽标——对外形状与纪律见配对头文件
|
||||
* (两态分派 / 无叠层 / 保存行=确认 / 两层守卫互不掩盖 四条都在头注)
|
||||
* 编辑即生效的取舍(如实记在这里,评审时不靠口头解释):定值四页有"叠层 + 整表
|
||||
* 封存 + ESC 弃稿",本两页没有——[run_par]/[adc_cal] 的语义 setter 就是
|
||||
* 执行侧写入口(写值与置脏不可拆),页面再造一份叠层只会造出"屏显值与
|
||||
* RAM 副本不一致"的第二个视图。后果是**按下 ENTER 提交那一行即刻生效**、
|
||||
* 退出页面不回滚;"哪些已生效"由每行的显示值本身说明(读的就是真源),
|
||||
* "落没落盘"由保存行说明。v1 认可这条语义(Lead 裁定 task-7 派发单),
|
||||
* 要"草稿/弃稿"得改注入表形状(叠层 + 批量提交),不在本批
|
||||
* 状态机(每视图独立一份,两页互不干扰):
|
||||
* ENTER --保存行按键--> 脏位在 ? WAIT : OK(无改动/已落 = OK,与定值写
|
||||
* 路径"空请求不消费队列位、直接 OK"同口径);WAIT 期间脏位归零 → 锁存
|
||||
* OK(此后他人再改重新置脏**不回退**显示——"我这次动作的结果"与"档位
|
||||
* 此刻的状态"分开,clear/clock 页先例);任一写被拒(执行侧返 -1)→
|
||||
* 粘滞 ERR + 该行反显,直到下次写成功或再按保存行重新评估
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-27 DTU 项目组 创建(道F task-7)
|
||||
*/
|
||||
|
||||
/* 头文件包含区 */
|
||||
#include <string.h>
|
||||
|
||||
#include "ui_w_ytview.h"
|
||||
|
||||
#include "ui_data.h"
|
||||
#include "ui_tokens.h"
|
||||
|
||||
/* 私有宏定义区 */
|
||||
|
||||
/* 保存行四态的内部编码(对外映射成 ui_w_setlist_st_t,不直接外露——枚举值序
|
||||
* 被各页钉着,这里换底层编码不影响组件) */
|
||||
#define YT_ST_ENTER 0u /* 初始 / 已作废:值列 ENTER */
|
||||
#define YT_ST_WAIT 1u /* 按过保存、脏位仍在:WAIT */
|
||||
#define YT_ST_OK 2u /* 脏位归零(或按时无脏):OK,锁存 */
|
||||
#define YT_ST_ERR 3u /* 最近一次写被执行侧拒:粘滞 ERR */
|
||||
|
||||
/* 徽标右界与文案:右界 141 的理由、测宽传 strlen 的版本坑,均沿用整定四页
|
||||
* 徽标的既有实测结论(ui_w_setview.c 的 draw_cal_badge 头注),不重新发明 */
|
||||
#define YT_BADGE_RIGHT_X 141
|
||||
#define YT_CAL_BADGE "未标定"
|
||||
|
||||
/* 私有变量定义区 */
|
||||
|
||||
/* 行名表(Flash):下标即执行侧 idx,注释是唯一的对表处——
|
||||
* 死区 0..7 = 电气语义通道 UA..IN、8 = 频率(104 地址映射 0x8030~ 同序);
|
||||
* 系数 0..7 = 电气语义通道 UA..IN(adc_semantic_t 前缀段) */
|
||||
static const char *const k_db_name[UI_DATA_YT_DB_N] = {
|
||||
"UA死区", "UB死区", "UC死区", "UN死区",
|
||||
"IA死区", "IB死区", "IC死区", "IN死区",
|
||||
"Hz死区" /* 频率死区行的上屏名用 Hz(本仓既有写法,交流量页 Hz 行同词):
|
||||
* "率"不在 183 字子集里,font-charset 机器先抓出来——改文案不改字库;
|
||||
* 这一行的小数位数与量纲由执行侧现给(0.5 Hz / decimals 3 / UNIT_HZ),
|
||||
* 名字里的 Hz 只是让现场知道该行量纲,不参与取数 */
|
||||
};
|
||||
static const char *const k_cal_name[UI_DATA_YT_CH_N] = {
|
||||
"UA系数", "UB系数", "UC系数", "UN系数",
|
||||
"IA系数", "IB系数", "IC系数", "IN系数"
|
||||
};
|
||||
/* 量纲代码 → 屏上串:µV 用 ASCII "uV"(字库无希腊小写 mu,缺字即白框——
|
||||
* font-charset 只保汉字子集,ASCII 字形里本来也没有 µ) */
|
||||
static const char *const k_unit_str[UI_DATA_YT_UNIT_UV + 1u] = {
|
||||
"V", "A", "Hz", "uV"
|
||||
};
|
||||
/* 显示域倍率(decimals 契约 0..3,与 ui_w_setlist 行表同口径) */
|
||||
static const int32_t k_p10[4] = { 1, 10, 100, 1000 };
|
||||
|
||||
typedef struct {
|
||||
ui_w_setlist_row_t rows[UI_DATA_YT_DB_N]; /* 按较大的死区表定容;系数表只占前 8 槽 */
|
||||
uint16_t badmap; /* 写被拒行反显位图(≤16 行) */
|
||||
uint8_t st; /* YT_ST_*,锁存的保存结果 */
|
||||
uint8_t n; /* 本次开页实际建成的行数 */
|
||||
} yt_view_t;
|
||||
|
||||
static yt_view_t s_db;
|
||||
static yt_view_t s_cal;
|
||||
|
||||
/* 函数定义区 */
|
||||
|
||||
/*
|
||||
* 函数功能:工程量 → 显示域整数(四舍五入,半程进位朝远离 0 侧)
|
||||
* 入口参数:v - 工程量 float;d - 小数位 0..3 uint8_t
|
||||
* 返回值:显示域整数 int32_t
|
||||
* 限定条件:v 与行 min/max 同来自执行侧,粗守上限量级(系数 1e9 µV)在 d=0 时
|
||||
* 仍落在 int32 内——这是系数行显示小数位取 0 的硬理由(见装配层建表处),
|
||||
* 不是排版偏好
|
||||
*/
|
||||
static int32_t yt_scale(float a_v, uint8_t a_d) {
|
||||
float p = (float)k_p10[a_d];
|
||||
float s = a_v * p;
|
||||
|
||||
return (int32_t)((s >= 0.0f) ? (s + 0.5f) : (s - 0.5f));
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:逐行从注入表建行表(名/量纲/小数位/min/max 全部现给,本模块零范围字面量)
|
||||
* 入口参数:v - 目标视图 yt_view_t *;want - 执行侧报出的行数 uint8_t;
|
||||
* names - 行名表 const char *const *;rowfn - 对应注入口的读函数
|
||||
* 返回值:无
|
||||
* 限定条件:attach 链内(开页)
|
||||
* 函数说明:读到第 i 行失败就停在 i 行(**不补空行、不跳过**):半张表比空表更
|
||||
* 危险——用户会以为下面的行"没有这回事"。行数超过行槽数组上限的部分
|
||||
* 同样不显示(正常不可能发生:want 已由调用方钳到契约值,这里双保险)
|
||||
*/
|
||||
static void yt_fill(yt_view_t *a_v, uint8_t a_want, const char *const *a_names,
|
||||
int (*a_rowfn)(uint8_t, ui_data_yt_row_t *)) {
|
||||
uint8_t i;
|
||||
|
||||
a_v->n = 0u;
|
||||
for (i = 0u; (i < a_want) && (i < (uint8_t)UI_DATA_YT_DB_N); i++) {
|
||||
ui_data_yt_row_t r;
|
||||
uint8_t d;
|
||||
|
||||
if (a_rowfn(i, &r) != 0) {
|
||||
break;
|
||||
}
|
||||
d = (r.decimals <= 3u) ? r.decimals : 3u; /* 组件契约 0..3,越界钳住而不是读脏倍率 */
|
||||
a_v->rows[a_v->n].name = a_names[i];
|
||||
a_v->rows[a_v->n].id = i;
|
||||
a_v->rows[a_v->n].kind = UI_W_SETLIST_KIND_NUM;
|
||||
a_v->rows[a_v->n].decimals = d;
|
||||
a_v->rows[a_v->n].min = yt_scale(r.lo, d);
|
||||
a_v->rows[a_v->n].max = yt_scale(r.hi, d);
|
||||
a_v->rows[a_v->n].opts = NULL;
|
||||
a_v->rows[a_v->n].unit = (r.unit <= UI_DATA_YT_UNIT_UV)
|
||||
? k_unit_str[r.unit] : NULL;
|
||||
a_v->n++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:写结果记账(成功/被拒两条腿共一个入口,防两页写两遍规则写漏)
|
||||
* 入口参数:v - 视图 yt_view_t *;id - 行下标 uint8_t;rc - 注入口返回值 int
|
||||
* 返回值:无
|
||||
* 限定条件:set 回调链内(uiTask 按键)
|
||||
* 函数说明:拒绝 → 该行反显置位 + 粘滞 ERR;成功 → 只清**这一行**的反显(别的
|
||||
* 行还欠着被执行侧拒的账,不陪它一起销)+ 作废上次保存结果(改了任何
|
||||
* 一位,上一次的 OK/ERR 都不该再挂屏,clock 页同一条纪律)
|
||||
*/
|
||||
static void yt_note_write(yt_view_t *a_v, uint8_t a_id, int a_rc) {
|
||||
if (a_rc != 0) {
|
||||
a_v->badmap = (uint16_t)(a_v->badmap | (uint16_t)(1u << a_id));
|
||||
a_v->st = YT_ST_ERR;
|
||||
} else {
|
||||
a_v->badmap = (uint16_t)(a_v->badmap & (uint16_t)~(1u << a_id));
|
||||
a_v->st = YT_ST_ENTER;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:保存行按键(不是"投递整表"——值早在逐行 ENTER 时写进副本并置脏,
|
||||
* 这里只做一次落盘确认的起点)
|
||||
* 入口参数:v - 视图 yt_view_t *
|
||||
* 返回值:无
|
||||
* 限定条件:可编辑态、保存行 ENTER;uiTask 按键链
|
||||
* 函数说明:先清越界反显再评估(按保存 = "我知道刚才被拒了,看新一轮结果");
|
||||
* 脏位仍在 → WAIT 开始锁存观察;无脏 → 直接 OK——两种都如实:真机
|
||||
* defaultTask 每 50 ms 无条件落一遍,WAIT 窗口毫秒级,**模拟器假件刻意
|
||||
* 不自动清脏**(由无头 pump 清),否则 WAIT 态永远不会被像素回归覆盖,
|
||||
* 黄金就成了只钉住最好看那一帧(Lead 裁定④的原文理由)
|
||||
*/
|
||||
static void yt_commit(yt_view_t *a_v) {
|
||||
a_v->badmap = 0u;
|
||||
a_v->st = (ui_data_yt_pending() != 0u) ? YT_ST_WAIT : YT_ST_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:保存行值列回读(组件每次绘制调一次;WAIT → OK 的边沿在这里收尾)
|
||||
* 入口参数:v - 视图 yt_view_t *
|
||||
* 返回值:组件态枚举 ui_w_setlist_st_t
|
||||
* 限定条件:绘制链内;只读视图组件不会调到这里(未绑 state)
|
||||
* 函数说明:锁存语义 = 只前进不回退:WAIT 期间看到脏位归零就落 OK;此后他人
|
||||
* (104 选择段)再改重新置脏也**不**把 OK 拽回 WAIT——用户按保存那次
|
||||
* 动作确实落盘了,档位的新脏是别人的动作,不归本次显示(ui_data.h
|
||||
* pending 成员注的两条口径,第②条落在这三行代码上)
|
||||
*/
|
||||
static ui_w_setlist_st_t yt_state(yt_view_t *a_v) {
|
||||
if ((a_v->st == YT_ST_WAIT) && (ui_data_yt_pending() == 0u)) {
|
||||
a_v->st = YT_ST_OK;
|
||||
}
|
||||
switch (a_v->st) {
|
||||
case YT_ST_WAIT:
|
||||
return UI_W_SETLIST_ST_WAIT;
|
||||
case YT_ST_OK:
|
||||
return UI_W_SETLIST_ST_OK;
|
||||
case YT_ST_ERR:
|
||||
return UI_W_SETLIST_ST_FAIL;
|
||||
default:
|
||||
return UI_W_SETLIST_ST_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
/* 函数功能:某行是否需要越界反显(写被拒的账挂在这行值上,同整定页越界反显
|
||||
* 的视觉语言) 返回值:true = 反显 bool */
|
||||
static bool yt_bad(const yt_view_t *a_v, uint16_t a_row) {
|
||||
if (a_row >= 16u) {
|
||||
return false;
|
||||
}
|
||||
return ((a_v->badmap >> a_row) & 0x1u) != 0u;
|
||||
}
|
||||
|
||||
/* ── 死区视图的回调组(id = 执行侧 idx 直通) ────────────────────────────── */
|
||||
|
||||
static int32_t yt_db_get(uint8_t a_id) {
|
||||
ui_data_yt_row_t r;
|
||||
|
||||
if ((a_id >= s_db.n) || (ui_data_yt_db_row(a_id, &r) != 0)) {
|
||||
return 0; /* 取不到现状的行显示 0 而不是旧缓存:本组件没有缓存可回退,
|
||||
0 配 "--" 之外的解释都不诚实;attach 时读失败该行根本不存在 */
|
||||
}
|
||||
return yt_scale(r.val, r.decimals);
|
||||
}
|
||||
|
||||
static void yt_db_write(uint8_t a_id, int32_t a_val) {
|
||||
float eng;
|
||||
if (a_id >= s_db.n) {
|
||||
return;
|
||||
}
|
||||
eng = (float)a_val / (float)k_p10[s_db.rows[a_id].decimals];
|
||||
yt_note_write(&s_db, a_id, ui_data_yt_db_set(a_id, eng));
|
||||
}
|
||||
|
||||
static void yt_db_commit(void) { yt_commit(&s_db); }
|
||||
static ui_w_setlist_st_t yt_db_state(void) { return yt_state(&s_db); }
|
||||
static bool yt_db_bad(uint16_t a_row) { return yt_bad(&s_db, a_row); }
|
||||
|
||||
/* ── 系数视图的回调组 ────────────────────────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* 函数功能:读系数行当前值(显示域;口径同 yt_db_get,现状现取零缓存)
|
||||
* 入口参数:a_id - 行下标(= 执行侧语义通道序) uint8_t
|
||||
* 返回值:工程量 × 10^decimals int32_t
|
||||
* 限定条件:组件取数回调链内(绘制)
|
||||
*/
|
||||
static int32_t yt_cal_get(uint8_t a_id) {
|
||||
ui_data_yt_row_t r;
|
||||
|
||||
if ((a_id >= s_cal.n) || (ui_data_yt_cal_row(a_id, &r) != 0)) {
|
||||
return 0;
|
||||
}
|
||||
return yt_scale(r.val, r.decimals);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:写一行系数(显示域 → µV/码工程量后转注入表;结果记账同 yt_db_write)
|
||||
* 入口参数:a_id - 行下标 uint8_t;a_val - 显示域新值 int32_t
|
||||
* 返回值:无(拒绝经 yt_note_write 上屏:行反显 + 保存行 ERR)
|
||||
* 限定条件:组件编辑提交回调链内(可编辑态)
|
||||
*/
|
||||
static void yt_cal_write(uint8_t a_id, int32_t a_val) {
|
||||
float uv;
|
||||
if (a_id >= s_cal.n) {
|
||||
return;
|
||||
}
|
||||
uv = (float)a_val / (float)k_p10[s_cal.rows[a_id].decimals];
|
||||
yt_note_write(&s_cal, a_id, ui_data_yt_cal_k_set(a_id, uv));
|
||||
}
|
||||
|
||||
static void yt_cal_commit(void) { yt_commit(&s_cal); }
|
||||
static ui_w_setlist_st_t yt_cal_state(void) { return yt_state(&s_cal); }
|
||||
static bool yt_cal_bad(uint16_t a_row) { return yt_bad(&s_cal, a_row); }
|
||||
|
||||
/* ── 对外 attach 与徽标 ──────────────────────────────────────────────────── */
|
||||
|
||||
/*
|
||||
* 函数功能:死区视图绑表(两态在同一函数里分派一次,页面只调这一个口)
|
||||
* 入口参数:a_inst - 页面持有的定值表实例 ui_w_setlist_t *
|
||||
* 返回值:无
|
||||
* 限定条件:页面 init_cb 内(本次进入权限已装载)
|
||||
* 函数说明:行数**执行侧现给**(ui_data_yt_db_n):未注入 = 0 行 = 空表页,
|
||||
* 一个像素都不画假数据;只读进入不绑 commit/state——组件因此既不画
|
||||
* 保存行也放开不了编辑(set 契约"必填",绑的就是真写口,只读态组件
|
||||
* 保证不会调它,与整定四页同一绑法同一理由)
|
||||
*/
|
||||
void ui_w_ytview_attach_db(ui_w_setlist_t *a_inst) {
|
||||
uint8_t want;
|
||||
|
||||
if (a_inst == NULL) {
|
||||
return;
|
||||
}
|
||||
want = ui_data_yt_db_n();
|
||||
if (want > (uint8_t)UI_DATA_YT_DB_N) {
|
||||
want = (uint8_t)UI_DATA_YT_DB_N;
|
||||
}
|
||||
s_db.badmap = 0u;
|
||||
s_db.st = YT_ST_ENTER;
|
||||
yt_fill(&s_db, want, k_db_name, ui_data_yt_db_row);
|
||||
|
||||
(void)memset(a_inst, 0, sizeof(*a_inst));
|
||||
a_inst->rows = s_db.rows;
|
||||
a_inst->count = s_db.n;
|
||||
a_inst->get = yt_db_get;
|
||||
a_inst->set = yt_db_write;
|
||||
a_inst->bad = yt_db_bad;
|
||||
if (ui_content_page_is_editable() != false) {
|
||||
a_inst->commit = yt_db_commit;
|
||||
a_inst->state = yt_db_state;
|
||||
}
|
||||
ui_w_setlist_reset(a_inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:系数视图绑表(形状与 attach_db 逐字同构;两视图各持一份状态机,
|
||||
* 死区页的 WAIT 不会串门到系数页)
|
||||
* 入口参数:a_inst - 页面持有的定值表实例 ui_w_setlist_t *
|
||||
* 返回值:无
|
||||
* 限定条件:页面 init_cb 内
|
||||
*/
|
||||
void ui_w_ytview_attach_cal(ui_w_setlist_t *a_inst) {
|
||||
uint8_t want;
|
||||
|
||||
if (a_inst == NULL) {
|
||||
return;
|
||||
}
|
||||
want = ui_data_yt_cal_n();
|
||||
if (want > (uint8_t)UI_DATA_YT_CH_N) {
|
||||
want = (uint8_t)UI_DATA_YT_CH_N;
|
||||
}
|
||||
s_cal.badmap = 0u;
|
||||
s_cal.st = YT_ST_ENTER;
|
||||
yt_fill(&s_cal, want, k_cal_name, ui_data_yt_cal_row);
|
||||
|
||||
(void)memset(a_inst, 0, sizeof(*a_inst));
|
||||
a_inst->rows = s_cal.rows;
|
||||
a_inst->count = s_cal.n;
|
||||
a_inst->get = yt_cal_get;
|
||||
a_inst->set = yt_cal_write;
|
||||
a_inst->bad = yt_cal_bad;
|
||||
if (ui_content_page_is_editable() != false) {
|
||||
a_inst->commit = yt_cal_commit;
|
||||
a_inst->state = yt_cal_state;
|
||||
}
|
||||
ui_w_setlist_reset(a_inst);
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:系数页"未标定"徽标(头文件里写清了死区页为何不调、右界为何取 141)
|
||||
* 入口参数:a_layer - 绘制层 lv_layer_t *
|
||||
* a_clip - 重绘区 const lv_area_t *
|
||||
* 返回值:无
|
||||
* 限定条件:系数页 draw_cb 内;cal_blocked() 为假时一个像素都不落
|
||||
*/
|
||||
void ui_w_ytview_draw_cal_badge(lv_layer_t *a_layer, const lv_area_t *a_clip) {
|
||||
lv_area_t area;
|
||||
lv_draw_label_dsc_t ld;
|
||||
const char *txt = YT_CAL_BADGE;
|
||||
|
||||
if (ui_data_yt_cal_blocked() == 0u) {
|
||||
return;
|
||||
}
|
||||
area.x2 = YT_BADGE_RIGHT_X;
|
||||
area.y1 = UI_TITLE_Y;
|
||||
area.y2 = UI_TITLE_Y + UI_TEXT_BAND_H;
|
||||
/* 该版本 lv_text_get_width:长度必须传 strlen(传 -1/0 死循环或返 0) */
|
||||
area.x1 = YT_BADGE_RIGHT_X
|
||||
- (int32_t)lv_text_get_width(txt, (uint32_t)strlen(txt), LV_FONT_DEFAULT, 0);
|
||||
if ((area.x1 > a_clip->x2) || (a_clip->x1 > area.x2) ||
|
||||
(area.y1 > a_clip->y2) || (a_clip->y1 > area.y2)) {
|
||||
return;
|
||||
}
|
||||
lv_draw_label_dsc_init(&ld);
|
||||
ld.base.layer = a_layer;
|
||||
ld.text = txt;
|
||||
ld.text_static = 1u;
|
||||
ld.color = UI_COL_FG;
|
||||
ld.ofs_x = 0;
|
||||
ld.ofs_y = 0;
|
||||
lv_draw_label(a_layer, &ld, &area);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 模块名称:UI 遥测整定视图模型(ui_w_ytview)
|
||||
* 模块功能:两张遥测整定页(遥测死区 9 行 / 遥测系数 8 行)的**共用视图模型**——
|
||||
* 行表现状从 ui_data.h 通道五注入表逐行现取(工程量 + 粗守域 + 量纲 +
|
||||
* 显示小数位),换算成 ui_w_setlist 的显示域(工程量 × 10^decimals),
|
||||
* 并把"写一行 / 落盘确认 / 越界反显 / 未标定徽标"四件事收在一处,供两张
|
||||
* 页面薄跳板(ui_page_deadband / ui_page_calib)共用——两页共用一份状态机
|
||||
* 与换算,与 ui_w_setview"行表只有一份,界内界外就不会两套口径"同一条理由
|
||||
* 与整定四页(ui_w_setview 的写方向)**故意不同形**,三处差别都写在 .c 的函数头:
|
||||
* ① 无工作叠层:死区/系数的真源本身就是 RAM 副本,语义 setter 是执行侧
|
||||
* 写入口(写值与置脏同一函数内发生),"编辑即生效、ESC 不回滚"是本档
|
||||
* 语义(与定值"攒一批整表封存"不同,取舍见 .c 文件头);
|
||||
* ② 保存行不是"投递整表",而是**落盘确认**:值在逐行 ENTER 时已经写进
|
||||
* 副本并置脏,保存行按键只锁存"我这次动作的结果"(脏位归零 = 整档已
|
||||
* 外科保存成功,判据 = ui_data_yt_pending(),与 104 留痕同一条
|
||||
* "先 peek 再算成功",不另发明);
|
||||
* ③ 没有 LOCK 态:本档不受定值校验和闭锁管辖;"未标定"(cal_blocked)
|
||||
* 只画徽标、不禁编辑——改系数恰恰是走向标定的动作,禁了就是把路堵死
|
||||
* 值域诚实性:行 min/max **逐行来自执行侧现给**(合法域唯一出口 run_par_db_range /
|
||||
* 粗守上限 RUN_PAR_K_MAX_UV)——本模块不出现任何一个范围数字字面量,
|
||||
* 抄一份就是卡片判据要拦的第二真源;µV 真带(执行段裁)没有公开访问器,
|
||||
* 本模块**不**补一份假的(task-43 收口前屏上只有粗守这一层,真带拒的值
|
||||
* 经 set 返回 -1 → 行反显 + 保存行 ERR,两层守卫互不掩盖)
|
||||
* 分层:只依赖 Drivers/BSP/UI 同层(ui_data.h 注入表 + ui_w_setlist/ui_w_editor),
|
||||
* 零 App include(layer-deps + 模拟器链接期两条老理由,见 ui_data.h 通道五表注)
|
||||
* 适用平台:STM32F407ZGTx / PC 模拟器(共用源码)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-27
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-27 DTU 项目组 创建(道F task-7;判据 docs/任务规划/UI补齐.md
|
||||
* §2 第 4 行;Lead 裁定:注入表八口形状、结果锁存、
|
||||
* 粗守 + 注释两层守卫、真带访问器归 task-43)
|
||||
*/
|
||||
#ifndef UI_W_YTVIEW_H
|
||||
#define UI_W_YTVIEW_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "ui_w_setlist.h" /* ui_w_setlist_t + lvgl(经 ui_content_page.h) */
|
||||
|
||||
/*
|
||||
* 函数功能:把**死区**视图绑到定值表实例(行表现状逐行现取;两态在内部按
|
||||
* ui_content_page_is_editable() 分派——只读不绑 commit/state,无保存行)
|
||||
* 入口参数:a_inst - 页面持有的实例(static,一页一份) ui_w_setlist_t *
|
||||
* 返回值:无
|
||||
* 限定条件:页面 init_cb 内调用(uiTask 上下文,此时本次进入的权限已装载)
|
||||
* 函数说明:每次开页都重填行表并清历史结果(st/badmap)——上一次开页的 WAIT/OK
|
||||
* 与越界反显都不该带到这一次来(同整定四页"开页清稿"纪律)
|
||||
*/
|
||||
void ui_w_ytview_attach_db(ui_w_setlist_t *a_inst);
|
||||
|
||||
/*
|
||||
* 函数功能:把**系数**视图绑到定值表实例(形状/分派/清稿与 attach_db 同一条路)
|
||||
* 入口参数:a_inst - 页面持有的实例 ui_w_setlist_t *
|
||||
* 返回值:无
|
||||
* 限定条件:页面 init_cb 内调用
|
||||
*/
|
||||
void ui_w_ytview_attach_cal(ui_w_setlist_t *a_inst);
|
||||
|
||||
/*
|
||||
* 函数功能:系数页标题右侧画"未标定"徽标(cal_blocked 的屏上表达)
|
||||
* 入口参数:a_layer - 绘制层 lv_layer_t *
|
||||
* a_clip - 重绘区 const lv_area_t *
|
||||
* 返回值:无
|
||||
* 限定条件:系数页 draw_cb 内(框架内置区块之后);只在 cal_blocked() 为真时落墨
|
||||
* 函数说明:**死区页刻意不调它**——死区数值来自 [run_par] 档、与两点标定无关,
|
||||
* 在那页画"未标定"是把不相关的告警挂到不相干的屏上(徽标是断言)。
|
||||
* 几何右界取 141:与整定四页徽标同一实测理由(标题带装饰右腿占 x 约
|
||||
* 147..158);本标题"遥测系数"4 字到 x 约 104,与徽标左缘仅 1 px 间隙,
|
||||
* 会被读成一句"遥测系数未标定"——该读法语义成立(本页说的就是系数、
|
||||
* 而系数此刻确实没标定过),像素黄金目检时确认过不再挪
|
||||
*/
|
||||
void ui_w_ytview_draw_cal_badge(lv_layer_t *a_layer, const lv_area_t *a_clip);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* UI_W_YTVIEW_H */
|
||||
Reference in New Issue
Block a user