/* * 101/104共用ASDU容器。公共头、VSQ/SQ、容量、IOA布局由本层维护, * 数值、品质、命令限定词和时标的编解码由具体信息对象完成。 * 参数与字节缓冲区均由调用方提供;无动态内存或可变全局状态。 * * 发送:具体类型Create -> Asdu_Create -> Add_InformationObject(逐个)-> Get_Data。 * 接收:Asdu_Parse -> 按info.type_id选择具体结构 -> Get_Element。 * Type ID决定载荷格式,COT表示传送原因,CA是站公共地址,IOA是点/对象地址; * VSQ低7位是对象数,bit7是SQ。SQ=1只保存首IOA,随后各对象地址依次加1。 */ #ifndef IEC60870_ASDU_H #define IEC60870_ASDU_H #include "IEC60870_Codec.h" #include "IEC60870_InformationObjects.h" #ifdef __cplusplus extern "C" { #endif /* COT首字节的低6位,不含独立的P/N和TEST标志;不是完整的COT适用规则表。 */ typedef enum { IEC60870_ASDU_COT_PERIODIC = 1, /* 周期/循环上报。 */ IEC60870_ASDU_COT_BACKGROUND = 2, /* 背景扫描。 */ IEC60870_ASDU_COT_SPONTANEOUS = 3, /* 突发上报。 */ IEC60870_ASDU_COT_INITIALIZED = 4, /* 初始化结束。 */ IEC60870_ASDU_COT_REQUEST = 5, /* 请求或被请求的数据。 */ IEC60870_ASDU_COT_ACTIVATION = 6, /* 激活请求。 */ IEC60870_ASDU_COT_ACTIVATION_CON = 7, /* 激活确认,P/N另行指定。 */ IEC60870_ASDU_COT_DEACTIVATION = 8, /* 停止激活请求。 */ IEC60870_ASDU_COT_DEACTIVATION_CON = 9, /* 停止激活确认。 */ IEC60870_ASDU_COT_ACTIVATION_TERMINATION = 10, /* 激活终止。 */ IEC60870_ASDU_COT_RETURN_INFO_REMOTE = 11, /* 远方命令引起的返回信息。 */ IEC60870_ASDU_COT_RETURN_INFO_LOCAL = 12, /* 当地命令引起的返回信息。 */ IEC60870_ASDU_COT_FILE_TRANSFER = 13, /* 文件传输。 */ IEC60870_ASDU_COT_INTERROGATED_BY_STATION = 20, /* 响应站总召唤的数据。 */ IEC60870_ASDU_COT_COUNTER_INTERROGATION = 37, /* 响应电能脉冲总召唤的数据。 */ IEC60870_ASDU_COT_COUNTER_GROUP_1 = 38, IEC60870_ASDU_COT_COUNTER_GROUP_2 = 39, IEC60870_ASDU_COT_COUNTER_GROUP_3 = 40, IEC60870_ASDU_COT_COUNTER_GROUP_4 = 41, IEC60870_ASDU_COT_UNKNOWN_TYPE = 44, /* 未知类型标识。 */ IEC60870_ASDU_COT_UNKNOWN_CAUSE = 45, /* 未知传送原因。 */ IEC60870_ASDU_COT_UNKNOWN_COMMON_ADDRESS = 46, /* 未知公共地址。 */ IEC60870_ASDU_COT_UNKNOWN_IOA = 47 /* 未知信息对象地址。 */ } IEC60870_AsduCause_t; /* 创建ASDU时仅提供公共头;Type ID由首个成功添加的对象决定,VSQ由容器维护。 * 字段赋值前不得截窄越界整数。COT/CA/IOA的业务适用性由应用层检查。 */ typedef struct { uint8_t cause_of_transmission; /* COT低6位,0..63,不含P/N及TEST。 */ uint8_t is_negative_confirm; /* P/N:0肯定、1否定。 */ uint8_t is_test; /* TEST:0/1。 */ uint8_t originator_address; /* 显式OA;COT为1字节时忽略,解析输出0。 */ uint16_t common_address; /* 1字节CA拒绝超过255的值,不截断。 */ } IEC60870_AsduHeader_t; /* 调用方保存的容器;控制字段由Create/Create_From_Buffer初始化,不得手工修改。 * 字节区通过Add或字段设置接口修改;parameters在使用期间必须有效且不变。 * Create_From_Buffer借用可写原文,只允许设置公共头,不允许继续追加对象。 * 同一实例由调用方串行使用;对象不分配或释放其借用的缓冲区。 */ typedef struct { uint8_t *buffer; /* ASDU原文,不含APCI。 */ uint16_t capacity; /* min(调用方容量, maxSizeOfASDU)。 */ uint16_t length; /* 当前字节数,空容器仅有公共头。 */ uint8_t type_id; /* 空容器为0,首个对象成功加入后固定。 */ uint8_t object_count; /* 按类型限制为0..127或0..1;借用时记录原VSQ数量。 */ uint8_t is_sequence; /* SQ:0逐对象地址,1连续地址。 */ uint8_t is_from_buffer;/* 1为借用收到的原文;0为Create创建的构造容器。 */ uint32_t first_ioa; /* 构造时供SQ连续性校验;借用原文时为0且不使用。 */ const IEC60870_AppLayerParameters_t *parameters; /* 借用参数,不复制。 */ } IEC60870_Asdu_t; /* 结果须显式比较IEC60870_ASDU_OK;FULL=0,ERROR/UNSUPPORTED为负值。 * 所有失败保留调用方输出。结构体、输出参数和字节区不得互相重叠; * 唯一例外是Build_Response允许输入/输出字节区重叠。 */ /* 创建空容器并固定参数、SQ和公共头;参数必须非空,头长=2+COT宽度+CA宽度。 * 有效容量不足头长返回FULL;SQ=1只适用于监视信息和文件目录。 * 初始Type ID为0,首个对象失败不会改变类型;空容器不能Get_Data。 */ IEC60870_AsduResult_t IEC60870_Asdu_Create(IEC60870_Asdu_t *self, const IEC60870_AppLayerParameters_t *parameters, uint8_t *buffer, uint16_t capacity, const IEC60870_AsduHeader_t *header, int is_sequence); /** * 功能:在调用方存储中建立可修改的ASDU对象,直接借用已有报文字节。 * 输入参数:parameters——对应原文布局的非空合法参数,使用期间有效且不变。 * buffer——非空可写ASDU原文,不含APCI;length——原文完整长度。 * 输出参数:self——成功时写入容器,失败保持原值;buffer不变。 * 返回值:OK为成功,ERROR为参数或最低长度非法。 * 说明:只检查公共头和已知类型最低长度,允许未知类型回显错误应答。 * 业务执行前仍应调用Parse进行完整校验;不复制、不分配、不追加对象。 * 字段设置会修改buffer;必须保证buffer和parameters在使用期间有效。 */ IEC60870_AsduResult_t IEC60870_Asdu_Create_From_Buffer(IEC60870_Asdu_t *self, const IEC60870_AppLayerParameters_t *parameters, uint8_t *buffer, uint16_t length); /** * 功能:原地设置ASDU的传送原因。 * 输入参数:self——合法构造容器或借用原文的对象;cause——0..63。 * 输出参数:self->buffer——仅更新COT低6位,保留P/N、TEST及其余字节。 * 返回值:OK为成功,ERROR为参数非法;失败不修改原文。 * 说明:不判断传送原因的业务适用性,也不发送报文。 */ IEC60870_AsduResult_t IEC60870_Asdu_Set_COT(IEC60870_Asdu_t *self, int cause); /** * 功能:原地设置ASDU的肯定或否定确认标志。 * 输入参数:self——合法ASDU对象;is_negative——0肯定、1否定。 * 输出参数:self->buffer——仅更新P/N位,保留COT、TEST及其余字节。 * 返回值:OK为成功,ERROR为参数非法;失败不修改原文。 * 说明:设置字段不代表业务已接受命令,也不提交发送。 */ IEC60870_AsduResult_t IEC60870_Asdu_Set_Negative(IEC60870_Asdu_t *self, int is_negative); /** * 功能:原地设置ASDU公共地址,适用于合法构造容器或借用可写原文的对象。 * 输入参数:self——合法ASDU对象;common_address——0..65535,须适合配置的CA宽度。 * 输出参数:self->buffer——仅更新CA字段,按小端序写入1或2字节。 * 返回值:OK为成功,ERROR为对象状态或字段宽度非法;失败不修改任何原文字节。 * 说明:偏移由COT宽度确定,保留OA、TEST、P/N及对象原文;不限制业务上的站地址范围。 * 不分配、不改变容器长度、不发送;原文修改后,旧解析视图须重新解析。 */ IEC60870_AsduResult_t IEC60870_Asdu_Set_CA(IEC60870_Asdu_t *self, uint16_t common_address); /* 通过对象函数表编码并复制到ASDU,成功后不再借用对象本身。 * 单类型约束、SQ连续地址和IOA宽度统一检查。字节容量不足、对象达到127个, * 或单对象类型已加入对象时返回FULL;类型不匹配、SQ或字段非法返回ERROR。 * FULL时由调用方提交当前非空ASDU,另起一条重试同一对象,不自动发送或拆包。 */ IEC60870_AsduResult_t IEC60870_Asdu_Add_InformationObject(IEC60870_Asdu_t *self, const IEC60870_InformationObject_t *object); /* 获取构造容器或Create_From_Buffer对象的借用原文及当前长度。 * Create创建的空容器不可导出;借用原文对象沿用公共头最低长度检查。 * 不分配、不复制、不发送;需完整检查对象值时先调用Parse。 * 缓冲区修改后原视图失效;提交104传输时应使用2/2/3配置及<=249的总长。 */ IEC60870_AsduResult_t IEC60870_Asdu_Get_Data(const IEC60870_Asdu_t *self, const uint8_t **asdu, uint16_t *length); /* 统一解析67种监视、命令、初始化结束、参数和文件类型。 * 验证精确长度和所有对象,文件段按LOS检查变长载荷。 * 监视与文件目录支持SQ=0/1;参数允许多个对象但要求SQ=0。 * 其余类型要求SQ=0、数量=1;未知类型返回UNSUPPORTED。 * info借用参数和输入原文,二者在取对象期间均须有效且不变。 */ IEC60870_AsduResult_t IEC60870_Asdu_Parse(const IEC60870_AppLayerParameters_t *parameters, const uint8_t *asdu, uint16_t length, IEC60870_AsduInfo_t *info); /* 按Type ID调用具体解析器,向对应类型的存储写入一个按值对象。 * 例如Type 1传入&point、sizeof(point),其中point为IEC60870_SinglePointInformation_t。 * 调用方必须依据info->type_id选择正确的结构类型;object_size至少为该结构尺寸, * 尺寸检查不能替代C语言中的类型匹配。index从0开始,输出无需预先Create。 * 对象不借用原文;公共头继续从info读取,选中对象之外不再次重复解码。 */ IEC60870_AsduResult_t IEC60870_Asdu_Get_Element(const IEC60870_AsduInfo_t *info, uint8_t index, void *object, size_t object_size); /* 复制请求,只更新COT及P/N,保留TEST、OA、CA、VSQ和对象原文。 * cause接受7/9/10/44..47,is_negative为0/1;否定激活确认传7、1。 * 仅检查公共头及最低长度,允许未知类型回显,不要求对象解析成功。 * 参数须对应请求布局。容量不足返回FULL,所有失败保留out及out_length。 * 输入/输出字节区支持原地与部分重叠;不执行命令或自动发送。 */ IEC60870_AsduResult_t IEC60870_Asdu_Build_Response(const IEC60870_AppLayerParameters_t *parameters, const uint8_t *request, uint16_t length, int cause, int is_negative, uint8_t *out, uint16_t capacity, uint16_t *out_length); #ifdef __cplusplus } #endif #endif /* IEC60870_ASDU_H */