169 lines
10 KiB
C
169 lines
10 KiB
C
/* 子站发送字节存储:一帧当前I报文 + 独立紧凑S/U队列,不执行TCP I/O。 */
|
||
#ifndef IEC104_SENDBUFFER_H
|
||
#define IEC104_SENDBUFFER_H
|
||
|
||
#include "IEC104_Link_Common.h"
|
||
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
#endif
|
||
|
||
/* 本地发送描述;子站启停只发送CON,TESTFR支持ACT和CON。 */
|
||
typedef enum {
|
||
IEC104_SEND_BUFFER_FRAME_INVALID = 0,
|
||
IEC104_SEND_BUFFER_FRAME_I,
|
||
IEC104_SEND_BUFFER_FRAME_S_ACK,
|
||
IEC104_SEND_BUFFER_FRAME_STARTDT_CON,
|
||
IEC104_SEND_BUFFER_FRAME_STOPDT_CON,
|
||
IEC104_SEND_BUFFER_FRAME_TESTFR_ACT,
|
||
IEC104_SEND_BUFFER_FRAME_TESTFR_CON
|
||
} IEC104_SendBufferFrameType_t;
|
||
|
||
/* 必须精确比较枚举值;正数不统一表示存在报文或发送完成。 */
|
||
typedef enum {
|
||
IEC104_SEND_BUFFER_ERROR = -1, /* 参数、身份、状态或报文描述非法 */
|
||
IEC104_SEND_BUFFER_FULL = 0, /* S/U控制队列无空槽 */
|
||
IEC104_SEND_BUFFER_OK = 1, /* 操作成功;记账时表示尚未整帧写完 */
|
||
IEC104_SEND_BUFFER_EMPTY = 2, /* 没有已提交帧;未Commit的预留不算已提交帧 */
|
||
IEC104_SEND_BUFFER_BUSY = 3, /* 已有预留,或I帧不能取得当前发送位置 */
|
||
IEC104_SEND_BUFFER_FRAME_WRITTEN = 4 /* 字节已全部写完,尚需适用的链路完成处理及缓冲释放 */
|
||
} IEC104_SendBufferResult_t;
|
||
|
||
typedef struct {
|
||
uint64_t connection_generation; /* 非零且跨连接不复用 */
|
||
uint64_t frame_serial; /* 每次预留分配;取消后也不复用 */
|
||
} IEC104_SendFrameId_t;
|
||
|
||
/* apdu只在本次预留有效时可写,Commit/Cancel后不可继续修改。 */
|
||
typedef struct {
|
||
IEC104_SendFrameId_t frame_id; /* 预留身份,Commit/Cancel必须原样传回 */
|
||
uint8_t *apdu; /* Link直接构帧的可写地址 */
|
||
uint16_t apdu_capacity; /* I槽255字节,S/U槽6字节 */
|
||
} IEC104_SendBufferReservation_t;
|
||
|
||
/* 数值可以复制;pending_data借用队首存储,Commit_Send_Completion/Abort/Init后失效。 */
|
||
typedef struct {
|
||
IEC104_SendFrameId_t frame_id; /* 当前发送身份,用于每次增量及完成匹配 */
|
||
const uint8_t *pending_data; /* APDU起点+written_bytes,供实际I/O借用 */
|
||
uint16_t remaining_bytes; /* 本次最多可写的剩余字节数 */
|
||
uint16_t apdu_length; /* 完整报文长度,不随部分写入改变 */
|
||
uint16_t written_bytes; /* 累计成功写入字节数 */
|
||
IEC104_SendBufferFrameType_t frame_type; /* 完成分派类型,不按当前Link状态推断 */
|
||
IEC104_IFrameTx_t i_frame_snapshot; /* I帧原始快照;S/U查询时为0 */
|
||
uint16_t encoded_nr; /* I/S中实际编码的N(R),U帧为0 */
|
||
uint64_t queued_at_ms; /* 本地发送期限的原始起点 */
|
||
uint64_t written_at_ms; /* 仅整帧写完时有效,允许时刻0 */
|
||
} IEC104_SendBufferHeadInfo_t;
|
||
|
||
/* 共用写入元数据;frame_type保存上面的枚举值。 */
|
||
typedef struct {
|
||
uint64_t frame_serial; /* 本次预留分配的编号,不随槽位复用而复用 */
|
||
uint64_t queued_at_ms; /* Commit时保存的原始单调时刻 */
|
||
uint64_t written_at_ms; /* 最后一段成功写入的实际时刻,允许0 */
|
||
uint16_t apdu_length; /* 完整长度;尚未提交的预留为0 */
|
||
uint16_t written_bytes; /* 累计已写长度,不可超过apdu_length */
|
||
uint16_t encoded_nr; /* Commit从APCI读取的确认快照 */
|
||
uint8_t frame_type; /* 已提交枚举类型;尚未提交为INVALID */
|
||
} IEC104_SendBufferFrameState_t;
|
||
|
||
/* 调用方仅为控制帧分配数组,每槽只有6字节APDU,不再为S/U预留255字节。 */
|
||
typedef struct {
|
||
IEC104_SendBufferFrameState_t state; /* 本槽身份、类型、写入进度及时间 */
|
||
uint8_t apdu[IEC104_APCI_LEN]; /* S/U固定6字节报文 */
|
||
} IEC104_SendBufferControlFrame_t;
|
||
|
||
typedef struct {
|
||
IEC104_SendBufferControlFrame_t *control_frames; /* 调用方提供的S/U槽数组 */
|
||
uint16_t control_capacity; /* S/U槽总数,1..65535 */
|
||
uint16_t control_head_index; /* 控制队首下标;空队列由control_count=0表示 */
|
||
uint16_t control_count; /* 已提交S/U数量,不含正在预留的槽 */
|
||
IEC104_SendBufferFrameState_t i_frame_state; /* 唯一当前I槽的元数据 */
|
||
IEC104_IFrameTx_t i_frame_snapshot; /* 原Prepare快照,交回Link.Complete使用 */
|
||
uint8_t i_apdu[IEC104_MAX_APDU_LEN]; /* 唯一当前I帧,Commit_Send_Completion成功后可复用 */
|
||
uint8_t has_i_frame; /* 0/1:I槽已提交,包括尚未写出字节的当前帧 */
|
||
uint8_t has_reservation; /* 0/1:当前是否存在尚未Commit/Cancel的预留 */
|
||
uint8_t reservation_is_i_frame; /* 0/1:预留是否为I槽;无预留时必须为0 */
|
||
uint8_t is_initialized; /* 1表示Init成功,不表示TCP存活 */
|
||
uint64_t connection_generation; /* 本次连接的非零、不复用代次 */
|
||
uint64_t next_frame_serial; /* 下一个预留身份,耗尽时报错而不回绕 */
|
||
} IEC104_SendBuffer_t;
|
||
|
||
/* 不复制协议序号边界;Link管序号,AsduQueue保留原文,Manager保存确认记录。 */
|
||
typedef struct {
|
||
uint64_t connection_generation; /* 查询时的绑定代次 */
|
||
uint16_t control_count; /* 已提交控制帧数 */
|
||
uint16_t i_frame_ns; /* 仅has_i_frame=1时有效 */
|
||
uint8_t has_i_frame; /* 当前是否有已提交I帧 */
|
||
uint8_t has_reservation; /* 当前是否还有未结束的预留 */
|
||
uint8_t is_idle; /* 当前无I帧、S/U或预留;完成发送后也可重新空闲 */
|
||
uint8_t control_has_room; /* 无预留且控制槽未满;只表示容量,不保证编号可分配 */
|
||
} IEC104_SendBufferStatus_t;
|
||
|
||
/*
|
||
* 初始化新连接;control_capacity>=1,generation非零且跨连接不复用。
|
||
* control_frames由调用方持有;无malloc、无锁,全部操作串行,存储/参数不得重叠。
|
||
* 控制帧有独立容量,不再设置control_reserve_count或APDU节点池。
|
||
*/
|
||
extern IEC104_SendBufferResult_t IEC104_SendBuffer_Init(
|
||
IEC104_SendBuffer_t *buffer, IEC104_SendBufferControlFrame_t *control_frames,
|
||
uint16_t control_capacity, uint64_t connection_generation);
|
||
|
||
/* 查询只返回数值副本,OK写status;失败不改输出,不提供已完成/ACK协议边界。 */
|
||
extern IEC104_SendBufferResult_t IEC104_SendBuffer_Get_Status(
|
||
const IEC104_SendBuffer_t *buffer, IEC104_SendBufferStatus_t *status);
|
||
|
||
/*
|
||
* S/U预留队尾,容量不足FULL;I只能在无当前帧且控制队列为空时预留,否则BUSY。
|
||
* 任一时刻只有一个预留;OK写输出,非OK不改对象或输出。编号耗尽ERROR。
|
||
* 先取得存储,再调用会产生发送义务的Link构帧接口。
|
||
*/
|
||
extern IEC104_SendBufferResult_t IEC104_SendBuffer_Reserve_S_U_Frame(
|
||
IEC104_SendBuffer_t *buffer, IEC104_SendBufferReservation_t *reservation);
|
||
extern IEC104_SendBufferResult_t IEC104_SendBuffer_Reserve_I_Frame(
|
||
IEC104_SendBuffer_t *buffer, IEC104_SendBufferReservation_t *reservation);
|
||
|
||
/*
|
||
* I提交为唯一当前帧,S/U追加到独立控制FIFO;校验APCI、身份和原始快照。
|
||
* 已准备的当前I帧(包括EAGAIN后尚未写出字节)先完成,再发后来的控制帧。
|
||
* 禁止交错写两个APDU,禁止撤回已编号的帧;Prepare后Commit失败须结束连接。
|
||
* Commit成功后借用的构帧指针不可再写;queued_at_ms是原始本地发送期限起点。
|
||
*/
|
||
extern 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);
|
||
|
||
/* 核对唯一预留身份后归还存储;仅限Link尚未构帧成功时使用,不回滚协议状态。 */
|
||
extern IEC104_SendBufferResult_t IEC104_SendBuffer_Cancel_Reservation(
|
||
IEC104_SendBuffer_t *buffer, IEC104_SendFrameId_t frame_id);
|
||
|
||
/* 查看已提交帧,不挑选业务;已写完但未提交完成时仍返回OK且remaining_bytes为0。上层入口为Manager.Prepare_Next_Send。 */
|
||
extern IEC104_SendBufferResult_t IEC104_SendBuffer_Peek_Send_Head(
|
||
const IEC104_SendBuffer_t *buffer, IEC104_SendBufferHeadInfo_t *head_info);
|
||
|
||
/*
|
||
* 匹配当前帧,记录实际成功写入的增量;0不推进,部分写入OK,整帧FRAME_WRITTEN。
|
||
* 最后一次时间不得早于入队时刻;已写完不能重报,输出缓冲借用须在完成前结束。
|
||
* 同帧最多一个未回报写操作;不执行I/O、不调用Link.Complete。
|
||
*/
|
||
extern 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);
|
||
|
||
/* 对应Link.Complete及I帧本地完成记录提交后调用;TESTFR_CON无Link.Complete。成功释放当前槽,不等待ACK。 */
|
||
extern IEC104_SendBufferResult_t IEC104_SendBuffer_Commit_Send_Completion(
|
||
IEC104_SendBuffer_t *buffer, IEC104_SendFrameId_t frame_id);
|
||
|
||
/* 1到期、0无帧/未到期、-1非法;部分写入不续期,已写完未释放的帧也参与检查。I帧ACK的t1由Manager查询。 */
|
||
extern int IEC104_SendBuffer_Head_Send_Expired(
|
||
const IEC104_SendBuffer_t *buffer, uint64_t connection_generation,
|
||
uint64_t now_ms, uint32_t timeout_ms);
|
||
|
||
/* 旧I/O结束后清除引用;不关闭TCP,不释放调用方数组。 */
|
||
extern void IEC104_SendBuffer_Abort(IEC104_SendBuffer_t *buffer);
|
||
|
||
#ifdef __cplusplus
|
||
}
|
||
#endif
|
||
#endif /* IEC104_SENDBUFFER_H */
|