目录
什么是诊断
诊断,就是通过问和听的方式,也即发送和接收信号的方式,发送一条指定ID的CAN报文,ECU接收到后回应另一条指定ID的报文来确认当前ECU的相关信息,诊断的本质也是CAN上面的一条报文。在汽车测试中,诊断是非常重要的功能。与ECU相关的信息大部份是通过诊断的方式获取的,比如说,当前ECU软件版本号,ECU工作状态等等。在CAPL中,操作ECU的方式,就是发送一条CAN报文,再接收指定的报文实现诊断功能。CANoe软件中有诊断功能,但是当需要批量测试诊断功能就需要CAPL语言来做了。同时也可通过诊断功能实现自动化测试,如清除ECU中的配置信息,更改VIN码之类的功能。
CAPL诊断发送
CAPL中实现发送诊断请求的功能一般情况以下有两种方式:
- 通过发送message的方式实现
- 通过调cdd文件中已定义好的诊断功能实现发送诊断
这里主要以第二种方法为示例讲解。示例代码如下:
/*@!Encoding:936*/
includes
{
}
variables
{
// GAC.ReadVIN 表示名为GAC的cdd文件中名为ReadVIN的诊断方法
diagRequest GAC.ReadVIN ReadVin; //发送的did结构体
}
//发送did
on key 'd'
{
diagSendRequest(ReadVin); //发送诊断请求
write("已发送诊断请求....");
}
由上面的示例可以知道,发送诊断请求,只需定义diagRequest对象和调用diagSendRequest()函数即可以。上面的示例代码是通过键盘事件触发的,实际使用时,也可以通过其他的事件触发。需要强调说明的是所有定义的diagRequest对象都需要在cdd文件中提前定义好。
CAPL接收
CAPL中,诊断接收可以通过on diagResponse *事件进行接收,也可以通过具体诊断报文进行接收,如 on diagResponse GAC.ReadVIN,其中 GAC.ReadVIN就是cdd库文件定文的诊断方法。这两种方式本质上是同一种,只不过一种是无差别接收,另一种是精准接收。代码示例如下:
on diagResponse *
{
byte data[20]; //响应数据接收数组
int size; //响应数据长度变量
diagResponse * resp; // 声明无具体解释的回应(即操作句柄)
size=this.GetPrimitiveSize(); // 获取响应数据的长度
this.GetPrimitiveData(data,elcount(data)); // 将实际响应复制到数组中
//通过遍历的方式打印数据
for(k=0;k<size;k++){
write("data[%d] = %x",k,data[k]);
}
}
上面就是最简单的诊断接收示例,需要注意的是,接收到的数据类型为byte[],需要将其转换为对应的类型才能进行下一步操作。
完整示例
实现功能:通过CAPL发送一个诊断,读取ECU中的vin码,再将响应的vin码写入到txt文件中。
/*@!Encoding:936*/
includes
{
}
variables
{
diagRequest GAC.ReadVIN ReadVin; //发送的did结构体
char out_char_array[60]; //诊断响应接收变量
int k;
dword glbHandle= 0; //文件操作句柄
long glbValue;
}
//发送did
on key 'd'
{
diagSendRequest(ReadVin);//发送诊断请求
write("d");
}
//诊断结果响应
on diagResponse *
{
long ret; //字符串写入结果接收变量
byte data[20];
int size;
double param_value1=0xFF;
double param_value2=0xFF;
diagResponse * resp; // 声明无具体解释的回应
size=this.GetPrimitiveSize(); // 获取响应数据的长度
this.GetPrimitiveData(data,elcount(data)); // 将实际响应从“on diagResponse*”复制到数据阵列中
for(k=0;k<20;k++){ //通过遍历的方式打印数据
write("data[%d] = %x",k,data[k]);
}
write("%d",size);
GBF_Convert_ByteArrToHexStr(data,size,out_char_array);
write("outr=%s",out_char_array);
//设置文件目录,2表明可以对这个目录下的文件可读可写
setFilePath(".//TestModule", 2);
//模式为2,表示追加模式,发生错误则返回0否则文件打开成功
glbHandle = openFileWrite ("out.txt",2);
if ( glbHandle!=0 ){
//写入字符串,发生错误则返回0,徜则返回1
ret = filePutString(out_char_array, strlen(out_char_array), glbHandle);
if(ret){
write ("write ok."); //在Wirte窗口打印成功提示
fileClose (glbHandle); //关闭文件
}
else{
write ("write error.");//在Wirte窗口打印失败提示
fileClose(glbHandle);//关闭文件
}
}
}
//byte[]转char[]函数
byte GBF_Convert_ByteArrToHexStr(byte rawData[], int datalen,char outHexStr[])
{
int gcOk = 0;
int gcNok = 0;
int i;
int hexLength;
int byteIndex;
byte tmpVal;
byte retVal;
char tmpStr[60];
char tmpErrStr[60];
byte dataType = 2;
// Init to failed
retVal = gcNok;
// Reset output array
for (i = 0; i < elcount(outHexStr); i++)
{
outHexStr[i] = 0;
}
// get the hex length
hexLength = datalen * dataType;
//check that the supplied output array can hold the hex string
if ( elcount(outHexStr) < hexLength )
{
snprintf(tmpErrStr, elcount(tmpErrStr), "GBF_ConvertIntArrToHexStr: ERROR: char array to small, Hex Data is %d but outHexStr only contains %d elements!", hexLength, elcount(outHexStr));
}
else
{
write("hexLength == %d",hexLength);
//All checks went fine, convert data
for (i = 0; i < hexLength; i++)
{
byteIndex = i / dataType;
tmpVal = ((byte)(rawData[byteIndex] >> (4 * (dataType -1 - (i % dataType))))) & 0x0F;
snprintf(tmpStr,elcount(tmpStr),"%X",tmpVal);
strncat(outHexStr,tmpStr,elcount(outHexStr));
if(i % dataType == dataType-1){
write("outHexStr = %d",strlen(outHexStr));
strncat(outHexStr,"",elcount(outHexStr));
}
}
retVal = gcOk;
}
return retVal;
}