Slave从站(server服务器)端编程
CS104(TCP / IP)服务器配置和设置
要配置和设置IEC 60870-5-104服务器/从属服务器,需要CS104_Slave数据类型的实例。
CS104_Slave slave = CS104_Slave_create(100,100);
创建服务器实例后,可以对其进行配置
CS104服务器模式
服务器提供了三种有关冗余连接支持和事件队列处理的模式:
默认模式(CS104_MODE_SINGLE_REDUNDANCY_GROUP)仅允许单个活动客户端连接。活动客户端连接是发送ASDU(应用程序数据单元)的连接。所有其他连接只是不发送应用程序层数据的备用连接。事件只有一个队列。当没有客户端连接或没有连接处于活动状态时,也会存储事件。
第二种模式(CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP)允许多个活动客户端连接。每个连接都有其自己的事件队列。关闭客户端连接后,事件队列将被删除。当多个客户端必须访问应用程序数据时,可以使用此模式。此模式易于使用。但是此模式的缺点是,当没有客户端连接时事件会丢失。
第三种模式(CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS)允许多个活动客户端连接,同时保留未连接任何客户端时的事件。在这种模式下,可以将客户端分配给特定的冗余组。分配基于客户端的IP地址。冗余组可以有多个同时连接,但是这些连接中只有一个可以处于活动状态。激活的连接数受冗余组数的限制。每个冗余组都有一个专用的事件队列。
可以使用CS104_Slave_setServerMode函数设置服务器模式:
CS104_Slave_setServerMode(从站,CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS);
CS104:定义多个冗余组
仅当使用服务器模式CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS时,才需要显式创建冗余组。您可以将多个IP地址分配给冗余组。然后,来自这些IP地址之一的传入连接将自动分配给该特定的冗余组。
当冗余组没有分配的IP地址时,它将用作“全部捕获”组。这意味着所有未分配给其他组之一的传入连接都将落入该组。
CS104_Slave_setServerMode(slave, CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS);
CS104_RedundancyGroup redGroup1 = CS104_RedundancyGroup_create("red-group-1");
CS104_RedundancyGroup_addAllowedClient(redGroup1, "192.168.2.9");
CS104_RedundancyGroup redGroup2 = CS104_RedundancyGroup_create("red-group-2");
CS104_RedundancyGroup_addAllowedClient(redGroup2, "192.168.2.223");
CS104_RedundancyGroup_addAllowedClient(redGroup2, "192.168.2.222");
CS104_RedundancyGroup redGroup3 = CS104_RedundancyGroup_create("catch-all");
CS104_Slave_addRedundancyGroup(slave, redGroup1);
CS104_Slave_addRedundancyGroup(slave, redGroup2);
CS104_Slave_addRedundancyGroup(slave, redGroup3);
CS101(串行)slave从站配置和设置
与主侧相似,CS101从侧也可以配置为两种链路层模式之一(平衡或不平衡)。CS101从站由CS101_SLave对象表示。
一个前CS101_Slave对象可以创建的SerialPort是必需的对象。所述的SerialPort对象表示串行接口和它的配置。
SerialPort port = SerialPort_create(serialPort, 9600, 8, 'E', 1);
CS101_Slave_create函数需要创建的SerialPort对象:
CS101_Slave slave = CS101_Slave_create(port,NULL,NULL,IEC60870_LINK_LAYER_UNBALANCED);
此功能具有以下签名:
CS101_Slave
CS101_Slave_create(SerialPort serialPort, LinkLayerParameters llParameters, CS101_AppLayerParameters alParameters, IEC60870_LinkLayerMode linkLayerMode)
可选地,可以指定链路层参数和应用程序层参数。如果应使用默认值,则可以跳过这些参数(设置为NULL)。最后一个参数指定使用平衡还是不平衡模式。
对于串行从站,还需要设置一个链路层地址:
CS101_Slave_setLinkLayerAddress(slave,1);
设置回调处理函数 callback handler functions
在启动或运行服务器之前,建议设置回调函数以处理从属事件。可以使用以下回调处理程序类型(有关功能签名的详细信息,请参阅API参考手册)。其中一些仅可用于CS 104服务器,而某些仅可用于CS101从服务器。
回调类型 | 事件 | 第101章 | 第104章 |
---|---|---|---|
CS101_InterrogationHandler | 询问请求 | + | + |
CS101_CounterInterrogationHandler | 反询问请求 | + | + |
CS101_ReadHandler | 读取单个信息对象的请求 | + | + |
CS101_ClockSynchronizationHandler | 收到时钟同步消息 | + | + |
CS101_ResetProcessHandler | 收到重置流程请求 | + | + |
CS101_DelayAcquisitionHandler | 收到延迟获取请求 | + | -- |
CS101_ASDUHandler | ASDU已收到但未由其他回调处理程序之一处理 | + | + |
CS101_ResetCUHandler | 收到类型为复位CU(通信单元)的链路层消息 | + | -- |
CS104_ConnectionRequestHandler | 一个新的TCP / IP客户端尝试连接 | -- | + |
/* set the callback handler for the clock synchronization command */
CS101_Slave_setClockSyncHandler(slave, clockSyncHandler, NULL);
/* set the callback handler for the interrogation command */
CS101_Slave_setInterrogationHandler(slave, interrogationHandler, NULL);
/* set handler for other message types */
CS101_Slave_setASDUHandler(slave, asduHandler, NULL);
/* set handler for reset CU (reset communication unit) message */
CS101_Slave_setResetCUHandler(slave, resetCUHandler, (void*) slave);
CS104启动/停止服务器
配置服务器后,可以使用CS104_Slave_start函数启动它。此函数启动一个新的后台线程,该线程正在侦听传入的客户端连接。
CS104_Slave_start(slave);
要停用IEC 60870-5-104服务,可以使用CS104_Slave_stop功能停止服务器。
CS104_Slave_stop(slave);
自动或定期发送消息
为了在server/slave 服务器/从站上 自动或定期地进行消息传输,用户必须分配代表单个ASDU的CS101_ASDU对象,将信息对象添加到ASDU,最后将ASDU放入传输队列中。传输队列是FIFO(先进先出)列表。如果队列已满,则最早的消息将被删除,并由新添加的消息代替。仅当存在活动的客户端连接或有效的链路层连接时,才发送消息。否则,消息将保留在队列中,直到激活连接为止。
CS 104:在CS 104从站中,队列大小由CS104_Slave_create函数的maxLowPrioQueueSize参数确定。如果将maxLowPrioQueueSize参数设置为零,则队列将始终具有CONFIG_SLAVE_MESSAGE_QUEUE_SIZE定义的大小。第二个参数maxHighPrioQueueSize确定高优先级数据队列的大小。放入此队列的消息会绕过低优先级队列的消息。高优先级队列用于库回调处理程序中的请求响应。
要发送自发或定期消息,必须执行以下步骤:
-
创建一个新的CS101_ASDU实例(使用CS101_COT_PERIODIC定期数据和CS101_COT_SPONTANEOUS自发数据)
CS101_ASDU newAsdu = CS101_ASDU_create(alParameters,false,CS101_COT_PERIODIC,0,1,false,false);
-
创建一个包含要发送数据的新信息对象实例
InformationObject io =(InformationObject)MeasuredValueScaled_create(NULL,110,scaledValue,IEC60870_QUALITY_GOOD);
-
将新的信息对象添加到ASDU
CS101_ASDU_addInformationObject(newAsdu,io);
-
释放信息对象内存
InformationObject_destroy(io);
-
将ASDU放入2类数据队列中进行传输
CS101_Slave_enqueueUserDataClass2(slave,newAsdu);
-
释放ASDU内存
CS101_ASDU_destroy(newAsdu);
注意:对于CS 104,您必须在步骤5中使用CS104_Slave_enqueueASDU函数:
CS104_Slave_enqueueASDU(slave,newAsdu);
处理查询请求
在服务器端,您应该使用InterrogationHandler回调函数来处理Interrogation请求。根据QOI(询问限定符)的值,您可以返回不同的信息对象。对于一个简单的系统,仅处理 总查询 请求就足够了(QOI = 20)。QOI值21-36用于询问组(1-16)。由从属实施者将信息对象分配给询问组。
根据规范,服务器必须用ACT_CON响应响应来自客户端的ACTIVATION请求,然后是ASDU,其中ASDU包含代表站质询或COT的信息对象,其中CS101_COT_INTERROGATED_BY_STATION代表相应的质询组(例如,质询组1的CS101_COT_INTERROGATED_BY_GROUP_1)。发送完所有信息对象后,服务器必须发送初始查询命令消息,其中COT = CS101_COT_ACTIVATION_TERMINATION,以指示查询数据的传输已完成。
static bool
interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi)
{
if (qoi == 20) { /* only handle station interrogation */
CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
IMasterConnection_sendACT_CON(connection, asdu, false);
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);
IMasterConnection_sendACT_TERM(connection, asdu);
}
else {
IMasterConnection_sendACT_CON(connection, asdu, true);
}
return true;
}