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 对变更记录行本就豁免,两种选择都不影响门禁。
126 lines
5.5 KiB
C
126 lines
5.5 KiB
C
/*
|
||
* 模块名称:SOE Record — 事件顺序记录编解码(纯逻辑,双端可测)
|
||
* 模块功能:定长 64B SOE(Sequence-of-Events)记录的构建与解析:
|
||
* CP56Time2a 时标 + 记录类型 + 元件号 + 单调序号 + 值 + 校验和。
|
||
* **零 I/O、零 OS 依赖**(仅 stdint/string)→ 主机侧 L0.5 离线单测
|
||
* (分层约束:协议/记录与硬件 I/O 分离)。
|
||
* 记录口径(docs/算法/继电保护功能复现规划.md §3.4「SOE 记录」「动作记录」,B9):
|
||
* 落盘文件 = /log/soe.log(8.3 合规;FF_USE_LFN=0 约束),定长 64B 一条
|
||
* (照 run.log 先例:count = filesize/64 O(1)、容量满删除重建)。
|
||
* 独立于 run.log 的理由:run.log 是文本记录(时间戳+epoch+UTF-8 描述,
|
||
* syslog_get_record/UI 按文本读),SOE 是二进制结构化记录(时标/类型/
|
||
* 序号/值),混入同一文件会破坏现有文本读者。
|
||
* 记录布局(64B,多字节字段一律小端):
|
||
* [0] 版本 SOE_REC_VER
|
||
* [1] 类型 soe_type_t
|
||
* [2] 元件号(prot_elm_id_t;SOE_ELM_NONE=0xFF = 非元件事件)
|
||
* [3] 标志位(bit7 IV = 记录时时钟无效,CP56 IV 位同步置位)
|
||
* [4..7] 序号 seq(uint32 小端;上电内单调,跨复位重启计数——见 soe_store.h)
|
||
* [8..14] CP56Time2a(7B,IEC 60870-5-4 §5.1.6;秒域时基 → ms 字段恒 0)
|
||
* [15..18] epoch 秒(uint32 小端;IV 时置 0,主机解码交叉校验用)
|
||
* [19..22] 值 value(uint32 小端;语义随类型,见 soe_type_t 注释)
|
||
* [23..62] 保留(恒 0)
|
||
* [63] 校验和 = sum8([0..62])(撕裂/半写检测,解析侧拒绝不匹配)
|
||
* 创建日期:2026-10-12(B9)
|
||
*/
|
||
#ifndef SOE_REC_H
|
||
#define SOE_REC_H
|
||
|
||
#include <stdint.h>
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* 记录尺寸与版本(定长;改布局必须升 SOE_REC_VER) */
|
||
#define SOE_REC_SIZE 64u
|
||
#define SOE_REC_VER 1u
|
||
|
||
/* 字段偏移(解析/主机解码工具共用口径) */
|
||
#define SOE_OFF_VER 0u
|
||
#define SOE_OFF_TYPE 1u
|
||
#define SOE_OFF_ELM 2u
|
||
#define SOE_OFF_FLAGS 3u
|
||
#define SOE_OFF_SEQ 4u /* 4B */
|
||
#define SOE_OFF_CP56 8u /* 7B */
|
||
#define SOE_OFF_EPOCH 15u /* 4B */
|
||
#define SOE_OFF_VALUE 19u /* 4B */
|
||
#define SOE_OFF_SUM 63u
|
||
|
||
/* 标志位 */
|
||
#define SOE_FLG_IV 0x80u /* bit7:记录时时钟无效(CP56 IV 位同步置位) */
|
||
|
||
/* 非元件事件的元件号 */
|
||
#define SOE_ELM_NONE 0xFFu
|
||
|
||
/*
|
||
* 记录类型(值语义随类型):
|
||
* 1 元件动作 value = 元件状态打包(bit0 asserted / bit1 picked / bit2 enabled)
|
||
* 2 元件返回 value = 同上(返回瞬间的旧状态快照)
|
||
* 3 软压板退出 value = 0
|
||
* 4 软压板投入 value = 1
|
||
* 5 采样闭锁 value = adc_health 闭锁位图(触发时的 hf & mask)
|
||
* 6 采样恢复 value = 0
|
||
* 7 标定闭锁 value = UA/UN/IA VALID 未齐的通道位图(卡 5,2026-10-25)
|
||
* 8 标定复归 value = 0
|
||
*/
|
||
typedef enum {
|
||
SOE_T_ELM_ASSERT = 1u,
|
||
SOE_T_ELM_RETURN,
|
||
SOE_T_PLATE_OUT,
|
||
SOE_T_PLATE_IN,
|
||
SOE_T_SAMPLE_BLOCK,
|
||
SOE_T_SAMPLE_RESUME,
|
||
/* 未标定闭锁(「数据可信」卡 5,2026-10-25 道A 追加):UA/UN/IA 的 VALID
|
||
* 未全置位 → 判据停走;与采样闭锁分型,边沿各留一条记录 */
|
||
SOE_T_CAL_BLOCK,
|
||
SOE_T_CAL_RESUME,
|
||
SOE_T_COUNT /* 哨兵值,非有效类型 */
|
||
} soe_type_t;
|
||
|
||
/* 解析产物(字段原样拷贝;CP56 不在此层换算,主机工具解码) */
|
||
typedef struct {
|
||
uint8_t ver;
|
||
uint8_t type;
|
||
uint8_t elm_id;
|
||
uint8_t flags;
|
||
uint32_t seq;
|
||
uint8_t cp56[7];
|
||
uint32_t epoch_s;
|
||
uint32_t value;
|
||
} soe_rec_t;
|
||
|
||
/*
|
||
* 函数功能:构建一条 64B SOE 记录(纯计算,无 I/O;任意上下文可调)
|
||
* 入口参数:rec - 输出缓冲(SOE_REC_SIZE 字节,不可为 NULL) uint8_t *
|
||
* type - 记录类型 soe_type_t(越界按 SOE_T_COUNT 处理为保留 0)
|
||
* elm_id - 元件号(非元件事件传 SOE_ELM_NONE) uint8_t
|
||
* flags - 标志位(SOE_FLG_IV = 时钟无效) uint8_t
|
||
* seq - 单调序号 uint32_t
|
||
* epoch_s - 墙钟 epoch 秒(IV 时传 0;同时 flags 须带 SOE_FLG_IV)
|
||
* value - 类型相关值 uint32_t
|
||
* 返回值:无
|
||
* 函数说明:epoch_s → CP56Time2a(UTC 分解,Hinnant civil 日历算法,与
|
||
* iec104_asdu.c 同一算法族;钳位 [2000-01-01, 2127-12-31],
|
||
* ms 字段恒 0——sys_clock 秒域时基,如实登记)。
|
||
* flags 带 IV 时 CP56 byte2 bit7 置位(标准"时间无效"语义)。
|
||
*/
|
||
void soe_rec_build(uint8_t *rec, uint8_t type, uint8_t elm_id, uint8_t flags,
|
||
uint32_t seq, uint32_t epoch_s, uint32_t value);
|
||
|
||
/*
|
||
* 函数功能:解析一条 64B SOE 记录(校验版本 + 校验和)
|
||
* 入口参数:rec - 原始记录(SOE_REC_SIZE 字节,不可为 NULL) const uint8_t *
|
||
* out - 解析输出(不可为 NULL) soe_rec_t *
|
||
* 返回值:0 成功 / -1 参数错 / -2 版本不符 / -3 校验和不符(撕裂/半写) int
|
||
* 函数说明:只读,不改动 rec。类型/标志不做强校验(前向兼容:新版本类型
|
||
* 旧解析器可读),版本与校验和是硬门槛。
|
||
*/
|
||
int soe_rec_parse(const uint8_t *rec, soe_rec_t *out);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
|
||
#endif /* SOE_REC_H */
|