【CAPL】CAPL 常用语法介绍

CAPL 有 *.can*.cin 两种文件格式;一般.can 文件中写testcaseMainTest(),而.cin文件中写供.can调用的接口方法和变量等,其结构类似于 C 语言中的.c.h文件

一个大致的结构如下:

// Test.cin
includes{}
variables{int x =6;	// 全局变量,若要使用该变量,引入Test.cin 文件即可
}void printVar(int val)
{write("Print variable val: %d", val);	// 作用类似于 C语言的 printf 函数
}// TC.can
includes{#include "Test.cin"
}
variables{}
testcase Test(){printVar(x);
}
void MainTest( ){Test();
}

在打开 CAPL Browser 编辑界面时,默认的组成部分有:Include、Variable、System、Value Objects,其中 Include 为需要包含的已存在的头文件,一般不配置;Variable为申明与定义全局变量,需要定义的变量包括需要发送的信号以及其数据类型。

0. CAPL中的数据类型有:

无符号整型:byte(1字节),word(2字节),dword(4字节)

有符号整型:int(2字节),long(4字节)

浮点数:float(8字节),double(16字节)

CAN消息类型:Message

定时器类型:timer(单位为s),msTimer(单位为ms);

单个字符:char(1字节)。

除了界面基础的信息外,在 CAPL 脚本中,我们大量使用官方定的的一些接口,这些接口通常需要查看 Help 文档或者是 CAPL 的手册,下面是梳理的一些常用接口。

1、定时器 

CAPL中的定时器的使用相当频繁,比如测试时需要向定时发送某条CAN报文时就需要用定时器;定时器的声明:

msTimer myTimer1;//声明了一个ms定时器,定时单位是毫秒timer myTimer2;//声明了一个以秒为单位的定时器;

设置定时器:

setTimer(myTimer1,400);//设置定时器myTimer1为一个400ms定时器;setTimerCyclic(myTimer2,2);//设置定时器myTimer2为一个2s为周期的循环定时器;

设置定时器定时事件,即当定时器计时时间到时将要执行的操作:

on timer myTimer1{    .......}

2、信息的操作和发送

//CAN消息发送:message 0x7ff Msg;//声明一个message,ID=0x7ffMsg.dlc=8;//设置其DLC=8;Msg.id=0x100;//更改其ID=0x100;Msg.byte(0)=55;//设置数据场的第一个字节为55output(Msg);//发送Msg//CANFD消息发送:  msg1.FDF=1;  msg1.BRS=1;  msg1.dlc=8;  Msg.id=0x100;//更改其ID=0x100;  msg1.byte(0)=0x44;  msg1.byte(10)=0x10;  msg1.byte(11)=0x11;  output(Msg);//发送Msg

3、节点上下线操作 

节点是在dbc中定义的,如VCUBMSMCU等,有时需要将它们离线,离线后不再向总线上发送报文,在线时可以向总线上发送报文。

节点上线:

void testSetEcuOnline(dbNode aNode);void testSetEcuOnline(char aNodeName[]);

节点下线:

void testSetEcuOffline(dbNode aNode);void testSetEcuOffline(char aNodeName[]);

4、检查错误帧 

进行CAN通讯的测试时,检查错误帧是很常见的,要用CAPL脚本实现自动检测错误帧也不困难,它的核心就是调用错误检查函数ChkStart_ErrorFrameOccured(),该函数一旦被调用,CANoe 就会从此函数被调用时开始持续检测总线上有没有出现错误帧。 

下面是一个小的例子:

dword chechId;dword numCheckEvents;checkId=ChkStart_ErrorFrameOccured();//开始检测错误帧TestAddCondition(checkId);//添加检测条件,如果出现了错误帧,则输出报告中会记录下来TestWaitForTimeout(5000);//持续检测5scheckControl_Stop(checkId);//停止检测错误帧numCheckEvents=ChkQuery_NumEvents(checkId);//对5s内的检测情况进行获取,若函数返回0则没有出现错误帧if(numCheckEvents>0)     TestStepFail("Error Frames Occured");

5、添加事件信号 

这种事件信号相当于信号量机制,一般使用在需要等待某个或者是多个条件满足时进行下一步操作。

具体做法是:在一个位置添加需要等待的事件,程序中的其他地方,如果某个事件发生了(如周期超界等),提供该事件的供应,则等待的程序段获得了该事件,继续执行下面的操作。主要使用的函数有以下几个:

//供应text事件long TestSupplyTextEvent( char aText[] );//添加text事件long TestJoinTextEvent(char[]aText);//等待text事件,有一个出现则程序执行下一步long TestWaitForAnyJoinedEvent(dword aTimeout);//等待text事件,所有等待事件都出现则程序执行下一步long TestWaitForAllJoinedEvents(dword aTimeout);

以下是一个例子:

TestJoinTextEvent("Test finished");TestJoinTextEvent("Error Frame Occured");TestWaitForAnyJoinedEvents(20000);或者:TestWaitForAllJoinedEvents(20000);在系统事件on errorFrame中:on errorFrame{   TestSupplyTextEvent("Error Frame occured");}在系统的on message 中:on message 0x400{   TestSupplyTextEvent("Test Finished")}

6、回调函数 

CAPL 中也有类似于C语言中的回调函数的机制,如检测报文周期和错误帧的函数中就可以使用,当周期超界或者总线出现错误帧就会自动调用回调函数执行一些操作;如:

ErrChkId=ChkStart_ErrorFramesOccured(Callback_ErrorFrameOccured");//检查错误帧,如果发现错误帧就调用回调函数回调函数设计如下:void Callback_errorFrameOccured(dword chk_id){  float t;  t=timeNow()/100000.0;//记录出现错误帧的时间  testStep("ErrorFrameTimeStamp","%.6f s",t);//打印该事件戳  TestSupplyTextEvent("ErrorFrameOccured");//供应Text事件}

7、监视总线的情况,这一般会用在查看一段时间内,总线上有没有出现通讯异常的情况。需要使用函数ChkStart_NodeBabbling( ). 如,检测一段时间内总线有没有出现停止通讯的情况:

CheckId=ChkStart_NodeBabbling(CAN::PT_MCU,0);//立即开始检查总线状态testWaitForTimeout(2000);//延时2sChkControl_Stop(CheckId);//停止检测QueryNumberEvents=ChkQuery_NumEvents(CheckId);//如果在2s内总线停止通讯,则QueryNumberEvents!=0

8、关于获取关键时间点

(1)CANoe中获取定时器当前计时值的函数为:timerToElapse();该函数原型如下:

long timerToElapse(timer);long timerToElpase(msTimer);                                 

(2)获取等待某个事件的时间,需要使用函数TestGetLastWaitElapsedTimeNS(),其原型如下:

float TestGetLastWaitElapsedTimeNS();

(3)获取当前的仿真时间点:

float timeNowFloat();

(4)等待指定报文:

long TestWaitForMessage(dbMessage aMessage,dword aTimeout);long TestWaitForMessage(dword aMessageId,dword aTimeout);

若在aTimeout时间内等到了指定ID的报文,函数返回1,否则返回0;

(5)获取报文的数据,等到了报文之后,如果想知道报文的具体内容可以使用函数:

message msg;long result;result=TestGetWaitEventMsgData(msg);.....处理msg.....

9、多总线测试 

设置总线背景,一般都总线测试都会有两路及以上的 CAN,这时若要通过 CAPL 脚本获取某个 CAN 通道上的报文时,就需要先设置好总线背景,即将总线设置为值监听某一路的 CAN 通道。下面是一个例子:

void BusContextConfiguration(char yBus[]){   yBusContext=GetBusNameContext(yBus);//这里的yBusContext为全局变量   SetBusContext(yBusContext);}//使用:BusContextConfiguration("CAN1");//将总线监听设为CAN1

此时等待某一路的 CAN 报文可是这样实现:

res=testWaitForMessage(CAN1::NM_IPU,600);//等待CAN1上的名称为NM_IPU的报文,等待事件为600ms

10、诊断报文的发送和接收

request_A.SendRequest(); //诊断请求TestWaitForDiagResponse(request_A, 5000); //诊断接收

11、将诊断请求 / 响应写入报告

TestReportWriteDiagObject (diagRequest req);TestReportWriteDiagObject (diagResponse resp);TestReportWriteDiagResponse (diagRequest req);

12、获取诊断请求 / 响应的原始数据

long diagGetPrimitiveByte( diagRequest request, DWORD bytePos);long diagGetPrimitiveByte( diagResponse response, DWORD bytePos);

13、获取诊断请求 / 响应的参数

long diagGetParameter (diagResponse obj, char parameterName[], double output[1])long diagGetParameter (diagRequest obj, char parameterName[], double output [1])double diagGetParameter (diagResponse obj, char parameterName[])double DiagGetParameter (diagRequest obj, char parameterName[])long diagGetParameter (diagResponse obj, long mode, char parameterName[], double output[1])long DiagGetParameter (diagRequest obj, long mode, char parameterName[], double output [1])double diagGetParameter (diagResponse obj, long mode, char parameterName[])double diagGetParameter (diagRequest obj, long mode, char parameterName[])

14. 最后分享最近刚使用 CAPL 脚本的一些注意点以及一个示例:

第一 CAPL 的局部变量是静态局部变量。经过使用发现,在variables{ }之外,事件或者函数内部定义的局部变量是静态局部变量,其值不会因为退出本事件或者函数,而变为初始值。所以如果真的需要一个局部变量,在每次退出之前,重新使用赋值语句赋为初始值。

第二建议使用系统变量或者环境变量,这样可以跨不同的capl脚本操作,比如检测某个环境变量或者系统变量的变化,来执行一些动作。

on sysvar sysvar::Engine::EngineStateSwitch{  $EngineState::OnOff = @this;if(@this)  $EngineState::EngineSpeed = @sysvar::Engine::EngineSpeedEntry;else  $EngineState::EngineSpeed = 0;}

第三个就是以太网转 CAN 的 Capl 脚本示例:

/*@!Encoding:1252*/
variables{  //  // Constants  //    const WORD kPort         = 23; // UDP port number for instance  const WORD kRxBufferSize = 1500;  const WORD kTxBufferSize = 1500;    //  // Structure of UDP payload  //    _align(1) struct CANData{    BYTE  dlc;BYTE  flags; // Bit 7 - Frame type (0 = standard, 1 = extended)   // Bit 6 - RTR bit ('1' = RTR bit is set)    DWORD canId;    BYTE  canData[8];  };    //  // Global variables  //    UdpSocket gSocket;  CHAR  gRxBuffer[kRxBufferSize];  CHAR  gTxBuffer[kTxBufferSize];  DWORD gOwnAddress;  DWORD gModuleAddress= 0xFFFFFFFF; // default is the broadcast address 255.255.255.255  and the TCP/IP stack will build the Network broadcast address
} Measurement start handler//
on start{DWORD addresses[1];    // get own IP address of the Windows TCP/IP stack  IpGetAdapterAddress( 1, addresses, elcount(addresses) );  gOwnAddress = addresses[0];    // open UDP socket  gSocket = UdpSocket::Open( 0, kPort );     if (gSocket.GetLastSocketError() != 0){write( "<%BASE_FILE_NAME%> Open UDP socket failed, result %d. Measurement stopped!", 							         gSocket.GetLastSocketError());stop();return;  }if (gSocket.ReceiveFrom( gRxBuffer, elcount(gRxBuffer) ) != 0){   if (gSocket.GetLastSocketError() != 997){  // ignore pending IO operation write( "<%BASE_FILE_NAME%> UDPReceive failed, result %d. Measurement stopped!", gSocket.GetLastSocketError() );      stop();      return;    }  }
} On receive UDP data handler using CAPL Callback //
void OnUdpReceiveFrom( dword socket, long result, dword address, dword port, char buffer[], dword size){DWORD  dataOffset;struct CANData canData;message * anMsg; if (address == gOwnAddress) return; // ignore own broadcasts  //  // Store IP address of module to reach  //    if (gModuleAddress == 0){gModuleAddress = address;}    //  // Handle received data  //    dataOffset = 0;while (dataOffset + __size_of(struct CANData) <= size){    memcpy( canData, buffer, dataOffset );        canMsg.id = (canData.canId & 0x1FFFFFFF) | ((canData.flags & 0x80) ? 0x80000000 : 0);    canMsg.dlc     = canData.dlc & 0x0f;    canMsg.rtr     = ((canData.flags & 0x40) ? 1 : 0);    canMsg.byte(0) = canData.canData[0];    canMsg.byte(1) = canData.canData[1];    canMsg.byte(2) = canData.canData[2];    canMsg.byte(3) = canData.canData[3];    canMsg.byte(4) = canData.canData[4];    canMsg.byte(5) = canData.canData[5];    canMsg.byte(6) = canData.canData[6];    canMsg.byte(7) = canData.canData[7];        output( canMsg );       dataOffset += __size_of(struct CANData);  }  
//  // Receive more data  //  if (gSocket.ReceiveFrom( gRxBuffer, elcount(gRxBuffer) ) != 0){    if (gSocket.GetLastSocketError() != 997){   // ignore pending IO operation    write( "<%BASE_FILE_NAME%> UDPReceive failed, result %d. Measurement stopped!", gSocket.GetLastSocketError() );      stop();      return;    }  }
} Handler for CAN messages//
on message *{  int i;  struct CANData canData;    if ((this.dir == RX) && (gModuleAddress != 0)){    canData.canId = this.id & 0x1FFFFFFF;    canData.flags = ((this.id & 0x80000000) ? 0x80 : 0x00) | ((this.rtr == 1) ? 0x40 : 0x00);    canData.dlc   = this.dlc;        for( i = 0; i < 8; i++ ){      canData.canData[i] = (i < this.dlc) ? this.byte(i) : 0;    }        memcpy( gTxBuffer, canData );        gSocket.SendTo( gModuleAddress, kPort, gTxBuffer, __size_of(struct CANData) );  }  else if (gModuleAddress == 0){    write( "<%BASE_FILE_NAME%> Tx not possible. Module to reach must send packets first." ); //Server simulation  }
}

反向应用,以下是一个简单的CAN转以太网(CAN to Ethernet)的CAPL(Communication Access Programming Language)脚本示例。这个脚本将监听CAN总线上的消息,并将其转发到指定的以太网目标地址和端口。

variables
{int port = 1234; // 以太网端口号char ip[] = "192.168.0.100"; // 目标IP地址
}on start
{// 初始化以太网连接setSocketParameter("IP", ip);setSocketParameter("Port", port);openSocket();
}on canMessage CAN1
{// 检查是否成功接收到CAN消息if (this.canId == 0x100) // 假设我们只转发ID为0x100的CAN消息{// 构造以太网消息char ethMsg[8];for (int i = 0; i < 8; i++){ethMsg[i] = this.byte(i);}// 发送以太网消息writeSocket(ethMsg, 8);}
}on key 'q' // 按下'q'键时关闭以太网连接
{closeSocket();write("Socket closed.");
}

在这个脚本中,我们定义了一个以太网的目标 IP 地址和端口号。脚本启动时,会初始化以太网连接。当接收到 CAN 消息时,如果消息的 ID 是 0x100,则会将消息内容转发到以太网。按下 ‘q’ 键时,会关闭以太网连接。

请根据实际需求调整IP地址、端口号和CAN消息ID。此外,确保你的网络环境允许CAPL脚本与目标IP地址进行通信。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/874546.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iOS ------ weak的基本原理

1.weak的基本概念 weak弱引用&#xff0c;所引用的对象的引用计数不会加一&#xff0c;引用对象被释放的时候会自动设置为nil多用于解决对象间的相互引用造成内存泄露的循环引用的问题 2.实现原理 Person *object [[Person alloc] init]; id __weak objc object;Runtime维…

Redis 7.x 系列【26】集群模式动态扩容、动态缩容

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 动态扩容1.1 安装、启动1.2 加入新节点1.3 分配哈希槽1.4 加入从节点 2. 缩容2.1 删…

护眼灯和普通台灯有什么区别?解密护眼灯行业常见的四大选购套路

护眼灯和普通台灯有什么区别&#xff1f;随着护眼台灯的普及&#xff0c;市场上涌现了许多新兴品牌。然而&#xff0c;并非所有品牌都具备专业的技术研发实力。因此&#xff0c;网络上关于护眼台灯的各种问题也日益增多&#xff0c;如耐磨性差、耐高温性不足&#xff0c;甚至可…

python实现图像缩放算法

图像缩放算法 1.最近邻插值图像缩放算法详解算法步骤Python 实现详细解释 优缺点2.双线性插值图像缩放算法详解算法步骤Python 实现详细解释 优缺点3.双三次插值图像缩放算法详解算法步骤Python 实现详细解释 优缺点 1.最近邻插值图像缩放算法详解 最近邻插值&#xff08;Near…

go-kratos 学习笔记(4) 服务注册与发现 nacos注册

接口实现​ Registry 接口分为两个&#xff0c;Registrar 为实例注册和反注册&#xff0c;Discovery 为服务实例列表获取 type Registrar interface {// 注册实例Register(ctx context.Context, service *ServiceInstance) error// 反注册实例Deregister(ctx context.Context…

ubuntu系统vscode调试c/c++大中型项目

文章目录 1. 插件2. 项目工程3. vsode的配置setting.jsonc_cpp_properties.json 4. 启动调试 前置阅读 vscode调试第一篇 1. 插件 c/c, cmake, cmake tools 2. 项目工程 对于我的项目需要用到很多的三方库&#xff0c;三方库的版本又会有很多&#xff0c;一般都是用cmake编译…

objdump命令的常见用法

objdump命令的常见用法 1 objdump简介1.1 简介1.2 主要功能 2 objdump命令3 objdump测试程序3.1 C语言测试程序3.2 编译方法 4 objdump命令解释和演示4.1 objdump -d4.2 objdump -a4.3 objdump -f4.4 objdump -p4.5 objdump -D4.6 objdump -S4.7 objdump -s4.8 objdump -t4.9 o…

QT写一个mainWindow

切换风格的写法&#xff1a; 先看看样式效果&#xff1a; mian_window.h文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>class MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent nullptr);~MainWindow();void Ini…

对比预测编码表示学习

对比预测编码表示学习 引言 文章主要提出如下几点&#xff1a;首先将高维数据压缩到更加紧凑的潜在嵌入&#xff08;latent embdding&#xff09;空间&#xff0c;在这个空间中条件预测更容易被建模。第二&#xff0c;在这个潜在空间中使用自回归模型&#xff0c;以对未来的多…

Sphinx 安装相关指令解释

安装指令 pip3 install sphinx-autobuildpip3 install sphinx_rtd_themepip3 install sphinx_markdown_tablepip3 install sphinx_markdown_tables pip3 install sphinx-autobuild 功能&#xff1a;安装 sphinx-autobuild 包。作用&#xff1a;sphinx-autobuild 是一个工具&am…

DC系列靶场---DC 2靶场的渗透测试(一)

信息收集 Nmap扫描 nmap -sV -p- -sC -T4 172.30.1.141 域名解析 echo 172.30.1.141 dc-2 >> /etc/hosts 目录枚举 gobuster dir -u http://172.30.1.141 -w work/lab/CTF/ATT_CK_01/SecLists-master/Discovery/Web-Content/big.txt -x .php,.rar,.html,.zip -t 20 -b…

探索XEX数字资产交易的优势与操作指南

随着数字资产市场的快速发展&#xff0c;越来越多的投资者开始关注并参与其中。XEX交易所作为一个新兴的数字资产交易平台&#xff0c;以其用户友好的界面和高效的交易服务&#xff0c;迅速吸引了大量用户。本文将介绍XEX数字资产交易的主要特点和优势&#xff0c;帮助新手更好…

物联网在电力行业的应用

作者主页: 知孤云出岫 这里写目录标题 作者主页:物联网在电力行业的应用简介主要应用领域代码案例分析1. 智能电表数据采集和分析2. 设备监控和预测性维护3. 能耗管理和优化4. 电力负载预测5. 分布式能源管理6. 电动汽车充电管理7. 电网安全与故障检测 物联网在电力行业的应用…

python+onlyoffice+vue3项目实战20240722笔记,环境搭建和前后端基础代码

开发后端 先创建data目录,然后在data目录下创建一个test.docx测试文档。 后端代码: import json import req import api from api import middleware, PlainTextResponseasync def doc_callback(request):data = await api.req.get_json(request)print("callback ==…

“微软蓝屏”事件暴露了的网络安全问题

近日&#xff0c;一次由微软视窗系统软件更新引发的全球性“微软蓝屏”事件&#xff0c;不仅成为科技领域的热点新闻&#xff0c;更是一次对全球IT基础设施韧性与安全性的深刻检验。这次事件&#xff0c;源于美国电脑安全技术公司“众击”提供的一个带有“缺陷”的软件更新&…

数据结构——堆(C语言版)

树 树的概念&#xff1a; 树&#xff08;Tree&#xff09;是一种抽象数据结构&#xff0c;它由节点&#xff08;node&#xff09;的集合组成&#xff0c;这些节点通过边相连&#xff0c;把 节点集合按照逻辑顺序抽象成图像&#xff0c;看起来就像一个倒挂着的树&#xff0c;也…

equals()和hashCode()

HashMap是Java集合框架中的一个非常重要的组成部分&#xff0c;它提供了基于键值对的快速查找功能。在Java中&#xff0c;HashMap的底层实现是基于数组和链表或红黑树的结合体&#xff0c;具体取决于元素的数量和散列冲突的情况。以下是HashMap的一些关键概念和底层原理&#x…

使用C#手搓Word插件

WordTools主要功能介绍 编码语言&#xff1a;C#【VSTO】 1、选择 1.1、表格 作用&#xff1a;全选文档中的表格&#xff1b; 1.2、表头 作用&#xff1a;全选文档所有表格的表头【第一行】&#xff1b; 1.3、表正文 全选文档中所有表格的除表头部分【除第一行部分】 1.…

java面向对象进阶篇--《多态》

目录 一、前言 二、认识多态 方法重写&#xff08;Override&#xff09;&#xff1a; 方法重载&#xff08;Overload&#xff09;&#xff1a; 示例&#xff1a; Person类&#xff08;父类&#xff09; Administrator&#xff08;子类&#xff09; Student&#xff08;子…

docker搭建ES 8.14 集群

参考&#xff1a;【docker搭建es8集群kibana】_docker 安装生产级 es 8.14 集群-CSDN博客 1、之前已搭建一台单机版的dockerES集群 参见 Elasticsearch docker 安装_docker 安装es8.14.3-CSDN博客 2、现在需要重新搭建为docker ES集群 准备新搭建3个点 一、准备工作 提前开…