补充遥调业务代码
CI / L0 静态门禁 (pre-commit) (push) Failing after 45s
CI / L0.5 主机侧单测 (MSVC) (push) Failing after 43s

This commit is contained in:
STF
2026-11-01 00:38:09 -07:00
parent cd42b4d61d
commit e1b0434774
233 changed files with 292584 additions and 268 deletions
@@ -1,6 +1,6 @@
# IEC104 函数 API 接口汇总(按实现文件)
依据当前工作区的协议库源码及对应头文件整理,按实现所在的 `.c` 文件分节。范围不包含示范程序、测试程序及其业务辅助函数;包括独立 Type 45/46 遥控模块的 14 个公开函数及其完整类型、返回值和生命周期约定。
依据当前工作区的协议库源码及对应头文件整理,按实现所在的 `.c` 文件分节。范围不包含示范程序、测试程序及其业务辅助函数;包括Type 45–51/58–64命令模块的16个公开函数及其完整类型、返回值和生命周期约定。
共覆盖 **25 个实现文件**,其中内部跨文件协作函数为Link 5个、信息对象层2个、召唤引擎2个。表中标有“内部协作”的函数不属于业务 API;文件内的 `static` 辅助函数、宏和类型定义不作为函数接口列出,各模块入口以对应小节和声明头文件为准。
@@ -19,7 +19,7 @@
| 接入站总召/分组总召 | [Interrogation](#file-21) | Handle_Event接收激活/撤销及状态通知,Process推进并检查期限;NextEvent取业务事件,以Reply_Begin/Submit_Batch/Complete_Data显式应答 |
| 接入计数量召唤 | [CounterInterrogation](#file-23) | 独立创建,复用驱动;读取用Reply_Begin和数据接口,冻结/复位用Execute_Begin |
| 接入时钟同步 | [ClockSynchronization](#file-24) | Handle_Event校验保存,Process推进应答及期限,只有显式Execute_Next调用一次应用动作;确认回显原时标 |
| 接入单点/双点遥控 | [Command 配置](#command-api-config)、[函数契约](#command-api-functions)、[返回值](#command-api-results) | NextEvent取得SELECT/EXECUTE,Accept/Reject消费事件;执行后Report_Result、Submit_Return_Info、Complete,FINISHED清理资源 |
| 接入遥控/遥调及带时标命令 | [Command 点表与配置](#command-api-config)、[函数契约](#command-api-functions)、[时间基准](#command-api-time-base) | 配置按CA/IOA排序的只读点表;NextEvent取得SELECT/EXECUTE,Accept/Reject消费,执行后Report_Result、Submit_Return_Info、Complete,FINISHED清理;应用提供时间基准 |
| 解析主站请求 | [Asdu_Get_Data / Parse / Get_Element](#file-16) | 先检查 I_FRAME 及 `asdu_result`,再做精确解析与业务条件检查 |
| 回复任意 ASDU | [MasterConnection_Send_ASDU](#file-18) | 使用 `&event.connection`;ASDU 可以是单独构造的对象 |
| 回复激活确认、终止 | [MasterConnection_Send_ACT_CON / Send_ACT_TERM](#file-18) | 修改传入 ASDU 的 COT/P/N 后提交,执行时机由业务决定 |
@@ -2496,9 +2496,9 @@ bool IEC104_ClockSynchronization_Get_Status(const IEC104_ClockSynchronization_t
## 25. IEC104_Command.c
实现:[IEC104_Command.c](../../104/business/IEC104_Command.c);声明:[IEC104_Command.h](../../104/business/IEC104_Command.h)。本节列出全部 **14 个公开函数**,并说明其配置、值类型及调用约束。状态机设计见[遥控设计](IEC104_Command_设计架构.md),事件循环与应用处理顺序见[遥控调用流程](IEC104_完整调用流程.md#flow-command)。
实现:[IEC104_Command.c](../../104/business/IEC104_Command.c);声明:[IEC104_Command.h](../../104/business/IEC104_Command.h)。本节列出全部 **16 个公开函数**,并说明其配置、值类型及调用约束。状态机设计见[遥控设计](IEC104_Command_设计架构.md),事件循环与应用处理顺序见[遥控调用流程](IEC104_完整调用流程.md#flow-command)。
模块只处理 Type 45(单点命令)和 Type 46(双点命令)的 COT=6 激活、COT=8 撤销。模块负责格式、寻址、选择/执行匹配、期限和协议收尾;应用负责 IOA 点表、远方/就地、闭锁、返校、输出及设备结果。未配置的类型返回 IGNORED,交后续处理器;不支持遥调、带时标命令、多个遥控并行或跨重连选择保持。决定窗口内的同点激活按[请求校验规则](#command-api-validation)静默丢弃,窗口外不按请求内容去重。
模块处理Type 45–51及58–64的COT=6激活、COT=8撤销,默认全局启用14种,但默认点表为空,不执行任何点。模块负责格式、寻址、点类型和模式准入、选择/执行匹配、时标判定、期限及协议收尾;应用提供有序只读点表,负责远方/就地、闭锁、返校、设定值限值、输出及设备结果,并提供可信时间基准。全局未启用的类型返回IGNORED,交后续处理器;点掩码不允许的已启用类型回否定。不支持多个命令并行或跨重连选择保持。决定窗口内的同点激活按[请求校验规则](#command-api-validation)静默丢弃,窗口外不按请求内容去重。
本节函数表省略公共前缀 `IEC104_Command_`;枚举表注明各自省略的前缀。不能把枚举的非零值统一视为成功。
@@ -2506,19 +2506,47 @@ bool IEC104_ClockSynchronization_Get_Status(const IEC104_ClockSynchronization_t
### 25.1 配置、容量与所有权
`IEC104_CommandConfig_t` 由 Config_Default 填写,Create 按值复制;调用后可修改或释放原配置存储,不影响实例。Create 不接受 NULL 配置,也不自动初始化或开启 Slave。
`IEC104_CommandConfig_t`由Config_Default填写,Create按值复制Config;调用后可修改或释放原Config存储,但其points指向的点表只被借用,必须持续有效且只读至Destroy成功。Create不接受NULL配置,也不自动初始化或开启Slave。
```c
typedef enum {
IEC104_COMMAND_MODE_DIRECT = 1,
IEC104_COMMAND_MODE_SELECT_EXECUTE = 2
} IEC104_CommandMode_t;
typedef struct {
uint16_t common_address;
uint32_t ioa;
uint16_t type_mask;
IEC104_CommandMode_t mode;
} IEC104_CommandPoint_t;
```
| 点字段/模式 | 校验与含义 |
|---|---|
| common_address | 1..65534;可以配置尚未注册的CA,但实际受理仍核对Slave注册身份 |
| ioa | 1..0xFFFFFF;点表按common_address再ioa严格升序且唯一,同一点允许类型放在同一行 |
| type_mask | 非零,仅45–51/58–64的14个已知位;可含全局未启用位,实际允许集合取全局与点掩码交集 |
| DIRECT(1) | 只直接执行;有S/E的类型收到SELECT回否定7;51/64只能用此模式 |
| SELECT_EXECUTE(2) | 先选择再执行,未选择执行回否定7;点掩码不得含51/64,即使全局未启用这两种也不允许 |
Create拒绝地址越界、无序/重复点、非法点掩码或模式。点查找为二分;表从Create至Destroy不可改写、重排或释放,不复制到模块、不通过回调查询。更新须Cancel和处理FINISHED、完成收尾,关闭Slave并读空旧通知后Destroy,再用新表Create、重启服务并建立新连接,完整分派CONNECTED/ACTIVATED。新实例不继承会话或时间基准,须重新Set_Time_Base;不能仅重建Command便挂回旧连接热更新。点表只定义命令许可与执行模式,不保存设备当前值。
| 字段 | 默认值 | 校验及语义 |
| --- | --- | --- |
| `type_mask` | `IEC104_COMMAND_TYPE_BIT(45U) \| IEC104_COMMAND_TYPE_BIT(46U)` | 非零,仅允许45/46两位;未选中的类型不接手 |
| `select_required_mask` | 与默认 `type_mask` 相同 | 必须为 `type_mask` 子集;某位为0表示该类型允许直接执行,也仍可选择后执行 |
| `select_timeout_ms` | 30000 | 必须非零;选择生效至匹配执行被Handle_Event受理,包含选择确认的发送等待 |
| `type_mask` | `0x3FFF`,45–51/58–64全部14位 | uint16_t,非零;可按应用裁剪,bit14/15必须为0;未选中的类型不接手 |
| `points` | NULL | const IEC104_CommandPoint_t *,借用的只读有序点表;与point_count配对 |
| `point_count` | 0 | size_t,点元素数;NULL/0合法空表,非NULL/0及NULL/非零均拒绝,不能超过SIZE_MAX/sizeof(IEC104_CommandPoint_t) |
| `select_timeout_ms` | 30000 | 每实例统一、仅选择模式有效,必须非零;Config_Default后、Create前可改;计时及选值见[选择有效期](IEC104_Command_设计架构.md#4-状态与期限) |
| `decision_timeout_ms` | 15000 | 工作事件生成至Accept/Reject消费;0禁用时长限制 |
| `execute_timeout_ms` | 60000 | 执行动作返回STARTED至Report_Result成功;0禁用时长限制 |
| `send_act_term` | true | 成功执行收尾时发送ACT_TERM;false时任何结果均不发 |
| `act_term_on_failure` | false | 仅 `send_act_term=true` 时有效:启动后Report_Result(FAILED)也发送ACT_TERM;不适用于动作返回FAILED或执行超时 |
| `time_tag_max_delay_ms` | 10000 | uint32_t,带时标激活的时差上限;0只关闭时差检查,不关闭IV及UTC换算检查 |
| `master_utc_offset_minutes` | 480 | int16_t,主站时标标准时区,范围−1439..1439分钟;SU由公共时间工具处理 |
| `negative_on_expired` | false | bool,过期激活默认静默丢弃;true改为否定ACT_CON |
`IEC104_COMMAND_TYPE_BIT(type)` 的布局为45..51对应bit0..6、58..64对应bit7..13,范围外返回0,避免非法移位。第一版Create仍只接受45/46两位;宏参数会多次求值,只传常量或无副作用表达式。结构体扩展要求库和调用者一起重新编译,不承诺二进制布局兼容。
`IEC104_COMMAND_TYPE_BIT(type)`的布局为45..51对应bit0..6、58..64对应bit7..13,范围外返回0,避免非法移位;宏参数会多次求值,只传常量或无副作用表达式。默认全局掩码不代表存在可执行点:空表新激活走到点策略检查时回47。模式按CA/IOA定义,同类型的不同点可以分别DIRECT或SELECT_EXECUTE。旧select_required_mask字段已移除,调用方须删除该字段设置并提供点表;这是源码接口迁移,库和所有调用者须一起重新编译,不承诺二进制兼容。公开函数仍为16个。
| 编译期设置 | 默认值 | 作用 |
| --- | --- | --- |
@@ -2526,7 +2554,7 @@ bool IEC104_ClockSynchronization_Get_Status(const IEC104_ClockSynchronization_t
| `IEC104_COMMAND_REPLY_CAPACITY` | 4 | 每实例独立短应答槽数,用于校验错误、实例忙和否定撤销;须大于0且小于 `UINT32_MAX / 2` |
| 唯一事务槽 | 固定1个 | 内含工作/终结事件、确认、撤销或失配应答、终止的存储及排序身份;必要事务应答不依赖独立池空位 |
两个容量宏均须大于0,编译选项作用于 IEC104_Command.c。全部存储静态分配,无堆分配;模块借用Slave,Slave寿命覆盖Command。所有入口与Slave在同一事件循环串行执行,不保存动作函数或context指针。
两个容量宏均须大于0,编译选项作用于IEC104_Command.c。模块内部存储静态分配,无堆分配;点表由调用方提供并借用。模块借用Slave,Slave寿命覆盖Command。所有入口与Slave在同一事件循环串行执行,不保存动作函数或context指针。
从事务创建至槽释放,所有站点、所有IOA均视为遥控忙;FINISHED未读或本事务最后应答尚未交接也占槽。决定窗口内同点合法激活只丢弃,不新增忙否定应答。独立短应答可在 `busy=false` 时仍待发送,不阻止创建新事务,但仍参与全局FIFO,且Destroy要求它们全部清空。独立池满只计过载并丢弃新应答,不覆盖旧内容、不主动断链。事务释放后,主站重发的相同命令按新请求处理;直接执行可能再次启动输出。
@@ -2544,14 +2572,23 @@ bool IEC104_ClockSynchronization_Get_Status(const IEC104_ClockSynchronization_t
| `station_id` | uint64_t | 原站点注册身份;同CA删除再注册后编号不同 |
| `common_address` | uint16_t | 原CA,实际站地址1..65534 |
| `originator_address` | uint8_t | 原OA;参与选择/执行及撤销匹配 |
| `type_id` | uint8_t | 45或46 |
| `ioa` | uint32_t | 非零24位信息对象地址;点表存在性由应用检查 |
| `is_select` | bool | 请求S/E位,true为选择,false为执行 |
| `type_id` | uint8_t | 45–51或58–64 |
| `ioa` | uint32_t | 非零24位信息对象地址;创建工作事务前已在命令点表中验证,设备动态可用性仍由应用检查 |
| `is_select` | bool | 请求S/E位,true为选择,false为执行;51/64恒为false |
| `selected` | bool | EXECUTE是否匹配了本事务已有选择;直接执行为false |
| `state` | uint8_t | Type45为0/1,Type46为1/2,通常对应分/合;具体设备操作含义由点表定义 |
| `qualifier` | uint8_t | QU原始0..31;1短脉冲、2长脉冲、3持续输出,0及其他值由应用解释 |
| `state` | uint8_t | 45/58为0/1;46/59为1/2;47/60为1降一步、2升一步;设定值和比特串为0 |
| `qualifier` | uint8_t | 45–47及对应时标类型为QU 0..31;48–50及对应时标类型为QL 0..127;51/64为0,具体含义由应用解释 |
| `value.normalized` | int16_t(union成员) | 48/61的原始16位定点编码,实际值为normalized/32768.0 |
| `value.scaled` | int16_t(union成员) | 49/62的标度化设定值 |
| `value.short_float` | float(union成员) | 50/63的短浮点设定值,已拒绝NaN及正负Inf |
| `value.bitstring` | uint32_t(union成员) | 51/64的32位比特串;最高位是数据位 |
| `has_time` | bool | 58–64为true;其他类型为false |
| `time` | IEC60870_CP56Time2a_t | 原始解码CP56,保留IV/SU;非时标类型清零 |
| `time_utc_ms` | uint64_t | 按配置时区换算并含毫秒余数的UTC纪元毫秒;非时标类型为0 |
| `accepted_ms` | uint64_t | 当前请求由Handle_Event受理的单调毫秒;包含Slave通知排队时间,不是网络收包时间 |
value为具名union,按type_id读取对应成员;状态类无值类型清零。已受理的带时标激活一定通过IV及UTC换算检查,即使time_tag_max_delay_ms为0也一样。头文件显式包含`IEC60870_Time.h`。事件与动作参数都从模块保存的规范原文重新解码;内部解码失败时NextEvent/Accept返回ERROR,Accept不安排肯定确认也不调用动作,而按SERVICE_ERROR收尾。已交付工作事件仍欠FINISHED;若原文持续不可解码,终结事件只提供清理用身份和SERVICE_ERROR,不可将其值字段用于动作。
<a id="command-api-events"></a>
`IEC104_CommandEvent_t` 同样是纯值副本,**NextEvent取走与Accept/Reject消费是两个步骤**。取走的工作事件仍可因撤销、期限或会话结束失效,不能脱离模块检查后直接启动设备动作。
@@ -2648,13 +2685,15 @@ Accept同步至多调用一次action,context原样转交、允许NULL,两者
| 函数 | 参数与前置条件 | 成功、失败和输出 |
| --- | --- | --- |
| Config_Default | config指向调用方可写配置 | 无返回;完整填写默认值,NULL无操作,不创建实例/启动期限 |
| Create | slave为有效服务,寿命覆盖实例;config非NULL、满足配置表;**在开始消费Slave通知前调用** | 成功返回静态句柄;非法服务/配置、同Slave重复创建、池满或实例号耗尽返回NULL。仅复制配置,不开启网络 |
| Create | slave为有效服务,寿命覆盖实例;config非NULL、点表符合范围/顺序/模式约束;**在开始消费Slave通知前调用** | 成功返回静态句柄;非法服务/配置/点表、同Slave重复创建、池满或实例号耗尽返回NULL。复制Config,借用点表至Destroy,不开启网络 |
| Destroy | 事务槽为空、无未读FINISHED、独立应答池为空,不在回调中 | true归还实例,旧指针失效;否则false且保留实例。不隐式Cancel、关闭Slave或释放应用引用 |
| Cancel | self有效;不在回调中 | 无返回;清全部模块待交应答,以CANCELLED结束活动事务,保留应交FINISHED,会话激活状态不变;无效句柄/回调中无操作 |
| Set_Time_Base | self有效、不在回调中;utc_ms及base_ms为同一时刻的UTC/Net单调毫秒 | true保存基准;UTC越界、base_ms晚于当前单调时间或非法调用返回false且原基准不变,详见时间基准一节 |
| Clear_Time_Base | 本站时间不可信时调用,不在回调中 | 无返回;清除基准;无效句柄或回调中无操作;不取消已受理事务 |
| Handle_Event | event为所属Slave按FIFO取出的通知,借用内容仍有效;所有连接/启停通知必须分派 | HANDLED含状态通知及已识别请求的受理、拒绝、丢弃;IGNORED继续分派;ERROR处理来源/参数/服务错误,不能再发44兜底。详见返回表 |
| Process | 每轮网络推进、Slave通知分派后调用 | true包含无事、WAIT、事务已终结或发送故障已收尾;false为句柄/重入/服务/组包错误。最多推进 `2*(1+应答槽容量)` 步,不调用应用动作 |
| NextEvent | event为非NULL输出 | READY写入一个纯值事件并记录已取走;EMPTY/ERROR保持输出。取走不消费工作决定;已有FINISHED离线也可取 |
| Accept | event为已取走、尚未消费的当前SELECT/EXECUTE;action仅SELECT可NULL;context按值传给回调 | 先校验会话/期限并准备确认,再消费事件,至多调用一次动作;OK不是动作或发送成功,精确返回见下表 |
| Accept | event为已取走、尚未消费的当前SELECT/EXECUTE;action仅SELECT可NULL;context按值传给回调 | 先校验会话/期限并解码保存的原文,成功后准备确认、消费事件并至多调用一次动作;OK不是动作或发送成功,精确返回见下表 |
| Reject | event同Accept;cause为两个RejectCause之一 | 不调用动作,消费事件并安排7/47,以REJECTED结束;FINISHED可早于否定交接 |
| Report_Result | event为Accept动作返回STARTED的原EXECUTE;result为SUCCEEDED/FAILED,尚未成功报告 | 一次记录并冻结结果、停止执行期限;可早于确认交接。不提交返回信息、不隐式Complete |
| Submit_Return_Info | event为上述EXECUTE;asdu符合返回信息校验,仍在EXECUTING且Complete未成功 | 一次提交一帧;只能在执行确认交接后接手,否则WAIT。不缓存ASDU、不占用持久FIFO位置 |
@@ -2694,32 +2733,38 @@ WAIT后保留本次事件、待报告结果或ASDU,先分派Slave通知、推
### 25.6 请求校验、匹配和撤销
45/46请求原文固定为10字节:6字节公共头、3字节IOA、1字节SCO/DCO。当前代次、已激活且可读取的配置内命令,在匹配前先做到期转换,然后按以下优先级处理;已识别请求的拒绝/丢弃均返回HANDLED。
各类型的规范ASDU长度分别为:45/46/47为10,48/49为12,50为14,51为13;对应时标类型58–64各增加7字节,最大21。6字节公共头及3字节IOA不变,值、S/E和CP56布局由私有表决定。当前代次、已激活且可读取的配置内命令,在匹配前先做到期转换,然后按以下优先级处理;已识别请求的拒绝/丢弃均返回HANDLED。
| 顺序 | 条件 | 处理 |
| --- | --- | --- |
| 1 | 对象数不是1,或对象不足4字节(含IOA) | 计rejected,不应答,不截取多对象伪造单命令 |
| 1 | 对象数不是1,或原文不足该类型的规范长度 | 计rejected,不应答,不截取多对象或补零伪造单命令;线上已知类型截短通常先由Receiver拒绝,不进入本模块 |
| 2 | COT不是6或8 | COT=45、P/N=1 |
| 3 | CA未注册、0或65535 | COT=46、P/N=1 |
| 4 | IOA=0 | COT=47、P/N=1 |
| 5 | SQ=1、长度不等于10、P/N=1、TEST=1、SCO保留位为1或DCS为0/3 | 否定ACT_CON(7);撤销请求否定DEACT_CON(9) |
| 5 | SQ=1、长度不等于规范长度、P/N=1、TEST=1、值非法(含50/63的NaN/Inf)或CP56格式非法 | 否定ACT_CON(7);撤销请求否定DEACT_CON(9) |
| 6 | 合法COT=8 | 按撤销规则匹配 |
| 7 | 合法COT=6,当前仍为SELECT_WAIT或EXECUTE_WAIT,同generation、station_id及IOA | 静默丢弃,仅增加dropped_requests,返回HANDLED |
| 8 | 其他合法COT=6 | 匹配选择、受理新事务或忙/无选择否定7 |
| 8 | 其他58–64的COT=6 | 按时间规则处理IV、UTC换算及时差;过期可静默丢弃 |
| 9 | SELECTED阶段同点执行且选择确认已交接 | 先匹配原选择;失配终结MISMATCH,匹配后生成EXECUTE,不重新查不可变点表 |
| 10 | 其余通过检查的COT=6 | 二分查CA/IOA点;无点回47,点类型不符或DIRECT点收到SELECT回否定7 |
| 11 | 点策略通过,实例仍忙或SELECT_EXECUTE点未选择执行 | 否定7;不新建事务或工作事件 |
| 12 | 前述检查通过 | 新建唯一事务,生成SELECT或EXECUTE |
回显保留原类型、TEST、OA、CA和对象字节,VSQ规范为1、长度取10字节,按应答替换COT/P/N;因对象截短或数量不符而丢弃时不回显。除IOA=0外,模块不检查业务点表;应用用Reject(UNKNOWN_IOA)表达点不存在。
回显保留原类型、TEST、OA、CA和对象字节(包括非法值和原CP56),VSQ规范为1、长度取该类型规范长度,按应答替换COT/P/N,不重新编码对象;过长请求不复制额外尾字节。因对象截短或数量不符而丢弃时不回显。新激活走到点检查时由模块判断点是否存在、类型和模式是否许可;拒绝不生成工作事件、事务或FINISHED。应用仍可用Reject(UNKNOWN_IOA)表达动态不可用的设备点,用Reject(NEGATIVE)表达越限、闭锁等策略拒绝。
决定窗口内的丢弃不比较类型、OA、S/E、状态或QU;同点合法激活即使这些字段不同也不替代原工作请求。type_mask及上表前置校验仍优先,COT=8不适用此规则。丢弃不刷新期限、不生成新事件或应答,不增加rejected、busy_rejected或overloads;应用继续处理原SELECT/EXECUTE事件。到期转换之后已非决定阶段时,不再按此规则丢弃。
SELECTED阶段的选择匹配要求原会话、原station_id、IOA、类型、OA一致,且SCO/DCO屏蔽S/E后的字节相同(状态和QU均比较)。此阶段选择肯定确认尚未交接时,任何执行均按忙拒绝并保留选择;交接后,同点执行失配终结为MISMATCH并回显此次执行。其他站点/IOA的新激活、SELECTED中的重复选择及EXECUTING/ENDED中的新激活仍否定7,原事务继续。
SELECTED阶段的选择匹配要求原会话、原station_id、IOA、类型、OA一致,值字节逐字节相同,S/E字节屏蔽bit7后相同(状态和QU/QL均比较);CP56不比较。浮点+0/−0原始字节不同,判MISMATCH;45与58等跨类型组合也先判失配,不被后续点类型拒绝替代。此阶段选择肯定确认尚未交接时,通过时效及点策略检查的执行按忙拒绝并保留选择;交接后,同点执行失配终结为MISMATCH并回显此次执行。51/64始终直接执行,不读取不存在的S/E字节。其他站点/IOA的新激活、SELECTED中的重复选择及EXECUTING/ENDED中的新激活在通过时效及点策略检查后仍忙否定7,原事务继续。匹配执行沿用选择时已经验证的不可变点策略。
撤销要求原会话、station_id、IOA、类型、OA一致,不比较状态/QU。SELECT_WAIT、SELECTED、EXECUTE_WAIT可成功撤销,用事务自带槽回肯定9,因此独立池满也可撤销;尚未提交的旧确认被取消,新DEACT_CON取得新FIFO序号。已经交给发送层的确认无法撤回,DEACT_CON排在其后。EXECUTING、ENDED或无匹配事务时回否定9,原事务不受影响。本地Cancel不发送DEACT_CON,STOPDT则是连接停用而非COT=8撤销。
撤销要求原会话、station_id、IOA、类型、OA一致,不比较值、状态或QU/QL,不判时差、IV及UTC换算,但仍检查CP56格式。SELECT_WAIT、SELECTED、EXECUTE_WAIT可成功撤销,用事务自带槽回肯定9,因此独立池满也可撤销;尚未提交的旧确认被取消,新DEACT_CON取得新FIFO序号。已经交给发送层的确认无法撤回,DEACT_CON排在其后。EXECUTING、ENDED或无匹配事务时回否定9,原事务不受影响。本地Cancel不发送DEACT_CON,STOPDT则是连接停用而非COT=8撤销。
<a id="command-api-timeouts"></a>
### 25.7 期限、会话与调度
全部期限使用 `IEC104_Net_Now_Ms` 的单调毫秒;本版无CP56及设备UTC参数。每阶段至多一个业务期限,已用时间大于或等于期限即到期。
事务期限使用`IEC104_Net_Now_Ms`的单调毫秒,与带时标请求的UTC时效判定分开。每阶段至多一个业务期限,已用时间大于或等于期限即到期。
选择有效期不是必须等待的延时;默认30秒不变。起止时刻、确认排队占用、模式与单事务关系,以及10秒/30秒的工程选值说明统一见[状态与期限](IEC104_Command_设计架构.md#4-状态与期限)。
| 期限 | 起点 | 停止点 | 到期处理 |
| --- | --- | --- | --- |
@@ -2737,6 +2782,23 @@ Process、工作事件NextEvent及有效应用应答先用Slave_Check_Request检
每轮网络推进并读空Slave通知后都调用Process、读取业务事件,再重试应用保留的结果/返回信息/Complete。`need_process=true` 要立即推进;false不表示无待重试任务。等待上限同时受Slave_Prepare、`timer_active/timeout_remaining_ms` 和应用异步状态约束,不能因发送WAIT紧循环空转,具体见[遥控调度流程](IEC104_完整调用流程.md#flow-command-schedule)。
<a id="command-api-time-base"></a>
#### 25.7.1 时间基准与带时标激活
| 函数 | 参数、结果及约束 |
|---|---|
| Set_Time_Base(self, utc_ms, base_ms) | 保存同一时刻的UTC纪元毫秒及Net单调毫秒;成功true。句柄无效、动作回调内调用、utc_ms超过`(uint64_t)IEC60870_TIME_MAX_SECONDS * 1000U + 999U`或base_ms晚于当前单调时间时false,原基准不变 |
| Clear_Time_Base(self) | 本站时间不可信时清除基准;句柄无效或动作回调内调用无效果 |
模块不读Clock,也不保存时间回调。STOP、断线和Cancel不自动清除基准;基准更新仅影响随后受理的请求,不追溯已经生成的工作事件。应用在校时动作成功后记录utc_ms/base_ms,待Execute_Next返回后再调用Set_Time_Base;不要在动作回调中跨模块推进。
只对58–64的COT=6激活,在格式校验、撤销处理和决定期间同点丢弃之后,SELECTED匹配及忙判断之前检查。先拒绝IV=1或UTC换算失败;max=`time_tag_max_delay_ms`为0时仅跳过时差比较,不要求基准。max非零时,按`utc_ms + (now - base_ms)`推算站内UTC;无基准、now早于base_ms、加法溢出或超出公共时间范围回否定7。命令时标按配置标准时区及SU换算,并加回毫秒余数。
站内UTC比命令UTC晚至少max时为过期,默认只增加time_expired并静默丢弃;negative_on_expired=true时另计rejected并回否定7。命令UTC比站内UTC晚至少max时回否定7,计time_rejected和rejected。无效时间、无基准及推算失败也计time_rejected和rejected;单调时钟早于基准另计clock_errors。恰好等于max也拒绝或丢弃。静默过期不增加dropped_requests,不创建事务或业务事件,不产生FINISHED。
已选择但执行请求过期时,原选择继续保留到select_timeout_ms。撤销不判时差或IV/UTC换算,仍须通过CP56格式检查。时效只在Handle_Event受理时判一次,动作启动前不再检查;默认10秒窗口及15秒决定期限可能使动作开始时的命令年龄接近25秒。需要更严格策略时应用自行检查request.time_utc_ms。时区、窗口、IV处理、过期响应及ACT_TERM须与主站约定。
<a id="command-api-order"></a>
<a id="command-api-return-info"></a>
@@ -2784,6 +2846,7 @@ TERM策略在Report_Result之后由Complete决定:成功且send_act_term=true
| `events_pending` | Slave还有通知须优先读取;为true时need_process=false |
| `need_process` | 有可立即尝试的本地发送、到期或失效会话收尾;发送WAIT后不持续置true。离线只剩FINISHED时为false,应用直接NextEvent读取 |
| `timer_active` | 当前启用非零时长期限;false不关闭时钟回退检测 |
| `time_base_valid` | bool,当前是否持有时间基准;查询不推算或重新验证基准 |
| `busy` | 唯一事务仍占槽,含未读FINISHED/待交事务回复;不包含仅有独立短应答的情况 |
| `timeout_remaining_ms` | 当前期限剩余毫秒,无期限/已到期/时钟倒退时为0;timer_active=true且为0时立即推进 |
| `pending_replies` | 模块持有的事务与独立应答总数;不含已接手的HIGH/在途帧,也不含应用保留的返回信息 |
@@ -2796,15 +2859,17 @@ TERM策略在Report_Result之后由Complete决定:成功且send_act_term=true
| `negative_submitted` | 已交接的P/N=1应答数 |
| `return_info_submitted` | Submit_Return_Info已交接的帧数 |
| `waits` | 底层发送明确返回WAIT的次数;通知门禁、模块FIFO等待及正常STOP让出不计入 |
| `rejected` | 接收校验、实例忙、失配及未选择等拒绝次数,包含无法可靠回显的畸形请求;不含决定窗口内同点合法激活的静默丢弃 |
| `rejected` | 到达模块的接收校验、实例忙、失配及未选择等拒绝次数,包含时间否定;不含决定期间同点激活或过期激活的静默丢弃,也不含Receiver已拒绝的截短 |
| `app_rejected` | 应用Reject或动作NOT_STARTED次数,与接收侧rejected分开 |
| `busy_rejected` | 因事务占用而拒绝的新激活次数;其他点同样计入,决定窗口内同点合法激活的静默丢弃不计入 |
| `busy_rejected` | 通过时效和点策略后因事务占用而拒绝的新激活次数;不存在的点、点类型/模式拒绝及决定窗口内同点静默丢弃不计入 |
| `overloads` | 独立应答池满或新事务/事件身份序号耗尽的过载次数;决定窗口内同点合法激活的静默丢弃不计入 |
| `dropped_requests` | 过载丢新,以及SELECT_WAIT/EXECUTE_WAIT内同generation、station_id及IOA的合法激活静默丢弃次数;后者可单独增加,不通过覆盖旧事务腾空间 |
| `cancelled` | CANCELLED、STOPPED、DISCONNECTED、CONNECTION_CHANGED、SEND_ERROR、SERVICE_ERROR导致的事务结束总数;不证明设备无副作用 |
| `deactivated` / `mismatches` | DEACTIVATED成功撤销 / MISMATCH终结的事务数 |
| `select_timeouts` / `decision_timeouts` / `execute_timeouts` | 对应期限导致的结束数;时钟回退也走相应到期路径 |
| `clock_errors` | 等待阶段发现单调时钟倒退次数;必要确认资源失败时最终原因可能为SERVICE_ERROR |
| `clock_errors` | 事务期限或时间基准检查发现单调时钟倒退的次数;同一请求可能触发两处检查并分别计数,不能视为异常请求数;必要确认资源失败时最终原因可能为SERVICE_ERROR |
| `time_expired` | unsigned long,达到过期时差上限的激活数,含静默丢弃和配置为否定两种情况 |
| `time_rejected` | unsigned long,IV/UTC换算失败、无基准、推算越界/时钟倒退或时标超前而否定的激活数;不含time_expired |
<a id="command-api-lifecycle"></a>
@@ -2832,6 +2897,9 @@ IEC104_Command_t *IEC104_Command_Create(IEC104_Slave_t *slave,
const IEC104_CommandConfig_t *config);
bool IEC104_Command_Destroy(IEC104_Command_t *self);
void IEC104_Command_Cancel(IEC104_Command_t *self);
bool IEC104_Command_Set_Time_Base(IEC104_Command_t *self,
uint64_t utc_ms, uint64_t base_ms);
void IEC104_Command_Clear_Time_Base(IEC104_Command_t *self);
IEC104_CommandEventResult_t IEC104_Command_Handle_Event(IEC104_Command_t *self,
const IEC104_SlaveEvent_t *event);
bool IEC104_Command_Process(IEC104_Command_t *self);
@@ -1,36 +1,71 @@
# IEC104 遥控模块设计
# IEC104 遥控、遥调与带时标命令设计
`104/business/IEC104_Command.c/h` 提供 Type 45 单命令和 Type 46 双命令业务。业务入口为 Slave,实例使用静态池,不动态分配内存、不保存应用回调;所有入口与所属 Slave 在同一事件循环中串行调用。一个 Slave 至多绑定一个 Command 实例,一个实例至多保留一个遥控事务。其他命令类型返回 IGNORED,交给后续业务或未知类型兜底。
`104/business/IEC104_Command.c/h`统一提供Type 45–51及58–64这14种控制命令。业务入口为Slave,实例使用静态池,不动态分配内存、不保存应用回调;所有入口与所属Slave在同一事件循环中串行调用。一个Slave至多绑定一个Command实例,一个实例至多保留一个命令事务。默认全局启用14种类型,但点表为空,不执行任何点;应用提供点表并可通过type_mask进一步裁剪。全局未启用或范围外的类型返回IGNORED,交给后续业务或未知类型兜底。
## 1. 职责与默认配置
模块管理请求校验、选择与执行匹配、撤销、三类期限、应答排序、事务身份及 FINISHED。应用管理点表、权限、闭锁、返校和设备输出,显式报告设备结果并构造监视方向返回信息。协议结束不代表物理输出已经停止。
模块管理请求校验、按点类型与模式准入、选择与执行匹配、撤销、三类期限、应答排序、事务身份及FINISHED。应用提供只读命令点表,管理权限、闭锁、限值、返校和设备输出,显式报告设备结果并构造监视方向返回信息。协议结束不代表物理输出已经停止。
| 配置 | 默认值 | 含义 |
|---|---|---|
| type_mask | Type 45、46 | 其他类型位在第一版配置中非法 |
| select_required_mask | Type 45、46 | 清除对应位允许直接执行;必须是 type_mask 的子集 |
| type_mask | 0x3FFF,全部14种类型 | 全局类型准入;非零,仅45–51及58–64,bit14/15必须为0 |
| points / point_count | NULL / 0 | 借用有序只读点表及元素数;空表合法,但没有可执行点 |
| decision_timeout_ms | 15000 | 工作事件生成至应用决定;0 禁用 |
| select_timeout_ms | 30000 | 选择生效至匹配执行被模块受理;不得为0 |
| select_timeout_ms | 30000 | 每实例统一的选择有效期,仅用于选择执行模式;不得为0,详见第4节 |
| execute_timeout_ms | 60000 | 执行动作返回 STARTED 至 Report_Result;0 禁用 |
| send_act_term | true | 成功执行后发送 ACT_TERM |
| act_term_on_failure | false | 显式启用时也发送失败执行的终止;send_act_term=false 时忽略 |
| time_tag_max_delay_ms | 10000 | 带时标激活的时差上限;0只关闭时差检查,仍拒绝IV及UTC换算失败 |
| master_utc_offset_minutes | 480 | 主站时标标准时区,范围−1439..1439分钟;SU由公共时间工具处理 |
| negative_on_expired | false | 过期激活默认静默丢弃;true时回否定ACT_CON |
TEST=1 不执行动作,返回否定确认。广播 CA=65535 不接受。失败终止可选策略发送原执行请求的 COT=10、P/N=0,表示执行过程终止,不表示设备执行成功;结果由应用监视信息和 FINISHED(EXECUTION_FAILED) 表达。该策略为本模块接入约定,默认不开启。
### 1.1 按CA/IOA配置命令点
`IEC104_CommandPoint_t`包含common_address、ioa、type_mask及mode。每个CA/IOA仅一行;同一点的多个允许类型放入同一掩码,并共享执行模式。`IEC104_COMMAND_MODE_DIRECT=1`表示只接收直接执行,SELECT回否定7;`IEC104_COMMAND_MODE_SELECT_EXECUTE=2`表示必须先选择,未选择的EXECUTE回否定7。51/64没有S/E,只能属于DIRECT点。
Create要求点表按CA再IOA严格升序且唯一,使用二分查找;CA范围1..65534,IOA范围1..0xFFFFFF,点type_mask非零且仅含14个已知类型位,mode仅允许上述两值。点掩码可以包含全局未启用的已知类型位,实际允许集合取两者交集;SELECT_EXECUTE点不得包含51/64,即使全局当前未启用它们也不允许。
points=NULL与point_count=0必须成对,非NULL空表、NULL非空表及元素数超过SIZE_MAX/sizeof(Point)均拒绝。空表可以创建实例,随后走到点策略检查的新激活因无点回47。允许先为尚未注册的CA配置点;受理时仍按Slave注册表核对实际站点身份,点表不代替站点注册。
Create按值复制Config,但仅借用点表,不复制表、不保存查询回调。应用须使表存储从Create至Destroy持续有效且只读,不得在原地改类型、模式或排序。更新须先Cancel、读完FINISHED并完成应用收尾,关闭Slave并读空生命周期通知后Destroy,再以新表Create并重新启动服务、建立连接,完整分派CONNECTED/ACTIVATED。新实例不继承旧会话或时间基准,必须重新设置可信基准;仅Destroy/Create不能挂回仍运行的旧连接实现热更新。表内不保存设备运行值。旧配置的select_required_mask已移除,调用方须迁移为具体点表并全部重新编译。
## 2. 请求校验和选择匹配
45/46 的104原文均为10字节:6字节公共头、3字节IOA、1字节SCO/DCO。唯一完整对象存在后,错误优先级为 COT、CA、IOA、布局/值、事务条件。COT非6/8回45,CA未注册或广播回46,IOA=0回47。对象数不是1或对象截短只计数丢弃;SQ、额外尾字节、P/N、TEST、SCO保留位和DCS非法值回否定7/9,回显规范为VSQ=1、10字节。已识别的拒绝和丢弃均返回HANDLED。
标准104采用2/2/3参数,6字节公共头后接3字节IOA;命令元素从`IEC104_ASDU_HEADER_LEN + 3U`开始。规范ASDU长度由类型决定:
只有SELECT_WAIT、EXECUTE_WAIT两个决定阶段,对同会话generation、同station_id、同IOA且通过全部现有校验的COT=6激活静默丢弃,返回HANDLED。是否已被应用取走工作事件不影响此规则;不比较类型、OA、S/E、状态或QU,但新请求仍须通过type_mask、COT、寻址、格式和值校验。只增加dropped_requests,不增加rejected、busy_rejected或overloads;不覆盖原请求、不刷新决定期限、不生成新事件或应答。先检查到期,已到期事务不再适用此规则;其他点仍忙拒绝,COT=8仍按撤销处理。
| 类型(无时标/带时标) | 命令元素 | ASDU长度 | 选择与校验 |
|---|---|---|---|
| 45 / 58 | SCO | 10 / 17 | 有S/E;保留位必须为0 |
| 46 / 59 | DCO | 10 / 17 | 有S/E;DCS只能为1、2 |
| 47 / 60 | RCO | 10 / 17 | 有S/E;RCS为1降一步、2升一步 |
| 48 / 61 | NVA + QOS | 12 / 19 | 有S/E;原始有符号16位定点值 |
| 49 / 62 | SVA + QOS | 12 / 19 | 有S/E;有符号16位标度值 |
| 50 / 63 | R32 + QOS | 14 / 21 | 有S/E;拒绝NaN及正负Inf |
| 51 / 64 | BSI | 13 / 20 | 无S/E,只能直接执行 |
SELECTED阶段的选择执行要求同会话、原站点注册编号、IOA、类型、OA,且SCO/DCO屏蔽S/E后相同。此阶段选择确认尚未交接时,执行按忙拒绝并保留选择;确认交接后同点不匹配执行终结为MISMATCH,否定回显本次执行。其他点的新激活只忙拒绝。匹配前检查选择期限,到期选择不能复活。
配置内请求先检查当前事务到期,再检查唯一完整对象。COT非6/8回45,CA未注册或广播回46,IOA=0回47;之后检查SQ、规范长度、P/N、TEST、命令值及CP56格式,错误回否定7/9。公共解析器负责SCO/DCO/RCO及CP56校验;QU/QL的业务含义交应用解释。对象数量不符、或到达模块的原文不足规范长度时计rejected并丢弃,不能补零伪造对象。线上已知类型的截短通常更早由Receiver拒绝,不会到达Command,不能据此期待Command计数增加或连接继续。
撤销按会话、站点编号、IOA、类型、OA匹配,不比较状态。SELECT_WAIT、SELECTED、EXECUTE_WAIT中的匹配撤销复用事务存储,肯定DEACT_CON回显撤销请求。执行已经启动、事务已结束或没有匹配时否定撤销。发送接口已经返回OK的选择确认不能撤回,随后DEACT_CON按顺序交接。
过长请求只回显规范长度前缀,VSQ规范为1;除VSQ、COT和P/N外保留原文字节,包括非法值。回显不重新构造合法对象。已识别的拒绝和丢弃均返回HANDLED。
只有SELECT_WAIT、EXECUTE_WAIT两个决定阶段,对同会话generation、同station_id、同IOA且通过前置报文校验的COT=6激活静默丢弃,返回HANDLED。是否已被应用取走工作事件不影响此规则;不比较类型、OA、S/E、状态或QU,但新请求仍须通过全局type_mask、COT、寻址、格式和值校验,此时尚不查点策略或时差。只增加dropped_requests,不增加rejected、busy_rejected或overloads;不覆盖原请求、不刷新决定期限、不生成新事件或应答。先检查到期,已到期事务不再适用此规则;其他点通过时效及点策略后才忙拒绝,COT=8仍按撤销处理。
决定期间同点激活的丢弃在时标判定之前;其他带时标激活在SELECTED匹配和忙判断之前执行第8节的时效检查。这样匹配的过期执行不会绕过检查,忙时其他点的过期请求也不会产生迟到的忙否定。
其后的顺序是SELECTED匹配→查点/点类型/直接点SELECT校验→实例忙或SELECT_EXECUTE点未选择执行→新建事务。走到点策略检查时,无CA/IOA点回47;类型不在该点掩码内、DIRECT点收到SELECT回否定7;空闲SELECT_EXECUTE点未选择便执行也回否定7。拒绝的新请求不建事务、不出工作事件或FINISHED,不改变原事务。点策略在忙检查之前,所以不存在的其他点不会被统一改为忙否定。撤销仍走前面的事务匹配,不因点表查询改变原有9应答。
SELECTED阶段的选择执行要求同会话、原站点注册编号、IOA、类型、OA,且值字节及屏蔽S/E后的命令字节相同。状态、QU/QL均参与比较,CP56不比较,允许选择和执行使用不同时标;45与58等跨类型组合仍为MISMATCH,不被后续点类型拒绝替代。浮点按原始字节比较,+0与−0不同。此阶段选择确认尚未交接时,通过时效及点策略检查的执行按忙拒绝并保留选择;确认交接后同点不匹配执行终结为MISMATCH,否定回显本次执行。匹配执行不重新查不可变点表,许可和模式已在选择时校验,类型完全一致保证沿用原策略。其他点的新激活先检查点策略,再忙拒绝。匹配前检查选择期限,到期选择不能复活。
撤销按会话、站点编号、IOA、类型、OA匹配,不比较值、状态或限定词,不判时差;带时标撤销仍须通过CP56格式检查。SELECT_WAIT、SELECTED、EXECUTE_WAIT中的匹配撤销复用事务存储,肯定DEACT_CON回显撤销请求。执行已经启动、事务已结束或没有匹配时否定撤销。发送接口已经返回OK的选择确认不能撤回,随后DEACT_CON按顺序交接。
## 3. 应用交互与设备结果
事件为纯值副本,身份包含模块实例、事务、工作事件序号和类型。Accept/Reject只接受已经取出且未消费的工作事件。模块使用自己保存的请求调用动作;调用者修改事件副本不能改变执行参数。
事件为纯值副本,身份包含模块实例、事务、工作事件序号和类型。Accept/Reject只接受已经取出且未消费的工作事件。模块每次从已保存的规范原文解码请求;调用者修改事件副本不能改变执行参数。
状态类45–47及对应时标类型使用state和QU;48–50使用`value.normalized/scaled/short_float`及QL,state为0;51/64使用`value.bitstring`,state、qualifier及is_select均为0。normalized保留16位定点原始值,实际值为normalized/32768.0。带时标请求另含原始CP56(保留IV/SU)及换算后的UTC毫秒,非时标请求相关字段清零。毫秒值包含CP56的毫秒余数,不能只把整秒乘1000。
保存的原文再次解码失败属于内部错误。Accept在安排肯定确认和消费事件之前解码,失败按SERVICE_ERROR收尾且不调用动作;NextEvent失败不把未交付工作事件标为已读,已交付事务仍保留FINISHED义务。若终结时原文仍不可解码,只交付事务身份及SERVICE_ERROR,不把值字段当作动作参数;应用仍可读取终结事件并释放资源,避免坏原文永久占槽。
| 入口 | 成功后的含义 |
|---|---|
@@ -50,9 +85,17 @@ Get_Transaction仅查询当前尚占槽的事务:ENDED仍可查询active=false
状态依次为SELECT_WAIT、SELECTED、EXECUTE_WAIT、EXECUTING、ENDED;直接执行跳过选择阶段。EXECUTING内部另记录结果是否已报告、返回阶段是否关闭和确认/终止交接状态,避免重复动作、覆盖结果及重复终止。
期限采用Net单调毫秒;已用时间大于或等于期限即到期,时钟倒退按到期并计clock_errors。期限配置为0只禁用时长限制,正在决定、选择或等待设备结果时仍检查单调时钟倒退。决定期限从工作事件生成开始,包含事件尚未被应用取走的时间;选择和执行期限从动作返回后重新采样开始。这里的请求到达指Handle_Event受理时刻,包含Slave通知排队时间。
期限采用Net单调毫秒;已用时间大于或等于期限即到期,时钟倒退按到期并计clock_errors。决定和执行期限配置为0只禁用时长限制,正在决定、选择或等待设备结果时仍检查单调时钟倒退;选择期限不得为0。决定期限从工作事件生成开始,包含事件尚未被应用取走的时间;选择和执行期限从相应动作返回STARTED后重新采样开始。这里的请求到达指Handle_Event受理时刻,包含Slave通知排队时间。
Accept/Reject消费交互后停止决定期限;Report_Result后停止执行期限。选择期限有意覆盖选择确认的发送等待,作为返校保持上限。执行超时不发ACT_TERM,已经执行动作所对应的肯定确认仍可继续提交;随后STOP或Cancel可以清除尚未提交的应答。结果报告后没有额外业务收尾期限,应用漏调Complete会持续占槽,直到明确取消或会话结束。
`select_timeout_ms`是每个Command实例统一的选择有效期,仅用于SELECT_EXECUTE点,不是每点独立参数。默认30000ms,应用可在Config_Default之后、Create之前修改为所需非零值。它是选择保持的最长时间,不是必须等满的延时;主站收到选择确认后即可发送执行。
选择动作返回STARTED后进入SELECTED,并重新采样单调时钟开始计时;NULL选择动作同样计时。选择确认尚在等待发送时,选择有效期已经消耗,不以主站看到确认作为起点。Handle_Event在期限内受理匹配执行后转入EXECUTE_WAIT,停止选择计时,并从新的EXECUTE工作事件开始应用decision_timeout_ms,等待Accept/Reject。Handle_Event受理执行时若恰好到达选择期限边界,原选择也已失效,以SELECT_TIMEOUT结束,不能被迟到执行恢复。
选择超时后,须待应交付的FINISHED读完且事务回复完成交接或清理,才能回收事务槽。选择保持及收尾期间,其他点的新激活通过前序报文、时标和点策略校验后仍按忙拒绝。DIRECT点没有选择等待,但仍共享同一个事务槽,不能绕过已有选择或执行事务。
30秒可作为联调起点。主站自动紧接着发送执行时,可考虑10秒;需要人工确认时,可考虑30秒。这些是工程配置建议,应结合主站选择与执行的实际间隔、网络往返、选择确认发送排队及事件循环调度余量联调确定,不代表协议规定值。选择有效期与time_tag_max_delay_ms独立:前者限制已选择事务的保持时间,后者检查带时标激活受理时的新鲜度;二者也都不替代链路t0..t3。
Accept/Reject消费交互后停止决定期限;Report_Result后停止执行期限。执行超时不发ACT_TERM,已经执行动作所对应的肯定确认仍可继续提交;随后STOP或Cancel可以清除尚未提交的应答。结果报告后没有额外业务收尾期限,应用漏调Complete会持续占槽,直到明确取消或会话结束。
## 5. 通知、FIFO与返回信息
@@ -76,12 +119,41 @@ Handle_Event先校验来源并按Slave的FIFO消费连接/启停通知,不因
退出顺序:Command_Cancel,读空FINISHED并清理应用引用,Slave_Close,读空并分派Slave通知,Command_Destroy,移除站点,Slave_Destroy。动作回调只允许Get_Status/Get_Transaction,不得重入修改入口、推进网络或发布上报。
## 7. 固定存储、验证与扩展
## 7. 布局、固定存储与验证
私有`COMMAND_LAYOUTS(X)`是唯一类型布局清单,记录type_id、value_length、has_se、has_time。X-macro分别展开只读布局表和字节数组union,规范长度按“9 + 值长度 + S/E字节数 + CP56长度”生成;`COMMAND_MAX_LENGTH`取该union的sizeof,目前为21字节,并静态检查不超过104的249字节ASDU上限。这是本模块14种控制命令的容量上界,不代表所有ASDU或其他业务的存储模型。
事务request_bytes、reply_bytes、独立回复槽及提交局部缓冲统一使用该容量,持久记录另存有效长度。复制、解析、包装和发送只使用规范有效长度,不能把sizeof(buffer)当作报文长度。原工作请求与撤销/失配回显分别保存,长短槽复用不会发送旧尾部。
S/E偏移只由`Se_Offset`计算,读取只能在已确认has_se的`Is_Select`或`Same_Command`中发生。51没有该字节,64相同偏移是CP56首字节,不能先读再判断。`Decode_Element`调用公共层公开的类型解码接口,不依赖内部信息对象布局表。布局、阶段支持掩码和实例type_mask分别表达“认识”“允许启用”和“本实例启用”,互不替代。
`IEC104_COMMAND_CAPACITY`默认跟随TCP服务容量;每实例一个事务槽,`IEC104_COMMAND_REPLY_CAPACITY`默认4。事务必要应答不依赖回复池剩余容量。池满只拒绝/丢新,不覆盖活动事务,不主动断开连接。Process每轮推进次数有界。实际sizeof及配置矩阵结果统一记录在[测试说明](../test/IEC104测试文档.md),不把宿主机结果当作其他目标的预算。
示例地址、动作与返回信息见[子站示例](../examples/IEC104子站示例.md)。Type45命令6001..6008对应Type1遥信1001..1008;Type46命令6101..6108对应Type3位置1101..1108,双点初值均为1。两类遥信均纳入示例站总召及遥信分组,逐批读取应用当前值,能反映遥控执行后的状态;点表及分组属于应用,不改变Command模块职责。
示例地址、动作与返回信息见[子站示例](../examples/IEC104子站示例.md)。45/58命令6001..6008对应Type1遥信1001..1008;46/59命令6101..6108对应Type3位置1101..1108,双点初值均为1;49/62命令6301..6308写入Type11遥测2001..2008。应用持有24行命令点表,6008、6108、6308采用DIRECT,其余SELECT_EXECUTE,CA来自命令行配置。总召及对应分组逐批读取应用当前值,反映执行后的状态;命令许可和模式由模块按该表执行,物理点映射、限值及召唤分组仍属于应用。
验证覆盖TCP容量1/2与回复容量1/4、期限边界、动作只调用一次、通知优先、FIFO、部分写入、结果先于确认、撤销及站点引用,并使用真实本机TCP核对45/46流程。决定窗口内的同点激活丢弃不依赖内容相同,也不提供窗口外去重;事务释放后,相同ASDU按新请求处理,允许直接执行时仍可能再次输出。
验证覆盖TCP容量1/2与回复容量1/4、独立手写的14种类型长度、值与时标校验、匹配、变长FIFO、期限边界、动作只调用一次、通知优先、部分写入、结果先于确认、撤销及站点引用,并使用真实本机TCP核对遥控、设定值、带时标执行和过期丢弃。测试期望不共用实现布局清单,避免表错时输入和期望一起错。决定窗口内的同点激活丢弃不依赖内容相同,也不提供窗口外去重;事务释放后,相同ASDU按新请求处理,允许直接执行时仍可能再次输出。
带时标58/59、遥调及并发不属于第一版。后续扩展需保持事件身份、引用与交接契约;结构体新增字段要求调用方一起重新编译,不承诺二进制布局兼容。
扩展仍须保持事件身份、引用与交接契约;公开Request、Event、Config、Status新增字段后,库及调用方须一起重新编译,不承诺二进制布局兼容。新增类型需同步清单、解码分支和独立测试;新值类型或掩码范围外类型还涉及公开接口。容量会随清单最大长度增长,须重新测量目标内存;这里不提供跨业务统一存储管理,也不实现并发事务、Type70/102/105/107或突发上送。
## 8. 带时标激活与时间基准
应用调用`IEC104_Command_Set_Time_Base(self, utc_ms, base_ms)`提供同一时刻的UTC纪元毫秒和Net单调毫秒。句柄无效、动作回调内调用、UTC超出公共时间工具范围或base_ms晚于当前单调时间时返回false;失败不替换原基准。`Clear_Time_Base`在本站时间不可信时清除基准,动作回调内调用不生效。模块不读取Clock,不保存时间回调;STOP和断线保留基准,更新只影响随后受理的请求。
判定仅针对58–64的COT=6激活,在Handle_Event受理时做一次。公共CP56格式错误已经按请求校验回否定;格式合法后先检查IV及UTC换算,随后才应用max=`time_tag_max_delay_ms`:
| 条件 | 处理与计数 |
|---|---|
| IV=1或UTC换算失败 | 否定7,time_rejected与rejected加一;max=0仍拒绝 |
| max=0 | 不比较时差,不要求时间基准 |
| max非零且无基准、now早于base_ms、推算溢出或超出时间范围 | 否定7,time_rejected与rejected加一;单调时钟倒退另计clock_errors |
| 站内UTC − 命令UTC ≥ max | time_expired加一;默认静默丢弃,negative_on_expired=true时回否定7并计rejected |
| 命令UTC − 站内UTC ≥ max | 否定7,time_rejected与rejected加一 |
| 其他 | 继续匹配选择、忙检查或创建事务 |
站内UTC由`utc_ms + (now - base_ms)`推算,先检查回退和加法范围。CP56按master_utc_offset_minutes换算,SU由公共工具处理,并加回毫秒余数。静默过期不计rejected或dropped_requests,不占事务槽、不生成工作事件或FINISHED;time_base_valid仅表示持有基准,并非每次查询都重新验证其时效。
clock_errors按发现异常的检查次数累计,事务期限检查和时间基准检查可能在同一请求中分别增加,不能把它当作异常请求数量。
执行请求过期时不改变已有选择,它继续保留至select_timeout_ms。撤销不做时差、IV或UTC换算判定,但仍检查CP56编码格式。决定期间同点激活先按原规则丢弃,不增加时间计数。默认静默丢弃避免迟到否定被主站误认为重试结果;max、IV策略、时区、过期响应和ACT_TERM应与主站互操作约定一致。
时效只在受理时检查,Accept前不再检查命令时标;受理后的等待由决定期限控制。默认10秒时差窗口加15秒决定期限,动作启动时命令可能已接近25秒旧。需要更严格限制时,应用可在动作中检查request.time_utc_ms并返回NOT_STARTED;这不是模块承诺的动作时效上限。
@@ -128,7 +128,7 @@ flowchart TD
| `IEC104_APP_LAYER_PARAMETERS_DEFAULT` | 定义在 [IEC104_Types.h](../../104/IEC104_Types.h):Type ID 1、VSQ 1、COT 2、默认 OA 0、CA 2、IOA 3、ASDU 最大 249 字节 |
| 静态存储容量 | 编译期由 [IEC104_TcpServer_Config.h](../../104/IEC104_TcpServer_Config.h) 配置,默认可创建 1 个服务。与上述运行参数分开 |
业务模块有各自的Config_Default/Create,不能把业务期限填进Slave的链路配置。遥控类型掩码、三个期限、ACT_TERM策略及编译期实例/应答池容量见[遥控创建与配置](#flow-command-create);这些期限须由应用与网络等待条件合并,Slave_Prepare不会自动读取业务模块状态。
业务模块有各自的Config_Default/Create,不能把业务期限填进Slave的链路配置。命令点表、全局类型掩码、三个期限、ACT_TERM策略及编译期实例/应答池容量见[遥控创建与配置](#flow-command-create);这些期限须由应用与网络等待条件合并,Slave_Prepare不会自动读取业务模块状态。
104 专用的默认参数宏保留在 104 接入层。共用 `IEC60870_AppLayerParameters_t` 只描述布局,不替调用方选择传输配置。业务一般通过 `IEC104_Slave_Get_Application_Layer_Parameters` 或事件连接的对应 getter 取得标准 104 参数,无需自行维护宏副本。
@@ -290,41 +290,61 @@ Execute结果须按枚举分支:WAIT先处理Slave通知,EMPTY只表示没
<a id="flow-command"></a>
### 1.5 接入单点、双点遥控模块
### 1.5 接入遥控、遥调与带时标命令模块
`IEC104_Command`接手Type45单点命令、Type46双点命令,提供选择、执行、撤销、期限和返回信息顺序管理。每个Slave至多一个实例,每实例至多一个未释放事务;不同站点的遥控也互斥。点表、闭锁、远方/就地、真实输出及设备忙状态由应用提供。模块使用静态池,不分配内存、不保存回调,只在Accept中调用本次传入的动作。接口声明与全部返回值见[API第25节](IEC104_API接口汇总.md#file-25),协议校验及内部存储见[遥控设计](IEC104_Command_设计架构.md)。
`IEC104_Command`接手Type45–51及58–64,提供按点准入、选择、执行、撤销、时标判定、期限和返回信息顺序管理。默认全局启用14种类型,但点表为空,必须提供具体点才能执行。每个Slave至多一个实例,每实例至多一个未释放事务;不同站点的命令也互斥。应用提供有序只读点表,按CA/IOA配置类型和模式,管理闭锁、远方/就地、限值、真实输出及设备忙状态。模块使用静态池,不分配内存、不保存回调,只在Accept中调用本次传入的动作。接口声明与全部返回值见[API第25节](IEC104_API接口汇总.md#file-25),协议校验及内部存储见[遥控设计](IEC104_Command_设计架构.md)。
<a id="flow-command-create"></a>
#### 1.5.1 创建实例与配置
编译`104/business/IEC104_Command.c`及Slave依赖,头文件路径包含`104/`、`104/business/`和`common/`。Slave句柄创建成功后即可创建遥控实例,允许在Slave_Init之前或之后,但必须早于第一次消费Slave通知。否则模块会漏掉CONNECTED/ACTIVATED,不能靠最终连接状态补出历史启停顺序。站点由Slave统一注册,CA不放进Command配置;当前不接受广播CA=65535。
编译`104/business/IEC104_Command.c`及Slave依赖,头文件路径包含`104/`、`104/business/`和`common/`。Slave句柄创建成功后即可创建遥控实例,允许在Slave_Init之前或之后,但必须早于第一次消费Slave通知。否则模块会漏掉CONNECTED/ACTIVATED,不能靠最终连接状态补出历史启停顺序。站点由Slave统一注册,Command点表按CA引用;允许先配置尚未注册的CA,实际请求仍检查注册身份,不接受广播CA=65535。
以下C函数只负责创建默认实例。调用方须检查返回NULL、清理已创建资源;成功后把句柄保存到所属事件循环,Slave寿命须覆盖Command_Destroy。
以下C函数为CA=1配置两个同类型、不同模式的点;静态const点表保证借用寿命。调用方须注册对应站点、检查返回NULL并清理已创建资源;成功后把句柄保存到所属事件循环,Slave及点表的寿命须覆盖Command_Destroy。
```c
#include "IEC104_Command.h"
static IEC104_Command_t *Create_Command(IEC104_Slave_t *slave)
{
static const IEC104_CommandPoint_t points[] = {
{1U, 6001U, IEC104_COMMAND_TYPE_BIT(45U) | IEC104_COMMAND_TYPE_BIT(58U),
IEC104_COMMAND_MODE_SELECT_EXECUTE},
{1U, 6008U, IEC104_COMMAND_TYPE_BIT(45U) | IEC104_COMMAND_TYPE_BIT(58U),
IEC104_COMMAND_MODE_DIRECT}
};
IEC104_CommandConfig_t config;
IEC104_Command_Config_Default(&config);
config.type_mask = IEC104_COMMAND_TYPE_BIT(45U) | IEC104_COMMAND_TYPE_BIT(58U);
config.points = points;
config.point_count = sizeof(points) / sizeof(points[0]);
return IEC104_Command_Create(slave, &config);
}
```
| 配置 | 默认行为 | 修改时的约束 |
|---|---|---|
| type_mask | 接手45、46 | 不得为0;本版其他类型位使Create失败,未启用类型返回IGNORED |
| select_required_mask | 45、46均须先选择 | 必须为type_mask子集;只清除允许直接执行类型的对应位 |
| type_mask | 0x3FFF,接手45–51/58–64 | 不得为0;可裁剪,保留bit14/15不能置位,未启用类型返回IGNORED |
| points / point_count | NULL / 0,空点表 | NULL与0必须成对;非空表按CA再IOA严格升序且唯一,Create借用至Destroy |
| decision_timeout_ms | 15000ms | 从工作事件生成开始计时;0只禁用时长限制 |
| select_timeout_ms | 30000ms | 不得为0;含选择确认等待发送的时间 |
| select_timeout_ms | 30000ms | 每实例统一、仅选择模式;不得为0,在Config_Default后、Create前调整,详见[选择有效期](IEC104_Command_设计架构.md#4-状态与期限) |
| execute_timeout_ms | 60000ms | 从执行动作返回STARTED至Report_Result;0只禁用时长限制 |
| send_act_term | true | false时成功、失败都不发ACT_TERM,仍须Complete |
| act_term_on_failure | false | 仅send_act_term=true时生效;失败终止的P/N仍为0,只表示流程结束 |
| time_tag_max_delay_ms | 10000ms | 0只关闭时差检查,仍拒绝IV及UTC换算失败 |
| master_utc_offset_minutes | 480 | 主站标准时区分钟数,范围−1439..1439;SU由公共时间工具处理 |
| negative_on_expired | false | 过期激活静默丢弃;true改为否定确认 |
静态池容量由编译宏`IEC104_COMMAND_CAPACITY`控制,默认与TCP服务容量相同;每实例独立应答槽由`IEC104_COMMAND_REPLY_CAPACITY`控制,默认4。事务自带确认、终止及撤销/失配应答存储,独立槽处理忙拒绝和校验否定,不能用增加应答槽开启多个事务。重复绑定同一Slave、池满或配置非法时Create返回NULL。
每点设置common_address、ioa、type_mask和mode:CA为1..65534,IOA为1..0xFFFFFF,点掩码非零且只含14个已知类型位;可包含全局未启用位,实际取交集。mode仅DIRECT或SELECT_EXECUTE;51/64不能放入SELECT_EXECUTE点。NULL/0是合法默认空表,但不会执行点命令。非NULL/0、NULL/非零、点表无序/重复、非法范围及元素数乘法越界都会使Create失败。
点表是借用存储,Create不复制、不保存查点回调;从Create到Destroy不可修改或释放。更新时先Cancel、处理FINISHED并完成应用收尾,Slave_Close后读空并分派旧通知,再Destroy;以新表Create后重新启动Slave、建立连接,按FIFO交付完整CONNECTED/ACTIVATED,并重新设置时间基准。新Command不继承旧会话,不能直接挂回旧连接热换点表。原select_required_mask字段已移除,旧调用方须改为点表并重新编译。全局type_mask仍可裁剪;示例用赋值明确选择已接入的六种类型,不能用`|=`裁剪全类型默认值。
启用带时标类型后,由应用把可信UTC纪元毫秒与同一时刻的Net单调毫秒交给`IEC104_Command_Set_Time_Base`。校时动作成功时先在应用保存request->utc_ms/base_ms,待`ClockSynchronization_Execute_Next`返回后再更新Command;不要在动作回调内跨模块调用修改入口。时间不可信时调用`Clear_Time_Base`,STOP/断线不会自动清除基准。示例system模式还在启动及每60秒读取一次Clock以吸收系统时间调整,simulated模式只使用成功校时产生的基准。
时标检查在Handle_Event受理激活时完成,早于SELECTED匹配及忙判断;IV、换算错误、无基准或超前回否定,过期默认不生成应答和事件。选择通过而执行过期时,原选择保留至选择期限。max=0不需要基准,但仍校验CP56、IV及UTC换算。该检查不在Accept前重做,默认受理窗口与决定期限可能使动作开始时的命令年龄接近25秒;更严格的现场策略可在动作中检查request.time_utc_ms。完整边界见[时间基准API](IEC104_API接口汇总.md#command-api-time-base)。
<a id="flow-command-dispatch"></a>
#### 1.5.2 先分派Slave通知,再读取遥控事件
@@ -333,7 +353,7 @@ static IEC104_Command_t *Create_Command(IEC104_Slave_t *slave)
| Command_Handle_Event结果 | 应用下一步 |
|---|---|
| EVENT_HANDLED | 此请求已由遥控接手;包括校验否定、忙拒绝、过载丢弃及决定窗口内同点激活的静默丢弃,不能再次兜底 |
| EVENT_HANDLED | 此请求已由遥控接手;包括校验否定、忙拒绝、过载丢弃、决定窗口内同点激活及过期激活的静默丢弃,不能再次兜底 |
| EVENT_IGNORED | I帧可继续交给后续业务;未启用类型、未激活、视图不可用或旧代次可能走此结果 |
| EVENT_ERROR | 记录来源、参数或服务错误并进入应用错误处理;不要把它当IGNORED继续执行命令 |
@@ -347,11 +367,13 @@ Handle_Event消费当前通知时只校验来源,不能在外层用`Check_Requ
#### 1.5.3 选择、执行匹配及应用决定
空闲收到S/E=1、COT=6,模块生成SELECT并进入SELECT_WAIT;S/E=0只有在该类型允许直接执行时才生成EXECUTE,否则否定ACT_CON。应用取到工作事件后检查点表和策略,调用Accept或Reject。IOA不存在用`IEC104_COMMAND_REJECT_UNKNOWN_IOA`回47;其他策略拒绝用`IEC104_COMMAND_REJECT_NEGATIVE`回否定7。
新激活通过报文、时标及已有选择匹配后,模块按CA/IOA二分查点。无点回47,点类型不符或DIRECT点收到S/E=1回否定7;点策略通过后才检查实例忙和SELECT_EXECUTE点未选择执行。空闲SELECT_EXECUTE点的选择生成SELECT,匹配执行才生成EXECUTE;DIRECT点的执行直接生成EXECUTE。51/64没有S/E,只能配置为DIRECT。被拒的新请求不建事务、不出工作事件或FINISHED,已有事务继续。
SELECT_WAIT、EXECUTE_WAIT期间,同会话generation、同station_id、同IOA的合法COT=6激活静默丢弃,返回HANDLED;不比较类型、OA、S/E、状态或QU,但type_mask及全部既有寻址、格式和值校验仍优先。无论应用是否已取走原工作事件,都继续处理该事件:丢弃不覆盖原请求、不刷新决定期限、不生成新事件或应答,只增加dropped_requests,不增加rejected、busy_rejected或overloads。期限到达后先终结原事务,再处理新请求;其他点仍忙拒绝,COT=8仍按撤销规则处理。
应用按type_id读取state、QU/QL及value对应union成员,检查权限、闭锁、动态设备可用性和限值后调用Accept或Reject。静态点存在性已由模块检查;动态设备点不可用仍可用`IEC104_COMMAND_REJECT_UNKNOWN_IOA`回47,越限等策略拒绝用`IEC104_COMMAND_REJECT_NEGATIVE`回否定7。
Accept先确认工作事件仍有效并准备确认存储,再消费事件、至多调用一次action。SELECT可传NULL,视为选择成功且select_effect=false;EXECUTE必须提供动作。action须短时返回,不推进Slave、不发布上报、不保留request指针;回调内只允许查询本实例Get_Status/Get_Transaction。异步设备工作由应用另存必要值和引用,设备结果回到同一事件循环提交。
SELECT_WAIT、EXECUTE_WAIT期间,同会话generation、同station_id、同IOA的COT=6激活通过前置报文校验后静默丢弃,返回HANDLED;不比较类型、OA、S/E、状态或QU,全局type_mask及寻址、格式和值校验仍优先,此时尚不查点策略或时差。无论应用是否已取走原工作事件,都继续处理该事件:丢弃不覆盖原请求、不刷新决定期限、不生成新事件或应答,只增加dropped_requests,不增加rejected、busy_rejected或overloads。期限到达后先终结原事务,再处理新请求;其他点通过时效和点策略后才忙拒绝,COT=8仍按撤销规则处理。
Accept先确认工作事件仍有效并从保存的原文解码请求,再准备确认、消费事件、至多调用一次action。内部解码失败返回ERROR并按SERVICE_ERROR收尾,不发肯定确认或调用动作。SELECT可传NULL,视为选择成功且select_effect=false;EXECUTE必须提供动作。action须短时返回,不推进Slave、不发布上报、不保留request指针;回调内只允许查询本实例Get_Status/Get_Transaction,不允许Set/Clear_Time_Base。异步设备工作由应用另存必要值和引用,设备结果回到同一事件循环提交。
| 动作结果 | 选择阶段 | 执行阶段 |
|---|---|---|
@@ -361,7 +383,7 @@ Accept先确认工作事件仍有效并准备确认存储,再消费事件、
`Accept==REPLY_OK`只表示事件已消费,以上三种结果都可返回OK。应用应检查自己保存的动作结果,或用Get_Transaction确认`active && phase==IEC104_COMMAND_PHASE_EXECUTING`,再安排执行结果流程;提交确认时可能已遇STOP或发送失败,所以STARTED也不保证返回后事务仍有效。选中后等待新的EXECUTE事件,不能再次Accept旧SELECT事件。
SELECTED阶段选择确认交给发送层后、选择未到期时,执行须匹配原会话、station_id、IOA、类型、OA以及屏蔽S/E后的命令字节(状态与QU)。此阶段选择确认尚未交接时收到执行,一律忙拒绝并保留选择;交接后同一点不匹配执行回否定7并以MISMATCH结束选择。其他点执行、SELECTED中的重复选择及EXECUTING/ENDED中的新激活仍忙拒绝,不破坏已有事务。
SELECTED阶段选择确认交给发送层后、选择未到期时,通过时效检查的执行须匹配原会话、station_id、IOA、类型、OA、原始值字节,以及屏蔽S/E后的命令字节(状态与QU/QL);不比较CP56。+0与−0原始字节不同,45与58等类型不同,均先判MISMATCH,不被后续点策略拒绝替代。匹配执行不再查不可变点表。选择确认尚未交接时,通过时效和点策略的执行忙拒绝并保留选择;其他点执行、SELECTED中的重复选择及EXECUTING/ENDED中的新激活先过点策略再忙拒绝,不破坏已有事务。
| Accept / Reject结果 | 是否可重试 |
|---|---|
@@ -457,7 +479,7 @@ WAIT_FINISHED: 继续Process和NextEvent,不重复Complete,不再Submit_Retu
Handle_Event在有效遥控I帧的匹配前检查;Process、NextEvent及应用应答入口在允许推进时先检查会话,再检查期限。未读通知优先于这些推进,但不能挡住Handle_Event消费通知;已存在FINISHED直接交付。主站命令的“到达”指Handle_Event受理时刻,含Slave通知排队时间,不是网络成帧时间。
Accept/Reject后决定计时停止;Report_Result后执行计时停止,不因后续发送WAIT超时。选择期限有意包含选择确认的发送等待,限制返校最长保持时间。报告结果后漏调Complete会持续占槽,直到应用Cancel或会话结束;链路和本地发送期限仍独立生效,见[期限查询](#flow-deadlines)。
Accept/Reject后决定计时停止;Report_Result后执行计时停止,不因后续发送WAIT超时。选择有效期不是必须等满的延时,匹配执行受理后改由决定期限管理;完整计时、排队及选值说明见[状态与期限](IEC104_Command_设计架构.md#4-状态与期限)。报告结果后漏调Complete会持续占槽,直到应用Cancel或会话结束;链路和本地发送期限仍独立生效,见[期限查询](#flow-deadlines)。
<a id="flow-command-abort"></a>
@@ -759,7 +781,7 @@ IEC60870_Asdu_Get_Data(&event.asdu, &bytes, &length)
解析视图 `info` 借用参数和原文字节,二者在取对象期间必须有效且不变。`Get_Element` 复制出具体对象,但输出结构须由 Type ID 选择;尺寸足够不能代替正确的 C 类型。若随后改写事件的 COT/P/N,不能继续把旧解析视图当作更新后的公共头。
以上是自定义业务直接使用公共编解码器的流程。交给Command_Handle_Event的45/46由模块按协议错误优先级完成校验,应用不先按“完整解析失败就忽略”过滤它,也不另发ACT_CON;应用从SELECT/EXECUTE事件开始检查点表与策略,见[遥控通知分派](#flow-command-dispatch)。
以上是自定义业务直接使用公共编解码器的流程。交给Command_Handle_Event的45–51/58–64由模块按协议错误优先级及命令点表完成校验,应用不先按“完整解析失败就忽略”过滤它,也不另发ACT_CON;应用从SELECT/EXECUTE事件开始检查设备动态策略,见[遥控通知分派](#flow-command-dispatch)。
### 6.3 延迟处理、事件复制与连接寿命
@@ -1324,7 +1346,7 @@ Close 后读空事件,也可以再次 Init 重开;新服务和连接的身
Type100种类规则:104/business/IEC104_GeneralInterrogation.c
计数量读取/冻结/复位:104/business/IEC104_CounterInterrogation.c
Type103校时校验与显式动作:104/business/IEC104_ClockSynchronization.c
Type45/46选择执行、结果及返回信息:104/business/IEC104_Command.c
Type45–51/58–64选择执行、时标、结果及返回信息:104/business/IEC104_Command.c
共用发送接口分派: IEC60870_Slave.c → IEC104_Slave.c 内的发送适配器
ASDU 与信息对象: IEC60870_Asdu.c → IEC60870_InformationObjects.c
监听和网络调度: IEC104_TcpServer.c
@@ -1337,5 +1359,5 @@ ASDU 与信息对象: IEC60870_Asdu.c → IEC60870_InformationObjects.c
平台非阻塞 I/O: IEC104_Net_Windows.c
```
库提供命令、校时、总召、参数和文件等信息对象的编解码,以及响应提交接口。召唤引擎已实现站/分组总召和计数量读取/冻结/复位;ClockSynchronization已实现Type103校验、补偿、期限及原文确认;Command已实现Type45/46选择执行、撤销、三类期限和按事务提交返回信息。遥调47..51、带时标命令58..64、参数和文件业务尚未接入,不能把对象编解码支持等同于完整业务支持。业务权限和硬件操作仍由应用提供;Time转换、Clock墙上时间及Net单调计时用途不同,默认示例只更新模拟时钟和模拟遥控点值。
库提供命令、校时、总召、参数和文件等信息对象的编解码,以及响应提交接口。召唤引擎已实现站/分组总召和计数量读取/冻结/复位;ClockSynchronization已实现Type103校验、补偿、期限及原文确认;Command已实现Type45–51/58–64选择执行、撤销、值与时标判定、三类期限和按事务提交返回信息。参数和文件业务尚未接入,不能把对象编解码支持等同于完整业务支持。业务权限和硬件操作仍由应用提供;Time转换、Clock墙上时间及Net单调计时用途不同,默认示例只更新模拟时钟和模拟点值。
@@ -7,7 +7,7 @@
> **当前接口同步**:2026-10-08。已新增 `IEC104_Receiver.c/h`,并从删除前备份原样恢复 `IEC104.c/h`及旧全局接口,两套接收实现共存。上面的2026-09-23同步说明为历史记录;本文主要描述Receiver,旧入口边界另行注明。
> **测试清理**:2026-10-19。接口过期的旧回归测试及专用辅助文件已删除,当前测试入口及覆盖范围见[测试说明](../test/IEC104测试文档.md)。
> **信息对象同步**:2026-10-21。`IEC60870_InformationObjects.c/h`提供12种监视、18种命令的独立对象编解码;`IEC60870_Asdu.c/h`统一维护公共头、VSQ/SQ、容量、对象分派及原文应答。编码经对象函数表,解码按Type ID分派;带时标类型复用基础值处理及公共CP56工具。该模块由业务显式调用,不改变Receiver准入或链路序号处理;当时实际总召、校时和遥控流程仍待业务接入。接口及示例见[README](README.md#asdu信息对象模块)。
> **业务同步**:2026-10-30。总召、计数量、Type103校时及Type45/46遥控已有独立业务模块;遥调和带时标命令仍待实现。遥控采用单事务、显式动作及返回信息提交,设备策略和物理输出由应用提供,见[遥控设计](IEC104_Command_设计架构.md)。
> **业务同步**:2026-10-31。总召、计数量、Type103校时及Command业务已实现;Command统一处理Type45–51及58–64,默认全局启用14种但点表为空。应用提供只读点表,按CA/IOA配置允许类型和直接/选择执行模式,51/64只能直接执行。采用单事务、显式动作及返回信息提交,设备策略和物理输出由应用提供,见[遥控设计](IEC104_Command_设计架构.md)。
> **TCP接入同步**:2026-10-15。`IEC104_Connection.c/h`负责子站连接调度;`IEC104_TcpServer.c/h`提供IPv4监听及非阻塞收发,Windows依赖在Net后端。业务通过Create取得静态池不透明句柄,使用Init、Prepare/Process或Poll、NextEvent、Enqueue_Event/Send_Response、Get_Status/Get_Remote_Address;Close_Connection只断开指定代次主站,Close整个服务后读空事件再Destroy。公开头文件只依赖`IEC104_Types.h`及标准类型。构建和业务接入见 [TCP服务设计](IEC104_TcpServer_设计架构.md)。
> **子站入口同步**:2026-10-23。`IEC104_Slave`统一管理服务、调度和应答,NextEvent自动提供ASDU视图及原连接接口;业务无需手动绑定generation。延迟任务通过Copy_Event复制原文并重建自引用,旧请求在重连、重新Init和服务槽复用后仍被隔离。共享TCP值类型已迁入`IEC104_TcpTypes.h`。
> **业务边界**:子站业务从Slave接入,Slave内部调用TcpServer。本文的Receiver、Connection、SendManager及链路调用说明面向内部实现;业务不创建或驱动这些对象,不持有内部借用视图。业务可使用Codec和公共时间工具处理NextEvent返回的ASDU副本。
@@ -470,8 +470,8 @@ Receiver已实现每连接成帧和完整APDU交付;独立发送队列、业
| `t1/t2/t3`、`k`和`w`调用时机 | Connection + TcpServer(已实现) | Poll使用单调时间推进Tick,执行确认、探测和超时收尾;被动监听不发起TCP连接,未实现主站`t0`建连流程 |
| I/S/U实际发送 | TcpServer + Connection(已实现) | Prepare取得待发字节,send后立即回报增量;部分发送下轮续传,业务不再直接调用底层完成接口 |
| 总召、计数量召唤与Type 103校时 | 独立业务模块(已实现) | 通过Slave事件及共用站点表分派,总召/计数量使用业务事件;校时由Execute_Next显式执行,确认回显原时标,见[时钟同步设计](IEC104_ClockSynchronization_设计架构.md) |
| 单点、双点遥控(Type45/46) | IEC104_Command(已实现) | 独立静态池、单事务互斥;管理选择执行、撤销、三类期限、原文确认及按事务提交返回信息,见[遥控设计](IEC104_Command_设计架构.md) |
| 遥调及带时标命令(47..51、58..64) | 待实现业务模块 | 公共信息对象已提供编解码;尚未接入选择执行、设备动作和时标策略 |
| 单点、双点遥控(Type45/46) | IEC104_Command(已实现) | 独立静态池、单事务互斥;按CA/IOA点表管理类型、直接/选择执行模式,处理撤销、三类期限、原文确认及返回信息,见[遥控设计](IEC104_Command_设计架构.md) |
| 遥调及带时标命令(47..51、58..64) | IEC104_Command(已实现) | 与45/46共用事务和FIFO;按类型解析值与限定词,51/64直接执行;应用提供时间基准,受理带时标激活时检查时效 |
| 实际遥信/遥测数据源、输出及设备设时 | 应用接入层 | 按业务模块请求读取点表或执行设备动作;示例提供模拟点表、遥控输出和模拟时钟,协议库不自动操作硬件 |
### 10.2 统一连接上下文
@@ -529,6 +529,6 @@ I帧完成接口要求快照的 `N(S)==sent_vs`,只推进本地完成边界和
| 2(已实现) | 接入发送前准备、部分写入续传及独立确认记录,补齐实际I/O和收发串行调度 | 发出 `N(S)=0` 后收到 `N(R)=1` 能正确确认并释放原ASDU;发送失败不重复推进状态,旧确认不清除新增待确认数据 |
| 3(已实现) | 连接生命周期、U 控制、定时器及窗口策略 | 受控传输与时间事件下可验证启动、停止、心跳、超时和窗口边界;序号错误停止当前批次并关闭连接 |
| 4(对象及四类业务已实现) | 公共对象编解码、总召/计数量、校时及Type45/46遥控;继续接入真实数据源和设备输出 | 验证地址路由、召唤进度、校时原文确认、遥控选择执行/撤销/期限及返回信息顺序;模拟模式不设系统时间,硬件设时、实际输出和主站联调须另行验收 |
| 5(待实现) | 遥调47..51及带时标命令58..64 | 补充按类型匹配、时标解码及延迟策略,再扩展应用接口与回归 |
| 5(已实现) | 遥调47..51及带时标命令58..64 | 私有布局统一推导长度与容量,公共解析器校验值和CP56,按值及QU/QL匹配;时间基准与过期策略由配置和应用接入,实际主站互操作仍须联调 |
---
+7 -5
View File
@@ -37,7 +37,7 @@ Slave提供[共用站点表](IEC104_API接口汇总.md#station-registry):应
| `business/IEC104_GeneralInterrogation.c/h` | Type100规则及兼容的Interrogation_Create入口 |
| `business/IEC104_CounterInterrogation.c/h` | Type101规则、创建及一次动作执行入口;见[计数量设计](IEC104_CounterInterrogation_设计架构.md) |
| `business/IEC104_ClockSynchronization.c/h` | Type103校时校验、显式动作及原文确认;见[时钟同步设计](IEC104_ClockSynchronization_设计架构.md) |
| `business/IEC104_Command.c/h` | Type45/46遥控、选择执行、撤销、三类期限及返回信息;见[遥控设计](IEC104_Command_设计架构.md) |
| `business/IEC104_Command.c/h` | Type45–51/58–64遥控、遥调、按点模式、选择执行、撤销、时标判定及返回信息;见[遥控设计](IEC104_Command_设计架构.md) |
| `IEC104_TcpTypes.h` | Slave与TcpServer共用的配置、调度、事件和状态值类型,无服务操作接口 |
| `IEC60870_InformationObjects.c/h` | 67种独立信息对象,覆盖监视、命令、初始化、参数和文件;编码通过只读函数表分派,支持变长文件段 |
| `IEC60870_Time.c/h` | 101/104共用时间工具:CP16/CP24/CP56Time2a编解码、本地标准时间转换、SU修正、日历/星期和纪元秒数工具;使用约定见[时间工具设计](../common/IEC60870_Time_设计架构.md) |
@@ -70,7 +70,7 @@ Slave提供[共用站点表](IEC104_API接口汇总.md#station-registry):应
| 104站/分组总召 | 在Slave及其依赖之外加入`104/business/IEC104_Interrogation.c`和`104/business/IEC104_GeneralInterrogation.c`,头文件搜索路径增加`104/business/`;应用按QOI选点表,响应NEED_DATA提交数据 |
| 104计数量召唤 | 引擎加`104/business/IEC104_CounterInterrogation.c`,头文件搜索路径增加`104/business/`;与总召分别创建,状态通知交付两边,动作通过Execute_Begin执行 |
| 104时钟同步 | Slave依赖加`104/business/IEC104_ClockSynchronization.c`,独立实例及静态池;只由Execute_Next执行同步action,模块不依赖Clock设备适配 |
| 104遥控 | Slave依赖加`104/business/IEC104_Command.c`,头文件搜索路径增加`104/business/`;每Slave一个实例、一个事务,Accept显式执行动作,应用报告结果和提交返回信息 |
| 104遥控/遥调 | Slave依赖加`104/business/IEC104_Command.c`,使用公共信息对象和Time解析;每Slave一个实例、一个事务,Accept显式执行动作,应用报告结果、提交返回信息并提供带时标命令的时间基准;模块不读取Clock |
| 101/104公共时间工具 | `IEC60870_Time.c`,对已有时间值进行日历校验、时间转换及编解码 |
| 101/104时间源:Windows系统时钟 | `IEC60870_Clock.c`、`IEC60870_Time.c`;`IEC60870_CLOCK_USE_RTC=0`(默认),Clock.c直接使用Windows的Kernel32接口 |
| 101/104时间源:RTC预留接口 | 上述两个源文件,定义`IEC60870_CLOCK_USE_RTC=1`,另需板级驱动实现`IEC60870_Clock_RTC_Read/Set`;具体芯片驱动暂不提供 |
@@ -128,7 +128,7 @@ I/S的启停准入和停止阶段立即确认规则集中在 `IEC104_Link.c` 的
公共层仅需 `IEC60870_Asdu.c`、`IEC60870_InformationObjects.c`、`IEC60870_Codec.c`、`IEC60870_Time.c`;头文件依赖也不包含任何104模块。101接入提供约定的字段宽度和容量,104接入提供标准2/2/3及249字节上限。两种接入共用同一套对象构造和解析接口。
业务包含`IEC104_Slave.h`,取得`Slave_NextEvent`交付的I帧后可直接使用ASDU视图并解析具体对象,或先构造ASDU再交给发送接口。ASDU编解码层不分配内存、不操作连接、不读取或设置系统时钟;共用连接接口可构造并提交确认/终止应答。站/分组总召可交给IEC104_Interrogation管理校验、分批、撤销、期限和重试,应用响应业务事件提交数据,自行维护IOA、分组及遍历状态;Type45/46的选择/执行由IEC104_Command管理,Type103由独立校时模块管理,权限策略及设备动作仍由应用接入。
业务包含`IEC104_Slave.h`,取得`Slave_NextEvent`交付的I帧后可直接使用ASDU视图并解析具体对象,或先构造ASDU再交给发送接口。ASDU编解码层不分配内存、不操作连接、不读取或设置系统时钟;共用连接接口可构造并提交确认/终止应答。站/分组总召可交给IEC104_Interrogation管理校验、分批、撤销、期限和重试,应用响应业务事件提交数据,自行维护IOA、分组及遍历状态;Type45–51/58–64由IEC104_Command管理,Type103由独立校时模块管理,权限策略及设备动作仍由应用接入。
| 层次 | 职责 |
|---|---|
@@ -324,9 +324,11 @@ BEGIN携带实际CA、station_id和QOI=20..36;应用检查数据源并决定
调用过action的确认走Try,错误校验应答走普通HIGH;WAIT只重试确认,STOP取消未交接任务但不回滚时钟。广播动作一次,快照目标分别确认;满槽丢新并计数,不断链。同内容的新请求仍执行,可能再次回拨。持续NORMAL发布可能推迟Try确认,应用通过包含回调执行期的confirm_pending串行协调新的带时标上报。默认示例只设模拟时钟,`--clock system`才调用Windows时间源。完整契约见[时钟同步设计](IEC104_ClockSynchronization_设计架构.md)和[调用流程](IEC104_完整调用流程.md#flow-clock-synchronization)。
### 单点、双点遥控业务
### 遥控、遥调与带时标命令业务
[Command](../../104/business/IEC104_Command.h)处理Type45/46、COT=6激活与COT=8撤销;默认两种类型都须先选择。每个Slave只能绑定一个实例,同一实例从创建事务到读取FINISHED并交接完应答期间一直忙,所有站点的新遥控互斥。Type47..51及58..64未接手,继续交给后续处理器或44兜底。
[Command](../../104/business/IEC104_Command.h)处理Type45–51及58–64、COT=6激活与COT=8撤销;默认全局type_mask=0x3FFF,但points=NULL、point_count=0,不接入任何可执行点。应用提供按CA再IOA严格升序且唯一的只读点表,每点配置允许类型和DIRECT或SELECT_EXECUTE;51/64没有S/E位,只能采用DIRECT。Create借用点表至Destroy,不复制、不允许热更新;实际受理类型取全局和点掩码的交集。无点的新激活回47,点类型或模式不符回否定7,不生成工作事务。每个Slave只能绑定一个实例,同一实例从创建事务到读取FINISHED并交接完应答期间一直忙,所有站点的新命令互斥。全局未启用类型继续交给后续处理器或44兜底。
设定值由request.value交付应用;选择和执行比较原始值字节及屏蔽S/E后的QU/QL,不比较CP56。带时标激活默认按10秒窗口检查,应用通过Set_Time_Base提供UTC纪元毫秒及对应Net单调毫秒;过期默认静默丢弃,超前、无基准或无效时间回否定。max=0只关闭时差检查,仍拒绝IV及UTC换算失败。校时成功后应在动作回调外更新基准;STOP/断线保留基准,失去可信时间时Clear_Time_Base。
Handle_Event校验并生成SELECT/EXECUTE事件,Accept先消费事件再至多调用一次本次传入的动作;不保存回调。EXECUTE启动后先Report_Result记录设备结果,再逐帧Submit_Return_Info,全部OK后Complete。确认未交接时仍可报告结果以停止执行期限;返回信息WAIT时由应用保存ASDU重试。执行确认、返回信息与ACT_TERM走Try,模块内应答按全局FIFO提交。
+1 -1
View File
@@ -23,7 +23,7 @@
| 104 | [总召设计](104/IEC104_Interrogation_设计架构.md) | 站总召、分组、撤销、超时与业务事件 |
| 104 | [计数量设计](104/IEC104_CounterInterrogation_设计架构.md) | 读取、冻结、复位、动作执行及双实例接入 |
| 104 | [时钟同步设计](104/IEC104_ClockSynchronization_设计架构.md) | Type 103、显式设时动作、滞留补偿、期限及广播确认 |
| 104 | [遥控设计](104/IEC104_Command_设计架构.md) | Type 45/46、选择执行、撤销、期限、返回信息及FINISHED清理 |
| 104 | [遥控设计](104/IEC104_Command_设计架构.md) | Type 45–51/58–64、按CA/IOA配置模式、选择执行、时标判定、返回信息及FINISHED清理 |
| 104 | [完整调用流程](104/IEC104_完整调用流程.md) | 各层调用顺序与业务流程 |
| 104 | [API 接口汇总](104/IEC104_API接口汇总.md) | 104 及其依赖的公共接口 |
| 公共 | [时间工具设计](common/IEC60870_Time_设计架构.md) | CP16/CP24/CP56、日历转换与时钟接口 |
@@ -2,7 +2,7 @@
[iec104_slave_example.c](../../examples/iec104_slave_example.c) 包含模拟点表、业务事件处理、其他命令兜底、事件循环及main。总召状态机使用独立库模块[IEC104_Interrogation.c/h](../../104/business/IEC104_Interrogation.h),不在示例内重复实现。
当前实现站总召(`C_IC_NA_1`,QOI=20),依次返回8个单点遥信、8个双点遥信和8个标度化遥测值;分组1(QOI=21)返回单点及双点遥信,分组2(QOI=22)只返回遥测。库支持全部16组,示例未配置的其余组由应用拒绝。通信通过 `IEC104_Slave`,总召通过 `IEC104_Interrogation` 接入,所有任务使用固定存储、在主循环线程串行推进。示例另含计数量召唤、Type 103校时及Type 45/46遥控;默认时钟为模拟值,遥控只更新内存点值,实际设备数据源和物理输出仍由应用接入。
当前实现站总召(`C_IC_NA_1`,QOI=20),依次返回8个单点遥信、8个双点遥信和8个标度化遥测值;分组1(QOI=21)返回单点及双点遥信,分组2(QOI=22)只返回遥测。库支持全部16组,示例未配置的其余组由应用拒绝。通信通过`IEC104_Slave`,总召通过`IEC104_Interrogation`接入,所有任务使用固定存储、在主循环线程串行推进。示例另含计数量召唤、Type 103校时、45/46遥控、49标度化设定值及58/59/62带时标命令;默认时钟为模拟值,命令只更新内存点值,实际设备数据源和物理输出仍由应用接入。
ExampleApp持有一个零初始化的Station节点。Example_Init按`--ca`注册CA及应用context,再以`config.use_station_registry=true`创建总召。命令行仍只注册一个站,状态区参数传NULL/0;扩展多CA时须提供broadcast_states/broadcast_capacity,广播先确认所有站,再逐站数据,最后终止所有站。BEGIN只判断接受;首个NEED_DATA才按station_id查原节点并Acquire,建立唯一取数游标。Complete_Data返回OK后释放游标及引用,异常时按FINISHED清理。处理中删除/同CA重建不改变旧数据源。节点须等Is_Reusable为true后释放,见[共用站点表](../104/IEC104_API接口汇总.md#station-registry)。
@@ -53,30 +53,41 @@ FRZ=1冻结并将序号模32加一;FRZ=2另将当前值清零;FRZ=3仅清当
启动输出 `LISTEN 地址:端口 CA=站地址`。退出保留原有STOP统计字段:`submitted`累计总召与兜底已接手ASDU,`gi_submitted`为已提交完毕的总召数,二者均不表示主站已经ACK。`waits`只统计总召发送WAIT;兜底HIGH满时丢新,计入`overloads`和`dropped_requests`,不记录待重试次数。兜底ERROR/FATAL计入`cancelled`,其中FATAL可能已写出字节,该计数不表示确定未发送。拒绝、主站撤销及业务超时统计继续保留,计数量另见`counter_submitted`和`counter_completed`。末尾校时字段`clock_executed`为实际action调用数、`clock_synchronized`为action返回true次数、`clock_submitted`为已交接校时ASDU数,包含错误及否定应答,广播按各提交目标分别计数;不混入原字段。
遥控统计另追加`command_selected`、`command_executed`、`command_completed`、`command_submitted`和`command_returns`,分别表示已接受的选择、已启动的执行、成功完成事务、已交接应答和返回信息帧数;保留原有统计字段的含义。
命令统计追加`command_selected`、`command_executed`、`command_completed`、`command_submitted`和`command_returns`,分别表示已接受的选择、已启动的执行、成功完成事务、已交接应答和返回信息帧数;`command_time_expired`统计过期激活,`command_time_rejected`统计无效时间、无基准、推算失败或超前造成的时间否定。静默过期不会增加选择/执行次数;保留原有统计字段的含义。
### Type 103校时
主站发送Type=103、SQ=0、对象数=1、COT=6、P/N=0、TEST=0、CA匹配或65535、IOA=0及有效CP56。默认按主站UTC+8标准时间解释,SU由时间工具处理;模块补偿受理至执行检查的等待。应用显式Execute_Next后回COT=7,原CP56字节原样回显。TEST否定7且不执行;COT=5读时否定45,不返回本站当前时间。
默认`--clock simulated`把请求的utc_ms/base_ms保存为模拟时钟基准,不改变系统时间。`--clock system`通过`IEC60870_Clock_Set(&request->local_time)`设置Windows时间;local_time已是固定UTC+8且已补偿,示例不再手工换时区或加等待。Clock内部转换为UTC后调用系统接口,权限不足时动作报告失败并回否定ACT_CON。自动回归只运行模拟模式。
默认`--clock simulated`把请求的utc_ms/base_ms保存为模拟时钟基准,不改变系统时间。`--clock system`通过`IEC60870_Clock_Set(&request->local_time)`设置Windows时间;local_time已是固定UTC+8且已补偿,示例不再手工换时区或加等待。Clock内部转换为UTC后调用系统接口,权限不足时动作报告失败并回否定ACT_CON。真实TCP自动回归只运行模拟模式;system接线另用mock时钟单测,不设置宿主系统时间。
两种模式在成功校时动作中都记录utc_ms/base_ms及待更新标志;`Example_Drain_Clock`在Execute_Next返回后、回调之外把基准交给Command。system模式还在启动及每60秒、请求分派之前读取Clock,读成功且IV=0时按固定UTC+8转换(Clock读值不叠加夏令时),关联同一时刻的Net单调毫秒;读取/换算/设置基准失败或IV=1时清除Command基准。仅总召测试未创建Command,相关接线先检查空句柄。
确认发送WAIT时只重试原确认,不再次设时;STOP取消未交接确认而不撤销已完成的动作。同内容的新请求会再执行,旧时标重发可能回拨。真实数据发布方应在confirm_pending期间暂停新的带时标上报并继续驱动网络,避免持续新增NORMAL推迟确认。完整范围及期限见[时钟同步设计](../104/IEC104_ClockSynchronization_设计架构.md)。
### Type 45/46遥控
### 遥控、设定值与带时标命令
`Example_Init_Command`使用默认配置:两种类型均须先选择,决定期限15秒、选择保持30秒、执行期限60秒;成功发送ACT_TERM,失败默认不发。整个实例只允许一个事务,所有站点的新遥控互斥;TEST请求否定,CA=65535回UNKNOWN_CA,其他遥控IOA由应用Reject(UNKNOWN_IOA)。
`Example_Init_Command`在Config_Default后用赋值把全局type_mask裁剪为已接入的45、46、49、58、59、62,并提供24行命令点表。库默认全局启用14种命令(0x3FFF),但points=NULL、point_count=0;未提供具体点不会执行命令。示例不能用`|=`裁剪全类型默认值。决定期限15秒、选择保持30秒、执行期限60秒,成功发送ACT_TERM,失败默认不发。整个实例只允许一个事务,所有站点的新命令互斥;TEST请求否定,CA=65535回UNKNOWN_CA,未配置命令IOA的新激活走到点检查时由模块回47。
| 命令 | 受控点及返回信息 | 初值与总召 |
|---|---|---|
| Type45,IOA6001..6008 | Type1,IOA1001..1008,COT=11;状态0分/1合 | 初值见下表,执行后总召读取更新值 |
| Type46,IOA6101..6108 | Type3,IOA1101..1108,COT=11;状态1分/2合 | 初值全部为1;执行后站总召及遥信分组读取更新值 |
示例保留select_timeout_ms=30000ms。需要调整时,在Example_Init_Command的Config_Default之后、Create之前设置非零值;它统一作用于选择模式点,直接点没有选择等待。该值是最长有效期,不要求等待满30秒;计时边界、确认排队和10秒/30秒的联调建议见[选择有效期](../104/IEC104_Command_设计架构.md#4-状态与期限)。
`Example_Init_Command`用局部const点表逐行明确列出24个点的CA、IOA、允许类型和模式,不通过循环或末点判断生成配置。CA取运行时common_address,保留`--ca`支持;各行按CA/IOA升序,只允许对应的基础类型及带时标类型。6008、6108、6308为DIRECT,其余21点为SELECT_EXECUTE。Create之前用memcpy将该表复制到ExampleApp拥有的command_points稳定数组,config.points指向该数组,point_count取显式表的元素数;模块不会借用局部数组。稳定数组从Create至Destroy保持只读,三组地址描述仅负责设备运行值映射。
| 命令 | 受控点及返回信息 | 模式 | 初值与总召 |
|---|---|---|---|
| Type45/58,IOA6001..6008 | Type1,IOA1001..1008,COT=11;状态0分/1合 | 6001..6007先选择,6008直接执行 | 初值见下表,执行后总召读取更新值 |
| Type46/59,IOA6101..6108 | Type3,IOA1101..1108,COT=11;状态1分/2合 | 6101..6107先选择,6108直接执行 | 初值全部为1;执行后站总召及遥信分组读取更新值 |
| Type49/62,IOA6301..6308 | Type11,IOA2001..2008,COT=11;写入标度化值 | 6301..6307先选择,6308直接执行 | 初值见下表,执行后站总召及QOI=22读取更新值 |
表中IOA均为十进制。单点命令6001..6008分别对应遥信1001..1008;双点命令6101..6108分别对应返回位置1101..1108。例如对Type46、IOA6103完成状态2的选择与执行后,后续QOI=20或21读取的Type3、IOA1103为2;FINISHED清理不会把它恢复为初值1。
选择S/E=1、执行S/E=0,均使用COT=6;执行的类型、IOA、OA、状态及QU须与选择一致。主站收到选择的肯定ACT_CON后再执行;执行依次返回ACT_CON、位置返回信息、ACT_TERM。撤销使用COT=8,同类型/IOA/OA匹配,不要求状态相同。47..51及58..64仍由兜底回44。
SELECT_EXECUTE点先发送S/E=1选择,收到肯定ACT_CON后再发S/E=0执行,均使用COT=6;执行的类型、IOA、OA、值及QU/QL须与选择一致,带时标命令可使用不同时标。DIRECT点只发送S/E=0执行,不接受选择;其SELECT请求回否定7,SELECT_EXECUTE点未选择便执行也回否定7。点类型不匹配同样回否定7,均不会调用应用动作。执行依次返回ACT_CON、返回信息、ACT_TERM。撤销使用COT=8,同类型/IOA/OA匹配,不要求值相同。示例未启用47、48、50、51、60、61、63、64,仍由兜底回44;这些类型由模块专项测试覆盖。
`Example_Drain_Command`先读取SELECT/EXECUTE/FINISHED;首次保存事务context前Acquire应用站点引用。`Example_Command_Action`模拟返校或修改`ExampleApp.signal_values/double_values`并返回STARTED,不在动作中上报。Accept的OK只表示事件已消费,示例通过Get_Transaction确认EXECUTING后,依次Report_Result(SUCCEEDED)、Example_Build_Command_Return、Submit_Return_Info和Complete;设备结果可以早于确认交接。
49/62允许的示例标度值为−10000..10000(包含端点),选择和执行都检查限值,越限调用Reject(NEGATIVE),动作内另作守卫。这个范围属于模拟点表策略,不是协议或Command模块的统一限值。比如对6301设为−1234,Type11返回信息及后续QOI=20/22的2001均为−1234;总召对象数与帧序不变。
58/59/62的CP56按主站UTC+8及SU解释,默认时差上限10秒,IV=1拒绝,时差恰好10秒也拒绝或丢弃。simulated模式启用时尚无可信基准,须先成功校时;system模式可由启动读时建立基准。过期激活默认静默丢弃,不占事务槽或调用动作;无基准、超前及换算失败回否定7。只在受理时检查时效,默认决定期限内继续等待可能使启动动作时的命令年龄接近25秒。撤销仍检查CP56格式,但不判时差。
`Example_Drain_Command`先读取SELECT/EXECUTE/FINISHED;首次保存事务context前Acquire应用站点引用。点表、动作和返回信息按基础类型分派;`Example_Command_Action`模拟返校或修改`ExampleApp.signal_values/double_values/scaled_values`并返回STARTED,不在动作中上报。Type11单对象返回需要12字节缓冲。Accept的OK只表示事件已消费,示例通过Get_Transaction确认EXECUTING后,依次Report_Result(SUCCEEDED)、Example_Build_Command_Return、Submit_Return_Info和Complete;设备结果可以早于确认交接。
每一步WAIT都保留原事件和当前步骤;返回信息使用独立ASDU缓冲,WAIT时保留同一帧,OK后才推进,避免重复提交。STALE停止该事务重试,继续读取FINISHED。FINISHED清除模拟返校/输出标志并Release应用引用,不恢复已修改的点值;真实输出若仍未停止,应用必须继续保持设备忙状态和引用。Example_Wait_Time同时计入遥控期限、模块推进和应用挂起重试,发送WAIT时允许等待网络而不空转。
@@ -126,7 +137,7 @@ FRZ=1冻结并将序号模32加一;FRZ=2另将当前值清零;FRZ=3仅清当
| 需要新增总召且4个任务槽已占满 | 尝试否定ACT_CON;无法安全提交/保存则丢新请求并计数,保留连接与原任务;匹配撤销复用原槽 |
| 接受/取数/整体业务期限到点 | 肯定ACT_CON提交前回否定确认并保留连接,提交后关闭原连接;已取出BEGIN才交付对应TIMEOUT终结事件 |
Example_Handle_Event先过滤asdu_result失败的I帧视图,避免进入业务分派或未知类型兜底;正常线上基础头/载荷截短已由Receiver和连接层处理。当前I帧依次交给总召、计数量、校时及遥控;都未接手时,在读取下一事件前同步调用`IEC104_Slave_Reject_Request`回UNKNOWN_TYPE。该接口借用当前事件生成原文错误应答,复用库的普通HIGH路径;示例不保存other_request,也没有other_pending、other_waiting或other_waits。未支持类型的连续及相同内容有效新请求逐条尝试应答,允许穿插正在进行的总召。103由校时接手,45/46由遥控接手,102、47、58仍回44;模块之间不承诺跨业务应答按接收顺序排列。
Example_Handle_Event先过滤asdu_result失败的I帧视图,避免进入业务分派或未知类型兜底;正常线上基础头/载荷截短已由Receiver和连接层处理。当前I帧依次交给总召、计数量、校时及Command;都未接手时,在读取下一事件前同步调用`IEC104_Slave_Reject_Request`回UNKNOWN_TYPE。该接口借用当前事件生成原文错误应答,复用库的普通HIGH路径;示例不保存other_request,也没有other_pending、other_waiting或other_waits。未启用类型的连续及相同内容有效新请求逐条尝试应答,允许穿插正在进行的总召。103由校时接手,45/46/49/58/59/62由Command接手,102、47及106仍回44;模块之间不承诺跨业务应答按接收顺序排列。
OK表示已接手;WAIT只因HIGH容量不足,示例丢弃本条并增加overloads/dropped_requests,保持连接、已有HIGH内容及当前业务。ERROR按失效请求处理并继续读通知;FATAL可能已写出字节,不得重试,继续排空收尾通知。接口不修改输入事件或其最近提交结果,不能通过Get_Last_Submit_Result读取本次返回值。未读启停通知会阻止本次应答;状态计数不能追溯已消费的STOP/START,禁止先保存事件再读完后续通知后调用。需要异步业务应答时另用Copy_Event、Check_Request并自行处理STOP取消,完整契约见[同步拒绝当前请求](../104/IEC104_API接口汇总.md#reject-request)。
@@ -134,7 +145,7 @@ OK表示已接手;WAIT只因HIGH容量不足,示例丢弃本条并增加over
`Example_Drain_Business`主动读取总召业务事件:BEGIN按QOI选点表,Reply_Begin接受或拒绝请求;NEED_DATA调用普通辅助函数Example_Build_Batch构造数据,再Submit_Batch,数据耗尽则Complete_Data;FINISHED按实例/请求编号清理对应资源,撤销和超时也走该分支。没有向库注册业务函数指针。数据源暂未就绪时可保存事件,后续就绪再应答;发送WAIT也保留事件,下一次调用重新组包,模块不缓存本批原文或重复生成NEED_DATA。
本例BEGIN只判断接受,首个NEED_DATA初始化游标并读取这一批;接真实数据源时,在Example_Build_Batch的逐点组包处读取当前值和品质。IOA、类型、分组成员及遍历策略均由调用者决定,协议库不登记或冻结完整IOA列表。示例单点常量表保存地址、初值及品质,双点初值均为1;单点和双点实际值分别存放在ExampleApp.signal_values和ExampleApp.double_values中,站总召及遥信分组逐批读取这些可写数组的当前值,包括遥控执行后的更新;这些地址不是库规定的范围。逐批采样不保证整轮同一时刻,也不自动解决与突发上报的新旧排序;完整边界见[总召模块设计](../104/IEC104_Interrogation_设计架构.md#6-数据批次与发送路径)。
本例BEGIN只判断接受,首个NEED_DATA初始化游标并读取这一批;接真实数据源时,在Example_Build_Batch的逐点组包处读取当前值和品质。IOA、类型、分组成员及遍历策略均由调用者决定,协议库不登记或冻结完整IOA列表。单点和遥测常量表保存地址、初值及品质,双点初值均为1;实际值分别存放在ExampleApp.signal_values、double_values和scaled_values中,站总召及对应分组逐批读取这些可写数组的当前值,包括遥控及设定值执行后的更新;这些地址不是库规定的范围。逐批采样不保证整轮同一时刻,也不自动解决与突发上报的新旧排序;完整边界见[总召模块设计](../104/IEC104_Interrogation_设计架构.md#6-数据批次与发送路径)。
Example_Build_Batch使用局部游标遍历,仅在Submit_Batch返回OK后推进应用游标,此时发送层已复制原文并确定帧顺序,输入存储可复用。返回WAIT时保留同一业务事件及游标,先推进网络、读空Slave通知,再从同一位置重新取当前值和品质,不能重发旧构造结果。STALE表示原交互已失效(包括取消、撤销、超时及提交故障),丢弃旧应答并处理FINISHED。接实际数据源时,须将点值更新及突发发布与总召读值及提交串行化;排序保证及持续突发可能推迟总召的边界见[数据批次与发送路径](../104/IEC104_Interrogation_设计架构.md#6-数据批次与发送路径)。
@@ -159,12 +170,12 @@ STOPDT或断线通知取消未提交任务。发送内部受理STOP而返回ERRO
- `iec104_slave_test.c`:站点注册/移除、延迟回收、重复CA、13站共享服务、CA/context隔离、注册编号防替换、快照增删及嵌套集合锁、关闭重启保留,以及原有应答、事件身份和背压回归。Reject_Request另覆盖原文回显、非法参数、连续请求、窗口背压、HIGH满、启停门禁、旧会话及FATAL。
- `iec104_slave_example_test.c`:完整报文、部分写入、各发送阶段HIGH满与WAIT、请求校验、STOP/重连、非法请求、传输通知预算及API生命周期;业务事件覆盖延迟应答、批次校验、混合类型、容量边界、拒绝/失败/取消、终结预留和实例复用。总召覆盖全部16组、各阶段撤销、三项期限、迟到应答及通知优先。调度覆盖短应答立即提交及顺序保护、非队首槽复用、满槽否定或计数丢弃且当前任务有效、各阶段相同/不同QOI均忙拒绝、结束后重发成功、撤销对象匹配、停止清理、BEGIN前等事件空位及BEGIN后完整执行期限,并验证忙状态不拦截Type101总电度请求。测试设置HIGH=251字节、业务事件FIFO=2槽,使用真实容量背压限制发送。
- `iec104_command_test.c`:独立可控Net/单调时钟测试,TCP服务容量1/2和独立应答槽容量1/4交叉构建,HIGH=251字节;覆盖选择执行、撤销、动作结果、期限、FIFO、返回信息及FINISHED/引用生命周期。
- `slave_example_loopback_test.cjs`:启动实际可执行程序,使用Windows网络后端和127.0.0.1随机端口,以独立主站字节验证分片请求、k=1时逐帧ACK、站总召、忙拒绝及结束后重发、撤销、错误应答、106与102同批分别回44、STOP/START、重连和正常退出。校时核对正常ACT_CON、TEST否定7、读时COT=5否定45、广播实际CA,以及103与102同批时先102回44、再103回7。遥控核对45/46选择执行、返回信息在确认和终止之间、撤销、广播46、未知IOA47、TEST否定、47/58回44,以及单点遥控后总召读取新值。服务约12秒后自行关闭,使用默认模拟时钟。
- `iec104_command_test.c`:独立可控Net/单调时钟测试,TCP服务容量1/2和独立应答槽容量1/4交叉构建,HIGH=251字节;覆盖命令点表校验与按点模式、14种命令布局、值和CP56校验、选择执行、匹配、时效及时间基准、变长FIFO、返回信息、动作结果、期限和FINISHED/引用生命周期。
- `slave_example_loopback_test.cjs`:启动实际可执行程序,使用Windows网络后端和127.0.0.1随机端口,以独立主站字节验证分片请求、k=1时逐帧ACK、站总召、忙拒绝及结束后重发、撤销、错误应答、106与102同批分别回44、STOP/START、重连和正常退出。校时核对正常ACT_CON、TEST否定7、读时COT=5否定45、广播实际CA,以及103与102同批时先102回44、再103回7。命令核对45/46/49/58/59/62完整流程,以及6008/6108/6308直接执行、直接点SELECT否定、未选执行及点类型不符否定;返回信息在确认和终止之间,另有越限/TEST/地址拒绝、撤销、47兜底及总召新值。正常带时标命令由最近成功校时和测试单调经过时间推算时标;过期58与106同批,结合后续全部计数量帧、动作/选择计数和time_expired验证没有58应答。服务约12秒后自行关闭,使用默认模拟时钟。
原114组基线由98组总召、12组计数量及4组兜底回归组成。兜底改用未支持的106/102,覆盖同批及反序、相同内容的有效新请求、k=1与写阻塞排队、HIGH满后丢新且不补发、STOP/STOPSTART取消,以及总召未结束时立即提交错误应答。启用校时的独立回归另外核对103业务。
新增16组校时回归覆盖无站广播45/46、同批及重复103逐条执行、FIFO中间否定、满槽丢新、Try等待不重执行、STOP/重连、Defer/FATAL、同一次采样检查期限及补偿、action内Get_Status与防重入、广播WAIT期间删除当前站及同CA替代不加入。所有action均为模拟,不设置系统时间。
17组校时及时间接线回归覆盖无站广播45/46、同批及重复103逐条执行、FIFO中间否定、满槽丢新、Try等待不重执行、STOP/重连、Defer/FATAL、同一次采样检查期限及补偿、action内Get_Status与防重入、广播WAIT期间删除当前站及同CA替代不加入。其中1组只替换示例的Clock_Read/Set,验证system启动、60秒刷新、失败清基准、校时成功后回调外移交及NULL Command;Clock库仍正常链接,所有动作均不设置宿主系统时间。另有1组命令点表接线回归,检查24点、可配置CA及混合执行模式。示例测试合计133组(99总召、12计数量、4兜底、17校时/接线、1命令接线),本轮实际验收见[测试记录](../test/IEC104测试文档.md#5-验证记录)。
畸形请求回归覆盖SQ/尾字节否定、零/多对象丢弃、基础头及载荷截短仍由Receiver拒绝、原事件不变、错误原因优先级、畸形激活/撤销不影响原BEGIN/NEED_DATA及期限、背压满槽后的FIFO恢复和多CA广播撤销隔离。
@@ -176,6 +187,6 @@ need_process回归覆盖完整业务交互、事件空位释放、总召等待
不排队提交回归覆盖旧NORMAL先于新总召、WAIT期间新突发先发后重新取值、HIGH积压、已接手总召部分写入后突发不能越过、背压不计取数超时而下一批重新计时、k=1窗口释放、到期ACK优先处理及发送故障终结。WAIT时验证队列和批次进度不增加,OK后覆盖输入存储验证发送层持有独立副本。
广播回归覆盖QOI=20..36、13个CA全部确认/数据/终止顺序、非空混合批次、状态区不足及跨实例重叠、已确认站删除/同CA替换、三阶段WAIT、超出FIFO容量的FINISHED、各阶段定向撤销及满槽广播撤销、拒绝与撤销同时等待、非当前站总期限和STOP/断线清理。Submit_Batch拒绝广播CA及非当前站CA;Type101继续交后续处理器。真实TCP用命令行注册的单站验证广播全站/分组、忙拒绝、非法QOI、撤销组合及连续兜底,共核对65个总召/兜底ASDU及11次正常完成的总召,计数量另行核对;5个校时应答独立计数,其中3次执行并成功,另2次为TEST及COT拒绝。多CA由可控Net回归覆盖。
广播回归覆盖QOI=20..36、13个CA全部确认/数据/终止顺序、非空混合批次、状态区不足及跨实例重叠、已确认站删除/同CA替换、三阶段WAIT、超出FIFO容量的FINISHED、各阶段定向撤销及满槽广播撤销、拒绝与撤销同时等待、非当前站总期限和STOP/断线清理。Submit_Batch拒绝广播CA及非当前站CA;Type101继续交后续处理器。真实TCP用命令行注册的单站验证广播全站/分组、忙拒绝、非法QOI、撤销组合及连续兜底;当前ASDU数量及STOP统计见[测试记录](../test/IEC104测试文档.md#5-验证记录),计数量和命令独立计数。5个校时应答中3次执行并成功,另2次为TEST及COT拒绝;多CA由可控Net回归覆盖。
这些检查覆盖本示例的模拟点表流程;实际主站设备、现场点表和设备执行接口仍需联调。
+54 -7
View File
@@ -25,13 +25,21 @@
## 2. 编译与运行
遥控专项入口为`test/iec104_command_test.c`,复用TCP事件环测试的可控Net后端,但独立驱动Slave和Command,不依赖示例的应用状态。`./scripts/build.ps1 -Test`同时构建和执行TCP容量1/2 × 遥控独立应答槽1/4的四种组合,每种27组。库源文件须包含`104/business/IEC104_Command.c`,测试本身包含可控TCP实现,不再额外链接Windows Net或第二份TcpServer。
命令专项入口为`test/iec104_command_test.c`,复用TCP事件环测试的可控Net后端,但独立驱动Slave和Command,不依赖示例的应用状态。`./scripts/build.ps1 -Test`同时构建和执行TCP容量1/2 × 命令独立应答槽1/4的四种组合,每种53组(原46组迁移及7组按点回归);原45/46基线为每种27组。库源文件须包含`104/business/IEC104_Command.c`,测试本身包含可控TCP实现,不再额外链接Windows Net或第二份TcpServer。
遥控覆盖45/46选择和直接执行、匹配/忙拒绝/撤销、三类动作结果、成功失败及终止配置、零/多帧返回信息、完整ASDU校验、全局FIFO、回复池满、窗口/部分写入、结果先于确认、重复及迟到事件、FINISHED配对、三个期限和时钟倒退(含期限配置0)、STOP提交竞态/FATAL、NORMAL覆盖反例、站点删除重注册、引用和单/双服务生命周期。真实回环另验证45/46返回信息及终止、TEST、广播/未知IOA、47/58兜底和遥控后总召读到新值。
命令原有覆盖45/46选择和直接执行、匹配/忙拒绝/撤销、三类动作结果、成功失败及终止配置、零/多帧返回信息、完整ASDU校验、全局FIFO、回复池满、窗口/部分写入、结果先于确认、重复及迟到事件、FINISHED配对、三个期限和时钟倒退(含期限配置0)、STOP提交竞态/FATAL、NORMAL覆盖反例、站点删除重注册、引用和单/双服务生命周期。扩展覆盖47–51/58–64;当前默认配置检查全局type_mask=0x3FFF及points=NULL、point_count=0。未启用47/58返回IGNORED的用例显式裁剪全局掩码,直接/选择执行由各点模式配置;非法配置使用保留bit14/15,另拒绝SELECT_EXECUTE点包含51/64、非法点表和时区越界。
全局默认掩码的14种完整流程测试提供显式点表:51/64配置DIRECT,其他类型配置SELECT_EXECUTE;7种带时标类型先验证无时间基准时否定,再设置基准走完流程。另单独检查原样默认空表不会执行点,避免把全局类型启用误认为已经配置命令点。
新增7组按点回归:原样默认空表逐14型;17项非法配置覆盖NULL/count配对、元素数乘法上限、CA/IOA范围、严格排序/唯一性、非法模式/点掩码及51/64模式限制;同类型不同点混合DIRECT/SELECT_EXECUTE及CA隔离;全局/点掩码交集及Config按值复制、点表只读借用;查点早于忙、决定期间同点静默/SELECTED跨类型MISMATCH/时效先于点策略;二分查找首中末、空隙及CA/IOA边界;选择超时、STOP和站点重新注册后的策略及身份隔离。完整实跑结果见最新验证记录。
变长输入采用测试侧独立手写长度表及报文字节,不共用Command布局清单。核对规范长度、额外尾字节、SQ、非法状态/CP56及NaN/Inf、带值动作参数、完整确认/撤销/终止回显;已知类型截短由Receiver拒绝,不能误记为Command接手并计rejected。匹配比较值与QU/QL并屏蔽S/E,CP56可不同,浮点+0/−0或跨类型仍失配;51/64数据位及CP56首字节不能被误读为S/E。回复池容量1/4下核对混合长度FIFO、WAIT重试和槽复用后的有效长度。
时标回归覆盖max=0、IV及UTC换算、无基准、等于/小于max、毫秒余数、超前、SU和时区、单调倒退与推算越界、回调内Set/Clear限制、失败设置保留原基准及STOP/断线保留基准。检查决定期间同点丢弃、忙时过期、SELECTED过期执行及寻址错误的顺序,区分time_expired、time_rejected、rejected与dropped_requests。真实回环核对45/46/49/58/59/62完整流程、限值拒绝、操作后总召当前值、47兜底,以及过期58没有应答、事件或动作。
决定期间同点激活的4组专项回归覆盖45/46、SELECT_WAIT与直接/选择后EXECUTE_WAIT、事件已取/未取、同批输入及发送背压。检查相同或不同命令字段均只增加dropped_requests,原事件、动作参数、回显和决定期限不变,不产生抢先的忙否定;另验证到期FINISHED配对、格式校验/type_mask/撤销/其他点忙拒绝,以及同CA重注册、STOP和重连的身份边界。
当前业务测试由99组总召、12组计数量、4组兜底及16组校时组成,共131组,单/双服务配置各跑一次。总召在原98组基础上新增双点批次发送WAIT后读取最新值的回归,验证站总召和遥信分组的单点/双点帧序、游标只在提交OK后推进;真实TCP另核对双点遥控后总召读到更新值。计数量覆盖独立实例、请求校验、Type15/37批次边界、忙隔离、冻结/复位、动作重复调用和WAIT/STALE/TEST、业务期限、STOP/断线、共享连接失败、发送背压及多站广播;兜底改用未支持的106/102,覆盖同批与反序/重复内容、窗口与写阻塞、HIGH满丢新及恢复、STOP/STOPSTART取消和活动总召中的应答。校时专项检查独立受理与执行、原文确认、补偿/期限、广播及生命周期。`iec104_interrogation_rules_test.c`另有3组规则测试,遍历限定词、数据COT和类型/SQ/FRZ矩阵并检查解析;没有网络交互。
当前业务测试由99组总召、12组计数量、4组兜底、17组校时/时间接线和1组命令点表接线组成,共133组,单/双服务配置各跑一次。总召在原98组基础上新增双点批次发送WAIT后读取最新值的回归,验证站总召和遥信分组的单点/双点帧序、游标只在提交OK后推进;真实TCP另核对双点遥控后总召读到更新值。计数量覆盖独立实例、请求校验、Type15/37批次边界、忙隔离、冻结/复位、动作重复调用和WAIT/STALE/TEST、业务期限、STOP/断线、共享连接失败、发送背压及多站广播;兜底改用未支持的106/102,覆盖同批与反序/重复内容、窗口与写阻塞、HIGH满丢新及恢复、STOP/STOPSTART取消和活动总召中的应答。校时专项检查独立受理与执行、原文确认、补偿/期限、广播及生命周期;1组用mock Clock_Read/Set验证system启动、60秒刷新、失败清基准、校时回调外移交和NULL Command,仍链接真实Clock库,但不设置宿主系统时间。命令接线验证24个点、可配置CA及三组末点直接执行。`iec104_interrogation_rules_test.c`另有3组规则测试,遍历限定词、数据COT和类型/SQ/FRZ矩阵并检查解析;没有网络交互。
在项目根目录的PowerShell中执行,要求GCC可用。下述TCP事件环和调度测试已包含TcpServer实现且自行实现Net接口,构建时不要再次链接`IEC104_TcpServer.c`或`IEC104_Net_Windows.c`。
@@ -152,7 +160,7 @@ if ($LASTEXITCODE -ne 0) { throw 'Slave replies tests failed' }
./scripts/build.ps1 -Test
```
脚本输出两次`Slave service: 32 groups passed ...`(单/双服务)、两次`slave example + event interrogation tests: 130 groups passed ...`、`interrogation rules: 3 groups passed ...`和`Slave example loopback: passed ...`。可控后端使用251字节HIGH和2槽召唤业务事件FIFO。真实回环使用默认存储、k=1、随机本机端口,验证总召与计数量交错、冻结/复位及后续读取、分组/广播、TEST拒绝、COT=8返回45、106/102同批回44、STOP/START、重连及8秒后的正常退出。校时另核对正常7、TEST否定7、COT=5否定45、广播实际CA及103/102同批先44再7,全部使用模拟时钟且连接保留;STOP为submitted=65、clock_executed=3、clock_synchronized=3、clock_submitted=5。这里没有真实计量设备、系统设时或遥控,不构成完整互操作认证。
脚本输出单/双服务各`Slave service: 32 groups passed ...`、各`slave example + event interrogation tests: 133 groups passed ...`,以及四组命令专项、`interrogation rules: 3 groups passed ...`和`Slave example loopback: passed ...`。可控后端使用251字节HIGH和2槽召唤业务事件FIFO。真实回环使用默认存储、k=1、随机本机端口,验证总召与计数量交错、冻结/复位及后续读取、分组/广播、校时、混合点模式的命令操作、兜底、STOP/START、重连及正常退出。真实TCP校时使用模拟模式,system接线用mock单测,命令操作内存点表;实际主站、设备输出及宿主系统设时未纳入这组自动回归,不构成完整互操作认证。当前实际通过数量、STOP统计和测量产物见第5节。
## 3. 覆盖范围
@@ -219,7 +227,7 @@ if ($LASTEXITCODE -ne 0) { throw 'Large configuration tests failed' }
### 遥控固定存储与栈测量
2026-10-30 Windows x64、GCC 16.1.0、`-O2`实测(单位字节):
下表是2026-10-30的45/46版本历史基线,Windows x64、GCC 16.1.0、`-O2`实测(单位字节);不是扩展后的当前容量:
| 对象 | 应答槽1 | 应答槽4(默认) |
|---|---|---|
@@ -228,12 +236,51 @@ if ($LASTEXITCODE -ne 0) { throw 'Large configuration tests failed' }
| 单独立应答槽 | 56 | 56 |
| Request / Event / Status | 48 / 88 / 120 | 48 / 88 / 120 |
默认TCP服务容量1时Command静态实例数组为616字节;容量2时实例数组按实例数增长。上述不含既有Slave、TCP和发送队列。探针源文件及可执行程序位于`.build/command-v1/size_probe.c`、`size_probe_1.exe`及`size_probe_4.exe`,探针包含Command实现以测量私有结构,只作本地测量,不增加公开接口。
该历史版本在默认TCP服务容量1时Command静态实例数组为616字节;容量2时实例数组按实例数增长。上述不含既有Slave、TCP和发送队列。探针源文件及可执行程序位于`.build/command-v1/size_probe.c`、`size_probe_1.exe`及`size_probe_4.exe`,探针包含Command实现以测量私有结构,只作本地测量,不增加公开接口。
`-fstack-usage`单函数栈帧:Command Handle_Event 208、Accept 128、Submit_Return_Info 144、Submit_Head 144、Process 64字节;示例main由800增至848字节,模拟动作8字节。原始记录为`.build/command-v1/command-stack.su`、`example-before.su`及`example-after.su`。这些是Windows宿主ABI的单函数测量,不是最坏调用链或其他目标工具链的内存预算。
历史`-fstack-usage`单函数栈帧:Command Handle_Event 208、Accept 128、Submit_Return_Info 144、Submit_Head 144、Process 64字节;示例main由800增至848字节,模拟动作8字节。原始记录为`.build/command-v1/command-stack.su`、`example-before.su`及`example-after.su`。这些是Windows宿主ABI的单函数测量,不是最坏调用链或其他目标工具链的内存预算。
2026-10-31的5a内部泛化阶段仍只启用45/46,但已由14种布局生成21字节最大缓冲。Windows x64实测实例为480/672字节(回复槽1/4),事务槽192、回复槽64,Request/Event/Config/Status仍为48/88/20/120。`-O2 -fstack-usage`单函数帧为Decode_Element 144、Copy_Request 96、Submit_Head 176、Handle_Event 304、Process 64、NextEvent 144、Accept 144、Submit_Return_Info 144字节;修改前同次探针的NextEvent为160字节。最终启用遥调和时标后的数值另记,不能把这一中间阶段当作最终内存预算。
5b增加value并启用47–51后,上述sizeof及栈帧测量均与5a相同;在此x64 ABI中value恰好使用Request原有对齐空隙,不代表其他目标布局不变。该阶段全量构建通过Slave 32组×2、示例131组×2、Command 33组×4、规则3组及真实TCP设定值流程,49执行后的总召读回通过。
5c启用58–64并加入时间基准后的最终Windows x64测量:
| 对象 | 应答槽1 | 应答槽4(默认) |
|---|---|---|
| Command实例 | 512 | 704 |
| 单事务槽 / 单独立应答槽 | 192 / 64 | 192 / 64 |
| Request / Event | 80 / 120 | 80 / 120 |
| Config / Status | 28 / 128 | 28 / 128 |
相对45/46历史基线,每实例增加64/88字节;默认单服务Command静态实例数组为704字节。容量仍由布局清单最大规范长度推导,当前为21,不是接收时分配。最终GCC `-O2 -fstack-usage`单函数帧:Decode_Element 144、Copy_Request 112、Submit_Head 176、Handle_Event 288、Process 64、NextEvent 176、Accept 176、Submit_Return_Info 144字节。公开头C++11严格编译检查通过。以上仍为宿主单函数帧,不是调用链上界;STM32F4须以目标工具链重新测量,不能用这些数字代替目标RAM/栈预算。
本轮产物统一保存在`.build/command-extension-20261031/`,包含修改前`before/`、三个阶段的`5a/`、`5b/`、`5c/`及`RESULTS.txt`。私有故障探针另验证保存原文损坏时的两条收尾路径:未交付工作事件时NextEvent返回ERROR且不产生事件;已交付后Accept返回ERROR、不调用动作或发肯定确认,FINISHED(SERVICE_ERROR)恰好一次,读完后事务槽可复用。
按点配置版的当前Windows x64实测:Command实例由512/704增至528/720字节(回复槽1/4),Config由28增至48字节;事务192、回复槽64、Request80、Event120、Status128字节保持不变。每个IEC104_CommandPoint_t为16字节,由应用持有;示例ExampleApp为1848字节,其中24项点表占384字节静态RAM,因main中的ExampleApp为static,不计作main局部栈。
本版GCC `-O2 -fstack-usage`测得Create从48增至64字节、Handle_Event从288增至304字节;Decode_Element 144、Copy_Request 112、Submit_Head 176、Process 64、NextEvent 176、Accept 176、Submit_Return_Info 144字节保持不变。C++11公开头和点表初始化检查通过。产物目录为`.build/command-point-config-20261031/`;这些数值仍只针对宿主ABI,完整构建及回环验收见第5节。
## 5. 验证记录
2026-10-31 示例逐点显式配置:Example_Init_Command改为局部const点表逐行列出24个CA/IOA、类型掩码及模式,移除配置生成的嵌套循环和末点判断;运行时common_address保留`--ca`支持。Create前复制到ExampleApp.command_points稳定数组供模块借用,三组地址描述只作运行值映射。既有IOA、类型、执行模式和点表寿命均不变,不新增测试组。`./scripts/build.ps1 -Test`严格GCC构建无警告、退出0:Command 53组×4、示例133组×2、Slave 32组×2、规则3组及真实TCP点模式与六种命令完整流程全部通过。修改前快照及证据位于`.build/command-explicit-points-20261031/`。
本次GCC `-O2 -fstack-usage`修改前后测量:Example_Init_Command均内联到main,没有独立帧;main均为864字节,Example_Command_Point均为40字节。局部表理论大小384字节,但该编译配置下最终main帧未增加;ExampleApp和持久点数组布局未改。编译无警告,原始测量为任务目录内的`example-before.su`和`example-after.su`,不能将该优化结果当作其他工具链的栈保证。
2026-10-31 原生按点配置:移除全局select_required_mask,Config新增借用的points/point_count;按CA/IOA严格有序且唯一的点表配置允许类型及DIRECT/SELECT_EXECUTE。全局默认14种但空点表不执行命令;51/64只能DIRECT。查点放在撤销、决定期间同点丢弃、时标及SELECTED匹配之后、忙检查之前,保留跨类型MISMATCH和现有事务语义。示例24点中6008/6108/6308直接执行,其余21点先选择;时间策略不变。
本轮`./scripts/build.ps1 -Test`退出0,GCC `-std=c11 -O2 -Wall -Wextra -Werror`构建零警告;Command 53组×4、示例133组×2、Slave 32组×2、规则3组及真实Windows TCP回环全部通过。回环覆盖点级直接/选择执行、六种命令完整流程、直接点SELECT/未选执行/点类型拒绝、数值限值、时效、总召新值、STOP及重连;STOP为submitted=83、gi_submitted=11、command_selected=7、command_executed=9、command_completed=9、command_returns=9、command_time_expired=1、command_time_rejected=0。
尺寸及栈增量见第4节;C++11头文件及点表初始化、调用流程内实际两点示例的C11/C++11严格语法编译均通过。17份Markdown的357个本地链接及锚点、UTF-8/LF、16个公开函数覆盖和diff空白检查通过。修改前快照及验证证据位于`.build/command-point-config-20261031/`;修改与检查仅限IEC60870-Slave,未设置宿主时钟或进行目标硬件验收。下方记录中的类型选择掩码仅代表当时版本。
2026-10-31 默认启用全部控制命令:按应用接入要求,Config_Default的type_mask改为45–51/58–64全部14位(0x3FFF),select_required_mask默认排除没有S/E的51/64(0x1FBF),其余12种仍须先选择。应用裁剪类型时同步裁剪选择掩码;示例显式赋值为已接入的45/46/49/58/59/62,保留未接入47等类型的44兜底。10秒窗口、UTC+8及过期静默策略不变。新增默认14种类型完整受理与执行回归,`./scripts/build.ps1 -Test`严格GCC构建零警告、退出码0:Command 46组×4、示例132组×2、Slave 32组×2、规则3组和真实TCP六类命令流程均通过。此变更只调整默认掩码,不改变结构布局;Request/Event/Status保持80/120/128字节。修改前快照位于`.build/command-default-all-20261031/before/`;下方三阶段记录保留变更前事实。
2026-10-31 遥调与带时标命令:在原Command模块内分5a布局泛化、5b遥调、5c时标三阶段完成45–51/58–64扩展。5a原45/46逐字节回归四配置各27组保持预期通过,5b增加到33组;最终`./scripts/build.ps1 -Test`通过严格GCC构建及全部运行,Slave 32组×2、示例132组×2、Command 45组×4及规则3组均通过,真实Windows TCP回环通过。该次验收时默认类型为45/46,额外类型显式启用;X-macro统一推导规范长度和21字节容量,原文、回复及FIFO使用有效长度。
本轮回环覆盖45/46/49/58/59/62选择、执行、返回信息及ACT_TERM;49/62写入的遥测和58/59更新的遥信由既有总召读回,初值与帧序保持兼容。过期58与106同批发送,核对106应答、后续计数量全部帧和统计,确认没有58应答或动作。STOP实测submitted=83、gi_submitted=11,command_selected=7、command_executed=6、command_completed=6、command_returns=6、command_time_expired=1、command_time_rejected=0。时间边界、IV/SU、时区、基准异常、撤销和匹配顺序由可控时钟专项覆盖;system读时接线使用mock,未设置宿主时钟。
三阶段sizeof及GCC栈帧见第4节;公开头C++11检查、内部原文损坏收尾探针通过。17份Markdown的356个本地链接及锚点、UTF-8/LF、16个公开函数在API文档中的覆盖和diff空白检查均通过。基线快照、阶段产物和RESULTS记录位于`.build/command-extension-20261031/`。所有修改及验证限于IEC60870-Slave目录,未执行父工程Keil构建、目标RAM/栈测量或硬件主站联调;宿主结果不代替这些验收。
2026-10-30 恢复双点遥控地址映射:Type46命令恢复为十进制6101..6108,对应Type3遥信仍为1101..1108。示例帮助、当前说明和回环请求同步更新。原EXE被运行进程占用时首次构建链接失败;用户关闭后,`./scripts/build.ps1`成功更新常规示例EXE,严格GCC构建无诊断。真实TCP回环验证6103选择/执行后返回1103,后续站总召、遥信分组及重连均读到新值,并通过既有单点遥控、撤销、错误应答及生命周期场景。沙箱内首次回环因Node启动子进程EPERM失败,获准在沙箱外重跑相同命令后退出码0。
2026-10-30 双点遥信加入总召:QOI20按Type1、Type3、Type11依次返回各8点,共8帧;QOI21返回单点及双点共6帧,QOI22仅遥测仍4帧。沿用1101..1108及double_values,每批4点、每次发送重试重取值;只新增双点遍历游标,不增加点值数组或发送缓冲。TCP容量1/2下各131组示例C测试通过,严格GCC构建无诊断;真实TCP验证Type46操作1103后,站总召、遥信分组及重连读回均为新值,STOP submitted=83、gi_submitted=11。原示例进程运行中,`./scripts/build.ps1`因无法覆盖被占用的exe而链接失败;随后以相同源文件及编译参数生成`.build/slave-example/iec104_slave_example_double_gi.exe`并通过回环,未中断当前连接。旧进程退出后,常规`./scripts/build.ps1`重跑成功,更新`iec104_slave_example.exe`,退出码0。可控测试产物为同目录`iec104_slave_example_double_test_1.exe`及`_2.exe`。