Files
STM32F4-Base/App/protocol/iec104/iec104_pts.c
T
wandering1 8c72698666 feat(运行参数): 卡10 run_par真源收编+104段0x8020~0x8058读写映射(道D M6-①)
新增 App/algo/run_par.c/h(「数据可信」卡 10,lead 四裁落地):
- [run_par] cfg 段:遥测死区 9 键 int32 毫单位(db_ua…db_freq),工厂默认 =
  卡 6 额定 0.5% 档(500mV×4 / 5mA×4 / 500mHz);注册走 adc_cal 同款内核前
  契约(app_main_init),不挂回调 = 引擎副本即生效值;键数 9≤16 不拆段
- 真源收编(裁③):pts 层 s_yc_db 运行时副本删除,COV 每拍实时读 run_par
  (协议→算法向下依赖,同 meas 先例;无投递无缓存 = 顺序/幂等平凡成立);
  iec104_pts_yc_set_deadband()/deadband() 保留为转发壳,壳内保留"成功清
  半截确认计数"语义并由 TC-IEC104-027 追加断言钉住(裁⑤)
- 104 地址映射(裁①):0x8030~0x8038 死区 9 点 / 0x8040~0x8047 k 覆盖 /
  0x8048~0x804F b 覆盖(逐项地址本仓自定——复现规划 §5.4 权威表不在仓,
  送检点表到位后替换 run_par.c 顶部常量段一处;已在 IEC104 §3.4 表头挂账);
  路由顺序 = run_par 表先、未命中落 prot_set——0x8027~0x802A 属 prot_set
  告警段永不覆盖,TC-IEC104-029 双向往返钉死;空隙地址自然 COT14/PN
- 通道系数覆盖 = adc_cal_set 代理(裁②a):合法带/越带 [ERR] 留痕全复用,
  run_par 不存第二份系数(防双真源);单侧写 RMW 用另一侧当前生效值;
  回显 = 生效值视图(文件坏值被哨兵回退 ROM 时主站看到的就是下游吃到的);
  adc_cal 配套:+adc_cal_b_codes() 读取口(回显/RMW 用)+ set() 授权写者
  注记扩至 iec104Task 直通路径(单字原子契约同「数据可信」§4.4)
- 持久化(裁②b):新事件位 SYS_EVT_RUN_PAR_SAVE(1<<8);iec104Task 主循环
  peek 投递 → defaultTask run_par_store_save() 一条事件一趟 cfg_file_save
  整档外科(覆盖 [run_par] 9 键 + 联动 [adc_cal] 两段);失败脏保留 = 天然
  重试(B7 prot_set 同款)。C_SE 现行无"暂停/测试态"门控——文档现行如此
  不造新语义,送检若要求接线点已写进 §3.4 注(裁④)

测试:新建 test/host/run_par(TC-RUNPAR-001~008,67 断言,真实 cfg 引擎 +
真实 adc_cal + 内存虚拟盘同 adc_cal 套件先例;[run_par] 注册契约/落盘往返/
外改文件即副本生效/两段式不提交不置脏/合法带拒+RMW+生效值回显/init 幂等
重注册留痕);iec104 套扩真链 TU(cfg_file+adc_cal+run_par 非桩,141 断言:
+TC-IEC104-029 路由 + 壳清确认计数 3 断言);meas 套静态更名 s_snap*→
s_ms_snap*(与 cfg_file.c s_snap 同 TU 相撞,D1 同款改名法,226 断言不变);
iec104_serv 套补 run_par_dirty_peek 假件。id_registry 建族 TC-HOST-RUNPAR
(游标 008)+ TC-HOST-IEC104 游标 028→029;新钩子 run-par-unit-test +
iec104 钩子触发面随真链扩;uvprojx +run_par.c(合法入库)+ SIM_EXEMPT 登记
(模拟器不编 104 协议,已核实 CMake 面)。

文档:IEC104 §3.4 运行参数段表(含送检替换账/门控未来条件/代理声明)、§7、
§8 第 4 条、变更记录;网络配置说明新增 §3.5 [run_par] 键表;复现规划 §5.4
补运行参数段落实进度。**遗留(交 lead)**:数据可信 §4.4 "引擎 RAM 副本"
一节当时的措辞对象含 pts 侧副本,s_yc_db 删除后该句过时(裁③b 点名,§4.4
在写域外未动)。

gate:@build 0/0(Code=219620 RO=22036 RW=1280 ZI=132864);vs 0cbae7d:
Flash +1912B、RAM(RW+ZI) +8B(37B 新静态被链接器并洞吸收);iec104Task 栈
1456B/margin 43%、defaultTask 3032B 不变;id-check/doc-links/cppcheck/
coding-standard 实跑过;--all-files 全量 45 钩子实跑(43 绿 + 仅 mem-budget /
mem-numbers 两红 = 台账受管区 vs .map 漂移,回填属 lead 单写者区,批 1/道D-1
/D2 同口径):gate: only mem-budget/mem-numbers pending lead ledger backfill
BOM/CR/U+FFFD/字面量 ASCII 逐文件自查通过。
2026-10-25 13:15:41 +08:00

631 lines
26 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:IEC 60870-5-104 点表与数据源适配(实现)
* 模块功能:实现 iec104_pts.h——YC/YX/YK/定值点表(ROM)、板端默认源
* (**L2 遥测快照**(卡 9)/ prot_elm / prot_set)、COV 比对(遥信变位 +
* 遥测死区 2 点确认 + 品质变化)。点表口径见 iec104_pts.h 文件头与
* 设计文档 §3。
* 线程约定:init/bind 建服务前;其余仅 104 服务任务上下文(快照非原子)。
* 创建日期:2026-10-12(B4)
* 修改记录:
* 2026-10-25 DTU 项目组 卡 6(「数据可信」):YC 死区由拟定码值域重定为
* 二次值工程量域(额定 0.5 % 规则),判据依据见
* k_yc 表头注释;数值随卡 9 迁运行参数,本表将退化为
* ROM 默认载体。
* 2026-10-25 DTU 项目组 卡 9/卡 11(「数据可信」,道D):YC 板端源改读
* L2 遥测快照(App/algo/meas.h,meas_get 无锁双区);
* 削顶五路由快照品质 CLIPPED 驱动线上 IV(不再绑死 -1,
* V0.2/N-18 零改动自动启用);0x401d 绑频率源(卡 11);
* 死区改运行时副本 + set/get 入口;COV 遥测腿 = 死区
* 连续 2 点确认(板端按快照 seq 去重)+ 品质翻转即报。
* 单测 = 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 <string.h>
#include "iec104_asdu.h" /* QDS/DIQ 品质位常量 */
#include "meas.h" /* 板端源:遥测 = L2 遥测快照(卡 9;品质域位同源) */
#include "run_par.h" /* 卡 10:死区真源 + 运行参数段 104 读写路由 */
#include "prot_elm.h" /* 板端源:遥信 = 元件状态 */
#include "prot_set.h" /* 板端源:定值 + 闭锁 */
#include "adc_input.h" /* adc_semantic_t(phasor 实例按语义绑定) */
/* ---------------- YC 表(§1.5 点号 4011+;死区列 = ROM 默认值) ------------ */
/* 源类型(「数据可信」卡 11:YC 表加 src 类型区分相量/频率) */
#define PTS_SRC_MEAS 0u /* L2 快照槽 sem(电气量,yc/qds 直读) */
#define PTS_SRC_FREQ 1u /* 快照频率(freq_valid 门 → IV) */
typedef struct {
uint32_t ioa;
int8_t sem; /* 快照 yc 槽 = 语义通道(-1 = 非测量量点,当前仅频率) */
float db_default; /* 死区工厂默认(生效真源 = run_par [run_par] 段,卡 10) */
uint8_t src; /* PTS_SRC_* 源类型 */
} pts_yc_def_t;
/* 死区规则 = 额定二次值的 0.5 %(「数据可信」§2.3 表第 2 行建议规则,卡 6 采纳;
* 额定基准 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 Hz 与旧值同量级,维持 0.5 并随
* 卡 10 运行参数段可调)。自卡 9 起本表**只作工厂默认载体**:生效死区 =
* App/algo/run_par.c 的 [run_par] 引擎副本(卡 10 真源收编,本层不再存副本);削顶五路不再 sem=-1 绑死,而是绑快照槽、由品质 CLIPPED
* 驱动线上 IV——V0.2(N-18)削顶消失后点位零改动自动启用(docs/任务规划/
* 数据可信.md §10 的 N-18 行)。
* ⚠ 过渡期(eng=0 引脚电压域,卡 12 标定前):上送值本身非二次值,死区
* 数字按本域偏保守(迟报不漏报);标定落地后复核(「数据可信」§2.3 表
* 第 2 行)。判定 = 死区连续 2 点确认(卡 9),见 iec104_pts.h COV 说明。 */
static const pts_yc_def_t k_yc[] = {
{ 0x4011u, (int8_t)ADC_SEM_UA, 0.5f, PTS_SRC_MEAS }, /* 1Ua:0.5 % × 100 V */
{ 0x4012u, (int8_t)ADC_SEM_UB, 0.5f, PTS_SRC_MEAS }, /* 1Ub:削顶 → 品质驱动 IV */
{ 0x4013u, (int8_t)ADC_SEM_UC, 0.5f, PTS_SRC_MEAS }, /* 1Uc:同上 */
{ 0x4014u, (int8_t)ADC_SEM_UN, 0.5f, PTS_SRC_MEAS }, /* 1U0(外部零序专用通道) */
{ 0x4015u, (int8_t)ADC_SEM_IA, 0.005f, PTS_SRC_MEAS }, /* 1Ia:0.5 % × 1 A */
{ 0x4016u, (int8_t)ADC_SEM_IB, 0.005f, PTS_SRC_MEAS }, /* 1Ib:削顶 → 品质驱动 IV */
{ 0x4017u, (int8_t)ADC_SEM_IC, 0.005f, PTS_SRC_MEAS }, /* 1Ic:同上 */
{ 0x4018u, (int8_t)ADC_SEM_IN, 0.005f, PTS_SRC_MEAS }, /* 1I0:同上 */
{ 0x401Du, -1, 0.5f, PTS_SRC_FREQ }, /* 1f:卡 11 绑频率源,
* freq_valid=0 → IV */
};
#define YC_N ((uint8_t)(sizeof(k_yc) / sizeof(k_yc[0])))
/* ===== 遥测 COV 运行时状态(卡 9;仅 iec104Task 上下文访问,无并发) =====
* 判据链:新测量点 → |v − 上次上送值| > 死区?→ 连续 2 点确认 → 置变化位。
* 基准 = **上次上送值**(非上次读值——否则慢漂移永不出死区,参考系统同款);
* 品质(线上 qds)翻转 = 事件:不参与 2 点计数、当拍直报。 */
static float s_yc_rep[YC_N]; /* 上次上送值(建线/确认上送时更新) */
static uint8_t s_yc_repq[YC_N]; /* 上次上送的线上 qds */
static uint8_t s_yc_cnf[YC_N]; /* 越死区连续计数(1 = 待确认;2 = 确认) */
static uint32_t s_yc_seq; /* COV 已消费到的快照 seq(板端源测点去重) */
static uint8_t s_yc_seqmode; /* 1 = 板端默认源(测点 = meas 换帧) */
/* ---------------- YX 表(IOA = 1..33;双点 = 开关位置类) ------------------ */
#define YX_N 33u
#define YX_DP_N 4u /* idx 0..3 = 双点(开关位置,V0.1 无硬件 → IV) */
/* 有效位图:YX15~18(idx14~17)+ YX23(idx22)+ YX31(idx30) */
#define YX_VALID_MASK (((1uLL << 14u) | (1uLL << 15u) | (1uLL << 16u) \
| (1uLL << 17u) | (1uLL << 22u) | (1uLL << 30u)))
/* ---------------- 数据源(默认 = 板端;bind_src 可整体替换) -------------- */
static int s_src_yc_read_ex(uint8_t idx, float *out, uint16_t *qds);
static uint64_t s_src_yx_bits(void);
static uint64_t s_src_yx_valid(void);
static int s_src_yk_exec(uint32_t ioa, uint8_t dpc, uint8_t select);
static int s_src_set_read(uint32_t addr, float *out);
static int s_src_set_write(uint32_t addr, float v, uint8_t select);
/* 板端默认源:遥测走**带品质**的 ex 通道(legacy yc_read 槽置 NULL——
* s_yc_point 优先 ex;注入源两种槽位均可,见 iec104_pts.h) */
static const iec104_pts_src_t k_default_src = {
NULL, s_src_yx_bits, s_src_yx_valid,
s_src_yk_exec, s_src_set_read, s_src_set_write, s_src_yc_read_ex
};
static const iec104_pts_src_t *s_src;
/* ---------------- COV 快照(遥信侧;遥测侧状态见上方 s_yc_* 块) --------- */
static uint16_t s_yc_have; /* bit i = 已建立上送基线 */
static uint64_t s_yx_last;
static uint8_t s_yx_snap_ok;
/* ============================ 公开接口 ================================== */
/*
* 函数功能:初始化点表模块(死区装 ROM 默认、清 COV/快照状态、恢复默认源)
* 限定条件:内核启动前或 104 服务启动前,调一次
* 函数说明:卡 9 起不再扫 phasor 实例(数据源 = 快照,槽位 = 语义通道,编译期
* 直接可定);重复调用 = 全状态复位(单测 reset 依赖此语义)。
*/
void iec104_pts_init(void) {
/* 死区不在本层复位——真源 = run_par(cfg 引擎副本),复位入口唯一
* run_par_init();本函数只复位 COV 状态(单测 reset 依赖此语义) */
(void)memset(s_yc_rep, 0, sizeof(s_yc_rep));
(void)memset(s_yc_repq, 0, sizeof(s_yc_repq));
(void)memset(s_yc_cnf, 0, sizeof(s_yc_cnf));
s_yc_have = 0u;
s_yc_seq = 0u;
s_yc_seqmode = 1u;
s_src = &k_default_src;
s_yx_snap_ok = 0u;
s_yx_last = 0u;
}
/*
* 函数功能:替换数据源(NULL = 恢复板端默认源;单测注入假源)
* 函数说明:注入源同时把 COV 测点口径切为**每次调用一测点**(假源无快照节拍,
* 步进由调用方控制);默认源 = 测点按 meas 换帧(seq 去重)。换源
* 不重建基线(基线状态统一由 init 复位,防跨源半截确认序列)。
*/
void iec104_pts_bind_src(const iec104_pts_src_t *src) {
if (src != NULL) {
s_src = src;
s_yc_seqmode = 0u;
} else {
s_src = &k_default_src;
s_yc_seqmode = 1u;
}
}
/*
* 函数功能:遥测点数
*/
uint8_t iec104_pts_yc_count(void) {
return YC_N;
}
/*
* 函数功能:遥测点 IOA(按下标;越界 → 0)
*/
uint32_t iec104_pts_yc_ioa(uint8_t idx) {
return (idx < YC_N) ? k_yc[idx].ioa : 0u;
}
/*
* 函数功能:品质域位映射(uint16 meas 域 → uint8 线上 QDS,卡 9)
* 入口参数:q16 - 源侧品质(低字节与线上同值;高字节为 MEAS_QDS_CLIPPED 域内位)
* 返 回 值:线上 QDS 字节 uint8_t
* 函数说明:CLIPPED(硬件削顶,104 无对应位)映射为 IV——「削顶通道 104 恒
* IV」口径(docs/任务规划/数据可信.md §8 风险表末行)改由快照品质
* 驱动,不再靠点表绑死 -1;OV/NT/IV 透传,其余高位丢弃。
*/
static uint8_t s_q_map(uint16_t q16) {
uint8_t w = (uint8_t)(q16 & (uint16_t)(IEC104_QDS_OV | IEC104_QDS_NT
| IEC104_QDS_IV));
if ((q16 & (uint16_t)MEAS_QDS_CLIPPED) != 0u) {
w |= (uint8_t)IEC104_QDS_IV;
}
return w;
}
/*
* 函数功能:读单点遥测(统一源通道 + 品质归一,yc_read 与 COV 共用唯一读径)
* 入口参数:idx - YC 表下标 v/q - 输出(值 / 线上 qds;调用方保证非空)
* 返 回 值:0 有效;-1 IV(含 CLIPPED 映射);-2 NT(未就绪) int
* 函数说明:ex 源在场 → 品质由源给(板端 = 快照槽位透传);否则退 legacy
* yc_read,品质从返回值推导(-1→IV / -2→NT / 0→无位)——旧注入源
* 行为与卡 9 前**完全一致**(单测假源零改动)。判定顺序 IV > NT:
* 开机首帧 qds=IV|NT 按无效处理(NT 只留给纯暂态)。任何无效 →
* 信息体清零(线上口径:主站只信品质)。
*/
static int s_yc_point(uint8_t idx, float *v, uint8_t *q) {
int rc;
uint16_t q16 = 0u;
if ((idx >= YC_N) || (s_src == NULL)
|| ((s_src->yc_read == NULL) && (s_src->yc_read_ex == NULL))) {
*v = 0.0f;
*q = (uint8_t)IEC104_QDS_IV;
return -1;
}
if (s_src->yc_read_ex != NULL) {
rc = s_src->yc_read_ex(idx, v, &q16);
} else {
rc = s_src->yc_read(idx, v);
q16 = (rc == -2) ? (uint16_t)MEAS_QDS_NT
: ((rc != 0) ? (uint16_t)MEAS_QDS_IV : 0u);
}
(void)rc; /* 有效性由归一后的 q 终裁(IV/NT 即无效),源返回值不直通 */
*q = s_q_map(q16);
rc = ((*q & (uint8_t)IEC104_QDS_IV) != 0u) ? -1
: (((*q & (uint8_t)IEC104_QDS_NT) != 0u) ? -2 : 0);
if (rc != 0) {
*v = 0.0f;
}
return rc;
}
/*
* 函数功能:读遥测(值 + 线上品质位;无效值清零,判据见 s_yc_point)
* 返 回 值:0 有效 / -1 无效(NT 亦无效,品质位与 IV 区分) int
*/
int iec104_pts_yc_read(uint8_t idx, float *v, uint8_t *qds) {
float val = 0.0f;
uint8_t q = (uint8_t)IEC104_QDS_IV;
(void)s_yc_point(idx, &val, &q);
if (v != NULL) {
*v = val;
}
if (qds != NULL) {
*qds = q;
}
return (q == 0u) ? 0 : -1;
}
/*
* 函数功能:写死区(卡 9 API 壳,转发 run_par 真源 + 清本层确认计数)
* 入口参数:idx - YC 表下标 db - 工程量绝对死区(≥0,分档上限见 run_par)
* 返 回 值:0 成功;-1 越界/负值/NaN/越程(run_par 副本不动) int
* 限定条件:**iec104Task 上下文**(run_par 副本写 = 引擎对齐 int32 单字,
* 104 执行段之外调用需自行避免与 104 写并发)
* 函数说明:成功时清零该点待确认计数——跨死区档位不保留半截确认序列;
* 不隐式重建基线(上送基准延续,新死区即刻生效)。落盘由 run_par
* 脏标志 + SYS_EVT_RUN_PAR_SAVE 走 defaultTask(卡 10 持久化链)。
*/
int iec104_pts_yc_set_deadband(uint8_t idx, float db) {
if (idx >= YC_N) {
return -1;
}
if (run_par_deadband_set(idx, db) != 0) {
return -1; /* 守卫(NaN/负/越程分档上限)单一实现,在 run_par */
}
s_yc_cnf[idx] = 0u; /* 壳语义:跨死区档位不保留半截确认序列 */
return 0;
}
/*
* 函数功能:读当前生效死区(转发 run_par 真源)
* 入口参数:idx - YC 表下标
* 返 回 值:生效死区;idx 越界返 0.0 float
* 限定条件:任意上下文(引擎副本 int32 单字读,Cortex-M 原子)
* 函数说明:UI 回显 / 读写往返校验用;工厂默认查点表 db_default 列。
*/
float iec104_pts_yc_deadband(uint8_t idx) {
return (idx < YC_N) ? run_par_deadband(idx) : 0.0f;
}
/*
* 函数功能:遥信点数
*/
uint8_t iec104_pts_yx_count(void) {
return (uint8_t)YX_N;
}
/*
* 函数功能:遥信点 IOA(idx + 1;越界 → 0)
*/
uint32_t iec104_pts_yx_ioa(uint8_t idx) {
return ((uint8_t)idx < YX_N) ? ((uint32_t)idx + 1u) : 0u;
}
/*
* 函数功能:该遥信是否双点型(idx < 4 = 开关位置类)
*/
uint8_t iec104_pts_yx_is_dp(uint8_t idx) {
return ((uint8_t)idx < YX_DP_N) ? 1u : 0u;
}
/*
* 函数功能:读遥信(无效通道 → DIQ_IV;双点 → DPC 编码)
*/
void iec104_pts_yx_read(uint8_t idx, uint8_t *out) {
uint64_t bits;
uint64_t valid;
uint8_t on;
if (out == NULL) {
return;
}
if ((idx >= YX_N) || (s_src == NULL) || (s_src->yx_bits == NULL)
|| (s_src->yx_valid == NULL)) {
*out = IEC104_DIQ_IV;
return;
}
valid = s_src->yx_valid();
if (((valid >> idx) & 1u) == 0u) {
*out = IEC104_DIQ_IV;
return;
}
bits = s_src->yx_bits();
on = (uint8_t)((bits >> idx) & 1u);
if (iec104_pts_yx_is_dp(idx) != 0u) {
*out = (on != 0u) ? (uint8_t)IEC104_DPC_ON : (uint8_t)IEC104_DPC_OFF;
} else {
*out = on;
}
}
/*
* 函数功能:COV 快照比对(遥信 = 有效位翻转;遥测 = 死区)
* 入口参数:cov = 输出变位位图(NULL = 只刷快照)
*/
void iec104_pts_cov_check(iec104_pts_cov_t *cov) {
iec104_pts_cov_t local;
(void)memset(&local, 0, sizeof(local));
/* YX:变位 = 有效位上的位翻转(无效位不参与,避免 IV 通道抖动成"变位") */
if ((s_src != NULL) && (s_src->yx_bits != NULL)
&& (s_src->yx_valid != NULL)) {
uint64_t bits = s_src->yx_bits() & s_src->yx_valid();
if (s_yx_snap_ok != 0u) {
local.yx_changed = bits ^ s_yx_last;
}
s_yx_last = bits;
s_yx_snap_ok = 1u;
}
/* YC(卡 9):测点推进 = 板端源按快照换帧(100 ms poll vs 200 ms 快照,
* 不去重则同一帧计两次 → "连续 2 点确认"退化成"连续 2 次轮询",单帧
* 噪声即可被确认,抗噪语义失效);注入源 = 每次调用一测点。
* 判定 = 死区连续 2 点确认 + 品质翻转直报(口径全文 → iec104_pts.h)。 */
if ((s_src != NULL)
&& ((s_src->yc_read != NULL) || (s_src->yc_read_ex != NULL))) {
uint8_t step = 1u;
if (s_yc_seqmode != 0u) {
uint32_t sq = meas_seq();
if (sq == s_yc_seq) {
step = 0u; /* 未换帧:本 poll 不算新测量点 */
} else {
s_yc_seq = sq;
}
}
if (step != 0u) {
uint8_t i;
for (i = 0u; i < YC_N; i++) {
float v;
uint8_t q;
int rc = s_yc_point(i, &v, &q);
if (((s_yc_have >> i) & 1u) == 0u) {
if (rc == 0) { /* 首个有效读数只建线不上报(初值归总召) */
s_yc_have |= (uint16_t)(1u << i);
s_yc_rep[i] = v;
s_yc_repq[i] = q;
s_yc_cnf[i] = 0u;
}
continue;
}
if (q != s_yc_repq[i]) {
/* 品质翻转 = 事件直报(含 0x401d 的 IV 进出,卡 11) */
local.yc_changed |= (uint16_t)(1u << i);
s_yc_repq[i] = q;
s_yc_cnf[i] = 0u;
if (rc == 0) {
s_yc_rep[i] = v;
}
continue;
}
if (rc != 0) {
s_yc_cnf[i] = 0u; /* 无效期间不计数(品质未变 = 状态延续) */
continue;
}
{
float d = v - s_yc_rep[i];
if (d < 0.0f) {
d = -d;
}
if (d > run_par_deadband(i)) {
s_yc_cnf[i]++;
if (s_yc_cnf[i] >= 2u) { /* 连续 2 越 → 确认:报 + 移基准 */
local.yc_changed |= (uint16_t)(1u << i);
s_yc_rep[i] = v;
s_yc_cnf[i] = 0u;
}
} else {
s_yc_cnf[i] = 0u; /* 回摆入死区 → 撤销候选(抗噪核心) */
}
}
}
}
}
if (cov != NULL) {
*cov = local;
}
}
/*
* 函数功能:遥控两段式(转发数据源;select=1 选择段 / 0 执行段)
* 返 回 值:0 接受 / -1 拒绝(调用方回 PN)
*/
int iec104_pts_yk_exec(uint32_t ioa, uint8_t dpc, uint8_t select) {
if ((s_src == NULL) || (s_src->yk_exec == NULL)) {
return -1;
}
return s_src->yk_exec(ioa, dpc, select);
}
/*
* 函数功能:按通讯地址读定值(工程量浮点)
* 返 回 值:0 / -1 未知地址
*/
int iec104_pts_set_read(uint32_t addr, float *out) {
if ((s_src == NULL) || (s_src->set_read == NULL)) {
return -1;
}
return s_src->set_read(addr, out);
}
/*
* 函数功能:按通讯地址写定值(select=1 只校验;0 执行写入)
* 返 回 值:0 / -1 越界或未知地址
*/
int iec104_pts_set_write(uint32_t addr, float v, uint8_t select) {
if ((s_src == NULL) || (s_src->set_write == NULL)) {
return -1;
}
return s_src->set_write(addr, v, select);
}
/* ============================ 板端默认源 ================================= */
/*
* 函数功能:板端默认源 - 遥测 = L2 遥测快照直读(「数据可信」卡 9/卡 11)
* 入口参数:idx - YC 表下标 out/qds - 值 + uint16 域品质(MEAS_QDS_*)
* 返 回 值:0 / -1 越界或频率未新鲜(IV)/ -2 快照从未发布(NT) int
* 限定条件:iec104Task 上下文(meas_get 为双区无锁读,App/algo/meas.h)
* 函数说明:**不做任何本地判断**——电气路值/品质整体透传快照槽(削顶路快照
* 已置 CLIPPED,s_q_map 映上线 IV;开机首帧 IV|NT 由 s_yc_point 按
* IV 处理),"新不新、能不能信"全在发布侧一处决定(卡 8 设计意图,
* 两处判断必打架)。频率路(PTS_SRC_FREQ):snap.freq_valid==0 →
* IV(卡 11 DoD 原句);有效时值 = snap.freq_hz,与 freq_get() 同源。
*/
static int s_src_yc_read_ex(uint8_t idx, float *out, uint16_t *qds) {
meas_snap_t snap;
if ((idx >= YC_N) || (out == NULL) || (qds == NULL)) {
return -1;
}
if (meas_get(&snap) != 0) {
*out = 0.0f; /* 从未发布(开机 <200 ms)= 暂态 */
*qds = (uint16_t)(MEAS_QDS_IV | MEAS_QDS_NT);
return -2;
}
if (k_yc[idx].src == (uint8_t)PTS_SRC_FREQ) {
if (snap.freq_valid == 0u) {
*out = 0.0f;
*qds = (uint16_t)MEAS_QDS_IV;
return -1;
}
*out = snap.freq_hz;
*qds = 0u;
return 0;
}
if (k_yc[idx].sem < 0) {
*out = 0.0f; /* 理论不可达:现无未绑测量量点 */
*qds = (uint16_t)MEAS_QDS_IV;
return -1;
}
*out = snap.yc[(uint8_t)k_yc[idx].sem];
*qds = snap.qds[(uint8_t)k_yc[idx].sem];
return 0;
}
/*
* 函数功能:板端默认源 - 遥信位图(保护元件动作态 + 定值闭锁)
*/
static uint64_t s_src_yx_bits(void) {
prot_elm_state_t oc1;
prot_elm_state_t oc2;
prot_elm_state_t oc3;
prot_elm_state_t ol;
prot_elm_state_t u0;
uint64_t bits = 0u;
prot_elm_state(PE_OC1, &oc1);
prot_elm_state(PE_OC2, &oc2);
prot_elm_state(PE_OC3, &oc3);
prot_elm_state(PE_OL, &ol);
prot_elm_state(PE_3U0, &u0);
if ((oc1.asserted != 0u) || (oc2.asserted != 0u) || (oc3.asserted != 0u)
|| (u0.asserted != 0u)) {
bits |= (1uLL << 14u); /* YX15 故障总 */
}
if (oc1.asserted != 0u) {
bits |= (1uLL << 15u); /* YX16 过流 I 段 */
}
if (oc2.asserted != 0u) {
bits |= (1uLL << 16u); /* YX17 过流 II 段 */
}
if (oc3.asserted != 0u) {
bits |= (1uLL << 17u); /* YX18 过流 III 段 */
}
if (ol.asserted != 0u) {
bits |= (1uLL << 22u); /* YX23 过载 */
}
if (prot_set_locked() != 0u) {
bits |= (1uLL << 30u); /* YX31 保护闭锁 */
}
return bits;
}
/*
* 函数功能:板端默认源 - 遥信有效位图(V0.1 = YX15~18/23/31)
*/
static uint64_t s_src_yx_valid(void) {
return YX_VALID_MASK;
}
/*
* 函数功能:板端默认源 - 遥控(YK11 复归 → prot_elm_request_reset;
* YK12 全局软压板 → PS_GLOBAL_EN 两段式转真,B7)
* 返 回 值:0 / -1 无出口硬件或未知 IOA(YK1~4/13/14 负向确认)
* 函数说明:本回调跑在 iec104Task 上下文,**不得**直接 prot_elm_init()
* (与 protectTask 的 eval 竞态)→ 落请求,下一拍 eval 头部消费(B5)。
* YK12 走 prot_set_set_sealed(原子写+封存,B7);protectTask 下一拍
* 读到压板态即复位元件并停判据(protect_task.c)。
*/
static int s_src_yk_exec(uint32_t ioa, uint8_t dpc, uint8_t select) {
switch (ioa) {
case 0x600Au: /* YK11 信号复归 → 元件状态复位(异步一拍) */
(void)dpc; /* 复归与双点值无关 */
if (select == 0u) {
prot_elm_request_reset();
}
return 0;
case 0x600Bu: /* YK12 全局软压板(PS_GLOBAL_EN)两段式 */
if ((dpc != (uint8_t)IEC104_DPC_ON) && (dpc != (uint8_t)IEC104_DPC_OFF)) {
return -1; /* 非法双点命令:选择段即拒 */
}
if (select != 0u) {
return 0; /* 选择段 = 只校验 */
}
return prot_set_set_sealed(PS_GLOBAL_EN,
(dpc == (uint8_t)IEC104_DPC_ON)
? PROT_SET_Q10(1) : PROT_SET_Q10(0));
default:
return -1; /* 其余 YK:无出口硬件/无对应能力 → 拒绝 */
}
}
/*
* 函数功能:板端默认源 - 定值读(**路由顺序 = run_par 表先、未命中落 prot_set**:
* 0x8027~0x802A 属 prot_set 告警段,run_par 命中集为精确地址匹配、
* 永不覆盖之;两表共存靠「先专属表、后通用扫」分流,勿反序——反序
* 当前无冲突但失去 run_par 守卫先行留痕的意义)
* 返 回 值:0 / -1 未知地址(本地项 comm_addr=0 不可寻址,B7 起显式排除)
*/
static int s_src_set_read(uint32_t addr, float *out) {
uint32_t i;
if (out == NULL) {
return -1;
}
if (run_par_104_read(addr, out) == 0) {
return 0; /* 运行参数段命中(卡 10) */
}
for (i = 0u; i < (uint32_t)PS_ID_COUNT; i++) {
if ((g_prot_set_meta[i].comm_addr != 0u)
&& (g_prot_set_meta[i].comm_addr == (uint16_t)addr)) {
*out = prot_set_get_f((prot_set_id_t)i);
return 0;
}
}
return -1;
}
/*
* 函数功能:板端默认源 - 定值写(**先 run_par 后 prot_set**,分流理由见
* s_src_set_read 头注;prot_set 腿 = Q10 范围校验 + 执行段
* 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) {
uint32_t i;
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++) {
const prot_set_meta_t *meta = &g_prot_set_meta[i];
if ((meta->comm_addr == 0u) || (meta->comm_addr != (uint16_t)addr)) {
continue;
}
q10 = PROT_SET_Q10(v);
if ((q10 < meta->min) || (q10 > meta->max)) {
return -1; /* 越界:选择段即拒(执行段同样拒) */
}
if (select != 0u) {
return 0; /* 选择段 = 只做范围校验 */
}
return prot_set_set_sealed((prot_set_id_t)i, q10);
}
return -1; /* 未知地址 */
}