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 逐文件自查通过。
290 lines
10 KiB
C
290 lines
10 KiB
C
/*
|
||
* 模块名称:运行参数段(L2/L3 整定层,「数据可信」卡 10)——实现
|
||
* 模块功能:见 run_par.h。工厂默认 = 「数据可信」卡 6 额定 0.5 % 档(电压 500
|
||
* mV / 电流 5 mA / 频率 500 mHz,毫单位 int32 定点落盘);[run_par]
|
||
* 段 9 键(db_ua…db_freq,均 ≤15 字符)≤ CFG_FILE_MAX_KEYS 不拆段
|
||
* (通道系数覆盖**不落本段**——直通 adc_cal 的 [adc_cal]/[adc_cal_ofs],
|
||
* 系数唯一真源在那两段,lead 裁②a)。
|
||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||
* 作者:DTU 项目组
|
||
* 创建日期:2026-10-25(道D 卡 10)
|
||
* 修改记录:
|
||
* v1.0 2026-10-25 DTU 项目组 创建:[run_par] 段 + 104 映射 + 直通 adc_cal
|
||
* + B7 式脏标志落盘请求
|
||
*/
|
||
|
||
/* 头文件包含区 */
|
||
#include <math.h>
|
||
#include <stddef.h>
|
||
#include <string.h>
|
||
|
||
#include "run_par.h"
|
||
|
||
#include "adc_cal.h"
|
||
#include "cfg_file.h"
|
||
|
||
#define DBG_TAG "[PAR]"
|
||
#include "dbg_log.h"
|
||
|
||
/* 常量定义区 */
|
||
|
||
/* cfg 段名(≤ CFG_FILE_MAX_NAME_LEN);键名 6~7 字符小写 */
|
||
#define RUN_PAR_SEC "run_par"
|
||
|
||
/* 104 运行参数地址(复现规划 §5.1 类内落实;权威逐项表不在仓,送检点表到位
|
||
* 后以送检为准替换本常量段——lead 裁①。0x8027~0x802A = prot_set 告警段,永不让位) */
|
||
#define RUN_PAR_IOA_DB0 0x8030u /* 9 点:0x8030~0x8038 */
|
||
#define RUN_PAR_IOA_K0 0x8040u /* k 覆盖 8 路:0x8040~0x8047 */
|
||
#define RUN_PAR_IOA_B0 0x8048u /* b 覆盖 8 路:0x8048~0x804F */
|
||
|
||
/* 死区合法域上限(毫单位):电压 50 V / 电流 1 A / 频率 5 Hz——守卫挡误写,
|
||
* 不限工况;下限统一 0(负死区 = 配置错误) */
|
||
#define RUN_PAR_DB_MAX_U 50000L
|
||
#define RUN_PAR_DB_MAX_I 1000L
|
||
#define RUN_PAR_DB_MAX_F 5000L
|
||
|
||
/* 静态变量定义区 */
|
||
|
||
/* [run_par] 段 RAM 副本(引擎原地更新;属主契约与 µs 级混窗见「数据可信」§4.4,
|
||
* 同 adc_cal——本层不挂回调,副本即生效值,消费侧实时读) */
|
||
typedef struct {
|
||
int32_t db[RUN_PAR_DB_N];
|
||
} run_par_db_t;
|
||
|
||
static const run_par_db_t s_db_rom = {
|
||
{ 500L, 500L, 500L, 500L, /* UA/UB/UC/UN:0.5 V(卡 6 额定档) */
|
||
5L, 5L, 5L, 5L, /* IA/IB/IC/IN:0.005 A */
|
||
500L } /* 频率:0.5 Hz(绝对档,非额定百分比) */
|
||
};
|
||
static run_par_db_t s_db;
|
||
|
||
/* 键表 offset 与 run_par_db_t 字段一一对应(sizeof(int32_t) 步进) */
|
||
static const cfg_key_def_t s_db_keys[] = {
|
||
{ "db_ua", CFG_VAL_INT, 0u, 4u },
|
||
{ "db_ub", CFG_VAL_INT, 4u, 4u },
|
||
{ "db_uc", CFG_VAL_INT, 8u, 4u },
|
||
{ "db_un", CFG_VAL_INT, 12u, 4u },
|
||
{ "db_ia", CFG_VAL_INT, 16u, 4u },
|
||
{ "db_ib", CFG_VAL_INT, 20u, 4u },
|
||
{ "db_ic", CFG_VAL_INT, 24u, 4u },
|
||
{ "db_in", CFG_VAL_INT, 28u, 4u },
|
||
{ "db_freq", CFG_VAL_INT, 32u, 4u }
|
||
};
|
||
|
||
/* 落盘脏标志:104 写成功置位,run_par_store_save 成功清零(B7 prot_set 同款,
|
||
* 只置不清 = 写盘失败天然重试);单字节,跨任务 peek 无害 */
|
||
static uint8_t s_dirty;
|
||
|
||
/* 内部函数实现区 */
|
||
|
||
/*
|
||
* 函数功能:死区点量程上限(毫单位)
|
||
* 入口参数:idx - 0..8 uint8_t
|
||
* 返 回 值:上限;idx 越界返 0 长(一切值皆拒) 内 int32_t
|
||
*/
|
||
static int32_t s_db_cap_milli(uint8_t idx) {
|
||
if (idx < 4u) {
|
||
return RUN_PAR_DB_MAX_U;
|
||
}
|
||
if (idx < RUN_PAR_DB_N - 1u) {
|
||
return RUN_PAR_DB_MAX_I;
|
||
}
|
||
if (idx == (uint8_t)(RUN_PAR_DB_N - 1u)) {
|
||
return RUN_PAR_DB_MAX_F;
|
||
}
|
||
return 0L;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:工程量浮点 → 毫单位定点(只舍不入的截断由调用侧范围守卫兜底)
|
||
* 入口参数:v - 工程量(须已判有限) float
|
||
* 返 回 值:四舍五入最近毫单位 内 int32_t
|
||
*/
|
||
static int32_t s_milli_of(float v) {
|
||
return (int32_t)lrintf(v * 1000.0f);
|
||
}
|
||
|
||
/*
|
||
* 函数功能:死区值校验(不提交),set/104 选择段共用同一守卫 = 两路语义同源
|
||
* 入口参数:idx - 0..8 uint8_t;db - 工程量 float
|
||
* 返 回 值:合法毫单位 ≥0;-1 非法(越界/NaN/负/越程) 内 int32_t
|
||
*/
|
||
static int32_t s_db_check(uint8_t idx, float db) {
|
||
int32_t milli;
|
||
|
||
if ((idx >= RUN_PAR_DB_N) || !isfinite(db) || !(db >= 0.0f)) {
|
||
return -1L;
|
||
}
|
||
milli = s_milli_of(db);
|
||
if ((milli < 0L) || ((long long)milli > (long long)s_db_cap_milli(idx))) {
|
||
return -1L;
|
||
}
|
||
return milli;
|
||
}
|
||
|
||
/* 公共函数实现区 */
|
||
|
||
/*
|
||
* 函数功能:初始化——副本置工厂默认 + 注册 [run_par] 段(幂等,内核前)
|
||
* 返 回 值:无
|
||
*/
|
||
void run_par_init(void) {
|
||
cfg_section_reg_t reg;
|
||
|
||
(void)memcpy(&s_db, &s_db_rom, sizeof(s_db));
|
||
s_dirty = 0u;
|
||
|
||
(void)memset(®, 0, sizeof(reg));
|
||
reg.name = RUN_PAR_SEC;
|
||
reg.p_data = &s_db;
|
||
reg.p_defaults = &s_db_rom;
|
||
reg.data_size = sizeof(s_db);
|
||
reg.p_keys = s_db_keys;
|
||
reg.key_count = (uint8_t)(sizeof(s_db_keys) / sizeof(s_db_keys[0]));
|
||
reg.p_cb = NULL; /* 不挂回调:RAM 副本即生效值(同 adc_cal) */
|
||
if (cfg_file_register(®) != 0) {
|
||
DBG_ERROR("register %s FAIL (dup/full?) - file values inactive",
|
||
RUN_PAR_SEC);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* 函数功能:读第 idx 路死区(工程量)
|
||
* 返 回 值:V / A / Hz;越界返 0.0(消费侧按"无死区"保守处理) float
|
||
*/
|
||
float run_par_deadband(uint8_t idx) {
|
||
if (idx >= RUN_PAR_DB_N) {
|
||
return 0.0f;
|
||
}
|
||
return ((float)s_db.db[idx]) / 1000.0f; /* 毫→工程唯一换算点 */
|
||
}
|
||
|
||
/*
|
||
* 函数功能:写第 idx 路死区(守卫通过才提交;成功置脏)
|
||
* 返 回 值:0 成功;-1 非法(副本不动) int
|
||
*/
|
||
int run_par_deadband_set(uint8_t idx, float db) {
|
||
int32_t milli = s_db_check(idx, db);
|
||
|
||
if (milli < 0L) {
|
||
return -1;
|
||
}
|
||
s_db.db[idx] = milli;
|
||
s_dirty = 1u;
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:104 运行参数地址读(命中集精确匹配,见 run_par.h 地址表)
|
||
* 返 回 值:0 命中;-1 非本段(调用方落 prot_set) int
|
||
*/
|
||
int run_par_104_read(uint32_t addr, float *out) {
|
||
if (out == NULL) {
|
||
return -1;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_DB0) && (addr < RUN_PAR_IOA_DB0 + RUN_PAR_DB_N)) {
|
||
*out = run_par_deadband((uint8_t)(addr - RUN_PAR_IOA_DB0));
|
||
return 0;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_K0) && (addr < RUN_PAR_IOA_K0 + RUN_PAR_CH_N)) {
|
||
*out = adc_cal_k_uv((adc_semantic_t)(addr - RUN_PAR_IOA_K0));
|
||
return 0;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_B0) && (addr < RUN_PAR_IOA_B0 + RUN_PAR_CH_N)) {
|
||
*out = adc_cal_b_codes((adc_semantic_t)(addr - RUN_PAR_IOA_B0));
|
||
return 0;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:104 运行参数地址写(两段式;k/b 直通 adc_cal_set——**run_par 不存
|
||
* 系数**,系数唯一真源 = [adc_cal]/[adc_cal_ofs],lead 裁②a)
|
||
* 返 回 值:0 接受 / -1 拒绝 / -2 非本段(调用方落 prot_set) int
|
||
* 函数说明:选择段 = 校验不提交(死区走与 set 同源守卫;k/b 的 adc_cal 合法带
|
||
* 由执行段裁决——IEC104 允许执行段 PN,两段结果可不一致,注释在此
|
||
* 明说防"选择过了怎么还 PN"的现场困惑);k/b 单侧写 RMW 用**当前
|
||
* 生效值**做另一侧(生效值经合法带哨兵,天然满足再写校验)。
|
||
*/
|
||
int run_par_104_write(uint32_t addr, float v, uint8_t select) {
|
||
uint8_t slot; /* 每个命中分支先赋值再使用(无未init读路径) */
|
||
int32_t k_q16;
|
||
int32_t b_codes;
|
||
int rc;
|
||
|
||
if ((addr >= RUN_PAR_IOA_DB0) && (addr < RUN_PAR_IOA_DB0 + RUN_PAR_DB_N)) {
|
||
int32_t milli;
|
||
|
||
slot = (uint8_t)(addr - RUN_PAR_IOA_DB0);
|
||
milli = s_db_check(slot, v);
|
||
if (milli < 0L) {
|
||
return -1;
|
||
}
|
||
if (select == 0u) {
|
||
s_db.db[slot] = milli;
|
||
s_dirty = 1u;
|
||
}
|
||
return 0;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_K0) && (addr < RUN_PAR_IOA_K0 + RUN_PAR_CH_N)) {
|
||
slot = (uint8_t)(addr - RUN_PAR_IOA_K0);
|
||
if (!isfinite(v) || !(v > 0.0f) || (v > 1.0e9f)) {
|
||
return -1;
|
||
}
|
||
if (select != 0u) {
|
||
return 0; /* 粗守过了 ≠ 执行必过:合法带裁决在执行段(见函数头) */
|
||
}
|
||
k_q16 = (int32_t)lrintf(v * 65536.0f);
|
||
b_codes = (int32_t)lrintf(adc_cal_b_codes((adc_semantic_t)slot));
|
||
rc = adc_cal_set((adc_semantic_t)slot, k_q16, b_codes);
|
||
if (rc == 0) {
|
||
s_dirty = 1u;
|
||
}
|
||
return rc;
|
||
}
|
||
if ((addr >= RUN_PAR_IOA_B0) && (addr < RUN_PAR_IOA_B0 + RUN_PAR_CH_N)) {
|
||
slot = (uint8_t)(addr - RUN_PAR_IOA_B0);
|
||
if (!isfinite(v) || (v < -1000000.0f) || (v > 1000000.0f)) {
|
||
return -1; /* 粗守 ±1e6 码;±8192 真带由 adc_cal 执行段裁 */
|
||
}
|
||
if (select != 0u) {
|
||
return 0;
|
||
}
|
||
b_codes = (int32_t)lrintf(v);
|
||
k_q16 = (int32_t)lrintf(adc_cal_k_uv((adc_semantic_t)slot) * 65536.0f);
|
||
rc = adc_cal_set((adc_semantic_t)slot, k_q16, b_codes);
|
||
if (rc == 0) {
|
||
s_dirty = 1u;
|
||
}
|
||
return rc;
|
||
}
|
||
return -2;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:落盘脏探查(非消费)
|
||
* 返 回 值:1 有未落盘整定;0 干净 uint8_t
|
||
*/
|
||
uint8_t run_par_dirty_peek(void) {
|
||
return s_dirty;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:执行落盘(整档外科保存成功后清脏;失败脏保持 = 下轮重试)
|
||
* 返 回 值:0 成功/无脏;-1 写盘失败 int
|
||
* 限定条件:defaultTask(配置引擎属主)上下文,SYS_EVT_RUN_PAR_SAVE 驱动
|
||
* 函数说明:一次 save 同时覆盖 [run_par] 9 键与刚被 adc_cal_set 改写的
|
||
* [adc_cal]/[adc_cal_ofs] 副本(lead 裁②:一条事件位一次 save)。
|
||
*/
|
||
int run_par_store_save(void) {
|
||
if (s_dirty == 0u) {
|
||
return 0;
|
||
}
|
||
if (cfg_file_save() != 0) {
|
||
DBG_ERROR("cfg save FAIL - keep dirty, retry next round");
|
||
return -1;
|
||
}
|
||
s_dirty = 0u;
|
||
return 0;
|
||
}
|