88 lines
4.2 KiB
C
88 lines
4.2 KiB
C
/* 召唤种类规则单测:直接调用无状态规则,验证限定词解析、数据COT和允许的数据类型。
|
|
* 使用独立请求字节,不经过网络,也不执行应用的冻结或复位动作。 */
|
|
#include "../104/business/IEC104_Interrogation_Internal.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #x); exit(1); } } while (0)
|
|
|
|
/* QCC同时包含RQT和FRZ;用非零IOA确认解析保留原值,业务合法性由后续校验决定。 */
|
|
static void test_decode(void)
|
|
{
|
|
static const uint8_t qcc[] = {0x05, 0x45, 0x85, 0xc5, 0x01, 0x3f};
|
|
IEC60870_AppLayerParameters_t parameters = {1, 1, 2, 0, 2, 3, 249};
|
|
unsigned i;
|
|
for (i = 0; i < sizeof(qcc); ++i) {
|
|
uint8_t bytes[] = {101, 1, 6, 0x5a, 1, 0, 0x34, 0x12, 0, 0};
|
|
IEC60870_AsduInfo_t info;
|
|
uint32_t ioa = 0;
|
|
uint8_t qualifier = 0, freeze = 0;
|
|
bytes[9] = qcc[i];
|
|
CHECK(IEC60870_Asdu_Parse(¶meters, bytes, sizeof(bytes), &info) == IEC60870_ASDU_OK);
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.decode(&info, &ioa, &qualifier, &freeze));
|
|
CHECK(ioa == 0x1234 && qualifier == (qcc[i] & 0x3f) && freeze == (qcc[i] >> 6));
|
|
}
|
|
{
|
|
uint8_t bytes[] = {100, 1, 6, 0, 1, 0, 0, 0, 0, 36};
|
|
IEC60870_AsduInfo_t info;
|
|
uint32_t ioa = 1;
|
|
uint8_t qualifier = 0, freeze = 3;
|
|
CHECK(IEC60870_Asdu_Parse(¶meters, bytes, sizeof(bytes), &info) == IEC60870_ASDU_OK);
|
|
CHECK(IEC104_GENERAL_INTERROGATION_RULES.decode(&info, &ioa, &qualifier, &freeze));
|
|
CHECK(ioa == 0 && qualifier == 36 && freeze == 0);
|
|
}
|
|
}
|
|
|
|
/* 遍历完整字节范围验证限定词边界,并检查总请求及四个分组的数据COT映射。 */
|
|
static void test_qualifiers(void)
|
|
{
|
|
unsigned q;
|
|
CHECK(IEC104_GENERAL_INTERROGATION_RULES.type_id == 100);
|
|
CHECK(IEC104_GENERAL_INTERROGATION_RULES.has_deactivation);
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.type_id == 101);
|
|
CHECK(!IEC104_COUNTER_INTERROGATION_RULES.has_deactivation);
|
|
for (q = 0; q < 256; ++q) {
|
|
CHECK(IEC104_GENERAL_INTERROGATION_RULES.qualifier_valid((uint8_t)q) == (q >= 20 && q <= 36));
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.qualifier_valid((uint8_t)q) == (q >= 1 && q <= 5));
|
|
}
|
|
for (q = 20; q <= 36; ++q) { CHECK(IEC104_GENERAL_INTERROGATION_RULES.data_cause((uint8_t)q) == q); }
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.data_cause(1) == 38);
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.data_cause(2) == 39);
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.data_cause(3) == 40);
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.data_cause(4) == 41);
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.data_cause(5) == 37);
|
|
}
|
|
|
|
/* 交叉验证Type/SQ/FRZ:计数量只在读取时允许Type15或非连续寻址的Type37。
|
|
* 总召保持原九种监视类型,不能因共用引擎而接受累计量或命令类型。 */
|
|
static void test_types(void)
|
|
{
|
|
static const uint8_t types[] = {1, 3, 5, 7, 9, 11, 13, 15, 16, 20, 21, 37, 100, 101};
|
|
unsigned i, sq, freeze;
|
|
for (i = 0; i < sizeof(types); ++i) {
|
|
for (sq = 0; sq < 2; ++sq) {
|
|
for (freeze = 0; freeze < 4; ++freeze) {
|
|
IEC60870_AsduInfo_t info;
|
|
bool expected = freeze == 0 && (types[i] == 15 || (types[i] == 37 && sq == 0));
|
|
memset(&info, 0, sizeof(info)); info.type_id = types[i]; info.is_sequence = (uint8_t)sq;
|
|
CHECK(IEC104_COUNTER_INTERROGATION_RULES.data_allowed(&info, (uint8_t)freeze) == expected);
|
|
if (freeze == 0) {
|
|
bool general = types[i] == 1 || types[i] == 3 || types[i] == 5 || types[i] == 7 ||
|
|
types[i] == 9 || types[i] == 11 || types[i] == 13 || types[i] == 20 || types[i] == 21;
|
|
CHECK(IEC104_GENERAL_INTERROGATION_RULES.data_allowed(&info, 0) == general);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
test_decode(); test_qualifiers(); test_types();
|
|
printf("interrogation rules: 3 groups passed; request=%zu event=%zu station=%zu\n",
|
|
sizeof(IEC104_InterrogationRequest_t), sizeof(IEC104_InterrogationEvent_t),
|
|
sizeof(IEC104_InterrogationStationState_t));
|
|
return 0;
|
|
}
|