CAPL语言设计(2)——数据类型
1. 变量的声明与定义
在程序运行过程中,其值可以被改变的量称为变量。变量可以用于保存用户输入的数据,也可以保存程序运行时产生的中间结果或者最终结果。下面将介绍如何定义变量名,以及如何定义全局变量和局部变量。
变量名
变量名的命名规则与C语言类似,必须注意遵循合法性、有效性和易读性的原则。
变量名的合法性
变量名的合法性如下:
(1)在变量名中只能使用字母字符、数字和下划线(_);
(2)变量名的第一个字符不能是数字;
(3)区分大小写字符;
(4)不能将CAPL关键字用作名称;
(5)不能将CAPL的函数名和变量名。
CAPL关键字可以划分为以下几类:
- 数据类型关键字,如int,byte,long
- 控制语句关键字,如if,else
- 存储类型关键字,如static,extern(外部变量声明)
- 其他关键字,如const,message
举例说明:
int volume; // 合法
int Volume; //合法
byte resp_code; //合法
long 123voltage; //不合法
char byte; //不合法
虽然函数名和对象名不属于关键字,CAPL也不允许将它们用作变量名,因为使用同一个名称用作函数名和变量名会把编译器搞糊涂。比如abs,它是CAPL的一个库函数,用来返回参数的绝对值,如果在程序中同时定义了一个abs变量,这时编译器无法有效辨别,编译将无法通过。
变量名的有效性
对于有效性,主要指变量名的字符长度最好不要超过32个,编译系统只能识别前32个字符,也就是说,前32个字符相同的两个不同变量将被编译系统认为是同一个变量。
变量名的易读性
对于易读性,主要是指在CAPL编程中提倡使用有一定含义的变量名,能做到“见名知意”。如果变量表示ECU的供电电压,可以将其命名为 ‘voltage_of_power’ 或者 ‘VoltageOfPower’ ,而不要将其命名为 ‘i’ 或者 ‘po’ 。
必须强调的是,与C语言不同,CAPL语言中为例用户使用的方便灵活,编译器不区分CAPL自带的关键字和函数名的大小写,例如DWORD、dword和Dword在代码中使用效果一样,所以用户定义变量时也需要避免此类情况。
这里需要指出的是,以上命名规则同样适用于函数名、数组名、结构体等。
全局变量与局部变量
在CAPL中,全局变量需要被声明在variables部分,同时可以使用直接赋值方法进行初始化。如果没有初始化,编译器会自动执行初始化,默认值为0。全局变量的作用域包括整个CAPL文件以及与此文件有链接的其他CAPL文件。
与C语言不同,局部变量在CAPL中总是被静态地创建。这意味着初始化只在程序体启动时被执行,当再次进入程序时,局部变量被嘉定是上一次跳出程序时的值。局部变量的作用域,仅限于当前函数体范围内,即该函数的大括号范围内。
2. 简单变量
CAPL中最常见、最简单的三种变量类型——整型、字符和浮点数。
整型
整数就是没有小数部分的数字,如3、33、-333和0。根据数值大小的不同,CAPL提供了如表1所示的几种整型。
整型 | 有/无符号 | 长度 | 范围 | 备注 |
---|---|---|---|---|
byte | unsigned | 1B | 0~255 | 字节,8位,无符号 |
word | unsigned | 2B | 0~65535 | 字,16位,无符号 |
dword | unsigned | 4B | 0~4294967295 | 双字,32位,无符号 |
gword | unsigned | 8B | 0~18446744073709551615 | 整型,64位,无符号 |
int | signed | 2B | -32768~32767 | 短整型,16位 |
long | signed | 4B | -2147483648~2147483647 | 长整型,32位 |
int64 | signed | 8B | -9223372036854775808~9223372036854775807 | 整型,64位 |
字符
与C语言不同,CAPL没有将char类型(长度1B)归至整型中,这是因为在CANoe中提供了byte类型。如果数据是具体数值则应该使用byte,而对于字符,则应用char(字符串使用char数组)。char类型和byte类型之间可以直接转换,例如:
byte data1 = 100;
char ch1 = 'd';
ch1 = b;
data1 = 's';
浮点型
CAPL提供两种浮点型变量:‘float’ 和 ‘double’ ,如表2所示。
浮点型 | 长度 | 范围 | 描述 |
---|---|---|---|
float | 8B | 1.7 × 1 0 − 308 1.7 \times 10^-308 1.7×10−308 ~ 1.7 × 1 0 + 308 1.7 \times 10^+308 1.7×10+308 | 单精度 |
double | 8B | 1.7 × 1 0 − 308 1.7 \times 10^-308 1.7×10−308 ~ 1.7 × 1 0 + 308 1.7 \times 10^+308 1.7×10+308 | 双精度 |
复合类型
CAPL语言除了支持简单变量外,同时也支持复合类型变量,如数组、结构和枚举。它们的生命方法与C语言基本一致。
结构
CAPL中可以简单滴按照C语言的方法来声明结构(struct),但结构名在程序中必须是唯一的。简单类型、枚举类型或者其他的结构都可以作为结构的成员。例如:
struct b {
struct a a1;
enum Colors cl;
int p;
long l;
};
结构b中包含:一个结构成员a,变量名为a1;一个枚举成员colors,变量名为c1;一个整型变量p;最后还有一个长整型变量l。
用户可以在类型定义时直接声明结构类型的变量,在这种情况下,类型的名称可以省略,也可以直接使用结构的名字来引用类型。例如:
struct Cost myCost;
struct {int Chinese; int Math; int English} Scores;
关于结构初始化,可以在变量声明期间直接初始化结构成员,不需要单独命名单个成员,编译器将按照结构定义的顺序默认初始化它们。例如:
struct Scores myScores = {Chinese = 79, Math = 99, English = 88};
struct Scores myScores = {79, 99, 88};
枚举
枚举(enum)类型的声明也与C语言中的语法完全一致,但需要注意的是,枚举的成员名必须唯一,否则将有可能代替隐藏数据库中同名的报文和信号。如果没有在声明枚举的同时对成员进行赋值,编译器将按照成员声明的顺序对成员进行初始化。即第一个成员被初始化为0,往后依次加1。例如:
enum {Apple, Pear, Banana} Fruit = Apple;
enum Colors {Red = 1, Green = 3, Blue = 9};
数组
数组(Array)作为一种基本的数据结构,也同样被CAPL支持,就像在C语言中一样。但为了方便使用,CAPL支持直接用字符串初始化字符数组的行为。例如:
int a[3] = {1, 2, 3}; // 有效
char b[13] = "Hello World!"; // 有效
long c[] = {999, 888, 777}; // 无效,在CAPL中必须显式定义数组的长度
同样,CAPL也支持多维数组,并且可以通过内建函数elCount(数组名)来获得数组成员的个数。如果数组的索引超出范围,即小于零或者大于等于数组长度,CAPL将会在数组下标前提示错误。例如:
int v[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int a[3] = {1, 2, 3};
int b = elCount(a); // b = 3
int c = a[4]; // 错误,超出边界范围
特殊类型
CAPL预定义了一些特有的、针对CANoe仿真的类。
报文
报文(CAN/LIN message)是车载网络最基本的构成部分,CAPL提供了各种网络相对应的报文类。这里主要介绍CAN报文和LIN报文。
使用关键字message来声明一个报文变量,当使用message声明报文变量时,默认变量为CAN报文变量。当有数据库支撑的时候,一个完整的声明应该包括message ID或者message name。使用ID 0xA或者报文名来声明一条数据库中的EngineData报文。例如:
message 0xA m1; // ID 0xA (hex)
message 100 m2; // ID 100 (dec)
message EngineData m3; // name EngineData
以标识符“x”结尾的ID表示这是一个扩展帧ID,例如:100x。
而“*”则表明这条报文在声明时还不含有CAN ID。例如:
on message CAN1.* // event机制
{
message *msg; // 使用“*”声明报文
if (this.dir!=rx) return; // 判断条件,只接收RX报文
msg = this; // 将RX报文保存下来
}
注意,使用这种方式声明报文时,一定要指定ID后才能将msg发送出去。
CAPL提供了一系列的选择器(Selector)以保证用户能够按照自己的意图去修改CAN message的属性,表3列出了CAN报文属性以及相关信息。
关键字 | 描述 | 数据类型 | 访问权限 |
---|---|---|---|
AN | CAN报文传输的逻辑通道,有效值范围:1~32 | word | —— |
MsgChannel | CAN报文传输的逻辑通道,有效值范围:1~32 | word | —— |
ID | CAN报文标识符 | dword | —— |
name | 报文在dbc文件中的名称 | char[] | readonly |
DIR | 报文的传输方向 | byte | —— |
RTR | 远程帧标志位 | byte | —— |
DLC | 报文的数据长度,有效值范围:0~15 | byte | —— |
Byte(x) | 数据字节位有效值范围:063(863字节仅适用于CAN-FD) | byte | —— |
例如,如果需要在CAN1网络上发送一条指定的报文,报文名:magicMessage;报文ID:0x252;包含8个字节0x10 10 20 30 40 00 00 00;可以定义如下:
magicMessage.CAN = 1;
magicMessage.DLC = 8;
magicMessage.Byte(0) = 0x10;
magicMessage.Byte(0) = 0x10;
magicMessage.Byte(0) = 0x20;
magicMessage.Byte(0) = 0x30;
magicMessage.Byte(0) = 0x40;
magicMessage.Byte(0) = 0x00;
magicMessage.Byte(0) = 0x00;
magicMessage.Byte(0) = 0x00;
output(magicMessage);
相应地,CAPL也根据LIN总线的特点提供了LIN报文的选择器,LIN报文属性及相关信息如表4所示。
关键字 | 描述 | 数据类型 | 访问权限 |
---|---|---|---|
MsgChannel | LIN报文传输逻辑通道,有效值范围:1~32 | word | —— |
ID | LIN报文标识符 | dword | —— |
DIR | LIN报文传输方向 | byte | readonly |
DLC | 报文的数据长度,有效值范围:0~8 | byte | —— |
Byte(x) | 数据字节位,有效值范围:0~7 | byte | —— |
诊断报文
CAPL通过诊断请求(DiagRequest)和诊断响应(DiagResponse)这两种对象来实现跟ECU之间的诊断服务交互。
通常情况下,诊断服务需要首先对Diagnostic对象声明和初始化。
DiagRequest ServiceQualifier request;
DiagResponse ServiceQualifier response;
上述声明语句分别声明了诊断请求对象“request”和诊断响应对象“response”;并通过给出的诊断服务“ServiceQualifier”进行初始化。这种初始化将在节点仿真开始时被执行一次。
如果使用 *
来代替“ServiceQualifier”,诊断对象将被初始化为未添加诊断描述的空对象,蛋对象的数据必须在发送之前完成设置。
系统变量
系统变量是一种特殊的变量,用来描述某种特殊状态(例如某种事件触发)或者记录测量数据。一般有系统定义和用户自定义两种,它们的作用域都是在各自的命令空间内。
系统变量的数据类型及描述如表5所示。
整型 | 有/无符号 | 长度 | 范围 | b>访问权限 |
---|---|---|---|---|
Interger | signed | 32b | -2147483648~+2147483647 | 32位,有符号 |
Interger | unsigned | 32b | 0~+4294967269 | 32位,无符号 |
Interger | signed | 64b | -9223372036854755808~+9223372036854755807 | 64位,有符号 |
Interger | unsigned | 64b | 0~+18446744073709551616 | 64位,有符号 |
Double | —— | 64b | —— | 64位,IEEE双精度 |
String | —— | —— | —— | 字符型 |
Data | —— | —— | —— | 字节数据 |
Interger Array | —— | —— | —— | 有符号32位数据数组 |
Double Array | —— | —— | —— | 64位IEEE双精度数组 |
定时器
CAPL提供了两种定时器变量:timer
基于秒的时间单位;msTimer
基于毫秒的时间单位,例如:
msTimer myTimer; // 声明一个毫秒定时器 myTimer
Timer myTimer1; // 声明一个秒定时器 myTimer1