4332142680
批 4 主体(票 task-15;规格 docs/UI设计/页面扩充详细方案.md §3.1.3 方案 A)。V0.1 只有 6 点有 硬件来源,其余 27 点在屏上就是 "--"——这是真实状态,不是页面没做完。 页面与数据 - 33 行固定名表(Flash const)+ 动态分页(8 行/页 = 5 页)。两处命名不照抄文档简写:idx0~3 屏上写「位置1~4」而非「YK1~4 位置」(遥信页出现 YK 前缀会被读成遥控对象,对应关系留在注释); idx30 写「保护闭锁」(写侧位号注释的语义即此,用编号反而丢信息)。idx4~13、idx31~32 点表未 定义功能名,如实写点号占位(YX5…、YX32),不编造功能名。 - 值映射次序:先判无效 → 再判单/双点 → 最后看位值。无效位上的位值不代表任何事实,次序颠倒就会 把垃圾位画成「复归」。 - 快照新增 yx_dp 双点位图(+8 B)。不教页面"前 4 点是双点":单双点是 104 点表事实(YX_DP_N / iec104_pts_yx_is_dp),抄进表现层就是第二真相——点表把双点段挪位或改起始下标,屏上不报错, 只会用错词形容同一个点。写侧逐点问 is_dp() 拼位图。 - UI_DATA_YX_N(33) 与 iec104_pts_yx_count() 编译期比不了(那个 #define 在协议层 .c 内),故写侧 留一条一次性 DBG_ERROR(约 90 B Flash,只在真漂移时打一次)。名表行数 = UI_DATA_YX_N 由数组 宽度法编译期守卫钉住(ARMCC C99 无 _Static_assert,本仓惯用写法)。 - 补 include 一条实测坑:DBG_ERROR 是宏,漏 include "dbg_log.h" 时 ARMCC 把它当隐式声明函数放过 编译期,错落在链接期(L6218E Undefined symbol DBG_ERROR)——报错点离根因隔了一整个链接步骤, 已在 include 处写明「DBG_TAG 必须在 include 之前」。 一个只有像素文本断言能抓到的截断 bug - 照抄批 1 的 *_PUT(dst, 源) 宏(长度取 sizeof(源)),但本表源是 const char *const 数组的 **元素(指针)**,sizeof 得 4 → 「故障总」被截成「故」,而 "YX9" 恰好 4 字节看起来完全正常。 编译器 / cppcheck / 编码钩子全不响。定位来自断言:第 2 页「过流Ⅰ段」匹配不到,同一行值列 「复归」却在原位 = 名字被截、值没错位。修法:长度取目标缓冲容量 UI_BUF_STRLEN。同名宏、不同 参数类别(数组 vs 指针),批 1 那处本身是对的,照抄才中招——教训写在宏定义上方。 模拟器与覆盖诚实性 - 固定帧发布 yx_dp = 0x0F,但刻意不把这几位置进 yx_valid:V0.1 无开入硬件,屏上就是 "--"。不用 「让它显示合位/分位」的假数据填满画面,那等于让黄金参考声称本机有开关位置遥信。 - 代价如实登记:合位/分位这条词映射当前**没有任何自动化证据**(像素恒 IV;页面依赖 LVGL 进不了 主机单测)。已写进方案 §3.1.3 落地注与契约落地进度·四,作为开入落地后的台架判据,不假装测过。 测试 - 主机单测 +2 条(TC-UIDATA-009c 双点位图原样透传、不被 valid 掩;009d 点数 33 的独立见证,注明 绊红时要同步改哪四处)→ 85/85。 - 文本断言 +33 条(yx / yxp2 两态,含页码钉住 33/8 = 5 页的分页数学);两页像素基线单独一笔。 - font-charset 本批零新增字:名表用字全部命中既有 183 字子集——「置/故/障/总/外/部」正属批 1 成组并入 KEEP_EXTRA 的那批字,第二次兑现(当时若按 extract 输出重写字符集,这些字早被洗掉, 上屏是空白,而门禁只会说「字不在清单里」)。 体积(同一棵树、构建前后的实测增量) - Code 224272→224688(+416);RO 24040→24640(+600);RW 1324→1328(+4); ZI 134844→135088(+244)→ Flash 约 +1020 B、RAM +248 B。 - RO 增量主要是 33 行名表与名称串,是"把 33 个真点名上屏"的代价,不是可省开销。 跳过的门禁(三条同族,同一根因):mem-numbers / mem-budget / size-gate(L0c)——台账与体积凭证 回填是 Lead 单写者,本分支不做静默处理,数值见上。
284 lines
16 KiB
C
284 lines
16 KiB
C
/*
|
||
* 模块名称:UI 数据快照层(ui_data)
|
||
* 模块功能:App 层 → UI 层唯一数据通道的"存侧"——只持有最新一帧实时量(rt)/
|
||
* 参数(par)/装置信息(info) 快照 + 发布序号,给页面只读访问;
|
||
* 不取数、不格式化、不判断业务含义(职责边界 =
|
||
* docs/UI设计/UI_数据供给设计说明.md §3.2)
|
||
* 适用范围:Drivers/BSP/UI(表现层叶子:不 include 任何 App 头、不 include 任何
|
||
* 页面头、不碰 LVGL——分层方向由 scripts/check_layer_deps.py 守护,
|
||
* BSP → App 反向 include 一律拦提交)
|
||
* 并发模型:写侧 = App/app/ui_data_feed.c(唯一发布者);读侧 = ui_page_* 的绘制/
|
||
* 按键钩子。两侧同在 uiTask 上下文串行执行 → 无锁、无双缓冲、无 seqlock
|
||
* (判据与"将来出现第二发布者"时的扩展路径见
|
||
* docs/UI设计/UI_数据供给设计说明.md §5.1)
|
||
* 页面用法:行缓存按"发布序号变过才整帧拷"门控,勿逐行整帧拷贝
|
||
* (docs/UI设计/UI_数据供给设计说明.md §5.4):
|
||
* static ui_data_rt_t s_rt;
|
||
* static uint32_t s_seq;
|
||
* if (ui_data_rt_seq() != s_seq) {
|
||
* (void)ui_data_rt(&s_rt);
|
||
* s_seq = ui_data_rt_seq();
|
||
* }
|
||
* 作者:王建锋
|
||
* 创建日期:2026-10-25
|
||
* 修改记录:
|
||
* v1.0 2026-10-25 王建锋 创建(道E task-14:契约 §4 快照层落地;相对契约
|
||
* 两通道新增第三条 info,划分理由见本头"通道划分"注
|
||
* 与契约 §4.4)
|
||
*/
|
||
|
||
#ifndef UI_DATA_H
|
||
#define UI_DATA_H
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
#include <stdint.h>
|
||
|
||
/*
|
||
* 契约版本:结构体字段只允许尾部追加,追加即升此值并在写侧填默认值
|
||
* (禁止删除或重排字段——页面按名字取,重排是静默错位;
|
||
* 口径 docs/UI设计/UI_数据供给设计说明.md §4.3)
|
||
*/
|
||
#define UI_DATA_VER 1u
|
||
|
||
/* ── 通道数量 ─────────────────────────────────────────────────────────────── */
|
||
|
||
/* 遥测语义通道数:下标序 = adc_semantic_t(UA/UB/UC/UN/IA/IB/IC/IN/TEMP)。
|
||
* BSP 侧不得 include App 头,故"两数相等"的编译期守卫落在写侧
|
||
* App/app/ui_data_feed.c,语义逐项对齐的断言落在 test/host/ui_data/
|
||
* (docs/UI设计/UI_数据供给设计说明.md §9.4 一致性测试) */
|
||
#define UI_DATA_YC_N 9u
|
||
/* 遥信点数:与 104 点表同序(bit i = 第 i 点),页面名表按点表逐行登记 */
|
||
#define UI_DATA_YX_N 33u
|
||
/* 定值项数:与 prot_set 枚举同序(校验和覆盖序),四视图划分不改序 */
|
||
#define UI_DATA_SET_N 40u
|
||
/* 相量实例槽上限:谐波(Ia 二次/五次)不能塞进"语义通道"下标空间——同一通道
|
||
* 的二次/五次谐波会与基波撞位,故实时帧另开一条按**相量实例序**索引的槽位,
|
||
* 供交流量页显示 Ia2/Ia5;实际发布槽数由 ph_n 给出,超出上限的实例本帧不发布
|
||
* (写侧口径见 App/app/ui_data_feed.c 的 fill_rt 函数)。上限取 8 = 现用 5 实例
|
||
* (IA/UA/UN 基波 + IA 二次/五次)+ 遥测谐波轨留位,每槽 8 B */
|
||
#define UI_DATA_PH_MAX 8u
|
||
|
||
/* ── 量纲口径 eng(诚实性字段:数值 val 的单位由它决定)───────────────────── */
|
||
|
||
/*
|
||
* 真值语义(现场口径 = docs/任务规划/数据可信.md §2.6 的承诺边界 + §4.2 三态
|
||
* 可用性;契约文档原先写的"0 = 原始码值(未标定)"是标定落地前的旧口径,
|
||
* 已按本表更正,勿再照抄旧表述):
|
||
* ENG_PIN 引脚电压 V —— 现状:adc_cal 的 ROM 默认系数是「数据可信」卡 1 定死
|
||
* 的引脚侧 LSB(TPAFE 152.59 uV/码、STM32 805.66 uV/码),采样环
|
||
* 折算出来的就是 ADC 引脚电压,不是端子二次值
|
||
* ENG_SEC 二次值 —— 电气 8 路 adc_cal_flags() 的 VALID 全置位(拿到端子变比
|
||
* 并完成两点标定)后由写侧升档,页面自动按二次值显示,零改动
|
||
* ENG_PRI 一次值 —— 预留(再乘 PT/CT 变比),当前写侧不会产出
|
||
*/
|
||
#define UI_DATA_ENG_PIN 0u
|
||
#define UI_DATA_ENG_SEC 1u
|
||
#define UI_DATA_ENG_PRI 2u
|
||
|
||
/* ── 品质位(与 104 QDS 语义对齐;页面只消费自己关心的位)─────────────────── */
|
||
|
||
#define UI_DATA_Q_IV 0x01u /* 无效:该通道无源/不可用 → 页面显示 "--" */
|
||
#define UI_DATA_Q_NT 0x02u /* 未就绪:尚未攒满一周波/未收敛 */
|
||
#define UI_DATA_Q_OV 0x04u /* 越限:超过量程/硬件削顶(V0.1 STM32 侧单极性) */
|
||
#define UI_DATA_Q_SB 0x08u /* 取代值(虚拟注入/替身值,页面应带角标) */
|
||
#define UI_DATA_Q_BL 0x10u /* 闭锁:该量被业务闭锁(未标定闭锁/采样闭锁) */
|
||
/* 原始码值域:val 不是工程量而是转换器码值——TEMP 不参与线性标定
|
||
* (docs/任务规划/数据可信.md §4.2 的"TEMP 透传码值"决策),有此位即页面
|
||
* 不得给 val 挂 V/A 单位,也不得与工程量互比 */
|
||
#define UI_DATA_Q_RAW 0x20u
|
||
|
||
/* ── 字符串容量(结构体内定长数组,跨层零指针;容量宏本地化)──────────────── */
|
||
|
||
#define UI_DATA_CLOCK_LEN 20u /* "YYYY-MM-DD HH:MM:SS" 19 + NUL */
|
||
#define UI_DATA_MODEL_LEN 8u /* 终端型号("F30") */
|
||
#define UI_DATA_VERSTR_LEN 16u /* 软件/硬件版本串 */
|
||
#define UI_DATA_DATE_LEN 12u /* 程序日期(编译期 __DATE__,留 NUL 余量) */
|
||
#define UI_DATA_SUM_LEN 8u /* 软件校验(无构建期 CRC 步骤时如实为 "--") */
|
||
#define UI_DATA_FUSER_LEN 16u /* FTP 用户名;与 Drivers/BSP/NETCFG/ftp_cfg.h 的
|
||
* FTP_CFG_USER_MAX 同宽,写侧受该上限约束 */
|
||
|
||
/* ── 通道一:实时量(写侧节拍 200 ms)────────────────────────────────────── */
|
||
|
||
/* 单路遥测:数值 + 品质 */
|
||
typedef struct {
|
||
float val; /* 数值;单位口径由 ui_data_rt_t.eng 决定,RAW 位另说 */
|
||
uint8_t qds; /* 品质位组合(UI_DATA_Q_*) */
|
||
uint8_t rsv[3]; /* 对齐填充(显式命名,勿留匿名空洞) */
|
||
} ui_data_yc_t;
|
||
|
||
/* 实时快照:遥测 9 路 + 遥信位图 + 频率 + 墙钟 */
|
||
typedef struct {
|
||
uint32_t seq; /* 发布序号(本层盖章,单调递增) */
|
||
uint32_t tick_ms; /* 发布时刻(tick 域,差值法可比) */
|
||
uint8_t valid; /* 0 = 尚无首帧(页面全显 "--") */
|
||
uint8_t eng; /* 量纲口径(UI_DATA_ENG_*) */
|
||
uint8_t rsv[2]; /* 预留(对齐填充) */
|
||
ui_data_yc_t yc[UI_DATA_YC_N]; /* 下标 = adc_semantic_t 语义序(基波有效值) */
|
||
uint8_t ph_n; /* 本帧发布的相量实例槽数(0..UI_DATA_PH_MAX) */
|
||
uint8_t rsv4[3]; /* 预留(对齐填充) */
|
||
ui_data_yc_t ph[UI_DATA_PH_MAX]; /* 下标 = 相量实例序(含谐波),见 UI_DATA_PH_MAX 注 */
|
||
uint64_t yx_bits; /* 遥信位图:bit i = 第 i 点"合/动作" */
|
||
uint64_t yx_valid; /* 遥信有效位图: bit i = 1 该点有硬件来源 */
|
||
uint64_t yx_dp; /* 遥信双点位图: bit i = 1 该点是双点型(M_DP)
|
||
* 值列打"合位/分位",单点打"动作/复归"。
|
||
* 为什么下发位图而不让页面自己认定"前 4 点是
|
||
* 双点":单/双点是**104 点表事实**(见
|
||
* App/protocol/iec104/iec104_pts.c 的 YX_DP_N 与
|
||
* iec104_pts_yx_is_dp),抄一份进表现层就是第二
|
||
* 真相——点表把某点改成双点、或把双点段挪到下标
|
||
* 中段,屏上不会报错,只会用错词形容同一个点。
|
||
* V0.1 双点 4 点无开入硬件 → 不在 yx_valid 内,
|
||
* 值列仍是 "--"(本字段不参与无效判定) */
|
||
uint8_t freq_valid; /* 1 = 测频读数新鲜(幅值门 OK 且已收敛) */
|
||
uint8_t rsv2[3]; /* 预留(对齐填充) */
|
||
float freq_hz; /* 测得频率(Hz);freq_valid=0 时不可消费 */
|
||
uint8_t clock_ok; /* 0 = 墙钟无效(RTC 异常/未对时) */
|
||
uint8_t rsv3[3]; /* 预留(对齐填充) */
|
||
char clock[UI_DATA_CLOCK_LEN]; /* "YYYY-MM-DD HH:MM:SS";无效时空串 */
|
||
} ui_data_rt_t;
|
||
|
||
/* ── 通道二:参数(写侧节拍 = 定值校验和变化才发)────────────────────────── */
|
||
|
||
/*
|
||
* 参数快照:40 项定值 Q10 原值 + 封存态
|
||
* 存 Q10 而非 float 的理由:定值是整表校验和覆盖的定点值,快照存浮点会引入
|
||
* "显示值与校验和口径不一致"的二义性(Q10 → 显示小数位换算属表现层,见
|
||
* docs/UI设计/UI_数据供给设计说明.md §8)
|
||
*/
|
||
typedef struct {
|
||
uint32_t seq; /* 发布序号(本层盖章) */
|
||
uint32_t checksum; /* 与 prot_set_checksum() 同值:页面据此判"参数变过" */
|
||
uint8_t valid; /* 0 = 尚无首帧 */
|
||
uint8_t locked; /* 1 = 定值闭锁(校验和自检失败等)→ 页面禁编辑 */
|
||
uint8_t bad_valid; /* 1 = bad_mask 有意义(整表装载/提交失败之后) */
|
||
uint8_t rsv; /* 预留 */
|
||
uint64_t bad_mask; /* 40 位:置 1 = 该定值项越界(页面反显该行) */
|
||
int32_t q10[UI_DATA_SET_N]; /* Q10 原值,下标 = prot_set_id_t 枚举序 */
|
||
} ui_data_par_t;
|
||
|
||
/* ── 通道三:装置信息(静态身份 + 通讯参数 + 运行时长)───────────────────── */
|
||
|
||
/*
|
||
* 装置信息快照(通道划分理由见本头文件头,契约侧同一条登记在
|
||
* docs/UI设计/UI_数据供给设计说明.md §4.4——同一事实只在一处详述)
|
||
* 写侧随 rt 同节拍发布(内容基本不变,代价是一次约 90 B 拷贝)
|
||
*/
|
||
typedef struct {
|
||
uint32_t seq; /* 发布序号(本层盖章) */
|
||
uint8_t valid; /* 0 = 尚无首帧 */
|
||
uint8_t rsv[3]; /* 预留(对齐填充) */
|
||
uint32_t uptime_s; /* 开机运行时长(秒;写侧按 tick 差算) */
|
||
uint16_t port_104; /* IEC 60870-5-104 上行监听端口 */
|
||
uint16_t port_disp; /* 远程显示(RemoDispBus)端口 */
|
||
char model[UI_DATA_MODEL_LEN]; /* 终端型号 */
|
||
char sw_ver[UI_DATA_VERSTR_LEN]; /* 软件版本 */
|
||
char hw_ver[UI_DATA_VERSTR_LEN]; /* 硬件版本 */
|
||
char build_date[UI_DATA_DATE_LEN]; /* 程序日期(编译期) */
|
||
char sw_sum[UI_DATA_SUM_LEN]; /* 软件校验;无构建期 CRC 步骤 = "--" */
|
||
char ftp_user[UI_DATA_FUSER_LEN]; /* FTP 登录用户名(只显名,不显口令) */
|
||
} ui_data_info_t;
|
||
|
||
/* ── 初始化 ───────────────────────────────────────────────────────────────── */
|
||
|
||
/*
|
||
* 函数功能:快照层初始化——三通道清零(valid=0、seq=0),页面即按"无首帧"渲染
|
||
* 入口参数:无
|
||
* 返回值:无
|
||
* 限定条件:内核启动前可调(纯内存操作,零 OS 依赖);可重复调用(幂等)
|
||
* 函数说明:静态存储上电本已清零,本函数的意义是运行期复位(例如"恢复出厂后
|
||
* 重开 UI"这类后续路径),以及让宿主侧测试能随时造出"首帧之前"的状态
|
||
*/
|
||
void ui_data_init(void);
|
||
|
||
/* ── 发布(写侧唯一入口;单写者同上下文,无锁)───────────────────────────── */
|
||
|
||
/*
|
||
* 函数功能:发布一帧实时量快照(覆盖存量并给 seq 盖章 +1)
|
||
* 入口参数:a_snap - 源帧(本层整帧拷入自有存储,调用方可复用其栈/静态空间)
|
||
* const ui_data_rt_t *
|
||
* 返回值:0 - 已发布;-1 - 参数无效(NULL,存量不动) int
|
||
* 限定条件:唯一发布者 = App/app/ui_data_feed.c,uiTask 上下文
|
||
* 函数说明:seq 由本层盖章(写侧不必填);其余字段原样入存,本层不做任何业务
|
||
* 判断与格式化
|
||
*/
|
||
int ui_data_publish_rt(const ui_data_rt_t *a_snap);
|
||
|
||
/*
|
||
* 函数功能:发布一帧参数快照(覆盖存量并给 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);
|
||
|
||
/*
|
||
* 函数功能:发布一帧装置信息快照(覆盖存量并给 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);
|
||
|
||
/* ── 只读访问(页面侧)───────────────────────────────────────────────────── */
|
||
|
||
/*
|
||
* 函数功能:读最新一帧实时量快照(整帧拷给页面私有缓存)
|
||
* 入口参数:a_out - 输出缓冲(非 NULL) ui_data_rt_t *
|
||
* 返回值:0 - 已拷出;-1 - 尚无首帧(输出不动,页面显示 "--");
|
||
* -2 - 参数无效(NULL) int
|
||
* 限定条件:页面钩子上下文(uiTask);勿在逐行 refresh 里无条件整帧拷贝,
|
||
* 先比 ui_data_rt_seq()(用法见本头文件头)
|
||
*/
|
||
int ui_data_rt(ui_data_rt_t *a_out);
|
||
|
||
/*
|
||
* 函数功能:读最新一帧参数快照
|
||
* 入口参数: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);
|
||
|
||
/*
|
||
* 函数功能:读最新一帧装置信息快照
|
||
* 入口参数:a_out - 输出缓冲(非 NULL) ui_data_info_t *
|
||
* 返回值:0 - 已拷出;-1 - 尚无首帧(输出不动);-2 - 参数无效 int
|
||
* 限定条件:同上
|
||
*/
|
||
int ui_data_info(ui_data_info_t *a_out);
|
||
|
||
/*
|
||
* 函数功能:查实时帧发布序号(页面缓存失效判据)
|
||
* 入口参数:无
|
||
* 返回值:当前 seq;从未发布 = 0 uint32_t
|
||
* 限定条件:无(只读单字,任意上下文安全)
|
||
* 函数说明:轻量查询——只读一个 uint32,可逐行调用(契约 §5.4 的门控就靠它)
|
||
*/
|
||
uint32_t ui_data_rt_seq(void);
|
||
|
||
/*
|
||
* 函数功能:查参数帧发布序号
|
||
* 入口参数:无
|
||
* 返回值:当前 seq;从未发布 = 0 uint32_t
|
||
* 限定条件:无
|
||
*/
|
||
uint32_t ui_data_par_seq(void);
|
||
|
||
/*
|
||
* 函数功能:查装置信息帧发布序号
|
||
* 入口参数:无
|
||
* 返回值:当前 seq;从未发布 = 0 uint32_t
|
||
* 限定条件:无
|
||
*/
|
||
uint32_t ui_data_info_seq(void);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* UI_DATA_H */
|