一、CAPL
CAPL(Communication Access Programming Language)基于C语言开发的,专门用于CANalyzer和CANoe工具环境。
二、语法
CAPL程序的结构包含了头文件、全局变量、事件函数、自定义函数
头文件:(CAPL Include File)
(1)功能函数
注释: /* Function,value 函数名称 description 函数描述 called everthing 函数被调用 calls 函数内包含其他函数 return 函数返回值 parameter 函数参数(输入) */
(2)文件路径保存→Node文件
includes{ } 头文件是对其他文件及dll文件的包含说明
variables{ } 是对全局变量的声明定义
char letter_a = “a”;
int number_days_in_year = 365;
message 0x003 xxx;
timer one_second;
int j, k = 2;
double x = 33.7;
char p;
工程中用到的各类事件
on start{ }
on timer{ } 对应报文周期相关
流程:
(1)Pre-Start
(2)Start(闪电的按键)
(3)各类事件结构(message ; signal ; key)
(4)Pre-Stop
(5)Stop(esc退出的按键)
(1)注释
1. /* 注释内容 */
2. /* 注释内容
注释内容*/
3. /*
注释内容
*/
4. // 注释内容
(2)变量名称
A.变量名、函数名和数组名可以由字母和数字和下划线组成,但是首字母不能是数字
B.对于用户定义的变量,小写字母和大写字母是不同的变量
C.CAPL支持的关键字是不区分大小写
(3)运算符
(4) 数据类型
A.整型
类型 | 有/无符号 | 长度 | 描述 | 语法结构 |
byte | unsigned | 1B | 字节,8位,无符号 | |
word | unsigned | 2B | 字,16位,无符号 | |
dword | unsigned | 4B | 双字,32位,无符号 | |
qword | unsigned | 8B | 整型,64位,无符号 | |
int | signed | 2B | 短整型,16位 | int value = 20; 十进制 int value2 = 0x14; 十六进制 |
long | signed | 4B | 长整型,32位 | |
int64 | signed | 8B | 整型,64位 |
B.浮点型
类型 | 长度 | 描述 | 语法结构 |
float | 8B | 单精度 | float value = 0.23 |
double | 8B | 双精度 |
B.字符
类型 | 长度 | 语法结构 |
char | 1B | (1)单引号: char value = ‘B’; char value2 = ‘8’; char value3 = ‘?’; (2)支持使用ASCII字符集: char value = 0x42; char value2 = 0x38; char value3 = 0x3F; (3)字符串 char value[30] = “Here’s a string in C and CAPL”; |
C.复合类型
类型 | 语法结构 | |
结构 | struct | struct Data { int age; long hight; char name[50]; }; |
枚举 | enum | enum Colors { Red, Green, Blue }; // 默认 从 0开始,每个元素+1 enum State { State_Off = -1, State_On = 1 }; //可以指定值 |
数组 | array | (1) char alphabet[27] = “ABCDEFGHIJK”; alphabet[0] = ‘A’ (2)整形或者浮点型数组 int sample_data[4] = { 100, 300, 500, 600 }; (3)二维数组 int M[4][5] = { |
多维数组 | elCount(数组名) |
D.特殊类型
类型 | 描述 | 代码 |
报文 | 使用关键字message来声明一个报文变量 | message 0xA m1; message EngineDate m3; |
诊断报文 | DiagRequest ServiceQualifier request;//诊断请 DiagResponse ServiceQualifier response;//诊断响应 | |
系统变量 | ||
定时器 | msTimer myTimer; //声明一个毫秒定时器myTimer 单位:ms timer myTimer;//声明一个秒定时器myTimer 单位:s |
(5)关键字
break | case | char | continue | default | do | double | else | float | for | if |
long | return | switch | whlie | enum | struct | auto | const | extern | goto | register |
signed | sizeof | static | typedef | union | unsigned | volatile | int | short | this | message |
dbMsg* | signal* | service singnal* | diag Request | sysval* | diag Response | timer | testcase |
this用法:
1.在普通方法中,this代表该方法运行所创建的那个对象;
2.在构造方法中,this代表该方法运行所创建的那个对象;
write用法:
(1)打印输出
void用法:
1.不返回任何格式 (有返回值时,不能用该关键词)
return用法
1.返回值
格式 | 输出 |
“%ld” or “%d” | 整数十进制格式显示 |
“%lx” or “%x” | 整数十六进制格式显示 |
“%lX” or “%X” | 整数十六进制格式显示 (字母大写显示) |
“%lu” or “%u” | 无符号整数格式显示 |
“%lo” or “%o” | 整数八进制格式显示 |
“%s” | 显示字符串 |
“%g” | 浮点数显示(最终显示为科学计数法) |
“%f” | 浮点数显示 |
“%c” | 显示一个字符 |
“%I64d” or “%lld” | 64位整数的十进制格式显示 |
“%I64x” or “%llx” | 64位整数的十六进制格式显示 |
“%I64X” or “%llX” | 64位整数的十六进制格式显示(字母大写显示) |
“%I64u” or “%llu” | 64位无符号整数的十进制格式显示 |
“%I64o” or “%llo” | 64位整数的八进制格式显示 |
“%%” | 显示一个% |
void WRITE()
{
write("Hello world");
write("number = %d",number);
write("number = %x",number);
write("number = %X",number);
write("number = %o",number);
write("number = %u",number);
write("str1 = %s",str1);
write("str = %g",str);
write("str = %f",str);
write("str = %5.3f",str);
write("str = %15.33f",str);
write("str = %6.8g",str);
write("str1 = %c",str2);
write("number = %I64d",number);
write("number = %I64x",number);
write("number = %I64X",number);
write("number = %I64o",number);
write("number = %I64u",number);
}
/*输出*/
Hello world
number = 160
number = a0
number = A0
number = 240
number = 160
str1 = abcd
str = 1.23457e+008
str = 123456789.987654
str = 123456789.988
str = 123456789.987654330000000000000000000000000
str = 1.2345679e+008
str1 = b
number = 712964571296
number = a6000000a0
number = A6000000A0
number = 12300000000240
number = 712964571296
Message
msg.CAN = 1 /*帧的传输通道*/
msg.ID = 0x600 /*帧的ID*/
msg.name=Msg1 /*帧的名称*/
/*DIR和RTR结合使用*/
msg.DIR = Tx / Rx /*帧的传输方向:Rx(received);Tx(sent)*/
msg.RTR =1 /*帧的传输请求的状态:0(无远程传输请求);1(远程传输请求)*/
msg.type=RXREMOTE /*TYPE = (RTR << 8) | DIR*/
msg.dlc = 8 /*帧的长度*/
(6)事件类型
类型 | 事件名 | 执行条件 | 语法结构 |
系统事件 | preStart | CANoe初始化时执行 | on preStart { . . . } |
start | 测量开始时执行 | on start { . . . } | |
stopMeasurement | 测量结束时执行 | on stopMeasurement { . . . } | |
CAN消息事件 | 自定义 | 接受到指定消息时执行 | on message 自定义名 { . . . } |
时间事件 | 定时时间到后执行 | on timer 自定义名 { . . . } | |
键盘事件 | 指定按键按下时 | on key 按键名 { . . . } | |
CAN控制器事件 | busoff | 硬件检测到busoff时执行 | on busoff { . . . } |
errorActive | 硬件检测到errorActive时执行 | on errorActive { . . . } | |
errorPassive | 硬件检测到errorPassive时执行 | on errorPassive { . . . } | |
warningLimit | 硬件检测到warningLimit时执行 | on warningLimit { . . . } | |
错误帧事件 | errorFrame | 硬件检测到错误帧时执行 | on errorFrame { . . . } |
系统事件
系统事件的发生顺序依次是:preStart-->start-->preStop-->stopMeasurement
on preStart /*系统事件,初始化时执行*/
{
write("Measurement started!");
msg_Count = 0;
}
on start /*系统事件,工程开始时执行*/
{
write("start Node A");
setTimer(cycTimer,20);
CallAllOnEnvVar(); // CANoe only
}
on preStop /*系统事件,工程预备停止时执行;发生在stopMeasurement事件前面*/
{
message ShutdownReq m;
output(m);
DeferStop(1000);
}
on stopMeasurement /*系统事件,工程停止时执行*/
{
write("Message 0x%x received: %d", msg.id, msg_Count);
}
CAN控制器事件
on errorPassive /*CAN控制器事件:硬件检测到errorPassive时执行*/
{
...
write("CAN Controller is in errorPassive state")
write(" errorCountTX = %d", this.errorCountTX);
write(" errorCountRX = %d", this.errorCountRX);
};
on busOff /*CAN控制器事件:硬件检测到BusOff时执行*/
{
int errRxCnt;
int errTxCnt;
int channel;
double timestamp; // [seconds]
timestamp = (double)timeNow() / (double)100000;
channel = this.can;
errRxCnt = this.errorCountRX;
errTxCnt = this.errorCountTX;
Write("Bus Off: time=%f channel=%d, errRxCnt=%d, errTxCnt=%d",
timestamp, channel, errRxCnt, errTxCnt);
resetCanEx(channel);
}
CAN报文事件
on message 123 /*接收到123(10进制)这个ID的报文时执行*/
on message 0x441 /*接收到0x441(16进制)这个ID的报文时执行*/
on message BCM /*接收到BCM(工程dbc文件中的报文名)这个报文时执行*/
on message* /*接收到任意报文时都执行(注意*与message之间没有空格)*/
on message 0x300-0x444 /*接收到这个范围内的ID报文时执行*/
{
write(“Received %x”,this.id); /*打印接收到的报文id*/
write(“Received Message %d in total!”,count);
}
报文的索引及发送操作(通过报文的信号(msg.VoiceType)去操作)
void TxMsg_VoiceStatus(void)
{
message VoiceStatus msg; /*将工程中dbc中定义的VoiceStatus这条报文取名为msg*/
msg.VoiceType = @VoiceType; /*对应赋值给到报文的信号,通过报文别名"msg."调出*/
msg.VoiceOperation = @VoiceOperation;
output(msg); /*通过output指令发送该报文*/
}
(通过数据类型(msg.byte(0))去操作 )
void TxMsg_VoiceStatus(void)
{
message VoiceStatus msg; /*将工程中dbc中定义的VoiceStatus这条报文取名为msg*/
msg.byte(0) = @VoiceType; /*报文第1个数据字节*/
msg.word(1) = @VoiceOperation; ; /*报文从第1个字节开始的一个字(2个字节)*/
output(msg); /*通过output指令发送该报文*/
}
键盘事件
on key ‘a’ /*在小写输入法下,按下键盘的’A’键时执行*/
on key ‘A’ /*在大写输入法下,按下键盘的’A’键时执行*/
on key ‘ ’ /*按下键盘的空格键时执行,注意单引号中间是有空格的*/
on key 0x20 /*按下键盘的空格键时执行*/
on key F2 /*按下键盘的’F2’键时执行*/
on key CtrlF3 /*同时按下键盘的’Ctrl’键和’F3’键时执行*/
on key* /*按下键盘的任意键时都会执行(注意*与key之间没有空格) */
{
write(“The Key Is Press”);
}
定时器事件
variables
{
msTimer Timer1; /*在variables中声明一个以ms为单位的定时器变量Timer1*/
}
msTimer myTimer;
message 100 msg;
...
on key 'a' {
setTimer(myTimer,20);
}
...
on timer myTimer {
output(msg);
}
on start
{
setTimer(Timer1,100); /将Timer1的定时时间设定为100ms,并启动它/
}
on timer Timer1 /定义的Timer1时间事件,每100ms执行一次/
{
setTimer(Timer1,100); /启动下一个周期循环/
}
on key ‘a‘ /键盘事件,按下键盘’A’键时执行/
{
cancelTimer(Timer1); /停止Timer1这个100ms执行一次的定时器/
}
错误帧事件(在硬件检测到错误帧时执行)
on errorFrame /*错误帧事件:硬件检测到错误帧时执行*/
{
write("The error has occur");
}
系统变量事件
通过”on sysvar”定义系统变量事件;该事件会在指定的系统变量值有新的输入时执行;
注:
系统变量与环境变量差别只在于环境变量是在dbc文件中定义的
on sysvar IO::DI_0
{
$Gateway::IOValue = @this;
}
on sysvar SysVar1 /*系统变量事件:指定的系统变量值有新的输入时执行*/
{
write("The SysVar1 is %d",@SysVar1);
}
环境变量事件
通过”on envVar”定义环境变量事件;该事件会在指定的环境变量值有新的输入时执行
注:
环境变量常常用于关联上一个面板控件,当我们对控件进行操作时,对应改变关联上的环境变量值
on envVar BCM_HightBeamAlarm /*环境变量事件:指定的环境变量值有输入时执行*/
{
byte num=0;
num = getValue(this); /*可以使用getValue(环境变量名/this关键字)获取指定的环境变量的值*/
if(num == 1)
{
write("The envVar is %d",@BCM_HightBeamAlarm);
}
else
{
putValue(this,1);/*使用putValue(环境变量名/this关键字,设定的值)改变指定的环境变量的值;直接赋值的话,格式是@BCM_HightBeamAlarm = 1; */
write("Change envVar to %d",@BCM_HightBeamAlarm);
}
}
(7)函数
函数 | 执行条件 | 语法结构 |
setTimer() | 设置开启一个定时器,时间到后关闭 | setTimer(Timer1,100) |
cancelTimer | 关闭定时器 | cancelTimer(Timer1) |
write() | 消息输出打印 | write(“The error has occur”) |
output | 发送报文 | output(msg) |
通用函数:
计算函数
字符串函数
CAN总线函数
诊断函数
diagResize:
调整诊断对象的大小以匹配指定的参数迭代,或将总线消息的大小设置为给定的字节数
diagRequest PAS3.* pasDiagReqst;
dword diagLen;
diagLen = stringToBytes(diagString, rawDiagReqst);
diagResize(pasDiagReqst, diagLen);
diagSetPrimitiveByte:
用于设定诊断对象指定字节的数值
for(i=0;i<diagLen;i++)
{
diagSetPrimitiveByte(pasDiagReqst, i, diagData[i]);
}
diagSetPrimitiveData:
用于设置完整服务原语的原始数据(通过传输协议传输的所有数据)
PrintDiagRequestBytes( diagRequest * req)
{
BYTE primitiveRaw[4095];
long size;
size = DiagGetPrimitiveData( req, primitiveRaw, elcount( primitiveRaw));
if( size > 0)
write( "Request = (%d)[%02x ...]", size, primitiveRaw[0]);
else
write( "Request: error %d", size);
}
TestReportWriteDiagObject / TestReportWriteDiagResponse:
用于在测试报告中增加诊断请求或响应的报文打印
diagRequest PAS3.* pasDiagReqst;
testReportWriteDiagObject(pasDiagReqst);
TestWaitForDiagRequestSent:
用于定义在指定时间内将要求的诊断请求发送出去
DiagRequest SerialNumber_Read req;
long result;
DiagSetTarget("Door");
req.SendRequest();
// waits until request is completely sent
if (TestWaitForDiagRequestSent(req, 2000)== 1)
TestStepPass("Request was sent successfully!");
else
TestStepFail("Request could not be sent!");
TestWaitForDiagResponse(req, 2000);
TestWaitForDiagResponse:
用于定义在指定时间内是否收到诊断请求的响应
diagResponse PAS3.* pasDiagRespd;
const cApplicationTimeoutMs = 5000;
result = testWaitForDiagResponse(pasDiagReqst, cApplicationTimeoutMs)
diagGetLastResponse
用于获取最新的诊断请求响应
diagResponse PAS3.* pasDiagRespd;
diagGetLastResponse(pasDiagRespd);
diagGetPrimitiveData :
用于读取一个诊断服务的原始数据
PrintDiagRequestBytes( diagRequest * req)
{
BYTE primitiveRaw[4095];
long size;
size = DiagGetPrimitiveData( req, primitiveRaw, elcount( primitiveRaw));
if( size > 0)
write( "Request = (%d)[%02x ...]", size, primitiveRaw[0]);
else
write( "Request: error %d", size);
}
diagGetPrimitiveByte :
用于读取诊断服务中的某一字节的数据
diagIsPositiveResponse / diagIsNegativeResponse:
用于判断回复的诊断相应是否为正/负响应,如果是则返回一个不为0的数值
on diagResponse *
{
// Handle the ambiguity of neg responses by treating them as '*'
if( diagIsNegativeResponse ( this ) )
{
write( "Received negative response for service 0x%x, code 0x%x",
(long) diagGetParameter( this, "SIDRQ_NR" ),
(long) diagGetParameter( this, "NRC" ) );
}
}
(8)流程控制
控制类型 | 执行条件 | 语法结构 |
if | if 括号内的表达式为真,或者非0,则执行if 下面的语句 | if (speed > 60) { //花括号内的语句都会执行 write("line 1"); write("line 2"); } |
如果没有花括号,则只执行if下面的第一行语句 | if (speed > 60) write("line 1");//这一行语句受if语句控制 write("line 2");//这一行语句不受if语句控制 | |
if else | 如果下面表达式为真或者非0,则执行语句块1,否则执行语句块2 | if (speed > 60) { write("line 1"); } else if(speed > 80) { write("line 2"); } else { write("line 3"); } |
switch | switch ( operator ) { case ‘+’: result = value1 + value2; break; case ‘-’: result = value1 – value2; break; case ‘*’: result = value1 * value2; break; default: write (“Unknown operator.”); break; } | |
while | 循环 | while( a < 20 ) { write("a 的值: %d\n", a); a++; } |
do…while | 循环 当条件为 true 时执行循环。 当条件为 false 时,退出循环 注: do…while 循环与 while 循环类似,但是 do…while 循环会确保至少执行一次循环 | /* do 循环执行,在条件被测试之前至少执行一次 */ do { write("a 的值: %d\n", a); a = a + 1; }while( a < 20 ); |
for | 循环 | /* for 循环执行 */ for( a = 10; a < 20; a = a + 1 ) { write("a 的值: %d\n", a); } /* for 循环执行 */ for( ; a < 20; )//第1,3个表达式为空 { write("a 的值: %d\n", a); a = a + 1; } |
break | 当 break 语句出现在一个循环内时,循环会立即终止 嵌套循环(即一个循环内嵌套另一个循环),break 语句会停止执行最内层的循环 | |
continue | ||
return | 不带参数的return也可以退出循环语句 用来在函数中,返回一个值或者值的表达式 | while( a < 20 ) result *= x; return result; |
三、案例
(1)周期消息发送
仿真或测试时向总线模拟发送周期消息
variables
{
msTimer myTimer;
message 100 msg;
}
on key 'a' {
setTimer(myTimer,20);
}
on timer myTimer {
output(msg);
setTimer(myTimer,20);
}
(2)应用报文Request/Response
ECU请求/应答式的功能
variables
{
message BCM_Request tBCM_Request; //请求报文
message BCM_Response tBCM_Response; //响应报文
int result = 0;
int waitTime = 1000;
}
void MainTest()
{
TestModuleTitle ("Test BCM Features");
TestModuleDescription ("Check all features in BCM.");
TestGroupBegin("BCM Wiper Feature", "Check the perfomance of Wiper");
Check_Wiper_Feature(0,0); //测试雨刮关闭功能
Check_Wiper_Feature(1,1); //测试雨刮开启功能
TestGroupEnd();
}
//自定义函数
testcase Check_Wiper_Feature(int request, int response )
{
tBCM_Request.WiperRequest.phys = request;// 报文名称.信号.信号值
output(tBCM_Request);
//测试请求发出去后1000ms内是否收到BCM的响应信号。
result = TestWaitForSignalMatch(BCM_Response::WiperResponse,response,waitTime);
passResult(result,request,response);
}
void passResult(long result,int request,int response)
{
switch(result){
case 1: TestStepPass("1.0","Test Pass - request : %d expect response : %d ",request,response);break;
case 0: TestStepFail("1.0","Timeout - request : %d expect response : %d ",request,response);break;
case -1: TestStepFail("1.0","General error - request : %d expect response : %d ",request,response);break;
case -2: TestStepFail("1.0","Signal is not valid");break;
default:break;
}
}
(3)检测总线中周期报文的发送周期
dword ChkCreate_MsgRelCycleTimeViolation (Message aObservedMessage, double aMinRelCycleTime, double aMaxRelCycleTime, Callback aCallback);
dword ChkStart_MsgRelCycleTimeViolation (Message aObservedMessage, double aMinRelCycleTime, double aMaxRelCycleTime, Callback aCallback);
dword ChkCreate_MsgAbsCycleTimeViolation (Message aObservedMessage, duration aMinCycleTime, duration aMaxCycleTime, char[] aCallback);
dword ChkStart_MsgAbsCycleTimeViolation (Message aObservedMessage, duration aMinCycleTime, duration aMaxCycleTime, char[] aCallback);
案例:
testcase CheckMsgEngineData()
{
float aMinRelCycleTime = 0.9;
float aMaxRelCycleTime = 1.1;
// Information for test report.
TestCaseTitle("TC 4", "Check cycle time of message EngineData");
// checks the cycle time of the message
gCycCheckId = ChkStart_MsgRelCycleTimeViolation (EngineData, aMinRelCycleTime , aMaxRelCycleTime );
TestAddCondition(gCycCheckId);
// sequence of different actions and waiting conditions
TestWaitForTimeout(1000);
TestRemoveCondition(gCycCheckId);
}