CANoe教程:CAPL编程
?
CANoe教程 | CAPL編程 - 數據類型
CAPL是一種類C語言,CAPL數據類型的定義很多C語言類似,但也有很多獨特的地方。 CAPL數據類型包括基本類型、結構體、枚舉、關聯類型和對象類型。變量的數據類型決定了變量存儲占用的空間。
1 基本類型
2 枚舉
枚舉變量的定義和使用同C語言:
enum State { State_Off = -1, State_On = 1 };如果枚舉成員的值未定義,那么第一個成員默認值為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 關聯類型
CAPL支持一種類似Python字典和C++ Map的關聯類型(Associative Fields),關聯類型的元素是鍵值對(key value pairs)。 關聯類型定義格式如下,左邊是value類型,右邊[ ]內是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:關聯浮點型
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:關聯字符串
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 結構體
結構的定義和使用同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中結構體默認按8字節對齊,可以在結構體定義前加_align來改變結構體對齊方式。
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可以使用如下函數獲取結構體大小(size)、對齊方式(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 對象類型
除了以上介紹的基礎數據類型,CAPL還提供了一些CANoe特有的對象類型來幫助用戶快速完成仿真測試功能的開發。
CAN messages
CAPL提供了各種網絡對應的報文類。本文以CAN message為例進行介紹。 報文變量定義格式:
message + message ID/message name + variable使用message關鍵字來聲明一個報文變量,message后是message ID或CANoe工程導入DBC后的message name,然后是在CAPL程序中要使用的報文變量名。
message 0xA m1; //定義一個ID為0xA的報文變量m1 message 100x m2; //定義一個ID為100的擴展幀報文變量m2,ID后的x后綴表示這是一個擴展幀 message EngineData m3; //定義一個在DBC中message name為EngineData的報文變量m3 ... output(m1); output(m2); output(m3);CAPL提供了一系列的選擇器(Selectors)來設置或讀取CAN message的屬性,例如:
?
example:
message 0x100 msg; //定義一個ID為0x100的message變量msg msg.CAN = 1; //將msg的通道設置為1 msg.DLC = 2; //將msg的DLC設置為2 msg.BYTE(0) = 0xAA; //給msg報文數據段的第一個byte賦值為0xAA; msg.BYTE(1) = 0xBB; //給msg報文數據段的第二個byte賦值為0xBB; output(msg); //將定義好的msg發送到總線中?6 定時器變量
CAPL提供兩種定時器變量: timer:基于秒(s)的定時器 msTimer:基于毫秒(ms)的定時器 example:點擊鍵盤'a'后以20ms為周期發送id為100的報文
msTimer myTimer; //定義一個ms定時器myTimer message 100 msg; ... on key 'a' {setTimer(myTimer,20); //點擊鍵盤'a'將定時器myTimer設置為20ms,并開始計時 } ... on timer myTimer { //響應定時器事件myTimer,將msg發送到總線,output(msg);setTimer(myTimer,20); //重新設置定時器myTimer為20ms }CANoe教程 | CAPL編程-運算符/流程控制
CAPL中算數運算符、邏輯運算符、位運算以及流程控制語句和C語言一致。?
1 運算符
2 流程控制
?
CANoe教程 | CAPL編程 - 事件驅動
1 事件概述
CAPL是一種面向過程、由事件驅動的類C語言。
事件驅動針對于順序執行,其區別如下:
順序執行:順序執行流程中,子例程或過程函數按照代碼編寫順序逐句執行。
事件驅動:CAPL程序由事件驅動,工程運行過程中發生指定的事件時才會運行相應的事件處理函數。
順序執行VS事件驅動
在CAPL中添加事件處理函數:?
重要的事件處理函數:
事件總覽:?[3]
2 事件詳解
事件起始關鍵字 on
on后加某種事件,工程運行時條件觸發,則執行函數體內的語句。
關鍵字this
系統變量、環境變量或CAN報文事件中,可以用this關鍵字訪問其指代的數據內容,如:
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測量系統的控制功能,主要有on start、on preStart、onstopMeasurement、on preStop、on key<newKey>以及on timer
系統事件Example:
CAN控制器事件
當CAN控制器或錯誤計數器狀態變化時調用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報文/信號事件
CAN報文或信號變化時調用報文/信號事件。
CAN報文/信號事件
報文事件:
信號事件:
on signal LightSwitch::OnOff {v1 = this.raw;v2 = $LightSwitch::OnOff.raw; }定時器事件
定義好定時器變量后,由SetTimer函數設置定時器間隔并啟動定時器。當定時器運行到設定的時間間隔時觸發定時器事件,并執行on timer函數體中的程序。
msTimer myTimer; message 100 msg; ... on key 'a' {setTimer(myTimer,20); } ... on timer myTimer {output(msg); }鍵盤事件
通過定義鍵盤事件,用戶可以在工程運行時通過點擊鍵盤觸發預先定義的行為。這在實際開發和測試時非常常用。比如用戶可以在任意時刻向總線發送特定的消息、改變信號或系統變量的值或是啟動停止測量。
系統變量/環境變量事件
系統變量和環境變量事件分別是對系統變量和環境變量發生變化時的響應。
系統變量事件:
on sysvar IO::DI_0 { $Gateway::IOValue = @this; }環境變量事件
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編程 - 實用CAPL代碼片段
本文根據CAPL編程中經常遇到的案例場景整理簡潔通用的代碼片段。
1 周期消息發送
無論是Simulation Setup中的仿真節點還是Test Setup中的Test Module所關聯的CAPL腳本在做仿真或測試時都經常需要向總線模擬發送周期消息。
點擊鍵盤按鍵 'a' 后向總線發送周期為20ms的can 消息msg:
variables { msTimer myTimer;message 100 msg; } on key 'a' {setTimer(myTimer,20); } on timer myTimer {output(msg);setTimer(myTimer,20); }2 應用報文Request/Response測試
ECU通常都有很多請求/應答式的功能,比如BCM可以接收用戶點擊車窗、雨刮、遮陽簾等車身相關硬件的控制按鈕向總線發出的開關請求(Request),然后由BCM向總線發出響應消息,并控制車窗、雨刮、遮陽簾等做出相應的反饋動作(Response)。
下面以測試BCM雨刮開關功能為例進行Request/Response測試。
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); //測試雨刮關閉功能Check_Wiper_Feature(1,1); //測試雨刮開啟功能TestGroupEnd(); } //Wiper Feature testcase 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 檢測總線中周期報文的發送周期是否在給定范圍內
TSL提供了兩組函數用于測試周期報文:
一組使用相對時間因子,當周期小于 (aMinRelCycleTime * GenMsgCycleTime)或大于(aMaxRelCycleTime* GenMsgCycleTime)時產生事件。
函數原型:
dword ChkCreate_MsgRelCycleTimeViolation (Message aObservedMessage, double aMinRelCycleTime, double aMaxRelCycleTime, Callback aCallback);dword ChkStart_MsgRelCycleTimeViolation (Message aObservedMessage, double aMinRelCycleTime, double aMaxRelCycleTime, Callback aCallback);另一組使用絕對時間參數,當周期小于 aMinCycleTime 或大于 aMaxCycleTime 時產生事件。
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); }測試報告中設置的命令如下,請您自行查閱CANoe幫助文檔,或者查找自帶的模板。
TestModuleTitle ("Test BCM Features");\\測試報告標題。TestModuleDescription ("Check all features in BCM.");\\測試報告描述。輸出的測試報告如下圖所示:
如上圖所示,測試報告展示了錯誤事件產生的次數以及錯誤事件所處的事件范圍。
4 統一診斷測試(UDS)
診斷測試經常需要進行切換session,22/2E讀寫等request/response式的操作,CANoe Demo工程UDSBasic.cfg中Simulation Setup窗口里的TestModule節點關聯的CAPL腳本為我們提供了一個很好的參考模板:
參考代碼:
/*@!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工程是學習CANoe最好的資源,熟悉以上示例代碼已經能夠幫助我們開發出大部分診斷測試case。
?
?
總結
以上是生活随笔為你收集整理的CANoe教程:CAPL编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Endnotex8 运行时出现错误 un
- 下一篇: Docker学习笔记08-----Doc