前言

最近有一个103规约需要开发,在这个浩大的工程开始之前,先完善一下前辈开发的电力系统规约字段解析工具,它可以选择不同的规约类型,输入对应的规约报文,然后告诉你对应的字段是什么含义,我需要完善积成103规约的解析功能,在完善完这个小工具之后再进行103规约的开发。

下图是我刚开发出的解析通用分类服务的功能:
在这里插入图片描述

这个小工具大家如果有需要,可以后台私信我。

复习预编译条件判断

1
2
3
4
5
6
7
8
9
10
11
12
#        空指令,无任何效果
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#elif预处理指令等效于#else和#if指令的综合
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息

//结构体访问用点,指针访问用箭头。

在电力系统中有很多协议会用到APDU这个东西,比如101、102、103、104协议都会用到,说白了APDU就是这些协议的整个帧的一个称呼。
在这里插入图片描述

在我这次开发的积成103规约中,APDU的长度是控制域的第一个字节到ASDU的最后一个字节的长度。APDU的最大值是253(255减去前两个字节,也就是16进制的0xFD。

通用分类标识序号(GIN)
什么是通用分类服务,分类的是什么服务,有哪些服务需要分类?有什么作用?
是否可以这么理解呢?就是再兼容范围内的我就可以用ASDU来传输,不在兼容范围的我就得用通用分类服务,但有些已经生产的保护设备在硬件方面难以改动,所以整了个专用范围来使这些保护设备可以使用通用分类服务。
在这里插入图片描述
通用分类服务可以传输任何数据,一般会有一个数据集,而数据集又包含上图中的四个项目。
通用分类服务用的是ASDU类型标识21和10,其中21表示请求命令,10表示响应命令,那么

在这里插入图片描述
监视方向:保护设备到控制系统
控制方向:控制系统到保护设备
103协议和104协议它们接收序号和发送序号的处理是否一致?答案是一致的。
在这里插入图片描述
在这里插入图片描述
NGD占一个字节,前六位表示数目,第七位表示计数器位(具有相同返回信息标识符(RII)的应用服务数据单元的一位计数器位),计数器位(COUNT)的初始值为零,第八位表示后续状态位,其中若是0 :后面未跟着具有相同返回信息标识符(RII)的应用服务数据单元; 若是1 :后面跟着具有相同返回信息标识符(RII)的应用服务数据单元。
GIN占两个字节,KOD占两个字节,GDD占三个字节,GID占i个字节,i:=数据宽度(DATASIZE)乘以数目(NUMBER)
发送序号和接收序号的首位都是0,是不用的,只用来标识此帧是I帧。发送序号和接收序号都是用7+8=15位来表示,也就是2^15=32768,可以表示32768个序号。
发送序号要和接收序号做比较。

在这里插入图片描述

1
qy.Format("%02X%s启动符\r\n", m_pRxdFm103->bStart, GetSpace(1));

这段代码使用了 CString 类的 Format 方法来格式化一个字符串。具体分析如下:

代码解释

1
qy.Format("%02X%s启动符\r\n", m_pRxdFm103->bStart, GetSpace(1));

各部分详解

  1. qy:

    • qy 是一个 CString 对象,用于存储格式化后的字符串。
  2. Format:

    • FormatCString 类的一个方法,用于格式化字符串。它的用法类似于标准 C 的 sprintf 函数。
  3. “%02X%s启动符\r\n”:

    • 这是格式化字符串,其中包含格式说明符:
      • %02X:表示一个两位的十六进制数,不足两位时前面补零。
      • %s:表示一个字符串。
      • 启动符:表示字符串的一部分,固定内容。
      • \r\n:表示回车换行。
  4. m_pRxdFm103->bStart:

    • m_pRxdFm103 是一个指向某个结构体或类实例的指针,bStart 是其中的一个成员变量。
    • bStart 的值将被格式化为两位的十六进制数。
  5. GetSpace(1):

    • GetSpace(1) 是一个函数调用,返回一个字符串。
    • 假设 GetSpace 函数返回一个长度为1的空格字符串。

整体作用

这段代码的作用是将 m_pRxdFm103->bStart 变量的值格式化为两位的十六进制数,并与一个由 GetSpace(1) 返回的字符串和固定的 “启动符\r\n” 连接起来,最终结果存储在 qy 对象中。

示例

假设 m_pRxdFm103->bStart 的值为 15(十进制),即 0x0F(十六进制),且 GetSpace(1) 返回一个空格字符 " ",那么 qy 的值将会是:

1
0F 启动符\r\n

这是一个常见的字符串格式化操作,用于生成易于阅读和分析的调试信息或日志在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//后七个字节是时间,也就是年月日时分秒
Data = &(m_pRxdFm_JC103->bData);
MSecond = (Data[0] + Data[1] * 256) % 1000;
Second = (Data[0] + Data[1] * 256) / 1000;
SysTime.MSecond = MSecond;
SysTime.Second = Second;
SysTime.Minute = Data[2] & 0x3f;
SysTime.Hour = Data[3] & 0x1f;
SysTime.Day = Data[4] & 0x1f;
SysTime.Month = Data[5] & 0x0f;
if ((Data[6] & 0x7f) < 70)
SysTime.Year = (Data[6] & 0x7f) % 100 + 2000;
else
SysTime.Year = (Data[6] & 0x7f) % 100 + 1900;

qy.Format("%02X %02X %02X %02X %02X %02X %02X%s时间:%d 年 %d 月 %d 日 %d:%d:%d.%d\r\n", Data[0], Data[1], Data[2], Data[3], Data[4], Data[5], Data[6], GetSpace(7),
SysTime.Year, SysTime.Month, SysTime.Day, SysTime.Hour, SysTime.Minute, SysTime.Second, SysTime.MSecond);

积成103报文:
68 11 06 00 02 00 06 81 08 00 FF 00 23 45 06 0B 08 08 18
其中23 45 06 0B 08 08 18代表的是时间,而这个报文我们采用如下结构体存放:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct
{
BYTE bStart; //启动字符
BYTE bLength; //长度
BYTE bControl1;//控制域1
BYTE bControl2;//控制域2
BYTE bControl3;//控制域3
BYTE bControl4;//控制域4
BYTE bType; //类型标识
BYTE bQualifier;//结构限定词
BYTE bReason; //传送原因
BYTE bAddress; //公共地址
BYTE bFUN; //功能类型
BYTE bINF; //信息序号
BYTE bData;
BYTE bGenData;
} TJC103RxdFm, TJC103TxdFm;

和上面解析时间的代码对照来看的话,Data[0]里面存放的就是23H,Data[1]里存放的就是45H,以此类推Data[6]就是18