CAPL語言的.can文件的搭建

image.png
創(chuàng)建一個Nodes文件夾,把我們的.can文件存儲到這;
這樣創(chuàng)建的這個capl文件,是在工程運(yùn)行后,如果觸發(fā)了某些條件,會運(yùn)行里面的某些功能代碼;所以說capl是一門事件驅(qū)動型語言;
CAPL語言中的一些基本數(shù)據(jù)類型
int age;
int num = 30;
float tall = 1.72;
const float myPI = 3.14;
//枚舉,默認(rèn)值是 0, 可以設(shè)置
enum Gender {
MALE = 1,
FEMALE
};
//結(jié)構(gòu)體
struct Humen{
char name[100];
byte age;
float tall;
enum Gender sex;
};
struct point{
int x;
int y;
};
struct point mypoint1 = {34,56};
struct point mypoint2 = {
x=34,
y=56
};
//聲明一個毫秒級定時器, 需要綁定事件,如果需要開啟和關(guān)閉;
msTimer t1;
msTimer t2;
msTimer t3;//報文的指定發(fā)送次數(shù)
int t3count = 0;
int t1Count = 0; //執(zhí)行的次數(shù)
//這個是秒級的 timer t;
int i = 0;
int j = 0;
//定義AEBlog日志是否開啟記錄;
int LoggingAEB_Start = 0;
int EngineMsgCount = 0;//記錄引擎的報文觸發(fā)次數(shù);
int LoggingHeadLightStarted = 0; //車燈的當(dāng)前狀態(tài)記錄
char LoggingHeadLight[50] = "LoggingHeadLight";
int currMaxSpeed = 0;//當(dāng)前的發(fā)動機(jī)最高轉(zhuǎn)速;
int speedFileHandle = 0;//存儲轉(zhuǎn)速的文件句柄;
下面是對定義的數(shù)據(jù)類型的一些使用
on key 'w'{
byte count1;
byte count2;
enum Gender zsySex = FEMALE;
// 定義Position類型的同時,聲明一個Position變量veh1,并賦值為Left
enum Position { Front=1, Rear=2, Left=3, Right=4} veh1 = Left;
// 使用Position聲明一個變量veh2,并賦值為Front
enum Position veh2 = Front;
char gender = 'F'; //char在C語言中占一個字節(jié)
char nameStr[5] = "ABCD"; //還有一個隱藏的/0,所以最多4個字符
char nameStr2[100] = "zhaungshaoyun"; //這個就是一般的字符串的寫法,C中沒有字符串的定義,只有字符數(shù)組;
char numStr1 = 'B';
int num2 = 2;
char numStr3 ;
//聲明數(shù)組Array
int intArray[10] = {1,2,3,4,5};
//這個是字符數(shù)組,但是不是字符串
char strArray[10] = {'a','b','c','d','a'};
// name2是字符串的字符數(shù)組,必須以'\0'結(jié)尾
char name2[6] = {'S', 'm', 'i', 't', 'h', '\0'};
//二維數(shù)組
int scores[2][3] = {
{1,2,3},
{4,5,6}
};
char currTime[100];
//計算字符串,數(shù)組的長度
write("%d-----%d------%d------%d",elCount(nameStr),elCount(nameStr2),elCount(strArray),elCount(scores));
//5--------100----------10------------2 ,二維的是計算行,列應(yīng)該要明確固定寫死。
numStr3 = numStr1+'!'; // 字符可以進(jìn)行運(yùn)算,因?yàn)橛袑?yīng)的ASCII
// B = 66 ! = 33 , = 99 是小寫的c
write("字符相加:%c , %c",numStr3 , (char)(numStr1 + num2) );
write("%c",gender);
write("%d",zsySex);
count1 = 5;
count2 = -5;
write("byte占一個字節(jié),超出范圍會截取:%d , %d",count1,count2);
//隨機(jī)數(shù) [0;100]
writeDbgLevel(5,"隨機(jī)數(shù):%d",random(100));
write("7.32四舍無入:%d", _round(7.32));
getLocalTimeString(currTime);
write("當(dāng)前時間:%s", currTime);
write("30度角的正弦值為:%.2f", sin(30 * pi / 180));
}
//結(jié)構(gòu)體
On key 's'{
struct Humen Jame = {
name = "jame",
age = 18,
tall = 1.77,
sex = FEMALE
};
struct Humen mery;
// mery.name = "jame";
//CAPL中是沒有 字符串 這種明確的數(shù)據(jù)類型的,結(jié)構(gòu)中的name的數(shù)據(jù)類型是char[],也就是char的數(shù)組。
//數(shù)組一旦初始化,不能重新整體賦值,只能訪問其中的元素;
//所以上面重新進(jìn)行賦值的操作是行不通的,只能對數(shù)組單個元素操作,比如mery.name[0] = 'j';
mery.age = 19;
mery.tall = 1.53;
mery.sex = MALE;
write("name = %s",Jame.name);
write("name = %s",mery.name);
}
下面是定時器的使用
//定時器的啟動
on key 'a'{
setTimer(t1,200);
setTimer(t2,100);
}
//定時器事件綁定
On timer t1{
write("time定時器啟動了");
t1Count++;
if(t1Count < 10){
setTimer(t1,200);
}
}
On timer t2{
sendDoorStateMsg();
}
void sendDoorStateMsg(){
message DoorState msg;
msg.LeftDoorState = i%2;
i++;
output(msg);
write("發(fā)送了一幀報文");
//開始定時器
setTimer(t2,100);
//取消定時器
//cancelTimer(t2);
}
下面是定義一個報文
//報文
On key 'b'{
message DoorState msg1; //DoorState的 ID = 0x666
message 0x666 msg2;
// 訪問報文變量msg1的屬性
write("報文的ID:%#x", msg1.id);
write("報文的名稱:%s", msg1.name);
write("報文的DLC(數(shù)據(jù)長度):%d", msg1.dlc);
write("報文的ID:%#x", msg2.id);
write("報文的名稱:%s", msg2.name);
write("報文的DLC(數(shù)據(jù)長度):%d", msg2.dlc);
//配置報文的信號值,使用報文變量配置,來著dbc文件;
msg1.LeftDoorState = 1;
msg1.RightDoorState = 0;
msg1.LeftWindowPosition = 50;
msg1.RightWindowPosition = 73;
// 使用output函數(shù)向總線上輸出報文msg1
output(msg1);
}
在進(jìn)行CAPL的報文創(chuàng)建時

image.png
這個DoorState 的報文是車門的報文,來著我們加載的dbc文件:

image.png
下面是log輸出的方式和函數(shù)的調(diào)用方法
On key 'c'{
// 4 代表輸出的位置是 Test page ,0123是信息類型
writeLineEx(4,0,"一個成功的輸出");
writeLineEx(4,1,"一個信息的輸出");
writeLineEx(4,2,"一個警告的輸出");
writeLineEx(4,3,"一個錯誤的輸出");
write("配置了輸出登記后的write輸出");
writeDbgLevel(2,"按照等級輸出");
add(1,3);
}
//函數(shù)
void add(int a,int b){
writeDbgLevel(5,"%d",a+b);
}
long addBig(int a,int b){
return a+b;
}
//以報文為參數(shù)的函數(shù)
void sendMsg(message * msg,int count){
//執(zhí)行一些操作,然后可以發(fā)送報文
//.......
int i;
for( i=0;i<count ;i++){
}
output(msg);
}
下面是程序的生命周期函數(shù)
//生命周期
//開始啟動時調(diào)用
On start {
writeDbgLevel(5,"啟動");
//配置輸出的等級,一般是全局配置的,只對writeDbgLevel方法生效;
setWriteDbgLevel(5);
//配置log日志的輸出路徑和文件名
setLogFileName("LoggingAEB","ZSYLogging/{LoggingBlock}_{MeasurementStart}.asc");
speedFileHandle = openFileWrite("test/EngineSpeedReport.txt",2);
if(speedFileHandle){
write("打開存儲轉(zhuǎn)速文本成功");
}else{
write("打開存儲轉(zhuǎn)速文本失敗");
}
}
//結(jié)束時調(diào)用
On stopMeasurement{
writeDbgLevel(5,"結(jié)束");
//關(guān)閉文件
if(speedFileHandle){
fileClose(speedFileHandle);
}
}
//結(jié)束程序
On key 'd'{
writeDbgLevel(5,"結(jié)束程序");
stop();
}
通過CAPL控制 LoggingAEB 日志模塊的啟動和停止
On key 'e'{
//log日志的開啟和停止,數(shù)據(jù)錄入到文件中,不會再次創(chuàng)建新的文件,是一個asc文件;
//如果關(guān)閉了剛才重新開始,會生成一個新的日志文件;
if(LoggingAEB_Start == 0){
LoggingAEB_Start = 1;
// 啟動名為LoggingAEB的日志模塊的記錄
startLogging("LoggingAEB");
}else{
LoggingAEB_Start = 0;
stopLogging("LoggingAEB");
}
}
如果需要CAPL控制日志的記錄,需要如下操作,代碼才能生效:

image.png
文件的讀寫操作
//文件的 數(shù)據(jù)寫入操作,返回文件句柄
int writeSomeMsgToFile(char filePath[]){
int fh;
char name[50] = "小莊";
int age = 18;
float tall = 1.73;
char mutStr[100]; //定義一個可變字符串,用于字符串的拼接
// 以覆蓋寫入文本字符的模式(0)打開文件,返回代表這個文件的句柄
// 注意:如果使用模式2,則表示追加寫入
fh = openFileWrite(filePath,0);
if(fh){
//打開文件成功,進(jìn)行寫入操作
//1.字符串拼接
snprintf(mutStr, elCount(mutStr), "我是%s,今年%d歲,我的身高%.2f米。\n", name, age, tall);
//2.寫入數(shù)據(jù)
filePutString(mutStr,elCount(mutStr),fh);
filePutString("XXXXXXXXX",100,fh);
filePutString("OOOOOO",100,fh);
//3.關(guān)閉文件
fileClose(fh);
}
return fh;
}
//文件的 數(shù)據(jù)讀取操作,返回文件句柄
int readSomeMsgToFile(char filePath[]){
int fh;//文件句柄
char msg[100];
fh = openFileRead(filePath,0);
if(fh){
while(fileGetString(msg,elCount(msg),fh)){
write("讀取的每一行的內(nèi)容:%s",msg);
}
fileClose(fh);
}
return fh;
}
//文件的讀寫
On key 'f'{
/*
這里如果是直接讀取文件內(nèi)已經(jīng)存在的信息,
沒辦法設(shè)置讀取的encode,導(dǎo)致可能亂碼,這
個應(yīng)該修改txt的文本格式;
*/
char filePath[50] = "test/zsyMsg.txt"; //文件地址,必須帶文件后綴名
if(writeSomeMsgToFile(filePath)){
writeDbgLevel(5,"寫入成功");
}else{
writeDbgLevel(5,"寫入失敗");
}
if(readSomeMsgToFile(filePath)){
writeDbgLevel(5,"讀取成功");
}else{
writeDbgLevel(5,"讀失敗");
}
}

image.png
通過timer來實(shí)現(xiàn)多次執(zhí)行某一件事情
On timer t3{
t3count++;
writeDbgLevel(5,"執(zhí)行 了%d次",t3count); //可以把這個log換成報文發(fā)送
if(t3count < 10){
setTimer(t3,50);
}
}
On key 'g'{
setTimer(t3,50);
}
報文的監(jiān)測
// 當(dāng)總線上監(jiān)測到了EngineState報文的時候觸發(fā)
On message EngineState {
write("第%d次檢測到%s[%#x]報文,報文的傳輸方向[%d], 引擎的開關(guān)[%d], 引擎的轉(zhuǎn)速[%d], 報文的長度[%d],報文的字節(jié)[%#X]",
++EngineMsgCount, this.name, this.id, this.dir, this.OnOff, this.EngineSpeed,this.dlc, this.word(0));
}
// 監(jiān)測LightState報文的出現(xiàn)(每次監(jiān)測到報文出現(xiàn),就觸發(fā)一次
On message LightState{
//writeDbgLevel(5,"監(jiān)測到LightState報文");
if(this.HeadLight == 1 && LoggingHeadLightStarted == 0){
//已開燈 + 未記錄日志
startLogging(LoggingHeadLight);
LoggingHeadLightStarted = 1;
writeDbgLevel(1, "開始記錄LoggingHeadLight的日志");
}else if(this.HeadLight == 0 && LoggingHeadLightStarted == 1){
stopLogging(LoggingHeadLight);
LoggingHeadLightStarted = 0;
writeDbgLevel(1, "停止記錄LoggingHeadLight的日志");
}
}
報文中的信號的監(jiān)測 (發(fā)生了改變,比如從0到1了,才會觸發(fā))
//監(jiān)測報文下面的信號的變化
On signal HeadLight {
write("信號HeadLight發(fā)生了變化,信號的值為:%d", (int)this);
write("引擎的開關(guān)%d",(int)$OnOff);
write("引擎的轉(zhuǎn)速%d",(int)$EngineSpeed);
write("引擎的值%d",(int)$EngineSpeed);
write("引擎的物理量%d",(int)$EngineSpeed.phys);
write("引擎的原始值%d",(int)$EngineSpeed.raw);
//這個方法直接監(jiān)測信號值控制開啟關(guān)閉,比上面的更直接;
if(this == 1){
startLogging(LoggingHeadLight);
writeDbgLevel(1, "開始記錄LoggingHeadLight的日志");
}else if(this == 0){
stopLogging(LoggingHeadLight);
writeDbgLevel(1, "停止記錄LoggingHeadLight的日志");
}
}
監(jiān)測信號值的更新,其實(shí)就是報文上報收到了,就觸發(fā)的意思
On signal_update HeadLight{
//這個信號50毫秒上報一次,所以這個打印50毫秒觸發(fā)一次;
write("接收到了頭燈的信號:%d",(int)this);
}
信號變化的簡單使用示例:
需求:檢測EngineState報文中的信號的變化,每次變化判定是否是本次乘車過程中的最高轉(zhuǎn)速;
EngineState報文中,有開關(guān)信號OnOff,和轉(zhuǎn)速信號EngineSpeed;
如果是最高轉(zhuǎn)速,寫入報告文件;
On start {
speedFileHandle = openFileWrite("test/EngineSpeedReport.txt",2);
if(speedFileHandle){
write("打開存儲轉(zhuǎn)速文本成功");
}else{
write("打開存儲轉(zhuǎn)速文本失敗");
}
}
//結(jié)束時調(diào)用
On stopMeasurement{
writeDbgLevel(5,"結(jié)束");
//關(guān)閉文件
if(speedFileHandle){
fileClose(speedFileHandle);
}
}
On signal EngineSpeed
{
char info[200]; //存儲的轉(zhuǎn)速信息
char currentTime[200];//當(dāng)前時間
char pattern[100] = "[%s] 出現(xiàn)了目前的最高轉(zhuǎn)速 [%d]\n"; //一個待用的拼接字符串
if(this > currMaxSpeed){
getLocalTimeString(currentTime);
currMaxSpeed = (int)this;
snprintf(info,elCount(info),pattern,currentTime,currMaxSpeed);
//文件的打開和關(guān)閉操作,放在了程序的啟動和關(guān)閉上了
filePutString(info,elCount(info),speedFileHandle);
}
}