8c72698666
新增 App/algo/run_par.c/h(「数据可信」卡 10,lead 四裁落地): - [run_par] cfg 段:遥测死区 9 键 int32 毫单位(db_ua…db_freq),工厂默认 = 卡 6 额定 0.5% 档(500mV×4 / 5mA×4 / 500mHz);注册走 adc_cal 同款内核前 契约(app_main_init),不挂回调 = 引擎副本即生效值;键数 9≤16 不拆段 - 真源收编(裁③):pts 层 s_yc_db 运行时副本删除,COV 每拍实时读 run_par (协议→算法向下依赖,同 meas 先例;无投递无缓存 = 顺序/幂等平凡成立); iec104_pts_yc_set_deadband()/deadband() 保留为转发壳,壳内保留"成功清 半截确认计数"语义并由 TC-IEC104-027 追加断言钉住(裁⑤) - 104 地址映射(裁①):0x8030~0x8038 死区 9 点 / 0x8040~0x8047 k 覆盖 / 0x8048~0x804F b 覆盖(逐项地址本仓自定——复现规划 §5.4 权威表不在仓, 送检点表到位后替换 run_par.c 顶部常量段一处;已在 IEC104 §3.4 表头挂账); 路由顺序 = run_par 表先、未命中落 prot_set——0x8027~0x802A 属 prot_set 告警段永不覆盖,TC-IEC104-029 双向往返钉死;空隙地址自然 COT14/PN - 通道系数覆盖 = adc_cal_set 代理(裁②a):合法带/越带 [ERR] 留痕全复用, run_par 不存第二份系数(防双真源);单侧写 RMW 用另一侧当前生效值; 回显 = 生效值视图(文件坏值被哨兵回退 ROM 时主站看到的就是下游吃到的); adc_cal 配套:+adc_cal_b_codes() 读取口(回显/RMW 用)+ set() 授权写者 注记扩至 iec104Task 直通路径(单字原子契约同「数据可信」§4.4) - 持久化(裁②b):新事件位 SYS_EVT_RUN_PAR_SAVE(1<<8);iec104Task 主循环 peek 投递 → defaultTask run_par_store_save() 一条事件一趟 cfg_file_save 整档外科(覆盖 [run_par] 9 键 + 联动 [adc_cal] 两段);失败脏保留 = 天然 重试(B7 prot_set 同款)。C_SE 现行无"暂停/测试态"门控——文档现行如此 不造新语义,送检若要求接线点已写进 §3.4 注(裁④) 测试:新建 test/host/run_par(TC-RUNPAR-001~008,67 断言,真实 cfg 引擎 + 真实 adc_cal + 内存虚拟盘同 adc_cal 套件先例;[run_par] 注册契约/落盘往返/ 外改文件即副本生效/两段式不提交不置脏/合法带拒+RMW+生效值回显/init 幂等 重注册留痕);iec104 套扩真链 TU(cfg_file+adc_cal+run_par 非桩,141 断言: +TC-IEC104-029 路由 + 壳清确认计数 3 断言);meas 套静态更名 s_snap*→ s_ms_snap*(与 cfg_file.c s_snap 同 TU 相撞,D1 同款改名法,226 断言不变); iec104_serv 套补 run_par_dirty_peek 假件。id_registry 建族 TC-HOST-RUNPAR (游标 008)+ TC-HOST-IEC104 游标 028→029;新钩子 run-par-unit-test + iec104 钩子触发面随真链扩;uvprojx +run_par.c(合法入库)+ SIM_EXEMPT 登记 (模拟器不编 104 协议,已核实 CMake 面)。 文档:IEC104 §3.4 运行参数段表(含送检替换账/门控未来条件/代理声明)、§7、 §8 第 4 条、变更记录;网络配置说明新增 §3.5 [run_par] 键表;复现规划 §5.4 补运行参数段落实进度。**遗留(交 lead)**:数据可信 §4.4 "引擎 RAM 副本" 一节当时的措辞对象含 pts 侧副本,s_yc_db 删除后该句过时(裁③b 点名,§4.4 在写域外未动)。 gate:@build 0/0(Code=219620 RO=22036 RW=1280 ZI=132864);vs 0cbae7d: Flash +1912B、RAM(RW+ZI) +8B(37B 新静态被链接器并洞吸收);iec104Task 栈 1456B/margin 43%、defaultTask 3032B 不变;id-check/doc-links/cppcheck/ coding-standard 实跑过;--all-files 全量 45 钩子实跑(43 绿 + 仅 mem-budget / mem-numbers 两红 = 台账受管区 vs .map 漂移,回填属 lead 单写者区,批 1/道D-1 /D2 同口径):gate: only mem-budget/mem-numbers pending lead ledger backfill BOM/CR/U+FFFD/字面量 ASCII 逐文件自查通过。
171 lines
6.8 KiB
C
171 lines
6.8 KiB
C
/*
|
||
* 模块名称:遥测快照发布层(L2 派生·发布侧,「数据可信」卡 8)
|
||
* 模块功能:实现见 meas.h——200 ms 基波有效值 + 品质快照,双区无锁读。
|
||
* 本文件纯算法:输入 = sample_ring 取窗 + 调用方注入(频率/削顶位图),
|
||
* 无 RTOS / 无寄存器 / 无日志(「数据可信」层内禁令:本目录不写日志)。
|
||
* 双区不变量(docs/任务规划/数据可信.md §4.5,L2-7 同型,先例 = App/algo/
|
||
* phasor.c 的双区注释):写侧只碰**非使用区**,全部字段填完后一次
|
||
* volatile 单字节 store 翻转使用区;读者先取索引再整体拷贝 → 拷贝期
|
||
* 间写者不可能改读者所在区(两帧间隔 200 ms 远大于 72 B 拷贝)。
|
||
* 单核顺序执行,无需内存屏障;volatile 阻编译器把字段写入挪到翻转后。
|
||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||
* 作者:DTU 项目组
|
||
* 创建日期:2026-10-25(道D 卡 8)
|
||
* 修改记录:
|
||
* v1.0 2026-10-25 DTU 项目组 创建:200 ms 遥测快照 + 双区无锁读
|
||
*/
|
||
|
||
#include "meas.h"
|
||
|
||
#include <stddef.h>
|
||
|
||
#include "phasor.h" /* phasor_calc():基波 DFT 纯函数核(与使用区同核) */
|
||
#include "sample_ring.h" /* sample_ring_window():以本拍为终止的一周波窗 */
|
||
|
||
/* ===== 双区(计算区 / 使用区),口径同 App/algo/phasor.c 的 L2-7 ===== */
|
||
static meas_snap_t s_ms_snap[2];
|
||
static volatile uint8_t s_ms_use;
|
||
static uint8_t s_ms_ready; /* 使用区是否已产出过有效结果 */
|
||
static uint32_t s_ms_seq; /* 只增帧号(写者单写者,读者原子单字读) */
|
||
|
||
/*
|
||
* 函数功能:清零一份快照(含 rsv 与全部数组元素)
|
||
* 入口参数:p - 目标快照
|
||
* 返 回 值:无
|
||
* 限定条件:p 非空
|
||
* 函数说明:静态辅助;逐字段清零(不依赖 memset,行为与本目录 phasor_zero 同型)。
|
||
*/
|
||
static void meas_zero(meas_snap_t *p) {
|
||
uint8_t i;
|
||
|
||
p->seq = 0u;
|
||
p->tick_ms = 0u;
|
||
p->valid = 0u;
|
||
p->rsv[0] = 0u;
|
||
p->rsv[1] = 0u;
|
||
p->rsv[2] = 0u;
|
||
p->freq_valid = 0u;
|
||
p->freq_hz = 0.0f;
|
||
for (i = 0u; i < (uint8_t)ADC_SEM_COUNT; i++) {
|
||
p->yc[i] = 0.0f;
|
||
p->qds[i] = 0u;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:遥测快照层初始化
|
||
* 入口参数:无
|
||
* 返 回 值:0 - 成功
|
||
* 限定条件:内核启动前或任务上下文皆可;幂等,可重复调用
|
||
* 函数说明:清双区 + 索引/就绪/序号复位。init 后到首次发布前,meas_get() 恒
|
||
* 返 -2(out 清零)、meas_seq() 恒返 0。
|
||
*/
|
||
int meas_init(void) {
|
||
meas_zero(&s_ms_snap[0]);
|
||
meas_zero(&s_ms_snap[1]);
|
||
s_ms_use = 0u;
|
||
s_ms_ready = 0u;
|
||
s_ms_seq = 0u;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:一拍遥测发布(被调即发布,节拍把关在调用方)
|
||
* 入口参数:tick_ms - 本拍时基(毫秒) uint32_t
|
||
* freq_valid_in - 频率读数新鲜度(freq_valid() 值) uint8_t
|
||
* freq_hz_in - 频率读数(freq_last() 值) float
|
||
* clip_mask - 削顶通道位图(bit s = 语义通道 s,来源唯一 =
|
||
* adc_cal_flags(),编排方注入) uint32_t
|
||
* 返 回 值:无
|
||
* 限定条件:仅 adcTask 上下文、全仓唯一调用点(meas.h 发布纪律);须在
|
||
* phasor_run() 之后调用(本拍窗含最新点,与使用区同窗同核)
|
||
* 函数说明:电气 8 路(语义序中 TEMP 之前的全部通道,映射表属主 =
|
||
* docs/硬件平台/ADC通道映射说明.md)逐路一周波基波 DFT;任一路取窗
|
||
* 不足(开机未满一周波)→ 该路 IV|NT + 整帧 valid=0,但照常发布
|
||
* (品质节拍性刷新,见 meas_tick 头注)。TEMP 只取最新一点透传 +
|
||
* NT(「数据可信」决策 5)。品质值不因 IV 清零——"值在场但不可信"
|
||
* 与"值缺失"分开,消费者按 qds 裁决。
|
||
*/
|
||
void meas_tick(uint32_t tick_ms, uint8_t freq_valid_in, float freq_hz_in,
|
||
uint32_t clip_mask) {
|
||
meas_snap_t *z = &s_ms_snap[1u - s_ms_use]; /* 计算区 = 非使用区 */
|
||
float win[PHASOR_N];
|
||
phasor_t p;
|
||
uint8_t all_ok = 1u;
|
||
uint8_t s;
|
||
|
||
meas_zero(z);
|
||
z->seq = s_ms_seq + 1u;
|
||
z->tick_ms = tick_ms;
|
||
|
||
for (s = 0u; s < (uint8_t)ADC_SEM_TEMP; s++) {
|
||
if (sample_ring_window((adc_semantic_t)s, (uint8_t)PHASOR_N, win) == 0) {
|
||
phasor_calc(win, (uint8_t)PHASOR_N, 1u, &p);
|
||
z->yc[s] = p.fAmSQ; /* 有效值口径(系数已折算,同 phasor 层) */
|
||
if ((clip_mask & (1uL << s)) != 0u) {
|
||
z->qds[s] |= (uint16_t)MEAS_QDS_CLIPPED;
|
||
}
|
||
} else {
|
||
/* 本路窗不足:值缺位(yc 保持 0)+ IV|NT;整帧判不可信 */
|
||
z->qds[s] = (uint16_t)(MEAS_QDS_IV | MEAS_QDS_NT);
|
||
all_ok = 0u;
|
||
}
|
||
}
|
||
|
||
/* TEMP:非交流量语义(「数据可信」决策 5),最新一点原始帧值透传 + NT;
|
||
* clip_mask 位 8 不参与(adc_cal_flags(TEMP) 恒 0,无削顶语义) */
|
||
{
|
||
float tv = 0.0f;
|
||
|
||
if (sample_ring_window(ADC_SEM_TEMP, 1u, &tv) == 0) {
|
||
z->yc[ADC_SEM_TEMP] = tv;
|
||
z->qds[ADC_SEM_TEMP] = (uint16_t)MEAS_QDS_NT;
|
||
} else {
|
||
z->qds[ADC_SEM_TEMP] = (uint16_t)(MEAS_QDS_IV | MEAS_QDS_NT);
|
||
}
|
||
}
|
||
|
||
z->valid = all_ok;
|
||
z->freq_valid = (freq_valid_in != 0u) ? 1u : 0u;
|
||
z->freq_hz = freq_hz_in;
|
||
|
||
/* 全部字段就绪后才翻索引 + 置就绪(顺序即协议,勿交换) */
|
||
s_ms_seq = z->seq;
|
||
s_ms_use = (uint8_t)(1u - s_ms_use);
|
||
s_ms_ready = 1u;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读当前使用区快照(无锁整体拷贝)
|
||
* 入口参数:out - 输出快照;失败路径同样清零填充
|
||
* 返 回 值:0 - 成功;-1 - 参数错;-2 - 从未发布过 int
|
||
* 限定条件:任意上下文;无(读 volatile 索引 + 普通拷贝)
|
||
* 函数说明:先取索引再索引拷贝,避免拷贝中途索引被翻转导致"半旧半新"。
|
||
* 返回 0 时帧内部自洽(同一发布拍的 seq/tick_ms/yc/qds 成套)。
|
||
*/
|
||
int meas_get(meas_snap_t *out) {
|
||
uint8_t z;
|
||
|
||
if (out == NULL) {
|
||
return -1;
|
||
}
|
||
if (s_ms_ready == 0u) {
|
||
meas_zero(out);
|
||
return -2;
|
||
}
|
||
z = s_ms_use;
|
||
*out = s_ms_snap[z];
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读已发布帧数(只增)
|
||
* 入口参数:无
|
||
* 返 回 值:seq,0 = 从未发布(单字读,原子) uint32_t
|
||
* 限定条件:任意上下文
|
||
* 函数说明:轻量"是否换帧"探针,正式取数以 meas_get() 为准。
|
||
*/
|
||
uint32_t meas_seq(void) {
|
||
return s_ms_seq;
|
||
}
|