253 lines
19 KiB
C
253 lines
19 KiB
C
/*
|
||
* 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 */
|