CANoe教程:CAPL编程
?
CANoe教程 | CAPL編程 - 數(shù)據(jù)類型
CAPL是一種類C語言,CAPL數(shù)據(jù)類型的定義很多C語言類似,但也有很多獨(dú)特的地方。 CAPL數(shù)據(jù)類型包括基本類型、結(jié)構(gòu)體、枚舉、關(guān)聯(lián)類型和對(duì)象類型。變量的數(shù)據(jù)類型決定了變量存儲(chǔ)占用的空間。
1 基本類型
2 枚舉
枚舉變量的定義和使用同C語言:
enum State { State_Off = -1, State_On = 1 };如果枚舉成員的值未定義,那么第一個(gè)成員默認(rèn)值為1,之后的成員按順序依次加1.?
枚舉變量的定義和使用:
variables {enum { Apple, Pear, Banana } fruit = Apple;enum Colors { Red, Green, Blue };enum Colors color; }enum Colors NextColor(enum Colors c) {if (c == Blue) return Red;else return (enum Colors) (c + 1); }3 關(guān)聯(lián)類型
CAPL支持一種類似Python字典和C++ Map的關(guān)聯(lián)類型(Associative Fields),關(guān)聯(lián)類型的元素是鍵值對(duì)(key value pairs)。 關(guān)聯(lián)類型定義格式如下,左邊是value類型,右邊[ ]內(nèi)是key類型:
int m[float]; // maps floats to ints float x[int64]; // maps int64s to floats char[30] s[ char[] ] // maps strings (of unspecified length) to strings of length < 30example 1:關(guān)聯(lián)浮點(diǎn)型
float m[float]; m[4.1] = 5.5; //key is 4.1 (float) and value is 5.5 (float) m[5.3] = 6.6; write ("4.1 is mapped to %2.2lf", m[4.1]); write ("5.3 is mapped to %2.2lf", m[5.3]); for (float mykey : m) {write("%2.2lf is mapped to %2.2lf.", mykey, m[mykey]); }example 2:關(guān)聯(lián)字符串
char[30] namen[char []]; strncpy(namen["Max"], "Mustermann", 30); strncpy(namen["Vector"], "Informatik", 30);for (char[] mykey : namen) {write("%s is mapped to %s", mykey, namen[mykey]); }4 結(jié)構(gòu)體
結(jié)構(gòu)的定義和使用同C:
variables {struct Point{int x;int y;};struct Point myPoint;struct Point allPoints[50]; } on start {myPoint.x = 7;myPoint.y = 2;allPoints[3].x = 1;allPoints[3].y = 5; }注意: CAPL中結(jié)構(gòu)體默認(rèn)按8字節(jié)對(duì)齊,可以在結(jié)構(gòu)體定義前加_align來改變結(jié)構(gòu)體對(duì)齊方式。
example:
struct Point { // note: default _align(8) byte x; // offset 0, size 1 byte y; // alignment 1, offset 1, size 1, padding before: 0 }; // size 2, alignment (of the struct) 1struct LongPoint { // note: default _align(8) byte x; // offset 0, size 1 qword y; // alignment 8, offset 8, size 8, padding before: 7 }; // size 16, alignment (of the struct) 8_align(2) struct Point2 { byte x; // offset 0, size 1, (alignment 1) qword y; // alignment 2, offset 2, size 8, padding before: 1 }; // size 10, alignment (of the struct) 2struct Points { // note: _align(8) per default struct Point p1; // offset 0, size 2, (alignment 1) byte x; // alignment 1, offset 2, size 1, padding before: 0 struct Point2 p2; // alignment 2, offset 4, size 10, padding before: 1 }; // size 14, alignment (of the struct) 2可以使用如下函數(shù)獲取結(jié)構(gòu)體大小(size)、對(duì)齊方式(alignment )和偏移量(offset )信息:
example:
struct Points { // note: _align(8) per default Point p1; // offset 0, size 2, (alignment 1) byte x; // alignment 1, offset 2, size 1, padding before: 0 Point2 p2; // alignment 2, offset 4, size 10, padding before: 1 }; // size 14, alignment (of the struct) 2__size_of(struct Points); // returns 14 __alignment_of(struct Points); // returns 2 __offset_of(struct Points, p1); // returns 0 __offset_of(struct Points, x); // returns 2 __offset_of(struct Points, p2); // returns 45 對(duì)象類型
除了以上介紹的基礎(chǔ)數(shù)據(jù)類型,CAPL還提供了一些CANoe特有的對(duì)象類型來幫助用戶快速完成仿真測(cè)試功能的開發(fā)。
CAN messages
CAPL提供了各種網(wǎng)絡(luò)對(duì)應(yīng)的報(bào)文類。本文以CAN message為例進(jìn)行介紹。 報(bào)文變量定義格式:
message + message ID/message name + variable使用message關(guān)鍵字來聲明一個(gè)報(bào)文變量,message后是message ID或CANoe工程導(dǎo)入DBC后的message name,然后是在CAPL程序中要使用的報(bào)文變量名。
message 0xA m1; //定義一個(gè)ID為0xA的報(bào)文變量m1 message 100x m2; //定義一個(gè)ID為100的擴(kuò)展幀報(bào)文變量m2,ID后的x后綴表示這是一個(gè)擴(kuò)展幀 message EngineData m3; //定義一個(gè)在DBC中message name為EngineData的報(bào)文變量m3 ... output(m1); output(m2); output(m3);CAPL提供了一系列的選擇器(Selectors)來設(shè)置或讀取CAN message的屬性,例如:
?
example:
message 0x100 msg; //定義一個(gè)ID為0x100的message變量msg msg.CAN = 1; //將msg的通道設(shè)置為1 msg.DLC = 2; //將msg的DLC設(shè)置為2 msg.BYTE(0) = 0xAA; //給msg報(bào)文數(shù)據(jù)段的第一個(gè)byte賦值為0xAA; msg.BYTE(1) = 0xBB; //給msg報(bào)文數(shù)據(jù)段的第二個(gè)byte賦值為0xBB; output(msg); //將定義好的msg發(fā)送到總線中?6 定時(shí)器變量
CAPL提供兩種定時(shí)器變量: timer:基于秒(s)的定時(shí)器 msTimer:基于毫秒(ms)的定時(shí)器 example:點(diǎn)擊鍵盤'a'后以20ms為周期發(fā)送id為100的報(bào)文
msTimer myTimer; //定義一個(gè)ms定時(shí)器myTimer message 100 msg; ... on key 'a' {setTimer(myTimer,20); //點(diǎn)擊鍵盤'a'將定時(shí)器myTimer設(shè)置為20ms,并開始計(jì)時(shí) } ... on timer myTimer { //響應(yīng)定時(shí)器事件myTimer,將msg發(fā)送到總線,output(msg);setTimer(myTimer,20); //重新設(shè)置定時(shí)器myTimer為20ms }CANoe教程 | CAPL編程-運(yùn)算符/流程控制
CAPL中算數(shù)運(yùn)算符、邏輯運(yùn)算符、位運(yùn)算以及流程控制語句和C語言一致。?
1 運(yùn)算符
2 流程控制
?
CANoe教程 | CAPL編程 - 事件驅(qū)動(dòng)
1 事件概述
CAPL是一種面向過程、由事件驅(qū)動(dòng)的類C語言。
事件驅(qū)動(dòng)針對(duì)于順序執(zhí)行,其區(qū)別如下:
順序執(zhí)行:順序執(zhí)行流程中,子例程或過程函數(shù)按照代碼編寫順序逐句執(zhí)行。
事件驅(qū)動(dòng):CAPL程序由事件驅(qū)動(dòng),工程運(yùn)行過程中發(fā)生指定的事件時(shí)才會(huì)運(yùn)行相應(yīng)的事件處理函數(shù)。
順序執(zhí)行VS事件驅(qū)動(dòng)
在CAPL中添加事件處理函數(shù):?
重要的事件處理函數(shù):
事件總覽:?[3]
2 事件詳解
事件起始關(guān)鍵字 on
on后加某種事件,工程運(yùn)行時(shí)條件觸發(fā),則執(zhí)行函數(shù)體內(nèi)的語句。
關(guān)鍵字this
系統(tǒng)變量、環(huán)境變量或CAN報(bào)文事件中,可以用this關(guān)鍵字訪問其指代的數(shù)據(jù)內(nèi)容,如:
on envvar Switch { // Declare a CAN message to be transmitteed message Controller msg;// Read out the value of the switch // Assign to the signal Stop msg.Stop = getvalue(this); // Output the message on the bus output(msg); }系統(tǒng)事件
系統(tǒng)事件主要用于處理CANoe測(cè)量系統(tǒng)的控制功能,主要有on start、on preStart、onstopMeasurement、on preStop、on key<newKey>以及on timer
系統(tǒng)事件Example:
CAN控制器事件
當(dāng)CAN控制器或錯(cuò)誤計(jì)數(shù)器狀態(tài)變化時(shí)調(diào)用CAN控制器事件。
CAN控制器事件Example:
//on errorPassive procedure on errorPassive {...write("CAN Controller is in errorPassive state")write(" errorCountTX = %d", this.errorCountTX);write(" errorCountRX = %d", this.errorCountRX); };//on busOff procedure on 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報(bào)文/信號(hào)事件
CAN報(bào)文或信號(hào)變化時(shí)調(diào)用報(bào)文/信號(hào)事件。
CAN報(bào)文/信號(hào)事件
報(bào)文事件:
信號(hào)事件:
on signal LightSwitch::OnOff {v1 = this.raw;v2 = $LightSwitch::OnOff.raw; }定時(shí)器事件
定義好定時(shí)器變量后,由SetTimer函數(shù)設(shè)置定時(shí)器間隔并啟動(dòng)定時(shí)器。當(dāng)定時(shí)器運(yùn)行到設(shè)定的時(shí)間間隔時(shí)觸發(fā)定時(shí)器事件,并執(zhí)行on timer函數(shù)體中的程序。
msTimer myTimer; message 100 msg; ... on key 'a' {setTimer(myTimer,20); } ... on timer myTimer {output(msg); }鍵盤事件
通過定義鍵盤事件,用戶可以在工程運(yùn)行時(shí)通過點(diǎn)擊鍵盤觸發(fā)預(yù)先定義的行為。這在實(shí)際開發(fā)和測(cè)試時(shí)非常常用。比如用戶可以在任意時(shí)刻向總線發(fā)送特定的消息、改變信號(hào)或系統(tǒng)變量的值或是啟動(dòng)停止測(cè)量。
系統(tǒng)變量/環(huán)境變量事件
系統(tǒng)變量和環(huán)境變量事件分別是對(duì)系統(tǒng)變量和環(huán)境變量發(fā)生變化時(shí)的響應(yīng)。
系統(tǒng)變量事件:
on sysvar IO::DI_0 { $Gateway::IOValue = @this; }環(huán)境變量事件
on envvar Switch { // Declare a CAN message to be transmitteed message Controller msg;// Read out the value of the switch // Assign to the signal Stop msg.Stop = getvalue(this); // Output the message on the bus output(msg); }?
CANoe教程 | CAPL編程 - 實(shí)用CAPL代碼片段
本文根據(jù)CAPL編程中經(jīng)常遇到的案例場(chǎng)景整理簡(jiǎn)潔通用的代碼片段。
1 周期消息發(fā)送
無論是Simulation Setup中的仿真節(jié)點(diǎn)還是Test Setup中的Test Module所關(guān)聯(lián)的CAPL腳本在做仿真或測(cè)試時(shí)都經(jīng)常需要向總線模擬發(fā)送周期消息。
點(diǎn)擊鍵盤按鍵 'a' 后向總線發(fā)送周期為20ms的can 消息msg:
variables { msTimer myTimer;message 100 msg; } on key 'a' {setTimer(myTimer,20); } on timer myTimer {output(msg);setTimer(myTimer,20); }2 應(yīng)用報(bào)文Request/Response測(cè)試
ECU通常都有很多請(qǐng)求/應(yīng)答式的功能,比如BCM可以接收用戶點(diǎn)擊車窗、雨刮、遮陽簾等車身相關(guān)硬件的控制按鈕向總線發(fā)出的開關(guān)請(qǐng)求(Request),然后由BCM向總線發(fā)出響應(yīng)消息,并控制車窗、雨刮、遮陽簾等做出相應(yīng)的反饋動(dòng)作(Response)。
下面以測(cè)試BCM雨刮開關(guān)功能為例進(jìn)行Request/Response測(cè)試。
DBC定義:
| WiperRequest | BCM_Request | Off : 0 On : 1 |
| WiperResponse | BCM_Response | Off : 0 On : 1 |
參考代碼:
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); //測(cè)試雨刮關(guān)閉功能Check_Wiper_Feature(1,1); //測(cè)試雨刮開啟功能TestGroupEnd(); } //Wiper Feature testcase testcase Check_Wiper_Feature(int request, int response ) {tBCM_Request.WiperRequest.phys = request;output(tBCM_Request);//測(cè)試請(qǐng)求發(fā)出去后1000ms內(nèi)是否收到BCM的響應(yīng)信號(hào)。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 檢測(cè)總線中周期報(bào)文的發(fā)送周期是否在給定范圍內(nèi)
TSL提供了兩組函數(shù)用于測(cè)試周期報(bào)文:
一組使用相對(duì)時(shí)間因子,當(dāng)周期小于 (aMinRelCycleTime * GenMsgCycleTime)或大于(aMaxRelCycleTime* GenMsgCycleTime)時(shí)產(chǎn)生事件。
函數(shù)原型:
dword ChkCreate_MsgRelCycleTimeViolation (Message aObservedMessage, double aMinRelCycleTime, double aMaxRelCycleTime, Callback aCallback);dword ChkStart_MsgRelCycleTimeViolation (Message aObservedMessage, double aMinRelCycleTime, double aMaxRelCycleTime, Callback aCallback);另一組使用絕對(duì)時(shí)間參數(shù),當(dāng)周期小于 aMinCycleTime 或大于 aMaxCycleTime 時(shí)產(chǎn)生事件。
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 messagegCycCheckId = ChkStart_MsgRelCycleTimeViolation (EngineData, aMinRelCycleTime , aMaxRelCycleTime );TestAddCondition(gCycCheckId);// sequence of different actions and waiting conditionsTestWaitForTimeout(1000);TestRemoveCondition(gCycCheckId); }測(cè)試報(bào)告中設(shè)置的命令如下,請(qǐng)您自行查閱CANoe幫助文檔,或者查找自帶的模板。
TestModuleTitle ("Test BCM Features");\\測(cè)試報(bào)告標(biāo)題。TestModuleDescription ("Check all features in BCM.");\\測(cè)試報(bào)告描述。輸出的測(cè)試報(bào)告如下圖所示:
如上圖所示,測(cè)試報(bào)告展示了錯(cuò)誤事件產(chǎn)生的次數(shù)以及錯(cuò)誤事件所處的事件范圍。
4 統(tǒng)一診斷測(cè)試(UDS)
診斷測(cè)試經(jīng)常需要進(jìn)行切換session,22/2E讀寫等request/response式的操作,CANoe Demo工程UDSBasic.cfg中Simulation Setup窗口里的TestModule節(jié)點(diǎn)關(guān)聯(lián)的CAPL腳本為我們提供了一個(gè)很好的參考模板:
參考代碼:
/*@!Encoding:1252*/ // -------------------------------------------------- // Simple test module for automated tests. // For the sake of simplicity, this example omits // security access mechanisms, especially for the // write services. In some cases, return parameters // are not checked. // // CANoe 10.0 and higher // --------------------------------------------------includes {// As this is a test module, neither including the CAPL callback interface nor adding// the corresponding transport protocol node layer DLL is necessary, because in this case,// the "built-in" diagnostic channel of CANoe can be used. }variables {enum bool {true=1, false=0};const cAccessModeNumerical=0;const cAccessModePhysical=1;const cAccessModeCoded=2;const test_vehicle_Speed_kmh = 40.0;// This timeout is used just to force CANoe to continue, i.e. normally a TestWaitForDiag...// function will return much earlier due to diagnostic level timing!const cApplicationTimeoutMs = 5000; char gTestIdStr[10]; // Test step ID for test reportword gTestCaseIndex=0;word gTestStepIndex=0;char gResultString[200]; // String for temporary test step result outputs }// Set and increment test step ID for test report updateTestIdStr() {snprintf(gTestIdStr, elcount(gTestIdStr), "%d.%d", gTestCaseIndex, gTestStepIndex); }setTestId(word tcIndex, word tsIndex) {gTestCaseIndex=tcIndex;gTestStepIndex=tsIndex;updateTestIdStr(); }incTestStepId() {gTestStepIndex++;updateTestIdStr(); }word SendRequestAndWaitForResponse(diagRequest *req, enum bool posResponseExpected) {long ret;// Trigger sending the requestif (0 > (ret=req.SendRequest())) { snprintf(gResultString, elcount(gResultString), "Trigger sending the request failed (Return code=%d)!", ret);testStepFail(gTestIdStr, gResultString);return 0;}testStepPass(gTestIdStr, "Trigger sending the request succeded.");incTestStepId();// Wait until the complete request has been sent, e.g. in case of long requests which spread over several messages (segmented message)if (1!=(ret=testWaitForDiagRequestSent(req, cApplicationTimeoutMs))){ snprintf(gResultString, elcount(gResultString), "Failed to finish sending the request (Return code=%d)!", ret);testStepFail(gTestIdStr, gResultString);return 0;}testStepPass(gTestIdStr, "Request was sent successfully.");incTestStepId();// Wait until the complete response has been received, e.g. segmented messages might take some time for transmissionif (1!=(ret=testWaitForDiagResponse(req, cApplicationTimeoutMs))) { snprintf(gResultString, elcount(gResultString), "Valid response missing or received too late (Return code=%d)!", ret);testStepFail(gTestIdStr, gResultString);return 0;}testStepPass(gTestIdStr, "Response received successfully.");incTestStepId();// Check whether the response was a positive responseif (-1==(ret=diagGetLastResponseCode(req))) {if (!posResponseExpected) {snprintf(gResultString, elcount(gResultString), "Positive response received although negative response was expected!");testStepFail(gTestIdStr, gResultString);return 0;}testStepPass(gTestIdStr, "Positive Response received as expected.");}else if (ret>0) {if (posResponseExpected) {snprintf(gResultString, elcount(gResultString), "Negative response received (NRC=0x%02x) although positive response was expected!", ret);testStepFail(gTestIdStr, gResultString);return 0;}testStepPass(gTestIdStr, "Negative Response received as expected (NRC=%d).", ret);}return 1; }// Check whether writing the vehicle speed parameter is done correctly by reading its value after writing testcase tcWriteAndReadVehicleSpeed() {diagRequest Door.Variant_Coding_Write req_write;diagRequest Door.Variant_Coding_Read req_read;double ret;word testCaseIndex; setTestId(1,1);TestStep(gTestIdStr, "Writing variant coding");if (0>req_write.SetParameter(cAccessModePhysical, "Codingstring.VehicleSpeedToLockDoor", test_vehicle_Speed_kmh)) {testStepFail(gTestIdStr, "Could not set parameter 'VehicleSpeedToLockDoor' in write request!");}else {if (0>req_write.SetParameter("Codingstring.VehicleType", "Sedan")) {testStepFail(gTestIdStr, "Could not set parameter 'VehicleType' in write request!");}else {sendRequestAndWaitForResponse(req_write, true);}}incTestStepId();TestStep(gTestIdStr, "Reading variant coding");if (sendRequestAndWaitForResponse(req_read, true)) {incTestStepId();ret=req_read.GetRespParameter(cAccessModePhysical, "Codingstring.VehicleSpeedToLockDoor");if (test_vehicle_Speed_kmh == ret) {testStepPass(gTestIdStr, "VehicleSpeedToLockDoor read as expected!");}else {testStepFail(gTestIdStr, "Read VehicleSpeedToLockDoor value is wrong (value=%f)!", ret);}} }void MainTest () {tcWriteAndReadVehicleSpeed(); }CANoe提供的Demo工程是學(xué)習(xí)CANoe最好的資源,熟悉以上示例代碼已經(jīng)能夠幫助我們開發(fā)出大部分診斷測(cè)試case。
?
?
總結(jié)
以上是生活随笔為你收集整理的CANoe教程:CAPL编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Endnotex8 运行时出现错误 un
- 下一篇: sql连接本地数据库