feat(ui-data): ui_data 三通道快照 + ui_data_feed 装配 + SOE 显示名映射
按 docs/UI设计/UI_数据供给设计说明.md 的 D 方案把 App→UI 数据通道落地:
App 组帧、BSP 存帧、页面只读;UI 侧零 App include,薄包装只在两端适配层。
本笔是纯增层、不接任何页面 → 零像素变化(菜单 8 状态 + 内容页 11 页黄金
参考逐字节不变,simulator/tests/ref 无 diff,未刷基线)。
新增
- Drivers/BSP/UI/ui_data.{c,h}:实时量 rt / 参数 par / 装置信息 info 三通道存储
+ 发布序号盖章 + 只读访问。零 App include、零 LVGL include(分层叶子);
读写同在 uiTask 上下文串行 → 无锁、无双缓冲、无 seqlock(契约 §5.1)。
- App/app/ui_data_feed.{c,h}:快照唯一填充者。rt/info 每 200 ms 一帧,par 只在
prot_set_checksum() 变化时重发;首轮立即发布,免启动初期闪一下 "--"。
- Drivers/BSP/UI/ui_w_soe.{c,h}:SOE 二进制记录的显示名映射与注入式 reader
契约。未标定闭锁=7 / 标定复归=8 已进表——编号按 App/log/soe_rec.h 与
App/algo/prot_elm.h 现场数过(类型 1..8 + 哨兵 9、元件 0..8、ELM_NONE=0xFF、
FLG_IV=0x80),不采信文档与消息转述。reader 备好但两端未绑定:落盘侧还
没有只读 API(Lead 已裁该 API 归下一工作项),未注入按"无记录"空态渲染。
量纲诚实性(契约 §4.1 同步改成真值口径,不再抄旧码值表述)
- eng 不写"0 = 原始码值":标定层 ROM 系数是引脚侧 LSB,采样环折算出来的就是
ADC 引脚电压(承诺边界 docs/任务规划/数据可信.md §2.6);电气 8 路
adc_cal_flags() 的 VALID 全置位才升 eng=1 二次值——整帧共用一个 eng,只要
还有一路是引脚电压就不能贴"二次值"标签,宁可整体保守。
- TEMP 不参与线性标定 → 恒带新增品质位 UI_DATA_Q_RAW=0x20,页面不挂单位。
- UB/UC/IB/IC/IN 无基波相量实例(V0.1 削顶)→ Q_IV|Q_OV;未满一周波 → Q_NT。
- 谐波另开 ph[] 相量实例槽带:yc 的语义通道下标容不下同通道的二次/五次谐波。
接线与双端注册
- App/task/ui_task.c:ui_menu_init 之前 ui_data_init + 首帧发布;主循环每轮
ui_data_feed_tick() 排在 ui_port_step 之前(本轮渲染用本轮刚发布的帧)。
- simulator/main.c:发布固定合成帧(无头与窗口两个初始化点,先于建页);
定值不手抄 40 个数,改为链入 App/algo/prot_set.c 取出厂默认(抄表等于造
第二真相);build_date 用固定串而非 __DATE__,否则跨天重建会把装置信息页
黄金参考刷掉,而黄金参考是判据本身。
- 注册:uvprojx 加 3 条(App 组 ui_data_feed.c + UI 组 ui_data.c/ui_w_soe.c)与
simulator/CMakeLists.txt;ui_data_feed.c 不进模拟器 → check_registration.py 的
SIM_EXEMPT 加一行(App/app/app_main.c 先例,已报备)。
主机单测与钩子
- test/host/ui_data:82 断言全绿。钉三类事:快照语义(首帧前读 -1、序号由本层
盖章、三通道互不串台、NULL 防御、init 幂等复位);跨层不透明下标现场一致
(yc 序 = adc_semantic_t、q10 序 = prot_set_id_t,同一 TU 里 include 两侧头);
SOE 编号与名字表现场一致(八类事件都有名、采样/标定四个词互不相同、
名长不超列宽、越界回占位)。
- .pre-commit-config.yaml 加 ui-data-unit-test 钩子(第 43 个,Lead 已批自加)。
- scripts/id_registry.json 建族 TC-HOST-UIDATA(属主登记处 = 单测目录,同
TC-HOST-ADCCAL 先例;不建族则新号被编号门禁判"未登记前缀")。
体积(相对 HEAD 台账 Code 215744 / RO 21396 / RW 1224 / ZI 131984)
- Code +1272 B、RO +108 B(字库 4 字,见上一笔)、RW +24 B、ZI +696 B
→ ER_IROM1 238520 B(22.7%)、RW_IRAM1 76504 B(58.4%,台账 57.8%)。
- .map 逐项:ui_data.o 488 B(Code 184/RW 12/ZI 476)、ui_data_feed.o 221 B
(1034/12/209);LVGL 池 0 增量;ui_w_soe 未接线不进镜像(.map 无其符号)。
- 台架未跑(本笔零像素、零接线):首帧时刻、200 ms 刷新、eng 升档与遥信
位图真值全部留"待台架实测",接线完成后请 Lead 排窗。
遗留与风险(随回报进台账)
- 写路径(契约 §6 的 param_request/state)不落:无消费者就是永不执行的死码,
随整定 4 页一起落;届时须先定"UI 整定 vs iec104Task 写 prot_set"的并发口径
(prot_set_set_sealed 的影子区非重入)。
- 公共地址 CA 未下发:固件里 CA 逐帧从 ASDU 头解析,无集中常量可取。
- 遥信位图与 App/protocol/iec104/iec104_pts.c 板端源是两处镜像(该文件位号
写在函数体内未导出,UI 不得 include 协议头),开入采集落地时应并成单源。
- 版本类字段是本文件内的宏(仓库无产品级版本登记表,值沿用页面既有显示
值);软件校验位在拿到构建期 CRC 步骤前一律显示 "--",不抄参考遗留值。
gate: 全量钩子(--all-files)40 项通过;cppcheck/test-cppcheck/coding-standard/
layer-deps(基线仍 3 条)/registration/bsp-err(存量 38 条不变)/font-charset/
id-check(增量无新账)/doc-links/ui-golden/主机单测与覆盖率全绿;唯一红项是
mem-numbers 与 mem-budget——两者只因 Flash/RAM 台账未回填(禁区,归 Lead)。
gate: only mem-budget/mem-numbers pending lead ledger backfill
自查:BOM(Drivers/BSP/UI/* 与 App/task/ui_task.c 保持 EF BB BF,新建 UI 两件
同规范;App/app 与 test/host 无 BOM 与本目录一致);CR = 0 全 LF;U+FFFD = 0;
字符串字面量纯 ASCII(中文只进注释)。
This commit is contained in:
@@ -147,6 +147,7 @@ flowchart TB
|
||||
#define UI_DATA_Q_OV 0x04u /* 越限:超过量程/削顶 */
|
||||
#define UI_DATA_Q_SB 0x08u /* 取代值 */
|
||||
#define UI_DATA_Q_BL 0x10u /* 闭锁 */
|
||||
#define UI_DATA_Q_RAW 0x20u /* 原始码值域:val 不是工程量而是转换器码值(TEMP 用) */
|
||||
|
||||
typedef struct {
|
||||
float val; /* 数值;口径由 ui_data_rt_t.eng 决定 */
|
||||
@@ -157,7 +158,8 @@ typedef struct {
|
||||
uint32_t seq; /* 发布序号(单调递增;页面据此判"是否需重建行缓存") */
|
||||
uint32_t tick_ms; /* 发布时刻(tick 域,差值法可比) */
|
||||
uint8_t valid; /* 0 = 尚无首帧(页面显示 "--") */
|
||||
uint8_t eng; /* 0 = 原始码值(未标定)/ 1 = 二次值 / 2 = 一次值 */
|
||||
uint8_t eng; /* 量纲口径:0 = ADC 引脚电压(现状)/ 1 = 二次值 /
|
||||
* 2 = 一次值(预留)——真值语义见下方"要点"第 1 条 */
|
||||
ui_data_yc_t yc[UI_DATA_YC_N];
|
||||
uint64_t yx_bits; /* 遥信位图:bit i = 第 i 点"合/动作" */
|
||||
uint64_t yx_valid; /* 遥信有效位图:bit i = 1 该点有硬件来源 */
|
||||
@@ -170,7 +172,7 @@ typedef struct {
|
||||
|
||||
**要点**:
|
||||
|
||||
- **`eng` 字段是诚实性的关键**:V0.1 未标定,`val` 是 ADC 码值(千级)。页面据此显示"未标定"角标或直接显示码值,**不会把码值当伏特**。标定落地后 `eng` 改 1,页面自动按工程量显示,**无代码改动**。
|
||||
- **`eng` 字段是诚实性的关键**(【2026-10-25 落地注】本条原写"0 = 原始码值(未标定)、`val` 是 ADC 码值(千级)",那是标定落地前的旧口径,已按现场改真值语义):标定层 `adc_cal` 的 ROM 默认系数按**引脚侧 LSB**定死(TPAFE 152.59 µV/码、STM32 805.66 µV/码),采样环折算出来的 `val` 现在是 **ADC 引脚电压 V**——既不是千级码值,也不是端子二次值。承诺边界的依据是 `docs/任务规划/数据可信.md` §2.6:这一波只承诺到"引脚电压","端子二次值"要等变比确认与两点标定。所以页面文案是 `eng=0` → "未标定"、`eng=1` → "二次值",而不是"码值";升档条件写在 `App/app/ui_data_feed.c` 的 `feed_eng()`——**电气 8 路 `adc_cal_flags()` 的 VALID 全置位才升**(整帧共用一个 `eng`,只要还有一路是引脚电压就不能给整帧贴"二次值"标签,宁可整体保守)。TEMP 不参与线性标定 → 该路恒带 `UI_DATA_Q_RAW`,页面据此不挂单位、不与其他路互比。
|
||||
- **字符串只用定长数组**:`clock[20]` 在结构体内,无指针、无生命周期问题(跨层传指针是经典事故源)。
|
||||
- **`yx_valid` 与 `yx_bits` 分开**:无效点不参与显示、不参与变位判断(沿用 104 点表的既有做法,避免"IV 通道抖动成变位")。
|
||||
|
||||
@@ -198,6 +200,21 @@ typedef struct {
|
||||
- **只允许尾部追加字段**,结构体开头保留 `seq/valid`。
|
||||
- 追加字段必须同时升 `UI_DATA_VER`(编译期常量),并在 `ui_data_feed` 里填默认值。
|
||||
- **禁止删除或重排字段**(页面按名字取,重排是静默错位)。
|
||||
### 4.4 通道三:装置信息 `ui_data_info_t`(2026-10-25 落地新增)
|
||||
|
||||
落地时比本文原稿多出一条同构通道,承载**既不属于 200 ms 实时量、也不属于校验和门控参数帧**的那批量:
|
||||
|
||||
| 字段 | 内容 | 为什么不塞进已有的两条通道 |
|
||||
|---|---|---|
|
||||
| `model` / `sw_ver` / `hw_ver` / `build_date` / `sw_sum` | 装置身份(型号、软硬件版本、程序日期、软件校验) | 编译期常量,一辈子不变;塞进 `rt` 等于每 200 ms 白拷约 60 B 字符串 |
|
||||
| `uptime_s` | 开机运行时长 | 每秒都在变;塞进 `par` 会被"校验和没变就不重发"的门控冻住(页面显示一个不再增长的时长 = 假数据) |
|
||||
| `port_104` / `port_disp` / `ftp_user` | 通讯参数(104 端口、远程显示端口、FTP 用户名) | 值必须在 App 侧取(页面不得自己 include 协议头,`docs/UI设计/页面扩充详细方案.md` §3.5),但它们是静态量,与遥测节拍无关 |
|
||||
|
||||
- 结构与两条主通道同构:`seq` + `valid` 打头、定长字符数组、零指针、由 `ui_data` 盖章序号、由 `App/app/ui_data_feed.c` 独家填充,随实时帧同节拍发布。
|
||||
- **公共地址 CA 故意不在本帧**:固件里 CA 是逐帧从 ASDU 头解析的,没有集中常量可取;等 104 侧给出查询点再按 `docs/UI设计/UI_数据供给设计说明.md` §4.3 的扩展规则追加字段并升 `UI_DATA_VER`。
|
||||
- 版本类字段目前是 `App/app/ui_data_feed.c` 里的宏(仓库内没有产品级版本登记表,值沿用页面既有显示值,有正式口径后只改那一处);**软件校验位在拿到构建期 CRC 生成步骤之前一律显示 `--`**,不抄参考项目遗留值。
|
||||
|
||||
> 另一处落地细化:`ui_data_rt_t` 除 `yc[]`(语义通道序基波有效值)外还带一条 `ph[]` 相量实例槽带(`ph_n` 给实际槽数)。原因是谐波(`Ia` 的二次/五次)无法在"语义通道"下标空间里表达——同一通道的谐波会与基波撞位,而交流量页要显示它们(`docs/UI设计/页面扩充详细方案.md` §3.1.1 第 4/5 行)。两条带的元素同形(数值 + 品质),页面行表按需选一条。
|
||||
|
||||
---
|
||||
|
||||
@@ -351,6 +368,14 @@ void ui_w_records_bind(const ui_rec_reader_t *reader); /* App 启动时注入
|
||||
基线"只减不增"——**本设计顺带把存量债还掉三条**,而不是新增。
|
||||
|
||||
> 另外两条(`net_cfg` / `ftp_cfg` → `cfg_file.h`)属配置引擎,与 UI 无关,不在本文范围。
|
||||
### 7.4 SOE 记录:同构 reader + UI 侧显示名映射(2026-10-25 落地新增)
|
||||
|
||||
`/log/soe.log` 是**二进制**定长 64 B 记录(时标/类型/元件号/序号/值),不是"时间戳 + 文本描述",不能与 `ui_w_records` 混读,故另立同构组件 `Drivers/BSP/UI/ui_w_soe.c`:
|
||||
|
||||
- **注入式 reader** `ui_soe_reader_t`(`count()` + `get(idx, &view)`,错误码 0/-1/-2 与 `ui_rec_reader_t` 同族);`get` 返回的是 **UI 侧视图结构** `ui_w_soe_rec_t`(时标串 + 不透明类型号 + 不透明元件号 + 标志 + 序号/值),适配层负责解码与 CP56 → 文本换算,UI 不 include `App/log/soe_rec.h`。
|
||||
- **显示名映射在 UI 侧**(`ui_w_soe_type_name()` / `ui_w_soe_elm_name()`):类型与元件都是**表现层文案**,不进快照也不回落到 App 层(同 `docs/UI设计/UI_数据供给设计说明.md` §8 的格式化归属结论)。UI 侧另立一套不透明编号常量(`UI_W_SOE_T_*` / `UI_W_SOE_ELM_*`),其**数值与 App 枚举逐项相等**——这条等价关系不靠文档转述,由主机单测在同一 TU 里 include 两侧头逐项核对(`test/host/ui_data/` 的用例 `TC-UIDATA-012`/`013`,2026-10-25 现场核对结果:类型 1..8 + 哨兵 9、`ELM_NONE=0xFF`、`FLG_IV=0x80`、元件 0..8 与 `PE_COUNT=9`)。
|
||||
- **未标定闭锁/标定复归两类新事件已进映射表**(`UI_W_SOE_T_CAL_BLOCK=7` / `UI_W_SOE_T_CAL_RESUME=8`,缺字 采/样/恢/效 已走字体重生成 + 像素回归)。单测额外钉了两条容易悄悄发生的错:八类事件必须有名(忘配名只会在屏上显示 `--`,像素门禁照不出来)、"采样闭锁/恢复"与"标定闭锁/复归"四名必须互不相同(同名 = 现场把未标定闭锁读成采样故障)。
|
||||
- **本批不绑定 reader**:落盘侧还没有只读 API(`App/log/soe_store.c` 只有 `post/drain/dropped`),需要加法式的 `soe_count()` / `soe_read(idx, &rec)`(FatFs `f_open` + `f_lseek`,与 `syslog_get_record` 同构);该文件属道F 的潜在写域,先后次序由 Lead 排。未注入期间组件按空态渲染("无记录"伪行),因此本批**不产生任何像素变化**。
|
||||
|
||||
---
|
||||
|
||||
@@ -408,6 +433,12 @@ typedef struct {
|
||||
|
||||
这是本设计对"可测性"的直接贡献:**页面数据来源可注入 → 像素回归不再依赖真实硬件**。
|
||||
|
||||
> 【2026-10-25 落地注】`simulator/main.c` 的实现形态与两条原稿未写死的细节:
|
||||
> 1. 固定帧由 `sim_publish_data_frames()` 一次性发布,**无头与窗口两个初始化点都调**(与记录 reader 注入同址、同"先于 `ui_menu_init`"纪律);
|
||||
> 2. 固定帧里的定值**不手抄 40 个数字**,而是把 `App/algo/prot_set.c`(零平台依赖的纯算法,双端共享核心)链进模拟器、`prot_set_init()` 后取出厂默认——抄表等于造第二真相(同 `scripts/check_font_charset.py` 的"单一规则源"口径);
|
||||
> 3. `build_date` 用**固定串**而不是 `__DATE__`:否则跨天重建会把装置信息页的黄金参考刷掉,而黄金参考是判据本身。
|
||||
> 4. 原稿设的 `--live` 合成正弦模式**尚未实现**(当前无消费者,等动态刷新真的需要人眼观察时再加)。
|
||||
|
||||
### 9.3 与"虚拟注入"的关系
|
||||
|
||||
`docs/UI设计/页面补齐规划.md` §7 提的厂家调试"虚拟注入",**天然落在 `ui_data_feed` 上**:注入值只要在 feed 组装时覆盖对应字段即可,**页面完全不知情**。
|
||||
@@ -458,6 +489,8 @@ typedef struct {
|
||||
|
||||
**步骤1~步骤2 是关键路径**:它们不产出任何用户可见效果,但决定后面所有页面是否可信。**不要跳过步骤1 直接改页面**——那会退化成"页面里塞假数据 2.0"。
|
||||
|
||||
> 【2026-10-25 落地进度】步骤1、步骤2 已落(道E task-14,分支 `task/ui-data-feed`):`ui_data` 三通道 + 主机单测 82 项全绿(`test/host/ui_data/`,已挂 `ui-data-unit-test` 钩子)、`ui_data_feed` 已在 `App/task/ui_task.c` 主循环接线并在建页前发首帧。**改动两处与原稿不同**:① 实时帧的 `eng` 与品质位按标定层真值语义落地(见 `docs/UI设计/UI_数据供给设计说明.md` §4.1 的落地注);② 新增第三条通道 `info`(同文档 §4.4)。**写路径(步骤5 的 `ui_data_param_request/state`)随整定 4 页一起落**,本批无消费者,先落等于落一段永不执行的代码。步骤3 起(页面接线)在道E 第二个工作项。
|
||||
|
||||
---
|
||||
|
||||
## 12. 验收判据
|
||||
|
||||
Reference in New Issue
Block a user