1d96fc9f87
- 新增 App/log/soe_rec.{c,h}:64B 定长 SOE 记录纯编解码(CP56Time2a
时标 + 类型 + 元件号 + 单调序号 + 值 + sum8 撕裂校验),零 I/O 零 OS
依赖,主机 L0.5 覆盖(32 断言,含与 iec104 CP56 解码器交叉互证)
- 新增 App/log/soe_store.{c,h}:protectTask 边沿投递(取时标 + 组记录 +
SPSC 待发环/消息队列,零文件系统);defaultTask 经 syslog_drain 尾部
排空落 /log/soe.log(卷锁 try / FA_WRITE|FA_OPEN_APPEND / 校验字节数 /
容量满 unlink 重建,全套照 run.log 实证纪律;无卷损坏归类路径)
- protect_task.c:元件动作/返回、软压板投退、采样闭锁/恢复六类边沿
→ soe_store_post;post 内禁 DBG_*(protect 静态最坏链 872 B 不变,
改前/改后 stack_report 九任务逐字节一致),丢弃留痕挪 drain 侧
- iec104:单点 YX 自发改 M_SP_TB_1(30) 带 CP56 时标(总召/周期保持
M_SP_NA_1,单测两种都断言);link cfg 新增 epoch_ms 时标源回调
(NULL/无效 → IV 位如实置位);serv 绑 sys_clock(秒域时基,
ms 域 = 秒×1000,不造假亚秒)
- 主机单测:test/host/soe/(32 断言)+ iec104 94→97(TB 时标/IV);
iec104_master.py 解析 type 30(B6/B7 流不回归);decode_soe.py
台架解码断言工具(seq 单调/校验和/UTC 交叉)
- 注册:uvprojx + check_registration SIM_EXEMPT 点名(MCU-only 胶水)
- 文档(我方属主区):`App/log/README.md` 增 soe 节(64B 记录/单写者/
post 禁 DBG/UTC 交叉坑);`docs/子系统设计/IEC104上行设计说明.md`
§1 范围行 + §3.2 时标注 + §7 台架行 + §8 第 2 条销项 + 变更记录行
台架腿(bench 锁 B9,release --bench-pass):run_boot_test PASS;
--b7-pre 21/21、--b7-post 13/13(B6/B7 回归 + 确定性动作边沿);
自发 YX 上送 M_SP_TB_1 带 ts(11:00:51 动作 / 11:00:53 复归);
FTP 下载 /log/soe.log 30 条记录 decode PASS(seq 0..28 单调、
ELM_ASSERT 14 条、UTC 交叉一致);定值/压板已恢复默认。
台账 +1088 B(SPSC 环 512 + 静态 FIL 528 + 计数器)随合并由 Lead
回填(ram_baseline --write + check_mem_numbers --write)。
gate: only mem-budget/mem-numbers pending lead ledger backfill
(pre-commit --all-files: 29 Passed / 2 Failed / 0 Skipped;
hygiene self-check OK: no BOM/CRLF/mojibake/trailws/non-ASCII literal)
sync 追平 v2-on-ui(B8 freq + N33 抬栈)后复跑:@build 0/0
(Code=209492 RW=1104 ZI=154184);stack_report 九任务全 PASS、
protectTask 872 B/57% 不变;ci_local L0.5 15 runner 951/951
(freq 46 / soe 32 / iec104 97 / prot_set 90 / prot_elm 47)。
顺带修 protect_task.c 陈旧注释:定值巡检 31 项 → 40 项(B8 后)。
120 lines
5.1 KiB
C
120 lines
5.1 KiB
C
/*
|
||
* 模块名称:SOE Record — 事件顺序记录编解码(纯逻辑,双端可测)
|
||
* 模块功能:定长 64B SOE(Sequence-of-Events)记录的构建与解析:
|
||
* CP56Time2a 时标 + 记录类型 + 元件号 + 单调序号 + 值 + 校验和。
|
||
* **零 I/O、零 OS 依赖**(仅 stdint/string)→ 主机侧 L0.5 离线单测
|
||
* (落实登记簿 A-4 约束 1"协议/记录与硬件 I/O 分离")。
|
||
* 记录口径(docs/算法/继电保护功能复现规划.md §3.4 L4-4/L4-5,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
|
||
*/
|
||
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,
|
||
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 */
|