403 lines
29 KiB
C
403 lines
29 KiB
C
/*
|
||
* TCP内部的子站串行发送管理:ASDU调度、I帧交接、完成分派和确认记录;不执行socket I/O。
|
||
*
|
||
* 本头文件供Connection实现使用;下文的调用方指内部协调层,业务只通过TcpServer接入:
|
||
* - TcpServer_Send_Response经Connection_Submit_Response校验会话,本模块Submit_Response直接准备或入HIGH。
|
||
* - NORMAL由TcpServer直接缓存到服务队列;本模块仅在有效会话中准备实际发送时分配协议N(S)。
|
||
* - Prepare_Next_Send或Submit_Response取得字节,TcpServer经Net写socket,再经Connection回报写入增量或失败。
|
||
* 暂不可写时保留当前帧等待;本模块的Handle接口不代替实际I/O。
|
||
* - Connection从Receiver取得完整APDU及APCI,将I/S帧交给Handle_I_S_Frame处理累计ACK。
|
||
* - TcpServer经Connection查询回执,成功复制为RECORD事件后移除内部记录;业务用NextEvent取事件副本。
|
||
*
|
||
* Manager是本连接Link和SendBuffer的运行期修改入口,在一次调用内完成成组更新;没有后台自动同步。
|
||
* Link维护协议序号,SendBuffer保存当前I帧及控制FIFO,AsduQueue保留原文,Manager保存帧记录和NORMAL来源编号。
|
||
* 同连接的这些操作由Connection串行组织;已经交给Manager的事件不得再手动调用底层接口重复提交。
|
||
*/
|
||
#ifndef IEC104_SENDMANAGER_H
|
||
#define IEC104_SENDMANAGER_H
|
||
|
||
#include "IEC104_AsduQueue.h"
|
||
#include "IEC104_SendBuffer.h"
|
||
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* 按具体结果分支:Prepare_Next_Send及Submit_Response返回OK才提供可写字节;仅入队返回QUEUED。
|
||
* WAIT等正数不表示本次有报文可发送,FRAME_WRITTEN表示完整完成流程已经提交。 */
|
||
typedef enum {
|
||
IEC104_SEND_MANAGER_PROTOCOL_ERROR = -3, /* I/S报文被Link拒绝,须结束连接;与内部交接失败区分 */
|
||
IEC104_SEND_MANAGER_FATAL = -2, /* 当前连接必须结束,先回收业务再Abort */
|
||
IEC104_SEND_MANAGER_ERROR = -1, /* 参数、代次、frame_id或接入状态非法 */
|
||
IEC104_SEND_MANAGER_EMPTY = 0, /* 没有待处理项,具体含义见各接口说明 */
|
||
IEC104_SEND_MANAGER_OK = 1, /* 当前操作成功;Prepare可写,Handle_Send_Progress尚未整帧写完 */
|
||
IEC104_SEND_MANAGER_QUEUED = 2, /* 控制帧进入FIFO,或Submit_Response已缓存HIGH;尚未发送 */
|
||
IEC104_SEND_MANAGER_WAIT = 3, /* 门控/窗口/容量/待完成义务阻塞,不重置期限 */
|
||
IEC104_SEND_MANAGER_FRAME_WRITTEN = 4, /* 本地完成处理成功且已释放当前帧缓冲,不能再手动完成同一帧 */
|
||
IEC104_SEND_MANAGER_IGNORED = 5, /* 重复STOP或当前没有匹配等待过程的TESTFR_CON,不重启过程 */
|
||
} IEC104_SendManagerResult_t;
|
||
|
||
/*
|
||
* 每个I帧一条存储记录,只保存关联、序号快照及完成事实,不保存完整APDU。
|
||
* records数组使用Storage类型,帧身份只存frame_serial;连接代次由所属Manager统一保存。
|
||
* local_send_completed由Handle_Send_Progress登记,peer_acknowledged由Handle_I_S_Frame登记;
|
||
* 必须先提交本地发送完成,再处理覆盖该帧的ACK;两项都成立时才返回就绪回执。
|
||
* SendBuffer在本地完成时就释放APDU槽;就绪记录由TcpServer复制为事件后,经Connection调用Remove移除。
|
||
* 收尾时剩余记录先复制为closing事件,再由Connection_Close统一清理;不等待业务处理事件。
|
||
* 队列HIGH原文在I帧提交及记录建立后即出队;直接应答不入ASDU队列,使用相同HIGH记录。
|
||
* NORMAL原文保留到有效ACK或被后续入队覆盖。
|
||
* 两级发送记录均独立等待本地完成和有效ACK;HIGH队列清空不撤回已提交I帧。
|
||
* 原文存储与回执消费分开;asdu_entry_serial只用于NORMAL确认匹配,HIGH为0;不保存业务user_tag。
|
||
* 覆盖不撤回帧、不提前释放k窗口;两级发送完成及回执仍使用独立frame_id。
|
||
*/
|
||
typedef struct {
|
||
uint64_t frame_serial; /* 本连接内不复用的发送编号;代次取所属Manager */
|
||
uint64_t asdu_entry_serial; /* NORMAL条目编号;HIGH固定0,不提供ASDU业务身份 */
|
||
uint64_t queued_at_ms; /* 实际准备I帧的时刻,本地发送期限起点 */
|
||
uint64_t written_at_ms; /* local_send_completed==1时有效,允许0 */
|
||
IEC104_IFrameTx_t i_frame_snapshot; /* 原始N(S)/N(R)/长度快照 */
|
||
IEC104_AsduPriority_t asdu_priority; /* 原始优先级 */
|
||
uint8_t local_send_completed; /* 0/1:完整写出且Link.Complete成功 */
|
||
uint8_t peer_acknowledged; /* 0/1:有效累计ACK覆盖 */
|
||
} IEC104_IFrameSendRecordStorage_t;
|
||
|
||
/* 内部查询的独立数值副本:用Manager代次补齐frame_id;TcpServer另转换为公开记录类型。 */
|
||
typedef struct {
|
||
IEC104_SendFrameId_t frame_id; /* 连接代次 + 不复用的发送身份 */
|
||
uint64_t asdu_entry_serial; /* NORMAL用于ACK释放;HIGH固定0,按frame_id读取帧回执 */
|
||
uint64_t queued_at_ms; /* 实际准备I帧的时刻,本地发送期限起点 */
|
||
uint64_t written_at_ms; /* local_send_completed==1时有效,允许0 */
|
||
IEC104_IFrameTx_t i_frame_snapshot; /* 原始N(S)/N(R)/长度快照 */
|
||
IEC104_AsduPriority_t asdu_priority; /* 原始优先级;HIGH只确认记录,NORMAL另确认原文队列 */
|
||
uint8_t local_send_completed; /* 0/1:完整写出且Link.Complete成功 */
|
||
uint8_t peer_acknowledged; /* 0/1:有效累计ACK覆盖,非业务执行结果 */
|
||
} IEC104_IFrameSendRecord_t;
|
||
|
||
/* 完成结果不含已移出发送FIFO槽的裸指针;TESTFR_ACT完成时可记录等待CON的原始起点。 */
|
||
typedef struct {
|
||
IEC104_SendFrameId_t frame_id; /* 已完成帧的连接代次与帧身份,供接入方关联事件 */
|
||
IEC104_SendBufferFrameType_t frame_type; /* 原帧保存的帧类型,不根据完成后的链路状态推断 */
|
||
IEC104_LinkResult_t link_completion_result; /* 保留OK/IGNORED,供调用方决定是否启动过程计时 */
|
||
IEC104_IFrameTx_t i_frame_snapshot; /* 仅I帧有效 */
|
||
uint64_t queued_at_ms; /* 原始入队时刻,部分写入或重试不续期 */
|
||
uint64_t written_at_ms; /* 实际整帧写完时刻;主动U帧成功后用于启动等待CON计时 */
|
||
} IEC104_LocalSendCompletion_t;
|
||
|
||
|
||
/*
|
||
* 发送管理器由Connection内含,完整布局用于内部静态存储;仅借用下列对象和数组。
|
||
* link_state/send_buffer指向本连接实际对象,不复制整份状态。
|
||
* 对Link/SendBuffer/AsduQueue的更新通过模块接口完成,Manager直接维护自身控制字段及records数组。
|
||
* 字段由接口维护,不提供锁或TCP连接存活判定。
|
||
*/
|
||
typedef struct {
|
||
IEC104_AsduQueue_t *asdu_queue; /* 仍保留的原文及NORMAL覆盖待确认计数 */
|
||
IEC104_SendBuffer_t *send_buffer; /* 唯一当前I帧及独立紧凑S/U FIFO */
|
||
IEC104_LinkState_t *link_state; /* 本连接唯一的CONTROLLED链路状态 */
|
||
IEC104_IFrameSendRecordStorage_t *records; /* 调用方提供的存储数组,各条不重复保存连接代次 */
|
||
uint16_t record_capacity; /* 包括未完成记录和未消费帧回执的总容量 */
|
||
uint16_t record_head_index; /* 最早尚未由TcpServer转为事件并移除的记录下标 */
|
||
uint16_t record_count; /* ACK已到也不自动减计数,消费回执后才释放 */
|
||
uint16_t high_unacked_count; /* 已登记且未ACK的HIGH I帧数,含直接应答及已出队应答 */
|
||
uint16_t next_record_ns; /* 下一条新登记I帧记录的N(S),与Link下一分配位置一致 */
|
||
uint16_t send_window_k; /* 交给Link检查的未确认I帧窗口上限 */
|
||
uint16_t high_priority_burst_limit; /* 0严格高优先;非0限制NORMAL有未提交条目时连续选择HIGH的次数 */
|
||
uint16_t high_priority_burst_count; /* 成功交接后更新;Init及从停用状态启动时清零 */
|
||
uint64_t connection_generation; /* 本Manager所有记录共用的代次;旧事件不得提交到新连接 */
|
||
uint8_t is_initialized; /* 1表示发送管理器已初始化,不表示TCP可用 */
|
||
} IEC104_SendManager_t;
|
||
|
||
/* 存储概况,不包含可修改的队列/Link指针;FAILED回收可用,不要求运行中的Link仍合法。 */
|
||
typedef struct {
|
||
uint64_t connection_generation; /* 当前绑定代次,Abort后不再可查询 */
|
||
uint16_t record_capacity; /* 调用方提供的记录槽总数 */
|
||
uint16_t record_count; /* 尚未消费的全部记录,包含已ACK回执 */
|
||
uint16_t high_unacked_count; /* HIGH已交接但尚未被对端ACK的记录数 */
|
||
} IEC104_SendManagerStatus_t;
|
||
|
||
/* 读取存储快照,OK写status,ERROR不改输出;不消费记录、不检查跨模块运行一致性。 */
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Get_Status(
|
||
const IEC104_SendManager_t *manager, IEC104_SendManagerStatus_t *status);
|
||
|
||
/* 当前发送期限的只读计算结果,不保存第二份计时起点。
|
||
* 对应has_*为1时remaining_ms有效,0表示已经到期;起点为0也是合法时刻。
|
||
* 队首身份与本地期限来自同一次查询,供超时后提交该帧失败;控制容量供连接安排t2/t3唤醒。 */
|
||
typedef struct {
|
||
IEC104_SendFrameId_t head_frame_id;
|
||
uint32_t local_send_remaining_ms;
|
||
uint32_t i_ack_remaining_ms;
|
||
uint8_t has_send_head;
|
||
uint8_t has_i_ack_deadline;
|
||
uint8_t control_has_room;
|
||
} IEC104_SendManagerDeadlines_t;
|
||
|
||
/* OK写完整副本,ERROR不改输出;两项期限均须非零,代次匹配,now_ms不得早于活动起点。
|
||
* 只观察已准备队首和最早已写完未ACK记录,不选择ASDU、不编号、不推进发送或关闭连接。
|
||
* 部分写入、重复ACK及队首更换不续期;部分ACK后使用下一条记录原有的written_at_ms。 */
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Get_Deadlines(
|
||
const IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
uint64_t now_ms, uint32_t local_send_timeout_ms, uint32_t t1_ms,
|
||
IEC104_SendManagerDeadlines_t *deadlines);
|
||
|
||
/*
|
||
* 通过绑定的队列复制ASDU入队,避免上层取出manager内部指针再修改队列。
|
||
* HIGH仅在STARTED+IDLE时接手;否则先ERROR,不检查容量、不入队。
|
||
* OK成功,WAIT队列空间/条目编号窗口不足,ERROR参数/会话/阶段非法;不生成I帧或分配N(S)。
|
||
* NORMAL覆盖和内部条目编号规则同AsduQueue,入队不表示已完成本地发送或收到ACK。
|
||
* 受理新STOPDT_ACT时,尚在HIGH中的应答作废;已提交I帧及NORMAL不受该清理影响。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Enqueue(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
IEC104_AsduPriority_t priority, const uint8_t *asdu, uint16_t asdu_length);
|
||
|
||
/* 仅STARTED+IDLE时接手;否则在容量检查前ERROR,不入HIGH。
|
||
* 准入后优先尝试直接准备I帧,不执行socket I/O;已有帧/旧HIGH/公平配额/窗口或记录容量阻塞时进入HIGH。
|
||
* START受理后即可接手;已有CON时暂存HIGH,不能越过控制FIFO发送。已启用时重复START保留HIGH。
|
||
* OK表示SendBuffer及发送记录已接手,仅此时写head_info;QUEUED表示HIGH已复制,WAIT表示尚未接手。
|
||
* QUEUED不保证最终发送;受理新STOPDT_ACT时,HIGH待发条目作废且不产生逐条回执。
|
||
* 直接应答不经过ASDU队列,也不调用Mark_Submitted;仍按HIGH计入未确认记录,使用相同的完成及ACK路径。
|
||
* ERROR表示输入/状态非法或HIGH总容量装不下,FATAL可能已编号,必须收尾;输出和输入不得重叠。
|
||
* 调用前Connection须先处理控制义务及期限;OK后应紧接一次有界的非阻塞写入尝试。 */
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Submit_Response(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
const uint8_t *asdu, uint16_t asdu_length, uint64_t now_ms,
|
||
IEC104_SendBufferHeadInfo_t *head_info);
|
||
|
||
/* 不排队应答:仅在无当前帧、HIGH/NORMAL无待发条目且窗口/记录有容量时准备I帧。
|
||
* OK已固定N(S)并复制进SendBuffer;WAIT不接手,不缓存原文,不产生QUEUED。
|
||
* 已提交待ACK的NORMAL不阻挡本次尝试,仍受k窗口限制;其余校验同Submit_Response。 */
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Try_Submit_Response(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
const uint8_t *asdu, uint16_t asdu_length, uint64_t now_ms,
|
||
IEC104_SendBufferHeadInfo_t *head_info);
|
||
|
||
/*
|
||
* 紧邻实际发送前调用;OK返回当前帧剩余字节,没有当前帧时才选择ASDU并分配N(S)。
|
||
* 输入:已Init的manager、匹配代次及当前单调时刻;输出head_info只在OK时有效。
|
||
* 当前I帧先续传,否则先发控制FIFO,再按优先级及配额选择未提交ASDU;最多一帧I待本地完成。
|
||
* 重复调用只返回同一当前帧,不重复编号;EAGAIN保持身份、内容、进度和原期限。
|
||
* EMPTY表示既无已准备帧,也无未提交ASDU;WAIT为门控/k窗口/记录容量阻塞。非OK不写head_info。
|
||
* Link构帧成功后的状态提交异常返回FATAL,须结束连接,不能撤回序号后重试。
|
||
* APDU提交及记录建立后,HIGH原文出队,NORMAL原文标记已提交;当前APDU与记录独立保留。
|
||
* 仅准备成功才推进调度配额;未提交ASDU被覆盖时没有对应帧回执。
|
||
* 跨字节环尾部的ASDU通过249字节局部缓冲拼接;连续ASDU直接借用,不malloc。
|
||
* now_ms为单调时刻。NORMAL没有未提交条目时,选择HIGH会清零配额计数。
|
||
* 先安排当前应生成的S/U,再调用本接口;不执行I/O,不因调用本接口即认定实际已发送。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Prepare_Next_Send(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation, uint64_t now_ms,
|
||
IEC104_SendBufferHeadInfo_t *head_info);
|
||
|
||
/*
|
||
* S/U入队接口:同连接串行执行会话校验、控制槽预留、Link构帧和FIFO提交。
|
||
* 公共参数:manager须已Init且绑定对象持续有效;connection_generation须与当前连接一致;
|
||
* now_ms为单调毫秒读数,允许0;apci参数须来自Parse_APCI成功的完整报文。
|
||
* now_ms是本次入队的单调时刻,不是发送完成时刻;不执行I/O或创建定时器。
|
||
* QUEUED表示已提交一帧,后续统一用Handle_Send_Progress/Failure处理发送结果。
|
||
* Queue_*返回WAIT表示控制队列满/尚未到期/当前不能生成,无新报文;可稍后重试。
|
||
* 外部遗留的未结束预留属于接入状态错误,会在会话校验中返回ERROR。
|
||
* 未生成报文时取消本次预留;已使用的frame_serial不复用。
|
||
* ERROR表示参数、会话、预留或准备结果非法;构帧成功后提交异常、取消预留异常均为FATAL,须断连。
|
||
* FATAL前可能已经改变Link状态,不能假定整次调用未发生,也不能取消预留后重试原会话。
|
||
* Handle_*接收接口的WAIT表示本次请求尚未受理:调用方必须有界保留该请求,
|
||
* 在其被处理前不得越过它分派后续接收帧;不能保留时按过载断连,不得静默丢弃。
|
||
* START成功受理即结束此前停止的总计时;新STOP从收到该请求时起计总期限。
|
||
* apci不得与本模块存储重叠。NULL、错误帧类型/功能码在入口返回ERROR;已解析I/S报文被Link
|
||
* 按接收阶段、序号或ACK规则拒绝时返回PROTOCOL_ERROR,内部状态或交接异常返回ERROR/FATAL。
|
||
* 所有构帧接口先预留再调用Link,无容量时优先WAIT;EMPTY/无需报文等结果不是独立状态查询。
|
||
* 采用这些接口后,不再为同一操作手动Reserve、Link构帧或Commit_Frame。
|
||
*/
|
||
|
||
/**
|
||
* 功能:按w门限、立即确认或子站停止排空规则生成S确认并入队。
|
||
* 输入参数:公共参数见上;w范围1..32767;immediate非0跳过数量门限,但不跳过启停/在途S门控。
|
||
* 输出参数:无独立输出对象;Link冻结N(R),SendBuffer保存S帧,发送完成后才提交确认位置。
|
||
* 返回值:QUEUED表示S已入队;WAIT表示暂不能生成或无需确认;ERROR/FATAL按公共约定处理。
|
||
* 调用时机:I帧接收、启动CON发送完成或其他确认条件变化后,由连接调度代码调用。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Queue_S_Ack(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
uint16_t w, int immediate, uint64_t now_ms);
|
||
|
||
/**
|
||
* 功能:t2到期后按公共S规则准备确认,仍受启停和已有S在途约束。
|
||
* 输入参数:公共参数见上;elapsed_ms为原待确认数据起点至今的毫秒数,t2_ms须非0。
|
||
* 输出参数:无独立输出对象;成功生成的S进入发送FIFO,不直接执行发送或读取时钟。
|
||
* 返回值:QUEUED表示S已入队;WAIT包括未到期、没有可发S或容量不足;ERROR/FATAL按公共约定。
|
||
* 说明:elapsed_ms等于t2_ms即到期;等待或重试不续期,接入方负责保存正确的原计时起点。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Queue_T2_Ack(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
uint32_t elapsed_ms, uint32_t t2_ms, uint64_t now_ms);
|
||
|
||
/**
|
||
* 功能:子站主动生成并排队TESTFR_ACT,不要求当前已经STARTED。
|
||
* 输入参数:公共参数见上;本入口不检查空闲时长,按t3触发时使用Queue_T3_Testfr。
|
||
* 输出参数:无独立输出对象;Link进入ACT_SEND_PENDING,ACT进入SendBuffer。
|
||
* 返回值:QUEUED表示ACT已入队;WAIT表示控制容量不足或已有探测;ERROR/FATAL按公共约定。
|
||
* 说明:完整发送后经Handle_Send_Progress才进入CON_PENDING,以实际written_at_ms作为t1起点。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Queue_Testfr_Act(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation, uint64_t now_ms);
|
||
|
||
/**
|
||
* 功能:空闲达到t3后准备TESTFR_ACT,复用同一主动探测状态。
|
||
* 输入参数:公共参数见上;idle_elapsed_ms为距有效链路活动的毫秒数,t3_ms须非0。
|
||
* 输出参数:无独立输出对象;成功时ACT入队,Link等待本地完整发送。
|
||
* 返回值:QUEUED表示ACT已入队;WAIT包括未到期、已有探测或容量不足;ERROR/FATAL按公共约定。
|
||
* 说明:等于t3_ms即满足空闲门限;不更新活动时间,不重置已有探测的t1,不创建系统定时器。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Queue_T3_Testfr(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
uint32_t idle_elapsed_ms, uint32_t t3_ms, uint64_t now_ms);
|
||
|
||
/**
|
||
* 功能:子站接收TESTFR_ACT并排队TESTFR_CON,不结束本端正在进行的主动探测。
|
||
* 输入参数:公共参数见上;apci必须是TESTFR_ACT,now_ms为应答入队时刻。
|
||
* 输出参数:无独立输出对象;每次QUEUED均生成一帧CON,按同一FIFO完成发送。
|
||
* 返回值:QUEUED表示已安排应答;WAIT表示请求尚未受理;ERROR/FATAL按公共约定。
|
||
* 说明:WAIT须有界保留请求并保持接收顺序;TESTFR_CON没有Link完成接口,仍用Manager提交发送结果。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Handle_Testfr_Act(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
const IEC104_ApciInfo_t *apci, uint64_t now_ms);
|
||
|
||
/* 按当前主动探测阶段受理TESTFR_CON;不需要发送槽,不生成应答。
|
||
* OK表示已结束CON_PENDING,Connection此时才撤销探测t1;提前/无匹配CON返回IGNORED且不缓存。
|
||
* ERROR表示参数或会话非法,FATAL表示Link处理异常;调用方不得再直接向Link重复提交。 */
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Handle_Testfr_Con(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation, const IEC104_ApciInfo_t *apci);
|
||
|
||
/*
|
||
* TcpServer取得待发字节并经Net写socket后,由Connection提交本次成功写入增量bytes_written_now。
|
||
* OK表示未写完;FRAME_WRITTEN表示已提交适用的链路完成结果并释放缓冲槽,同时写completion。
|
||
* TESTFR_CON没有Link.Complete;其完整写入只提交缓冲完成,不推进链路过程。
|
||
* bytes_written_now=0不续期;最后一次now_ms为整帧实际发送时刻。错误不写completion。
|
||
* 链路Complete或SendBuffer.Commit_Send_Completion异常时返回FATAL。
|
||
* 同一写入增量不能重复提交,也不能再手动调用缓冲进度、链路Complete或缓冲完成接口。
|
||
* 该函数完成后再分派依赖本地完成的接收事件;不自动处理TESTFR_CON或创建t1定时器。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Handle_Send_Progress(
|
||
IEC104_SendManager_t *manager, IEC104_SendFrameId_t frame_id,
|
||
uint16_t bytes_written_now, uint64_t now_ms, IEC104_LocalSendCompletion_t *completion);
|
||
|
||
/* 匹配队首发送失败/发送超时,分派对应Complete(-1),返回FATAL;错frame_id返回ERROR。 */
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Handle_Send_Failure(
|
||
IEC104_SendManager_t *manager, IEC104_SendFrameId_t frame_id);
|
||
|
||
/*
|
||
* apci须已通过Parse_APCI;仅接受I/S,Link.Handle验证后登记ACK,HIGH核减未确认记录数,NORMAL清理原文或覆盖计数。
|
||
* 内部记录保留到转为事件后显式移除;ACK不代替本地Complete。Link拒绝报文返回PROTOCOL_ERROR;
|
||
* 接受后的同步或原文释放异常返回FATAL,参数、会话或记录预检非法返回ERROR。
|
||
* N(R)只能覆盖已由Handle_Send_Progress提交整帧完成的I帧;ACK越界时不推进确认和接收位置。
|
||
* 异步接入须先提交对应发送结果,再按接收顺序分派相关I/S帧,不能用ACK推定写入成功。
|
||
* 调用方不能先手动Handle再调用本接口,也不能直接用网络N(R)释放业务记录。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Handle_I_S_Frame(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation, const IEC104_ApciInfo_t *apci);
|
||
|
||
/*
|
||
* 返回最早已本地完成、尚未ACK的I帧记录;OK复制记录,EMPTY/ERROR不改输出。
|
||
* 报文槽可能已复用,结果只包含帧身份、序号、时间及NORMAL来源编号,不提供完整APDU或user_tag。
|
||
* 只读当前有效会话,不消费回执;仅有排队/部分发送帧时返回EMPTY。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Peek_Oldest_Sent_Unacked_Record(
|
||
const IEC104_SendManager_t *manager, IEC104_IFrameSendRecord_t *record);
|
||
|
||
/*
|
||
* 按最早已完成未确认I帧的原始written_at_ms检查t1;无定时器或断连副作用。
|
||
* 1到期、0无等待/未到期、-1非法;t1_ms非零,代次须匹配,时钟不得早于起点。
|
||
* ACK越过旧记录后选取下一条的原始时刻;重复ACK和后续发送均不续期。
|
||
*/
|
||
extern int IEC104_SendManager_T1_I_Ack_Expired(
|
||
const IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
uint64_t now_ms, uint32_t t1_ms);
|
||
|
||
/*
|
||
* 返回最早同时满足本地完成和对端ACK的业务记录;无记录EMPTY,尚未就绪WAIT。
|
||
* OK只复制记录,不释放;TcpServer成功复制到事件队列后,经Connection调用Remove_Confirmed_Record。
|
||
* 确认记录未被消费也占record_capacity,满时Prepare_Next_Send等待;S/U仍可继续发送。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Peek_Confirmed_Record(
|
||
const IEC104_SendManager_t *manager, IEC104_IFrameSendRecord_t *record);
|
||
|
||
/* TcpServer已成功保存事件副本后,经Connection调用;核对队首frame_id及两个完成标志,返回OK或ERROR。 */
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Remove_Confirmed_Record(
|
||
IEC104_SendManager_t *manager, IEC104_SendFrameId_t frame_id);
|
||
|
||
/*
|
||
* 按交接顺序复制第offset条业务记录,供故障回收;不依赖故障后的链路状态。
|
||
* 0<=offset<record_count时返回OK;越过已有记录返回EMPTY;非法参数返回ERROR。
|
||
* 输出frame_id由Manager的connection_generation和存储记录的frame_serial组合,返回独立数值副本。
|
||
* 保留peer_acknowledged事实:已确认事件无需重发;未确认不等于对端未收到。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Get_Record_By_Offset(
|
||
const IEC104_SendManager_t *manager, uint16_t offset, IEC104_IFrameSendRecord_t *record);
|
||
|
||
/* 先结束旧I/O并读取回执,再终止SendBuffer和本对象;Queue.Rebind清空HIGH并恢复NORMAL,Queue.Abort全部丢弃。 */
|
||
extern void IEC104_SendManager_Abort(IEC104_SendManager_t *manager);
|
||
|
||
/* ==================== 子站:发送CON ==================== */
|
||
/*
|
||
* START先取得控制槽,再立即激活链路并排队CON;停止等待可被结束,已排队CON不撤回。
|
||
* QUEUED表示已受理,WAIT表示容量不足且未受理,非法为ERROR/FATAL。
|
||
* 从停用状态重新激活时清理HIGH;已激活时重复START保留HIGH和公平配额。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Slave_Handle_Startdt_Act(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
const IEC104_ApciInfo_t *apci, uint64_t now_ms);
|
||
|
||
/*
|
||
* STOP立即停用并清理待发HIGH,无需控制槽;当前APDU、记录及NORMAL仍按原规则处理。
|
||
* OK为新停止义务,IGNORED为排空中重复STOP;ERROR/FATAL为非法或故障。
|
||
* 停止后不接手新HIGH;后到START不恢复已经清理的HIGH。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Slave_Handle_Stopdt_Act(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation,
|
||
const IEC104_ApciInfo_t *apci);
|
||
|
||
/*
|
||
* 推进停止排空并排队S/CON。QUEUED为已入队,EMPTY为无义务,WAIT等容量/确认。
|
||
* 已排队S覆盖全部接收数据时,CON可紧随其后;生成CON即结束Link的排空状态。
|
||
* stop_con_id可为NULL;非NULL时仅在STOP_CON成功入队后写入其身份,其他结果不改输出。
|
||
* 调用方用帧身份结束对应STOP期限,不能由任意旧CON完成清掉新停止的计时。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Slave_Queue_Stopdt_Response(
|
||
IEC104_SendManager_t *manager, uint64_t connection_generation, uint64_t now_ms,
|
||
IEC104_SendFrameId_t *stop_con_id);
|
||
|
||
/* ==================== 子站初始化 ==================== */
|
||
/*
|
||
* 绑定子站新连接:链路已按CONTROLLED角色State_Init,SendBuffer为空,AsduQueue同代次,且没有保留或已覆盖的待确认条目。
|
||
* AsduQueue允许有未提交业务;成功绑定时清空HIGH,保留NORMAL。records为调用方分配的Storage数组,容量1..32767;send_window_k范围1..32767,数组和参数对象不得重叠。
|
||
* 数组中的帧身份只保存frame_serial,连接代次统一取Manager;查询仍输出带完整frame_id的IEC104_IFrameSendRecord_t。
|
||
* 所有对象持续有效且同连接串行操作;无锁、无动态分配、无业务回调。
|
||
* 模块自身合法性和初始条件通过公开查询检查;额外核对Link的固定角色必须为CONTROLLED。
|
||
* 绑定后I帧由Prepare_Next_Send或Submit_Response统一准备,I/S接收经Handle_I_S_Frame,所有发送进度经Handle_Send_Progress。
|
||
* S/TESTFR及子站启停使用本模块的Queue/Handle封装,完成时按原帧类型直接提交Link。
|
||
* 不拥有socket,不调度U帧接收和系统定时器;接收成帧由Receiver提供,调用方负责按序分派。
|
||
* ERROR时不修改manager;成功后不得改写Link角色或绑定对象,Abort后新连接重新初始化。
|
||
*/
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Init(
|
||
IEC104_SendManager_t *manager, IEC104_AsduQueue_t *asdu_queue,
|
||
IEC104_SendBuffer_t *send_buffer, IEC104_LinkState_t *link_state,
|
||
IEC104_IFrameSendRecordStorage_t *records, uint16_t record_capacity,
|
||
uint16_t send_window_k, uint16_t high_priority_burst_limit);
|
||
|
||
/* Connection构造专用:先在局部对象中校验新会话,再一次提交初始CONTROLLED Link、空Buffer和Manager。
|
||
* 成功时Manager直接绑定参数给出的最终Link/Buffer地址,清空HIGH、保留NORMAL;ERROR时所有输出及队列不变。
|
||
* 数组、队列和输出对象不得重叠;仅用于首次构造或旧I/O已结束并Abort后的新连接,不能迁移运行中的会话。
|
||
* 最后一次可能失败的操作是队列HIGH清理,其后仅提交已校验对象,没有需要回滚的失败步骤。 */
|
||
extern IEC104_SendManagerResult_t IEC104_SendManager_Init_Connection(
|
||
IEC104_SendManager_t *manager, IEC104_AsduQueue_t *asdu_queue,
|
||
IEC104_SendBuffer_t *send_buffer, IEC104_LinkState_t *link_state,
|
||
IEC104_SendBufferControlFrame_t *control_frames, uint16_t control_capacity,
|
||
IEC104_IFrameSendRecordStorage_t *records, uint16_t record_capacity,
|
||
uint16_t send_window_k, uint16_t high_priority_burst_limit, uint64_t connection_generation);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
#endif /* IEC104_SENDMANAGER_H */
|