Files
STM32F4-Base/IEC60870-Slave/104/IEC104_SendManager.h
T
STF 7596f3371a
CI / L0 静态门禁 (pre-commit) (push) Failing after 44s
CI / L0.5 主机侧单测 (MSVC) (push) Failing after 42s
补充总召唤的广播地址,电度召唤+时钟同步业务的实现
2026-10-29 03:20:19 -07:00

403 lines
29 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.
/*
* 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 */