/* * 模块名称: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) * v1.1 2026-10-26 DTU 项目组 道F task-1(docs/任务规划/UI补齐.md §2 第 1 行): * 新增定值写路径(param_request/param_state/ * param_drain,契约 §6.2 深度 4 环形队列)+ 参数帧尾 * 追加 cal_block(UI_DATA_VER 1→2,整定页"未标定"徽标 * 的数据源);写路径仍不引入任何 App 依赖(执行事实 * 经 ui_data_param_ops_t 由 App 侧注入,同契约 §7.2 * 记录页注入式 reader 的路子) */ #ifndef UI_DATA_H #define UI_DATA_H #ifdef __cplusplus extern "C" { #endif #include /* * 契约版本:结构体字段只允许尾部追加,追加即升此值并在写侧填默认值 * (禁止删除或重排字段——页面按名字取,重排是静默错位; * 口径 docs/UI设计/UI_数据供给设计说明.md §4.3) */ /* v2(2026-10-26,道F task-1):ui_data_par_t 尾追加 cal_block——版本号是页面与 * 写侧唯一的对齐凭据,追加字段不升版 = 老页面按新帧长 memcpy 时静默错位 */ #define UI_DATA_VER 2u /* ── 通道数量 ─────────────────────────────────────────────────────────────── */ /* 遥测语义通道数:下标序 = 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 枚举序 */ /* v2 尾追加:未标定闭锁(整定页"未标定"徽标的数据源)。 * 1 = UA/UN/IA 三路里至少一路的标定 VALID 未置位 → 保护判据当前停走,页面必须 * 让用户看见"改了定值也不会动作";0 = 三路全 VALID(判据在跑)。 * ⚠ 口径**不是** rt.eng:eng 是整帧量纲档(要求电气 8 路全 VALID),V0.1 双 * 转换器下 UB/UC/IB/IC/IN 永不置位 → eng 恒为引脚档,照 eng 画徽标就成了 * "标定做完屏上仍永不解锁"。三路口径的权威实现 = App/task/protect_task.c * 的 s_cal_locked()(写侧 App/app/ui_data_feed.c 按同一条判据现算,本层只存 * 真值、不判断)。与上面 locked(定值校验和自检闭锁 = 禁编辑)是两件事, * 屏上不得互认(三词三源见 docs/任务规划/数据可信.md §4.3) */ uint8_t cal_block; } 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); /* ── 写路径:定值整定请求(页面只投递,执行在 App 侧)───────────────────── */ /* * 请求队列深度(契约 docs/UI设计/UI_数据供给设计说明.md §6.2 定死 4) * 为什么是 4 不是 1:整定页"保存"是用户按键,一条请求要等 App 侧 drain 才落定; * 深度 1 会让连按两次保存把第二条判成"忙",而 4 格 = 本层 .bss 多 * 4 × sizeof(ui_data_param_req_t)(168 B/格,主机单测 TC-UIDATA-016 钉住尺寸, * 内存台账 docs/内存管理/RAM使用分析与优化方案.md)。加深 = 纯 RAM 代价, * 本层不做任何按内存自适应 */ #define UI_DATA_PARAM_QD 4u /* * 一条定值写入请求 = "本次保存改了哪些项 + 它们的 Q10 新值" * * 为什么是「位图 + 定长 40 槽」而不是「项数 + 变长 id 数组」:表现层编辑叠层本就 * 是一张改动位图(uint64_t)+ 一张工作值表(int32_t[UI_DATA_SET_N]),本形状与它 * 逐位同构 → 提交侧一次 memcpy、执行侧逐项按位取,两侧都不必再维护第三种表示; * 定长还让队列退化成 O(1) 静态环形数组(零堆、零变长拷贝、零 malloc 门禁风险)。 * * ⚠ mask 只有 64 位 → 定值项数硬上限 64;越界位由 ui_data.c 的编译期守卫拦住, * 将来项数破 64 必须换表示(不会静默丢项)。 * ⚠ **mask 未置位的槽本次不提交**:本层与执行侧都不读它——调用方可以只填 mask 内 * 的槽(整帧 memcpy 入队会把 mask 外的字节一并存下来,但那字节永不被读,不影响 * 任何判定;先 memset 再填也完全可以,两种写法都安全)。 * 绝不允许"从显示值回提全表"——Q10 存不下 0.95 这类返回系数(真存 972/1024), * 按显示域往返会把用户没动的项顺手改掉 * (docs/任务规划/UI补齐.md §2 第 2 行判据"值不落盘"的前提)。 */ typedef struct { uint64_t mask; /* bit i = 1 → 第 i 项随本请求提交 */ int32_t q10[UI_DATA_SET_N]; /* Q10 新值;仅 mask 置位的槽有意义 */ } ui_data_param_req_t; /* * 写路径状态(页面保存行显示;语义表 = 契约 §6.3) * IDLE - 从未投递 → 页面显 ENTER * WAIT - 已投递未落定(队列里还有请求在路上)→ 页面显 WAIT * OK - 最近一条已逐项写入并置脏 → 页面显 OK * ERR_RANGE - 最近一条整批拒绝:任一项越界 → 一字未写 → 页面显 ERR 并高亮 * 越界行(越界项经 ui_data_param_err_mask() 取) * ERR_LOCKED - 最近一条被定值闭锁拒(写侧自检失败态)→ 页面显 LOCK 并禁编辑 * ERR_BUSY - 最近一次投递被拒:队列满 → 页面显 ERR、页脚"忙,请重试" * 状态变量的三条写入路径(本层唯二写者 = request 与 drain,同在 uiTask): * 投递成功 → WAIT;投递被拒(满)→ ERR_BUSY;drain 落定 → 终态,但队列仍有 * 残留时改写回 WAIT。故"满被拒"看得见(ERR_BUSY),随后逐步 drain 又回到 WAIT */ #define UI_DATA_PARAM_ST_IDLE 0 #define UI_DATA_PARAM_ST_WAIT 1 #define UI_DATA_PARAM_ST_OK 2 #define UI_DATA_PARAM_ST_ERR_RANGE 3 #define UI_DATA_PARAM_ST_ERR_LOCKED 4 #define UI_DATA_PARAM_ST_ERR_BUSY 5 /* * 执行侧函数表(由 App 在 ui_data_feed_tick() 每次调用时传入;本层不保存副本) * * 为什么用注入而不是直接调 prot_set:分层方向硬约束——Drivers/BSP 不得 include * App 头(scripts/check_layer_deps.py 拦提交),而"什么算越界、写了要不要置脏" * 全在 App/algo/prot_set 那侧。同契约 §7.2 记录页的注入式 reader:BSP 声明抽象 * 函数表,App 给实现,主机单测给桩 → 队列与状态机的**生产代码**能在 PC 上跑 * 全表(越界不改值 / 闭锁拒写 / 正常往返三条判据都覆盖到真代码,不是副本)。 * ⚠ 本表不含任何"范围数值"——上下限真源仍是 App/algo/prot_set.c 的 * g_prot_set_meta,本层只按 check() 的返回值决定整批写不写(不新增第二真源) */ typedef struct { uint8_t (*locked)(void); /* 1 = 定值闭锁中 */ int (*check)(uint8_t a_id, int32_t a_q10); /* 0 合法 / -1 越界 */ int (*apply)(uint8_t a_id, int32_t a_q10); /* 0 成功 / -1 失败 */ } ui_data_param_ops_t; /* * 函数功能:投递一条定值写入请求(页面"保存"行的唯一出口——只投递,不执行) * 入口参数:a_req - 请求(mask 与 q10 整条拷入队列,调用方可复用其栈空间) * const ui_data_param_req_t * * 返回值:0 - 已入队(状态转 WAIT);mask 为 0 的空请求也返回 0,但按"无改动可 * 保存"直接落终态 OK 且**不占槽位**(保存行 ENTER 时一个都没改 * 是正常操作,报 ERR 会让现场以为设备有问题); * -1 - 队列满(状态转 ERR_BUSY,在队请求一条不动); * -2 - 参数无效:NULL、或 mask 含 UI_DATA_SET_N 以上的位 * (状态与队列都不动:调用方的编程错误不该落成屏上的"越界") int * 限定条件:uiTask 上下文(页面按键钩子);与 drain 同上下文 → 无锁 * 函数说明:本函数**不**校验值域(越界与否是执行侧事实,在 drain 里整批预检), * 也不碰 prot_set、不碰文件系统(落盘归 defaultTask,AGENTS 模块约定) */ int ui_data_param_request(const ui_data_param_req_t *a_req); /* * 函数功能:取写路径状态(页面保存行显示) * 入口参数:无 * 返回值:UI_DATA_PARAM_ST_* 之一 int * 限定条件:无(只读单字,任意上下文安全) * 函数说明:ERR_TIMEOUT(契约 §6.3 的"3 s 未取走")本层**不产出**——uiTask 每 * 10 ms 就 drain 一次,真出现"永不落定"说明 App 侧没接线,属装配缺陷; * 页面若要兜底可自己按投递时刻计时(docs/UI设计/UI_数据供给设计说明.md * §6.3 已把它列为页面本地可选项) */ int ui_data_param_state(void); /* * 函数功能:取最近一条请求的越界项位图(页面高亮越界行) * 入口参数:无 * 返回值:bit i = 1 → 第 i 项越界;最近终态不是 ERR_RANGE 时恒 0 uint64_t * 限定条件:无(只读单字) * 函数说明:与 ui_data_par_t.bad_mask 是两条不同来源——bad_mask 是 prot_set 整表 * 装载/解析留下的越界位图(App 侧事实),本函数是**本次提交**里被预检 * 挡下的项;页面在 ERR_RANGE 那一帧用本函数,别把两者混成一份 */ uint64_t ui_data_param_err_mask(void); /* * 函数功能:处理一条定值写入请求(App 装配层节拍入口;出队 → 判定 → 落定) * 入口参数:a_ops - 执行侧函数表(三个函数指针都不得为 NULL) * const ui_data_param_ops_t * * 返回值:1 - 处理并提交了一条(状态已落终态或转 WAIT);0 - 队列空(状态不动); * -1 - 参数无效(ops 或其函数指针为 NULL,队列与状态都不动) int * 限定条件:uiTask 上下文,紧跟页面之后调(App/app/ui_data_feed.c 的 tick); * 每轮最多处理一条——整定是低频人工事件,一轮一条已够,且把最坏耗时 * 钉在 40 次 check + 40 次 apply,不会随队列堆积而抖动 * 函数说明:固定四步(顺序即判据,契约 §6.2)—— * ① 取队首一条;② ops->locked() 非 0 → ERR_LOCKED,**本条丢弃**(闭锁 * 是业务态,用户解闭锁后重新提交;不做"排队等解锁",那会让屏上的 WAIT * 无限期挂着);③ 按 mask 逐项 ops->check() 整批预检,任一项越界 → * ERR_RANGE 且**一字不写**(all-or-nothing,与 prot_set_load_raw 同纪律), * 越界项记入 err_mask;④ 预检全过再逐项 ops->apply(),全成 → OK。 * ④ 中途失败(预检已过仍失败 = 执行侧与预检不一致,理论不可达)不回滚: * prot_set 无回滚接口,谎称"整批未写"比如实报 ERR_RANGE 更坏,故按实际 * 失败项置 err_mask 后报 ERR_RANGE,并在注释里写明可能部分已生效 */ int ui_data_param_drain(const ui_data_param_ops_t *a_ops); #ifdef __cplusplus } #endif #endif /* UI_DATA_H */