Files
STM32F4-Base/IEC60870-Slave/104/IEC104_Interrogation.h
T
STF 4c55544f05
CI / L0 静态门禁 (pre-commit) (push) Failing after 45s
CI / L0.5 主机侧单测 (MSVC) (push) Failing after 42s
通信协议代码
2026-10-26 08:31:48 -04:00

253 lines
19 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 104总召业务:C_IC_NA_1、QOI=20..36,应用通过事件接受请求、交付数据和清理快照。
* 借用一个Slave,不创建socket/线程,不注册业务回调;所有接口在同一事件循环串行调用。
* 先把Slave通知按FIFO交给Handle_Event,读空后Process,再NextEvent并显式应答。
* 事件来源和会话状态由Slave公共接口校验;本模块维护总召阶段、业务期限和应用事件。
* 退出顺序:Cancel -> 读完业务FINISHED并清理应用资源 -> Destroy -> Destroy Slave。
*/
#ifndef IEC104_INTERROGATION_H
#define IEC104_INTERROGATION_H
#include "IEC104_Slave.h"
#ifdef __cplusplus
extern "C" {
#endif
/* 不透明静态池句柄,只能由Create取得、Destroy释放,不能自行分配、复制对象或free。
* 销毁成功后停止使用旧句柄;同一实例的全部接口须在所属事件循环中串行调用。 */
typedef struct IEC104_Interrogation IEC104_Interrogation_t;
/* 请求信息随事件按值复制,不暴露可发送的connection。
* 应用据此选择站/分组点表、构造数据头;分组成员及采样快照由应用维护。 */
typedef struct {
uint64_t generation, receive_serial; /* 原TCP会话代次、接收I帧标识,供应用关联和诊断。 */
uint16_t common_address; /* 实际本站地址;广播65535解析为此地址,应用数据批次须使用同一CA。 */
uint8_t originator_address; /* 请求的源发地址OA,应答数据须原样保留。 */
uint8_t qualifier; /* 20=站总召,21..36=第1..16组;应用维护分组点表。数据COT等于此值。 */
bool is_test; /* 请求的TEST标志,数据批次须保持一致。 */
uint8_t max_objects_per_asdu; /* 1..127,实际另受249字节容量限制。 */
} IEC104_InterrogationRequest_t;
/* FINISHED通知应用释放本次快照/游标;终结原因不等于网络发送回执。
* COMPLETED表示ACT_TERM被发送层接手,不表示写完或收到ACK。 */
typedef enum {
IEC104_INTERROGATION_FINISH_NONE = 0, /* 未终结;BEGIN/NEED_DATA事件也使用此值。 */
IEC104_INTERROGATION_FINISH_COMPLETED, /* 全部批次和ACT_TERM均已提交。 */
IEC104_INTERROGATION_FINISH_REJECTED, /* 应用拒绝;否定ACT_CON可能仍在排队。 */
IEC104_INTERROGATION_FINISH_CANCELLED, /* 应用调用本地Cancel放弃任务。 */
IEC104_INTERROGATION_FINISH_STOPPED, /* 收到Slave的停用通知,结束本次数据传输。 */
IEC104_INTERROGATION_FINISH_DISCONNECTED, /* 原TCP会话断开。 */
IEC104_INTERROGATION_FINISH_CONNECTION_CHANGED, /* 连接代次或当前传输状态已不再适用于任务。 */
IEC104_INTERROGATION_FINISH_SOURCE_ERROR, /* 应用通过Fail_Request报告快照/取数失败。 */
IEC104_INTERROGATION_FINISH_SEND_ERROR, /* 提交应答失败且不是可等待的发送容量不足。 */
IEC104_INTERROGATION_FINISH_OVERLOAD, /* 兼容保留;满槽现在拒绝/丢弃新请求,不终结已有任务。 */
IEC104_INTERROGATION_FINISH_INVALID_REQUEST, /* 请求无法完整解析,关闭原连接。 */
IEC104_INTERROGATION_FINISH_SERVICE_ERROR, /* 服务查询、内部状态或构造应答失败。 */
IEC104_INTERROGATION_FINISH_DEACTIVATED, /* 主站COT=8撤销;COT=9可能仍待发送。 */
IEC104_INTERROGATION_FINISH_BEGIN_TIMEOUT, /* BEGIN入队后未在期限内得到应用决定。 */
IEC104_INTERROGATION_FINISH_DATA_TIMEOUT, /* NEED_DATA入队后未在期限内得到应用应答。 */
IEC104_INTERROGATION_FINISH_TOTAL_TIMEOUT /* BEGIN生成至ACT_TERM提交超过执行整体期限。 */
} IEC104_InterrogationFinishReason_t;
typedef enum {
IEC104_INTERROGATION_BEGIN = 1, /* 接受/拒绝请求,确认前可建立快照。 */
IEC104_INTERROGATION_NEED_DATA, /* 索取一批数据;暂未就绪时保存事件。 */
IEC104_INTERROGATION_FINISHED /* 清理快照/游标,无需应答。 */
} IEC104_InterrogationEventType_t;
/* 纯值事件,无借用指针和自引用,可赋值、排入应用队列、保存到后续循环。
* 字段只读,应答时原样传回;实例/请求/批次标识用于拒绝迟到和重复应答。
* 每个已取出的BEGIN恰好配对一次FINISHED,取消也不丢失终结通知。
* BEGIN尚未取出便取消时移除BEGIN,不产生FINISHED(应用尚未建立资源)。
* 取消移除队列中的NEED_DATA;已取出的旧事件仍可读取,应答返回STALE。 */
typedef struct {
IEC104_InterrogationEventType_t type; /* 决定调用Reply_Begin、提交数据或只清理资源。 */
uint64_t instance_id, request_id; /* 实例及请求身份;必须与事件其他字段一起原样传回。 */
IEC104_InterrogationRequest_t request; /* 原激活请求的元数据,FINISHED仍保留此副本。 */
size_t batch_index, objects_submitted; /* 已被发送层接手的批次/对象数;首个NEED_DATA的批次号为0。 */
IEC104_InterrogationFinishReason_t reason; /* 仅FINISHED有意义。 */
} IEC104_InterrogationEvent_t;
/* Create复制此配置;创建后修改调用方对象不会改变已有实例。
* 接受/取数期限从事件入队开始,包含应用尚未NextEvent读取它的时间。 */
typedef struct {
uint16_t common_address; /* 唯一本站地址1..65534;请求可以使用本站CA或广播65535,应答用本站CA。 */
uint8_t max_objects_per_asdu; /* 每批1..127个对象,配置无业务函数指针。 */
uint32_t begin_timeout_ms; /* BEGIN入队至Reply_Begin/Fail_Request,默认15000ms。 */
uint32_t data_timeout_ms; /* 每次NEED_DATA入队至Submit/Complete/Fail,默认15000ms。 */
uint32_t total_timeout_ms; /* BEGIN生成至ACT_TERM提交,含取数/发送等待,默认60000ms。 */
/* 上述期限是业务策略,独立于104链路t0..t3;每项0表示禁用该项。
* BEGIN尚未生成时不计业务期限;START/CONFIRM超时转为否定ACT_CON并保留连接。
* 肯定ACT_CON被发送层接手后,DATA/TERMINATE超时关闭原连接,不发送成功ACT_TERM。
* 同一执行任务的总期限与阶段期限同时到点时优先报告总超时。 */
} IEC104_InterrogationConfig_t;
/* 输入Slave通知的处理结果;其他类型I帧返回IGNORED,交后续业务处理。 */
typedef enum {
IEC104_INTERROGATION_EVENT_ERROR = -1, /* 参数、复制、关闭连接等处理失败。 */
IEC104_INTERROGATION_EVENT_IGNORED = 0, /* 本模块未接手,可转交其他业务处理器。 */
IEC104_INTERROGATION_EVENT_HANDLED = 1 /* 已处理,含满槽丢弃;不保证应用接受或已发送响应。 */
} IEC104_InterrogationEventResult_t;
typedef enum {
IEC104_INTERROGATION_NEXT_ERROR = -1, /* 非法句柄或NULL输出。 */
IEC104_INTERROGATION_NEXT_EMPTY = 0, /* 当前无业务事件,不代表总召已经结束。 */
IEC104_INTERROGATION_NEXT_READY = 1 /* 已复制并移除最早事件,输出可独立保存。 */
} IEC104_InterrogationNextResult_t;
/* 显式比较,不能按真假判断。
* OK:应答被模块接手,交互已消费,不表示发帧/ACK。
* WAIT:Slave还有未读通知,先按FIFO处理,再用同一事件/批次重试;不改变任务。
* STALE:实例/请求/批次/类型不匹配、未取出、已应答、已取消或已超时;不要重试旧事件。
* ERROR:参数/批次/服务查询错误,不消费交互,可修正再提交。
* NEED_DATA阶段Fail_Request例外:即使关闭连接失败,仍取消并交付FINISHED,不能重试。 */
typedef enum {
IEC104_INTERROGATION_REPLY_STALE = -2,
IEC104_INTERROGATION_REPLY_ERROR = -1,
IEC104_INTERROGATION_REPLY_WAIT = 0,
IEC104_INTERROGATION_REPLY_OK = 1
} IEC104_InterrogationReplyResult_t;
/* 优先描述唯一未终结总召的诊断阶段;仅有应答时REPLY,无任务时IDLE。
* 阶段由模块推进,应用通过事件交互,不直接修改。 */
typedef enum {
IEC104_INTERROGATION_IDLE, /* 没有协议任务。 */
IEC104_INTERROGATION_REPLY, /* 等待提交单帧确认(含撤销确认)或错误应答。 */
IEC104_INTERROGATION_START, /* 等事件空位生成BEGIN,或等待应用接受/拒绝;两者均占用总召资格。 */
IEC104_INTERROGATION_CONFIRM, /* 应用已接受,等待提交肯定ACT_CON。 */
IEC104_INTERROGATION_DATA, /* 生成NEED_DATA或提交已经备好的数据批次。 */
IEC104_INTERROGATION_TERMINATE /* 应用声明数据结束,等待提交ACT_TERM。 */
} IEC104_InterrogationPhase_t;
/* Get_Status返回的独立副本;计数自Create起累计,Cancel和重连不清零。
* 会话及等待标志反映最近已处理的通知/推进结果,不能代替Slave的实时服务查询。 */
typedef struct {
uint64_t generation; /* 已消费的连接代次,断线后0。 */
bool active; /* 已消费ACTIVATED且未停用,不是socket实时状态。 */
bool gi_pending; /* 唯一总召尚未终结,含BEGIN尚未生成;为true时否定所有新总召激活。 */
bool waiting; /* 存在发送、应用或事件空位等待;总召等应用时仍可推进单帧应答。 */
bool application_waiting; /* BEGIN/NEED_DATA尚未应答,包含事件尚未取出的情况。 */
bool events_pending; /* 最近推进/应答检查发现Slave FIFO未空,须先处理网络通知。 */
bool timer_active; /* 存在启用的业务期限;为false时timeout_remaining_ms无意义。 */
uint32_t timeout_remaining_ms; /* 最近期限距当前单调时钟的毫秒数;0表示应立即Process。 */
unsigned pending_requests, pending_events; /* 协议任务数(含单帧应答)、业务事件数(不含预留)。 */
IEC104_InterrogationPhase_t phase; /* 唯一总召的阶段;无总召时为REPLY或IDLE。 */
size_t batch_index, objects_submitted; /* 活动总召已提交的批次/对象数;仅有应答时取最早应答保留的进度。 */
unsigned long submitted, gi_submitted; /* 接手ASDU数、提交ACT_TERM的总召数。 */
unsigned long waits, cancelled, rejected, overloads;
/* waits统计发送层返回WAIT;cancelled统计取消的协议任务(含单帧应答);
* rejected统计校验/总召忙/应用/确认前失败/满槽拒绝;overloads统计需要保存但槽满的次数。 */
unsigned long negative_submitted, dropped_requests; /* 已提交否定应答数、无法保存或提交而丢弃的新请求数。 */
unsigned long deactivated, timeouts; /* 成功匹配的主站撤销、执行阶段业务超时次数。 */
IEC104_InterrogationFinishReason_t last_finish_reason; /* 最近合法总召的终结原因,含BEGIN尚未交付的任务。 */
} IEC104_InterrogationStatus_t;
/* 初始化调用方的可写配置:CA=1,每批最多127个对象,
* 接受/取数/执行整体期限为15000/15000/60000ms;NULL无操作。 */
void IEC104_Interrogation_Config_Default(IEC104_InterrogationConfig_t *config);
/* 复制配置,借用Slave;同一Slave只能绑定一个实例,在消费Slave通知前创建。
* slave须保持有效直到本实例Destroy;config在返回后可释放或复用。
* NULL:参数非法、重复绑定、池满或实例编号耗尽;无网络操作,无malloc。
* 编译.c可覆盖IEC104_INTERROGATION_CAPACITY(默认随TCP实例容量)、
* IEC104_INTERROGATION_TASK_CAPACITY(默认4)、IEC104_INTERROGATION_EVENT_CAPACITY
* (默认4,最小2)。任务池容纳至多一个总召及短应答,不是多个总召的等待队列。
* 业务FIFO为已交付BEGIN预留FINISHED位置。 */
IEC104_Interrogation_t *IEC104_Interrogation_Create(IEC104_Slave_t *slave,
const IEC104_InterrogationConfig_t *config);
/* 协议任务和业务事件均为空才释放槽,否则false并保持原状态。
* 先Cancel,再NextEvent读完FINISHED并清理应用资源,最后Destroy;成功后句柄失效。 */
bool IEC104_Interrogation_Destroy(IEC104_Interrogation_t *self);
/* 放弃未提交任务;保留已交付BEGIN的FINISHED(CANCELLED),移除未交付工作事件。
* 不撤回已接手帧、不补发ACT_TERM;非法句柄无操作。 */
void IEC104_Interrogation_Cancel(IEC104_Interrogation_t *self);
/* 按FIFO接收本Slave的每个通知,不消费Slave FIFO,不保存调用方指针。
* 总召须SQ=0、一个对象、COT=6或8、P/N=0、CA为本站或65535、IOA=0、QOI=20..36。
* 广播常量为IEC104_CA_BROADCAST;应用事件、确认、数据、否定应答及终止均使用实际本站CA。
* 原输入事件不修改;建立内部请求副本时一次性设置应答CA,发送及重试沿用该地址。
* 空闲时保存一个COT=6激活并进入START;从保存起至终结,拒绝所有后续激活,不区分QOI。
* 忙或QOI/P/N非法时回否定ACT_CON;保留已有请求的QOI/OA/TEST、工作事件及数据进度。
* 单帧应答在无未读Slave通知、原会话有效且无更早REPLY时尝试立即提交,未接手再保存。
* 满槽时尝试否定确认;无法安全提交/保存则仅丢新请求并累计dropped_requests,不断链。
* 撤销COT=8不受忙拒绝限制,按连接代次、实际站CA、OA、TEST、QOI匹配当前总召;
* 广播激活可定向撤销,广播撤销可匹配本站任务;匹配则停止该任务,
* 为已交付BEGIN保留FINISHED(DEACTIVATED),按单帧应答规则提交肯定COT=9;
* 不匹配/非法QOI或P/N回否定COT=9。
* 撤销无需应用再次批准;应用收到FINISHED后停止采样并释放本次资源。
* 匹配撤销复用原任务槽,并将确认排到单帧应答队尾;满池也不需要额外槽。
* COT/CA/IOA错误回45/46/47;撤销不撤回发送层已接手的帧,不补发ACT_TERM。
* 尚未生成/交付BEGIN的撤销不产生FINISHED;已有BEGIN交付才通知应用清理。
* 布局非法关闭原连接;STOPDT/断线取消总召及待发短应答并保留终结事件。
* 其他I帧IGNORED,不替总电度等处理器回答UNKNOWN_TYPE。 */
IEC104_InterrogationEventResult_t IEC104_Interrogation_Handle_Event(
IEC104_Interrogation_t *self, const IEC104_SlaveEvent_t *event);
/* 自动推进当前就绪任务,不等待应用或网络;调用方不需要指定处理步数。
* Slave FIFO未空仅设置events_pending。顺序:START -> BEGIN -> 应用接受 -> ACT_CON ->
* NEED_DATA/提交批次(可重复)-> 应用Complete_Data -> ACT_TERM -> FINISHED。
* 工作事件只交付一次;应用暂未就绪可保存事件,后续Process不会重复请求。
* 单帧应答保持FIFO,可在总召等待应用/业务事件空位时提交;双方可推进时先应答再总召轮转,
* 继续处理当前就绪任务,直至空闲、所有任务等待应用/事件空位,或发送返回WAIT。
* 每条REPLY成功提交后释放;总召提交当前帧后结束或等待应用,不保存后续总召激活。
* 本函数不接收新请求或执行应用代码,因此工作量由固定任务池自然限定。
* 应答可穿插总召各批次,本次总召自身ACT_CON/数据/ACT_TERM顺序不变。
* 沿原connection走HIGH;发送WAIT保留字节,OK后才增加批次/对象计数。
* WAIT让出本轮,下轮优先检查单帧应答;已被发送层接手的帧不被后到应答越过。
* 使用平台单调时钟检查期限,先处理Slave通知;START/CONFIRM超时转为否定REPLY并继续调度,
* 肯定ACT_CON提交后的DATA/TERMINATE超时关闭原连接;FINISHED保留具体超时原因。
* 执行总期限从BEGIN生成开始;此前等待业务事件空位不计业务期限,应及时读完FINISHED。
* 调用方持续驱动Process,并用Get_Status的剩余期限限制Poll/应用休眠时长。
* true本轮正常(可等待/取消/超时);false参数、组包、关闭连接或服务查询失败。 */
bool IEC104_Interrogation_Process(IEC104_Interrogation_t *self);
/* 取最早业务事件;event须为非NULL输出,只有READY写入,EMPTY/ERROR保持输出不变。
* READY后工作事件才允许应答;FINISHED仅用于应用清理,不调用应答接口。
* 事件是可保存的纯值副本,本函数无网络操作,也不自动执行应用代码。 */
IEC104_InterrogationNextResult_t IEC104_Interrogation_NextEvent(
IEC104_Interrogation_t *self, IEC104_InterrogationEvent_t *event);
/* 应答已取出的BEGIN。true允许提交ACT_CON;false立即FINISHED(REJECTED),
* 并排队否定ACT_CON;不支持的分组可在这里拒绝。应用按instance_id/request_id管理快照。
* 接受前检查点表、数据源及必要快照资源;无需预先编码所有数据,仍可逐批交付。
* event须原样传回;OK表示接受/拒绝决定已消费,实际ACT_CON由后续Process提交。
* 所有应答接口也检查期限,不能通过迟到应答绕过超时;Slave通知未读空时仍优先返回WAIT。 */
IEC104_InterrogationReplyResult_t IEC104_Interrogation_Reply_Begin(
IEC104_Interrogation_t *self, const IEC104_InterrogationEvent_t *event, bool accept);
/* 应答已取出的NEED_DATA,交付非空ASDU;验证并复制最多249字节。
* event为当前批次事件;OK表示数据复制进任务,后续Process才提交并累计批次/对象数。
* OK后输入ASDU、缓冲及参数可立即复用;发送WAIT不再次索取该批数据。
* 参数须2/2/3,COT=请求QOI(20..36)、P/N=0、CA=配置本站地址、OA/TEST与事件相同。
* 广播请求的数据CA也不能使用65535;对象数不超配置。
* 接受Type 1/3/5/7/9/11/13/20/21,SQ=0/1均可,各批可切换类型。
* 非法批次ERROR,保留NEED_DATA供修正,不断线;无法继续则Fail_Request。
* 应用使用Asdu_Create/InformationObject_Create/Add自行构造业务ASDU。 */
IEC104_InterrogationReplyResult_t IEC104_Interrogation_Submit_Batch(
IEC104_Interrogation_t *self, const IEC104_InterrogationEvent_t *event,
const IEC60870_Asdu_t *asdu);
/* 应答已取出的NEED_DATA,声明数据耗尽;不以空ASDU作为结束标记。
* OK只切换到TERMINATE;Process提交ACT_TERM成功后才交付FINISHED(COMPLETED)。 */
IEC104_InterrogationReplyResult_t IEC104_Interrogation_Complete_Data(
IEC104_Interrogation_t *self, const IEC104_InterrogationEvent_t *event);
/* 应答已取出的BEGIN/NEED_DATA,报告数据源失败,两者均交付FINISHED(SOURCE_ERROR)。
* BEGIN阶段复用原槽保存否定ACT_CON,保留连接和其他任务;OK不表示否定确认已发送,须继续Process。
* NEED_DATA阶段肯定ACT_CON已经提交:关闭原连接并取消任务,不发送成功终止,不影响后来新代次。
* 检查通过后即终结交互;即使NEED_DATA阶段关闭失败返回ERROR,任务仍已取消,不能重试。 */
IEC104_InterrogationReplyResult_t IEC104_Interrogation_Fail_Request(
IEC104_Interrogation_t *self, const IEC104_InterrogationEvent_t *event);
/* 取得独立状态副本,按当前单调时钟计算最近期限;只查询,不触发超时处理。
* timer_active时,调用方等待时长不得超过timeout_remaining_ms;false时不限制等待。
* 返回false为非法句柄/NULL,输出保持不变。 */
bool IEC104_Interrogation_Get_Status(const IEC104_Interrogation_t *self,
IEC104_InterrogationStatus_t *status);
#ifdef __cplusplus
}
#endif
#endif /* IEC104_INTERROGATION_H */