feat(104): [comm] 配置段落地,公共地址与端口由 defaultTask 读、iec104 服务端消费

新增 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 台账不同码,按约定由收
尾一笔统一回填。
This commit is contained in:
2026-10-26 19:34:28 +08:00
parent 0f8b88337e
commit e7136cbea3
13 changed files with 570 additions and 22 deletions
@@ -4,7 +4,7 @@
> 范围、点表、链路流程、板端接入、预算与测试。规格来源
> `docs/算法/继电保护功能复现规划.md` §1.5(B0 产品定义,验收 = 省电科院点表);
> 代码落点 `App/protocol/iec104/`;未闭环的送检验收挂 `docs/任务规划/登记簿.md` §7。
> **角色**:本 DTU = **被控站(受控端)**,104 **TCP 服务器,端口 2404**;主站
> **角色**:本 DTU = **被控站(受控端)**,104 **TCP 服务器,端口 `[comm]` 段 `port`(工厂默认 2404)**;主站
> (控制端,点表参考 `192.168.1.199`)主动连入。
## 1. 范围(V0.1 子集)
@@ -182,7 +182,14 @@
t1/t2/t3 全部用注入 tick 判定,单次 RPC 停顿不会误触 t1(15 s),连续停顿由
"发送 3 连败拆链"兜底。COV 突发延迟 ≤100ms(服务周期),远小于 SOE 5000ms 线。
- **对时**:C_CS → `sys_clock_set_unix()`(含 SD2506 RTC 写回);epoch 秒 >2^32
(2106-02-07 后)显式拒绝回 PN。公共地址 V0.1 固定 1。
(2106-02-07 后)显式拒绝回 PN。
- **通讯参数可配置化(道G task-8,2026-10-26 落地)**:公共地址与监听端口真源 =
`/config.ini` `[comm]` 段(键表 → `docs/子系统设计/网络配置说明.md` §3.6)。消费方式 =
`iec104Task` 启动读一次 `iec104_comm_ca()` 填链路 cfg(**ca 生效 = 改后重启**),
`s_bind_listen()` 每次现取 `iec104_comm_port()`(端口另在下次重 bind 自然生效);
getter 合法带 ca∈1..65535 / port∈1024..65535,带外自愈回工厂默认 1/2404
(adc_cal 合法带哨兵同款;getter 不打日志,省 iec104Task 深链栈)。不挂段回调、
不新增事件位、不走 SYS_EVT_NET_RECONFIG(热切换代价见 §8,原「V0.1 固定 1」口径作废)。
## 6. RAM / 栈预算(2026-10-12 实测)
@@ -197,7 +204,7 @@
| 层 | 内容 |
|---|---|
| L0.5 | `test/host/iec104/`:APCI 重组/坏帧、STARTDT 握手、总召全流程(含 RBT/分片/预算)、COV、k/w 窗、t1/t2/t3(假时基)、遥控两段式与 PN、YK12 软压板两段转真 + 置脏(B7)、定值两段写 + set_sealed 后 selfcheck、C_RD(含本地项不可寻址)、C_CS(含闰年)、错地址/错类型 EVI;**板端默认源真实链**(ring→phasor→freq→meas 快照,非桩):值/品质透传、CLIPPED→IV、0x401D 绑频(收敛 ±0.02 Hz / 未新鲜 IV)、死区运行时化 + 2 点确认/回摆撤销、seq 去重(卡 9/11,TC-IEC104-025~028)+ 运行参数段路由/两段式/RMW(卡 10,TC-IEC104-029);`test/host/run_par/`:[run_par] 注册/落盘往返/守卫/系数直通合法带(TC-RUNPAR-001~008);`test/host/prot_set/`:blob 往返 + 损坏整表拒绝(all-or-nothing)|
| L0.5 | `test/host/iec104/`:APCI 重组/坏帧、STARTDT 握手、总召全流程(含 RBT/分片/预算)、COV、k/w 窗、t1/t2/t3(假时基)、遥控两段式与 PN、YK12 软压板两段转真 + 置脏(B7)、定值两段写 + set_sealed 后 selfcheck、C_RD(含本地项不可寻址)、C_CS(含闰年)、错地址/错类型 EVI;**板端默认源真实链**(ring→phasor→freq→meas 快照,非桩):值/品质透传、CLIPPED→IV、0x401D 绑频(收敛 ±0.02 Hz / 未新鲜 IV)、死区运行时化 + 2 点确认/回摆撤销、seq 去重(卡 9/11,TC-IEC104-025~028)+ 运行参数段路由/两段式/RMW(卡 10,TC-IEC104-029)+ `[comm]` 段真链(道G task-8,TC-IEC104-031~033:注册/出厂模板/沉降重载/带外自愈/setter 守卫/ca 端到端 = C_RD 正答与旧写死 ca=1 转 COT13+EVI);`test/host/iec104_serv/` 钉端口与 ca 取值来源(TC-I104SRV-001b/c);`test/host/cfg_file/` 钉段/键容量上限(TC-CONFIG-056/057);`test/host/run_par/`:[run_par] 注册/落盘往返/守卫/系数直通合法带(TC-RUNPAR-001~008);`test/host/prot_set/`:blob 往返 + 损坏整表拒绝(all-or-nothing)|
| L0 | `iec104-unit-test` 钩子 + cppcheck/coding-standard |
| 台架 | `@test` 锚点(104 服务启动/监听行);**PC 主站客户端 `test/host/iec104_master.py`(纯标准库,B6 2026-10-12 就绪)**:STARTDT/总召四组+actterm/C_SE 定值两段写→YX15·16 变位(COT=3)/恢复复归/YK11 两段正答/C_RD 读回,台架往返 **18/18 PASS**(证据 `../测试说明/boot_log_data/20261012_B6_iec104_roundtrip_log.txt`);**B7 持久化模式 `--b7-pre/--b7-post/--b7-mark/--b7-check`**(中间 ST-LINK 复位):YK12 两段投退→保护退出/联动、**定值与压板态跨复位保持**(含非默认 7.5 往返),**21/21 + 13/13 + 4/4+4/4 PASS**(证据 `../测试说明/boot_log_data/20261012_B7_iec104_persist_log.txt`);**B9 SOE 腿**(同工具同轮回归):自发 YX 断言 **M_SP_TB_1 带 ts**(工具解析 type 30/1 双型)+ FTP 下载 `/log/soe.log` 经 `test/host/soe/decode_soe.py` 解码断言(校验和/seq 单调/CP56↔epoch UTC 交叉),pre 21/21 + post 13/13 不回归;lib60870 级第三方全流程归 B10 |
@@ -217,7 +224,12 @@
7. **后连优先未实现**(V0.1 = 首到优先):单连接 socket 连接态无法 accept,
旧会话结束前新主站接入被拒;后连优先需 net 层(CH395F 单连接模型)支持,
真需要冗余切换时随 net 扩展立项。
8. 公共地址固定 1、监听端口固定 2404:可配置化(config.ini)留待后续产品分期,尚未登记具体条目。
8. ~~公共地址固定 1、监听端口固定 2404~~——**可配置化已落地**(道G task-8,
2026-10-26,`[comm]` 段,见 §5 与 `docs/子系统设计/网络配置说明.md` §3.6)。
**主站地址(accept 侧 IP 白名单)仍不做**:现无既有消费点;单连接 socket 上
「拒绝非白名单」= close + re-listen,端口扫描可打成 SPI/看门狗风暴,且配错
即锁死真主站(FTP 口令那套失败锁的代价在 104 侧没有等价护栏可低成本复刻)
→ 待送检/现场需求明确后另行立项,`[comm]` 段当前**没有**该键。
## 变更记录
@@ -231,3 +243,4 @@
| 2026-10-25 | **卡 10(道D,「数据可信」M6-①)**:新增 `App/algo/run_par.c/h`——遥测死区 9 点真源收编([run_par] 段 9 键 int32 毫单位落盘,工厂默认 = 卡 6 档;pts 层 s_yc_db 副本删除、set/get 转转发壳并保留清确认计数语义);104 **0x8020~0x8058 读写映射**(§3.4 新表:逐项地址本仓自定、送检点表到位后替换常量段;0x8027~0x802A 永不覆盖,路由 run_par 先 prot_set 后);**k/b 系数覆盖 = adc_cal_set 代理**(合法带/越带留痕复用,run_par 不存系数);持久化走新事件位 `SYS_EVT_RUN_PAR_SAVE`(1<<8) 整档外科 save;单测 test/host/run_par 新建(TC-RUNPAR-001~008,67 断言)+ iec104 套 +TC-IEC104-029 路由钉(141 断言);台架/送检腿待排窗 |
| 2026-10-25 | **卡 9/卡 11(道D,「数据可信」M5/M6)**:YC 板端源改读 L2 遥测快照(削顶五路从点表绑死 -1 改为**槽位绑定 + 快照品质 CLIPPED→IV**,V0.2/N-18 零改动启用);`0x401D` 绑频率源(`freq_valid()==0` → IV);死区改运行时副本 + `iec104_pts_yc_set_deadband()`(卡 10 接线点);COV 遥测腿 = 死区连续 2 点确认(快照 `seq` 去重)+ 品质翻转直报;`iec104_pts_src_t` 表尾追加 `yc_read_ex`(旧源退路语义不变);单测 97→127(TC-IEC104-025~028 走真实 L2 链);台架腿(总召读回一致 / 频率 ±0.01 Hz)待排窗——主站工具已记 val+qds,无需改工具 |
| 2026-10-12 | **B9 时标 SOE**:自发单点 YX 换 **M_SP_TB_1(30)**(SIQ+CP56,§3.2 注;总召/周期保持 NA 型);link cfg 增 `epoch_ms` 时标源(NULL/无效 → IV 如实);serv 绑 `sys_clock`(秒域 → ms 域=秒×1000);主站工具解析 type 30/1 双型;单测 iec104 94→97(TB 时标 + IV 路径,两种类型都断言);台架腿见 §7 B9 行;落盘侧见 `App/log/README.md` soe 节 |
| 2026-10-26 | **道G task-8:`[comm]` 段落地(「UI 补齐」卡 5 非 UI 前置)**——ca/端口从写死改 `[comm]` 可配(getter 合法带自愈,键表 → `docs/子系统设计/网络配置说明.md` §3.6);`iec104_comm_init()` 内核前注册于 `app_main_init`(先注册后 load 契约,同 [adc_cal]/[run_par]);生效 = 改后重启(ca 任务启动读一次;port 下次重 bind 自然生效),不挂回调不新增事件位;§5/§8 同步销项,**主站地址不做**的理由登记 §8 第 8 条;死 define `IEC104_PORT` 删除(零 .c 消费);`IEC104_SERV_PORT` 降为快照兼容出口(真源 getter,task-9 切 ui_data_feed);单测 = `test/host/iec104` TC-IEC104-031~033(套件 174/174 PASS)+ `test/host/iec104_serv` TC-I104SRV-001b/c(37/37 PASS)+ `test/host/cfg_file` TC-CONFIG-056/057(207/207 PASS)|
@@ -171,6 +171,31 @@ write=1
- 生效链路:load/poll 原地改副本 → 104 COV 每拍实时读(无回调,卡 9/10 契约);
104 写成功 → `SYS_EVT_RUN_PAR_SAVE` → 本任务 `cfg_file_save()` 整档外科落盘。
### 3.6 `[comm]` 段:IEC 60870-5-104 通讯参数(2026-10-26 新增,「UI 补齐」卡 5 非 UI 前置,道G task-8)
104 被控站的公共地址与监听端口持久载体(段属主 = `App/protocol/iec104/`,键表与
合法带见 `App/protocol/iec104/iec104_comm.h`,注册 `iec104_comm_init()` 在
`app_main_init()` 内核前——「先注册后 load」契约同 [adc_cal] / [run_par]):
| 键 | 含义 | 工厂默认 | getter 合法带(带外自愈回默认) |
|---|---|---|---|
| `ca` | 公共地址(= 链路地址,ASDU 头 2B) | 1 | 1 ~ 65535 |
| `port` | 被控站 TCP 监听端口 | 2404 | 1024 ~ 65535 |
- **生效 = 改后重启**(与 [network] 免重启重建**不同**,验收口径 →
`docs/任务规划/UI补齐.md` §2 第 5 行):iec104Task 启动时读一次 getter;
端口另在下次重 bind(拆链/代次重建)自然生效。不挂段回调、不新增事件位——
CH395F 单连接 socket 上「换端口/拒绝重听」= close + re-listen,做成热生效
会被端口扫描打成 SPI/看门狗风暴(详论 → 本段只登记,正文唯一详述处 =
`docs/子系统设计/IEC104上行设计说明.md` §8)。
- **主站地址(accept 侧 IP 白名单)本段不做**:无既有消费点 + 上述拒绝代价 +
配错即锁死真主站,待送检/现场需求立项;读者勿假设该键已存在。
- 落盘:UI 保存(task-9)经 `net_cfg_save_request()` → `SYS_EVT_CFG_SAVE` 整档
外科 save;引擎属主 = defaultTask 不变。
- 容量台账:本段上线后 `/config.ini` 受管段 = 6/8([network] 4 键 / [ftp] 3 键 /
[adc_cal] 9 键 / [adc_cal_ofs] 8 键 / [run_par] 9 键 / [comm] 2 键);段/键上限
的机器钉 = `test/host/cfg_file` TC-CONFIG-056/057。
---
## 4. 生效链路
@@ -329,6 +354,7 @@ ENTER 进入选中地址行 → `↑/↓` 调活动字节(0..255 **环绕**)
| 日期 | 变更 |
|---|---|
| 2026-10-26 | v2.5:**新增 §3.6 `[comm]` 段(道G task-8,「UI 补齐」卡 5 非 UI 前置)**——104 公共地址/监听端口 2 键、改后重启生效口径、主站地址不做的理由登记、段容量台账 6/8 与机器钉 |
| 2026-10-25 | v2.4:**新增 §3.5 `[run_par]` 段(「数据可信」卡 10)**——遥测死区 9 键毫单位布局、104 写路径合法域、系数覆盖不落本段(真源 [adc_cal])、守卫仅在写路径、事件化落盘 |
| 2026-10-11 | v2.3:**新增 §3.4 `[ftp]` 段(N-19 FTP 鉴权)**——`user` / `pwd_hash`(SHA-256(user‖password))/ `write` 字段表、鉴权流程(USER 恒 331 / 恒定时间比较 / 未登录命令门 / 5 败锁 60 s)、改口令步骤与忘口令恢复路径、出厂默认口令启动告警、明文嗅探边界 |
| 2026-10-10 | v2.2:**删除旧档 NCFG 迁移**——`net_cfg_legacy.c/h` 整体移除、`cfg_store_legacy_path()` 撤销;旧档 `/cfg/netcfg.bin` 若存在则被忽略(不读、不删、不迁移),配置回落工厂默认。理由:该一次性路径把整条配置写链挂进启动期调用图,defaultTask 静态最坏栈链 +600 B,且本仓库无携带旧档的设备 |
@@ -155,6 +155,7 @@ mac=00:00:00:00:00:00
| `Lib/FatFs/ffconf.h` | 修改 | MCU | `FF_FS_REENTRANT` 0→1;`FF_FS_NORTC` 1→0 |
| `App/util/sys_clock.c/.h` | 修改 | MCU | 新增 `sys_clock_get_time()`(日历时间获取器);改归 ④中间件层,自 `App/` 移入 `App/util/` |
| `Drivers/BSP/NETCFG/net_cfg.h/.c` | 重构 | 双端 | `net_cfg_*` API 不变;load/save 走 cfg_file;注册 `[network]` 段 |
| `App/protocol/iec104/iec104_comm.h` + `iec104_pts.c` 尾段 | 新增(2026-10-26 道G task-8) | MCU(模拟器不编 protocol) | 注册 `[comm]` 段(104 ca/端口,ca 1..65535 / port 1024..65535 合法带 getter 自愈);内核前 `iec104_comm_init()` 注册(同 [adc_cal]/[run_par] 先注册后 load 契约);生效 = 改后重启(键表与理由 → `docs/子系统设计/网络配置说明.md` §3.6) |
| `Drivers/BSP/NETCFG/net_cfg_legacy.c` | **已删除**(2026-10-10;曾于 P3 新增) | — | 旧 NCFG blob 解析 + 迁移;随"不保留旧档兼容"整体移除(见 §9.1) |
| `Drivers/BSP/NETCFG/net_cfg_store.h` / `net_cfg_store_mcu.c` | **删除** | — | 由 `cfg_file_store.*` 取代 |
| `simulator/sim/net_cfg_store_sim.c` | **删除** | 模拟器 | 由 `cfg_file_store_sim.c` 取代 |
@@ -587,3 +588,4 @@ void adc_cfg_init(void)
| 2026-10-03 | v1.13:**真机验证 PASS(A1 闭环)**——v1.11 的 P0-1 堆分配改造在真机上验证通过:`[CFG] load: OK (523B, heap free 8840B)`(**经 FreeRTOS 堆分配**,即 512B C 堆约束的验收点);Forbidden `FAIL`/`[ERR]` 零命中;FTP 改配置 IP→`192.168.1.101` 后**复位即加载生效**(`[NET_CFG] load: OK ip=192.168.1.101` + 芯片 `IP_INF` .101 + 21 端口探测可达),原档恢复后复测回 .100。真机全闭环 `@test` PASS 7/7。**台架步骤与判据见 `测试说明/实机测试实施指南.md` §3 / §10** |
| 2026-10-04 | **P0-1 真机独立复跑 PASS(二次确认,代码零改动)**——`@build` 0/0(增量,无源文件新于镜像)→ `@test` **VERDICT PASS(rc=0)**:`[2026-10-04 00:01:21][CFG] load: OK (523B, heap free 8840B)` + `[NET_CFG] load: OK ip=192.168.1.100`,Forbidden `FAIL`/`ERROR`/`HardFault`/`[ERR]` 零命中,`selftest: no fault record`(先清掉 2026-10-02/03 故障注入窗口遗留的 67 条台账)→ ST-LINK 全片回读 `No difference found`(`0x08000000~0x08040F9B`)。**本次为 `待处理内容.md` §4.1「待验证」唯一未闭环项(P0-1)的收口**,该节现已清空 |
| 2026-10-10 | v1.14:**删除旧档 NCFG 迁移(D10 撤销)**——`Drivers/BSP/NETCFG/net_cfg_legacy.{c,h}` 整体移除、`cfg_store_legacy_path()` 撤销(MCU / 模拟器后端与主机单测 `test/host/cfg_file/cfg_file_test.c` 同步删除),`net_cfg_load` 不再调用 `net_cfg_legacy_migrate()`;旧档 `/cfg/netcfg.bin` 若存在则被**忽略**(不读、不删、不迁移),配置回落工厂默认。理由:该一次性路径把整条配置写链挂进启动期调用图,defaultTask 静态最坏栈链 **+600 B**(该链整体 5312 → 2776 B,另三个杠杆见 `docs/内存管理/RAM使用分析与优化方案.md`),而本仓库不存在携带旧档的设备。连带影响:`App/util` 的 CRC-16/MODBUS 失去唯一调用者(现为零调用者);模拟器 `--cfgtest` 不再覆盖迁移场景;§2 D10 / §3.5 / §4.2 / §5.1 / §6.2 / §6.4 / §7 / §9 / §11.1 同步 |
| 2026-10-26 | v1.15:**新增 `[comm]` 段(道G task-8,「UI 补齐」卡 5 非 UI 前置)**——`App/protocol/iec104/iec104_comm.h` 契约 + `iec104_pts.c` 尾段注册(104 公共地址 / 监听端口 2 键,getter 合法带自愈,adc_cal 先例);内核前 `iec104_comm_init()` 注册([adc_cal] / [run_par] 同「先注册后 load」契约);生效 = **改后重启**(不挂回调、不新增事件位,区别于 [network] 免重启重建);段占位 5→6 / 8,键表与主站地址不做理由 → `docs/子系统设计/网络配置说明.md` §3.6;机器钉 = `test/host/cfg_file` TC-CONFIG-056/057(段/键上限)+ `test/host/iec104` TC-IEC104-031~033(模板/重载/带外/ca 端到端)