Files
STM32F4-Base/Drivers/BSP/UI/ui_data.c
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

197 lines
6.9 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 数据快照存储(ui_data)
* 模块功能:三通道快照(实时量 rt / 参数 par / 装置信息 info)的存储、发布盖章与
* 只读访问——本模块是纯内存叶子:不取数、不格式化、不判断业务含义、
* 不碰 LVGL、不 include 任何 App 头(职责边界与依赖方向 =
* Drivers/BSP/UI/ui_data.h 文件头与
* docs/UI设计/UI_数据供给设计说明.md §3.2)
* 并发模型:单写者同上下文——唯一发布者 App/app/ui_data_feed.c 与全部页面钩子都在
* uiTask 内串行执行,故无锁、无双缓冲、无 seqlock;将来真出现第二个
* 发布者,保护收在本模块内部(双槽 + 每槽 seq + 单字节活动下标,照
* App/algo/phasor.c 双区先例),页面代码不受影响
* (判据 docs/UI设计/UI_数据供给设计说明.md §5.1)
* 适用平台:STM32F407ZGTx / PC 模拟器(两端共用)
* 作者:王建锋
* 创建日期:2026-10-25
* 修改记录:
* v1.0 2026-10-25 王建锋 创建(道E task-14)
*/
/* 头文件包含区 */
#include <string.h>
#include "ui_data.h"
/* 静态变量定义区 */
/* 三通道存储(上电全零 = "尚无首帧",与 ui_data_init() 后状态一致) */
static ui_data_rt_t s_rt;
static ui_data_par_t s_par;
static ui_data_info_t s_info;
/*
* 发布计数:seq 由本层盖章,与入帧携带的 seq 无关——入帧的该字段被覆盖。
* 读侧"有无首帧"的判据是计数为 0,不是帧内 valid:valid 是"这一帧有没有有效
* 数据"(写侧可能发布一帧全无效的真值),两者语义不同,混用会让"首帧之前"
* 与"首帧但无数据"在页面上显示成同一种态。
*/
static uint32_t s_rt_cnt;
static uint32_t s_par_cnt;
static uint32_t s_info_cnt;
/* 函数定义区 */
/*
* 函数功能:快照层初始化——三通道清零(valid=0、seq=0),页面即按"无首帧"渲染
* 入口参数:无
* 返回值:无
* 限定条件:内核启动前可调(纯内存操作,零 OS 依赖);可重复调用(幂等)
* 函数说明:静态存储上电本已清零,本函数的意义是运行期复位(例如"恢复出厂后
* 重开 UI"这类后续路径),以及让宿主侧测试能随时造出"首帧之前"的状态
*/
void ui_data_init(void) {
(void)memset(&s_rt, 0, sizeof(s_rt));
(void)memset(&s_par, 0, sizeof(s_par));
(void)memset(&s_info, 0, sizeof(s_info));
s_rt_cnt = 0u;
s_par_cnt = 0u;
s_info_cnt = 0u;
}
/*
* 函数功能:发布一帧实时量快照(覆盖存量并给 seq 盖章 +1)
* 入口参数:a_snap - 源帧(本层整帧拷入自有存储,调用方可复用其栈/静态空间)
* const ui_data_rt_t *
* 返回值:0 - 已发布;-1 - 参数无效(NULL,存量不动) int
* 限定条件:唯一发布者 = App/app/ui_data_feed.c,uiTask 上下文
* 函数说明:seq 由本层盖章(入帧 seq 被忽略);其余字段原样入存,本层不做任何
* 业务判断与格式化
*/
int ui_data_publish_rt(const ui_data_rt_t *a_snap) {
if (a_snap == NULL) {
return -1;
}
(void)memcpy(&s_rt, a_snap, sizeof(s_rt));
s_rt_cnt += 1u;
s_rt.seq = s_rt_cnt;
return 0;
}
/*
* 函数功能:发布一帧参数快照(覆盖存量并给 seq 盖章 +1)
* 入口参数:a_snap - 源帧 const ui_data_par_t *
* 返回值:0 - 已发布;-1 - 参数无效(NULL,存量不动) int
* 限定条件:同上;发布节拍 = 定值校验和变化才发
* (docs/UI设计/UI_数据供给设计说明.md §5.2)
*/
int ui_data_publish_par(const ui_data_par_t *a_snap) {
if (a_snap == NULL) {
return -1;
}
(void)memcpy(&s_par, a_snap, sizeof(s_par));
s_par_cnt += 1u;
s_par.seq = s_par_cnt;
return 0;
}
/*
* 函数功能:发布一帧装置信息快照(覆盖存量并给 seq 盖章 +1)
* 入口参数:a_snap - 源帧 const ui_data_info_t *
* 返回值:0 - 已发布;-1 - 参数无效(NULL,存量不动) int
* 限定条件:同上
*/
int ui_data_publish_info(const ui_data_info_t *a_snap) {
if (a_snap == NULL) {
return -1;
}
(void)memcpy(&s_info, a_snap, sizeof(s_info));
s_info_cnt += 1u;
s_info.seq = s_info_cnt;
return 0;
}
/*
* 函数功能:读最新一帧实时量快照(整帧拷给页面私有缓存)
* 入口参数:a_out - 输出缓冲(非 NULL) ui_data_rt_t *
* 返回值:0 - 已拷出;-1 - 尚无首帧(输出不动,页面显示 "--");
* -2 - 参数无效(NULL) int
* 限定条件:页面钩子上下文(uiTask);勿在逐行 refresh 里无条件整帧拷贝,
* 先比 ui_data_rt_seq()(用法见 Drivers/BSP/UI/ui_data.h 文件头)
*/
int ui_data_rt(ui_data_rt_t *a_out) {
if (a_out == NULL) {
return -2;
}
if (s_rt_cnt == 0u) {
return -1;
}
(void)memcpy(a_out, &s_rt, sizeof(s_rt));
return 0;
}
/*
* 函数功能:读最新一帧参数快照
* 入口参数:a_out - 输出缓冲(非 NULL) ui_data_par_t *
* 返回值:0 - 已拷出;-1 - 尚无首帧(输出不动);-2 - 参数无效 int
* 限定条件:同上;定值页同样用 ui_data_par_seq() 门控页面私有缓存
*/
int ui_data_par(ui_data_par_t *a_out) {
if (a_out == NULL) {
return -2;
}
if (s_par_cnt == 0u) {
return -1;
}
(void)memcpy(a_out, &s_par, sizeof(s_par));
return 0;
}
/*
* 函数功能:读最新一帧装置信息快照
* 入口参数:a_out - 输出缓冲(非 NULL) ui_data_info_t *
* 返回值:0 - 已拷出;-1 - 尚无首帧(输出不动);-2 - 参数无效 int
* 限定条件:同上
*/
int ui_data_info(ui_data_info_t *a_out) {
if (a_out == NULL) {
return -2;
}
if (s_info_cnt == 0u) {
return -1;
}
(void)memcpy(a_out, &s_info, sizeof(s_info));
return 0;
}
/*
* 函数功能:查实时帧发布序号(页面缓存失效判据)
* 入口参数:无
* 返回值:当前 seq;从未发布 = 0 uint32_t
* 限定条件:无(只读单字,任意上下文安全)
* 函数说明:轻量查询——只读一个 uint32,可逐行调用;序号只在发布时 +1,页面
* 据此把"整帧拷贝"从每帧每行降到只在换帧时一次
*/
uint32_t ui_data_rt_seq(void) {
return s_rt.seq;
}
/*
* 函数功能:查参数帧发布序号
* 入口参数:无
* 返回值:当前 seq;从未发布 = 0 uint32_t
* 限定条件:无
*/
uint32_t ui_data_par_seq(void) {
return s_par.seq;
}
/*
* 函数功能:查装置信息帧发布序号
* 入口参数:无
* 返回值:当前 seq;从未发布 = 0 uint32_t
* 限定条件:无
*/
uint32_t ui_data_info_seq(void) {
return s_info.seq;
}