IEC61850开发实战(一)

IEC61850开发实战(一)

报告服务的实现

随着智能电网的推广,越来越多的开发者加入了IEC61850的阵营,为了能够更好的为IEC61850开发者提供帮助和交流,大连云行科技将陆续推出IEC61850开发实战的文章,希望能够给广大IEC61850的开发者提供有效的帮助。

本次推出的是IEC61850中最常用的报告功能服务端的实现方法,内容包含了CID建模,编码实现,还包括后面的运行效果和MMS报文截图。

一、CID建模

1、通讯部分:

<Communication>
<SubNetwork name="SubNetworkName">
<ConnectedAP apName="SubstationRing1" iedName="NewIED">
<Address>
<P type="OSI-AP-Title">1,1,9999,1</P>
<P type="OSI-AE-Qualifier">12</P>
<P type="OSI-PSEL">00000001</P>
<P type="OSI-SSEL">0001</P>
<P type="OSI-TSEL">0001</P>
<P type="IP">192.168.95.128</P>
<P type="IP-SUBNET">255.255.255.0</P>
<P type="IP-GATEWAY">192.168.95.2</P>
<P type="MAC-Address">00-0C-29-D0-7D-33</P>
</Address>
</ConnectedAP>
</SubNetwork>
</Communication>

这里重点关注P type为"IP""IP-SUBNET""IP-GATEWAY""MAC-Address"四个配置,分别代表了本机IP地址,子网掩码、网关IP地址、本机MAC地址。需要按照报告服务端装置的实际情况配置,多网卡情况需要根据实际需要选择一个网卡进行配置。

2、服务、逻辑设备、逻辑节点、数据、数据属性

按照这个顺序逐一添加,这里考虑读者具备基本的建模知识,所以建模相关知识就不详细介绍了。

这里截取部分CID信息如下:

逻辑节点MMXU:

<LN desc="" inst="0" lnClass="MMXU" lnType="MMXU_0" prefix="">

数据和数据属性:

<DOI desc="" name="A">
<SDI name="phsA">
<SDI name="cVal">
<SDI name="mag">
<DAI name="i"></DAI>
</SDI>
</SDI>
<DAI name="q"></DAI>
<DAI name="t"></DAI>
</SDI>
</DOI>

因为是要开发报告服务端,所以要配置报告控制块,那么配置控制块之前就需要创建数据集如下:
<DataSet name="MMXUDataSet">
<FCDA daName="phsA" doName="A" fc="MX" ldInst="Example" lnClass="MMXU" lnInst="0" prefix=""/>
</DataSet>

下面就是报告控制块的设置(非缓存报告控制块URCB):
<ReportControl confRev="1" datSet="MMXUDataSet" desc="" name="Measurement" rptID="MMXUID">

<TrgOps dupd="true"/>
<OptFields dataSet="true" reasonCode="true" seqNum="true" timeStamp="true"/>
<RptEnabled max="3"/>
</ReportControl>

如果是要设置缓存报告控制块BRCB,需要加上缓存时间和缓存标志,例如:

<ReportControl bufTime="50000" buffered="true" confRev......

触发选项TrgOps部分设置了数据更新,也就是代表数据集中数据属性的值发生刷新就会触发内部事件,对于非缓存报告控制块会立即发送报告。

二、编码

如下图是服务端开发使用的PIS-10的方法:

IEC61850_Create() 和 IEC61850_Free() 用于创建或删除客户端/服务端对象。
IEC61850_LoadSCLFile() 使用SCL文件配置客户端/服务器。如果想重新对客户端/服务器进行配置则必须首先删除客户端/服务端(通过IEC61850_Free()实现) 并将新的配置下装到新的客户端/服务端(由 IEC61850_Create()创建)。
IEC61850_Start() 和IEC61850_Stop() 用于启动或停止客户端/服务端的服务(如 GOOSE、MMS等)。

调用IEC61850_Create()函数时必须向其传递IEC61850_Parameters参量。该参量包含一个用了标识MMS行为的标志位、一个可选标志位、命令超时结束的时间值、支持的最大连接数以及已定义的任何回调函数的指针。. 整个结构在使用之前应该在内存中被置为0以避免内存中的随机值影响函数的正确执行。

代码示例如下:

struct IEC61850_Parameters tServerParam = {0};

memset(&tServerParam, 0, sizeof(struct IEC61850_Parameters) );

tServerParam.ClientServerFlag = IEC61850_SERVER; //服务端
tServerParam.Ed1_Ed2_Flag = IEC61850_Edition2; //采用2.0
tServerParam.uiOptions = 0; //可查阅AIP手册了解

//下面是对回调函数的指定,因为这次不需要回调所以可以不写回调,但是PIS-10要求必须指定测试的回调,所以请添加如下代码:
tServerParam.ptOprTestCallback = (IEC61850_ControlOperativeTestCallback) OperativeTestCallbackHandler;

完成这些后即可创建服务端对象,代码如下:

myIEC61850Object= IEC61850_Create(&tServerParam, &eErrorCode);

创建成功后可以加载前面创建的CID文件,代码如下:

returnError = LoadSCLFile("../cidFiles/server.cid");

加载CID成功后可以执行如下代码完成服务端的运行:

eErrorCode = IEC61850_Start(myIEC61850Object);

报告的触发条件,按照服务端主动发送的有:完整性、数据变化、数据更新、质量变化,完整性非常简单,只需要在CID建模的时候设置一下即可,设置方法如下:

1、在<ReportControl>节点增加属性intgPd来设置完整性发送的周期,例如intgPd="30000"(代表周期为30秒);

2、在<OptFields>节点中设置period="true"。

这样设置后就已经可以按照周期发送报告了。如果要使用数据更新来触发要如何做呢?PIS-10提供了DAID的方式,下面为您介绍:

首先需要CID建模的时候为DA设置DAID

<DOI desc="" name="A">
<SDI name="phsA">
<SDI name="cVal">
<SDI name="mag">
<DAI name="i"></DAI>

这个DA设置后就应该是:

<DOI desc="" name="A">
<SDI name="phsA">
<SDI name="cVal">
<SDI name="mag">
<DAI name="i">
<Private type="SystemCorp_Generic">
<SystemCorp_Generic:GenericPrivateObject xmlns:SystemCorp_Generic="http://www.systemcorp.com.au/61850/SCL/Generic" Field1="3" Field2="1" Field3="1" Field4="1" Field5="1"/>
</Private>
</DAI>
</SDI>
</SDI>

这里就是用3,1,1,1,1来对应DA name="i"的这个DA,如何更新呢?请看如下代码:

enum IEC61850_ErrorCodes UpdateValueByDaid()
{
enum IEC61850_ErrorCodes eErrorCode = IEC61850_ERROR_NONE;

struct IEC61850_DataAttributeID_Generic updateDAID = { 0 }; //定义一个DAID的结构体变量(PIS-10标准结构体)
struct IEC61850_DataAttributeData updateDAData = { 0 }; // 定义一个数据属性结构体变量(PIS-10标准结构体)
Integer32 nInputValue = 0;

IEC61850 myIEC61850Object = GetMyServerClient(); //获取IEC61850对象

//DAID结构体设置
updateDAID.Generic_type = IEC61850_DAID_GENERIC;
printf("请输入DAID的Field1值: ");
scanf("%d", &updateDAID.uiField1);
printf("请输入DAID的Field2值: ");
scanf("%d", &updateDAID.uiField2);
printf("请输入DAID的Field3值: ");
scanf("%d", &updateDAID.uiField3);
printf("请输入DAID的Field4值: ");
scanf("%d", &updateDAID.uiField4);
printf("请输入DAID的Field5值: ");
scanf("%d", &updateDAID.uiField5);

/* 数据属性结构体设置 */
updateDAData.uiBitLength = sizeof(Integer32)* 8;
updateDAData.ucType = IEC61850_DATATYPE_INT32;
printf("请输入要更新的值: ");
scanf("%d", &nInputValue);
updateDAData.pvData = &nInputValue;

//更新DAID对应的DA
eErrorCode = IEC61850_Update(myIEC61850Object, (struct IEC61850_DataAttributeID*)&updateDAID, &updateDAData, 1);

if (eErrorCode == IEC61850_ERROR_NONE)
{
char strError[SIZE_OF_ERROR_STRING] = { 0 };
snprintf(strError, SIZE_OF_ERROR_STRING, "更新成功:DAID(%d,%d,%d,%d,%d)更新为[%d]\n", updateDAID.uiField1, updateDAID.uiField2, updateDAID.uiField3, updateDAID.uiField4, updateDAID.uiField5, nInputValue);
SetErrorString(strError, SIZE_OF_ERROR_STRING);

}
else
{
char strError[SIZE_OF_ERROR_STRING] = { 0 };
snprintf(strError, SIZE_OF_ERROR_STRING, "更新失败:(%i) %s\n", eErrorCode, IEC61850_ErrorString(eErrorCode));
SetErrorString(strError, SIZE_OF_ERROR_STRING);
}

return eErrorCode;
}

三、运行和抓包

为了方便运行这里写了一个循环菜单,运行效果如图:


上面是输入命令1后按照提示一点点输入,最后回车效果如下:


由于我们这里运行了客户端程序,所以能够抓到报告的消息包,如下图:

感兴趣的同学可以免费下载PIS-10的试用LIB包,样例代码等。关注公众号获取更多支持。加入QQ群:365273095 获得支持并反馈您的问题。

发表评论