CAPL語言的基本使用

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);
  }
  
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容