605 lines
35 KiB
C
605 lines
35 KiB
C
/* 新增37种类型的独立报文字节向量及边界测试。
|
||
* 期望字节手工给出,不通过生产编码器计算;联合体仅用于测试存储。 */
|
||
#include "IEC60870_Asdu.h"
|
||
#include <limits.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#define CHECK(c) do { if (!(c)) { fprintf(stderr, "%s:%d: %s\n", __func__, __LINE__, #c); return 0; } } while (0)
|
||
#define COUNT(a) (sizeof(a) / sizeof((a)[0]))
|
||
#define SHORT_TIME_BYTES 0xD5, 0xDD, 0xA2
|
||
#define LONG_TIME_BYTES 0xD5, 0xDD, 0xA2, 0x8C, 0x9D, 0x02, 0x18
|
||
|
||
static const IEC60870_CP24Time2a_t short_time = {56789, 34, 1};
|
||
static const IEC60870_CP56Time2a_t long_time = {{2024,2,29,12,34,56,789,4},1,1,1};
|
||
static const IEC60870_CP16Time2a_t elapsed = {0xBEEF};
|
||
static const IEC60870_BinaryCounterReading_t counter = {INT32_MIN,21,1,1,1};
|
||
static const uint8_t segment_data[] = {0xDE,0xAD,0xBE,0xEF};
|
||
/* 公共测试显式提供布局,不引入104传输头文件。 */
|
||
static const IEC60870_AppLayerParameters_t default_parameters = {1, 1, 2, 0, 2, 3, 249};
|
||
|
||
typedef union {
|
||
IEC60870_StepPositionInformation_t v5;
|
||
IEC60870_BitString32_t v7;
|
||
IEC60870_SinglePointWithCP24Time2a_t v2;
|
||
IEC60870_DoublePointWithCP24Time2a_t v4;
|
||
IEC60870_StepPositionWithCP24Time2a_t v6;
|
||
IEC60870_Bitstring32WithCP24Time2a_t v8;
|
||
IEC60870_MeasuredValueNormalizedWithCP24Time2a_t v10;
|
||
IEC60870_MeasuredValueScaledWithCP24Time2a_t v12;
|
||
IEC60870_MeasuredValueShortWithCP24Time2a_t v14;
|
||
IEC60870_IntegratedTotalsWithCP24Time2a_t v16;
|
||
IEC60870_StepPositionWithCP56Time2a_t v32;
|
||
IEC60870_Bitstring32WithCP56Time2a_t v33;
|
||
IEC60870_EventOfProtectionEquipment_t v17;
|
||
IEC60870_PackedStartEventsOfProtectionEquipment_t v18;
|
||
IEC60870_PackedOutputCircuitInfo_t v19;
|
||
IEC60870_EventOfProtectionEquipmentWithCP56Time2a_t v38;
|
||
IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_t v39;
|
||
IEC60870_PackedOutputCircuitInfoWithCP56Time2a_t v40;
|
||
IEC60870_PackedSinglePointWithSCD_t v20;
|
||
IEC60870_MeasuredValueNormalizedWithoutQuality_t v21;
|
||
IEC60870_Bitstring32Command_t v51;
|
||
IEC60870_Bitstring32CommandWithCP56Time2a_t v64;
|
||
IEC60870_EndOfInitialization_t v70;
|
||
IEC60870_TestCommand_t v104;
|
||
IEC60870_DelayAcquisitionCommand_t v106;
|
||
IEC60870_ParameterNormalizedValue_t v110;
|
||
IEC60870_ParameterScaledValue_t v111;
|
||
IEC60870_ParameterFloatValue_t v112;
|
||
IEC60870_ParameterActivation_t v113;
|
||
IEC60870_FileReady_t v120;
|
||
IEC60870_SectionReady_t v121;
|
||
IEC60870_FileCallOrSelect_t v122;
|
||
IEC60870_FileACK_t v124;
|
||
IEC60870_FileLastSegmentOrSection_t v123;
|
||
IEC60870_FileSegment_t v125;
|
||
IEC60870_FileDirectory_t v126;
|
||
IEC60870_QueryLog_t v127;
|
||
} Object;
|
||
|
||
typedef struct {
|
||
uint8_t type, length, single, sequence;
|
||
uint8_t payload[16];
|
||
} Vector;
|
||
|
||
static const Vector vectors[] = {
|
||
{2, 4, 0, 1, {0xF1, SHORT_TIME_BYTES}},
|
||
{4, 4, 0, 1, {0x93, SHORT_TIME_BYTES}},
|
||
{5, 2, 0, 1, {0xFE, 0x81}},
|
||
{6, 5, 0, 1, {0xFE, 0x81, SHORT_TIME_BYTES}},
|
||
{7, 5, 0, 1, {0xEF, 0xCD, 0xAB, 0x89, 0x51}},
|
||
{8, 8, 0, 1, {0xEF, 0xCD, 0xAB, 0x89, 0x51, SHORT_TIME_BYTES}},
|
||
{10, 6, 0, 1, {0, 0x80, 0x81, SHORT_TIME_BYTES}},
|
||
{12, 6, 0, 1, {0xFE, 0xFF, 0x10, SHORT_TIME_BYTES}},
|
||
{14, 8, 0, 1, {0, 0, 0xC0, 0x3F, 0x41, SHORT_TIME_BYTES}},
|
||
{16, 8, 0, 1, {0, 0, 0, 0x80, 0xF5, SHORT_TIME_BYTES}},
|
||
{17, 6, 0, 1, {0x9A, 0xEF, 0xBE, SHORT_TIME_BYTES}},
|
||
{18, 7, 0, 1, {0x21, 0x98, 0xEF, 0xBE, SHORT_TIME_BYTES}},
|
||
{19, 7, 0, 1, {9, 0x98, 0xEF, 0xBE, SHORT_TIME_BYTES}},
|
||
{20, 5, 0, 1, {0x34, 0x12, 0xCD, 0xAB, 0x81}},
|
||
{21, 2, 0, 1, {0, 0x80}},
|
||
{32, 9, 0, 1, {0xFE, 0x81, LONG_TIME_BYTES}},
|
||
{33, 12, 0, 1, {0xEF, 0xCD, 0xAB, 0x89, 0x51, LONG_TIME_BYTES}},
|
||
{38, 10, 0, 1, {0x9A, 0xEF, 0xBE, LONG_TIME_BYTES}},
|
||
{39, 11, 0, 1, {0x21, 0x98, 0xEF, 0xBE, LONG_TIME_BYTES}},
|
||
{40, 11, 0, 1, {9, 0x98, 0xEF, 0xBE, LONG_TIME_BYTES}},
|
||
{51, 4, 1, 0, {0xEF, 0xCD, 0xAB, 0x89}},
|
||
{64, 11, 1, 0, {0xEF, 0xCD, 0xAB, 0x89, LONG_TIME_BYTES}},
|
||
{70, 1, 1, 0, {0x82}},
|
||
{104, 2, 1, 0, {0xAA, 0x55}},
|
||
{106, 2, 1, 0, {0xEF, 0xBE}},
|
||
{110, 3, 0, 0, {0, 0x80, 0xC3}},
|
||
{111, 3, 0, 0, {0xFE, 0xFF, 0xC3}},
|
||
{112, 5, 0, 0, {0, 0, 0xC0, 0x3F, 0xC3}},
|
||
{113, 1, 0, 0, {0x82}},
|
||
{120, 6, 1, 0, {0x34, 0x12, 1, 0xCD, 0xAB, 0x80}},
|
||
{121, 7, 1, 0, {0x34, 0x12, 0x56, 1, 0xCD, 0xAB, 0x80}},
|
||
{122, 4, 1, 0, {0x34, 0x12, 0x56, 0x12}},
|
||
{123, 5, 1, 0, {0x34, 0x12, 0x56, 1, 0xA5}},
|
||
{124, 4, 1, 0, {0x34, 0x12, 0x56, 0x12}},
|
||
{125, 8, 1, 0, {0x34, 0x12, 0x56, 4, 0xDE, 0xAD, 0xBE, 0xEF}},
|
||
{126, 13, 0, 1, {0x34, 0x12, 1, 0xCD, 0xAB, 0xE3, LONG_TIME_BYTES}},
|
||
{127, 16, 1, 0, {0x34, 0x12, LONG_TIME_BYTES, LONG_TIME_BYTES}},
|
||
};
|
||
|
||
static IEC60870_AsduResult_t Create_Object(uint8_t type, uint32_t ioa, Object *out)
|
||
{
|
||
switch (type) {
|
||
case 2: return IEC60870_SinglePointWithCP24Time2a_Create(&out->v2, ioa, 1, 0xF0, &short_time);
|
||
case 4: return IEC60870_DoublePointWithCP24Time2a_Create(&out->v4, ioa, 3, 0x90, &short_time);
|
||
case 5: return IEC60870_StepPositionInformation_Create(&out->v5, ioa, -2, 1, 0x81);
|
||
case 6: return IEC60870_StepPositionWithCP24Time2a_Create(&out->v6, ioa, -2, 1, 0x81, &short_time);
|
||
case 7: return IEC60870_BitString32_Create(&out->v7, ioa, UINT32_C(0x89ABCDEF), 0x51);
|
||
case 8: return IEC60870_Bitstring32WithCP24Time2a_Create(&out->v8, ioa, UINT32_C(0x89ABCDEF), 0x51, &short_time);
|
||
case 10: return IEC60870_MeasuredValueNormalizedWithCP24Time2a_Create(&out->v10, ioa, INT16_MIN, 0x81, &short_time);
|
||
case 12: return IEC60870_MeasuredValueScaledWithCP24Time2a_Create(&out->v12, ioa, -2, 0x10, &short_time);
|
||
case 14: return IEC60870_MeasuredValueShortWithCP24Time2a_Create(&out->v14, ioa, 1.5f, 0x41, &short_time);
|
||
case 16: return IEC60870_IntegratedTotalsWithCP24Time2a_Create(&out->v16, ioa, &counter, &short_time);
|
||
case 17: return IEC60870_EventOfProtectionEquipment_Create(&out->v17, ioa, 2, 0x98, &elapsed, &short_time);
|
||
case 18: return IEC60870_PackedStartEventsOfProtectionEquipment_Create(&out->v18, ioa, 0x21, 0x98, &elapsed, &short_time);
|
||
case 19: return IEC60870_PackedOutputCircuitInfo_Create(&out->v19, ioa, 9, 0x98, &elapsed, &short_time);
|
||
case 20: return IEC60870_PackedSinglePointWithSCD_Create(&out->v20, ioa, 0x1234, 0xABCD, 0x81);
|
||
case 21: return IEC60870_MeasuredValueNormalizedWithoutQuality_Create(&out->v21, ioa, INT16_MIN);
|
||
case 32: return IEC60870_StepPositionWithCP56Time2a_Create(&out->v32, ioa, -2, 1, 0x81, &long_time);
|
||
case 33: return IEC60870_Bitstring32WithCP56Time2a_Create(&out->v33, ioa, UINT32_C(0x89ABCDEF), 0x51, &long_time);
|
||
case 38: return IEC60870_EventOfProtectionEquipmentWithCP56Time2a_Create(&out->v38, ioa, 2, 0x98, &elapsed, &long_time);
|
||
case 39: return IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Create(&out->v39, ioa, 0x21, 0x98, &elapsed, &long_time);
|
||
case 40: return IEC60870_PackedOutputCircuitInfoWithCP56Time2a_Create(&out->v40, ioa, 9, 0x98, &elapsed, &long_time);
|
||
case 51: return IEC60870_Bitstring32Command_Create(&out->v51, ioa, UINT32_C(0x89ABCDEF));
|
||
case 64: return IEC60870_Bitstring32CommandWithCP56Time2a_Create(&out->v64, ioa, UINT32_C(0x89ABCDEF), &long_time);
|
||
case 70: return IEC60870_EndOfInitialization_Create(&out->v70, ioa, 0x82);
|
||
case 104: return IEC60870_TestCommand_Create(&out->v104, ioa);
|
||
case 106: return IEC60870_DelayAcquisitionCommand_Create(&out->v106, ioa, &elapsed);
|
||
case 110: return IEC60870_ParameterNormalizedValue_Create(&out->v110, ioa, INT16_MIN, 0xC3);
|
||
case 111: return IEC60870_ParameterScaledValue_Create(&out->v111, ioa, -2, 0xC3);
|
||
case 112: return IEC60870_ParameterFloatValue_Create(&out->v112, ioa, 1.5f, 0xC3);
|
||
case 113: return IEC60870_ParameterActivation_Create(&out->v113, ioa, 0x82);
|
||
case 120: return IEC60870_FileReady_Create(&out->v120, ioa, 0x1234, UINT32_C(0xABCD01), 0x80);
|
||
case 121: return IEC60870_SectionReady_Create(&out->v121, ioa, 0x1234, 0x56, UINT32_C(0xABCD01), 0x80);
|
||
case 122: return IEC60870_FileCallOrSelect_Create(&out->v122, ioa, 0x1234, 0x56, 0x12);
|
||
case 123: return IEC60870_FileLastSegmentOrSection_Create(&out->v123, ioa, 0x1234, 0x56, 1, 0xA5);
|
||
case 124: return IEC60870_FileACK_Create(&out->v124, ioa, 0x1234, 0x56, 0x12);
|
||
case 125: return IEC60870_FileSegment_Create(&out->v125, ioa, 0x1234, 0x56, segment_data, sizeof(segment_data));
|
||
case 126: return IEC60870_FileDirectory_Create(&out->v126, ioa, 0x1234, UINT32_C(0xABCD01), 0xE3, &long_time);
|
||
case 127: return IEC60870_QueryLog_Create(&out->v127, ioa, 0x1234, &long_time, &long_time);
|
||
default: return IEC60870_ASDU_UNSUPPORTED;
|
||
}
|
||
}
|
||
|
||
static IEC60870_AsduResult_t Parse_Object(uint8_t type, uint32_t ioa, const uint8_t *payload, uint16_t length, Object *out)
|
||
{
|
||
switch (type) {
|
||
case 5: return IEC60870_StepPositionInformation_Get_From_Buffer(&out->v5, ioa, payload, length);
|
||
case 7: return IEC60870_BitString32_Get_From_Buffer(&out->v7, ioa, payload, length);
|
||
case 2: return IEC60870_SinglePointWithCP24Time2a_Get_From_Buffer(&out->v2, ioa, payload, length);
|
||
case 4: return IEC60870_DoublePointWithCP24Time2a_Get_From_Buffer(&out->v4, ioa, payload, length);
|
||
case 6: return IEC60870_StepPositionWithCP24Time2a_Get_From_Buffer(&out->v6, ioa, payload, length);
|
||
case 8: return IEC60870_Bitstring32WithCP24Time2a_Get_From_Buffer(&out->v8, ioa, payload, length);
|
||
case 10: return IEC60870_MeasuredValueNormalizedWithCP24Time2a_Get_From_Buffer(&out->v10, ioa, payload, length);
|
||
case 12: return IEC60870_MeasuredValueScaledWithCP24Time2a_Get_From_Buffer(&out->v12, ioa, payload, length);
|
||
case 14: return IEC60870_MeasuredValueShortWithCP24Time2a_Get_From_Buffer(&out->v14, ioa, payload, length);
|
||
case 16: return IEC60870_IntegratedTotalsWithCP24Time2a_Get_From_Buffer(&out->v16, ioa, payload, length);
|
||
case 32: return IEC60870_StepPositionWithCP56Time2a_Get_From_Buffer(&out->v32, ioa, payload, length);
|
||
case 33: return IEC60870_Bitstring32WithCP56Time2a_Get_From_Buffer(&out->v33, ioa, payload, length);
|
||
case 17: return IEC60870_EventOfProtectionEquipment_Get_From_Buffer(&out->v17, ioa, payload, length);
|
||
case 18: return IEC60870_PackedStartEventsOfProtectionEquipment_Get_From_Buffer(&out->v18, ioa, payload, length);
|
||
case 19: return IEC60870_PackedOutputCircuitInfo_Get_From_Buffer(&out->v19, ioa, payload, length);
|
||
case 38: return IEC60870_EventOfProtectionEquipmentWithCP56Time2a_Get_From_Buffer(&out->v38, ioa, payload, length);
|
||
case 39: return IEC60870_PackedStartEventsOfProtectionEquipmentWithCP56Time2a_Get_From_Buffer(&out->v39, ioa, payload, length);
|
||
case 40: return IEC60870_PackedOutputCircuitInfoWithCP56Time2a_Get_From_Buffer(&out->v40, ioa, payload, length);
|
||
case 20: return IEC60870_PackedSinglePointWithSCD_Get_From_Buffer(&out->v20, ioa, payload, length);
|
||
case 21: return IEC60870_MeasuredValueNormalizedWithoutQuality_Get_From_Buffer(&out->v21, ioa, payload, length);
|
||
case 51: return IEC60870_Bitstring32Command_Get_From_Buffer(&out->v51, ioa, payload, length);
|
||
case 64: return IEC60870_Bitstring32CommandWithCP56Time2a_Get_From_Buffer(&out->v64, ioa, payload, length);
|
||
case 70: return IEC60870_EndOfInitialization_Get_From_Buffer(&out->v70, ioa, payload, length);
|
||
case 104: return IEC60870_TestCommand_Get_From_Buffer(&out->v104, ioa, payload, length);
|
||
case 106: return IEC60870_DelayAcquisitionCommand_Get_From_Buffer(&out->v106, ioa, payload, length);
|
||
case 110: return IEC60870_ParameterNormalizedValue_Get_From_Buffer(&out->v110, ioa, payload, length);
|
||
case 111: return IEC60870_ParameterScaledValue_Get_From_Buffer(&out->v111, ioa, payload, length);
|
||
case 112: return IEC60870_ParameterFloatValue_Get_From_Buffer(&out->v112, ioa, payload, length);
|
||
case 113: return IEC60870_ParameterActivation_Get_From_Buffer(&out->v113, ioa, payload, length);
|
||
case 120: return IEC60870_FileReady_Get_From_Buffer(&out->v120, ioa, payload, length);
|
||
case 121: return IEC60870_SectionReady_Get_From_Buffer(&out->v121, ioa, payload, length);
|
||
case 122: return IEC60870_FileCallOrSelect_Get_From_Buffer(&out->v122, ioa, payload, length);
|
||
case 124: return IEC60870_FileACK_Get_From_Buffer(&out->v124, ioa, payload, length);
|
||
case 123: return IEC60870_FileLastSegmentOrSection_Get_From_Buffer(&out->v123, ioa, payload, length);
|
||
case 125: return IEC60870_FileSegment_Get_From_Buffer(&out->v125, ioa, payload, length);
|
||
case 126: return IEC60870_FileDirectory_Get_From_Buffer(&out->v126, ioa, payload, length);
|
||
case 127: return IEC60870_QueryLog_Get_From_Buffer(&out->v127, ioa, payload, length);
|
||
default: return IEC60870_ASDU_UNSUPPORTED;
|
||
}
|
||
}
|
||
|
||
static IEC60870_InformationObject_t *Base(Object *object)
|
||
{
|
||
return (IEC60870_InformationObject_t *)object;
|
||
}
|
||
|
||
/* 独立按协议宽度拼接地址,未调用生产工具。 */
|
||
static void Write_Address(uint8_t *out, uint32_t value, int width)
|
||
{
|
||
int i;
|
||
for (i = 0; i < width; ++i) { out[i] = (uint8_t)(value >> (8 * i)); }
|
||
}
|
||
|
||
static uint16_t Expected_Frame(const Vector *v, const IEC60870_AppLayerParameters_t *p,
|
||
int sq, int count, uint32_t first, uint8_t *out)
|
||
{
|
||
uint16_t n = (uint16_t)(2 + p->sizeOfCOT + p->sizeOfCA);
|
||
int i;
|
||
out[0] = v->type;
|
||
out[1] = (uint8_t)((sq << 7) | count);
|
||
out[2] = 0xC3;
|
||
if (p->sizeOfCOT == 2) { out[3] = 0xA7; }
|
||
Write_Address(out + 2 + p->sizeOfCOT, p->sizeOfCA == 2 ? 0x1234U : 0x34U, p->sizeOfCA);
|
||
for (i = 0; i < count; ++i) {
|
||
if (i == 0 || !sq) {
|
||
Write_Address(out + n, first + (uint32_t)(sq ? i : i * 7), p->sizeOfIOA);
|
||
n = (uint16_t)(n + p->sizeOfIOA);
|
||
}
|
||
memcpy(out + n, v->payload, v->length);
|
||
n = (uint16_t)(n + v->length);
|
||
}
|
||
return n;
|
||
}
|
||
|
||
static int Reject_Frame(const IEC60870_AppLayerParameters_t *p, const uint8_t *frame, uint16_t length)
|
||
{
|
||
IEC60870_AsduInfo_t info;
|
||
uint8_t saved[sizeof(info)];
|
||
memset(&info, 0xA5, sizeof(info));
|
||
memcpy(saved, &info, sizeof(info));
|
||
CHECK(IEC60870_Asdu_Parse(p, frame, length, &info) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &info, sizeof(info)) == 0);
|
||
return 1;
|
||
}
|
||
|
||
static int test_all_layouts(void)
|
||
{
|
||
int cot, ca, ioa, sq;
|
||
unsigned int cases = 0;
|
||
size_t k;
|
||
for (cot = 1; cot <= 2; ++cot) for (ca = 1; ca <= 2; ++ca) for (ioa = 1; ioa <= 3; ++ioa) {
|
||
IEC60870_AppLayerParameters_t p = default_parameters;
|
||
IEC60870_AsduHeader_t header = {3,1,1,0xA7,0};
|
||
uint32_t first = ioa == 1 ? 0x40U : ioa == 2 ? 0x3440U : 0x123440U;
|
||
p.sizeOfCOT = cot; p.sizeOfCA = ca; p.sizeOfIOA = ioa; p.maxSizeOfASDU = 254;
|
||
header.common_address = ca == 1 ? 0x34 : 0x1234;
|
||
for (k = 0; k < COUNT(vectors); ++k) for (sq = 0; sq <= vectors[k].sequence; ++sq) {
|
||
const Vector *v = &vectors[k];
|
||
IEC60870_Asdu_t asdu, saved_asdu;
|
||
IEC60870_AsduInfo_t info;
|
||
Object object, parsed, saved_object;
|
||
uint8_t bytes[260], expected[260], saved_bytes[260], encoded[262];
|
||
const uint8_t *data;
|
||
uint16_t length, wire_length, object_length;
|
||
int count = v->single ? 1 : 2, i;
|
||
memset(bytes, 0xA5, sizeof(bytes));
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, bytes, sizeof(bytes), &header, sq) == IEC60870_ASDU_OK);
|
||
for (i = 0; i < count; ++i) {
|
||
uint32_t address = first + (uint32_t)(sq ? i : i * 7);
|
||
CHECK(Create_Object(v->type, address, &object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_InformationObject_Get_Type(Base(&object)) == v->type);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, Base(&object)) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_InformationObject_Encode(Base(&object), &p, 0, encoded, sizeof(encoded), &object_length) == IEC60870_ASDU_OK);
|
||
CHECK(object_length == ioa + v->length);
|
||
CHECK(memcmp(encoded + ioa, v->payload, v->length) == 0);
|
||
}
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &data, &length) == IEC60870_ASDU_OK);
|
||
wire_length = Expected_Frame(v, &p, sq, count, first, expected);
|
||
CHECK(length == wire_length && memcmp(data, expected, length) == 0);
|
||
CHECK(bytes[length] == 0xA5);
|
||
CHECK(IEC60870_Asdu_Parse(&p, expected, length, &info) == IEC60870_ASDU_OK);
|
||
CHECK(info.object_count == count && info.is_sequence == sq && info.common_address == header.common_address);
|
||
for (i = 0; i < count; ++i) {
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, (uint8_t)i, &parsed, sizeof(parsed)) == IEC60870_ASDU_OK);
|
||
CHECK(Base(&parsed)->ioa == first + (uint32_t)(sq ? i : i * 7));
|
||
CHECK(IEC60870_InformationObject_Encode(Base(&parsed), &p, 0, encoded, sizeof(encoded), &object_length) == IEC60870_ASDU_OK);
|
||
CHECK(memcmp(encoded + ioa, v->payload, v->length) == 0);
|
||
}
|
||
memset(&parsed, 0xA5, sizeof(parsed)); memcpy(&saved_object, &parsed, sizeof(parsed));
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 0, &parsed, 1) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(&parsed, &saved_object, sizeof(parsed)) == 0);
|
||
CHECK(Reject_Frame(&p, expected, (uint16_t)(length - 1)));
|
||
expected[length] = 0;
|
||
CHECK(Reject_Frame(&p, expected, (uint16_t)(length + 1)));
|
||
for (i = 0; i < v->length; ++i) {
|
||
CHECK(Parse_Object(v->type, first, v->payload, (uint16_t)i, &parsed) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(&parsed, &saved_object, sizeof(parsed)) == 0);
|
||
}
|
||
CHECK(Parse_Object(v->type, first, v->payload, v->length, &parsed) == IEC60870_ASDU_OK);
|
||
/* 原对象存储可复用,加入ASDU后保存的字节必须独立。 */
|
||
memset(&object, 0, sizeof(object));
|
||
CHECK(memcmp(data, expected, length) == 0);
|
||
saved_asdu = asdu; memcpy(saved_bytes, bytes, sizeof(bytes));
|
||
CHECK(Create_Object(v->type, first, &object) == IEC60870_ASDU_OK);
|
||
if (v->single) {
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, Base(&object)) == IEC60870_ASDU_FULL);
|
||
} else if (sq) {
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, Base(&object)) == IEC60870_ASDU_ERROR);
|
||
} else {
|
||
asdu.capacity = asdu.length; saved_asdu = asdu;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, Base(&object)) == IEC60870_ASDU_FULL);
|
||
}
|
||
CHECK(memcmp(&asdu, &saved_asdu, sizeof(asdu)) == 0);
|
||
CHECK(memcmp(bytes, saved_bytes, sizeof(bytes)) == 0);
|
||
++cases;
|
||
}
|
||
}
|
||
printf("Extended ASDU: %u independent wire layouts verified\n", cases);
|
||
return 1;
|
||
}
|
||
|
||
static int test_sequence_and_counts(void)
|
||
{
|
||
size_t k;
|
||
uint8_t frame[260], buffer[260], saved[260];
|
||
IEC60870_AsduHeader_t header = {3,1,1,0xA7,0x1234};
|
||
IEC60870_Asdu_t asdu, before;
|
||
Object object;
|
||
for (k = 0; k < COUNT(vectors); ++k) {
|
||
const Vector *v = &vectors[k];
|
||
uint16_t n = Expected_Frame(v, &default_parameters, 0, 1, 0x40, frame);
|
||
frame[1] = 0;
|
||
CHECK(Reject_Frame(&default_parameters, frame, n));
|
||
if (!v->sequence) {
|
||
n = Expected_Frame(v, &default_parameters, 1, 1, 0x40, frame);
|
||
CHECK(Reject_Frame(&default_parameters, frame, n));
|
||
memset(buffer, 0xA5, sizeof(buffer));
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &default_parameters, buffer, sizeof(buffer), &header, 1) == IEC60870_ASDU_OK);
|
||
before = asdu; memcpy(saved, buffer, sizeof(buffer));
|
||
CHECK(Create_Object(v->type, 0x40, &object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, Base(&object)) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(&asdu, &before, sizeof(asdu)) == 0 && memcmp(buffer, saved, sizeof(buffer)) == 0);
|
||
}
|
||
if (v->single) {
|
||
n = Expected_Frame(v, &default_parameters, 0, 2, 0x40, frame);
|
||
CHECK(Reject_Frame(&default_parameters, frame, n));
|
||
}
|
||
if (v->sequence) {
|
||
IEC60870_AppLayerParameters_t p = default_parameters;
|
||
p.sizeOfIOA = 1;
|
||
n = Expected_Frame(v, &p, 1, 2, 255, frame);
|
||
CHECK(Reject_Frame(&p, frame, n));
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int test_invalid_fields(void)
|
||
{
|
||
/* 每项给出独立的非法保留位/状态/时间修改,不依赖生产描述表。 */
|
||
static const struct { uint8_t type, offset, mask; } faults[] = {
|
||
{2,0,2},{4,0,4},{5,1,2},{6,1,2},{7,4,2},{8,4,2},
|
||
{10,2,2},{12,2,2},{14,4,2},{16,7,0x40},
|
||
{17,0,4},{18,0,0x40},{19,0,0x10},{20,4,2},
|
||
{32,1,2},{33,4,2},{38,0,4},{39,1,4},{40,0,0x10},
|
||
{104,0,1},{120,5,1},{121,6,1},{125,3,1},
|
||
{126,11,0x80},{127,14,0x80}
|
||
};
|
||
size_t i, k;
|
||
for (i = 0; i < COUNT(faults); ++i) {
|
||
Object object, before;
|
||
uint8_t payload[17], frame[260];
|
||
for (k = 0; k < COUNT(vectors) && vectors[k].type != faults[i].type; ++k) {}
|
||
CHECK(k < COUNT(vectors));
|
||
memcpy(payload, vectors[k].payload, vectors[k].length);
|
||
payload[faults[i].offset] |= faults[i].mask;
|
||
memset(&object, 0xA5, sizeof(object)); memcpy(&before, &object, sizeof(object));
|
||
CHECK(Parse_Object(vectors[k].type, 1, payload, vectors[k].length, &object) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(&object, &before, sizeof(object)) == 0);
|
||
{
|
||
uint16_t n = Expected_Frame(&vectors[k], &default_parameters, 0, 1, 1, frame);
|
||
memcpy(frame + 9, payload, vectors[k].length);
|
||
CHECK(Reject_Frame(&default_parameters, frame, n));
|
||
}
|
||
}
|
||
/* 多对象中的第二个非法对象也必须导致整条ASDU解析失败。 */
|
||
{
|
||
IEC60870_AsduInfo_t info;
|
||
uint8_t frame[] = {5,0x82,3,0,1,0,1,0,0,0xFE,0x81,0xFE,0x83};
|
||
CHECK(Reject_Frame(&default_parameters, frame, sizeof(frame)));
|
||
frame[12] = 0x81;
|
||
CHECK(IEC60870_Asdu_Parse(&default_parameters, frame, sizeof(frame), &info) == IEC60870_ASDU_OK);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int test_short_times(void)
|
||
{
|
||
IEC60870_CP16Time2a_t cp16, parsed16 = {0};
|
||
IEC60870_CP24Time2a_t cp24 = {59999,59,1}, parsed24, before24;
|
||
uint8_t bytes[4] = {0xA5,0xA5,0xA5,0xA5}, saved[4];
|
||
uint32_t i;
|
||
for (i = 0; i <= 65535U; ++i) {
|
||
cp16.milliseconds = (uint16_t)i;
|
||
CHECK(IEC60870_Time_To_CP16(&cp16, bytes, 2));
|
||
CHECK(bytes[0] == (uint8_t)i && bytes[1] == (uint8_t)(i >> 8));
|
||
CHECK(IEC60870_Time_From_CP16(bytes, 2, &parsed16) && parsed16.milliseconds == i);
|
||
}
|
||
CHECK(IEC60870_Time_To_CP24(&cp24, bytes, 3));
|
||
CHECK(bytes[0] == 0x5F && bytes[1] == 0xEA && bytes[2] == 0xBB && bytes[3] == 0xA5);
|
||
CHECK(IEC60870_Time_From_CP24(bytes, 3, &parsed24));
|
||
CHECK(parsed24.milliseconds == 59999 && parsed24.minute == 59 && parsed24.is_invalid == 1);
|
||
before24 = parsed24;
|
||
memcpy(saved, bytes, sizeof(bytes));
|
||
cp24.milliseconds = 60000;
|
||
CHECK(!IEC60870_Time_To_CP24(&cp24, bytes, sizeof(bytes)));
|
||
CHECK(memcmp(bytes, saved, sizeof(bytes)) == 0);
|
||
cp24.milliseconds = 0; cp24.minute = 60;
|
||
CHECK(!IEC60870_Time_To_CP24(&cp24, bytes, sizeof(bytes)));
|
||
cp24.minute = 0; cp24.is_invalid = 2;
|
||
CHECK(!IEC60870_Time_To_CP24(&cp24, bytes, sizeof(bytes)));
|
||
bytes[2] |= 0x40;
|
||
CHECK(!IEC60870_Time_From_CP24(bytes, 3, &parsed24));
|
||
CHECK(memcmp(&parsed24, &before24, sizeof(parsed24)) == 0);
|
||
bytes[0] = 0x60; bytes[1] = 0xEA; bytes[2] = 0;
|
||
CHECK(!IEC60870_Time_From_CP24(bytes, 3, &parsed24));
|
||
bytes[0] = 0; bytes[1] = 0; bytes[2] = 60;
|
||
CHECK(!IEC60870_Time_From_CP24(bytes, 3, &parsed24));
|
||
CHECK(!IEC60870_Time_From_CP24(bytes, 2, &parsed24));
|
||
CHECK(!IEC60870_Time_From_CP24(NULL, 3, &parsed24));
|
||
CHECK(!IEC60870_Time_From_CP16(bytes, 1, &parsed16));
|
||
CHECK(!IEC60870_Time_To_CP16(&cp16, bytes, 1));
|
||
CHECK(!IEC60870_Time_To_CP24(NULL, bytes, 3));
|
||
cp24.milliseconds = 0; cp24.minute = 0; cp24.is_invalid = 0;
|
||
CHECK(IEC60870_Time_To_CP24(&cp24, bytes, 3));
|
||
CHECK(bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0);
|
||
return 1;
|
||
}
|
||
|
||
static int test_constructor_atomicity(void)
|
||
{
|
||
Object object, before;
|
||
size_t i;
|
||
uint8_t encoded[262], saved_bytes[262];
|
||
uint16_t length = 0xA55A;
|
||
IEC60870_CP24Time2a_t bad_time = {60000,0,0};
|
||
memset(&object, 0xA5, sizeof(object)); memcpy(&before, &object, sizeof(object));
|
||
for (i = 0; i < COUNT(vectors); ++i) {
|
||
CHECK(Create_Object(vectors[i].type, UINT32_C(0x1000000), &object) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(&object, &before, sizeof(object)) == 0);
|
||
CHECK(Parse_Object(vectors[i].type, 1, NULL, vectors[i].length, &object) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(&object, &before, sizeof(object)) == 0);
|
||
}
|
||
#define REJECT_CREATE(call) do { CHECK((call) == IEC60870_ASDU_ERROR); CHECK(memcmp(&object, &before, sizeof(object)) == 0); } while (0)
|
||
REJECT_CREATE(IEC60870_StepPositionInformation_Create(&object.v5, 1, -65, 0, 0));
|
||
REJECT_CREATE(IEC60870_StepPositionInformation_Create(&object.v5, 1, 64, 0, 0));
|
||
REJECT_CREATE(IEC60870_StepPositionInformation_Create(&object.v5, 1, 0, 2, 0));
|
||
REJECT_CREATE(IEC60870_BitString32_Create(&object.v7, 1, 0, 2));
|
||
REJECT_CREATE(IEC60870_SinglePointWithCP24Time2a_Create(&object.v2, 1, 1, 0, &bad_time));
|
||
REJECT_CREATE(IEC60870_SinglePointWithCP24Time2a_Create(&object.v2, 1, 1, 0, NULL));
|
||
REJECT_CREATE(IEC60870_EventOfProtectionEquipment_Create(&object.v17, 1, 4, 0, &elapsed, &short_time));
|
||
REJECT_CREATE(IEC60870_EventOfProtectionEquipment_Create(&object.v17, 1, 1, 4, &elapsed, &short_time));
|
||
REJECT_CREATE(IEC60870_PackedStartEventsOfProtectionEquipment_Create(&object.v18, 1, 64, 0, &elapsed, &short_time));
|
||
REJECT_CREATE(IEC60870_PackedOutputCircuitInfo_Create(&object.v19, 1, 16, 0, &elapsed, &short_time));
|
||
REJECT_CREATE(IEC60870_EndOfInitialization_Create(&object.v70, 1, 256));
|
||
REJECT_CREATE(IEC60870_DelayAcquisitionCommand_Create(&object.v106, 1, NULL));
|
||
REJECT_CREATE(IEC60870_ParameterNormalizedValue_Create(&object.v110, 1, 0, -1));
|
||
REJECT_CREATE(IEC60870_FileReady_Create(&object.v120, 1, 1, UINT32_C(0x1000000), 0));
|
||
REJECT_CREATE(IEC60870_SectionReady_Create(&object.v121, 1, 1, 1, 0, 1));
|
||
REJECT_CREATE(IEC60870_FileSegment_Create(&object.v125, 1, 1, 1, NULL, 1));
|
||
REJECT_CREATE(IEC60870_FileSegment_Create(&object.v125, 1, 1, 1, segment_data, 256));
|
||
REJECT_CREATE(IEC60870_FileDirectory_Create(&object.v126, 1, 1, 0, 0, NULL));
|
||
REJECT_CREATE(IEC60870_QueryLog_Create(&object.v127, 1, 1, &long_time, NULL));
|
||
#undef REJECT_CREATE
|
||
CHECK(Create_Object(5, 1, &object) == IEC60870_ASDU_OK);
|
||
object.v5.value = 100;
|
||
memset(encoded, 0xA5, sizeof(encoded)); memcpy(saved_bytes, encoded, sizeof(encoded));
|
||
CHECK(IEC60870_InformationObject_Encode(Base(&object), &default_parameters, 0, encoded, sizeof(encoded), &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(encoded, saved_bytes, sizeof(encoded)) == 0 && length == 0xA55A);
|
||
return 1;
|
||
}
|
||
|
||
static int test_file_segments(void)
|
||
{
|
||
int cot, ca, ioa, max;
|
||
uint8_t input[255], bytes[270], before_bytes[270], encoded[262];
|
||
IEC60870_AsduHeader_t header = {13,0,0,0,1};
|
||
unsigned int i;
|
||
CHECK(IEC60870_FileSegment_Get_Max_Data_Size(&default_parameters) == 236);
|
||
CHECK(IEC60870_FileSegment_Get_Max_Data_Size(NULL) == -1);
|
||
for (i = 0; i < sizeof(input); ++i) { input[i] = (uint8_t)(i ^ 0x5A); }
|
||
for (cot = 1; cot <= 2; ++cot) for (ca = 1; ca <= 2; ++ca) for (ioa = 1; ioa <= 3; ++ioa) {
|
||
IEC60870_AppLayerParameters_t p = default_parameters;
|
||
IEC60870_FileSegment_t segment, parsed;
|
||
IEC60870_Asdu_t asdu, before;
|
||
IEC60870_AsduInfo_t info;
|
||
const uint8_t *wire;
|
||
uint16_t n, object_length;
|
||
int h = 2 + cot + ca;
|
||
p.sizeOfCOT = cot; p.sizeOfCA = ca; p.sizeOfIOA = ioa; p.maxSizeOfASDU = 254;
|
||
max = IEC60870_FileSegment_Get_Max_Data_Size(&p);
|
||
CHECK(max == 254 - h - ioa - 4);
|
||
CHECK(IEC60870_FileSegment_Create(&segment, 1, 0x1234, 7, input, (uint16_t)max) == IEC60870_ASDU_OK);
|
||
memset(bytes, 0xA5, sizeof(bytes));
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, bytes, sizeof(bytes), &header, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &segment.object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &wire, &n) == IEC60870_ASDU_OK && n == 254);
|
||
CHECK(wire[h+ioa] == 0x34 && wire[h+ioa+1] == 0x12 && wire[h+ioa+2] == 7 && wire[h+ioa+3] == max);
|
||
CHECK(memcmp(wire+h+ioa+4, input, (size_t)max) == 0 && bytes[254] == 0xA5);
|
||
CHECK(IEC60870_Asdu_Parse(&p, wire, n, &info) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 0, &parsed, sizeof(parsed)) == IEC60870_ASDU_OK);
|
||
CHECK(parsed.length == max && memcmp(parsed.data, input, (size_t)max) == 0);
|
||
memset(bytes+h+ioa+4, 0, (size_t)max);
|
||
CHECK(memcmp(parsed.data, input, (size_t)max) == 0); /* 解析结果拥有独立数据。 */
|
||
CHECK(IEC60870_FileSegment_Create(&segment, 1, 1, 1, input, (uint16_t)(max+1)) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, bytes, sizeof(bytes), &header, 0) == IEC60870_ASDU_OK);
|
||
before = asdu; memcpy(before_bytes, bytes, sizeof(bytes));
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &segment.object) == IEC60870_ASDU_FULL);
|
||
CHECK(memcmp(&asdu, &before, sizeof(asdu)) == 0 && memcmp(bytes, before_bytes, sizeof(bytes)) == 0);
|
||
CHECK(IEC60870_FileSegment_Create(&segment, 1, 1, 1, input, 255) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_InformationObject_Encode(&segment.object, &p, 0, encoded, sizeof(encoded), &object_length) == IEC60870_ASDU_OK);
|
||
CHECK(object_length == ioa+259 && encoded[ioa+3] == 255);
|
||
CHECK(IEC60870_FileSegment_Get_From_Buffer(&parsed, 1, encoded+ioa, 259) == IEC60870_ASDU_OK);
|
||
CHECK(parsed.length == 255 && memcmp(parsed.data, input, sizeof(input)) == 0);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &segment.object) == IEC60870_ASDU_FULL);
|
||
CHECK(IEC60870_FileSegment_Create(&segment, 1, 1, 1, NULL, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &segment.object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &wire, &n) == IEC60870_ASDU_OK && n == h+ioa+4);
|
||
CHECK(IEC60870_Asdu_Parse(&p, wire, n, &info) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 0, &parsed, sizeof(parsed)) == IEC60870_ASDU_OK && parsed.length == 0);
|
||
p.maxSizeOfASDU = h+ioa+4;
|
||
CHECK(IEC60870_FileSegment_Get_Max_Data_Size(&p) == 0);
|
||
p.maxSizeOfASDU--;
|
||
CHECK(IEC60870_FileSegment_Get_Max_Data_Size(&p) == -1);
|
||
}
|
||
/* 默认104配置:236字节恰好249;237字节必须返回FULL。 */
|
||
{
|
||
IEC60870_FileSegment_t segment;
|
||
IEC60870_Asdu_t asdu;
|
||
const uint8_t *wire;
|
||
uint16_t n;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &default_parameters, bytes, sizeof(bytes), &header, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_FileSegment_Create(&segment, 1, 1, 1, input, 237) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &segment.object) == IEC60870_ASDU_FULL);
|
||
CHECK(IEC60870_FileSegment_Create(&segment, 1, 1, 1, input, 236) == IEC60870_ASDU_OK);
|
||
memset(input, 0, sizeof(input));
|
||
CHECK(segment.data[0] == 0x5A); /* 构造结果也不借用源数据。 */
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &segment.object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &wire, &n) == IEC60870_ASDU_OK && n == 249);
|
||
bytes[12]++; /* LOS称有237字节,但原文只有236字节。 */
|
||
CHECK(Reject_Frame(&default_parameters, bytes, n));
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &wire, &n) == IEC60870_ASDU_ERROR);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int test_numeric_and_parameter_fields(void)
|
||
{
|
||
IEC60870_StepPositionInformation_t step;
|
||
IEC60870_ParameterNormalizedValue_t parameter;
|
||
IEC60870_ParameterFloatValue_t floating;
|
||
IEC60870_PackedSinglePointWithSCD_t packed;
|
||
IEC60870_FileReady_t file;
|
||
IEC60870_Asdu_t asdu;
|
||
IEC60870_AsduInfo_t info;
|
||
IEC60870_AsduHeader_t header = {6,0,0,0,1};
|
||
uint8_t bytes[254], payload[16], raw_float[] = {0x23,0x01,0xC0,0x7F,0xFF};
|
||
uint16_t n;
|
||
const uint8_t *wire;
|
||
int v;
|
||
for (v = -64; v <= 63; ++v) {
|
||
CHECK(IEC60870_StepPositionInformation_Create(&step, 1, v, 0, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_InformationObject_Encode(&step.object, &default_parameters, 1, payload, sizeof(payload), &n) == IEC60870_ASDU_OK);
|
||
CHECK(n == 2 && payload[0] == ((unsigned int)v & 0x7FU) && payload[1] == 0);
|
||
CHECK(IEC60870_StepPositionInformation_Get_From_Buffer(&step, 1, payload, 2) == IEC60870_ASDU_OK && step.value == v);
|
||
}
|
||
CHECK(IEC60870_PackedSinglePointWithSCD_Get_From_Buffer(&packed, 1, (const uint8_t[]){0x34,0x12,0xCD,0xAB,0x81}, 5) == IEC60870_ASDU_OK);
|
||
CHECK(packed.status == 0x1234 && packed.change == 0xABCD && packed.quality == 0x81);
|
||
CHECK(IEC60870_FileReady_Create(&file, 1, 1, UINT32_C(0xFFFFFF), 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_InformationObject_Encode(&file.object, &default_parameters, 0, payload, sizeof(payload), &n) == IEC60870_ASDU_OK);
|
||
CHECK(payload[5] == 255 && payload[6] == 255 && payload[7] == 255);
|
||
CHECK(IEC60870_ParameterFloatValue_Get_From_Buffer(&floating, 1, raw_float, sizeof(raw_float)) == IEC60870_ASDU_OK);
|
||
CHECK(floating.qualifier == 255);
|
||
CHECK(IEC60870_InformationObject_Encode(&floating.object, &default_parameters, 0, payload, sizeof(payload), &n) == IEC60870_ASDU_OK);
|
||
CHECK(memcmp(payload+3, raw_float, sizeof(raw_float)) == 0); /* NaN位模式与QPM全部保留。 */
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &default_parameters, bytes, sizeof(bytes), &header, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_ParameterNormalizedValue_Create(¶meter, 1, INT16_MIN, 0xFF) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, ¶meter.object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_ParameterNormalizedValue_Create(¶meter, 999, INT16_MAX, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, ¶meter.object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &wire, &n) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Parse(&default_parameters, wire, n, &info) == IEC60870_ASDU_OK && info.object_count == 2);
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 1, ¶meter, sizeof(parameter)) == IEC60870_ASDU_OK);
|
||
CHECK(parameter.object.ioa == 999 && parameter.value == INT16_MAX && parameter.qualifier == 0);
|
||
return 1;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
int passed = 0;
|
||
#define RUN(test) do { if (!(test)()) { return 1; } ++passed; } while (0)
|
||
RUN(test_all_layouts);
|
||
RUN(test_sequence_and_counts);
|
||
RUN(test_invalid_fields);
|
||
RUN(test_short_times);
|
||
RUN(test_constructor_atomicity);
|
||
RUN(test_file_segments);
|
||
RUN(test_numeric_and_parameter_fields);
|
||
printf("Extended ASDU: %d groups passed (37 additional types, short times, variable file segments)\n", passed);
|
||
return 0;
|
||
}
|
||
|