自学canoe-canoe从入门到精通(15)
参考:CANoe开发从入门到精通 (杨金升 )(清华大学出版社2019年出版)
仿真+诊断
汽车诊断技术是基于车载网络的一项重要应用,既可实时监控整车的状态,提高汽车的安全性,也可以帮助维修人员方便快捷地定位故障,提高维修效率。
1 汽车诊断技术概述
汽车诊断技术是在不拆卸车辆的情况下,通过读取车辆在运行过程中所记录的数据或故障码来查明故障原因,并确定故障部位的汽车应用技术。利用该技术,可以快速检测汽车故障(如传感器短路/开路、排放错误、异常操作等)来提高汽车安全性和维修效率。
诊断采用“问-答”模式,即诊断仪向车辆指定的ECU发送请求数据,指定的ECU会做出响应,将对应的应答数据返回给诊断仪。利用已定义的诊断描述文件,可将相应的数据解析为可读的诊断信息。
诊断仪请求命令:22 4A 05(油箱温度请求命令)
车辆应答数据:62 4A 05 5A FF(返回当前油箱的温度数据)
在诊断描述文件中,定义了车辆应答数据的第4个字节表示邮箱温度的十六进制数据,所以车辆的应答数据中5A就代表油箱温度为90℃。
1.1 诊断术语
客户端(Client):诊断请求的提出者(诊断仪),发送诊断请求。
服务器端(Server):诊断响应的提供者(ECU),发送诊断响应。
远程客户端/服务器(Remote Client/Server):与客户端/服务器不在同一个网段。
肯定响应(Positive Response):服务器端正确执行客户端诊断请求时做出的响应。
否定响应(Negative Response):服务器端无法正确执行客户端的诊断请求时做出的响应。
1.2 OBD诊断与增强型诊断
OBD ( On-Board Diagnostic ) 车 载 诊 断 系 统 , 最 初 起 源 于CARB(California Air Resources Board,加州空气资源委员会)为1988年之后生产的加州汽车所制定的排放法规。随着这套法规逐渐标准化实施,SAE(Society of Automotive Engineers,美国汽车工程师协会)提出了OBD Ⅱ,所有执行OBD Ⅱ标准的汽车都需要具备标准化的车辆数据诊断OBD接口、标准化的诊断解码工具、标准化的诊断协议、标准化的故障码定义和标准化的维修服务指南。OBD系统实时监控引擎的运行状况和尾气处理系统的工作状态,一旦发现有可能引起排放超标的情况,会立刻发出警示,同时将故障信息记录到存储器,通过标准的诊断仪器和诊断接口可以读取故障码的相关信息。根据故障码的提示,维修人员能迅速准确地判断故障的性质和部位。增强型诊断是指除OBD诊断以外的诊断,主要目的是为了方便汽车开发、标定、下线检测、售后维修和代码升级等,如引擎模块中包含OBD诊断和增强型诊断,车身、仪表等电控单元全部采用了增强型诊断。
1.3 诊断协议
ISO标准中对K线和CAN线诊断制定了一系列的诊断协议,K线/CAN线诊断协议如表。
1.4 诊断接口
OBD接口已成为现代汽车的标准接口,通常位于驾驶员座位前的左下方。
对于单个ECU而言,一般采用诊断工具与总线(K线、CAN、LIN等)直接相连进行诊断测试。
1.5 诊断周期
诊断贯穿车辆的开发到售后的整个生命周期。
在车辆研发阶段,整车厂与各个ECU供应商共同定义开发诊断功能。ECU供应商对单个ECU进行诊断测试,整车厂在生产过程中对整车系统进行诊断测试。车辆下线后,售后保养维修单位可以根据诊断规范定义,利用诊断仪对故障车辆进行排查。
1.6 UDS诊断服务
由于增强型诊断在整车和ECU的应用中发挥越来越重要的作用
UDS(Unified Diagnostic Service,统一诊断服务)也就是ISO14229,提供了诊断服务的基本框架,是面向整车所有ECU单元的诊断协议。
UDS只定义了应用层的诊断协议和服务,因此无论是对CAN线、K线或者是Ethernet都可以实现UDS诊断。整车厂和零部件供应商可以根据所用的总线和ECU功能,选择实现其中的所有或部分诊断服务,也可以使用整车厂自定义的诊断服务。UDS不是法规强制要求,没有统一实现标准,其优势在于方便生产线检测设备的开发,同时更加方便了售后服务和车联网等功能的实现。目前,UDS诊断服务的主要应用包括诊断/通信管理、数据处理、故障信息读取、在线编程及功能/元件测试等
每个功能支持的诊断服务。
2 CANoe诊断功能简介
CANoe可以满足ECU开发的各个阶段中对诊断测试的设计和执行的要求。
2.1 诊断描述文件
对于简单的诊断描述可以使用CANoe自带的Basic Diagnostic Editor(基本诊断编辑器)。
CDD文件是当前ECU诊断测试中较为通用的诊断描述文件。CDD文件中定义了诊断协议、诊断服务的请求、响应以及相关的参数。
2.2 安全访问服务
安全访问(Security Access)服务指为了对已经锁住诊断功能的ECU执行诊断操作时,需要使用一个key(密钥)来解锁ECU。这个密钥是根据ECU诊断响应发送过来的seed(种子)计算出来的。客户端用于计算密钥的算法可以通过调用Security DLL生成。
2.3 诊断测试窗口
在Diagnostics/ISO TP Configuration窗口中配置完诊断描述文件和 Security DLL 文 件 后 , 将 会 自 动 弹 出 Diagnostic SessionControl ( 诊 断 会 话 控 制 ) 、 Fault Memory ( 故 障 记 忆 ) 和Diagnostic Console(诊断控制台)三个窗口。
以上三个窗口均由CANoe根据CDD文件自动生成,其中,诊断控制台包含所有诊断服务的列表,也提供编辑诊断请求数据的子窗口以及诊断通信收发的结构。
需要特别指出的是,CANoe提供的诊断窗口中显示的相关诊断参数信息都是在CDD文件中预先定义好的。
3 CANoe常见诊断函数
3.1 通信/设定功能函数
通信/设定函数(Functions for Communication and Setup)主要用来设定诊断目标,发送诊断请求及响应等
3.2 安全访问函数
安全访问函数(Security Access Functions)主要是为了对ECU进行解锁操作。
3.3 对象访问函数
对象访问函数(Functions for Access to the Whole Object)主要用于获取诊断响应类型及参数,设定诊断请求数据等。
3.4 参数访问函数
参数访问函数(Functions for Access to One Parameter)用于获取/设置诊断相关参数的数值或类型。
3.5 诊断测试函数
诊断测试函数(Functions to Support Tests)作为TFS的一部分,主要针对诊断测试功能实现。
4 工程实例简介
(1)基于CDD文件的诊断控制台/故障码窗口等功能实现。
(2)基于Tester节点的测试面板:手动诊断执行,诊断参数读写操作的可视化实现。
(3)基于CAPL测试模块,自动化诊断测试的实现。
5 工程实现
在没有真实IPC的情况下能正常演示,因此在实现以上功能的同时需要在IPC模块中加入虚拟的诊断响应。
5.1 工程文件搭建
Vehicle_System_Simulation_ Diagnosis下创建CDD和Diagnosis两个文件夹来放置诊断相关文件。
CDD文件夹下添加CDD文件及PDF格式文档
在文件夹Diagnosis中创建Test和Security两个文件夹,文件夹Test下创建一个名为CAN的文件夹
5.2 导入CDD文件
在左边视图中选择CAN Networks→CAN,出现右侧的子对话框
在Diagnostic Descriptions中可以添加、删除、打开对应的诊断描述文件 。。 单 击 Add Diagnostic Description→Add Diagnostic Description(CDD, ODX/PDX, MDX),双击选择前面复制的CDD文件(IPC_v0.01.cdd)。
添加后效果。
ECU Qualifier指当前规范文件在CANoe中的标识符名称,在同一个仿真里,诊断描述文件的标识符名称必须是唯一的。在CAPL程序中,用户可以通过diagSetTarget函数来切换不同的诊断描述文件。Simulation by指在当前仿真工程中用来仿真诊断功能的节点名称,将此处选择为节点IPC。
5.3 Security DLL文件配置
为了防止ECU数据被误改,许多诊断服务需要在不同的安全访问等级下访问,在CANoe中用户需要配置自定义的动态链接库(DLL)文 件 来 提 供 解 锁 安 全 访 问 算 法 。
添加动态链接库文件。
选择CAN Networks→CAN→IPC→Diagnostic Layer,在Seed &Key DLL处导入上面的DLL文件
5.4 诊断控制台
导入CDD文件以后,就可以利用诊断测试窗口进行测试了,诊断控制台相对于会话控制窗口和故障码窗口在诊断测试和分析中使用更为广泛。
可以看到,在左侧诊断请求列表中,列出了当前CDD文件中定义的所有的服务请求,用户可以通过鼠标双击对应的诊断服务请求,并在诊断响应结果区域内查看诊断请求响应结果。对于执行写功能的服务请求,可以在诊断请求设置区域内填入诊断服务要写入的参数值。
5.5 诊断测试面板
利用诊断可以对ECU的参数及信息进行读写和配置。对有诊断方面经验的工程师来说,诊断控制台可以满足大部分的需求。
对于没有诊断方面经验的工程师来说,利用面板可以更为直观地看到诊断执行结果以及参数。
创建一个诊断功能面板,主要作用是诊断功能测试、ECU状态的读取和部分ECU配置的修改。
5.5.1 创建Diagnosis系统变量
5.5.2 创建Diagnosis系统变量
创建面板,绑定诊断系统变量。
5.6 添加Tester节点
诊断面板上的功能是通过CAPL程序实现的。在Simulation Setup窗口的网络视图中,鼠标右键单击CAN1网络连线,选择Insert Network Node,并将其命名为Tester。
为了能够更好地支持面板中车辆信息的读取功能,将工程中的Automation功能停用,并通过CAPL程序实现立即解锁和切换引擎状态到RUN,使节点IPC、BCM、Gateway在测量开始时就进入正常工作状态。
此处Tester节点实现类似诊断仪的功能。该节点关联的CAPL程序主要是实现诊断请求的发送和诊断响应的处理。
面板代码:
/*@!Encoding:936*/includes
{}variables
{msTimer tester;
}void SendRequestAndCheckReturnvalue(diagRequest * req)
{//Function for sending specified diagnostic requestlong ret;ret=req.SendRequest();if(ret>=0) {write("Request was successfully added to the send queue.");}else {write("ERROR: Could not start sending the request");}
}void show_menu()
{//Information in write windowwrite(" ");write("IPC Diagnostics Tester");write("---------------------------------------------");write("Please select any service in Diagnostic Panel");write("---------------------------------------------");
}on start
{//Init the system show_menu();Init_ManufactureDate_Data();Init_SerialNumber_Data();Init_ECUVIN_Data();Init_ECUCoding_Data();Start_Vehicle_System();settimer(tester,200);
}void Start_Vehicle_System()
{//Start the vehicle system at the measurement start for diagnosis test@Vehicle_Key::Unlock_Car=1;@Vehicle_Key::Key_State=3;
}on timer tester
{//Get the Tester_Present status@Diagnosis::Tester_Present_Status=diagGetTesterPresentState("IPC");settimer(tester,200);
}void Init_ManufactureDate_Data()
{//Init display controlchar str[20]=" ";sysvarString * ECU_Manufacturing_Date_Data;sysSetVariableString(sysvar::Diagnosis::ECU_Manufacturing_Date_Data,str);sysSetVariableString(sysvar::Diagnosis::ECU_Manufacturing_Date_String,str);setControlColors("Diagnositc Panel", "Input/Output Box 4", MakeRGB(224,224,224), MakeRGB(0,0,0));
}void Init_SerialNumber_Data()
{//Init display controlbyte data[9];char str[20]=" ";sysvarData * ECU_Serial_Number_Data;sysSetVariableData(sysvar::Diagnosis::ECU_Serial_Number_Data,data,elCount(data)); sysSetVariableString(sysvar::Diagnosis::ECU_Serial_Number_Data_String,str);setControlColors("Diagnositc Panel", "Input/Output Box 5", MakeRGB(224,224,224), MakeRGB(0,0,0));
}void Init_ECUVIN_Data()
{//Init display controlbyte data[17];char str[20]=" ";sysvarData * ECU_VIN_Data;sysSetVariableData(sysvar::Diagnosis::ECU_VIN_Data,data,elCount(data)); sysSetVariableString(sysvar::Diagnosis::ECU_VIN_String,str);setControlColors("Diagnositc Panel", "Input/Output Box 6", MakeRGB(224,224,224), MakeRGB(0,0,0));
}void Init_ECUCoding_Data()
{//Init display controlchar str[20]=" ";sysSetVariableString(sysvar::Diagnosis::Variant_Coding_String,str);setControlColors("Diagnositc Panel", "Variant_Coding", MakeRGB(224,224,224), MakeRGB(0,0,0));
}on sysvar Diagnosis::ECU_Manufacturing_Date_Clean
{if(@this){Init_ManufactureDate_Data();}
}
on sysvar Diagnosis::ECU_VIN_Clean
{if(@this){Init_ECUVIN_Data();}
}
on sysvar Diagnosis::ECU_Serial_Number_Clean
{if(@this){Init_SerialNumber_Data();}
}/Manufacture Date Read/Write
on sysvar Diagnosis::ECU_Manufacturing_Date_Read
{//Send ECU_Manufacturing_Date_Read requestif(@this){diagRequest IPC.ECU_Manufacturing_Date_Read req;write("------ Reading The ECU Manufacturing Date ------");SendRequestAndCheckReturnvalue(req); }
}on diagResponse IPC.ECU_Manufacturing_Date_Read
{//Get and display the ECU_Manufacturing_Date_Read response databyte rawdata[6];char str1[3];char str2[3];char str3[3];char str[12];sysvarString * ECU_Manufacturing_Date_Data;sysvarString * ECU_Manufacturing_Date_String;if(this.IsPositiveResponse()){DiagGetPrimitiveData(this,rawdata,elCount(rawdata));ltoa(rawdata[3],str1,16);ltoa(rawdata[4],str2,16);ltoa(rawdata[5],str3,16);strncat(str,"20",3);strncat(str,str1,6);strncat(str,":",7);strncat(str,str2,9);strncat(str,":",10);strncat(str,str3,12);sysSetVariableString(sysvar::Diagnosis::ECU_Manufacturing_Date_Data,str);sysSetVariableString(sysvar::Diagnosis::ECU_Manufacturing_Date_String, "OK");setControlColors("Diagnositc Panel", "Input/Output Box 4", MakeRGB(51,255,0), MakeRGB(0,0,0));}else{sysSetVariableString(sysvar::Diagnosis::ECU_Manufacturing_Date_String, "NOK");setControlColors("Diagnositc Panel", "Input/Output Box 4", MakeRGB(255,0,0), MakeRGB(0,0,0));}
}on sysvar Diagnosis::ECU_Manufacturing_Date_Write
{//Send ECU_Manufacturing_Date_Write requestdiagRequest IPC.ECU_Manufacturing_Date_Write req;char str[12];dword year, month, day;sysvarString * ECU_Manufacturing_Date_Data; //Put the value to the diagnosis parameterif(@this){write("------ Write The ECU Manufacturing Date ------");sysGetVariableString(sysvar::Diagnosis::ECU_Manufacturing_Date_Data,str,elCount(str));strtoul(str,0,year);strtoul(str,5,month);strtoul(str,8,day);if(month>12){write("Month value is invalid, write function aborted");return;}if(day>31){write("Day value is invalid, write function aborted");return;}req.SetParameter("Manufacturing_date_Year",year);req.SetParameter("Manufacturing_date_Month",month);req.SetParameter("Manufacturing_date_Day",day);SendRequestAndCheckReturnvalue(req); }
}on diagResponse IPC.ECU_Manufacturing_Date_Write
{//Indicate whether the response is positive or negative if(this.IsPositiveResponse()){sysSetVariableString(sysvar::Diagnosis::ECU_Manufacturing_Date_String, "OK");setControlColors("Diagnositc Panel", "Input/Output Box 4", MakeRGB(51,255,0), MakeRGB(0,0,0)); }else{sysSetVariableString(sysvar::Diagnosis::ECU_Manufacturing_Date_String, "NOK");setControlColors("Diagnositc Panel", "Input/Output Box 4", MakeRGB(255,0,0), MakeRGB(0,0,0));}
}
/Manufacture Date Read/WriteSerial Number Read/Write///
on sysvar Diagnosis::ECU_Serial_Number_Read
{//Send ECU_Serial_Number_Read requestif(@this){diagRequest IPC.ECU_Serial_Number_Read req;write("------ Reading The ECU Serial Number ------");SendRequestAndCheckReturnvalue(req);}
}on diagResponse IPC.ECU_Serial_Number_Read
{//Get and display the ECU_Serial_Number_Read response dataBYTE parameter[9];sysvarData * ECU_Serial_Number_Data;sysvarData * ECU_Serial_Number_Data_String;if(this.IsPositiveResponse()){ECU_Serial_Number_Data=sysvar::Diagnosis::ECU_Serial_Number_Data;diagGetParameterRaw(this,"Serial_Number",parameter,elCount(parameter));sysSetVariableData(ECU_Serial_Number_Data,parameter,elCount(parameter));sysSetVariableString(sysvar::Diagnosis::ECU_Serial_Number_Data_String, "OK");setControlColors("Diagnositc Panel", "Input/Output Box 5", MakeRGB(51,255,0), MakeRGB(0,0,0));}else{sysSetVariableString(sysvar::Diagnosis::ECU_Serial_Number_Data_String, "NOK");setControlColors("Diagnositc Panel", "Input/Output Box 5", MakeRGB(255,0,0), MakeRGB(0,0,0));}
}on sysvar Diagnosis::ECU_Serial_Number_Write
{//Send ECU_Serial_Number_Write requestdiagRequest IPC.ECU_Serial_Number_Write reqRaw;sysvarData * ECU_Serial_Number_Data;BYTE parameter[9];long copiedbyte;long ret;//Put the value to the diagnosis parameterif(@this){ECU_Serial_Number_Data=sysvar::Diagnosis::ECU_Serial_Number_Data;sysGetVariableData(ECU_Serial_Number_Data,parameter,copiedbyte);ret=reqRaw.SetParameterRaw("Serial_Number",parameter,elCount(parameter));if(ret==0){SendRequestAndCheckReturnvalue(reqRaw);}elsewrite("ERROR: Could not set parameter");}
}on diagResponse IPC.ECU_Serial_Number_Write
{//Indicate whether the response is positive or negative if(this.IsPositiveResponse()){sysSetVariableString(sysvar::Diagnosis::ECU_Serial_Number_Data_String, "OK");setControlColors("Diagnositc Panel", "Input/Output Box 5", MakeRGB(51,255,0), MakeRGB(0,0,0)); }else{sysSetVariableString(sysvar::Diagnosis::ECU_Serial_Number_Data_String, "NOK");setControlColors("Diagnositc Panel", "Input/Output Box 5", MakeRGB(255,0,0), MakeRGB(0,0,0));}
}
Serial Number Read/Write//////Vehicle Identification Number Read/Write
on sysvar Diagnosis::ECU_VIN_Read
{//Send Vehicle_Identification_Number_Read requestdiagRequest IPC.Vehicle_Identification_Number_Read req;write("------ Reading The ECU Identification Number ------");SendRequestAndCheckReturnvalue(req);
}on diagResponse IPC.Vehicle_Identification_Number_Read
{//Get and display the Vehicle_Identification_Number_Read response dataBYTE parameter[17];sysvarData * ECU_VIN_Data;if(this.IsPositiveResponse()){ECU_VIN_Data=sysvar::Diagnosis::ECU_VIN_Data;diagGetParameterRaw(this,"VIN",parameter,elCount(parameter));sysSetVariableData(ECU_VIN_Data,parameter,elCount(parameter));sysSetVariableString(sysvar::Diagnosis::ECU_VIN_String, "OK");setControlColors("Diagnositc Panel", "Input/Output Box 6", MakeRGB(51,255,0), MakeRGB(0,0,0));}else{sysSetVariableString(sysvar::Diagnosis::ECU_VIN_String, "NOK");setControlColors("Diagnositc Panel", "Input/Output Box 6", MakeRGB(255,0,0), MakeRGB(0,0,0));}
}on sysvar Diagnosis::ECU_VIN_Write
{//Send Vehicle_Identification_Number_Write requestdiagRequest IPC.Vehicle_Identification_Number_Write reqRaw;sysvarData * ECU_VIN_Data;BYTE parameter[17];long copiedbyte;long ret;//Put the value to the diagnosis parameterif(@this){ECU_VIN_Data=sysvar::Diagnosis::ECU_VIN_Data;sysGetVariableData(ECU_VIN_Data,parameter,copiedbyte);ret=reqRaw.SetParameterRaw("VIN",parameter,elCount(parameter));if(ret==0){SendRequestAndCheckReturnvalue(reqRaw);}elsewrite("ERROR: Could not set parameter");}
}on diagResponse IPC.Vehicle_Identification_Number_Write
{//Indicate whether the response is positive or negative if(this.IsPositiveResponse()){sysSetVariableString(sysvar::Diagnosis::ECU_VIN_String, "OK");setControlColors("Diagnositc Panel", "Input/Output Box 6", MakeRGB(51,255,0), MakeRGB(0,0,0)); }else{sysSetVariableString(sysvar::Diagnosis::ECU_VIN_String, "NOK");setControlColors("Diagnositc Panel", "Input/Output Box 6", MakeRGB(255,0,0), MakeRGB(0,0,0)); }
}
///ECU Identification Number Read/Write//Variant Coding Read/Write
on sysvar Diagnosis::Variant_Coding_Read
{//Send Variant_Coding_Read requestdiagRequest IPC.Variant_Coding_Read req;write("------ Reading The Variant Coding ------");SendRequestAndCheckReturnvalue(req);
}on diagResponse IPC.Variant_Coding_Read
{//Get and display the Variant_Coding_Read response dataif(this.IsPositiveResponse()){@Diagnosis::Country_Code=this.GetParameter("Coding_String.Country_Code");@Diagnosis::Vehicle_Type=this.GetParameter("Coding_String.Vehicle_Type");@Diagnosis::Belt_Warning=this.GetParameter("Coding_String.Belt_Warning");@Diagnosis::OverSpeed_Warning=this.GetParameter("Coding_String.OverSpeed_Warning");sysSetVariableString(sysvar::Diagnosis::Variant_Coding_String, "OK");setControlColors("Diagnositc Panel", "Input/Output Box 7", MakeRGB(51,255,0), MakeRGB(0,0,0)); }else{sysSetVariableString(sysvar::Diagnosis::Variant_Coding_String, "NOK");setControlColors("Diagnositc Panel", "Input/Output Box 7", MakeRGB(255,0,0), MakeRGB(0,0,0));}
}on sysvar Diagnosis::Variant_Coding_Write
{//Send Variant_Coding_Write requestdiagRequest IPC.Variant_Coding_Write reqRaw;double country_code,vehicle_type,beltwarning,overspeedwarning;long ret;country_code=@Diagnosis::Country_Code;vehicle_type=@Diagnosis::Vehicle_Type;beltwarning=@Diagnosis::Belt_Warning;overspeedwarning=@Diagnosis::OverSpeed_Warning;//Put the value to the diagnosis parameterif(@this){reqRaw.SetParameter("Coding_String.Country_Code",country_code);reqRaw.SetParameter("Coding_String.Vehicle_Type",vehicle_type);reqRaw.SetParameter("Coding_String.Belt_Warning",beltwarning);reqRaw.SetParameter("Coding_String.OverSpeed_Warning",overspeedwarning);SendRequestAndCheckReturnvalue(reqRaw);}
}on diagResponse IPC.Variant_Coding_Write
{//Indicate whether the response is positive or negative if(this.IsPositiveResponse()){sysSetVariableString(sysvar::Diagnosis::Variant_Coding_String, "OK");setControlColors("Diagnositc Panel", "Input/Output Box 7", MakeRGB(51,255,0), MakeRGB(0,0,0)); }else{sysSetVariableString(sysvar::Diagnosis::Variant_Coding_String, "NOK");setControlColors("Diagnositc Panel", "Input/Output Box 7", MakeRGB(255,0,0), MakeRGB(0,0,0)); }
}
//Variant Coding Read/Write
5.7 虚拟诊断响应
对节点IPC的诊断是针对虚拟节点,因此,对于诊断请求的响应也需要在节点IPC中使用CAPL程序模拟(若不实现CAPL模拟,节点仅回复肯定响应)。节点IPC实现了对诊断请求的响应(肯定响应或否定响应)以及诊断响应中参数的设置等功能。
在IPC节点中诊断响应和响应参数的设置。
/*@!Encoding:1252*/
includes
{//To add the head files
}variables
{enum ESession {DefaultSession, ExtendedSession, ProductionSession};enum ESecurityLevel {Lock, Unlock_1, Unlock_2};int CURRENT_SESSION;int CURRENT_SECURITY_LEVEL;byte SESSION_TIMING_P2[2] = {0x00, 0x96};byte SESSION_TIMING_PX[2] = {0x00, 0xC8};byte MANUFACTURING_DATE[3] = {18, 10, 18};byte SERIAL_NUMBER[9] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};byte VEHICLE_IDENTIFICATION_NUMBER[17] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39};byte Speedbuffer[2],EngTempbuffer[1];byte SECURITY_SEED[4];byte SECURITY_KEY[4]; byte SECURITY_KEY_RECEIVED[4];double countrycode=0,vehicletype=3,beltwarning=0,overspeedwarning=0;int busflag=0; //current bus status: 0 - Deactivate CANoe IL ; 1 - Activate CANoe IL //To add the variables
}///Function/
on preStart
{ILControlInit(); //Initialize CANoe IL ILControlStop(); //Deactivate CANoe IL
}on start
{CURRENT_SESSION=DefaultSession;
}on signal_update LockStatus
{if(this!=busflag){if(this==1){ILControlStart(); //Activate CANoe IL }else if(this==0){ILControlStop(); //Deactivate CANoe IL }busflag=this;}
}on signal_update Gear
{@Cluster::Gear_Status= this; //To display current Gear status in panel
}
///Function///Diagnosis///Session///
on diagRequest IPC.DefaultSession_Start
{//Set parameter and response for default session requestdiagResponse this resp;resp.SetParameterRaw("P2",SESSION_TIMING_P2,elCount(SESSION_TIMING_P2));resp.SetParameterRaw("P2Ex",SESSION_TIMING_PX,elCount(SESSION_TIMING_PX));CURRENT_SESSION=DefaultSession;CURRENT_SECURITY_LEVEL=Lock;@Diagnosis::Security_Level=0;@Diagnosis::Session=0;resp.SendPositiveResponse();
}on diagRequest IPC.ExtendedDiagnosticSession_Start
{//Set parameter and response for extended session requestdiagResponse this resp;resp.SetParameterRaw("P2",SESSION_TIMING_P2,elCount(SESSION_TIMING_P2));resp.SetParameterRaw("P2Ex",SESSION_TIMING_PX,elCount(SESSION_TIMING_PX));CURRENT_SESSION=ExtendedSession;@Diagnosis::Session=1;resp.SendPositiveResponse();
}on diagRequest IPC.Production_Session_Start
{//Set parameter and response for production session requestdiagResponse this resp;//Judge the positive request conditionif(CURRENT_SESSION==ExtendedSession){resp.SetParameterRaw("P2",SESSION_TIMING_P2,elCount(SESSION_TIMING_P2));resp.SetParameterRaw("P2Ex",SESSION_TIMING_PX,elCount(SESSION_TIMING_PX));CURRENT_SESSION=ProductionSession;@Diagnosis::Session=2;@Diagnosis::Security_Level=0;CURRENT_SECURITY_LEVEL=Lock;resp.SendPositiveResponse(); }elseresp.SendNegativeResponse(0x7E); }
//Session////Manufacture Date Read/Write
on diagRequest IPC.ECU_Manufacturing_Date_Read
{//Set parameter and response for ECU_Manufacturing_Date_Read requestdiagResponse this resp;//Judge the positive request conditionif(@Diagnosis::Manufacture_Date_NRC==1){resp.SendNegativeResponse(0x22);}else{resp.SetParameter("Manufacturing_date_Year",MANUFACTURING_DATE[0]);resp.SetParameter("Manufacturing_date_Month",MANUFACTURING_DATE[1]);resp.SetParameter("Manufacturing_date_Day",MANUFACTURING_DATE[2]);resp.SendPositiveResponse();}
}on diagRequest IPC.ECU_Manufacturing_Date_Write
{//Judge the positive request condition for ECU_Manufacturing_Date_Write requestdiagResponse this resp;if(CURRENT_SECURITY_LEVEL==Unlock_1){resp.SendPositiveResponse(); }elseresp.SendNegativeResponse(0x33);
}
/Manufacture Date Read/WriteSerial Number Read/Write///
on diagRequest IPC.ECU_Serial_Number_Read
{//Set parameter and response for ECU_Serial_Number_Read requestdiagResponse this resp;if(@Diagnosis::Serial_Number_NRC){resp.SendNegativeResponse(0x22);}else{resp.SetParameterRaw("Serial_Number",SERIAL_NUMBER,elCount(SERIAL_NUMBER));resp.SendPositiveResponse(); } }on diagRequest IPC.ECU_Serial_Number_Write
{diagResponse this resp;if(CURRENT_SECURITY_LEVEL==Unlock_1){this.GetParameterRaw("Serial_Number",SERIAL_NUMBER,elCount(SERIAL_NUMBER));resp.sendPositiveResponse(); }elseresp.SendNegativeResponse(0x33);
}
Serial Number Read/Write//////ECU Identification Number Read/Write
on diagRequest IPC.Vehicle_Identification_Number_Read
{//Set parameter and response for Vehicle_Identification_Number_Read requestdiagResponse this resp;if(CURRENT_SECURITY_LEVEL==Unlock_1){if(@Diagnosis::Identification_Number_NRC){resp.SendNegativeResponse(0x22);}else{resp.SetParameterRaw("VIN",VEHICLE_IDENTIFICATION_NUMBER,elCount(VEHICLE_IDENTIFICATION_NUMBER));resp.SendPositiveResponse(); }}elseresp.SendNegativeResponse(0x33);
}on diagRequest IPC.Vehicle_Identification_Number_Write
{diagResponse this resp;if(CURRENT_SECURITY_LEVEL==Unlock_2){this.GetParameterRaw("VIN",VEHICLE_IDENTIFICATION_NUMBER,elCount(VEHICLE_IDENTIFICATION_NUMBER));resp.sendPositiveResponse();}elseresp.SendNegativeResponse(0x33);
}
///ECU Identification Number Read/Write//Variant Coding Read/Write
on diagRequest IPC.Variant_Coding_Read
{//Set parameter and response for Variant_Coding_Read requestdiagResponse this resp;if(@Diagnosis::Variant_Coding_NRC){resp.SendNegativeResponse(0x22); }else{resp.SetParameter("Coding_String.Country_Code",countrycode);resp.SetParameter("Coding_String.Vehicle_Type",vehicletype);resp.SetParameter("Coding_String.Belt_Warning",beltwarning);resp.SetParameter("Coding_String.OverSpeed_Warning",overspeedwarning);resp.sendPositiveResponse(); }
}on diagRequest IPC.Variant_Coding_Write
{diagResponse this resp;if(CURRENT_SECURITY_LEVEL==Unlock_1){countrycode=this.GetParameter("Coding_String.Country_Code");vehicletype=this.GetParameter("Coding_String.Vehicle_Type");beltwarning=this.GetParameter("Coding_String.Belt_Warning");overspeedwarning=this.GetParameter("Coding_String.OverSpeed_Warning");resp.sendPositiveResponse(); }elseresp.SendNegativeResponse(0x33);}
//Variant Coding Read/Write//DID//
on message EngineData
{EngTempbuffer[0]=this.byte(0)*2;Speedbuffer[1]=this.byte(2);Speedbuffer[0]=this.byte(3);
}on diagRequest IPC.DID_Engine_Speed_Read
{//Set parameter and response for DID_Engine_Speed_Read requestdiagResponse this resp;resp.SetParameterRaw("DID_Engine_Speed",Speedbuffer,elcount(Speedbuffer));resp.SendPositiveResponse();
}on diagRequest IPC.DID_Engine_Temperature_Read
{//Set parameter and response for DID_Engine_Temperature_Read requestdiagResponse this resp;resp.SetParameterRaw("DID_Engine_Temperature",EngTempbuffer,elcount(EngTempbuffer));resp.SendPositiveResponse();
}on diagRequest IPC.Power_Supply_Voltage_Read
{//Set parameter and response for Power_Supply_Voltage_Read requestdiagResponse this resp;resp.SetParameter("DataRecord",@Diagnosis::Power_Supply_Voltage);resp.SendPositiveResponse();
}
//DID///Active Session
on diagRequest IPC.Active_Diagnostic_Session_Read
{//Set parameter and response for Active_Diagnostic_Session_Read requestdiagResponse this resp;switch (CURRENT_SESSION){case DefaultSession:{resp.SetParameter("Diagnostic_Session_Type",0x01);break;}case ExtendedSession:{resp.SetParameter("Diagnostic_Session_Type",0x03);break;}case ProductionSession:{resp.SetParameter("Diagnostic_Session_Type",0x02);break;}}resp.SendPositiveResponse();
}
/Active Session//Security/
on diagRequest IPC.SeedLevel1_Request
{//Generate level1 security key and send responsediagResponse this resp;if(CURRENT_SESSION==ExtendedSession){Seed_Generation();KeyLevel1_Calculate();resp.SetParameterRaw("SecuritySeed",SECURITY_SEED,elcount(SECURITY_SEED));resp.SendPositiveResponse(); }else resp.SendNegativeResponse(0x7E);//Sub function not supported in current session
}on diagRequest IPC.KeyLevel1_Send
{//Varify the level1 security key diagResponse this resp;this.GetParameterRaw("SecurityKey",SECURITY_KEY_RECEIVED,elCount(SECURITY_KEY_RECEIVED));if(KEY_VERIFY()==1){resp.SendPositiveResponse();CURRENT_SECURITY_LEVEL=Unlock_1;@Diagnosis::Security_Level=1;}elseresp.SendNegativeResponse(0x35);//Invalid key
}on diagRequest IPC.Seed_Level_2_Request
{//Generate level2 security key and send responsediagResponse this resp;if(CURRENT_SECURITY_LEVEL==Unlock_1){Seed_Generation();KeyLevel2_Calculate();resp.SetParameterRaw("SecuritySeed",SECURITY_SEED,elcount(SECURITY_SEED));resp.SendPositiveResponse(); }elseresp.SendNegativeResponse(0x33);//Security access denied}on diagRequest IPC.Key_Level_2_Send
{//Varify the level2 security key diagResponse this resp;this.GetParameterRaw("SecurityKey",SECURITY_KEY_RECEIVED,elCount(SECURITY_KEY_RECEIVED));if(KEY_VERIFY()==1){resp.SendPositiveResponse();CURRENT_SECURITY_LEVEL=Unlock_2;@Diagnosis::Security_Level=2;}elseresp.SendNegativeResponse(0x35);
}void Seed_Generation()
{//Generate seedint i;byte temp;for(i = 0; i < 4; i++){temp = (int)random(250);SECURITY_SEED[i] = temp;SECURITY_KEY[i] = temp;}
}void KeyLevel1_Calculate()
{//Calculate the key for security leve1 1byte temp;temp = SECURITY_KEY[0];SECURITY_KEY[0] = SECURITY_KEY[1];SECURITY_KEY[1] = temp;
}void KeyLevel2_Calculate()
{//Calculate the key for security leve1 1byte temp;temp = SECURITY_KEY[1];SECURITY_KEY[1] = SECURITY_KEY[2];SECURITY_KEY[2] = temp;
}int KEY_VERIFY()
{//Verify the security key;int i;for (i = 0; i < 4; i++){if(SECURITY_KEY[i] != SECURITY_KEY_RECEIVED[i])return -1;}return 1;
}
//Security//Conmmunication Control
on diagRequest IPC.EnableRxAndDisableTx_Control
{//Send negative response for EnableRxAndDisableTx_Control requestdiagResponse this resp;resp.SendNegativeResponse(0x12);
}on diagRequest IPC.EnableRxAndEnableTx_Control
{//Send negative response for EnableRxAndEnableTx_Control requestdiagResponse this resp;resp.SendNegativeResponse(0x12);
}
/Conmmunication Control//Hard Reset///
on diagRequest IPC.HardReset_Reset
{//Send negative response for HardReset_Reset requestdiagResponse this resp;resp.SendNegativeResponse(0x12);
}//Hard Reset/////Diagnosis/
5.8 自动化诊断测试方法分析
手动诊断测试的实现,在实际的诊断测试过程中,对时间参数有十分严格的要求(如响应时间、诊断报文的最小发送间隔
等),因此单纯的手动测试往往无法完全满足测试的需求,这时诊断测试的自动化实现显得尤为重要。
首先,查阅规范IPC_v0.01.pdf文档制定测试流程。可以看到Vehicle Identification Number的数据格式为17B的ASCII码。再查阅Session control, VIN码的读写都必须在Extended Session下执行。
最后,检查VIN码安全访问控制。VIN码的读需要security level 1解锁,写则需要security level 2解锁,而security level 2则需要在security level 1解锁的基础上解锁。
下面是诊断测试流程。
5.9 CAPL诊断测试模块实现
1.基本诊断通信
请求和响应是诊断的基本要素,在CAPL中发送诊断请求并得到诊断响应则是测试的最基本步骤。
此 处 声 明 了 一 个 diagRequest 变 量 , 通 过 long diagSendRequest ( diagRequest obj ) 函 数 将 其 发 送 出 去 。
IPC.ExtendedDiagnosticSession_Start是开始执行CDD文件中定义好的ExtendedDiagnosticSession。
当用户输入“IPC.”之后,CANoe会自 动 过 滤 出 IPC 支 持 的 诊 断 服 务 供 用 户 选 择 。 此 处 将 切 换 到ExtendedDiagnosticSession作为诊断请求。
当诊断请求发送后,CANoe已经被动接收了诊断响应,如果有响应 的 话 , 用 户 只 需 要 去 获 取 响 应 的 数 据 。 此 处 使 用 long diagGetLastResponseCode(diagRequest req)来判断响应的类型,结构如下。
需要注意的是,诊断命令的发送和接收都是需要时间的,如果诊断请求发送后立即验证响应的类型,很可能此时响应还未完成。
TFS提供了两个测试函数来设定发送和响应的等待时间。
timeout应不大于CDD文件中规定的时间间隔,以达到测试目的。
2.安全访问解锁
根据诊断描述,执行VIN码的读写操作都需要解锁相对应的安全等级。
(1)请求安全访问种子(Seed)。
SeedRequest 的 发 送 与 基 本 的 诊 断 请 求 发 送 一 致 , 也 使 用diagSendRequest函数。再使用diagGetRespPrimitiveByte函数将安全种子从诊断响应中取出。该函数返回响应中bytePos位置的数据,循环结构如下
(2)通过Seed&Key dll计算出密钥(Key)。
CANoe提供diagGenerateKeyFromSeed函数来调用dll计算出密钥,当返回值为0时,密钥计算成功。具体实现如下。
(3)发送密钥。
可以通过diagSetParameterRaw函数将密钥赋值给诊断请求,再将KeySend请求发送出去,并期待得到肯定响应。
3.General CAPL test case编写
需要注意的是,诊断测试对每个测试步骤要求严格,因此在编写CAPL测试用例时,应尽量多添加必要的说明信息,以增加报告的可读性。CANoe已经提供了一系列的函数用以增强报告的可读性,这里主要介绍以下几种。
4.CAPL测试模块
添加 CAPL的测试文件
双击打开测试窗口。
5.10 制作GenerateKey.dll
在实际项目中,整车厂往往只会提供SecurityAccess的算法,而具体的GenerateKey.dll则需要ECU供应商自己实现。
CAPL通过调用diagGenerateKeyFromSeed函数来生成key,而该w函数则是调用GenerateKey.dll去计算key。
在实际应用中,整车厂很可能不会直接提供算法的源码,而是提供封装好的库,这些库往往是面向多个产品的。这个时候就需要使用ipOption参数传递对应的产品号。
6.工程现象
手动诊断现象:
自动测试: