0f7b5a87fb
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
141 lines
6.7 KiB
C
141 lines
6.7 KiB
C
/*
|
||
* 模块名称:IEC 60870-5-104 链路层(APCI + 状态机,纯逻辑)
|
||
* 模块功能:TCP 流 → APDU 重组、U/S/I 帧编解码、STARTDT/STOPDT/TESTFR、
|
||
* k/w 流量窗与 t1/t2/t3 超时、发送序号 N(S)/N(R) 管理,以及应用层
|
||
* 流程编排:总召(act→背景扫描→actterm,首召 RBT 用 COT=4)、
|
||
* 突发上送(COV,COT=3)、周期上送(COT=1)、读单个(C_RD→COT=5)、
|
||
* 双点遥控两段式(选择/执行,失配否定确认 PN)、定值读(C_RD)/
|
||
* 定值写(C_SE_NC 选择=范围校验 / 执行=写入+封存)、时钟同步(C_CS
|
||
* → clock_sync 回调)、试验(C_TS)与进程复位(C_RP,清快照)。
|
||
* **零 I/O**:收 = on_rx 喂 TCP 流片段,发 = send_apdu 回调,
|
||
* 时基 = 调用方注入 now_ms(tick ms,回绕安全:差值法)
|
||
* → 全栈(asdu+pts+link)主机侧 L0.5 可离线跑(分层约束)。
|
||
* 参数(DL/T 634.5104 标准默认,§1.5 未另定):k=12 w=8 t1=15s t2=10s t3=20s。
|
||
* 端口:TCP **2404**(§1.5 B0 定义;本 DTU = 被控站/服务器,主站连入)。
|
||
* 线程约定:单实例单任务上下文(板端 = 104 服务在 netTask 消息循环内驱动,
|
||
* 见 iec104_serv 胶水层);非线程安全,禁止多任务共享一实例。
|
||
* 失败语义:send_apdu 内**不得阻塞**;发送失败由胶水层断链(主站会重连),
|
||
* 本层不感知发送失败。
|
||
* 创建日期:2026-10-12(B4)
|
||
*/
|
||
#ifndef IEC104_LINK_H
|
||
#define IEC104_LINK_H
|
||
|
||
#include <stdint.h>
|
||
|
||
#include "iec104_pts.h"
|
||
|
||
/* 服务端口(§1.5:以太网 TCP 2404) */
|
||
#define IEC104_PORT 2404U
|
||
|
||
/* 链路参数(标准默认值) */
|
||
#define IEC104_K_MAX 12U /* 未确认 I 帧上限 */
|
||
#define IEC104_W_MAX 8U /* 收到 I 帧未发确认的强制 S 阈值 */
|
||
#define IEC104_T1_MS 15000UL /* 发送/确认超时(超时 = 链路故障) */
|
||
#define IEC104_T2_MS 10000UL /* 无 I 发送时强制 S 确认超时(< t1) */
|
||
#define IEC104_T3_MS 20000UL /* 空闲发 TESTFR 周期(< t1) */
|
||
|
||
/* TCP 流重组缓冲:APDU 上限 253 + 半帧余量 */
|
||
#define IEC104_RXBUF_SZ 512U
|
||
|
||
/* 单次 poll 发送预算(ASDU 数)——防总召大扫描饿死宿主任务 */
|
||
#define IEC104_POLL_BUDGET 4U
|
||
|
||
/* 链路状态 */
|
||
typedef enum {
|
||
IEC104_ST_IDLE = 0, /* TCP 未建立(或已断开) */
|
||
IEC104_ST_OPEN, /* TCP 已建立,等 STARTDT act */
|
||
IEC104_ST_RUN, /* STARTDT 已确认,数据服务开 */
|
||
IEC104_ST_STOPPED /* STOPDT 已确认:停数据,保 S/TESTFR */
|
||
} iec104_state_t;
|
||
|
||
/* 配置与回调(ctx 原样回传给回调) */
|
||
typedef struct {
|
||
uint16_t ca; /* 公共地址 = 链路地址(2B,§1.5) */
|
||
/* 发一个完整 APDU(含 6B APCI 头)。不得阻塞;生命周期仅回调内有效 */
|
||
void (*send_apdu)(void *ctx, const uint8_t *apdu, uint16_t len);
|
||
/* C_CS 时钟同步:epoch 毫秒;NULL 或返回 -1 → 否定确认 */
|
||
int (*clock_sync)(void *ctx, uint64_t epoch_ms);
|
||
/* 时标源(B9):当前 epoch 毫秒(M_SP_TB_1 自发用)。
|
||
* NULL 或返回 UINT64_MAX = 时钟无效 → CP56 IV 位如实置位。
|
||
* 纯读(sys_clock 推算),不得阻塞、不得有副作用。 */
|
||
uint64_t (*epoch_ms)(void *ctx);
|
||
} iec104_link_cfg_t;
|
||
|
||
/* 链路实例(胶水层静态持有,零堆) */
|
||
typedef struct {
|
||
const iec104_link_cfg_t *cfg;
|
||
void *ctx;
|
||
iec104_state_t state;
|
||
uint8_t init_pending; /* 1 = 下次总召按初始化(COT=4 + RBT) */
|
||
/* 序号(0..32767,帧内 ×2) */
|
||
uint16_t tx_seq;
|
||
uint16_t rx_seq;
|
||
uint16_t tx_unacked; /* 已发 I 未获认(k 窗) */
|
||
uint16_t rx_unacked; /* 已收 I 未确认(w 阈) */
|
||
uint8_t testfr_pending; /* 1 = TESTFR act 未获 con(t1 同样约束它) */
|
||
/* 定时器到期时刻(now_ms 域;差值法判超时,回绕安全) */
|
||
uint32_t t1_deadline;
|
||
uint32_t t2_deadline;
|
||
uint32_t t3_deadline;
|
||
uint32_t now_ms;
|
||
/* TCP 流重组 */
|
||
uint8_t rx_buf[IEC104_RXBUF_SZ];
|
||
uint16_t rx_len;
|
||
/* 总召进度:0 无 / 1 背景扫描中 / 2 待发 actterm */
|
||
uint8_t interp_step;
|
||
uint8_t interp_yc_idx; /* 背景扫描 YC 游标 */
|
||
uint8_t interp_yx_idx; /* 背景扫描 YX 游标 */
|
||
uint8_t interp_cot; /* 本次总召用的 COT(BG_SCAN 或 INIT) */
|
||
/* 遥控选择暂存(执行段比对) */
|
||
uint32_t yk_sel_ioa; /* 0 = 无选择 */
|
||
uint8_t yk_sel_dpc;
|
||
/* 周期上送(COT=1)下次触发时刻 */
|
||
uint32_t period_next;
|
||
/* COV 待发暂存(k 窗满时保留到下次 poll,变化不许丢) */
|
||
uint64_t pend_yx;
|
||
uint16_t pend_yc;
|
||
} iec104_link_t;
|
||
|
||
/*
|
||
* 函数功能:初始化实例(置 IDLE、清序号;不发送任何帧)
|
||
* 入口参数:lk - 实例;cfg/ctx - 配置与回调上下文
|
||
*/
|
||
void iec104_link_init(iec104_link_t *lk, const iec104_link_cfg_t *cfg, void *ctx);
|
||
|
||
/*
|
||
* 函数功能:TCP 连接建立(新连接或重连后调用)
|
||
* 入口参数:now_ms - 当前时基
|
||
* 函数说明:序号清零、init_pending 置位(首召 = 初始化总召)、进 OPEN。
|
||
*/
|
||
void iec104_link_open(iec104_link_t *lk, uint32_t now_ms);
|
||
|
||
/*
|
||
* 函数功能:TCP 断开(对端断开或胶水层发送失败主动断链时调用)
|
||
* 函数说明:回 IDLE、清重组缓冲与选择暂存;不发送任何帧。
|
||
*/
|
||
void iec104_link_close(iec104_link_t *lk);
|
||
|
||
/*
|
||
* 函数功能:喂入 TCP 收到的流片段(任意切分/粘帧)
|
||
* 入口参数:data/len - 流片段;now_ms - 当前时基
|
||
* 函数说明:内部重组,凑齐 APDU 即处理;坏帧(起始符/长度非法)→ 视同链路
|
||
* 故障,实例回 IDLE(胶水层检测到 IDLE 跳变应断开 socket)。
|
||
* 返回后实例仍为 RUN 不代表对端合法——仅表示未触发断链条件。
|
||
*/
|
||
void iec104_link_on_rx(iec104_link_t *lk, const uint8_t *data, uint16_t len,
|
||
uint32_t now_ms);
|
||
|
||
/*
|
||
* 函数功能:周期驱动(定时器 + COV + 总召分片 + 周期上送)
|
||
* 入口参数:now_ms - 当前时基(建议 ≤100 ms 调一次)
|
||
* 函数说明:仅 RUN 态产生数据帧;STOPPED 态只处理 t2/t3(S/TESTFR)。
|
||
* 单次调用发送量 ≤ IEC104_POLL_BUDGET 个 ASDU。
|
||
*/
|
||
void iec104_link_poll(iec104_link_t *lk, uint32_t now_ms);
|
||
|
||
/* 当前状态(日志/看门狗用) */
|
||
iec104_state_t iec104_link_state(const iec104_link_t *lk);
|
||
|
||
#endif /* IEC104_LINK_H */
|