/** * 子站发送字节存储:一份当前I APDU,以及独立的固定6字节S/U控制FIFO。 * 已准备的当前帧连续发送;本地完成提交后复用存储,等待ACK的记录由Manager保存。 * HIGH原文已在提交时出队;NORMAL原文由Queue保留至ACK或被后续入队覆盖。 * 本文件不进行I/O,不分配协议序号,不推进Link状态;所有操作由同连接上下文串行执行。 */ #include "IEC104_SendBuffer.h" #include "IEC104_Codec.h" #include #include /** * 功能:将控制队列的逻辑偏移换算为数组下标。 * 输入参数:buffer——容量非零且边界已验证的缓冲;offset——相对控制队首的槽数偏移,不是字节数。 * 输出参数:无。 * 返回值:0..control_capacity-1范围内的物理下标。 * 说明:队尾位置也用此公式计算;使用32位加法,避免两个16位索引相加溢出。 */ static uint16_t Control_Index(const IEC104_SendBuffer_t *buffer, uint16_t offset) { return (uint16_t)(((uint32_t)buffer->control_head_index + offset) % buffer->control_capacity); } /** * 功能:检查一帧已提交报文的身份、类型、长度和写入进度。 * 输入参数:buffer——已检查公共字段的缓冲;state——非NULL帧元数据;is_i——非0检查当前I帧。 * 输出参数:无。 * 返回值:合法返回1,否则0。 * 说明:I帧另核对构帧快照;控制帧固定6字节。这里只检查元数据,不重新解析APDU字节。 */ static int Frame_State_Valid(const IEC104_SendBuffer_t *buffer, const IEC104_SendBufferFrameState_t *state, int is_i) { if (state->frame_serial == 0U || state->frame_serial >= buffer->next_frame_serial || state->written_bytes > state->apdu_length || state->encoded_nr > IEC104_SEQUENCE_MASK || (state->written_bytes == state->apdu_length && state->written_at_ms < state->queued_at_ms)) { return 0; } if (is_i) { return state->frame_type == IEC104_SEND_BUFFER_FRAME_I && state->apdu_length >= IEC104_APCI_LEN + IEC104_ASDU_HEADER_LEN && state->apdu_length <= IEC104_MAX_APDU_LEN && buffer->i_frame_snapshot.send_number <= IEC104_SEQUENCE_MASK && buffer->i_frame_snapshot.receive_number == state->encoded_nr && buffer->i_frame_snapshot.frame_length == state->apdu_length; } return state->frame_type >= IEC104_SEND_BUFFER_FRAME_S_ACK && state->frame_type <= IEC104_SEND_BUFFER_FRAME_TESTFR_CON && state->apdu_length == IEC104_APCI_LEN && (state->frame_type == IEC104_SEND_BUFFER_FRAME_S_ACK || state->encoded_nr == 0U); } /** * 功能:检查缓冲控制结构、当前I帧、控制FIFO端点和唯一预留。 * 输入参数:buffer——待检查对象,允许NULL。 * 输出参数:无。 * 返回值:访问到的边界及元数据自洽返回1,否则0。 * 说明:不遍历控制FIFO中间槽,不维护空闲链、保留链或另一套协议序号。 * 当前I帧存在时,后续控制队首必须尚未写出字节,避免两个APDU交错发送。 */ static int Buffer_State_Valid(const IEC104_SendBuffer_t *buffer) { const IEC104_SendBufferFrameState_t *state; if (buffer == NULL || buffer->is_initialized != 1U || buffer->control_frames == NULL || buffer->control_capacity == 0U || buffer->control_head_index >= buffer->control_capacity || buffer->control_count > buffer->control_capacity || buffer->connection_generation == 0U || buffer->next_frame_serial == 0U || buffer->has_i_frame > 1U || buffer->has_reservation > 1U || buffer->reservation_is_i_frame > 1U || (buffer->has_reservation == 0U && buffer->reservation_is_i_frame != 0U)) { return 0; } if (buffer->has_i_frame != 0U && !Frame_State_Valid(buffer, &buffer->i_frame_state, 1)) { return 0; } if (buffer->control_count != 0U) { state = &buffer->control_frames[buffer->control_head_index].state; if (!Frame_State_Valid(buffer, state, 0) || (buffer->has_i_frame != 0U && state->written_bytes != 0U) || !Frame_State_Valid(buffer, &buffer->control_frames[ Control_Index(buffer, (uint16_t)(buffer->control_count - 1U))].state, 0)) { return 0; } } /* 预留槽还未进入发送集合,类型与长度保持初始值;身份必须是刚分配的最后一个。 */ if (buffer->has_reservation != 0U) { if (buffer->reservation_is_i_frame != 0U) { if (buffer->has_i_frame != 0U || buffer->control_count != 0U) { return 0; } state = &buffer->i_frame_state; } else { if (buffer->control_count == buffer->control_capacity) { return 0; } state = &buffer->control_frames[Control_Index(buffer, buffer->control_count)].state; } if (state->frame_serial == 0U || state->frame_serial != buffer->next_frame_serial - 1U || state->frame_type != IEC104_SEND_BUFFER_FRAME_INVALID || state->apdu_length != 0U || state->written_bytes != 0U) { return 0; } } return 1; } /** * 功能:核对事件是否属于指定帧的当前连接代次和预留身份。 * 输入参数:buffer/state——已验证对象;id——调用方提交的帧身份。 * 输出参数:无。 * 返回值:代次及非零frame_serial都匹配返回1,否则0。 * 说明:N(S)会回绕,不能用协议序号代替本地发送身份。 */ static int Frame_Id_Matches(const IEC104_SendBuffer_t *buffer, const IEC104_SendBufferFrameState_t *state, IEC104_SendFrameId_t id) { return id.connection_generation == buffer->connection_generation && id.frame_serial != 0U && id.frame_serial == state->frame_serial; } /** * 功能:核对本地完成分派类型与已解析APCI是否一致。 * 输入参数:type——请求保存的帧类型;apci——成功解析的非NULL描述。 * 输出参数:无。 * 返回值:匹配返回1,否则0。 * 说明:只允许子站发送I、S、STARTDT_CON、STOPDT_CON及TESTFR ACT/CON。 */ static int Frame_Type_Matches(IEC104_SendBufferFrameType_t type, const IEC104_ApciInfo_t *apci) { switch (type) { case IEC104_SEND_BUFFER_FRAME_I: return apci->frame_type == IEC104_FRAME_TYPE_I; case IEC104_SEND_BUFFER_FRAME_S_ACK: return apci->frame_type == IEC104_FRAME_TYPE_S; case IEC104_SEND_BUFFER_FRAME_STARTDT_CON: return apci->frame_type == IEC104_FRAME_TYPE_U && apci->u_function == IEC104_U_STARTDT_CON; case IEC104_SEND_BUFFER_FRAME_STOPDT_CON: return apci->frame_type == IEC104_FRAME_TYPE_U && apci->u_function == IEC104_U_STOPDT_CON; case IEC104_SEND_BUFFER_FRAME_TESTFR_ACT: return apci->frame_type == IEC104_FRAME_TYPE_U && apci->u_function == IEC104_U_TESTFR_ACT; case IEC104_SEND_BUFFER_FRAME_TESTFR_CON: return apci->frame_type == IEC104_FRAME_TYPE_U && apci->u_function == IEC104_U_TESTFR_CON; default: return 0; } } /** * 功能:初始化新连接的当前I帧存储,并绑定独立的控制槽数组。 * 输入参数:control_frames——持续有效的调用方数组;control_capacity——1..65535个槽; * connection_generation——非零且跨连接不复用的代次。 * 输出参数:buffer——非NULL,仅成功时写入空闲状态。 * 返回值:OK或ERROR,失败不改buffer。 * 说明:唯一I APDU内置于buffer;不malloc、不清空控制数组、不创建socket或定时器。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Init( IEC104_SendBuffer_t *buffer, IEC104_SendBufferControlFrame_t *control_frames, uint16_t control_capacity, uint64_t connection_generation) { IEC104_SendBuffer_t prepared; if (buffer == NULL || control_frames == NULL || control_capacity == 0U || connection_generation == 0U) { return IEC104_SEND_BUFFER_ERROR; } memset(&prepared, 0, sizeof(prepared)); prepared.control_frames = control_frames; prepared.control_capacity = control_capacity; prepared.connection_generation = connection_generation; prepared.next_frame_serial = 1U; prepared.is_initialized = 1U; *buffer = prepared; return IEC104_SEND_BUFFER_OK; } /** * 功能:查询控制帧数量、当前I帧及预留状态。 * 输入参数:buffer——有效发送缓冲。 * 输出参数:status——非NULL,仅OK时写入数值副本。 * 返回值:OK或ERROR,失败不改输出。 * 说明:i_frame_ns仅在has_i_frame=1时有效;is_idle表示当前为空闲, * 不证明TCP为新连接,也不报告Link的完成或ACK边界。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Get_Status( const IEC104_SendBuffer_t *buffer, IEC104_SendBufferStatus_t *status) { IEC104_SendBufferStatus_t prepared; if (!Buffer_State_Valid(buffer) || status == NULL) { return IEC104_SEND_BUFFER_ERROR; } prepared.connection_generation = buffer->connection_generation; prepared.control_count = buffer->control_count; prepared.has_i_frame = buffer->has_i_frame; prepared.i_frame_ns = buffer->has_i_frame != 0U ? buffer->i_frame_snapshot.send_number : 0U; prepared.has_reservation = buffer->has_reservation; prepared.is_idle = buffer->control_count == 0U && buffer->has_i_frame == 0U && buffer->has_reservation == 0U; /* Connection据此判断到期的t2/t3是否能准备新控制帧,避免容量阻塞时反复零等待。 */ prepared.control_has_room = buffer->has_reservation == 0U && buffer->control_count < buffer->control_capacity; *status = prepared; return IEC104_SEND_BUFFER_OK; } /** * 功能:取得唯一预留,为随后Link构帧提供可写存储。 * 输入参数:buffer——有效缓冲;is_i——1预留当前I槽,0预留控制队尾。 * 输出参数:reservation——非NULL,仅OK时写入身份、APDU地址和容量;buffer记录预留。 * 返回值:OK成功;BUSY表示已有预留或I帧不能取得当前发送位置; * FULL表示控制槽满;ERROR表示参数、状态或身份编号耗尽。 * 说明:I槽只能在当前I及控制队列均空时预留;S/U可以排在已准备的I帧之后。 * 编号在成功预留时消耗,取消后也不复用;非OK不修改对象或输出。 */ static IEC104_SendBufferResult_t Reserve_Frame( IEC104_SendBuffer_t *buffer, IEC104_SendBufferReservation_t *reservation, uint8_t is_i) { IEC104_SendBufferFrameState_t *state; IEC104_SendBufferReservation_t prepared; if (!Buffer_State_Valid(buffer) || reservation == NULL) { return IEC104_SEND_BUFFER_ERROR; } if (buffer->has_reservation != 0U || (is_i != 0U && (buffer->has_i_frame != 0U || buffer->control_count != 0U))) { return IEC104_SEND_BUFFER_BUSY; } if (is_i == 0U && buffer->control_count == buffer->control_capacity) { return IEC104_SEND_BUFFER_FULL; } if (buffer->next_frame_serial == UINT64_MAX) { return IEC104_SEND_BUFFER_ERROR; } if (is_i != 0U) { state = &buffer->i_frame_state; prepared.apdu = buffer->i_apdu; prepared.apdu_capacity = IEC104_MAX_APDU_LEN; memset(&buffer->i_frame_snapshot, 0, sizeof(buffer->i_frame_snapshot)); } else { IEC104_SendBufferControlFrame_t *control = &buffer->control_frames[ Control_Index(buffer, buffer->control_count)]; state = &control->state; prepared.apdu = control->apdu; prepared.apdu_capacity = IEC104_APCI_LEN; } memset(state, 0, sizeof(*state)); memset(prepared.apdu, 0, prepared.apdu_capacity); state->frame_serial = buffer->next_frame_serial++; buffer->has_reservation = 1U; buffer->reservation_is_i_frame = is_i; prepared.frame_id.connection_generation = buffer->connection_generation; prepared.frame_id.frame_serial = state->frame_serial; *reservation = prepared; return IEC104_SEND_BUFFER_OK; } /** * 功能:预留控制FIFO的下一个空槽。 * 输入参数:buffer——有效发送缓冲。 * 输出参数:reservation——仅OK时返回frame_id和6字节可写APDU存储。 * 返回值:OK、FULL、BUSY或ERROR,含义见Reserve_Frame。 * 说明:不占I帧窗口或业务记录;成功后须构帧提交,或在未构帧成功时取消预留。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Reserve_S_U_Frame( IEC104_SendBuffer_t *buffer, IEC104_SendBufferReservation_t *reservation) { return Reserve_Frame(buffer, reservation, 0U); } /** * 功能:预留唯一当前I帧的APDU存储。 * 输入参数:buffer——有效发送缓冲。 * 输出参数:reservation——仅OK时返回frame_id和最大255字节可写存储。 * 返回值:OK、BUSY或ERROR,含义见Reserve_Frame。 * 说明:此处尚未分配协议N(S);后续由Link.Prepare_I_Frame在实际发送准备时分配。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Reserve_I_Frame( IEC104_SendBuffer_t *buffer, IEC104_SendBufferReservation_t *reservation) { return Reserve_Frame(buffer, reservation, 1U); } /** * 功能:校验预留报文并发布为当前I帧或控制FIFO的新队尾。 * 输入参数:buffer/frame_id——本次预留及身份;frame_type/apdu_length——实际报文描述; * i_frame_snapshot——I帧必须传原Prepare快照,S/U必须为NULL; * queued_at_ms——原始本地发送期限起点,使用单调毫秒,允许0。 * 输出参数:buffer及对应槽元数据——成功时保存描述、时间、快照并解除预留。 * 返回值:OK或ERROR;ERROR不修改缓冲状态,调用前存在的合法预留仍保留。 * 说明:调用前APDU已写入reservation.apdu。Link构帧成功后本接口出错须结束连接, * 不能通过Cancel撤回已经分配的N(S)或已建立的控制发送义务。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Commit_Frame( IEC104_SendBuffer_t *buffer, IEC104_SendFrameId_t frame_id, IEC104_SendBufferFrameType_t frame_type, uint16_t apdu_length, const IEC104_IFrameTx_t *i_frame_snapshot, uint64_t queued_at_ms) { IEC104_SendBufferFrameState_t *state; const uint8_t *apdu; IEC104_ApciInfo_t parsed; if (!Buffer_State_Valid(buffer) || buffer->has_reservation == 0U) { return IEC104_SEND_BUFFER_ERROR; } if (buffer->reservation_is_i_frame != 0U) { if (frame_type != IEC104_SEND_BUFFER_FRAME_I || i_frame_snapshot == NULL || apdu_length < IEC104_APCI_LEN + IEC104_ASDU_HEADER_LEN || apdu_length > IEC104_MAX_APDU_LEN || i_frame_snapshot->frame_length != apdu_length) { return IEC104_SEND_BUFFER_ERROR; } state = &buffer->i_frame_state; apdu = buffer->i_apdu; } else { const IEC104_SendBufferControlFrame_t *control = &buffer->control_frames[ Control_Index(buffer, buffer->control_count)]; if (frame_type == IEC104_SEND_BUFFER_FRAME_I || i_frame_snapshot != NULL || apdu_length != IEC104_APCI_LEN) { return IEC104_SEND_BUFFER_ERROR; } state = &buffer->control_frames[Control_Index(buffer, buffer->control_count)].state; apdu = control->apdu; } /* 完成全部匹配检查后才发布元数据;实际字节和保存的完成分派类型不能分离。 */ if (!Frame_Id_Matches(buffer, state, frame_id) || !IEC104_Parse_APCI(apdu, apdu_length, &parsed) || !Frame_Type_Matches(frame_type, &parsed) || (i_frame_snapshot != NULL && (i_frame_snapshot->send_number != parsed.send_number || i_frame_snapshot->receive_number != parsed.receive_number))) { return IEC104_SEND_BUFFER_ERROR; } state->apdu_length = apdu_length; state->frame_type = (uint8_t)frame_type; state->encoded_nr = parsed.receive_number; state->queued_at_ms = queued_at_ms; if (i_frame_snapshot != NULL) { buffer->i_frame_snapshot = *i_frame_snapshot; buffer->has_i_frame = 1U; } else { buffer->control_count++; } buffer->has_reservation = 0U; buffer->reservation_is_i_frame = 0U; return IEC104_SEND_BUFFER_OK; } /** * 功能:取消身份匹配的唯一预留。 * 输入参数:buffer——有效缓冲;frame_id——Reserve返回的完整身份。 * 输出参数:buffer——成功时清除预留标志,不推进控制队列或发布当前I帧。 * 返回值:OK或ERROR,身份不符或没有预留时不修改状态。 * 说明:调用方保证Link尚未成功构帧;这里只归还存储,不回滚Link,不复用frame_serial。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Cancel_Reservation( IEC104_SendBuffer_t *buffer, IEC104_SendFrameId_t frame_id) { const IEC104_SendBufferFrameState_t *state; if (!Buffer_State_Valid(buffer) || buffer->has_reservation == 0U) { return IEC104_SEND_BUFFER_ERROR; } state = buffer->reservation_is_i_frame != 0U ? &buffer->i_frame_state : &buffer->control_frames[Control_Index(buffer, buffer->control_count)].state; if (!Frame_Id_Matches(buffer, state, frame_id)) { return IEC104_SEND_BUFFER_ERROR; } buffer->has_reservation = 0U; buffer->reservation_is_i_frame = 0U; return IEC104_SEND_BUFFER_OK; } /** * 功能:取得已经准备好的当前发送帧及剩余字节。 * 输入参数:buffer——有效发送缓冲。 * 输出参数:head_info——非NULL,仅OK时写入身份、元数据及剩余字节借用指针。 * 返回值:OK有当前帧,EMPTY没有已提交报文,ERROR表示参数或状态非法。 * 说明:优先续传当前I帧,否则取控制队首;不选择ASDU、不重新编号。 * 已写完但尚未Commit_Send_Completion的帧仍返回OK,此时remaining_bytes为0。 * 原始帧即使因EAGAIN尚未写出字节也保持当前位置,借用在完成释放前结束。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Peek_Send_Head( const IEC104_SendBuffer_t *buffer, IEC104_SendBufferHeadInfo_t *head_info) { IEC104_SendBufferHeadInfo_t prepared; const IEC104_SendBufferFrameState_t *state; const uint8_t *apdu; if (!Buffer_State_Valid(buffer) || head_info == NULL) { return IEC104_SEND_BUFFER_ERROR; } memset(&prepared, 0, sizeof(prepared)); if (buffer->has_i_frame != 0U) { state = &buffer->i_frame_state; apdu = buffer->i_apdu; prepared.i_frame_snapshot = buffer->i_frame_snapshot; } else if (buffer->control_count != 0U) { state = &buffer->control_frames[buffer->control_head_index].state; apdu = buffer->control_frames[buffer->control_head_index].apdu; } else { return IEC104_SEND_BUFFER_EMPTY; } prepared.frame_id.connection_generation = buffer->connection_generation; prepared.frame_id.frame_serial = state->frame_serial; prepared.pending_data = apdu + state->written_bytes; prepared.remaining_bytes = (uint16_t)(state->apdu_length - state->written_bytes); prepared.apdu_length = state->apdu_length; prepared.written_bytes = state->written_bytes; prepared.frame_type = (IEC104_SendBufferFrameType_t)state->frame_type; prepared.encoded_nr = state->encoded_nr; prepared.queued_at_ms = state->queued_at_ms; prepared.written_at_ms = state->written_at_ms; *head_info = prepared; return IEC104_SEND_BUFFER_OK; } /** * 功能:记录当前帧本次实际成功写入的字节增量。 * 输入参数:buffer/frame_id——当前缓冲及帧身份;bytes_written_now——不超过剩余长度的增量; * written_at_ms——本次实际写入时刻;仅整帧写完时校验不早于queued_at_ms并保存。 * 输出参数:buffer——推进written_bytes;整帧写完时登记written_at_ms。 * 返回值:OK尚未完整;FRAME_WRITTEN恰好整帧写完;ERROR表示身份、长度、时间或状态非法。 * 说明:增量0不推进;部分写入不续期。函数只记账,不写socket、不调用Link.Complete, * 同一成功增量不能重报;最终完成后再重复记账返回ERROR。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Record_Written_Bytes( IEC104_SendBuffer_t *buffer, IEC104_SendFrameId_t frame_id, uint16_t bytes_written_now, uint64_t written_at_ms) { IEC104_SendBufferFrameState_t *state; if (!Buffer_State_Valid(buffer) || (buffer->has_i_frame == 0U && buffer->control_count == 0U)) { return IEC104_SEND_BUFFER_ERROR; } state = buffer->has_i_frame != 0U ? &buffer->i_frame_state : &buffer->control_frames[buffer->control_head_index].state; if (!Frame_Id_Matches(buffer, state, frame_id) || state->written_bytes == state->apdu_length || bytes_written_now > (uint16_t)(state->apdu_length - state->written_bytes) || (bytes_written_now == (uint16_t)(state->apdu_length - state->written_bytes) && written_at_ms < state->queued_at_ms)) { return IEC104_SEND_BUFFER_ERROR; } /* 只累计成功字节;queued_at_ms始终保留,最后一段才建立实际写完时刻。 */ state->written_bytes = (uint16_t)(state->written_bytes + bytes_written_now); if (state->written_bytes == state->apdu_length) { state->written_at_ms = written_at_ms; return IEC104_SEND_BUFFER_FRAME_WRITTEN; } return IEC104_SEND_BUFFER_OK; } /** * 功能:释放已整帧写完且身份匹配的当前I槽或控制队首。 * 输入参数:buffer——有效缓冲;frame_id——本次已经完成的当前帧身份。 * 输出参数:buffer——清除has_i_frame,或推进控制队首并减少数量。 * 返回值:OK或ERROR;未写完、身份不符或新队首非法时不释放。 * 说明:调用方须先提交该帧适用的Link完成结果;I帧还须登记本地完成事实。 * I槽不等待ACK;Manager继续保留发送记录,NORMAL原文可能仍在Queue或已被覆盖。 * TESTFR_CON没有Link完成接口;本接口本身不分派Link完成或解释ACK。 */ IEC104_SendBufferResult_t IEC104_SendBuffer_Commit_Send_Completion( IEC104_SendBuffer_t *buffer, IEC104_SendFrameId_t frame_id) { IEC104_SendBufferFrameState_t *state; if (!Buffer_State_Valid(buffer) || (buffer->has_i_frame == 0U && buffer->control_count == 0U)) { return IEC104_SEND_BUFFER_ERROR; } state = buffer->has_i_frame != 0U ? &buffer->i_frame_state : &buffer->control_frames[buffer->control_head_index].state; if (!Frame_Id_Matches(buffer, state, frame_id) || state->written_bytes != state->apdu_length) { return IEC104_SEND_BUFFER_ERROR; } if (buffer->has_i_frame != 0U) { buffer->has_i_frame = 0U; } else { /* 先验证即将成为队首的槽,再整体推进,避免错误返回时丢失当前帧引用。 */ if (buffer->control_count > 1U && !Frame_State_Valid(buffer, &buffer->control_frames[Control_Index(buffer, 1U)].state, 0)) { return IEC104_SEND_BUFFER_ERROR; } buffer->control_head_index = Control_Index(buffer, 1U); buffer->control_count--; } return IEC104_SEND_BUFFER_OK; } /** * 功能:判断当前发送帧是否达到本地排队及写入总期限。 * 输入参数:buffer/connection_generation——有效缓冲及匹配代次; * now_ms——当前单调毫秒;timeout_ms——非零期限。 * 输出参数:无,不修改进度或关闭连接。 * 返回值:1到期,0无当前帧或未到期,-1参数/状态非法或当前时刻早于起点。 * 说明:从原queued_at_ms作差,等于期限即到期;部分写入和重复查询不续期。 * 尚未提交到本缓冲区的ASDU排队时间不在此计算;I帧等待ACK的t1由Manager检查。 * 已整帧写完但尚未提交完成释放的帧仍在检查范围内。 */ int IEC104_SendBuffer_Head_Send_Expired( const IEC104_SendBuffer_t *buffer, uint64_t connection_generation, uint64_t now_ms, uint32_t timeout_ms) { IEC104_SendBufferHeadInfo_t head; IEC104_SendBufferResult_t result; if (buffer == NULL || connection_generation != buffer->connection_generation || timeout_ms == 0U) { return -1; } result = IEC104_SendBuffer_Peek_Send_Head(buffer, &head); if (result == IEC104_SEND_BUFFER_EMPTY) { return 0; } if (result != IEC104_SEND_BUFFER_OK || now_ms < head.queued_at_ms) { return -1; } return now_ms - head.queued_at_ms >= timeout_ms; } /** * 功能:清除发送缓冲控制结构和内置当前I帧,解除对控制数组的引用。 * 输入参数:buffer——待终止对象,允许NULL。 * 输出参数:buffer——非NULL时清零;调用方控制数组不清零、不释放。 * 返回值:无。 * 说明:不关闭socket、不取消异步I/O,调用方必须先结束旧I/O及报文字节借用。 */ void IEC104_SendBuffer_Abort(IEC104_SendBuffer_t *buffer) { if (buffer != NULL) { memset(buffer, 0, sizeof(*buffer)); } }