merge: task/runpar-par 合入 v2-on-ui
任务 runpar(1 个提交)经 task-worktree 回收: - feat(运行参数): 卡10 run_par真源收编+104段0x8020~0x8058读写映射(道D M6-①)
This commit is contained in:
+12
-1
@@ -396,6 +396,16 @@ repos:
|
|||||||
files: ^(App/algo/meas\.(c|h)|App/algo/(phasor|sample_ring)\.(c|h)|App/task/adc_task\.c|test/host/meas/.*|test/host/common/unit_test_harness\.py)$
|
files: ^(App/algo/meas\.(c|h)|App/algo/(phasor|sample_ring)\.(c|h)|App/task/adc_task\.c|test/host/meas/.*|test/host/common/unit_test_harness\.py)$
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|
||||||
|
# 7h5) 运行参数段(「数据可信」卡 10,道D)主机侧单元测试(2026-10-25 加)
|
||||||
|
# 死区把守突发上报的量、104 整定段是现场唯一远程写入口:注册契约、
|
||||||
|
# 分档量程守卫、两段式、系数直通的合法带与生效值回显必须离线钉死。
|
||||||
|
- id: run-par-unit-test
|
||||||
|
name: 运行参数段单测(主机侧)
|
||||||
|
entry: python test/host/run_par/run_unit_test.py
|
||||||
|
language: system
|
||||||
|
files: ^(App/algo/run_par\.(c|h)|App/algo/adc_cal\.(c|h)|App/cfg/cfg_file(_store)?\.(c|h)|test/host/run_par/.*|test/host/common/unit_test_harness\.py)$
|
||||||
|
pass_filenames: false
|
||||||
|
|
||||||
|
|
||||||
# 7i) 保护定值模型(P1 / L5-1 载体)主机侧单元测试(2026-10-11 加)
|
# 7i) 保护定值模型(P1 / L5-1 载体)主机侧单元测试(2026-10-11 加)
|
||||||
# 定值是保护动作的"输入宪法":范围校验、整表装载 all-or-nothing、
|
# 定值是保护动作的"输入宪法":范围校验、整表装载 all-or-nothing、
|
||||||
@@ -463,7 +473,8 @@ repos:
|
|||||||
name: IEC104 协议栈单测(主机侧)
|
name: IEC104 协议栈单测(主机侧)
|
||||||
entry: python test/host/iec104/run_unit_test.py
|
entry: python test/host/iec104/run_unit_test.py
|
||||||
language: system
|
language: system
|
||||||
files: ^(App/protocol/iec104/iec104_(asdu|pts|link)\.(c|h)|App/algo/(prot_set|prot_elm|phasor|sample_ring|adc_input|freq)\.(c|h)|test/host/iec104/.*|test/host/common/unit_test_harness\.py)$
|
# 卡 10 起本 TU 链入真实 cfg_file/adc_cal/run_par(板端真源链),触发面同步扩
|
||||||
|
files: ^(App/protocol/iec104/iec104_(asdu|pts|link)\.(c|h)|App/algo/(prot_set|prot_elm|phasor|sample_ring|adc_input|freq|meas|adc_cal|run_par)\.(c|h)|App/cfg/cfg_file(_store)?\.(c|h)|test/host/iec104/.*|test/host/common/unit_test_harness\.py)$
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|
||||||
# 7m2) SOE 动作记录编解码主机侧单元测试(2026-10-12 加,B9)
|
# 7m2) SOE 动作记录编解码主机侧单元测试(2026-10-12 加,B9)
|
||||||
|
|||||||
+23
-1
@@ -18,6 +18,9 @@
|
|||||||
* 创建日期:2026-10-25(道A 卡 2/卡 4)
|
* 创建日期:2026-10-25(道A 卡 2/卡 4)
|
||||||
* 修改记录:
|
* 修改记录:
|
||||||
* v1.0 2026-10-25 DTU 项目组 创建:Q16 系数/零点两段落盘 + 折算 + 三态 flags
|
* v1.0 2026-10-25 DTU 项目组 创建:Q16 系数/零点两段落盘 + 折算 + 三态 flags
|
||||||
|
* v1.1 2026-10-25 DTU 项目组 卡 10(道D):新增 adc_cal_b_codes() 生效零点
|
||||||
|
* 读取口;set() 授权写者扩至 iec104Task 直通路径
|
||||||
|
* (run_par 0x8040~/0x8048~,契约同 §4.4)
|
||||||
* + 合法带哨兵回退 + 两点标定公式(台架注入待跑)
|
* + 合法带哨兵回退 + 两点标定公式(台架注入待跑)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -295,11 +298,30 @@ float adc_cal_k_uv(adc_semantic_t sem) {
|
|||||||
return ((float)k) / 65536.0f;
|
return ((float)k) / 65536.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:读通道当前生效零点偏移(码值,浮点)——adc_cal_k_uv 孪生入口
|
||||||
|
* 入口参数:sem - 语义通道 adc_semantic_t
|
||||||
|
* 返 回 值:b[码值](越带时 s_eff_kb 已回 0);TEMP/越界返 0 float
|
||||||
|
* 限定条件:无(任意上下文,纯读)
|
||||||
|
* 函数说明:卡 10 的 104 b 覆盖回显 + k 单侧改写 RMW 读侧(App/algo/run_par.c)
|
||||||
|
*/
|
||||||
|
float adc_cal_b_codes(adc_semantic_t sem) {
|
||||||
|
int32_t k;
|
||||||
|
int32_t b;
|
||||||
|
|
||||||
|
if ((unsigned)sem >= (unsigned)ADC_SEM_TEMP) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
(void)s_eff_kb((unsigned)sem, &k, &b);
|
||||||
|
return ((float)b);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:整定入口——写通道系数与零点,并置位 VALID(「数据可信」卡 4)
|
* 函数功能:整定入口——写通道系数与零点,并置位 VALID(「数据可信」卡 4)
|
||||||
* 入口参数:sem - 语义通道;k_q16 - 系数(µV/码×65536);b_codes - 零点(码值)
|
* 入口参数:sem - 语义通道;k_q16 - 系数(µV/码×65536);b_codes - 零点(码值)
|
||||||
* 返 回 值:0 - 成功;-1 - 参数非法/越出合法带(RAM 不动,[ERR] 留痕) int
|
* 返 回 值:0 - 成功;-1 - 参数非法/越出合法带(RAM 不动,[ERR] 留痕) int
|
||||||
* 限定条件:属主上下文(defaultTask 整定/标定链);落盘由属主经 cfg_file_save
|
* 限定条件:整定路径上下文(defaultTask 整定/标定链;iec104Task 经 run_par
|
||||||
|
* 直通为卡 10 授权写者,单字原子契约同 §4.4);落盘由属主经 cfg_file_save
|
||||||
* 函数说明:见 adc_cal.h 同名声明
|
* 函数说明:见 adc_cal.h 同名声明
|
||||||
*/
|
*/
|
||||||
int adc_cal_set(adc_semantic_t sem, int32_t k_q16, int32_t b_codes) {
|
int adc_cal_set(adc_semantic_t sem, int32_t k_q16, int32_t b_codes) {
|
||||||
|
|||||||
+14
-1
@@ -23,6 +23,7 @@
|
|||||||
* 创建日期:2026-10-25(道A 卡 2/卡 4)
|
* 创建日期:2026-10-25(道A 卡 2/卡 4)
|
||||||
* 修改记录:
|
* 修改记录:
|
||||||
* v1.0 2026-10-25 DTU 项目组 创建:Q16 系数/零点 + 折算/flags/集
|
* v1.0 2026-10-25 DTU 项目组 创建:Q16 系数/零点 + 折算/flags/集
|
||||||
|
* v1.1 2026-10-25 DTU 项目组 卡 10:+adc_cal_b_codes()(run_par 回显/RMW 用)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef ADC_CAL_H
|
#ifndef ADC_CAL_H
|
||||||
@@ -99,13 +100,25 @@ uint32_t adc_cal_flags(adc_semantic_t sem);
|
|||||||
*/
|
*/
|
||||||
float adc_cal_k_uv(adc_semantic_t sem);
|
float adc_cal_k_uv(adc_semantic_t sem);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:读通道当前生效零点偏移(码值,浮点)——adc_cal_k_uv 的孪生入口
|
||||||
|
* 入口参数:sem - 语义通道 adc_semantic_t
|
||||||
|
* 返 回 值:b[码值](越带时为 0,与折算同视图);TEMP/越界返 0 float
|
||||||
|
* 限定条件:无
|
||||||
|
* 函数说明:104 运行参数段(「数据可信」卡 10)b 覆盖回显与 k 单侧改写的 RMW
|
||||||
|
* 读侧用(App/algo/run_par.c);勿据其反推二次值(看 flags)。
|
||||||
|
*/
|
||||||
|
float adc_cal_b_codes(adc_semantic_t sem);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:整定入口——写通道系数与零点,并置位 VALID(「数据可信」卡 4)
|
* 函数功能:整定入口——写通道系数与零点,并置位 VALID(「数据可信」卡 4)
|
||||||
* 入口参数:sem - 语义通道(电气 8 路) adc_semantic_t
|
* 入口参数:sem - 语义通道(电气 8 路) adc_semantic_t
|
||||||
* k_q16 - 系数,µV/码 × 65536 内 int32_t
|
* k_q16 - 系数,µV/码 × 65536 内 int32_t
|
||||||
* b_codes - 零点偏移,码值 内 int32_t
|
* b_codes - 零点偏移,码值 内 int32_t
|
||||||
* 返 回 值:0 - 成功;-1 - 参数非法/越出合法带(RAM 不动,[ERR] 留痕) int
|
* 返 回 值:0 - 成功;-1 - 参数非法/越出合法带(RAM 不动,[ERR] 留痕) int
|
||||||
* 限定条件:**属主上下文**(defaultTask 的标定流程/整定链),勿在 adcTask 调用
|
* 限定条件:**整定路径上下文**(defaultTask 标定流程/整定链;卡 10 起 iec104Task
|
||||||
|
* 经 App/algo/run_par.c 的 0x8040~/0x8048~ 直通写亦为授权写者——写的仍是
|
||||||
|
* 引擎对齐 int32 单字,µs 级混窗契约同「数据可信」§4.4)。勿在 adcTask 调用
|
||||||
* 函数说明:只写 RAM 副本;落盘由属主经 cfg_file_save 完成(卡 10 前整定链
|
* 函数说明:只写 RAM 副本;落盘由属主经 cfg_file_save 完成(卡 10 前整定链
|
||||||
* 未接,台架两点标定(卡 12)是首个消费者)
|
* 未接,台架两点标定(卡 12)是首个消费者)
|
||||||
*/
|
*/
|
||||||
|
|||||||
+18
-18
@@ -23,10 +23,10 @@
|
|||||||
#include "sample_ring.h" /* sample_ring_window():以本拍为终止的一周波窗 */
|
#include "sample_ring.h" /* sample_ring_window():以本拍为终止的一周波窗 */
|
||||||
|
|
||||||
/* ===== 双区(计算区 / 使用区),口径同 App/algo/phasor.c 的 L2-7 ===== */
|
/* ===== 双区(计算区 / 使用区),口径同 App/algo/phasor.c 的 L2-7 ===== */
|
||||||
static meas_snap_t s_snap[2];
|
static meas_snap_t s_ms_snap[2];
|
||||||
static volatile uint8_t s_snap_use;
|
static volatile uint8_t s_ms_use;
|
||||||
static uint8_t s_snap_ready; /* 使用区是否已产出过有效结果 */
|
static uint8_t s_ms_ready; /* 使用区是否已产出过有效结果 */
|
||||||
static uint32_t s_snap_seq; /* 只增帧号(写者单写者,读者原子单字读) */
|
static uint32_t s_ms_seq; /* 只增帧号(写者单写者,读者原子单字读) */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:清零一份快照(含 rsv 与全部数组元素)
|
* 函数功能:清零一份快照(含 rsv 与全部数组元素)
|
||||||
@@ -61,11 +61,11 @@ static void meas_zero(meas_snap_t *p) {
|
|||||||
* 返 -2(out 清零)、meas_seq() 恒返 0。
|
* 返 -2(out 清零)、meas_seq() 恒返 0。
|
||||||
*/
|
*/
|
||||||
int meas_init(void) {
|
int meas_init(void) {
|
||||||
meas_zero(&s_snap[0]);
|
meas_zero(&s_ms_snap[0]);
|
||||||
meas_zero(&s_snap[1]);
|
meas_zero(&s_ms_snap[1]);
|
||||||
s_snap_use = 0u;
|
s_ms_use = 0u;
|
||||||
s_snap_ready = 0u;
|
s_ms_ready = 0u;
|
||||||
s_snap_seq = 0u;
|
s_ms_seq = 0u;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,14 +88,14 @@ int meas_init(void) {
|
|||||||
*/
|
*/
|
||||||
void meas_tick(uint32_t tick_ms, uint8_t freq_valid_in, float freq_hz_in,
|
void meas_tick(uint32_t tick_ms, uint8_t freq_valid_in, float freq_hz_in,
|
||||||
uint32_t clip_mask) {
|
uint32_t clip_mask) {
|
||||||
meas_snap_t *z = &s_snap[1u - s_snap_use]; /* 计算区 = 非使用区 */
|
meas_snap_t *z = &s_ms_snap[1u - s_ms_use]; /* 计算区 = 非使用区 */
|
||||||
float win[PHASOR_N];
|
float win[PHASOR_N];
|
||||||
phasor_t p;
|
phasor_t p;
|
||||||
uint8_t all_ok = 1u;
|
uint8_t all_ok = 1u;
|
||||||
uint8_t s;
|
uint8_t s;
|
||||||
|
|
||||||
meas_zero(z);
|
meas_zero(z);
|
||||||
z->seq = s_snap_seq + 1u;
|
z->seq = s_ms_seq + 1u;
|
||||||
z->tick_ms = tick_ms;
|
z->tick_ms = tick_ms;
|
||||||
|
|
||||||
for (s = 0u; s < (uint8_t)ADC_SEM_TEMP; s++) {
|
for (s = 0u; s < (uint8_t)ADC_SEM_TEMP; s++) {
|
||||||
@@ -130,9 +130,9 @@ void meas_tick(uint32_t tick_ms, uint8_t freq_valid_in, float freq_hz_in,
|
|||||||
z->freq_hz = freq_hz_in;
|
z->freq_hz = freq_hz_in;
|
||||||
|
|
||||||
/* 全部字段就绪后才翻索引 + 置就绪(顺序即协议,勿交换) */
|
/* 全部字段就绪后才翻索引 + 置就绪(顺序即协议,勿交换) */
|
||||||
s_snap_seq = z->seq;
|
s_ms_seq = z->seq;
|
||||||
s_snap_use = (uint8_t)(1u - s_snap_use);
|
s_ms_use = (uint8_t)(1u - s_ms_use);
|
||||||
s_snap_ready = 1u;
|
s_ms_ready = 1u;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -149,12 +149,12 @@ int meas_get(meas_snap_t *out) {
|
|||||||
if (out == NULL) {
|
if (out == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (s_snap_ready == 0u) {
|
if (s_ms_ready == 0u) {
|
||||||
meas_zero(out);
|
meas_zero(out);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
z = s_snap_use;
|
z = s_ms_use;
|
||||||
*out = s_snap[z];
|
*out = s_ms_snap[z];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,5 +166,5 @@ int meas_get(meas_snap_t *out) {
|
|||||||
* 函数说明:轻量"是否换帧"探针,正式取数以 meas_get() 为准。
|
* 函数说明:轻量"是否换帧"探针,正式取数以 meas_get() 为准。
|
||||||
*/
|
*/
|
||||||
uint32_t meas_seq(void) {
|
uint32_t meas_seq(void) {
|
||||||
return s_snap_seq;
|
return s_ms_seq;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:运行参数段(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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:运行参数段(L2/L3 整定层,「数据可信」卡 10)
|
||||||
|
* 模块功能:遥测死区(9 点)与通道标定系数覆盖(k/b 各 8 路)的**单一真源**:
|
||||||
|
* - 死区 = 本模块 int32 毫单位 RAM 副本(注册进 cfg 引擎 [run_par] 段,
|
||||||
|
* 工厂默认 = 「数据可信」卡 6 额定 0.5 % 档),104 消费侧
|
||||||
|
* (App/protocol/iec104/iec104_pts.c)每拍实时读取——无投递、无缓存
|
||||||
|
* 副本(卡 9 曾在 pts 侧留运行时副本 s_yc_db,卡 10 落地后收编于此,
|
||||||
|
* 双源必打架);
|
||||||
|
* - 系数覆盖 = **直通 adc_cal_set()**([adc_cal]/[adc_cal_ofs] 是系数
|
||||||
|
* 唯一真源,卡 4 已定;本模块只加 104 地址映射 + 落盘请求,不另存
|
||||||
|
* 一份系数——同 D1 裁"两处判断必打架"逻辑)。
|
||||||
|
* 104 地址映射(docs/算法/继电保护功能复现规划.md §5.1 运行参数类
|
||||||
|
* 0x8020H~0x8058H;逐项表原文"待补"(参考项目 CommPara_ConfigTable
|
||||||
|
* 不在仓),本表即卡 10 的落实,送检点表若另给权威地址只改常量段:
|
||||||
|
* 0x8030~0x8038 = 死区 UA/UB/UC/UN/IA/IB/IC/IN/FREQ(工程量 V/A/Hz);
|
||||||
|
* 0x8040~0x8047 = k 覆盖(µV/码);0x8048~0x804F = b 覆盖(码值,可负);
|
||||||
|
* 其余留白(未启用类别),**0x8027~0x802A 属 prot_set 告警段**,本表
|
||||||
|
* 永不让位(两表按精确地址分流,见 iec104_pts.c set 路由)。
|
||||||
|
* 持久化(B7 prot_set 先例同款):任一写成功 → 脏标志只置不清;
|
||||||
|
* iec104Task 主循环 peek → 置 SYS_EVT_RUN_PAR_SAVE → defaultTask
|
||||||
|
* (配置引擎属主)run_par_store_save() 调 cfg_file_save() 整档外科
|
||||||
|
* 保存(含 [run_par] 与刚被 adc_cal_set 改写的 [adc_cal]/[adc_cal_ofs]
|
||||||
|
* 副本)→ 成功清脏;失败留脏 = 下轮天然重试。本模块自身零文件 IO
|
||||||
|
* (属主纪律)。
|
||||||
|
* 并发契约:引擎副本 int32 对齐字读写在 Cortex-M4 上原子;poll 重载 µs 级窗口
|
||||||
|
* 为「数据可信」§4.4 明载设计(adc_cal 同款)。写路径 = 104 执行段
|
||||||
|
* (iec104Task)与引擎读路径互不加锁,勿改成"读侧缓存"(缓存 = 新真源)。
|
||||||
|
* 纯算法:只用 stdint/stddef/math + cfg_file.h/adc_cal.h 头(注册/直通均为
|
||||||
|
* 接口级)→ 主机侧可单测(test/host/run_par,链真 cfg 引擎 + 真 adc_cal)。
|
||||||
|
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||||||
|
* 作者:DTU 项目组
|
||||||
|
* 创建日期:2026-10-25(道D 卡 10)
|
||||||
|
* 修改记录:
|
||||||
|
* v1.0 2026-10-25 DTU 项目组 创建:[run_par] 段 + 104 地址映射 + 直通
|
||||||
|
* adc_cal + B7 式脏标志落盘请求
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RUN_PAR_H
|
||||||
|
#define RUN_PAR_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 死区/覆盖点数(死区 = YC 表行数:电气 8 + 频率;覆盖 = 语义电气 8 路) */
|
||||||
|
#define RUN_PAR_DB_N 9u
|
||||||
|
#define RUN_PAR_CH_N 8u
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:运行参数初始化——副本置工厂默认 + 注册 [run_par] 段进 cfg 引擎
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:无
|
||||||
|
* 限定条件:**内核启动前**调用(app_main_init,adc_cal_init 之后);注册必须先
|
||||||
|
* 于 defaultTask 的 cfg_file_load(N-19 契约,同 adc_cal 先例);幂等
|
||||||
|
* 函数说明:注册失败(段名重复/段数超限)打 [ERR] 后继续——工厂默认照常生效,
|
||||||
|
* 仅文件整定失效(adc_cal_init 同款降级)。
|
||||||
|
*/
|
||||||
|
void run_par_init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:读第 idx 路遥测死区(工程量)
|
||||||
|
* 入口参数:idx - 0..7 = 语义通道序(UA..IN),8 = 频率 uint8_t
|
||||||
|
* 返 回 值:死区 V / A / Hz;idx 越界返 0.0(调用方按"无死区"保守处理) float
|
||||||
|
* 限定条件:任意上下文(int32 单字读原子;104 COV 每拍调用)
|
||||||
|
* 函数说明:毫单位 → 浮点的换算点(除以 1000 唯一在此,勿在消费侧另写)。
|
||||||
|
*/
|
||||||
|
float run_par_deadband(uint8_t idx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:写第 idx 路死区(工程量,含范围守卫 + 脏置位)
|
||||||
|
* 入口参数:idx - 同上 uint8_t;db - 死区工程量(V/A/Hz,≥0) float
|
||||||
|
* 返 回 值:0 成功;-1 越界/越程/NaN(副本不动) int
|
||||||
|
* 限定条件:**iec104Task 上下文**(104 执行段 / 卡 11 后的整定路径;UI 若整定
|
||||||
|
* 需另行走配置引擎属主路径,勿跨任务直写)
|
||||||
|
* 函数说明:合法域按点型分档——电压 0~50 V、电流 0~1 A、频率 0~5 Hz(上限 =
|
||||||
|
* 额定 × 二档裕量,守卫目的是挡误写不是限工况;见 .c 常量段)。
|
||||||
|
* 成功即置脏(落盘请求,见文件头持久化契约)。
|
||||||
|
*/
|
||||||
|
int run_par_deadband_set(uint8_t idx, float db);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:104 运行参数地址读(C_RD 路由,先于 prot_set 扫描)
|
||||||
|
* 入口参数:addr - 通讯地址(0x8020~0x8058 段内) uint32_t;out - 工程量浮点
|
||||||
|
* (死区 V/A/Hz;k 覆盖 µV/码;b 覆盖 码值) float *
|
||||||
|
* 返 回 值:0 命中并写出;-1 地址不归本段(调用方继续走 prot_set) int
|
||||||
|
* 限定条件:iec104Task 上下文
|
||||||
|
* 函数说明:命中集 = 0x8030~0x8038 / 0x8040~0x8047 / 0x8048~0x804F 精确匹配;
|
||||||
|
* k/b 读回 = adc_cal **当前生效值**(与折算同视图:文件坏值被合法带
|
||||||
|
* 哨兵回退 ROM 时,回显的就是 ROM——主站看到的即下游实际吃到的)。
|
||||||
|
*/
|
||||||
|
int run_par_104_read(uint32_t addr, float *out);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:104 运行参数地址写(C_SE 两段式路由,先于 prot_set 扫描)
|
||||||
|
* 入口参数:addr - 通讯地址 uint32_t;v - 工程量浮点 float;
|
||||||
|
* select - 1 = 选择段(只校验不落副本)/ 0 = 执行段 uint8_t
|
||||||
|
* 返 回 值:0 接受;-1 拒绝(越程/NaN/合法带外/非本段可写地址命中守卫);
|
||||||
|
* -2 地址不归本段(调用方继续走 prot_set) int
|
||||||
|
* 限定条件:iec104Task 上下文;执行段成功即置脏
|
||||||
|
* 函数说明:k/b 覆盖经 adc_cal_set() 合法带哨兵(µV/码 ×[0.5,2.0]、|b|≤8192,
|
||||||
|
* 越带 -1 → 104 侧 PN 否定确认,adc_cal 不留痕由**其自身**越带 [ERR]
|
||||||
|
* 机制负责——本模块直通不重复留痕);两段式与 prot_set 定值写同构。
|
||||||
|
*/
|
||||||
|
int run_par_104_write(uint32_t addr, float v, uint8_t select);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:落盘脏标志探查(非消费)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:1 = 有已整定未落盘的改动;0 = 干净 uint8_t
|
||||||
|
* 限定条件:任意上下文(单字节);iec104_serv 主循环 peek 后投递事件(B7 同款)
|
||||||
|
*/
|
||||||
|
uint8_t run_par_dirty_peek(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 函数功能:执行落盘请求(cfg_file_save 整档外科保存 + 成功清脏)
|
||||||
|
* 入口参数:无
|
||||||
|
* 返 回 值:0 成功(或无脏可存);-1 写盘失败(脏保持,下轮重试) int
|
||||||
|
* 限定条件:**defaultTask(配置引擎属主)上下文**,由 SYS_EVT_RUN_PAR_SAVE 驱动
|
||||||
|
* 函数说明:save 走引擎唯一写闸(超长拒写/原子改名,E-01 契约);本函数不清
|
||||||
|
* 引擎以外模块的脏语义([adc_cal] 副本值已由写路径就位)。
|
||||||
|
*/
|
||||||
|
int run_par_store_save(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* RUN_PAR_H */
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "tpafe5160.h"
|
#include "tpafe5160.h"
|
||||||
#include "adc_input.h" /* L1 采集层:adc_input_init()(语义映射 + STM32 ADC1 扫描) */
|
#include "adc_input.h" /* L1 采集层:adc_input_init()(语义映射 + STM32 ADC1 扫描) */
|
||||||
#include "adc_cal.h" /* L1.5 标定层:adc_cal_init()(ROM 系数 + cfg 两段注册,道A 卡2/4) */
|
#include "adc_cal.h" /* L1.5 标定层:adc_cal_init()(ROM 系数 + cfg 两段注册,道A 卡2/4) */
|
||||||
|
#include "run_par.h" /* 运行参数段:run_par_init()([run_par] 注册 + 104 地址映射真源,道D 卡10) */
|
||||||
#include "phasor.h" /* L2 相量层:phasor_init()(系数表 + 实例 + 双区交换) */
|
#include "phasor.h" /* L2 相量层:phasor_init()(系数表 + 实例 + 双区交换) */
|
||||||
#include "freq.h" /* L2 派生量:freq_init()(相位解缠绕测频,B8) */
|
#include "freq.h" /* L2 派生量:freq_init()(相位解缠绕测频,B8) */
|
||||||
#include "prot_set.h" /* L3 定值模型:prot_set_init()(B5,内核前封存) */
|
#include "prot_set.h" /* L3 定值模型:prot_set_init()(B5,内核前封存) */
|
||||||
@@ -261,6 +262,12 @@ int app_main_init(void) {
|
|||||||
DBG_INFO("adc cal init: OK (k_q16 tpafe=%ld stm32=%ld)",
|
DBG_INFO("adc cal init: OK (k_q16 tpafe=%ld stm32=%ld)",
|
||||||
(long)ADC_CAL_K_Q16_TPAFE, (long)ADC_CAL_K_Q16_STM32);
|
(long)ADC_CAL_K_Q16_TPAFE, (long)ADC_CAL_K_Q16_STM32);
|
||||||
|
|
||||||
|
/* 4c2) 运行参数段初始化(「数据可信」卡 10):死区 9 点工厂默认 + 注册
|
||||||
|
* [run_par] 段。同 adc_cal 的「先注册后 load」契约 → 必须内核前;
|
||||||
|
* 104 消费(COV 实时读 / 0x8020 段读写映射)在 iec104Task 启动后进行。 */
|
||||||
|
run_par_init();
|
||||||
|
DBG_INFO("run par init: OK (db9 k/b via adc_cal, evt=1<<8)");
|
||||||
|
|
||||||
/* 4d) 保护定值 + 元件层初始化(B5):出厂默认定值封存校验和 + 元件状态
|
/* 4d) 保护定值 + 元件层初始化(B5):出厂默认定值封存校验和 + 元件状态
|
||||||
* 复位并解析相量索引(须在 phasor_init 之后)。内核前完成 → iec104
|
* 复位并解析相量索引(须在 phasor_init 之后)。内核前完成 → iec104
|
||||||
* 定值读/protectTask 首拍前定值必已装载。判据消费在 protectTask。 */
|
* 定值读/protectTask 首拍前定值必已装载。判据消费在 protectTask。 */
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ extern osEventFlagsId_t sys_evtHandle;
|
|||||||
#define SYS_EVT_PROT_SET_SAVE (1u << 7) /* 保护定值落盘请求(B7):iec104Task 检测到定值脏置位,
|
#define SYS_EVT_PROT_SET_SAVE (1u << 7) /* 保护定值落盘请求(B7):iec104Task 检测到定值脏置位,
|
||||||
* defaultTask(配置引擎属主)消费并写 /prot_set.bin
|
* defaultTask(配置引擎属主)消费并写 /prot_set.bin
|
||||||
* (消费型命令位;写盘失败脏标志保留 → 下轮再通知重试) */
|
* (消费型命令位;写盘失败脏标志保留 → 下轮再通知重试) */
|
||||||
|
#define SYS_EVT_RUN_PAR_SAVE (1u << 8) /* 运行参数段落盘请求(「数据可信」卡 10):iec104Task
|
||||||
|
* 检测到 run_par 脏(死区 9 键 / 系数覆盖联动 [adc_cal])
|
||||||
|
* 置位,defaultTask 消费并 cfg_file_save 整档外科保存
|
||||||
|
* (消费型命令位;失败脏保留 → 下轮重试,B7 同款) */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,18 @@
|
|||||||
* 死区改运行时副本 + set/get 入口;COV 遥测腿 = 死区
|
* 死区改运行时副本 + set/get 入口;COV 遥测腿 = 死区
|
||||||
* 连续 2 点确认(板端按快照 seq 去重)+ 品质翻转即报。
|
* 连续 2 点确认(板端按快照 seq 去重)+ 品质翻转即报。
|
||||||
* 单测 = test/host/iec104 TC-IEC104-025~028。
|
* 单测 = test/host/iec104 TC-IEC104-025~028。
|
||||||
|
* 卡 10 2026-10-25 DTU 项目组 死区真源收编 App/algo/run_par.c(本层 s_yc_db
|
||||||
|
* 副本删除,set/get 转转发壳,壳保留清确认计数语义);
|
||||||
|
* 定值读写路由 = run_par 表先、prot_set 后(0x8020~
|
||||||
|
* 0x8058 运行参数段 C_RD/C_SE,系数覆盖直通 adc_cal)。
|
||||||
*/
|
*/
|
||||||
#include "iec104_pts.h"
|
#include "iec104_pts.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "iec104_asdu.h" /* QDS/DIQ 品质位常量 */
|
#include "iec104_asdu.h" /* QDS/DIQ 品质位常量 */
|
||||||
#include "meas.h" /* 板端源:遥测 = L2 遥测快照(卡 9;品质域位同源) */
|
#include "meas.h" /* 板端源:遥测 = L2 遥测快照(卡 9;品质域位同源) */
|
||||||
|
#include "run_par.h" /* 卡 10:死区真源 + 运行参数段 104 读写路由 */
|
||||||
#include "prot_elm.h" /* 板端源:遥信 = 元件状态 */
|
#include "prot_elm.h" /* 板端源:遥信 = 元件状态 */
|
||||||
#include "prot_set.h" /* 板端源:定值 + 闭锁 */
|
#include "prot_set.h" /* 板端源:定值 + 闭锁 */
|
||||||
#include "adc_input.h" /* adc_semantic_t(phasor 实例按语义绑定) */
|
#include "adc_input.h" /* adc_semantic_t(phasor 实例按语义绑定) */
|
||||||
@@ -37,7 +42,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t ioa;
|
uint32_t ioa;
|
||||||
int8_t sem; /* 快照 yc 槽 = 语义通道(-1 = 非测量量点,当前仅频率) */
|
int8_t sem; /* 快照 yc 槽 = 语义通道(-1 = 非测量量点,当前仅频率) */
|
||||||
float db_default; /* 死区 ROM 默认(运行时生效值 = s_yc_db,卡 10 覆盖) */
|
float db_default; /* 死区工厂默认(生效真源 = run_par [run_par] 段,卡 10) */
|
||||||
uint8_t src; /* PTS_SRC_* 源类型 */
|
uint8_t src; /* PTS_SRC_* 源类型 */
|
||||||
} pts_yc_def_t;
|
} pts_yc_def_t;
|
||||||
|
|
||||||
@@ -45,8 +50,8 @@ typedef struct {
|
|||||||
* 额定基准 100 V / 1 A 与 App/algo/prot_set.c 头部同源,勿在此另立口径):
|
* 额定基准 100 V / 1 A 与 App/algo/prot_set.c 头部同源,勿在此另立口径):
|
||||||
* 电压类 0.5 % × 100 V = 0.5 V 电流类 0.5 % × 1 A = 0.005 A
|
* 电压类 0.5 % × 100 V = 0.5 V 电流类 0.5 % × 1 A = 0.005 A
|
||||||
* 频率点不适用"额定百分比"(额定附近 0.5 Hz 与旧值同量级,维持 0.5 并随
|
* 频率点不适用"额定百分比"(额定附近 0.5 Hz 与旧值同量级,维持 0.5 并随
|
||||||
* 卡 10 运行参数段可调)。自卡 9 起本表**只作 ROM 默认载体**:运行时死区 =
|
* 卡 10 运行参数段可调)。自卡 9 起本表**只作工厂默认载体**:生效死区 =
|
||||||
* s_yc_db 副本;削顶五路不再 sem=-1 绑死,而是绑快照槽、由品质 CLIPPED
|
* App/algo/run_par.c 的 [run_par] 引擎副本(卡 10 真源收编,本层不再存副本);削顶五路不再 sem=-1 绑死,而是绑快照槽、由品质 CLIPPED
|
||||||
* 驱动线上 IV——V0.2(N-18)削顶消失后点位零改动自动启用(docs/任务规划/
|
* 驱动线上 IV——V0.2(N-18)削顶消失后点位零改动自动启用(docs/任务规划/
|
||||||
* 数据可信.md §10 的 N-18 行)。
|
* 数据可信.md §10 的 N-18 行)。
|
||||||
* ⚠ 过渡期(eng=0 引脚电压域,卡 12 标定前):上送值本身非二次值,死区
|
* ⚠ 过渡期(eng=0 引脚电压域,卡 12 标定前):上送值本身非二次值,死区
|
||||||
@@ -70,7 +75,6 @@ static const pts_yc_def_t k_yc[] = {
|
|||||||
* 判据链:新测量点 → |v − 上次上送值| > 死区?→ 连续 2 点确认 → 置变化位。
|
* 判据链:新测量点 → |v − 上次上送值| > 死区?→ 连续 2 点确认 → 置变化位。
|
||||||
* 基准 = **上次上送值**(非上次读值——否则慢漂移永不出死区,参考系统同款);
|
* 基准 = **上次上送值**(非上次读值——否则慢漂移永不出死区,参考系统同款);
|
||||||
* 品质(线上 qds)翻转 = 事件:不参与 2 点计数、当拍直报。 */
|
* 品质(线上 qds)翻转 = 事件:不参与 2 点计数、当拍直报。 */
|
||||||
static float s_yc_db[YC_N]; /* 死区运行时副本(ROM 默认 = k_yc 列) */
|
|
||||||
static float s_yc_rep[YC_N]; /* 上次上送值(建线/确认上送时更新) */
|
static float s_yc_rep[YC_N]; /* 上次上送值(建线/确认上送时更新) */
|
||||||
static uint8_t s_yc_repq[YC_N]; /* 上次上送的线上 qds */
|
static uint8_t s_yc_repq[YC_N]; /* 上次上送的线上 qds */
|
||||||
static uint8_t s_yc_cnf[YC_N]; /* 越死区连续计数(1 = 待确认;2 = 确认) */
|
static uint8_t s_yc_cnf[YC_N]; /* 越死区连续计数(1 = 待确认;2 = 确认) */
|
||||||
@@ -115,11 +119,8 @@ static uint8_t s_yx_snap_ok;
|
|||||||
* 直接可定);重复调用 = 全状态复位(单测 reset 依赖此语义)。
|
* 直接可定);重复调用 = 全状态复位(单测 reset 依赖此语义)。
|
||||||
*/
|
*/
|
||||||
void iec104_pts_init(void) {
|
void iec104_pts_init(void) {
|
||||||
uint8_t i;
|
/* 死区不在本层复位——真源 = run_par(cfg 引擎副本),复位入口唯一
|
||||||
|
* run_par_init();本函数只复位 COV 状态(单测 reset 依赖此语义) */
|
||||||
for (i = 0u; i < YC_N; i++) {
|
|
||||||
s_yc_db[i] = k_yc[i].db_default;
|
|
||||||
}
|
|
||||||
(void)memset(s_yc_rep, 0, sizeof(s_yc_rep));
|
(void)memset(s_yc_rep, 0, sizeof(s_yc_rep));
|
||||||
(void)memset(s_yc_repq, 0, sizeof(s_yc_repq));
|
(void)memset(s_yc_repq, 0, sizeof(s_yc_repq));
|
||||||
(void)memset(s_yc_cnf, 0, sizeof(s_yc_cnf));
|
(void)memset(s_yc_cnf, 0, sizeof(s_yc_cnf));
|
||||||
@@ -235,32 +236,35 @@ int iec104_pts_yc_read(uint8_t idx, float *v, uint8_t *qds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:写死区运行时值(「数据可信」卡 9;卡 10 运行参数段的落点入口)
|
* 函数功能:写死区(卡 9 API 壳,转发 run_par 真源 + 清本层确认计数)
|
||||||
* 入口参数:idx - YC 表下标 db - 工程量绝对死区(≥0) float
|
* 入口参数:idx - YC 表下标 db - 工程量绝对死区(≥0,分档上限见 run_par)
|
||||||
* 返 回 值:0 成功;-1 越界/负值/NaN(RAM 不动) int
|
* 返 回 值:0 成功;-1 越界/负值/NaN/越程(run_par 副本不动) int
|
||||||
* 限定条件:**iec104Task 上下文**(配置属主 = defaultTask,卡 10 接线时经
|
* 限定条件:**iec104Task 上下文**(run_par 副本写 = 引擎对齐 int32 单字,
|
||||||
* 104 任务落地本调用,勿跨任务直调——本表状态无锁)
|
* 104 执行段之外调用需自行避免与 104 写并发)
|
||||||
* 函数说明:成功时清零该点待确认计数——跨死区档位不保留半截确认序列;
|
* 函数说明:成功时清零该点待确认计数——跨死区档位不保留半截确认序列;
|
||||||
* 不隐式重建基线(上送基准延续,新死区即刻生效)。
|
* 不隐式重建基线(上送基准延续,新死区即刻生效)。落盘由 run_par
|
||||||
|
* 脏标志 + SYS_EVT_RUN_PAR_SAVE 走 defaultTask(卡 10 持久化链)。
|
||||||
*/
|
*/
|
||||||
int iec104_pts_yc_set_deadband(uint8_t idx, float db) {
|
int iec104_pts_yc_set_deadband(uint8_t idx, float db) {
|
||||||
if ((idx >= YC_N) || !(db >= 0.0f) || !(db < 1000000.0f)) {
|
if (idx >= YC_N) {
|
||||||
return -1; /* NaN 被 !(db>=0) 捕获(比较式写法,防 lint 报恒真) */
|
return -1;
|
||||||
}
|
}
|
||||||
s_yc_db[idx] = db;
|
if (run_par_deadband_set(idx, db) != 0) {
|
||||||
s_yc_cnf[idx] = 0u;
|
return -1; /* 守卫(NaN/负/越程分档上限)单一实现,在 run_par */
|
||||||
|
}
|
||||||
|
s_yc_cnf[idx] = 0u; /* 壳语义:跨死区档位不保留半截确认序列 */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:读当前生效死区
|
* 函数功能:读当前生效死区(转发 run_par 真源)
|
||||||
* 入口参数:idx - YC 表下标
|
* 入口参数:idx - YC 表下标
|
||||||
* 返 回 值:运行时死区;idx 越界返 0.0 float
|
* 返 回 值:生效死区;idx 越界返 0.0 float
|
||||||
* 限定条件:任意上下文(单字 float 读,Cortex-M 原子)
|
* 限定条件:任意上下文(引擎副本 int32 单字读,Cortex-M 原子)
|
||||||
* 函数说明:UI 回显 / 卡 10 读写往返校验用;ROM 默认查点表,勿在此反推。
|
* 函数说明:UI 回显 / 读写往返校验用;工厂默认查点表 db_default 列。
|
||||||
*/
|
*/
|
||||||
float iec104_pts_yc_deadband(uint8_t idx) {
|
float iec104_pts_yc_deadband(uint8_t idx) {
|
||||||
return (idx < YC_N) ? s_yc_db[idx] : 0.0f;
|
return (idx < YC_N) ? run_par_deadband(idx) : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -389,7 +393,7 @@ void iec104_pts_cov_check(iec104_pts_cov_t *cov) {
|
|||||||
if (d < 0.0f) {
|
if (d < 0.0f) {
|
||||||
d = -d;
|
d = -d;
|
||||||
}
|
}
|
||||||
if (d > s_yc_db[i]) {
|
if (d > run_par_deadband(i)) {
|
||||||
s_yc_cnf[i]++;
|
s_yc_cnf[i]++;
|
||||||
if (s_yc_cnf[i] >= 2u) { /* 连续 2 越 → 确认:报 + 移基准 */
|
if (s_yc_cnf[i] >= 2u) { /* 连续 2 越 → 确认:报 + 移基准 */
|
||||||
local.yc_changed |= (uint16_t)(1u << i);
|
local.yc_changed |= (uint16_t)(1u << i);
|
||||||
@@ -564,7 +568,10 @@ static int s_src_yk_exec(uint32_t ioa, uint8_t dpc, uint8_t select) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:板端默认源 - 定值读(扫 g_prot_set_meta 通讯地址映射)
|
* 函数功能:板端默认源 - 定值读(**路由顺序 = run_par 表先、未命中落 prot_set**:
|
||||||
|
* 0x8027~0x802A 属 prot_set 告警段,run_par 命中集为精确地址匹配、
|
||||||
|
* 永不覆盖之;两表共存靠「先专属表、后通用扫」分流,勿反序——反序
|
||||||
|
* 当前无冲突但失去 run_par 守卫先行留痕的意义)
|
||||||
* 返 回 值:0 / -1 未知地址(本地项 comm_addr=0 不可寻址,B7 起显式排除)
|
* 返 回 值:0 / -1 未知地址(本地项 comm_addr=0 不可寻址,B7 起显式排除)
|
||||||
*/
|
*/
|
||||||
static int s_src_set_read(uint32_t addr, float *out) {
|
static int s_src_set_read(uint32_t addr, float *out) {
|
||||||
@@ -573,6 +580,9 @@ static int s_src_set_read(uint32_t addr, float *out) {
|
|||||||
if (out == NULL) {
|
if (out == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (run_par_104_read(addr, out) == 0) {
|
||||||
|
return 0; /* 运行参数段命中(卡 10) */
|
||||||
|
}
|
||||||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||||||
if ((g_prot_set_meta[i].comm_addr != 0u)
|
if ((g_prot_set_meta[i].comm_addr != 0u)
|
||||||
&& (g_prot_set_meta[i].comm_addr == (uint16_t)addr)) {
|
&& (g_prot_set_meta[i].comm_addr == (uint16_t)addr)) {
|
||||||
@@ -584,13 +594,22 @@ static int s_src_set_read(uint32_t addr, float *out) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 函数功能:板端默认源 - 定值写(Q10 范围校验;执行段 prot_set_set_sealed
|
* 函数功能:板端默认源 - 定值写(**先 run_par 后 prot_set**,分流理由见
|
||||||
* 原子写+封存,B7 收口 B5 竞态窗口;置脏 → 104 任务通知落盘)
|
* s_src_set_read 头注;prot_set 腿 = Q10 范围校验 + 执行段
|
||||||
* 返 回 值:0 / -1 越界或未知地址(本地项 comm_addr=0 不可寻址)
|
* prot_set_set_sealed 原子写+封存,B7 收口 B5 竞态窗口)
|
||||||
|
* 返 回 值:0 / -1 越界或未知地址(本地项 comm_addr=0 不可寻址);两条落盘
|
||||||
|
* 链独立:run_par 腿脏 → SYS_EVT_RUN_PAR_SAVE(cfg_file),
|
||||||
|
* prot_set 腿脏 → SYS_EVT_PROT_SET_SAVE(/prot_set.bin)
|
||||||
*/
|
*/
|
||||||
static int s_src_set_write(uint32_t addr, float v, uint8_t select) {
|
static int s_src_set_write(uint32_t addr, float v, uint8_t select) {
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
int32_t q10;
|
int32_t q10;
|
||||||
|
int rp;
|
||||||
|
|
||||||
|
rp = run_par_104_write(addr, v, select);
|
||||||
|
if (rp != -2) {
|
||||||
|
return rp; /* 本段地址:0 接受 / -1 拒绝,不再下探 */
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
|
||||||
const prot_set_meta_t *meta = &g_prot_set_meta[i];
|
const prot_set_meta_t *meta = &g_prot_set_meta[i];
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
* 快照(削顶路由品质驱动 IV);死区改运行时副本 +
|
* 快照(削顶路由品质驱动 IV);死区改运行时副本 +
|
||||||
* set/get 入口;COV 遥测腿 = 死区 2 点确认(seq 去重)
|
* set/get 入口;COV 遥测腿 = 死区 2 点确认(seq 去重)
|
||||||
* + 品质变化即报;0x401d 绑频率源(freq_valid → IV)。
|
* + 品质变化即报;0x401d 绑频率源(freq_valid → IV)。
|
||||||
|
* 2026-10-25 DTU 项目组 卡 10:死区真源迁 run_par([run_par] cfg 段 +
|
||||||
|
* 0x8020~0x8058 读写映射;本层 set/get 为转发壳)。
|
||||||
*/
|
*/
|
||||||
#ifndef IEC104_PTS_H
|
#ifndef IEC104_PTS_H
|
||||||
#define IEC104_PTS_H
|
#define IEC104_PTS_H
|
||||||
@@ -81,9 +83,11 @@ uint32_t iec104_pts_yc_ioa(uint8_t idx);
|
|||||||
/* 读 YC:v 有效值(无效时为 0);qds 输出线上品质位(IV/NT/OV,CLIPPED 已映射
|
/* 读 YC:v 有效值(无效时为 0);qds 输出线上品质位(IV/NT/OV,CLIPPED 已映射
|
||||||
* 为 IV)。返回 0/无效-1(NT 亦无效,品质位区分) */
|
* 为 IV)。返回 0/无效-1(NT 亦无效,品质位区分) */
|
||||||
int iec104_pts_yc_read(uint8_t idx, float *v, uint8_t *qds);
|
int iec104_pts_yc_read(uint8_t idx, float *v, uint8_t *qds);
|
||||||
/* 死区运行时写(「数据可信」卡 9;卡 10 运行参数段的落点入口):
|
/* 死区写(卡 9 API;卡 10 起 = 转发 App/algo/run_par.c 真源 + 清本层确认计数):
|
||||||
* db = 工程量绝对值 ≥0;越界/负值/NaN → -1 且 RAM 不动;成功时清零该点
|
* db = 工程量绝对值 ≥0(电压 50 V / 电流 1 A / 频率 5 Hz 分档上限);越界/负值/
|
||||||
* 待确认计数(跨死区基线不残留半截确认)。仅 104 服务任务上下文调用。 */
|
* NaN → -1 且真源不动;成功时清零该点待确认计数(跨档位不残留半截确认——此壳
|
||||||
|
* 行为有单测钉住)。仅 104 服务任务上下文调用。落盘 = run_par 脏 → 事件 →
|
||||||
|
* defaultTask(卡 10 持久化链,docs/子系统设计/IEC104上行设计说明.md §3.4)。 */
|
||||||
int iec104_pts_yc_set_deadband(uint8_t idx, float db);
|
int iec104_pts_yc_set_deadband(uint8_t idx, float db);
|
||||||
/* 读当前生效死区(越界 idx 返 0) */
|
/* 读当前生效死区(越界 idx 返 0) */
|
||||||
float iec104_pts_yc_deadband(uint8_t idx);
|
float iec104_pts_yc_deadband(uint8_t idx);
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "nand_ftl.h"
|
#include "nand_ftl.h"
|
||||||
#include "net_cfg.h"
|
#include "net_cfg.h"
|
||||||
#include "cfg_file.h"
|
#include "cfg_file.h"
|
||||||
|
#include "run_par.h" /* 运行参数段落盘(卡 10,配置引擎属主 = 本任务) */
|
||||||
#include "prot_set_store.h"
|
#include "prot_set_store.h"
|
||||||
#include "sys_log.h"
|
#include "sys_log.h"
|
||||||
#include "sys_events.h"
|
#include "sys_events.h"
|
||||||
@@ -207,7 +208,7 @@ void default_task_func(void *argument) {
|
|||||||
if (sys_evtHandle != NULL) {
|
if (sys_evtHandle != NULL) {
|
||||||
(void)osEventFlagsWait(sys_evtHandle,
|
(void)osEventFlagsWait(sys_evtHandle,
|
||||||
SYS_EVT_CFG_SAVE | SYS_EVT_CONSOLE_TX
|
SYS_EVT_CFG_SAVE | SYS_EVT_CONSOLE_TX
|
||||||
| SYS_EVT_PROT_SET_SAVE,
|
| SYS_EVT_PROT_SET_SAVE | SYS_EVT_RUN_PAR_SAVE,
|
||||||
osFlagsWaitAny, 50u);
|
osFlagsWaitAny, 50u);
|
||||||
}
|
}
|
||||||
/* 控制台唯一发送点:把环形缓冲中的日志按块发出(无待发时立即返回) */
|
/* 控制台唯一发送点:把环形缓冲中的日志按块发出(无待发时立即返回) */
|
||||||
@@ -231,6 +232,9 @@ void default_task_func(void *argument) {
|
|||||||
/* 保护定值落盘(B7):内部消费脏标志,无改动 = 空操作;
|
/* 保护定值落盘(B7):内部消费脏标志,无改动 = 空操作;
|
||||||
* 写失败脏回置 → iec104Task 下轮 poll 再投递事件 = 天然重试 */
|
* 写失败脏回置 → iec104Task 下轮 poll 再投递事件 = 天然重试 */
|
||||||
(void)prot_set_store_save();
|
(void)prot_set_store_save();
|
||||||
|
/* 运行参数段落盘(卡 10):同款脏语义;save = 引擎整档外科写
|
||||||
|
* ([run_par] 9 键 + 被 adc_cal_set 联动改写的 [adc_cal] 两段一趟落) */
|
||||||
|
(void)run_par_store_save();
|
||||||
#endif
|
#endif
|
||||||
/* 配置文件变更轮询(2s 周期;沉降去抖在引擎内) */
|
/* 配置文件变更轮询(2s 周期;沉降去抖在引擎内) */
|
||||||
if ((cfg_poll_cnt++ % 40u) == 0u) {
|
if ((cfg_poll_cnt++ % 40u) == 0u) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "iec104_pts.h"
|
#include "iec104_pts.h"
|
||||||
#include "net_socket.h"
|
#include "net_socket.h"
|
||||||
#include "prot_set.h" /* 定值脏标志 peek(B7 落盘通知) */
|
#include "prot_set.h" /* 定值脏标志 peek(B7 落盘通知) */
|
||||||
|
#include "run_par.h" /* 运行参数脏 peek(卡 10 落盘通知,同款语义) */
|
||||||
#include "sys_clock.h"
|
#include "sys_clock.h"
|
||||||
#include "sys_events.h"
|
#include "sys_events.h"
|
||||||
#include "task_watchdog.h"
|
#include "task_watchdog.h"
|
||||||
@@ -324,6 +325,11 @@ void iec104_serv_task(void *argument) {
|
|||||||
if ((prot_set_dirty_peek() != 0u) && (sys_evtHandle != NULL)) {
|
if ((prot_set_dirty_peek() != 0u) && (sys_evtHandle != NULL)) {
|
||||||
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_PROT_SET_SAVE);
|
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_PROT_SET_SAVE);
|
||||||
}
|
}
|
||||||
|
/* 运行参数整定(C_SE 0x8020~0x8058 段)→ 通知属主落盘 config.ini
|
||||||
|
* (卡 10;peek 非消费 = 写盘失败天然重试,与上块 B7 语义完全同款) */
|
||||||
|
if ((run_par_dirty_peek() != 0u) && (sys_evtHandle != NULL)) {
|
||||||
|
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_RUN_PAR_SAVE);
|
||||||
|
}
|
||||||
osDelay(IEC104_POLL_MS);
|
osDelay(IEC104_POLL_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1022,6 +1022,11 @@
|
|||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
<FilePath>..\App\algo\meas.c</FilePath>
|
<FilePath>..\App\algo\meas.c</FilePath>
|
||||||
</File>
|
</File>
|
||||||
|
<File>
|
||||||
|
<FileName>run_par.c</FileName>
|
||||||
|
<FileType>1</FileType>
|
||||||
|
<FilePath>..\App\algo\run_par.c</FilePath>
|
||||||
|
</File>
|
||||||
<File>
|
<File>
|
||||||
<FileName>prot_set.c</FileName>
|
<FileName>prot_set.c</FileName>
|
||||||
<FileType>1</FileType>
|
<FileType>1</FileType>
|
||||||
|
|||||||
@@ -112,6 +112,31 @@
|
|||||||
开机在 STORAGE_READY 屏障前装载,损坏/越界/版本换代 = 整表拒绝保持出厂默认
|
开机在 STORAGE_READY 屏障前装载,损坏/越界/版本换代 = 整表拒绝保持出厂默认
|
||||||
(all-or-nothing)+ 自检留痕 `prot set corrupt`;写盘失败脏标志保留 = 下轮重试。
|
(all-or-nothing)+ 自检留痕 `prot set corrupt`;写盘失败脏标志保留 = 下轮重试。
|
||||||
|
|
||||||
|
**运行参数段 `0x8020H~0x8058H`(「数据可信」卡 10,2026-10-25 落实)**:
|
||||||
|
|
||||||
|
> ⚠ 逐项地址为**本仓自定**(`docs/算法/继电保护功能复现规划.md` §5.4 的权威表不在仓——
|
||||||
|
参考项目 `CommPara_ConfigTable` 拿不到,送检点表到位后**以送检为准替换常量表**,
|
||||||
|
映射表只落在 `App/algo/run_par.c` 顶部常量段一处)。
|
||||||
|
|
||||||
|
| 地址 | 项 | 域(C_SE/C_RD 浮点工程量) | 工厂默认 | 真源与守卫 |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 0x8030~0x8038 | 遥测死区 UA/UB/UC/UN/IA/IB/IC/IN/FREQ | V / A / Hz,分档上限 50/1/5 | 卡 6 额定 0.5 % 档(0.5 V ×4 / 0.005 A ×4 / 0.5 Hz) | `run_par` [run_par] 段 9 键 int32 毫单位;落盘键表 → `docs/子系统设计/网络配置说明.md` §3.5 |
|
||||||
|
| 0x8040~0x8047 | 通道 k 覆盖(µV/码) | 正浮点;adc_cal 合法带 ROM×[0.5,2.0] 执行段裁决 | 回显生效值(ROM = 152.587890625 / 805.6640625) | **写 = `adc_cal_set` 代理,系数唯一真源在 [adc_cal] 段,run_par 不存系数**(防后人拿 run_par 表当真源表) |
|
||||||
|
| 0x8048~0x804F | 通道 b 覆盖(码值,可负) | ±8192(adc_cal 带) | 0(回显) | 同上;单侧写 RMW 用另一侧**当前生效值** |
|
||||||
|
| 其余 | 留白(PT/CT 变比等未启用类别) | — | — | C_RD → COT14+EVI / C_SE → actcon+PN |
|
||||||
|
|
||||||
|
- `0x8027~0x802A` 是 prot_set 告警段(过压/欠压),**run_par 命中集精确匹配、永不覆盖**;
|
||||||
|
两表共存的路由顺序 = **run_par 表先、未命中落 prot_set**(`iec104_pts.c` set 路由,
|
||||||
|
证据钉在 TC-IEC104-029)。
|
||||||
|
- 现行门控 = 两段式 + 范围/合法带校验(与保护定值同款),**无「仅暂停/测试态可写」
|
||||||
|
门控**——文档现行如此,不造新语义;若送检点表要求该类门控,接线点在
|
||||||
|
`run_par_104_write` 执行段入口,加一处即可。
|
||||||
|
- 持久化(卡 10):任一写成功置 `run_par` 脏 → iec104Task 同款 peek 投递
|
||||||
|
`SYS_EVT_RUN_PAR_SAVE`(1<<8) → defaultTask `run_par_store_save()` 调 `cfg_file_save()`
|
||||||
|
整档外科保存(一趟覆盖 [run_par] 9 键 + 被代理改写的 [adc_cal]/[adc_cal_ofs],
|
||||||
|
lead 裁②一条事件位一趟够用);失败脏保留 = 下轮重试。消费侧(COV 判定)**实时读
|
||||||
|
引擎副本**,无缓存无投递(卡 9 的 pts 层 s_yc_db 副本已收编进 run_par)。
|
||||||
|
|
||||||
## 4. 链路与流程
|
## 4. 链路与流程
|
||||||
|
|
||||||
- 参数:k=12 / w=8 / t1=15s / t2=10s / t3=20s(标准默认);序号 15bit 回绕,
|
- 参数:k=12 / w=8 / t1=15s / t2=10s / t3=20s(标准默认);序号 15bit 回绕,
|
||||||
@@ -172,7 +197,7 @@
|
|||||||
|
|
||||||
| 层 | 内容 |
|
| 层 | 内容 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| L0.5 | `test/host/iec104/`:APCI 重组/坏帧、STARTDT 握手、总召全流程(含 RBT/分片/预算)、COV、k/w 窗、t1/t2/t3(假时基)、遥控两段式与 PN、YK12 软压板两段转真 + 置脏(B7)、定值两段写 + set_sealed 后 selfcheck、C_RD(含本地项不可寻址)、C_CS(含闰年)、错地址/错类型 EVI;**板端默认源真实链**(ring→phasor→freq→meas 快照,非桩):值/品质透传、CLIPPED→IV、0x401D 绑频(收敛 ±0.02 Hz / 未新鲜 IV)、死区运行时化 + 2 点确认/回摆撤销、seq 去重(卡 9/11,TC-IEC104-025~028);`test/host/prot_set/`:blob 往返 + 损坏整表拒绝(all-or-nothing)|
|
| L0.5 | `test/host/iec104/`:APCI 重组/坏帧、STARTDT 握手、总召全流程(含 RBT/分片/预算)、COV、k/w 窗、t1/t2/t3(假时基)、遥控两段式与 PN、YK12 软压板两段转真 + 置脏(B7)、定值两段写 + set_sealed 后 selfcheck、C_RD(含本地项不可寻址)、C_CS(含闰年)、错地址/错类型 EVI;**板端默认源真实链**(ring→phasor→freq→meas 快照,非桩):值/品质透传、CLIPPED→IV、0x401D 绑频(收敛 ±0.02 Hz / 未新鲜 IV)、死区运行时化 + 2 点确认/回摆撤销、seq 去重(卡 9/11,TC-IEC104-025~028)+ 运行参数段路由/两段式/RMW(卡 10,TC-IEC104-029);`test/host/run_par/`:[run_par] 注册/落盘往返/守卫/系数直通合法带(TC-RUNPAR-001~008);`test/host/prot_set/`:blob 往返 + 损坏整表拒绝(all-or-nothing)|
|
||||||
| L0 | `iec104-unit-test` 钩子 + cppcheck/coding-standard |
|
| L0 | `iec104-unit-test` 钩子 + cppcheck/coding-standard |
|
||||||
| 台架 | `@test` 锚点(104 服务启动/监听行);**PC 主站客户端 `test/host/iec104_master.py`(纯标准库,B6 2026-10-12 就绪)**:STARTDT/总召四组+actterm/C_SE 定值两段写→YX15·16 变位(COT=3)/恢复复归/YK11 两段正答/C_RD 读回,台架往返 **18/18 PASS**(证据 `../测试说明/boot_log_data/20261012_B6_iec104_roundtrip_log.txt`);**B7 持久化模式 `--b7-pre/--b7-post/--b7-mark/--b7-check`**(中间 ST-LINK 复位):YK12 两段投退→保护退出/联动、**定值与压板态跨复位保持**(含非默认 7.5 往返),**21/21 + 13/13 + 4/4+4/4 PASS**(证据 `../测试说明/boot_log_data/20261012_B7_iec104_persist_log.txt`);**B9 SOE 腿**(同工具同轮回归):自发 YX 断言 **M_SP_TB_1 带 ts**(工具解析 type 30/1 双型)+ FTP 下载 `/log/soe.log` 经 `test/host/soe/decode_soe.py` 解码断言(校验和/seq 单调/CP56↔epoch UTC 交叉),pre 21/21 + post 13/13 不回归;lib60870 级第三方全流程归 B10 |
|
| 台架 | `@test` 锚点(104 服务启动/监听行);**PC 主站客户端 `test/host/iec104_master.py`(纯标准库,B6 2026-10-12 就绪)**:STARTDT/总召四组+actterm/C_SE 定值两段写→YX15·16 变位(COT=3)/恢复复归/YK11 两段正答/C_RD 读回,台架往返 **18/18 PASS**(证据 `../测试说明/boot_log_data/20261012_B6_iec104_roundtrip_log.txt`);**B7 持久化模式 `--b7-pre/--b7-post/--b7-mark/--b7-check`**(中间 ST-LINK 复位):YK12 两段投退→保护退出/联动、**定值与压板态跨复位保持**(含非默认 7.5 往返),**21/21 + 13/13 + 4/4+4/4 PASS**(证据 `../测试说明/boot_log_data/20261012_B7_iec104_persist_log.txt`);**B9 SOE 腿**(同工具同轮回归):自发 YX 断言 **M_SP_TB_1 带 ts**(工具解析 type 30/1 双型)+ FTP 下载 `/log/soe.log` 经 `test/host/soe/decode_soe.py` 解码断言(校验和/seq 单调/CP56↔epoch UTC 交叉),pre 21/21 + post 13/13 不回归;lib60870 级第三方全流程归 B10 |
|
||||||
|
|
||||||
@@ -183,8 +208,10 @@
|
|||||||
双点带时标不做(V0.1 双点恒 IV,无意义)。
|
双点带时标不做(V0.1 双点恒 IV,无意义)。
|
||||||
3. YK1~4/13/14 否定确认——出口硬件确认后(R7)转真执行(YK12 已于 B7 转真)。
|
3. YK1~4/13/14 否定确认——出口硬件确认后(R7)转真执行(YK12 已于 B7 转真)。
|
||||||
4. ~~遥测死区 10.0 码值为拟定值~~——工程量死区已于 2026-10-25 卡 6 落地(额定 0.5%);
|
4. ~~遥测死区 10.0 码值为拟定值~~——工程量死区已于 2026-10-25 卡 6 落地(额定 0.5%);
|
||||||
卡 9(2026-10-25)已改**运行时副本 + set/get 入口 + 2 点确认**;主站可读写(8020H 段
|
卡 9 已加 2 点确认;卡 10(2026-10-25)已落地 **0x8020~0x8058 读写映射 + [run_par]
|
||||||
run_par 接线 `iec104_pts_yc_set_deadband`)仍归「数据可信」卡 10。
|
持久化 + 系数覆盖直通 adc_cal**——死区系数唯一真源 = cfg 引擎副本(run_par 存死区、
|
||||||
|
**不存系数**,系数真源在 [adc_cal] 段,见 §3.4 表注),`iec104_pts_yc_set_deadband`
|
||||||
|
降级为转发壳。
|
||||||
5. 多主站/双链路冗余不做(单连接语义)。
|
5. 多主站/双链路冗余不做(单连接语义)。
|
||||||
6. 主站侧验证工具 = `test/host/iec104_master.py`(B6 2026-10-12 就绪,台架往返 18/18,覆盖总召/变位/遥控/定值);**第三方送检环境仍未就绪**——lib60870 级第三方全流程归 B10。
|
6. 主站侧验证工具 = `test/host/iec104_master.py`(B6 2026-10-12 就绪,台架往返 18/18,覆盖总召/变位/遥控/定值);**第三方送检环境仍未就绪**——lib60870 级第三方全流程归 B10。
|
||||||
7. **后连优先未实现**(V0.1 = 首到优先):单连接 socket 连接态无法 accept,
|
7. **后连优先未实现**(V0.1 = 首到优先):单连接 socket 连接态无法 accept,
|
||||||
@@ -201,5 +228,6 @@
|
|||||||
| 2026-10-12 | **B7 定值持久化 + 软压板**:YK12 转真(§3.3,两段式 → `PS_GLOBAL_EN`);定值执行段改 `prot_set_set_sealed` 原子写+封存(§3.4,收口 B5 竞态窗口);本地项 `comm_addr=0` 不可寻址;持久化 = 脏标志 → `SYS_EVT_PROT_SET_SAVE` → defaultTask 原子写 `/prot_set.bin`,开机屏障前整表原子装载(损坏拒绝保默认 + 自检留痕);单测 +7(iec104 94 断言)+ prot_set blob 往返/损坏 33 断言 |
|
| 2026-10-12 | **B7 定值持久化 + 软压板**:YK12 转真(§3.3,两段式 → `PS_GLOBAL_EN`);定值执行段改 `prot_set_set_sealed` 原子写+封存(§3.4,收口 B5 竞态窗口);本地项 `comm_addr=0` 不可寻址;持久化 = 脏标志 → `SYS_EVT_PROT_SET_SAVE` → defaultTask 原子写 `/prot_set.bin`,开机屏障前整表原子装载(损坏拒绝保默认 + 自检留痕);单测 +7(iec104 94 断言)+ prot_set blob 往返/损坏 33 断言 |
|
||||||
| 2026-10-12 | **B7 台架腿闭环**(§7 台架行回填):主站工具加 `--b7-pre/--b7-post/--b7-mark/--b7-check` 四模式(中间 ST-LINK 复位);pre 21/21(YK12 OFF→低定值不动作 / ON→YX15·16 即动作)、post 13/13(**压板退出态跨复位保持** + 重启后联动在位)、mark/check 4/4+4/4(**非默认定值 7.5 跨复位保持**);开机日志 `[CFG] prot set loaded (/prot_set.bin)`;证据 `../测试说明/boot_log_data/20261012_B7_*` |
|
| 2026-10-12 | **B7 台架腿闭环**(§7 台架行回填):主站工具加 `--b7-pre/--b7-post/--b7-mark/--b7-check` 四模式(中间 ST-LINK 复位);pre 21/21(YK12 OFF→低定值不动作 / ON→YX15·16 即动作)、post 13/13(**压板退出态跨复位保持** + 重启后联动在位)、mark/check 4/4+4/4(**非默认定值 7.5 跨复位保持**);开机日志 `[CFG] prot set loaded (/prot_set.bin)`;证据 `../测试说明/boot_log_data/20261012_B7_*` |
|
||||||
| 2026-10-25 | **标定折算落地联动(卡 6)**:遥测读数由原始码值改工程量(§3.1 注已重写);死区按额定 0.5% 逐点重定(0.5 V / 0.005 A / 0.5 Hz),§8 第 4 条改「已落地,8020H 段仍归卡 10」 |
|
| 2026-10-25 | **标定折算落地联动(卡 6)**:遥测读数由原始码值改工程量(§3.1 注已重写);死区按额定 0.5% 逐点重定(0.5 V / 0.005 A / 0.5 Hz),§8 第 4 条改「已落地,8020H 段仍归卡 10」 |
|
||||||
|
| 2026-10-25 | **卡 10(道D,「数据可信」M6-①)**:新增 `App/algo/run_par.c/h`——遥测死区 9 点真源收编([run_par] 段 9 键 int32 毫单位落盘,工厂默认 = 卡 6 档;pts 层 s_yc_db 副本删除、set/get 转转发壳并保留清确认计数语义);104 **0x8020~0x8058 读写映射**(§3.4 新表:逐项地址本仓自定、送检点表到位后替换常量段;0x8027~0x802A 永不覆盖,路由 run_par 先 prot_set 后);**k/b 系数覆盖 = adc_cal_set 代理**(合法带/越带留痕复用,run_par 不存系数);持久化走新事件位 `SYS_EVT_RUN_PAR_SAVE`(1<<8) 整档外科 save;单测 test/host/run_par 新建(TC-RUNPAR-001~008,67 断言)+ iec104 套 +TC-IEC104-029 路由钉(141 断言);台架/送检腿待排窗 |
|
||||||
| 2026-10-25 | **卡 9/卡 11(道D,「数据可信」M5/M6)**:YC 板端源改读 L2 遥测快照(削顶五路从点表绑死 -1 改为**槽位绑定 + 快照品质 CLIPPED→IV**,V0.2/N-18 零改动启用);`0x401D` 绑频率源(`freq_valid()==0` → IV);死区改运行时副本 + `iec104_pts_yc_set_deadband()`(卡 10 接线点);COV 遥测腿 = 死区连续 2 点确认(快照 `seq` 去重)+ 品质翻转直报;`iec104_pts_src_t` 表尾追加 `yc_read_ex`(旧源退路语义不变);单测 97→127(TC-IEC104-025~028 走真实 L2 链);台架腿(总召读回一致 / 频率 ±0.01 Hz)待排窗——主站工具已记 val+qds,无需改工具 |
|
| 2026-10-25 | **卡 9/卡 11(道D,「数据可信」M5/M6)**:YC 板端源改读 L2 遥测快照(削顶五路从点表绑死 -1 改为**槽位绑定 + 快照品质 CLIPPED→IV**,V0.2/N-18 零改动启用);`0x401D` 绑频率源(`freq_valid()==0` → IV);死区改运行时副本 + `iec104_pts_yc_set_deadband()`(卡 10 接线点);COV 遥测腿 = 死区连续 2 点确认(快照 `seq` 去重)+ 品质翻转直报;`iec104_pts_src_t` 表尾追加 `yc_read_ex`(旧源退路语义不变);单测 97→127(TC-IEC104-025~028 走真实 L2 链);台架腿(总召读回一致 / 频率 ±0.01 Hz)待排窗——主站工具已记 val+qds,无需改工具 |
|
||||||
| 2026-10-12 | **B9 时标 SOE**:自发单点 YX 换 **M_SP_TB_1(30)**(SIQ+CP56,§3.2 注;总召/周期保持 NA 型);link cfg 增 `epoch_ms` 时标源(NULL/无效 → IV 如实);serv 绑 `sys_clock`(秒域 → ms 域=秒×1000);主站工具解析 type 30/1 双型;单测 iec104 94→97(TB 时标 + IV 路径,两种类型都断言);台架腿见 §7 B9 行;落盘侧见 `App/log/README.md` soe 节 |
|
| 2026-10-12 | **B9 时标 SOE**:自发单点 YX 换 **M_SP_TB_1(30)**(SIQ+CP56,§3.2 注;总召/周期保持 NA 型);link cfg 增 `epoch_ms` 时标源(NULL/无效 → IV 如实);serv 绑 `sys_clock`(秒域 → ms 域=秒×1000);主站工具解析 type 30/1 双型;单测 iec104 94→97(TB 时标 + IV 路径,两种类型都断言);台架腿见 §7 B9 行;落盘侧见 `App/log/README.md` soe 节 |
|
||||||
|
|||||||
@@ -150,6 +150,27 @@ write=1
|
|||||||
|
|
||||||
> **诚实边界**:FTP 协议**明文传输口令**——本方案挡的是**未授权访问**,不挡**网络嗅探**(CH395F 上实现 TLS 不现实)。`pwd_hash` 只防 `/config.ini` 泄露后的**离线口令还原**,盐 = 用户名(无每机随机盐,局域网威胁模型下够用)。
|
> **诚实边界**:FTP 协议**明文传输口令**——本方案挡的是**未授权访问**,不挡**网络嗅探**(CH395F 上实现 TLS 不现实)。`pwd_hash` 只防 `/config.ini` 泄露后的**离线口令还原**,盐 = 用户名(无每机随机盐,局域网威胁模型下够用)。
|
||||||
|
|
||||||
|
### 3.5 `[run_par]` 段:运行参数(2026-10-25 新增,「数据可信」卡 10)
|
||||||
|
|
||||||
|
遥测死区 9 点的持久载体(引擎属主 defaultTask;**现场远程写入口 = 104 段
|
||||||
|
`0x8030~0x8038`**,本段键供 FTP 手改 / 排障直读,逐项语义与地址表 →
|
||||||
|
`docs/子系统设计/IEC104上行设计说明.md` §3.4)。**值 = 工程量毫单位 int32 有符号**
|
||||||
|
(`db_ua=500` 即 0.5 V;电流域 `db_ia=5` 即 0.005 A;频率域 `db_freq=500` 即 0.5 Hz)。
|
||||||
|
|
||||||
|
| 键 | 通道 | 工厂默认 | 104 写路径合法域(毫单位) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `db_ua` `db_ub` `db_uc` `db_un` | 电压死区 | 500 | 0 ~ 50000(50 V) |
|
||||||
|
| `db_ia` `db_ib` `db_ic` `db_in` | 电流死区 | 5 | 0 ~ 1000(1 A) |
|
||||||
|
| `db_freq` | 频率死区 | 500 | 0 ~ 5000(5 Hz) |
|
||||||
|
|
||||||
|
- 键数 9 ≤ 16,**通道系数覆盖不落本段**——系数唯一真源 = `[adc_cal]` / `[adc_cal_ofs]`
|
||||||
|
两段(卡 4 布局,docs/任务规划/数据可信.md §4.4;104 `0x8040~0x804F` 写 =
|
||||||
|
`adc_cal_set` 代理,lead 裁②a)。
|
||||||
|
- 守卫在**写路径**(104/壳 API 拒 NaN/负/越程);**文件通路引擎照收**(同 adc_cal 的
|
||||||
|
「人工编辑 + 沉降去抖」契约)——死区超程只让上送变钝、不毁量纲,无合法带哨兵。
|
||||||
|
- 生效链路:load/poll 原地改副本 → 104 COV 每拍实时读(无回调,卡 9/10 契约);
|
||||||
|
104 写成功 → `SYS_EVT_RUN_PAR_SAVE` → 本任务 `cfg_file_save()` 整档外科落盘。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 生效链路
|
## 4. 生效链路
|
||||||
@@ -308,6 +329,7 @@ ENTER 进入选中地址行 → `↑/↓` 调活动字节(0..255 **环绕**)
|
|||||||
|
|
||||||
| 日期 | 变更 |
|
| 日期 | 变更 |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
| 2026-10-25 | v2.4:**新增 §3.5 `[run_par]` 段(「数据可信」卡 10)**——遥测死区 9 键毫单位布局、104 写路径合法域、系数覆盖不落本段(真源 [adc_cal])、守卫仅在写路径、事件化落盘 |
|
||||||
| 2026-10-11 | v2.3:**新增 §3.4 `[ftp]` 段(N-19 FTP 鉴权)**——`user` / `pwd_hash`(SHA-256(user‖password))/ `write` 字段表、鉴权流程(USER 恒 331 / 恒定时间比较 / 未登录命令门 / 5 败锁 60 s)、改口令步骤与忘口令恢复路径、出厂默认口令启动告警、明文嗅探边界 |
|
| 2026-10-11 | v2.3:**新增 §3.4 `[ftp]` 段(N-19 FTP 鉴权)**——`user` / `pwd_hash`(SHA-256(user‖password))/ `write` 字段表、鉴权流程(USER 恒 331 / 恒定时间比较 / 未登录命令门 / 5 败锁 60 s)、改口令步骤与忘口令恢复路径、出厂默认口令启动告警、明文嗅探边界 |
|
||||||
| 2026-10-10 | v2.2:**删除旧档 NCFG 迁移**——`net_cfg_legacy.c/h` 整体移除、`cfg_store_legacy_path()` 撤销;旧档 `/cfg/netcfg.bin` 若存在则被忽略(不读、不删、不迁移),配置回落工厂默认。理由:该一次性路径把整条配置写链挂进启动期调用图,defaultTask 静态最坏栈链 +600 B,且本仓库无携带旧档的设备 |
|
| 2026-10-10 | v2.2:**删除旧档 NCFG 迁移**——`net_cfg_legacy.c/h` 整体移除、`cfg_store_legacy_path()` 撤销;旧档 `/cfg/netcfg.bin` 若存在则被忽略(不读、不删、不迁移),配置回落工厂默认。理由:该一次性路径把整条配置写链挂进启动期调用图,defaultTask 静态最坏栈链 +600 B,且本仓库无携带旧档的设备 |
|
||||||
| 2026-09-14 | v2.1:存储迁移至**通用配置文件** `/config.ini`(INI `[network]` 段,cfg_file 引擎:逐键校验 / 外科手术式保存 / 原子写 / 旧档首启迁移 / 沉降轮询);`net_cfg_store.*` 弃用(由 `cfg_file_store.*` 取代);对外 API 不变 |
|
| 2026-09-14 | v2.1:存储迁移至**通用配置文件** `/config.ini`(INI `[network]` 段,cfg_file 引擎:逐键校验 / 外科手术式保存 / 原子写 / 旧档首启迁移 / 沉降轮询);`net_cfg_store.*` 弃用(由 `cfg_file_store.*` 取代);对外 API 不变 |
|
||||||
|
|||||||
@@ -409,6 +409,11 @@
|
|||||||
|
|
||||||
> ⚠️ **待补**:逐项**取值范围 / 步长 / 默认值**取自参考项目 `APP/EquipCfg/Const_CommPara.c` 的 `CommPara_ConfigTable`(`0x8020~0x8882`);本表在实现各保护元件时逐项落实(见 §10.3)。
|
> ⚠️ **待补**:逐项**取值范围 / 步长 / 默认值**取自参考项目 `APP/EquipCfg/Const_CommPara.c` 的 `CommPara_ConfigTable`(`0x8020~0x8882`);本表在实现各保护元件时逐项落实(见 §10.3)。
|
||||||
>
|
>
|
||||||
|
> **2026-10-25 运行参数段落实进度**:卡 10 已落 `App/algo/run_par.c`——本仓自定逐项地址
|
||||||
|
> 0x8030~0x8038(死区 9 点)/ 0x8040~0x804F(k/b 覆盖直通 adc_cal),表与理由见
|
||||||
|
> `docs/子系统设计/IEC104上行设计说明.md` §3.4;**送检点表到位后以送检为准替换常量段**。
|
||||||
|
> 变比/报警/录波/功率置负等其余运行参数类项目前无消费者,留白不预埋。
|
||||||
|
>
|
||||||
> **2026-10-11 落实进度**:`App/algo/prot_set.c` 已建 RAM 模型(Q10 定点,31 项),**通讯地址逐项对照参考 `Comm/Pub/Const_CommSetPara.c` `g_tCommSetParaTab`**(基址 0x8820:过流 Ⅰ/Ⅱ/Ⅲ = 0x8856~0x8861、过负荷 = 0x8871~0x8874、零序过压 = 0x888E~0x8890;过压/欠压沿用告警段 0x8027~0x802A)。⚠ **默认值/范围为拟定值**(参考 EEPROM 出厂默认表不在本仓库,按 DTU 馈线典型量级拟定),待对照参考出厂表逐项修订。
|
> **2026-10-11 落实进度**:`App/algo/prot_set.c` 已建 RAM 模型(Q10 定点,31 项),**通讯地址逐项对照参考 `Comm/Pub/Const_CommSetPara.c` `g_tCommSetParaTab`**(基址 0x8820:过流 Ⅰ/Ⅱ/Ⅲ = 0x8856~0x8861、过负荷 = 0x8871~0x8874、零序过压 = 0x888E~0x8890;过压/欠压沿用告警段 0x8027~0x802A)。⚠ **默认值/范围为拟定值**(参考 EEPROM 出厂默认表不在本仓库,按 DTU 馈线典型量级拟定),待对照参考出厂表逐项修订。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ SIM_EXEMPT = [
|
|||||||
("App/algo/sample_ring.c", "L2 环形缓冲纯算法,模拟器无消费路径"),
|
("App/algo/sample_ring.c", "L2 环形缓冲纯算法,模拟器无消费路径"),
|
||||||
("App/algo/adc_cal.c", "L1.5 标定折算纯算法 + cfg 段注册(消费者 = MCU 侧 adcTask 每拍;模拟器不跑采样任务),模拟器无消费路径"),
|
("App/algo/adc_cal.c", "L1.5 标定折算纯算法 + cfg 段注册(消费者 = MCU 侧 adcTask 每拍;模拟器不跑采样任务),模拟器无消费路径"),
|
||||||
("App/algo/meas.c", "L2 遥测快照发布纯算法(唯一发布者 = MCU 侧 adcTask 200 ms 分频;104/UI 侧消费属卡 9/UI 波次另行接线,模拟器无消费路径;L0.5 单测 test/host/meas 覆盖),模拟器无消费路径"),
|
("App/algo/meas.c", "L2 遥测快照发布纯算法(唯一发布者 = MCU 侧 adcTask 200 ms 分频;104/UI 侧消费属卡 9/UI 波次另行接线,模拟器无消费路径;L0.5 单测 test/host/meas 覆盖),模拟器无消费路径"),
|
||||||
|
("App/algo/run_par.c", "运行参数段纯算法 + [run_par] cfg 段注册 + 104 地址映射(写者 = iec104Task 直通、属主落盘 defaultTask;模拟器不跑 104 协议,模拟器无消费路径;L0.5 单测 test/host/run_par 覆盖),模拟器无消费路径"),
|
||||||
("App/app/app_main.c", "固件启动/就绪屏障,MCU 专属"),
|
("App/app/app_main.c", "固件启动/就绪屏障,MCU 专属"),
|
||||||
("App/app/ui_data_feed.c", "UI 快照装配器(依赖 phasor/prot_set/iec104 端口常量等固件侧数据源);模拟器不跑装配链,快照由 simulator/main.c 直接发布固定帧,模拟器无消费路径"),
|
("App/app/ui_data_feed.c", "UI 快照装配器(依赖 phasor/prot_set/iec104 端口常量等固件侧数据源);模拟器不跑装配链,快照由 simulator/main.c 直接发布固定帧,模拟器无消费路径"),
|
||||||
("App/fs/vol_lock.c", "FreeRTOS 互斥量卷锁,MCU 专属"),
|
("App/fs/vol_lock.c", "FreeRTOS 互斥量卷锁,MCU 专属"),
|
||||||
|
|||||||
@@ -1796,6 +1796,29 @@
|
|||||||
"owner_note": "B 层口径(用户裁定 2026-10-21,同 TC-HOST-ADCHEALTH/TC-HOST-ADCCAL 先例;2026-10-25 道D 卡8 建族):属主登记处 = 单测代码目录,不逐号补文档",
|
"owner_note": "B 层口径(用户裁定 2026-10-21,同 TC-HOST-ADCHEALTH/TC-HOST-ADCCAL 先例;2026-10-25 道D 卡8 建族):属主登记处 = 单测代码目录,不逐号补文档",
|
||||||
"cursor_note": "游标按建族时实测最大号收录(2026-10-25):越界即视为未登记的冒号牌"
|
"cursor_note": "游标按建族时实测最大号收录(2026-10-25):越界即视为未登记的冒号牌"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "TC-HOST-RUNPAR",
|
||||||
|
"class": "TC",
|
||||||
|
"title": "主机侧单测用例号:运行参数段(test/host/run_par,道D 卡10)",
|
||||||
|
"pattern": "\\bTC-RUNPAR-[0-9]{1,4}[a-z]?\\b",
|
||||||
|
"digit_width": [
|
||||||
|
2,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"owner_docs_glob": [
|
||||||
|
"test/host/run_par/*"
|
||||||
|
],
|
||||||
|
"scope": "global",
|
||||||
|
"frozen": false,
|
||||||
|
"cursor": "TC-RUNPAR-008",
|
||||||
|
"cursor_owner_only": true,
|
||||||
|
"check_dangling": true,
|
||||||
|
"allow_orphans": true,
|
||||||
|
"subitem_ok": true,
|
||||||
|
"owner_note": "B 层口径(用户裁定 2026-10-21,同 TC-HOST-MEAS/TC-HOST-ADCCAL 先例;2026-10-25 道D 卡 10 建族):属主登记处 = 单测代码目录,不逐号补文档;路由顺序证据链在 TC-IEC104-029(test/host/iec104)",
|
||||||
|
"cursor_note": "游标按建族时实测最大号收录(2026-10-25):越界即视为未登记的冒号牌"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "TC-HOST-IEC104",
|
"name": "TC-HOST-IEC104",
|
||||||
"class": "TC",
|
"class": "TC",
|
||||||
@@ -1810,7 +1833,7 @@
|
|||||||
],
|
],
|
||||||
"scope": "global",
|
"scope": "global",
|
||||||
"frozen": false,
|
"frozen": false,
|
||||||
"cursor": "TC-IEC104-028",
|
"cursor": "TC-IEC104-029",
|
||||||
"cursor_owner_only": true,
|
"cursor_owner_only": true,
|
||||||
"check_dangling": true,
|
"check_dangling": true,
|
||||||
"allow_orphans": true,
|
"allow_orphans": true,
|
||||||
|
|||||||
@@ -41,10 +41,12 @@
|
|||||||
* →线上 IV 清零 / 干净路品质 0 / 频率未新鲜 IV
|
* →线上 IV 清零 / 干净路品质 0 / 频率未新鲜 IV
|
||||||
* TC-IEC104-026 0x401D 绑频:收敛后 ±0.02 Hz / freq_valid==0 → IV 清零
|
* TC-IEC104-026 0x401D 绑频:收敛后 ±0.02 Hz / freq_valid==0 → IV 清零
|
||||||
* (卡 11 DoD 原句)
|
* (卡 11 DoD 原句)
|
||||||
* TC-IEC104-027 死区运行时化:set/get + 非法拒 + 运行档压住旧档步长 +
|
* TC-IEC104-027 死区运行时化(卡 10 后 = run_par 真源 + 转发壳):set/get
|
||||||
* 2 点确认/回摆撤销/init 复位 ROM 默认
|
* + 非法拒 + 运行档压旧档 + 2 点确认/回摆撤销 + 壳清半截确认计数
|
||||||
* TC-IEC104-028 品质翻转直报(假源)+ 板端 seq 去重(同帧不计两点)+
|
* TC-IEC104-028 品质翻转直报(假源)+ 板端 seq 去重(同帧不计两点)+
|
||||||
* 换帧只变品质时按集合直报
|
* 换帧只变品质时按集合直报
|
||||||
|
* TC-IEC104-029 卡 10 路由:0x8030/0x8038 命中 / 0x8027 仍归 prot_set /
|
||||||
|
* 空隙 COT14 / C_SE 两段式 / k 带拒 / b 单侧 RMW 保 k
|
||||||
*
|
*
|
||||||
* 通过:全部 TC PASS(main 返回 0)。
|
* 通过:全部 TC PASS(main 返回 0)。
|
||||||
* 创建日期:2026-10-12(B4)
|
* 创建日期:2026-10-12(B4)
|
||||||
@@ -67,6 +69,115 @@ static int s_total = 0, s_passed = 0, s_failed = 0;
|
|||||||
#include "phasor.c"
|
#include "phasor.c"
|
||||||
#include "freq.c"
|
#include "freq.c"
|
||||||
#include "meas.c" /* 卡 9:板端默认 YC 源 = L2 遥测快照(真实链,非桩) */
|
#include "meas.c" /* 卡 9:板端默认 YC 源 = L2 遥测快照(真实链,非桩) */
|
||||||
|
|
||||||
|
/* ===================== 卡 10 真链桩(同 adc_cal 套件先例) =====================
|
||||||
|
* run_par 依赖真实 cfg 引擎与 adc_cal:dbg_log 走 stub/ 计数宏,文件/堆走
|
||||||
|
* 内存虚拟盘(cfg_store_* 强符号),adc_input_desc 按 V0.1 权威映射给桩。
|
||||||
|
* 顺序敏感:桩头与强符号先于被纳入的 .c。 */
|
||||||
|
#include "dbg_log.h" /* stub/ 优先解析:DBG_* → 计数 */
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "cfg_file_store.h"
|
||||||
|
|
||||||
|
#define RP_VFS_CAP 8192u
|
||||||
|
|
||||||
|
static uint8_t rp_s_vfs[RP_VFS_CAP];
|
||||||
|
static size_t rp_s_vfs_len = 0u;
|
||||||
|
static uint32_t rp_s_vfs_mtime = 100u;
|
||||||
|
static int rp_s_vfs_exists = 0;
|
||||||
|
|
||||||
|
void *cfg_store_alloc(size_t size) { return malloc(size); }
|
||||||
|
void cfg_store_free(void *p_buf) { free(p_buf); }
|
||||||
|
uint32_t cfg_store_heap_free(void) { return 0u; }
|
||||||
|
|
||||||
|
int cfg_store_read(const char *p_path, uint8_t *p_buf, size_t cap, size_t *p_nread) {
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
(void)p_path;
|
||||||
|
if (!rp_s_vfs_exists) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n = rp_s_vfs_len;
|
||||||
|
if (n > cap) {
|
||||||
|
n = cap;
|
||||||
|
}
|
||||||
|
(void)memcpy(p_buf, rp_s_vfs, n);
|
||||||
|
*p_nread = n;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len) {
|
||||||
|
(void)p_path;
|
||||||
|
if (len > RP_VFS_CAP) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
(void)memcpy(rp_s_vfs, p_buf, len);
|
||||||
|
rp_s_vfs_len = len;
|
||||||
|
rp_s_vfs_exists = 1;
|
||||||
|
rp_s_vfs_mtime++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg_store_stat(const char *p_path, cfg_file_stat_t *p_st) {
|
||||||
|
(void)p_path;
|
||||||
|
if (!rp_s_vfs_exists) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
p_st->size = (uint32_t)rp_s_vfs_len;
|
||||||
|
p_st->mtime = rp_s_vfs_mtime;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg_store_remove(const char *p_path) {
|
||||||
|
(void)p_path;
|
||||||
|
rp_s_vfs_exists = 0;
|
||||||
|
rp_s_vfs_len = 0u;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cfg_store_config_path(void) { return "/config.ini"; }
|
||||||
|
const char *cfg_store_tmp_path(void) { return "/config.tmp"; }
|
||||||
|
|
||||||
|
/* ---- sys_log 桩:只计数自检留痕 ---- */
|
||||||
|
#include "sys_log.h"
|
||||||
|
|
||||||
|
static int rp_s_syslog_count = 0;
|
||||||
|
|
||||||
|
void syslog_post(syslog_id_t id, const char *desc) {
|
||||||
|
(void)id;
|
||||||
|
(void)desc;
|
||||||
|
rp_s_syslog_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- adc_input_desc 桩:V0.1 权威映射(docs/硬件平台/ADC通道映射说明.md) ---- */
|
||||||
|
#include "adc_input.h"
|
||||||
|
|
||||||
|
static const adc_channel_desc_t rp_s_desc[ADC_SEM_COUNT] = {
|
||||||
|
{ ADC_SRC_TPAFE, 6u }, /* UA = buf[6] */
|
||||||
|
{ ADC_SRC_STM32, 14u }, /* UB = CH14(PC4) */
|
||||||
|
{ ADC_SRC_STM32, 3u }, /* UC = CH3 */
|
||||||
|
{ ADC_SRC_TPAFE, 5u }, /* UN = buf[5] */
|
||||||
|
{ ADC_SRC_TPAFE, 2u }, /* IA = buf[2] */
|
||||||
|
{ ADC_SRC_STM32, 6u }, /* IB = CH6 */
|
||||||
|
{ ADC_SRC_STM32, 1u }, /* IC = CH1 */
|
||||||
|
{ ADC_SRC_STM32, 2u }, /* IN = CH2 */
|
||||||
|
{ ADC_SRC_STM32, 5u }, /* TEMP = CH5 */
|
||||||
|
};
|
||||||
|
|
||||||
|
const adc_channel_desc_t *adc_input_desc(adc_semantic_t ch) {
|
||||||
|
if ((int)ch < 0 || (unsigned)ch >= (unsigned)ADC_SEM_COUNT) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &rp_s_desc[(unsigned)ch];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dbg 计数定义(stub/dbg_log.h 声明 extern) */
|
||||||
|
int g_dbgstub104_err_count = 0;
|
||||||
|
int g_dbgstub104_info_count = 0;
|
||||||
|
int g_dbgstub104_debug_count = 0;
|
||||||
|
|
||||||
|
#include "cfg_file.c"
|
||||||
|
#include "adc_cal.c"
|
||||||
|
#include "run_par.c"
|
||||||
#include "prot_elm.c"
|
#include "prot_elm.c"
|
||||||
#include "iec104_pts.c"
|
#include "iec104_pts.c"
|
||||||
#include "iec104_link.c"
|
#include "iec104_link.c"
|
||||||
@@ -215,6 +326,7 @@ static void reset_all(uint32_t now)
|
|||||||
{
|
{
|
||||||
prot_set_init();
|
prot_set_init();
|
||||||
prot_elm_init();
|
prot_elm_init();
|
||||||
|
run_par_init(); /* 卡 10:死区/脏复位到工厂默认(重复注册 -1 走计数桩,无害) */
|
||||||
iec104_pts_init();
|
iec104_pts_init();
|
||||||
iec104_pts_bind_src(&g_fake);
|
iec104_pts_bind_src(&g_fake);
|
||||||
(void)memset(s_f_yc, 0, sizeof(s_f_yc));
|
(void)memset(s_f_yc, 0, sizeof(s_f_yc));
|
||||||
@@ -1038,7 +1150,8 @@ static void tc_db_runtime(void)
|
|||||||
iec104_pts_cov_check(&cov);
|
iec104_pts_cov_check(&cov);
|
||||||
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 2nd point under 2.0 still silent");
|
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 2nd point under 2.0 still silent");
|
||||||
|
|
||||||
iec104_pts_init(); /* 恢复 ROM 默认档(0.5) */
|
run_par_init(); /* 恢复工厂默认档(0.5)——卡 10 复位入口 */
|
||||||
|
iec104_pts_init();
|
||||||
iec104_pts_bind_src(&g_fake);
|
iec104_pts_bind_src(&g_fake);
|
||||||
s_f_yc[0] = 100.0f;
|
s_f_yc[0] = 100.0f;
|
||||||
iec104_pts_cov_check(&cov); /* 建线 */
|
iec104_pts_cov_check(&cov); /* 建线 */
|
||||||
@@ -1057,6 +1170,18 @@ static void tc_db_runtime(void)
|
|||||||
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 pullback cleared pending count");
|
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 pullback cleared pending count");
|
||||||
iec104_pts_cov_check(&cov);
|
iec104_pts_cov_check(&cov);
|
||||||
CHECK((cov.yc_changed & 0x0001u) != 0u, "TC-IEC104-027 two fresh crossings confirm again");
|
CHECK((cov.yc_changed & 0x0001u) != 0u, "TC-IEC104-027 two fresh crossings confirm again");
|
||||||
|
|
||||||
|
/* 壳语义钉(lead 裁⑤):iec104_pts_yc_set_deadband 成功须清零该点半截确认
|
||||||
|
* 计数——若不清,下面第一拍就凑满 2 点直发,与"跨档位重新起算"矛盾 */
|
||||||
|
s_f_yc[0] = 100.0f; /* rep=101.4 起 d=1.4 > 0.5:第 1 越挂起 */
|
||||||
|
iec104_pts_cov_check(&cov);
|
||||||
|
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 shell pin arms first");
|
||||||
|
iec104_pts_yc_set_deadband(0u, 0.4f);
|
||||||
|
s_f_yc[0] = 100.5f; /* d=0.9 > 0.4:计数已清则仍只是第 1 越 */
|
||||||
|
iec104_pts_cov_check(&cov);
|
||||||
|
CHECK((cov.yc_changed & 0x0001u) == 0u, "TC-IEC104-027 shell clears pending count");
|
||||||
|
iec104_pts_cov_check(&cov); /* 新档连续第 2 越 → 确认 */
|
||||||
|
CHECK((cov.yc_changed & 0x0001u) != 0u, "TC-IEC104-027 reconfirms after shell clear");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tc_cov_qds_seq(void)
|
static void tc_cov_qds_seq(void)
|
||||||
@@ -1100,6 +1225,47 @@ static void tc_cov_qds_seq(void)
|
|||||||
"TC-IEC104-028 clipped flip per frame reported as a set");
|
"TC-IEC104-028 clipped flip per frame reported as a set");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 卡 10:0x8020~0x8058 读写路由(板端默认源,run_par 表先 / prot_set 后) */
|
||||||
|
static void tc_pts_rpar_route(void)
|
||||||
|
{
|
||||||
|
float v;
|
||||||
|
|
||||||
|
reset_all(1000u);
|
||||||
|
iec104_pts_bind_src(NULL);
|
||||||
|
CHECK((iec104_pts_set_read(0x8030u, &v) == 0) && (fabsf(v - 0.5f) < 1e-4f),
|
||||||
|
"TC-IEC104-029 0x8030 deadband UA reads factory default");
|
||||||
|
CHECK((iec104_pts_set_read(0x8038u, &v) == 0) && (fabsf(v - 0.5f) < 1e-4f),
|
||||||
|
"TC-IEC104-029 0x8038 deadband freq reads factory 0.5 Hz");
|
||||||
|
/* 0x8027~0x802A = prot_set 告警段(欠压默认 20 V)——run_par 精确匹配
|
||||||
|
* 永不覆盖;路由顺序的直接证据 */
|
||||||
|
CHECK((iec104_pts_set_read(0x8027u, &v) == 0) && (fabsf(v - 20.0f) < 0.01f),
|
||||||
|
"TC-IEC104-029 0x8027 still served by prot_set table");
|
||||||
|
CHECK(iec104_pts_set_read(0x8039u, &v) == -1, "TC-IEC104-029 gap address unknown");
|
||||||
|
/* C_SE 两段式:选择段校验不提交,执行段落真源 */
|
||||||
|
CHECK((iec104_pts_set_write(0x8031u, 0.7f, 1u) == 0)
|
||||||
|
&& (fabsf(iec104_pts_yc_deadband(1u) - 0.5f) < 1e-4f),
|
||||||
|
"TC-IEC104-029 select phase validates without commit");
|
||||||
|
CHECK((iec104_pts_set_write(0x8031u, 0.7f, 0u) == 0)
|
||||||
|
&& (fabsf(iec104_pts_yc_deadband(1u) - 0.7f) < 1e-4f)
|
||||||
|
&& (run_par_dirty_peek() != 0u),
|
||||||
|
"TC-IEC104-029 execute commits and marks dirty");
|
||||||
|
CHECK(iec104_pts_set_write(0x8031u, 51.0f, 1u) == -1,
|
||||||
|
"TC-IEC104-029 over-range rejected in select");
|
||||||
|
/* k/b 覆盖直通 adc_cal(UA 合法带 = ROM x[0.5,2.0] = [76.29,305.17]) */
|
||||||
|
CHECK((iec104_pts_set_write(0x8040u, 300.0f, 0u) == 0)
|
||||||
|
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) < 0.01f),
|
||||||
|
"TC-IEC104-029 k override lands in adc_cal");
|
||||||
|
CHECK(iec104_pts_set_write(0x8040u, 400.0f, 0u) == -1,
|
||||||
|
"TC-IEC104-029 k band reject (via adc_cal)");
|
||||||
|
CHECK((iec104_pts_set_write(0x8048u, -5.0f, 0u) == 0)
|
||||||
|
&& (fabsf(adc_cal_b_codes(ADC_SEM_UA) + 5.0f) < 1e-3f)
|
||||||
|
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) < 0.01f),
|
||||||
|
"TC-IEC104-029 b single-side write keeps k (RMW)");
|
||||||
|
CHECK((iec104_pts_set_read(0x8048u, &v) == 0) && (fabsf(v + 5.0f) < 1e-3f),
|
||||||
|
"TC-IEC104-029 b echo via run_par read path");
|
||||||
|
run_par_init(); /* 收尾:把 k/b 脏与副本复位留给后续 TC 的干净出厂态 */
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
printf("=== IEC104 Protocol Stack Host Tests ===\n");
|
printf("=== IEC104 Protocol Stack Host Tests ===\n");
|
||||||
@@ -1131,6 +1297,7 @@ int main(void)
|
|||||||
tc_pts_freq();
|
tc_pts_freq();
|
||||||
tc_db_runtime();
|
tc_db_runtime();
|
||||||
tc_cov_qds_seq();
|
tc_cov_qds_seq();
|
||||||
|
tc_pts_rpar_route();
|
||||||
printf("=== IEC104 Tests: %d/%d PASSED (failed=%d) ===\n",
|
printf("=== IEC104 Tests: %d/%d PASSED (failed=%d) ===\n",
|
||||||
s_passed, s_total, s_failed);
|
s_passed, s_total, s_failed);
|
||||||
return (s_failed == 0) ? 0 : 1;
|
return (s_failed == 0) ? 0 : 1;
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
做什么:用本机 MSVC 离线编译 test/host/iec104/iec104_test.c(#include 纳入
|
做什么:用本机 MSVC 离线编译 test/host/iec104/iec104_test.c(#include 纳入
|
||||||
生产源文件 iec104_asdu.c + iec104_pts.c + iec104_link.c,及数据源链接
|
生产源文件 iec104_asdu.c + iec104_pts.c + iec104_link.c,及数据源链接
|
||||||
依赖 prot_set.c + prot_elm.c + phasor.c + sample_ring.c),验证
|
依赖 prot_set.c + prot_elm.c + phasor.c + sample_ring.c + freq.c +
|
||||||
|
meas.c,卡 10 起再 + cfg_file.c + adc_cal.c + run_par.c 真链),验证
|
||||||
APCI 重组 / STARTDT 握手 / 总召分片 / COV / k-w-t1~t3 / 遥控与定值两段式 /
|
APCI 重组 / STARTDT 握手 / 总召分片 / COV / k-w-t1~t3 / 遥控与定值两段式 /
|
||||||
C_RD / C_CS / 错误应答(EVI)。
|
C_RD / C_CS / 错误应答(EVI)。
|
||||||
|
|
||||||
@@ -28,8 +29,14 @@ from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402
|
|||||||
BUILD = TestBuild(
|
BUILD = TestBuild(
|
||||||
name="IEC104 Tests",
|
name="IEC104 Tests",
|
||||||
src=os.path.join(HERE, "iec104_test.c"),
|
src=os.path.join(HERE, "iec104_test.c"),
|
||||||
|
# 卡 10 起 stub 先于生产目录(dbg_log.h → 计数宏桩);App/cfg + App/log
|
||||||
|
# 真头供 cfg_file.h / cfg_file_store.h / sys_log.h;adc_input_desc /
|
||||||
|
# syslog_post / cfg_store_* 由测试单元给强符号桩(同 adc_cal 套件先例)。
|
||||||
include_dirs=[
|
include_dirs=[
|
||||||
|
os.path.join(HERE, "stub"),
|
||||||
os.path.join(ROOT, "App", "algo"),
|
os.path.join(ROOT, "App", "algo"),
|
||||||
|
os.path.join(ROOT, "App", "cfg"),
|
||||||
|
os.path.join(ROOT, "App", "log"),
|
||||||
os.path.join(ROOT, "App", "protocol", "iec104"),
|
os.path.join(ROOT, "App", "protocol", "iec104"),
|
||||||
HERE,
|
HERE,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 主机侧单测桩:dbg_log.h(IEC104 套件专用)
|
||||||
|
* 模块功能:为卡 10 起纳入本 TU 的 cfg_file.c / adc_cal.c / run_par.c 提供
|
||||||
|
* 主机可编译的日志宏(映射为计数)。⚠ 仅供 test/host/iec104/ 使用,
|
||||||
|
* 不参与 MCU 构建。模式同 test/host/adc_cal/stub/ 先例。
|
||||||
|
* 创建日期:2026-10-25(道D 卡 10)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DBG_LOG_H
|
||||||
|
#define DBG_LOG_H
|
||||||
|
|
||||||
|
extern int g_dbgstub104_err_count;
|
||||||
|
extern int g_dbgstub104_info_count;
|
||||||
|
extern int g_dbgstub104_debug_count;
|
||||||
|
|
||||||
|
#define DBG_INFO(fmt, ...) do { g_dbgstub104_info_count++; } while (0)
|
||||||
|
#define DBG_DEBUG(fmt, ...) do { g_dbgstub104_debug_count++; } while (0)
|
||||||
|
#define DBG_ERROR(fmt, ...) do { g_dbgstub104_err_count++; } while (0)
|
||||||
|
|
||||||
|
#endif /* DBG_LOG_H */
|
||||||
@@ -202,6 +202,9 @@ int sys_clock_set_unix(uint32_t unix_secs)
|
|||||||
return g_clock_set_rc;
|
return g_clock_set_rc;
|
||||||
}
|
}
|
||||||
uint8_t prot_set_dirty_peek(void) { return (uint8_t)g_dirty; }
|
uint8_t prot_set_dirty_peek(void) { return (uint8_t)g_dirty; }
|
||||||
|
/* run_par 脏 peek 假件(卡 10):任务主循环在单测中不跑,脏恒 0 =
|
||||||
|
* 不投递 SYS_EVT_RUN_PAR_SAVE;落盘链真身覆盖在 test/host/run_par。 */
|
||||||
|
uint8_t run_par_dirty_peek(void) { return 0u; }
|
||||||
uint32_t osEventFlagsSet(osEventFlagsId_t ef_id, uint32_t flags)
|
uint32_t osEventFlagsSet(osEventFlagsId_t ef_id, uint32_t flags)
|
||||||
{
|
{
|
||||||
(void)ef_id;
|
(void)ef_id;
|
||||||
|
|||||||
@@ -0,0 +1,393 @@
|
|||||||
|
/*
|
||||||
|
* 模块名称:运行参数段(run_par)主机侧单元测试
|
||||||
|
* 模块功能:离线钉死「数据可信」卡 10 的三层契约——① [run_par] cfg 段:
|
||||||
|
* 注册/工厂默认/落盘往返/文件改值生效(真实 cfg 引擎 + 内存虚拟盘,
|
||||||
|
* 同 adc_cal 套件先例);② 104 地址映射:命中集/空隙/两段式/分档
|
||||||
|
* 量程守卫;③ k/b 覆盖直通 adc_cal_set(真实标定层):合法带拒绝、
|
||||||
|
* 单侧 RMW 保另一侧、回显 = 生效值视图。
|
||||||
|
* 被测对象:真实源文件(#include 纳入):run_par.c + cfg_file.c + adc_cal.c;
|
||||||
|
* dbg 计数桩(stub/dbg_log.h)+ cfg_store_* 内存虚拟盘 + adc_input_desc
|
||||||
|
* V0.1 权威映射桩。**路由顺序证据(run_par 表先 / prot_set 后)在
|
||||||
|
* test/host/iec104 TC-IEC104-029 钉(那边才有全协议栈)**。
|
||||||
|
*
|
||||||
|
* 覆盖点(TC-RUNPAR-xxx;属主登记处 = 本目录,族 TC-HOST-RUNPAR):
|
||||||
|
* TC-RUNPAR-001 init 工厂默认 = 卡 6 额定档 + idx 守卫
|
||||||
|
* TC-RUNPAR-002 死区写往返 + 分档上限(V/A/Hz)+ NaN/负值拒 + 定点量化
|
||||||
|
* TC-RUNPAR-003 落盘链:set→脏→store_save 成功清脏 + 文件含 9 键 +
|
||||||
|
* load 回读往返 + 外部改文件值生效 + 无脏 save 空操作
|
||||||
|
* TC-RUNPAR-004 104 读映射:0x8030~0x8038 命中 / 空隙与邻段 -1 / k·b 回显
|
||||||
|
* TC-RUNPAR-005 104 两段式:选择段不提交不置脏,执行段提交置脏
|
||||||
|
* TC-RUNPAR-006 k/b 直通:合法带接受与拒绝 / 非正 k 拒 / b 单侧 RMW 保 k /
|
||||||
|
* 非法带值经 load 注入后回显 = 生效值(哨兵视图)
|
||||||
|
* TC-RUNPAR-007 init 幂等:重复调用复位默认 + 清脏;重注册被引擎拒(留痕)
|
||||||
|
* TC-RUNPAR-008 引擎副本 = 生效值(无回调):load 原地改副本即时可见
|
||||||
|
*
|
||||||
|
* 通过:全部 TC PASS(main 返回 0)。
|
||||||
|
* 创建日期:2026-10-25(道D 卡 10)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* 先桩头后生产源码(顺序敏感,同 adc_cal 套件) */
|
||||||
|
#include "dbg_log.h"
|
||||||
|
|
||||||
|
/* ===================== 内存虚拟文件系统(cfg_store_* 强符号) ===================== */
|
||||||
|
#include "cfg_file_store.h"
|
||||||
|
|
||||||
|
#define VFS_CAP 8192u
|
||||||
|
|
||||||
|
static uint8_t s_vfs[VFS_CAP];
|
||||||
|
static size_t s_vfs_len = 0u;
|
||||||
|
static uint32_t s_vfs_mtime = 100u;
|
||||||
|
static int s_vfs_exists = 0;
|
||||||
|
static int s_vfs_writes = 0;
|
||||||
|
|
||||||
|
void *cfg_store_alloc(size_t size) { return malloc(size); }
|
||||||
|
void cfg_store_free(void *p_buf) { free(p_buf); }
|
||||||
|
uint32_t cfg_store_heap_free(void) { return 0u; }
|
||||||
|
|
||||||
|
int cfg_store_read(const char *p_path, uint8_t *p_buf, size_t cap, size_t *p_nread) {
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
(void)p_path;
|
||||||
|
if (!s_vfs_exists) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
n = s_vfs_len;
|
||||||
|
if (n > cap) {
|
||||||
|
n = cap;
|
||||||
|
}
|
||||||
|
(void)memcpy(p_buf, s_vfs, n);
|
||||||
|
*p_nread = n;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len) {
|
||||||
|
(void)p_path;
|
||||||
|
if (len > VFS_CAP) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
(void)memcpy(s_vfs, p_buf, len);
|
||||||
|
s_vfs_len = len;
|
||||||
|
s_vfs_exists = 1;
|
||||||
|
s_vfs_mtime++;
|
||||||
|
s_vfs_writes++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg_store_stat(const char *p_path, cfg_file_stat_t *p_st) {
|
||||||
|
(void)p_path;
|
||||||
|
if (!s_vfs_exists) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
p_st->size = (uint32_t)s_vfs_len;
|
||||||
|
p_st->mtime = s_vfs_mtime;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cfg_store_remove(const char *p_path) {
|
||||||
|
(void)p_path;
|
||||||
|
s_vfs_exists = 0;
|
||||||
|
s_vfs_len = 0u;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cfg_store_config_path(void) { return "/config.ini"; }
|
||||||
|
const char *cfg_store_tmp_path(void) { return "/config.tmp"; }
|
||||||
|
|
||||||
|
/* ---- sys_log 桩 ---- */
|
||||||
|
#include "sys_log.h"
|
||||||
|
|
||||||
|
static int s_syslog_count = 0;
|
||||||
|
|
||||||
|
void syslog_post(syslog_id_t id, const char *desc) {
|
||||||
|
(void)id;
|
||||||
|
(void)desc;
|
||||||
|
s_syslog_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- adc_input_desc 桩:V0.1 权威映射(docs/硬件平台/ADC通道映射说明.md) ---- */
|
||||||
|
#include "adc_input.h"
|
||||||
|
|
||||||
|
static const adc_channel_desc_t s_desc_stub[ADC_SEM_COUNT] = {
|
||||||
|
{ ADC_SRC_TPAFE, 6u }, /* UA = buf[6] */
|
||||||
|
{ ADC_SRC_STM32, 14u }, /* UB = CH14(PC4) */
|
||||||
|
{ ADC_SRC_STM32, 3u }, /* UC = CH3 */
|
||||||
|
{ ADC_SRC_TPAFE, 5u }, /* UN = buf[5] */
|
||||||
|
{ ADC_SRC_TPAFE, 2u }, /* IA = buf[2] */
|
||||||
|
{ ADC_SRC_STM32, 6u }, /* IB = CH6 */
|
||||||
|
{ ADC_SRC_STM32, 1u }, /* IC = CH1 */
|
||||||
|
{ ADC_SRC_STM32, 2u }, /* IN = CH2 */
|
||||||
|
{ ADC_SRC_STM32, 5u }, /* TEMP = CH5 */
|
||||||
|
};
|
||||||
|
|
||||||
|
const adc_channel_desc_t *adc_input_desc(adc_semantic_t ch) {
|
||||||
|
if ((int)ch < 0 || (unsigned)ch >= (unsigned)ADC_SEM_COUNT) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &s_desc_stub[(unsigned)ch];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dbg 计数定义(stub/dbg_log.h extern 声明) */
|
||||||
|
int g_rptest_err_count = 0;
|
||||||
|
int g_rptest_info_count = 0;
|
||||||
|
int g_rptest_debug_count = 0;
|
||||||
|
|
||||||
|
/* ===================== 测试框架(极简,同 adc_cal 套件) ===================== */
|
||||||
|
static int s_total = 0, s_passed = 0, s_failed = 0;
|
||||||
|
|
||||||
|
#define CHECK(cond, name) do { \
|
||||||
|
s_total++; \
|
||||||
|
if (cond) { s_passed++; printf(" [PASS] %s\n", name); } \
|
||||||
|
else { s_failed++; printf(" [FAIL] %s (line %d)\n", name, __LINE__); } \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ASSERT_NEAR(a, b, eps, name) CHECK(fabsf((float)(a) - (float)(b)) <= (eps), name)
|
||||||
|
|
||||||
|
/* ===================== 被测代码 ===================== */
|
||||||
|
#include "cfg_file.c"
|
||||||
|
#include "adc_cal.c"
|
||||||
|
#include "run_par.c"
|
||||||
|
|
||||||
|
static void vfs_reset(void) {
|
||||||
|
s_vfs_len = 0u;
|
||||||
|
s_vfs_mtime = 100u;
|
||||||
|
s_vfs_exists = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全复位:引擎段复位 + run_par 复位 + 标定复位(重注册被拒 = 预期,清计数) */
|
||||||
|
static void reset_all_rp(void) {
|
||||||
|
run_par_init();
|
||||||
|
adc_cal_init();
|
||||||
|
g_rptest_err_count = 0;
|
||||||
|
}
|
||||||
|
/* ===================== 测试用例 ===================== */
|
||||||
|
|
||||||
|
static void tc_defaults(void)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
vfs_reset();
|
||||||
|
reset_all_rp();
|
||||||
|
for (i = 0u; i < 4u; i++) {
|
||||||
|
CHECK(fabsf(run_par_deadband(i) - 0.5f) <= 1e-6f, "TC-RUNPAR-001 voltage ch factory 0.5 V");
|
||||||
|
}
|
||||||
|
for (i = 4u; i < 8u; i++) {
|
||||||
|
CHECK(fabsf(run_par_deadband(i) - 0.005f) <= 1e-9f, "TC-RUNPAR-001 current ch factory 0.005 A");
|
||||||
|
}
|
||||||
|
CHECK(fabsf(run_par_deadband(8u) - 0.5f) <= 1e-6f, "TC-RUNPAR-001 freq factory 0.5 Hz");
|
||||||
|
CHECK(run_par_deadband(9u) == 0.0f, "TC-RUNPAR-001 idx guard returns zero");
|
||||||
|
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-001 init leaves clean");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tc_set_range(void)
|
||||||
|
{
|
||||||
|
volatile float fnan;
|
||||||
|
|
||||||
|
vfs_reset();
|
||||||
|
reset_all_rp();
|
||||||
|
CHECK((run_par_deadband_set(0u, 0.7f) == 0)
|
||||||
|
&& (fabsf(run_par_deadband(0u) - 0.7f) <= 1e-6f),
|
||||||
|
"TC-RUNPAR-002 write round trip voltage domain");
|
||||||
|
CHECK((run_par_deadband_set(4u, 0.02f) == 0)
|
||||||
|
&& (fabsf(run_par_deadband(4u) - 0.02f) <= 1e-6f),
|
||||||
|
"TC-RUNPAR-002 write round trip current domain");
|
||||||
|
CHECK(run_par_deadband_set(0u, 50.0f) == 0, "TC-RUNPAR-002 voltage cap endpoint inclusive");
|
||||||
|
CHECK((run_par_deadband_set(0u, 50.001f) == -1)
|
||||||
|
&& (fabsf(run_par_deadband(0u) - 50.0f) <= 1e-6f),
|
||||||
|
"TC-RUNPAR-002 over voltage cap rejected");
|
||||||
|
CHECK(run_par_deadband_set(4u, 1.001f) == -1, "TC-RUNPAR-002 current cap 1 A enforced");
|
||||||
|
CHECK(run_par_deadband_set(8u, 5.001f) == -1, "TC-RUNPAR-002 freq cap 5 Hz enforced");
|
||||||
|
CHECK(run_par_deadband_set(0u, -0.001f) == -1, "TC-RUNPAR-002 negative rejected");
|
||||||
|
fnan = 0.0f;
|
||||||
|
fnan = fnan / fnan;
|
||||||
|
CHECK(run_par_deadband_set(0u, fnan) == -1, "TC-RUNPAR-002 NaN rejected");
|
||||||
|
CHECK(run_par_deadband_set(9u, 1.0f) == -1, "TC-RUNPAR-002 idx guard rejected");
|
||||||
|
CHECK((run_par_deadband_set(2u, 0.1234f) == 0)
|
||||||
|
&& (fabsf(run_par_deadband(2u) - 0.123f) <= 1e-6f),
|
||||||
|
"TC-RUNPAR-002 milli quantization rounds to nearest");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tc_persist(void)
|
||||||
|
{
|
||||||
|
char probe[24];
|
||||||
|
|
||||||
|
vfs_reset();
|
||||||
|
reset_all_rp();
|
||||||
|
CHECK(run_par_store_save() == 0, "TC-RUNPAR-003 clean save is no-op ok");
|
||||||
|
CHECK(run_par_deadband_set(2u, 0.9f) == 0, "TC-RUNPAR-003 seed write ok");
|
||||||
|
CHECK(run_par_dirty_peek() == 1u, "TC-RUNPAR-003 write marks dirty");
|
||||||
|
CHECK((run_par_store_save() == 0) && (run_par_dirty_peek() == 0u),
|
||||||
|
"TC-RUNPAR-003 save clears dirty on success");
|
||||||
|
(void)memset(probe, 0, sizeof(probe));
|
||||||
|
(void)memcpy(probe, "db_uc=", 6u);
|
||||||
|
CHECK((s_vfs_exists == 1) && (strstr((const char *)s_vfs, "db_uc=900") != NULL),
|
||||||
|
"TC-RUNPAR-003 file carries milli value");
|
||||||
|
CHECK(strstr((const char *)s_vfs, "db_freq=500") != NULL,
|
||||||
|
"TC-RUNPAR-003 all nine keys present (freq spot check)");
|
||||||
|
/* 往返:改内存 → 落盘 → 引擎 load 回读 */
|
||||||
|
CHECK(run_par_deadband_set(4u, 0.02f) == 0, "TC-RUNPAR-003 second seed");
|
||||||
|
(void)run_par_store_save();
|
||||||
|
run_par_deadband_set(4u, 0.5f); /* 覆盖内存态 */
|
||||||
|
CHECK(cfg_file_load() == 0, "TC-RUNPAR-003 engine reload ok");
|
||||||
|
CHECK(fabsf(run_par_deadband(4u) - 0.02f) <= 1e-9f,
|
||||||
|
"TC-RUNPAR-003 reload restores last saved value");
|
||||||
|
/* set(4,0.5) 是一次未落盘的本地整定 → 脏必须活过 reload(reload 不是
|
||||||
|
* 清脏事件;只有 save 成功才清) */
|
||||||
|
CHECK(run_par_dirty_peek() == 1u, "TC-RUNPAR-003 unsaved local edit survives reload");
|
||||||
|
/* 外部改档(FTP 手改 config.ini 的现场形态):load 后副本即生效值 */
|
||||||
|
{
|
||||||
|
static const char s_edited[] =
|
||||||
|
"[run_par]\ndb_ua=250\ndb_ub=500\ndb_uc=500\ndb_un=500\n"
|
||||||
|
"db_ia=10\ndb_ib=5\ndb_ic=5\ndb_in=5\ndb_freq=800\n";
|
||||||
|
(void)memcpy(s_vfs, s_edited, sizeof(s_edited) - 1u);
|
||||||
|
s_vfs_len = sizeof(s_edited) - 1u;
|
||||||
|
s_vfs_exists = 1;
|
||||||
|
s_vfs_mtime++;
|
||||||
|
}
|
||||||
|
CHECK(cfg_file_load() == 0, "TC-RUNPAR-003 edited file accepted");
|
||||||
|
CHECK((fabsf(run_par_deadband(0u) - 0.25f) <= 1e-6f)
|
||||||
|
&& (fabsf(run_par_deadband(4u) - 0.01f) <= 1e-9f)
|
||||||
|
&& (fabsf(run_par_deadband(8u) - 0.8f) <= 1e-6f),
|
||||||
|
"TC-RUNPAR-003 edited values live in copy immediately");
|
||||||
|
/* 越界文件值:引擎照收(int32 域),104/COV 读到超程值 —— 守卫在**写路径**,
|
||||||
|
* 文件通路的防线是"人工编辑 + 沉降去抖 + 可再改回";与 adc_cal 合法带不同,
|
||||||
|
* 死区超程不毁量纲(只是上送变钝),此处钉住"引擎不吞值"即可 */
|
||||||
|
CHECK(fabsf(run_par_deadband(0u) - 0.25f) <= 1e-6f, "TC-RUNPAR-003 value stable re-read");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tc_104_read(void)
|
||||||
|
{
|
||||||
|
float v;
|
||||||
|
|
||||||
|
vfs_reset();
|
||||||
|
reset_all_rp();
|
||||||
|
(void)run_par_deadband_set(0u, 0.6f);
|
||||||
|
CHECK((run_par_104_read(0x8030u, &v) == 0) && (fabsf(v - 0.6f) <= 1e-6f),
|
||||||
|
"TC-RUNPAR-004 db first slot hit");
|
||||||
|
CHECK((run_par_104_read(0x8038u, &v) == 0) && (fabsf(v - 0.5f) <= 1e-6f),
|
||||||
|
"TC-RUNPAR-004 db last slot (freq) hit");
|
||||||
|
CHECK(run_par_104_read(0x8039u, &v) == -1, "TC-RUNPAR-004 gap address not owned");
|
||||||
|
CHECK(run_par_104_read(0x8027u, &v) == -1, "TC-RUNPAR-004 prot_set alarm addr untouched");
|
||||||
|
CHECK(run_par_104_read(0x802Fu, &v) == -1, "TC-RUNPAR-004 below base not claimed");
|
||||||
|
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 152.587890625f) <= 1e-4f),
|
||||||
|
"TC-RUNPAR-004 k echo = adc_cal effective UA");
|
||||||
|
CHECK((run_par_104_read(0x8047u, &v) == 0) && (fabsf(v - 805.6640625f) <= 1e-4f),
|
||||||
|
"TC-RUNPAR-004 k echo last channel (IN, STM32 ROM)");
|
||||||
|
CHECK((run_par_104_read(0x8048u, &v) == 0) && (v == 0.0f),
|
||||||
|
"TC-RUNPAR-004 b echo factory zero");
|
||||||
|
CHECK(run_par_104_read(0x8050u, &v) == -1, "TC-RUNPAR-004 above b block not owned");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tc_104_two_step(void)
|
||||||
|
{
|
||||||
|
float v;
|
||||||
|
|
||||||
|
vfs_reset();
|
||||||
|
reset_all_rp();
|
||||||
|
CHECK((run_par_104_write(0x8031u, 0.8f, 1u) == 0)
|
||||||
|
&& (fabsf(run_par_deadband(1u) - 0.5f) <= 1e-6f)
|
||||||
|
&& (run_par_dirty_peek() == 0u),
|
||||||
|
"TC-RUNPAR-005 select validates without commit or dirty");
|
||||||
|
CHECK((run_par_104_write(0x8031u, 0.8f, 0u) == 0)
|
||||||
|
&& (fabsf(run_par_deadband(1u) - 0.8f) <= 1e-6f)
|
||||||
|
&& (run_par_dirty_peek() == 1u),
|
||||||
|
"TC-RUNPAR-005 execute commits and marks dirty");
|
||||||
|
CHECK(run_par_104_write(0x8031u, 60.0f, 1u) == -1, "TC-RUNPAR-005 range reject in select");
|
||||||
|
CHECK(run_par_104_write(0x8039u, 1.0f, 0u) == -2, "TC-RUNPAR-005 foreign addr passes through");
|
||||||
|
CHECK(run_par_104_write(0x8027u, 1.0f, 0u) == -2, "TC-RUNPAR-005 alarm addr passes through");
|
||||||
|
CHECK((run_par_104_read(0x8031u, &v) == 0) && (fabsf(v - 0.8f) <= 1e-6f),
|
||||||
|
"TC-RUNPAR-005 committed value readable at same addr");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tc_104_cal_proxy(void)
|
||||||
|
{
|
||||||
|
float v;
|
||||||
|
|
||||||
|
vfs_reset();
|
||||||
|
reset_all_rp();
|
||||||
|
CHECK(run_par_104_write(0x8040u, 300.0f, 0u) == 0, "TC-RUNPAR-006 k in-band accepted");
|
||||||
|
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 300.0f) <= 0.01f),
|
||||||
|
"TC-RUNPAR-006 k echo follows adc_cal_set");
|
||||||
|
CHECK((run_par_104_write(0x8040u, 400.0f, 0u) == -1)
|
||||||
|
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
|
||||||
|
"TC-RUNPAR-006 k over band rejected by adc_cal, RAM intact");
|
||||||
|
CHECK((run_par_104_write(0x8040u, 70.0f, 0u) == -1),
|
||||||
|
"TC-RUNPAR-006 k under band rejected (0.5x ROM)");
|
||||||
|
CHECK(run_par_104_write(0x8040u, -1.0f, 0u) == -1, "TC-RUNPAR-006 non-positive k rejected");
|
||||||
|
CHECK((run_par_104_write(0x8048u, -5.0f, 0u) == 0)
|
||||||
|
&& (fabsf(adc_cal_b_codes(ADC_SEM_UA) + 5.0f) <= 1e-3f)
|
||||||
|
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
|
||||||
|
"TC-RUNPAR-006 b single-side RMW keeps k");
|
||||||
|
CHECK(run_par_104_write(0x8048u, -9000.0f, 0u) == -1, "TC-RUNPAR-006 b over +-8192 rejected");
|
||||||
|
CHECK((run_par_104_write(0x8040u, 200.0f, 1u) == 0)
|
||||||
|
&& (fabsf(adc_cal_k_uv(ADC_SEM_UA) - 300.0f) <= 0.01f),
|
||||||
|
"TC-RUNPAR-006 k select does not touch adc_cal");
|
||||||
|
/* 生效值视图回显:文件塞越带 k → 哨兵回退 ROM → 104 回显 ROM */
|
||||||
|
{
|
||||||
|
static const char s_badk[] =
|
||||||
|
"[adc_cal]\nk_ua=99011483\nvalid=1\n";
|
||||||
|
(void)memcpy(s_vfs, s_badk, sizeof(s_badk) - 1u);
|
||||||
|
s_vfs_len = sizeof(s_badk) - 1u;
|
||||||
|
s_vfs_exists = 1;
|
||||||
|
s_vfs_mtime++;
|
||||||
|
}
|
||||||
|
CHECK(cfg_file_load() == 0, "TC-RUNPAR-006 poisoned file loaded by engine");
|
||||||
|
CHECK((run_par_104_read(0x8040u, &v) == 0) && (fabsf(v - 152.587890625f) <= 1e-4f),
|
||||||
|
"TC-RUNPAR-006 echo = effective (ROM fallback), not raw file value");
|
||||||
|
reset_all_rp();
|
||||||
|
CHECK(cfg_file_save() == 0, "TC-RUNPAR-006 reset file rewritten");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tc_init_idempotent(void)
|
||||||
|
{
|
||||||
|
int err_before;
|
||||||
|
|
||||||
|
vfs_reset();
|
||||||
|
reset_all_rp();
|
||||||
|
(void)run_par_deadband_set(1u, 0.9f);
|
||||||
|
(void)run_par_104_write(0x8040u, 200.0f, 0u);
|
||||||
|
err_before = g_rptest_err_count;
|
||||||
|
run_par_init();
|
||||||
|
CHECK(fabsf(run_par_deadband(1u) - 0.5f) <= 1e-6f, "TC-RUNPAR-007 re-init restores defaults");
|
||||||
|
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-007 re-init clears dirty");
|
||||||
|
CHECK(g_rptest_err_count > err_before, "TC-RUNPAR-007 dup register leaves trace");
|
||||||
|
CHECK(fabsf(run_par_deadband(0u) - 0.5f) <= 1e-6f, "TC-RUNPAR-007 values intact after dup reg");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tc_copy_is_effective(void)
|
||||||
|
{
|
||||||
|
static const char s_file[] =
|
||||||
|
"[run_par]\ndb_ua=650\ndb_ub=500\ndb_uc=500\ndb_un=500\n"
|
||||||
|
"db_ia=5\ndb_ib=5\ndb_ic=5\ndb_in=5\ndb_freq=500\n";
|
||||||
|
|
||||||
|
vfs_reset();
|
||||||
|
reset_all_rp();
|
||||||
|
(void)memcpy(s_vfs, s_file, sizeof(s_file) - 1u);
|
||||||
|
s_vfs_len = sizeof(s_file) - 1u;
|
||||||
|
s_vfs_exists = 1;
|
||||||
|
s_vfs_mtime++;
|
||||||
|
CHECK(cfg_file_load() == 0, "TC-RUNPAR-008 load ok");
|
||||||
|
CHECK(fabsf(run_par_deadband(0u) - 0.65f) <= 1e-6f,
|
||||||
|
"TC-RUNPAR-008 no cache layer: copy read is effective value");
|
||||||
|
CHECK(run_par_dirty_peek() == 0u, "TC-RUNPAR-008 load itself is not a local edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
printf("=== RunPar Layer Host Tests ===\n");
|
||||||
|
tc_defaults();
|
||||||
|
tc_set_range();
|
||||||
|
tc_persist();
|
||||||
|
tc_104_read();
|
||||||
|
tc_104_two_step();
|
||||||
|
tc_104_cal_proxy();
|
||||||
|
tc_init_idempotent();
|
||||||
|
tc_copy_is_effective();
|
||||||
|
printf("=== RunPar Tests: %d/%d PASSED (failed=%d) ===\n",
|
||||||
|
s_passed, s_total, s_failed);
|
||||||
|
return (s_failed == 0) ? 0 : 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""运行参数段(run_par,「数据可信」卡 10)主机侧单元测试运行器(L0.5,纯标准库)。
|
||||||
|
|
||||||
|
做什么:用本机 MSVC 离线编译 test/host/run_par/run_par_test.c(#include 纳入
|
||||||
|
run_par.c + cfg_file.c + adc_cal.c 真实链 + 内存虚拟盘桩),钉死 [run_par]
|
||||||
|
注册/工厂默认/落盘往返、104 地址映射两段式、k/b 覆盖直通 adc_cal 合法带、
|
||||||
|
生效值视图回显与 init 幂等。
|
||||||
|
|
||||||
|
用法(仓库根目录):
|
||||||
|
& '.venv\\Scripts\\python.exe' test/host/run_par/run_unit_test.py
|
||||||
|
|
||||||
|
退出码:0=PASS / 1=测试失败 / 2=环境错误。
|
||||||
|
|
||||||
|
设计依据:docs/任务规划/数据可信.md(卡 10)+ docs/子系统设计/IEC104上行设计说明.md §3.4。
|
||||||
|
创建日期:2026-10-25(道D 卡 10)
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(HERE)))
|
||||||
|
sys.path.insert(0, os.path.join(ROOT, "test", "host", "common"))
|
||||||
|
|
||||||
|
from unit_test_harness import TestBuild, run_host_unit_test # noqa: E402
|
||||||
|
|
||||||
|
# 顺序敏感:stub 先于生产目录(dbg_log.h → 计数宏桩);App/cfg + App/log 真头
|
||||||
|
# 供 cfg_file.h / cfg_file_store.h / sys_log.h;adc_input_desc / syslog_post /
|
||||||
|
# cfg_store_* 内存虚拟盘由测试单元给强符号桩(同 adc_cal 套件先例)。
|
||||||
|
BUILD = TestBuild(
|
||||||
|
name="RunPar Layer Tests",
|
||||||
|
src=os.path.join(HERE, "run_par_test.c"),
|
||||||
|
include_dirs=[
|
||||||
|
os.path.join(HERE, "stub"),
|
||||||
|
os.path.join(ROOT, "App", "algo"),
|
||||||
|
os.path.join(ROOT, "App", "cfg"),
|
||||||
|
os.path.join(ROOT, "App", "log"),
|
||||||
|
HERE,
|
||||||
|
],
|
||||||
|
summary_marker="=== RunPar Tests:",
|
||||||
|
)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(run_host_unit_test(BUILD))
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 主机侧单测桩:dbg_log.h(run_par 套件专用)
|
||||||
|
* 模块功能:为 cfg_file.c / adc_cal.c / run_par.c 提供主机可编译的日志宏
|
||||||
|
* (映射为计数)。⚠ 仅供 test/host/run_par/ 使用,不参与 MCU 构建。
|
||||||
|
* 模式同 test/host/adc_cal/stub/ 先例。
|
||||||
|
* 创建日期:2026-10-25(道D 卡 10)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DBG_LOG_H
|
||||||
|
#define DBG_LOG_H
|
||||||
|
|
||||||
|
extern int g_rptest_err_count;
|
||||||
|
extern int g_rptest_info_count;
|
||||||
|
extern int g_rptest_debug_count;
|
||||||
|
|
||||||
|
#define DBG_INFO(fmt, ...) do { g_rptest_info_count++; } while (0)
|
||||||
|
#define DBG_DEBUG(fmt, ...) do { g_rptest_debug_count++; } while (0)
|
||||||
|
#define DBG_ERROR(fmt, ...) do { g_rptest_err_count++; } while (0)
|
||||||
|
|
||||||
|
#endif /* DBG_LOG_H */
|
||||||
Reference in New Issue
Block a user