/* * IPv4子站TCP业务接口:隐藏Connection、队列及存储布局;socket只借给事件循环注册。 * 子站业务默认通过IEC104_Slave.h接入;本接口供Slave及独立TCP接入使用。 * Connection、SendManager等由TCP内部调度,服务对象仍保持不透明。 * 一个Slave拥有一个本模块句柄;本模块每个实例同时只服务一条主站TCP连接。 * 本层交付完整APDU及发送/连接事件,Slave再建立ASDU视图和原连接应答接口。 * 本层不解析具体业务命令、不自动应答ASDU;104序号、启停和超时由内部Connection处理。 * * 生命周期:Create -> Config_Default/Init -> Prepare/Process/NextEvent -> Close/NextEvent -> Destroy。 * 独立TCP循环也可使用Poll,由它组合准备、平台等待和推进;两种入口使用同一推进实现。 * Close释放网络并保留通知,Destroy在通知读空后归还静态槽,两者不能互相替代。 * 服务结果、提交结果和取事件结果是三种枚举;须分别比较OK、SUBMIT_OK、EVENT_READY。 * error可为NULL;非NULL时完整填写本次调用诊断,历史网络错误另见状态快照。 * * 同一服务的全部接口在同一线程串行调用;Create/Destroy也须跨服务串行调用。 * 工程只链接一个Net后端;当前Windows实现需链接Ws2_32。 */ #ifndef IEC104_TCPSERVER_H #define IEC104_TCPSERVER_H #include "IEC104_TcpTypes.h" #ifdef __cplusplus extern "C" { #endif /* 不透明句柄:这里仅声明结构体名称,完整成员在IEC104_TcpServer.c中定义。 * 业务只持有Create返回的指针,不能定义对象、计算其sizeof、复制对象或访问内部成员。 * Slave可以保存并传递此指针,无需看见完整结构;这不表示把底层对象公开给业务。 * Connection、SendManager和缓冲区都由实现持有,业务无需为它们分配存储。 */ typedef struct IEC104_TcpServer IEC104_TcpServer_t; /* 从模块内部静态池取得对象,无malloc、无网络操作;无可用槽时返回NULL。 * 默认容量1,可在IEC104_TcpServer_Config.h或编译参数中配置;成功后必须配对Destroy。 * 代次或注册编号耗尽的槽不再分配;句柄至Destroy成功前有效,不得free、清零或自行分配对象。 */ extern IEC104_TcpServer_t *IEC104_TcpServer_Create(void); /* 只释放已Close(或尚未Init)且事件已读空的对象,成功返回OK。 * 无效句柄、重入、仍在运行、未读事件分别返回INVALID_HANDLE/BUSY/INVALID_STATE/EVENTS_PENDING。 * 不隐式丢弃事件或关闭运行中的服务;成功后所有旧句柄副本失效。 * 同一静态槽再分配仍保留代次及注册计数;调用方须结束旧业务任务,不能继续使用旧句柄。 * 成功后应将业务保存的指针置NULL;对象来自静态池,不得调用free。 */ extern IEC104_TcpResult_t IEC104_TcpServer_Destroy( IEC104_TcpServer_t *server, IEC104_TcpError_t *error); /* 填写监听和协议默认值;NULL无操作,不占用对象池。 * 监听默认0.0.0.0:2404;协议默认值沿用Connection配置,静态存储容量由Config.h控制。 */ extern void IEC104_TcpServer_Config_Default(IEC104_TcpServerConfig_t *config); /* 成功返回OK;server来自Create,或已Close且事件已读空。 * 参数/基础配置失败为INVALID_ARGUMENT/INVALID_CONFIG,运行中为INVALID_STATE,旧事件未读为EVENTS_PENDING。 * 协议配置须满足0 至多一次Net_Wait -> Process,使用同一套推进逻辑。 * 每轮返回后NextEvent处理事件;使用应用自己的事件循环时直接调用Prepare/Process。 * max_wait_ms是本轮网络等待上限:0仅查询就绪,正数允许等待;UINT32_MAX仍为有限毫秒数。 * 实际等待还受最近协议/接入/发送重试期限约束;已有事件或可立即推进的工作时不等待。 * 无socket关注时也可有限等待;0毫秒立即返回,UINT32_MAX不会转成系统无限睡眠。 * 上限只约束网络等待,不保证整个函数的执行耗时;0模式下由业务主循环安排其他任务和休眠。 * OK表示服务可继续(包括客户端已断线);尚未Init为INVALID_STATE,重入为BUSY。 * NETWORK_ERROR/INTERNAL_ERROR及身份编号耗尽为服务级失败,保存error后应Close。 * 空闲、接入容量/资源暂缺、可恢复的网络中断及客户端故障均不作为服务级失败。 * 普通通知满仍可处理排在前面的S/U,不以普通通知容量阻挡CON;已确认回执暂留内部记录。 * 遇到无处交付的I帧时暂停续收并置receive_paused;启停专用槽满时暂缓受理后续改变状态的ACT。 * 消费对应通知后由Process恢复,不丢通知、不因通知积压直接断连;等待启停槽不设置receive_paused。 * 保留的I帧会挡住同一TCP流中后续S/U;应用须及时消费事件,原有协议期限不延长,超时仍可断连。 * EOF后停止为未编号NORMAL创建新I帧,原文留给下一连接;当前已编号帧、已受理HIGH及控制帧继续收尾。 * 部分写入跨Poll续发,不再等待新的ACK/STARTDT。 * 若仅因已确认记录尚未转为事件而缺少记录槽,则保留已受理应答,预算恢复或NextEvent腾出空间后续发; * 已排空或仍需对端输入才能解除限制时关闭,原有协议及发送期限仍生效。 */ extern IEC104_TcpResult_t IEC104_TcpServer_Poll( IEC104_TcpServer_t *server, uint32_t max_wait_ms, IEC104_TcpError_t *error); /* 复制并取出最早事件,不执行I/O或业务函数;仅READY修改输出。 * EMPTY/ERROR保持输出不变,副本无需Release;Close后可读完全部收尾事件。 * 本层事件只含值和原文字节,可按值保存;Slave事件另外含自引用,须通过Slave_Copy_Event复制。 * 建立/激活/停用/断开均通过本入口逐条交付,不合并中间变化。 * 消费后安排保留I帧交付、启停CON续发、回执转移或接入重试;这里只取副本,不执行I/O或业务函数。 * 应用须及时消费通知并持续调用Process/Poll;等待通知空间不延长协议期限,长期不消费仍可能超时。 * READY后事件已从内部FIFO移除;需要稍后处理时由业务另存副本,不能依赖自动重投。 */ extern IEC104_TcpEventResult_t IEC104_TcpServer_NextEvent( IEC104_TcpServer_t *server, IEC104_TcpEvent_t *event); /* 复制主动上报到服务级低优先级事件队列(内部NORMAL);本入口只缓存,不立即写socket。 * Init成功后即可调用,无需连接代次,首次连接前和断线期间也可缓存。 * EOF后全部尚未编号的数据及新增数据留给下一连接;重连后现存NORMAL按原顺序重新组帧,STARTDT完成后才发送。 * 容量不足沿用覆盖最旧原文的策略,包括未ACK原文;不持久化,Close清空队列。 * 未ACK不代表对端未收到,恢复可能重复;同一服务应面向相同业务接收方或冗余域。 * 有活动连接且未EOF时,成功入队使Prepare报告need_process;离线缓存不会触发空转。 * 本入口与Send_Response均接收6..249字节ASDU,不含APCI;OK已接手,WAIT需业务保存重试。 * 所有调用须在所属事件循环线程串行执行;异步生产者先唤醒该线程,入队后刷新等待条件。 */ extern IEC104_TcpSubmitResult_t IEC104_TcpServer_Enqueue_Event( IEC104_TcpServer_t *server, const uint8_t *asdu, uint16_t asdu_length); /* 提交当前请求的应答ASDU,须携带原I_FRAME事件的generation;旧代次、无连接及EOF后均拒绝。 * 仅STARTED+IDLE时接手;否则在容量检查前ERROR,不入HIGH、不因拒绝断连。 * 已受理START、窗口及记录有余量、没有当前帧或旧HIGH且公平配额允许时,直接准备I帧并尝试一次send。 * 准入后,已到协议期限时先入HIGH,交由Process接收待处理ACK后再检查期限;其他暂不能准备的情况也入HIGH。 * send后按真实时刻登记进度;若期间跨过ACK/TESTFR的t1,安排立即Process先接收再按原期限判定。 * 确认t1已到且尚未解除时,Process也暂停为HIGH/NORMAL创建新I帧;已有帧及控制发送仍可推进。 * 不增加宽限时间;本地发送和STOP总期限仍在发送推进时检查。 * START受理即激活,已有CON待发时应答入HIGH,实际I帧发送仍排在控制FIFO之后。 * STOP受理即停用并清空待发HIGH,不等待START_CON完成;新应答返回ERROR。 * 后到START成功受理即重新激活并取消旧STOP总计时,已有CON继续按FIFO完成。 * 本项目保留已启用状态下重复START不清理HIGH的约定;重新激活不恢复已经丢弃的HIGH。 * 不等待、不循环发送,并遵守发送重试暂停;无进展时保留当前APDU,由Process/Poll续发,不重复放入HIGH。 * OK已复制到发送缓冲或HIGH,可以复用原文;不承诺已写完或已ACK。HIGH满返回WAIT,本次未接手。 * 受理新STOPDT_ACT时,尚在HIGH中的应答立即作废,无逐条事件;已有I帧及ACK记录继续排空。 * FATAL可能已写出字节并关闭当前连接,须处理收尾事件;HIGH及直接应答均不转给新连接。 * 所有调用在事件循环线程串行执行;返回后刷新Prepare,及时处理本地续跑、socket及定时变化。 */ extern IEC104_TcpSubmitResult_t IEC104_TcpServer_Send_Response( IEC104_TcpServer_t *server, uint64_t generation, const uint8_t *asdu, uint16_t asdu_length); /* 不排队应答:HIGH/NORMAL有待发数据、当前帧未完成、窗口/记录不足或协议工作到期时WAIT。 * WAIT未接手,不保存ASDU;继续Process/Poll后可用新取值重试。 * OK已复制到SendBuffer并固定发送顺序,部分写入时也不能重交,不要求已ACK。 * 调用串行、会话身份、FATAL和等待条件刷新约定同Send_Response。 */ extern IEC104_TcpSubmitResult_t IEC104_TcpServer_Try_Send_Response( IEC104_TcpServer_t *server, uint64_t generation, const uint8_t *asdu, uint16_t asdu_length); /* 查询当前主站的IPv4、源端口及连接代次;仅OK写peer,不查询socket、不推进协议。 * server须已Init且当前持有客户端(包括EOF收尾期间);未Init为INVALID_STATE,无客户端为NOT_CONNECTED。 * NULL输出为INVALID_ARGUMENT;无效句柄/重入为INVALID_HANDLE/BUSY;失败不改peer,error可为NULL。 * 结果是可独立保存的副本,不保证以后仍有同一连接;Close_Connection应携带该副本的generation。 * 处理历史事件时使用事件内peer,不可将此处查到的新主站地址归到旧代次。 */ extern IEC104_TcpResult_t IEC104_TcpServer_Get_Remote_Address( const IEC104_TcpServer_t *server, IEC104_TcpPeerInfo_t *peer, IEC104_TcpError_t *error); /* 立即结束指定代次的当前主站,保留监听、服务队列和已有业务事件;不执行STOPDT或等待发送/ACK。 * generation来自原连接事件或Get_Remote_Address,0为INVALID_ARGUMENT;非零但与当前连接不符为STALE_GENERATION。 * 未Init为INVALID_STATE,无客户端为NOT_CONNECTED;拒绝结果均不关闭连接、不产生新的收尾事件。 * 成功复用断线收尾流程:交付剩余I帧/记录及DISCONNECTED,清空HIGH,保留现存NORMAL并恢复离线缓存。 * 主动断开不新增协议故障,不补造发送完成或ACK;部分/未确认NORMAL恢复后可能重复,由业务处理。 * 队列解除绑定异常返回INTERNAL_ERROR/QUEUE_STATE:客户端已结束,监听保留,须Close服务处理异常。 * 同一事件循环线程串行调用;返回后重新Prepare并移除旧客户端注册,后续Process继续接入新主站。 * 此操作不是封禁;对端可再次连接。Close才关闭整个服务并清空缓存。 */ extern IEC104_TcpResult_t IEC104_TcpServer_Close_Connection( IEC104_TcpServer_t *server, uint64_t generation, IEC104_TcpError_t *error); /* 取得端口、诊断、会话及HIGH/NORMAL队列快照,不执行I/O;Create后、Init失败后、Close后均可用。 * OK时写入status;INVALID_HANDLE/INVALID_ARGUMENT/BUSY保持status不变,但仍填写error。 * 会话/队列分别检查connection_status_valid/asdu_queue_status_valid,无效快照不妨碍读取其他服务状态。 * 队列统计在Init成功后即可读取,断线期间继续有效;Close后无效且清零,不返回历史队列占用。 * accept_paused及剩余时间按查询时刻计算;last_accept_error和连续失败计数用于观察接入异常。 * 所有调用须在同一线程串行执行;查询为常数时间,不消费ASDU或记录,也不改变发送调度。 * 接口失败诊断直接使用该次调用的error,统计数值不能代替提交返回值。 */ extern IEC104_TcpResult_t IEC104_TcpServer_Get_Status( const IEC104_TcpServer_t *server, IEC104_TcpServerStatus_t *status, IEC104_TcpError_t *error); /* OK表示完成收尾或本就未初始化;无效句柄/重入为INVALID_HANDLE/BUSY。 * 关闭网络、清空ASDU队列并撤销接入/发送重试期限,保留未读事件及接入历史诊断。 * 不等待业务发送或ACK;重复Close有效。事件读空后可以Init重开,或Destroy归还静态槽。 * 应用负责取消注册和定时器;关闭前已取得的socket及通知不可作为下一次Init的身份使用。 */ extern IEC104_TcpResult_t IEC104_TcpServer_Close( IEC104_TcpServer_t *server, IEC104_TcpError_t *error); #ifdef __cplusplus } #endif #endif /* IEC104_TCPSERVER_H */