便携式航电实时系统测试平台ICD描述方法

123 阅读15分钟

第一章 使用DPD进行ICD描述

1

1.1 简介

本文介绍ETest采用的ICD描述方法:使用DPD协议描述语言进行描述。

嵌入式设备通信协议定义语言(简称DPD语言)是一种编译型语言。编译的结果是带有强命名的协议数据,可以对接口通信数据进行编码/解码。

DPD语言具有较强的描述能力,能够进行整形数据、浮点型数据、字符型数据的定义还可以进行分支结构、循环结构的定义。对于非整字节的定义,能够按位长度进行定义。

DPD语言描述的单位是“协议”。每个“协议”包含若干个“字段”。

1.2 定义协议

每个协议的定义都是用Protocol关键字和End关键字括起来的一段代码。

语法:

Protocol Name [协议段]{1,*} End

例子:

Protocol Protl Segment Head StandardUInt32 Default=0 ByteOrder=Small Segment Type StandardUInt8 Default=0 Segment Length StandardUInt32 Default=0 ByteOrder=Small Segment Tail StandardInt8 Default=0 Segment CheckBit StandardUInt8 Default=0 End

1.2.1 Protocol

定义协议以Protocol关键字开始。

1.2.2 Name

有效字符:字母,汉字,数字,下划线。

约束:不能以数字开头。

1.2.3 协议段

定义协议里所包含的协议段,至少有1个协议段,不限制最多数量。

本文约定:

{n,*}:表示至少n个,最多不限。

{n,m}:表示至少n个,最多m个,其中m > n。

{*}:表示任意个。

1.2.4 End

关闭协议定义,针对该协议的所有定义结束。

1.3 定义协议段

语法:

Segment Name Type [修饰][注释]

例子:

Protocol COMW Segment Head StandardUInt32 Default=0 ByteOrder=Small #注释 End
  1. Segment

协议段定义开始。

  1. Name

有效字符:字母,汉字,数字,下划线。

约束:不能以数字开头。

  1. Type

协议段类型,基本类型包括整型、浮点型和布尔类型;此外,还支持字符串、校验字段、数组、子接口、协议块、计算字段。

  1. 修饰

针对每种Type都有不同的修饰语法,详见后面章节。

  1. 注释

注释以#开头,至该行的末尾。

1.3.1 整型协议段

整形协议段的类型如下表所示:

整形协议段类型

类型定义取值范围
StandardInt32有符号32 位整数-2,147,483,648到 2,147,483,647
StandardUInt32无符号32 位整数0 到4,294,967,295
StandardInt16有符号16位整数-32,768 到32,767
StandardUInt16无符号16位整数0 到65,535
StandardInt8有符号8位整数-128 到127
StandardUInt8无符号8 位整数0到255
RandomInt任意长度整数 (VXWorks下位机暂不支持)根据长度确定
RandomUInt任意长度无符号整数 (VXWorks下位机暂不支持)根据长度确定

语法:

Segment name StandardInt32 [Default=] [Format=] [ByteOrder=*]

Segment name StandardUInt32 [Default=] [ByteOrder=]

Segment name StandardInt16 [Default=] [Format=] [ByteOrder=*]

Segment name StandardUInt16 [Default=] [ByteOrder=]

Segment name StandardInt8 [Default=] [Format=]

Segment name StandardUInt8 [Default=*]

Segment name RandomInt Length=9 Default=0 Format=Complement

Segment name RandomUInt Length=9 Default=0

例子:

Protocol COMW Segment data1 StandardInt32 Default=0 Format=Complement ByteOrder=Small Segment data2 StandardUInt32 Default=0 ByteOrder=Small Segment data3 StandardInt16 Default=0 Format=Complement ByteOrder=Small Segment data4 StandardUInt16 Default=0 ByteOrder=Small Segment data5 StandardInt8 Default=0 Format=Complement Segment data6 StandardUInt8 Default=0 End

1.3.2 浮点型协议段

浮点型协议段的类型如下表所示:

浮点型协议段类型

类型大致范围精度
StandardDouble±5.0 × 10 −324 到 ±1.7 × 10 301215 到 16 位
StandardFloat-3.4 × 10 312 到 +3.4 × 10 3127 位

语法:

Segment name StandardDouble [Default=100]

Segment name StandardFloat [Default=100]

例子:

Protocol COMW Segment data1 StandardDouble Default=1.0 Segment dtat2 StandardFloat Default=1.0 End

1.3.3 布尔类型协议段

Boolean

表示一个开关量,取值为True或者False,对应内存中的一个Bit。

语法:

Segment I2 Boolean [Default=True/False]

例子:

Protocol COMW Segment data1 Boolean Segment data2 Boolean Default=True Segment data3 Boolean Default=False End

注:本类型在VXWorks实时下位机暂不支持。

1.3.4 IF分支

IF分支的内容根据某个字段值的变化而有所变化。

语法:

Segment I3 If name==1

Then subProtocol1

Else subProtocol2

例子:

Protocol SubProtocol1 Segment val1 StandardInt16 Default=0 Format=Complement ByteOrder=Small End Protocol SubProtocol2 Segment val2 StandardInt16 Default=0 Format=Complement ByteOrder=Small End Protocol Main Segment name StandardInt8 Default=0 Format=Complement Segment value IF name==True Then SubProtocol1 ELSE SubProtocol2 End

注:本类型在VXWorks实时下位机暂不支持。

1.3.5 SWTICH分支

SWITCH分支字段根据某个字段的值的不同进入不同的分支。

语法:

Segment I2 Switch segVal

Case 1 SubProtocol1

Case 1 SubProtocol2

Default SubProtocol3

例子:

Protocol SubProtocol1 Segment val1 StandardInt16 Default=0 Format=Complement ByteOrder=Small End Protocol SubProtocol2 Segment val2 StandardInt16 Default=0 Format=Complement ByteOrder=Small End Protocol SubProtocol2 Segment val2 StandardInt16 Default=0 Format=Complement ByteOrder=Small End Protocol Main Segment SegName StandardInt8 Default=0 Format=Complement Segment I2 Switch SegName Case 1 SubProtocol1 Case 2 SubProtocol2 Default SubProtocol3 End

注:本类型在VXWorks实时下位机暂不支持。

1.3.6 校验字段

校验字段是指按照一定的校验算法,对协议数据进行校验,生成校验数据所形成的字段。

校验字段的定义方法如下:

Protocol COMW Segment data1 StandardUInt8 Default=0 Segment data2 StandardUInt8 Default=0 Segment data3 StandardUInt8 Default=0 Segment data4 StandardUInt8 Default=0 Segment Check CRC Range=(0,10) Algorithm=CRC_16_0X1021 BitCount=8 End

其中最后一个字段为校验字段。Check为字段名;CRC为协议字段的关键字;Range为指定校验数据的范围(以位为单位),不写默认为前面所有数据;Algorithm属性表示使用的协议算法名。BitCount属性为校验值的长度(以位为单位)。

发送数据时,校验字段自动根据所指定的校验算法,计算出校验字段的值。

接收数据时,校验字段自动根据所指定的校验算法,计算出校验字段的值,并与所接收的校验字段的值进行比较,在测试脚本中可通过校验字段的Checked属性(Proto1.Check.Checked,Proto1为仿真模型协议线的名称)来判断校验值是否正确。校验值相同,Checked的值为True;校验值不同,Checked的值为False。

注:

1:源数据范围:默认的源数据从协议头开始,到定义的协议字段为止;

可通过Range属性修改校验范围,使用方法为:Range=(a,b)

a 为起始bit的序号;b为结束bit的序号;当b=-1时代表至校验字段前。

Range(0,15),表示从0号位开始,至15号位;

Range(16,0),表示从16号位开始,至校验字段前。

2:对Windows下位机,系统提供如下的协议算法(Algorithm),用户还可根据需要自己定义(见章节2.4.7:CRC生成):

CRC_SUM_8(按字节相加,大端序);

CRC_Check_Sum(按字节相加,小端序);

CRC_SUM_16(按双字节相加,结果的字节序按小端序);

CRC_SUM_16_2(按双字节相加,结果的字节序按大端序);

CRC_SUM_32(按四字节相加,结果的字节序按小端序);

CRC_SUM_32_2(按四字节相加,结果的字节序按大端序);

CRC_XOR_8(按字节或);

CRC_XOR_16(按双字节或,结果的字节序按小端序);

CRC_XOR_16_2(按字节或,结果的字节序按大端序);

CRC_XOR_32(按四字节或,结果的字节序按小端序);

CRC_XOR_32_2(按字节或,结果的字节序按大端序)

用户可以自己开发CRC插件。

开发方式目前有两种:C和C#。

第一种开发方式:C的插件开发方式:

a) 提供如下的导出 函数:

extern "C" __declspec(dllexport) wchar_t * CRCDescript()

这个函数返回该插件的名称,注意名称需要符合 [标示符]的规范。

b) 提供如下导出函数:

extern "C" __declspec(dllexport) void CalcluteCRC(const unsigned char* data,const int data_len, unsigned char* const sum,unsigned int sum_len)

这个函数用于计算校验值。

参数:

const unsigned char* data:需要校验的数据。

const int data_len:需要校验的数据的长度。

unsigned char* const sum:校验值数据。

unsigned int sum_len:校验值数据长度(以位为单位)。

注:校验值数据长度只能为8的整数倍。

范本如下:

#include "stdafx.h" #include "CRCLib.h" #include // for std::size_t #include // for std::cout #include <Objbase.h> #include using namespace std; extern "C" __declspec(dllexport) wchar_t * CRCDescript() { wchar_t str=L"CRC_Check_Sum"; size_t len=wcslen(str)+1; wchar_t desc = (wchar_t)::CoTaskMemAlloc(lensizeof(wchar_t)); swprintf_s(desc, len, str); return desc; } extern "C" __declspec(dllexport) void CalcluteCRC(const unsigned char* data,const int data_len, unsigned char* const sum,unsigned int sum_len) { unsigned long checksumT=::accumulate(data,data+data_len,0); ::memcpy(sum,&checksumT,sum_len); for(int i=0;i<sum_len/2;++i) { unsigned char t=sum[i]; sum[i]=sum[sum_len-i-1]; sum[sum_len-i-1]=t; } }

第二种开发方式:C#的插件开发方式:

建立一个类,从 Kiyun.EmbedTest.Protocol.CRCLibCore.CS_CRCAlgorithm继承;并且完成下面两步:

  1. 在构造函数中为Name字段赋值为校验算法的名称(同协议中的Algorithm属性值一致)。

  2. 实现 CalcluteCRCFunc 方法,在方法体中完成校验算法的实现;这个方法包含两个参数:bitData参数是校验的输入数据;sum_bit_len是计算后的校验字段的位数。

在校验实现算法程序中,可使用Console.Write输出调试信息到控制台。

编写完成的算法生成DLL动态链接库。动态链接库的名称必须以“CRC_”开头,否则系统加载不成功。

范本如下:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace KiyunCRC8 { public class CRC8 : Kiyun.EmbedTest.Protocol.CRCLibCore.CS_CRCAlgorithm { byte [] CRC8Table= new byte[256]{ 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53}; public CRC8() { this.Name = "Kiyun_CRC8"; } private byte getByte(IEnumerable bitData, int offset) { System.Collections.BitArray bit8 = new System.Collections.BitArray(8); for (int i = 0; i < 8; i++) bit8[i] = bitData.ElementAt(offset + i); byte[] byteArray = new byte[1]; bit8.CopyTo(byteArray, 0); return byteArray[0]; } protected override System.Collections.Generic.IEnumerable CalcluteCRCFunc(System.Collections.Generic.IEnumerable bitData, uint sum_bit_len) { if (sum_bit_len != 8) return null; var ret = new bool[(int)sum_bit_len]; byte crc8 = 0; int len = bitData.Count(); byte byteData = new byte(); int offset = 0; byteData = getByte(bitData, offset); while(offset<len) { crc8 = CRC8Table[crc8 ^ byteData]; offset += 8; if (offset >= len) break; byteData = getByte(bitData, offset); } byte[] crcRet = new byte[1]; crcRet[0] = crc8; System.Collections.BitArray retByte = new System.Collections.BitArray(crcRet); for (int i=0;i<sum_bit_len;i++) { ret[i] = retByte[i]; } return ret; } } }

对VXWorks下位机,仅支持CRC_16和CRC_32两种校验算法。

1.3.7 数组字段

在协议中可以定义某个协议段为数组类型。数组的长度可以是固定长度的,也可以是可变长的。数组元素是一个子协议。

定义数组协议段的关键字是Array。它有两个属性,Count和Child。Count表示数组的长度。Child指名数组元素的类型。

如果Count的值是一个常量,则数组的长度是固定的。如果Count的值是一个已定义的协议段的名称,则数组的长度在运行中由协议段的值决定。

具体的例子如下:

  1. 固定长度的数组
Protocol T Segment data1 StandardUInt8 Default=0 Segment data2 StandardUInt8 Default=0 Segment data3 StandardUInt8 Default=0 Segment data4 StandardUInt8 Default=0 End Protocol P Segment Head StandardUInt8 Default=0 Segment Data Array Count=3 Child=T End
  1. 2)可变长度的数组
Protocol T Segment data1 StandardUInt8 Default=0 Segment data2 StandardUInt8 Default=0 Segment data3 StandardUInt8 Default=0 Segment data4 StandardUInt8 Default=0 End Protocol P Segment Head StandardUInt8 Default=0 Segment ArryaCount StandardInt8 Default=0 Segment Data Array Count=ArryaCount Child=T End

以上是数组字段在协议中的定义方式。下面简要介绍一下协议字段的值在测试脚本中的引用方式,其中Proto1为仿真模型协议线的名称。

a) 数组的长度

Proto1.Data.Count

b) 为数组里的元素进行赋值

Proto1.Data[0].data1.Value=11

Proto1.Data[0].data2.Value=22

Proto1.Data[0].data3.Value=33

c) 读取数组里的元素值

Proto1.Data[0].data1.Value

1.3.8 字符串字段

在协议中可以定义字符串类型的字段。

语法:

Segment name String Code='GB2312' Tail='&' Default='kiyun'

例子:

Protocol p1 Segment name StandardInt8 Default=0 Format=Complement Segment S5 String Code='GB2312' Tail='&' Default='kiyun' End

其中String是字符串类型的关键字;Code是编码方式;Tail是结束标志;Default是默认值。

注:本类型在VXWorks实时下位机暂不支持。

1.3.9 协议块

在协议中可以定义协议块的字段。

语法:

Segment name Block Child=P

例子:

Protocol p1 Segment name1 StandardInt8 Default=0 Format=Complement Segment name2 StandardInt8 Default=0 Format=Complement Segment name3 StandardInt8 Default=0 Format=Complement End Protocol p2 Segment name4 StandardInt8 Default=0 Format=Complement Segment name5 Block Child=p1 End

其中p1是本协议段之前出现的一个协议段的名字;Block是一个字块;Child是该字块的协议模板。

1.3.10 字节块

在协议中可以定义字节块的字段。

语法:

Segment P Buffer Count=1

例子:

Protocol p1 Segment name StandardInt8 Default=0 Format=Complement Segment P Buffer Count=10 End

其中P是字节块类型的关键字;Buffer是字节块的缓冲区;Count是统计字节块数;默认值为1。

注:本类型在VXWorks实时下位机暂不支持。

1.4 主协议和附属协议

每个协议定义单元中,可以定义多个协议(多个用Protocol和End括起来的主体。)

定义的最后一个协议是主协议,其余为附属协议。协议数据的编码解码是从主协议开始的。

主协议中可以含有对附属协议的引用。如在IF协议端、SWITCH协议段或数组协议段中。

1.5 关键字

1.5.1 数据类型关键字

下表列出数据类型关键字:

数据类型关键字

名称描述
StandardInt32标准32位整数
StandardUint32标准32位无符号整数
StandardInt16标准16位整数
StandardUint16标准16位无符号整数
StandardInt8标准8位整数
StandardUint8标准8位无符号整数
StandardDouble标准双精度浮点数
StandardFloat标准浮点数
Boolean布尔,占用1Bit空间
CRC校验字段类型
Array数组字段类型
String字符串类型
Block协议块
Buffer字节块

1.5.2 其他关键字

下表列出其他关键字。

其他关键字

名称说明
Protocol定义协议
Segment定义协议段
End协议定义结束
FormatStandardInt32、StandardInt16、StandardInt8、RandomInt类型字段的属性, 代表存储格式:原码,补码还是反码
PrimitiveFormat字段的可取值之一,表示原码
InversionFormat字段的可取值之一,表示反码
ComplementFormat字段的可取值之一,表示补码
ByteOrderStandardUInt32、StandardInt32、StandardUInt16、StandardInt16类型字段的属性, 代表字节存储顺序 取值包括:Small、Big。默认为Small
BigByteOrder字段的可取值之一 大端,整数的高位字节位于内存的前端
SmallByteOrder字段的可取值之一 小端,与Big相反
LengthRandomInt,RandomUInt字段的属性, 代表的长度(按位),范围(1-32)
AlgorismCRC字段的属性,代表校验函数
BitCountCRC字段的属性,代表位数
CountArray字段和Buffer字段的属性,代表长度
ChildArray字段和Block字段的属性,代表子协议
CodeString字段的属性,代表编码方式
TailString字段的属性,代表结束标识
If条件判断语句
Then条件为真时的分支
Else条件为假的分支
Switch数值判断语句
Case数值分支
Default默认值分支