1199 lines
69 KiB
C
1199 lines
69 KiB
C
/* 独立字节向量、边界和失败原子性测试,不操作网络或系统时钟。
|
||
* 这里只用联合体为多种具体类型复用测试存储;生产API均采用独立类型。 */
|
||
#include "IEC60870_Asdu.h"
|
||
#include <limits.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#define CHECK(condition) do { if (!(condition)) { \
|
||
fprintf(stderr, "%s:%d: %s\n", __func__, __LINE__, #condition); return 0; \
|
||
} } while (0)
|
||
#define COUNT(array) (sizeof(array) / sizeof((array)[0]))
|
||
#define TIME_BYTES 0xD5, 0xDD, 0xA2, 0x8C, 0x9D, 0x02, 0x18
|
||
|
||
/* 显式采用2/2/3、249布局作为一组测试参数;公共模块不依赖104默认宏。 */
|
||
static const IEC60870_AppLayerParameters_t parameters104 = {1, 1, 2, 0, 2, 3, 249};
|
||
|
||
typedef struct {
|
||
uint8_t type, size, timed, command;
|
||
uint8_t payload[12];
|
||
} Vector;
|
||
|
||
/* 期望载荷沿用独立字节向量,不调用待测编码器计算。 */
|
||
static const Vector vectors[] = {
|
||
{1, 1, 0, 0, {0xF1}},
|
||
{3, 1, 0, 0, {0x93}},
|
||
{9, 3, 0, 0, {0x00,0x80,0x81}},
|
||
{11, 3, 0, 0, {0xFE,0xFF,0x10}},
|
||
{13, 5, 0, 0, {0x00,0x00,0xC0,0x3F,0x41}},
|
||
{15, 5, 0, 0, {0x00,0x00,0x00,0x80,0xF5}},
|
||
{30, 8, 1, 0, {0xF1,TIME_BYTES}},
|
||
{31, 8, 1, 0, {0x93,TIME_BYTES}},
|
||
{34, 10, 1, 0, {0x00,0x80,0x81,TIME_BYTES}},
|
||
{35, 10, 1, 0, {0xFE,0xFF,0x10,TIME_BYTES}},
|
||
{36, 12, 1, 0, {0x00,0x00,0xC0,0x3F,0x41,TIME_BYTES}},
|
||
{37, 12, 1, 0, {0x00,0x00,0x00,0x80,0xF5,TIME_BYTES}},
|
||
{45, 1, 0, 1, {0x8D}},
|
||
{46, 1, 0, 1, {0x86}},
|
||
{47, 1, 0, 1, {0x09}},
|
||
{48, 3, 0, 1, {0x00,0x80,0xC1}},
|
||
{49, 3, 0, 1, {0xFE,0xFF,0x7F}},
|
||
{50, 5, 0, 1, {0x00,0x00,0xC0,0x3F,0x80}},
|
||
{58, 8, 1, 1, {0x8D,TIME_BYTES}},
|
||
{59, 8, 1, 1, {0x86,TIME_BYTES}},
|
||
{60, 8, 1, 1, {0x09,TIME_BYTES}},
|
||
{61, 10, 1, 1, {0x00,0x80,0xC1,TIME_BYTES}},
|
||
{62, 10, 1, 1, {0xFE,0xFF,0x7F,TIME_BYTES}},
|
||
{63, 12, 1, 1, {0x00,0x00,0xC0,0x3F,0x80,TIME_BYTES}},
|
||
{100, 1, 0, 1, {0x14}},
|
||
{101, 1, 0, 1, {0xC5}},
|
||
{102, 0, 0, 1, {0}},
|
||
{103, 7, 1, 1, {TIME_BYTES}},
|
||
{105, 1, 0, 1, {0x02}},
|
||
{107, 9, 1, 1, {0x34,0x12,TIME_BYTES}},
|
||
};
|
||
|
||
typedef union {
|
||
IEC60870_SinglePointInformation_t v1;
|
||
IEC60870_DoublePointInformation_t v3;
|
||
IEC60870_MeasuredValueNormalized_t v9;
|
||
IEC60870_MeasuredValueScaled_t v11;
|
||
IEC60870_MeasuredValueShort_t v13;
|
||
IEC60870_IntegratedTotals_t v15;
|
||
IEC60870_SinglePointWithCP56Time2a_t v30;
|
||
IEC60870_DoublePointWithCP56Time2a_t v31;
|
||
IEC60870_MeasuredValueNormalizedWithCP56Time2a_t v34;
|
||
IEC60870_MeasuredValueScaledWithCP56Time2a_t v35;
|
||
IEC60870_MeasuredValueShortWithCP56Time2a_t v36;
|
||
IEC60870_IntegratedTotalsWithCP56Time2a_t v37;
|
||
IEC60870_SingleCommand_t v45;
|
||
IEC60870_DoubleCommand_t v46;
|
||
IEC60870_StepCommand_t v47;
|
||
IEC60870_SetpointCommandNormalized_t v48;
|
||
IEC60870_SetpointCommandScaled_t v49;
|
||
IEC60870_SetpointCommandShort_t v50;
|
||
IEC60870_SingleCommandWithCP56Time2a_t v58;
|
||
IEC60870_DoubleCommandWithCP56Time2a_t v59;
|
||
IEC60870_StepCommandWithCP56Time2a_t v60;
|
||
IEC60870_SetpointCommandNormalizedWithCP56Time2a_t v61;
|
||
IEC60870_SetpointCommandScaledWithCP56Time2a_t v62;
|
||
IEC60870_SetpointCommandShortWithCP56Time2a_t v63;
|
||
IEC60870_InterrogationCommand_t v100;
|
||
IEC60870_CounterInterrogationCommand_t v101;
|
||
IEC60870_ReadCommand_t v102;
|
||
IEC60870_ClockSynchronizationCommand_t v103;
|
||
IEC60870_ResetProcessCommand_t v105;
|
||
IEC60870_TestCommandWithCP56Time2a_t v107;
|
||
} TestObject;
|
||
|
||
static IEC60870_CP56Time2a_t Sample_Time(void)
|
||
{
|
||
IEC60870_CP56Time2a_t time = {{2024, 2, 29, 12, 34, 56, 789, 4}, 1, 1, 1};
|
||
return time;
|
||
}
|
||
|
||
static int Check_Time(const IEC60870_CP56Time2a_t *time)
|
||
{
|
||
CHECK(time->time.year == 2024 && time->time.month == 2 && time->time.day == 29);
|
||
CHECK(time->time.hour == 12 && time->time.minute == 34 && time->time.second == 56);
|
||
CHECK(time->time.ms == 789 && time->time.weekday == 4);
|
||
CHECK(time->is_invalid == 1 && time->is_summer_time == 1 && time->has_weekday == 1);
|
||
return 1;
|
||
}
|
||
|
||
static IEC60870_AsduHeader_t Header(void)
|
||
{
|
||
IEC60870_AsduHeader_t header = {0};
|
||
header.cause_of_transmission = IEC60870_ASDU_COT_SPONTANEOUS;
|
||
header.is_test = 1;
|
||
header.originator_address = 0x21;
|
||
header.common_address = 0x4567;
|
||
return header;
|
||
}
|
||
|
||
/* 为每个Type ID构造真实具体对象,测试应用调用方式及函数表绑定。 */
|
||
static IEC60870_AsduResult_t Sample_Object(uint8_t type, uint32_t ioa, TestObject *object)
|
||
{
|
||
IEC60870_CP56Time2a_t time = Sample_Time();
|
||
const IEC60870_BinaryCounterReading_t counter = {INT32_MIN, 21, 1, 1, 1};
|
||
switch (type) {
|
||
case 1: return IEC60870_SinglePointInformation_Create(&object->v1, ioa, 1, 0xF0);
|
||
case 3: return IEC60870_DoublePointInformation_Create(&object->v3, ioa, 3, 0x90);
|
||
case 9: return IEC60870_MeasuredValueNormalized_Create(&object->v9, ioa, INT16_MIN, 0x81);
|
||
case 11: return IEC60870_MeasuredValueScaled_Create(&object->v11, ioa, -2, 0x10);
|
||
case 13: return IEC60870_MeasuredValueShort_Create(&object->v13, ioa, 1.5f, 0x41);
|
||
case 15: return IEC60870_IntegratedTotals_Create(&object->v15, ioa, &counter);
|
||
case 30: return IEC60870_SinglePointWithCP56Time2a_Create(&object->v30, ioa, 1, 0xF0, &time);
|
||
case 31: return IEC60870_DoublePointWithCP56Time2a_Create(&object->v31, ioa, 3, 0x90, &time);
|
||
case 34: return IEC60870_MeasuredValueNormalizedWithCP56Time2a_Create(&object->v34, ioa, INT16_MIN, 0x81, &time);
|
||
case 35: return IEC60870_MeasuredValueScaledWithCP56Time2a_Create(&object->v35, ioa, -2, 0x10, &time);
|
||
case 36: return IEC60870_MeasuredValueShortWithCP56Time2a_Create(&object->v36, ioa, 1.5f, 0x41, &time);
|
||
case 37: return IEC60870_IntegratedTotalsWithCP56Time2a_Create(&object->v37, ioa, &counter, &time);
|
||
case 45: return IEC60870_SingleCommand_Create(&object->v45, ioa, 1, 1, 3);
|
||
case 46: return IEC60870_DoubleCommand_Create(&object->v46, ioa, 2, 1, 1);
|
||
case 47: return IEC60870_StepCommand_Create(&object->v47, ioa, 1, 0, 2);
|
||
case 48: return IEC60870_SetpointCommandNormalized_Create(&object->v48, ioa, INT16_MIN, 1, 65);
|
||
case 49: return IEC60870_SetpointCommandScaled_Create(&object->v49, ioa, -2, 0, 127);
|
||
case 50: return IEC60870_SetpointCommandShort_Create(&object->v50, ioa, 1.5f, 1, 0);
|
||
case 58: return IEC60870_SingleCommandWithCP56Time2a_Create(&object->v58, ioa, 1, 1, 3, &time);
|
||
case 59: return IEC60870_DoubleCommandWithCP56Time2a_Create(&object->v59, ioa, 2, 1, 1, &time);
|
||
case 60: return IEC60870_StepCommandWithCP56Time2a_Create(&object->v60, ioa, 1, 0, 2, &time);
|
||
case 61: return IEC60870_SetpointCommandNormalizedWithCP56Time2a_Create(&object->v61, ioa, INT16_MIN, 1, 65, &time);
|
||
case 62: return IEC60870_SetpointCommandScaledWithCP56Time2a_Create(&object->v62, ioa, -2, 0, 127, &time);
|
||
case 63: return IEC60870_SetpointCommandShortWithCP56Time2a_Create(&object->v63, ioa, 1.5f, 1, 0, &time);
|
||
case 100: return IEC60870_InterrogationCommand_Create(&object->v100, ioa, 20);
|
||
case 101: return IEC60870_CounterInterrogationCommand_Create(&object->v101, ioa, 5, 3);
|
||
case 102: return IEC60870_ReadCommand_Create(&object->v102, ioa);
|
||
case 103: return IEC60870_ClockSynchronizationCommand_Create(&object->v103, ioa, &time);
|
||
case 105: return IEC60870_ResetProcessCommand_Create(&object->v105, ioa, 2);
|
||
case 107: return IEC60870_TestCommandWithCP56Time2a_Create(&object->v107, ioa, 0x1234, &time);
|
||
default: return IEC60870_ASDU_UNSUPPORTED;
|
||
}
|
||
}
|
||
|
||
static IEC60870_InformationObject_t *Object_Base(TestObject *object, uint8_t type)
|
||
{
|
||
switch (type) {
|
||
case 1: return &object->v1.object;
|
||
case 3: return &object->v3.object;
|
||
case 9: return &object->v9.object;
|
||
case 11: return &object->v11.object;
|
||
case 13: return &object->v13.object;
|
||
case 15: return &object->v15.object;
|
||
case 30: return &object->v30.base.object;
|
||
case 31: return &object->v31.base.object;
|
||
case 34: return &object->v34.base.object;
|
||
case 35: return &object->v35.base.object;
|
||
case 36: return &object->v36.base.object;
|
||
case 37: return &object->v37.base.object;
|
||
case 45: return &object->v45.object;
|
||
case 46: return &object->v46.object;
|
||
case 47: return &object->v47.object;
|
||
case 48: return &object->v48.object;
|
||
case 49: return &object->v49.object;
|
||
case 50: return &object->v50.object;
|
||
case 58: return &object->v58.base.object;
|
||
case 59: return &object->v59.base.object;
|
||
case 60: return &object->v60.base.object;
|
||
case 61: return &object->v61.base.object;
|
||
case 62: return &object->v62.base.object;
|
||
case 63: return &object->v63.base.object;
|
||
case 100: return &object->v100.object;
|
||
case 101: return &object->v101.object;
|
||
case 102: return &object->v102.object;
|
||
case 103: return &object->v103.object;
|
||
case 105: return &object->v105.object;
|
||
case 107: return &object->v107.object;
|
||
default: return NULL;
|
||
}
|
||
}
|
||
|
||
static size_t Object_Size(uint8_t type)
|
||
{
|
||
switch (type) {
|
||
case 1: return sizeof(IEC60870_SinglePointInformation_t);
|
||
case 3: return sizeof(IEC60870_DoublePointInformation_t);
|
||
case 9: return sizeof(IEC60870_MeasuredValueNormalized_t);
|
||
case 11: return sizeof(IEC60870_MeasuredValueScaled_t);
|
||
case 13: return sizeof(IEC60870_MeasuredValueShort_t);
|
||
case 15: return sizeof(IEC60870_IntegratedTotals_t);
|
||
case 30: return sizeof(IEC60870_SinglePointWithCP56Time2a_t);
|
||
case 31: return sizeof(IEC60870_DoublePointWithCP56Time2a_t);
|
||
case 34: return sizeof(IEC60870_MeasuredValueNormalizedWithCP56Time2a_t);
|
||
case 35: return sizeof(IEC60870_MeasuredValueScaledWithCP56Time2a_t);
|
||
case 36: return sizeof(IEC60870_MeasuredValueShortWithCP56Time2a_t);
|
||
case 37: return sizeof(IEC60870_IntegratedTotalsWithCP56Time2a_t);
|
||
case 45: return sizeof(IEC60870_SingleCommand_t);
|
||
case 46: return sizeof(IEC60870_DoubleCommand_t);
|
||
case 47: return sizeof(IEC60870_StepCommand_t);
|
||
case 48: return sizeof(IEC60870_SetpointCommandNormalized_t);
|
||
case 49: return sizeof(IEC60870_SetpointCommandScaled_t);
|
||
case 50: return sizeof(IEC60870_SetpointCommandShort_t);
|
||
case 58: return sizeof(IEC60870_SingleCommandWithCP56Time2a_t);
|
||
case 59: return sizeof(IEC60870_DoubleCommandWithCP56Time2a_t);
|
||
case 60: return sizeof(IEC60870_StepCommandWithCP56Time2a_t);
|
||
case 61: return sizeof(IEC60870_SetpointCommandNormalizedWithCP56Time2a_t);
|
||
case 62: return sizeof(IEC60870_SetpointCommandScaledWithCP56Time2a_t);
|
||
case 63: return sizeof(IEC60870_SetpointCommandShortWithCP56Time2a_t);
|
||
case 100: return sizeof(IEC60870_InterrogationCommand_t);
|
||
case 101: return sizeof(IEC60870_CounterInterrogationCommand_t);
|
||
case 102: return sizeof(IEC60870_ReadCommand_t);
|
||
case 103: return sizeof(IEC60870_ClockSynchronizationCommand_t);
|
||
case 105: return sizeof(IEC60870_ResetProcessCommand_t);
|
||
case 107: return sizeof(IEC60870_TestCommandWithCP56Time2a_t);
|
||
default: return 0;
|
||
}
|
||
}
|
||
|
||
/* 直接测试各类型的载荷解析入口,独立于ASDU布局。 */
|
||
static IEC60870_AsduResult_t Parse_Object(uint8_t type, uint32_t ioa,
|
||
const uint8_t *payload, uint16_t length, TestObject *object)
|
||
{
|
||
switch (type) {
|
||
case 1: return IEC60870_SinglePointInformation_Get_From_Buffer(object == NULL ? NULL : &object->v1, ioa, payload, length);
|
||
case 3: return IEC60870_DoublePointInformation_Get_From_Buffer(object == NULL ? NULL : &object->v3, ioa, payload, length);
|
||
case 9: return IEC60870_MeasuredValueNormalized_Get_From_Buffer(object == NULL ? NULL : &object->v9, ioa, payload, length);
|
||
case 11: return IEC60870_MeasuredValueScaled_Get_From_Buffer(object == NULL ? NULL : &object->v11, ioa, payload, length);
|
||
case 13: return IEC60870_MeasuredValueShort_Get_From_Buffer(object == NULL ? NULL : &object->v13, ioa, payload, length);
|
||
case 15: return IEC60870_IntegratedTotals_Get_From_Buffer(object == NULL ? NULL : &object->v15, ioa, payload, length);
|
||
case 30: return IEC60870_SinglePointWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v30, ioa, payload, length);
|
||
case 31: return IEC60870_DoublePointWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v31, ioa, payload, length);
|
||
case 34: return IEC60870_MeasuredValueNormalizedWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v34, ioa, payload, length);
|
||
case 35: return IEC60870_MeasuredValueScaledWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v35, ioa, payload, length);
|
||
case 36: return IEC60870_MeasuredValueShortWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v36, ioa, payload, length);
|
||
case 37: return IEC60870_IntegratedTotalsWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v37, ioa, payload, length);
|
||
case 45: return IEC60870_SingleCommand_Get_From_Buffer(object == NULL ? NULL : &object->v45, ioa, payload, length);
|
||
case 46: return IEC60870_DoubleCommand_Get_From_Buffer(object == NULL ? NULL : &object->v46, ioa, payload, length);
|
||
case 47: return IEC60870_StepCommand_Get_From_Buffer(object == NULL ? NULL : &object->v47, ioa, payload, length);
|
||
case 48: return IEC60870_SetpointCommandNormalized_Get_From_Buffer(object == NULL ? NULL : &object->v48, ioa, payload, length);
|
||
case 49: return IEC60870_SetpointCommandScaled_Get_From_Buffer(object == NULL ? NULL : &object->v49, ioa, payload, length);
|
||
case 50: return IEC60870_SetpointCommandShort_Get_From_Buffer(object == NULL ? NULL : &object->v50, ioa, payload, length);
|
||
case 58: return IEC60870_SingleCommandWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v58, ioa, payload, length);
|
||
case 59: return IEC60870_DoubleCommandWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v59, ioa, payload, length);
|
||
case 60: return IEC60870_StepCommandWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v60, ioa, payload, length);
|
||
case 61: return IEC60870_SetpointCommandNormalizedWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v61, ioa, payload, length);
|
||
case 62: return IEC60870_SetpointCommandScaledWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v62, ioa, payload, length);
|
||
case 63: return IEC60870_SetpointCommandShortWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v63, ioa, payload, length);
|
||
case 100: return IEC60870_InterrogationCommand_Get_From_Buffer(object == NULL ? NULL : &object->v100, ioa, payload, length);
|
||
case 101: return IEC60870_CounterInterrogationCommand_Get_From_Buffer(object == NULL ? NULL : &object->v101, ioa, payload, length);
|
||
case 102: return IEC60870_ReadCommand_Get_From_Buffer(object == NULL ? NULL : &object->v102, ioa, payload, length);
|
||
case 103: return IEC60870_ClockSynchronizationCommand_Get_From_Buffer(object == NULL ? NULL : &object->v103, ioa, payload, length);
|
||
case 105: return IEC60870_ResetProcessCommand_Get_From_Buffer(object == NULL ? NULL : &object->v105, ioa, payload, length);
|
||
case 107: return IEC60870_TestCommandWithCP56Time2a_Get_From_Buffer(object == NULL ? NULL : &object->v107, ioa, payload, length);
|
||
default: return IEC60870_ASDU_UNSUPPORTED;
|
||
}
|
||
}
|
||
|
||
static int Check_Object(const Vector *v, const TestObject *object)
|
||
{
|
||
switch (v->type) {
|
||
case 1:
|
||
CHECK(object->v1.value == 1);
|
||
CHECK(object->v1.quality == 0xF0);
|
||
break;
|
||
case 3:
|
||
CHECK(object->v3.value == 3);
|
||
CHECK(object->v3.quality == 0x90);
|
||
break;
|
||
case 9:
|
||
CHECK(object->v9.value == INT16_MIN);
|
||
CHECK(object->v9.quality == 0x81);
|
||
break;
|
||
case 11:
|
||
CHECK(object->v11.value == -2);
|
||
CHECK(object->v11.quality == 0x10);
|
||
break;
|
||
case 13:
|
||
CHECK(object->v13.value == 1.5f);
|
||
CHECK(object->v13.quality == 0x41);
|
||
break;
|
||
case 15:
|
||
CHECK(object->v15.value.value == INT32_MIN);
|
||
CHECK(object->v15.value.sequence_number == 21);
|
||
CHECK(object->v15.value.has_carry == 1);
|
||
CHECK(object->v15.value.is_adjusted == 1);
|
||
CHECK(object->v15.value.is_invalid == 1);
|
||
break;
|
||
case 30:
|
||
CHECK(object->v30.base.value == 1);
|
||
CHECK(object->v30.base.quality == 0xF0);
|
||
CHECK(Check_Time(&object->v30.time));
|
||
break;
|
||
case 31:
|
||
CHECK(object->v31.base.value == 3);
|
||
CHECK(object->v31.base.quality == 0x90);
|
||
CHECK(Check_Time(&object->v31.time));
|
||
break;
|
||
case 34:
|
||
CHECK(object->v34.base.value == INT16_MIN);
|
||
CHECK(object->v34.base.quality == 0x81);
|
||
CHECK(Check_Time(&object->v34.time));
|
||
break;
|
||
case 35:
|
||
CHECK(object->v35.base.value == -2);
|
||
CHECK(object->v35.base.quality == 0x10);
|
||
CHECK(Check_Time(&object->v35.time));
|
||
break;
|
||
case 36:
|
||
CHECK(object->v36.base.value == 1.5f);
|
||
CHECK(object->v36.base.quality == 0x41);
|
||
CHECK(Check_Time(&object->v36.time));
|
||
break;
|
||
case 37:
|
||
CHECK(object->v37.base.value.value == INT32_MIN);
|
||
CHECK(object->v37.base.value.sequence_number == 21);
|
||
CHECK(object->v37.base.value.has_carry == 1);
|
||
CHECK(object->v37.base.value.is_adjusted == 1);
|
||
CHECK(object->v37.base.value.is_invalid == 1);
|
||
CHECK(Check_Time(&object->v37.time));
|
||
break;
|
||
case 45:
|
||
CHECK(object->v45.state == 1);
|
||
CHECK(object->v45.is_select == 1);
|
||
CHECK(object->v45.qualifier == 3);
|
||
break;
|
||
case 46:
|
||
CHECK(object->v46.state == 2);
|
||
CHECK(object->v46.is_select == 1);
|
||
CHECK(object->v46.qualifier == 1);
|
||
break;
|
||
case 47:
|
||
CHECK(object->v47.state == 1);
|
||
CHECK(object->v47.is_select == 0);
|
||
CHECK(object->v47.qualifier == 2);
|
||
break;
|
||
case 48:
|
||
CHECK(object->v48.value == INT16_MIN);
|
||
CHECK(object->v48.is_select == 1);
|
||
CHECK(object->v48.qualifier == 65);
|
||
break;
|
||
case 49:
|
||
CHECK(object->v49.value == -2);
|
||
CHECK(object->v49.is_select == 0);
|
||
CHECK(object->v49.qualifier == 127);
|
||
break;
|
||
case 50:
|
||
CHECK(object->v50.value == 1.5f);
|
||
CHECK(object->v50.is_select == 1);
|
||
CHECK(object->v50.qualifier == 0);
|
||
break;
|
||
case 58:
|
||
CHECK(object->v58.base.state == 1);
|
||
CHECK(object->v58.base.is_select == 1);
|
||
CHECK(object->v58.base.qualifier == 3);
|
||
CHECK(Check_Time(&object->v58.time));
|
||
break;
|
||
case 59:
|
||
CHECK(object->v59.base.state == 2);
|
||
CHECK(object->v59.base.is_select == 1);
|
||
CHECK(object->v59.base.qualifier == 1);
|
||
CHECK(Check_Time(&object->v59.time));
|
||
break;
|
||
case 60:
|
||
CHECK(object->v60.base.state == 1);
|
||
CHECK(object->v60.base.is_select == 0);
|
||
CHECK(object->v60.base.qualifier == 2);
|
||
CHECK(Check_Time(&object->v60.time));
|
||
break;
|
||
case 61:
|
||
CHECK(object->v61.base.value == INT16_MIN);
|
||
CHECK(object->v61.base.is_select == 1);
|
||
CHECK(object->v61.base.qualifier == 65);
|
||
CHECK(Check_Time(&object->v61.time));
|
||
break;
|
||
case 62:
|
||
CHECK(object->v62.base.value == -2);
|
||
CHECK(object->v62.base.is_select == 0);
|
||
CHECK(object->v62.base.qualifier == 127);
|
||
CHECK(Check_Time(&object->v62.time));
|
||
break;
|
||
case 63:
|
||
CHECK(object->v63.base.value == 1.5f);
|
||
CHECK(object->v63.base.is_select == 1);
|
||
CHECK(object->v63.base.qualifier == 0);
|
||
CHECK(Check_Time(&object->v63.time));
|
||
break;
|
||
case 100:
|
||
CHECK(object->v100.qualifier == 20);
|
||
break;
|
||
case 101:
|
||
CHECK(object->v101.qualifier == 5);
|
||
CHECK(object->v101.freeze == 3);
|
||
break;
|
||
case 102:
|
||
|
||
break;
|
||
case 103:
|
||
|
||
CHECK(Check_Time(&object->v103.time));
|
||
break;
|
||
case 105:
|
||
CHECK(object->v105.qualifier == 2);
|
||
break;
|
||
case 107:
|
||
CHECK(object->v107.sequence == 0x1234);
|
||
CHECK(Check_Time(&object->v107.time));
|
||
break;
|
||
default: CHECK(0);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* 独立拼接可变宽度头,避免把容器编码结果再次用作期望值。 */
|
||
static uint16_t Wire_Header(const IEC60870_AppLayerParameters_t *p,
|
||
uint8_t type, uint8_t vsq, uint8_t cot, uint8_t *frame)
|
||
{
|
||
uint16_t n = 0;
|
||
frame[n++] = type; frame[n++] = vsq; frame[n++] = cot;
|
||
if (p->sizeOfCOT == 2) { frame[n++] = 0x21; }
|
||
frame[n++] = 0x67;
|
||
if (p->sizeOfCA == 2) { frame[n++] = 0x45; }
|
||
return n;
|
||
}
|
||
|
||
static int Reject_Frame(const IEC60870_AppLayerParameters_t *p,
|
||
const uint8_t *frame, uint16_t length, IEC60870_AsduResult_t expected)
|
||
{
|
||
IEC60870_AsduInfo_t info;
|
||
unsigned char saved[sizeof(info)];
|
||
memset(&info, 0xA5, sizeof(info));
|
||
memcpy(saved, &info, sizeof(info));
|
||
CHECK(IEC60870_Asdu_Parse(p, frame, length, &info) == expected);
|
||
CHECK(memcmp(saved, &info, sizeof(info)) == 0);
|
||
return 1;
|
||
}
|
||
|
||
/* 12种配置 x(12监视类型 x 两种SQ + 18命令类型)=504种完整布局。
|
||
* 对命令新增编码测试;监视使用两个对象,验证连续/独立地址和所有字段。 */
|
||
static int test_wire_matrix(void)
|
||
{
|
||
static const uint8_t addresses[2][3] = {{0x56,0x34,0x12}, {0x57,0x34,0x12}};
|
||
IEC60870_AppLayerParameters_t p = parameters104;
|
||
size_t v;
|
||
for (p.sizeOfCOT = 1; p.sizeOfCOT <= 2; ++p.sizeOfCOT) {
|
||
for (p.sizeOfCA = 1; p.sizeOfCA <= 2; ++p.sizeOfCA) {
|
||
for (p.sizeOfIOA = 1; p.sizeOfIOA <= 3; ++p.sizeOfIOA) {
|
||
uint32_t base = p.sizeOfIOA == 1 ? 0x56U : p.sizeOfIOA == 2 ? 0x3456U : 0x123456U;
|
||
for (v = 0; v < COUNT(vectors); ++v) {
|
||
const Vector *vector = &vectors[v];
|
||
int sq;
|
||
for (sq = 0; sq <= (vector->command ? 0 : 1); ++sq) {
|
||
uint8_t frame[64], expected[64], saved[64];
|
||
IEC60870_Asdu_t asdu, saved_asdu;
|
||
IEC60870_AsduInfo_t info;
|
||
IEC60870_AsduHeader_t header = Header();
|
||
TestObject input, output;
|
||
const uint8_t *data = NULL;
|
||
uint16_t length = 77, h, n;
|
||
int i, count = vector->command ? 1 : 2;
|
||
header.common_address = p.sizeOfCA == 1 ? 0x67 : 0x4567;
|
||
header.cause_of_transmission = vector->command ? 6 : 3;
|
||
header.is_negative_confirm = vector->command;
|
||
memset(frame, 0xA5, sizeof(frame));
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame + 1, 62, &header, sq) == IEC60870_ASDU_OK);
|
||
CHECK(asdu.type_id == 0 && frame[1] == 0 && asdu.object_count == 0);
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &data, &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(data == NULL && length == 77);
|
||
h = Wire_Header(&p, vector->type, (uint8_t)(count | (sq << 7)),
|
||
vector->command ? 0xC6 : 0x83, expected);
|
||
n = h;
|
||
for (i = 0; i < count; ++i) {
|
||
CHECK(Sample_Object(vector->type, base + (uint32_t)i, &input) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, Object_Base(&input, vector->type)) == IEC60870_ASDU_OK);
|
||
if (!sq || i == 0) {
|
||
memcpy(expected + n, addresses[i], (size_t)p.sizeOfIOA);
|
||
n += (uint16_t)p.sizeOfIOA;
|
||
}
|
||
memcpy(expected + n, vector->payload, vector->size); n += vector->size;
|
||
}
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &data, &length) == IEC60870_ASDU_OK);
|
||
CHECK(length == n && data == frame + 1 && memcmp(data, expected, n) == 0);
|
||
CHECK(frame[0] == 0xA5 && frame[n + 1] == 0xA5 && asdu.parameters == &p);
|
||
CHECK(IEC60870_Asdu_Parse(&p, expected, n, &info) == IEC60870_ASDU_OK);
|
||
CHECK(info.type_id == vector->type && info.object_count == count && info.is_sequence == sq);
|
||
CHECK(info.parameters == &p && info.information_objects == expected + h);
|
||
CHECK(info.common_address == header.common_address && info.is_test == 1);
|
||
CHECK(info.originator_address == (p.sizeOfCOT == 2 ? 0x21 : 0));
|
||
CHECK(info.cause_of_transmission == (vector->command ? 6 : 3));
|
||
CHECK(info.is_negative_confirm == vector->command);
|
||
for (i = 0; i < count; ++i) {
|
||
unsigned char snapshot[sizeof(output)];
|
||
memset(&output, 0xA5, sizeof(output)); memcpy(snapshot, &output, sizeof(output));
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, (uint8_t)i, &output, Object_Size(vector->type) - 1) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(snapshot, &output, sizeof(output)) == 0);
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, (uint8_t)i, &output, Object_Size(vector->type)) == IEC60870_ASDU_OK);
|
||
CHECK(Object_Base(&output, vector->type)->ioa == base + (uint32_t)i);
|
||
CHECK(IEC60870_InformationObject_Get_Type(Object_Base(&output, vector->type)) == vector->type);
|
||
CHECK(Check_Object(vector, &output));
|
||
CHECK(memcmp((unsigned char *)&output + Object_Size(vector->type),
|
||
snapshot + Object_Size(vector->type), sizeof(output) - Object_Size(vector->type)) == 0);
|
||
}
|
||
for (i = 0; i < n; ++i) { CHECK(Reject_Frame(&p, expected, (uint16_t)i, IEC60870_ASDU_ERROR)); }
|
||
expected[n] = 0;
|
||
CHECK(Reject_Frame(&p, expected, (uint16_t)(n + 1), IEC60870_ASDU_ERROR));
|
||
/* 命令数量上限;第二个对象不改变容器或原文。 */
|
||
if (vector->command) {
|
||
memcpy(saved, frame, sizeof(frame)); memcpy(&saved_asdu, &asdu, sizeof(asdu));
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, Object_Base(&input, vector->type)) == IEC60870_ASDU_FULL);
|
||
CHECK(memcmp(saved, frame, sizeof(frame)) == 0 && memcmp(&saved_asdu, &asdu, sizeof(asdu)) == 0);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 1) == IEC60870_ASDU_OK);
|
||
memcpy(saved, frame, sizeof(frame)); memcpy(&saved_asdu, &asdu, sizeof(asdu));
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, Object_Base(&input, vector->type)) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, frame, sizeof(frame)) == 0 && memcmp(&saved_asdu, &asdu, sizeof(asdu)) == 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int test_object_entrypoints(void)
|
||
{
|
||
size_t v;
|
||
for (v = 0; v < COUNT(vectors); ++v) {
|
||
const Vector *vector = &vectors[v];
|
||
TestObject input, output;
|
||
unsigned char snapshot[sizeof(output)];
|
||
uint8_t encoded[20], saved[20], payload[13];
|
||
uint16_t length = 99, n;
|
||
CHECK(Sample_Object(vector->type, 0x123456, &input) == IEC60870_ASDU_OK);
|
||
memset(encoded, 0xA5, sizeof(encoded));
|
||
CHECK(IEC60870_InformationObject_Encode(Object_Base(&input, vector->type), ¶meters104, 0,
|
||
encoded, sizeof(encoded), &length) == IEC60870_ASDU_OK);
|
||
CHECK(length == vector->size + 3U && encoded[0] == 0x56 && encoded[1] == 0x34 && encoded[2] == 0x12);
|
||
CHECK(memcmp(encoded + 3, vector->payload, vector->size) == 0 && encoded[length] == 0xA5);
|
||
memcpy(saved, encoded, sizeof(saved)); length = 99;
|
||
CHECK(IEC60870_InformationObject_Encode(Object_Base(&input, vector->type), ¶meters104, 0,
|
||
encoded, (uint16_t)(vector->size + 2), &length) == IEC60870_ASDU_FULL);
|
||
CHECK(length == 99 && memcmp(encoded, saved, sizeof(saved)) == 0);
|
||
if (!vector->command) {
|
||
CHECK(IEC60870_InformationObject_Encode(Object_Base(&input, vector->type), ¶meters104, 1,
|
||
encoded, sizeof(encoded), &length) == IEC60870_ASDU_OK);
|
||
CHECK(length == vector->size && memcmp(encoded, vector->payload, length) == 0);
|
||
} else {
|
||
CHECK(IEC60870_InformationObject_Encode(Object_Base(&input, vector->type), ¶meters104, 1,
|
||
encoded, sizeof(encoded), &length) == IEC60870_ASDU_ERROR);
|
||
}
|
||
memcpy(payload, vector->payload, vector->size);
|
||
CHECK(Parse_Object(vector->type, 0x123456, payload, vector->size, &output) == IEC60870_ASDU_OK);
|
||
memcpy(payload + 1, vector->payload, vector->size);
|
||
CHECK(Parse_Object(vector->type, 0x123456, payload + 1, vector->size, &output) == IEC60870_ASDU_OK);
|
||
memset(payload, 0, sizeof(payload)); /* 按值对象不借用载荷。 */
|
||
CHECK(Check_Object(vector, &output));
|
||
CHECK(Object_Base(&output, vector->type)->ioa == 0x123456);
|
||
memset(&output, 0xA5, sizeof(output)); memcpy(snapshot, &output, sizeof(output));
|
||
for (n = 0; n < vector->size; ++n) {
|
||
CHECK(Parse_Object(vector->type, 0, vector->payload, n, &output) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(snapshot, &output, sizeof(output)) == 0);
|
||
}
|
||
CHECK(Parse_Object(vector->type, 0, vector->payload, (uint16_t)(vector->size + 1), &output) == IEC60870_ASDU_ERROR);
|
||
CHECK(Parse_Object(vector->type, 0x1000000, vector->payload, vector->size, &output) == IEC60870_ASDU_ERROR);
|
||
CHECK(Parse_Object(vector->type, 0, vector->payload, vector->size, NULL) == IEC60870_ASDU_ERROR);
|
||
if (vector->size != 0U) {
|
||
CHECK(Parse_Object(vector->type, 0, NULL, vector->size, &output) == IEC60870_ASDU_ERROR);
|
||
}
|
||
CHECK(memcmp(snapshot, &output, sizeof(output)) == 0);
|
||
Object_Base(&input, vector->type)->virtual_function_table = NULL;
|
||
CHECK(IEC60870_InformationObject_Get_Type(Object_Base(&input, vector->type)) == 0);
|
||
CHECK(IEC60870_InformationObject_Encode(Object_Base(&input, vector->type), ¶meters104, 0,
|
||
encoded, sizeof(encoded), &length) == IEC60870_ASDU_ERROR);
|
||
}
|
||
{
|
||
IEC60870_ReadCommand_t read;
|
||
CHECK(IEC60870_ReadCommand_Get_From_Buffer(&read, 7, NULL, 0) == IEC60870_ASDU_OK && read.object.ioa == 7);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int test_capacity_and_addresses(void)
|
||
{
|
||
IEC60870_AppLayerParameters_t p = parameters104;
|
||
IEC60870_Asdu_t asdu, saved_asdu;
|
||
IEC60870_AsduInfo_t info;
|
||
IEC60870_AsduHeader_t header = Header();
|
||
IEC60870_MeasuredValueNormalized_t value;
|
||
IEC60870_SinglePointInformation_t point, decoded;
|
||
uint8_t frame[300], before[300];
|
||
const uint8_t *data = NULL;
|
||
uint16_t length = 0;
|
||
unsigned int i;
|
||
memset(frame, 0xA5, sizeof(frame));
|
||
CHECK(IEC60870_MeasuredValueNormalized_Create(&value, 0, -1, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 1) == IEC60870_ASDU_OK);
|
||
for (i = 0; i < 80; ++i) {
|
||
value.object.ioa = i;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &value.object) == IEC60870_ASDU_OK);
|
||
}
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &data, &length) == IEC60870_ASDU_OK && length == 249);
|
||
CHECK(asdu.capacity == 249 && frame[1] == 0xD0 && frame[249] == 0xA5);
|
||
memcpy(before, frame, sizeof(frame)); memcpy(&saved_asdu, &asdu, sizeof(asdu));
|
||
value.object.ioa = 80;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &value.object) == IEC60870_ASDU_FULL);
|
||
CHECK(memcmp(before, frame, sizeof(frame)) == 0 && memcmp(&saved_asdu, &asdu, sizeof(asdu)) == 0);
|
||
/* FULL后开启下一条并重试原对象。 */
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 1) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &value.object) == IEC60870_ASDU_OK && asdu.object_count == 1);
|
||
|
||
p.sizeOfCOT = 1; p.sizeOfCA = 1; p.sizeOfIOA = 2; p.maxSizeOfASDU = 254;
|
||
header.common_address = 0x67;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_OK);
|
||
for (i = 0; i < 50; ++i) {
|
||
value.object.ioa = i;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &value.object) == IEC60870_ASDU_OK);
|
||
}
|
||
CHECK(asdu.length == 254 && asdu.capacity == 254 && frame[254] == 0xA5);
|
||
memcpy(before, frame, sizeof(frame)); memcpy(&saved_asdu, &asdu, sizeof(asdu));
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &value.object) == IEC60870_ASDU_FULL);
|
||
CHECK(memcmp(before, frame, sizeof(frame)) == 0 && memcmp(&saved_asdu, &asdu, sizeof(asdu)) == 0);
|
||
CHECK(IEC60870_Asdu_Parse(&p, frame, 254, &info) == IEC60870_ASDU_OK);
|
||
CHECK(!IEC60870_Parse_ASDU_Header(¶meters104, frame, 254, &info));
|
||
|
||
/* 首对象装不下:类型和VSQ保持空状态;参数上限与实际容量都要生效。 */
|
||
for (i = 0; i < 3; ++i) {
|
||
p.maxSizeOfASDU = i == 0 ? 4 : i == 1 ? 8 : 254;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, i == 2 ? 8 : sizeof(frame), &header, 0) == IEC60870_ASDU_OK);
|
||
memcpy(before, frame, sizeof(frame)); memcpy(&saved_asdu, &asdu, sizeof(asdu));
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &value.object) == IEC60870_ASDU_FULL);
|
||
CHECK(asdu.type_id == 0 && asdu.object_count == 0 && asdu.length == 4);
|
||
CHECK(memcmp(before, frame, sizeof(frame)) == 0 && memcmp(&saved_asdu, &asdu, sizeof(asdu)) == 0);
|
||
}
|
||
p.maxSizeOfASDU = 9;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &value.object) == IEC60870_ASDU_OK && asdu.length == 9);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &value.object) == IEC60870_ASDU_FULL);
|
||
p.maxSizeOfASDU = 254;
|
||
|
||
CHECK(IEC60870_SinglePointInformation_Create(&point, 0, 1, 0) == IEC60870_ASDU_OK);
|
||
for (p.sizeOfIOA = 1; p.sizeOfIOA <= 3; ++p.sizeOfIOA) {
|
||
uint32_t max_ioa = p.sizeOfIOA == 1 ? 0xFFU : p.sizeOfIOA == 2 ? 0xFFFFU : 0xFFFFFFU;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 1) == IEC60870_ASDU_OK);
|
||
point.object.ioa = max_ioa - 1U;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
++point.object.ioa;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Parse(&p, frame, asdu.length, &info) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 1, &decoded, sizeof(decoded)) == IEC60870_ASDU_OK && decoded.object.ioa == max_ioa);
|
||
memcpy(before, frame, sizeof(frame)); memcpy(&saved_asdu, &asdu, sizeof(asdu));
|
||
++point.object.ioa;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_ERROR);
|
||
point.object.ioa = 0;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(before, frame, sizeof(frame)) == 0 && memcmp(&saved_asdu, &asdu, sizeof(asdu)) == 0);
|
||
frame[4] = 0xFF; /* SQ首地址改为上限,隐含的第二个地址溢出。 */
|
||
CHECK(Reject_Frame(&p, frame, asdu.length, IEC60870_ASDU_ERROR));
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 1) == IEC60870_ASDU_OK);
|
||
for (i = 0; i < 127; ++i) {
|
||
point.object.ioa = i;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
}
|
||
point.object.ioa = 127;
|
||
memcpy(before, frame, sizeof(frame)); memcpy(&saved_asdu, &asdu, sizeof(asdu));
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_FULL);
|
||
CHECK(frame[1] == 0xFF && memcmp(before, frame, sizeof(frame)) == 0);
|
||
CHECK(memcmp(&saved_asdu, &asdu, sizeof(asdu)) == 0);
|
||
}
|
||
/* SQ=0允许不连续的独立地址。 */
|
||
p.sizeOfIOA = 3;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_OK);
|
||
point.object.ioa = 7;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
point.object.ioa = 0x123456;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Parse(&p, frame, asdu.length, &info) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 1, &decoded, sizeof(decoded)) == IEC60870_ASDU_OK && decoded.object.ioa == 0x123456);
|
||
return 1;
|
||
}
|
||
|
||
static int test_malformed_payloads(void)
|
||
{
|
||
size_t v;
|
||
for (v = 0; v < COUNT(vectors); ++v) {
|
||
const Vector *vector = &vectors[v];
|
||
uint8_t frame[64];
|
||
uint16_t n = Wire_Header(¶meters104, vector->type, 1, 3, frame);
|
||
uint16_t h = n;
|
||
TestObject output;
|
||
unsigned char saved[sizeof(output)];
|
||
frame[n++] = 0; frame[n++] = 0; frame[n++] = 0;
|
||
memcpy(frame + n, vector->payload, vector->size); n += vector->size;
|
||
if (vector->command) {
|
||
frame[1] = 0x81;
|
||
CHECK(Reject_Frame(¶meters104, frame, n, IEC60870_ASDU_ERROR));
|
||
frame[1] = 2;
|
||
memcpy(frame + n, frame + h, n - h);
|
||
CHECK(Reject_Frame(¶meters104, frame, (uint16_t)(n + n - h), IEC60870_ASDU_ERROR));
|
||
frame[1] = 1;
|
||
}
|
||
if (vector->timed) {
|
||
frame[n - 5] |= 0x40; /* CP56分钟保留位。 */
|
||
} else if (vector->type == 1 || vector->type == 45) {
|
||
frame[h + 3] |= 2;
|
||
} else if (vector->type == 3) {
|
||
frame[h + 3] |= 4;
|
||
} else if (vector->type == 9 || vector->type == 11 || vector->type == 13) {
|
||
frame[n - 1] |= 2;
|
||
} else if (vector->type == 46 || vector->type == 47) {
|
||
frame[h + 3] |= 3; /* 双命令/步调节状态3非法。 */
|
||
} else { continue; }
|
||
CHECK(Reject_Frame(¶meters104, frame, n, IEC60870_ASDU_ERROR));
|
||
memset(&output, 0xA5, sizeof(output)); memcpy(saved, &output, sizeof(output));
|
||
CHECK(Parse_Object(vector->type, 0, frame + h + 3, vector->size, &output) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &output, sizeof(output)) == 0);
|
||
}
|
||
{
|
||
uint8_t frame[] = {1,0x82,3,0,1,0,0,0,0,1,2};
|
||
CHECK(Reject_Frame(¶meters104, frame, sizeof(frame), IEC60870_ASDU_ERROR)); /* 后部对象错误 */
|
||
frame[1] = 0;
|
||
CHECK(Reject_Frame(¶meters104, frame, 6, IEC60870_ASDU_ERROR));
|
||
frame[0] = 250;
|
||
CHECK(Reject_Frame(¶meters104, frame, 6, IEC60870_ASDU_UNSUPPORTED));
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int test_constructor_failures(void)
|
||
{
|
||
TestObject object;
|
||
unsigned char saved[sizeof(object)];
|
||
IEC60870_CP56Time2a_t time = Sample_Time();
|
||
IEC60870_CP56Time2a_t bad_time = time;
|
||
IEC60870_BinaryCounterReading_t counter = {INT32_MIN, 21, 1, 1, 1};
|
||
IEC60870_BinaryCounterReading_t bad_counter = counter;
|
||
bad_time.time.day = 30; /* 不存在的2024-02-30。 */
|
||
bad_counter.sequence_number = 32;
|
||
memset(&object, 0xA5, sizeof(object)); memcpy(saved, &object, sizeof(object));
|
||
CHECK(IEC60870_SinglePointInformation_Create(NULL, 0, 1, 0xF0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SinglePointInformation_Create(&object.v1, 0x1000000, 1, 0xF0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SinglePointInformation_Create(&object.v1, 0, 1, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_DoublePointInformation_Create(NULL, 0, 3, 0x90) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoublePointInformation_Create(&object.v3, 0x1000000, 3, 0x90) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoublePointInformation_Create(&object.v3, 0, 3, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_MeasuredValueNormalized_Create(NULL, 0, INT16_MIN, 0x81) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueNormalized_Create(&object.v9, 0x1000000, INT16_MIN, 0x81) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueNormalized_Create(&object.v9, 0, 0, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_MeasuredValueScaled_Create(NULL, 0, -2, 0x10) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueScaled_Create(&object.v11, 0x1000000, -2, 0x10) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueScaled_Create(&object.v11, 0, 0, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_MeasuredValueShort_Create(NULL, 0, 1.5f, 0x41) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueShort_Create(&object.v13, 0x1000000, 1.5f, 0x41) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueShort_Create(&object.v13, 0, 0, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_IntegratedTotals_Create(NULL, 0, &counter) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_IntegratedTotals_Create(&object.v15, 0x1000000, &counter) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_IntegratedTotals_Create(&object.v15, 0, &bad_counter) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SinglePointWithCP56Time2a_Create(NULL, 0, 1, 0xF0, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SinglePointWithCP56Time2a_Create(&object.v30, 0x1000000, 1, 0xF0, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SinglePointWithCP56Time2a_Create(&object.v30, 0, 1, 0xF0, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_DoublePointWithCP56Time2a_Create(NULL, 0, 3, 0x90, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoublePointWithCP56Time2a_Create(&object.v31, 0x1000000, 3, 0x90, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoublePointWithCP56Time2a_Create(&object.v31, 0, 3, 0x90, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_MeasuredValueNormalizedWithCP56Time2a_Create(NULL, 0, INT16_MIN, 0x81, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueNormalizedWithCP56Time2a_Create(&object.v34, 0x1000000, INT16_MIN, 0x81, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueNormalizedWithCP56Time2a_Create(&object.v34, 0, INT16_MIN, 0x81, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_MeasuredValueScaledWithCP56Time2a_Create(NULL, 0, -2, 0x10, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueScaledWithCP56Time2a_Create(&object.v35, 0x1000000, -2, 0x10, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueScaledWithCP56Time2a_Create(&object.v35, 0, -2, 0x10, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_MeasuredValueShortWithCP56Time2a_Create(NULL, 0, 1.5f, 0x41, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueShortWithCP56Time2a_Create(&object.v36, 0x1000000, 1.5f, 0x41, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_MeasuredValueShortWithCP56Time2a_Create(&object.v36, 0, 1.5f, 0x41, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_IntegratedTotalsWithCP56Time2a_Create(NULL, 0, &counter, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_IntegratedTotalsWithCP56Time2a_Create(&object.v37, 0x1000000, &counter, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_IntegratedTotalsWithCP56Time2a_Create(&object.v37, 0, &counter, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SingleCommand_Create(NULL, 0, 1, 1, 3) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SingleCommand_Create(&object.v45, 0x1000000, 1, 1, 3) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SingleCommand_Create(&object.v45, 0, 2, 1, 3) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_DoubleCommand_Create(NULL, 0, 2, 1, 1) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoubleCommand_Create(&object.v46, 0x1000000, 2, 1, 1) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoubleCommand_Create(&object.v46, 0, 0, 1, 1) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_StepCommand_Create(NULL, 0, 1, 0, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_StepCommand_Create(&object.v47, 0x1000000, 1, 0, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_StepCommand_Create(&object.v47, 0, 3, 0, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SetpointCommandNormalized_Create(NULL, 0, INT16_MIN, 1, 65) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandNormalized_Create(&object.v48, 0x1000000, INT16_MIN, 1, 65) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandNormalized_Create(&object.v48, 0, 0, 1, 128) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SetpointCommandScaled_Create(NULL, 0, -2, 0, 127) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandScaled_Create(&object.v49, 0x1000000, -2, 0, 127) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandScaled_Create(&object.v49, 0, 0, 2, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SetpointCommandShort_Create(NULL, 0, 1.5f, 1, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandShort_Create(&object.v50, 0x1000000, 1.5f, 1, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandShort_Create(&object.v50, 0, 0, -1, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SingleCommandWithCP56Time2a_Create(NULL, 0, 1, 1, 3, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SingleCommandWithCP56Time2a_Create(&object.v58, 0x1000000, 1, 1, 3, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SingleCommandWithCP56Time2a_Create(&object.v58, 0, 1, 1, 3, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_DoubleCommandWithCP56Time2a_Create(NULL, 0, 2, 1, 1, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoubleCommandWithCP56Time2a_Create(&object.v59, 0x1000000, 2, 1, 1, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoubleCommandWithCP56Time2a_Create(&object.v59, 0, 2, 1, 1, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_StepCommandWithCP56Time2a_Create(NULL, 0, 1, 0, 2, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_StepCommandWithCP56Time2a_Create(&object.v60, 0x1000000, 1, 0, 2, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_StepCommandWithCP56Time2a_Create(&object.v60, 0, 1, 0, 2, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SetpointCommandNormalizedWithCP56Time2a_Create(NULL, 0, INT16_MIN, 1, 65, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandNormalizedWithCP56Time2a_Create(&object.v61, 0x1000000, INT16_MIN, 1, 65, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandNormalizedWithCP56Time2a_Create(&object.v61, 0, INT16_MIN, 1, 65, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SetpointCommandScaledWithCP56Time2a_Create(NULL, 0, -2, 0, 127, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandScaledWithCP56Time2a_Create(&object.v62, 0x1000000, -2, 0, 127, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandScaledWithCP56Time2a_Create(&object.v62, 0, -2, 0, 127, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SetpointCommandShortWithCP56Time2a_Create(NULL, 0, 1.5f, 1, 0, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandShortWithCP56Time2a_Create(&object.v63, 0x1000000, 1.5f, 1, 0, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SetpointCommandShortWithCP56Time2a_Create(&object.v63, 0, 1.5f, 1, 0, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_InterrogationCommand_Create(NULL, 0, 20) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_InterrogationCommand_Create(&object.v100, 0x1000000, 20) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_InterrogationCommand_Create(&object.v100, 0, 256) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_CounterInterrogationCommand_Create(NULL, 0, 5, 3) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_CounterInterrogationCommand_Create(&object.v101, 0x1000000, 5, 3) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_CounterInterrogationCommand_Create(&object.v101, 0, 64, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_ReadCommand_Create(NULL, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_ReadCommand_Create(&object.v102, 0x1000000) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_ClockSynchronizationCommand_Create(NULL, 0, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_ClockSynchronizationCommand_Create(&object.v103, 0x1000000, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_ClockSynchronizationCommand_Create(&object.v103, 0, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_ResetProcessCommand_Create(NULL, 0, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_ResetProcessCommand_Create(&object.v105, 0x1000000, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_ResetProcessCommand_Create(&object.v105, 0, -1) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_TestCommandWithCP56Time2a_Create(NULL, 0, 0x1234, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_TestCommandWithCP56Time2a_Create(&object.v107, 0x1000000, 0x1234, &time) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_TestCommandWithCP56Time2a_Create(&object.v107, 0, 0, &bad_time) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
CHECK(IEC60870_SinglePointInformation_Create(&object.v1, 0, -1, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_DoublePointInformation_Create(&object.v3, 0, 4, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SingleCommand_Create(&object.v45, 0, 1, 0, 32) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_CounterInterrogationCommand_Create(&object.v101, 0, 1, 4) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_IntegratedTotals_Create(&object.v15, 0, NULL) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_SinglePointWithCP56Time2a_Create(&object.v30, 0, 1, 0, NULL) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_ClockSynchronizationCommand_Create(&object.v103, 0, NULL) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(saved, &object, sizeof(object)) == 0);
|
||
return 1;
|
||
}
|
||
|
||
/* 编码时重新检查对象,不依赖Create曾经检查过字段。 */
|
||
static int test_add_atomicity(void)
|
||
{
|
||
uint8_t frame[64], before[64];
|
||
IEC60870_Asdu_t asdu, saved;
|
||
IEC60870_AsduHeader_t header = Header();
|
||
IEC60870_SinglePointInformation_t point;
|
||
IEC60870_DoublePointInformation_t double_point;
|
||
IEC60870_SinglePointWithCP56Time2a_t timed;
|
||
IEC60870_CP56Time2a_t time = Sample_Time();
|
||
IEC60870_AsduInfo_t info;
|
||
unsigned char object_before[sizeof(point)];
|
||
CHECK(IEC60870_SinglePointInformation_Create(&point, 5, 1, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_DoublePointInformation_Create(&double_point, 6, 2, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, ¶meters104, frame, sizeof(frame), &header, 1) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
memcpy(before, frame, sizeof(frame)); memcpy(&saved, &asdu, sizeof(asdu));
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &double_point.object) == IEC60870_ASDU_ERROR);
|
||
point.object.ioa = 7;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_ERROR);
|
||
point.object.ioa = 6; point.quality = 2;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_ERROR);
|
||
point.quality = 0; point.value = 2;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_ERROR);
|
||
point.value = 1; point.object.virtual_function_table = double_point.object.virtual_function_table;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_ERROR);
|
||
point.object.type_id = 250;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_UNSUPPORTED);
|
||
CHECK(memcmp(before, frame, sizeof(frame)) == 0 && memcmp(&saved, &asdu, sizeof(asdu)) == 0);
|
||
CHECK(IEC60870_Asdu_Parse(¶meters104, frame, asdu.length, &info) == IEC60870_ASDU_OK);
|
||
memset(&point, 0xA5, sizeof(point)); memcpy(object_before, &point, sizeof(point));
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 1, &point, sizeof(point)) == IEC60870_ASDU_ERROR);
|
||
frame[9] |= 2; /* 视图建立后修改原文,取对象仍验证选中载荷。 */
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 0, &point, sizeof(point)) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(object_before, &point, sizeof(point)) == 0);
|
||
CHECK(IEC60870_SinglePointWithCP56Time2a_Create(&timed, 0, 1, 0, &time) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, ¶meters104, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_OK);
|
||
memcpy(before, frame, sizeof(frame)); memcpy(&saved, &asdu, sizeof(asdu));
|
||
timed.time.time.day = 30;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &timed.base.object) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(before, frame, sizeof(frame)) == 0 && memcmp(&saved, &asdu, sizeof(asdu)) == 0);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, ¶meters104, frame, 5, &header, 0) == IEC60870_ASDU_FULL);
|
||
header.is_test = 2;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, ¶meters104, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
|
||
header = Header(); header.cause_of_transmission = 64;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, ¶meters104, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
|
||
header = Header(); header.is_negative_confirm = 2;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, ¶meters104, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
|
||
header = Header();
|
||
CHECK(IEC60870_Asdu_Create(&asdu, ¶meters104, frame, sizeof(frame), &header, 2) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(before, frame, sizeof(frame)) == 0 && memcmp(&saved, &asdu, sizeof(asdu)) == 0);
|
||
return 1;
|
||
}
|
||
|
||
static int test_numeric_bits(void)
|
||
{
|
||
static const uint32_t bits[] = {0,0x80000000U,0x00000001U,0x7F7FFFFFU,0x7F800000U,0xFF800000U,0x7FC12345U};
|
||
static const uint8_t float_types[] = {13,36,50,63};
|
||
static const uint8_t time_bytes[] = {TIME_BYTES};
|
||
size_t i, t;
|
||
for (t = 0; t < COUNT(float_types); ++t) {
|
||
for (i = 0; i < COUNT(bits); ++i) {
|
||
uint8_t payload[12] = {0}, encoded[16];
|
||
TestObject object;
|
||
uint16_t length, size = (float_types[t] == 36 || float_types[t] == 63) ? 12 : 5;
|
||
unsigned int b;
|
||
for (b = 0; b < 4; ++b) { payload[b] = (uint8_t)(bits[i] >> (b * 8)); }
|
||
if (size == 12) { memcpy(payload + 5, time_bytes, 7); }
|
||
CHECK(Parse_Object(float_types[t], 0, payload, size, &object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_InformationObject_Encode(Object_Base(&object, float_types[t]), ¶meters104,
|
||
0, encoded, sizeof(encoded), &length) == IEC60870_ASDU_OK);
|
||
CHECK(length == size + 3U && memcmp(encoded + 3, payload, size) == 0);
|
||
}
|
||
}
|
||
{
|
||
static const int16_t values[] = {INT16_MIN,-1,0,INT16_MAX};
|
||
static const uint8_t expected[][2] = {{0,0x80},{0xFF,0xFF},{0,0},{0xFF,0x7F}};
|
||
IEC60870_MeasuredValueNormalized_t object, decoded;
|
||
uint8_t encoded[6];
|
||
uint16_t length;
|
||
for (i = 0; i < COUNT(values); ++i) {
|
||
CHECK(IEC60870_MeasuredValueNormalized_Create(&object, 0, values[i], 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_InformationObject_Encode(&object.object, ¶meters104, 0, encoded, sizeof(encoded), &length) == IEC60870_ASDU_OK);
|
||
CHECK(length == 6 && memcmp(encoded + 3, expected[i], 2) == 0);
|
||
CHECK(IEC60870_MeasuredValueNormalized_Get_From_Buffer(&decoded, 0, encoded + 3, 3) == IEC60870_ASDU_OK && decoded.value == values[i]);
|
||
}
|
||
}
|
||
{
|
||
static const int32_t values[] = {INT32_MIN,-1,0,INT32_MAX};
|
||
static const uint8_t expected[][4] = {
|
||
{0,0,0,0x80}, {0xFF,0xFF,0xFF,0xFF}, {0,0,0,0}, {0xFF,0xFF,0xFF,0x7F}
|
||
};
|
||
IEC60870_IntegratedTotals_t object, decoded;
|
||
IEC60870_BinaryCounterReading_t counter = {0,31,1,1,1};
|
||
uint8_t encoded[8];
|
||
uint16_t length;
|
||
for (i = 0; i < COUNT(values); ++i) {
|
||
counter.value = values[i];
|
||
CHECK(IEC60870_IntegratedTotals_Create(&object, 0, &counter) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_InformationObject_Encode(&object.object, ¶meters104, 0, encoded, sizeof(encoded), &length) == IEC60870_ASDU_OK);
|
||
CHECK(length == 8 && memcmp(encoded + 3, expected[i], 4) == 0 && encoded[7] == 0xFF);
|
||
CHECK(IEC60870_IntegratedTotals_Get_From_Buffer(&decoded, 0, encoded + 3, 5) == IEC60870_ASDU_OK);
|
||
CHECK(decoded.value.value == values[i] && decoded.value.sequence_number == 31);
|
||
CHECK(decoded.value.has_carry == 1 && decoded.value.is_adjusted == 1 && decoded.value.is_invalid == 1);
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static int test_parameters_and_nulls(void)
|
||
{
|
||
static const IEC60870_AppLayerParameters_t invalid[] = {
|
||
{0,1,2,0,2,3,249}, {2,1,2,0,2,3,249}, {1,0,2,0,2,3,249},
|
||
{1,2,2,0,2,3,249}, {1,1,0,0,2,3,249}, {1,1,3,0,2,3,249},
|
||
{1,1,2,0,0,3,249}, {1,1,2,0,3,3,249}, {1,1,2,0,2,0,249},
|
||
{1,1,2,0,2,4,249}, {1,1,2,-1,2,3,249}, {1,1,2,256,2,3,249},
|
||
{1,1,2,0,2,3,5}, {1,1,2,0,2,3,255}, {1,1,2,0,2,3,INT_MAX},
|
||
{1,1,INT_MIN,0,2,3,249}, {1,1,2,0,INT_MAX,3,249}, {1,1,2,0,2,-1,249}
|
||
};
|
||
IEC60870_Asdu_t asdu, second, saved;
|
||
IEC60870_AsduInfo_t info;
|
||
IEC60870_AsduHeader_t header = Header();
|
||
IEC60870_SinglePointInformation_t point;
|
||
IEC60870_AppLayerParameters_t p = {1,1,1,0,1,1,249};
|
||
uint8_t frame[32], other[32], before[32];
|
||
const uint8_t request[] = {1,1,3,0,1,0,1,0,0,1};
|
||
const uint8_t *data = frame;
|
||
uint16_t length = 77;
|
||
size_t i;
|
||
CHECK(IEC60870_SinglePointInformation_Create(&point, 0, 1, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_AppLayerParameters_Valid(¶meters104));
|
||
for (i = 0; i <= COUNT(invalid); ++i) {
|
||
const IEC60870_AppLayerParameters_t *bad = i == COUNT(invalid) ? NULL : &invalid[i];
|
||
CHECK(!IEC60870_AppLayerParameters_Valid(bad));
|
||
memset(frame, 0xA5, sizeof(frame)); memcpy(before, frame, sizeof(frame));
|
||
memset(&asdu, 0xA5, sizeof(asdu)); memcpy(&saved, &asdu, sizeof(asdu));
|
||
CHECK(IEC60870_Asdu_Create(&asdu, bad, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(&saved, &asdu, sizeof(asdu)) == 0);
|
||
CHECK(Reject_Frame(bad, request, sizeof(request), IEC60870_ASDU_ERROR));
|
||
CHECK(IEC60870_InformationObject_Encode(&point.object, bad, 0, frame, sizeof(frame), &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Build_Response(bad, request, sizeof(request), 7, 0, frame, sizeof(frame), &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(length == 77 && memcmp(before, frame, sizeof(frame)) == 0);
|
||
}
|
||
header.common_address = 256;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(memcmp(&saved, &asdu, sizeof(asdu)) == 0 && memcmp(before, frame, sizeof(frame)) == 0);
|
||
header.common_address = 255;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, 3, &header, 0) == IEC60870_ASDU_FULL);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_OK);
|
||
CHECK(frame[3] == 255 && frame[4] == 0xA5);
|
||
CHECK(IEC60870_Asdu_Create(NULL, &p, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, NULL, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, frame, sizeof(frame), NULL, 0) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(NULL, &point.object) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, NULL) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Get_Data(NULL, &data, &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Parse(&p, NULL, 8, &info) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Parse(¶meters104, request, sizeof(request), NULL) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Get_Element(NULL, 0, &point, sizeof(point)) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Parse(¶meters104, request, sizeof(request), &info) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 0, NULL, sizeof(point)) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_InformationObject_Get_Type(NULL) == 0);
|
||
CHECK(IEC60870_InformationObject_Encode(NULL, &p, 0, frame, sizeof(frame), &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_InformationObject_Encode(&point.object, &p, 2, frame, sizeof(frame), &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_InformationObject_Encode(&point.object, &p, 0, NULL, sizeof(frame), &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_InformationObject_Encode(&point.object, &p, 0, frame, sizeof(frame), NULL) == IEC60870_ASDU_ERROR);
|
||
CHECK(length == 77 && data == frame);
|
||
point.object.ioa = 256;
|
||
CHECK(IEC60870_InformationObject_Encode(&point.object, &p, 0, frame, sizeof(frame), &length) == IEC60870_ASDU_ERROR);
|
||
point.object.ioa = 0;
|
||
CHECK(IEC60870_Asdu_Create(&asdu, ¶meters104, frame, sizeof(frame), &header, 1) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Create(&second, &p, other, sizeof(other), &header, 0) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&second, &point.object) == IEC60870_ASDU_OK);
|
||
++point.object.ioa;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
point.object.ioa = 5;
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&second, &point.object) == IEC60870_ASDU_OK);
|
||
CHECK(asdu.length == 11 && second.length == 8 && frame[1] == 0x82 && other[1] == 2);
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, NULL, &length) == IEC60870_ASDU_ERROR && length == 77);
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &data, NULL) == IEC60870_ASDU_ERROR && data == frame);
|
||
/* 容器/视图被篡改时,布局检查拒绝,不越界读载荷。 */
|
||
++asdu.length;
|
||
CHECK(IEC60870_Asdu_Get_Data(&asdu, &data, &length) == IEC60870_ASDU_ERROR);
|
||
info.parameters = NULL;
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 0, &point, sizeof(point)) == IEC60870_ASDU_ERROR);
|
||
return 1;
|
||
}
|
||
|
||
static int test_responses(void)
|
||
{
|
||
static const int causes[] = {7,9,10,44,45,46,47};
|
||
IEC60870_AppLayerParameters_t p = parameters104;
|
||
size_t v, ci;
|
||
int pn;
|
||
for (p.sizeOfCOT = 1; p.sizeOfCOT <= 2; ++p.sizeOfCOT) {
|
||
for (p.sizeOfCA = 1; p.sizeOfCA <= 2; ++p.sizeOfCA) {
|
||
for (p.sizeOfIOA = 1; p.sizeOfIOA <= 3; ++p.sizeOfIOA) {
|
||
for (v = 0; v < COUNT(vectors); ++v) {
|
||
uint8_t frame[32], reply[32], expected[32];
|
||
uint16_t n = Wire_Header(&p, vectors[v].type, 1, 0x86, frame), length;
|
||
int b;
|
||
for (b = 0; b < p.sizeOfIOA; ++b) { frame[n++] = (uint8_t)(0x56 - b); }
|
||
memcpy(frame + n, vectors[v].payload, vectors[v].size); n += vectors[v].size;
|
||
for (ci = 0; ci < COUNT(causes); ++ci) {
|
||
for (pn = 0; pn <= 1; ++pn) {
|
||
memcpy(expected, frame, n); expected[2] = (uint8_t)(0x80 | (pn << 6) | causes[ci]);
|
||
CHECK(IEC60870_Asdu_Build_Response(&p, frame, n, causes[ci], pn, reply, sizeof(reply), &length) == IEC60870_ASDU_OK);
|
||
CHECK(length == n && memcmp(expected, reply, n) == 0);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
{
|
||
uint8_t frame[40], saved[40];
|
||
static const uint8_t unknown[] = {250,0,0x83,0x67};
|
||
const IEC60870_AppLayerParameters_t short_p = {1,1,1,0,1,1,249};
|
||
IEC60870_AsduInfo_t info;
|
||
IEC60870_ReadCommand_t read;
|
||
uint16_t length = 77;
|
||
memcpy(frame, unknown, sizeof(unknown));
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 4, 44, 1, frame + 1, 39, &length) == IEC60870_ASDU_OK);
|
||
CHECK(length == 4 && frame[1] == 250 && frame[3] == 0xEC && frame[4] == 0x67);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame + 1, 4, 47, 0, frame, 40, &length) == IEC60870_ASDU_OK);
|
||
CHECK(frame[0] == 250 && frame[2] == 0xAF && frame[3] == 0x67);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 4, 10, 0, frame, 40, &length) == IEC60870_ASDU_OK && frame[2] == 0x8A);
|
||
memcpy(saved, frame, sizeof(frame)); length = 77;
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 4, 7, 0, frame, 3, &length) == IEC60870_ASDU_FULL);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 4, 6, 0, frame, 40, &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 4, 7, 2, frame, 40, &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 3, 7, 0, frame, 40, &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, NULL, 4, 7, 0, frame, 40, &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 4, 7, 0, NULL, 40, &length) == IEC60870_ASDU_ERROR);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 4, 7, 0, frame, 40, NULL) == IEC60870_ASDU_ERROR);
|
||
CHECK(length == 77 && memcmp(saved, frame, sizeof(frame)) == 0);
|
||
memcpy(frame, unknown, sizeof(unknown)); frame[0] = 102; frame[1] = 1; frame[4] = 0x56;
|
||
CHECK(IEC60870_Asdu_Parse(&short_p, frame, 5, &info) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Get_Element(&info, 0, &read, sizeof(read)) == IEC60870_ASDU_OK && read.object.ioa == 0x56);
|
||
CHECK(IEC60870_Asdu_Build_Response(&short_p, frame, 5, 7, 0, frame, 40, &length) == IEC60870_ASDU_OK && length == 5);
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* CA宽度、COT决定的偏移、借用/构造对象及失败原子性使用独立字节期望。 */
|
||
static int test_set_ca(void)
|
||
{
|
||
static const uint16_t addresses[] = {0, 1, 255, 256, 0x1234, UINT16_MAX};
|
||
IEC60870_AppLayerParameters_t p = parameters104;
|
||
unsigned form;
|
||
size_t i;
|
||
CHECK(IEC60870_Asdu_Set_CA(NULL, 1) == IEC60870_ASDU_ERROR);
|
||
for (p.sizeOfCOT = 1; p.sizeOfCOT <= 2; ++p.sizeOfCOT) {
|
||
for (p.sizeOfCA = 1; p.sizeOfCA <= 2; ++p.sizeOfCA) {
|
||
for (form = 0; form < 3; ++form) {
|
||
IEC60870_Asdu_t asdu, saved;
|
||
IEC60870_AsduHeader_t header = {6, 1, 1, 0x78, 0x34};
|
||
IEC60870_SinglePointInformation_t point;
|
||
uint8_t bytes[32], expected[32];
|
||
unsigned offset = 2U + (unsigned)p.sizeOfCOT;
|
||
memset(bytes, 0xa5, sizeof(bytes));
|
||
CHECK(IEC60870_Asdu_Create(&asdu, &p, bytes, sizeof(bytes), &header, 0) == IEC60870_ASDU_OK);
|
||
if (form != 0) {
|
||
CHECK(IEC60870_SinglePointInformation_Create(&point, 0x1234, 1, 0x80) == IEC60870_ASDU_OK);
|
||
CHECK(IEC60870_Asdu_Add_InformationObject(&asdu, &point.object) == IEC60870_ASDU_OK);
|
||
}
|
||
if (form == 2) {
|
||
CHECK(IEC60870_Asdu_Create_From_Buffer(&asdu, &p, bytes, asdu.length) == IEC60870_ASDU_OK);
|
||
}
|
||
saved = asdu;
|
||
for (i = 0; i < COUNT(addresses); ++i) {
|
||
memcpy(expected, bytes, sizeof(bytes));
|
||
if (p.sizeOfCA == 1 && addresses[i] > 255) {
|
||
CHECK(IEC60870_Asdu_Set_CA(&asdu, addresses[i]) == IEC60870_ASDU_ERROR);
|
||
} else {
|
||
expected[offset] = (uint8_t)addresses[i];
|
||
if (p.sizeOfCA == 2) { expected[offset + 1U] = (uint8_t)(addresses[i] >> 8); }
|
||
CHECK(IEC60870_Asdu_Set_CA(&asdu, addresses[i]) == IEC60870_ASDU_OK);
|
||
}
|
||
CHECK(memcmp(bytes, expected, sizeof(bytes)) == 0 && memcmp(&asdu, &saved, sizeof(asdu)) == 0);
|
||
}
|
||
memcpy(expected, bytes, sizeof(bytes));
|
||
asdu.parameters = NULL;
|
||
CHECK(IEC60870_Asdu_Set_CA(&asdu, 1) == IEC60870_ASDU_ERROR); asdu = saved;
|
||
asdu.length = (uint16_t)(offset + (unsigned)p.sizeOfCA - 1U);
|
||
CHECK(IEC60870_Asdu_Set_CA(&asdu, 1) == IEC60870_ASDU_ERROR); asdu = saved;
|
||
asdu.buffer = NULL;
|
||
CHECK(IEC60870_Asdu_Set_CA(&asdu, 1) == IEC60870_ASDU_ERROR); asdu = saved;
|
||
asdu.type_id ^= 1U;
|
||
CHECK(IEC60870_Asdu_Set_CA(&asdu, 1) == IEC60870_ASDU_ERROR); asdu = saved;
|
||
{
|
||
int width = p.sizeOfCA;
|
||
p.sizeOfCA = 3;
|
||
CHECK(IEC60870_Asdu_Set_CA(&asdu, 1) == IEC60870_ASDU_ERROR);
|
||
p.sizeOfCA = width;
|
||
}
|
||
CHECK(memcmp(bytes, expected, sizeof(bytes)) == 0);
|
||
}
|
||
}
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
int main(void)
|
||
{
|
||
int passed = 0;
|
||
#define RUN(test) do { if (!(test)()) { return 1; } ++passed; } while (0)
|
||
RUN(test_wire_matrix);
|
||
RUN(test_object_entrypoints);
|
||
RUN(test_capacity_and_addresses);
|
||
RUN(test_malformed_payloads);
|
||
RUN(test_constructor_failures);
|
||
RUN(test_add_atomicity);
|
||
RUN(test_numeric_bits);
|
||
RUN(test_parameters_and_nulls);
|
||
RUN(test_responses);
|
||
RUN(test_set_ca);
|
||
#undef RUN
|
||
printf("ASDU: %d groups passed (30 concrete types, 504 wire layouts, 12 parameter configurations)\n", passed);
|
||
return 0;
|
||
}
|