/* * 信息对象的具体实现。每个类型分别定义值编解码、只读编码函数表及公开构造/解析入口。 * 带CP24/CP56的普通监视和控制对象复用基础值处理;保护事件另处理CP16和QDP。 * 公共入口先在局部对象/字节数组中完成验证,成功后才写回调用方存储。 */ #include "IEC60870_InformationObjects_Internal.h" #include "IEC60870_Codec.h" #include #include #include _Static_assert(CHAR_BIT == 8, "IEC60870 requires 8-bit bytes"); _Static_assert(sizeof(float) == 4 && FLT_RADIX == 2 && FLT_MANT_DIG == 24 && FLT_MAX_EXP == 128, "IEC60870 short float requires IEEE 754 binary32"); /** * 功能:按低字节在前读取16位补码值,用于归一化和标度值。 * 输入参数:data——至少2字节的有效输入;调用方已验证长度。 * 输出参数:无。 * 返回值:-32768..32767范围内的int16_t值。 * 说明:先扩展到int32_t再减65536恢复负值,避免越界无符号到有符号转换。 */ static int16_t Read_I16(const uint8_t *data) { uint32_t raw = IEC60870_Object_Read_LE(data, 2); return (int16_t)(raw < 0x8000U ? (int32_t)raw : (int32_t)raw - 65536); } /** * 功能:按低字节在前读取32位补码累计值。 * 输入参数:data——至少4字节的有效输入;调用方已验证长度。 * 输出参数:无。 * 返回值:完整int32_t范围内的计数值,包括INT32_MIN。 * 说明:负数通过低31位减去2^31恢复,每一步运算均在int32_t范围内。 */ static int32_t Read_I32(const uint8_t *data) { uint32_t raw = IEC60870_Object_Read_LE(data, 4); return raw <= INT32_MAX ? (int32_t)raw : (int32_t)(raw & UINT32_C(0x7FFFFFFF)) - INT32_MAX - 1; } /** * 功能:将4字节报文恢复为float位模式。 * 输入参数:data——至少4字节的有效输入,低字节在前。 * 输出参数:value——非空float对象,保存解码后的IEEE 754 binary32位模式。 * 返回值:无。 * 说明:memcpy避免未对齐访问和指针别名问题,不进行浮点运算或NaN/Inf过滤。 */ static void Read_Float(const uint8_t *data, float *value) { uint32_t bits = IEC60870_Object_Read_LE(data, 4); memcpy(value, &bits, sizeof(bits)); } /** * 功能:将float位模式编码为低字节在前的4字节载荷。 * 输入参数:value——非空float指针,按位复制,不作浮点到整数的数值转换。 * 输出参数:data——至少4字节的可写空间。 * 返回值:无。 * 说明:先用memcpy取得uint32_t位表示,再按协议字节序写入。 */ static void Write_Float(uint8_t *data, const float *value) { uint32_t bits; memcpy(&bits, value, sizeof(bits)); IEC60870_Object_Write_LE(data, bits, 4); } /** * 功能:初始化具体对象中的公共基类身份。 * 输入参数:ioa——已验证为0..0xFFFFFF的信息对象地址。 * vtable——与具体结构匹配的非空静态只读函数表。 * 输出参数:object——非空公共基类首成员;此函数只填写地址、类型和函数表。 * 返回值:无。 * 说明:对象仅借用永久有效的函数表;不分配内存,不保留请求或连接状态。 */ static void Initialize_Object(IEC60870_InformationObject_t *object, uint32_t ioa, const IEC60870_InformationObjectVTable *vtable) { object->ioa = ioa; object->type_id = vtable->type_id; object->virtual_function_table = vtable; } /* ---- Type 1:单点信息 ---- */ /** * 功能:编码Type 1(单点信息)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:SIQ(1):bit0为单点状态,bit1..3为保留位,高4位为品质。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SinglePointInformation_Encode_Value(const IEC60870_SinglePointInformation_t *self, uint8_t *payload) { /* SIQ/DIQ仅允许高4位品质,点状态分别占1/2位。 */ if (self->value > 1U || (self->quality & 0x0FU) != 0U) { return 0; } payload[0] = (uint8_t)(self->value | self->quality); return 1; } /** * 功能:解码Type 1(单点信息)的值字段。 * 输入参数:payload——已确认有1字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:SIQ(1):bit0为单点状态,bit1..3为保留位,高4位为品质。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SinglePointInformation_Decode_Value(const uint8_t *payload, IEC60870_SinglePointInformation_t *self) { if ((payload[0] & 0x0EU) != 0U) { return 0; } self->value = payload[0] & 1U; self->quality = payload[0] & 0xF0U; return 1; } /** * 功能:Type 1编码函数表的适配入口,将公共基类还原为SinglePointInformation对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少1字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SinglePointInformation_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SinglePointInformation_Encode_Value((const IEC60870_SinglePointInformation_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SinglePointInformation_VTable = { 1, 1, 0, sizeof(IEC60870_SinglePointInformation_t), SinglePointInformation_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 1(单点信息)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——单点状态,0或1。 * quality——品质仅允许BL/SB/NT/IV(0xF0范围内的位),不得夹带状态或保留位。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_SinglePointInformation_Create(IEC60870_SinglePointInformation_t *self, uint32_t ioa, int value, int quality) { IEC60870_SinglePointInformation_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || value < 0 || value > 1 || quality < 0 || quality > 255) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = (uint8_t)value; result.quality = (uint8_t)quality; Initialize_Object(&result.object, ioa, &SinglePointInformation_VTable); if (!SinglePointInformation_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 1(单点信息)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的1字节载荷,必须非空。 * length——必须恰好为1;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SinglePointInformation_Get_From_Buffer(IEC60870_SinglePointInformation_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SinglePointInformation_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 1U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SinglePointInformation_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &SinglePointInformation_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 3:双点信息 ---- */ /** * 功能:编码Type 3(双点信息)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:DIQ(1):低2位为双点状态,bit2..3为保留位,高4位为品质。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int DoublePointInformation_Encode_Value(const IEC60870_DoublePointInformation_t *self, uint8_t *payload) { /* SIQ/DIQ仅允许高4位品质,点状态分别占1/2位。 */ if (self->value > 3U || (self->quality & 0x0FU) != 0U) { return 0; } payload[0] = (uint8_t)(self->value | self->quality); return 1; } /** * 功能:解码Type 3(双点信息)的值字段。 * 输入参数:payload——已确认有1字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:DIQ(1):低2位为双点状态,bit2..3为保留位,高4位为品质。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int DoublePointInformation_Decode_Value(const uint8_t *payload, IEC60870_DoublePointInformation_t *self) { if ((payload[0] & 0x0CU) != 0U) { return 0; } self->value = payload[0] & 3U; self->quality = payload[0] & 0xF0U; return 1; } /** * 功能:Type 3编码函数表的适配入口,将公共基类还原为DoublePointInformation对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少1字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int DoublePointInformation_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return DoublePointInformation_Encode_Value((const IEC60870_DoublePointInformation_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable DoublePointInformation_VTable = { 3, 1, 0, sizeof(IEC60870_DoublePointInformation_t), DoublePointInformation_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 3(双点信息)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——双点状态,0..3,包含中间状态和不确定状态。 * quality——品质仅允许BL/SB/NT/IV(0xF0范围内的位),不得夹带状态或保留位。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_DoublePointInformation_Create(IEC60870_DoublePointInformation_t *self, uint32_t ioa, int value, int quality) { IEC60870_DoublePointInformation_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || value < 0 || value > 3 || quality < 0 || quality > 255) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = (uint8_t)value; result.quality = (uint8_t)quality; Initialize_Object(&result.object, ioa, &DoublePointInformation_VTable); if (!DoublePointInformation_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 3(双点信息)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的1字节载荷,必须非空。 * length——必须恰好为1;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_DoublePointInformation_Get_From_Buffer(IEC60870_DoublePointInformation_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_DoublePointInformation_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 1U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!DoublePointInformation_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &DoublePointInformation_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 9:归一化测量值(原始16位定点值) ---- */ /** * 功能:编码Type 9(归一化测量值(原始16位定点值))的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少3字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:NVA(2)+QDS(1):有符号16位归一化原始定点值,随后为品质。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int MeasuredValueNormalized_Encode_Value(const IEC60870_MeasuredValueNormalized_t *self, uint8_t *payload) { /* QDS的bit1..3为保留位;bit0为溢出,高4位为品质。 */ if ((self->quality & 0x0EU) != 0U) { return 0; } IEC60870_Object_Write_LE(payload, (uint16_t)self->value, 2); payload[2] = self->quality; return 1; } /** * 功能:解码Type 9(归一化测量值(原始16位定点值))的值字段。 * 输入参数:payload——已确认有3字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:NVA(2)+QDS(1):有符号16位归一化原始定点值,随后为品质。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int MeasuredValueNormalized_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueNormalized_t *self) { if ((payload[2] & 0x0EU) != 0U) { return 0; } self->value = Read_I16(payload); self->quality = payload[2]; return 1; } /** * 功能:Type 9编码函数表的适配入口,将公共基类还原为MeasuredValueNormalized对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少3字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int MeasuredValueNormalized_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueNormalized_Encode_Value((const IEC60870_MeasuredValueNormalized_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable MeasuredValueNormalized_VTable = { 9, 3, 0, sizeof(IEC60870_MeasuredValueNormalized_t), MeasuredValueNormalized_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 9(归一化测量值(原始16位定点值))。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——归一化原始int16_t定点值,实际数值为value/32768.0。 * quality——QDS品质仅允许OV/BL/SB/NT/IV(0xF1范围内的位),bit1..3必须为0。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueNormalized_Create(IEC60870_MeasuredValueNormalized_t *self, uint32_t ioa, int16_t value, int quality) { IEC60870_MeasuredValueNormalized_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || quality < 0 || quality > 255) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.quality = (uint8_t)quality; Initialize_Object(&result.object, ioa, &MeasuredValueNormalized_VTable); if (!MeasuredValueNormalized_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 9(归一化测量值(原始16位定点值))。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的3字节载荷,必须非空。 * length——必须恰好为3;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueNormalized_Get_From_Buffer(IEC60870_MeasuredValueNormalized_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueNormalized_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 3U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueNormalized_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &MeasuredValueNormalized_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 11:标度化测量值 ---- */ /** * 功能:编码Type 11(标度化测量值)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少3字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:SVA(2)+QDS(1):有符号16位标度值,随后为品质。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int MeasuredValueScaled_Encode_Value(const IEC60870_MeasuredValueScaled_t *self, uint8_t *payload) { /* QDS的bit1..3为保留位;bit0为溢出,高4位为品质。 */ if ((self->quality & 0x0EU) != 0U) { return 0; } IEC60870_Object_Write_LE(payload, (uint16_t)self->value, 2); payload[2] = self->quality; return 1; } /** * 功能:解码Type 11(标度化测量值)的值字段。 * 输入参数:payload——已确认有3字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:SVA(2)+QDS(1):有符号16位标度值,随后为品质。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int MeasuredValueScaled_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueScaled_t *self) { if ((payload[2] & 0x0EU) != 0U) { return 0; } self->value = Read_I16(payload); self->quality = payload[2]; return 1; } /** * 功能:Type 11编码函数表的适配入口,将公共基类还原为MeasuredValueScaled对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少3字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int MeasuredValueScaled_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueScaled_Encode_Value((const IEC60870_MeasuredValueScaled_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable MeasuredValueScaled_VTable = { 11, 3, 0, sizeof(IEC60870_MeasuredValueScaled_t), MeasuredValueScaled_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 11(标度化测量值)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——有符号16位标度值,范围-32768..32767。 * quality——QDS品质仅允许OV/BL/SB/NT/IV(0xF1范围内的位),bit1..3必须为0。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueScaled_Create(IEC60870_MeasuredValueScaled_t *self, uint32_t ioa, int16_t value, int quality) { IEC60870_MeasuredValueScaled_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || quality < 0 || quality > 255) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.quality = (uint8_t)quality; Initialize_Object(&result.object, ioa, &MeasuredValueScaled_VTable); if (!MeasuredValueScaled_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 11(标度化测量值)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的3字节载荷,必须非空。 * length——必须恰好为3;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueScaled_Get_From_Buffer(IEC60870_MeasuredValueScaled_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueScaled_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 3U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueScaled_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &MeasuredValueScaled_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 13:短浮点测量值 ---- */ /** * 功能:编码Type 13(短浮点测量值)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少5字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:短浮点(4)+QDS(1):IEEE 754 binary32位模式,随后为品质。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int MeasuredValueShort_Encode_Value(const IEC60870_MeasuredValueShort_t *self, uint8_t *payload) { /* QDS的bit1..3为保留位;bit0为溢出,高4位为品质。 */ if ((self->quality & 0x0EU) != 0U) { return 0; } Write_Float(payload, &self->value); payload[4] = self->quality; return 1; } /** * 功能:解码Type 13(短浮点测量值)的值字段。 * 输入参数:payload——已确认有5字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:短浮点(4)+QDS(1):IEEE 754 binary32位模式,随后为品质。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int MeasuredValueShort_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueShort_t *self) { if ((payload[4] & 0x0EU) != 0U) { return 0; } Read_Float(payload, &self->value); self->quality = payload[4]; return 1; } /** * 功能:Type 13编码函数表的适配入口,将公共基类还原为MeasuredValueShort对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少5字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int MeasuredValueShort_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueShort_Encode_Value((const IEC60870_MeasuredValueShort_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable MeasuredValueShort_VTable = { 13, 5, 0, sizeof(IEC60870_MeasuredValueShort_t), MeasuredValueShort_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 13(短浮点测量值)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——短浮点值,采用IEEE 754 binary32,不进行NaN/Inf业务过滤。 * quality——QDS品质仅允许OV/BL/SB/NT/IV(0xF1范围内的位),bit1..3必须为0。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueShort_Create(IEC60870_MeasuredValueShort_t *self, uint32_t ioa, float value, int quality) { IEC60870_MeasuredValueShort_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || quality < 0 || quality > 255) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.quality = (uint8_t)quality; Initialize_Object(&result.object, ioa, &MeasuredValueShort_VTable); if (!MeasuredValueShort_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 13(短浮点测量值)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的5字节载荷,必须非空。 * length——必须恰好为5;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueShort_Get_From_Buffer(IEC60870_MeasuredValueShort_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueShort_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 5U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueShort_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &MeasuredValueShort_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 15:累计量 ---- */ /** * 功能:编码Type 15(累计量)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少5字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:BCR(5):前4字节为有符号累计值,末字节为5位序号及CY/CA/IV。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int IntegratedTotals_Encode_Value(const IEC60870_IntegratedTotals_t *self, uint8_t *payload) { const IEC60870_BinaryCounterReading_t *value = &self->value; if (value->sequence_number > 31U || value->has_carry > 1U || value->is_adjusted > 1U || value->is_invalid > 1U) { return 0; } IEC60870_Object_Write_LE(payload, (uint32_t)value->value, 4); /* BCR末字节:低5位序号、bit5进位、bit6调整、bit7无效。 */ payload[4] = (uint8_t)(value->sequence_number | (value->has_carry << 5) | (value->is_adjusted << 6) | (value->is_invalid << 7)); return 1; } /** * 功能:解码Type 15(累计量)的值字段。 * 输入参数:payload——已确认有5字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:BCR(5):前4字节为有符号累计值,末字节为5位序号及CY/CA/IV。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int IntegratedTotals_Decode_Value(const uint8_t *payload, IEC60870_IntegratedTotals_t *self) { self->value.value = Read_I32(payload); self->value.sequence_number = payload[4] & 31U; self->value.has_carry = (payload[4] >> 5) & 1U; self->value.is_adjusted = (payload[4] >> 6) & 1U; self->value.is_invalid = payload[4] >> 7; return 1; } /** * 功能:Type 15编码函数表的适配入口,将公共基类还原为IntegratedTotals对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少5字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int IntegratedTotals_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return IntegratedTotals_Encode_Value((const IEC60870_IntegratedTotals_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable IntegratedTotals_VTable = { 15, 5, 0, sizeof(IEC60870_IntegratedTotals_t), IntegratedTotals_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 15(累计量)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——非空BCR指针;计数为int32_t,序号0..31,CY/CA/IV均为0/1。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_IntegratedTotals_Create(IEC60870_IntegratedTotals_t *self, uint32_t ioa, const IEC60870_BinaryCounterReading_t *value) { IEC60870_IntegratedTotals_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || value == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = *value; Initialize_Object(&result.object, ioa, &IntegratedTotals_VTable); if (!IntegratedTotals_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 15(累计量)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的5字节载荷,必须非空。 * length——必须恰好为5;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_IntegratedTotals_Get_From_Buffer(IEC60870_IntegratedTotals_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_IntegratedTotals_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 5U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!IntegratedTotals_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &IntegratedTotals_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 30:带时标单点信息 ---- */ /** * 功能:编码Type 30(带时标单点信息)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少8字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:SIQ(1):bit0为单点状态,bit1..3为保留位,高4位为品质,之后追加CP56Time2a(7)。 * 值部分调用SinglePointInformation_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SinglePointWithCP56Time2a_Encode_Value(const IEC60870_SinglePointWithCP56Time2a_t *self, uint8_t *payload) { return SinglePointInformation_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 1, IEC60870_CP56_LEN); } /** * 功能:解码Type 30(带时标单点信息)的值字段和CP56时标。 * 输入参数:payload——已确认有8字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:SIQ(1):bit0为单点状态,bit1..3为保留位,高4位为品质,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SinglePointWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_SinglePointWithCP56Time2a_t *self) { return SinglePointInformation_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 1, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 30编码函数表的适配入口,将公共基类还原为SinglePointWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少8字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SinglePointWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SinglePointWithCP56Time2a_Encode_Value((const IEC60870_SinglePointWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SinglePointWithCP56Time2a_VTable = { 30, 8, 0, sizeof(IEC60870_SinglePointWithCP56Time2a_t), SinglePointWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 30(带时标单点信息)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——单点状态,0或1。 * quality——品质仅允许BL/SB/NT/IV(0xF0范围内的位),不得夹带状态或保留位。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_SinglePointWithCP56Time2a_Create(IEC60870_SinglePointWithCP56Time2a_t *self, uint32_t ioa, int value, int quality, const IEC60870_CP56Time2a_t *time) { IEC60870_SinglePointWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || value < 0 || value > 1 || quality < 0 || quality > 255 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = (uint8_t)value; result.base.quality = (uint8_t)quality; result.time = *time; Initialize_Object(&result.base.object, ioa, &SinglePointWithCP56Time2a_VTable); if (!SinglePointWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 30(带时标单点信息)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的8字节载荷,必须非空。 * length——必须恰好为8;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SinglePointWithCP56Time2a_Get_From_Buffer(IEC60870_SinglePointWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SinglePointWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 8U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SinglePointWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &SinglePointWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 31:带时标双点信息 ---- */ /** * 功能:编码Type 31(带时标双点信息)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少8字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:DIQ(1):低2位为双点状态,bit2..3为保留位,高4位为品质,之后追加CP56Time2a(7)。 * 值部分调用DoublePointInformation_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int DoublePointWithCP56Time2a_Encode_Value(const IEC60870_DoublePointWithCP56Time2a_t *self, uint8_t *payload) { return DoublePointInformation_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 1, IEC60870_CP56_LEN); } /** * 功能:解码Type 31(带时标双点信息)的值字段和CP56时标。 * 输入参数:payload——已确认有8字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:DIQ(1):低2位为双点状态,bit2..3为保留位,高4位为品质,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int DoublePointWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_DoublePointWithCP56Time2a_t *self) { return DoublePointInformation_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 1, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 31编码函数表的适配入口,将公共基类还原为DoublePointWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少8字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int DoublePointWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return DoublePointWithCP56Time2a_Encode_Value((const IEC60870_DoublePointWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable DoublePointWithCP56Time2a_VTable = { 31, 8, 0, sizeof(IEC60870_DoublePointWithCP56Time2a_t), DoublePointWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 31(带时标双点信息)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——双点状态,0..3,包含中间状态和不确定状态。 * quality——品质仅允许BL/SB/NT/IV(0xF0范围内的位),不得夹带状态或保留位。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_DoublePointWithCP56Time2a_Create(IEC60870_DoublePointWithCP56Time2a_t *self, uint32_t ioa, int value, int quality, const IEC60870_CP56Time2a_t *time) { IEC60870_DoublePointWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || value < 0 || value > 3 || quality < 0 || quality > 255 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = (uint8_t)value; result.base.quality = (uint8_t)quality; result.time = *time; Initialize_Object(&result.base.object, ioa, &DoublePointWithCP56Time2a_VTable); if (!DoublePointWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 31(带时标双点信息)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的8字节载荷,必须非空。 * length——必须恰好为8;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_DoublePointWithCP56Time2a_Get_From_Buffer(IEC60870_DoublePointWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_DoublePointWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 8U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!DoublePointWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &DoublePointWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 34:带时标归一化测量值 ---- */ /** * 功能:编码Type 34(带时标归一化测量值)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少10字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:NVA(2)+QDS(1):有符号16位归一化原始定点值,随后为品质,之后追加CP56Time2a(7)。 * 值部分调用MeasuredValueNormalized_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int MeasuredValueNormalizedWithCP56Time2a_Encode_Value(const IEC60870_MeasuredValueNormalizedWithCP56Time2a_t *self, uint8_t *payload) { return MeasuredValueNormalized_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 3, IEC60870_CP56_LEN); } /** * 功能:解码Type 34(带时标归一化测量值)的值字段和CP56时标。 * 输入参数:payload——已确认有10字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:NVA(2)+QDS(1):有符号16位归一化原始定点值,随后为品质,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int MeasuredValueNormalizedWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueNormalizedWithCP56Time2a_t *self) { return MeasuredValueNormalized_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 3, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 34编码函数表的适配入口,将公共基类还原为MeasuredValueNormalizedWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少10字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int MeasuredValueNormalizedWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueNormalizedWithCP56Time2a_Encode_Value((const IEC60870_MeasuredValueNormalizedWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable MeasuredValueNormalizedWithCP56Time2a_VTable = { 34, 10, 0, sizeof(IEC60870_MeasuredValueNormalizedWithCP56Time2a_t), MeasuredValueNormalizedWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 34(带时标归一化测量值)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——归一化原始int16_t定点值,实际数值为value/32768.0。 * quality——QDS品质仅允许OV/BL/SB/NT/IV(0xF1范围内的位),bit1..3必须为0。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueNormalizedWithCP56Time2a_Create(IEC60870_MeasuredValueNormalizedWithCP56Time2a_t *self, uint32_t ioa, int16_t value, int quality, const IEC60870_CP56Time2a_t *time) { IEC60870_MeasuredValueNormalizedWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || quality < 0 || quality > 255 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = value; result.base.quality = (uint8_t)quality; result.time = *time; Initialize_Object(&result.base.object, ioa, &MeasuredValueNormalizedWithCP56Time2a_VTable); if (!MeasuredValueNormalizedWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 34(带时标归一化测量值)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的10字节载荷,必须非空。 * length——必须恰好为10;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueNormalizedWithCP56Time2a_Get_From_Buffer(IEC60870_MeasuredValueNormalizedWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueNormalizedWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 10U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueNormalizedWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &MeasuredValueNormalizedWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 35:带时标标度化测量值 ---- */ /** * 功能:编码Type 35(带时标标度化测量值)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少10字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:SVA(2)+QDS(1):有符号16位标度值,随后为品质,之后追加CP56Time2a(7)。 * 值部分调用MeasuredValueScaled_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int MeasuredValueScaledWithCP56Time2a_Encode_Value(const IEC60870_MeasuredValueScaledWithCP56Time2a_t *self, uint8_t *payload) { return MeasuredValueScaled_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 3, IEC60870_CP56_LEN); } /** * 功能:解码Type 35(带时标标度化测量值)的值字段和CP56时标。 * 输入参数:payload——已确认有10字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:SVA(2)+QDS(1):有符号16位标度值,随后为品质,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int MeasuredValueScaledWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueScaledWithCP56Time2a_t *self) { return MeasuredValueScaled_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 3, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 35编码函数表的适配入口,将公共基类还原为MeasuredValueScaledWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少10字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int MeasuredValueScaledWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueScaledWithCP56Time2a_Encode_Value((const IEC60870_MeasuredValueScaledWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable MeasuredValueScaledWithCP56Time2a_VTable = { 35, 10, 0, sizeof(IEC60870_MeasuredValueScaledWithCP56Time2a_t), MeasuredValueScaledWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 35(带时标标度化测量值)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——有符号16位标度值,范围-32768..32767。 * quality——QDS品质仅允许OV/BL/SB/NT/IV(0xF1范围内的位),bit1..3必须为0。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueScaledWithCP56Time2a_Create(IEC60870_MeasuredValueScaledWithCP56Time2a_t *self, uint32_t ioa, int16_t value, int quality, const IEC60870_CP56Time2a_t *time) { IEC60870_MeasuredValueScaledWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || quality < 0 || quality > 255 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = value; result.base.quality = (uint8_t)quality; result.time = *time; Initialize_Object(&result.base.object, ioa, &MeasuredValueScaledWithCP56Time2a_VTable); if (!MeasuredValueScaledWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 35(带时标标度化测量值)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的10字节载荷,必须非空。 * length——必须恰好为10;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueScaledWithCP56Time2a_Get_From_Buffer(IEC60870_MeasuredValueScaledWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueScaledWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 10U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueScaledWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &MeasuredValueScaledWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 36:带时标短浮点测量值 ---- */ /** * 功能:编码Type 36(带时标短浮点测量值)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少12字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:短浮点(4)+QDS(1):IEEE 754 binary32位模式,随后为品质,之后追加CP56Time2a(7)。 * 值部分调用MeasuredValueShort_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int MeasuredValueShortWithCP56Time2a_Encode_Value(const IEC60870_MeasuredValueShortWithCP56Time2a_t *self, uint8_t *payload) { return MeasuredValueShort_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 5, IEC60870_CP56_LEN); } /** * 功能:解码Type 36(带时标短浮点测量值)的值字段和CP56时标。 * 输入参数:payload——已确认有12字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:短浮点(4)+QDS(1):IEEE 754 binary32位模式,随后为品质,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int MeasuredValueShortWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueShortWithCP56Time2a_t *self) { return MeasuredValueShort_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 5, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 36编码函数表的适配入口,将公共基类还原为MeasuredValueShortWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少12字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int MeasuredValueShortWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueShortWithCP56Time2a_Encode_Value((const IEC60870_MeasuredValueShortWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable MeasuredValueShortWithCP56Time2a_VTable = { 36, 12, 0, sizeof(IEC60870_MeasuredValueShortWithCP56Time2a_t), MeasuredValueShortWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 36(带时标短浮点测量值)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——短浮点值,采用IEEE 754 binary32,不进行NaN/Inf业务过滤。 * quality——QDS品质仅允许OV/BL/SB/NT/IV(0xF1范围内的位),bit1..3必须为0。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueShortWithCP56Time2a_Create(IEC60870_MeasuredValueShortWithCP56Time2a_t *self, uint32_t ioa, float value, int quality, const IEC60870_CP56Time2a_t *time) { IEC60870_MeasuredValueShortWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || quality < 0 || quality > 255 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = value; result.base.quality = (uint8_t)quality; result.time = *time; Initialize_Object(&result.base.object, ioa, &MeasuredValueShortWithCP56Time2a_VTable); if (!MeasuredValueShortWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 36(带时标短浮点测量值)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的12字节载荷,必须非空。 * length——必须恰好为12;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueShortWithCP56Time2a_Get_From_Buffer(IEC60870_MeasuredValueShortWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueShortWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 12U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueShortWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &MeasuredValueShortWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 37:带时标累计量 ---- */ /** * 功能:编码Type 37(带时标累计量)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少12字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:BCR(5):前4字节为有符号累计值,末字节为5位序号及CY/CA/IV,之后追加CP56Time2a(7)。 * 值部分调用IntegratedTotals_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int IntegratedTotalsWithCP56Time2a_Encode_Value(const IEC60870_IntegratedTotalsWithCP56Time2a_t *self, uint8_t *payload) { return IntegratedTotals_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 5, IEC60870_CP56_LEN); } /** * 功能:解码Type 37(带时标累计量)的值字段和CP56时标。 * 输入参数:payload——已确认有12字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:BCR(5):前4字节为有符号累计值,末字节为5位序号及CY/CA/IV,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int IntegratedTotalsWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_IntegratedTotalsWithCP56Time2a_t *self) { return IntegratedTotals_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 5, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 37编码函数表的适配入口,将公共基类还原为IntegratedTotalsWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少12字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int IntegratedTotalsWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return IntegratedTotalsWithCP56Time2a_Encode_Value((const IEC60870_IntegratedTotalsWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable IntegratedTotalsWithCP56Time2a_VTable = { 37, 12, 0, sizeof(IEC60870_IntegratedTotalsWithCP56Time2a_t), IntegratedTotalsWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 37(带时标累计量)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——非空BCR指针;计数为int32_t,序号0..31,CY/CA/IV均为0/1。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_IntegratedTotalsWithCP56Time2a_Create(IEC60870_IntegratedTotalsWithCP56Time2a_t *self, uint32_t ioa, const IEC60870_BinaryCounterReading_t *value, const IEC60870_CP56Time2a_t *time) { IEC60870_IntegratedTotalsWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || value == NULL || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = *value; result.time = *time; Initialize_Object(&result.base.object, ioa, &IntegratedTotalsWithCP56Time2a_VTable); if (!IntegratedTotalsWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 37(带时标累计量)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的12字节载荷,必须非空。 * length——必须恰好为12;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_IntegratedTotalsWithCP56Time2a_Get_From_Buffer(IEC60870_IntegratedTotalsWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_IntegratedTotalsWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 12U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!IntegratedTotalsWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &IntegratedTotalsWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 45:单命令 ---- */ /** * 功能:编码Type 45(单命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:SCO(1):bit0为状态,bit1保留,bit2..6为QU,bit7为S/E。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SingleCommand_Encode_Value(const IEC60870_SingleCommand_t *self, uint8_t *payload) { /* SCO的bit1必须为0;单命令状态为0/1;QU占bit2..6,S/E占bit7。 */ if (self->state > 1U || self->is_select > 1U || self->qualifier > 31U) { return 0; } payload[0] = (uint8_t)(self->state | (self->qualifier << 2) | (self->is_select << 7)); return 1; } /** * 功能:解码Type 45(单命令)的值字段。 * 输入参数:payload——已确认有1字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:SCO(1):bit0为状态,bit1保留,bit2..6为QU,bit7为S/E。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SingleCommand_Decode_Value(const uint8_t *payload, IEC60870_SingleCommand_t *self) { if ((payload[0] & 2U) != 0U) { return 0; } self->state = payload[0] & 1U; self->is_select = payload[0] >> 7; self->qualifier = (payload[0] >> 2) & 31U; return 1; } /** * 功能:Type 45编码函数表的适配入口,将公共基类还原为SingleCommand对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少1字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SingleCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SingleCommand_Encode_Value((const IEC60870_SingleCommand_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SingleCommand_VTable = { 45, 1, 1, sizeof(IEC60870_SingleCommand_t), SingleCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 45(单命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * state——单命令状态,0或1;SCO保留位由编码器保持为0。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——命令限定词QU,0..31;具体业务含义由应用判断。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_SingleCommand_Create(IEC60870_SingleCommand_t *self, uint32_t ioa, int state, int is_select, int qualifier) { IEC60870_SingleCommand_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || state < 0 || state > 1 || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 31) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.state = (uint8_t)state; result.is_select = (uint8_t)is_select; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &SingleCommand_VTable); if (!SingleCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 45(单命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的1字节载荷,必须非空。 * length——必须恰好为1;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SingleCommand_Get_From_Buffer(IEC60870_SingleCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SingleCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 1U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SingleCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &SingleCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 46:双命令 ---- */ /** * 功能:编码Type 46(双命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:DCO(1):低2位为状态(仅1/2),bit2..6为QU,bit7为S/E。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int DoubleCommand_Encode_Value(const IEC60870_DoubleCommand_t *self, uint8_t *payload) { /* DCO/RCO仅接收状态1/2;QU占bit2..6,S/E占bit7。 */ if ((self->state != 1U && self->state != 2U) || self->is_select > 1U || self->qualifier > 31U) { return 0; } payload[0] = (uint8_t)(self->state | (self->qualifier << 2) | (self->is_select << 7)); return 1; } /** * 功能:解码Type 46(双命令)的值字段。 * 输入参数:payload——已确认有1字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:DCO(1):低2位为状态(仅1/2),bit2..6为QU,bit7为S/E。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int DoubleCommand_Decode_Value(const uint8_t *payload, IEC60870_DoubleCommand_t *self) { self->state = payload[0] & 3U; if (self->state != 1U && self->state != 2U) { return 0; } self->is_select = payload[0] >> 7; self->qualifier = (payload[0] >> 2) & 31U; return 1; } /** * 功能:Type 46编码函数表的适配入口,将公共基类还原为DoubleCommand对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少1字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int DoubleCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return DoubleCommand_Encode_Value((const IEC60870_DoubleCommand_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable DoubleCommand_VTable = { 46, 1, 1, sizeof(IEC60870_DoubleCommand_t), DoubleCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 46(双命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * state——双命令/步调节状态,仅允许1或2。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——命令限定词QU,0..31;具体业务含义由应用判断。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_DoubleCommand_Create(IEC60870_DoubleCommand_t *self, uint32_t ioa, int state, int is_select, int qualifier) { IEC60870_DoubleCommand_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || state < 1 || state > 2 || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 31) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.state = (uint8_t)state; result.is_select = (uint8_t)is_select; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &DoubleCommand_VTable); if (!DoubleCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 46(双命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的1字节载荷,必须非空。 * length——必须恰好为1;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_DoubleCommand_Get_From_Buffer(IEC60870_DoubleCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_DoubleCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 1U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!DoubleCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &DoubleCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 47:步调节命令 ---- */ /** * 功能:编码Type 47(步调节命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:RCO(1):低2位为调节状态(仅1/2),bit2..6为QU,bit7为S/E。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int StepCommand_Encode_Value(const IEC60870_StepCommand_t *self, uint8_t *payload) { /* DCO/RCO仅接收状态1/2;QU占bit2..6,S/E占bit7。 */ if ((self->state != 1U && self->state != 2U) || self->is_select > 1U || self->qualifier > 31U) { return 0; } payload[0] = (uint8_t)(self->state | (self->qualifier << 2) | (self->is_select << 7)); return 1; } /** * 功能:解码Type 47(步调节命令)的值字段。 * 输入参数:payload——已确认有1字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:RCO(1):低2位为调节状态(仅1/2),bit2..6为QU,bit7为S/E。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int StepCommand_Decode_Value(const uint8_t *payload, IEC60870_StepCommand_t *self) { self->state = payload[0] & 3U; if (self->state != 1U && self->state != 2U) { return 0; } self->is_select = payload[0] >> 7; self->qualifier = (payload[0] >> 2) & 31U; return 1; } /** * 功能:Type 47编码函数表的适配入口,将公共基类还原为StepCommand对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少1字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int StepCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return StepCommand_Encode_Value((const IEC60870_StepCommand_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable StepCommand_VTable = { 47, 1, 1, sizeof(IEC60870_StepCommand_t), StepCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 47(步调节命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * state——双命令/步调节状态,仅允许1或2。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——命令限定词QU,0..31;具体业务含义由应用判断。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_StepCommand_Create(IEC60870_StepCommand_t *self, uint32_t ioa, int state, int is_select, int qualifier) { IEC60870_StepCommand_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || state < 1 || state > 2 || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 31) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.state = (uint8_t)state; result.is_select = (uint8_t)is_select; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &StepCommand_VTable); if (!StepCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 47(步调节命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的1字节载荷,必须非空。 * length——必须恰好为1;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_StepCommand_Get_From_Buffer(IEC60870_StepCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_StepCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 1U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!StepCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &StepCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 48:归一化设定值命令(原始16位定点值) ---- */ /** * 功能:编码Type 48(归一化设定值命令(原始16位定点值))的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少3字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:NVA(2)+QOS(1):归一化原始定点值;QOS低7位为QL,bit7为S/E。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SetpointCommandNormalized_Encode_Value(const IEC60870_SetpointCommandNormalized_t *self, uint8_t *payload) { if (self->is_select > 1U || self->qualifier > 127U) { return 0; } IEC60870_Object_Write_LE(payload, (uint16_t)self->value, 2); /* 数值后接QOS:低7位QL,高位S/E。 */ payload[2] = (uint8_t)(self->qualifier | (self->is_select << 7)); return 1; } /** * 功能:解码Type 48(归一化设定值命令(原始16位定点值))的值字段。 * 输入参数:payload——已确认有3字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:NVA(2)+QOS(1):归一化原始定点值;QOS低7位为QL,bit7为S/E。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SetpointCommandNormalized_Decode_Value(const uint8_t *payload, IEC60870_SetpointCommandNormalized_t *self) { self->value = Read_I16(payload); self->qualifier = payload[2] & 127U; self->is_select = payload[2] >> 7; return 1; } /** * 功能:Type 48编码函数表的适配入口,将公共基类还原为SetpointCommandNormalized对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少3字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SetpointCommandNormalized_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SetpointCommandNormalized_Encode_Value((const IEC60870_SetpointCommandNormalized_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SetpointCommandNormalized_VTable = { 48, 3, 1, sizeof(IEC60870_SetpointCommandNormalized_t), SetpointCommandNormalized_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 48(归一化设定值命令(原始16位定点值))。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——归一化原始int16_t定点值,实际数值为value/32768.0。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——设定值限定词QL,0..127;编码到QOS低7位。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandNormalized_Create(IEC60870_SetpointCommandNormalized_t *self, uint32_t ioa, int16_t value, int is_select, int qualifier) { IEC60870_SetpointCommandNormalized_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 127) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.is_select = (uint8_t)is_select; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &SetpointCommandNormalized_VTable); if (!SetpointCommandNormalized_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 48(归一化设定值命令(原始16位定点值))。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的3字节载荷,必须非空。 * length——必须恰好为3;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandNormalized_Get_From_Buffer(IEC60870_SetpointCommandNormalized_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SetpointCommandNormalized_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 3U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SetpointCommandNormalized_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &SetpointCommandNormalized_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 49:标度化设定值命令 ---- */ /** * 功能:编码Type 49(标度化设定值命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少3字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:SVA(2)+QOS(1):标度值;QOS低7位为QL,bit7为S/E。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SetpointCommandScaled_Encode_Value(const IEC60870_SetpointCommandScaled_t *self, uint8_t *payload) { if (self->is_select > 1U || self->qualifier > 127U) { return 0; } IEC60870_Object_Write_LE(payload, (uint16_t)self->value, 2); /* 数值后接QOS:低7位QL,高位S/E。 */ payload[2] = (uint8_t)(self->qualifier | (self->is_select << 7)); return 1; } /** * 功能:解码Type 49(标度化设定值命令)的值字段。 * 输入参数:payload——已确认有3字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:SVA(2)+QOS(1):标度值;QOS低7位为QL,bit7为S/E。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SetpointCommandScaled_Decode_Value(const uint8_t *payload, IEC60870_SetpointCommandScaled_t *self) { self->value = Read_I16(payload); self->qualifier = payload[2] & 127U; self->is_select = payload[2] >> 7; return 1; } /** * 功能:Type 49编码函数表的适配入口,将公共基类还原为SetpointCommandScaled对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少3字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SetpointCommandScaled_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SetpointCommandScaled_Encode_Value((const IEC60870_SetpointCommandScaled_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SetpointCommandScaled_VTable = { 49, 3, 1, sizeof(IEC60870_SetpointCommandScaled_t), SetpointCommandScaled_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 49(标度化设定值命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——有符号16位标度值,范围-32768..32767。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——设定值限定词QL,0..127;编码到QOS低7位。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandScaled_Create(IEC60870_SetpointCommandScaled_t *self, uint32_t ioa, int16_t value, int is_select, int qualifier) { IEC60870_SetpointCommandScaled_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 127) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.is_select = (uint8_t)is_select; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &SetpointCommandScaled_VTable); if (!SetpointCommandScaled_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 49(标度化设定值命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的3字节载荷,必须非空。 * length——必须恰好为3;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandScaled_Get_From_Buffer(IEC60870_SetpointCommandScaled_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SetpointCommandScaled_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 3U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SetpointCommandScaled_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &SetpointCommandScaled_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 50:短浮点设定值命令 ---- */ /** * 功能:编码Type 50(短浮点设定值命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少5字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:短浮点(4)+QOS(1):浮点位模式;QOS低7位为QL,bit7为S/E。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SetpointCommandShort_Encode_Value(const IEC60870_SetpointCommandShort_t *self, uint8_t *payload) { if (self->is_select > 1U || self->qualifier > 127U) { return 0; } Write_Float(payload, &self->value); /* 数值后接QOS:低7位QL,高位S/E。 */ payload[4] = (uint8_t)(self->qualifier | (self->is_select << 7)); return 1; } /** * 功能:解码Type 50(短浮点设定值命令)的值字段。 * 输入参数:payload——已确认有5字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:短浮点(4)+QOS(1):浮点位模式;QOS低7位为QL,bit7为S/E。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SetpointCommandShort_Decode_Value(const uint8_t *payload, IEC60870_SetpointCommandShort_t *self) { Read_Float(payload, &self->value); self->qualifier = payload[4] & 127U; self->is_select = payload[4] >> 7; return 1; } /** * 功能:Type 50编码函数表的适配入口,将公共基类还原为SetpointCommandShort对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少5字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SetpointCommandShort_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SetpointCommandShort_Encode_Value((const IEC60870_SetpointCommandShort_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SetpointCommandShort_VTable = { 50, 5, 1, sizeof(IEC60870_SetpointCommandShort_t), SetpointCommandShort_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 50(短浮点设定值命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——短浮点值,采用IEEE 754 binary32,不进行NaN/Inf业务过滤。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——设定值限定词QL,0..127;编码到QOS低7位。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandShort_Create(IEC60870_SetpointCommandShort_t *self, uint32_t ioa, float value, int is_select, int qualifier) { IEC60870_SetpointCommandShort_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 127) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.is_select = (uint8_t)is_select; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &SetpointCommandShort_VTable); if (!SetpointCommandShort_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 50(短浮点设定值命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的5字节载荷,必须非空。 * length——必须恰好为5;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandShort_Get_From_Buffer(IEC60870_SetpointCommandShort_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SetpointCommandShort_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 5U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SetpointCommandShort_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &SetpointCommandShort_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 58:带时标单命令 ---- */ /** * 功能:编码Type 58(带时标单命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少8字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:SCO(1):bit0为状态,bit1保留,bit2..6为QU,bit7为S/E,之后追加CP56Time2a(7)。 * 值部分调用SingleCommand_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SingleCommandWithCP56Time2a_Encode_Value(const IEC60870_SingleCommandWithCP56Time2a_t *self, uint8_t *payload) { return SingleCommand_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 1, IEC60870_CP56_LEN); } /** * 功能:解码Type 58(带时标单命令)的值字段和CP56时标。 * 输入参数:payload——已确认有8字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:SCO(1):bit0为状态,bit1保留,bit2..6为QU,bit7为S/E,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SingleCommandWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_SingleCommandWithCP56Time2a_t *self) { return SingleCommand_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 1, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 58编码函数表的适配入口,将公共基类还原为SingleCommandWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少8字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SingleCommandWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SingleCommandWithCP56Time2a_Encode_Value((const IEC60870_SingleCommandWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SingleCommandWithCP56Time2a_VTable = { 58, 8, 1, sizeof(IEC60870_SingleCommandWithCP56Time2a_t), SingleCommandWithCP56Time2a_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 58(带时标单命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * state——单命令状态,0或1;SCO保留位由编码器保持为0。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——命令限定词QU,0..31;具体业务含义由应用判断。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_SingleCommandWithCP56Time2a_Create(IEC60870_SingleCommandWithCP56Time2a_t *self, uint32_t ioa, int state, int is_select, int qualifier, const IEC60870_CP56Time2a_t *time) { IEC60870_SingleCommandWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || state < 0 || state > 1 || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 31 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.state = (uint8_t)state; result.base.is_select = (uint8_t)is_select; result.base.qualifier = (uint8_t)qualifier; result.time = *time; Initialize_Object(&result.base.object, ioa, &SingleCommandWithCP56Time2a_VTable); if (!SingleCommandWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 58(带时标单命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的8字节载荷,必须非空。 * length——必须恰好为8;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SingleCommandWithCP56Time2a_Get_From_Buffer(IEC60870_SingleCommandWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SingleCommandWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 8U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SingleCommandWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &SingleCommandWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 59:带时标双命令 ---- */ /** * 功能:编码Type 59(带时标双命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少8字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:DCO(1):低2位为状态(仅1/2),bit2..6为QU,bit7为S/E,之后追加CP56Time2a(7)。 * 值部分调用DoubleCommand_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int DoubleCommandWithCP56Time2a_Encode_Value(const IEC60870_DoubleCommandWithCP56Time2a_t *self, uint8_t *payload) { return DoubleCommand_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 1, IEC60870_CP56_LEN); } /** * 功能:解码Type 59(带时标双命令)的值字段和CP56时标。 * 输入参数:payload——已确认有8字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:DCO(1):低2位为状态(仅1/2),bit2..6为QU,bit7为S/E,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int DoubleCommandWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_DoubleCommandWithCP56Time2a_t *self) { return DoubleCommand_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 1, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 59编码函数表的适配入口,将公共基类还原为DoubleCommandWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少8字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int DoubleCommandWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return DoubleCommandWithCP56Time2a_Encode_Value((const IEC60870_DoubleCommandWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable DoubleCommandWithCP56Time2a_VTable = { 59, 8, 1, sizeof(IEC60870_DoubleCommandWithCP56Time2a_t), DoubleCommandWithCP56Time2a_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 59(带时标双命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * state——双命令/步调节状态,仅允许1或2。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——命令限定词QU,0..31;具体业务含义由应用判断。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_DoubleCommandWithCP56Time2a_Create(IEC60870_DoubleCommandWithCP56Time2a_t *self, uint32_t ioa, int state, int is_select, int qualifier, const IEC60870_CP56Time2a_t *time) { IEC60870_DoubleCommandWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || state < 1 || state > 2 || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 31 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.state = (uint8_t)state; result.base.is_select = (uint8_t)is_select; result.base.qualifier = (uint8_t)qualifier; result.time = *time; Initialize_Object(&result.base.object, ioa, &DoubleCommandWithCP56Time2a_VTable); if (!DoubleCommandWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 59(带时标双命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的8字节载荷,必须非空。 * length——必须恰好为8;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_DoubleCommandWithCP56Time2a_Get_From_Buffer(IEC60870_DoubleCommandWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_DoubleCommandWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 8U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!DoubleCommandWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &DoubleCommandWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 60:带时标步调节命令 ---- */ /** * 功能:编码Type 60(带时标步调节命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少8字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:RCO(1):低2位为调节状态(仅1/2),bit2..6为QU,bit7为S/E,之后追加CP56Time2a(7)。 * 值部分调用StepCommand_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int StepCommandWithCP56Time2a_Encode_Value(const IEC60870_StepCommandWithCP56Time2a_t *self, uint8_t *payload) { return StepCommand_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 1, IEC60870_CP56_LEN); } /** * 功能:解码Type 60(带时标步调节命令)的值字段和CP56时标。 * 输入参数:payload——已确认有8字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:RCO(1):低2位为调节状态(仅1/2),bit2..6为QU,bit7为S/E,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int StepCommandWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_StepCommandWithCP56Time2a_t *self) { return StepCommand_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 1, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 60编码函数表的适配入口,将公共基类还原为StepCommandWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少8字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int StepCommandWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return StepCommandWithCP56Time2a_Encode_Value((const IEC60870_StepCommandWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable StepCommandWithCP56Time2a_VTable = { 60, 8, 1, sizeof(IEC60870_StepCommandWithCP56Time2a_t), StepCommandWithCP56Time2a_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 60(带时标步调节命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * state——双命令/步调节状态,仅允许1或2。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——命令限定词QU,0..31;具体业务含义由应用判断。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_StepCommandWithCP56Time2a_Create(IEC60870_StepCommandWithCP56Time2a_t *self, uint32_t ioa, int state, int is_select, int qualifier, const IEC60870_CP56Time2a_t *time) { IEC60870_StepCommandWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || state < 1 || state > 2 || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 31 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.state = (uint8_t)state; result.base.is_select = (uint8_t)is_select; result.base.qualifier = (uint8_t)qualifier; result.time = *time; Initialize_Object(&result.base.object, ioa, &StepCommandWithCP56Time2a_VTable); if (!StepCommandWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 60(带时标步调节命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的8字节载荷,必须非空。 * length——必须恰好为8;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_StepCommandWithCP56Time2a_Get_From_Buffer(IEC60870_StepCommandWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_StepCommandWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 8U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!StepCommandWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &StepCommandWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 61:带时标归一化设定值命令 ---- */ /** * 功能:编码Type 61(带时标归一化设定值命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少10字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:NVA(2)+QOS(1):归一化原始定点值;QOS低7位为QL,bit7为S/E,之后追加CP56Time2a(7)。 * 值部分调用SetpointCommandNormalized_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SetpointCommandNormalizedWithCP56Time2a_Encode_Value(const IEC60870_SetpointCommandNormalizedWithCP56Time2a_t *self, uint8_t *payload) { return SetpointCommandNormalized_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 3, IEC60870_CP56_LEN); } /** * 功能:解码Type 61(带时标归一化设定值命令)的值字段和CP56时标。 * 输入参数:payload——已确认有10字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:NVA(2)+QOS(1):归一化原始定点值;QOS低7位为QL,bit7为S/E,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SetpointCommandNormalizedWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_SetpointCommandNormalizedWithCP56Time2a_t *self) { return SetpointCommandNormalized_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 3, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 61编码函数表的适配入口,将公共基类还原为SetpointCommandNormalizedWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少10字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SetpointCommandNormalizedWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SetpointCommandNormalizedWithCP56Time2a_Encode_Value((const IEC60870_SetpointCommandNormalizedWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SetpointCommandNormalizedWithCP56Time2a_VTable = { 61, 10, 1, sizeof(IEC60870_SetpointCommandNormalizedWithCP56Time2a_t), SetpointCommandNormalizedWithCP56Time2a_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 61(带时标归一化设定值命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——归一化原始int16_t定点值,实际数值为value/32768.0。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——设定值限定词QL,0..127;编码到QOS低7位。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandNormalizedWithCP56Time2a_Create(IEC60870_SetpointCommandNormalizedWithCP56Time2a_t *self, uint32_t ioa, int16_t value, int is_select, int qualifier, const IEC60870_CP56Time2a_t *time) { IEC60870_SetpointCommandNormalizedWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 127 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = value; result.base.is_select = (uint8_t)is_select; result.base.qualifier = (uint8_t)qualifier; result.time = *time; Initialize_Object(&result.base.object, ioa, &SetpointCommandNormalizedWithCP56Time2a_VTable); if (!SetpointCommandNormalizedWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 61(带时标归一化设定值命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的10字节载荷,必须非空。 * length——必须恰好为10;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandNormalizedWithCP56Time2a_Get_From_Buffer(IEC60870_SetpointCommandNormalizedWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SetpointCommandNormalizedWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 10U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SetpointCommandNormalizedWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &SetpointCommandNormalizedWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 62:带时标标度化设定值命令 ---- */ /** * 功能:编码Type 62(带时标标度化设定值命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少10字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:SVA(2)+QOS(1):标度值;QOS低7位为QL,bit7为S/E,之后追加CP56Time2a(7)。 * 值部分调用SetpointCommandScaled_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SetpointCommandScaledWithCP56Time2a_Encode_Value(const IEC60870_SetpointCommandScaledWithCP56Time2a_t *self, uint8_t *payload) { return SetpointCommandScaled_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 3, IEC60870_CP56_LEN); } /** * 功能:解码Type 62(带时标标度化设定值命令)的值字段和CP56时标。 * 输入参数:payload——已确认有10字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:SVA(2)+QOS(1):标度值;QOS低7位为QL,bit7为S/E,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SetpointCommandScaledWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_SetpointCommandScaledWithCP56Time2a_t *self) { return SetpointCommandScaled_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 3, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 62编码函数表的适配入口,将公共基类还原为SetpointCommandScaledWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少10字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SetpointCommandScaledWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SetpointCommandScaledWithCP56Time2a_Encode_Value((const IEC60870_SetpointCommandScaledWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SetpointCommandScaledWithCP56Time2a_VTable = { 62, 10, 1, sizeof(IEC60870_SetpointCommandScaledWithCP56Time2a_t), SetpointCommandScaledWithCP56Time2a_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 62(带时标标度化设定值命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——有符号16位标度值,范围-32768..32767。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——设定值限定词QL,0..127;编码到QOS低7位。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandScaledWithCP56Time2a_Create(IEC60870_SetpointCommandScaledWithCP56Time2a_t *self, uint32_t ioa, int16_t value, int is_select, int qualifier, const IEC60870_CP56Time2a_t *time) { IEC60870_SetpointCommandScaledWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 127 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = value; result.base.is_select = (uint8_t)is_select; result.base.qualifier = (uint8_t)qualifier; result.time = *time; Initialize_Object(&result.base.object, ioa, &SetpointCommandScaledWithCP56Time2a_VTable); if (!SetpointCommandScaledWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 62(带时标标度化设定值命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的10字节载荷,必须非空。 * length——必须恰好为10;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandScaledWithCP56Time2a_Get_From_Buffer(IEC60870_SetpointCommandScaledWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SetpointCommandScaledWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 10U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SetpointCommandScaledWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &SetpointCommandScaledWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 63:带时标短浮点设定值命令 ---- */ /** * 功能:编码Type 63(带时标短浮点设定值命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少12字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:短浮点(4)+QOS(1):浮点位模式;QOS低7位为QL,bit7为S/E,之后追加CP56Time2a(7)。 * 值部分调用SetpointCommandShort_Encode_Value,成功后追加7字节CP56。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int SetpointCommandShortWithCP56Time2a_Encode_Value(const IEC60870_SetpointCommandShortWithCP56Time2a_t *self, uint8_t *payload) { return SetpointCommandShort_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 5, IEC60870_CP56_LEN); } /** * 功能:解码Type 63(带时标短浮点设定值命令)的值字段和CP56时标。 * 输入参数:payload——已确认有12字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:短浮点(4)+QOS(1):浮点位模式;QOS低7位为QL,bit7为S/E,之后追加CP56Time2a(7)。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int SetpointCommandShortWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_SetpointCommandShortWithCP56Time2a_t *self) { return SetpointCommandShort_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 5, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 63编码函数表的适配入口,将公共基类还原为SetpointCommandShortWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少12字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int SetpointCommandShortWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SetpointCommandShortWithCP56Time2a_Encode_Value((const IEC60870_SetpointCommandShortWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable SetpointCommandShortWithCP56Time2a_VTable = { 63, 12, 1, sizeof(IEC60870_SetpointCommandShortWithCP56Time2a_t), SetpointCommandShortWithCP56Time2a_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 63(带时标短浮点设定值命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * value——短浮点值,采用IEEE 754 binary32,不进行NaN/Inf业务过滤。 * is_select——选择/执行标志S/E,1为选择、0为执行;本函数不执行命令。 * qualifier——设定值限定词QL,0..127;编码到QOS低7位。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandShortWithCP56Time2a_Create(IEC60870_SetpointCommandShortWithCP56Time2a_t *self, uint32_t ioa, float value, int is_select, int qualifier, const IEC60870_CP56Time2a_t *time) { IEC60870_SetpointCommandShortWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || is_select < 0 || is_select > 1 || qualifier < 0 || qualifier > 127 || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.base.value = value; result.base.is_select = (uint8_t)is_select; result.base.qualifier = (uint8_t)qualifier; result.time = *time; Initialize_Object(&result.base.object, ioa, &SetpointCommandShortWithCP56Time2a_VTable); if (!SetpointCommandShortWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 63(带时标短浮点设定值命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的12字节载荷,必须非空。 * length——必须恰好为12;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_SetpointCommandShortWithCP56Time2a_Get_From_Buffer(IEC60870_SetpointCommandShortWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SetpointCommandShortWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 12U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SetpointCommandShortWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &SetpointCommandShortWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 100:总召唤命令 ---- */ /** * 功能:编码Type 100(总召唤命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:QOI(1):总召唤限定词,整字节保存,业务层判断召唤范围。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int InterrogationCommand_Encode_Value(const IEC60870_InterrogationCommand_t *self, uint8_t *payload) { payload[0] = self->qualifier; return 1; } /** * 功能:解码Type 100(总召唤命令)的值字段。 * 输入参数:payload——已确认有1字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:QOI(1):总召唤限定词,整字节保存,业务层判断召唤范围。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int InterrogationCommand_Decode_Value(const uint8_t *payload, IEC60870_InterrogationCommand_t *self) { self->qualifier = payload[0]; return 1; } /** * 功能:Type 100编码函数表的适配入口,将公共基类还原为InterrogationCommand对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少1字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int InterrogationCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return InterrogationCommand_Encode_Value((const IEC60870_InterrogationCommand_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable InterrogationCommand_VTable = { 100, 1, 1, sizeof(IEC60870_InterrogationCommand_t), InterrogationCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 100(总召唤命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * qualifier——总召唤限定词QOI,0..255;本层保留全部编码值。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_InterrogationCommand_Create(IEC60870_InterrogationCommand_t *self, uint32_t ioa, int qualifier) { IEC60870_InterrogationCommand_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || qualifier < 0 || qualifier > 255) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &InterrogationCommand_VTable); if (!InterrogationCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 100(总召唤命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的1字节载荷,必须非空。 * length——必须恰好为1;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_InterrogationCommand_Get_From_Buffer(IEC60870_InterrogationCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_InterrogationCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 1U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!InterrogationCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &InterrogationCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 101:电能脉冲召唤命令 ---- */ /** * 功能:编码Type 101(电能脉冲召唤命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:QCC(1):低6位为请求限定词RQT,高2位为冻结限定词FR。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int CounterInterrogationCommand_Encode_Value(const IEC60870_CounterInterrogationCommand_t *self, uint8_t *payload) { if (self->qualifier > 63U || self->freeze > 3U) { return 0; } /* QCC:低6位RQT,高2位FR。 */ payload[0] = (uint8_t)(self->qualifier | (self->freeze << 6)); return 1; } /** * 功能:解码Type 101(电能脉冲召唤命令)的值字段。 * 输入参数:payload——已确认有1字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:QCC(1):低6位为请求限定词RQT,高2位为冻结限定词FR。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int CounterInterrogationCommand_Decode_Value(const uint8_t *payload, IEC60870_CounterInterrogationCommand_t *self) { self->qualifier = payload[0] & 63U; self->freeze = payload[0] >> 6; return 1; } /** * 功能:Type 101编码函数表的适配入口,将公共基类还原为CounterInterrogationCommand对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少1字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int CounterInterrogationCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return CounterInterrogationCommand_Encode_Value((const IEC60870_CounterInterrogationCommand_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable CounterInterrogationCommand_VTable = { 101, 1, 1, sizeof(IEC60870_CounterInterrogationCommand_t), CounterInterrogationCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 101(电能脉冲召唤命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * qualifier——电能脉冲请求限定词RQT,0..63。 * freeze——冻结限定词FR,0..3,编码到QCC高2位。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_CounterInterrogationCommand_Create(IEC60870_CounterInterrogationCommand_t *self, uint32_t ioa, int qualifier, int freeze) { IEC60870_CounterInterrogationCommand_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || qualifier < 0 || qualifier > 63 || freeze < 0 || freeze > 3) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.qualifier = (uint8_t)qualifier; result.freeze = (uint8_t)freeze; Initialize_Object(&result.object, ioa, &CounterInterrogationCommand_VTable); if (!CounterInterrogationCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 101(电能脉冲召唤命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的1字节载荷,必须非空。 * length——必须恰好为1;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_CounterInterrogationCommand_Get_From_Buffer(IEC60870_CounterInterrogationCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_CounterInterrogationCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 1U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!CounterInterrogationCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &CounterInterrogationCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 102:读命令 ---- */ /** * 功能:编码Type 102(读命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少0字节的临时区;本类型不访问该区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:无值载荷:报文只包含由ASDU层处理的IOA。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int ReadCommand_Encode_Value(const IEC60870_ReadCommand_t *self, uint8_t *payload) { /* 读命令仅含IOA,无值载荷;不解引用payload。 */ (void)self; (void)payload; return 1; } /** * 功能:解码Type 102(读命令)的值字段。 * 输入参数:payload——已确认有0字节的载荷;允许NULL,函数不解引用。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:无值载荷:报文只包含由ASDU层处理的IOA。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int ReadCommand_Decode_Value(const uint8_t *payload, IEC60870_ReadCommand_t *self) { /* 读命令仅含IOA,无值载荷;不解引用payload。 */ (void)self; (void)payload; return 1; } /** * 功能:Type 102编码函数表的适配入口,将公共基类还原为ReadCommand对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少0字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int ReadCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return ReadCommand_Encode_Value((const IEC60870_ReadCommand_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable ReadCommand_VTable = { 102, 0, 1, sizeof(IEC60870_ReadCommand_t), ReadCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 102(读命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_ReadCommand_Create(IEC60870_ReadCommand_t *self, uint32_t ioa) { IEC60870_ReadCommand_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); Initialize_Object(&result.object, ioa, &ReadCommand_VTable); if (!ReadCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 102(读命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的0字节载荷,允许NULL。 * length——必须恰好为0;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_ReadCommand_Get_From_Buffer(IEC60870_ReadCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_ReadCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 0U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!ReadCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &ReadCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 103:时钟同步命令 ---- */ /** * 功能:编码Type 103(时钟同步命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少7字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:CP56Time2a(7):源时间及IV/SU,不执行系统校时。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int ClockSynchronizationCommand_Encode_Value(const IEC60870_ClockSynchronizationCommand_t *self, uint8_t *payload) { return IEC60870_Time_To_CP56(&self->time, payload, IEC60870_CP56_LEN); } /** * 功能:解码Type 103(时钟同步命令)的值字段和CP56时标。 * 输入参数:payload——已确认有7字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:CP56Time2a(7):源时间及IV/SU,不执行系统校时。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int ClockSynchronizationCommand_Decode_Value(const uint8_t *payload, IEC60870_ClockSynchronizationCommand_t *self) { return IEC60870_Time_From_CP56(payload, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 103编码函数表的适配入口,将公共基类还原为ClockSynchronizationCommand对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少7字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int ClockSynchronizationCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return ClockSynchronizationCommand_Encode_Value((const IEC60870_ClockSynchronizationCommand_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable ClockSynchronizationCommand_VTable = { 103, 7, 1, sizeof(IEC60870_ClockSynchronizationCommand_t), ClockSynchronizationCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 103(时钟同步命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_ClockSynchronizationCommand_Create(IEC60870_ClockSynchronizationCommand_t *self, uint32_t ioa, const IEC60870_CP56Time2a_t *time) { IEC60870_ClockSynchronizationCommand_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.time = *time; Initialize_Object(&result.object, ioa, &ClockSynchronizationCommand_VTable); if (!ClockSynchronizationCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 103(时钟同步命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的7字节载荷,必须非空。 * length——必须恰好为7;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_ClockSynchronizationCommand_Get_From_Buffer(IEC60870_ClockSynchronizationCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_ClockSynchronizationCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 7U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!ClockSynchronizationCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &ClockSynchronizationCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 105:复位进程命令 ---- */ /** * 功能:编码Type 105(复位进程命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:QRP(1):复位进程限定词,整字节保存,业务层判断操作含义。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int ResetProcessCommand_Encode_Value(const IEC60870_ResetProcessCommand_t *self, uint8_t *payload) { payload[0] = self->qualifier; return 1; } /** * 功能:解码Type 105(复位进程命令)的值字段。 * 输入参数:payload——已确认有1字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:QRP(1):复位进程限定词,整字节保存,业务层判断操作含义。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int ResetProcessCommand_Decode_Value(const uint8_t *payload, IEC60870_ResetProcessCommand_t *self) { self->qualifier = payload[0]; return 1; } /** * 功能:Type 105编码函数表的适配入口,将公共基类还原为ResetProcessCommand对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少1字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int ResetProcessCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return ResetProcessCommand_Encode_Value((const IEC60870_ResetProcessCommand_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable ResetProcessCommand_VTable = { 105, 1, 1, sizeof(IEC60870_ResetProcessCommand_t), ResetProcessCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 105(复位进程命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * qualifier——复位进程限定词QRP,0..255;本层不执行复位。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * 仅创建内存对象;加入ASDU时才按应用层参数编码IOA并检查报文容量。 */ IEC60870_AsduResult_t IEC60870_ResetProcessCommand_Create(IEC60870_ResetProcessCommand_t *self, uint32_t ioa, int qualifier) { IEC60870_ResetProcessCommand_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || qualifier < 0 || qualifier > 255) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &ResetProcessCommand_VTable); if (!ResetProcessCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 105(复位进程命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的1字节载荷,必须非空。 * length——必须恰好为1;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_ResetProcessCommand_Get_From_Buffer(IEC60870_ResetProcessCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_ResetProcessCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 1U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!ResetProcessCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &ResetProcessCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 107:带时标测试命令 ---- */ /** * 功能:编码Type 107(带时标测试命令)的值载荷,不处理IOA或ASDU头。 * 输入参数:self——非空具体对象;只检查本类型的值字段,不核对公共基类身份。 * 输出参数:payload——至少9字节的临时区。 * 返回值:1表示字段合法且编码完成;0表示字段或时标非法。 * 说明:布局:测试序号(2)+CP56Time2a(7):测试序号与链路发送序号无关。 * 多字节整数和浮点字节均低字节在前,结构体不直接映射报文。 * 失败可以留下部分临时字节;公开入口只在全部验证成功后写入用户缓冲区。 */ static int TestCommandWithCP56Time2a_Encode_Value(const IEC60870_TestCommandWithCP56Time2a_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, self->sequence, 2); return IEC60870_Time_To_CP56(&self->time, payload + 2, IEC60870_CP56_LEN); } /** * 功能:解码Type 107(带时标测试命令)的值字段和CP56时标。 * 输入参数:payload——已确认有9字节的载荷。 * 输出参数:self——调用方已清零的临时具体对象;不填写IOA、Type ID或函数表。 * 返回值:1表示解码成功;0表示保留位、状态或时标不合法。 * 说明:布局:测试序号(2)+CP56Time2a(7):测试序号与链路发送序号无关。 * 本内部函数不重复检查指针/长度,失败可留下部分字段,由公开解析器丢弃临时结果。 */ static int TestCommandWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_TestCommandWithCP56Time2a_t *self) { self->sequence = (uint16_t)IEC60870_Object_Read_LE(payload, 2); return IEC60870_Time_From_CP56(payload + 2, IEC60870_CP56_LEN, &self->time); } /** * 功能:Type 107编码函数表的适配入口,将公共基类还原为TestCommandWithCP56Time2a对象。 * 输入参数:object——来自该具体类型的公共首成员指针;调用前已核对Type ID和函数表。 * 输出参数:payload——至少9字节的临时载荷区,不包含IOA。 * 返回值:1表示值编码成功;0表示字段或时标非法。 * 说明:类型转换依赖结构体首成员地址相同的约定,不能向此入口传入伪造基类。 */ static int TestCommandWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return TestCommandWithCP56Time2a_Encode_Value((const IEC60870_TestCommandWithCP56Time2a_t *)object, payload); } /* 表项依次为Type ID、线上载荷字节数、单对象标志、内存结构尺寸、编码入口。 * sizeof仅用于检查解码输出存储,不能作为报文载荷长度。 */ static const IEC60870_InformationObjectVTable TestCommandWithCP56Time2a_VTable = { 107, 9, 1, sizeof(IEC60870_TestCommandWithCP56Time2a_t), TestCommandWithCP56Time2a_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 107(带时标测试命令)。 * 输入参数:ioa——信息对象地址,0..0xFFFFFF;编码时再按配置的IOA宽度检查上限。 * sequence——16位测试序号,0..65535,与APCI的N(S)/N(R)无关。 * time——非空CP56日历指针,采用2000..2099年约定;保留IV/SU,成功后按值复制。 * 输出参数:self——非空具体对象存储;成功时写入值、IOA、类型和只读编码函数表。 * 返回值:IEC60870_ASDU_OK表示构造完成;IEC60870_ASDU_ERROR表示参数、字段或时标非法。 * 说明:不分配堆内存。先在局部对象中试编码校验,再整体写回self;失败保持原对象不变。 * BCR/CP56输入按值复制,返回后调用方可以复用输入;构造成功不表示命令已执行。 */ IEC60870_AsduResult_t IEC60870_TestCommandWithCP56Time2a_Create(IEC60870_TestCommandWithCP56Time2a_t *self, uint32_t ioa, uint16_t sequence, const IEC60870_CP56Time2a_t *time) { IEC60870_TestCommandWithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || time == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.sequence = sequence; result.time = *time; Initialize_Object(&result.object, ioa, &TestCommandWithCP56Time2a_VTable); if (!TestCommandWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:从精确长度载荷中解析Type 107(带时标测试命令)。 * 输入参数:ioa——已从对象区提取的地址,0..0xFFFFFF;SQ隐含地址由ASDU层还原。 * payload——不包含IOA或ASDU头的9字节载荷,必须非空。 * length——必须恰好为9;截短和多余尾字节均拒绝。 * 输出参数:self——非空具体对象存储;成功结果按值保存,不借用输入。 * 返回值:IEC60870_ASDU_OK表示解析完成;IEC60870_ASDU_ERROR表示地址、长度或字段非法。 * 说明:仅在值与可选时标全部通过校验后绑定基类并写回self,任何失败保持原对象不变。 */ IEC60870_AsduResult_t IEC60870_TestCommandWithCP56Time2a_Get_From_Buffer(IEC60870_TestCommandWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_TestCommandWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || length != 9U || payload == NULL) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!TestCommandWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &TestCommandWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 5:步位置信息 ---- */ /** * 功能:编码步位置信息的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少2字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:VTI(1:低7位补码位置、bit7瞬态) + QDS(1);失败只影响临时区。 */ static int StepPositionInformation_Encode_Value(const IEC60870_StepPositionInformation_t *self, uint8_t *payload) { if (self->value < -64 || self->value > 63 || self->is_transient > 1U || (self->quality & 0x0EU)) { return 0; } payload[0] = (uint8_t)(((uint8_t)self->value & 0x7FU) | (self->is_transient << 7)); payload[1] = self->quality; return 1; } /** * 功能:解码步位置信息的值字段。 * 输入参数:payload——已校验完整长度的2字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:VTI(1:低7位补码位置、bit7瞬态) + QDS(1);公开入口在成功后统一提交。 */ static int StepPositionInformation_Decode_Value(const uint8_t *payload, IEC60870_StepPositionInformation_t *self) { if (payload[1] & 0x0EU) { return 0; } self->value = (int8_t)((payload[0] & 0x40U) ? (int)(payload[0] & 0x7FU) - 128 : (int)(payload[0] & 0x7FU)); self->is_transient = payload[0] >> 7; self->quality = payload[1]; return 1; } /** * 功能:将公共基类分派到StepPositionInformation编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int StepPositionInformation_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return StepPositionInformation_Encode_Value((const IEC60870_StepPositionInformation_t *)object, payload); } static const IEC60870_InformationObjectVTable StepPositionInformation_VTable = { 5, 2, 0, sizeof(IEC60870_StepPositionInformation_t), StepPositionInformation_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 5(步位置信息)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_StepPositionInformation_Create(IEC60870_StepPositionInformation_t *self, uint32_t ioa, int value, int is_transient, int quality) { IEC60870_StepPositionInformation_t result; uint8_t payload[2]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (value < -64 || value > 63) || (is_transient < 0 || is_transient > 1) || (quality < 0 || quality > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = (int8_t)value; result.is_transient = (uint8_t)is_transient; result.quality = (uint8_t)quality; Initialize_Object(&result.object, ioa, &StepPositionInformation_VTable); if (!StepPositionInformation_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 5(步位置信息)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_StepPositionInformation_Get_From_Buffer(IEC60870_StepPositionInformation_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_StepPositionInformation_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 2U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!StepPositionInformation_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &StepPositionInformation_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 7:32位比特串监视 ---- */ /** * 功能:编码32位比特串监视的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少5字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:BSI(4) + QDS(1);失败只影响临时区。 */ static int BitString32_Encode_Value(const IEC60870_BitString32_t *self, uint8_t *payload) { if (self->quality & 0x0EU) { return 0; } IEC60870_Object_Write_LE(payload + 0, self->value, 4); payload[4] = self->quality; return 1; } /** * 功能:解码32位比特串监视的值字段。 * 输入参数:payload——已校验完整长度的5字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:BSI(4) + QDS(1);公开入口在成功后统一提交。 */ static int BitString32_Decode_Value(const uint8_t *payload, IEC60870_BitString32_t *self) { if (payload[4] & 0x0EU) { return 0; } self->value = (uint32_t)IEC60870_Object_Read_LE(payload + 0, 4); self->quality = payload[4]; return 1; } /** * 功能:将公共基类分派到BitString32编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int BitString32_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return BitString32_Encode_Value((const IEC60870_BitString32_t *)object, payload); } static const IEC60870_InformationObjectVTable BitString32_VTable = { 7, 5, 0, sizeof(IEC60870_BitString32_t), BitString32_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 7(32位比特串监视)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_BitString32_Create(IEC60870_BitString32_t *self, uint32_t ioa, uint32_t value, int quality) { IEC60870_BitString32_t result; uint8_t payload[5]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (quality < 0 || quality > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.quality = (uint8_t)quality; Initialize_Object(&result.object, ioa, &BitString32_VTable); if (!BitString32_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 7(32位比特串监视)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_BitString32_Get_From_Buffer(IEC60870_BitString32_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_BitString32_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 5U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!BitString32_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &BitString32_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 2:带CP24单点信息 ---- */ /** * 功能:编码带CP24单点信息的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少4字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(1) + CP24(3);失败只影响临时区。 */ static int SinglePointWithCP24Time2a_Encode_Value(const IEC60870_SinglePointWithCP24Time2a_t *self, uint8_t *payload) { return SinglePointInformation_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP24(&self->time, payload + 1, IEC60870_CP24_LEN); } /** * 功能:解码带CP24单点信息的值字段。 * 输入参数:payload——已校验完整长度的4字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(1) + CP24(3);公开入口在成功后统一提交。 */ static int SinglePointWithCP24Time2a_Decode_Value(const uint8_t *payload, IEC60870_SinglePointWithCP24Time2a_t *self) { return SinglePointInformation_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP24(payload + 1, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到SinglePointWithCP24Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int SinglePointWithCP24Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SinglePointWithCP24Time2a_Encode_Value((const IEC60870_SinglePointWithCP24Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable SinglePointWithCP24Time2a_VTable = { 2, 4, 0, sizeof(IEC60870_SinglePointWithCP24Time2a_t), SinglePointWithCP24Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 2(带CP24单点信息)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_SinglePointWithCP24Time2a_Create(IEC60870_SinglePointWithCP24Time2a_t *self, uint32_t ioa, int value, int quality, const IEC60870_CP24Time2a_t *time) { IEC60870_SinglePointWithCP24Time2a_t result; uint8_t payload[4]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_SinglePointInformation_Create(&result.base, ioa, value, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &SinglePointWithCP24Time2a_VTable); if (!SinglePointWithCP24Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 2(带CP24单点信息)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_SinglePointWithCP24Time2a_Get_From_Buffer(IEC60870_SinglePointWithCP24Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SinglePointWithCP24Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 4U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SinglePointWithCP24Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &SinglePointWithCP24Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 4:带CP24双点信息 ---- */ /** * 功能:编码带CP24双点信息的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少4字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(1) + CP24(3);失败只影响临时区。 */ static int DoublePointWithCP24Time2a_Encode_Value(const IEC60870_DoublePointWithCP24Time2a_t *self, uint8_t *payload) { return DoublePointInformation_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP24(&self->time, payload + 1, IEC60870_CP24_LEN); } /** * 功能:解码带CP24双点信息的值字段。 * 输入参数:payload——已校验完整长度的4字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(1) + CP24(3);公开入口在成功后统一提交。 */ static int DoublePointWithCP24Time2a_Decode_Value(const uint8_t *payload, IEC60870_DoublePointWithCP24Time2a_t *self) { return DoublePointInformation_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP24(payload + 1, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到DoublePointWithCP24Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int DoublePointWithCP24Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return DoublePointWithCP24Time2a_Encode_Value((const IEC60870_DoublePointWithCP24Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable DoublePointWithCP24Time2a_VTable = { 4, 4, 0, sizeof(IEC60870_DoublePointWithCP24Time2a_t), DoublePointWithCP24Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 4(带CP24双点信息)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_DoublePointWithCP24Time2a_Create(IEC60870_DoublePointWithCP24Time2a_t *self, uint32_t ioa, int value, int quality, const IEC60870_CP24Time2a_t *time) { IEC60870_DoublePointWithCP24Time2a_t result; uint8_t payload[4]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_DoublePointInformation_Create(&result.base, ioa, value, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &DoublePointWithCP24Time2a_VTable); if (!DoublePointWithCP24Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 4(带CP24双点信息)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_DoublePointWithCP24Time2a_Get_From_Buffer(IEC60870_DoublePointWithCP24Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_DoublePointWithCP24Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 4U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!DoublePointWithCP24Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &DoublePointWithCP24Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 6:带CP24步位置信息 ---- */ /** * 功能:编码带CP24步位置信息的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少5字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(2) + CP24(3);失败只影响临时区。 */ static int StepPositionWithCP24Time2a_Encode_Value(const IEC60870_StepPositionWithCP24Time2a_t *self, uint8_t *payload) { return StepPositionInformation_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP24(&self->time, payload + 2, IEC60870_CP24_LEN); } /** * 功能:解码带CP24步位置信息的值字段。 * 输入参数:payload——已校验完整长度的5字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(2) + CP24(3);公开入口在成功后统一提交。 */ static int StepPositionWithCP24Time2a_Decode_Value(const uint8_t *payload, IEC60870_StepPositionWithCP24Time2a_t *self) { return StepPositionInformation_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP24(payload + 2, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到StepPositionWithCP24Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int StepPositionWithCP24Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return StepPositionWithCP24Time2a_Encode_Value((const IEC60870_StepPositionWithCP24Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable StepPositionWithCP24Time2a_VTable = { 6, 5, 0, sizeof(IEC60870_StepPositionWithCP24Time2a_t), StepPositionWithCP24Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 6(带CP24步位置信息)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_StepPositionWithCP24Time2a_Create(IEC60870_StepPositionWithCP24Time2a_t *self, uint32_t ioa, int value, int is_transient, int quality, const IEC60870_CP24Time2a_t *time) { IEC60870_StepPositionWithCP24Time2a_t result; uint8_t payload[5]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_StepPositionInformation_Create(&result.base, ioa, value, is_transient, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &StepPositionWithCP24Time2a_VTable); if (!StepPositionWithCP24Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 6(带CP24步位置信息)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_StepPositionWithCP24Time2a_Get_From_Buffer(IEC60870_StepPositionWithCP24Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_StepPositionWithCP24Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 5U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!StepPositionWithCP24Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &StepPositionWithCP24Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 8:带CP24比特串监视 ---- */ /** * 功能:编码带CP24比特串监视的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少8字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(5) + CP24(3);失败只影响临时区。 */ static int Bitstring32WithCP24Time2a_Encode_Value(const IEC60870_Bitstring32WithCP24Time2a_t *self, uint8_t *payload) { return BitString32_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP24(&self->time, payload + 5, IEC60870_CP24_LEN); } /** * 功能:解码带CP24比特串监视的值字段。 * 输入参数:payload——已校验完整长度的8字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(5) + CP24(3);公开入口在成功后统一提交。 */ static int Bitstring32WithCP24Time2a_Decode_Value(const uint8_t *payload, IEC60870_Bitstring32WithCP24Time2a_t *self) { return BitString32_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP24(payload + 5, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到Bitstring32WithCP24Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int Bitstring32WithCP24Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return Bitstring32WithCP24Time2a_Encode_Value((const IEC60870_Bitstring32WithCP24Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable Bitstring32WithCP24Time2a_VTable = { 8, 8, 0, sizeof(IEC60870_Bitstring32WithCP24Time2a_t), Bitstring32WithCP24Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 8(带CP24比特串监视)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_Bitstring32WithCP24Time2a_Create(IEC60870_Bitstring32WithCP24Time2a_t *self, uint32_t ioa, uint32_t value, int quality, const IEC60870_CP24Time2a_t *time) { IEC60870_Bitstring32WithCP24Time2a_t result; uint8_t payload[8]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_BitString32_Create(&result.base, ioa, value, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &Bitstring32WithCP24Time2a_VTable); if (!Bitstring32WithCP24Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 8(带CP24比特串监视)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_Bitstring32WithCP24Time2a_Get_From_Buffer(IEC60870_Bitstring32WithCP24Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_Bitstring32WithCP24Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 8U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!Bitstring32WithCP24Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &Bitstring32WithCP24Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 10:带CP24归一化测量值 ---- */ /** * 功能:编码带CP24归一化测量值的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少6字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(3) + CP24(3);失败只影响临时区。 */ static int MeasuredValueNormalizedWithCP24Time2a_Encode_Value(const IEC60870_MeasuredValueNormalizedWithCP24Time2a_t *self, uint8_t *payload) { return MeasuredValueNormalized_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP24(&self->time, payload + 3, IEC60870_CP24_LEN); } /** * 功能:解码带CP24归一化测量值的值字段。 * 输入参数:payload——已校验完整长度的6字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(3) + CP24(3);公开入口在成功后统一提交。 */ static int MeasuredValueNormalizedWithCP24Time2a_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueNormalizedWithCP24Time2a_t *self) { return MeasuredValueNormalized_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP24(payload + 3, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到MeasuredValueNormalizedWithCP24Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int MeasuredValueNormalizedWithCP24Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueNormalizedWithCP24Time2a_Encode_Value((const IEC60870_MeasuredValueNormalizedWithCP24Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable MeasuredValueNormalizedWithCP24Time2a_VTable = { 10, 6, 0, sizeof(IEC60870_MeasuredValueNormalizedWithCP24Time2a_t), MeasuredValueNormalizedWithCP24Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 10(带CP24归一化测量值)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueNormalizedWithCP24Time2a_Create(IEC60870_MeasuredValueNormalizedWithCP24Time2a_t *self, uint32_t ioa, int16_t value, int quality, const IEC60870_CP24Time2a_t *time) { IEC60870_MeasuredValueNormalizedWithCP24Time2a_t result; uint8_t payload[6]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_MeasuredValueNormalized_Create(&result.base, ioa, value, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &MeasuredValueNormalizedWithCP24Time2a_VTable); if (!MeasuredValueNormalizedWithCP24Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 10(带CP24归一化测量值)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueNormalizedWithCP24Time2a_Get_From_Buffer(IEC60870_MeasuredValueNormalizedWithCP24Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueNormalizedWithCP24Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 6U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueNormalizedWithCP24Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &MeasuredValueNormalizedWithCP24Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 12:带CP24标度化测量值 ---- */ /** * 功能:编码带CP24标度化测量值的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少6字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(3) + CP24(3);失败只影响临时区。 */ static int MeasuredValueScaledWithCP24Time2a_Encode_Value(const IEC60870_MeasuredValueScaledWithCP24Time2a_t *self, uint8_t *payload) { return MeasuredValueScaled_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP24(&self->time, payload + 3, IEC60870_CP24_LEN); } /** * 功能:解码带CP24标度化测量值的值字段。 * 输入参数:payload——已校验完整长度的6字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(3) + CP24(3);公开入口在成功后统一提交。 */ static int MeasuredValueScaledWithCP24Time2a_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueScaledWithCP24Time2a_t *self) { return MeasuredValueScaled_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP24(payload + 3, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到MeasuredValueScaledWithCP24Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int MeasuredValueScaledWithCP24Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueScaledWithCP24Time2a_Encode_Value((const IEC60870_MeasuredValueScaledWithCP24Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable MeasuredValueScaledWithCP24Time2a_VTable = { 12, 6, 0, sizeof(IEC60870_MeasuredValueScaledWithCP24Time2a_t), MeasuredValueScaledWithCP24Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 12(带CP24标度化测量值)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueScaledWithCP24Time2a_Create(IEC60870_MeasuredValueScaledWithCP24Time2a_t *self, uint32_t ioa, int16_t value, int quality, const IEC60870_CP24Time2a_t *time) { IEC60870_MeasuredValueScaledWithCP24Time2a_t result; uint8_t payload[6]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_MeasuredValueScaled_Create(&result.base, ioa, value, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &MeasuredValueScaledWithCP24Time2a_VTable); if (!MeasuredValueScaledWithCP24Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 12(带CP24标度化测量值)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueScaledWithCP24Time2a_Get_From_Buffer(IEC60870_MeasuredValueScaledWithCP24Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueScaledWithCP24Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 6U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueScaledWithCP24Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &MeasuredValueScaledWithCP24Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 14:带CP24短浮点测量值 ---- */ /** * 功能:编码带CP24短浮点测量值的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少8字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(5) + CP24(3);失败只影响临时区。 */ static int MeasuredValueShortWithCP24Time2a_Encode_Value(const IEC60870_MeasuredValueShortWithCP24Time2a_t *self, uint8_t *payload) { return MeasuredValueShort_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP24(&self->time, payload + 5, IEC60870_CP24_LEN); } /** * 功能:解码带CP24短浮点测量值的值字段。 * 输入参数:payload——已校验完整长度的8字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(5) + CP24(3);公开入口在成功后统一提交。 */ static int MeasuredValueShortWithCP24Time2a_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueShortWithCP24Time2a_t *self) { return MeasuredValueShort_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP24(payload + 5, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到MeasuredValueShortWithCP24Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int MeasuredValueShortWithCP24Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueShortWithCP24Time2a_Encode_Value((const IEC60870_MeasuredValueShortWithCP24Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable MeasuredValueShortWithCP24Time2a_VTable = { 14, 8, 0, sizeof(IEC60870_MeasuredValueShortWithCP24Time2a_t), MeasuredValueShortWithCP24Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 14(带CP24短浮点测量值)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueShortWithCP24Time2a_Create(IEC60870_MeasuredValueShortWithCP24Time2a_t *self, uint32_t ioa, float value, int quality, const IEC60870_CP24Time2a_t *time) { IEC60870_MeasuredValueShortWithCP24Time2a_t result; uint8_t payload[8]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_MeasuredValueShort_Create(&result.base, ioa, value, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &MeasuredValueShortWithCP24Time2a_VTable); if (!MeasuredValueShortWithCP24Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 14(带CP24短浮点测量值)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueShortWithCP24Time2a_Get_From_Buffer(IEC60870_MeasuredValueShortWithCP24Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueShortWithCP24Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 8U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueShortWithCP24Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &MeasuredValueShortWithCP24Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 16:带CP24累计量 ---- */ /** * 功能:编码带CP24累计量的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少8字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(5) + CP24(3);失败只影响临时区。 */ static int IntegratedTotalsWithCP24Time2a_Encode_Value(const IEC60870_IntegratedTotalsWithCP24Time2a_t *self, uint8_t *payload) { return IntegratedTotals_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP24(&self->time, payload + 5, IEC60870_CP24_LEN); } /** * 功能:解码带CP24累计量的值字段。 * 输入参数:payload——已校验完整长度的8字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(5) + CP24(3);公开入口在成功后统一提交。 */ static int IntegratedTotalsWithCP24Time2a_Decode_Value(const uint8_t *payload, IEC60870_IntegratedTotalsWithCP24Time2a_t *self) { return IntegratedTotals_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP24(payload + 5, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到IntegratedTotalsWithCP24Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int IntegratedTotalsWithCP24Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return IntegratedTotalsWithCP24Time2a_Encode_Value((const IEC60870_IntegratedTotalsWithCP24Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable IntegratedTotalsWithCP24Time2a_VTable = { 16, 8, 0, sizeof(IEC60870_IntegratedTotalsWithCP24Time2a_t), IntegratedTotalsWithCP24Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 16(带CP24累计量)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_IntegratedTotalsWithCP24Time2a_Create(IEC60870_IntegratedTotalsWithCP24Time2a_t *self, uint32_t ioa, const IEC60870_BinaryCounterReading_t *value, const IEC60870_CP24Time2a_t *time) { IEC60870_IntegratedTotalsWithCP24Time2a_t result; uint8_t payload[8]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_IntegratedTotals_Create(&result.base, ioa, value) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &IntegratedTotalsWithCP24Time2a_VTable); if (!IntegratedTotalsWithCP24Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 16(带CP24累计量)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_IntegratedTotalsWithCP24Time2a_Get_From_Buffer(IEC60870_IntegratedTotalsWithCP24Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_IntegratedTotalsWithCP24Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 8U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!IntegratedTotalsWithCP24Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &IntegratedTotalsWithCP24Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 32:带CP56步位置信息 ---- */ /** * 功能:编码带CP56步位置信息的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少9字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(2) + CP56(7);失败只影响临时区。 */ static int StepPositionWithCP56Time2a_Encode_Value(const IEC60870_StepPositionWithCP56Time2a_t *self, uint8_t *payload) { return StepPositionInformation_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 2, IEC60870_CP56_LEN); } /** * 功能:解码带CP56步位置信息的值字段。 * 输入参数:payload——已校验完整长度的9字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(2) + CP56(7);公开入口在成功后统一提交。 */ static int StepPositionWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_StepPositionWithCP56Time2a_t *self) { return StepPositionInformation_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 2, IEC60870_CP56_LEN, &self->time); } /** * 功能:将公共基类分派到StepPositionWithCP56Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int StepPositionWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return StepPositionWithCP56Time2a_Encode_Value((const IEC60870_StepPositionWithCP56Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable StepPositionWithCP56Time2a_VTable = { 32, 9, 0, sizeof(IEC60870_StepPositionWithCP56Time2a_t), StepPositionWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 32(带CP56步位置信息)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_StepPositionWithCP56Time2a_Create(IEC60870_StepPositionWithCP56Time2a_t *self, uint32_t ioa, int value, int is_transient, int quality, const IEC60870_CP56Time2a_t *time) { IEC60870_StepPositionWithCP56Time2a_t result; uint8_t payload[9]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_StepPositionInformation_Create(&result.base, ioa, value, is_transient, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &StepPositionWithCP56Time2a_VTable); if (!StepPositionWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 32(带CP56步位置信息)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_StepPositionWithCP56Time2a_Get_From_Buffer(IEC60870_StepPositionWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_StepPositionWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 9U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!StepPositionWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &StepPositionWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 33:带CP56比特串监视 ---- */ /** * 功能:编码带CP56比特串监视的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少12字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(5) + CP56(7);失败只影响临时区。 */ static int Bitstring32WithCP56Time2a_Encode_Value(const IEC60870_Bitstring32WithCP56Time2a_t *self, uint8_t *payload) { return BitString32_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 5, IEC60870_CP56_LEN); } /** * 功能:解码带CP56比特串监视的值字段。 * 输入参数:payload——已校验完整长度的12字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(5) + CP56(7);公开入口在成功后统一提交。 */ static int Bitstring32WithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_Bitstring32WithCP56Time2a_t *self) { return BitString32_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 5, IEC60870_CP56_LEN, &self->time); } /** * 功能:将公共基类分派到Bitstring32WithCP56Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int Bitstring32WithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return Bitstring32WithCP56Time2a_Encode_Value((const IEC60870_Bitstring32WithCP56Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable Bitstring32WithCP56Time2a_VTable = { 33, 12, 0, sizeof(IEC60870_Bitstring32WithCP56Time2a_t), Bitstring32WithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 33(带CP56比特串监视)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_Bitstring32WithCP56Time2a_Create(IEC60870_Bitstring32WithCP56Time2a_t *self, uint32_t ioa, uint32_t value, int quality, const IEC60870_CP56Time2a_t *time) { IEC60870_Bitstring32WithCP56Time2a_t result; uint8_t payload[12]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_BitString32_Create(&result.base, ioa, value, quality) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &Bitstring32WithCP56Time2a_VTable); if (!Bitstring32WithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 33(带CP56比特串监视)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_Bitstring32WithCP56Time2a_Get_From_Buffer(IEC60870_Bitstring32WithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_Bitstring32WithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 12U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!Bitstring32WithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &Bitstring32WithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 17:带CP24保护事件 ---- */ /** * 功能:编码带CP24保护事件的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少6字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:SEP(1:ES低2位,QDP高5位) + CP16(2) + CP24(3);失败只影响临时区。 */ static int EventOfProtectionEquipment_Encode_Value(const IEC60870_EventOfProtectionEquipment_t *self, uint8_t *payload) { if (self->event > 3U || (self->quality & 0x07U)) { return 0; } payload[0] = (uint8_t)(self->event | self->quality); return IEC60870_Time_To_CP16(&self->elapsed_time, payload + 1, IEC60870_CP16_LEN) && IEC60870_Time_To_CP24(&self->time, payload + 3, IEC60870_CP24_LEN); } /** * 功能:解码带CP24保护事件的值字段。 * 输入参数:payload——已校验完整长度的6字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:SEP(1:ES低2位,QDP高5位) + CP16(2) + CP24(3);公开入口在成功后统一提交。 */ static int EventOfProtectionEquipment_Decode_Value(const uint8_t *payload, IEC60870_EventOfProtectionEquipment_t *self) { if (payload[0] & 0x04U) { return 0; } self->event = payload[0] & 0x03U; self->quality = payload[0] & 0xF8U; return IEC60870_Time_From_CP16(payload + 1, IEC60870_CP16_LEN, &self->elapsed_time) && IEC60870_Time_From_CP24(payload + 3, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到EventOfProtectionEquipment编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int EventOfProtectionEquipment_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return EventOfProtectionEquipment_Encode_Value((const IEC60870_EventOfProtectionEquipment_t *)object, payload); } static const IEC60870_InformationObjectVTable EventOfProtectionEquipment_VTable = { 17, 6, 0, sizeof(IEC60870_EventOfProtectionEquipment_t), EventOfProtectionEquipment_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 17(带CP24保护事件)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_EventOfProtectionEquipment_Create(IEC60870_EventOfProtectionEquipment_t *self, uint32_t ioa, int event, int quality, const IEC60870_CP16Time2a_t *elapsed_time, const IEC60870_CP24Time2a_t *time) { IEC60870_EventOfProtectionEquipment_t result; uint8_t payload[6]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (event < 0 || event > 3) || (quality < 0 || quality > 255) || (elapsed_time == NULL) || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.event = (uint8_t)event; result.quality = (uint8_t)quality; result.elapsed_time = *elapsed_time; result.time = *time; Initialize_Object(&result.object, ioa, &EventOfProtectionEquipment_VTable); if (!EventOfProtectionEquipment_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 17(带CP24保护事件)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_EventOfProtectionEquipment_Get_From_Buffer(IEC60870_EventOfProtectionEquipment_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_EventOfProtectionEquipment_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 6U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!EventOfProtectionEquipment_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &EventOfProtectionEquipment_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 18:带CP24成组保护启动事件 ---- */ /** * 功能:编码带CP24成组保护启动事件的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少7字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:事件(1) + QDP(1) + CP16(2) + CP24(3);失败只影响临时区。 */ static int PackedStartEventsOfProtectionEquipment_Encode_Value(const IEC60870_PackedStartEventsOfProtectionEquipment_t *self, uint8_t *payload) { if (self->event > 63U || (self->quality & 0x07U)) { return 0; } payload[0] = self->event; payload[1] = self->quality; return IEC60870_Time_To_CP16(&self->elapsed_time, payload + 2, IEC60870_CP16_LEN) && IEC60870_Time_To_CP24(&self->time, payload + 4, IEC60870_CP24_LEN); } /** * 功能:解码带CP24成组保护启动事件的值字段。 * 输入参数:payload——已校验完整长度的7字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:事件(1) + QDP(1) + CP16(2) + CP24(3);公开入口在成功后统一提交。 */ static int PackedStartEventsOfProtectionEquipment_Decode_Value(const uint8_t *payload, IEC60870_PackedStartEventsOfProtectionEquipment_t *self) { if (payload[0] > 63U || (payload[1] & 0x07U)) { return 0; } self->event = payload[0]; self->quality = payload[1]; return IEC60870_Time_From_CP16(payload + 2, IEC60870_CP16_LEN, &self->elapsed_time) && IEC60870_Time_From_CP24(payload + 4, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到PackedStartEventsOfProtectionEquipment编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int PackedStartEventsOfProtectionEquipment_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return PackedStartEventsOfProtectionEquipment_Encode_Value((const IEC60870_PackedStartEventsOfProtectionEquipment_t *)object, payload); } static const IEC60870_InformationObjectVTable PackedStartEventsOfProtectionEquipment_VTable = { 18, 7, 0, sizeof(IEC60870_PackedStartEventsOfProtectionEquipment_t), PackedStartEventsOfProtectionEquipment_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 18(带CP24成组保护启动事件)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_PackedStartEventsOfProtectionEquipment_Create(IEC60870_PackedStartEventsOfProtectionEquipment_t *self, uint32_t ioa, int event, int quality, const IEC60870_CP16Time2a_t *elapsed_time, const IEC60870_CP24Time2a_t *time) { IEC60870_PackedStartEventsOfProtectionEquipment_t result; uint8_t payload[7]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (event < 0 || event > 63) || (quality < 0 || quality > 255) || (elapsed_time == NULL) || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.event = (uint8_t)event; result.quality = (uint8_t)quality; result.elapsed_time = *elapsed_time; result.time = *time; Initialize_Object(&result.object, ioa, &PackedStartEventsOfProtectionEquipment_VTable); if (!PackedStartEventsOfProtectionEquipment_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 18(带CP24成组保护启动事件)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_PackedStartEventsOfProtectionEquipment_Get_From_Buffer(IEC60870_PackedStartEventsOfProtectionEquipment_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_PackedStartEventsOfProtectionEquipment_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 7U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!PackedStartEventsOfProtectionEquipment_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &PackedStartEventsOfProtectionEquipment_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 19:带CP24保护输出电路信息 ---- */ /** * 功能:编码带CP24保护输出电路信息的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少7字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:事件(1) + QDP(1) + CP16(2) + CP24(3);失败只影响临时区。 */ static int PackedOutputCircuitInfo_Encode_Value(const IEC60870_PackedOutputCircuitInfo_t *self, uint8_t *payload) { if (self->event > 15U || (self->quality & 0x07U)) { return 0; } payload[0] = self->event; payload[1] = self->quality; return IEC60870_Time_To_CP16(&self->elapsed_time, payload + 2, IEC60870_CP16_LEN) && IEC60870_Time_To_CP24(&self->time, payload + 4, IEC60870_CP24_LEN); } /** * 功能:解码带CP24保护输出电路信息的值字段。 * 输入参数:payload——已校验完整长度的7字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:事件(1) + QDP(1) + CP16(2) + CP24(3);公开入口在成功后统一提交。 */ static int PackedOutputCircuitInfo_Decode_Value(const uint8_t *payload, IEC60870_PackedOutputCircuitInfo_t *self) { if (payload[0] > 15U || (payload[1] & 0x07U)) { return 0; } self->event = payload[0]; self->quality = payload[1]; return IEC60870_Time_From_CP16(payload + 2, IEC60870_CP16_LEN, &self->elapsed_time) && IEC60870_Time_From_CP24(payload + 4, IEC60870_CP24_LEN, &self->time); } /** * 功能:将公共基类分派到PackedOutputCircuitInfo编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int PackedOutputCircuitInfo_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return PackedOutputCircuitInfo_Encode_Value((const IEC60870_PackedOutputCircuitInfo_t *)object, payload); } static const IEC60870_InformationObjectVTable PackedOutputCircuitInfo_VTable = { 19, 7, 0, sizeof(IEC60870_PackedOutputCircuitInfo_t), PackedOutputCircuitInfo_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 19(带CP24保护输出电路信息)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_PackedOutputCircuitInfo_Create(IEC60870_PackedOutputCircuitInfo_t *self, uint32_t ioa, int event, int quality, const IEC60870_CP16Time2a_t *elapsed_time, const IEC60870_CP24Time2a_t *time) { IEC60870_PackedOutputCircuitInfo_t result; uint8_t payload[7]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (event < 0 || event > 15) || (quality < 0 || quality > 255) || (elapsed_time == NULL) || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.event = (uint8_t)event; result.quality = (uint8_t)quality; result.elapsed_time = *elapsed_time; result.time = *time; Initialize_Object(&result.object, ioa, &PackedOutputCircuitInfo_VTable); if (!PackedOutputCircuitInfo_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 19(带CP24保护输出电路信息)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_PackedOutputCircuitInfo_Get_From_Buffer(IEC60870_PackedOutputCircuitInfo_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_PackedOutputCircuitInfo_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 7U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!PackedOutputCircuitInfo_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &PackedOutputCircuitInfo_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 38:带CP56保护事件 ---- */ /** * 功能:编码带CP56保护事件的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少10字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:SEP(1:ES低2位,QDP高5位) + CP16(2) + CP56(7);失败只影响临时区。 */ static int EventOfProtectionEquipmentWithCP56Time2a_Encode_Value(const IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t *self, uint8_t *payload) { if (self->event > 3U || (self->quality & 0x07U)) { return 0; } payload[0] = (uint8_t)(self->event | self->quality); return IEC60870_Time_To_CP16(&self->elapsed_time, payload + 1, IEC60870_CP16_LEN) && IEC60870_Time_To_CP56(&self->time, payload + 3, IEC60870_CP56_LEN); } /** * 功能:解码带CP56保护事件的值字段。 * 输入参数:payload——已校验完整长度的10字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:SEP(1:ES低2位,QDP高5位) + CP16(2) + CP56(7);公开入口在成功后统一提交。 */ static int EventOfProtectionEquipmentWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t *self) { if (payload[0] & 0x04U) { return 0; } self->event = payload[0] & 0x03U; self->quality = payload[0] & 0xF8U; return IEC60870_Time_From_CP16(payload + 1, IEC60870_CP16_LEN, &self->elapsed_time) && IEC60870_Time_From_CP56(payload + 3, IEC60870_CP56_LEN, &self->time); } /** * 功能:将公共基类分派到EventOfProtectionEquipmentWithCP56Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int EventOfProtectionEquipmentWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return EventOfProtectionEquipmentWithCP56Time2a_Encode_Value((const IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable EventOfProtectionEquipmentWithCP56Time2a_VTable = { 38, 10, 0, sizeof(IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t), EventOfProtectionEquipmentWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 38(带CP56保护事件)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_EventOfProtectionEquipmentWithCP56Time2a_Create(IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t *self, uint32_t ioa, int event, int quality, const IEC60870_CP16Time2a_t *elapsed_time, const IEC60870_CP56Time2a_t *time) { IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t result; uint8_t payload[10]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (event < 0 || event > 3) || (quality < 0 || quality > 255) || (elapsed_time == NULL) || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.event = (uint8_t)event; result.quality = (uint8_t)quality; result.elapsed_time = *elapsed_time; result.time = *time; Initialize_Object(&result.object, ioa, &EventOfProtectionEquipmentWithCP56Time2a_VTable); if (!EventOfProtectionEquipmentWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 38(带CP56保护事件)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_EventOfProtectionEquipmentWithCP56Time2a_Get_From_Buffer(IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 10U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!EventOfProtectionEquipmentWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &EventOfProtectionEquipmentWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 39:带CP56成组保护启动事件 ---- */ /** * 功能:编码带CP56成组保护启动事件的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少11字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:事件(1) + QDP(1) + CP16(2) + CP56(7);失败只影响临时区。 */ static int PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Encode_Value(const IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t *self, uint8_t *payload) { if (self->event > 63U || (self->quality & 0x07U)) { return 0; } payload[0] = self->event; payload[1] = self->quality; return IEC60870_Time_To_CP16(&self->elapsed_time, payload + 2, IEC60870_CP16_LEN) && IEC60870_Time_To_CP56(&self->time, payload + 4, IEC60870_CP56_LEN); } /** * 功能:解码带CP56成组保护启动事件的值字段。 * 输入参数:payload——已校验完整长度的11字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:事件(1) + QDP(1) + CP16(2) + CP56(7);公开入口在成功后统一提交。 */ static int PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t *self) { if (payload[0] > 63U || (payload[1] & 0x07U)) { return 0; } self->event = payload[0]; self->quality = payload[1]; return IEC60870_Time_From_CP16(payload + 2, IEC60870_CP16_LEN, &self->elapsed_time) && IEC60870_Time_From_CP56(payload + 4, IEC60870_CP56_LEN, &self->time); } /** * 功能:将公共基类分派到PackedStartEventsOfProtectionEquipmentWithCP56Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Encode_Value((const IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable PackedStartEventsOfProtectionEquipmentWithCP56Time2a_VTable = { 39, 11, 0, sizeof(IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t), PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 39(带CP56成组保护启动事件)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Create(IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t *self, uint32_t ioa, int event, int quality, const IEC60870_CP16Time2a_t *elapsed_time, const IEC60870_CP56Time2a_t *time) { IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t result; uint8_t payload[11]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (event < 0 || event > 63) || (quality < 0 || quality > 255) || (elapsed_time == NULL) || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.event = (uint8_t)event; result.quality = (uint8_t)quality; result.elapsed_time = *elapsed_time; result.time = *time; Initialize_Object(&result.object, ioa, &PackedStartEventsOfProtectionEquipmentWithCP56Time2a_VTable); if (!PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 39(带CP56成组保护启动事件)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Get_From_Buffer(IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 11U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &PackedStartEventsOfProtectionEquipmentWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 40:带CP56保护输出电路信息 ---- */ /** * 功能:编码带CP56保护输出电路信息的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少11字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:事件(1) + QDP(1) + CP16(2) + CP56(7);失败只影响临时区。 */ static int PackedOutputCircuitInfoWithCP56Time2a_Encode_Value(const IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t *self, uint8_t *payload) { if (self->event > 15U || (self->quality & 0x07U)) { return 0; } payload[0] = self->event; payload[1] = self->quality; return IEC60870_Time_To_CP16(&self->elapsed_time, payload + 2, IEC60870_CP16_LEN) && IEC60870_Time_To_CP56(&self->time, payload + 4, IEC60870_CP56_LEN); } /** * 功能:解码带CP56保护输出电路信息的值字段。 * 输入参数:payload——已校验完整长度的11字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:事件(1) + QDP(1) + CP16(2) + CP56(7);公开入口在成功后统一提交。 */ static int PackedOutputCircuitInfoWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t *self) { if (payload[0] > 15U || (payload[1] & 0x07U)) { return 0; } self->event = payload[0]; self->quality = payload[1]; return IEC60870_Time_From_CP16(payload + 2, IEC60870_CP16_LEN, &self->elapsed_time) && IEC60870_Time_From_CP56(payload + 4, IEC60870_CP56_LEN, &self->time); } /** * 功能:将公共基类分派到PackedOutputCircuitInfoWithCP56Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int PackedOutputCircuitInfoWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return PackedOutputCircuitInfoWithCP56Time2a_Encode_Value((const IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable PackedOutputCircuitInfoWithCP56Time2a_VTable = { 40, 11, 0, sizeof(IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t), PackedOutputCircuitInfoWithCP56Time2a_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 40(带CP56保护输出电路信息)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_PackedOutputCircuitInfoWithCP56Time2a_Create(IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t *self, uint32_t ioa, int event, int quality, const IEC60870_CP16Time2a_t *elapsed_time, const IEC60870_CP56Time2a_t *time) { IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t result; uint8_t payload[11]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (event < 0 || event > 15) || (quality < 0 || quality > 255) || (elapsed_time == NULL) || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.event = (uint8_t)event; result.quality = (uint8_t)quality; result.elapsed_time = *elapsed_time; result.time = *time; Initialize_Object(&result.object, ioa, &PackedOutputCircuitInfoWithCP56Time2a_VTable); if (!PackedOutputCircuitInfoWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 40(带CP56保护输出电路信息)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_PackedOutputCircuitInfoWithCP56Time2a_Get_From_Buffer(IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 11U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!PackedOutputCircuitInfoWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &PackedOutputCircuitInfoWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 20:带变位检测的成组单点 ---- */ /** * 功能:编码带变位检测的成组单点的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少5字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:SCD(4:状态16位 + 变位16位) + QDS(1);失败只影响临时区。 */ static int PackedSinglePointWithSCD_Encode_Value(const IEC60870_PackedSinglePointWithSCD_t *self, uint8_t *payload) { if (self->quality & 0x0EU) { return 0; } IEC60870_Object_Write_LE(payload + 0, self->status, 2); IEC60870_Object_Write_LE(payload + 2, self->change, 2); payload[4] = self->quality; return 1; } /** * 功能:解码带变位检测的成组单点的值字段。 * 输入参数:payload——已校验完整长度的5字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:SCD(4:状态16位 + 变位16位) + QDS(1);公开入口在成功后统一提交。 */ static int PackedSinglePointWithSCD_Decode_Value(const uint8_t *payload, IEC60870_PackedSinglePointWithSCD_t *self) { if (payload[4] & 0x0EU) { return 0; } self->status = (uint16_t)IEC60870_Object_Read_LE(payload + 0, 2); self->change = (uint16_t)IEC60870_Object_Read_LE(payload + 2, 2); self->quality = payload[4]; return 1; } /** * 功能:将公共基类分派到PackedSinglePointWithSCD编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int PackedSinglePointWithSCD_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return PackedSinglePointWithSCD_Encode_Value((const IEC60870_PackedSinglePointWithSCD_t *)object, payload); } static const IEC60870_InformationObjectVTable PackedSinglePointWithSCD_VTable = { 20, 5, 0, sizeof(IEC60870_PackedSinglePointWithSCD_t), PackedSinglePointWithSCD_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 20(带变位检测的成组单点)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_PackedSinglePointWithSCD_Create(IEC60870_PackedSinglePointWithSCD_t *self, uint32_t ioa, uint16_t status, uint16_t change, int quality) { IEC60870_PackedSinglePointWithSCD_t result; uint8_t payload[5]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (quality < 0 || quality > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.status = status; result.change = change; result.quality = (uint8_t)quality; Initialize_Object(&result.object, ioa, &PackedSinglePointWithSCD_VTable); if (!PackedSinglePointWithSCD_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 20(带变位检测的成组单点)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_PackedSinglePointWithSCD_Get_From_Buffer(IEC60870_PackedSinglePointWithSCD_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_PackedSinglePointWithSCD_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 5U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!PackedSinglePointWithSCD_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &PackedSinglePointWithSCD_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 21:无品质归一化测量值 ---- */ /** * 功能:编码无品质归一化测量值的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少2字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NVA(2),不包含QDS;失败只影响临时区。 */ static int MeasuredValueNormalizedWithoutQuality_Encode_Value(const IEC60870_MeasuredValueNormalizedWithoutQuality_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, (uint16_t)self->value, 2); return 1; } /** * 功能:解码无品质归一化测量值的值字段。 * 输入参数:payload——已校验完整长度的2字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NVA(2),不包含QDS;公开入口在成功后统一提交。 */ static int MeasuredValueNormalizedWithoutQuality_Decode_Value(const uint8_t *payload, IEC60870_MeasuredValueNormalizedWithoutQuality_t *self) { self->value = Read_I16(payload); return 1; } /** * 功能:将公共基类分派到MeasuredValueNormalizedWithoutQuality编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int MeasuredValueNormalizedWithoutQuality_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return MeasuredValueNormalizedWithoutQuality_Encode_Value((const IEC60870_MeasuredValueNormalizedWithoutQuality_t *)object, payload); } static const IEC60870_InformationObjectVTable MeasuredValueNormalizedWithoutQuality_VTable = { 21, 2, 0, sizeof(IEC60870_MeasuredValueNormalizedWithoutQuality_t), MeasuredValueNormalizedWithoutQuality_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 21(无品质归一化测量值)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueNormalizedWithoutQuality_Create(IEC60870_MeasuredValueNormalizedWithoutQuality_t *self, uint32_t ioa, int16_t value) { IEC60870_MeasuredValueNormalizedWithoutQuality_t result; uint8_t payload[2]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; Initialize_Object(&result.object, ioa, &MeasuredValueNormalizedWithoutQuality_VTable); if (!MeasuredValueNormalizedWithoutQuality_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 21(无品质归一化测量值)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_MeasuredValueNormalizedWithoutQuality_Get_From_Buffer(IEC60870_MeasuredValueNormalizedWithoutQuality_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_MeasuredValueNormalizedWithoutQuality_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 2U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!MeasuredValueNormalizedWithoutQuality_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &MeasuredValueNormalizedWithoutQuality_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 51:32位比特串命令 ---- */ /** * 功能:编码32位比特串命令的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少4字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:BSI(4),不包含QDS;失败只影响临时区。 */ static int Bitstring32Command_Encode_Value(const IEC60870_Bitstring32Command_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload + 0, self->value, 4); return 1; } /** * 功能:解码32位比特串命令的值字段。 * 输入参数:payload——已校验完整长度的4字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:BSI(4),不包含QDS;公开入口在成功后统一提交。 */ static int Bitstring32Command_Decode_Value(const uint8_t *payload, IEC60870_Bitstring32Command_t *self) { self->value = (uint32_t)IEC60870_Object_Read_LE(payload + 0, 4); return 1; } /** * 功能:将公共基类分派到Bitstring32Command编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int Bitstring32Command_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return Bitstring32Command_Encode_Value((const IEC60870_Bitstring32Command_t *)object, payload); } static const IEC60870_InformationObjectVTable Bitstring32Command_VTable = { 51, 4, 1, sizeof(IEC60870_Bitstring32Command_t), Bitstring32Command_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 51(32位比特串命令)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_Bitstring32Command_Create(IEC60870_Bitstring32Command_t *self, uint32_t ioa, uint32_t value) { IEC60870_Bitstring32Command_t result; uint8_t payload[4]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; Initialize_Object(&result.object, ioa, &Bitstring32Command_VTable); if (!Bitstring32Command_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 51(32位比特串命令)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_Bitstring32Command_Get_From_Buffer(IEC60870_Bitstring32Command_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_Bitstring32Command_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 4U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!Bitstring32Command_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &Bitstring32Command_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 64:带CP56比特串命令 ---- */ /** * 功能:编码带CP56比特串命令的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少11字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:基础值(4) + CP56(7);失败只影响临时区。 */ static int Bitstring32CommandWithCP56Time2a_Encode_Value(const IEC60870_Bitstring32CommandWithCP56Time2a_t *self, uint8_t *payload) { return Bitstring32Command_Encode_Value(&self->base, payload) && IEC60870_Time_To_CP56(&self->time, payload + 4, IEC60870_CP56_LEN); } /** * 功能:解码带CP56比特串命令的值字段。 * 输入参数:payload——已校验完整长度的11字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:基础值(4) + CP56(7);公开入口在成功后统一提交。 */ static int Bitstring32CommandWithCP56Time2a_Decode_Value(const uint8_t *payload, IEC60870_Bitstring32CommandWithCP56Time2a_t *self) { return Bitstring32Command_Decode_Value(payload, &self->base) && IEC60870_Time_From_CP56(payload + 4, IEC60870_CP56_LEN, &self->time); } /** * 功能:将公共基类分派到Bitstring32CommandWithCP56Time2a编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int Bitstring32CommandWithCP56Time2a_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return Bitstring32CommandWithCP56Time2a_Encode_Value((const IEC60870_Bitstring32CommandWithCP56Time2a_t *)object, payload); } static const IEC60870_InformationObjectVTable Bitstring32CommandWithCP56Time2a_VTable = { 64, 11, 1, sizeof(IEC60870_Bitstring32CommandWithCP56Time2a_t), Bitstring32CommandWithCP56Time2a_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 64(带CP56比特串命令)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_Bitstring32CommandWithCP56Time2a_Create(IEC60870_Bitstring32CommandWithCP56Time2a_t *self, uint32_t ioa, uint32_t value, const IEC60870_CP56Time2a_t *time) { IEC60870_Bitstring32CommandWithCP56Time2a_t result; uint8_t payload[11]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (IEC60870_Bitstring32Command_Create(&result.base, ioa, value) != IEC60870_ASDU_OK) { return IEC60870_ASDU_ERROR; } result.time = *time; Initialize_Object(&result.base.object, ioa, &Bitstring32CommandWithCP56Time2a_VTable); if (!Bitstring32CommandWithCP56Time2a_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 64(带CP56比特串命令)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_Bitstring32CommandWithCP56Time2a_Get_From_Buffer(IEC60870_Bitstring32CommandWithCP56Time2a_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_Bitstring32CommandWithCP56Time2a_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 11U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!Bitstring32CommandWithCP56Time2a_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.base.object, ioa, &Bitstring32CommandWithCP56Time2a_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 70:初始化结束 ---- */ /** * 功能:编码初始化结束的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:COI(1),地址适用性由应用判断;失败只影响临时区。 */ static int EndOfInitialization_Encode_Value(const IEC60870_EndOfInitialization_t *self, uint8_t *payload) { payload[0] = self->cause; return 1; } /** * 功能:解码初始化结束的值字段。 * 输入参数:payload——已校验完整长度的1字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:COI(1),地址适用性由应用判断;公开入口在成功后统一提交。 */ static int EndOfInitialization_Decode_Value(const uint8_t *payload, IEC60870_EndOfInitialization_t *self) { self->cause = payload[0]; return 1; } /** * 功能:将公共基类分派到EndOfInitialization编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int EndOfInitialization_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return EndOfInitialization_Encode_Value((const IEC60870_EndOfInitialization_t *)object, payload); } static const IEC60870_InformationObjectVTable EndOfInitialization_VTable = { 70, 1, 1, sizeof(IEC60870_EndOfInitialization_t), EndOfInitialization_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 70(初始化结束)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_EndOfInitialization_Create(IEC60870_EndOfInitialization_t *self, uint32_t ioa, int cause) { IEC60870_EndOfInitialization_t result; uint8_t payload[1]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (cause < 0 || cause > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.cause = (uint8_t)cause; Initialize_Object(&result.object, ioa, &EndOfInitialization_VTable); if (!EndOfInitialization_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 70(初始化结束)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_EndOfInitialization_Get_From_Buffer(IEC60870_EndOfInitialization_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_EndOfInitialization_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 1U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!EndOfInitialization_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &EndOfInitialization_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 104:无时标测试命令 ---- */ /** * 功能:编码无时标测试命令的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少2字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:固定测试图样AA 55;失败只影响临时区。 */ static int TestCommand_Encode_Value(const IEC60870_TestCommand_t *self, uint8_t *payload) { (void)self; payload[0] = 0xAAU; payload[1] = 0x55U; return 1; } /** * 功能:解码无时标测试命令的值字段。 * 输入参数:payload——已校验完整长度的2字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:固定测试图样AA 55;公开入口在成功后统一提交。 */ static int TestCommand_Decode_Value(const uint8_t *payload, IEC60870_TestCommand_t *self) { (void)self; return payload[0] == 0xAAU && payload[1] == 0x55U; } /** * 功能:将公共基类分派到TestCommand编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int TestCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return TestCommand_Encode_Value((const IEC60870_TestCommand_t *)object, payload); } static const IEC60870_InformationObjectVTable TestCommand_VTable = { 104, 2, 1, sizeof(IEC60870_TestCommand_t), TestCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 104(无时标测试命令)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_TestCommand_Create(IEC60870_TestCommand_t *self, uint32_t ioa) { IEC60870_TestCommand_t result; uint8_t payload[2]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); Initialize_Object(&result.object, ioa, &TestCommand_VTable); if (!TestCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 104(无时标测试命令)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_TestCommand_Get_From_Buffer(IEC60870_TestCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_TestCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 2U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!TestCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &TestCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 106:延时获得命令 ---- */ /** * 功能:编码延时获得命令的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少2字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:CP16(2);失败只影响临时区。 */ static int DelayAcquisitionCommand_Encode_Value(const IEC60870_DelayAcquisitionCommand_t *self, uint8_t *payload) { return IEC60870_Time_To_CP16(&self->delay, payload, IEC60870_CP16_LEN); } /** * 功能:解码延时获得命令的值字段。 * 输入参数:payload——已校验完整长度的2字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:CP16(2);公开入口在成功后统一提交。 */ static int DelayAcquisitionCommand_Decode_Value(const uint8_t *payload, IEC60870_DelayAcquisitionCommand_t *self) { return IEC60870_Time_From_CP16(payload, IEC60870_CP16_LEN, &self->delay); } /** * 功能:将公共基类分派到DelayAcquisitionCommand编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int DelayAcquisitionCommand_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return DelayAcquisitionCommand_Encode_Value((const IEC60870_DelayAcquisitionCommand_t *)object, payload); } static const IEC60870_InformationObjectVTable DelayAcquisitionCommand_VTable = { 106, 2, 1, sizeof(IEC60870_DelayAcquisitionCommand_t), DelayAcquisitionCommand_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 106(延时获得命令)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_DelayAcquisitionCommand_Create(IEC60870_DelayAcquisitionCommand_t *self, uint32_t ioa, const IEC60870_CP16Time2a_t *delay) { IEC60870_DelayAcquisitionCommand_t result; uint8_t payload[2]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (delay == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.delay = *delay; Initialize_Object(&result.object, ioa, &DelayAcquisitionCommand_VTable); if (!DelayAcquisitionCommand_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 106(延时获得命令)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_DelayAcquisitionCommand_Get_From_Buffer(IEC60870_DelayAcquisitionCommand_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_DelayAcquisitionCommand_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 2U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!DelayAcquisitionCommand_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &DelayAcquisitionCommand_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 110:归一化测量值参数 ---- */ /** * 功能:编码归一化测量值参数的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少3字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:参数值(2) + QPM(1),QPM不是QDS;失败只影响临时区。 */ static int ParameterNormalizedValue_Encode_Value(const IEC60870_ParameterNormalizedValue_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, (uint16_t)self->value, 2); payload[2] = self->qualifier; return 1; } /** * 功能:解码归一化测量值参数的值字段。 * 输入参数:payload——已校验完整长度的3字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:参数值(2) + QPM(1),QPM不是QDS;公开入口在成功后统一提交。 */ static int ParameterNormalizedValue_Decode_Value(const uint8_t *payload, IEC60870_ParameterNormalizedValue_t *self) { self->value = Read_I16(payload); self->qualifier = payload[2]; return 1; } /** * 功能:将公共基类分派到ParameterNormalizedValue编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int ParameterNormalizedValue_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return ParameterNormalizedValue_Encode_Value((const IEC60870_ParameterNormalizedValue_t *)object, payload); } static const IEC60870_InformationObjectVTable ParameterNormalizedValue_VTable = { 110, 3, 0, sizeof(IEC60870_ParameterNormalizedValue_t), ParameterNormalizedValue_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 110(归一化测量值参数)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_ParameterNormalizedValue_Create(IEC60870_ParameterNormalizedValue_t *self, uint32_t ioa, int16_t value, int qualifier) { IEC60870_ParameterNormalizedValue_t result; uint8_t payload[3]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (qualifier < 0 || qualifier > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &ParameterNormalizedValue_VTable); if (!ParameterNormalizedValue_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 110(归一化测量值参数)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_ParameterNormalizedValue_Get_From_Buffer(IEC60870_ParameterNormalizedValue_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_ParameterNormalizedValue_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 3U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!ParameterNormalizedValue_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &ParameterNormalizedValue_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 111:标度化测量值参数 ---- */ /** * 功能:编码标度化测量值参数的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少3字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:参数值(2) + QPM(1),QPM不是QDS;失败只影响临时区。 */ static int ParameterScaledValue_Encode_Value(const IEC60870_ParameterScaledValue_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, (uint16_t)self->value, 2); payload[2] = self->qualifier; return 1; } /** * 功能:解码标度化测量值参数的值字段。 * 输入参数:payload——已校验完整长度的3字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:参数值(2) + QPM(1),QPM不是QDS;公开入口在成功后统一提交。 */ static int ParameterScaledValue_Decode_Value(const uint8_t *payload, IEC60870_ParameterScaledValue_t *self) { self->value = Read_I16(payload); self->qualifier = payload[2]; return 1; } /** * 功能:将公共基类分派到ParameterScaledValue编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int ParameterScaledValue_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return ParameterScaledValue_Encode_Value((const IEC60870_ParameterScaledValue_t *)object, payload); } static const IEC60870_InformationObjectVTable ParameterScaledValue_VTable = { 111, 3, 0, sizeof(IEC60870_ParameterScaledValue_t), ParameterScaledValue_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 111(标度化测量值参数)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_ParameterScaledValue_Create(IEC60870_ParameterScaledValue_t *self, uint32_t ioa, int16_t value, int qualifier) { IEC60870_ParameterScaledValue_t result; uint8_t payload[3]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (qualifier < 0 || qualifier > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &ParameterScaledValue_VTable); if (!ParameterScaledValue_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 111(标度化测量值参数)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_ParameterScaledValue_Get_From_Buffer(IEC60870_ParameterScaledValue_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_ParameterScaledValue_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 3U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!ParameterScaledValue_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &ParameterScaledValue_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 112:短浮点测量值参数 ---- */ /** * 功能:编码短浮点测量值参数的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少5字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:参数值(4) + QPM(1),QPM不是QDS;失败只影响临时区。 */ static int ParameterFloatValue_Encode_Value(const IEC60870_ParameterFloatValue_t *self, uint8_t *payload) { Write_Float(payload, &self->value); payload[4] = self->qualifier; return 1; } /** * 功能:解码短浮点测量值参数的值字段。 * 输入参数:payload——已校验完整长度的5字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:参数值(4) + QPM(1),QPM不是QDS;公开入口在成功后统一提交。 */ static int ParameterFloatValue_Decode_Value(const uint8_t *payload, IEC60870_ParameterFloatValue_t *self) { Read_Float(payload, &self->value); self->qualifier = payload[4]; return 1; } /** * 功能:将公共基类分派到ParameterFloatValue编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int ParameterFloatValue_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return ParameterFloatValue_Encode_Value((const IEC60870_ParameterFloatValue_t *)object, payload); } static const IEC60870_InformationObjectVTable ParameterFloatValue_VTable = { 112, 5, 0, sizeof(IEC60870_ParameterFloatValue_t), ParameterFloatValue_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 112(短浮点测量值参数)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_ParameterFloatValue_Create(IEC60870_ParameterFloatValue_t *self, uint32_t ioa, float value, int qualifier) { IEC60870_ParameterFloatValue_t result; uint8_t payload[5]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (qualifier < 0 || qualifier > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.value = value; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &ParameterFloatValue_VTable); if (!ParameterFloatValue_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 112(短浮点测量值参数)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_ParameterFloatValue_Get_From_Buffer(IEC60870_ParameterFloatValue_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_ParameterFloatValue_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 5U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!ParameterFloatValue_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &ParameterFloatValue_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 113:参数激活 ---- */ /** * 功能:编码参数激活的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少1字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:QPA(1);失败只影响临时区。 */ static int ParameterActivation_Encode_Value(const IEC60870_ParameterActivation_t *self, uint8_t *payload) { payload[0] = self->qualifier; return 1; } /** * 功能:解码参数激活的值字段。 * 输入参数:payload——已校验完整长度的1字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:QPA(1);公开入口在成功后统一提交。 */ static int ParameterActivation_Decode_Value(const uint8_t *payload, IEC60870_ParameterActivation_t *self) { self->qualifier = payload[0]; return 1; } /** * 功能:将公共基类分派到ParameterActivation编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int ParameterActivation_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return ParameterActivation_Encode_Value((const IEC60870_ParameterActivation_t *)object, payload); } static const IEC60870_InformationObjectVTable ParameterActivation_VTable = { 113, 1, 0, sizeof(IEC60870_ParameterActivation_t), ParameterActivation_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 113(参数激活)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_ParameterActivation_Create(IEC60870_ParameterActivation_t *self, uint32_t ioa, int qualifier) { IEC60870_ParameterActivation_t result; uint8_t payload[1]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (qualifier < 0 || qualifier > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &ParameterActivation_VTable); if (!ParameterActivation_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 113(参数激活)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_ParameterActivation_Get_From_Buffer(IEC60870_ParameterActivation_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_ParameterActivation_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 1U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!ParameterActivation_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &ParameterActivation_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 120:文件准备就绪 ---- */ /** * 功能:编码文件准备就绪的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少6字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NOF(2) + LOF(3) + FRQ(1);失败只影响临时区。 */ static int FileReady_Encode_Value(const IEC60870_FileReady_t *self, uint8_t *payload) { if (self->file_length > UINT32_C(0xFFFFFF) || (self->qualifier & 0x7FU)) { return 0; } IEC60870_Object_Write_LE(payload, self->file_name, 2); IEC60870_Object_Write_LE(payload + 2, self->file_length, 3); payload[5] = self->qualifier; return 1; } /** * 功能:解码文件准备就绪的值字段。 * 输入参数:payload——已校验完整长度的6字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NOF(2) + LOF(3) + FRQ(1);公开入口在成功后统一提交。 */ static int FileReady_Decode_Value(const uint8_t *payload, IEC60870_FileReady_t *self) { if (payload[5] & 0x7FU) { return 0; } self->file_name = (uint16_t)IEC60870_Object_Read_LE(payload, 2); self->file_length = IEC60870_Object_Read_LE(payload + 2, 3); self->qualifier = payload[5]; return 1; } /** * 功能:将公共基类分派到FileReady编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int FileReady_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return FileReady_Encode_Value((const IEC60870_FileReady_t *)object, payload); } static const IEC60870_InformationObjectVTable FileReady_VTable = { 120, 6, 1, sizeof(IEC60870_FileReady_t), FileReady_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 120(文件准备就绪)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_FileReady_Create(IEC60870_FileReady_t *self, uint32_t ioa, uint16_t file_name, uint32_t file_length, int qualifier) { IEC60870_FileReady_t result; uint8_t payload[6]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (file_length > UINT32_C(0xFFFFFF)) || (qualifier < 0 || qualifier > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.file_name = file_name; result.file_length = file_length; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &FileReady_VTable); if (!FileReady_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 120(文件准备就绪)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_FileReady_Get_From_Buffer(IEC60870_FileReady_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_FileReady_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 6U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!FileReady_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &FileReady_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 121:节准备就绪 ---- */ /** * 功能:编码节准备就绪的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少7字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NOF(2) + NOS(1) + LOS(3) + SRQ(1);失败只影响临时区。 */ static int SectionReady_Encode_Value(const IEC60870_SectionReady_t *self, uint8_t *payload) { if (self->section_length > UINT32_C(0xFFFFFF) || (self->qualifier & 0x7FU)) { return 0; } IEC60870_Object_Write_LE(payload, self->file_name, 2); payload[2] = self->section; IEC60870_Object_Write_LE(payload + 3, self->section_length, 3); payload[6] = self->qualifier; return 1; } /** * 功能:解码节准备就绪的值字段。 * 输入参数:payload——已校验完整长度的7字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NOF(2) + NOS(1) + LOS(3) + SRQ(1);公开入口在成功后统一提交。 */ static int SectionReady_Decode_Value(const uint8_t *payload, IEC60870_SectionReady_t *self) { if (payload[6] & 0x7FU) { return 0; } self->file_name = (uint16_t)IEC60870_Object_Read_LE(payload, 2); self->section = payload[2]; self->section_length = IEC60870_Object_Read_LE(payload + 3, 3); self->qualifier = payload[6]; return 1; } /** * 功能:将公共基类分派到SectionReady编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int SectionReady_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return SectionReady_Encode_Value((const IEC60870_SectionReady_t *)object, payload); } static const IEC60870_InformationObjectVTable SectionReady_VTable = { 121, 7, 1, sizeof(IEC60870_SectionReady_t), SectionReady_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 121(节准备就绪)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_SectionReady_Create(IEC60870_SectionReady_t *self, uint32_t ioa, uint16_t file_name, int section, uint32_t section_length, int qualifier) { IEC60870_SectionReady_t result; uint8_t payload[7]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (section < 0 || section > 255) || (section_length > UINT32_C(0xFFFFFF)) || (qualifier < 0 || qualifier > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.file_name = file_name; result.section = (uint8_t)section; result.section_length = section_length; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &SectionReady_VTable); if (!SectionReady_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 121(节准备就绪)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_SectionReady_Get_From_Buffer(IEC60870_SectionReady_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_SectionReady_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 7U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!SectionReady_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &SectionReady_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 122:文件/节召唤或选择 ---- */ /** * 功能:编码文件/节召唤或选择的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少4字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NOF(2) + NOS(1) + SCQ(1);失败只影响临时区。 */ static int FileCallOrSelect_Encode_Value(const IEC60870_FileCallOrSelect_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, self->file_name, 2); payload[2] = self->section; payload[3] = self->qualifier; return 1; } /** * 功能:解码文件/节召唤或选择的值字段。 * 输入参数:payload——已校验完整长度的4字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NOF(2) + NOS(1) + SCQ(1);公开入口在成功后统一提交。 */ static int FileCallOrSelect_Decode_Value(const uint8_t *payload, IEC60870_FileCallOrSelect_t *self) { self->file_name = (uint16_t)IEC60870_Object_Read_LE(payload, 2); self->section = payload[2]; self->qualifier = payload[3]; return 1; } /** * 功能:将公共基类分派到FileCallOrSelect编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int FileCallOrSelect_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return FileCallOrSelect_Encode_Value((const IEC60870_FileCallOrSelect_t *)object, payload); } static const IEC60870_InformationObjectVTable FileCallOrSelect_VTable = { 122, 4, 1, sizeof(IEC60870_FileCallOrSelect_t), FileCallOrSelect_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 122(文件/节召唤或选择)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_FileCallOrSelect_Create(IEC60870_FileCallOrSelect_t *self, uint32_t ioa, uint16_t file_name, int section, int qualifier) { IEC60870_FileCallOrSelect_t result; uint8_t payload[4]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (section < 0 || section > 255) || (qualifier < 0 || qualifier > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.file_name = file_name; result.section = (uint8_t)section; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &FileCallOrSelect_VTable); if (!FileCallOrSelect_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 122(文件/节召唤或选择)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_FileCallOrSelect_Get_From_Buffer(IEC60870_FileCallOrSelect_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_FileCallOrSelect_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 4U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!FileCallOrSelect_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &FileCallOrSelect_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 124:文件/节确认 ---- */ /** * 功能:编码文件/节确认的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少4字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NOF(2) + NOS(1) + AFQ(1);失败只影响临时区。 */ static int FileACK_Encode_Value(const IEC60870_FileACK_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, self->file_name, 2); payload[2] = self->section; payload[3] = self->qualifier; return 1; } /** * 功能:解码文件/节确认的值字段。 * 输入参数:payload——已校验完整长度的4字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NOF(2) + NOS(1) + AFQ(1);公开入口在成功后统一提交。 */ static int FileACK_Decode_Value(const uint8_t *payload, IEC60870_FileACK_t *self) { self->file_name = (uint16_t)IEC60870_Object_Read_LE(payload, 2); self->section = payload[2]; self->qualifier = payload[3]; return 1; } /** * 功能:将公共基类分派到FileACK编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int FileACK_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return FileACK_Encode_Value((const IEC60870_FileACK_t *)object, payload); } static const IEC60870_InformationObjectVTable FileACK_VTable = { 124, 4, 1, sizeof(IEC60870_FileACK_t), FileACK_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 124(文件/节确认)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_FileACK_Create(IEC60870_FileACK_t *self, uint32_t ioa, uint16_t file_name, int section, int qualifier) { IEC60870_FileACK_t result; uint8_t payload[4]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (section < 0 || section > 255) || (qualifier < 0 || qualifier > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.file_name = file_name; result.section = (uint8_t)section; result.qualifier = (uint8_t)qualifier; Initialize_Object(&result.object, ioa, &FileACK_VTable); if (!FileACK_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 124(文件/节确认)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_FileACK_Get_From_Buffer(IEC60870_FileACK_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_FileACK_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 4U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!FileACK_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &FileACK_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 123:最后文件段或节 ---- */ /** * 功能:编码最后文件段或节的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少5字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NOF(2) + NOS(1) + LSQ(1) + CHS(1),这里只传递校验和;失败只影响临时区。 */ static int FileLastSegmentOrSection_Encode_Value(const IEC60870_FileLastSegmentOrSection_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, self->file_name, 2); payload[2] = self->section; payload[3] = self->qualifier; payload[4] = self->checksum; return 1; } /** * 功能:解码最后文件段或节的值字段。 * 输入参数:payload——已校验完整长度的5字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NOF(2) + NOS(1) + LSQ(1) + CHS(1),这里只传递校验和;公开入口在成功后统一提交。 */ static int FileLastSegmentOrSection_Decode_Value(const uint8_t *payload, IEC60870_FileLastSegmentOrSection_t *self) { self->file_name = (uint16_t)IEC60870_Object_Read_LE(payload, 2); self->section = payload[2]; self->qualifier = payload[3]; self->checksum = payload[4]; return 1; } /** * 功能:将公共基类分派到FileLastSegmentOrSection编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int FileLastSegmentOrSection_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return FileLastSegmentOrSection_Encode_Value((const IEC60870_FileLastSegmentOrSection_t *)object, payload); } static const IEC60870_InformationObjectVTable FileLastSegmentOrSection_VTable = { 123, 5, 1, sizeof(IEC60870_FileLastSegmentOrSection_t), FileLastSegmentOrSection_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 123(最后文件段或节)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_FileLastSegmentOrSection_Create(IEC60870_FileLastSegmentOrSection_t *self, uint32_t ioa, uint16_t file_name, int section, int qualifier, int checksum) { IEC60870_FileLastSegmentOrSection_t result; uint8_t payload[5]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (section < 0 || section > 255) || (qualifier < 0 || qualifier > 255) || (checksum < 0 || checksum > 255)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.file_name = file_name; result.section = (uint8_t)section; result.qualifier = (uint8_t)qualifier; result.checksum = (uint8_t)checksum; Initialize_Object(&result.object, ioa, &FileLastSegmentOrSection_VTable); if (!FileLastSegmentOrSection_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 123(最后文件段或节)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_FileLastSegmentOrSection_Get_From_Buffer(IEC60870_FileLastSegmentOrSection_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_FileLastSegmentOrSection_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 5U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!FileLastSegmentOrSection_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &FileLastSegmentOrSection_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 125:文件段 ---- */ /** * 功能:编码文件段的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少4+LOS字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NOF(2) + NOS(1) + LOS(1) + 段数据(LOS),SQ=0且仅一个对象;失败只影响临时区。 */ static int FileSegment_Encode_Value(const IEC60870_FileSegment_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, self->file_name, 2); payload[2] = self->section; payload[3] = self->length; memcpy(payload + 4, self->data, self->length); return 1; } /** * 功能:解码文件段的值字段。 * 输入参数:payload——已校验完整长度的变长载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NOF(2) + NOS(1) + LOS(1) + 段数据(LOS),SQ=0且仅一个对象;公开入口在成功后统一提交。 */ static int FileSegment_Decode_Value(const uint8_t *payload, IEC60870_FileSegment_t *self) { self->file_name = (uint16_t)IEC60870_Object_Read_LE(payload, 2); self->section = payload[2]; self->length = payload[3]; memcpy(self->data, payload + 4, self->length); return 1; } /** * 功能:将公共基类分派到FileSegment编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int FileSegment_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return FileSegment_Encode_Value((const IEC60870_FileSegment_t *)object, payload); } /** * 功能:取得文件段的实际值载荷长度。 * 输入参数:object——已核对身份的文件段基类。 * 输出参数:无。 * 返回值:4+LOS,最大259字节。 * 说明:只计算线上长度;不包含IOA或ASDU公共头。 */ static uint16_t FileSegment_Payload_Size(const IEC60870_InformationObject_t *object) { const IEC60870_FileSegment_t *self = (const IEC60870_FileSegment_t *)object; return (uint16_t)(4U + self->length); } /** * 功能:从文件段原文安全取得载荷长度。 * 输入参数:payload——值载荷;length——实际可读字节数。 * 输出参数:无。 * 返回值:完整段返回4+LOS,截短或空指针返回0。 * 说明:读取LOS前要求至少4字节;精确尾长由ASDU层检查。 */ static uint16_t FileSegment_Payload_Size_From_Buffer(const uint8_t *payload, uint16_t length) { uint16_t required; if (payload == NULL || length < 4U) { return 0; } required = (uint16_t)(4U + payload[3]); return required <= length ? required : 0U; } static const IEC60870_InformationObjectVTable FileSegment_VTable = { 125, 4, 1, sizeof(IEC60870_FileSegment_t), FileSegment_Encode, 0, FileSegment_Payload_Size, FileSegment_Payload_Size_From_Buffer }; /** * 功能:在调用方存储中构造Type 125(文件段)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_FileSegment_Create(IEC60870_FileSegment_t *self, uint32_t ioa, uint16_t file_name, int section, const uint8_t *data, uint16_t data_length) { IEC60870_FileSegment_t result; uint8_t payload[IEC60870_OBJECT_MAX_PAYLOAD]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (section < 0 || section > 255) || (data_length > 255U) || ((data_length != 0U && data == NULL))) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.file_name = file_name; result.section = (uint8_t)section; result.length = (uint8_t)data_length; if (data_length != 0U) { memcpy(result.data, data, data_length); } Initialize_Object(&result.object, ioa, &FileSegment_VTable); if (!FileSegment_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 125(文件段)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:先验证LOS与载荷长度,再复制数据;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_FileSegment_Get_From_Buffer(IEC60870_FileSegment_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_FileSegment_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length < 4U || length != (uint16_t)(4U + payload[3])) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!FileSegment_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &FileSegment_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 126:文件目录 ---- */ /** * 功能:编码文件目录的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少13字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NOF(2) + LOF(3) + SOF(1) + CP56(7);失败只影响临时区。 */ static int FileDirectory_Encode_Value(const IEC60870_FileDirectory_t *self, uint8_t *payload) { if (self->file_length > UINT32_C(0xFFFFFF)) { return 0; } IEC60870_Object_Write_LE(payload, self->file_name, 2); IEC60870_Object_Write_LE(payload + 2, self->file_length, 3); payload[5] = self->status; return IEC60870_Time_To_CP56(&self->creation_time, payload + 6, IEC60870_CP56_LEN); } /** * 功能:解码文件目录的值字段。 * 输入参数:payload——已校验完整长度的13字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NOF(2) + LOF(3) + SOF(1) + CP56(7);公开入口在成功后统一提交。 */ static int FileDirectory_Decode_Value(const uint8_t *payload, IEC60870_FileDirectory_t *self) { self->file_name = (uint16_t)IEC60870_Object_Read_LE(payload, 2); self->file_length = IEC60870_Object_Read_LE(payload + 2, 3); self->status = payload[5]; return IEC60870_Time_From_CP56(payload + 6, IEC60870_CP56_LEN, &self->creation_time); } /** * 功能:将公共基类分派到FileDirectory编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int FileDirectory_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return FileDirectory_Encode_Value((const IEC60870_FileDirectory_t *)object, payload); } static const IEC60870_InformationObjectVTable FileDirectory_VTable = { 126, 13, 0, sizeof(IEC60870_FileDirectory_t), FileDirectory_Encode, 1, NULL, NULL }; /** * 功能:在调用方存储中构造Type 126(文件目录)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_FileDirectory_Create(IEC60870_FileDirectory_t *self, uint32_t ioa, uint16_t file_name, uint32_t file_length, int status, const IEC60870_CP56Time2a_t *creation_time) { IEC60870_FileDirectory_t result; uint8_t payload[13]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (file_length > UINT32_C(0xFFFFFF)) || (status < 0 || status > 255) || (creation_time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.file_name = file_name; result.file_length = file_length; result.status = (uint8_t)status; result.creation_time = *creation_time; Initialize_Object(&result.object, ioa, &FileDirectory_VTable); if (!FileDirectory_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 126(文件目录)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_FileDirectory_Get_From_Buffer(IEC60870_FileDirectory_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_FileDirectory_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 13U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!FileDirectory_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &FileDirectory_VTable); *self = result; return IEC60870_ASDU_OK; } /* ---- Type 127:归档查询 ---- */ /** * 功能:编码归档查询的值字段。 * 输入参数:self——具体对象,已核对存储类型。 * 输出参数:payload——至少16字节的临时区。 * 返回值:1合法;0字段或时间非法。 * 说明:NOF(2) + 查询起始CP56(7) + 查询结束CP56(7);失败只影响临时区。 */ static int QueryLog_Encode_Value(const IEC60870_QueryLog_t *self, uint8_t *payload) { IEC60870_Object_Write_LE(payload, self->file_name, 2); return IEC60870_Time_To_CP56(&self->start_time, payload + 2, IEC60870_CP56_LEN) && IEC60870_Time_To_CP56(&self->stop_time, payload + 9, IEC60870_CP56_LEN); } /** * 功能:解码归档查询的值字段。 * 输入参数:payload——已校验完整长度的16字节载荷。 * 输出参数:self——已清零的局部对象,不填写基类身份。 * 返回值:1合法;0保留位或时间非法。 * 说明:NOF(2) + 查询起始CP56(7) + 查询结束CP56(7);公开入口在成功后统一提交。 */ static int QueryLog_Decode_Value(const uint8_t *payload, IEC60870_QueryLog_t *self) { self->file_name = (uint16_t)IEC60870_Object_Read_LE(payload, 2); return IEC60870_Time_From_CP56(payload + 2, IEC60870_CP56_LEN, &self->start_time) && IEC60870_Time_From_CP56(payload + 9, IEC60870_CP56_LEN, &self->stop_time); } /** * 功能:将公共基类分派到QueryLog编码。 * 输入参数:object——已核对类型和函数表的具体对象首成员。 * 输出参数:payload——足够大的临时载荷区。 * 返回值:1成功;0字段非法。 * 说明:类型转换依赖首成员布局,禁止伪造公共基类。 */ static int QueryLog_Encode(const IEC60870_InformationObject_t *object, uint8_t *payload) { return QueryLog_Encode_Value((const IEC60870_QueryLog_t *)object, payload); } static const IEC60870_InformationObjectVTable QueryLog_VTable = { 127, 16, 1, sizeof(IEC60870_QueryLog_t), QueryLog_Encode, 0, NULL, NULL }; /** * 功能:在调用方存储中构造Type 127(归档查询)。 * 输入参数:ioa——24位地址;其余值的范围见公开结构及声明注释。 * 输出参数:self——成功后整体保存对象,失败不变。 * 返回值:OK成功;ERROR参数、字段或时间非法。 * 说明:局部对象验证通过后提交,不分配内存;ASDU容量另行检查。 */ IEC60870_AsduResult_t IEC60870_QueryLog_Create(IEC60870_QueryLog_t *self, uint32_t ioa, uint16_t file_name, const IEC60870_CP56Time2a_t *start_time, const IEC60870_CP56Time2a_t *stop_time) { IEC60870_QueryLog_t result; uint8_t payload[16]; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || (start_time == NULL) || (stop_time == NULL)) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); result.file_name = file_name; result.start_time = *start_time; result.stop_time = *stop_time; Initialize_Object(&result.object, ioa, &QueryLog_VTable); if (!QueryLog_Encode_Value(&result, payload)) { return IEC60870_ASDU_ERROR; } *self = result; return IEC60870_ASDU_OK; } /** * 功能:解析Type 127(归档查询)。 * 输入参数:ioa——已还原地址;payload——值载荷;length——精确字节数。 * 输出参数:self——成功时整体写入,失败不变。 * 返回值:OK成功;ERROR参数、长度、保留位或时间非法。 * 说明:逐字段恢复数值并初始化基类;结果不借用输入。 */ IEC60870_AsduResult_t IEC60870_QueryLog_Get_From_Buffer(IEC60870_QueryLog_t *self, uint32_t ioa, const uint8_t *payload, uint16_t length) { IEC60870_QueryLog_t result; if (self == NULL || ioa > IEC60870_ASDU_MAX_IOA || payload == NULL || length != 16U) { return IEC60870_ASDU_ERROR; } memset(&result, 0, sizeof(result)); if (!QueryLog_Decode_Value(payload, &result)) { return IEC60870_ASDU_ERROR; } Initialize_Object(&result.object, ioa, &QueryLog_VTable); *self = result; return IEC60870_ASDU_OK; } /** * 功能:取得当前配置下一条ASDU能容纳的最大文件段数据长度。 * 输入参数:parameters——合法应用层参数,包含最大ASDU长度和字段宽度。 * 输出参数:无。 * 返回值:0..255为数据容量;-1表示参数非法或连空文件段也容纳不下。 * 说明:扣除公共头、IOA和4字节段头;104默认配置返回236。 */ int IEC60870_FileSegment_Get_Max_Data_Size(const IEC60870_AppLayerParameters_t *parameters) { int size; if (!IEC60870_AppLayerParameters_Valid(parameters)) { return -1; } size = parameters->maxSizeOfASDU - (2 + parameters->sizeOfCOT + parameters->sizeOfCA) - parameters->sizeOfIOA - 4; if (size < 0) { return -1; } return size > 255 ? 255 : size; } /** * 功能:根据Type ID取得内部只读类型描述。 * 输入参数:type_id——报文或已初始化对象中的类型标识。 * 输出参数:无。 * 返回值:支持时返回静态描述表指针,未知类型返回NULL。 * 说明:表中保存载荷长度、对象存储尺寸、单对象限制和编码函数;不解析值字段。 */ const IEC60870_InformationObjectVTable *IEC60870_InformationObject_Get_Descriptor(uint8_t type_id) { switch (type_id) { case 1: return &SinglePointInformation_VTable; case 3: return &DoublePointInformation_VTable; case 9: return &MeasuredValueNormalized_VTable; case 11: return &MeasuredValueScaled_VTable; case 13: return &MeasuredValueShort_VTable; case 15: return &IntegratedTotals_VTable; case 30: return &SinglePointWithCP56Time2a_VTable; case 31: return &DoublePointWithCP56Time2a_VTable; case 34: return &MeasuredValueNormalizedWithCP56Time2a_VTable; case 35: return &MeasuredValueScaledWithCP56Time2a_VTable; case 36: return &MeasuredValueShortWithCP56Time2a_VTable; case 37: return &IntegratedTotalsWithCP56Time2a_VTable; case 45: return &SingleCommand_VTable; case 46: return &DoubleCommand_VTable; case 47: return &StepCommand_VTable; case 48: return &SetpointCommandNormalized_VTable; case 49: return &SetpointCommandScaled_VTable; case 50: return &SetpointCommandShort_VTable; case 58: return &SingleCommandWithCP56Time2a_VTable; case 59: return &DoubleCommandWithCP56Time2a_VTable; case 60: return &StepCommandWithCP56Time2a_VTable; case 61: return &SetpointCommandNormalizedWithCP56Time2a_VTable; case 62: return &SetpointCommandScaledWithCP56Time2a_VTable; case 63: return &SetpointCommandShortWithCP56Time2a_VTable; case 100: return &InterrogationCommand_VTable; case 101: return &CounterInterrogationCommand_VTable; case 102: return &ReadCommand_VTable; case 103: return &ClockSynchronizationCommand_VTable; case 105: return &ResetProcessCommand_VTable; case 107: return &TestCommandWithCP56Time2a_VTable; case 5: return &StepPositionInformation_VTable; case 7: return &BitString32_VTable; case 2: return &SinglePointWithCP24Time2a_VTable; case 4: return &DoublePointWithCP24Time2a_VTable; case 6: return &StepPositionWithCP24Time2a_VTable; case 8: return &Bitstring32WithCP24Time2a_VTable; case 10: return &MeasuredValueNormalizedWithCP24Time2a_VTable; case 12: return &MeasuredValueScaledWithCP24Time2a_VTable; case 14: return &MeasuredValueShortWithCP24Time2a_VTable; case 16: return &IntegratedTotalsWithCP24Time2a_VTable; case 32: return &StepPositionWithCP56Time2a_VTable; case 33: return &Bitstring32WithCP56Time2a_VTable; case 17: return &EventOfProtectionEquipment_VTable; case 18: return &PackedStartEventsOfProtectionEquipment_VTable; case 19: return &PackedOutputCircuitInfo_VTable; case 38: return &EventOfProtectionEquipmentWithCP56Time2a_VTable; case 39: return &PackedStartEventsOfProtectionEquipmentWithCP56Time2a_VTable; case 40: return &PackedOutputCircuitInfoWithCP56Time2a_VTable; case 20: return &PackedSinglePointWithSCD_VTable; case 21: return &MeasuredValueNormalizedWithoutQuality_VTable; case 51: return &Bitstring32Command_VTable; case 64: return &Bitstring32CommandWithCP56Time2a_VTable; case 70: return &EndOfInitialization_VTable; case 104: return &TestCommand_VTable; case 106: return &DelayAcquisitionCommand_VTable; case 110: return &ParameterNormalizedValue_VTable; case 111: return &ParameterScaledValue_VTable; case 112: return &ParameterFloatValue_VTable; case 113: return &ParameterActivation_VTable; case 120: return &FileReady_VTable; case 121: return &SectionReady_VTable; case 122: return &FileCallOrSelect_VTable; case 124: return &FileACK_VTable; case 123: return &FileLastSegmentOrSection_VTable; case 125: return &FileSegment_VTable; case 126: return &FileDirectory_VTable; case 127: return &QueryLog_VTable; default: return NULL; } } /** * 功能:读取已初始化对象的类型,并核对其编码函数表身份。 * 输入参数:object——已由具体Create或Get_From_Buffer初始化的基类指针,可为NULL。 * 输出参数:无。 * 返回值:身份匹配时返回Type ID;NULL、未知类型或函数表不匹配时返回0。 * 说明:不检查IOA、数值、品质或时标;这些字段在实际编码时验证。 */ uint8_t IEC60870_InformationObject_Get_Type(const IEC60870_InformationObject_t *object) { const IEC60870_InformationObjectVTable *type; if (object == NULL) { return 0; } type = IEC60870_InformationObject_Get_Descriptor(object->type_id); return type != NULL && object->virtual_function_table == type ? type->type_id : 0; } /** * 功能:通过具体对象的函数表编码一个对象,可按SQ需要省略IOA。 * 输入参数:object——已初始化的非空具体对象基类,不能手工伪造类型或函数表。 * parameters——非空且合法的应用层参数,本函数使用其IOA宽度。 * is_sequence——0写入IOA;1仅写值载荷,适用于监视对象和文件目录。 * capacity——buffer实际可写容量,单位字节。 * 输出参数:buffer——调用方提供的对象字节缓冲区,不含ASDU头;不得与对象重叠。 * length——非空输出指针,成功时填写实际写入的IOA加载荷字节数。 * 返回值:OK为成功,FULL为空间不足,UNSUPPORTED为未知类型,ERROR为参数或字段非法。 * 说明:先核对身份并在临时区完成值编码,再检查容量并提交,失败保留buffer和length。 * SQ首对象仍须传0;地址连续性、对象数量和整条ASDU的上限由容器层维护。 */ IEC60870_AsduResult_t IEC60870_InformationObject_Encode( const IEC60870_InformationObject_t *object, const IEC60870_AppLayerParameters_t *parameters, int is_sequence, uint8_t *buffer, uint16_t capacity, uint16_t *length) { const IEC60870_InformationObjectVTable *type; uint8_t payload[IEC60870_OBJECT_MAX_PAYLOAD]; uint16_t address_size, payload_size, required; if (object == NULL || buffer == NULL || length == NULL || !IEC60870_AppLayerParameters_Valid(parameters) || (is_sequence != 0 && is_sequence != 1)) { return IEC60870_ASDU_ERROR; } type = IEC60870_InformationObject_Get_Descriptor(object->type_id); if (type == NULL) { return IEC60870_ASDU_UNSUPPORTED; } /* 先比较受控静态表的地址,再解引用函数表,拒绝身份不匹配的对象。 */ if (object->virtual_function_table != type || object->ioa > IEC60870_Object_Max_IOA(parameters) || (is_sequence && !type->supports_sequence) || !object->virtual_function_table->encode(object, payload)) { return IEC60870_ASDU_ERROR; } /* 函数表只生成值载荷;统一入口在前面补IOA,避免各类型重复处理宽度。 */ address_size = is_sequence ? 0U : (uint16_t)parameters->sizeOfIOA; payload_size = type->get_payload_size ? type->get_payload_size(object) : type->payload_size; if (payload_size > sizeof(payload)) { return IEC60870_ASDU_ERROR; } required = (uint16_t)(address_size + payload_size); if (capacity < required) { return IEC60870_ASDU_FULL; } if (address_size != 0U) { IEC60870_Object_Write_LE(buffer, object->ioa, address_size); } memcpy(buffer + address_size, payload, payload_size); *length = required; return IEC60870_ASDU_OK; } /** * 功能:按Type ID分派具体载荷解析器,供ASDU校验和取对象共用。 * 输入参数:type_id——需要解析的具体类型。 * ioa——已还原的地址,0..0xFFFFFF。 * payload——不含IOA的载荷;只有0字节载荷的读命令允许NULL。 * length——载荷字节数,须精确匹配该类型。 * object_size——非空object的可用字节数,至少为该具体结构的sizeof。 * 输出参数:object——匹配具体类型的输出存储;NULL表示仅校验,不输出对象。 * 返回值:OK为成功,UNSUPPORTED为未知类型,ERROR为地址、长度、字段或存储尺寸非法。 * 说明:每个分支使用对应的局部具体对象,成功后才memcpy写回,不向调用方暴露部分结果。 * 存储尺寸检查不能替代类型匹配;调用方须依据Type ID选择正确的结构体。 */ 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) { const IEC60870_InformationObjectVTable *type = IEC60870_InformationObject_Get_Descriptor(type_id); if (type == NULL) { return IEC60870_ASDU_UNSUPPORTED; } if (object != NULL && object_size < type->object_size) { return IEC60870_ASDU_ERROR; } /* 编码时对象已经有函数表;解码时只有Type ID,必须先选择对应结构及解析器。 */ switch (type_id) { case 1: { IEC60870_SinglePointInformation_t result; IEC60870_AsduResult_t status = IEC60870_SinglePointInformation_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 3: { IEC60870_DoublePointInformation_t result; IEC60870_AsduResult_t status = IEC60870_DoublePointInformation_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 9: { IEC60870_MeasuredValueNormalized_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueNormalized_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 11: { IEC60870_MeasuredValueScaled_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueScaled_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 13: { IEC60870_MeasuredValueShort_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueShort_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 15: { IEC60870_IntegratedTotals_t result; IEC60870_AsduResult_t status = IEC60870_IntegratedTotals_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 30: { IEC60870_SinglePointWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_SinglePointWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 31: { IEC60870_DoublePointWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_DoublePointWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 34: { IEC60870_MeasuredValueNormalizedWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueNormalizedWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 35: { IEC60870_MeasuredValueScaledWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueScaledWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 36: { IEC60870_MeasuredValueShortWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueShortWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 37: { IEC60870_IntegratedTotalsWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_IntegratedTotalsWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 45: { IEC60870_SingleCommand_t result; IEC60870_AsduResult_t status = IEC60870_SingleCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 46: { IEC60870_DoubleCommand_t result; IEC60870_AsduResult_t status = IEC60870_DoubleCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 47: { IEC60870_StepCommand_t result; IEC60870_AsduResult_t status = IEC60870_StepCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 48: { IEC60870_SetpointCommandNormalized_t result; IEC60870_AsduResult_t status = IEC60870_SetpointCommandNormalized_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 49: { IEC60870_SetpointCommandScaled_t result; IEC60870_AsduResult_t status = IEC60870_SetpointCommandScaled_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 50: { IEC60870_SetpointCommandShort_t result; IEC60870_AsduResult_t status = IEC60870_SetpointCommandShort_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 58: { IEC60870_SingleCommandWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_SingleCommandWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 59: { IEC60870_DoubleCommandWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_DoubleCommandWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 60: { IEC60870_StepCommandWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_StepCommandWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 61: { IEC60870_SetpointCommandNormalizedWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_SetpointCommandNormalizedWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 62: { IEC60870_SetpointCommandScaledWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_SetpointCommandScaledWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 63: { IEC60870_SetpointCommandShortWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_SetpointCommandShortWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 100: { IEC60870_InterrogationCommand_t result; IEC60870_AsduResult_t status = IEC60870_InterrogationCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 101: { IEC60870_CounterInterrogationCommand_t result; IEC60870_AsduResult_t status = IEC60870_CounterInterrogationCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 102: { IEC60870_ReadCommand_t result; IEC60870_AsduResult_t status = IEC60870_ReadCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 103: { IEC60870_ClockSynchronizationCommand_t result; IEC60870_AsduResult_t status = IEC60870_ClockSynchronizationCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 105: { IEC60870_ResetProcessCommand_t result; IEC60870_AsduResult_t status = IEC60870_ResetProcessCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 107: { IEC60870_TestCommandWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_TestCommandWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 5: { IEC60870_StepPositionInformation_t result; IEC60870_AsduResult_t status = IEC60870_StepPositionInformation_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 7: { IEC60870_BitString32_t result; IEC60870_AsduResult_t status = IEC60870_BitString32_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 2: { IEC60870_SinglePointWithCP24Time2a_t result; IEC60870_AsduResult_t status = IEC60870_SinglePointWithCP24Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 4: { IEC60870_DoublePointWithCP24Time2a_t result; IEC60870_AsduResult_t status = IEC60870_DoublePointWithCP24Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 6: { IEC60870_StepPositionWithCP24Time2a_t result; IEC60870_AsduResult_t status = IEC60870_StepPositionWithCP24Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 8: { IEC60870_Bitstring32WithCP24Time2a_t result; IEC60870_AsduResult_t status = IEC60870_Bitstring32WithCP24Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 10: { IEC60870_MeasuredValueNormalizedWithCP24Time2a_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueNormalizedWithCP24Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 12: { IEC60870_MeasuredValueScaledWithCP24Time2a_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueScaledWithCP24Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 14: { IEC60870_MeasuredValueShortWithCP24Time2a_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueShortWithCP24Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 16: { IEC60870_IntegratedTotalsWithCP24Time2a_t result; IEC60870_AsduResult_t status = IEC60870_IntegratedTotalsWithCP24Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 32: { IEC60870_StepPositionWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_StepPositionWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 33: { IEC60870_Bitstring32WithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_Bitstring32WithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 17: { IEC60870_EventOfProtectionEquipment_t result; IEC60870_AsduResult_t status = IEC60870_EventOfProtectionEquipment_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 18: { IEC60870_PackedStartEventsOfProtectionEquipment_t result; IEC60870_AsduResult_t status = IEC60870_PackedStartEventsOfProtectionEquipment_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 19: { IEC60870_PackedOutputCircuitInfo_t result; IEC60870_AsduResult_t status = IEC60870_PackedOutputCircuitInfo_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 38: { IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_EventOfProtectionEquipmentWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 39: { IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 40: { IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_PackedOutputCircuitInfoWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 20: { IEC60870_PackedSinglePointWithSCD_t result; IEC60870_AsduResult_t status = IEC60870_PackedSinglePointWithSCD_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 21: { IEC60870_MeasuredValueNormalizedWithoutQuality_t result; IEC60870_AsduResult_t status = IEC60870_MeasuredValueNormalizedWithoutQuality_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 51: { IEC60870_Bitstring32Command_t result; IEC60870_AsduResult_t status = IEC60870_Bitstring32Command_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 64: { IEC60870_Bitstring32CommandWithCP56Time2a_t result; IEC60870_AsduResult_t status = IEC60870_Bitstring32CommandWithCP56Time2a_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 70: { IEC60870_EndOfInitialization_t result; IEC60870_AsduResult_t status = IEC60870_EndOfInitialization_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 104: { IEC60870_TestCommand_t result; IEC60870_AsduResult_t status = IEC60870_TestCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 106: { IEC60870_DelayAcquisitionCommand_t result; IEC60870_AsduResult_t status = IEC60870_DelayAcquisitionCommand_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 110: { IEC60870_ParameterNormalizedValue_t result; IEC60870_AsduResult_t status = IEC60870_ParameterNormalizedValue_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 111: { IEC60870_ParameterScaledValue_t result; IEC60870_AsduResult_t status = IEC60870_ParameterScaledValue_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 112: { IEC60870_ParameterFloatValue_t result; IEC60870_AsduResult_t status = IEC60870_ParameterFloatValue_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 113: { IEC60870_ParameterActivation_t result; IEC60870_AsduResult_t status = IEC60870_ParameterActivation_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 120: { IEC60870_FileReady_t result; IEC60870_AsduResult_t status = IEC60870_FileReady_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 121: { IEC60870_SectionReady_t result; IEC60870_AsduResult_t status = IEC60870_SectionReady_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 122: { IEC60870_FileCallOrSelect_t result; IEC60870_AsduResult_t status = IEC60870_FileCallOrSelect_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 124: { IEC60870_FileACK_t result; IEC60870_AsduResult_t status = IEC60870_FileACK_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 123: { IEC60870_FileLastSegmentOrSection_t result; IEC60870_AsduResult_t status = IEC60870_FileLastSegmentOrSection_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 125: { IEC60870_FileSegment_t result; IEC60870_AsduResult_t status = IEC60870_FileSegment_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 126: { IEC60870_FileDirectory_t result; IEC60870_AsduResult_t status = IEC60870_FileDirectory_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } case 127: { IEC60870_QueryLog_t result; IEC60870_AsduResult_t status = IEC60870_QueryLog_Get_From_Buffer(&result, ioa, payload, length); if (status == IEC60870_ASDU_OK && object != NULL) { memcpy(object, &result, sizeof(result)); } return status; } default: return IEC60870_ASDU_UNSUPPORTED; } }