零基础实现IEC61850(七)细说数据更新
前言
前面的文章用到了数据更新,但是重点介绍的是方法,然而IEC61850提供了多种数据类型,都可能让使用者感觉无从下手,所以需要有一篇文章进行全面的介绍,本文重点根据类型区分介绍。
本文所有样例源码已经上传至https://gitee.com/yunxingtianxia/yx-tools 的 yx-pis-dll\example\07 updateData 目录
IEC61850都有哪些类型
| 类型 | 类型描述 | 协议栈定义 |
|---|---|---|
| BOOLEAN | 布尔 | IEC61850_DATATYPE_BOOLEAN |
| INT8 | 有符号整型 | IEC61850_DATATYPE_INT8 |
| INT16 | 有符号整型 | IEC61850_DATATYPE_INT16 |
| INT32 | 有符号整型 | IEC61850_DATATYPE_INT32 |
| INT64 | 有符号整型 | IEC61850_DATATYPE_INT64 |
| INT8U | 无符号整型 | IEC61850_DATATYPE_INT8U |
| INT16U | 无符号整型 | IEC61850_DATATYPE_INT16U |
| INT32U | 无符号整型 | IEC61850_DATATYPE_INT32U |
| FLOAT32 | 单精度浮点型 | IEC61850_DATATYPE_FLOAT32 |
| FLOAT64 | 双精度浮点型 | IEC61850_DATATYPE_FLOAT64 |
| ENUMERATED | 枚举 | IEC61850_DATATYPE_ENUMERATED |
| CODEDENUM | 枚举 | IEC61850_DATATYPE_CODED_ENUM |
| OCTETSTRING | 八位组串 | IEC61850_DATATYPE_OCTET_STRING |
| VisibleString | 可视字符串 | IEC61850_DATATYPE_VISIBLE_STRING |
| UNICODESTRING | Unicode字符串 | IEC61850_DATATYPE_UNICODE_STRING |
| TimeStamp | 时间戳 | IEC61850_DATATYPE_TIMESTAMP |
| Quality | 品质 | IEC61850_DATATYPE_QUALITY |
协议栈类型使用规则
我们统一使用数据属性结构体(IEC61850_DataAttributeData)来进行数据模型中数据属性进行赋值取值的操作
structIEC61850_DataAttributeData{unsignedchartype;/*!< 数据类型。<br>取值为 `enum IEC61850_DataType` 类型的枚举值之一。<br>若数据类型为数组或者结构体,则每个数组元素或结构体成员,都将是一个单独的 `IEC61850_DataAttributeData` 类型的数据。 */intarrayIndex;/*!< 数组元素索引。<br>若当前数据属性为数组类型数据中的某个元素时,该值有效,值为当前数据对应的数组元素索引;否则该值无意义,值为 0。 */unsignedintbitLength;/*!< 数据长度信息。其值的意义,因数据类型而异。<br>若数据类型是数组,则该值代表数组元素数量;<br>若数据类型是结构体,则代表结构体成员的数量;<br>若数据类型既不是数组也不是结构体,则该值代表对应数据类型所占的实际位(bit)数。(例如,INT32 类型数据大小为 32 bit,则该值对应为 32。) */void*pvData;/*!< 数据值的指针。<br>该指针指向数据值所在的内存空间地址。<br>若数据类型为数组数据,则该指针指向一个包含 `bitLength` 个 `IEC61850_DataAttributeData` 类型的数组元素的数组的首地址;<br>若数据类型为结构体,则该指针指向一个由 `bitLength` 个连续的 `IEC61850_DataAttributeData` 类型的结构体成员数据所构成的内存空间的首地址;<br>若数据类型既不是数组,也不是结构体,则可以按照对应的数据类型对该地址解引用来获取数据值。 */}看看之前的例子
IEC61850_ErrorCodeUpdateMMXUPhsAMagi(){IEC61850_ErrorCode result=S_OK;S32 newVal=0;IEC61850_DataAttributeData daData={0};char*reference="TEMPLATELDevice1/MMXU0.A.phsA.cVal.mag.i";printf("Enter an S32 value: ");scanf("%d",&newVal);daData.bitLength=sizeof(S32)*8;daData.type=IEC61850_DATATYPE_INT32;daData.pvData=&newVal;result=IEC61850_UpdateWithReference(g_iec61850,reference,&daData);}这里面和类型相关,会存在差异的部分只有这四行
S32 newVal=0;//定义合适的变量daData.bitLength=sizeof(S32)*8;//指定数据长度daData.type=IEC61850_DATATYPE_INT32;//指定数据类型daData.pvData=&newVal;//指针指向正确的变量接下来就针对不同类型,交代不同类型的这四行代码如何写
建模
建模工具直接访问云端版:
http://scl.yunxing.tech:8888
也可在该地址下载桌面版。
创建一个CID模型
模型中尽量包含全面的数据类型。
详细可查看本样例代码提供的CID文件,这里为每个要更新的数据属性都设置了DAID。
<LNinst="0"lnClass="GGIO"lnType="GGIO_0"prefix=""><DOIdesc="Behaviour"name="Beh"><DAIname="stVal"sAddr="@5.0.0.0.0"/><DAIname="d"sAddr="@5.0.0.0.1"/><DAIname="dU"sAddr="@5.0.0.0.2"/></DOI><DOIdesc="General single alarm"name="Alm_bool"><DAIname="stVal"sAddr="@0.0.0.0.1"/><DAIname="q"sAddr="@0.0.0.0.2"/><DAIname="t"sAddr="@0.0.0.0.3"/></DOI><DOIdesc="Analogue input"name="AnIn_int8"><SDIname="mag"><DAIname="i"sAddr="@1.0.0.0.8"/></SDI></DOI><DOIdesc="Analogue input"name="AnIn_int16"><SDIname="mag"><DAIname="i"sAddr="@1.0.0.0.16"/></SDI></DOI><DOIdesc="Analogue input"name="AnIn_int32"><SDIname="mag"><DAIname="i"sAddr="@1.0.0.0.32"/></SDI></DOI><DOIdesc="Analogue input"name="AnIn_int64"><SDIname="mag"><DAIname="i"sAddr="@1.0.0.0.64"/></SDI></DOI><DOIdesc="Analogue input"name="AnIn_int8u"><SDIname="mag"><DAIname="i"sAddr="@2.0.0.0.8"/></SDI></DOI><DOIdesc="Analogue input"name="AnIn_int16u"><SDIname="mag"><DAIname="i"sAddr="@2.0.0.0.16"/></SDI></DOI><DOIdesc="Analogue input"name="AnIn_int32u"><SDIname="mag"><DAIname="i"sAddr="@2.0.0.0.32"/></SDI></DOI><DOIdesc="Analogue input"name="AnIn_float32"><SDIname="mag"><DAIname="f"sAddr="@3.0.0.0.32"/></SDI></DOI><DOIdesc="Analogue input"name="AnIn_float64"><SDIname="mag"><DAIname="f"sAddr="@3.0.0.0.64"/></SDI></DOI><DOIdesc="Double point controllable status output"name="DPCSO11"><DAIname="stVal"sAddr="@6.0.0.0.0"/></DOI></LN>BOOLEAN布尔
Boolean newVal=TRUE;daData.bitLength=8;daData.type=IEC61850_DATATYPE_BOOLEAN;daData.pvData=&newVal;INT8有符号整型
S8 newVal=123; daData.bitLength=8;daData.type=IEC61850_DATATYPE_INT8;daData.pvData=&newVal;INT16有符号整型
S16 newVal=123; daData.bitLength=16;daData.type=IEC61850_DATATYPE_INT16;daData.pvData=&newVal;INT32有符号整型
S32 newVal=123; daData.bitLength=32;daData.type=IEC61850_DATATYPE_INT32;daData.pvData=&newVal;INT64有符号整型
S64 newVal=123; daData.bitLength=64;daData.type=IEC61850_DATATYPE_INT64;daData.pvData=&newVal;INT8U无符号整型
U8 newVal=123;daData.bitLength=8;daData.type=IEC61850_DATATYPE_INT8U;daData.pvData=&newVal;INT16U无符号整型
U16 newVal=123;daData.bitLength=16;daData.type=IEC61850_DATATYPE_INT16U;daData.pvData=&newVal;INT32U无符号整型
U32 newVal=123;daData.bitLength=32;daData.type=IEC61850_DATATYPE_INT32U;daData.pvData=&newVal;FLOAT32单精度浮点型
Float32 newVal=0.8;daData.bitLength=32;daData.type=IEC61850_DATATYPE_FLOAT32;daData.pvData=&newVal;FLOAT64双精度浮点型
Float64 newVal=0.8;daData.bitLength=64;daData.type=IEC61850_DATATYPE_FLOAT64;daData.pvData=&newVal;ENUMERATED枚举
枚举类型还是有些特殊,在CID建模中,类型选择Enum,并要其指定具体的定义,不过一般我们用标准定义好的,比如Beh的状态值,定义如下:
<EnumTypeid="BehaviourModeKind"><EnumValord="1">on</EnumVal><EnumValord="2">blocked</EnumVal><EnumValord="3">test</EnumVal><EnumValord="4">test/blocked</EnumVal><EnumValord="5">off</EnumVal></EnumType>其实所谓枚举值,就是为该值赋值枚举定义的值,这里面是1,2,3,4,5。以此为例代码如下:
tIEC61850Enum newVal=1;daData.bitLength=IEC61850_ENUM_BITSIZE;daData.type=IEC61850_DATATYPE_ENUMERATED;daData.pvData=&newVal;CODEDENUM固定枚举
这种是不需要自定义枚举值的,而是固定的一些类型,比如CID模型中的Tcmd、Dbpos等
以Dbpos为例子,其他的因为不常用,如果确实用到可以联系我们
eDbPosValues newVal=DBPOS_ON;daData.bitLength=IEC61850_DBPOS_BITSIZE;daData.type=IEC61850_DATATYPE_DBPOS;daData.pvData=&newVal;OCTETSTRING八位组串
这个就略了,因为几乎没有需要用户去更新和使用该类型的场景,如果确实用到可以联系我们
VisibleString可视字符串
char*newVal="this is new VisibleString";daData.bitLength=strnlen(newVal,255)*8;daData.type=IEC61850_DATATYPE_VISIBLE_STRING;daData.pvData=newVal;UNICODESTRINGUnicode字符串
注意:这里源码文件本身采用了utf8编码,目前所使用的vs2022用安装了Force UTF-8(No Bom)2022插件
char*newVal="这里是中文";daData.bitLength=strnlen(newVal,255)*8;daData.type=IEC61850_DATATYPE_UNICODE_STRING;daData.pvData=newVal;TimeStamp时间戳
IEC61850_TimeStamp t={0};IEC61850_GetTime(NULL,&t);daData.bitLength=IEC61850_TIMESTAMP_BITSIZE;daData.type=IEC61850_DATATYPE_TIMESTAMP;daData.pvData=&t;Quality品质
IEC61850_QualityFlag q=IEC61850_QUALITY_GOOD;daData.bitLength=IEC61850_QUALITY_BITSIZE;daData.type=IEC61850_DATATYPE_QUALITY;daData.pvData=&q;