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 后)。
153 lines
7.2 KiB
C
153 lines
7.2 KiB
C
/*
|
||
* 模块名称:保护任务(L3 板端接线,B5)——实现
|
||
* 模块功能:见 protect_task.h。每拍三件事:
|
||
* ① L5-4 采样故障闭锁:adc_health_flags() 电平位命中 → 元件状态
|
||
* 复位(防陈旧判据)+ 本拍不 eval;
|
||
* ② prot_set_selfcheck()(L5-1 定值完整性巡检,40 项 sum32——
|
||
* B8 后 PS_ID_COUNT=40,开销
|
||
* 可忽略;失配 → prot_set 内部闭锁,eval 自动复位全元件);
|
||
* ③ prot_elm_run()(phasor rms → 判据步进)+ 动作位图边沿日志;
|
||
* ④ 全局软压板 PS_GLOBAL_EN(104 YK12 两段式投退,B7):退出 =
|
||
* 复位元件 + 停判据(与采样闭锁同构,独立边沿日志)。
|
||
* 定值/元件初始化在 app_main_init(内核前,B5);本任务只消费。
|
||
* 线程约定:prot_elm_run / prot_set_selfcheck 仅本任务上下文(prot_elm.h);
|
||
* 跨上下文复归(104 YK11)经 prot_elm_request_reset 落请求。
|
||
* 创建日期:2026-10-12(B5)
|
||
*/
|
||
#include "protect_task.h"
|
||
|
||
#include "cmsis_os2.h"
|
||
|
||
#define DBG_TAG "[PROT]"
|
||
#define DBG_LINE_BUF_MAX 128U
|
||
#include "dbg_log.h"
|
||
|
||
#include "adc_health.h"
|
||
#include "console_log.h" /* dbg_console_writer_ready():启动锚点行门控(B6) */
|
||
#include "prot_elm.h"
|
||
#include "prot_set.h"
|
||
#include "soe_store.h" /* B9:动作/复归边沿 → SOE 投递(零 FS,队列异步) */
|
||
#include "task_watchdog.h"
|
||
|
||
/* L5-4 采样故障电平闭锁位:stall(可自愈)+ backward(锁存)都须停判据。
|
||
* 锁存类(FRSTDATA/HW_ERR 等)不在此列——它们是一次性事件痕迹,不构成
|
||
* "当前测量不可信";持续 stall/backward 才是。 */
|
||
#define PROT_HF_LOCK_MASK (ADC_HF_TPAFE_STALL | ADC_HF_STM32_STALL | ADC_HF_SEQ_BACKWARD)
|
||
|
||
static uint32_t s_prev_active; /* 上拍动作位图(边沿日志用) */
|
||
static uint8_t s_prev_hf_lock; /* 上拍采样闭锁态(边沿日志用) */
|
||
static uint8_t s_prev_plate_out; /* 上拍全局软压板退出态(边沿日志用,B7) */
|
||
|
||
/*
|
||
* 函数功能:动作位图变化沿日志 + SOE 投递(逐变化元件)
|
||
* 入口参数:active - 本拍 prot_elm_active_mask()
|
||
* 函数说明:V0.1 无出口硬件(规划 R7),动作 = 遥信 + 日志 + SOE。
|
||
* SOE 走 soe_store_post(纯 RAM 投递,落盘由 defaultTask 异步执行,
|
||
* 本任务零 NAND I/O——protect 静态最坏链不变,B9 DoD 硬约束)。
|
||
* value = 元件状态打包(bit0 asserted / bit1 picked / bit2 enabled)。
|
||
*/
|
||
static void s_log_active_edge(uint32_t active) {
|
||
uint32_t chg = active ^ s_prev_active;
|
||
uint32_t i;
|
||
|
||
for (i = 0u; (i < (uint32_t)PE_COUNT) && (chg != 0u); i++) {
|
||
const uint32_t bit = 1u << i;
|
||
|
||
if ((chg & bit) != 0u) {
|
||
prot_elm_state_t st;
|
||
|
||
chg &= ~bit;
|
||
prot_elm_state((prot_elm_id_t)i, &st);
|
||
DBG_INFO("elm %s asserted=%u picked=%u",
|
||
prot_elm_name((prot_elm_id_t)i),
|
||
(unsigned)st.asserted, (unsigned)st.picked);
|
||
(void)soe_store_post(
|
||
((active & bit) != 0u) ? (uint8_t)SOE_T_ELM_ASSERT
|
||
: (uint8_t)SOE_T_ELM_RETURN,
|
||
(uint8_t)i,
|
||
(uint32_t)st.asserted | ((uint32_t)st.picked << 1)
|
||
| ((uint32_t)st.enabled << 2));
|
||
}
|
||
}
|
||
s_prev_active = active;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:protectTask 线程入口
|
||
* 入口参数:argument - 未用
|
||
* 返 回 值:无(不返回)
|
||
* 函数说明:无就绪屏障(判据只依赖 phasor 使用区,未就绪时 prot_elm_run
|
||
* 整拍自跳;采样闭锁另有 adc_health 电平位兜底)→ 上电即跑。
|
||
* 每拍喂看门狗(TASK_WD_PROTECT,产品 required_mask 0x1F7)。
|
||
*/
|
||
void protect_task(void *argument) {
|
||
uint8_t start_logged = 0u; /* 启动锚点行留痕(writer-ready 门控,B6) */
|
||
|
||
(void)argument;
|
||
|
||
for (;;) {
|
||
uint32_t now = osKernelGetTickCount();
|
||
uint32_t hf = adc_health_flags();
|
||
|
||
/* 启动锚点行:控制台写者注册后首拍补打(2026-10-10 门控纪律,adcTask
|
||
* 首帧同款)。写者注册前 con_append 走无锁裸发,与 defaultTask 启动日志
|
||
* 逐字节交错——B6 台架实证:本行曾损坏为 "st[art: cycle=220ms elm=70"
|
||
* 致 @test 锚点 MISS(控制台单写者日志设计说明.md §3.4)。 */
|
||
if ((start_logged == 0u) && (dbg_console_writer_ready() != 0u)) {
|
||
DBG_INFO("protect task start: cycle=%ums elm=%u",
|
||
(unsigned)PROTECT_TASK_CYCLE_MS, (unsigned)PE_COUNT);
|
||
start_logged = 1u;
|
||
}
|
||
|
||
if ((hf & PROT_HF_LOCK_MASK) != 0u) {
|
||
/* L5-4:测量链不可信 → 复位元件(本任务上下文,直接 init 合法)
|
||
* 并跳过判据;stall 自愈后下一拍自动恢复 */
|
||
if (s_prev_hf_lock == 0u) {
|
||
DBG_ERROR("sampling FAULT -> protection blocked (hf=0x%08lX)",
|
||
(unsigned long)(hf & PROT_HF_LOCK_MASK));
|
||
(void)soe_store_post((uint8_t)SOE_T_SAMPLE_BLOCK,
|
||
SOE_ELM_NONE,
|
||
hf & (uint32_t)PROT_HF_LOCK_MASK);
|
||
}
|
||
s_prev_hf_lock = 1u;
|
||
prot_elm_init();
|
||
} else {
|
||
if (s_prev_hf_lock != 0u) {
|
||
DBG_INFO("sampling recovered -> protection resumed");
|
||
(void)soe_store_post((uint8_t)SOE_T_SAMPLE_RESUME,
|
||
SOE_ELM_NONE, 0u);
|
||
s_prev_hf_lock = 0u;
|
||
}
|
||
/* L5-1 巡检:定值区与封存校验和不符 → prot_set 闭锁(下一拍起
|
||
* eval 自动复位全元件)。B7 收口:104 定值写改走
|
||
* prot_set_set_sealed(影子区写+封存后单字交换),selfcheck 另有
|
||
* seqlock 式复核(提交撞见 = 本轮跳过),误锁窗口已消除。 */
|
||
(void)prot_set_selfcheck();
|
||
/* 全局软压板(PS_GLOBAL_EN,104 YK12 两段式投退,B7):退出 =
|
||
* 保护停止判据并复位元件状态(重新投入从干净快照起步) */
|
||
if (prot_set_get_ctl(PS_GLOBAL_EN) == 0u) {
|
||
if (s_prev_plate_out == 0u) {
|
||
DBG_INFO("global plate OUT -> protection disabled");
|
||
(void)soe_store_post((uint8_t)SOE_T_PLATE_OUT,
|
||
SOE_ELM_NONE, 0u);
|
||
s_prev_plate_out = 1u;
|
||
}
|
||
prot_elm_init();
|
||
} else {
|
||
if (s_prev_plate_out != 0u) {
|
||
DBG_INFO("global plate IN -> protection enabled");
|
||
(void)soe_store_post((uint8_t)SOE_T_PLATE_IN,
|
||
SOE_ELM_NONE, 1u);
|
||
s_prev_plate_out = 0u;
|
||
}
|
||
prot_elm_run(now);
|
||
}
|
||
}
|
||
|
||
s_log_active_edge(prot_elm_active_mask());
|
||
|
||
task_wd_beat(TASK_WD_PROTECT);
|
||
osDelay(PROTECT_TASK_CYCLE_MS);
|
||
}
|
||
}
|