Files
STM32F4-Base/docs/子系统设计/IEC104上行设计说明.md
T
wandering1 2525c2d948 docs(104): 端口口径统一成「工厂默认」,登记「改后立即重 bind」待办
用户裁定两条:① 剩余裸写端口的地方加「工厂默认」限定词,不改固件行为;
② 生效语义按需求口径「保存即生效」定,现实现(端口下次重 bind 才生效)是
要改的一侧 → 建任务登记,本笔不动代码行为。

限定词(24 处 / 17 文件):AGENTS 数据主线、App/protocol/README、根 README
文档索引、Src/freertos.c 三处任务注释、docs/README、项目启动顺序管理、存储
架构说明、软件架构与运行时规格(2)、RAM 使用分析、产品需求设计文档(5)、
项目架构总览(3)、test/host/README、iec104_master 流程注释、IEC104 上行设
计说明、继电保护功能复现规划。配图两张同批重跑:gen_arch_svg.py 与
gen_memory_svg.py(脚本与产物同一笔)。

两处陈旧计数(都做过双源核证):
- 测试开发标准流程 像素层「60 内容页」→ 内容页黄金参考 66 张(64 页);
  双源 = simulator/tests/ref/shot_*.bin 实数 66 与 check_content_pages.py
  的 PAGES 66 条(含两个 _p2)。
- .pre-commit-config.yaml 的 uinav-selftest 注释「18 张黄金参考」→ 写成
  「接线当时 18 张、现 66 张」:git log -S 定到 6a904c1b(2026-10-25),那
  天 ref 目录确实 18 张,写时为真,但它描述的是钩子现有行为,属当前态。

口径修正与登记:
- 页面扩充详细方案「通讯设置 = 无配置模型(端口与 CA 都是 #define)」→ 标
  「当时」,并写明现已在 /config.ini 的 [comm] 段可配、UI 通讯设置页可改。
- 产品需求 §通讯配置:删掉「改后需重启生效」这句把两件事混为一谈的话,改成
  需求要求「保存即生效」+ 现实现(公共地址重启、端口下次重 bind)+ 指向登记簿。
- docs/任务规划/登记簿.md §1 新增「104 端口改后立刻重 bind」:含隐性耦合
  —— .serialtest/config.json 的必检锚点写死 listening on 2404,端口一改串口
  回归必假失败;改行为时要么锚点只查 listening on,要么由脚本注入端口。

复扫(旧形态归零判据)剩 20 处未带限定词,逐条定性为刻意不改:默认值定义处、
出厂模板与断言、usage 与 argparse default、UI 黄金参考显示值、台架主站地址
(同数不同义)、串口锚点、叙事句、引用关系图坐标。逐条理由见一次性台账。

门禁:check_doc_links --sections-strict 对 16 篇改动 .md 逐个 rc=0;
check_mermaid --all 31 文件 62 围栏新发 0;check_nested_comment --all 449
文件新发 0;check_bom_drift --all 99 个名单内文件无漂移;
check_generated_fresh PASS(25 生成器 / 21 一致 / 4 只减不增基线);字节体检
无新增 BOM / CR / U+FFFD / 行尾空格(三连空行与 HEAD 逐项相同 = 存量)。
/keil-build 0 错 0 警,Program Size Code=246720 RO=29836 RW=2052 ZI=149708
与凭证同码:本笔只碰注释与配图文字,无固件行为变更。
2026-10-30 15:40:27 +08:00

258 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# IEC 60870-5-104 上行设计说明(B4)
> **定位**:DTU 上行规约 IEC 60870-5-104(DL/T 634.5104-2009)的设计基线——
> 范围、点表、链路流程、板端接入、预算与测试。规格来源
> `docs/算法/继电保护功能复现规划.md` §1.5(B0 产品定义,验收 = 省电科院点表);
> 代码落点 `App/protocol/iec104/`;未闭环的送检验收挂 `docs/任务规划/登记簿.md` §7。
> **角色**:本 DTU = **被控站(受控端)**,104 **TCP 服务器,端口 `[comm]` 段 `port`(工厂默认 2404)**;主站
> (控制端,点表参考 `192.168.1.199`)主动连入。
## 1. 范围(V0.1 子集)
| 面 | 本期做 | 本期不做(登记 §8) |
|---|---|---|
| 链路 | APCI U/S/I、STARTDT/STOPDT/TESTFR、k/w 窗、t1/t2/t3 | 多主站并发(单连接语义,见 §5) |
| 遥测 YC | 短浮点 M_ME_NC_1(系数 1):背景/周期/死区上送 | 归一化/标度、谐波轨(「遥测谐波轨(可选)」) |
| 遥信 YX | 单点 M_SP + 双点 M_DP:背景 + 突发(COT=3);**突发单点带 CP56 时标 = M_SP_TB_1(B9,随「SOE 记录」 SOE 落地)** | 双点带时标(V0.1 双点恒 IV,无意义) |
| 遥控 YK | 双点 C_DC 两段式(选择 82H/执行 02H) | 带时标、升降/步位 |
| 定值 | C_RD 读 + C_SE_NC 两段写(地址 = prot_set `comm_addr`) | 定值区切换/下装文件 |
| 其他 | C_CS 对时(→ `sys_clock`)、C_TS、C_RP | 固有参数 8001H~800AH(字符串类)、C_CI 电度、文件传输(录波) |
## 2. 分层与模块(采集链路测试可验证性约束:协议解析与硬件 I/O 分离)
| 模块 | 文件 | 职责 | I/O |
|---|---|---|---|
| ASDU 编解码 | `App/protocol/iec104/iec104_asdu.c/h` | 类型/COT 常量、ASDU 头、IOA 3B 小端、短浮点、CP56Time2a↔epoch | 零 |
| 点表/数据源 | `App/protocol/iec104/iec104_pts.c/h` | YC/YX/YK/定值表、COV(变位+死区)、**数据源 vtable**(板端绑 phasor/prot_*,单测绑假源) | 零(源注入) |
| 链路状态机 | `App/protocol/iec104/iec104_link.c/h` | TCP 流重组、U/S/I、k/w/t1/t2/t3、总召/突发/遥控/定值/对时编排 | 零(收=on_rx 喂流,发=回调,时基注入) |
| 板端胶水 | `App/task/iec104_serv.c/h`(随实现定名) | `[comm]` port 监听(工厂默认 2404)、数据通道收发、断链重听、netTask 驱动 | 全部(net_* API) |
| 通讯参数段 | `App/protocol/iec104/iec104_comm.c/h`(道G task-8) | `[comm]` 段注册 + ca/端口 getter 合法带自愈 / setter 守卫 | cfg_file 引擎(只注册不属主) |
> 三个纯逻辑模块 + 真实数据源(`App/algo/` 全纯逻辑)可整体在主机侧编译 →
> 主机侧纯逻辑单测覆盖**全协议栈**(`test/host/iec104/`),与 `test/host/prot_elm/` 同模式。
## 3. 点表(V0.1)
### 3.1 遥测 YC(IOA 0x4011+,短浮点,系数 1)
| IOA | 点 | 来源 | V0.1 状态 |
|---|---|---|---|
| 0x4011 | 1Ua | 测频与遥测快照槽 `ADC_SEM_UA`(104 改读快照) | ✅ 有效 |
| 0x4012/0x4013 | 1Ub/1Uc | 同表槽位;STM32 侧削顶(`docs/算法/继电保护功能复现规划.md` §1.6) | ❌ 发 IV——**品质驱动**(快照 CLIPPED 位映射,非点表绑死;V0.2 消除双转换器无共同采样触发后零改动自动启用) |
| 0x4014 | 1U0 | 快照槽 `ADC_SEM_UN`(外部零序专用通道) | ✅ 有效 |
| 0x4015 | 1Ia | 快照槽 `ADC_SEM_IA` | ✅ 有效 |
| 0x4016~0x4018 | 1Ib/1Ic/1I0 | 同 Ub/Uc:绑槽 + 品质驱动 | ❌ 发 IV |
| 0x401D | 1f 频率 | 快照 `freq_hz`(频率点绑定;B8 自适应窗测频 `freq_get()` 同源) | ✅ 随测频收敛生效;`freq_valid()==0` → IV(该点 DoD 原句) |
- **数据源 = 测频与遥测快照**(`App/algo/meas.h`,「数据可信」遥测快照发布与 104 消费):200 ms 基波有效值
+ 每点品质,104 与 UI 共用同一份上游;读数单位 = ADC 引脚电压(eng=0,`App/algo/adc_cal`
折算层 2026-10-25 落地;二次值等两点标定置 VALID)。槽位值/品质整体透传,本层
**不再自行判新旧**(快照 `seq` 仅供 COV 去重)。
- 品质映射:快照 `MEAS_QDS_*` 低字节与线上 QDS 同值直传;**CLIPPED → IV**(「削顶通道
恒 IV」口径不变、改由品质驱动,docs/任务规划/数据可信.md §8 风险表「削顶通道被当成可测」行的单测要求);
快照从未发布(开机 <200 ms)→ IV|NT;判定顺序 IV 优先于 NT。
- 死区(104 改读快照):从「越死区即报」改为**连续 2 个新测量点越限才确认**——板端测量点 =
快照换帧(100 ms 轮询 vs 200 ms 快照按 `seq` 去重,防同一帧把单点噪声重复计满 2 点;
基准恒为**上次上送值**,非上次读值——防慢漂移把基准吃掉,参考系统同款);品质翻转
当拍直报、不计数。死区数值 = 运行时副本(`iec104_pts_yc_set_deadband`,ROM 默认沿
下游常数按工程量重定的额定 0.5% 档:0.5 V / 0.005 A / 0.5 Hz;运行参数段接 8020H 段后主站可读写);
标准源注入复核(两点标定)待台架。
### 3.2 遥信 YX(IOA 1~33)
| 段 | 点 | 来源 | V0.1 状态 |
|---|---|---|---|
| 1~4 | 开关位置(双点) | 无出口/位置硬件 | ❌ IV |
| 5~14 | 预留 | — | ❌ IV |
| 15 | 故障总 | OC1∨OC2∨OC3∨3U0(`prot_elm`) | ✅ |
| 16~18 | 过流 Ⅰ/Ⅱ/Ⅲ 段 | `PE_OC1/2/3` asserted | ✅ |
| 19~20 | 零序过流 Ⅰ/Ⅱ | 「三段式零序过流」未实现 | ❌ IV |
| 21~22 | 接地故障/重载 | 未实现 | ❌ IV |
| 23 | 过载 | `PE_OL` asserted | ✅ |
| 24~30 | 外部遥信(RS-485) | B2 暂缓(`docs/任务规划/暂缓-RS485.md`) | ❌ IV |
| 31 | 保护闭锁 | `prot_set_locked()` | ✅ |
| 32~33 | 预留 | — | ❌ IV |
> **点存在性优先**:无效点照常入总召/周期表并置 IV——点表是验收口径,缺行比
> 置 IV 更糟(主站侧无法区分"没做"与"接线错")。
>
> **保护类 YX(15/16~18/23/31)自 B5(2026-10-12)起为活数据**:protectTask
> 20 ms 判据节拍驱动 `prot_elm` 状态 → pts COV 变位上送(≤120 ms 端到端)。
>
> **突发单点遥信自 B9(2026-10-12)起带时标 = M_SP_TB_1(30)**:仅自发
> (COT=3)单点组(idx≥4)换型,信息体 = SIQ + CP56Time2a(8B);**总召/
> 周期扫描保持 M_SP_NA_1/M_DP_NA_1**(时标只用于变位事件,标准语义,防
> "全量换型")。时标源 = `sys_clock`(link cfg `epoch_ms` 回调):秒域时基
> → ms 域 = 秒×1000(亚秒恒 0,不造假);墙钟未建 → CP56 IV 位如实置位。
> 与 `/log/soe.log` 落盘记录同源同时基(`App/log/soe_store.c`,见
> `App/log/README.md`)——104 时标 = 事件上送时刻,SOE 记录 = 事件发生
> 时刻(20 ms 判据拍),两者差 ≤ 一拍 + 排空延迟(≤50 ms)。
### 3.3 遥控 YK(IOA 0x6000+,双点两段式)
| IOA | 点 | V0.1 行为 |
|---|---|---|
| 0x6000~0x6003 | YK1~4 开关 | **否定确认**(无出口硬件,`docs/算法/继电保护功能复现规划.md` R7) |
| 0x600A | YK11 信号复归 | ✅ 执行段 → `prot_elm_request_reset()`(B5 起跨上下文安全:请求由 protectTask 下一拍 eval 头部消费,不当拍直接复位) |
| 0x600B | YK12 遥控软压板 | ✅ 两段式转真(B7):选择段校验 DPC(ON/OFF,非法即 PN);执行段 → `prot_set_set_sealed(PS_GLOBAL_EN)` 原子写+封存;protectTask 下一拍读到退出态即复位元件并停判据(`App/task/protect_task.c`) |
| 0x600C | YK13 电池活化 | 否定确认(无对应硬件) |
| 0x600D | YK14 解除闭锁 | 否定确认(闭锁源于定值校验和,只能重写定值解除) |
### 3.4 定值(IOA = 通讯地址)
- 地址 = `App/algo/prot_set.h` 元数据 `comm_addr`(0x8820 基址 + 告警段 0x8027~0x802A,
逐项对照参考项目 `Const_CommSetPara.c`,见 `docs/算法/继电保护功能复现规划.md` §5.4)——
**点表不另立地址表**,prot_set 即单一事实源。`comm_addr = 0` 的本地项(投退/返回系数/
全局软压板)不可经 C_RD/C_SE 寻址(B7 起显式排除,防 IOA=0 误命中首个本地项)。
- 写两段式:选择 = 范围校验;执行 = `prot_set_set_sealed`(**原子写+封存**:影子区
写好后单字交换活跃区,protectTask 的「定值校验和」巡检任何时刻都看到自洽快照,
B5 登记的 µs 级竞态窗口就此收口)。
- 持久化(B7):执行段置 prot_set 脏标志 → iec104Task 每轮 poll `dirty_peek` 投递
`SYS_EVT_PROT_SET_SAVE` → **defaultTask(配置引擎属主)** 经 `App/cfg/prot_set_store.c`
原子写 `/prot_set.bin`(blob = magic+版本+计数+值区+sum32 正/反码对);
开机在 STORAGE_READY 屏障前装载,损坏/越界/版本换代 = 整表拒绝保持出厂默认
(all-or-nothing)+ 自检留痕 `prot set corrupt`;写盘失败脏标志保留 = 下轮重试。
**运行参数段 `0x8020H~0x8058H`(「数据可信」交付项,2026-10-25 落实)**:
> ⚠ 逐项地址为**本仓自定**(`docs/算法/继电保护功能复现规划.md` §5.4 的权威表不在仓——
参考项目 `CommPara_ConfigTable` 拿不到,送检点表到位后**以送检为准替换常量表**,
映射表只落在 `App/algo/run_par.c` 顶部常量段一处)。
| 地址 | 项 | 域(C_SE/C_RD 浮点工程量) | 工厂默认 | 真源与守卫 |
|---|---|---|---|---|
| 0x8030~0x8038 | 遥测死区 UA/UB/UC/UN/IA/IB/IC/IN/FREQ | V / A / Hz,分档上限 50/1/5 | 下游常数按工程量重定的额定 0.5 % 档(0.5 V ×4 / 0.005 A ×4 / 0.5 Hz) | `run_par` [run_par] 段 9 键 int32 毫单位;落盘键表 → `docs/子系统设计/网络配置说明.md` §3.5 |
| 0x8040~0x8047 | 通道 k 覆盖(µV/码) | 正浮点;adc_cal 合法带 ROM×[0.5,2.0] 执行段裁决 | 回显生效值(ROM = 152.587890625 / 805.6640625) | **写 = `adc_cal_set` 代理,系数唯一真源在 [adc_cal] 段,run_par 不存系数**(防后人拿 run_par 表当真源表) |
| 0x8048~0x804F | 通道 b 覆盖(码值,可负) | ±8192(adc_cal 带) | 0(回显) | 同上;单侧写 RMW 用另一侧**当前生效值** |
| 其余 | 留白(PT/CT 变比等未启用类别) | — | — | C_RD → COT14+EVI / C_SE → actcon+PN |
- `0x8027~0x802A` 是 prot_set 告警段(过压/欠压),**run_par 命中集精确匹配、永不覆盖**;
两表共存的路由顺序 = **run_par 表先、未命中落 prot_set**(`iec104_pts.c` set 路由,
证据钉在 TC-IEC104-029)。
- 现行门控 = 两段式 + 范围/合法带校验(与保护定值同款),**无「仅暂停/测试态可写」
门控**——文档现行如此,不造新语义;若送检点表要求该类门控,接线点在
`run_par_104_write` 执行段入口,加一处即可。
- 持久化(运行参数段):任一写成功置 `run_par` 脏 → iec104Task 同款 peek 投递
`SYS_EVT_RUN_PAR_SAVE`(1<<8) → defaultTask `run_par_store_save()` 调 `cfg_file_save()`
整档外科保存(一趟覆盖 [run_par] 9 键 + 被代理改写的 [adc_cal]/[adc_cal_ofs],
lead 裁②一条事件位一趟够用);失败脏保留 = 下轮重试。消费侧(COV 判定)**实时读
引擎副本**,无缓存无投递(104 改读快照时的 pts 层 s_yc_db 副本已收编进 run_par)。
## 4. 链路与流程
- 参数:k=12 / w=8 / t1=15s / t2=10s / t3=20s(标准默认);序号 15bit 回绕,
超时判定一律差值法(tick 回绕安全,同 `App/algo/` 惯例)。
- 状态机:IDLE→(TCP 建立)OPEN→(STARTDT act/con)RUN⇄STOPPED;t1 超时或坏帧
→ 实例回 IDLE,**由胶水层负责断 socket**(纯逻辑层不感知 I/O)。
- 总召(C_IC 组 1):actcon → 分片背景扫描(YX 双点组 SQ=1 → YX 单点组 SQ=1 →
YC 组 SQ=1 → 频率单点)→ actterm;首召/ RBT 位 → COT=4(初始化),否则 COT=2。
单次 poll 发送预算 4 个 ASDU(防大扫描饿死宿主任务)。
- 突发:COV = 遥信变位 + 遥测**死区 2 点确认 + 品质翻转直报**(`iec104_pts_cov_check`,
104 改读快照语义见 §3.1 注),COT=3 逐点 SQ=0。
- 周期:RUN 态 60 s 全 YC 上送 COT=1。
- 遥控/定值写:两段式,失配/越界 → COT|PN 否定确认;未知 IOA → COT=14+EVI、
错公共地址 → COT=13+EVI、未知类型 → COT=12+EVI。
- 对时 C_CS:CP56 → epoch ms → 胶水层 `sys_clock` 设置(IV 位置起即 PN)。
## 5. 板端接入(iec104Task 胶水,2026-10-12 实现回填)
- **承载任务 = 专用 iec104Task(640 words / Normal)**,非 netTask:netTask 是
socket RPC 执行者,`net_recv/net_send` 在 netTask 内调用 = 自己等自己回执(死锁)。
手写于 `Src/freertos.c` USER CODE 区(timeSyncTask/adcTask 同款先例),文件
`App/task/iec104_serv.c`(纯编排,协议语义全在 `App/protocol/iec104/`)。
- **看门狗**:`TASK_WD_IEC104 = 7` 纳入 `wd_required_mask()` 两分支(产品 0x1F7 /
HW 测试层 0x1F5,含 B5 protect 位 bit8);主循环 100ms 节拍喂位,屏障等待按
100ms 分片喂位(timeSyncTask 2026-10-02 台架先例)。
- **就绪屏障**:等 `SYS_EVT_NET_READY`(`osFlagsNoClear` + 分片)后才建监听。
- **单连接语义**(`AGENTS.md` 模块约定,抄 `ui_net_sink.c` 模板):监听端口(工厂默认 2404)与
数据通道复用同一 socket(accept 返回即 listen fd);EOF 时 net 层 auto_relisten,
应用层**不得**关 fd(`client == listen` 分支);仅模拟器多连接后端(fd 不同)显式关。
- **门控 + 兜底**:`net_listener_armed`/`net_can_read`(纯 RAM 读)省无谓 RPC 往返
(门控不用只认 ESTABLISHED 的 `net_can_accept`——那会把监听
服役窗口全挡掉,CONNECT 事件补 accept 探针从不运行);
每 32 轮无条件 recv 一次防 RECV_OK 事件未收到(沿用 UI 推流侧「周期 recv 兜事件丢失」的先例)。
- **网络代次自愈**:`net_get_generation()` 变化 → 关旧 fd 重建监听(netTask
NET_RECONFIG 重建路径的既有约定)。
- **主动拆链**(与 EOF 路径相对):协议故障(坏帧/N(R) 倒退/t1 超时)或发送连续
3 次失败(短写先例)→ 本端 `net_close` + 重建监听,促主站重连——违例时
主站不会自行断开,必须本端断(time_sync 会话收尾先例,非芯片 auto_relisten 风暴路径)。
- **抢占策略修正(V0.1 如实登记)**:建档时定"后连优先",实现后确认单连接
socket 在连接态无法 accept(监听即数据通道),**V0.1 实为首到优先**——旧会话
经 EOF/t1/拆链结束后新主站才能接入;后连优先需 net 层支持,见 §8 第 7 条。
- **k/w 与 RPC 时延**:`net_send` 经 netTask 串行 RPC(最坏往返无界 ≈4 s);
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。
- **通讯参数可配置化(道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」口径作废)。
- **面板编辑(道G task-9,2026-10-27)**:「通讯参数 → 通讯设置」页(`#page=comm`,
`Drivers/BSP/UI/ui_page_comm.c`)经**通道六注入**(`ui_data_comm_ops_t`,理由与
通道五同构:分层 / 模拟器 / 「写值+置脏」原子性)编辑 ca/port;合法带由执行侧
现给(面板粗守与 setter 守卫同一出口,抄数即第二真源)。保存行 ENTER →
WAIT/OK 锁存判定 = `iec104_comm_dirty_peek()`,落盘 = defaultTask 每轮无条件
`iec104_comm_store_save()`(run_par/prot_set 同款 B7 脏位形态,**无事件位**,
延续上条 D5 口径)。屏上 OK 只证明**落盘完成**,不证明"已在新值上运行"——
生效时刻仍按上条(ca 重启 / port 重 bind)。容量核对:生产占 **6/8 段、
[comm] 2/16 键**,超限现象 = 注册返 -1 → `comm init: FAIL` 启动锚 + getter
回 ROM 默认,语义由 cfg_file 宿主套 TC-CONFIG-056/057 钉住,不抬宏。
- **观测计数(只读 getter,2026-10-27 道G task-12)**:`App/task/iec104_serv.c` 另有四枚
**只增不清**的计数,经 `iec104_serv_get_stats()`(声明在 `App/task/iec104_serv.h`)对外只读:
`conn_up`(主站接入次数)/ `apdu_tx`(成功发出的 APDU 帧数)/ `tx_fail`(发送短写或失败次数)/
`rx_bytes`(收到的**字节**数,不是帧数——帧边界在 link 层重组,serv 层看不见)。口径是
**上电以来、跨重连不清零**,给页面用时那一栏要写「本次开机以来」,与运行时长同一类。
⚠ 与本节三枚**控制量**(`s_liveness_ctr` / `s_send_fail_run` / `s_conn_dead`,为"要不要拆链"
服务、会被复位、只有 8 位)无关,拿它们当统计数字必错。为什么留在 serv.c 而不并进通用
统计模块、也不放进 `iec104_link_t`(实例是文件级 static,喂数据侧拿不到指针,且
`link_open/link_close` 会把"上电以来"洗成"本次连接"):104 的账本归 104。getter
**任意上下文可读**(不取锁、不读时刻、不碰 socket;四枚 u32 单字原子,跨字段不保证同一
瞬间,四枚之间没有"合起来才成立"的判据),到顶饱和不回绕。**「总召次数 / 上送 I 帧数」
不在这里**:那是 link 层事件,serv 照不到,需要它得去
`App/protocol/iec104/iec104_link.c` 加——本卡未做,别在页面上摆一个数却没有产生它的地方。
## 6. RAM / 栈预算(2026-10-12 实测)
| 项 | 实测 | 依据 |
|---|---|---|
| 静态(新增) | link 实例 ~560 B(含 512 B 流重组缓冲)+ pts 快照 ~120 B + 胶水 rx 128 B | `iec104_link_t` 字段和 |
| iec104Task 栈 | 640 words(2560 B):静态最坏链 1456 B(拆链→重建监听→net_bind→SPI 故障→日志尾),余 1104 B / 43% **PASS** | `stack_report.py` 2026-10-12;512 words 时余 592 B < ISR 底线 → 抬栈 |
| CCM 堆 | 任务栈 2.5 KB + TCB,min_ever 6,640 → ≈6,100 B(> 3,072 告警线) | adcTask 抬栈同口径台账 |
| 总量门禁 | RW_IRAM1 73.1% / RW_CCM 75.3%(<92%) | `ram_baseline.py --update` 2026-10-12 |
## 7. 测试方案
| 层 | 内容 |
|---|---|
| 主机侧纯逻辑单测 | `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 去重(104 改读快照与频率点绑定,TC-IEC104-025~028)+ 运行参数段路由/两段式/RMW(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)|
| 静态门禁 | `iec104-unit-test` 钩子 + cppcheck/coding-standard |
| 台架 | `/serial-boot-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 |
## 8. V0.1 限制与待办(如实登记)
1. 固有参数 8001H~800AH 未实现(字符串类无法短浮点上送)→ 未知 IOA 应答;随送检需求再设计。
2. ~~SOE 带时标~~ **突发单点已带 CP56 时标 = M_SP_TB_1(B9 落地,见 §3.2)**;
双点带时标不做(V0.1 双点恒 IV,无意义)。
3. YK1~4/13/14 否定确认——出口硬件确认后(R7)转真执行(YK12 已于 B7 转真)。
4. ~~遥测死区 10.0 码值为拟定值~~——工程量死区已于 2026-10-25 随下游常数按工程量重定落地(额定 0.5%);
104 改读快照已加 2 点确认;运行参数段(2026-10-25)已落地 **0x8020~0x8058 读写映射 + [run_par]
持久化 + 系数覆盖直通 adc_cal**——死区系数唯一真源 = cfg 引擎副本(run_par 存死区、
**不存系数**,系数真源在 [adc_cal] 段,见 §3.4 表注),`iec104_pts_yc_set_deadband`
降级为转发壳。
5. 多主站/双链路冗余不做(单连接语义)。
6. 主站侧验证工具 = `test/host/iec104_master.py`(B6 2026-10-12 就绪,台架往返 18/18,覆盖总召/变位/遥控/定值);**第三方送检环境仍未就绪**——lib60870 级第三方全流程归 B10。
7. **后连优先未实现**(V0.1 = 首到优先):单连接 socket 连接态无法 accept,
旧会话结束前新主站接入被拒;后连优先需 net 层(CH395F 单连接模型)支持,
真需要冗余切换时随 net 扩展立项。
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]` 段当前**没有**该键。