添加遥控业务代码
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# IEC104 函数 API 接口汇总(按实现文件)
|
||||
|
||||
依据当前工作区的协议库源码及对应头文件整理,按实现所在的 `.c` 文件分节。范围不包含示范程序、测试程序及其业务辅助函数;2026-10-29补充独立Type 103时钟同步模块。
|
||||
依据当前工作区的协议库源码及对应头文件整理,按实现所在的 `.c` 文件分节。范围不包含示范程序、测试程序及其业务辅助函数;包括独立 Type 45/46 遥控模块的 14 个公开函数及其完整类型、返回值和生命周期约定。
|
||||
|
||||
共覆盖 **24 个实现文件**,其中内部跨文件协作函数为Link 5个、信息对象层2个、召唤引擎2个。表中标有“内部协作”的函数不属于业务 API;文件内的 `static` 辅助函数、宏和类型定义不作为函数接口列出,各模块入口以对应小节和声明头文件为准。
|
||||
共覆盖 **25 个实现文件**,其中内部跨文件协作函数为Link 5个、信息对象层2个、召唤引擎2个。表中标有“内部协作”的函数不属于业务 API;文件内的 `static` 辅助函数、宏和类型定义不作为函数接口列出,各模块入口以对应小节和声明头文件为准。
|
||||
|
||||
模块接口并不都供业务直接调用:子站业务入口使用 `IEC104_Slave.h`,TcpServer 由 Slave 持有,Connection、SendManager 及其底层接口供相应实现层协作。运行顺序见 [完整调用流程](IEC104_完整调用流程.md)。各文件小节先说明使用约定,再列功能表和可展开的完整函数原型;原型中的参数名、类型和返回类型与声明头文件一致。
|
||||
|
||||
@@ -19,6 +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清理资源 |
|
||||
| 解析主站请求 | [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 后提交,执行时机由业务决定 |
|
||||
@@ -28,7 +29,7 @@
|
||||
| 构造主动上报 | [具体信息对象 Create](#file-17) → [Asdu_Create / Add_InformationObject](#file-16) → [Slave_Enqueue_Event](#file-19) | 使用 Slave 提供的应用层参数;提交到 NORMAL,OK 后原文已复制 |
|
||||
| 查看当前主站和容量 | [Slave_Get_Remote_Address / Get_Status](#file-19) | 取得只读副本;状态查询不预留容量,不保证下一次提交成功 |
|
||||
| 断开原主站,继续监听 | [Slave_Close_Connection](#file-19) | 传原连接事件,不手工替换 generation |
|
||||
| 停止并释放服务 | [Slave_Close → NextEvent 读空 → Destroy](#file-19) | Close 保留通知但清除 ASDU 缓存;Destroy 不隐式关闭或丢弃通知 |
|
||||
| 停止并释放服务 | [Slave关闭](#file-19)、[遥控退出顺序](#command-api-lifecycle) | 先Cancel各业务并处理其清理事件,再Close、读空并分派通知;销毁业务、归还引用并移除站点后才Slave_Destroy,不能只清通信队列 |
|
||||
| 使用时标、读取实际时间 | [Time](#file-15)、[Clock](#file-14) | Time 负责转换;Clock 负责实际读写。命令校验与执行由业务负责 |
|
||||
|
||||
“后续只调 Slave”指服务的创建、驱动、事件、状态和关闭统一从 `IEC104_Slave_*` 进入。ASDU 与信息对象仍使用共用 `IEC60870_*`;请求应答通过 Slave 事件提供的 `IEC60870_MasterConnection_t` 分派,无需业务再操作 TcpServer。
|
||||
@@ -41,7 +42,9 @@
|
||||
| --- | --- |
|
||||
| `IEC104_Slave_t` / `IEC104_Slave.h` | 不透明服务句柄,Create/Destroy 管理静态槽;内部持有私有 TcpServer 指针 |
|
||||
| `IEC104_Station_t` | 调用方拥有的固定地址节点,Add/Remove管理注册关系;CA、context及引用由Slave统一管理,字段仅库可改写 |
|
||||
| `IEC104_Interrogation_t` / `IEC104_Interrogation.h` | 不透明总召业务句柄,借用Slave;管理请求副本、分批、撤销、期限和WAIT进度,不拥有网络服务 |
|
||||
| `IEC104_Interrogation_t` / `IEC104_Interrogation.h` | 总召与计数量使用的共用不透明句柄,种类入口分别创建实例;管理请求副本、分批、撤销、期限和WAIT进度,借用Slave、不拥有网络服务 |
|
||||
| `IEC104_ClockSynchronization_t` / `IEC104_ClockSynchronization.h` | 独立校时句柄,借用Slave;显式Execute_Next执行动作,Cancel清理任务,Destroy要求任务空且不在回调中,不隐式关闭Slave |
|
||||
| `IEC104_Command_t` / `IEC104_Command.h` | 不透明遥控句柄,借用Slave;每Slave至多一个实例、每实例一个事务,工作事件及FINISHED均为纯值副本 |
|
||||
| `IEC104_SlaveEventIdentity_t` | Slave校验后交付的来源身份,包含服务句柄、实例编号和连接代次;可按值保存,不借用事件原文;字段只读 |
|
||||
| `IEC104_TcpServer_t` / `IEC104_TcpServer.h` | 不透明底层 TCP 服务,持有连接、队列、帧缓冲、记录和事件 FIFO;可独立接入,但应用须自行管理原请求身份 |
|
||||
| `IEC60870_MasterConnection_t` / `IEC60870_Slave.h` | 共用发送函数表及上下文;104 实现由 Slave 安装,不创建第二个服务、socket 或线程 |
|
||||
@@ -65,6 +68,7 @@
|
||||
| `IEC104_TcpEventResult_t` | READY=1、EMPTY=0、ERROR=-1 | 仅 READY 提供新事件;EMPTY/ERROR 保持输出原值 |
|
||||
| `IEC104_TcpSubmitResult_t` | OK=1、WAIT=2、ERROR=-1、FATAL=-2 | OK 已接手;WAIT 未接手;ERROR 为身份/参数/阶段错误;FATAL 需按事实收尾 |
|
||||
| `IEC104_SlaveRequestState_t` | READY=2、WAIT_EVENTS=1、STALE=0、ERROR=-1 | READY仅说明会话可处理;WAIT_EVENTS先读空通知;STALE终结旧任务;ERROR处理参数或查询失败 |
|
||||
| `IEC104_CommandReplyResult_t` | OK=1、WAIT=0、ERROR=-1、STALE=-2 | Accept的OK只表示消费事件;Submit_Return_Info的OK才表示帧序固定。WAIT保留输入,STALE禁止再执行旧命令 |
|
||||
| 共用发送的 `bool` | true 为已接手,false 为未成功 | 104 通过 `Slave_Get_Last_Submit_Result` 进一步分类,不能把 false 一律当作 WAIT |
|
||||
| `IEC60870_AsduResult_t` | OK=1、FULL=0、ERROR=-1、UNSUPPORTED=-2 | 单次 ASDU/对象操作只有 OK 修改输出;FULL 时处理容量或数量限制 |
|
||||
| 时间与时钟的 `int` | 1 成功,0 失败 | 编解码成功与时间品质有效分开判断 |
|
||||
@@ -83,7 +87,7 @@
|
||||
|
||||
带 `IEC104_TcpError_t *error` 的接口允许 NULL;提供存储时完整写入本次 `result/stage/socket_error`,成功为 OK/NONE/0。它表示本次 API 调用结果;连接故障另见事件或状态里的 `fault`。例如客户端断开时 Poll 仍可返回 OK,再通过 DISCONNECTED 交付原因。诊断存储不得与其他输入/输出重叠。
|
||||
|
||||
发送分三个阶段:**提交接手 → 本地整帧写出 → 对端链路 ACK**。OK 只承诺第一阶段;正常 RECORD 的两项完成标志为真,收尾 RECORD 则按实际事实填写。ACK 也不等于对端业务执行成功。`Get_Last_Submit_Result` 是最近一次提交的快照,不随后续 Poll 或 ACK 更新;FATAL 可能发生在部分乃至全部字节已经写出之后。
|
||||
发送分三个阶段:**提交接手 → 本地整帧写出 → 对端链路 ACK**。发送接口的 OK 只承诺第一阶段;正常 RECORD 的两项完成标志为真,收尾 RECORD 则按实际事实填写。ACK 也不等于对端业务执行成功。业务决定接口另有含义,例如 Command_Accept 的 OK 只表示已消费工作事件,Command_Complete 的 OK 只表示已关闭返回信息阶段,均不能推定确认或终止已交接。`Get_Last_Submit_Result` 是最近一次提交的快照,不随后续 Poll 或 ACK 更新;FATAL 可能发生在部分乃至全部字节已经写出之后。
|
||||
|
||||
<a id="api-lifetime"></a>
|
||||
|
||||
@@ -94,10 +98,11 @@
|
||||
| Slave / TcpServer 句柄 | 静态池持有,地址稳定;应用不 malloc/free、不复制,Destroy 成功后丢弃旧指针 |
|
||||
| 原始 `IEC104_TcpEvent_t`、状态及地址快照 | 独立值副本,可跨 Poll/Close 保存;只读 type 对应的 union 成员,状态字段须先检查有效标志 |
|
||||
| `IEC104_SlaveEvent_t` | APDU 是副本,但 asdu 与 connection 含自引用;用 Copy_Event 保存,禁止结构赋值/memcpy/搬移,地址必须稳定 |
|
||||
| `IEC104_CommandRequest_t` / `IEC104_CommandEvent_t` | 纯值副本,可直接保存;应答原样回传事件身份,不能用修改request改变实际动作。动作中的request指针仅本次调用有效 |
|
||||
| `event.connection` | 使用 `&event.connection`;它绑定原 Slave 实例和原 generation。单独复制 connection 无法重建上下文 |
|
||||
| `IEC60870_Asdu_t` / `IEC60870_AsduInfo_t` | 借用参数和原文字节,使用期间保持有效;解析视图使用期间原文不可改写 |
|
||||
| 具体信息对象 | Create/Get_Element/Get_From_Buffer 使用调用方存储,保存数值及只读函数表,可复制完整对象,无需 Destroy |
|
||||
| Add / 发送 / 入队 | Add 成功已编码复制信息对象;发送或入队 OK 已复制 ASDU,可复用本次输入。WAIT 须保存原 connection、ASDU 和业务进度 |
|
||||
| Add / 发送 / 入队 | Add 成功已编码复制信息对象;发送或入队 OK 已接手 ASDU,可复用本次输入。WAIT须保留输入及原身份;Command返回信息使用纯值EXECUTE事件,不能跨重连继续提交 |
|
||||
| Slave 返回的应用层参数 | 静态只读常量,已取得的指针不借用服务存储;Get_Data 返回的报文指针则借用 ASDU 缓冲 |
|
||||
|
||||
`Send_ACT_CON/TERM` 是组合操作:先改 COT/P/N,再提交传输。即使提交 WAIT/ERROR/FATAL,已改字段也不会回滚,不能套用“单次 ASDU setter 失败保持原值”的约定。事件被 NextEvent 的下一次 READY 替换后,其旧视图和应答上下文不能继续使用。
|
||||
@@ -114,6 +119,8 @@
|
||||
|
||||
编译期容量见 [IEC104_TcpServer_Config.h](../../104/IEC104_TcpServer_Config.h):服务槽 1、接收暂存 2048 字节、HIGH/NORMAL 各 1024/4096 字节、控制帧 8 槽、发送记录 32 槽、事件保证容量 68 条,其中启停保留 4 条、收尾保留 34 条、普通通知保证 30 条。实际通知使用变长字节环,短通知可能多于保证条数;这些容量不等于 k 窗口,队列字节数还包含内部条目头。Slave 和直接创建的 TcpServer 共享底层服务名额。
|
||||
|
||||
Command 的实例池与独立应答池另由 `IEC104_COMMAND_CAPACITY`、`IEC104_COMMAND_REPLY_CAPACITY` 配置,分别默认跟随 TCP 服务容量和取 4;每实例仍只有一个遥控事务。宏须作用于 IEC104_Command.c 的编译,详见[遥控配置与容量](#command-api-config)。
|
||||
|
||||
NORMAL 允许覆盖最旧原文;事件 FIFO 不覆盖。HIGH 清除、NORMAL 被覆盖、SendBuffer 已释放、发送记录已回收分别表示不同事实,不能据此推定对端已执行业务。
|
||||
|
||||
<a id="api-events"></a>
|
||||
@@ -161,6 +168,7 @@ CONNECTED 先于同代次其他通知,DISCONNECTED 最后;实际启停变化
|
||||
- [IEC104_GeneralInterrogation.c](#file-22):1 个函数(保留旧Create名称)。
|
||||
- [IEC104_CounterInterrogation.c](#file-23):2 个函数。
|
||||
- [IEC104_ClockSynchronization.c](#file-24):8 个函数。
|
||||
- [IEC104_Command.c](#file-25):14 个函数。
|
||||
|
||||
<a id="file-1"></a>
|
||||
|
||||
@@ -2014,7 +2022,7 @@ Init 失败不销毁对象;Create 后、Init 失败后和 Close 后仍可 Get_
|
||||
| `IEC104_TCP_SUBMIT_ERROR` | 参数/事件/cause非法、原会话失效或存在未读启停通知;不按容量不足计数,继续处理通知及相应失效原因 |
|
||||
| `IEC104_TCP_SUBMIT_FATAL` | 发送故障,可能已经写出字节;不得重试,继续排空RECORD/DISCONNECTED等收尾通知 |
|
||||
|
||||
示例在总召、计数量及后续业务均未接手后显式调用此接口;Slave不会根据类型自动吞掉或拒绝NextEvent交付的请求。HIGH真正满时允许丢弃本条错误应答,因此不承诺所有负载下都有响应;已接手条目不因新兜底请求到达而被覆盖或主动断链。
|
||||
示例在总召、计数量、校时和遥控均未接手后显式调用此接口;Slave不会根据类型自动吞掉或拒绝NextEvent交付的请求。HIGH真正满时允许丢弃本条错误应答,因此不承诺所有负载下都有响应;已接手条目不因新兜底请求到达而被覆盖或主动断链。
|
||||
|
||||
<a id="station-registry"></a>
|
||||
|
||||
@@ -2024,13 +2032,13 @@ Init 失败不销毁对象;Create 后、Init 失败后和 Close 后仍可 Get_
|
||||
|
||||
每个`IEC104_Station_t`须零初始化,注册成功后直到`Is_Reusable`返回true均须保持节点及context的地址和寿命稳定,不得赋值搬移、清零或直接改字段。CA及context在注册时指定;context内容、IOA点表、权限和业务状态归应用管理。同一Slave内已注册CA唯一,不同Slave可以注册相同CA,但不能共用同一个节点。查找和注册为线性扫描,适合当前少量站点规模。
|
||||
|
||||
`Get_Station`只查具体的已注册站点,0、65535及未知CA返回NULL。地址检查与业务分派分离的思路亦见[lib60870的CA许可检查](https://github.com/mz-automation/lib60870/blob/master/lib60870-C/src/iec60870/cs104/cs104_slave.c)。NextEvent不自动丢弃或应答报文:业务负责对象布局、COT、CA、权限及广播规则。总召开启`use_station_registry`后已接入按CA查找和广播遍历,其他业务可复用同一基础接口。
|
||||
`Get_Station`只查具体的已注册站点,0、65535及未知CA返回NULL。地址检查与业务分派分离的思路亦见[lib60870的CA许可检查](https://github.com/mz-automation/lib60870/blob/master/lib60870-C/src/iec60870/cs104/cs104_slave.c)。NextEvent不自动丢弃或应答报文:业务负责对象布局、COT、CA、权限及广播规则。总召开启`use_station_registry`后已接入按CA查找和广播遍历;Command始终使用共用站点表,拒绝广播CA,创建事务时Acquire原节点,选择匹配使用注册编号而非仅比较CA。
|
||||
|
||||
延迟业务保存节点前调用`Acquire_Station`,清理完成后调用`Release_Station`。Remove立即使节点退出CA查找、计数和普通遍历;已有引用仍可读取原CA/context,最后一个引用和快照归还后才清零节点。Remove返回OK不等于可释放存储,须检查`Is_Reusable`。同CA可立即注册另一个节点;`Get_Id/Get_Station_By_Id`以不复用的注册编号定位原节点,避免旧事务查到替代站点。引用仍不能代替原连接身份检查。
|
||||
|
||||
`StationSnapshot_Begin/Next/End`提供跨循环遍历:Begin记录注册编号上界,Next跳过已删除的未来站点,并排除后来新增或同CA重建的节点;不复制或冻结IOA和点值。快照须零初始化、地址稳定、配对End,Next返回NULL也不自动结束。任一快照活动时退役节点延迟回收,因此调用方不得提前释放这些节点。需要跨End保留Next返回值时另行Acquire。`Lock_Stations/Unlock_Stations`仍作为显式冻结集合的兼容接口,锁定期间Add/Remove返回BUSY;总召广播不使用集合锁,合法多目标请求在调用方状态区中持有各节点引用,错误回复遍历使用快照。所有操作在同一事件循环串行执行,不是线程锁。
|
||||
|
||||
Close/Init不改变站点表。Slave_Destroy要求站点全部Remove、引用及快照归还、集合锁解除,否则返回INVALID_STATE。Remove不释放应用context,也不撤回已接手报文;已选中的总召可持原引用完成。若业务要求立即停止该事务,应先显式取消并处理FINISHED;不能靠删除再注册同一CA清空旧报文。
|
||||
Close/Init不改变站点表。Slave_Destroy要求站点全部Remove、引用及快照归还、集合锁解除,否则返回INVALID_STATE。Remove不释放应用context,也不撤回已接手报文;已选中的总召或已经创建的遥控事务可持原引用完成。若业务要求立即停止协议事务,应先显式取消并处理FINISHED;不能靠删除再注册同一CA清空旧报文。遥控FINISHED只表示协议结束,物理输出仍可能继续;应用应在首个工作事件时取得自己的引用并保留至输出真正结束,详见[遥控引用与退出](#command-api-lifecycle)。
|
||||
|
||||
`IEC104_StationResult_t`须显式比较:OK成功;INVALID_ARGUMENT参数/配对非法;DUPLICATE_CA已有注册CA;ALREADY_REGISTERED节点仍由某服务持有;NOT_FOUND节点不属于本服务或重复删除;BUSY显式集合锁阻止修改;LIMIT引用/快照/锁计数或注册编号耗尽。失败不改变注册关系;零计数Release/Unlock返回INVALID_ARGUMENT。
|
||||
|
||||
@@ -2070,12 +2078,12 @@ Windows x64 GCC测量(默认4任务、4业务事件):节点40字节、快
|
||||
| `IEC104_Slave_Get_Event_Identity` | 校验事件并取得来源身份;允许同实例历史会话通知,失败保持输出。 |
|
||||
| `IEC104_Slave_Check_Request` | 统一检查未读通知、原服务实例、连接代次和激活状态,返回业务推进条件。 |
|
||||
| `IEC104_Slave_Get_Last_Submit_Result` | 按事件connection查询最近发送的OK/WAIT/ERROR/FATAL,仅WAIT可等待容量重试。 |
|
||||
| `IEC104_Slave_Try_Send_ASDU` | 沿事件原connection尝试不排队提交ASDU,直接返回并记录OK/WAIT/ERROR/FATAL;WAIT不保存原文,应用重读后重试,普通共用发送接口不变。 |
|
||||
| `IEC104_Slave_Try_Send_ASDU` | 沿事件原connection尝试不排队提交ASDU,直接返回并记录OK/WAIT/ERROR/FATAL;WAIT不保存原文,应用按业务语义保留或重构后重试(总召重新取当前值),普通共用发送接口不变。 |
|
||||
| `IEC104_Slave_Send_Response` | 使用保存的原事件身份提交独立ASDU;保留原实例/代次检查,直接发送或进入HIGH,返回提交结果,不要求保留完整事件。 |
|
||||
| `IEC104_Slave_Try_Send_Response` | 使用保存的原事件身份尝试不排队提交;WAIT不缓存数据,原队列、窗口和期限规则保持不变。 |
|
||||
| `IEC104_Slave_Reject_Request` | 同步拒绝当前事件,原文仅改COT=44..47/P/N=1,复用HIGH;直接返回提交结果,输入及最近提交结果不变。 |
|
||||
| `IEC104_Slave_Get_Application_Layer_Parameters` | 返回标准104静态只读参数,供主动上报构造ASDU。 |
|
||||
| `IEC104_Slave_Enqueue_Event` | 校验ASDU对象并提交NORMAL,支持离线缓存和原有覆盖策略。 |
|
||||
| `IEC104_Slave_Enqueue_Event` | 校验104布局和容器可读性后提交NORMAL,支持离线缓存和原有覆盖策略;完整对象及业务校验由调用方或业务模块承担。 |
|
||||
| `IEC104_Slave_Get_Remote_Address` | 取得当前主站地址副本,不借用内部连接对象。 |
|
||||
| `IEC104_Slave_Close_Connection` | 按原事件关闭指定连接,内部提取身份;旧代次不能关闭新连接。 |
|
||||
| `IEC104_Slave_Close_Connection_By_Identity` | 按保存的原事件身份关闭原连接;与完整事件入口同样检查服务实例/代次,保留监听和NORMAL。 |
|
||||
@@ -2483,3 +2491,363 @@ IEC104_ClockSynchronizationExecuteResult_t IEC104_ClockSynchronization_Execute_N
|
||||
bool IEC104_ClockSynchronization_Get_Status(const IEC104_ClockSynchronization_t *self,
|
||||
IEC104_ClockSynchronizationStatus_t *status);
|
||||
```
|
||||
|
||||
<a id="file-25"></a>
|
||||
|
||||
## 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)。
|
||||
|
||||
模块只处理 Type 45(单点命令)和 Type 46(双点命令)的 COT=6 激活、COT=8 撤销。模块负责格式、寻址、选择/执行匹配、期限和协议收尾;应用负责 IOA 点表、远方/就地、闭锁、返校、输出及设备结果。未配置的类型返回 IGNORED,交后续处理器;不支持遥调、带时标命令、多个遥控并行或跨重连选择保持。决定窗口内的同点激活按[请求校验规则](#command-api-validation)静默丢弃,窗口外不按请求内容去重。
|
||||
|
||||
本节函数表省略公共前缀 `IEC104_Command_`;枚举表注明各自省略的前缀。不能把枚举的非零值统一视为成功。
|
||||
|
||||
<a id="command-api-config"></a>
|
||||
|
||||
### 25.1 配置、容量与所有权
|
||||
|
||||
`IEC104_CommandConfig_t` 由 Config_Default 填写,Create 按值复制;调用后可修改或释放原配置存储,不影响实例。Create 不接受 NULL 配置,也不自动初始化或开启 Slave。
|
||||
|
||||
| 字段 | 默认值 | 校验及语义 |
|
||||
| --- | --- | --- |
|
||||
| `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受理,包含选择确认的发送等待 |
|
||||
| `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或执行超时 |
|
||||
|
||||
`IEC104_COMMAND_TYPE_BIT(type)` 的布局为45..51对应bit0..6、58..64对应bit7..13,范围外返回0,避免非法移位。第一版Create仍只接受45/46两位;宏参数会多次求值,只传常量或无副作用表达式。结构体扩展要求库和调用者一起重新编译,不承诺二进制布局兼容。
|
||||
|
||||
| 编译期设置 | 默认值 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| `IEC104_COMMAND_CAPACITY` | `IEC104_TCP_SERVER_CAPACITY` | 全库静态Command实例数;每个Slave至多绑定一个实例,两个宏都不增加单实例并发事务数 |
|
||||
| `IEC104_COMMAND_REPLY_CAPACITY` | 4 | 每实例独立短应答槽数,用于校验错误、实例忙和否定撤销;须大于0且小于 `UINT32_MAX / 2` |
|
||||
| 唯一事务槽 | 固定1个 | 内含工作/终结事件、确认、撤销或失配应答、终止的存储及排序身份;必要事务应答不依赖独立池空位 |
|
||||
|
||||
两个容量宏均须大于0,编译选项作用于 IEC104_Command.c。全部存储静态分配,无堆分配;模块借用Slave,Slave寿命覆盖Command。所有入口与Slave在同一事件循环串行执行,不保存动作函数或context指针。
|
||||
|
||||
从事务创建至槽释放,所有站点、所有IOA均视为遥控忙;FINISHED未读或本事务最后应答尚未交接也占槽。决定窗口内同点合法激活只丢弃,不新增忙否定应答。独立短应答可在 `busy=false` 时仍待发送,不阻止创建新事务,但仍参与全局FIFO,且Destroy要求它们全部清空。独立池满只计过载并丢弃新应答,不覆盖旧内容、不主动断链。事务释放后,主站重发的相同命令按新请求处理;直接执行可能再次启动输出。
|
||||
|
||||
<a id="command-api-types"></a>
|
||||
<a id="command-api-request"></a>
|
||||
|
||||
### 25.2 请求、事件与动作类型
|
||||
|
||||
`IEC104_CommandRequest_t` 为纯值快照,无借用指针,可跨循环按值保存。每次动作使用模块内部请求重新构造的快照;修改应用保存的event.request不能改变实际输出参数。
|
||||
|
||||
| 字段 | 类型 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| `generation` | uint64_t | 原连接代次,不能改成新代次重试 |
|
||||
| `receive_serial` | uint64_t | 当前工作请求的原I帧接收编号 |
|
||||
| `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为执行 |
|
||||
| `selected` | bool | EXECUTE是否匹配了本事务已有选择;直接执行为false |
|
||||
| `state` | uint8_t | Type45为0/1,Type46为1/2,通常对应分/合;具体设备操作含义由点表定义 |
|
||||
| `qualifier` | uint8_t | QU原始0..31;1短脉冲、2长脉冲、3持续输出,0及其他值由应用解释 |
|
||||
| `accepted_ms` | uint64_t | 当前请求由Handle_Event受理的单调毫秒;包含Slave通知排队时间,不是网络收包时间 |
|
||||
|
||||
<a id="command-api-events"></a>
|
||||
|
||||
`IEC104_CommandEvent_t` 同样是纯值副本,**NextEvent取走与Accept/Reject消费是两个步骤**。取走的工作事件仍可因撤销、期限或会话结束失效,不能脱离模块检查后直接启动设备动作。
|
||||
|
||||
| 字段 | 类型/用途 | 约定 |
|
||||
| --- | --- | --- |
|
||||
| `type` | IEC104_CommandEventType_t | 与三层编号共同验证身份;SELECT=1、EXECUTE=2、FINISHED=3 |
|
||||
| `instance_id` | uint64_t | 模块实例身份,静态池槽复用也不会复用实例号 |
|
||||
| `transaction_id` | uint64_t | 实例内事务身份 |
|
||||
| `event_serial` | uint64_t | 当前事件身份;匹配执行和FINISHED各有新序号 |
|
||||
| `request` | IEC104_CommandRequest_t | 最后被接纳的工作请求;撤销及不匹配请求只改变相应回显,不覆盖本字段 |
|
||||
| `reason` | IEC104_CommandFinishReason_t | 仅FINISHED有效;工作事件为FINISH_NONE |
|
||||
| `select_effect` / `execute_effect` | bool | 仅FINISHED有效,表示对应动作曾返回STARTED或FAILED,可能需要清理;SELECT的NULL动作例外,不置select_effect |
|
||||
|
||||
事件类型省略 `IEC104_COMMAND_` 前缀:SELECT由应用Accept/Reject选择;EXECUTE由应用Accept/Reject执行,request.selected区分是否已选择;FINISHED只通知收尾,无需应答。应答时保留整个原事件,按实例号、事务号、事件号和type匹配,不以CA/IOA替代事件身份。
|
||||
|
||||
<a id="command-api-actions"></a>
|
||||
|
||||
```c
|
||||
typedef IEC104_CommandActionResult_t (*IEC104_CommandAction_t)(
|
||||
void *context, const IEC104_CommandRequest_t *request);
|
||||
```
|
||||
|
||||
Accept同步至多调用一次action,context原样转交、允许NULL,两者均不保存。request指针只在此次回调内有效,需异步使用时复制值。回调须短时返回,不推进Slave事件循环、不发布任何上报;本模块中仅允许Get_Status/Get_Transaction,修改入口拒绝重入,Cancel无效果、Destroy返回false。
|
||||
|
||||
| 动作返回值(省略 `IEC104_COMMAND_ACTION_`) | 数值 | 选择/执行效果 |
|
||||
| --- | --- | --- |
|
||||
| NOT_STARTED | 0 | 应用必须保证完全无副作用;否定确认,以REJECTED结束,不置对应effect |
|
||||
| STARTED | 1 | 选择已生效,或执行已启动;选择进入SELECTED,执行进入EXECUTING,结果以后Report_Result |
|
||||
| FAILED | 2 | 启动尝试失败但可能留下副作用;否定确认,以ACTION_FAILED结束,置对应effect |
|
||||
|
||||
非法动作返回值按FAILED处理,模块不重试动作。SELECT可以action=NULL,逻辑选择视为STARTED但无物理副作用;EXECUTE必须提供action。Accept返回OK仍可能对应NOT_STARTED/FAILED,或确认提交时遇到故障;应用保存自己的动作结果,并通过Get_Transaction及后续FINISHED判断协议是否继续,不能把OK当作设备成功。
|
||||
|
||||
`IEC104_CommandResult_t` 用于已经STARTED的执行:`IEC104_COMMAND_RESULT_SUCCEEDED=1`、`IEC104_COMMAND_RESULT_FAILED=2`,表示设备最终结果,与动作是否成功启动不同。`IEC104_CommandRejectCause_t` 为 `IEC104_COMMAND_REJECT_NEGATIVE=1`(COT=7、P/N=1)或 `IEC104_COMMAND_REJECT_UNKNOWN_IOA=2`(COT=47、P/N=1);其他值返回ERROR。
|
||||
|
||||
<a id="command-api-phases"></a>
|
||||
|
||||
### 25.3 事务阶段与结束原因
|
||||
|
||||
阶段枚举为 `IEC104_CommandPhase_t`,省略 `IEC104_COMMAND_PHASE_` 前缀:
|
||||
|
||||
| 阶段 | 数值 | 当前等待及主要迁移 |
|
||||
| --- | --- | --- |
|
||||
| SELECT_WAIT | 1 | 等应用消费SELECT;STARTED后SELECTED,拒绝、撤销或超时后ENDED |
|
||||
| SELECTED | 2 | 保持已生效选择;确认可能仍待交接,交接后且未到期的匹配执行才进入EXECUTE_WAIT |
|
||||
| EXECUTE_WAIT | 3 | 等应用消费EXECUTE;已停止选择期限,STARTED后EXECUTING |
|
||||
| EXECUTING | 4 | 等设备结果、返回信息、Complete及最终交接;Report_Result和Complete各自置标志,不另设阶段 |
|
||||
| ENDED | 5 | 协议已结束,可能仍欠FINISHED或待交应答;达到两个回收条件后释放槽 |
|
||||
|
||||
没有公开IDLE枚举;实例无事务由Status.busy=false表示,旧事件的Get_Transaction返回false。
|
||||
|
||||
`IEC104_CommandTransactionInfo_t` 是仍占槽事务的只读快照,Get_Transaction只接受该事务已经交付的SELECT、EXECUTE或FINISHED事件,包括已消费的旧SELECT。它不保留已回收事务的历史,也不做会话/期限转换。
|
||||
|
||||
| 字段 | 含义 |
|
||||
| --- | --- |
|
||||
| `active` | 阶段不是ENDED;不能据此保证当前连接允许操作 |
|
||||
| `phase` | 上表中的当前阶段 |
|
||||
| `confirm_submitted` | **当前工作请求**的确认已由发送层接手;SELECT匹配EXECUTE时会重新清零,不表示写完/ACK |
|
||||
| `result_reported` | Report_Result成功,结果已冻结且执行期限停止 |
|
||||
| `completed` | Complete成功,返回信息阶段已关闭,可能仍在等待最终交接 |
|
||||
| `term_submitted` | ACT_TERM已交接;配置不发TERM时一直false,不能只等待此字段判断完成 |
|
||||
|
||||
<a id="command-api-finish"></a>
|
||||
|
||||
终结枚举为 `IEC104_CommandFinishReason_t`,下表省略 `IEC104_COMMAND_FINISH_` 前缀。首次原因冻结,后续STOP/Cancel/发送失败不会改写已ENDED事务的原因或重复产生FINISHED。
|
||||
|
||||
| 原因 | 触发及协议含义 |
|
||||
| --- | --- |
|
||||
| NONE=0 | 工作事件占位,不是正常FINISHED原因 |
|
||||
| COMPLETED | 已记录Report_Result(SUCCEEDED)、Complete已调用,最后应答已交接;不表示主站ACK |
|
||||
| EXECUTION_FAILED | STARTED后Report_Result报告失败,Complete后的协议收尾已完成 |
|
||||
| REJECTED | 应用Reject或动作NOT_STARTED |
|
||||
| ACTION_FAILED | 动作FAILED或非法动作返回值,可能有设备副作用 |
|
||||
| MISMATCH | 选择确认交接后,同一点的执行在类型、OA、状态或QU上不匹配 |
|
||||
| DEACTIVATED | COT=8成功撤销尚未启动执行的事务;与STOPDT不同 |
|
||||
| SELECT_TIMEOUT | 已生效选择到期,释放选择,仅丢弃模块尚持有的选择确认 |
|
||||
| DECISION_TIMEOUT | 未及时Accept/Reject,安排否定ACT_CON |
|
||||
| EXECUTE_TIMEOUT | 未及时Report_Result;不发TERM,模块持有的肯定执行确认仍可继续提交 |
|
||||
| CANCELLED | 本地Command_Cancel |
|
||||
| STOPPED | 原连接STOPDT停用通知 |
|
||||
| DISCONNECTED | 原连接断开通知 |
|
||||
| CONNECTION_CHANGED | 新连接代次或原会话身份失效 |
|
||||
| SEND_ERROR | 发送ERROR/FATAL,且不是等待分派正常STOP通知的例外 |
|
||||
| SERVICE_ERROR | Slave查询、组包或内部身份/应答序号等资源不能继续使用 |
|
||||
|
||||
以上结束原因均不证明物理输出已停止;副作用清理及独立站点引用见[生命周期](#command-api-lifecycle)。
|
||||
|
||||
<a id="command-api-functions"></a>
|
||||
|
||||
### 25.4 全部函数的参数与前置条件
|
||||
|
||||
所有self必须为尚未Destroy的本模块句柄。除Config_Default和Create外,下表的接口都作用于现有实例;除两项只读查询外,不在动作回调中调用。完整声明见[函数签名](#command-api-signatures)。
|
||||
|
||||
| 函数 | 参数与前置条件 | 成功、失败和输出 |
|
||||
| --- | --- | --- |
|
||||
| Config_Default | config指向调用方可写配置 | 无返回;完整填写默认值,NULL无操作,不创建实例/启动期限 |
|
||||
| Create | slave为有效服务,寿命覆盖实例;config非NULL、满足配置表;**在开始消费Slave通知前调用** | 成功返回静态句柄;非法服务/配置、同Slave重复创建、池满或实例号耗尽返回NULL。仅复制配置,不开启网络 |
|
||||
| Destroy | 事务槽为空、无未读FINISHED、独立应答池为空,不在回调中 | true归还实例,旧指针失效;否则false且保留实例。不隐式Cancel、关闭Slave或释放应用引用 |
|
||||
| Cancel | self有效;不在回调中 | 无返回;清全部模块待交应答,以CANCELLED结束活动事务,保留应交FINISHED,会话激活状态不变;无效句柄/回调中无操作 |
|
||||
| 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不是动作或发送成功,精确返回见下表 |
|
||||
| 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位置 |
|
||||
| Complete | event为上述EXECUTE;结果已成功报告,计划提交的返回信息已全部OK,可为零帧 | 一次关闭返回信息阶段并按策略安排TERM;可早于确认交接。后续由Process完成,不能因TERM仍待交而重复调用 |
|
||||
| Get_Transaction | event可为尚占槽事务任一已交付事件;info非NULL | true写入快照,ENDED时active=false;无效身份/参数、已回收返回false且不改info。刚取出的FINISHED也可能已触发回收 |
|
||||
| Get_Status | status非NULL,可在动作回调中调用 | true写入当前状态及计数;句柄/参数或Slave查询失败返回false且不改status。只计算期限剩余,不转换状态 |
|
||||
|
||||
模块不会保存Handle_Event输入指针、Accept的action/context或Submit_Return_Info的ASDU指针。Command事件可按值复制,Slave事件若需延迟保存仍须使用Slave_Copy_Event。
|
||||
|
||||
<a id="command-api-results"></a>
|
||||
|
||||
### 25.5 返回值与重试规则
|
||||
|
||||
| 返回类型 | 枚举值 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| IEC104_CommandEventResult_t | EVENT_ERROR=-1 / EVENT_IGNORED=0 / EVENT_HANDLED=1 | 前缀为IEC104_COMMAND_;HANDLED不等于已发应答,IGNORED的I帧才交后续业务及最终44兜底 |
|
||||
| IEC104_CommandNextResult_t | NEXT_ERROR=-1 / NEXT_EMPTY=0 / NEXT_READY=1 | 仅READY改写输出;EMPTY也可能表示须优先读Slave通知 |
|
||||
| IEC104_CommandReplyResult_t | REPLY_STALE=-2 / REPLY_ERROR=-1 / REPLY_WAIT=0 / REPLY_OK=1 | 前缀为IEC104_COMMAND_;按下表区分消费工作事件、记录结果、关闭阶段和实际接手ASDU |
|
||||
|
||||
Handle_Event的IGNORED包括非本版/未配置类型、未激活I帧、ASDU视图不可用、RECORD及旧代次通知。当前CONNECTED/ACTIVATED/DEACTIVATED/DISCONNECTED返回HANDLED;参数、外来服务身份、禁止重入及内部处理错误返回ERROR。
|
||||
|
||||
下表均以合法self、有效枚举参数为基础;非法参数可先返回ERROR,不能假定所有失效事务调用都优先返回STALE。五个应答入口先验证事件交付/阶段,再检查Slave会话,READY后执行期限转换并再次验证阶段。
|
||||
|
||||
| 接口 | OK:本次已接手 | WAIT:本次未接手 | STALE:旧操作失效 | ERROR:检查参数或用法 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Accept | 工作事件已消费;动作最多一次,NOT_STARTED/FAILED也返回OK | 有未读Slave通知;动作未调用、事件未消费 | 身份/类型/阶段不符、事件已消费、事务结束或到期;不得自行补执行 | 非法参数、EXECUTE未给动作、回调重入、服务/确认资源错误;动作未调用 |
|
||||
| Reject | 工作事件已消费,应答曾安排;可能已交接,也可能被随后发送故障取消 | 有未读通知;事件未消费 | 与Accept相同 | 非法cause/参数、重入或内部错误 |
|
||||
| Report_Result | 结果已记录,执行期限停止 | 有未读通知;结果未记录 | 事务已结束/到期,或不是已STARTED的原EXECUTE事件 | result非法、有效事务中重复报告/已Complete、参数/重入/服务错误 |
|
||||
| Submit_Return_Info | ASDU已由发送层接手,帧序固定,不能重交 | 未读通知、确认未交接、模块尚有待交应答、网络背压或正常STOP通知待分派;没有缓存ASDU | 身份/阶段失效、事务结束,或发送ERROR/FATAL导致取消(正常STOP例外) | ASDU校验失败、Complete后仍待收尾、参数/重入/服务错误;本帧未接手 |
|
||||
| Complete | 已关闭返回阶段,TERM按配置由Process提交;不必重调 | 有未读通知;尚未关闭阶段 | 身份/阶段失效或事务结束 | 结果尚未报告、仍待收尾时重复调用、参数/重入/服务或终止序号错误 |
|
||||
|
||||
WAIT后保留本次事件、待报告结果或ASDU,先分派Slave通知、推进网络,再重试当前步骤;不能跳过仍WAIT的Report_Result或返回信息就调用Complete。Accept/Reject的OK后不得再次调用该工作决定,Complete的OK后不得再提交返回信息。STALE后停止提交原事务,仍应读取FINISHED清理资源。参数错误可在事务有效时修正;服务/资源错误可能已结束事务,应查询状态、继续分派通知和FINISHED,不一概重试。
|
||||
|
||||
有效事务中重复Report_Result/Complete返回ERROR;已ENDED或回收后旧EXECUTE返回STALE。Accept即使动作STARTED,随后尝试提交确认仍可能结束事务,OK不保证它仍处于EXECUTING。Get_Transaction可用于查看当前阶段,但只是只读快照,下一次修改入口仍会重新检查身份、通知及期限。
|
||||
|
||||
<a id="command-api-validation"></a>
|
||||
|
||||
### 25.6 请求校验、匹配和撤销
|
||||
|
||||
45/46请求原文固定为10字节:6字节公共头、3字节IOA、1字节SCO/DCO。当前代次、已激活且可读取的配置内命令,在匹配前先做到期转换,然后按以下优先级处理;已识别请求的拒绝/丢弃均返回HANDLED。
|
||||
|
||||
| 顺序 | 条件 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 对象数不是1,或对象不足4字节(含IOA) | 计rejected,不应答,不截取多对象伪造单命令 |
|
||||
| 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) |
|
||||
| 6 | 合法COT=8 | 按撤销规则匹配 |
|
||||
| 7 | 合法COT=6,当前仍为SELECT_WAIT或EXECUTE_WAIT,同generation、station_id及IOA | 静默丢弃,仅增加dropped_requests,返回HANDLED |
|
||||
| 8 | 其他合法COT=6 | 匹配选择、受理新事务或忙/无选择否定7 |
|
||||
|
||||
回显保留原类型、TEST、OA、CA和对象字节,VSQ规范为1、长度取10字节,按应答替换COT/P/N;因对象截短或数量不符而丢弃时不回显。除IOA=0外,模块不检查业务点表;应用用Reject(UNKNOWN_IOA)表达点不存在。
|
||||
|
||||
决定窗口内的丢弃不比较类型、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,原事务继续。
|
||||
|
||||
撤销要求原会话、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撤销。
|
||||
|
||||
<a id="command-api-timeouts"></a>
|
||||
|
||||
### 25.7 期限、会话与调度
|
||||
|
||||
全部期限使用 `IEC104_Net_Now_Ms` 的单调毫秒;本版无CP56及设备UTC参数。每阶段至多一个业务期限,已用时间大于或等于期限即到期。
|
||||
|
||||
| 期限 | 起点 | 停止点 | 到期处理 |
|
||||
| --- | --- | --- | --- |
|
||||
| decision_timeout_ms | SELECT/EXECUTE工作事件生成,包含未被取走时间 | Accept/Reject成功消费 | 否定7,终结DECISION_TIMEOUT;曾交付工作事件才产生FINISHED |
|
||||
| select_timeout_ms | 选择动作返回后重新采样;NULL选择同样计时 | Handle_Event受理匹配执行 | 终结SELECT_TIMEOUT,丢弃模块尚持有的选择确认,不改发否定 |
|
||||
| execute_timeout_ms | 执行动作返回STARTED后重新采样 | Report_Result成功 | 终结EXECUTE_TIMEOUT,不发TERM,原肯定确认仍可提交 |
|
||||
|
||||
决定和执行期限为0只关闭时长限制;处于等待决定、选择保持或等待设备结果时,`now < timer_start_ms` 仍按该阶段到期并计clock_errors。回调耗时不追认为已经消费的决定超时,选择/执行从回调返回后重新计时;回调仍必须短时完成,以免阻塞Slave事件循环。
|
||||
|
||||
确认待发不阻止Report_Result,设备结果产生后应及时报告。Report_Result成功后无业务收尾期限,应用漏调Complete会持续占槽;选择期限则有意包含确认发送等待。已交接的迟到选择确认不能撤回,过期后到达的执行仍被拒绝。底层链路、发送和STOP期限始终独立生效。
|
||||
|
||||
Handle_Event按通知FIFO更新CONNECTED/ACTIVATED/DEACTIVATED/DISCONNECTED,不以“还有后续未读通知”为由阻挡当前通知;新CONNECTED结束旧事务并更新代次,ACTIVATED启用,DEACTIVATED结束为STOPPED,DISCONNECTED结束并清当前代次,旧代次和RECORD忽略。有效I帧匹配前检查期限。
|
||||
|
||||
Process、工作事件NextEvent及有效应用应答先用Slave_Check_Request检查原身份:WAIT_EVENTS时让出,先读空并分派Slave通知;STALE终结旧事务,服务查询错误终结为SERVICE_ERROR。READY后才检查期限和改变业务状态。已经生成的FINISHED可在离线或通知未读空时由NextEvent取出;Get_Status/Get_Transaction只读,不推进期限。
|
||||
|
||||
每轮网络推进并读空Slave通知后都调用Process、读取业务事件,再重试应用保留的结果/返回信息/Complete。`need_process=true` 要立即推进;false不表示无待重试任务。等待上限同时受Slave_Prepare、`timer_active/timeout_remaining_ms` 和应用异步状态约束,不能因发送WAIT紧循环空转,具体见[遥控调度流程](IEC104_完整调用流程.md#flow-command-schedule)。
|
||||
|
||||
<a id="command-api-order"></a>
|
||||
<a id="command-api-return-info"></a>
|
||||
|
||||
### 25.8 应答FIFO与返回信息
|
||||
|
||||
事务确认、撤销/失配回复、终止与独立短应答共用生成序号,Process只提交全局最早一项,队首WAIT就结束本轮。Handle_Event产生独立应答时,仅在模块原本无待提交应答时尝试当场交接;仍须通过会话检查。新回复不越过更早的模块回复,过载不覆盖旧内容。
|
||||
|
||||
| 内容 | 发送路径 | 交接语义 |
|
||||
| --- | --- | --- |
|
||||
| 45/46/47、校验/忙否定、选择阶段确认、DEACT_CON,以及执行因Reject/NOT_STARTED/决定超时产生的否定 | Slave_Send_Response | 可进入HIGH;OK后不再归模块持有,不能Cancel撤回 |
|
||||
| 执行动作STARTED后的肯定确认、执行动作FAILED后的否定确认 | Slave_Try_Send_Response | 不入HIGH/NORMAL;OK时帧序已固定,不越过已排队的过程信息 |
|
||||
| Submit_Return_Info提交的监视信息 | 先检查模块全部待交应答,再用原事务身份Try | WAIT不缓存、不占持久排序位置;后续重试须重新通过全部门禁 |
|
||||
| ACT_TERM | Try | 排在执行确认和已提交返回信息后;Complete只安排,Process负责后续交接 |
|
||||
|
||||
Try要求当前发送缓冲、HIGH/NORMAL未交接内容及窗口/记录资源均允许;持续过程信息积压可能推迟确认/返回信息/TERM,不承诺有界完成。`high_priority_burst_limit`不能改变本模块已固定的事务帧序。交接后仍可能未写完、更未ACK。
|
||||
|
||||
Submit_Return_Info只接受已STARTED的原EXECUTE事件,确认已交接、Complete尚未接手时可用;既可早于也可晚于Report_Result。一次调用只能提交一帧非空ASDU,事务允许调用零次或多次。
|
||||
|
||||
| ASDU检查 | 当前规则 |
|
||||
| --- | --- |
|
||||
| 参数与长度 | 参数有效,COT/CA/IOA宽度为2/2/3,配置maxSizeOfASDU及实际长度不超过249;原文可完整解析,无截短、错误对象或多余尾字节 |
|
||||
| 类型与对象 | 监视方向Type1..21或30..40,至少一个对象;SQ=0/1均可,只要布局有效 |
|
||||
| COT | 11(远方命令返回信息)或3(突发);不接受12 |
|
||||
| P/N、TEST、CA | P/N=0,TEST与原命令一致(本版为0),CA必须等于命令CA |
|
||||
| IOA与OA | IOA可不同于命令IOA,不做点表匹配;当前接口也不要求返回信息OA等于命令OA,由应用按接入约定填写 |
|
||||
|
||||
远方及当地命令返回信息常量分别为 `IEC60870_ASDU_COT_RETURN_INFO_REMOTE=11`、`IEC60870_ASDU_COT_RETURN_INFO_LOCAL=12`;定义了12不表示本遥控返回信息接口接受12。
|
||||
|
||||
应用持有ASDU及其参数/缓冲:WAIT未接手,保留本帧,或从同一事件快照重建后重试;OK后发送层已接手,可释放或复用输入且不得重交。模块既不缓存WAIT的返回信息,也不把原连接数据放进可能覆盖并跨连接保留的NORMAL。断线后应用放弃原事务尚未提交部分;如设备仍产生需要报告的变化,另用Enqueue_Event/COT=3。同一变化不要同时经两个路径重复发布;动作FAILED时也只能用独立过程事件报告后续变化,不能继续调用原执行返回接口。
|
||||
|
||||
TERM策略在Report_Result之后由Complete决定:成功且send_act_term=true时发;失败还须act_term_on_failure=true;其他情况不发。失败TERM仍为原执行请求的COT=10、P/N=0,表示流程终止,不表示设备成功,这是本模块可配置约定。即使不发TERM,也须等执行确认交接才结束为COMPLETED/EXECUTION_FAILED。
|
||||
|
||||
发送返回ERROR且核实为原连接仍健康、已经STOP、对应通知尚未读取时,保留模块应答并让出,返回信息接口返回WAIT,待DEACTIVATED处理;其他ERROR及全部FATAL取消全部模块应答、结束活动事务为SEND_ERROR。FATAL可能已写出部分乃至全部字节,不能重试旧帧。Accept/Reject在动作/决定已经消费后仍返回OK,不以随后发送失败要求应用重做动作。
|
||||
|
||||
<a id="command-api-status"></a>
|
||||
|
||||
### 25.9 状态字段和累计计数
|
||||
|
||||
`IEC104_CommandStatus_t` 由Get_Status填写,计数自Create起累计;查询不发送、不处理到期。各计数可重叠,`unsigned long` 达上限自然回绕,适合诊断而非可靠业务账单。
|
||||
|
||||
| 字段 | 语义与调度用途 |
|
||||
| --- | --- |
|
||||
| `generation` | 已分派通知确定的当前连接代次,断线后0 |
|
||||
| `active` | 模块已处理ACTIVATED且尚未停用,不代替实时会话门禁 |
|
||||
| `events_pending` | Slave还有通知须优先读取;为true时need_process=false |
|
||||
| `need_process` | 有可立即尝试的本地发送、到期或失效会话收尾;发送WAIT后不持续置true。离线只剩FINISHED时为false,应用直接NextEvent读取 |
|
||||
| `timer_active` | 当前启用非零时长期限;false不关闭时钟回退检测 |
|
||||
| `busy` | 唯一事务仍占槽,含未读FINISHED/待交事务回复;不包含仅有独立短应答的情况 |
|
||||
| `timeout_remaining_ms` | 当前期限剩余毫秒,无期限/已到期/时钟倒退时为0;timer_active=true且为0时立即推进 |
|
||||
| `pending_replies` | 模块持有的事务与独立应答总数;不含已接手的HIGH/在途帧,也不含应用保留的返回信息 |
|
||||
| `pending_events` | 尚未取走的工作事件或FINISHED数,本版最多1;已取走尚未Accept的事件不在此数内 |
|
||||
| `accepted` | 生成的工作事件数,选择后执行计两次,不是成功设备操作数 |
|
||||
| `selected` / `executed` | 选择/执行动作返回STARTED次数;NULL选择也计selected |
|
||||
| `completed` / `execution_failed` | 以COMPLETED/EXECUTION_FAILED完成协议收尾的事务数 |
|
||||
| `action_failed` | 以ACTION_FAILED终结的事务数,含非法动作返回值 |
|
||||
| `submitted` | 所有已交接帧总数,包含返回信息,不等于主站接收数 |
|
||||
| `negative_submitted` | 已交接的P/N=1应答数 |
|
||||
| `return_info_submitted` | Submit_Return_Info已交接的帧数 |
|
||||
| `waits` | 底层发送明确返回WAIT的次数;通知门禁、模块FIFO等待及正常STOP让出不计入 |
|
||||
| `rejected` | 接收校验、实例忙、失配及未选择等拒绝次数,包含无法可靠回显的畸形请求;不含决定窗口内同点合法激活的静默丢弃 |
|
||||
| `app_rejected` | 应用Reject或动作NOT_STARTED次数,与接收侧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 |
|
||||
|
||||
<a id="command-api-lifecycle"></a>
|
||||
|
||||
### 25.10 FINISHED、站点引用与退出
|
||||
|
||||
曾经由NextEvent取走任一工作事件的事务,恰好交付一次FINISHED;从未取走工作事件就结束的事务直接移除未读事件,不产生FINISHED。这是事务级配对:SELECT已取走、随后EXECUTE尚未取走就撤销/超时,仍欠一个FINISHED;不会因生成EXECUTE清除SELECT留下的清理义务。
|
||||
|
||||
COMPLETED/EXECUTION_FAILED在Complete后的最后应答交接后交付;不发TERM时指确认已交接。其他原因在事务终结时即可交付,否定或DEACT_CON可以仍待发送。FINISHED中的request、原因及effect保留首次终结快照,不被撤销原文或后续STOP覆盖。
|
||||
|
||||
ENDED事务只有同时满足两个条件才释放:FINISHED已读取或无需交付;所有**本事务**应答已交接或取消。模块随后Release站点并清空槽。独立短应答不借用事务槽,也不延迟该槽回收;它们仍影响FIFO及Destroy。NextEvent返回FINISHED时可能已经释放模块的最后引用,此时Get_Transaction即使使用刚返回的FINISHED也可返回false。
|
||||
|
||||
事务创建时按注册表Acquire站点,槽释放时Release;站点删除后新请求回46,原事务持原编号继续,同CA替代站点不会继承选择。应用要跨循环保留context或启动异步设备输出,应在**首个工作事件**时按station_id取得原节点并另行Acquire,不能等读到FINISHED后再申请引用。FINISHED后物理输出未结束时,由应用继续维持设备忙状态和该引用,直到确实空闲再Release。
|
||||
|
||||
正常生命周期为:创建Slave、注册站点并Create Command;开始按FIFO分派通知;持续Process/NextEvent和应用应答;退出时Command_Cancel、读完FINISHED并处理应用资源、Slave_Close、读空并分派Slave通知、Command_Destroy、Remove_Station、Slave_Destroy。存在仍在进行的设备输出时,应用保留自己的引用并延后实际存储回收/Slave_Destroy,不能为遵循顺序提前释放context。Cancel不关闭连接、不撤回已交接帧、不回滚输出,也不清除已经冻结的终结原因。应用侧具体清理步骤见[FINISHED与引用流程](IEC104_完整调用流程.md#flow-command-finished)。
|
||||
|
||||
<a id="command-api-signatures"></a>
|
||||
|
||||
### 25.11 完整函数签名
|
||||
|
||||
```c
|
||||
#include "IEC104_Command.h"
|
||||
|
||||
void IEC104_Command_Config_Default(IEC104_CommandConfig_t *config);
|
||||
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);
|
||||
IEC104_CommandEventResult_t IEC104_Command_Handle_Event(IEC104_Command_t *self,
|
||||
const IEC104_SlaveEvent_t *event);
|
||||
bool IEC104_Command_Process(IEC104_Command_t *self);
|
||||
IEC104_CommandNextResult_t IEC104_Command_NextEvent(IEC104_Command_t *self,
|
||||
IEC104_CommandEvent_t *event);
|
||||
IEC104_CommandReplyResult_t IEC104_Command_Accept(IEC104_Command_t *self,
|
||||
const IEC104_CommandEvent_t *event, IEC104_CommandAction_t action, void *context);
|
||||
IEC104_CommandReplyResult_t IEC104_Command_Reject(IEC104_Command_t *self,
|
||||
const IEC104_CommandEvent_t *event, IEC104_CommandRejectCause_t cause);
|
||||
IEC104_CommandReplyResult_t IEC104_Command_Report_Result(IEC104_Command_t *self,
|
||||
const IEC104_CommandEvent_t *event, IEC104_CommandResult_t result);
|
||||
IEC104_CommandReplyResult_t IEC104_Command_Submit_Return_Info(IEC104_Command_t *self,
|
||||
const IEC104_CommandEvent_t *event, const IEC60870_Asdu_t *asdu);
|
||||
IEC104_CommandReplyResult_t IEC104_Command_Complete(IEC104_Command_t *self,
|
||||
const IEC104_CommandEvent_t *event);
|
||||
bool IEC104_Command_Get_Transaction(const IEC104_Command_t *self,
|
||||
const IEC104_CommandEvent_t *event, IEC104_CommandTransactionInfo_t *info);
|
||||
bool IEC104_Command_Get_Status(const IEC104_Command_t *self, IEC104_CommandStatus_t *status);
|
||||
```
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# IEC104 遥控模块设计
|
||||
|
||||
`104/business/IEC104_Command.c/h` 提供 Type 45 单命令和 Type 46 双命令业务。业务入口为 Slave,实例使用静态池,不动态分配内存、不保存应用回调;所有入口与所属 Slave 在同一事件循环中串行调用。一个 Slave 至多绑定一个 Command 实例,一个实例至多保留一个遥控事务。其他命令类型返回 IGNORED,交给后续业务或未知类型兜底。
|
||||
|
||||
## 1. 职责与默认配置
|
||||
|
||||
模块管理请求校验、选择与执行匹配、撤销、三类期限、应答排序、事务身份及 FINISHED。应用管理点表、权限、闭锁、返校和设备输出,显式报告设备结果并构造监视方向返回信息。协议结束不代表物理输出已经停止。
|
||||
|
||||
| 配置 | 默认值 | 含义 |
|
||||
|---|---|---|
|
||||
| type_mask | Type 45、46 | 其他类型位在第一版配置中非法 |
|
||||
| select_required_mask | Type 45、46 | 清除对应位允许直接执行;必须是 type_mask 的子集 |
|
||||
| decision_timeout_ms | 15000 | 工作事件生成至应用决定;0 禁用 |
|
||||
| select_timeout_ms | 30000 | 选择生效至匹配执行被模块受理;不得为0 |
|
||||
| execute_timeout_ms | 60000 | 执行动作返回 STARTED 至 Report_Result;0 禁用 |
|
||||
| send_act_term | true | 成功执行后发送 ACT_TERM |
|
||||
| act_term_on_failure | false | 显式启用时也发送失败执行的终止;send_act_term=false 时忽略 |
|
||||
|
||||
TEST=1 不执行动作,返回否定确认。广播 CA=65535 不接受。失败终止可选策略发送原执行请求的 COT=10、P/N=0,表示执行过程终止,不表示设备执行成功;结果由应用监视信息和 FINISHED(EXECUTION_FAILED) 表达。该策略为本模块接入约定,默认不开启。
|
||||
|
||||
## 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。
|
||||
|
||||
只有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阶段的选择执行要求同会话、原站点注册编号、IOA、类型、OA,且SCO/DCO屏蔽S/E后相同。此阶段选择确认尚未交接时,执行按忙拒绝并保留选择;确认交接后同点不匹配执行终结为MISMATCH,否定回显本次执行。其他点的新激活只忙拒绝。匹配前检查选择期限,到期选择不能复活。
|
||||
|
||||
撤销按会话、站点编号、IOA、类型、OA匹配,不比较状态。SELECT_WAIT、SELECTED、EXECUTE_WAIT中的匹配撤销复用事务存储,肯定DEACT_CON回显撤销请求。执行已经启动、事务已结束或没有匹配时否定撤销。发送接口已经返回OK的选择确认不能撤回,随后DEACT_CON按顺序交接。
|
||||
|
||||
## 3. 应用交互与设备结果
|
||||
|
||||
事件为纯值副本,身份包含模块实例、事务、工作事件序号和类型。Accept/Reject只接受已经取出且未消费的工作事件。模块使用自己保存的请求调用动作;调用者修改事件副本不能改变执行参数。
|
||||
|
||||
| 入口 | 成功后的含义 |
|
||||
|---|---|
|
||||
| Accept(SELECT) | 消费选择事件;可无动作,仅建立协议选择 |
|
||||
| Accept(EXECUTE) | 消费执行事件并至多调用一次非NULL动作 |
|
||||
| Report_Result | 冻结成功/失败结果并停止执行期限,重复报告报错 |
|
||||
| Submit_Return_Info | 一帧已被发送层接手并固定顺序;WAIT未接手 |
|
||||
| Complete | 关闭返回信息阶段,模块按配置继续交接终止;重复调用报错 |
|
||||
|
||||
动作返回NOT_STARTED必须保证没有副作用;STARTED表示选择生效或执行启动;FAILED表示尝试失败且可能留下副作用。SELECT的NULL动作不置物理副作用标志。执行动作返回STARTED后,应用可以在确认尚未发送时调用Report_Result,不把发送等待算成设备执行耗时。动作返回FAILED的否定确认同样使用Try,不能越过已有过程信息。
|
||||
|
||||
Accept的OK只表示事件被消费,不表示动作成功。应用应检查自己的动作结果或Get_Transaction中的执行阶段。报告结果后可以提交零帧或多帧返回信息,全部交接后Complete。每个WAIT都须保存当前交互,先处理网络和通知再重试;不能越过失败的Report_Result直接Complete。
|
||||
|
||||
Get_Transaction仅查询当前尚占槽的事务:ENDED仍可查询active=false,槽释放后旧事件查询返回false且不修改输出。FINISHED、旧SELECT及旧EXECUTE不能用于启动新动作;已消费EXECUTE仅供其对应的结果、返回信息和完成接口使用。
|
||||
|
||||
## 4. 状态与期限
|
||||
|
||||
状态依次为SELECT_WAIT、SELECTED、EXECUTE_WAIT、EXECUTING、ENDED;直接执行跳过选择阶段。EXECUTING内部另记录结果是否已报告、返回阶段是否关闭和确认/终止交接状态,避免重复动作、覆盖结果及重复终止。
|
||||
|
||||
期限采用Net单调毫秒;已用时间大于或等于期限即到期,时钟倒退按到期并计clock_errors。期限配置为0只禁用时长限制,正在决定、选择或等待设备结果时仍检查单调时钟倒退。决定期限从工作事件生成开始,包含事件尚未被应用取走的时间;选择和执行期限从动作返回后重新采样开始。这里的请求到达指Handle_Event受理时刻,包含Slave通知排队时间。
|
||||
|
||||
Accept/Reject消费交互后停止决定期限;Report_Result后停止执行期限。选择期限有意覆盖选择确认的发送等待,作为返校保持上限。执行超时不发ACT_TERM,已经执行动作所对应的肯定确认仍可继续提交;随后STOP或Cancel可以清除尚未提交的应答。结果报告后没有额外业务收尾期限,应用漏调Complete会持续占槽,直到明确取消或会话结束。
|
||||
|
||||
## 5. 通知、FIFO与返回信息
|
||||
|
||||
Handle_Event先校验来源并按Slave的FIFO消费连接/启停通知,不因后续未读通知而拒绝当前通知。有效I帧在匹配前检查期限;动作及发送入口先Check_Request,再处理到期。WAIT_EVENTS时不消费应用交互。NextEvent在离线时仍能交付已有FINISHED。
|
||||
|
||||
事务必要应答保存在事务槽,独立校验/忙/撤销否定保存在固定回复池,各自取得全局顺序号。Process只提交最早应答;队首WAIT时停止本轮。返回信息直接Try前也检查模块内是否存在更早应答,避免绕过本地FIFO。WAIT的应用ASDU未被接纳、不占持久排序位置。ACT_TERM在Complete被接受后安排,保证确认和已经接手的返回信息在先。
|
||||
|
||||
返回信息必须能完整解析,为监视方向Type 1–21或30–40,COT=11或3、P/N=0、TEST与命令一致、CA等于命令CA;监视IOA可以不同。它绑定原连接并走Try,不进入允许覆盖及跨连接保留的NORMAL队列。断线时尚未提交部分由应用放弃;如仍需报告设备变化,可独立用COT=3发布过程事件,不继续旧命令事务。
|
||||
|
||||
提交返回ERROR但原连接健康、仅因STOP通知待消费时让出;其他ERROR/FATAL终结为SEND_ERROR并清理待交应答,FATAL不可重试。Try必须等已有HIGH/NORMAL及当前帧让出,持续积压时不承诺有界完成。全部等待时调度不能空转。
|
||||
|
||||
## 6. FINISHED、引用和退出
|
||||
|
||||
事务级“曾交付工作事件”与当前事件已读状态分开保存。曾取走SELECT或EXECUTE的事务恰好产生一次FINISHED;从未取走任何工作事件就结束的事务不产生FINISHED。此规则也适用于直接执行尚未读就撤销,以及已读SELECT但后续EXECUTE未读时撤销/超时。
|
||||
|
||||
成功和执行失败在最终应答交接后交付FINISHED;其他终结原因立即交付,不等待否定或DEACT_CON。终结原因一旦确定不被后续STOP/Cancel改写,清理不重复通知。FINISHED保存最后一个接纳的工作请求和副作用标志,不被撤销或不匹配请求的回显覆盖。
|
||||
|
||||
事务只有FINISHED已读(或无需交付)且所有所属应答已交接/取消后才释放。释放前实例仍忙。独立应答池也属于实例生命周期:Cancel/STOP/发送失败清理它,Destroy要求事务和整个回复池均空且不在动作回调中。
|
||||
|
||||
事务创建时Acquire原站点,释放时Release。应用首次工作事件需要跨循环保留context或启动异步输出时,另行Acquire;在FINISHED清理且设备实际空闲后Release,不能等模块释放引用后再申请。站点删除阻止新请求,已有事务持原注册编号完成;同CA新节点不接续旧选择。跨协议结束仍在运行的输出由应用继续维护忙状态。
|
||||
|
||||
退出顺序:Command_Cancel,读空FINISHED并清理应用引用,Slave_Close,读空并分派Slave通知,Command_Destroy,移除站点,Slave_Destroy。动作回调只允许Get_Status/Get_Transaction,不得重入修改入口、推进网络或发布上报。
|
||||
|
||||
## 7. 固定存储、验证与扩展
|
||||
|
||||
`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模块职责。
|
||||
|
||||
验证覆盖TCP容量1/2与回复容量1/4、期限边界、动作只调用一次、通知优先、FIFO、部分写入、结果先于确认、撤销及站点引用,并使用真实本机TCP核对45/46流程。决定窗口内的同点激活丢弃不依赖内容相同,也不提供窗口外去重;事务释放后,相同ASDU按新请求处理,允许直接执行时仍可能再次输出。
|
||||
|
||||
带时标58/59、遥调及并发不属于第一版。后续扩展需保持事件身份、引用与交接契约;结构体新增字段要求调用方一起重新编译,不承诺二进制布局兼容。
|
||||
@@ -1,14 +1,14 @@
|
||||
# IEC104 完整调用流程
|
||||
|
||||
本文依据 2026-10-29 当前工作区的协议库源码,说明从服务创建到关闭释放的调用顺序,与 [API 接口汇总](IEC104_API接口汇总.md) 配合阅读。范围仅为库本身:不包含示范程序、测试程序或应用业务实现。接口汇总按实现文件查函数,本文按运行过程追踪函数之间的关系。
|
||||
本文依据 2026-10-30 当前工作区的协议库源码,说明从服务创建到关闭释放的调用顺序,与 [API 接口汇总](IEC104_API接口汇总.md) 配合阅读。本文以库接口为主,引用实际示例说明应用接入,但不逐函数展开示例或测试实现。接口汇总按实现文件查函数,本文按运行过程追踪函数之间的关系。
|
||||
|
||||
主线为当前已实现的 **IPv4 TCP 子站**:应用通过 `IEC104_Slave_*` 接入,Slave内部持有并调用 `IEC104_TcpServer_*`,内部使用 Connection、SendManager、Link、Receiver、SendBuffer 和 AsduQueue。当前网络后端为 Windows;主站文件只提供 Link 层启停过程,未接入这一套 TCP 子站调度。
|
||||
|
||||
调用树中的 `[内部]` 表示对应 `.c` 内的静态辅助函数,不是应用可直接调用的 API。调用树表示主要步骤;具体的等待、失败和恢复分支在后文说明。
|
||||
|
||||
可先读[应用主线](#flow-1)和[对象关系](#layers),需要业务时读[总召接入](#flow-interrogation)和[时钟同步接入](#flow-clock-synchronization),再查[事件与 ASDU](#flow-6)、[应答与上报](#flow-7)、[容量等待](#flow-12)、[断线重连](#flow-13)、[自有事件循环](#flow-14)和[关闭释放](#flow-15)。
|
||||
可先读[应用主线](#flow-1)和[对象关系](#layers),需要业务时读[总召接入](#flow-interrogation)、[计数量接入](#flow-counter-interrogation)、[时钟同步接入](#flow-clock-synchronization)和[遥控接入](#flow-command),再查[事件与 ASDU](#flow-6)、[应答与上报](#flow-7)、[容量等待](#flow-12)、[断线重连](#flow-13)、[自有事件循环](#flow-14)和[关闭释放](#flow-15)。
|
||||
|
||||
业务调用主线为 `Slave_Create → Slave_Config_Default/Init → Slave_Prepare/Process 或 Poll → Slave_NextEvent → 共用发送接口 → Slave_Close → 读空事件 → Slave_Destroy`。NextEvent 直接交付 ASDU 及原连接接口;延迟处理可用Slave_Copy_Event,也可保存来源身份及必要请求数据,通过Slave身份入口应答。以下展开 Slave 调用到的 TCP 内部流程;其中 `server` 和应答所用的 `generation` 由 Slave 维护。
|
||||
业务调用主线为 `Slave_Create → 站点/业务模块绑定及Slave_Init → 网络推进与通知分派 → 业务Process/事件/显式应答 → 业务Cancel和清理 → Slave_Close及通知收尾 → 业务Destroy/站点移除 → Slave_Destroy`。Slave_NextEvent直接交付ASDU及原连接接口;独立业务模块自行保存原身份,并交付可按值保留的业务事件。以下展开应用与TCP内部流程;其中`server`和应答所用的`generation`由Slave维护。
|
||||
|
||||
<a id="flow-1"></a>
|
||||
|
||||
@@ -18,43 +18,47 @@
|
||||
IEC104_Slave_Create()
|
||||
→ IEC104_Slave_Config_Default()
|
||||
→ 按需要修改监听地址、端口和协议配置
|
||||
→ 注册站点,创建所需的总召/计数量/校时/遥控实例
|
||||
→ IEC104_Slave_Init()
|
||||
→ 循环:
|
||||
IEC104_Slave_Poll()
|
||||
IEC104_Slave_NextEvent(),逐条读到 EMPTY
|
||||
CONNECTED → 记录主站地址和连接代次;此时尚未启用数据传输
|
||||
ACTIVATED → 处理数据传输启用通知
|
||||
I_FRAME → 检查 asdu_result,完整解析并校验业务条件
|
||||
通过 &event.connection 调用共用 Send_ASDU / ACT_CON / ACT_TERM
|
||||
I_FRAME → 检查 asdu_result,分派业务模块;全部未接手才兜底
|
||||
自定义同步业务可通过 &event.connection 应答
|
||||
RECORD → 处理发送事实回执
|
||||
DEACTIVATED → 处理数据传输停用通知
|
||||
DISCONNECTED → 结束该 generation 的连接业务
|
||||
有主动上报时构造 ASDU,调用 IEC104_Slave_Enqueue_Event()
|
||||
推进业务Process,读取业务事件,执行应用决定/结果报告/返回信息/Complete
|
||||
有独立主动上报时调用 IEC104_Slave_Enqueue_Event()
|
||||
对此前返回 WAIT 且仍有效的业务提交,择机重试
|
||||
→ 分别Cancel业务模块,读完FINISHED并清理应用资源
|
||||
→ IEC104_Slave_Close()
|
||||
→ IEC104_Slave_NextEvent(),处理收尾事件直到 EMPTY
|
||||
→ IEC104_Slave_NextEvent(),分派收尾通知直到 EMPTY;再读完业务FINISHED
|
||||
→ 分别Destroy业务模块,归还引用并移除站点
|
||||
→ IEC104_Slave_Destroy()
|
||||
```
|
||||
|
||||
以上是调用顺序伪代码。事件不会每轮都出现,排列也不是固定脚本;实际以 `NextEvent` 返回的 FIFO 顺序为准。
|
||||
以上是调用顺序伪代码,省略各调用的错误处理,返回值见下表及各业务小节。业务实例也可在Slave_Init后创建,条件是尚未开始消费通知。事件不会每轮都出现,排列也不是固定脚本;实际以Slave_NextEvent返回的FIFO顺序为准,生命周期通知须分派给所有业务实例。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Slave_Create / Config_Default / Init"] --> B["Slave_Poll:等待并推进一次"]
|
||||
A["创建Slave / 注册站点 / 绑定业务 / Init"] --> B["Slave_Poll:等待并推进一次"]
|
||||
B --> C["NextEvent:逐条取事件"]
|
||||
C --> D{"事件类型"}
|
||||
D -->|I_FRAME| E["验证 ASDU / 通过 event.connection 应答"]
|
||||
D -->|I_FRAME| E["验证视图 / 按业务模块分派 / 未接手才兜底"]
|
||||
D -->|RECORD| F["读取本地完成和对端 ACK 事实"]
|
||||
D -->|连接或启停| G["按 generation 处理状态变化"]
|
||||
D -->|连接或启停| G["按 generation 分派给全部业务模块"]
|
||||
E --> C
|
||||
F --> C
|
||||
G --> C
|
||||
C -->|EMPTY| H["提交主动上报 / 安排有效的 WAIT 重试"]
|
||||
C -->|EMPTY| H["业务Process / 业务事件 / 显式应答及WAIT重试"]
|
||||
H --> I{"继续运行?"}
|
||||
I -->|是| B
|
||||
I -->|否| J["Close"]
|
||||
J --> K["NextEvent 读完收尾事件"]
|
||||
K --> L["Destroy"]
|
||||
I -->|否| J["业务Cancel / 读完FINISHED / Slave_Close"]
|
||||
J --> K["分派收尾通知 / 清理业务资源"]
|
||||
K --> L["业务Destroy / 移除站点 / Slave_Destroy"]
|
||||
```
|
||||
|
||||
使用这条主线时:
|
||||
@@ -69,12 +73,13 @@ flowchart TD
|
||||
|
||||
| 接口组 | 成功或有结果 | 需要区别处理的结果 |
|
||||
| --- | --- | --- |
|
||||
| Init、Poll、Prepare、Process、Close 等 | `IEC104_TCP_OK`,数值为 0 | 错误时保存该次 `IEC104_TcpError_t`;客户端断开本身可正常返回 OK,由事件说明 |
|
||||
| NextEvent | `IEC104_TCP_EVENT_READY` | EMPTY 表示已读空;ERROR 不是有效事件 |
|
||||
| Slave/TcpServer的Init、Poll、Prepare、Process、Close等 | `IEC104_TCP_OK`,数值为0 | 错误时保存该次`IEC104_TcpError_t`;客户端断开本身可正常返回OK,由事件说明 |
|
||||
| Slave_NextEvent / TcpServer_NextEvent | `IEC104_TCP_EVENT_READY` | EMPTY表示通知已读空;ERROR不是有效事件 |
|
||||
| Slave_Enqueue_Event、底层 TcpServer_Send_Response | `IEC104_TCP_SUBMIT_OK` | WAIT 尚未接手;ERROR 为参数、代次或准入等错误;FATAL 须结合收尾事实处理 |
|
||||
| 共用连接的Send_ASDU、Send_ACT_CON、Send_ACT_TERM | `true` | 104侧用Get_Last_Submit_Result区分WAIT/ERROR/FATAL;false不等于可以直接重试 |
|
||||
| Command_Accept / Reject / Report_Result / Submit_Return_Info / Complete | `IEC104_COMMAND_REPLY_OK`,数值为1 | WAIT=0保留当前步骤;STALE=-2停止旧交互;ERROR=-1检查参数/服务或用法,OK含义依接口而定 |
|
||||
|
||||
`SUBMIT_OK` 表示已经复制接手,应避免重复提交;`SUBMIT_WAIT` 表示未接手,由业务稍后重试。普通应答保留原文,总召数据使用不排队接口,WAIT后重读当前值再试。应答返回 FATAL 时可能已经写出部分甚至全部字节,不能直接按“完全未发送”处理。
|
||||
`SUBMIT_OK`表示发送层已经接手,应避免重复提交;`SUBMIT_WAIT`表示未接手,由业务稍后重试。普通应答保留原文,总召数据WAIT后重读当前值;遥控返回信息WAIT保留同一帧及EXECUTE事件,不能照搬总召的重采样策略。Command_Accept的OK只表示消费事件,不等于STARTED或确认已发送。应答返回FATAL时可能已经写出部分甚至全部字节,不能直接按“完全未发送”处理。
|
||||
|
||||
<a id="layers"></a>
|
||||
|
||||
@@ -84,6 +89,8 @@ flowchart TD
|
||||
flowchart TD
|
||||
App["应用业务"] --> Slave["IEC104_Slave:生命周期、事件封装、连接身份绑定"]
|
||||
App --> Common["IEC60870_MasterConnection:共用发送接口"]
|
||||
App --> Business["总召 / 计数量 / 校时 / 遥控:显式业务交互"]
|
||||
Business -->|原请求身份应答| Slave
|
||||
Common -->|函数指针分派| Slave
|
||||
Slave -->|内部持有指针| TCP["IEC104_TcpServer:监听、socket、FIFO、调度"]
|
||||
TCP --> Conn["IEC104_Connection:接收、状态机、期限协调"]
|
||||
@@ -105,6 +112,8 @@ flowchart TD
|
||||
|
||||
`Check_Request`返回`WAIT_EVENTS`时先逐条处理Slave通知,返回`STALE`时终结原业务,返回`ERROR`时处理查询或参数错误;只有`READY`才继续业务阶段。READY不保证发送容量,也不能替代业务撤销和期限判断。后续业务模块复用这两个接口,不读取事件的内部身份字段,也不自行组合TCP连接状态。
|
||||
|
||||
此门禁用于推进原请求,不得挡住Handle_Event正在消费的生命周期通知。遥控模块先按事件来源分派CONNECTED/ACTIVATED/DEACTIVATED/DISCONNECTED,按FIFO处理完后再推进事务;NextEvent中的FINISHED即使离线也必须能够读取,才能完成资源清理。
|
||||
|
||||
### 1.2 配置入口与默认值
|
||||
|
||||
运行配置先调用 `IEC104_Slave_Config_Default(&config)`,再修改所需字段,最后交给 Init。Init 复制配置;修改原配置变量不会改变已运行的服务。
|
||||
@@ -119,6 +128,8 @@ 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不会自动读取业务模块状态。
|
||||
|
||||
104 专用的默认参数宏保留在 104 接入层。共用 `IEC60870_AppLayerParameters_t` 只描述布局,不替调用方选择传输配置。业务一般通过 `IEC104_Slave_Get_Application_Layer_Parameters` 或事件连接的对应 getter 取得标准 104 参数,无需自行维护宏副本。
|
||||
|
||||
<a id="flow-interrogation"></a>
|
||||
@@ -156,7 +167,7 @@ Slave_Create
|
||||
→ Interrogation_Destroy → Slave_Remove_Station → Slave_Destroy
|
||||
```
|
||||
|
||||
必须在开始消费Slave通知前创建总召模块,一个Slave只绑定一个实例。配置按值复制,Slave须存活到总召Destroy之后;Init失败也要释放已创建实例。所有接口串行调用;异步采样结果须回到所属事件循环再提交。模块不创建线程,也不会调用应用函数。
|
||||
必须在开始消费Slave通知前创建总召模块,同一Slave每种召唤各至多一个实例,Type100总召和Type101计数量可以独立并存。配置按值复制,Slave须存活到总召Destroy之后;Init失败也要释放已创建实例。所有接口串行调用;异步采样结果须回到所属事件循环再提交。模块不创建线程,也不会调用应用函数。
|
||||
|
||||
上面的顺序使用共用站点表,CA只在注册时指定。多目标广播先确认全部站,再逐站取数,最后终止全部站;各站BEGIN可先于首条FINISHED。应用可在首个NEED_DATA建立唯一取数游标,Complete_Data返回OK后复用;异常时按FINISHED清理。需保留context则按station_id查原节点并Acquire,使用结束后Release。删除立即退出路由,已有引用结束后才可释放存储;同CA重建不改变旧数据源。兼容应用可保留use_station_registry=false的单CA或固定station模式。详见[共用站点表](IEC104_API接口汇总.md#station-registry)。
|
||||
|
||||
@@ -244,17 +255,28 @@ COT/CA/IOA错误回45/46/47;激活的非法QOI、P/N、总召忙或应用拒
|
||||
|
||||
构建在Slave及依赖之外加入104/business/IEC104_Interrogation.c及104/business/IEC104_GeneralInterrogation.c,头文件搜索路径包含104/、104/business/和common/。静态容量宏为IEC104_INTERROGATION_CAPACITY(默认TCP实例容量的两倍)、IEC104_INTERROGATION_TASK_CAPACITY(默认4)、IEC104_INTERROGATION_EVENT_CAPACITY(默认4,最小2)。每实例4个任务槽共用于至多一个召唤及短应答,不是后续召唤队列。任务使用固定槽位和顺序索引,完成的非队首应答可立即释放槽位,其他任务的自引用不受影响。生成BEGIN时同时预留一个终结槽;未读业务事件挤满队列时暂停生成工作事件,已排队单帧应答仍可调度,取消仍可交付FINISHED,不使用malloc。
|
||||
|
||||
同时接入计数量时另编译104/business/IEC104_CounterInterrogation.c,创建独立实例,配置独占广播状态区。连接及启停通知必须同时交付两边;I帧先总召、再计数量,未接手再交校时等后续模块或兜底。每轮读空Slave通知后推进两个召唤实例,轮换先后顺序,合并need_process、events_pending及最小剩余期限。FRZ=0走确认、数据、终止;FRZ=1..3通过Execute_Begin执行一次本地同步动作,确认后直接FINISHED。两个实例分别Cancel、读取FINISHED、Destroy。动作失败及快照约定见[计数量设计](IEC104_CounterInterrogation_设计架构.md)。
|
||||
|
||||
本接口替换旧begin/read_batch/finish及parameter配置:应用使用NextEvent和显式应答,遍历状态自行持有。Destroy前须Cancel并读完业务事件。Process只接收实例,旧调用删除第二个预算实参。当前只保留一个未终结总召,忙时否定后续激活,不自动执行此前被拒绝的请求。已删除queue_timeout_ms、QUEUED/QUEUE_TIMEOUT、queued_interrogations和queue_timeouts;调用方删除相关字段及分支,使用Config_Default初始化配置并重新编译。negative_submitted和dropped_requests继续记录否定提交与满槽丢弃;FINISH_OVERLOAD保留枚举值,满槽不以此终结已有任务。
|
||||
|
||||
<a id="flow-counter-interrogation"></a>
|
||||
|
||||
#### 1.3.1 同时接入计数量召唤
|
||||
|
||||
另编译`104/business/IEC104_CounterInterrogation.c`,使用CounterInterrogation_Create创建独立实例,配置独占广播状态区;返回的句柄沿用Interrogation_Process/NextEvent/Cancel/Destroy驱动。连接和启停通知交付两个召唤实例,I帧先总召、再计数量,未接手再交校时、遥控或兜底。每轮读空Slave通知后轮换推进两个召唤实例,合并各自的need_process、events_pending和最近期限。
|
||||
|
||||
| 操作 | BEGIN之后的应用流程 | 模块收尾 |
|
||||
|---|---|---|
|
||||
| FRZ=0读取 | Reply_Begin接受;NEED_DATA按固定冻结快照构造批次,Submit_Batch;读完Complete_Data | ACT_CON、数据、ACT_TERM,随后FINISHED |
|
||||
| FRZ=1..3冻结/复位 | 用CounterInterrogation_Execute_Begin先校验有效BEGIN,再调用本次传入的同步动作一次;WAIT保留事件,STALE/ERROR不执行动作 | 动作结果决定肯定/否定ACT_CON,确认后FINISHED;不走NEED_DATA或ACT_TERM |
|
||||
|
||||
两个实例分别Cancel、读取FINISHED和Destroy;不能把计数量BEGIN交给总召实例应答。冻结/复位动作返回false必须保证未改变计数器,这与遥控ACTION_FAILED可有副作用的契约不同。详细分组、数据COT和快照约定见[计数量设计](IEC104_CounterInterrogation_设计架构.md)及[计数量API](IEC104_API接口汇总.md#file-23)。
|
||||
|
||||
<a id="flow-clock-synchronization"></a>
|
||||
|
||||
### 1.4 接入独立时钟同步模块
|
||||
|
||||
编译`104/business/IEC104_ClockSynchronization.c`并包含对应头文件,在Slave创建并初始化后、消费任何Slave通知前创建校时实例。配置只含源标准时区和执行期限,默认480分钟及15000ms;模块使用独立静态池和每实例4个任务槽,不与召唤引擎共享任务。详情见[时钟同步设计](IEC104_ClockSynchronization_设计架构.md)。
|
||||
编译`104/business/IEC104_ClockSynchronization.c`并包含对应头文件,在Slave_Create成功后、消费任何Slave通知前创建校时实例,可在Slave_Init前完成绑定,示例即采用此顺序。配置只含源标准时区和执行期限,默认480分钟及15000ms;模块使用独立静态池和每实例4个任务槽,不与召唤引擎共享任务。详情见[时钟同步设计](IEC104_ClockSynchronization_设计架构.md)。
|
||||
|
||||
1. 将CONNECTED、ACTIVATED、DEACTIVATED及DISCONNECTED按FIFO交付总召、计数量、校时所有实例。I帧按总召→计数量→校时分派,均IGNORED才用Reject_Request兜底;HANDLED也可能表示拒绝或满槽丢弃。
|
||||
1. 将CONNECTED、ACTIVATED、DEACTIVATED及DISCONNECTED按FIFO交付总召、计数量、校时及遥控所有实例。I帧按总召→计数量→校时→遥控分派,均IGNORED才用Reject_Request兜底;HANDLED也可能表示拒绝或满槽丢弃。
|
||||
2. `Handle_Event`校验Type 103的唯一对象、COT/CA/IOA、CP56/IV/TEST、源时区及本站日期范围。合法请求保存原文及accepted_ms,不设置时钟;广播在受理时建立目标快照。
|
||||
3. 读空Slave通知后调用`Process`推进队首应答和未执行请求期限。Process不调用action,不越过等待执行的队首提交后续应答。
|
||||
4. 应用明确准备执行时调用`Execute_Next(cs, action, context)`。入口检查原会话,使用同一单调采样检查期限及计算滞留补偿,向action交付对应base_ms的utc_ms和固定UTC+8 local_time。只调用最早EXECUTE一次;可以越过较早REPLY执行,但确认保持原FIFO位置。
|
||||
@@ -266,6 +288,235 @@ Execute结果须按枚举分支:WAIT先处理Slave通知,EMPTY只表示没
|
||||
|
||||
时间只补偿Handle_Event受理后的本站滞留;base_ms不是设备生效时刻,不补网络、通知FIFO和驱动耗时。新请求不按内容去重,重发旧时标可能再次回拨。Try不隔离新增NORMAL,持续发布可能使确认延迟;confirm_pending是应用协调依据,不是传输层预留或线程锁。
|
||||
|
||||
<a id="flow-command"></a>
|
||||
|
||||
### 1.5 接入单点、双点遥控模块
|
||||
|
||||
`IEC104_Command`接手Type45单点命令、Type46双点命令,提供选择、执行、撤销、期限和返回信息顺序管理。每个Slave至多一个实例,每实例至多一个未释放事务;不同站点的遥控也互斥。点表、闭锁、远方/就地、真实输出及设备忙状态由应用提供。模块使用静态池,不分配内存、不保存回调,只在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。
|
||||
|
||||
以下C函数只负责创建默认实例。调用方须检查返回NULL、清理已创建资源;成功后把句柄保存到所属事件循环,Slave寿命须覆盖Command_Destroy。
|
||||
|
||||
```c
|
||||
#include "IEC104_Command.h"
|
||||
|
||||
static IEC104_Command_t *Create_Command(IEC104_Slave_t *slave)
|
||||
{
|
||||
IEC104_CommandConfig_t config;
|
||||
IEC104_Command_Config_Default(&config);
|
||||
return IEC104_Command_Create(slave, &config);
|
||||
}
|
||||
```
|
||||
|
||||
| 配置 | 默认行为 | 修改时的约束 |
|
||||
|---|---|---|
|
||||
| type_mask | 接手45、46 | 不得为0;本版其他类型位使Create失败,未启用类型返回IGNORED |
|
||||
| select_required_mask | 45、46均须先选择 | 必须为type_mask子集;只清除允许直接执行类型的对应位 |
|
||||
| decision_timeout_ms | 15000ms | 从工作事件生成开始计时;0只禁用时长限制 |
|
||||
| select_timeout_ms | 30000ms | 不得为0;含选择确认等待发送的时间 |
|
||||
| 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,只表示流程结束 |
|
||||
|
||||
静态池容量由编译宏`IEC104_COMMAND_CAPACITY`控制,默认与TCP服务容量相同;每实例独立应答槽由`IEC104_COMMAND_REPLY_CAPACITY`控制,默认4。事务自带确认、终止及撤销/失配应答存储,独立槽处理忙拒绝和校验否定,不能用增加应答槽开启多个事务。重复绑定同一Slave、池满或配置非法时Create返回NULL。
|
||||
|
||||
<a id="flow-command-dispatch"></a>
|
||||
|
||||
#### 1.5.2 先分派Slave通知,再读取遥控事件
|
||||
|
||||
每轮先用`IEC104_Slave_NextEvent`按FIFO读到EMPTY。CONNECTED、ACTIVATED、DEACTIVATED、DISCONNECTED逐条交给所有已创建业务模块;不能因为前一个模块返回HANDLED就跳过其他模块的生命周期通知。I帧按总召、计数量、校时、遥控依次分派,只有全部IGNORED才调用44兜底。RECORD不驱动遥控工作状态。
|
||||
|
||||
| Command_Handle_Event结果 | 应用下一步 |
|
||||
|---|---|
|
||||
| EVENT_HANDLED | 此请求已由遥控接手;包括校验否定、忙拒绝、过载丢弃及决定窗口内同点激活的静默丢弃,不能再次兜底 |
|
||||
| EVENT_IGNORED | I帧可继续交给后续业务;未启用类型、未激活、视图不可用或旧代次可能走此结果 |
|
||||
| EVENT_ERROR | 记录来源、参数或服务错误并进入应用错误处理;不要把它当IGNORED继续执行命令 |
|
||||
|
||||
Handle_Event消费当前通知时只校验来源,不能在外层用`Check_Request==WAIT_EVENTS`挡住它:后续通知尚未出队正是必须继续分派的原因。合法遥控I帧在匹配前先检查当前事务期限;应用动作及发送推进则由模块在操作前检查会话,遇未读通知先让出。一次通知消费预算用完但尚未EMPTY时,先安排续读,暂不Accept或重试返回信息。
|
||||
|
||||
读空后调用`IEC104_Command_Process`,再循环`IEC104_Command_NextEvent`。Process的true包含空闲、等待或发送故障已转换为事务终结,不能据此判断设备成功;false按服务/用法错误处理。NextEvent的READY才写出新事件;EMPTY停止本轮读取,可能仍有应答或应用重试;ERROR不使用旧输出。已有FINISHED即使离线、或又出现未读Slave通知也可交付,必须继续消费。
|
||||
|
||||
工作事件为纯值,按值保存`IEC104_CommandEvent_t`即可,不需要Slave_Copy_Event。事件中的instance_id、transaction_id、event_serial及type一起校验;应用不得改写request来改变执行内容。SELECT和匹配后的EXECUTE属于同一事务、不同工作事件;各工作事件只交付一次,WAIT不会生成替代事件。
|
||||
|
||||
<a id="flow-command-decision"></a>
|
||||
|
||||
#### 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。
|
||||
|
||||
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。SELECT可传NULL,视为选择成功且select_effect=false;EXECUTE必须提供动作。action须短时返回,不推进Slave、不发布上报、不保留request指针;回调内只允许查询本实例Get_Status/Get_Transaction。异步设备工作由应用另存必要值和引用,设备结果回到同一事件循环提交。
|
||||
|
||||
| 动作结果 | 选择阶段 | 执行阶段 |
|
||||
|---|---|---|
|
||||
| NOT_STARTED | 否定确认,REJECTED;保证没有副作用 | 否定确认,REJECTED;不得后续调用Report_Result |
|
||||
| STARTED | 肯定确认,SELECTED,开始选择期限 | 肯定确认,EXECUTING,开始执行期限 |
|
||||
| FAILED或非法返回值 | 否定确认,ACTION_FAILED,置select_effect | 否定确认走Try,ACTION_FAILED,置execute_effect;可能已有变化 |
|
||||
|
||||
`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中的新激活仍忙拒绝,不破坏已有事务。
|
||||
|
||||
| Accept / Reject结果 | 是否可重试 |
|
||||
|---|---|
|
||||
| REPLY_OK | 工作事件已消费,不能重交;Reject也可能仍有待发送否定 |
|
||||
| REPLY_WAIT | 未消费、未调用动作;保留同一事件,先读通知后再试 |
|
||||
| REPLY_STALE | 已消费、身份/阶段失效或到期;停止旧操作,不能执行设备输出,继续处理FINISHED |
|
||||
| REPLY_ERROR | 参数/原因/重入、服务或确认资源错误;检查错误并修复或取消,不能当作背压盲目重试 |
|
||||
|
||||
决定窗口内丢弃的是同点合法激活,不要求内容完全重复,也不构成窗口外去重。已消费事件的重复Accept不会再调用动作;事务释放后,主站发来的相同命令仍按新请求处理,允许直接执行时可能再次输出。
|
||||
|
||||
<a id="flow-command-execution"></a>
|
||||
|
||||
#### 1.5.4 设备结果、返回信息与Complete
|
||||
|
||||
执行STARTED后,应用持有原EXECUTE事件作为三个收尾接口的令牌。设备完成时优先调用Report_Result(SUCCEEDED或FAILED):它不要求肯定确认已经交接,OK立即停止执行期限。不要因确认还在WAIT就拖延报告设备结果;报告以后确认或返回信息可以继续等待发送层。结果只能记录一次,不可把SUCCEEDED改为FAILED。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant M as 主站
|
||||
participant C as Command
|
||||
participant A as 应用事件循环
|
||||
participant D as 设备
|
||||
M->>C: 选择 S/E=1,COT=6
|
||||
C->>A: NextEvent:SELECT
|
||||
A->>C: Accept(选择动作)
|
||||
C-->>M: ACT_CON,S/E=1
|
||||
M->>C: 匹配执行 S/E=0,COT=6
|
||||
C->>A: NextEvent:EXECUTE
|
||||
A->>C: Accept
|
||||
C->>A: 本次执行动作
|
||||
A->>D: 启动输出
|
||||
A-->>C: STARTED
|
||||
C-->>A: REPLY_OK
|
||||
Note over C,A: 肯定执行确认可能尚在WAIT
|
||||
D-->>A: 设备结果
|
||||
A->>C: Report_Result,OK停止执行期限
|
||||
C-->>M: 肯定ACT_CON
|
||||
loop 零帧或多帧返回信息
|
||||
A->>C: Submit_Return_Info(WAIT保留原帧)
|
||||
C-->>M: OK后按序发送COT=11或3
|
||||
end
|
||||
A->>C: Complete
|
||||
C-->>M: 按配置发送ACT_TERM
|
||||
C->>A: NextEvent:FINISHED
|
||||
```
|
||||
|
||||
图示选择了“设备结果早于确认交接”的合法次序,通常确认也可能先交接;网络箭头抽象表示发送层交接顺序,不代表本地整帧写完或主站已ACK,FINISHED也不等待这两个节点。真实驱动的启动调用发生在Accept动作中,Report_Result、返回信息及Complete都在动作返回后的事件循环执行。直接执行跳过前面的选择过程。
|
||||
|
||||
返回信息由应用构造,使用`IEC104_Command_Submit_Return_Info(cmd, &execute_event, &asdu)`,不得混用旧SELECT事件。必须为监视方向Type1..21或30..40、至少一个完整对象、COT=11或3、P/N=0,CA/TEST与命令一致;当前TEST命令被拒绝,正常返回TEST=0。IOA可不同于命令IOA,应用决定受控对象映射;104参数必须2/2/3且ASDU不超过249字节。
|
||||
|
||||
模块先等肯定执行确认交接,并让模块内所有已有应答先行,再用原请求身份走Try。WAIT不缓存ASDU、不为它保留FIFO位置,应用须保存同一帧的完整原文及EXECUTE事件;OK后帧序已固定,才可复用缓冲、推进到下一帧。返回信息不进入NORMAL,因此不会被覆盖或留给新连接,但持续HIGH/NORMAL积压仍可能推迟Try。
|
||||
|
||||
Report_Result之前或之后都可提交返回信息;设备结果已经可用时,应优先报告以停止执行计时。所有返回信息都OK后调用Complete,允许零帧。Complete的OK立即关闭返回信息阶段,Process按配置交接ACT_TERM;不能因等待终止而重交Complete或已成功的返回信息。不发ACT_TERM时仍须等待肯定确认交接才终结;失败配置发ACT_TERM时P/N=0只表示流程结束。
|
||||
|
||||
| 接口 | OK | WAIT | STALE | ERROR |
|
||||
|---|---|---|---|---|
|
||||
| Report_Result | 结果已记录、期限停止 | 有未读通知,结果未记录 | 事务到期/结束或事件不符,停止旧结果提交 | 参数/结果非法、有效事务已报告或已Complete、重入或服务错误 |
|
||||
| Submit_Return_Info | 原ASDU已接手,不能重交 | 确认/FIFO/通知/发送条件未满足,保留原帧 | 事务/事件失效或发送失败终结,丢弃原连接重试 | ASDU/参数/服务错误,或Complete后尚待收尾;本次未接手 |
|
||||
| Complete | 返回阶段关闭,后续由模块收尾 | 有未读通知,本次未接手 | 事务结束或事件不符 | 尚未报告结果、有效事务重复调用、参数/重入/服务或终止资源错误 |
|
||||
|
||||
下面是应用持久化步骤的**伪代码**,其中“保存/等待设备/错误处理”为应用动作,不是库函数。不能在一次调用中遇WAIT后继续执行下一步:
|
||||
|
||||
```text
|
||||
保存原EXECUTE事件及应用站点引用
|
||||
等待设备结果时继续驱动Slave/Command,仍可提交已经产生的返回信息
|
||||
设备结果已知 → 步骤=REPORT
|
||||
REPORT: IEC104_Command_Report_Result
|
||||
OK → 步骤=RETURN;WAIT → 保存结果,返回事件循环
|
||||
STALE → 停止此事务重试,等待/处理FINISHED;ERROR → 应用错误处理
|
||||
RETURN: 尚有返回信息时 IEC104_Command_Submit_Return_Info
|
||||
OK → 只推进这一帧;WAIT → 保留同一ASDU,返回事件循环
|
||||
STALE → 停止此事务重试,等待/处理FINISHED;ERROR → 修复本帧或应用错误处理
|
||||
所有帧已OK或没有返回信息 → 步骤=COMPLETE
|
||||
COMPLETE: IEC104_Command_Complete
|
||||
OK → 步骤=WAIT_FINISHED;WAIT → 保留步骤,返回事件循环
|
||||
STALE → 停止此事务重试,等待/处理FINISHED;ERROR → 应用错误处理
|
||||
WAIT_FINISHED: 继续Process和NextEvent,不重复Complete,不再Submit_Return_Info
|
||||
```
|
||||
|
||||
动作FAILED或事务STALE后若实际位置仍发生变化,不能再用旧事务的Submit_Return_Info;应用按需要用Enqueue_Event、COT=3上报。同一变位不要既经本接口又经NORMAL重复发布。
|
||||
|
||||
<a id="flow-command-timeouts"></a>
|
||||
|
||||
#### 1.5.5 三类期限与发送等待
|
||||
|
||||
所有业务期限取`IEC104_Net_Now_Ms`,不读设备墙钟。阶段切换后只保留一个计时起点,边界相等即到期;时钟倒退即使在决定/执行期限配置0时也走到期处理并增加clock_errors。Get_Status只算剩余时间,不执行转换。
|
||||
|
||||
| 期限 | 起点 → 正常终点 | 到期处理 |
|
||||
|---|---|---|
|
||||
| 决定 | SELECT/EXECUTE事件生成(含未被应用取走的时间)→ Accept/Reject消费 | 否定7,DECISION_TIMEOUT;事务从未交付任何工作事件时不产生FINISHED |
|
||||
| 选择 | 选择动作返回后重新取时间 → Handle_Event受理匹配执行 | SELECT_TIMEOUT,丢弃模块尚持有的选择确认;已交发送层的迟到肯定确认无法撤回 |
|
||||
| 执行 | 执行动作返回STARTED后重新取时间 → Report_Result | EXECUTE_TIMEOUT,不发ACT_TERM;尚未交接的肯定执行确认照常提交 |
|
||||
|
||||
Handle_Event在有效遥控I帧的匹配前检查;Process、NextEvent及应用应答入口在允许推进时先检查会话,再检查期限。未读通知优先于这些推进,但不能挡住Handle_Event消费通知;已存在FINISHED直接交付。主站命令的“到达”指Handle_Event受理时刻,含Slave通知排队时间,不是网络成帧时间。
|
||||
|
||||
Accept/Reject后决定计时停止;Report_Result后执行计时停止,不因后续发送WAIT超时。选择期限有意包含选择确认的发送等待,限制返校最长保持时间。报告结果后漏调Complete会持续占槽,直到应用Cancel或会话结束;链路和本地发送期限仍独立生效,见[期限查询](#flow-deadlines)。
|
||||
|
||||
<a id="flow-command-abort"></a>
|
||||
|
||||
#### 1.5.6 撤销、STOP和发送故障
|
||||
|
||||
主站撤销使用COT=8,须匹配会话、station_id、IOA、类型和OA,不比较状态/QU;请求本身仍须先通过格式和对象值合法性校验。撤销选择不是STOPDT,不停用整个连接。
|
||||
|
||||
| 情况 | 应答和事务处理 |
|
||||
|---|---|
|
||||
| SELECT_WAIT、SELECTED、EXECUTE_WAIT匹配撤销 | 复用事务槽回肯定DEACT_CON(9),替换模块尚持有的确认,DEACTIVATED结束;是否FINISHED取决于是否曾交付工作事件 |
|
||||
| EXECUTING、ENDED或不匹配撤销 | 独立否定9,原事务不受影响;不能借撤销回滚已启动设备 |
|
||||
| 选择确认已由普通发送接口返回OK | 即使仍在HIGH也不能撤回,DEACT_CON排在它之后 |
|
||||
| DEACTIVATED通知(STOPDT) | 丢弃模块尚持有应答,STOPPED结束;后续START不恢复旧选择或执行令牌 |
|
||||
| DISCONNECTED / 新CONNECTED | 清理旧会话事务,原因DISCONNECTED / CONNECTION_CHANGED;旧返回信息不转到新连接 |
|
||||
| Cancel | CANCELLED结束,清空模块持有应答,会话保持原激活状态;不发送主站撤销确认 |
|
||||
| 发送ERROR,确认为原连接正常STOP且通知尚未分派 | 保留应答并让出,Submit_Return_Info返回WAIT;继续分派DEACTIVATED,由它终结为STOPPED |
|
||||
| 其他发送ERROR或FATAL | SEND_ERROR结束,清空模块持有应答;Submit_Return_Info返回STALE,FATAL可能已写出字节,不能重发 |
|
||||
|
||||
首次终结原因和FINISHED的工作请求不因后来的STOP/Cancel改变。FINISHED只提示应用结束协议交互,不隐式撤除返校、停止物理输出或回滚点值。设备仍忙时,应用必须拒绝后续不安全操作,即使模块事务槽已经释放。
|
||||
|
||||
<a id="flow-command-finished"></a>
|
||||
|
||||
#### 1.5.7 FINISHED配对、站点引用与退出
|
||||
|
||||
同一事务只要取走过SELECT或EXECUTE,就恰好产生一次FINISHED;从未交付工作事件便撤销/到期时直接移除事件。SELECT已读、匹配EXECUTE尚未读便结束仍需FINISHED,request是最后接纳的EXECUTE请求。撤销和失配请求只用于应答回显,不覆盖FINISHED的request;应用按instance_id与transaction_id清理资源,不根据新CA猜测归属。
|
||||
|
||||
COMPLETED/EXECUTION_FAILED在Complete后的最后应答交接后交付;其他原因可先于否定确认或DEACT_CON交接。模块释放槽需要同时满足“FINISHED已读或无需交付”和“事务应答全部交接/取消”。期间busy仍为true,所有站点的新遥控都被拒绝;独立短应答本身不占事务槽,但也须在Destroy前清空。
|
||||
|
||||
模块创建事务时Acquire站点、释放槽时Release;应用保留context或异步设备操作需要自己的引用。首个工作事件到来时先按request.station_id查原节点并Acquire,成功后才保存context与事务身份;后续匹配EXECUTE沿用同一引用。查找/Acquire失败走应用错误清理,不能仅按CA改绑到新站。站点删除后新请求回46,已有事务引用继续保护原节点。
|
||||
|
||||
`NextEvent(FINISHED)`可能在返回前释放模块引用,因此不能等读到FINISHED才申请应用引用。应用完成返校清理且物理输出确已结束后才Release;execute_effect=true遇超时、断线、发送错误时,设备可能仍在运行。Get_Transaction只查询当前仍占槽事务,刚读出的FINISHED若触发回收,查询返回false且不写输出,这是正常结果。
|
||||
|
||||
正常退出按以下顺序执行,完整服务关闭流程见[关闭释放](#flow-15):
|
||||
|
||||
1. 停止产生新动作,分别Cancel遥控及其他业务模块。
|
||||
2. 读取全部业务FINISHED;停止原事务重试,清理应用资源。真实输出尚未结束时继续完成应用设备清理和引用归还。
|
||||
3. Slave_Close,读空保留的Slave通知,把非I帧生命周期通知交给模块,不再受理新请求或执行动作。
|
||||
4. 再读空可能产生的FINISHED,Command_Destroy;false时检查未读事件、事务或独立应答,不能丢弃句柄假定成功。
|
||||
5. 所有业务和应用引用归还后Remove_Station,最后Slave_Destroy。
|
||||
|
||||
<a id="flow-command-schedule"></a>
|
||||
|
||||
#### 1.5.8 事件循环与挂起重试
|
||||
|
||||
示例[iec104_slave_example.c](../../examples/iec104_slave_example.c)由Example_Handle_Event分派通知,Example_Step先Command_Process,再Example_Drain_Command读取业务事件和重试应用步骤,随后推进召唤与校时。Example_Command_Action只改变模拟设备;Example_Drain_Command在动作外报告结果、构造并逐帧交付返回信息;FINISHED清标志和引用,保留已执行点值。
|
||||
|
||||
等待前重新Get_Status并合并应用状态:
|
||||
|
||||
| 调度条件 | 下一步 |
|
||||
|---|---|
|
||||
| events_pending或本轮Slave通知未读空 | 立即续读并分派Slave通知,不执行旧工作事件 |
|
||||
| pending_events非零 | 及时NextEvent,尤其读取可离线交付的FINISHED |
|
||||
| need_process=true | 安排本地Command_Process;仍按通知优先,不把它理解为必能发送 |
|
||||
| timer_active=true | 等待上限取timeout_remaining_ms与网络/应用上限的较小值,0即立即检查期限 |
|
||||
| 应用有可立即决定的工作事件、刚完成设备结果或未尝试的收尾步骤 | 立即进行相应应用接口;Process不会替应用报告结果或再次提交ASDU |
|
||||
| 应用步骤上次返回WAIT | 保留事件、结果/ASDU和步骤,允许网络等待;下一轮网络推进并读空通知后重试,不等待新的工作事件 |
|
||||
|
||||
Get_Status不执行超时,need_process=false也不免除每轮网络推进后的Process及应用重试。模块最多推进`2 * (1 + IEC104_COMMAND_REPLY_CAPACITY)`步,队首WAIT结束本轮;不能用紧循环反复重试Try。通知、业务FINISHED、应用设备完成是不同来源,都须纳入唤醒和等待策略;接入自有事件循环见[第14节](#flow-14)。
|
||||
|
||||
<a id="flow-2"></a>
|
||||
|
||||
## 2. 创建服务与接入主站
|
||||
@@ -508,6 +759,8 @@ 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)。
|
||||
|
||||
### 6.3 延迟处理、事件复制与连接寿命
|
||||
|
||||
`IEC104_TcpEvent_t` 只是值与字节数组,可以按值复制;`IEC104_SlaveEvent_t` 含指向自身的 ASDU 视图和 connection 上下文,**不能用结构赋值、memcpy 或搬移数组元素来保存**。事件使用期间地址必须稳定。
|
||||
@@ -526,7 +779,7 @@ Copy_Event 不发送报文,也不把历史请求重新绑定到当前主站。
|
||||
|
||||
READY 会替换传给 NextEvent 的整个输出;EMPTY/ERROR 保持输出原值。把事件地址交给待处理任务后,不应再用同一存储接收下一条事件。多个应答可以使用同一个原请求 connection,待发 ASDU 也可以单独构造,不必都复用 `event.asdu`。
|
||||
|
||||
需要精简任务存储时,另一种方式是保存`Slave_Get_Event_Identity`返回的纯值身份,以及业务需要的请求字段/字节。随后先处理通知并检查原请求,再用`Slave_Send_Response / Slave_Try_Send_Response`提交独立ASDU;失败需关闭原连接时用`Slave_Close_Connection_By_Identity`。这些入口保留原服务实例和原连接代次检查。总召采用此方式;完整事件及Copy_Event接口继续保留,容量及排序不因内部存储精简改变。
|
||||
需要精简任务存储时,另一种方式是保存`Slave_Get_Event_Identity`返回的纯值身份,以及业务需要的请求字段/字节。随后先处理通知并检查原请求,再用`Slave_Send_Response / Slave_Try_Send_Response`提交独立ASDU;失败需关闭原连接时用`Slave_Close_Connection_By_Identity`。这些入口保留原服务实例和原连接代次检查。总召、校时及遥控内部采用此类存储;完整事件及Copy_Event接口继续保留。应用接入遥控时只保存NextEvent交付的纯值CommandEvent,不能为延迟操作另造连接身份或绕过事务接口。
|
||||
|
||||
<a id="flow-7"></a>
|
||||
|
||||
@@ -538,6 +791,8 @@ READY 会替换传给 NextEvent 的整个输出;EMPTY/ERROR 保持输出原值
|
||||
|
||||
下图为普通事件应答路径。总召确认/终止经`IEC104_Slave_Send_Response`按保存的原身份进入同一普通HIGH路径;数据经`IEC104_Slave_Try_Send_Response → IEC104_TcpServer_Try_Send_Response → IEC104_Connection_Try_Submit_Response → IEC104_SendManager_Try_Submit_Response`:当前发送缓冲空闲、两级队列均无未提交条目且链路/窗口允许时才直接组帧;否则WAIT,不缓存HIGH。到期协议工作也先返回WAIT,由网络Process处理已到达的ACK。OK后沿同一发送缓冲、记录和非阻塞写入路径推进。
|
||||
|
||||
遥控采用不同的业务提交规则:选择确认、DEACT_CON及无执行副作用的否定走普通路径;执行动作返回STARTED/FAILED后的确认、返回信息和ACT_TERM走Try。模块内待发应答全局FIFO,应用返回信息还须等待模块已有应答清空,不能直接调用共用Send_ACT_TERM绕过Complete。普通HIGH的OK仅表示交接,Try的OK已经确定帧序;具体应用步骤见[执行收尾](#flow-command-execution)。
|
||||
|
||||
```text
|
||||
应用:IEC60870_MasterConnection_Send_ASDU / Send_ACT_CON / Send_ACT_TERM
|
||||
→ 共用层检查连接及函数指针,分派给 Slave 的发送适配器
|
||||
@@ -577,6 +832,8 @@ HIGH 是当前连接的应答队列。STOP 受理后尚留在 HIGH 的应答会
|
||||
|
||||
### 7.2 主动上报:NORMAL
|
||||
|
||||
独立变位可以走本节NORMAL路径。遥控引起且属于当前有效事务的返回信息使用Submit_Return_Info,避免覆盖和跨连接恢复,且保证在肯定执行确认之后、ACT_TERM之前。动作FAILED或原事务已经STALE后仍需报告设备变化时,应用另按COT=3上报;同一变化不能在两条路径重复发布。
|
||||
|
||||
```text
|
||||
应用:取得 Slave 的应用层参数
|
||||
→ 具体信息对象 Create()
|
||||
@@ -791,6 +1048,8 @@ I 帧受理后,Connection 从首个尚未确认 I 帧的成帧时刻记录 t2
|
||||
|
||||
对端主动发来的 TESTFR_ACT 走另一条应答路径:`IEC104_SendManager_Handle_Testfr_Act` 调用 `IEC104_Link_Handle_Testfr_Act`,生成 TESTFR_CON 并进入控制 FIFO。提前或没有匹配过程的 TESTFR_CON 不缓存为未来确认。
|
||||
|
||||
<a id="flow-deadlines"></a>
|
||||
|
||||
### 11.3 期限查询与执行
|
||||
|
||||
```text
|
||||
@@ -821,6 +1080,8 @@ TcpServer 查询等待条件
|
||||
|
||||
t2/t3 只有在控制容量和 Link 条件允许时才作为可调度动作,避免无容量时按已到期时间不断空转。协议时间使用单调毫秒时钟,Windows 后端通过 `IEC104_Net_Now_Ms` 提供;与业务的 CP56Time2a/RTC 时间分开。
|
||||
|
||||
上表只列传输期限。Command的决定/选择/执行期限由业务入口检查,Slave不会代它唤醒;应用须读取Command_Get_Status并缩短本轮等待,见[遥控三类期限](#flow-command-timeouts)和[自有事件循环](#flow-14)。设备结果已知时及时Report_Result,不能把确认发送等待误算成设备执行耗时。
|
||||
|
||||
<a id="flow-12"></a>
|
||||
|
||||
## 12. 容量不足时,流程在哪里等待
|
||||
@@ -833,16 +1094,19 @@ t2/t3 只有在控制容量和 Link 条件允许时才作为可调度动作,
|
||||
| 普通通知空间不足,只有已确认记录 | 记录暂留 Manager;不会单凭这项积压阻挡前面的 S/U 处理或 CON 发送 | 有普通空间和交付预算后转为 RECORD |
|
||||
| 启停专用槽满,下一 ACT 会改变状态 | 该 ACT 留在 Receiver,尚未改变 Link 状态,也不为它生成 CON | NextEvent 消费启停通知后,由 Process/Poll 重试受理 |
|
||||
| 控制帧槽满 | 需要预留槽的 START/TESTFR 请求暂缓,已有控制发送保留 | 既有帧完成并释放控制槽后重试 |
|
||||
| HIGH 空间不足 | 本次业务应答未接手,提交返回 WAIT | 应用保留原文,在连接和准入仍有效时重试 |
|
||||
| HIGH 空间不足 | 普通应答未接手,提交返回WAIT;独立业务模块保留自己的应答 | 模块Process重试;自定义业务自行保留原文。同步44兜底采用丢新策略,不盲目保存旧事件 |
|
||||
| k 窗口用尽 | 已提交发送继续等待确认,不为新业务分配 N(S) | 收到合法累计 ACK |
|
||||
| 发送记录槽用尽 | 即使有些帧已 ACK,也不能再建新 I 帧记录 | 已确认记录成功复制到事件 FIFO 后释放槽 |
|
||||
| socket 暂不可写 | 当前 APDU 和偏移保留 | 可写或适用的重试调度后继续 |
|
||||
| 遥控Try确认或ACT_TERM等待 | Command保留队首和全局FIFO序号,后面的忙拒绝也等待 | 网络推进、通知读空后仍调用Command_Process |
|
||||
| 遥控返回信息WAIT | 应用保留原EXECUTE事件及同一ASDU,模块未缓存也未预留排序位置 | 继续驱动网络/模块,再重试当前应用步骤;设备结果不因此延迟报告 |
|
||||
| 遥控FINISHED未读或最后事务应答未交接 | 唯一事务槽保持busy,新的遥控否定;独立应答池满则丢新计数 | 及时读取FINISHED并推进剩余应答,两条件满足后释放槽 |
|
||||
|
||||
其中,**启停槽满不再扣住已经生成的 CON**。限制发生在下一次改变状态的 ACT 受理之前;已有 CON 仍可续写。同状态重复 ACT 不需要新增启停通知槽,仍按自身控制发送条件处理。
|
||||
|
||||
不过,保留的 ACT 或 I 帧仍占据同一 TCP 字节流的前部,后续帧不能越过它。当前实现保留这个顺序约束,应用应及时消费通知。等待不直接等于断线,但不会延长原协议期限。
|
||||
|
||||
`NextEvent` 本身只复制、移除最早事件并安排后续工作,不执行接收、发送或协议受理。因此“读走一条通知”和“被暂缓的 ACT 得到受理”之间,还需要一次 Process/Poll 推进。
|
||||
`Slave_NextEvent`本身只复制、移除最早通知并安排后续工作,不执行接收、发送或协议受理。因此“读走一条通知”和“被暂缓的ACT得到受理”之间,还需要一次Slave_Process/Poll推进。业务模块的NextEvent具有各自契约,例如Command_NextEvent还会校验会话、检查期限或释放已结束槽,不能把这项结论套到所有同名入口。
|
||||
|
||||
通知 FIFO 保证已经入队的通知逐条交付。启停变化不合并、不覆盖;但 ACK 回执若因普通容量或预算暂留内部,可能晚于随后入队的启停通知。不能把所有 RECORD 与启停事件理解为严格按网络发生时刻排序。
|
||||
|
||||
@@ -929,35 +1193,48 @@ DISCONNECTED(旧 generation)
|
||||
|
||||
NORMAL 恢复依据是仍保留的原文,不会恢复已经覆盖的旧条目。未 ACK 不保证对端没有收到,因此跨连接恢复可能重复,业务应按自身协议处理重复。
|
||||
|
||||
遥控选择、EXECUTE令牌和尚未提交的返回信息均不跨连接恢复。应用分派旧DISCONNECTED并处理FINISHED后,才能按新工作事件建立新事务;同CA重建站点也不能替换旧station_id。设备输出是否完成与网络重连独立,应用仍须维护忙状态和引用,见[异常终结](#flow-command-abort)。
|
||||
|
||||
<a id="flow-14"></a>
|
||||
|
||||
## 14. 接入应用自己的事件循环
|
||||
|
||||
独立循环使用 `IEC104_Slave_Poll` 即可;已有 select、GUI 或其他统一事件循环时,使用 `IEC104_Slave_Prepare / Process` 替代 Poll。Slave 复用 TCP 层的调度值类型,不需要应用取得私有 TcpServer。
|
||||
|
||||
下面是包含独立业务模块的循环伪代码。网络、业务、应用设备完成分别产生工作;四类业务的推进方式见[总召](#flow-interrogation)、[计数量](#flow-counter-interrogation)、[校时](#flow-clock-synchronization)及[遥控调度](#flow-command-schedule)。按各接口自己的结果类型判断:Slave_Process等比较IEC104_TCP_OK,业务Process/Get_Status的false进入错误处理,不能把失败当作本轮已读空或已提交。Get_Transaction的false也可能只是事务槽已正常回收,须按该查询的契约处理。
|
||||
|
||||
```text
|
||||
1. IEC104_Slave_NextEvent()
|
||||
先处理已有业务通知,逐条读到 EMPTY。
|
||||
READY → 按FIFO分派;EMPTY → 进入第2步;ERROR → 保存错误并清理。
|
||||
通知预算用完但未EMPTY → 立即续读,不执行旧业务动作。
|
||||
|
||||
2. IEC104_Slave_Prepare(slave, &info, &error)
|
||||
取得 service_token、socket/token、关注位、定时条件。
|
||||
2. 推进已启用的业务模块并处理应用工作:
|
||||
Command_Process → Command_NextEvent → 工作事件Accept/Reject、设备结果Report_Result、
|
||||
挂起Submit_Return_Info/Complete重试及FINISHED清理。
|
||||
两个Interrogation实例轮换Process/NextEvent/显式应答;校时Process/Execute_Next。
|
||||
每个WAIT保留当前交互;若期间产生Slave通知,先回第1步。
|
||||
|
||||
3. 判断是否能等待:
|
||||
pending_event_count > 0 → 继续消费事件,再重新 Prepare。
|
||||
need_process != 0 → 使用当前 service_token 构造本地空批次,
|
||||
调用 Process;之后处理事件并重新 Prepare。
|
||||
其余情况 → 更新完整 socket 注册集合及一次性定时器,
|
||||
与应用其他任务一起等待。
|
||||
3. IEC104_Slave_Prepare(slave, &info, &error),成功后查询各业务Get_Status。
|
||||
取得service_token、socket/token、关注位、网络与业务的定时条件。
|
||||
|
||||
4. 将本轮 socket 就绪和定时到期汇成一个 IEC104_TcpReady_t:
|
||||
4. 合并本轮等待:
|
||||
Slave有pending_event_count或业务events_pending → 回第1步。
|
||||
Slave need_process → 用当前service_token构造本地空批次,Process后回第1步。
|
||||
业务有pending_events/need_process、校时execute_pending,或设备结果/未尝试的应用步骤就绪
|
||||
→ 本地续跑第2步。
|
||||
其余情况 → 更新完整socket注册集合;等待上限取网络、全部业务剩余期限、应用上限的最小值。
|
||||
应用步骤上次WAIT不单独强制0等待;网络推进后重试,不忙轮询。
|
||||
|
||||
5. 将本轮 socket 就绪和网络定时到期汇成一个 IEC104_TcpReady_t:
|
||||
同一 socket 的读写位合并。
|
||||
token 使用实际注册时保存的值。
|
||||
timer_expired 只在定时器真正到期时置 1。
|
||||
timer_expired只在Slave_Prepare报告的网络期限真正到期时置1。
|
||||
只有业务期限或设备完成唤醒时,不伪造socket就绪/网络超时,可使用本地空批次。
|
||||
|
||||
5. IEC104_Slave_Process(slave, &ready, &error)
|
||||
6. IEC104_Slave_Process(slave, &ready, &error)
|
||||
不等待,有界推进当前输入、发送、期限和接入。
|
||||
|
||||
6. 回到第 1 步。
|
||||
7. 回到第1步;业务等待条件需重新查询,不能仅重用上轮快照。
|
||||
```
|
||||
|
||||
本地唤醒可使用以下字段组合,不伪造网络就绪:
|
||||
@@ -974,18 +1251,22 @@ ready.service_token = info.service_token;
|
||||
|
||||
`timer_active == 1 && timeout_ms == 0` 表示应立即调度;`timer_active == 0` 表示没有定时器,两者不能混淆。`UINT32_MAX` 仍是有限毫秒数。超时从 Prepare 查询时刻计起,若中途处理其他任务耗时较长,应在真正等待前重新 Prepare。采用边沿或一次性通知的事件循环也必须落实 `need_process`,并按最新快照重新注册,避免预算让出后无人续跑。
|
||||
|
||||
业务剩余时间用各自Get_Status重新采样,遥控对应`timeout_remaining_ms`;Slave_Prepare不会自动合并它们。遥控Report_Result后的应用收尾可能没有业务期限,必须靠设备完成或挂起步骤状态继续驱动,不能只等网络定时器。FINISHED可以离线读取,清理资源不以连接激活为前提。
|
||||
|
||||
旧服务 token 的整批通知被忽略;已关闭或替换 socket 的旧注册通知也被忽略。不要把旧通知的 token 换成新值,否则会破坏隔离。
|
||||
|
||||
<a id="flow-15"></a>
|
||||
|
||||
## 15. 整个服务关闭与对象释放
|
||||
|
||||
接入总召模块时,先Interrogation_Cancel,再读取所有业务FINISHED并清理应用资源;最后在Slave_Destroy之前释放总召实例。总召的业务FIFO和Slave的通知FIFO须分别读空,详见[总召接入](#flow-interrogation)。
|
||||
关闭前先停止发起新业务,并分别取消所有已创建模块:总召/计数量用Interrogation_Cancel,校时用ClockSynchronization_Cancel,遥控用Command_Cancel。召唤和遥控仍可能欠应用FINISHED,须分别读取并清理引用;校时取消不回滚已经设置的时钟。遥控FINISHED只表示协议结束,物理输出仍忙时须继续应用设备清理,见[遥控引用与退出](#flow-command-finished)。
|
||||
|
||||
使用共用站点表时,还须归还业务引用、结束全部快照、解除集合锁,并Remove所有注册节点后才可Slave_Destroy。节点Is_Reusable为true后才可释放;Close/Init保留站点表,不能用网络关闭代替业务清理。
|
||||
|
||||
```text
|
||||
应用停止提交新业务
|
||||
→ 各业务Cancel
|
||||
→ 总召/计数量/遥控各自NextEvent读完FINISHED,清理应用资源和已完成设备操作的引用
|
||||
→ IEC104_Slave_Close() → IEC104_TcpServer_Close()
|
||||
├─ 当前客户端按 Drop_Client 流程收尾
|
||||
├─ 关闭监听 socket
|
||||
@@ -993,14 +1274,18 @@ ready.service_token = info.service_token;
|
||||
└─ IEC104_Net_Cleanup()
|
||||
→ 应用撤销自有事件循环中的 socket 注册和定时器
|
||||
→ IEC104_Slave_NextEvent()
|
||||
逐条处理保留下来的事件,直到 EMPTY
|
||||
逐条读空保留通知,向业务模块分派非I帧生命周期通知;不再受理新请求或执行动作
|
||||
→ 再读完业务FINISHED,完成必要设备清理和应用引用归还
|
||||
→ Interrogation_Destroy(总召/计数量各自)、ClockSynchronization_Destroy、Command_Destroy
|
||||
每个成功后才丢弃句柄;失败须检查活动任务、未读事件或未清空应答
|
||||
→ 结束站点快照/集合锁,归还引用,Remove全部站点
|
||||
→ IEC104_Slave_Destroy()
|
||||
├─ IEC104_TcpServer_Destroy(),归还私有 TCP 槽
|
||||
└─ 归还 Slave 槽,保留不回绕的实例编号历史
|
||||
→ 应用保存的句柄置 NULL
|
||||
```
|
||||
|
||||
Close 不等待业务发送或 ACK,保留未读事件,但不保留 NORMAL 缓存。Destroy 要求服务已经关闭或尚未 Init,且事件已读空;不会隐式丢弃事件或替调用者关闭运行中的服务。
|
||||
Slave_Close不等待业务发送或ACK,保留未读通知,但不保留NORMAL缓存。Slave_Destroy要求服务已经关闭或尚未Init、通知已读空且站点资源已清理;不会代替业务Cancel/Destroy。Command_Destroy另要求事务槽、FINISHED及独立应答槽均空,Cancel后仍须读完FINISHED;不能把网络已关闭当作所有对象已释放。
|
||||
|
||||
Close 后读空事件,也可以再次 Init 重开;新服务和连接的身份编号不复用旧编号。Init 失败时句柄仍归应用持有,可处理错误后重试或 Destroy。
|
||||
|
||||
@@ -1036,6 +1321,10 @@ Close 后读空事件,也可以再次 Init 重开;新服务和连接的身
|
||||
```text
|
||||
子站入口与身份绑定:IEC104_Slave.c
|
||||
站总召/分组总召、撤销、业务期限与事件:104/business/IEC104_Interrogation.c
|
||||
Type100种类规则:104/business/IEC104_GeneralInterrogation.c
|
||||
计数量读取/冻结/复位:104/business/IEC104_CounterInterrogation.c
|
||||
Type103校时校验与显式动作:104/business/IEC104_ClockSynchronization.c
|
||||
Type45/46选择执行、结果及返回信息:104/business/IEC104_Command.c
|
||||
共用发送接口分派: IEC60870_Slave.c → IEC104_Slave.c 内的发送适配器
|
||||
ASDU 与信息对象: IEC60870_Asdu.c → IEC60870_InformationObjects.c
|
||||
监听和网络调度: IEC104_TcpServer.c
|
||||
@@ -1048,5 +1337,5 @@ ASDU 与信息对象: IEC60870_Asdu.c → IEC60870_InformationObjects.c
|
||||
平台非阻塞 I/O: IEC104_Net_Windows.c
|
||||
```
|
||||
|
||||
库提供命令、校时、总召、参数和文件等信息对象的编解码,以及响应提交接口。独立召唤引擎已实现站/分组总召和计数量读取/冻结/复位,应用通过NextEvent与显式应答管理数据源。独立ClockSynchronization已实现Type 103校验、补偿、期限及原文确认,仅由Execute_Next调用应用动作。遥控、参数和文件业务尚未接入;业务级权限及硬件操作仍由应用提供。Time的编码转换、Clock的墙上时间与Net的协议单调时钟属于不同用途;默认示例校时只更新模拟值。
|
||||
库提供命令、校时、总召、参数和文件等信息对象的编解码,以及响应提交接口。召唤引擎已实现站/分组总召和计数量读取/冻结/复位;ClockSynchronization已实现Type103校验、补偿、期限及原文确认;Command已实现Type45/46选择执行、撤销、三类期限和按事务提交返回信息。遥调47..51、带时标命令58..64、参数和文件业务尚未接入,不能把对象编解码支持等同于完整业务支持。业务权限和硬件操作仍由应用提供;Time转换、Clock墙上时间及Net单调计时用途不同,默认示例只更新模拟时钟和模拟遥控点值。
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
> **同步日期**:2026-09-23。发送模块已改为变长ASDU队列、发送前编号及单当前I帧。严格警告编译、原有119个用例及临时专项校验通过;测试源码、配置和测试文档保持原样。
|
||||
> **当前接口同步**: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-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)。
|
||||
> **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副本。
|
||||
@@ -469,7 +470,9 @@ 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) | 待实现业务模块 | 公共信息对象已提供编解码;尚未接入选择执行、设备动作和时标策略 |
|
||||
| 实际遥信/遥测数据源、输出及设备设时 | 应用接入层 | 按业务模块请求读取点表或执行设备动作;示例提供模拟点表、遥控输出和模拟时钟,协议库不自动操作硬件 |
|
||||
|
||||
### 10.2 统一连接上下文
|
||||
|
||||
@@ -525,6 +528,7 @@ I帧完成接口要求快照的 `N(S)==sent_vs`,只推进本地完成边界和
|
||||
| 1(已实现接收基础) | Receiver独立实例、完整帧视图、消费数和显式释放;Link由连接层共享 | 两个接收实例可交错输入;待处理帧不被后续Feed覆盖,旧代次调用被拒绝 |
|
||||
| 2(已实现) | 接入发送前准备、部分写入续传及独立确认记录,补齐实际I/O和收发串行调度 | 发出 `N(S)=0` 后收到 `N(R)=1` 能正确确认并释放原ASDU;发送失败不重复推进状态,旧确认不清除新增待确认数据 |
|
||||
| 3(已实现) | 连接生命周期、U 控制、定时器及窗口策略 | 受控传输与时间事件下可验证启动、停止、心跳、超时和窗口边界;序号错误停止当前批次并关闭连接 |
|
||||
| 4(对象及三类业务已实现) | 公共对象编解码、总召/计数量、校时校验及显式执行;继续接入真实数据源和遥控 | 自动回归覆盖地址路由、召唤进度、校时原文确认及时间边界;模拟模式不设系统时间,硬件设时和实际主站联调须另行验收 |
|
||||
| 4(对象及四类业务已实现) | 公共对象编解码、总召/计数量、校时及Type45/46遥控;继续接入真实数据源和设备输出 | 验证地址路由、召唤进度、校时原文确认、遥控选择执行/撤销/期限及返回信息顺序;模拟模式不设系统时间,硬件设时、实际输出和主站联调须另行验收 |
|
||||
| 5(待实现) | 遥调47..51及带时标命令58..64 | 补充按类型匹配、时标解码及延迟策略,再扩展应用接口与回归 |
|
||||
|
||||
---
|
||||
|
||||
@@ -36,6 +36,8 @@ Slave提供[共用站点表](IEC104_API接口汇总.md#station-registry):应
|
||||
| `business/IEC104_Interrogation.c/h` | 共用召唤引擎:任务、广播、期限、事件、取消与发送等待;接入见[总召流程](IEC104_完整调用流程.md#flow-interrogation) |
|
||||
| `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) |
|
||||
| `IEC104_TcpTypes.h` | Slave与TcpServer共用的配置、调度、事件和状态值类型,无服务操作接口 |
|
||||
| `IEC60870_InformationObjects.c/h` | 67种独立信息对象,覆盖监视、命令、初始化、参数和文件;编码通过只读函数表分派,支持变长文件段 |
|
||||
| `IEC60870_Time.c/h` | 101/104共用时间工具:CP16/CP24/CP56Time2a编解码、本地标准时间转换、SU修正、日历/星期和纪元秒数工具;使用约定见[时间工具设计](../common/IEC60870_Time_设计架构.md) |
|
||||
@@ -68,6 +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显式执行动作,应用报告结果和提交返回信息 |
|
||||
| 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`;具体芯片驱动暂不提供 |
|
||||
@@ -125,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、分组及遍历状态;其他命令的选择/执行、校时权限及业务任务由应用接入。
|
||||
业务包含`IEC104_Slave.h`,取得`Slave_NextEvent`交付的I帧后可直接使用ASDU视图并解析具体对象,或先构造ASDU再交给发送接口。ASDU编解码层不分配内存、不操作连接、不读取或设置系统时钟;共用连接接口可构造并提交确认/终止应答。站/分组总召可交给IEC104_Interrogation管理校验、分批、撤销、期限和重试,应用响应业务事件提交数据,自行维护IOA、分组及遍历状态;Type45/46的选择/执行由IEC104_Command管理,Type103由独立校时模块管理,权限策略及设备动作仍由应用接入。
|
||||
|
||||
| 层次 | 职责 |
|
||||
|---|---|
|
||||
@@ -321,6 +324,14 @@ 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兜底。
|
||||
|
||||
Handle_Event校验并生成SELECT/EXECUTE事件,Accept先消费事件再至多调用一次本次传入的动作;不保存回调。EXECUTE启动后先Report_Result记录设备结果,再逐帧Submit_Return_Info,全部OK后Complete。确认未交接时仍可报告结果以停止执行期限;返回信息WAIT时由应用保存ASDU重试。执行确认、返回信息与ACT_TERM走Try,模块内应答按全局FIFO提交。
|
||||
|
||||
已取走工作事件的事务恰好产生一次FINISHED;FINISHED只结束协议事务,应用自行维护尚未结束的物理输出和站点引用。退出时Cancel、读完FINISHED,再关闭Slave并分派余下通知,最后Destroy。完整规则见[遥控设计](IEC104_Command_设计架构.md)、[API第25节](IEC104_API接口汇总.md#file-25)和[遥控调用流程](IEC104_完整调用流程.md#flow-command)。
|
||||
|
||||
## 启停主流程
|
||||
|
||||
| 角色与过程 | 调用顺序 |
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
## 推荐阅读顺序
|
||||
|
||||
1. [模块说明与接入示例](104/README.md):公共 ASDU、子站统一入口及模块依赖。
|
||||
2. [子站示例说明](examples/IEC104子站示例.md):构建运行、模拟点表、总召、计数量与校时处理。
|
||||
2. [子站示例说明](examples/IEC104子站示例.md):构建运行、模拟点表、总召、计数量、校时与遥控处理。
|
||||
3. [完整调用流程](104/IEC104_完整调用流程.md):从应用入口到协议收发的调用关系。
|
||||
4. [API 接口汇总](104/IEC104_API接口汇总.md):按文件查阅接口。
|
||||
5. [测试说明](test/IEC104测试文档.md):测试入口、构建命令、覆盖范围与验证记录。
|
||||
@@ -23,8 +23,9 @@
|
||||
| 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_完整调用流程.md) | 各层调用顺序与业务流程 |
|
||||
| 104 | [API 接口汇总](104/IEC104_API接口汇总.md) | 104 及其依赖的公共接口 |
|
||||
| 公共 | [时间工具设计](common/IEC60870_Time_设计架构.md) | CP16/CP24/CP56、日历转换与时钟接口 |
|
||||
| 示例 | [子站示例说明](examples/IEC104子站示例.md) | 编译运行、命令参数、点表与总召接入 |
|
||||
| 示例 | [子站示例说明](examples/IEC104子站示例.md) | 编译运行、命令参数、点表及四类业务接入 |
|
||||
| 测试 | [测试说明与验证记录](test/IEC104测试文档.md) | 公共模块、104 及真实 TCP 回环测试 |
|
||||
|
||||
@@ -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个标度化遥测值;分组1(QOI=21)只返回遥信,分组2(QOI=22)只返回遥测。库支持全部16组,示例未配置的其余组由应用拒绝。通信通过 `IEC104_Slave`,总召通过 `IEC104_Interrogation` 接入,所有任务使用固定存储、在主循环线程串行推进。示例另含计数量召唤和Type 103校时;默认时钟为模拟值,实际设备数据源及遥控仍由应用接入。
|
||||
当前实现站总召(`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遥控;默认时钟为模拟值,遥控只更新内存点值,实际设备数据源和物理输出仍由应用接入。
|
||||
|
||||
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)。
|
||||
|
||||
@@ -24,11 +24,11 @@ config.broadcast_capacity = sizeof(gi_stations) / sizeof(gi_stations[0]);
|
||||
|
||||
FRZ=1冻结并将序号模32加一;FRZ=2另将当前值清零;FRZ=3仅清当前值,冻结值和序号不变。动作通过Execute_Begin执行一次;TEST动作拒绝。FRZ=0读冻结值并发送ACT_TERM;FRZ=1..3只回ACT_CON后结束。CY/CA/IV在模拟点表中为0,不模拟真实计量品质。STOP统计原字段保持总召含义,末尾增加counter_submitted和counter_completed。
|
||||
|
||||
主循环向总召、计数量、校时实例广播生命周期通知,交替推进召唤业务并显式执行校时;其他类型在三个模块均未接手后兜底。原Example_Init仍可用于仅总召测试,计数量及校时通过各自初始化辅助函数补充,两个召唤实例各需独立广播状态区,校时使用共用站点快照。协议及应用动作限制见[计数量设计](../104/IEC104_CounterInterrogation_设计架构.md)和[时钟同步设计](../104/IEC104_ClockSynchronization_设计架构.md)。
|
||||
主循环向总召、计数量、校时、遥控实例广播生命周期通知,读空Slave通知后优先推进遥控,再交替推进召唤业务并显式执行校时;其他类型在四个模块均未接手后兜底。原Example_Init仍可用于仅总召测试,命令行入口分别调用计数量、校时及Example_Init_Command补齐实例,两个召唤实例各需独立广播状态区,校时使用共用站点快照。协议及应用动作限制见[计数量设计](../104/IEC104_CounterInterrogation_设计架构.md)、[时钟同步设计](../104/IEC104_ClockSynchronization_设计架构.md)和[遥控设计](../104/IEC104_Command_设计架构.md)。
|
||||
|
||||
在项目根目录的 PowerShell 中执行,需要 MinGW GCC:
|
||||
|
||||
示例通过 `IEC104_Interrogation.h`、`IEC104_CounterInterrogation.h` 和 `IEC104_ClockSynchronization.h` 引入业务声明;构建脚本添加 `104/`、`104/business/` 和 `common/` 头文件搜索路径,编辑器也应配置这三个目录。构建可执行程序仍使用下面的脚本,它会链接所需库源文件、Clock适配及Windows网络库;只编译当前C文件会缺少库函数实现。编辑器的C语言标准应与构建保持一致,使用C11,并选择实际的MinGW GCC编译器。
|
||||
示例通过 `IEC104_Interrogation.h`、`IEC104_CounterInterrogation.h`、`IEC104_ClockSynchronization.h` 和 `IEC104_Command.h` 引入业务声明;构建脚本添加 `104/`、`104/business/` 和 `common/` 头文件搜索路径,编辑器也应配置这三个目录。构建可执行程序仍使用下面的脚本,它会链接所需库源文件、Clock适配及Windows网络库;只编译当前C文件会缺少库函数实现。编辑器的C语言标准应与构建保持一致,使用C11,并选择实际的MinGW GCC编译器。
|
||||
|
||||
```powershell
|
||||
./scripts/build.ps1
|
||||
@@ -53,6 +53,8 @@ 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`,分别表示已接受的选择、已启动的执行、成功完成事务、已交接应答和返回信息帧数;保留原有统计字段的含义。
|
||||
|
||||
### 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,不返回本站当前时间。
|
||||
@@ -61,27 +63,46 @@ FRZ=1冻结并将序号模32加一;FRZ=2另将当前值清零;FRZ=3仅清当
|
||||
|
||||
确认发送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)。
|
||||
|
||||
| 命令 | 受控点及返回信息 | 初值与总召 |
|
||||
|---|---|---|
|
||||
| Type45,IOA6001..6008 | Type1,IOA1001..1008,COT=11;状态0分/1合 | 初值见下表,执行后总召读取更新值 |
|
||||
| Type46,IOA6101..6108 | Type3,IOA1101..1108,COT=11;状态1分/2合 | 初值全部为1;执行后站总召及遥信分组读取更新值 |
|
||||
|
||||
表中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。
|
||||
|
||||
`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;设备结果可以早于确认交接。
|
||||
|
||||
每一步WAIT都保留原事件和当前步骤;返回信息使用独立ASDU缓冲,WAIT时保留同一帧,OK后才推进,避免重复提交。STALE停止该事务重试,继续读取FINISHED。FINISHED清除模拟返校/输出标志并Release应用引用,不恢复已修改的点值;真实输出若仍未停止,应用必须继续保持设备忙状态和引用。Example_Wait_Time同时计入遥控期限、模块推进和应用挂起重试,发送WAIT时允许等待网络而不空转。
|
||||
|
||||
## 主站操作和模拟点表
|
||||
|
||||
主站先建立TCP并发送 `STARTDT_ACT`,随后发送总召:Type=100、SQ=0、对象数=1、COT=6、P/N=0、CA匹配、IOA=0,QOI=20选择全站,21/22选择本例的两个分组。TEST请求同样只读取模拟点表,应答保留TEST和OA。
|
||||
|
||||
| IOA | 类型 | 模拟值(按地址顺序) |
|
||||
| IOA | 类型 | 模拟初值(按地址顺序) |
|
||||
|---|---|---|
|
||||
| 1001..1008 | 单点信息 `M_SP_NA_1`,Type=1 | 0、1、0、1、1、0、1、0 |
|
||||
| 1101..1108 | 双点信息 `M_DP_NA_1`,Type=3 | 1、1、1、1、1、1、1、1 |
|
||||
| 2001..2008 | 标度化测量值 `M_ME_NB_1`,Type=11 | 2300、2310、2290、5000、-125、0、1234、42 |
|
||||
|
||||
所有品质为0。遥测值为原始有符号16位标度值,本示例没有定义物理单位和倍率。
|
||||
|
||||
一次成功站总召依次提交6个ASDU:
|
||||
一次成功站总召依次提交8个ASDU:
|
||||
|
||||
1. Type=100,COT=7,肯定激活确认。
|
||||
2. 两帧Type=1、COT=20的单点数据,每帧4个对象。
|
||||
3. 两帧Type=11、COT=20的标度化遥测,每帧4个对象。
|
||||
4. Type=100,COT=10,激活终止。
|
||||
3. 两帧Type=3、COT=20的双点数据,每帧4个对象。
|
||||
4. 两帧Type=11、COT=20的标度化遥测,每帧4个对象。
|
||||
5. Type=100,COT=10,激活终止。
|
||||
|
||||
分组总召各提交4个ASDU:ACT_CON、两批对应类型数据、ACT_TERM。分组1数据为Type=1/COT=21,分组2数据为Type=11/COT=22;确认和终止保留请求QOI。BEGIN校验分组,首个NEED_DATA按该组初始化应用游标,不把模拟点表写进库。
|
||||
分组1(QOI=21)共提交6个ASDU:ACT_CON、两帧Type=1单点数据、两帧Type=3双点数据、ACT_TERM,数据COT均为21,每帧4个对象。分组2(QOI=22)仍共提交4个ASDU:ACT_CON、两帧Type=11/COT=22遥测、ACT_TERM,每帧4个对象。确认和终止保留请求QOI。BEGIN校验分组,首个NEED_DATA按该组初始化应用游标,不把模拟点表写进库。
|
||||
|
||||
全部应答使用原请求的连接身份。总召确认、终止及短应答走普通HIGH路径,总召数据由Submit_Batch尝试不排队提交;校时调用过action的确认也使用Try。未提交HIGH/NORMAL、当前帧或窗口背压时Try返回WAIT;校验错误仍走普通HIGH。总召数据不使用Enqueue_Event,只有本批接手后才推进游标及后续终止。
|
||||
全部应答使用原请求的连接身份。总召确认、终止及短应答走普通HIGH路径,总召数据由Submit_Batch尝试不排队提交;校时调用过action的确认也使用Try。遥控执行动作产生副作用后的确认、返回信息和ACT_TERM均走Try,选择确认及校验否定走普通HIGH。未提交HIGH/NORMAL、当前帧或窗口背压时Try返回WAIT;遥控的模块FIFO还会保留更早应答优先。总召数据和遥控返回信息不使用Enqueue_Event,只有本帧接手后才推进应用步骤。
|
||||
|
||||
主站撤销使用相同的实际站CA/OA/TEST/QOI,COT改为8、P/N=0。激活和撤销均接受本站地址或65535,匹配时65535映射为本站地址,允许广播激活配本站撤销及本站激活配广播撤销。尚有匹配总召时模块停止任务、回复肯定COT=9,并通过FINISHED(DEACTIVATED)通知应用;不匹配或已完成则否定COT=9。已提交发送层的帧可能仍在确认前发出,撤销后不再提交旧批次和ACT_TERM。示例点数很少,总召可能在主站发撤销前已全部提交;观察中途撤销和各阶段拥塞可使用下面的可控网络测试。
|
||||
|
||||
@@ -105,7 +126,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现在由校时模块接手,102仍回44;模块之间不承诺跨业务应答按接收顺序排列。
|
||||
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;模块之间不承诺跨业务应答按接收顺序排列。
|
||||
|
||||
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)。
|
||||
|
||||
@@ -113,7 +134,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列表。示例常量表只用于展示业务接入,不是库规定的地址范围。逐批采样不保证整轮同一时刻,也不自动解决与突发上报的新旧排序;完整边界见[总召模块设计](../104/IEC104_Interrogation_设计架构.md#6-数据批次与发送路径)。
|
||||
本例BEGIN只判断接受,首个NEED_DATA初始化游标并读取这一批;接真实数据源时,在Example_Build_Batch的逐点组包处读取当前值和品质。IOA、类型、分组成员及遍历策略均由调用者决定,协议库不登记或冻结完整IOA列表。示例单点常量表保存地址、初值及品质,双点初值均为1;单点和双点实际值分别存放在ExampleApp.signal_values和ExampleApp.double_values中,站总召及遥信分组逐批读取这些可写数组的当前值,包括遥控执行后的更新;这些地址不是库规定的范围。逐批采样不保证整轮同一时刻,也不自动解决与突发上报的新旧排序;完整边界见[总召模块设计](../104/IEC104_Interrogation_设计架构.md#6-数据批次与发送路径)。
|
||||
|
||||
Example_Build_Batch使用局部游标遍历,仅在Submit_Batch返回OK后推进应用游标,此时发送层已复制原文并确定帧顺序,输入存储可复用。返回WAIT时保留同一业务事件及游标,先推进网络、读空Slave通知,再从同一位置重新取当前值和品质,不能重发旧构造结果。STALE表示原交互已失效(包括取消、撤销、超时及提交故障),丢弃旧应答并处理FINISHED。接实际数据源时,须将点值更新及突发发布与总召读值及提交串行化;排序保证及持续突发可能推迟总召的边界见[数据批次与发送路径](../104/IEC104_Interrogation_设计架构.md#6-数据批次与发送路径)。
|
||||
|
||||
@@ -125,18 +146,21 @@ Example_Init使用默认业务期限:BEGIN应答15秒、每批NEED_DATA至首
|
||||
|
||||
STOPDT或断线通知取消未提交任务。发送内部受理STOP而返回ERROR时,总召确认原连接正常停用后先让出,待通知终结为STOPPED;其他ERROR/FATAL仍失败。Submit_Batch为等待STOP通知而返回WAIT时不停止取数计时,应用继续按FIFO交付通知,旧交互随后STALE。事件消费预算耗尽时先续读通知,再推进业务。关闭时执行`Interrogation_Cancel → 读空业务事件并清理FINISHED/Release → Slave_Close → 读空Slave通知 → Interrogation_Destroy → Slave_Remove_Station → Slave_Destroy`。总召Destroy遇到活动任务或未读业务事件会拒绝释放;应用引用须持有到FINISHED清理,不能用Remove成功代替资源清理。
|
||||
|
||||
遥控同样执行`Command_Cancel → 读完FINISHED → Slave_Close → 读空并分派通知 → Command_Destroy`。示例先取消全部业务并清理应用引用,再关闭Slave;关闭后的生命周期通知仍交付全部模块。遥控FINISHED可以在离线时读取,事务槽及独立应答清空后才允许Destroy,最后移除站点并销毁Slave。
|
||||
|
||||
## 验证
|
||||
|
||||
```powershell
|
||||
./scripts/build.ps1 -Test
|
||||
```
|
||||
|
||||
`-Test` 需要预先安装 Node.js。脚本依次运行32组Slave/站点测试(单服务及双服务配置)、130组总召/计数量/兜底/校时可控网络测试(单服务及双服务)、3组规则单测和真实本机TCP测试:
|
||||
`-Test` 需要预先安装 Node.js。脚本运行Slave/站点测试、总召/计数量/兜底/校时可控网络测试、规则单测、遥控容量矩阵和真实本机TCP测试;实跑数量及记录见[测试说明](../test/IEC104测试文档.md):
|
||||
|
||||
- `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槽,使用真实容量背压限制发送。
|
||||
- `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;连接保留。服务约8秒后自行关闭,使用默认模拟时钟。
|
||||
- `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秒后自行关闭,使用默认模拟时钟。
|
||||
|
||||
原114组基线由98组总召、12组计数量及4组兜底回归组成。兜底改用未支持的106/102,覆盖同批及反序、相同内容的有效新请求、k=1与写阻塞排队、HIGH满后丢新且不补发、STOP/STOPSTART取消,以及总召未结束时立即提交错误应答。启用校时的独立回归另外核对103业务。
|
||||
|
||||
|
||||
@@ -25,7 +25,13 @@
|
||||
|
||||
## 2. 编译与运行
|
||||
|
||||
当前业务测试由98组总召、12组计数量、4组兜底及16组校时组成,共130组,单/双服务配置各跑一次。计数量覆盖独立实例、请求校验、Type15/37批次边界、忙隔离、冻结/复位、动作重复调用和WAIT/STALE/TEST、业务期限、STOP/断线、共享连接失败、发送背压及多站广播;兜底改用未支持的106/102,覆盖同批与反序/重复内容、窗口与写阻塞、HIGH满丢新及恢复、STOP/STOPSTART取消和活动总召中的应答。校时专项检查独立受理与执行、原文确认、补偿/期限、广播及生命周期。`iec104_interrogation_rules_test.c`另有3组规则测试,遍历限定词、数据COT和类型/SQ/FRZ矩阵并检查解析;没有网络交互。
|
||||
遥控专项入口为`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。
|
||||
|
||||
遥控覆盖45/46选择和直接执行、匹配/忙拒绝/撤销、三类动作结果、成功失败及终止配置、零/多帧返回信息、完整ASDU校验、全局FIFO、回复池满、窗口/部分写入、结果先于确认、重复及迟到事件、FINISHED配对、三个期限和时钟倒退(含期限配置0)、STOP提交竞态/FATAL、NORMAL覆盖反例、站点删除重注册、引用和单/双服务生命周期。真实回环另验证45/46返回信息及终止、TEST、广播/未知IOA、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矩阵并检查解析;没有网络交互。
|
||||
|
||||
在项目根目录的PowerShell中执行,要求GCC可用。下述TCP事件环和调度测试已包含TcpServer实现且自行实现Net接口,构建时不要再次链接`IEC104_TcpServer.c`或`IEC104_Net_Windows.c`。
|
||||
|
||||
@@ -211,8 +217,35 @@ if ($LASTEXITCODE -ne 0) { throw 'Large configuration tests failed' }
|
||||
|
||||
`-O2 -fstack-usage`报告的单函数栈帧:Handle_Event 256、Execute_Next 240、Submit_Head 224、Process 64、Get_Status 80字节;示例main由修改前768增至800字节,Clock_Action为48字节。记录为同目录的`example-before.su`、`example-after.su`及`clock.su`。这些是x64宿主ABI和单函数测量,不是最坏调用链,也不代表ARMCC/MCU的RAM或栈占用;嵌入式接入须按实际工具链重新核算。
|
||||
|
||||
### 遥控固定存储与栈测量
|
||||
|
||||
2026-10-30 Windows x64、GCC 16.1.0、`-O2`实测(单位字节):
|
||||
|
||||
| 对象 | 应答槽1 | 应答槽4(默认) |
|
||||
|---|---|---|
|
||||
| Command实例 | 448 | 616 |
|
||||
| 单事务槽 | 168 | 168 |
|
||||
| 单独立应答槽 | 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实现以测量私有结构,只作本地测量,不增加公开接口。
|
||||
|
||||
`-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的单函数测量,不是最坏调用链或其他目标工具链的内存预算。
|
||||
|
||||
## 5. 验证记录
|
||||
|
||||
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`。
|
||||
|
||||
2026-10-30 恢复单点遥控地址映射:Type45恢复6001..6008控制Type1遥信1001..1008;Type46仍使用1101..1108。`./scripts/build.ps1`严格GCC构建无诊断;沿用已获准的真实TCP回环命令,验证6001选择/执行后返回1001及总召新值、1001作为Type45命令时回47、双点1103返回1103,并通过原有撤销、错误应答和生命周期场景,退出码0。示例帮助、说明和回环期望同步更新。
|
||||
|
||||
2026-10-30 示例遥控地址对齐遥信:Type45改用1001..1008,Type46改用1101..1108,命令与返回信息使用同一IOA。严格GCC构建无诊断,Slave单/双服务各32组、既有业务各130组、遥控四配置各27组及3组规则测试通过。真实TCP核对新地址的选择/执行/返回/终止、撤销及单点操作后的总召新值,旧地址6001回47。`./scripts/build.ps1 -Test`末尾回环受沙箱子进程EPERM限制,首次整体退出码1;获准单独重跑原回环命令后退出码0。帮助文本、示例说明和回环断言同步更新,协议库点址校验保持通用。
|
||||
|
||||
2026-10-30 遥控决定窗口同点激活:SELECT_WAIT/EXECUTE_WAIT内,通过校验且同会话、同站点注册编号、同IOA的激活只计dropped_requests并HANDLED,不覆盖请求、不刷新期限、不生成新事件或忙否定。新增4组回归,TCP容量1/2与回复槽1/4的四组合各27组通过;`./scripts/build.ps1 -Test`的严格GCC构建无诊断,Slave单/双服务各32组、既有业务各130组及3组规则测试通过。脚本末尾真实TCP回环因沙箱阻止Node子进程而报EPERM,首次整体退出码1;随后获准单独重跑原回环命令,退出码0,真实TCP全流程通过。Command公开头C++11严格语法检查通过,文档链接与UTF-8/LF检查通过;未改公开接口或固定存储容量,修改仅限IEC60870-Slave。
|
||||
|
||||
2026-10-30 Type45/46遥控:新增独立单事务模块、固定回复池、选择/执行/撤销、显式动作与设备结果、原连接返回信息及全局FIFO收尾。`./scripts/build.ps1 -Test`以GCC `-std=c11 -O2 -Wall -Wextra -Werror`构建无诊断,Slave单/双服务各32组、既有业务单/双服务各130组、遥控四配置各23组和3组召唤规则测试全部通过;真实Windows TCP回环通过45/46完整流程、撤销、TEST/地址拒绝、未支持类型、总召新值及原有计数量/校时/启停重连,脚本最终退出码0。沙箱内首次Node启动因EPERM失败,随后在获准环境重跑完整原脚本通过。校时使用模拟模式,遥控操作模拟点。修改前84个文件的快照、容量测试、sizeof探针及栈记录保存在`.build/command-v1/`;所有实现与验证均限定在IEC60870-Slave目录。
|
||||
|
||||
2026-10-29 Type 103时钟同步:新增独立静态池模块、显式Execute_Next动作、64位受理后滞留补偿、执行期限、原文ACT_CON及广播快照确认;示例默认模拟,显式system模式才设置Windows时间。整合`build.ps1 -Test`中GCC `-std=c11 -O2 -Wall -Wextra -Werror`构建无诊断,Slave单/双服务各32组、业务单/双服务各130组(98总召、12计数量、4兜底、16校时)及3组规则测试均通过。脚本内首次Node启动因沙箱`spawnSync EPERM`失败,因此该次脚本整体不是成功退出;随后获准单独重跑相同Node回环命令,退出码0,真实本机TCP通过,核对submitted=65、clock_executed=3、clock_synchronized=3、clock_submitted=5。默认模拟校时的正常/TEST/COT=5/广播及103+102同批均覆盖,未执行system模式或真实设备设时。改前基线、大小探针及栈测量位于`.build/clock-synchronization-20261029/`。
|
||||
|
||||
2026-10-28 业务目录整理:共用召唤引擎、内部规则头、总召和计数量共7个源码/头文件移到`104/business/`,迁移前后SHA256逐字节一致;构建加入该目录的头文件搜索路径,同步测试直接包含路径及文档源码链接。严格GCC构建通过,单/双服务各110组业务测试、23组Slave测试及3组规则测试通过,公开头C++检查和真实Windows TCP回环通过。沙箱限制Node启动子进程时,回环使用原授权命令在沙箱外执行。迁移清单及备份位于`.build/business-layout-20261028/`,修改仅限IEC60870-Slave;未执行Keil目标构建或硬件联调。
|
||||
|
||||
Reference in New Issue
Block a user