b7c0889cc7
用户裁定「不要用字母加数字的编号,语义标识是最好的」后的批次二代码半(道 ui-param 执行,逐文件站点数与 自证见其交付)。唯一口径 = 映射表 ci-artifacts/lprot_map.tsv 的功能名列原文,**零自造同义词**;L2.5 与 L-TEST 经实测各 0 处(不是本族),TC 族一字未动。 固件 22 文件 128 处(adc_task.c 21、phasor.c 22、phasor.h 13…),主机测试 13 文件 87 处(pha sor_test.c 57…);设计文档指针新增 19 行,另 16 只文件本来就写着规划文档 ⇒ 不重复加。 **两条只有真改过 215 处才会踩到的坑,都写进注释规范实践**: · 指针**不能接在原句尾部**——那会替原文一个未闭合的半括号封口,实测咬坏 App/algo/sample_ring.h 这类 跨行句 ⇒ 指针一律**单独成行**; · 号删掉会留下**叠词**(「自检失败闭锁 闭锁判定」「定值校验和 定值巡检」)⇒ 复扫并收口 4 处,措辞只用 映射名本身;具体对象需要留住时写成「自检失败闭锁(采样故障闭锁)」。 自证(不靠嘴说):改前把 35 只文件原文备份到仓外 temp,逐文件比 `/*`、`*/` 计数、三类括号计数、行数增量 恰等于指针行数、全角括号增删相等、残留号 0 ⇒ **35/35 通过**;Lead 复核后跑权威 @build **0 错 0 警**, Program Size `Code=233900 RO=26104 RW=1536 ZI=136808` —— **与三条道各自改前改后测的四数逐 字节同数**, 这就是「只动注释与文本」的机器证据;八套主机单测 exit 0,cppcheck 按钩子参数零发现,coding-standard 0。 文件头「修改记录」里的号**一并换名、不还原**(Lead 裁定):这些号是全仓退役的,留一个查无此号的引用比 留一条改过名的条目更坏——「冻结历史不回改」保护的是**别替历史改口**,不是保护一个再也查不到的字符串; id-check 对变更记录行本就豁免,两种选择都不影响门禁。
183 lines
9.9 KiB
C
183 lines
9.9 KiB
C
/*
|
||
* 模块名称:保护定值模型(P1,定值校验和 载体)
|
||
* 模块功能:保护定值的 RAM 模型——编号 / 通讯地址 / 范围 / 默认值 / 校验和自检,
|
||
* 以及 B7 定值持久化的**纯逻辑层**(blob 序列化/解析 + 原子装载)。
|
||
* 文件读写胶水在 App/cfg/prot_set_store.c(MCU 端)。
|
||
*
|
||
* 设计口径(docs/算法/继电保护功能复现规划.md §5):
|
||
* - 定点存储:Q10(×1024),与参考系统定点系数 2^10 一致(§5.3);
|
||
* - 通讯地址:与参考 g_tCommSetParaTab(Comm/Pub/Const_CommSetPara.c)逐项对齐,
|
||
* 基址 0x8820(=34848);告警类沿用运行参数段 0x8027/0x8029;
|
||
* - 校验和(定值校验和):sum32 正码 + 反码成对封存(参考 tagPRS 的
|
||
* dwTagSum/dwTagSumN 模式),selfcheck 失配 → 置闭锁;
|
||
* - ⚠ 默认值/范围为**拟定值**:参考 EEPROM 出厂默认表未在本仓库,
|
||
* 待对照后逐项修订(规划 §10.3 待补项);范围先按 DTU 馈线典型量级。
|
||
*
|
||
* 线程约定(B7 定):装载(init/store_parse)在 defaultTask(存储 bring-up,
|
||
* STORAGE_READY 前);单项写在 iec104Task(C_SE/YK12,经 set_sealed 原子路径);
|
||
* 落盘在 defaultTask(配置引擎属主)。读在保护消费者任务(快照值原子)。
|
||
* 双缓冲交换 = 单字写,读侧永远看到一致快照;写者之间靠启动时序串行
|
||
* (装载先于 NET_READY,104 写在其后)。
|
||
*
|
||
* 创建日期:2026-10-11
|
||
*/
|
||
#ifndef PROT_SET_H
|
||
#define PROT_SET_H
|
||
|
||
#include <stdint.h>
|
||
|
||
/* Q10 定点:工程量 × 1024(与参考 2^10 口径一致)。
|
||
* 必须写成单精度:×/÷1024 只是 2^10 指数平移,float 与 double 的结果逐位相同
|
||
* (宏里 42 个字面量 + int32 全域往返均已穷举核对),而写成 double 会把
|
||
* MicroLIB 的软浮点拉进镜像——F407 只有单精度 FPU,本工程的保护判据每
|
||
* 20 ms 节拍每元件要读三次定值(见 prot_elm_run),软件双精度纯属白烧。 */
|
||
#define PROT_SET_Q10(v) ((int32_t)((float)(v) * 1024.0f))
|
||
#define PROT_SET_FROM_Q10(v) ((float)(v) / 1024.0f)
|
||
|
||
/* 定值项编号(枚举序 = RAM 数组序 = 校验和覆盖序,只增不删不改序) */
|
||
typedef enum {
|
||
/* --- 三段式相间过流(A 相):投退/跳闸投退/电流/延时 --- */
|
||
PS_OC1_EN = 0, /* 0x8856 Ⅰ段投退 */
|
||
PS_OC1_TRIP, /* 0x8857 Ⅰ段跳闸投退(0 告警 / 1 跳闸) */
|
||
PS_OC1_CUR, /* 0x8858 Ⅰ段电流定值(A,二次值) */
|
||
PS_OC1_DLY, /* 0x8859 Ⅰ段延时(ms) */
|
||
PS_OC2_EN, /* 0x885A */
|
||
PS_OC2_TRIP, /* 0x885B */
|
||
PS_OC2_CUR, /* 0x885C */
|
||
PS_OC2_DLY, /* 0x885D */
|
||
PS_OC3_EN, /* 0x885E */
|
||
PS_OC3_TRIP, /* 0x885F */
|
||
PS_OC3_CUR, /* 0x8860 */
|
||
PS_OC3_DLY, /* 0x8861 */
|
||
/* --- 过负荷(A 相) --- */
|
||
PS_OL_EN, /* 0x8871 */
|
||
PS_OL_TRIP, /* 0x8872 */
|
||
PS_OL_CUR, /* 0x8873 */
|
||
PS_OL_DLY, /* 0x8874 */
|
||
/* --- 过压 / 欠压(UA;沿用参考告警段 0x8029/0x8027) --- */
|
||
PS_OV_EN, /* 本地投退(参考告警类无独立投退,V0.1 补) */
|
||
PS_OV_VOL, /* 0x8029 过电压门限(V,二次值) */
|
||
PS_OV_DLY, /* 0x802A 过电压延时(ms) */
|
||
PS_UV_EN, /* 本地投退 */
|
||
PS_UV_VOL, /* 0x8027 低电压门限(V,二次值) */
|
||
PS_UV_DLY, /* 0x8028 低电压延时(ms) */
|
||
/* --- 零序过压(UN) --- */
|
||
PS_3U0_EN, /* 0x888E */
|
||
PS_3U0_VOL, /* 0x888F 3U0 定值(V,二次值) */
|
||
PS_3U0_DLY, /* 0x8890 延时(ms) */
|
||
/* --- 公共 --- */
|
||
PS_RET_OVER, /* 过量返回系数(返回门槛 = 定值 × 系数,<1) */
|
||
PS_RET_UNDER, /* 欠量返回系数(返回门槛 = 定值 × 系数,>1) */
|
||
PS_PT_PRI, /* 0x8820 PT 一次额定(V) */
|
||
PS_PT_SEC, /* 0x8821 PT 二次额定(V) */
|
||
PS_CT_PRI, /* 0x8825 CT 一次额定(A) */
|
||
PS_CT_SEC, /* 0x8826 CT 二次额定(A) */
|
||
/* --- 全局软压板(B7):104 YK12 两段式投退;0 = 保护退出服务 --- */
|
||
PS_GLOBAL_EN, /* 本地项(无通讯地址,经 YK12 写、C_RD 不可读) */
|
||
/* --- 过频 / 低频(B8):投退/动作频率/延时 + 各自返回系数 ---
|
||
* ⚠ 通讯地址:参考 g_tCommSetParaTab 的保护定值段(0x8820~0x8882)
|
||
* 含"低压/低频"类目(规划 §5.1),但 OF/UF 的**逐项地址不在本仓库**
|
||
* (参考 Comm 源文件未随仓库提供)→ 按"找不到对应即本地项"规则全部
|
||
* comm_addr=0 登记本地项(104 C_SE 不可寻址),待对照参考表后补。
|
||
* ⚠ 默认值/范围为**拟定**(同全表口径,规划 §5.4 落实进度注)。 */
|
||
PS_OF_EN, /* 过频投退(本地) */
|
||
PS_OF_HZ, /* 过频动作频率(Hz) */
|
||
PS_OF_DLY, /* 过频延时(ms) */
|
||
PS_UF_EN, /* 低频投退(本地) */
|
||
PS_UF_HZ, /* 低频动作频率(Hz) */
|
||
PS_UF_DLY, /* 低频延时(ms) */
|
||
PS_RET_OF, /* 过频返回系数(返回门槛 = 定值 × 系数,<1;本地) */
|
||
PS_RET_UF, /* 低频返回系数(返回门槛 = 定值 × 系数,>1;本地) */
|
||
PS_ID_COUNT
|
||
} prot_set_id_t;
|
||
|
||
/* 定值项元数据(ROM 常量表,校验和自检不覆盖它——只覆盖 RAM 值区) */
|
||
typedef struct {
|
||
uint16_t comm_addr; /* 通讯信息体地址(0 = 本地项,不上送) */
|
||
uint8_t is_ctl; /* 1 = 投退/枚举项(0/1),0 = 模拟量项 */
|
||
int32_t def; /* 默认值(Q10;投退项为 0/1 的 Q10) */
|
||
int32_t min; /* 下限(Q10) */
|
||
int32_t max; /* 上限(Q10) */
|
||
} prot_set_meta_t;
|
||
|
||
extern const prot_set_meta_t g_prot_set_meta[PS_ID_COUNT];
|
||
|
||
/*
|
||
* 函数功能:装载出厂默认值并封存校验和(幂等,内核启动前可调)
|
||
*/
|
||
void prot_set_init(void);
|
||
|
||
/*
|
||
* 函数功能:整表装载外部值(P4 起由 cfg_file 定值段调用),逐项范围校验
|
||
* 入口参数:vals - PS_ID_COUNT 个 Q10 值
|
||
* 返 回 值:0 全部合法(已装载并封存)/ -1 存在越界项(**整表不装载**,
|
||
* 返回前可经 prot_set_bad_mask() 取越界位图)
|
||
*/
|
||
int prot_set_load_raw(const int32_t *vals);
|
||
|
||
/* 越界位图(bit i = 第 i 项越界;load_raw 失败后有效。B8:项数 32→40,
|
||
* 位图随项数扩为 64 位——消费方仅主机单测,板端闭锁走 prot_set_locked()) */
|
||
uint64_t prot_set_bad_mask(void);
|
||
|
||
/*
|
||
* 函数功能:单项写入(范围校验;**不自动重封校验和**——批量改完必须
|
||
* 显式 prot_set_seal(),否则 selfcheck 判"定值被非法改写")
|
||
* 返 回 值:0 / -1 越界拒写
|
||
*/
|
||
int prot_set_set(prot_set_id_t id, int32_t q10);
|
||
|
||
int32_t prot_set_get(prot_set_id_t id); /* Q10 原值 */
|
||
float prot_set_get_f(prot_set_id_t id); /* 工程量浮点 */
|
||
uint8_t prot_set_get_ctl(prot_set_id_t id); /* 投退项:!=0 视为投入 */
|
||
|
||
/* 校验和(定值校验和):sum32 覆盖 RAM 值区;seal 封存正/反码对 */
|
||
uint32_t prot_set_checksum(void);
|
||
void prot_set_seal(void);
|
||
/* 自检:重算并与封存值比对,失配 → 置闭锁并返回 -1(已闭锁也返回 -1) */
|
||
int prot_set_selfcheck(void);
|
||
uint8_t prot_set_locked(void);
|
||
|
||
/*
|
||
* 函数功能:单项**原子**写入+封存(B7,收口 B5 竞态窗口)
|
||
* 入口参数:id - 定值项编号;q10 - Q10 定点值
|
||
* 返 回 值:0 / -1 越界或非法编号拒写(原值不动)
|
||
* 函数说明:影子缓冲拷贝 → 改影子 → 重封校验和 → 单字交换活跃区。
|
||
* selfcheck 永远看到"值区与封存一致"的完整快照,不存在
|
||
* "已改值未封存"中间态(区别于 prot_set_set+seal 的批处理路径,
|
||
* 两条路径不得在一次批处理中途交错)。置脏标志(待持久化)。
|
||
*/
|
||
int prot_set_set_sealed(prot_set_id_t id, int32_t q10);
|
||
|
||
/* 脏标志(存在未落盘的定值改动):set_sealed 置位;load/init 不置 */
|
||
uint8_t prot_set_dirty_peek(void); /* 非消费(生产者用它通知属主任务) */
|
||
uint8_t prot_set_dirty_take(void); /* 消费(属主任务落盘前取) */
|
||
void prot_set_dirty_mark(void); /* 写盘失败回置(等下轮重试) */
|
||
|
||
/* ---------------- 持久化 blob(B7;纯逻辑,主机可单测) ----------------
|
||
* 格式(小端):magic 'PSET'(4B) + version u16 + count u16
|
||
* + values u32×count + sum32 u32 + ~sum32 u32
|
||
* 版本 2(B8:定值表 32→40 项,OF/UF 尾追加);count 必须等于 PS_ID_COUNT
|
||
* (定值表增项 → 旧 blob 整体作废,编号不回收但格式换代,宁可用出厂默认
|
||
* 也不半装载)。ver 1 旧档在 parse 即被版本闸拒绝 → 出厂默认 + 日志
|
||
* (措辞 = "version change",非 CORRUPT,见 prot_set_store/default_task)。 */
|
||
#define PROT_SET_BLOB_MAGIC 0x50534554uL /* 字节序 P,S,E,T */
|
||
#define PROT_SET_BLOB_VER 2u
|
||
#define PROT_SET_BLOB_HDR_SZ 8u /* magic(4)+ver(2)+cnt(2) */
|
||
#define PROT_SET_BLOB_SZ (PROT_SET_BLOB_HDR_SZ + 4u * (uint32_t)PS_ID_COUNT + 8u)
|
||
|
||
/*
|
||
* 函数功能:把当前值区序列化为 blob(校验和按值区现算,不依赖封存值)
|
||
* 返 回 值:0 / -1 缓冲不足或参数非法;*p_len 输出实际长度
|
||
*/
|
||
int prot_set_store_serialize(uint8_t *p_buf, uint32_t cap, uint32_t *p_len);
|
||
|
||
/*
|
||
* 函数功能:解析 blob 并**整表原子**装载(all-or-nothing)
|
||
* 入口参数:p_buf/len - blob 字节流
|
||
* 返 回 值:0 已装载并封存 / -1 拒绝(magic/版本/长度/校验对不符或
|
||
* 任一项越界 → 值区一字不动;越界项经 prot_set_bad_mask() 指认)
|
||
*/
|
||
int prot_set_store_parse(const uint8_t *p_buf, uint32_t len);
|
||
|
||
#endif /* PROT_SET_H */
|