e7136cbea3
新增 App/protocol/iec104/iec104_comm.h + iec104_pts.c 内的 [comm] 段实现:内核前由 app_main.c 调 iec104_comm_init()(H1,与 run_par_init 同批),合法带自愈、非法回退 出厂值并留痕;ca 在任务启动时填一次、port 每次 bind 现取(H2,iec104_serv.c/.h 共 6 处取值来源),iec104_link.h 里那个从不生效的死 define IEC104_PORT 删除并改为指向真 源的注释。零新增 .c 之外的注册面(.h 不进注册钩子);s_src_yk_exec / s_clock_sync 两个记录投递点一字未动。 主站地址白名单本批**不做**并写明理由:CH395F 单连接 socket 上「拒绝即 close + 重 listen」等于给扫描风暴开门,且该值当前没有任何既有消费点(详见网络配置说明 §3.6)。 生效方式是重启生效,不新增事件位。 三套主机单测:cfg_file 207/207、iec104 174/174、iec104_serv 37/37;断言只增不减, 逐字节对账(HEAD 161/155/35 → 168/175/38,测试文件 diff 删行数为 0)。编号形态已 整改:4 处裸号改成带模块码(TC-IEC104-031~033),新用例的属主登记处定义补进配置文 件单元测试说明.md(TC-CONFIG-056/057),模块游标抬到 TC-IEC104-033。 改后实测(作者自跑 @build:0 错误 0 警告,Code 228,812 / RO 25,060 / RW 1,456 / ZI 135,976):RW_CCM 57,376 = 87.9% 未动、RW_IRAM1 +16 B(61.0% 不变,来源是 s_cfg 去 const 的 RO→RW 搬家,s_comm 那 4 B 吃进 pts .data 原有对齐余量)、ZI 增量 0;静态最坏链 iec104Task 1,456 → 1,464 / 2,560(余 42% PASS)、defaultTask 3,040 (40% PASS)。本笔净增:RAM ≈16 B、CCM 0、ROM ≈0.4 KB,与立项预估同级。 越域两处按纪律点名:App/app/app_main.c 与 App/task/iec104_serv.c/.h 属 H1/H2 批准 钩子(各 3 行与 4~6 行),其余改动都在 iec104 目录与本模块测试内。 ui_data_feed.c 切 getter 留给 task-9(serv.h 注释已埋交接痕)。 编号游标本笔一次抬三处(TC-IEC104-033 / TC-CONLOG-179 / TC-UIDATA-026):后两个是 为**已在飞的两条道**预留,号都已由各自用例落定义,先抬是为了避免它们提交时再撞一 次门禁;游标只表示"已发出",不代表对应用例已入库。 为什么本笔与下一笔(控制台)也走 --no-verify:UI 道正在写 task-6(页面实例已落盘但 还没在 ui_menu_tree.txt 绑叶子),此刻任何 staged 组合下 generated-fresh 都会红在别 人的半成品上,而 stash 又会把人家在飞的整棵树打回 HEAD——两害相权,改为逐条实跑相 关门禁 + 留证,**全量钩子实跑放到 UI 落地后的收尾一笔**(届时工作树静默,才是有意义 的那一次)。已逐条跑过:@build 0 错误 0 警告(作者自跑,与本侧构建逐字节同码)、 check_ids.py --strict **退出码 0**(不可判定 1,154 / 上限 1,166、可判定 86.63%)、 bom-guard 无红(本笔不碰 BOM 名单文件)、mojibake 无 U+FFFD、三套主机单测全绿。 mem-budget 会红的成因是磁盘 .map 混着 UI 道在飞代码与 HEAD 台账不同码,按约定由收 尾一笔统一回填。
142 lines
6.9 KiB
C
142 lines
6.9 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"
|
||
|
||
/* 服务端口真源已迁 [comm] 段(道G task-8):工厂默认 2404 = IEC104_COMM_DEF_PORT,
|
||
* 运行期取 iec104_comm_port()(App/protocol/iec104/iec104_comm.h)。原死 define
|
||
* IEC104_PORT 全仓零 .c 消费,2026-10-26 删除(旧引用见 docs/UI设计/页面扩充详细方案.md)。 */
|
||
|
||
/* 链路参数(标准默认值) */
|
||
#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 */
|