目录
工程背景
工程目的
报文发送情况
工程实现
工程步骤概述
1 测试方法分析
1-1 检测报文周期
1-2 检测报文长度DLC
1-3 功能测试
1-4 检测未定义报文
2 添加Test Module
2-1创建测试环境
2-2 插入CAPL Test Module
3 CAPL编写测试用例
3-1 测试模块入口函数MainTest
3-2 CAPL测试用例——检测报文周期
3-3 CAPL测试用例——检测报文长度DLC
3-4 CAPL测试用例——检测未定义报文undefined msg
3-5 CAPL测试用例——功能测试
4 工程运行测试
5 故障注入
5-1 系统变量控制报文发送
5-2 IG节点发送自定义报文
6 测试报告
本工程主要目的是: 基于第3个仿真工程, 熟悉CANoe的报文测试功能。
本工程将围绕CAN总线中的报文,在Test Module中实现测试功能。主要包括:
第3个仿真工程报文发送与接收情况如下:
报文的相关属性整理如下表:
报文中的信号属性如下表:
本章实例基于第12章的仿真工程,为了便于区别,需要将原工程的文件夹复制并 名为Vehicle_System_Simulation_Test, 工程名称也另存为Vehicle_System_CAN_Test.cfg, 在该工程文件夹下创建一个名为Testmodul的文件夹,用于存放相关的测试代码。
接下来,将在此基础上添加测试模块及故障注入面板等。
工程包括以下几个关键步骤:
本工程的目的是分别测试不同报文的发送周期、报文长度DLC;功能测试(检测信号值是否在期望的数值范围内 );未定义报文。
检测报文周期的方法是划定某一段特定的时间,选取该时间段中第一条待测报文作为起始时间戳,观察此待测报文后续重复的时间间隔。
TSL函数——检测函数
检测函数1:
ChkStart_MsgAbsCycleTimeViolation ( Message aObservedMessage,duration aMinCycleTime, duration aMaxCycleTime)
函数功能:观察总线周期性报文aObservedMessage的每次出现,如果该报文的间隔不符合规范要求,则会触发一个代表异常出现的特殊事件。
返回值:>0,返回一个IDaCheckedId,即观察待测报文的事件;=0报错。
TSL函数——状态报告函数
状态报告函数1:long ChKQuery_NumEvents(dword aCheckId)
函数功能:查询该时间段内异常出现的特殊事件的个数
状态报告函数2:double ChkQuery_StatProbeIntervalAvg(dword aCheckId)
返回该时间段内,该报文的平均周期间隔时间
状态报告函数3: double ChkQuery_StatProbeIntervalMin(dword aCheckId)
返回该时间段内,该报文的最小周期间隔时间
状态报告函数4: double ChkQuery_StatProbeIntervalMax(dword aCheckId)
返回该时间段内,该报文的最大周期间隔时间
TSL函数——检测控制函数
检测控制函数1: long ChkControl_Destroy(Check aCheckId)
用于测试结束时,销毁该事件对象aCheckId,释放资源。返回0操作成功,<0报错。
函数:
状态报告函数 ChkQuery_NumEvents
检测控制函数 ChkControl_Destroy
检测函数:dword ChkStart_InconsistentDLC(Message aMessage,char [] aCallback)
检测发送到总线上指定报文长度是否与DBC数据库中的定义一致
aMessage待测报文;char [] aCallback回调函数名,可选参数
返回值:>0:返回一个事件对象aCheckId;=0报错
功能测试用过CAPL程序逻辑来设置某信号的数值,然后使用ChkStart_MsgSignalValueInvalid函数来检测信号值是否在期望的数值范围内。
dword ChkStart_MsgSignalValueInvalid (Signal aObservedSignal,double aMinValue, double aMaxValue, Callback aCallback)
函数参数:待测信号,必须是定在DBC中的信号,最小信号值,最大信号值,回调CAPL函数名,可选。
返回值:返回一个事件对象aCheckId,即检测未定义报文的事件
检测函数:dword ChkStart_UndefinedMessageReceived (char [] CaplCallback)
作用:观察当前总线上是否有未定义的报文
返回值:>0:返回一个事件对象aCheckId,即待观测报文的事件;=0报错。
状态报告函数:long ChkQuery_EventMessageId (dword aCheckId)
作用:返回触发该事件的报文的MessageId
返回值:>0返回触发该事件的报文ID;<0报错。
创建测试环境,命名为NetworkTester。
插入CAPL Test Module,并配置此模块Configuration对话框
配置Module的Name为:Network Tester,在TestModule文件夹下创建CAPL文件NetworkTester.can
选中TestModule,右键选中Edit,可以编辑NetworkTester.can 。
CAPL测试模块中can文件要求:-必须包含MainTest函数,所有的测试用例均从此接口进入
void MainTest()
{
testModuleTitle("NetworkTester");
testModuleDescription("Message Specification Test and Function Test Demo.");
testGroupBegin("Check msg cycle time","Check the differ mesage cycle time");
Init_Test_Condition();
CheckMsgEngineData();
CheckMsgVehicleData();
CheckMsgGear_Info();
CheckMsgIgnition_Info();
CheckMsgLight_Info();
testGroupEnd();
testGroupBegin("Check msg DLC","Check DLC of a message");
CheckDLCLock_Info();
testGroupEnd();
testGroupBegin("Check undefined msg","Check the undefined message");
CheckUndefinedMessage();
testGroupEnd();
testGroupBegin("Fucntion Test","Check the engine speed after setup");
CheckEngine_Speed();
testGroupEnd();
}
//初始化仿真工程状态,确保各个模块处于Online
Init_Test_Condition()
{
@Vehicle_Key::Unlock_Car = 1;
@Vehicle_Key::Car_Driver = 0;
@Vehicle_Key::Key_State = 2;
testWaitForTimeout(500);
}
分别测试EngineData(50),VehicleData(50),Gear_Info(50),Ignition_Info(50) ,Light_Info(500)的报文周期。
以EngineData为例,CAPL程序逻辑如下:
其他报文只需在3测试用例部分,改为相应的报文名称即可。1声明检测事件,4用例检测结果,5销毁检测事件都是通用功能。
具体代码示例如下:
variables
{
//TC1
dword gCycCheckId;//声明检测事件的ID
int gUndefinedMsgCheckResult;//声明未定义报文的检测结果
const long kMIN_CYCLE_TIME = 40;//一般最小周期时间常量
const long kMAX_CYCLE_TIME = 60;//一般最大周期时间常量
const long Light_MIN_CYCLE_TIME = 490;//定义报文Light_Info最小周期时间常量
const long Light_MAX_CYCLE_TIME = 510;//定义报文Light_Info最大周期时间常量
const long kTIMEOUT = 4000;//定义测试等待时间常量
//自定义报文——使用IG模块
}
//周期时间检测结果函数
CheckMsgCyc(float aCycMinCycleTime, float aCycMaxCycleTime)
{
long lQueryResultProbeAvg;//声明平均时间
long lQueryResultProbeMin;//声明最小测量时间
long lQueryResultProbeMax;//声明最大测量时间
char lbuffer[100];
testAddCondition(gCycCheckId);//在该函数中添加事件
testWaitForTimeout(kTIMEOUT);//等待测试时间结束
//统计平均时间
lQueryResultProbeAvg = ChkQuery_StatProbeIntervalAvg(gCycCheckId);
//统计min时间
lQueryResultProbeMin = ChkQuery_StatProbeIntervalMin(gCycCheckId);
//统计max时间
lQueryResultProbeMax = ChkQuery_StatProbeIntervalMax(gCycCheckId);
if(ChkQuery_NumEvents(gCycCheckId)>0)
{
//统计异常次数//打印报告
snprintf(lbuffer,elCount(lbuffer),"Valid values %.0fms - %.0fms",aCycMinCycleTime,aCycMaxCycleTime);
testStepFail("",lbuffer);
snprintf(lbuffer,elCount(lbuffer),"Average cycle time: %dms",lQueryResultProbeAvg);
testStepFail("",lbuffer);
snprintf(lbuffer,elCount(lbuffer),"Min cycle time: %dms",lQueryResultProbeMin);
testStepFail("",lbuffer);
snprintf(lbuffer,elCount(lbuffer),"Average cycle time: %dms",lQueryResultProbeMax);
testStepFail("",lbuffer);
}
else
{
snprintf(lbuffer,elCount(lbuffer),"Valid values %.0fms - %.0fms",aCycMinCycleTime,aCycMaxCycleTime);
testStepPass("",lbuffer);
snprintf(lbuffer,elCount(lbuffer),"Average cycle time: %dms",lQueryResultProbeAvg);
testStepPass("",lbuffer);
snprintf(lbuffer,elCount(lbuffer),"Min cycle time: %dms",lQueryResultProbeMin);
testStepPass("",lbuffer);
snprintf(lbuffer,elCount(lbuffer),"Average cycle time: %dms",lQueryResultProbeMax);
testStepPass("",lbuffer);
}
ChkControl_Destroy(gCycCheckId);//销毁事件
}
//TC1:Check Cycle time of msg EngineData
testcase CheckMsgEngineData()
{
float lCycMinCycleTime;//声明最小周期时间
float lCycMaxCycleTime;//声明最大周期时间
lCycMinCycleTime = kMIN_CYCLE_TIME;//赋值
lCycMaxCycleTime = kMAX_CYCLE_TIME;
//测试报告提示信息
testCaseTitle("TC-1","TC-1:Check cycle time of msg EngineData");
//开始观察待测报文
gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(EngineData,lCycMinCycleTime,lCycMaxCycleTime);
CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);//周期时间检测结果函数
testRemoveCondition(gCycCheckId);//移除测试条件
}
//TC-2:Check Cycle time of msg VehicleData
testcase CheckMsgVehicleData()
{
float lCycMinCycleTime;
float lCycMaxCycleTime;
lCycMinCycleTime = kMIN_CYCLE_TIME;
lCycMaxCycleTime = kMAX_CYCLE_TIME;
testCaseTitle("TC-2","TC-2:Check cycle time of msg VehicleData");
gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(VehicleData,lCycMinCycleTime,lCycMaxCycleTime);
CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
testRemoveCondition(gCycCheckId);
}
//TC-3:Check Cycle time of msg Gear_Info
testcase CheckMsgGear_Info()
{
float lCycMinCycleTime;
float lCycMaxCycleTime;
lCycMinCycleTime = kMIN_CYCLE_TIME;
lCycMaxCycleTime = kMAX_CYCLE_TIME;
testCaseTitle("TC-3","TC-3:Check cycle time of msg Gear_Info");
gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(Gear_Info,lCycMinCycleTime,lCycMaxCycleTime);
CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
testRemoveCondition(gCycCheckId);
}
//TC-4:Check Cycle time of msg Ignition_Info
testcase CheckMsgIgnition_Info()
{
float lCycMinCycleTime;
float lCycMaxCycleTime;
lCycMinCycleTime = kMIN_CYCLE_TIME;
lCycMaxCycleTime = kMAX_CYCLE_TIME;
testCaseTitle("TC-4","TC-4:Check cycle time of msg Ignition_Info");
gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(Ignition_Info,lCycMinCycleTime,lCycMaxCycleTime);
CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
testRemoveCondition(gCycCheckId);
}
//TC-5:Check Cycle time of msg Light_Inf
testcase CheckMsgLight_Info()
{
float lCycMinCycleTime;
float lCycMaxCycleTime;
lCycMinCycleTime = kMIN_CYCLE_TIME;
lCycMaxCycleTime = kMAX_CYCLE_TIME;
testCaseTitle("TC-5","TC-5:Check cycle time of msg Light_Info");
gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(Light_Info,lCycMinCycleTime,lCycMaxCycleTime);
CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
testRemoveCondition(gCycCheckId);
}
//TC6:DLC 报文长度测试
testcase CheckDLCLock_Info()
{
dword checkId;
//测试报告提示信息
testCaseTitle("TC-6","TC-6:Check msg DLC of Lock_Info");
//管事观测报文Lock_Info的DLC
checkId = ChkStart_InconsistentDlc(Lock_Info);
testAddCondition(checkId);
//等待测试时间结束
testWaitForTimeout(kTIMEOUT);
testRemoveCondition(checkId);
}
//TC-7:检测未定义信号
testcase CheckUndefinedMessage()
{
long lEventUndefineMessageId;//声明未定义报文Id
char lbuffer[100];
gUndefinedMsgCheckResult = 0;//?初始化未定义报文数量为0
testCaseTitle("TC-7","TC-7:Check CAN channel for undefined message");
//开始观测当前总线
gCycCheckId = ChkStart_UndefinedMessageReceived("UndefinedMsgCallback");
//延时,即测量该时间段
testWaitForTimeout(kTIMEOUT);
switch(gUndefinedMsgCheckResult)
{
case 1:
write("undefined message detected!");
//获取未定义报文ID
lEventUndefineMessageId = ChkQuery_EventMessageId(gCycCheckId);
snprintf(lbuffer,elCount(lbuffer),"Undefined message detected: Id 0x%x",lEventUndefineMessageId);
testStepFail("",lbuffer);
break;
default:
write("Iamdefault");
testStepPass("","No undefined message detected!");
break;
}
ChkControl_Destroy(gCycCheckId);//销毁事件
}
UndefinedMsgCallback(dword aCheckId)
{
//回调函数,检测到未定义报文时调用
write("Test: undefined message finded");
ChkQuery_EventStatusToWrite(aCheckId);
gUndefinedMsgCheckResult=1;//将未定义报文个数置为1
}
testcase CheckEngine_Speed()
{
dword checkId;
testCaseTitle("TC-8","TC-8:Check Engine Speed Value");
@Vehicle_Key::Unlock_Car = 1;
@Vehicle_Key::Car_Driver = 0;
@Vehicle_Key::Key_State = 2;
@Vehicle_Control::Eng_Speed = 2000;
//开始观测,信号值是否在范围内
checkId = ChkStart_MsgSignalValueInvalid(EngineData::EngSpeed,1900,2100);
testWaitForTimeout(kTIMEOUT);
if(ChkQuery_EventSignalValue(checkId))
{
testStepPass("","Correct Engine Speed Value");
}
else
{
testStepFail("","Incorrect Engine Speed Value");
}
}
运行工程后,运行测试。
测试用例运行结果如图所示,Light_Info的报文周期检测报错。
——查看CANdb,发现只有报文Light_Info的报文周期为500,代码中使用的检测范围为40-60,将周期范围更改为Light_MIN_CYCLE_TIME和Light_MAX_CYCLE_TIME,如下示例:
//TC-5:Check Cycle time of msg Light_Info
testcase CheckMsgLight_Info()
{
float lCycMinCycleTime;
float lCycMaxCycleTime;
//lCycMinCycleTime = kMIN_CYCLE_TIME;//kMIN_CYCLE_TIME=40==>Light_MIN_CYCLE_TIME=490
//lCycMaxCycleTime = kMAX_CYCLE_TIME;//kMAX_CYCLE_TIME=60==>Light_MAX_CYCLE_TIME=510
lCycMinCycleTime = Light_MIN_CYCLE_TIME;
lCycMaxCycleTime = Light_MAX_CYCLE_TIME;
testCaseTitle("TC-5","TC-5:Check cycle time of msg Light_Info");
gCycCheckId = ChkStart_MsgAbsCycleTimeViolation(Light_Info,lCycMinCycleTime,lCycMaxCycleTime);
CheckMsgCyc(lCycMinCycleTime,lCycMaxCycleTime);
testRemoveCondition(gCycCheckId);
}
再次运行测试用例,全部通过。
为了验证测试用例的正确性,可以使用多种故障注入方法实现故障注入,常见的有:使用故障注入函数,采用网络节点CAPL编程,以及使用IG节点。
书中制作了一个面板: Msg_Switch和Custom_Msg,分别控制报文的发送和关闭,以及发送自定义报文。
我自己尝试使用Panel模块实现这个面板:创建系统变量,将面板复选框关联系统变量,再在CAPL编程中读取系统变量,根据变量值控制对应的报文函数。
但最后,使用Panel只实现了Msg_Switch部分。Custom_Msg没有找到对应的未定义报文创建方法,有实现的小伙伴欢迎分享。最后使用IG节点实现了未定义报文的发送。
创建系统变量,将面板复选框关联系统变量,再在CAPL编程中读取系统变量,根据变量值控制对应的报文函数。
测试用例涉及7个报文,因而创建7个系统变量创建如下:
以Gateway_EngineData_off为例,系统变量创建过程如下。
先建立一个Value Table共同使用。因为7个系统变量具有相同的数值解释:勾选为1代表停止报文发送,不勾选为0代表报文正常发送。使用相同的ValueTable可以统一管理。
Panel创建过程如下:
先添加一个Panel面板,命名为NetworkTest.
添加复选框组件,修改名称并,关联对应的系统变量,下图示例为EngineData,其余报文控制根据名称一一对应即可。
Panel创建完毕后,在NetworkTest.can中添加变量监听事件,控制相应的报文发送。
以EngineData为例,CAPL编程如下:
on sysvar_update TestSysVar::Gateway_EngineData_off
{
if (@this==1)
{
testDisableMsg(EngineData);
write("Test: disable EngineData");
//testDisableMsg(Cluster_Info);
// ILDisableMsg("Cluster_Info");
}
else{
testEnableMsg(EngineData);
write("Test: enable EngineData");
}
}
控制报文发送和终止的函数不止有testDisableMsg和testEnableMsg,其它函数可以参考此文(完善后放链接)。
IG节点可分为 CAN IG和IG,区别是CANIG只支持CAN报文,而IG可支持CAN、LIN、MOST等其他报文。此外还有IG和PDU IG的区别,PDU IG可以支持任意网络协议,包括CAN以及Ethernet 、FlexRay。
5-2-1 创建IG节点
在Simulation Setup总线上创建CAN IG 模块,CAN IG模块允许用户发送自定义的CAN报文。
5-2-2 添加自定义报文,并配置
如上图添加了3个自定义报文Msg_01,Msg_02,Msg_03,并按下图定义相关属性
5-2-3 运行
保存后,启动工程,启动测试用例,发送自定义报文。
测试结果显示检测到了未定义报文。
测试报告有2种格式:①CANoe TestReport Viewer(推荐)②XML/HTML格式(以前的)
可如下图所示,修改为自己所需的格式:
测试报告的打开位置如下
①CANoe TestReport Viewer(推荐)
②XML/HTML格式(以前的)
END