前言
前面的文章用到了数据更新,但是重点介绍的是方法,然而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)来进行数据模型中数据属性进行赋值取值的操作
struct IEC61850_DataAttributeData
{
unsigned char type; /*!< 数据类型。<br>取值为 `enum IEC61850_DataType` 类型的枚举值之一。<br>若数据类型为数组或者结构体,则每个数组元素或结构体成员,都将是一个单独的 `IEC61850_DataAttributeData` 类型的数据。 */
int arrayIndex; /*!< 数组元素索引。<br>若当前数据属性为数组类型数据中的某个元素时,该值有效,值为当前数据对应的数组元素索引;否则该值无意义,值为 0。 */
unsigned int bitLength; /*!< 数据长度信息。其值的意义,因数据类型而异。<br>若数据类型是数组,则该值代表数组元素数量;<br>若数据类型是结构体,则代表结构体成员的数量;<br>若数据类型既不是数组也不是结构体,则该值代表对应数据类型所占的实际位(bit)数。(例如,INT32 类型数据大小为 32 bit,则该值对应为 32。) */
void * pvData; /*!< 数据值的指针。<br>该指针指向数据值所在的内存空间地址。<br>若数据类型为数组数据,则该指针指向一个包含 `bitLength` 个 `IEC61850_DataAttributeData` 类型的数组元素的数组的首地址;<br>若数据类型为结构体,则该指针指向一个由 `bitLength` 个连续的 `IEC61850_DataAttributeData` 类型的结构体成员数据所构成的内存空间的首地址;<br>若数据类型既不是数组,也不是结构体,则可以按照对应的数据类型对该地址解引用来获取数据值。 */
}
看看之前的例子
IEC61850_ErrorCode UpdateMMXUPhsAMagi()
{
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; //指针指向正确的变量
接下来就针对不同类型,交代不同类型的这四行代码如何写
创建一个CID模型
模型中尽量包含全面的数据类型。
详细可查看本样例代码提供的CID文件,这里为每个要更新的数据属性都设置了DAID。
<LN inst="0" lnClass="GGIO" lnType="GGIO_0" prefix="">
<DOI desc="Behaviour" name="Beh">
<DAI name="stVal" sAddr="@5.0.0.0.0"/>
<DAI name="d" sAddr="@5.0.0.0.1"/>
<DAI name="dU" sAddr="@5.0.0.0.2"/>
</DOI>
<DOI desc="General single alarm" name="Alm_bool">
<DAI name="stVal" sAddr="@0.0.0.0.1"/>
<DAI name="q" sAddr="@0.0.0.0.2"/>
<DAI name="t" sAddr="@0.0.0.0.3"/>
</DOI>
<DOI desc="Analogue input" name="AnIn_int8">
<SDI name="mag">
<DAI name="i" sAddr="@1.0.0.0.8"/>
</SDI>
</DOI>
<DOI desc="Analogue input" name="AnIn_int16">
<SDI name="mag">
<DAI name="i" sAddr="@1.0.0.0.16"/>
</SDI>
</DOI>
<DOI desc="Analogue input" name="AnIn_int32">
<SDI name="mag">
<DAI name="i" sAddr="@1.0.0.0.32"/>
</SDI>
</DOI>
<DOI desc="Analogue input" name="AnIn_int64">
<SDI name="mag">
<DAI name="i" sAddr="@1.0.0.0.64"/>
</SDI>
</DOI>
<DOI desc="Analogue input" name="AnIn_int8u">
<SDI name="mag">
<DAI name="i" sAddr="@2.0.0.0.8"/>
</SDI>
</DOI>
<DOI desc="Analogue input" name="AnIn_int16u">
<SDI name="mag">
<DAI name="i" sAddr="@2.0.0.0.16"/>
</SDI>
</DOI>
<DOI desc="Analogue input" name="AnIn_int32u">
<SDI name="mag">
<DAI name="i" sAddr="@2.0.0.0.32"/>
</SDI>
</DOI>
<DOI desc="Analogue input" name="AnIn_float32">
<SDI name="mag">
<DAI name="f" sAddr="@3.0.0.0.32"/>
</SDI>
</DOI>
<DOI desc="Analogue input" name="AnIn_float64">
<SDI name="mag">
<DAI name="f" sAddr="@3.0.0.0.64"/>
</SDI>
</DOI>
<DOI desc="Double point controllable status output" name="DPCSO11">
<DAI name="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的状态值,定义如下:
<EnumType id="BehaviourModeKind">
<EnumVal ord="1">on</EnumVal>
<EnumVal ord="2">blocked</EnumVal>
<EnumVal ord="3">test</EnumVal>
<EnumVal ord="4">test/blocked</EnumVal>
<EnumVal ord="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;