/* * TCP内部的子站连接协调:串行衔接Receiver、Link和SendManager,不执行socket I/O。 * 本头文件供TcpServer实现使用;业务通过IEC104_TcpServer.h接入。 * 下文的调用方指TcpServer;对象及借用存储由其管理,内部帧和记录由其转为业务事件。 */ #ifndef IEC104_CONNECTION_H #define IEC104_CONNECTION_H #include "IEC104_Receiver.h" #include "IEC104_SendManager.h" #ifdef __cplusplus extern "C" { #endif /* 按具体值分支;只有SEND_READY提供发送字节,FRAME_WRITTEN提供完成结果。 */ typedef enum { IEC104_CONNECTION_FATAL = -2, /* 已冻结连接;停止I/O,读取记录后Close */ IEC104_CONNECTION_ERROR = -1, /* 参数、身份、调用阶段或时钟非法 */ IEC104_CONNECTION_EMPTY = 0, /* 无可查询项或无待发送业务 */ IEC104_CONNECTION_OK = 1, /* 本次操作成功;发送进度接口中表示尚未整帧写完 */ IEC104_CONNECTION_WAIT = 2, /* 接收请求未受理,或发送门控/容量阻塞 */ IEC104_CONNECTION_FRAME_READY = 3, /* I帧已完成链路处理,等待TcpServer复制为事件并释放 */ IEC104_CONNECTION_SEND_READY = 4, /* 当前帧的剩余字节可交给实际传输 */ IEC104_CONNECTION_FRAME_WRITTEN = 5 /* 整帧写完及对应完成处理均已提交 */ } IEC104_ConnectionResult_t; typedef enum { IEC104_CONNECTION_UNINITIALIZED = 0, /* 零值标记;仍须成功Init后才能调用运行接口 */ IEC104_CONNECTION_ACTIVE, /* TCP由TcpServer管理;不表示已经STARTED */ IEC104_CONNECTION_FAILED, /* 保留接收原文、发送记录及ASDU队列供回收 */ IEC104_CONNECTION_CLOSED /* 借用已解除;ASDU队列仍由TcpServer持有 */ } IEC104_ConnectionPhase_t; typedef enum { IEC104_CONNECTION_FAULT_NONE = 0, /* 尚未登记故障 */ IEC104_CONNECTION_FAULT_FRAME, /* 起始字节、APCI或ASDU最低长度非法 */ IEC104_CONNECTION_FAULT_PROTOCOL, /* 序号、ACK或接收阶段不符合协议 */ IEC104_CONNECTION_FAULT_INTERNAL, /* 模块状态或交接失败 */ IEC104_CONNECTION_FAULT_TRANSPORT, /* 发送失败或外部传输故障;正常有序EOF由TcpServer收尾 */ IEC104_CONNECTION_FAULT_LOCAL_SEND_TIMEOUT, /* 当前APDU排队及写入总期限到达 */ IEC104_CONNECTION_FAULT_I_ACK_TIMEOUT, /* 已完整发出的I帧等待对端ACK达到t1 */ IEC104_CONNECTION_FAULT_TESTFR_TIMEOUT, /* 本端TESTFR_ACT已发完,等待CON达到t1 */ IEC104_CONNECTION_FAULT_STOP_TIMEOUT /* STOP已受理,确认及对应CON发送未在总期限内完成 */ } IEC104_ConnectionFault_t; /* 内部连接配置的类型别名;配置值由公共Types定义,与TcpServer使用同一布局。 */ typedef IEC104_ProtocolConfig_t IEC104_ConnectionConfig_t; /* TcpServer借用原文至匹配的Release或Close;代次与receive_serial共同标识这次内部读取。 */ typedef struct { IEC104_ReceivedFrame_t frame; /* 完整APDU/APCI;仅交付已受理的I帧 */ uint64_t receive_serial; /* 本连接内不复用,拒绝迟到的重复Release */ } IEC104_ConnectionReceivedFrame_t; /* 无指针的观察快照;CLOSED后的链路字段是收尾时的历史值,不用这些字段自行组合收发门控。 */ typedef struct { uint64_t connection_generation; /* 本快照所属连接代次 */ IEC104_ConnectionPhase_t phase; /* Connection运行、故障冻结或已关闭阶段 */ IEC104_ConnectionFault_t fault; /* 首次故障原因;正常关闭仍可为NONE */ IEC104_DataTransferState_t dt_state; /* 受理START/STOP后立即更新的数据传输状态 */ IEC104_DtProcedure_t dt_procedure; /* 子站仅IDLE或STOP_DRAINING,CON进度由发送缓冲维护 */ IEC104_TestfrState_t testfr_state; /* 本端主动探测的发送/等待CON进度 */ uint16_t send_record_count; /* 全部未消费I帧记录数,包括已经ACK的回执 */ uint8_t has_received_i_frame; /* TcpServer可Peek,复制为事件后显式Release */ uint8_t has_unhandled_frame; /* WAIT或故障时保留的尚未受理帧 */ } IEC104_ConnectionStatus_t; /* * 仅供TcpServer内部的启停通知适配;不是业务回调,不得重入修改接口,可只读查询状态。 * can_accept只读查询启停通知容量;notify在实际状态改变后、请求处理返回前复制通知。 * 两个函数必须同时设置或同时为空;为空时独立Connection不产生通知背压。 */ typedef struct { void *context; int (*can_accept)(void *context); int (*notify)(void *context, IEC104_DataTransferState_t state); } IEC104_ConnectionTransferObserver_t; /* * TcpServer内部持有,完整布局用于静态存储;全部字段由接口维护,无malloc、无锁、无业务回调。 * Init后地址固定,不能复制/移动:Manager引用本结构内唯一的Link和SendBuffer。 * 同一连接的所有调用必须串行;不要再直接调用内含对象的修改接口。 * Link和SendBuffer的运行期修改统一经Manager;Connection只读Link,发送期限也通过Manager查询。 * ASDU队列独立持有以支持重连恢复;对象、数组及输入/输出参数存储不得重叠。 */ typedef struct { IEC104_Receiver_t receiver; /* 组装半帧并保留完整APDU;I帧由TcpServer复制为事件后释放 */ IEC104_LinkState_t link_state; /* 本连接唯一协议状态,收发共用序号和启停进度 */ IEC104_SendBuffer_t send_buffer; /* 当前I帧及控制FIFO,保存实际写入进度 */ IEC104_SendManager_t send_manager; /* 关联ASDU、发送帧和ACK记录,引用本对象内的Link/Buffer */ IEC104_ConnectionConfig_t config; /* Init时复制的配置,运行期间保持不变 */ IEC104_ConnectionTransferObserver_t transfer_observer; /* 内部通知容量及变化交接 */ IEC104_SendFrameId_t stop_con_frame_id; /* 当前STOP对应的最后一帧CON,拒绝旧完成结束新期限 */ uint64_t connection_generation; /* 非零且跨连接不复用,所有外部事件均须匹配 */ uint64_t last_now_ms; /* 串行事件的单调时刻,不接受倒退 */ uint64_t last_activity_ms; /* 完整合法APDU到达或本地整帧完成;反向启停CON除外 */ uint64_t received_at_ms; /* 当前完整接收帧到达时刻,WAIT不续期 */ uint64_t receive_serial; /* 每次成帧递增;耗尽时结束连接 */ uint64_t rx_ack_started_ms; /* 当前连续未确认接收区间的最早起点 */ uint64_t testfr_started_ms; /* 本次TESTFR_ACT完整写出的时刻 */ uint64_t stop_started_ms; /* 本轮STOP原成帧时刻;受理时启用计时,受理前WAIT也计入总期限 */ IEC104_ConnectionPhase_t phase; /* Connection生命周期,独立于Link的STARTED/STOPPED */ IEC104_ConnectionFault_t fault; /* 只登记首次故障;Close保留该值供查询 */ uint8_t rx_link_applied; /* I帧已交给Manager,不得因重复Peek/驱动再交一次 */ uint8_t rx_ack_timer_active; /* 0/1:存在尚未全部确认的接收区间,rx_ack_started_ms有效 */ uint8_t testfr_timer_active; /* 0/1:本次ACT已完整发送,testfr_started_ms有效 */ uint8_t stop_timer_active; /* 0/1:STOP已受理、未取消且CON尚未完成,stop_started_ms有效 */ uint8_t receive_batch_active; /* 有界接收批次内暂缓对端确认超时,下一次Tick结束批次 */ uint8_t receive_batch_pending; /* 固定输入快照尚未处理完,下一轮必须立即续收 */ } IEC104_Connection_t; /* 写入Link默认值;本地发送和停止期限默认等于默认t1,HIGH严格优先。NULL无操作。 */ extern void IEC104_Connection_Config_Default(IEC104_ConnectionConfig_t *config); /* * 初始化新子站连接,初始STOPPED;generation非零且跨连接不复用,now_ms允许0。 * queue已Init/Rebind到该代次且无待ACK条目;可有待发NORMAL,成功时清空HIGH。 * control_capacity>=1,record_capacity为1..32767;config不可为NULL。 * 配置须满足0 Queue.Rebind(新代次) -> Connection.Init -> 新STARTDT。 */ extern IEC104_ConnectionResult_t IEC104_Connection_Close( IEC104_Connection_t *connection, uint64_t connection_generation); #ifdef __cplusplus } #endif #endif /* IEC104_CONNECTION_H */