英文网站标题,哪些平台可以免费推广,wordpress建立扁平化,南京发布最新消息一、前言
通过之前整体性的协议分析#xff0c;目前确定先基于IEC104做深入分析#xff0c;来结合分析电网常见的业务#xff0c;以此从协议侧关联深入到业务侧。在国内该标准也应用比较稳定和广泛了#xff0c;所以研究104协议相关资料也会更全一些。
二、资料及标准收集…一、前言
通过之前整体性的协议分析目前确定先基于IEC104做深入分析来结合分析电网常见的业务以此从协议侧关联深入到业务侧。在国内该标准也应用比较稳定和广泛了所以研究104协议相关资料也会更全一些。
二、资料及标准收集
除了一些网上找到的总结文档主要还是要参考国标/行标来分析总结。 一些参考文章 https://www.cnblogs.com/yking/p/17622619.html https://www.cnblogs.com/yking/p/17620510.html https://www.jianshu.com/p/772582a9db11 https://blog.csdn.net/chenyitao736866376/article/details/99120024 https://zhuanlan.zhihu.com/p/66988575 国标、行标的下载方法及网站现在大部分都要开会员 http://www.bzfxw.com/down63_193406.html https://mp.weixin.qq.com/s/H6wDjbdhiGh0I_ImGf3X4A https://zhuanlan.zhihu.com/p/82467306 https://gcbz.org/ 一些标准就不具体给地址了具体可能需要参考标准 建议查看对照的标准
DL/T 634.5104-2009 远动设备及系统 第5-104部分传输规约 采用标准传输规约集的IEC 60870-5-101DL/T 634.5101-2002 远动设备及系统 第5-101部分传输规约 基本远动任务配套标准 GB/T 18657.5 远动设备及系统 第 5部分:传输规约 第 5篇:基本应用功能 GB/T 18657.4 远动设备及系统 第 5部分:传输规约 第 4篇:应用信息元素的定义和编码 GB/T 18657.3 远动设备及系统 第 5部分:传输规约 第 3篇:应用数据的一般结构 GB/T 18657.2 远动设备及系统 第 5部分:传输规约 第 2篇:链路传输规则 GB/T 18657.1 远动设备及系统 第 5部分:传输规约 第 1篇:传输帧格式
“电气技术”在线术语数据库(IEVhttps://www.electropedia.org/iev/iev.nsf/index?openformpart371 语言选择Chinese即可包括远动、摇测等术语。
三、协议细化解读及结合报文帧再分析
1、ISO/OSI网络模型及报文帧整体情况
ISO/OSI网络模型 IEC 国标 根据标准说明不难理解104是将101的应用层与TCP/IP提供的传输功能进行了结合。由于传输层是走TCP方式TCP为流式传输为了区分ASDU的启动或停止增加了APCI用于检出ASDU的启动和结束。对于整体的TCP报文又通过启动字符68H定义起点并增加长度字段这段报文整体就是APDU这样对报文的理解的就比较清楚了报文头报文长度报文内容也许还有校验字节也许还有报文尾不管我们是串口通信还是这类网络通信大部分时候都可以这么类比来理解我想即使是电报时代也大概类似吧否则一串不知道头尾的内容很难翻译。
APCI应用协议控制信息ASDU应用服务数据单元APDU应用协议 数据单元
应用数据的一般结构APDU-APCIASDU APCI ASDU数据单元标识符信息体应用服务数据单元公共时标 这下根据报文APDU就引入了APCI和ASDU继续往下分别分析APCI和ASDU。
2、APCI控制域及三种帧格式I、S、U 启动字符68H0x68定义了数据流中的起点。APDU的长度域定义了APDU体的长度包括APCI的四个控制域八位位组和ASDU。ASDU的最大长度限制在249以内因为APDU域的最大长度是253APDU长度最大值255-启动字符-长度控制域的长度为4个八位位组。控制域定义了保护报文不至丢失和重复传送的控制信息、报文传输启动/停止以及传输连接的监视等控制信息。三种类型的控制域格式用于编号的信息传输I格式、编号的监视功能S格式和未编号的控制功能U格式。控制域第一个八位位组的比特10定义I格式I格式的APDU总是包含一个ASDU。I格式的控制信息如下图2所示。 实际报文对照
控制域的第一个八位位组的比特11并且比特20定义S格式。S格式的APDU只包括APCI。S格式对的控制信息如图7所示。 控制域的第一个八位位组的比特11并且21定义了U格式。U格式的APDU只包括APCI U格式的控制消息如图8所示。在同一时刻TESTFR、STOPDT或STARTDT中只有一个功能可以被激活。 3、防止报文丢失和报文重复
发送序列号NS和接收序列号R的使用域ITU-T X.25定义的方法一致。两个序列号在每个APDU和每个方向上都应按顺序加一。发送方增加发送序列号NS接受方增加接收序列号NR。接收站认可接收的每个APDU或者多个APDU将最后一个正确接收的APDU的发送序列号加1作为接收序列号返回。发送站把一个或多个APDU保存在保存在缓存区中直到它收到接收序列号这个接收序列号是对所有发送序列号小于该号的APDU的有效确认这时就可以删除缓冲区里已正确传送过的APDU。如只在一个方向进行较长的数据传输应在另一个方向发送S格式认可这些APDU。这种方法在两个方向上都适用。在建立一个TCP连接后发送和接收序列号都应该被置为0。 VS—发送状态变量 VR—接收状态变量 ACK —指示DTE已经正确收到所有小于或等于这个编号的I格式的APDU I(a,b) —I格式的APDUa发送序列号b接收序列号 S(b) —S格式的APDUb接收序列号 U —未编号的U格式的APDU。
4、测试过程
未使用但已打开的连接可通过发送测试APDUTESTFRact并由接收站发送TESTFRcon在两个方向上进行周期性测试。发送站和接收站在规定时间段内没有数据传输超时均可启动测试过程。 一般可以通过发送测试信息来确认对端是否断开如果超时时间内未收到对应的TESTFR的con就可以在应用层主动关闭连接相当于一层应用层控制实际上一般STARTDT和STOPDT就够用了这个很少用所以lib60870的demo没有实现对应的TESTFR的发送但是其定了结构我上节总结没有找到方法这一节自己实现了一下然后测试抓包发现可以正常添加使用。 在cs104_connection.c中添加该函数
void
CS104_Connection_sendTestFR(CS104_Connection self)
{
#if (CONFIG_USE_SEMAPHORES 1)Semaphore_wait(self-conStateLock);
#endif /* (CONFIG_USE_SEMAPHORES 1) */self-conState STATE_WAITING_FOR_TESTFR_CON;#if (CONFIG_USE_SEMAPHORES 1)Semaphore_wait(self-socketWriteLock);
#endifwriteToSocket(self, TESTFR_ACT_MSG, TESTFR_ACT_MSG_SIZE);
#if (CONFIG_USE_SEMAPHORES 1)Semaphore_post(self-socketWriteLock);
#endif#if (CONFIG_USE_SEMAPHORES 1)Semaphore_post(self-conStateLock);
#endif /* (CONFIG_USE_SEMAPHORES 1) */
}在cs104_connection.h中添加声明
void
CS104_Connection_sendTestFR(CS104_Connection self);在simple_client.c中调用测试
int
main(int argc, char** argv)
{const char* ip localhost;uint16_t port IEC_60870_5_104_DEFAULT_PORT;const char* localIp NULL;int localPort -1;if (argc 1)ip argv[1];if (argc 2)port atoi(argv[2]);if (argc 3)localIp argv[3];if (argc 4)port atoi(argv[4]);printf(Connecting to: %s:%i\n, ip, port);CS104_Connection con CS104_Connection_create(ip, port);CS101_AppLayerParameters alParams CS104_Connection_getAppLayerParameters(con);alParams-originatorAddress 3;CS104_Connection_setConnectionHandler(con, connectionHandler, NULL);CS104_Connection_setASDUReceivedHandler(con, asduReceivedHandler, NULL);/* optional bind to local IP address/interface */if (localIp)CS104_Connection_setLocalAddress(con, localIp, localPort);/* uncomment to log messages *///CS104_Connection_setRawMessageHandler(con, rawMessageHandler, NULL);if (CS104_Connection_connect(con)) {printf(Connected!\n);CS104_Connection_sendStartDT(con);Thread_sleep(2000);CS104_Connection_sendInterrogationCommand(con, CS101_COT_ACTIVATION, 1, IEC60870_QOI_STATION);Thread_sleep(5000);struct sCP56Time2a testTimestamp;CP56Time2a_createFromMsTimestamp(testTimestamp, Hal_getTimeInMs());CS104_Connection_sendTestCommandWithTimestamp(con, 1, 0x4938, testTimestamp);#if 0InformationObject sc (InformationObject)SingleCommand_create(NULL, 5000, true, false, 0);printf(Send control command C_SC_NA_1\n);CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, sc);InformationObject_destroy(sc);/* Send clock synchronization command */struct sCP56Time2a newTime;CP56Time2a_createFromMsTimestamp(newTime, Hal_getTimeInMs());printf(Send time sync command\n);CS104_Connection_sendClockSyncCommand(con, 1, newTime);
#endifprintf(Wait ...\n);//TESTFR测试CS104_Connection_sendTestFR(con);Thread_sleep(1000);//STOPDT测试CS104_Connection_sendStopDT(con);}elseprintf(Connect failed!\n);Thread_sleep(1000);CS104_Connection_destroy(con);printf(exit\n);
}
编译并运行测试后抓包
5、采用启/停的传输控制
控制站如A站可以有效的利用STARTDT启动数据传输和STOPDT停止数据传输来控制被控站B站的数据传输。例如当在站间有超过一个以上打开的连接可利用时一次只有一个连接可用于数据传输。定义STARTDT和STOPDT的功能在于从一个连接切换到另一个连接时避免数据丢失。STARTDT和STOPDT还可与站间的单个连接一起用于控制连接的通信量。 连接建立后被控站不会自动使能连接上的用户数据传输即当一个连接建立时STOPDT是缺省状态。在这种状态下除了未编号的控制功能和对这些功能的确认被控站不通过这个连接发送任何数据。控制站应通过这个连接发送STARTDT激活指令激活这个连接中的用户数据传输。被控站用STARTDT确认响应这个命令。如果STARTDT没有被确认被控站将关闭这个连接。站初始化之后STARTDT必须总是在来自被控站的任何用户数据传输例如总召唤信息开始发送。被控站只有在发送STARTDT确认后才能发送任何待发用户数据。 STARTDT/STOPDT是一种控制站激活/解除激活监视方向的机制即使没有收到激活确认被控站也可以发送命令或者设定值。发送和接收计数器继续计数不依赖于STARTDT/STOPDT的使用。 其它的被控站的开始/停止状态切换及控制站的开始/停止状态切换流程图如下详细的处理过程参考标准说明描述的还是挺详细的
6、端口号
每个TCP地址由一个IP地址和 一个端口号组成。标准端口为2404已由IANA确认 。
7、未被确认的I格式的APDU最大数目
k表示在某一特定的事件内未被DTE确认即不被承认的连续编号的I格式APDU的最大数目。每一I格式帧都按顺序编好号从0到模数n减1。以n为模的操作中k值永远不会超过n-1。
当未确认I格式的APDU达到k个时发送方停止传送。接收方达到w个I格式的APDU 后确认。模n操作时k的最大值是n-1。
k值的最大范围1到327672的15次方-1APDU精确到一个APDU。 w值的最大范围1到32767 APDU精确到一个APDU推荐w不应超过2/3的k。
8、ASDU的结构和类型
根据GB/T 18657.3IEC 60870-5-3中对应用数据的一般结构的描述我们能更详细的了解ASDU应用服务数据单元的结构情况及各个字段的含义。
8.1 应用服务单元ASDU
应用服务数据单元ASDU由数据单元标识符和信息体组成
8.2 数据单元标识符
数据单元标识符DATA UINT IDENTIFIER由类型标识TYPE IDENTIFICATION应用服务数据单元长度LENGTH OF ASDU选用、可变结构限定词VARIABLE STRUCTURE QUALIFIER选用、传送原因CAUSE OF TRANSMISSION选用、应用服务数据单元的公共地址COMMON ADDRESS OF ASDU选用组成。 类型标识TYPE IDENTIFICATION、应用服务数据单元长度LENGTH OF ASDU和可变结构限定词VARIABLE STRUCTURE QUALIFIER统称为数据单元类型DATA UINT TYPE。 类型标识是一个码再各种协议集活系统的可能类型中明确标识应用服务数据单元的类型。如有应用服务数据单元长度它以八位位组表示应用服务数据单元的总长度。如有可变结构限定词它表示应用服务数据单元在各种通信情况中的结构变化。类型标识能使接收的 应用服务将各数据单元发往正确的应用进程以便应用进程处理指明的数据单元类型。它也能使接收的应用进程观察数据单元包含哪种类型数据并从当地表格中确定其结构。如有数据单元标识符类型标识是唯一的非选用的域元素。 传送原因如未明确定义也可以包含在数据单元类型中。 如定义了应用服务数据单元的公共地址它常位于信息体的前面。 类型标识目前定义大致如下 在控制方向传送过程信息给指定站时可以带或者不带时标但对某一给定的站两者不可混合发送。 注在控制方向上具有”CON“标记的ASDU是被确认的应用服务在监视方向上可以用不同的传送原因镜像同样的报文内容。这些镜像ASDU用作肯定或否定认可确定。
8.3 信息体
应用服务数据单元可包含一个或多个信息体。信息体的一般结构如图8所示。 信息体可由信息体标识符和信息元素集组成。信息体标识符可由信息体类型和信息体地址组成。 如信息体的结构不同又未在数据单元类型中定义可定义信息体类型。 每个信息体可以选择地加上信息体时标。如规定信息体时标常将它排在信息体的最后。
8.4.1 信息体标识
远动系统中信息体的标识应支持各种可能配置。简单的远动系统只需物理地址就可标识信息体 地址的结构常使它们可以代表被控制的进程的镜像。各种识别原则一般由相应的标准数据模型考虑。标准数据模型的详细定义或选择由具体应用方式的标准协议集规定。 为在各种远动过程中实现高的数据传输效率定义了一种通用的数据结构如图 6、图了、图 8所示。 信息体一般由数据单元类型(或信息体类型)和应用服务数据单元的公共地址(或信息体地址)标识。在 紧凑的表示方法中公共地址可以包含在数据单元类型中和信息元素集一起传输。也可以采用将数据 单元类型、传送原因和应用服务数据单元公共地址综合到数据单元标识符中(见图9)等其他方法。多级 的结构性地址(见图10)也是允许的 但不论采用哪种方法都应采用图6、图 7、图8中列出的顺序。 每个信息体由数据单元标识符标识。数据单元标识符可以采用表 1的结构。信息体的标识可以采用在信息体标识表上加指针的方法。信息体组也可以由组的标识定义 标识表可以包含附加的信息体属性为信息体规定固定的赋值如物理地址等如图9所示。信息体属性也可以为信息元素定义。
8.4.2 信息体地址
非结构性地址以从一个数集中选择的数区分不同的信息体。 结构性地址考虑了技术的、物理的、拓扑的或地理的结构以标识信息体。这种方式应为每一级定义充分的地址空间以便每一级最大地扩展。 在生成系统或修改系统配置时将地址赋予信息体。
8.4.3 信息元素集
有下列三种信息元素集(图 11) 第1种信息元素集由单个信息元素组成由相应的信息体地址或应用服务数据单元公共地址标识单个信息元素的例子是命令、事件、状态值或模拟值等。 第 2种信息元素集由定义的一组相同的信息元素集组成(例如同一格式的测量值)。这种信息体的地址或应用服务数据单元的公共地址是该序列第 1个信息元素的相应地址后面的信息元素由预先定义的序列地址方案标识。 第 3种信息元素集由定义的一组不同的信息元素集组成(例如表示一个电力馈线状态的模拟值和数字值的组合)。这种情况的信息体地址或应用服务数据单元的公共地址是整个信息体的相应地址每个信息元素由预先定义的结构方案标识。 信息元素是变量在传输时由预先定义的数据类型和编码表示。变量的类型为布尔、整数、实数、比特串(位串)、八位位组串和综合类型等。GB/T 18657.4给出了常用的信息元素的规范建议。
8.4 构造应用服务数据单元的导则
应用服务数据单元用于包含在通过通信服务的通信中的应用过程之间的数据交换。基于本标准构成的协议集包含这些应用服务数据单元。数据交换所需的主要基本过程在GB/T 18657.5中规定。 **各应用服务数据单元由域元素组成。域元素由在GB/T 18657.4中规定的整数、布尔、比特串等语法数据类型定义。此外信息元素和时标的语义定义也在 GB/T 18657.4中描述并在应用协议集中规定。下面的规范用文本块图和语法描述方法说明在GB/T 18657.4中定义的域元素和它们的功能目的。 ** 基于一般结构的具体的应用服务数据单元的规范按以下步骤确定。规范不需包括 5.1中定义的所有域元素例如可变结构限定词可以省略。 ** 在构造应用服务数据单元之前分析应用服务数据单元所属的特定协议集的任务是非常重要的即应知道定义信息类别、信息容量、需要的精度(例如测量值的准确度:11比特符号)、地址的结构等规范。**定义这些约束条件后就可按下列步骤构造应用服务数据单元。 如图5所示几个应用服务数据单元可以组成一个应用规约数据单元(APDU)。简单的情况是1个应用规约数据单元只有 1个应用服务数据单元。这意味着应用服务数据单元和应用规约数据单元是相同的。
8.4.1 步骤1数据单元标识符域元素的选择
步骤1选择用于有关的应用服务数据单元的域元素可以省略选用的域元素应遵守通用结构定义的域元素的顺序。建议从一个应用协议集的所有应用服务数据单元中选择域元素的公共集。 例:一个具体的应用协议集的数据单元标识符由下列域元素组成(图 12)。
8.4.2 步骤2数据单元标识符域元素长度的选择
步骤 2规定域元素的长度。域元素可由1个或多个八位位组组成。或者1个八位位组可包含两个或多个域元素或 1个域元素可能分配到几个八位位组的几部分中。不论如何只要可能建议规定每个域元素的八位位组的总数。一个协议集中各应用服务数据单元的类型标识的长度应相同。此外在一个具体的协议集中建议各应用服务数据单元的数据单元标识符的其他域元素的长度相同。 例:上例协议集的应用服务数据单元的域元素的长度为(图13):
8.4.3 步骤3数据单元标识符数据类型的定义
步骤 3规定域元素的数据类型。数据类型为整数型、布尔型等。 注 :一个域元素可由几种数据类型组成 。在一个具体协议集中 建议只用一种数据类型定义数据单元标识符的各域元素。 例:定义下列数据类型〔图 14)。 数据单元标识符:CP40 {类型标识应用服务数据单元的长度传送原因应用服务数据单元的公共地址 } 类型标识 :UI8[1… 8] 应用服务数据单元的长度 : UI8[1. .8] 传送原因:CP8{UI6[1…6],BS2[7…8]} 应用服务数据单元的公共地址:UI16[1. .16]
8.4.4 步骤4信息体的定义
**每个信息体可由信息体类型、信息体地址、信息元素集和信息体时标组成(见图8)。**如个别信息体类型和信息体时标域元素需由特定的协议集定义它们应在以上步骤中规定。常用的域元素和时标在GB/T 18657.4中规定。本标准5.1.5已定义信息元素集可以是单个信息元素、信息元素序列或信息元素组合。这些信息元素由应用服务数据单元的公共地址或信息体地址寻址。在下面的例子中信息元素由应用服务数据单元的公共地址寻址。信息元素的语法描述方法引自GB/T 18657.4。
例 1:
单个信息元素(仅有一个信息元素图 15) 宽度为 2的比特串:BS2[1…2]或 8比特带符号的整数:I8[1…8],或 7比特无符号带差错指示的整数:CP8{UI7,BS1}
例 2:
信息元素序列(几个相同数据类型的信息元素图16).
例 3:(图 17) 例 4:
信息元素组合(几个不同的信息元素图 18)。 7比特无符号整数宽度为1的1个比特串宽度为2的4个比特串:CP16{U17[1…7],BS1[8],BS2[9…10],BS2[11…12],BS2[13…14],BS2[15…16]} 特定协议集中使用的所有信息体均应按这种方法规定。
8.4.5 步骤5对信息体赋予类型标识和语义定义
步骤 5定义域元素的值的功能解释。
**类型标识(图 18) **
如表1的规定以上定义的信息体由该域元素选择。 例 : 类型标识:UI8[1…8]0.255 0:未用 1:信息体1:8个单点信息 2:信息体2:8个8比特的测量值 3:等等
应用服务数据单元的长度
该域元素规定应用服务数据单元(包括全部的域)的八位位组的数目。 例 : 应用服务数据单元的长度:UI8[1. . 8]0. . .255 应用服务数据单元的长度在八位位组内由0^-255的数规定即由一个长度八位位组UI8规定。
**传送原因 **
该域元素对相同的应用服务数据单元赋予不同的传送原因。因此请求的或自发的数据可以用相同的数据单元类型传输由传送原因域元素区别。 例 : 6比特无符号整数和宽度为 2的 1个比特串 传送原因:CP8{U16[1…6],BS2[7…8]} U16[1…6]0…63 0:自发数据 1:循环数据 2:请求数据 3:等等 BS2[7] :LS当地服务 LS0:远方 LS1:当地 BS2[8]:TE测试(Test) TE0:不测 TE1:测试
应用服务数据单元的公共地址
以该结构性或非结构性域元素(见5.1.2)作信息体地址。如信息体无具体的信息体类型和地址则以应用服务数据单元的公共地址直接作信息元素集地址。 例 : 应用服务数据单元的公共地址:UI16[1…16]0…65535 以范围为0~65535的整数作不同的信息元素集的地址。 如这些例子所示建议用表格为应用协议集的各域元素定义。该表格应表示出可能的值的范围并定义采用的值的功能说明。
9、基本应用功能及对应字段
帧基本格式确定了之后通过定义的基本应用功能对应的流程和字段也有不同的标准定义。 控制站等同于客户连接者被控站为服务器监听者。 本章定义了利用标准通信服务的各种基本应用功能。用在控制站和被控站之间交换的数据单元的序列的图以及完成这些功能的数据单元的任务的方法来描述这些功能。首先描述以站初始化和用问答式(查询)方法采集数据两种基本应用功能 这两种功能是执行其他基本应用功能的基础由具体应用和下面详细描述的链路服务配合完成。其他基本应用功能可能涉及查询过程描述时不再重复过程的细节 。 用箭头表示传输过程的顺序每个箭头代表一个规约数据单元PDU。用分层的字母结构形式为应用规约数据单元或应用服务数据单元命名。命名可由各配套标准补充完整。因为没有明确的应用规约控制信息(APCI)应用服务数据单元(ASDU)和应用规约数据单元(APDU)在 GB/T 18657规约定义中是相同的。
9.1 标号规则
下面按分层规则规定应用服务数据单元标号。它提供了这样的可能性:在本标准中采用全局标号在不同配套标准中采用具体标号。
最高一级为:
第 1信息级的种类标号监视信息M控制信息C参 数P文件传输F
第2级定义:
第2信息级的种类标号监视信息M单点信息M_ SP双点信息M_ DP测量值M_ME继电保护事件M_ EP累计量M_IT步位置信息M_ ST比特和八位位组串M_ B初始化结束M _ EI可用的应用层M_ AA控 制 信 息C单点命令C_ SC双点命令C_DC设点命令C_ SE步调节命令C_RC召唤命令C_ IC时钟同步命令C_CS延时采集C_ CD计数值召唤命令C_CI测试命令C_ TS复位进程命令C_RP读命令C_RD初始化结束C_ El参 数P测量值参数P_ME参数激活P_AC文件传输F目录F_DR文件、节或目录的选择或召唤F_SC最后的节或段F_LS文件或节的认可F_AF文件准备好F_FR节准备好F_SR段F_SG
第3级为各配套标准采用定义应用服务数据单元的具体类型、时标的应用等。第3级的第1个字母指明是否使用时标(N无时标T有时标)第2个字母指明类型。每一个配套标准可从“A”开始用字母顺序定义自己的类型例如 :
说明标号不带时标的归一化的测量值(类型A)M_ME _NA带时标有标度的测量值(类型 B)M_ME_TB单点命令类型A不带时标C_SC_NA
此外最后的数字表示哪一个配套标准定义了应用服务数据单元的标号。例如:
说明标号配套标准 101M_ME_NA_1或 C_SC_NA_1配套标准 102M_ME_NA_2或 C_SC_NA_2
这标号方法是开放的如需要不同的配套标准可在各层次补充完整。 控制方向的应用服务数据单元可以在监视方向形成镜像。这些应用服务数据单元的镜像用于肯定或否定认可。为明确区分控制和监视两个方向需要在标号的基础上附加说明。用下列缩写符分别标记两个方向的应用服务数据单元。
说明标号控制方向:激活ACT监视方向:激活确认ACTCON控制方向:停止激活DEACT监视方向:停止激活确认DEACTCON监视方向:激活终止ACTTERM
还可采用下列缩写符:
说明标号监视方向:循环传输CYCLIC监视方向:突发(自发)传输SPONT
采用非平衡传输过程时激活(ACT)可由发送/无回答(SEND/NO REPLY)链路服务作为广播报文传输(例如站召唤或时钟同步)。这时每一个接收到激活(ACT)的被控站应分别传输激活确认(ACTCON)到控制站。
9.2 站初始化GB/T 18657.5的6.1.5~6.1.7
和应用相关的远动操作开始工作前需用站初始化过程将站设置成正确的工作状态。 应区分冷启动和热启动两种过程。冷启动是站的主要启动引导过程在按实际状态刷新数据库之前先将过程变量信息清除。热启动是站复位或者重新激活的重新引导过程不清除在重新激活前采集的过程变量信息。另一个区别是控制站的初始化和被控站的初始化。以下规定主要考虑涉及站间数据传输的初始化过程。 控制站通常配备有冗余的控制设备和数据库以保证正在工作的控制设备因故障而切换时不丢失信息。这种情况不需要启动总召唤刷新控制站的数据库。只在刚合上电源或者整个控制站复位后总召唤以及在一些系统中的时钟同步过程才是不可少的。 被控站可能由当地命令控制复位或接受控制站的请求而复位。 这里以DL/T 634.5104中的描述为准来具体分析。 连接的释放既可由控制站也可由被控站提出连接的建立有两种方式
由一对控制站和被控站中的控制站建立连接。两个平等的控制站固定选择参数其中一个站建立连接端到端方式。
图19显示关闭一个已建立的连接首先由控制站向其TCP发出主动关闭请求接着被控站向其TCP发出被动关闭请求。图19接着显示建立一个新连接首先由控制主站向其TCP发送主动打开请求接着被控站向其TCP发出被动打开请求。最后图19显示可选择由被控站主动关闭连接。 图20显示控制站初始化时依次与每一个被控站建立连接。由子站1开始控制站向TCP发出主动打开请求如果被控站的TCP有监听状态状态未显示在图中连接就建立起来了。其他的被控站也重复相同的过程。 图21显示控制站反复尝试与被控站建立连接。直到被控站完成本地的初始化向TCP发出被动打开请求取得监听状态连接才成功。 图22显示控制站向TCP发出主动打开请求建立连接然后向被控站发出复位进程命令被控站返回确认并向TCP发出主动关闭请求。控制站向TCP发出被动关闭请求后连接被释放然后控制站向TCP循环发出主动打开请求试着连接被控站。当被控子站完成初始化并再次可用被控站返回CLTSYNACK。当控制站确认CTLSYNACK后连接建立。
9.3 用查询方式采集数据GB/T 18657.5的6.2
工作在非平衡传输过程的数据采集系统系统采用查询方式进行数据采集以各被控站的过程变量的实际状态刷新控制站保存的数据。控制站顺序地召唤各被控站被控站只在被查询时才传输。 请求1级和2级用户数据是GB/T 18657.2的链路功能无法用于本标准中。但是可以按照GB/T 18657.5中图10底部所示的方法读取请求数据。因为循环请求数据会加重网络传输负担因此尽管允许也应尽量避免。 循环或非循环查询方式的各种可能查询过程如图10所示。 第1种过程为控制站的通信服务发送“请求1级用户数据”被控站回答否定认可(NACK)。这过程发生在事件采集中没有事件等待传输时。 第2种过程为控制站发送“请求2级用户数据”到一个被控站该站返回了数据。返回的数据在控制站以A_USER_CLASS2.ind传递给应用功能其要求访问位(ACD-bit)1(见GB/T 18657.2-2002的5.1-2)即被控站向控制站表示有1级用户数据请求控制站发送“请求 1级用户数据”。 第3种过程控制站的应用功能产生A_RD_DATA(读数据)的请求它以C_RD_PDU(发送/确认链路服务)发送到被控站。然后请求的数据被“请求1级用户数据”查询以M_PDU传输到控制站并以A_M_DATA.ind传递给用户层。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_RD_DATA.req发送C_RDA_RD_DATA.ind接收C_RDA_M_DATA.req发送MA_M_DATA.ind接收M
9.4 循环数据传输GB/T 18657.5的6.3
循环数据传输用于平衡和非平衡传输方式远动系统的传输过程为过程变量当前值提供连续刷新功能。这过程的优先级一般较低可被事件触发的通信请求中断。 被控站的应用进程循环地将过程变量实际值写人缓冲区。缓冲区的实际值按循环间隔时间传输到控制站见图 11。传到控制站的数据由A CYCLIC D八TA. ind向控制站的过程传输。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_CYCLIC_DATA.req发送M CYCLICA_CYCLIC_DATA.ind接收M CYCLIC
9.5 事件采集GB/T 18657.5的6.4
事件在应用层突发地发生。将事件通知远方站的过程和通信系统的操作有关。在任何情况下采集事件比将这些事件传输到远方站快所以当地进程需要事件缓冲区以采集事件。 在平衡式通信系统中被控站按给定的优先级直接传输事件即中断低优先级的传输过程如中断循环传输过程。 在非平衡式通信系统中被控站的进程要等待控制站的传输请求。 顺序过程见图12 在非平衡传输系统中控制站定期地或由被控站请求启动采集事件即当控制站查询时被控站通知控制站发生了事件。在平衡传输系统中事件的传输由发送/确认过程完成。 若被控站存贮了一个或几个事件这些信息以A_ SPONT PDU传输到控制站并以A_ EVENT.ind原语发送给应用层见图 12。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_EVENT.req发送M SPONTA_EVENT.ind接收M SPONT
9.6 以快速-检验quick-check过程采集数据DL标准中未选用
该方法用于非平衡传输系统中的一些应用以加速事件的采集。 控制站周期地向所有被控站发送全局请求PDU查询被控站的访问要求。传输这些 PDU后有三种可能情况见图13。
第1种情况:从传输上次事件后没有发生事件。
这种情况下对全局请求无后回答超时后这过程就终止。
第2种情况:在寻址的被控站中有一个发生了事件。
该被控站向通信服务发送A_EVENT.req请求原语。被控站接收到查询访问的请求后、发送响应访问要求的PDU给控制站然后控制站发送请求1级用户数据的PDU给被控站被控站以1级用户数据PDU格式向控制站发送事件。控制站接收后以A_EVENT.ind指示原语传递给应用功能。
第3种情况:多个寻址的被控站发生了事件。
这种情况下等待事件传输的各被控站同时向控制站发送响应访问请求的PDU.这时发送帧冲突。控制站检出了冲突被控站的全部帧传输结束后控制站启动图10所述的事件查询过程。
9.7 总召唤-子站召唤GB/T 18657.5的6.6
子站召唤功能用于站初始化过程结束后或控制站检出信息丢失时刷新控制站的数据。控制站的总召唤功能请求被控站传送全部过程变量的实际值。 接收到召唤命令以后被控站传送被召唤的信息。控制站和被控站的应用功能一般都知道被请求信息的总量。控制站校核召唤传输的信息总量就可确定召唤过程是否结束。如召唤的信息总量没有在控制站定义子站需发送标志召唤过程的结束的召唤服务结束(选用)。 随时发生在被控站的事件可中断子站召唤过程应十分小心以避免混乱。这些混乱可能由于接收到已过时的召唤信息引起。 顺序过程的描述见图14 控制站的应用进程给通信服务发送召唤命令A_GENINCOM.req请求原语通信服务传输C_IC ACT PDU(召唤命令PDU)到被控站被控站以A_GENINCOM.ind指示原语传递给应用进程。 被控站开始应用进程的召唤过程后传输对总召唤的确认即传输由请求原语八_GENINCOM.req启动的C_ IC ACTCON PDU。该PDU被控制站接收后以A_GENINACK.ind指示原语传递给控制站的应用功能。这个服务是选用的。 被控站的应用功能以M(监视信息)规约数据单元向控制站传输被召唤的信息这些信息由请求原语A_INTINF.req启动以指示原语A_INTINF.ind传送给控制站的应用功能。 传输了最后的被召唤的信息以后可由被控站的应用功能标明召唤过程的结束通过由请求原语 A_ENDINT.req启动的C_IC_ACTTERM PDU传送给控制站再以A_ENDINT.ind指示原语告知控制站的应用功能。这个服务是选用的。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_GENINCOM.req发送C_IC ACTA_GENINCOM.ind接收C_IC ACTA_GENINACK.req发送C_IC ACTCONA_GENINACK.req接收C_IC ACTCONA_INTINF.req发送MA_INTINF.ind接收MA_ENDINT.req发送C_IC ACTTERMA_ENDINT.ind接收C_IC ACTTERM
9.8 时钟同步GB/T 18657.5的6.7
** 为给传输到控制站或当地登录的带时标的事件或信息体提供准确的日历时间应将被控站的时钟和控制站的时钟同步。系统初始化后控制站先同步然后通过控制站传输C_ CS ACT(时钟同步命令))PDU定期地再同步。**
C_CS_ACT PDU包含全部当前时钟时间即传输C_CS ACT PDU第1比特瞬间的具有所需时间分辨率的日期和时间的信息。被控站接收该PDU时必需校正其时间信息或在控制站发送时钟同步命令 PDU前先校正控制站的时间信息。时间校正值是时间同步帧长和传输速率的乘积加传输延时的和。被控站时钟同步操作依赖于具体的进程要求不属标准化范围 被控站在时钟同步后产生C_CS ACTCON PDU。该PDU包含被控站同步前的当地时间信息减去时间校正值在所有存贮在缓冲区中的等待传输的带时标的PDU传输后传输。发生在时钟同步以后的事件在被控站传输C_CS ACTCON PUD后传输。 被控站期待在规定的时间间隔内接收到时钟同步命令。该时间间隔和时钟的准确度及容许时间偏差有关。如在该时间间隔内没有接收到时间同步命令被控站将全部带时标的信息体加上标记表示时间信息的准确度可疑。同样在被控站硬件复位或初始化之后接收到有效时问同步命令C_ CS ACT PDU以前的所有带时标信息体也将加上时间信息可能不准确的标记。接收有效的C_CS ACT PDU后发生的带时标的事件不带这种标记。 C_CS ACT PDU可按发送/无回答服务发送(广播发送到多个被控站)或按链路层的发送/确认服务发送。 顺序过程的描述见图15。控制站的应用进程以CLOCKSYN.req请求原语向通信服务发送时钟同步命令通信服务发送包含时钟时间的C_CS ACT PDU给被控站被控站以A_CLOCKSYN.ind指示原语传递给应用进程。 执行时钟同步操作后被控站应用进程产生由八_TIMEMESS.req请求原语启动、以C_CS CT-CON PDU传输的时间报文。该PDU包含同步前瞬间有效的时间信息减去时间校正值。该信息在控制站以A TIMEMESS.ind指示原语传递给应用进程。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_CLOCKSYN.req发送C_CS ACTA_CLOCKSYN.ind接收C_CS ACTA_TIMEMESS.req发送C_CS ACTCONA_TIMEMESS.req接收C_CS ACTCON
按照GB/T 18657.2链路层提供发送时钟命令的精确时间因为本部分不适用该链路层故GB/T 18657.5中定义的时钟同步过程无法应用于本部分。 但是当最大网络延迟小于接收站要求的时钟精度时配置中仍然可以使用时钟同步。例如如果网络提供者保证网络延迟不大于400msX.25 WAN的典型值并且被控站要求的精度为1s时钟同步过程就可以使用从而避免在几百甚至上千个被控站安装时钟同步接收器或类似的装置。 时钟同步过程参照GB/T 18657.5的6.7删去”比特1“和”时间修正“要求以及链路层选项发送/无回答或发送/确认。 被控站的时钟必须与控制站同步以提供具有正确的按时间顺序排列的带时标的事件和信息对象不管发送给控制站还是记录在本地。系统初始化完成后控制站进行初始化同步以后每隔一段约定的时间发送C_CS ACT PDU再同步。
9.9 命令传输GB/T 18657.5的6.8
远动系统的命令用于改变运行设备的状态(见IEV 371-03-01)。这样命令可使受控过程向预定方向发展。 命令可由操作人员或控制站自动监视过程启动。对未授权的访问或误动作的防止和系统或进程相关。 典型的运行设备或应用进程涉及的任务对象包括:
电气接触器、隔离刀闸断路器当地控制进程的启动和停止当地控制顺序中的执行步骤点、告警限值、具体参数等的设置。
有两种标准的命令传输过程即: 1选择和执行命令 2直接命令 选择和执行命令用于控制站对被控站准备控制操作。检查控制操作是否已正确准备好然后执行命令。 直接命令用于控制站对被控站立即进行控制操作。被控站的应用功能为了安全检查接收的命令报文的允许性和有效性。如检查正确执行操作检查。 也可由操作员或应用进程进行。直到接收到正确的执行指示后被控站才执行控制操作。 选择和执行命令以及直接命令的顺序过程如图16所示描述如下:
选择和执行命令过程
控制站应用进程给通信服务发送请求原语A_SELECT.req通信服务发送包含C_ACT(选择命令)的PDU给被控站被控站接收后以A_SELECT.ind指示原语传递给应用进程。如被控站的应用进程准备接收“选择命令”告知的控制命令它产生“选择响应”通过A_ SELECT. resp选择命令响应原语返送给通信服务该命令响应以C_ACTCON PDU传输给控制站控制站接收后通过A_SELECT.con选择确认原语产生“选择确认”。这个过程仅用于选择和执行命令不被中断由超时控制。 选择过程可由“撤消命令”停止执行。该命令以C_DEACT发给被控站由C_DEACTCON响应。 如选择命令得到确认以A_EXCO.req请求原语向通信服务发“执行命令”。控制站以C ACT PDU将执行命令传输给被控站被控站以A_EXCO.ind指示原语传递给应用进程功能以C ACT-CON PDU将“执行响应”返回给控制站产生肯定或否定的确认。这样规定的控制操作即将开始。这个过程不被中断由超时控制。
直接命令过程
被控站的应用进程检查寻址的命令输出是否被闭锁即执行是否已准备好。如果检查结果是肯定的被控站的应用进程向执行设备发出命令同时返送肯定的C_ACTCON PDU否则返送否定的C_ACTCON PDU。 当命令传递到应用进程时寻址的运行设备将改变状态这状态改变受到监视并通过返回信息告知控制站(见IEV 371-02-05)。在特殊命令情况下例如控制动作慢的隔离刀闸的双命令(见 IEV 371-03-03)当原先的合或者分状态信息还存在时状态改变的开始可以选择地以“控制操作开始”的M_PDU返送给控制站。当命令执行完毕设备处于新状态时被控站的应用进程以“控制操作完成”的M PDU返送给控制站见图16。 最后可用C_ACTTERM PDU表明控制操作结束(选用)。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_SELECT.req发送C_SCC_DCC_SEC_RCC_BO ACTA_SELECT.ind接收C_SCC_DCC_SEC_RCC_BO ACTA_SELECT.res发送C_SCC_DCC_SEC_RCC_BO ACTCONA_SELECT.con接收C_SCC_DCC_SEC_RCC_BO ACTCONA_BREAK.req发送C_SCC_DCC_SEC_RCC_BO DEACTA_BREAK.ind接收C_SCC_DCC_SEC_RCC_BO DEACTA_BREAK.res发送C_SCC_DCC_SEC_RCC_BO DEACTCONA_BREAK.con接收C_SCC_DCC_SEC_RCC_BO DEACTCONA_EXCO.req发送C_SCC_DCC_SEC_RCC_BO ACTA_EXCO.ind接收C_SCC_DCC_SEC_RCC_BO ACTA_EXCO.res发送C_SCC_DCC_SEC_RCC_BO ACTCONA_EXCO.con接收C_SCC_DCC_SEC_RCC_BO ACTCONA_RETURN_INF.req发送M_SPM_DPM_STA_RETURN_INF.ind接收M_SPM_DPM_STA_COTERM.req发送C_SCC_DCC_SEC_RCC_BO ACTTERMA_COTERM.ind接收C_SCC_DCC_SEC_RCC_BO ACTTERM
9.10 传输累计量远程累计GB/T 18657.5的6.9
**远程累计定义为“应用通信技术传输某一被测量对一特定参量(如时间)的累计值。**累计可发生在传送前或传送后。如在传送前累计就以‘传输累计量”表述。IEV 371-01-05) 。 累计量是一个按规定时段进行累计的值。连续采集累计量的具体时间和时间间隔都是系统参量一些系统在控制站使用命令周期激活累计量采集还有一些系统则以被控站的当地时钟定期激活累计量采集。控制站时钟同步可由远动系统(见 6. 7 )或由外部同步时钟过程进行例如接收国家的或者国际的无线电广播时间信息。 采集累计量信息有两种不同的方法: 1)采集累计值 被控站周期地在特定时刻记忆(冻结)累计量将累计量存贮到缓冲存贮器再将记忆值传送到控制站累计计数器连续工作不因记忆操作而复位。这种情况的每个时段的增量值由控制站计算。增量值是两次连续传输值的差。 2)采集增量信息 被控站周期地在具体时刻记忆(冻结)累计量将累计量存贮到缓冲存贮器再将记忆值传送到控制站。 顺序过程的描述见图17。作为一种选用方式控制站周期地在特定时刻传输C_CI ACT PDU(可以是记忆计数命令或记忆增量命令)给被控站。这两种命令都使瞬时累计量存贮到缓冲存贮器。若为记忆增量命令还要将累计计数器中累计量清零。这个过程也可由被控站的当地时钟激活。 上述过程执行后记忆的累计值可以由C_CI ACT(请求累计量)请求上传.然后被控站以C_CI ACTCON响应传输记忆值。也可以将记忆值作为事件对象传输到控制站这时控制站按事件采集记忆值(M_ IT PDU)(见6.4)。 累计量传输可由A_IBREAK. req请求原语终止。该原语以C_CIDEACT传输给被控站被控站收到后以C_CI DEACTCON响应。 在请求累计量情况下累计量传完后可用 C_CI ACTTERM PDU表示控制操作过程结束(选用)。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_MEMCNT.req发送C_CI ACTA_MEMCNT.ind接收C_CI ACTA_MEMCNT.res发送C_CI ACTCONA_MEMCNT.con接收C_CI ACTCONA_MEMINCR.req发送C_CI ACTA_MEMINCR.ind接收C_CI ACTA_MEMINCR.res发送C_CI ACTCONA_MEMINCR.con接收C_CI ACTCONA_REQINTO.req发送C_CI ACTA_REQINTO.ind接收C_CI ACTA_REQINTO.res发送C_CI ACTCONA_REQINTO.con接收C_CI ACTCONA_INTO_INF.req发送M_ITA_INTO_INF.ind接收M_ITA_ITERM.req发送C_CI ACTTERMA_ITERM.ind接收C_CI ACTTERM
9.11 参数装载GB/T 18657.5的6.10
系统以装载参数改变被控站定义的参数如门限值、测量值的上下限。装载参数一般分两步进行 1)以参数命令要求被控站装载一个或多个参数被控站存贮这些参数暂不激活。 2)再以参数激活命令激活上述装载的参数 如要求在同一时间内激活一定数量的参数需要分两个步骤。如只装载一个参数装载参数和激活可结合起来一步完成。 顺序过程的描述见图18.控制站的应用进程向通信服务发送A_PARAM.req请求原语通信服务传输包含P_ME ACT(多数命令)的PDU被控站接收后向应用进程传递 A_PARAM.ind指示原语。被控站的应用功能产生参数命令ACK通过A_PARAM.res响应原语返送给通信服务。被控站向控制站传输命令响应P_ME ACTCON PDU控制站通过A_PARAM.can确认原语产生认可。 如上述装载的参数分别被激活单个参数激活命令通过A_PACTIV.req请求原语发送给通信服务通信服务以P_AC ACT PDU向被控站传输该命令以A_PACTIV.ind指示原语传递给被控站的应用进程。然后通过P_AC ACTCON PDU向控制站返送认可确认装载的参数已运行。 在当地改变参数的情况下被控站可传送 P_ME SPONT PDU给控制站。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_PARAM.req发送P_ME ACTA_PARAM.ind接收P_ME ACTA_PARAM.res发送P_ME ACTCONA_PARAM.con接收P_ME ACTCONA_PACTIV.req发送P_AC ACTA_PACTIV.ind接收P_AC ACTA_PACTIV.res发送P_AC ACTCONA_PACTIV.con接收P_AC ACTCONA_LCPACH.req发送P_ME SPONTA_LCPACH.ind接收P_ME SPONT
9.12 测试过程GB/T 18657.5的6.11
测试过程用以检查控制站到被控站、被控站回控制站、包括相应应用功能的整个环路。 顺序过程的描述见图19。控制站的应用功能向通信服务发送测试请求原语A_TEST.req通信服务传输包括C_TS ACT(测试命令)的PDU给被控站。被控站以A_TEST.ind指示原语传递给应用功能被控站的应用功能产生测试命令的ACK通过A_TEST.res响应原语返送给通信服务。该命令响应以C_TSACTCON PDU传输给控制站在控制站通过A_TEST.con原语确认响应控制站检查C_TEST PDU的镜像。若在限定的时间内接收到同样的 PDU检查结果为肯定的。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_TEST.req发送C_TS ACTA_TEST.ind接收C_TS ACTA_TEST.res发送C_TS ACTCONA_TEST.con接收C_TS ACTCON
9.13 文件传输GB/T 18657.5的6.12
如远动系统中某信息体的八位位组数超过应用服务数据单元 ASDU规定的最大长度就需要采用文件传输以分段的形式将信息体传送到目的地。在远动系统中文件可从被控站传向控制站也可控制站传向被控站。被控站因事件引起的大量数据记录(如故障录波器的数据记录)顺序地传送到控制站。这些文件的类型和数目在被控站登录应以目录PDU告知控制站。 从控制站到被控站的参数表或程序的下载进程由控制站管理不需要传输目录。 两个方向的文件结构相同(见图20)。一个文件可分成几节一个节可分成几段由PDU按段顺序传输。故障录波器记录的一个故障可以表示为文件的一个节。若干数值可组合一起成为文件的段。 虽然两个方向文件的结构相同但传输过程不同所以分别描述。
1监视方向的文件传输
从被控站到控制站的文件传输主要用于通知控制站已发生事件并已登录大量数据。文件数量、登录时间和事件类型(如继电保护跳闸命令瞬间记录的数据)用目录PDU告知控制站由控制站决定是否传输及传输哪个文件。被控站将已成功传输文件的数据记录删除为新文件腾出内存空间。 顺序过程描述 顺序过程的描述见图 21。被控站因发生事件而生成一个新文件自发地向控制站传送新文件目录 PDU其内容包含被控站记录还没有向控制站发送的文件的数量、类型和生成时刻。 控制站还可在任何时候通过A_CALL_DIRECTORY请求采集被控站登录的文件数量和类型。 当控制站准备好接收文件时向被控站发送SELECT_FILE PDU被控站准备传送所选文件并将准备状态用 FILE_ READY PDU告知控制站。 控制站用CALL_ FILE PDU请求选择的文件。被控站用SECTION_ READY PDU响应告知控制站文件的第 1节已准备传输。 控制站发送CALL_SECTION PDU(肯定)请求第1节或发送CALL_SECTION PDU(否定)拒绝请求第1节。在否定情况下被控站向控制站发送SECTION_ READY PDU表示文件第2节(下一节)已准备好。在肯定情况下被控站用SEGMENT PDU顺序传输该节第 1段到第n段最后一段发送完后 被控站发送LAST_ SEGMENT PDU给控制站。控制站以ACK_SECTION PDU肯定或否定地确认有关节的接收。如被控站收到否定认可再向控制站发送SECTION_READY PDU表示准备再传输该节。如收到肯定认可被控站发送SECTION_READY PDU表示下一节已准备好。重复这个过程传输文件的以下各节。 传输了最后一节后被控站以LAST_SECTION PDU表明文件传输结束。控制站以ACK_FILE PDU确认整个文件的正确接收被控站可从缓冲存贮器和目录中删除这个文件再通过目录 PDU将修改后的实际目录情况传送给控制站。
2控制方向的文件传输
控制方向的文件传输主要用于下载参数表或程序。控制站安排传输数据文件的类型、数量和规模因此不需要传输目录。 顺序过程描述 顺序过程的描述见图22。控制站以FILE_READY PDU告知被控站要传输文件。若被控站已准备接收文件发送CALL_FILE PDU给控制站。控制站以SECTION_READY PDU告知被控站已准备好文件某节。如被控站准备接收向控制站传输CALL_SECTION PDU。 控制站以SEGMENTS PDU向被控站传输准备好的节的各段 用LAST_SEGMENT PDU指明最后一段。如被控站正确接收了各段传输 ACK_SECTION PDU给控制站。 如上所述控制站依次传输文件下面各节。传输最后一节后‘控制站传输LAST_SECTION PDU表明文件传输结束。如被控站正确接收了整个文件以ACK_FILE PDU确认。
应用服务GB/T 18657.5TCP服务RFC 793ASDU标识GB/T 18657.5A_CALL_DIRECTORY.req发送F_SCA_CALL_DIRECTORY.ind接收F_SCA_CALL_DIRECTORY.res发送F_DRA_CALL_DIRECTORY.con接收F_DRA_SELECT_FILE.req发送F_SCA_SELECT_FILE.ind接收F_SCA_FILE_READY.req发送F_FRA_FILE_READY.ind接收F_FRA_CALL_FILE.req发送F_SCA_CALL_FILE.ind接收F_SCA_SECTION1_READY.req发送F_SRA_SECTION1_READY.ind接收F_SRA_CALL_SECTION1.req发送F_SCA_CALL_SECTION1.ind接收F_SCA_SEGMENT1.req发送F_SGA_SEGMENT1.ind接收F_SGA_SEGMENTn.req发送F_SGA_SEGMENTn.ind接收F_SGA_LAST_SEGMENT.req发送F_LSA_LAST_SEGMENT.ind接收F_LSA_ACK_SECTION1.req发送F_AFA_ACK_SECTION1.ind接收F_AFA_SECTIONm_READY.req发送F_SRA_SECTIONm_READY.ind接收F_SRA_CALL_SECTIONm.req发送F_SCA_CALL_SECTIONm.ind接收F_SCA_ACK_SECTIONm.req发送F_AFA_ACK_SECTIONm.ind接收F_AFA_LAST_SECTION.req发送F_LSA_LAST_SECTION.ind接收F_LSA_ACK_FILE.req发送F_AFA_ACK_FILE.ind接收F_AFA_DIRECTORY.req发送F_DRA_DIRECTORY.ind接收F_DR
9.14 传输延时采集DL标准中未选用
**被控站时钟同步包括时间校正。时间校正值决定于传输延时和设备内部延时的和。**后者决定了对设备本身的要求不在本标准范围内。传输延时值可以通过参数分别采集或由控制站启动动态过程采集。传输延时的动态采集过程如下。 顺序过程描述 顺序过程的描述见图23。控制站发送C_CD ACT(延时数据采集)PDU其中包含发送该PDU第1比特瞬间的时刻(时间SDT)。被控站以接收的PDU中的时间同步其内部时钟(或辅助时钟)从而和时间 SDT同步。被控站返送 C_CD ACTCON PDU响应时间同步。该PDU包含发送该帧的第1比特的被控站时刻(SDTtR)控制站在RDT时刻接收到该响应PDU。这样控制站可用下式计算传输延时 tD: 控制站用C_CD SPONTANEOUS PDU将采集的传输延时传输到被控站(选用)以校正同步时间。 注:上述方法假定控制方向和监视方向的传输时间相同。
10、控制方向带时标的过程信息ASDU
这个似乎是DL标准中新增的新增了一些控制方向带时标CP56Time2a的ASDU。这个时标包含从毫秒到年的日期和分钟时间在DL/T 634.5101中有定义。当使用那些可能产生较大的命令延迟的网络时本部分建议在发送时使用带时标的ASDU这样当被控站收到一个超过最大允许延迟系统特定参数的命令或设定时不会发回一个协议上的响应例如被控站不回复一个”肯定“确认或”否定“确认。这是因为这个确认信息可能被明显地滞后并难以与最初的主站请求相关联。命令被传递至被控站的应用时这个命令将被识别出来是接收得”太迟“了且不得执行命令中的任何操作该时标包含了控制站的命令初始形成时的时间。
10.1 、传送原因
基于DL/T 634.5101的7.2.3
10.2、新增的带时标的ASDU分析
1这里以第一个类型标识58C_SC_TA_1为例
带时标CP56Time2a的单命令见图23. 单个信息对象SQ0 C_SC_TA_1: CP{数据单元标识符信息对象地址SOCCP56Time2a} 类型标识58:C_SC_TA_1中使用的传送原因 在控制方向 6:激活 8:停止激活 在监视方向 9:停止激活确认 10:激活终止 44:未知的类型标识 45:未知的传送原因 46:未知的ASDU公共地址 47:未知的信息对象地址
2其它
类新版标识59C_DC_TA_1 类型标识60C_RC_TA_1 类型标识61C_SE_TA_1 类型标识62C_SE_TB_1 类型标识63C_SE_TC_1 类型标识64C_BO_TA_1 类型标识107C_TS_TA_1 类型标识127F_SC_NB_1 整体来说通过类型标识来区别不同的ASDU都增加了CP56Time2a的时标用于网络延迟可能较高的环境。
11、互操作性
对于可选性和互斥性选项做了详细说明这对于需要配置控制站和被控站的参数有很大的帮助如果要开发配置软件或者配置平台这里的配置互斥性和可选性就会很有帮助。这里不展开了 对照标准即可。
12、四摇
电气术语在线数据库IEVhttps://www.electropedia.org/iev/iev.nsf/index?openformpart371
四、104客户端服务端结合电网四摇实例
我们可以了解到对应电力四摇等概念其实就是发送开关量进行远程控制或者主动上报一些模拟量或累积量这样我们结合协议的理解以及60870的104客户端服务端实例就比较容易实现简单的例子了 借助于其时间同步的命令同步修改服务端的时间和客户端保持一致这样104协议也可以开发一个简单的客户端服务端时间同步功能否则就像104协议所说大量的电网设备都需要单独部分程序进行时间同步了。如下做两种模拟实验场景来熟悉下协议的应用开发 实验场景1
客户端作为主机开关量发送带时标的命令服务端作为从机收到命令后计算单向时延并返回客户端作为主机发送命令启动模拟量上报服务端作为从机间隔上报带时标的模拟量客户端收到后计算单向时延
实验场景2
客户端作为主机发送命令或模拟量并获取当前ns级时间服务端作为从机只需要回复确认指令客户端根据回复内容获取当前ns级时间之后算出命令或模拟量发送到服务端的双向时延
这两种应用只是臆想的应用场景用于测试其TCP发包的单向或双向时延是否具备参考价值不得而知只是为了熟悉协议开发而做的实验这其中的重点仍是对协议的熟悉以及对接口调用的熟悉这里例子给到实验场景1的代码实验场景2可以自行根据理解去模拟实现一下实际的开发能帮助你快速理解掌握协议。
#include stdbool.h
#include stdio.h
#include string.h
#include signal.h
#include time.h
#include sys/time.h#include cs104_slave.h#include hal_thread.h
#include hal_time.h
#include power_def.h#define mcu_opi_oddsef_ms 0static bool running true;void
sigint_handler(int signalId)
{running false;
}void
printCP56Time2a(CP56Time2a time)
{printf(%02i:%02i:%02i:%03i %02i/%02i/%04i, CP56Time2a_getHour(time),CP56Time2a_getMinute(time),CP56Time2a_getSecond(time),CP56Time2a_getMillisecond(time),CP56Time2a_getDayOfMonth(time),CP56Time2a_getMonth(time),CP56Time2a_getYear(time) 2000);
}/* Callback handler to log sent or received messages (optional) */
static void
rawMessageHandler(void* parameter, IMasterConnection conneciton, uint8_t* msg, int msgSize, bool sent)
{if (sent)printf(SEND: );elseprintf(RCVD: );int i;for (i 0; i msgSize; i) {printf(%02x , msg[i]);}printf(\n);
}void opi_utc_update(uint8_t *msgBuf, uint16_t size)
{time_t rawTime;//struct tm *pNowTM;struct tm nowTM;struct timeval nowTV;int microSec 0;if (size 8)return;rawTime time(NULL);printf(RawTime 1 is %lu\r\n, rawTime);/*Set my time*/nowTM.tm_year 100 (int)msgBuf[0];nowTM.tm_mon msgBuf[1] - 1;nowTM.tm_mday msgBuf[2];nowTM.tm_hour msgBuf[3];nowTM.tm_min msgBuf[4];nowTM.tm_sec msgBuf[5];rawTime mktime(nowTM);if (nowTM.tm_gmtoff ! 0)rawTime nowTM.tm_gmtoff;printf(RawTime 3 is %lu\r\n, rawTime);microSec msgBuf[6];microSec 8;microSec msgBuf[7];microSec mcu_opi_oddsef_ms;if (microSec 1000){microSec % 1000;rawTime 1;}microSec * 1000;/*Set time of day*/nowTV.tv_sec rawTime;nowTV.tv_usec microSec;if (settimeofday(nowTV, NULL) ! 0){printf(Can not set system time\r\n);return;}/*update hardware time*/int rc system(hwclock --systohc);printf(return value is %d \r\n, rc);
}static bool
clockSyncHandler (void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a newTime)
{printf(Process time sync command with time );printCP56Time2a(newTime);printf(\n);uint64_t newSystemTimeInMs CP56Time2a_toMsTimestamp(newTime);/* Set time for ACT_CON message */
// CP56Time2a_setFromMsTimestamp(newTime, Hal_getTimeInMs());/* update system time here */uint8_t set_time[8] {CP56Time2a_getYear(newTime), CP56Time2a_getMonth(newTime),CP56Time2a_getDayOfMonth(newTime), CP56Time2a_getHour(newTime),CP56Time2a_getMinute(newTime), CP56Time2a_getSecond(newTime),CP56Time2a_getMillisecond(newTime), mcu_opi_oddsef_ms};opi_utc_update(set_time, 8);return true;
}static bool
interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi)
{printf(Received interrogation for group %i\n, qoi);if (qoi IEC60870_QOI_STATION) { /* only handle station interrogation */printf(station group %i\n, qoi);CS101_AppLayerParameters alParams IMasterConnection_getApplicationLayerParameters(connection);IMasterConnection_sendACT_CON(connection, asdu, false);/* The CS101 specification only allows information objects without timestamp in GI responses */CS101_ASDU newAsdu CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION,0, 1, false, false);InformationObject io (InformationObject) MeasuredValueScaled_create(NULL, 100, -1, IEC60870_QUALITY_GOOD);CS101_ASDU_addInformationObject(newAsdu, io);CS101_ASDU_addInformationObject(newAsdu, (InformationObject)MeasuredValueScaled_create((MeasuredValueScaled) io, 101, 23, IEC60870_QUALITY_GOOD));CS101_ASDU_addInformationObject(newAsdu, (InformationObject)MeasuredValueScaled_create((MeasuredValueScaled) io, 102, 2300, IEC60870_QUALITY_GOOD));InformationObject_destroy(io);IMasterConnection_sendASDU(connection, newAsdu);CS101_ASDU_destroy(newAsdu);newAsdu CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION,0, 1, false, false);io (InformationObject) SinglePointInformation_create(NULL, 104, true, IEC60870_QUALITY_GOOD);CS101_ASDU_addInformationObject(newAsdu, io);CS101_ASDU_addInformationObject(newAsdu, (InformationObject)SinglePointInformation_create((SinglePointInformation) io, 105, false, IEC60870_QUALITY_GOOD));InformationObject_destroy(io);IMasterConnection_sendASDU(connection, newAsdu);CS101_ASDU_destroy(newAsdu);newAsdu CS101_ASDU_create(alParams, true, CS101_COT_INTERROGATED_BY_STATION,0, 1, false, false);CS101_ASDU_addInformationObject(newAsdu, io (InformationObject) SinglePointInformation_create(NULL, 300, true, IEC60870_QUALITY_GOOD));CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 301, false, IEC60870_QUALITY_GOOD));CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 302, true, IEC60870_QUALITY_GOOD));CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 303, false, IEC60870_QUALITY_GOOD));CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 304, true, IEC60870_QUALITY_GOOD));CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 305, false, IEC60870_QUALITY_GOOD));CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 306, true, IEC60870_QUALITY_GOOD));CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 307, false, IEC60870_QUALITY_GOOD));InformationObject_destroy(io);IMasterConnection_sendASDU(connection, newAsdu);CS101_ASDU_destroy(newAsdu);newAsdu CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION,0, 1, false, false);io (InformationObject) BitString32_create(NULL, 500, 0xaaaa);CS101_ASDU_addInformationObject(newAsdu, io);InformationObject_destroy(io);IMasterConnection_sendASDU(connection, newAsdu);CS101_ASDU_destroy(newAsdu);IMasterConnection_sendACT_TERM(connection, asdu);} else if(qoi IEC60870_QOI_GROUP_1) {printf(1 group %i\n, qoi);} else if(qoi IEC60870_QOI_GROUP_2) {printf(2 group %i\n, qoi);}else {printf(send act con\n);IMasterConnection_sendACT_CON(connection, asdu, true);}return true;
}static bool g_send_yx false;
static bool g_send_yc false;
static bool g_send_yk false;
static bool g_send_yt false;
static int g_yk_ott 0;
static int g_yt_ott 0;static bool
asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu)
{printf(received command type:%d\n, CS101_ASDU_getTypeID(asdu));if (CS101_ASDU_getTypeID(asdu) C_SC_NA_1) {printf(received single command\n);if (CS101_ASDU_getCOT(asdu) CS101_COT_ACTIVATION) {InformationObject io CS101_ASDU_getElement(asdu, 0);if (io) {if (InformationObject_getObjectAddress(io) 5000) {SingleCommand sc (SingleCommand) io;printf(IOA: %i switch to %i\n, InformationObject_getObjectAddress(io),SingleCommand_getState(sc));CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);} else if (InformationObject_getObjectAddress(io) TEST_TYPE_YX) {SingleCommand sc (SingleCommand) io;printf(IOA: %i switch to %i\n, InformationObject_getObjectAddress(io),SingleCommand_getState(sc));CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);if (SingleCommand_getState(sc)) {g_send_yx true;} else {g_send_yx false;}} else if (InformationObject_getObjectAddress(io) TEST_TYPE_YC) {SingleCommand sc (SingleCommand) io;printf(IOA: %i switch to %i\n, InformationObject_getObjectAddress(io),SingleCommand_getState(sc));CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);if (SingleCommand_getState(sc)) {g_send_yc true;} else {g_send_yc false;}}elseCS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);InformationObject_destroy(io);}else {printf(ERROR: message has no valid information object\n);return true;}}elseCS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);IMasterConnection_sendASDU(connection, asdu);return true;} else if (CS101_ASDU_getTypeID(asdu) C_SC_TA_1) {printf(received single command with timestamp\n);if (CS101_ASDU_getCOT(asdu) CS101_COT_ACTIVATION) {InformationObject io CS101_ASDU_getElement(asdu, 0);if (io) {if (InformationObject_getObjectAddress(io) TEST_TYPE_YK) {SingleCommandWithCP56Time2a sc (SingleCommandWithCP56Time2a ) io;struct sCP56Time2a newTime;CP56Time2a_createFromMsTimestamp(newTime, Hal_getTimeInMs());printf( measured scaled values with CP56Time2a timestamp:\n);printf(IOA: %i switch to %i\n, InformationObject_getObjectAddress(io),SingleCommand_getState(sc));CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);int time_sec_rcv CP56Time2a_getSecond(newTime);int time_ms_rcv CP56Time2a_getMillisecond(newTime);int time_sec_send CP56Time2a_getSecond(SingleCommandWithCP56Time2a_getTimestamp(sc));int time_ms_send CP56Time2a_getMillisecond(SingleCommandWithCP56Time2a_getTimestamp(sc));g_yk_ott (time_sec_rcv - time_sec_send) * 1000 (time_ms_rcv - time_ms_send);printf(yk ott:%d ms\n, g_yk_ott);if (SingleCommand_getState(sc)) {g_send_yk true;} else {g_send_yk false;}} else if (InformationObject_getObjectAddress(io) TEST_TYPE_YT) {SingleCommandWithCP56Time2a sc (SingleCommandWithCP56Time2a ) io;struct sCP56Time2a newTime;CP56Time2a_createFromMsTimestamp(newTime, Hal_getTimeInMs());printf( measured scaled values with CP56Time2a timestamp:\n);printf(IOA: %i switch to %i\n, InformationObject_getObjectAddress(io),SingleCommand_getState(sc));CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);int time_sec_rcv CP56Time2a_getSecond(newTime);int time_ms_rcv CP56Time2a_getMillisecond(newTime);int time_sec_send CP56Time2a_getSecond(SingleCommandWithCP56Time2a_getTimestamp(sc));int time_ms_send CP56Time2a_getMillisecond(MeasuredValueScaledWithCP56Time2a_getTimestamp(sc));g_yt_ott (time_sec_rcv - time_sec_send) * 1000 (time_ms_rcv - time_ms_send);printf(yt ott:%d ms\n, g_yt_ott);if (SingleCommand_getState(sc)) {g_send_yt true;} else {g_send_yt false;}}elseCS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);InformationObject_destroy(io);}else {printf(ERROR: message has no valid information object\n);return true;}}elseCS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);IMasterConnection_sendASDU(connection, asdu);return true;}return false;
}static bool
connectionRequestHandler(void* parameter, const char* ipAddress)
{printf(New connection request from %s\n, ipAddress);#if 0if (strcmp(ipAddress, 127.0.0.1) 0) {printf(Accept connection\n);return true;}else {printf(Deny connection\n);return false;}
#elsereturn true;
#endif
}static void
connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event)
{if (event CS104_CON_EVENT_CONNECTION_OPENED) {printf(Connection opened (%p)\n, con);}else if (event CS104_CON_EVENT_CONNECTION_CLOSED) {printf(Connection closed (%p)\n, con);}else if (event CS104_CON_EVENT_ACTIVATED) {printf(Connection activated (%p)\n, con);}else if (event CS104_CON_EVENT_DEACTIVATED) {printf(Connection deactivated (%p)\n, con);}
}int
main(int argc, char** argv)
{/* Add Ctrl-C handler */signal(SIGINT, sigint_handler);/* create a new slave/server instance with default connection parameters and* default message queue size */CS104_Slave slave CS104_Slave_create(10, 10);CS104_Slave_setLocalAddress(slave, 0.0.0.0);/* Set mode to a single redundancy group* NOTE: library has to be compiled with CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP enabled (1)*/CS104_Slave_setServerMode(slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP);/* get the connection parameters - we need them to create correct ASDUs -* you can also modify the parameters here when default parameters are not to be used */CS101_AppLayerParameters alParams CS104_Slave_getAppLayerParameters(slave);/* when you have to tweak the APCI parameters (t0-t3, k, w) you can access them here */CS104_APCIParameters apciParams CS104_Slave_getConnectionParameters(slave);printf(APCI parameters:\n);printf( t0: %i\n, apciParams-t0);printf( t1: %i\n, apciParams-t1);printf( t2: %i\n, apciParams-t2);printf( t3: %i\n, apciParams-t3);printf( k: %i\n, apciParams-k);printf( w: %i\n, apciParams-w);/* set the callback handler for the clock synchronization command */CS104_Slave_setClockSyncHandler(slave, clockSyncHandler, NULL);/* set the callback handler for the interrogation command */CS104_Slave_setInterrogationHandler(slave, interrogationHandler, NULL);/* set handler for other message types */CS104_Slave_setASDUHandler(slave, asduHandler, NULL);/* set handler to handle connection requests (optional) */CS104_Slave_setConnectionRequestHandler(slave, connectionRequestHandler, NULL);/* set handler to track connection events (optional) */CS104_Slave_setConnectionEventHandler(slave, connectionEventHandler, NULL);/* uncomment to log messages */CS104_Slave_setRawMessageHandler(slave, rawMessageHandler, NULL);CS104_Slave_start(slave);if (CS104_Slave_isRunning(slave) false) {printf(Starting server failed!\n);goto exit_program;}while (running) {Thread_sleep(1000);CS101_ASDU newAsdu CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false);struct sCP56Time2a newTime;CP56Time2a_createFromMsTimestamp(newTime, Hal_getTimeInMs());if (g_send_yc) {//未收到停止指令一直发送时延InformationObject io (InformationObject) MeasuredValueScaledWithCP56Time2a_create(NULL, TEST_TYPE_YC, 1,IEC60870_QUALITY_GOOD, newTime);CS101_ASDU_addInformationObject(newAsdu, io);InformationObject_destroy(io);}if (g_send_yx) {//未收到停止指令一直发送时延InformationObject io (InformationObject) MeasuredValueScaledWithCP56Time2a_create(NULL, TEST_TYPE_YX, 1,IEC60870_QUALITY_GOOD, newTime);CS101_ASDU_addInformationObject(newAsdu, io);InformationObject_destroy(io);}if (g_send_yk) {InformationObject io (InformationObject) MeasuredValueScaledWithCP56Time2a_create(NULL, TEST_TYPE_YK, g_yk_ott,IEC60870_QUALITY_GOOD, newTime);CS101_ASDU_addInformationObject(newAsdu, io);InformationObject_destroy(io);//每一次时延计算及传输都需要发送指令g_send_yk false;}if (g_send_yt) {InformationObject io (InformationObject) MeasuredValueScaledWithCP56Time2a_create(NULL, TEST_TYPE_YT, g_yt_ott,IEC60870_QUALITY_GOOD, newTime);CS101_ASDU_addInformationObject(newAsdu, io);InformationObject_destroy(io);//每一次时延计算及传输都需要发送指令g_send_yt false;}/* Add ASDU to slave event queue */CS104_Slave_enqueueASDU(slave, newAsdu);CS101_ASDU_destroy(newAsdu);}CS104_Slave_stop(slave);exit_program:CS104_Slave_destroy(slave);Thread_sleep(500);
}
#include stdio.h
#include stdlib.h
#include signal.h#include cs104_connection.h
#include hal_time.h
#include hal_thread.h#include power_def.hstatic bool running true;
static bool is_send true;void
sigint_handler(int signalId) {running false;
}/* Callback handler to log sent or received messages (optional) */
static void
raw_message_handler(void *parameter, uint8_t *msg, int msgSize, bool sent) {if (sent)printf(SEND: );elseprintf(RCVD: );int i;for (i 0; i msgSize; i) {printf(%02x , msg[i]);}printf(\n);
}/* Connection event handler */
static void
connection_handler(void *parameter, CS104_Connection connection, CS104_ConnectionEvent event) {switch (event) {case CS104_CONNECTION_OPENED:printf(Connection established\n);break;case CS104_CONNECTION_CLOSED:printf(Connection closed\n);break;case CS104_CONNECTION_FAILED:printf(Failed to connect\n);break;case CS104_CONNECTION_STARTDT_CON_RECEIVED:printf(Received STARTDT_CON\n);break;case CS104_CONNECTION_STOPDT_CON_RECEIVED:printf(Received STOPDT_CON\n);break;}
}/** CS101_ASDUReceivedHandler implementation** For CS104 the address parameter has to be ignored*/
static bool
asdu_received_handler(void *parameter, int address, CS101_ASDU asdu) {printf(RECVD ASDU type: %s(%i) elements: %i\n,TypeID_toString(CS101_ASDU_getTypeID(asdu)),CS101_ASDU_getTypeID(asdu),CS101_ASDU_getNumberOfElements(asdu));if (CS101_ASDU_getTypeID(asdu) M_ME_TE_1) {int i;for (i 0; i CS101_ASDU_getNumberOfElements(asdu); i) {MeasuredValueScaledWithCP56Time2a io (MeasuredValueScaledWithCP56Time2a) CS101_ASDU_getElement(asdu, i);printf( IOA: %i value: %i\n,InformationObject_getObjectAddress((InformationObject) io),MeasuredValueScaled_getValue((MeasuredValueScaled) io));switch (InformationObject_getObjectAddress((InformationObject) io)) {case TEST_TYPE_YX: {struct sCP56Time2a newTime;CP56Time2a_createFromMsTimestamp(newTime, Hal_getTimeInMs());printf( measured scaled values with CP56Time2a timestamp:\n);int time_sec_rcv CP56Time2a_getSecond(newTime);int time_ms_rcv CP56Time2a_getMillisecond(newTime);int time_sec_send CP56Time2a_getSecond(MeasuredValueScaledWithCP56Time2a_getTimestamp(io));int time_ms_send CP56Time2a_getMillisecond(MeasuredValueScaledWithCP56Time2a_getTimestamp(io));int ott (time_sec_rcv - time_sec_send) * 1000 (time_ms_rcv - time_ms_send);printf(yx ott:%d ms\n, ott);}break;case TEST_TYPE_YC: {struct sCP56Time2a newTime;CP56Time2a_createFromMsTimestamp(newTime, Hal_getTimeInMs());printf( measured scaled values with CP56Time2a timestamp:\n);int time_sec_rcv CP56Time2a_getSecond(newTime);int time_ms_rcv CP56Time2a_getMillisecond(newTime);int time_sec_send CP56Time2a_getSecond(MeasuredValueScaledWithCP56Time2a_getTimestamp(io));int time_ms_send CP56Time2a_getMillisecond(MeasuredValueScaledWithCP56Time2a_getTimestamp(io));int ott (time_sec_rcv - time_sec_send) * 1000 (time_ms_rcv - time_ms_send);printf(yc ott:%d ms\n, ott);}break;case TEST_TYPE_YK: {int ott MeasuredValueScaled_getValue((MeasuredValueScaled) io);printf(yk ott:%d ms\n, ott);is_send true;}break;case TEST_TYPE_YT: {int ott MeasuredValueScaled_getValue((MeasuredValueScaled) io);printf(yt ott:%d ms\n, ott);is_send true;}break;}MeasuredValueScaledWithCP56Time2a_destroy(io);}} else if (CS101_ASDU_getTypeID(asdu) M_SP_NA_1) {printf( single point information:\n);int i;for (i 0; i CS101_ASDU_getNumberOfElements(asdu); i) {SinglePointInformation io (SinglePointInformation) CS101_ASDU_getElement(asdu, i);printf( IOA: %i value: %i\n,InformationObject_getObjectAddress((InformationObject) io),SinglePointInformation_getValue((SinglePointInformation) io));SinglePointInformation_destroy(io);}} else if (CS101_ASDU_getTypeID(asdu) C_TS_TA_1) {printf( test command with timestamp\n);}return true;
}int
main(int argc, char **argv) {const char *ip localhost;uint16_t port IEC_60870_5_104_DEFAULT_PORT;signal(SIGINT, sigint_handler);
// const char* localIp NULL;
// int localPort -1;if (argc 3) {printf(./%s ip port type\n, argv[0]);return 0;}ip argv[1];port atoi(argv[2]);printf(Connecting to: %s:%i\n, ip, port);CS104_Connection con CS104_Connection_create(ip, port);CS101_AppLayerParameters alParams CS104_Connection_getAppLayerParameters(con);alParams-originatorAddress 3;CS104_Connection_setConnectionHandler(con, connection_handler, NULL);CS104_Connection_setASDUReceivedHandler(con, asdu_received_handler, NULL);/* optional bind to local IP address/interface */if (localIp)
// CS104_Connection_setLocalAddress(con, localIp, localPort);/* uncomment to log messages */CS104_Connection_setRawMessageHandler(con, raw_message_handler, NULL);if (CS104_Connection_connect(con)) {printf(Connected!\n);CS104_Connection_sendStartDT(con);Thread_sleep(2000);CS104_Connection_sendInterrogationCommand(con, CS101_COT_ACTIVATION, 1, IEC60870_QOI_STATION);Thread_sleep(2000);CS104_Connection_sendTestFR(con);Thread_sleep(1000);/* Send clock synchronization command */struct sCP56Time2a newTime;CP56Time2a_createFromMsTimestamp(newTime, Hal_getTimeInMs());printf(Send time sync command\n);CS104_Connection_sendClockSyncCommand(con, 1, newTime);Thread_sleep(1000);while (running) {switch (atoi(argv[3])) {case TEST_TYPE_YX: {//if (is_send) {CS101_ASDU newAsdu CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false);InformationObject io (InformationObject) MeasuredValueScaledWithCP56Time2a_create(NULL, TEST_TYPE_YX, true,IEC60870_QUALITY_GOOD, newTime);CS101_ASDU_addInformationObject(newAsdu, io);InformationObject_destroy(io);CS104_Connection_sendASDU(con, newAsdu);is_send false;}}break;case TEST_TYPE_YC: {//发送命令触发从站定时传输遥测指标传输任意值在主站来计算摇测指令传输时延if (is_send) {CS101_ASDU newAsdu CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false);InformationObject io (InformationObject) MeasuredValueScaledWithCP56Time2a_create(NULL, TEST_TYPE_YC, true,IEC60870_QUALITY_GOOD, newTime);CS101_ASDU_addInformationObject(newAsdu, io);InformationObject_destroy(io);CS104_Connection_sendASDU(con, newAsdu);is_send false;}}break;case TEST_TYPE_YK: {//发出带时标的YK指令到从站命令的在从站计算时延if (is_send) {struct sCP56Time2a local_time;CP56Time2a_createFromMsTimestamp(local_time, Hal_getTimeInMs());InformationObject sc (InformationObject) SingleCommandWithCP56Time2a_create(NULL,TEST_TYPE_YK,true, false,0, local_time);CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, sc);InformationObject_destroy(sc);is_send false;}}break;case TEST_TYPE_YT: {//发出带时标的YT指令到从站在从站计算时延if (is_send) {struct sCP56Time2a local_time;CP56Time2a_createFromMsTimestamp(local_time, Hal_getTimeInMs());InformationObject sc (InformationObject) SingleCommandWithCP56Time2a_create(NULL,TEST_TYPE_YT,true, false,0, local_time);CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, sc);InformationObject_destroy(sc);is_send false;}}break;default:break;}Thread_sleep(1000);}InformationObject sc_yx (InformationObject) SingleCommand_create(NULL, TEST_TYPE_YX, false, false,0);CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, sc_yx);InformationObject_destroy(sc_yx);InformationObject sc_yc (InformationObject) SingleCommand_create(NULL, TEST_TYPE_YC, false, false,0);CS104_Connection_sendProcessCommandEx(con, CS101_COT_ACTIVATION, 1, sc_yc);InformationObject_destroy(sc_yc);printf(Wait ...\n);Thread_sleep(1000);CS104_Connection_sendStopDT(con);} elseprintf(Connect failed!\n);Thread_sleep(1000);CS104_Connection_destroy(con);printf(exit\n);
}
//
// Created by Administrator on 2024/1/5.
//#ifndef TESTTERMINAL_POWER_DEF_H
#define TESTTERMINAL_POWER_DEF_Htypedef enum {TEST_TYPE_YX 1, //遥信TEST_TYPE_YC, //摇测TEST_TYPE_YK, //遥控TEST_TYPE_YT, //摇调
} TEST_TYPE;typedef enum {SIGNAL_COMMAND 1,DOUBLE_COMMAND,STEP_COMMAND,SET_POINT_SCALED,SET_POINT_NORMALIZED,SET_POINT_SHORT,BIT_STRING_32,
} IEC60870_COMMAND_TYPE;typedef enum {SELECT_COMMAND 1,BREAK_COMMAND,EXECUTE_COMMAND,
} IEC60870_OPERATION_TYPE;typedef enum {SIGNAL_POINT_INFO 1,DOUBLE_POINT_INFO,STEP_POINT_INFO,BIT_STRING_32_INFO,MEASURED_NORMALIZED_INFO,MEASURED_SCALED_INFO,MEASURED_SHORT_INFO,
} IEC60870_INFO_TYPE;#endif //TESTTERMINAL_POWER_DEF_H
结果 如下为抓包内容 1.rar
五、最后
基本上iec101、iec104协议由于应用时间比较长了资料相对多一些再加上开源库所以掌握起来会相对简单一些。