/* 信息对象与ASDU的内部协作,不作为业务API。 * ASDU使用类型描述计算布局;具体对象通过只读函数表编码;解码按Type ID另行分派。 * 字节辅助函数不读写连接状态,也不依赖结构体对齐或主机字节序。 */ #ifndef IEC60870_INFORMATION_OBJECTS_INTERNAL_H #define IEC60870_INFORMATION_OBJECTS_INTERNAL_H #include "IEC60870_InformationObjects.h" /* 单个文件段LOS最多255字节,另有4字节文件段头和最多3字节IOA。 * 即使对象可表示更长载荷,ASDU容器仍按配置的总长上限返回FULL。 */ #define IEC60870_OBJECT_MAX_PAYLOAD 259U #define IEC60870_OBJECT_MAX_ENCODED (IEC60870_OBJECT_MAX_PAYLOAD + 3U) /* 每个类型独立的只读编码函数表;解码仍由Type ID选择具体类型的解析函数。 * encode仅写临时载荷,公共入口验证后原子提交;变长类型提供两个长度回调。 */ typedef struct IEC60870_InformationObjectVTable { uint8_t type_id; /* 线上类型标识;同时用于核对对象基类身份。 */ uint16_t payload_size; /* 固定类型的精确长度;变长类型的最低长度,不含IOA。 */ uint8_t single_object; /* 1表示仅允许一个对象;SQ约束由supports_sequence独立决定。 */ size_t object_size; /* 具体C结构的sizeof,用于输出存储检查,与线上长度无关。 */ int (*encode)(const IEC60870_InformationObject_t *object, uint8_t *payload); /* 写临时载荷,1成功/0字段非法。 */ uint8_t supports_sequence; /* 1允许SQ=1;参数类型允许多个对象但仅支持SQ=0。 */ uint16_t (*get_payload_size)(const IEC60870_InformationObject_t *object); /* NULL为固定长度。 */ uint16_t (*get_payload_size_from_buffer)(const uint8_t *payload, uint16_t length); /* 变长载荷精确长度;0非法。 */ } IEC60870_InformationObjectVTable; /* 未支持的Type ID返回NULL;地址和长度校验由使用方在读报文前完成。 */ const IEC60870_InformationObjectVTable *IEC60870_InformationObject_Get_Descriptor(uint8_t type_id); /* 按Type ID调用具体Get_From_Buffer;object==NULL时仅验证,不输出。 * object_size是调用方实际提供的容量,必须至少为所选具体类型的sizeof; * 非空object必须对应该具体结构,尺寸足够不能替代类型匹配。失败不修改输出。 */ IEC60870_AsduResult_t IEC60870_InformationObject_Decode(uint8_t type_id, uint32_t ioa, const uint8_t *payload, uint16_t length, void *object, size_t object_size); /** * 功能:从低字节在前的字节序列读取无符号值。 * 输入参数:data——至少length字节的有效输入,调用方先完成边界检查。 * length——1..4,用于1/2/3字节地址或2/4字节数值。 * 输出参数:无。 * 返回值:按位拼接的uint32_t,未使用高位为0。 * 说明:逐字节读,允许未对齐输入;不检查指针或范围。 */ static inline uint32_t IEC60870_Object_Read_LE(const uint8_t *data, unsigned int length) { uint32_t value = 0; unsigned int i; for (i = 0; i < length; ++i) { value |= (uint32_t)data[i] << (8U * i); } return value; } /** * 功能:将数值低length字节按低字节在前写入报文。 * 输入参数:value——待写数值;需要保留的高位是否越界由调用方预先检查。 * length——1..4;只写指定字节数,不写尾部填充。 * 输出参数:data——至少length字节的可写区域。 * 返回值:无。 * 说明:逐字节写,不要求对齐;IOA必须先按所选宽度校验,防止被静默截断。 */ static inline void IEC60870_Object_Write_LE(uint8_t *data, uint32_t value, unsigned int length) { unsigned int i; for (i = 0; i < length; ++i) { data[i] = (uint8_t)(value >> (8U * i)); } } /** * 功能:计算所选IOA字段宽度能够表示的最大地址。 * 输入参数:parameters——非空且已通过AppLayerParameters_Valid校验,sizeOfIOA为1..3。 * 输出参数:无。 * 返回值:分别为0xFF、0xFFFF或0xFFFFFF。 * 说明:位移最多24位;本函数不检查参数,非法宽度不得传入。 */ static inline uint32_t IEC60870_Object_Max_IOA(const IEC60870_AppLayerParameters_t *parameters) { return (UINT32_C(1) << (8U * (unsigned int)parameters->sizeOfIOA)) - 1U; } #endif /* IEC60870_INFORMATION_OBJECTS_INTERNAL_H */