Files
STM32F4-Base/Drivers/BSP/UI/ui_w_soe.h
T
wandering1 201d713226 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(中文只进注释)。
2026-10-25 09:56:22 +08:00

166 lines
8.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:UI SOE 记录页共享组件(ui_w_soe)
* 模块功能:把"/log/soe.log 二进制定长 64 B 事件记录 → 每记录 2 行(偶数行时标、
* 奇数行"元件 + 事件类型")+ 单条读盘缓存 + 空态伪行"无记录"这套逻辑
* 收成一份,与文本记录组件 ui_w_records 同构(用户学习成本为零);
* 数据经注入式 reader 函数表取,本头**不含任何 App 头**
* 与文本记录的区别:SOE 是结构化二进制记录(时标/类型/元件号/序号/值),
* 不是"时间戳 + 描述文本",不能让文本读者混读——故另立组件与另立
* 记录视图结构(照 ui_w_records.h 的注入模式,不复用其 desc 字段)
* 显示名映射(本组件私有,属表现层):事件类型与元件都按**不透明编号**入参,
* 编号值与 App 侧枚举逐项相等(一致性由 test/host/ui_data 主机单测
* 现场核对,不是照抄文档转述);文案是显示规格,不进快照也不进 App 层
* (口径 docs/UI设计/UI_数据供给设计说明.md §8)
* 适用范围:Drivers/BSP/UI(分层方向由 scripts/check_layer_deps.py 守护)
* 说明:reader 注入点已备好但**两端均未绑定**——SOE 文件的只读 API
* (条数 / 按序号读一条)尚未在落盘侧提供,绑定与页面接线属道E 后续
* 任务;未绑定期间本组件按空态渲染("无记录"伪行),不崩不读野
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-14:事件类型/元件显示名映射 +
* reader 契约;映射含未标定闭锁/复归两类新事件)
*/
#ifndef UI_W_SOE_H
#define UI_W_SOE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* 时间戳缓冲容量(与文本记录组件同宽:19 字符 + NUL) */
#define UI_W_SOE_TS_LEN 20u
/*
* 事件类型不透明编号:UI 侧只认编号,不认识 App 的 soe_type_t。
* ⚠ 编号值与 App/log/soe_rec.h 的 soe_rec.h 枚举逐项相等(现场核对 2026-10-25:
* 1 元件动作 / 2 元件返回 / 3 软压板退出 / 4 软压板投入 / 5 采样闭锁 /
* 6 采样恢复 / 7 未标定闭锁 / 8 标定复归 / 9 = 哨兵),漂移由主机单测拦
* (test/host/ui_data 的 TC-UIDATA-012);新增类型 = 枚举尾追加 + 这里加编号
* + 名字表加一行 + 单测加一条
*/
#define UI_W_SOE_T_NONE 0u /* UI 私有:未知/未映射编号(读失败行) */
#define UI_W_SOE_T_ELM_ASSERT 1u /* 元件动作(值 = 状态打包) */
#define UI_W_SOE_T_ELM_RETURN 2u /* 元件返回(值 = 返回前状态快照) */
#define UI_W_SOE_T_PLATE_OUT 3u /* 软压板退出 */
#define UI_W_SOE_T_PLATE_IN 4u /* 软压板投入 */
#define UI_W_SOE_T_SAMPLE_BLOCK 5u /* 采样闭锁(值 = 采样故障闭锁位图) */
#define UI_W_SOE_T_SAMPLE_RESUME 6u /* 采样恢复 */
#define UI_W_SOE_T_CAL_BLOCK 7u /* 未标定闭锁(值 = VALID 未齐的通道位图) */
#define UI_W_SOE_T_CAL_RESUME 8u /* 标定复归 */
#define UI_W_SOE_T_COUNT 9u /* 哨兵 = 名字表长度(不是有效类型) */
/* 元件编号:与保护元件枚举同序(0..8),0xFF = 非元件事件
* (一致性由同名单测 TC-UIDATA-013 现场核对) */
#define UI_W_SOE_ELM_N 9u
#define UI_W_SOE_ELM_NONE 0xFFu
/* 记录标志位:bit7 = 记录时时钟无效(时标不可信,页面须显式标记而非显示假时间) */
#define UI_W_SOE_FLG_IV 0x80u
/*
* SOE 记录视图(reader 的输出形态;字段原样搬运,不做时标换算与业务判断)
* 单条 32 B:读盘缓存按实例存,一页 8 行 = 4 条记录也只缓存当前正在渲染的那条
*/
typedef struct {
char ts[UI_W_SOE_TS_LEN]; /* "YYYY-MM-DD HH:MM:SS";IV 或未对时时为空串 */
uint8_t type; /* UI_W_SOE_T_*(未知编号原样透传,名字表回 "--") */
uint8_t elm; /* 0..UI_W_SOE_ELM_N-1 或 UI_W_SOE_ELM_NONE */
uint8_t flags; /* UI_W_SOE_FLG_IV */
uint8_t rsv; /* 预留(对齐填充) */
uint32_t seq; /* 记录序号(上电内单调) */
uint32_t value; /* 类型相关值(页面当前不展开,留作详情行) */
} ui_w_soe_rec_t;
/*
* 注入式 SOE reader(只读函数表;契约与 ui_w_records.h 的 ui_rec_reader_t 同构,
* 只是数据是二进制记录,所以取回的是整条视图而不是两段文本)
* 实现体在 App / 模拟器侧,uiTask 上下文同步调用——绘制路径上的读盘时序与
* 文本记录页一致,无新增并发面
*/
typedef struct {
/*
* 函数功能:取 SOE 记录总条数
* 入口参数:无
* 返回值:条数(0 = 空态,页面显示"无记录"伪行) uint16_t
*/
uint16_t (*count)(void);
/*
* 函数功能:读第 idx 条 SOE 记录(解码成视图)
* 入口参数:a_idx - 记录序号(0 起) uint32_t
* a_out - 输出视图(非 NULL) ui_w_soe_rec_t *
* 返回值:0 = 成功;-1 = 序号越界;-2 = 读失败或校验和不符
* (码值与文本记录 reader 同族) int
*/
int (*get)(uint32_t a_idx, ui_w_soe_rec_t *a_out);
} ui_soe_reader_t;
/* SOE 记录页实例(读盘缓存必须按实例存,理由同 ui_w_records.h 的实例说明) */
typedef struct {
uint8_t cache_valid; /* 1 = rec 是 last_rec 那条 */
uint32_t last_rec; /* 已缓存的记录序号 */
ui_w_soe_rec_t rec; /* 当前记录缓存 */
} ui_w_soe_t;
/*
* 函数功能:注入 SOE reader(启动时一次;NULL = 撤销注入,页面回空态)
* 入口参数:a_reader - 只读函数表(生命周期由注入方保证,组件只存指针)
* const ui_soe_reader_t *
* 返回值:无
* 限定条件:uiTask 启动期注入(ui_menu_init 之前,与记录 reader 同址同纪律)
*/
void ui_w_soe_bind(const ui_soe_reader_t *a_reader);
/*
* 函数功能:事件类型编号 → 显示名(表现层映射表)
* 入口参数:a_type - 类型编号(UI_W_SOE_T_*) uint8_t
* 返回值:Flash 字符串指针(恒非 NULL;未知编号回 "--",不空指针、不空白) const char *
* 限定条件:无
* 函数说明:元件类事件(动作/返回)的名字短(2 字),放**值列**;
* 非元件事件(压板/采样/标定四类,4 字)放**名列**——两列宽度不同,
* 由 ui_w_soe_refresh 按 elm 是否有效决定落在哪一列
*/
const char *ui_w_soe_type_name(uint8_t a_type);
/*
* 函数功能:元件编号 → 显示名(表现层映射表)
* 入口参数:a_elm - 元件编号(0..UI_W_SOE_ELM_N-1) uint8_t
* 返回值:Flash 字符串指针(恒非 NULL;越界或非元件事件回 "--") const char *
* 限定条件:无
* 函数说明:名称最长 4 字(零序过压/过流Ⅰ段),名列可用宽约 94 px = 7 汉字
* (口径 docs/UI设计/页面扩充详细方案.md §2.3 第 4 条)
*/
const char *ui_w_soe_elm_name(uint8_t a_elm);
/*
* 函数功能:总行数(每条记录 2 行;空态 = 单条伪行"无记录")
* 入口参数:a_inst - SOE 页实例 ui_w_soe_t *
* 返回值:总行数 uint16_t
* 限定条件:无(未注入 reader 视同 0 条)
*/
uint16_t ui_w_soe_count(ui_w_soe_t *a_inst);
/*
* 函数功能:逐行取数(偶数行 = 时标,奇数行 = 元件 + 事件类型)
* 入口参数:a_inst - SOE 页实例 ui_w_soe_t *
* a_row - 全局行下标 uint16_t
* a_name - 名输出缓冲(框架 UI_BUF_STRLEN) char *
* a_value - 值输出缓冲(同上) char *
* 返回值:无
* 限定条件:页面打开中(框架绘制/按键上下文,uiTask)
* 函数说明:时标行——时钟无效(IV 位)或时标为空时名列显示 "--",
* **不显示猜测时间**(诚实性判据,同待机屏空串先例);
* 内容行——元件事件名 = 元件、值 = 动作/返回;非元件事件名 = 类型、
* 值 = 空串(值列宽约 59 px = 4 汉字,非元件事件词放这里放不下)
*/
void ui_w_soe_refresh(ui_w_soe_t *a_inst, uint16_t a_row, char *a_name, char *a_value);
#ifdef __cplusplus
}
#endif
#endif /* UI_W_SOE_H */