便携式航电实时系统测试平台实时脚本(C++)扩展API

106 阅读12分钟

第一章 实时脚本(C++)扩展API

实时脚本在ETest中作为下位机脚本,运行在VXWorks环境下。

实时脚本为C++脚本,符合C++ 17规范。

同时,实时脚本还带有ETest提供的扩展API,提供了一系列服务和支持。本章主要介绍这些扩展API。

1

1

2

3

4

5

6

1

1.1 接口操作

本节介绍对ETest下位机接口设备硬件进行操作,完成通信的接口API。

所有接口类均在命名空间 Kiyun::LowerComputer::Rasl::Device 中。

所有接口都采用设备与通道两层结构,通道统一命名为 Channel_T,作为设备的内嵌类。如串行通信口,类名为 Com_T,其通道为 Com_T::Channel_T

每个接口的方法主要有两组,分别是 read/write 和 intRead/asyncWrite。其中前一组是阻塞式读/写,后一组是异步(非阻塞式)读/写。其中,异步读/写均为立即返回,不等待读写操作完成;读/写完成后调用回调函数。注意有的设备没有异步读/写API。

在正常使用中,接口对象由 ETest 根据测试连接图自动构造,并以图中的通道名为对象名,可在代码中直接使用。

1.1.1 AD接口

使用模数转换通道主要用到的类型是Ad_T类型。本节对模数转换通道进行介绍。

本节所描述的类型所在命名空间均为:Kiyun::LowerComputer::Rasl::Device

1.1.1.1 使用方法及范例

在ETest系统中,模数转换操作主要由Ad_T::Channel_T类型完成。

在使用ETest进行仿真模型设计和设备规划过程中,可以建立模数转换通道,并且配置参数。当通道建立完成并且参数配置完成后,系统运行环境会自动创建Ad_T::Channel_T类型的通道,名称和在“仿真模型”中建立的通道名称一致。在实时脚本中,即可直接使用该对象,进行数据的采集操作。

如,在以下范例中,对象f_ad即为Ad_T::Channel_T类型的对象,可以用来完成模拟量采集功能。

单次采集:

单次采集多个点:

连续采集:

if (!f_ad.isValid()) { KYIO(err) << "AD channel initial failed."; return -1; //返回值需要根据该代码片段所处函数返回值类型设定 } if (f_ad.isRunning()) { KYIO(err) << "AD channel is sampling."; return -2; //返回值需要根据该代码片段所处函数返回值类型设定 } Os::Semaphore_T semAd; //需引入"Kiyun::LowerComputer::Rasl"命名空间 f_ad.intRead([&](float* buf, size_t size, Error_T error) { if (error) { KYIO(err) << "Occur errors in intRead:" << error.message(); return false; } //通过发送信号量的方式终止连续采集任务的执行,需自行给定终止的条件 /* if(...){ semAd.notifyOne(); return false; } */ return true; //返回true时会一直执行连续采集直到采集出错或有其他的终止条件 } ); f_ad.start(); //开始连续采集 semAd.wait(); //等待信号量,接收到信号量后终止连续采集任务 f_ad.stop(); //停止连续采集

1.1.1.2 Ad_T类

本类型是模数转换操作类型。它包含了IntReader_T类型和Channel_T类。

1) IntReader_T类型

在进行中断读取的时候,需要用到一个函数类型作为参数,当读取操作完成的时候,本函数会被调用。

类型定义为:

typedef std::function<bool(float*,size_t,Error_T)> IntReader_T;

1.1.1.3 Ad_T::Channel_T类

本类型的对象是进行AD采集的主体。具有以下方法:

1) isValid方法

检测通道是否有效。强烈建议使用前判断通道合法性。

函数原型:bool isValid() const

参数说明:无

返回值:true:通道有效,fasle:通道无效

1) path方法

返回通道的标识,可用于在数据上传时构造上传通道。

函数原型:Path_T path() const

参数:无

返回值:通道标识符(类型参加“通用类型介绍”)。

2) read方法一

采集一次数据。

函数原型:float read()

参数:无

返回值:采集到的数据

3) read方法二

多次同步采集数据。采集点数越多,函数返回时间越长。

函数原型:BOOL read(float* buf, size_t size)

参数:buf:存放采集到数据的地址;sizebuf的长度(采集点数)

返回值:操作是否成功

4) intRead方法

进行通道注册连续采集。连续采集工作与单次采集工作不能同时进行。

函数原型:void intRead(const IntRead_T& hdl)

函数功能:

参数:hdl:回调函数,函数返回值表示“是否继续接收”,true继续,false停止;

返回值:无

5) isRuning方法

判断是否存在连续采集任务。

函数原型:BOOL isRuning()

参数:无

返回值:TRUE:存在连续采集任务,FALSE:不存在连续采集任务

6) start方法

开始连续采集任务。

函数原型:BOOL start()

参数:无

返回值:TRUE:成功打开或者已经存在任务,FALSE:打开失败

7) stop方法

停止连续采集任务。

函数原型:BOOL stop()

参数:无

返回值:TRUE:成功关闭或者不存在连续采集任务,FALSE:关闭失败

8) reset方法

重置连续采集通道

函数原型:BOOL reset ()

参数:无

返回值:TRUE:成功清除已注册的连续采集通道,FALSE:重置失败(采集任务正在进行)

1.1.2 DA接口

使用数模转换通道主要用到的类型是Da_T类型。本节对数模转换通道进行介绍。

1.1.2.1 使用方法及范例

在ETest系统中,数模转换操作主要由Da_T::Channel_T类型完成。

在使用ETest进行仿真模型设计和设备规划过程中,可以建立数模转换通道,并且配置参数。当通道建立完成并且参数配置完成后,系统运行环境会自动创建Da_T::Channel_T类型的通道,名称和在“仿真模型”中建立的通道名称一致。在实时脚本中,即可直接使用该对象,进行数据的采集操作。

如,在以下范例中,对象da即为Da_T::Channel_T类型的对象,可以用来完成模拟量转换输出功能。

int kiyunMain() { if(!da.isValid()) { KYIO(err) << "DA channel initial failed."; return -1; } da.write(5.0); Timer_T::delay(5);// Da 保持输出5s da.stop();// 禁止输出(Da退出会自动调用) return 0; }

1.1.2.2 Da_T::Channel_T

本类型所在命名空间为:Kiyun::LowerComputer::Rasl::Device,是进行DA转换的主体。具有以下方法:

1) isValid方法

检测通道是否有效。强烈建议使用前判断通道合法性。

函数原型:bool isValid() const

参数:无

返回值:true:通道有效,fasle:通道无效

2) path方法

返回通道的标识,可用于在数据上传时构造上传通道。

函数原型:Path_T path() const

参数:无

返回值:通道标识符(类型参加“通用类型介绍”)。

3) write方法

设置输出幅值。

函数原型:void write(float value)

参数:value:输出幅值(电压值)。

返回值:无

注:Da32(AECDA-CPCI-32)写操作需要延迟 25ms,Da8(AECDA-CPCI-8-S2)输出间隔应至少1us

4) start方法

开始DA输出。

(write方法会自动调用start方法开始输出)。

函数原型:void start()

参数:无

返回值:无

5) stop方法

停止DA输出。之后可以调用start方法重新启动输出。

函数原型:void stop()

参数:无

返回值:无

1.1.3 串口

使用串口总线主要用到的类型是COM_T类型。本节对串口总线操作进行介绍。

1.1.3.1 使用方法及范例

在ETest系统中,串口总线操作主要由Com_T::Channel_T类型完成。

在使用Etest进行仿真模型设计和设备规划过程中,可以建立串口总线通道,并且配置参数。当通道建立完成并且参数配置完成后,系统运行环境会自动创建Com_T::Channel_T类型的通道,名称和在“仿真模型”中建立的通道名称一致。在实时脚本中,即可直接使用该对象,进行数据的采集操作。

如,交联环境如下配置:

对象com0和com1即为Com_T::Channel_T类型的对象,可以用来完成串口总线功能。

示例代码如下:

/** 系统固定头文件 / #include <Rasl/rasl.hpp> #include <DataUploader/DataUploader.hpp> #include "StaticVariable.h" #include "rasl-dpd/user.hpp" #include "UserChannels.rasi" #include "DataCollect.hpp" /* 系统固定头文件 / //用户测试代码需要新增的头文件 namespace{ using namespace Kiyun::LowerComputer; using namespace Kiyun::LowerComputer::Rasl; using namespace Kiyun::LowerComputer::Rasl::Frame; using namespace Kiyun::LowerComputer::Rasl::Device; //通道设备命名空间 using namespace Kiyun::LowerComputer::DataUploader; //数据上传命名空间 using namespace Kiyun::Channel; using namespace Kiyun::DataCollect; //数据采集接口命名空间 using namespace Dpdp; //协议命名空间 //本地变量、本地函数声明 //Os::Semaphore_T f_exitSem; Os::Semaphore_T readDone; std::string rs; // 返回值表示“是否继续接收” bool reader(char buf, size_t size, Error_T) { std::string temp( buf, buf + size); rs += temp; auto done = rs.size() >= 7; if (done) readDone.notifyOne(); return !done; } void whenWriteDone(size_t size, Error_T) { KYIO(log) << "Write done : " << size; } int sync_rw() { std::string ws = "abcdefg"; com0.write(ws.data(), ws.size());//同步写 std::string rs(7, '\0'); com1.read(&rs[0], rs.size());//同步读(定量字节) KYIO(out) << rs; return 0; } int async_rw() { com1.intRead(reader);//注册中断读 std::string ws = "abcdefg"; //异步写 com0.asyncWrite(ws.data(), ws.size(), whenWriteDone); readDone.wait(); KYIO(out) << rs; return 0; } } //主函数入口 int Main() { //设置全局日志属性 KYLOG_GLOBAL().severity(Logging_T::Severity_E::TRAC_e); KYLOG_SCOPE("ETestRT::Main").tag("发送").tag("检查"); KYLOG(INFO) << "Hello world, Kiyun!\n"; //可保存到日志文件 KYIO(log) << "Hello World,Kiyun!\n"; //发送到控制台 /测试代码/ if(!com1.isValid()) { KYIO(err) << "COM232 channels initial failed."; return -1; //返回值需要根据该代码片段所处函数返回值类型设定 } KYIO(log) <<com1.path(); sync_rw(); async_rw(); //f_exitSem.wait(); return 0; } bool Exit(Os::Task_T) { //返回值为true,表示能中止正在运行的实时任务 //f_exitSem.notifyOne(); //return true; return false; //默认情况下该实时任务不能被中止 }

1.1.3.2 Com::Channel_T

本类型所在命名空间为:Kiyun::LowerComputer::Rasl::Device,是进行串口总线操作的主体。具有以下类型和方法:

1) isValid方法

检测通道是否有效。强烈建议使用前判断通道合法性。

函数原型:bool isValid() const

参数:无

返回值:true:通道有效,fasle:通道无效

2) path方法

在进行数据上传时获取通道标识。

函数原型:Path_T path() const

参数:无

返回值:通道标识符

3) WriteHandler_T类型

typedef std::function<void(size_t, Error_T)> WriteHandler_T;

4) asyncWrite方法

异步输出。

函数原型:void asyncWrite(const char* buf, size_t size, WriteHandler_T whenDone)

参数:buf:输出数据的首地址;size:数据长度;whenDone:回调函数。

返回值:无

5) IntRead_T类型

typedef std::function<bool(char*, size_t, Error_T)> IntReader_T;

6) intRead方法

中断读。读取结束时调用参数中的回调函数。

函数原型:void intRead(const IntRead_T& hdl)

参数:hdl:回调函数,函数返回值表示“是否继续接收”,true继续,false停止

返回值:无

7) read方法

同步读。

函数原型:Error_T read(char* buf, size_t size, Timeout_T timeout = 1_s)

参数:buf:存放输入数据的地址;size:读取的字节数;timeout:超时等待时间,默认为1s

返回值:错误码

8) write方法

同步写

函数原型:Error_T write(const char* buf, size_t size, Timeout_T timeout = 1_s)

参数:buf:存放输出数据的地址;size:输出的字节数;timeout:超时等待时间,默认为1s

返回值:错误码

  1. clear方法

清除缓冲区

函数原型:void clear();

参数:无。

返回值:无。

  1. empty方法

判断缓冲区是否为空

函数原型:bool empty();

参数:无。

返回值:布尔型,是/否为空。

1.1.4 CAN

使用CAN总线主要用到的类型是Can_T类型。本节对CAN总线操作进行介绍。

1.1.4.1 使用方法及范例

在ETest系统中,CAN总线操作主要由Can_T::Channel_T类型完成。

在使用ETest进行仿真模型设计和设备规划过程中,可以建立CAN总线通道,并且配置参数。当通道建立完成并且参数配置完成后,系统运行环境会自动创建Can_T::Channel_T类型的通道,名称和在“仿真模型”中建立的通道名称一致。在实时脚本中,即可直接使用该对象,进行数据的采集操作。

如,交联环境图按照下图配置。

对象f_can1f_can2即为Can_T::Channel_T类型的对象,可以用来完成CAN总线功能。

示例脚本如下:

| /** 系统固定头文件 / #include <Rasl/rasl.hpp> #include <DataUploader/DataUploader.hpp> #include "StaticVariable.h" #include "rasl-dpd/user.hpp" #include "UserChannels.rasi" #include "DataCollect.hpp" /* 系统固定头文件 */ //用户测试代码需要新增的头文件 namespace{ using namespace Kiyun::LowerComputer; using namespace Kiyun::LowerComputer::Rasl; using namespace Kiyun::LowerComputer::Rasl::Frame; using namespace Kiyun::LowerComputer::Rasl::Device; //通道设备命名空间 using namespace Kiyun::LowerComputer::DataUploader; //数据上传命名空间 using namespace Kiyun::Channel; using namespace Kiyun::DataCollect; //数据采集接口命名空间 using namespace Dpdp; //协议命名空间 using namespace Frame; using namespace std; //本地变量、本地函数声明 //Os::Semaphore_T f_exitSem; //Can 2路通道,0~1 void f_can_asynRead(void); void f_can_asynWrite(void); void f_can_syncRead(void); void f_can_syncWrite(void); void f_can_printf(const Can_T::CanFrame_T& frameWrite); void f_can_printf(const vector<uint8_t>& vet); } //主函数入口 int Main() { KYIO(out) << "Can Test"; if (!f_can1.isValid()||!f_can2.isValid())//使用通道前,建议判断通道是否可用 { KYIO(err) << "can channels is inVaild ,exit..."; return -1; } //同步读写 f_can_syncWrite(); Timer_T::delay(0.1); f_can_syncRead(); //中断读写 f_can_asynRead(); f_can_asynWrite(); Timer_T::delay(0.1); KYIO(log)<<f_can1.path(); KYIO(log)<<f_can2.path(); return 0; } namespace { void f_can_syncWrite() { Can_T::CanFrame_T frameWrite;//有默认值,标准帧数据帧 frameWrite.id = 1234; frameWrite.dataCnt = 8; for (size_t i = 0; i < frameWrite.dataCnt; i++) { frameWrite.dataBuf[i] = i + 10; } Error_T err = f_can1.write(frameWrite); if (err) KYIO(err) << "write err:" << err.message(); else KYIO(log) << "write OK"; } void f_can_syncRead() { Can_T::CanFrame_T frameRead; auto err = f_can2.read(frameRead); if (!err) { f_can_printf(frameRead); KYIO(log) << "goto buf: "; auto vet = Can_T::toBuf(frameRead); f_can_printf(vet); } else { KYIO(err) << "read err:" << err.message(); } } void f_can_asynWrite() { Can_T::CanFrame_T frameWrite; frameWrite.idFormat = Can_T::IdMode_E::EXTENDED_e; frameWrite.frameFormat = Can_T::FrameMode_E::DATA_e; frameWrite.id = 0x02; frameWrite.dataCnt = 8; for (size_t i = 0; i < frameWrite.dataCnt; i++) { frameWrite.dataBuf[i] = i + 50; } f_can1.asyncWrite(frameWrite, [](Error_T err) { if(!err) KYIO(out) << "async write OK"; }); } void f_can_asynRead() { f_can2.intRead([&](Can_T::CanFrame_T frameRead,Error_T err) { static int sum = 1; f_can_printf(frameRead); if (0 == (sum++) % 10) return false; return true; }); } void f_can_printf(const Can_T::CanFrame_T& frameRead) { std::string str = ""; char ch[20]; str.append("received | "); sprintf(ch, "0x%04x | ", frameRead.id); str.append(ch); if (Can_T::FrameMode_E::REMOTE_e == frameRead.frameFormat) { str.append("remote | "); } else if(Can_T::FrameMode_E::DATA_e == frameRead.frameFormat) { str.append("data | "); } if (Can_T::IdMode_E::EXTENDED_e == frameRead.idFormat) { str.append("extended | "); } else if(Can_T::IdMode_E::STANDARD_e == frameRead.idFormat) { str.append("standard | "); } sprintf(ch, "0x%x | ", frameRead.dataCnt); str.append(ch); str.append("x| "); for (size_t i = 0; i < frameRead.dataCnt; i++) { sprintf(ch, "%02x ", frameRead.dataBuf[i]); str.append(ch); } KYIO(log) << str; } void f_can_printf(const vector<uint8_t>& vet) { KYIO(log) << "buffer size = " << vet.size(); string str; char buffer[20]; str.append("received | "); for (auto ch : vet) { sprintf(buffer, "%02x ", ch); str.append(buffer); } KYIO(log) << str; } } bool Exit(Os::Task_T) { //返回值为true,表示能中止正在运行的实时任务 //f_exitSem.notifyOne(); //return true; return false; //默认情况下该实时任务不能被中止 } | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------