058e7c061c
- IEC104上行设计说明.md 与 iec104_pts.c 的 s_q_map 注释原写「§8 风险 表末行」,末行其实是「加压烧板」;含单测要求的是「削顶通道被当成可 测」行。两处都改成行名,行名完整落在一行内 - ADC前端注入验证流程.md 同源错引用改成「削顶负向用例被当成固件 bug」行 - 数据可信.md §8 表头加一条引用规矩:写行名,别写「末行 / 第 N 行」, 本表会插行 - doc-links 只校验章节编号存在性,看不见「末行」这类行内指向,也从不扫 .c/.h —— 这两处此前没有门能拦 @build 0 errors 0 warnings(只改注释,Program Size 未变)
683 lines
30 KiB
C
683 lines
30 KiB
C
/*
|
||
* 模块名称:IEC 60870-5-104 点表与数据源适配(实现)
|
||
* 模块功能:实现 iec104_pts.h——YC/YX/YK/定值点表(ROM)、板端默认源
|
||
* (**测频与遥测快照**(104 改读快照)/ prot_elm / prot_set)、COV 比对(遥信变位 +
|
||
* 遥测死区 2 点确认 + 品质变化)。点表口径见 iec104_pts.h 文件头与
|
||
* 设计文档 §3。
|
||
* 线程约定:init/bind 建服务前;其余仅 104 服务任务上下文(快照非原子)。
|
||
* 创建日期:2026-10-12(B4)
|
||
* 修改记录:
|
||
* 2026-10-25 DTU 项目组 下游常数按工程量重定(「数据可信」):YC 死区由拟定码值域重定为
|
||
* 二次值工程量域(额定 0.5 % 规则),判据依据见
|
||
* k_yc 表头注释;数值随 104 改读快照迁运行参数,本表将退化为
|
||
* ROM 默认载体。
|
||
* 2026-10-25 DTU 项目组 104 改读快照与频率点绑定(「数据可信」,道D):YC 板端源改读
|
||
* 测频与遥测快照(App/algo/meas.h,meas_get 无锁双区);
|
||
* 削顶五路由快照品质 CLIPPED 驱动线上 IV(不再绑死 -1,
|
||
* V0.2 消除双转换器无共同采样触发后零改动自动启用);0x401d 绑频率源(频率点绑定);
|
||
* 死区改运行时副本 + set/get 入口;COV 遥测腿 = 死区
|
||
* 连续 2 点确认(板端按快照 seq 去重)+ 品质翻转即报。
|
||
* 单测 = test/host/iec104 TC-IEC104-025~028。
|
||
* 运行参数段 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)。
|
||
* 2026-10-26 DTU 项目组 道G task-8「UI 补齐」第 5 项非 UI 前置:新增
|
||
* [comm] 通讯参数段(ca/port)初版落本文件尾部;
|
||
* s_src_yk_exec() 等遥控/记录投递路径零改动。
|
||
* 2026-10-26 DTU 项目组 同日晚纠偏:[comm] 实现整体迁出至配对模块
|
||
* iec104_comm.c(coding-standard CODE-RULE-17 (2.2)
|
||
* 按文件名配对判定,本文件的公开符号必须声明于
|
||
* iec104_pts.h——[comm] 不属点表,拆出正解;契约
|
||
* = iec104_comm.h,本文件不再引用 cfg_file)。
|
||
*/
|
||
#include "iec104_pts.h"
|
||
|
||
#include <string.h>
|
||
|
||
#include "iec104_asdu.h" /* QDS/DIQ 品质位常量 */
|
||
#include "meas.h" /* 板端源:遥测 = 测频与遥测快照(104 改读快照;品质域位同源) */
|
||
#include "run_par.h" /* 运行参数段:死区真源 + 104 读写路由 */
|
||
#include "prot_elm.h" /* 板端源:遥信 = 元件状态 */
|
||
#include "prot_set.h" /* 板端源:定值 + 闭锁 */
|
||
#include "sys_log.h" /* task-10:遥控记录(SYSLOG_YK)/ 操作记录(SYSLOG_OPER) */
|
||
#include "adc_input.h" /* adc_semantic_t(phasor 实例按语义绑定) */
|
||
|
||
/* ---------------- YC 表(§1.5 点号 4011+;死区列 = ROM 默认值) ------------ */
|
||
/* 源类型(「数据可信」频率点绑定:YC 表加 src 类型区分相量/频率) */
|
||
#define PTS_SRC_MEAS 0u /* 全周傅氏相量快照槽 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] 段 = 运行参数段真源) */
|
||
uint8_t src; /* PTS_SRC_* 源类型 */
|
||
} pts_yc_def_t;
|
||
|
||
/* 死区规则 = 额定二次值的 0.5 %(「数据可信」§2.3 表第 2 行建议规则,随下游常数按工程量重定采纳;
|
||
* 额定基准 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 并随
|
||
* 运行参数段可调)。自 104 改读快照起本表**只作工厂默认载体**:生效死区 =
|
||
* App/algo/run_par.c 的 [run_par] 引擎副本(运行参数段真源收编,本层不再存副本);削顶五路不再 sem=-1 绑死,而是绑快照槽、由品质 CLIPPED
|
||
* 驱动线上 IV——V0.2 削顶消失后点位零改动自动启用(docs/任务规划/
|
||
* 数据可信.md §10 的「双转换器无共同采样触发」行)。
|
||
* ⚠ 过渡期(eng=0 引脚电压域,两点标定完成前):上送值本身非二次值,死区
|
||
* 数字按本域偏保守(迟报不漏报);标定落地后复核(「数据可信」§2.3 表
|
||
* 第 2 行)。判定 = 死区连续 2 点确认(104 改读快照),见 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:频率点绑定——绑频率源,
|
||
* freq_valid=0 → IV */
|
||
};
|
||
#define YC_N ((uint8_t)(sizeof(k_yc) / sizeof(k_yc[0])))
|
||
|
||
/* ===== 遥测 COV 运行时状态(104 改读快照;仅 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 服务启动前,调一次
|
||
* 函数说明:104 改读快照起不再扫 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,104 改读快照)
|
||
* 入口参数: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→无位)——旧注入源
|
||
* 行为与 104 改读快照前**完全一致**(单测假源零改动)。判定顺序 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;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:写死区(104 改读快照的 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(运行参数段持久化链)。
|
||
*/
|
||
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(104 改读快照):测点推进 = 板端源按快照换帧(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 进出,频率点绑定) */
|
||
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);
|
||
}
|
||
|
||
/* ============================ 板端默认源 ================================= */
|
||
|
||
/*
|
||
* 函数功能:板端默认源 - 遥测 = 测频与遥测快照直读(「数据可信」104 改读快照与频率点绑定)
|
||
* 入口参数: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 处理),"新不新、能不能信"全在发布侧一处决定(遥测快照发布的设计意图,
|
||
* 两处判断必打架)。频率路(PTS_SRC_FREQ):snap.freq_valid==0 →
|
||
* IV(频率点绑定 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)。
|
||
*/
|
||
/* 遥控记录的文案表(task-10)。**全部 ROM 常量、零拼接**:本回调跑在
|
||
* iec104Task,而 iec104_serv.c 的日志缓冲已经因为"深链模块"被收到 128 B,
|
||
* 这里再 snprintf 一条"YK ioa=0x600B dpc=2 sel=0 rc=0"就是往那条链上加缓冲;
|
||
* 记录类型由 SYSLOG_YK 这个文件本身回答,屏上话术归 UI(App/log/README.md 口径)。
|
||
* 最长一条 20 字符 < SYSLOG_DESC_MAX(28)。 */
|
||
#define YK_REC_YK11_SEL "YK11 select"
|
||
#define YK_REC_YK11_EXEC "YK11 exec"
|
||
#define YK_REC_YK12_SEL "YK12 select"
|
||
#define YK_REC_YK12_ON "YK12 exec on"
|
||
#define YK_REC_YK12_OFF "YK12 exec off"
|
||
#define YK_REC_YK12_FAIL "YK12 exec fail"
|
||
#define YK_REC_DPC_BAD "YK12 bad dpc"
|
||
#define YK_REC_UNKNOWN "YK unknown ioa"
|
||
|
||
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();
|
||
/* 遥控记录(执行段已受理)+ 操作记录(本地/远端强制复归)。
|
||
* 两条不是重复留痕:前者回答"主站下了什么命令、结果如何",后者
|
||
* 回答"装置被谁改动了状态"——UI 的遥控记录页与操作记录页各读一份。
|
||
* YK11 没有可失败的下游(request_reset 只置单字节标志),所以它
|
||
* 只有受理态,没有 fail 分支。 */
|
||
syslog_post_quiet(SYSLOG_YK, YK_REC_YK11_EXEC);
|
||
syslog_post_quiet(SYSLOG_OPER, "force reset");
|
||
} else {
|
||
syslog_post_quiet(SYSLOG_YK, YK_REC_YK11_SEL);
|
||
}
|
||
return 0;
|
||
case 0x600Bu: { /* YK12 全局软压板(PS_GLOBAL_EN)两段式 */
|
||
int rc;
|
||
if ((dpc != (uint8_t)IEC104_DPC_ON) && (dpc != (uint8_t)IEC104_DPC_OFF)) {
|
||
syslog_post_quiet(SYSLOG_YK, YK_REC_DPC_BAD);
|
||
return -1; /* 非法双点命令:选择段即拒 */
|
||
}
|
||
if (select != 0u) {
|
||
syslog_post_quiet(SYSLOG_YK, YK_REC_YK12_SEL);
|
||
return 0; /* 选择段 = 只校验 */
|
||
}
|
||
/* 执行段:set_sealed 自带范围校验与封存,失败 = 值被拒、RAM 未改。
|
||
* 这里**必须接住返回值再落记录**(bsp-err:错误码不得裸丢弃;
|
||
* 原实现是 return 直出,现在多一条记录后仍然原值返回,语义不变)。 */
|
||
rc = prot_set_set_sealed(PS_GLOBAL_EN,
|
||
(dpc == (uint8_t)IEC104_DPC_ON)
|
||
? PROT_SET_Q10(1) : PROT_SET_Q10(0));
|
||
if (rc != 0) {
|
||
syslog_post_quiet(SYSLOG_YK, YK_REC_YK12_FAIL);
|
||
} else {
|
||
syslog_post_quiet(SYSLOG_YK,
|
||
(dpc == (uint8_t)IEC104_DPC_ON) ? YK_REC_YK12_ON
|
||
: YK_REC_YK12_OFF);
|
||
/* 压板投退是一次真实的运维动作(保护退出/恢复服务)→ 同时进操作记录 */
|
||
syslog_post_quiet(SYSLOG_OPER,
|
||
(dpc == (uint8_t)IEC104_DPC_ON) ? "plate in"
|
||
: "plate out");
|
||
}
|
||
return rc;
|
||
}
|
||
default:
|
||
syslog_post_quiet(SYSLOG_YK, YK_REC_UNKNOWN);
|
||
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; /* 运行参数段命中 */
|
||
}
|
||
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; /* 未知地址 */
|
||
}
|