Files
STM32F4-Base/IEC60870-Slave/test/iec60870_asdu_test.c
T
STF 4c55544f05
CI / L0 静态门禁 (pre-commit) (push) Failing after 45s
CI / L0.5 主机侧单测 (MSVC) (push) Failing after 42s
通信协议代码
2026-10-26 08:31:48 -04:00

1199 lines
69 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 独立字节向量、边界和失败原子性测试,不操作网络或系统时钟。
* 这里只用联合体为多种具体类型复用测试存储;生产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), &parameters104, 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), &parameters104, 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), &parameters104, 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), &parameters104, 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), &parameters104, 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(&parameters104, 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(&parameters104, 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(&parameters104, frame, n, IEC60870_ASDU_ERROR));
frame[1] = 2;
memcpy(frame + n, frame + h, n - h);
CHECK(Reject_Frame(&parameters104, 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(&parameters104, 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(&parameters104, frame, sizeof(frame), IEC60870_ASDU_ERROR)); /* 后部对象错误 */
frame[1] = 0;
CHECK(Reject_Frame(&parameters104, frame, 6, IEC60870_ASDU_ERROR));
frame[0] = 250;
CHECK(Reject_Frame(&parameters104, 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, &parameters104, 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(&parameters104, 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, &parameters104, 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, &parameters104, frame, 5, &header, 0) == IEC60870_ASDU_FULL);
header.is_test = 2;
CHECK(IEC60870_Asdu_Create(&asdu, &parameters104, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
header = Header(); header.cause_of_transmission = 64;
CHECK(IEC60870_Asdu_Create(&asdu, &parameters104, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
header = Header(); header.is_negative_confirm = 2;
CHECK(IEC60870_Asdu_Create(&asdu, &parameters104, frame, sizeof(frame), &header, 0) == IEC60870_ASDU_ERROR);
header = Header();
CHECK(IEC60870_Asdu_Create(&asdu, &parameters104, 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]), &parameters104,
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, &parameters104, 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, &parameters104, 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(&parameters104));
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(&parameters104, request, sizeof(request), NULL) == IEC60870_ASDU_ERROR);
CHECK(IEC60870_Asdu_Get_Element(NULL, 0, &point, sizeof(point)) == IEC60870_ASDU_ERROR);
CHECK(IEC60870_Asdu_Parse(&parameters104, 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, &parameters104, 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;
}