便携式航电实时系统测试平台实时脚本介绍

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

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

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

    1.  接口操作

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

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

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

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

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

      1. AD接口

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

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

        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;   //返回值需要根据该代码片段所处函数返回值类型设定
     }

    float adReadData = f_ad.read();  //返回值为单次采集得到的数据
    KYIO(out) << "AD = " << adReadData;

单次采集多个点:

//判断通道初始化是否成功
      if(!f_ad.isValid()) {
         KYIO(err) << "AD channel initial failed";
         return -1;    //返回值需要根据该代码片段所处函数返回值类型设定
      }

    /*
    需要先申请一块内存空间存储AD采集到的数据,并指定当次AD采集点的数目
    如下所示:申请了一块内存空间readBuf存储采集到的数据,并指定采集点的数目为"c_采样点数"
    */
    const size_t c_采样点数 = 1024;
    std::unique_ptr<float> readBuf(new float[c_采样点数]());
    
    if(f_ad.read(readBuf.get(), c_采样点数)){
        KYIO(log) << "f_ad channels read success.";
    }
    else{
        KYIO(err) << "f_ad channels read failed.";
        return -1;    //返回值需要根据该代码片段所处函数返回值类型设定
    }

连续采集:

  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. Ad_T类

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

  1. IntReader_T类型

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

类型定义为:

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

        1. Ad_T::Channel_T类

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

  1. isValid方法

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

函数原型:bool isValid() const

参数说明:无

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

  1. path方法

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

函数原型:Path_T path() const

参数:无

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

  1. read方法一

采集一次数据。

函数原型:float read()

参数:无

返回值:采集到的数据

  1. read方法二

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

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

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

返回值:操作是否成功

  1. intRead方法

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

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

函数功能:

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

返回值:无

  1. isRuning方法

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

函数原型:BOOL isRuning()

参数:无

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

  1. start方法

开始连续采集任务。

函数原型:BOOL start()

参数:无

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

  1. stop方法

停止连续采集任务。

函数原型:BOOL stop()

参数:无

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

  1. reset方法

重置连续采集通道

函数原型:BOOL reset ()

参数:无

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

      1. DA接口

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

        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. Da_T::Channel_T

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

  1. isValid方法

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

函数原型:bool isValid() const

参数:无

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

  1. path方法

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

函数原型:Path_T path() const

参数:无

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

  1. write方法

设置输出幅值。

函数原型:void write(float value)

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

返回值:无

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

  1. start方法

开始DA输出。

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

函数原型:void start()

参数:无

返回值:无

  1. stop方法

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

函数原型:void stop()

参数:无

返回值:无

      1. 串口

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

        1. 使用方法及范例

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

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

如,交联环境如下配置:

对象com0com1即为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. Com::Channel_T

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

  1. isValid方法

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

函数原型:bool isValid() const

参数:无

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

  1. path方法

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

函数原型:Path_T path() const

参数:无

返回值:通道标识符

  1. WriteHandler_T类型

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

  1. asyncWrite方法

异步输出。

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

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

返回值:无

  1. IntRead_T类型

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

  1. intRead方法

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

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

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

返回值:无

  1. read方法

同步读。

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

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

返回值:错误码

  1. 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. CAN

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

        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;  //默认情况下该实时任务不能被中止
}

        1. Can::Channel_T

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

  1. isValid方法

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

函数原型:bool isValid() const

参数:无

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

  1. path方法

获取通道标识,在数据上传时使用。

函数原型:Path_T  path() const

参数:无

返回值:通道标识符

  1. asyncWrite方法

异步输出。

函数原型:void  asyncWrite(CanFrame_T& can, const WriteHandler_T& hdl)

参数:can:输出数据的首地址;hdl:回调函数。

返回值:无。

  1. intRead方法

中断读。

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

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

返回值:无。

  1. read方法

同步读。

函数原型:BOOL read(Can_T::CanFrame_T & frame, Timeout_T timeout = 1_s)

参数:frame:存放输入数据的地址;timeout:超时等待时间,默认为1s。

返回值:操作是否成功。

  1. write方法

同步写。

函数原型:BOOL write(const Can_T::CanFrame_T & frame,Timeout_T timeout = 1_s);

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

返回值:操作是否成功。

      1. DI/DO接口

使用数字量输入/输出操作主要用到的类型是Dio_T类型。本节对数字量输入/输出操作进行介绍。

        1. 使用方法及范例

在ETest系统中,数字量输入/输出操作主要由Dio_T::Channel_T类型完成。

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

如,在以下范例中,创建交联环境图如下:

对象CH_数字输入_1CH_数字输出_1即为Dio::Channel_T类型的对象,可以用来完成数字量输入/输出功能。

在设备规划中需要设置DI通道的属性“跳变检测模式”为正确的模式,才能在intRead函数中进行跳变沿检测处理。

示例代码如下:

/** 系统固定头文件 */
#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;
}

//主函数入口
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(!CH_数字输出_1.isValid()) {
         KYIO(err) << "CH_数字输出_1 channels initial failed.";
         return -1;      //返回值需要根据该代码片段所处函数返回值类型设定
      }
      
    KYIO(log) <<CH_数字输出_1.path();
       
    if(!CH_数字输入_1.isValid()) {
         KYIO(err) << "CH_数字输出_1 channels initial failed.";
         return -1;      //返回值需要根据该代码片段所处函数返回值类型设定
      }
      
    KYIO(log) <<CH_数字输入_1.path();
    
    CH_数字输入_1.intRead([](int chID,int state) {
        KYIO(out) << "Notify ..." << chID << "  "<< state;
        return true;
      });

      CH_数字输出_1.write(TRUE);
      Timer_T::delay(1);
      if (CH_数字输入_1.read())  {
          KYIO(log) << "DI HIGH";
      }
      else  {
           KYIO(log) << "DI LOW";
      }
    Timer_T::delay(5);
    
      CH_数字输出_1.write(FALSE);
      Timer_T::delay(1);
      if (CH_数字输入_1.read())  {
          KYIO(log) << "DI HIGH";
      }
      else  {
          KYIO(log) << "DI LOW";
       }
          CH_数字输出_1.write(TRUE);
      Timer_T::delay(1);
      
    Timer_T::delay(5);

    //f_exitSem.wait();
    return 0;
}

bool Exit(Os::Task_T) {
    //返回值为true,表示能中止正在运行的实时任务
    //f_exitSem.notifyOne();
    //return true;
    
    return false;  //默认情况下该实时任务不能被中止
}

        1. Dio::Channel_T

该类所在的命名空间:Kiyun::LowerComputer::Rasl::Device。具有以下方法:

  1. isValid方法

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

函数原型:bool isValid() const

参数:无

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

  1. path方法

获取通道标识。数据上传时会用到。

函数原型:Path_T path() const

参数:无

返回值:通道标识符

  1. read方法

获取数字量输入或者状态。

函数原型:BOOL read();

参数:

返回值:输出状态,TRUE为高电平,FALSE为低电平。

  1. write方法

设置数字量输出状态。

函数原型:void write(BOOL status)

参数:status:输出状态,TRUE为高电平,FALSE为低电平

返回值:无

  1. intRead方法

数字量输入跳变检测。

函数原型:void intRead(Channel_T::IORVectorHandler_T inputRVector)

参数:inputRVector 跳变检测回调函数,函数说明为:

Bool inputRVectorint idint state

参数id为发生跳变的通道号。

参数state为当前通道的状态(0或者1)。

返回值表示“是否继续检测”,true继续,false停止。

返回值:

      1. 1553B接口

使用UDP接口操作主要用到的类型是M1553_T类型。本节对1553B接口操作进行介绍。

        1. 使用方法及范例

在ETest系统中,1553B接口操作主要由M1553_T类型完成。

在使用Etest进行仿真模型设计和设备规划过程中,可以建立1553BC、1553RT、1553MT、1553BSP通道,并且配置参数。当通道建立完成并且参数配置完成后,系统运行环境会自动创建对于类型的通道,名称和在“仿真模型”中建立的通道名称一致。在实时脚本中,即可直接使用该对象,进行数据的读/写操作。

创建仿真模型如下图所示。

  • 1 1553范例仿真模型

在设备规划中,选中设备,在“属性”窗体编辑属性,建立消息列表。

  • 2 1553范例设备规划

  • 3 1553范例消息编辑器

编写如下实时脚本,完成消息的收发。

同步读写:

/** 系统固定头文件 */
#include <Rasl/rasl.hpp>
#include <DataUploader/DataUploader.hpp>
#include "StaticVariable.h"
#include "rasl-dpd/user.hpp"
#include "UserChannels.rasi"
#include "DataCollect.hpp"
/** 系统固定头文件 */
//#define MC_APPNAME "M1553-TEST"
#include <Rasl/rasl.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 std;
      using namespace Mince::LowerComputer::Rasl;
      using namespace Mince::LowerComputer::Rasl::Frame;
      using namespace Mince::LowerComputer::Rasl::Device;
      void fc_msgPrint(M1553_T::RecvMsg_T* rmsg, size_t size);
      void fc_msgPrint(const std::vector<uint8_t>& buf);
}

int test1553() 
{

      MCIO(log) << "-------------1553 TEST-----------------";
      if (!mt.isValid()) {
            MCIO(err) << "bm is inValid...exit";
            return -1;
      }
      if (!dsp.isValid()) {
            MCIO(err) << "dsp is inValid...exit";
            return -1;
      }

      uint16_t outData[32] = {0};
      for (int i = 0; i < 32; i++)
            outData[i] = 0x1234 + i;

      //BC to RT
      bc0.write(outData, 32);

      //RT to BC
      outData[0] = 0x4321;
      rt5.write(outData, 32);

      //RT to RT
      outData[0] = 0x2134;
      rt2.write(outData, 32);

      //执行消息
      dsp.write(0);
      dsp.write(1);
      dsp.write(2);


      //延时保证消息已处理
      Timer_T::delay(0.1);

      MCIO(log) << "-------------BC READ-----------------";
      fc_msgPrint(bc0.read());
      fc_msgPrint(bc1.read());
      fc_msgPrint(bc2.read());



      MCIO(log) << "-------------RT READ-----------------";
      MCIO(log) << "------rt 2,7 read";
      fc_msgPrint(rt2.read());
      MCIO(log) << "------rt 5,8 read";
      fc_msgPrint(rt5.read());
     

      MCIO(log) << "-------------MT READ-----------------";

      //fc_msgPrint(mt.read());
      M1553_T::RecvMsg_T rmsg[10];
      size_t res = 0;
      auto err = mt.read(rmsg, 10, res);
      if (!err)
            fc_msgPrint(rmsg, res);
      else
            MCIO(err) << "BM read err:" << err.message();
      return 0;
}

int Main()
{
      return test1553();
}

namespace {

      void fc_msgPrint(M1553_T::RecvMsg_T* rmsg, size_t size)
      {
            char ch[100];
            std::string str;
            for (size_t i = 0; i < size; i++)
            {
                  str = "";
                  snprintf(ch, sizeof(ch), "bsw = 0x%04x,", rmsg[i].bsw);
                  str.append(ch);
                  snprintf(ch, sizeof(ch), "cmd1 = 0x%04x,", rmsg[i].msgBlock.cmdWord1);
                  str.append(ch);
                  snprintf(ch, sizeof(ch), "cmd2 = 0x%04x,", rmsg[i].msgBlock.cmdWord2);
                  str.append(ch);
                  str.append(" data : ");
                  for (size_t j = 0; j < 32; ++j)
                  {
                        snprintf(ch, sizeof(ch), "%04x ", rmsg[i].msgBlock.dataBlk[j]);
                        str.append(ch);
                  }
                  MCIO(log) << str;
            }
      }

      void fc_msgPrint(const std::vector<uint8_t>& buf)
      {
            char ch[100];
            std::string str;

            auto wordBuf = M1553_T::toWord(buf);

            for (const auto& val: wordBuf)
            {
                  snprintf(ch, 100, "%04x ", val);
                  str.append(ch);
            }
            MCIO(log) <<"data : "<< str;
      }
}

异步读写

/** 系统固定头文件 */
#include <Rasl/rasl.hpp>
#include <DataUploader/DataUploader.hpp>
#include "StaticVariable.h"
#include "rasl-dpd/user.hpp"
#include "UserChannels.rasi"
#include "DataCollect.hpp"
/** 系统固定头文件 */
//#define MC_APPNAME "M1553-TEST"
#include <Rasl/rasl.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 std;
      using namespace Mince::LowerComputer::Rasl;
      using namespace Mince::LowerComputer::Rasl::Frame;
      using namespace Mince::LowerComputer::Rasl::Device;
      void fc_msgPrint(M1553_T::RecvMsg_T* rmsg, size_t size);
      void fc_msgPrint(const std::vector<uint8_t>& buf);
}

int test1553() 
{

      MCIO(log) << "-------------1553 TEST-----------------";
      if (!mt.isValid()) {
            MCIO(err) << "bm is inValid...exit";
            return -1;
      }
      if (!dsp.isValid()) {
            MCIO(err) << "dsp is inValid...exit";
            return -1;
      }

      uint16_t outData[32] = {0};
      for (int i = 0; i < 32; i++)
            outData[i] = 0x1234 + i;

      //BC to RT
      bc0.write(outData, 32);

      //RT to BC
      outData[0] = 0x4321;
      rt5.write(outData, 32);

      //RT to RT
      outData[0] = 0x2134;
      rt2.write(outData, 32);

      //执行消息
      dsp.write(0);
      dsp.write(1);
      dsp.write(2);


      //延时保证消息已处理
      Timer_T::delay(0.1);

      dsp.write(0, false);
      Timer_T::delay(1);

      if (!dsp.isRunning())
           MCIO(log) << "bc stoped...";
      else
           MCIO(log) << "bc is running...";

      uint32_t isRunning = 1;
      while (isRunning) {
           isRunning = dsp.isRunning();
      }
      printf("BC stopped.\n");

      bc0.intRead([&](uint16_t * buf, size_t size,Error_T err)->bool {
           MCIO(log) << "-----------------------bc intRead:" << (int)buf[0];
           return true;
      });

      rt2.intRead([&](uint16_t * buf, size_t size, Error_T err)->bool {
           MCIO(log) << "-----------------------rt intRead:" << (int)buf[0];
           return true;
      });

      mt.intRead([&](uint16_t * buf, size_t size, Error_T err)->bool {
           MCIO(log) << "------------------------mt intRead:" << (int)buf[0];
           return true;
      });
      
      return 0;
}

int Main()
{
      return test1553();
}

namespace {

      void fc_msgPrint(M1553_T::RecvMsg_T* rmsg, size_t size)
      {
            char ch[100];
            std::string str;
            for (size_t i = 0; i < size; i++)
            {
                  str = "";
                  snprintf(ch, sizeof(ch), "bsw = 0x%04x,", rmsg[i].bsw);
                  str.append(ch);
                  snprintf(ch, sizeof(ch), "cmd1 = 0x%04x,", rmsg[i].msgBlock.cmdWord1);
                  str.append(ch);
                  snprintf(ch, sizeof(ch), "cmd2 = 0x%04x,", rmsg[i].msgBlock.cmdWord2);
                  str.append(ch);
                  str.append(" data : ");
                  for (size_t j = 0; j < 32; ++j)
                  {
                        snprintf(ch, sizeof(ch), "%04x ", rmsg[i].msgBlock.dataBlk[j]);
                        str.append(ch);
                  }
                  MCIO(log) << str;
            }
      }

      void fc_msgPrint(const std::vector<uint8_t>& buf)
      {
            char ch[100];
            std::string str;

            auto wordBuf = M1553_T::toWord(buf);

            for (const auto& val: wordBuf)
            {
                  snprintf(ch, 100, "%04x ", val);
                  str.append(ch);
            }
            MCIO(log) <<"data : "<< str;
      }
}

        1. M1553_T类

类型所在命名空间:Kiyun::LowerComputer::Rasl::Device

  1. isValid方法

检查通道是否有效。

函数原型:bool isValid() const

参数:无

返回值:true:对象初始化有效,fasle:对象初始化无效

  1. path方法

获取通道标识。

函数原型:Path_T path() const

参数:无

返回值:通道标识符

  1. isRunning

函数原型:BOOL isRunning ()

函数功能:BC 是否开始消息处理

参数说明:

返回值:TRUE : BC正在运行;FALSE:BC 未运行

  1. write

函数原型:BOOL write(uint16_t msgId = 4095, BOOL bRun = TRUE)

函数功能:BC消息运行控制函数,仅供DSP通道使用

参数说明:msgid : 消息的id号,从0开始编号,到4094结束 ,id 大于4094则表示运行所有消息

bRun:消息运行控制位:

     TRUE:消息编号为msgId的消息开始执行

     FALSE:消息编号为msgId的消息不再执行

返回值:TRUE:表示执行成功;FALSE :表示失败

  1. write

函数原型:BOOL write(const uint16_t* data, size_t size)

函数功能:修改发送BC,RT 消息的数据块

参数说明:data: 数组首地址 ; size:数组大小,最多32个字,RT为循环缓冲size最大为4096

返回值:TRUE:表示执行成功;FALSE :表示失败

注:write之后需要DSP 执行此消息才能发送数据,否则只修改了发送缓冲区数据

  1. read

函数原型:BOOL read(uint16_t* data, size_t size,size_t& res);

函数功能:BC RT MT接收到的数据

参数说明:data: 存放收到的数据字的数组首地址

size: 存放数据的数组大小

res:实际读到的数据字个数

返回值:TRUE:表示执行成功;FALSE :表示当前没有可读的数据

注:没有读到数据时,请检查DSP是否执行了此消息。

  1. read

函数原型:std::vector<uint8_t> read()

函数功能:BC RT MT接收到的数据的字节流(字节顺序小端)

参数说明: 无

返回值:  vector的size() 不为0则表示读取成功,否则读取失败

  1. read

函数原型:BOOL read(RecvMsg_T* msg, size_t size,size_t& res)

函数功能:MT同步读,仅供MT通道使用

参数说明: msg:MT接收数据消息的缓冲区地址

size :缓冲区大小

res:实际读取到的消息个数

返回值:TRUE:读取成功;FALSE:读取失败(没有数据)

  1. intRead

函数原型:void IntRead(const IntReader_T& hdl)

函数功能:BC RT MT中断读,通道取值范围0~1

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

返回值:  无

      1. ARINC429接口

使用ARINC429接口操作主要用到的类型是M429_T类型。本节对ARINC429接口操作进行介绍。

        1. 使用方法及范例

在ETest系统中,ARINC429接口操作主要由M429_T类型完成。

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

如,在以下范例中,创建仿真模型如下。

  • 4 429范例仿真模型

编写实时测试用例如下所示。

1、同步读写功能

/** 系统固定头文件 */
#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;                                 //协议命名空间
    const size_t fc_inBufSize = 1024;  
    //本地变量、本地函数声明
    //Os::Semaphore_T f_exitSem;
}
extern "C"

int minceMain() {
    MCIO(out) << "----------M429_TEST---------";

    uint32_t inBuf[fc_inBufSize + 1];
    uint32_t outBuf[25];
    const uint16_t c_outBufSize = sizeof(outBuf) / sizeof(outBuf[0]);
    for (int i = 0; i < (int)c_outBufSize; i++)
        outBuf[i] = i;
    Error_T err = Error_T();

    outBuf[0] = 0x54030201;
    outBuf[1] = 0x2177;
    outBuf[2] = 0xffff;
    outBuf[3] = 0xffffff;
    outBuf[4] = 0xffffffff;
    if (!CH_429S_1.isValid()) {
        MCIO(err) << "channel is InValid";
        return -1;
    }
    //同步写
    err = CH_429S_1.write(outBuf, 5);
    if (err)
        MCIO(err) << "error when write  : " << err.message();
    else
        MCIO(log) << "Written " << c_outBufSize << " Is Over...";

    for (int i =0 ;i< 5;++i)
    {
        //同步读
        uint16_t readSize = 1;
        err = CH_429D_1.read(inBuf, readSize);
        if (!err) {
            std::string str;
            char ch[100];
            for (int i = 0; i < (int)readSize; i++)
            {
                snprintf(ch, sizeof(ch), "0x%x ", inBuf[i]);
                str.append(ch);
            }
            MCIO(log) << "ch0 sync read :" << str;
        }
        else
            MCIO(log) << "No " << readSize << " data to Read ,Read TimeOut ...";
    }
    
    return 0;
}

  1. 异步读写功能

/** 系统固定头文件 */
#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;                                 //协议命名空间
    const size_t fc_inBufSize = 1024;  
    //本地变量、本地函数声明
    //Os::Semaphore_T f_exitSem;
}
extern "C"

int minceMain() {
    MCIO(out) << "----------M429_TEST---------";

    uint32_t inBuf[fc_inBufSize + 1];
    uint32_t outBuf[25];
    const uint16_t c_outBufSize = sizeof(outBuf) / sizeof(outBuf[0]);
    for (int i = 0; i < (int)c_outBufSize; i++)
        outBuf[i] = i;
    Error_T err = Error_T();

    outBuf[0] = 0x54030201;
    outBuf[1] = 0x2177;
    outBuf[2] = 0xffff;
    outBuf[3] = 0xffffff;
    outBuf[4] = 0xffffffff;
    if (!CH_429S_1.isValid()) {
        MCIO(err) << "channel is InValid";
        return -1;
    }
    //同步写
    err = CH_429S_1.write(outBuf, 5);
    if (err)
        MCIO(err) << "error when write  : " << err.message();
    else
        MCIO(log) << "Written " << c_outBufSize << " Is Over...";

    for (int i =0 ;i< 5;++i)
    {
        //同步读
        uint16_t readSize = 1;
        err = CH_429D_1.read(inBuf, readSize);
        if (!err) {
            std::string str;
            char ch[100];
            for (int i = 0; i < (int)readSize; i++)
            {
                snprintf(ch, sizeof(ch), "0x%x ", inBuf[i]);
                str.append(ch);
            }
            MCIO(log) << "ch0 sync read :" << str;
        }
        else
            MCIO(log) << "No " << readSize << " data to Read ,Read TimeOut ...";
    }
    
    return 0;
}

        1. M429_T类

类型所在命名空间:Kiyun::LowerComputer::Rasl::Device

  1. isValid

函数原型:bool isValid() const(强烈建议使用前判断通道合法性)

函数功能:检测通道是否有效

参数说明:无

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

  1. path

函数原型:Path_T path() const

函数功能:获取通道标识

参数说明:无

返回值:通道标识符

  1. asyncWrite

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

函数功能:异步输出,通道取值范围0~7

参数说明:buf:429输出数据的首地址;size :数组的大小(最多511个);timeout:超时等待时间,默认为1s

返回值:无

  1. intRead

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

函数功能:中断读,通道取值范围0~7

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

返回值:无

  1. read

函数原型:Error_T    read (uint32_t* buf, uint16_t readSize,Timeout_T timeout = 1_s);

函数功能:同步读,通道取值范围0~7

参数说明:buf:存放429输入数据的地址; readSize :需读取的数据个数(需要buf能存放下);timeout:超时等待时间,默认为1s

返回值:错误码,0表示成功,其他则表示有出错

  1. write

函数原型:Error_T write(const uint32_t* buf, uint16_t size, Timeout_T timeout = 1_s);

函数功能:同步写,通道取值范围0~7

参数说明:buf:存放输出数据的首地址;size :数组的大小(最多511个);timeout:超时等待时间,默认为1s

返回值:错误码,0表示成功,其他则表示有出错

      1. FC接口

使用FC接口操作主要用到的类型是FC_T类型。本节对FC接口操作进行介绍。

        1. 使用方法及范例

在ETest系统中,FC接口操作主要由FC_T类型完成。

在使用Etest进行仿真模型设计和设备规划过程中,可以建立FC通道,并且配置参数。当通道建立完成并且参数配置完成后,系统运行环境会自动创建MFC_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;                                 //协议命名空间
    using namespace std;
    
    //本地变量、本地函数声明
    //Os::Semaphore_T f_exitSem;
}

//主函数入口
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 (!fc0.isValid() || !fc1.isValid()){
        MCIO(err) << "channel is invalid...exit";
        return -1;
    }
    char sendBuf[] = "abc123";
    std::vector<uint8_t> recvBuf;

    // port0 to port1
    Error_T err = fc0.write(sendBuf, sizeof(sendBuf));
    if (err)
        MCIO(log) << "port0 write err:" << err.message();
    else
        MCIO(log) << "port0 write OK";
    Timer_T::delay(0.1);
    recvBuf.clear();
    recvBuf = fc1.read();
    if (!recvBuf.empty()){
        MCIO(log) << "port1 received :" << string(recvBuf.cbegin(), recvBuf.cend());
    }

    // port1 to port0
    sendBuf[0] = 'A';
    err = fc1.write(sendBuf, sizeof(sendBuf));
    if (err)
        MCIO(log) << "port1 write err:" << err.message();
    else
        MCIO(log) << "port1 write OK";
    Timer_T::delay(0.1);
    recvBuf.clear();
    recvBuf = fc0.read();
    if (!recvBuf.empty()){
        MCIO(log) << "port0 received :" << std::string(recvBuf.cbegin(), recvBuf.cend());
    }

    //中断读
    
     fc1.intRead([](uint8_t * buf ,size_t sz, uint32_t id, Error_T err)->bool {
            MCIO(log) << "msg_" << id << "| intRead :" << string(buf, buf + sz);
            return true;
       });
    
    MCIO(log) << "I'm listening...";
    sendBuf[0] = 'B';
    fc0.asyncWrite(sendBuf, sizeof(sendBuf), [](size_t size, Error_T err) {
        if(!err)
            MCIO(log) << "write OK";
    });
    MCIO(log) << fc0.path();
    Timer_T::delay(0.1);
    //f_exitSem.wait();
    return 0;
}

bool Exit(Os::Task_T) {
    //返回值为true,表示能中止正在运行的实时任务
    //f_exitSem.notifyOne();
    //return true;
    
    return false;  //默认情况下该实时任务不能被中止
}

        1. FC_T类

类型所在命名空间:Kiyun::LowerComputer::Rasl::Device

  1. isValid

函数原型:bool isValid() const(强烈建议使用前判断通道合法性)

函数功能:检测通道是否有效

参数说明:无

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

  1. path

函数原型:Path_T path() const

函数功能:获取通道标识

参数说明:无

返回值:通道标识符

  1. read

函数原型:Error_T read(char * buf, size_t size,size_t& res)

函数功能:读取一次

参数说明:buf:存放输入数据的地址;size:缓冲区大小;res: 读取到的字节数

返回值:错误码

  1. write

函数原型:Error_T write(const char* buf, size_t size)

函数功能:同步写

参数说明:buf:存放输出数据的地址;size:输出的字节数

返回值:错误码

  1. asyncWrite

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

函数功能:异步写

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

返回值:无

  1. intRead

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

函数功能:中断读

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

返回值:无

      1. RapidIO接口
        1. 使用方法及范例

在ETest系统中,RapidIO接口操作主要由RapidIO_T类型完成。

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

        1. RapidIO_T类

类所在命名空间:Kiyun::LowerComputer::Rasl::Device

  1. className

函数原型:static const  std::string& className()

函数功能:获取板卡标识

参数说明:无

返回值:板卡类型标识符

  1. get

函数原型:static RapidIO_T get(int id)

函数功能:获取RapidIO板卡对象

参数说明:id:板卡标识号

返回值:RapidIO板卡对象

  1. get

函数原型:static Channel_T get(const Setup_T& cfg)

函数功能:获取RapidIO板卡的一个通道对象

参数说明:cfg:板卡初始化结构

返回值:SRIO通道对象

  1. getChannel

函数原型:Channel_T getChannel(const Channel_T::Setup_T& cfg)

函数功能:获取系统中RapidIO板卡的一个通道对象

参数说明:cfg:板卡初始化结构

返回值:SRIO通道对象

  1. isValid

函数原型:bool isValid() const

函数功能:检查RapidIO板卡对象是否有效(建议使用前判断合法性)

参数说明:无

返回值:true:板卡有效,fasle:板卡无效

        1. Channel_T(SRIO通道)
  1. setup

参数类型

可选值

默认值

设备标识

(deviceID)

16位无符号整数

0

  1. isValid

函数原型:bool isValid() const(强烈建议使用前判断通道合法性)

函数功能:检测通道是否有效

参数说明:无

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

  1. path

函数原型:Path_T path() const

函数功能:获取通道标识

参数说明:无

返回值:通道标识符

  1. asyncWrite

函数原型:void asyncWrite(const Frame_T& frame, WriteHandler_T whenDone)

函数功能:仅支持发送NWriteResp报文

参数说明:frame 为SRIO报文结构体,whenDone 收到响应的报文的回调函数

返回值:无

  1. intRead

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

函数功能:注册总的报文处理回调函数,需根据报文不同类型分开处理

参数说明:回调函数 IntRead_T hdl = bool(const uint8_t* package,size_t size,Error_T err)

1.函数返回值表示“报文是否已处理”,true已处理,false未处理

2. package需要根据不用的类型解析包,包头(8字节)+ 数据(0-256)

3.size表示package的字节长度,err表示错误码(未使用)

返回值:无

  1. read

函数原型:Error_T read(Frame_T& frame)

函数功能:发送NREAD报文,并且等待数据返回

参数说明:frame 为SRIO报文结构体,需要指定目地器件ID(frame.i_dest) 、数据长度(frame.i_len)、数据在目地器件存储器中的地址(frame.i_addr)

返回值:错误码,0表示成功,其他则表示有出错

  1. write

函数原型:Error_T write(const Frame_T& frame)

函数功能:支持发送SRIO所有报文格式(NWRITE、SWRITE 、NWRITER、DOORBELL、RESPND、RESPWD等等)

参数说明:frame 为SRIO报文结构体,需要指定目地器件ID(frame.i_dest) 、数据长度(frame.i_len)、数据在目地器件存储器中的地址(frame.i_addr) 、数据(frame.i_data 0-256)

返回值:错误码,0表示成功,其他则表示有出错

    1.  操作系统相关服务

ETest 提供了 Task_T Semaphore_T Mutex_T Counter_T 等类以提供操作系统相关的服务,可以实现任务调度与协同。

      1. Task_T

所在命名空间为:Kiyun::LowerComputer::Rasl::Os。这个类的对象代表在下位机中运行的任务。

/** 系统固定头文件 */
#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 std;
    using namespace Rasl::Frame;
    using Rasl::Logging_T;
    using Rasl::Os::Task_T;
    
    //本地变量、本地函数声明
    //Os::Semaphore_T f_exitSem;
    int x(0);
    int adder(void*) {
        for(int i = 0; i < 100000; ++i)
            ++x;
        return 0;
    }
}

//主函数入口
int Main()
{
    //设置全局日志属性
      KYLOG_GLOBAL().severity(Logging_T::Severity_E::DEBG_e);
    KYLOG_SCOPE("main").tag("信号相关").tag("硬件");
    KYLOG(INFO) << "Hello world, Kiyun!\n";   //可保存到日志文件
    KYIO(log) << "Hello World,Kiyun!\n";      //发送到控制台

    /*测试代码*/

    vector<Task_T> tasks;
    for(int i = 0; i < 200; ++i) {
        tasks.push_back(Task_T::spawn(adder).first);
    }
    for(auto t : tasks)
        t.wait();
        
    KYLOG(INFO) << "x : " << x;
    
    KYLOG_DEF(INFO).tag("单项标记");
    KYLOG_USE() << "Y : " << x;
    
    //f_exitSem.wait();
    return 0;
}

bool Exit(Os::Task_T) {
    //返回值为true,表示能中止正在运行的实时任务
    //f_exitSem.notifyOne();
    //return true;
    
    return false;  //默认情况下该实时任务不能被中止
}

  1. spawn方法

函数原型:std::pair<Task_T, Error_T> spawn(const SimpleEntry_T& ent)

函数功能:启动一个任务。

参数:ent:型如 int (void*) 的函数。其参数忽略即可,正常情况应返回 0。

返回值:一个 pair 对象,first 成员是启动的 Task_T 对象,second 成员是错误情况。大多数情况下,second 成员可以安全的忽略。

  1. wait方法

函数原型:void wait();

函数功能:等待任务结束。调用者任务本身阻塞,直到所 wait 的任务退出才继续运行。必须在 spawn 所返回的 Task_T 对象上使用。

  1. waitFor方法

函数原型:

Mince::LowerComputer::Rasl::Error_T waitFor(

    Mince::LowerComputer::Rasl::Frame::Timeout_T t

);

函数功能:等待任务结束或超时。调用者任务本身阻塞,直到所 wait 的任务退出或者超出指定的时间才继续运行。必须在 spawn 所返回的 Task_T 对象上使用。

参数:t:超时时长。

返回值:无错误代表所等待的任务已退出,有错误代表超时。

auto task = Task_T::spawn(someFunction);

auto err = task.waitFor(5_s);

if(err)

    KYLOG(WARN) << "任务在指定时间内未结束!";

      1. Semaphore_T类

所在命名空间为:Kiyun::LowerComputer::Rasl::Os

  1. 构造方法

函数原型:Semaphore_T()

函数功能:初始化一个Semaphore_T对象

参数:无

返回值:无

  1. notifyOne

函数原型:void notifyOne()

函数功能:发送一个信号量,通知正在等待信号量的第一个线程继续执行。

参数:

返回值:无

  1. notifyAll

函数原型:void notifyAll()

函数功能:通知所有等待信号量的线程继续执行。

参数:无

返回值:

  1. wait

函数原型:void wait()

函数功能:等待信号量。执行此方法的任务立即阻塞,直到其它任务在此信号量上调用 notify 为止。

参数:无

返回值:

  1. waitFor

函数原型:bool waitFor(Kiyun::LowerComputer::Rasl::Frame::Timeout_T time)

函数功能:等待信号量的到来,最长等待时间为time,单位为秒。

参数:time:等待时长

返回值: true: 信号量到达;false: 超时。

      1. Mutex_T

所在命名空间为:Kiyun::LowerComputer::Rasl::Os。这个类用作互斥锁,通常用于保护那些“不可同时访问的资源”。

#include <Rasl/rasl.hpp>

namespace {

    using namespace std;

    using namespace Kiyun::LowerComputer;

    using namespace Rasl::Frame;

   

    using Rasl::Os::Task_T;

    using Rasl::Os::Mutex_T;

   

    int x = 0;

    Mutex_T xMut;

    int adder(void*) {

       KYLOG_SCOPE("add");

       xMut.lock();

       for(int i = 0; i < 100000; ++i)

           ++x;

       xMut.unlock();

       KYLOG(INFO) << "加完";

       return 0;

    }

}

int kiyunMain() {

    KYLOG_SCOPE("main");

   

    vector<Task_T> tasks;

    for(int i = 0; i < 200; ++i) {

       tasks.push_back(Task_T::spawn(adder).first);

    }

    for(auto t : tasks)

       t.wait();

      

    KYLOG(INFO) << "x : " << x;

    return 0;

}

  1. lock 方法

函数原型:void lock();

函数功能:取得锁。如果锁已被其它任务取得,则任务立即阻塞,直到锁被释放为止。如果锁已被本任务取得,再次调用 lock,后果未定义。

参数:无

返回值:无

  1. unlock 方法

函数原型:void unlock();

函数功能:释放锁。

参数:无

返回值:无

  1. tryLock 方法

函数原型:bool tryLock();

函数功能:取得锁。如果锁已被其它任务取得,则返回 false,否则返回 true。如果锁已被本任务取得,再次调用本方法,后果未定义。

参数:无

返回值:true: 取锁成功;false:未取得锁。

  1. Guard_T内嵌类

其构造函数以一个 Mutex_T 对象为参数,并在构造时调用其 lock 方法。在析构时调用对应 Mutex_T 对象的 unlock 方法。

std::queue<命令_T> 队列;

Mutex_T 队列锁;

void 入列(const 命令_T& cmd) {

    MCLOG_SCOPE("入列");

    {   // 用花括号划定锁的保护范围

        Mutex_T::Guard_T guard{队列锁};

        队列.push(cmd);

    }   // guard 对象在此之前析构,这里不再是锁的保护范围

    MCLOG(INFO) << "命令已入列。";

}

      1. Counter_T

所在命名空间为:Kiyun::LowerComputer::Rasl::Os。这个类用作计数器,用于“生产者与消费者”模式时,给资源计数。在多个生产者和/或多个消费者的场合,计数器能够保证资源计数的正确性。

#include <Rasl/rasl.hpp>

#include <queue>

namespace {

    using namespace std;

    using namespace Kiyun::LowerComputer;

    using namespace Rasl::Frame;

    using Rasl::Error_T;

    using Rasl::Device::Com_T;

    using Rasl::Os::Task_T;

    using Rasl::Logging_T;

    auto com00 = Com_T::get(Com_T::Setup_T("com/0/0")

       .baudRate(115200)

       .parity(Com_T::Parity_E::NONE_e)

    );

    auto com01 = Com_T::get(Com_T::Setup_T("com/0/1")

       .baudRate(115200)

       .parity(Com_T::Parity_E::NONE_e)

    );

    auto com02 = Com_T::get(Com_T::Setup_T("com/0/2")

       .baudRate(115200)

       .parity(Com_T::Parity_E::NONE_e)

    );

    queue<string> que;

    Rasl::Os::Mutex_T bufMut;

    Rasl::Os::Counter_T readDone;

   

    struct Reader_T {

       string rs;

       bool operator() (char* buf, size_t size, Error_T) {

           rs += string{buf, buf + size};

           auto done = rs.size() >= 7;

           if(done) {

              {

                  Rasl::Os::Mutex_T::Guard_T guard(bufMut);

                  que.emplace(move(rs));

              }

              readDone.count();

           }

           return true;

       }

    };

}

int kiyunMain() {

    KYLOG_GLOBAL().severity(Logging_T::Severity_E::INFO_e);

    KYLOG_SCOPE("main");

    Reader_T reader0, reader1, reader2;

    com00.intRead(reader0);

    com01.intRead(reader1);

    com02.intRead(reader2);

    // 10秒钟没有输入则退出

    while(readDone.waitFor(10_s)) {

       string rs = que.front();

       que.pop();

       KYLOG(INFO) << rs;

    }

    return 0;

}

  1. count方法

函数原型:void count();

函数功能:使资源计数加1。应当由“生产者”调用,表示可供“消费者”使用的资源多了一个。

参数:无

返回值:无

  1. countAll方法

函数原型:void countAll();

函数功能:应当由“生产者”调用。满足当前所有等待资源的“消费者”。

参数:无

返回值:无

  1. wait方法

函数原型:void wait();

函数功能:使资源计数减1。应当由“消费者”调用,如果当前资源计数为0,则当前任务立即阻塞,直到有“生产者”将资源计数增加为止。

参数:无

返回值:无

  1. waitFor方法

函数原型:bool waitFor(Mince::LowerComputer::Rasl::Frame::Timeout_T);

函数功能:使资源计数减1或者超时。应当由“消费者”调用,如果当前可用资源计数为0,则当前任务立即阻塞,直到有“生产者”增加资源计数或超时为止。

参数:超时时限。

返回值:true:资源计数减1;false:超时。

    1.  时序框架

Etest实时脚本API提供了Frequency_T、Period_T、Ticker_T和 Timer_T等类,以便进行与时序有关的操作。

      1. 使用方法及范例

创建周期性触发的系统时钟使用Ticker_T类型。可以先创建一个Ticker_T对象,指定其触发周期,然后使用Start方法启动定时器,在启动的时候给出的参数为每次时钟周期到时被调用的函数。最后可以使用stop方法停止定时器。如以下范例:

#define MC_APPNAME "TickerTest" // 自动用作输出前缀

#include <Rasl/rasl.hpp>

#include "rasl-dpd/user.hpp"

#include "UserChannels.rasi"

namespace {

    using namespace Kiyun::LowerComputer::Rasl::Frame;

    using namespace std;

}

int kiyunMain() {

    KYIO(log) << "Setting ...";

    unsigned mtCount = 0;

    Ticker_T ticker1 = Ticker_T::get(2.0_s);   // 2.0 秒,指定周期值

    Ticker_T ticker2 = Ticker_T::get(0.8_hz);  // 0.8 赫兹,指定频率值

    Ticker_T ticker3 = Ticker_T::get(0.8);     // Ticker 默认接受频率值

    auto ticker4 = Ticker_T::get(10_hz);

    ticker1.start([]() -> bool {

        KYIO(out) << " =-+---- 2.0 seconds ";

        return true;

    });

    ticker2.start([]() -> bool {

        KYIO(out) << " =-+---- 0.8 herz";

        return true;

    });

    ticker3.start([]() -> bool {

        KYIO(out) << " =-+---- 0.8";

        return true;

    }); //

    ticker4.start([&mtCount]{

        ++mtCount;

        return true;

    });

    KYIO(log) << "Fired ...";

    Timer_T::delay(5.0);    // Timer 默认接受时长(周期),5秒后停止时钟

    KYIO(out) << "Ticker count : " << mtCount;

    ticker1.stop();

    ticker2.stop();

    ticker3.stop();

    ticker4.stop();

    return 0;

}

创建一次性的系统延时可以使用Timer_T类型的delay方法。

使用方法为:

Timer_T::delay(5.0);    // Timer 结束时长(单位为秒)

      1. Frequency_T类与 Period_T 类

所在命名空间:Kiyun::LowerComputer::Rasl::Frame

这两个类分别代表 频率 与 周期(时长)。其中 Period_T 有别名为 Timeout_T(时长)。

  1. 构造函数

函数原型:

Frequency_T(float);

Period_T(float);

函数功能:初始化一个Ticker_T对象

参数:step:频率(​以赫兹为单位)或周期(以秒为单位)的浮点值。

返回值:Frequency_T / Peroid_T对象

  1. 自定义字面量

_hz 后缀可构造 Frequency_T 的对象;_s 后缀可构造 Period_T 的对象。如:

Frequency_T 频率 = 0.5_hz;

Period_T 周期 = 1e-3_s; // 1毫秒

  1. 算术运算

Frequency_T 和 Period_T 对象支持带量纲的算术运算。如下所示:

auto f1 = 0.5_hz * 2;       // => 1_hz

f1 = f1 / 2.0;              // => 0.5_hz

auto t1 = 1.0 / 0.5_hz;     // => 2_s

float res = 2.0_hz * 4_s;   // => 8.0 (无量纲)

t1 = 10_s - 5_s;            // => 5_s

t1 = 5_s + 5_s;             // => 10_s

t1 = 1_s * 0.1;             // => 0.1_s

f1 = 1.0 / t1;              // => 10_hz

注意:Frequency_T 对象不支持加减运算,因为其物理意义不明确。

各算术运算对应的赋值操作是支持的。如:

auto f1 = 0.5_hz * 2;    // => 1_hz

f1 /= 2;                 // => 0.5_hz

      1. Ticker_T

所在命名空间:Kiyun::LowerComputer::Rasl::Frame

  1. get方法一

函数原型:static Ticker_T get(Step_t&& step)

函数功能:初始化一个Ticker_T对象

参数:step:用于计算“下一时刻”的函数对象。类型描述为

typedef std::function<unsigned(unsigned)> Step_T;

注意 Frequency_T 和 Timer_T 类的对象均符合这一定义。

返回值:Ticker_T对象

  1. get方法二

函数原型:static Ticker_T get(const Step_t& step)

函数功能:同上。

  1. get方法三

函数原型:static Ticker_T get(Frequency_T freq)

函数功能:初始化一个Ticker_T对象

参数:freq:频率对象。

返回值:Ticker_T对象

  1. start方法

函数原型:void start(Listener_T&& hdl)

函数功能:启动一个系统时钟

参数:hdl: 回调函数,类型描述为:

typedef std::function<bool()> Listener_T;

返回值:无

  1. 注:hdl返回值为false时,系统时钟对象会被析构
  1. stop方法

函数原型:void stop()

函数功能:停止系统时钟

参数:无

返回值:

      1. Timer_T类

实现系统延时功能。

  1. delay方法

函数原型:static void delay(Timeout_T t);

参数:t:延时时间。

typedef Period_T Timeout_T;

返回值:无。

  1. make方法

函数原型:static Timer_T make(Timeout_T t, Listener_T&& lis);

此方法立即返回,并在指定的延时后调用指定的函数。

参数:t:延时时间。

typedef Period_T Timeout_T;

参数:lis:延时到达后调用的回调函数。其原型为:void(Timer_T)

返回值:Timer_T 对象,可供调用 cancel 方法。

  1. cancel方法

函数原型:void cancel();

此方法只能在 make 方法的返回值对象上调用,取消定时器。

  1. restart方法

函数原型:void restart();

此方法只能在 make 方法的返回值对象上调用,定时器将重新开始计时。

    1.  基础服务

本章节介绍在实时脚本语言中提供的基本输出与日志功能。

MC_APPNAME 宏:这个宏定义当前模块的名称。如果没有这个宏,那么会以模块的文件名作为名称。这个名称会出现在基本输出和日志输出中。

      1. 基本输出

提供标准错误、标准输出和标准日志三个流,可以通过 KYIO(err)、KYIO(out)和KYIO(log) 来使用。支持基本类型的流出操作,如下所示:

#define MC_APPNAME "TestApp"

// 在屏幕上显示  ERR | TestApp | write timeout : 10

KYIO(err) << "write timeout : " << 10;

size_t size = 100;

// 在屏幕上显示  INF | TestApp | write done : 100

KYIO(out) << "write done : " << size;

const char name = 'c';

// 在屏幕上显示  LOG | TestApp | write done : com

KYIO(log) << "write done : " << name;

      1. 日志使用方法

下位机程序中输出的日志会显示在上位机日志查看器中。

以下代码均假定引入了命名空间:

using namespace std;

using namespace Kiyun::LowerComputer;

using namespace Rasl;

在下位机程序中有五个宏可用:

1、KYLOG_GLOBAL 用来设置全局属性,一般用在程序入口处。如:

KYLOG_GLOBAL().severity(Logging_T::Severity_E::NOTI_e);

上面的代码,指定全局过滤级别为“通知”,即只发送通知以上(含)级别的消息。级别有 TRAC_e, DEBG_e, INFO_e, NOTI_e, WARN_e, ERRO_e, NONE_e。其中 NONE_e 留作系统消息,不要使用。

2、KYLOG_SCOPE 用来设置局部属性,用在函数或作用域开头。

KYLOG_SCOPE("类名::方法名").tag("发送").tag("检查");

上面的代码设置当前 scope 为 "类名::方法名",这个名字是任意的,建议就用函数名,它会记录为调用堆栈。为当前 scope 内的所有日志消息加了“发送”和“检查”两个标签。标签是任意的,用来在上位机调试器中过滤日志消息,以聚焦于关心的方面。

3、KYLOG 用来输出日志。

KYLOG_SCOPE("类名::方法名");

// ... 其它代码

KYLOG(INFO) << "消息码(16进制): " << hex << setw(sizeof(x) * 2) << setfill('0') << x;

其中 INFO 指定本条日志的级别为 INFO_e,注意 _e 后缀不必写。

如果 x 是 int 类型值为 20,输出将是: 消息码(16进制) : 00000014。

如果 x 是 short 类型值为 20,输出将是: 消息码(16进制) : 0014。

KYLOG 支持所有流操作符和流类型。

4、为单个日志添加标签:

- KYLOG_DEF 与 KYLOG_USE 宏

KYLOG_DEF(NOTI).tag("信号处理相关").tag("发送相关");

KYLOG_USE() << "日志内容";

这两个宏必须连在一起,中间不能有任何其它代码。这种方式可以为单条日志加 tag,以便在日志查看器中过滤。

注意: `KYLOG(WARN)` 其实相当于 `KYLOG_DEF(WARN);KYLOG_USE()。

  1. 进阶用法

日志器对外表现为一个标准的输出流,符合 std::ostream 接口要求,可以使用 C++流的格式化与设置等功能。示例如下:

void formatMask(uint32_t mask) {

    KYLOG_SCOPE("formatExample");

    KYLOG(INFO) << "掩码为 : 0x"

        << setw(8) // 宽度为8

        << setfill('0') // 宽度不足部分填 0

        << hex  // 16进制显示

        << mask;

}

formatMask(100); // 在日志查看器中显示: 掩码为 : 0x00000064

具体用法可查阅 c++ STL 库手册。

    1.  应用支持-协议编解码

本章节介绍在实时脚本语言中使用协议的方法。

      1. 使用方法

用户在Etest仿真模型中建立的接口协议描述,在实时脚本中可以作为类型使用,进行原始数据的解包和组包。

如:将某通道中协议名声明为“信号协议”;在编译后,Etest环境会生成一个名为“信号协议_T”的C++类,可供使用。该类所在命名空间为:Kiyun::LowerComputer::Dpdp

(注:目前暂不支持协议中的分支定义与自定义长度整形的定义。)

以协议“信号协议”为例进行说明:

类名:信号协议_T

构造函数:信号协议_T

auto 协议包 = 数据协议_T();

属性访问方法:

协议中的每一个字段都可以按名称访问。比如声明了如下DPD:

Protocol 信号协议

    Segment 发送时间 StandardUint32

    Segment 采集时间 StandardUint32

    Segment 信号幅值 StandardFloat

End

会生成类Kiyun::LowerComputer::Rasl::Dpdp::信号协议_T

  1. 初始化时赋值:

auto 信号 = 信号协议_T().发送时间(0).采集时间(0).信号幅值(0);

  1. 初始化后逐属性赋值:

auto 信号 = 信号协议_T()

信号.发送时间(66);

信号.采集时间(55);

信号.信号幅值(5);

  1. 获取协议属性值:

uint32_t sendEpoch = 信号.发送时间()

float amplitude = 信号.信号赋值()

      1.  信号协议_T”类
  1. feed

单个输入。(一次从原始数据解析一个字节为协议包)

函数原型:FeedResult_T feed(uint8_t food)

参数:food:待解析的数据

返回值:FeedResult_T对象

  1. 示例
  • feed方法返回一个FeedResult_T对象,具有gooddone两个bool属性,分别表示“正确”与“完整”。另一个errPos属性,在“不正确”的情况下指明协议的错误位置,单位为字节。

for(auto c:buf){

    auto 解析结果 = 协议包.feed(c);

    if(!解析结果.good){

       //错误处理

    }

    else if(解析结果.done) {

       //数据包已经完整

       //....使用

       协议包.clear(); //清零,可以重新feed

    }

}

  1. fromBuffer

批量输入(将缓存中的数据解析为协议包)。

函数原型:FeedResult_T fromBuffer(const Buffer_T& buf)

参数:无

返回值: FeedResult_T对象

  1. 示例

Buffer_T buf; //buf为std::vector<uint8_t>的别名

auto 解析结果 = 协议包.fromBuffer(buf);

  1. spit

逐个输出协议包中的数据。

函数原型:SpitResult_T spit()

参数:无

返回值: SpitResult_T对象

  1. 示例

SpitRes_T 输出结果;

do {

   输出结果 = 协议包.spit();

   char c = 输出结果.res;

   // 处理 c

} while(!输出结果.done); // 到完成为止

  1. toBuffer

批量输出。

函数原型:bool toBuffer(Buffer_T& buf)

参数:buf:std::vector<uint8_t>的别名,存放从协议包解析出的数据

返回值: true,成功;false,失败

  1. 示例

Buffer_T buf;

bool 成功 = 协议包.toBuffer(buf);

if(!成功) {

   //错误处理

}

    1.  应用支持-数据上传

数据上传将测试数据从下位机上传到上位机,上位机的数据中心负责测试数据的保存,并转发到监控界面。

数据上传的数据内容和数量由用户在实时脚本中决定。用户使用数据上传接口,将数据上传到上位机;

数据上传操作由数据上传服务完成。数据上传服务作为一个后台进程在下位机运行。数据上传服务的优先级比较低,会在系统不繁忙的时候运行,不会抢占系统资源。

本章节介绍在实时脚本语言中将数据上传的方法。

      1. 使用方法简介

数据上传操作主要包括两个步骤:1、创建UploadService_T 类型的对象“上传通道”;2、将数据入队列等待上传。

如:执行如下代码,则表示将“输出”通道输出的读数据上传。

auto 上传通道 = Channel_T::get(

    输出.path(), Channel_T::Direction_E::READ_e

);

//buf 类型为 vector<char>,包含需要上传的数据

auto data = DataUploader::Data_T{上传通道, 0, buf};

UploadService_T::get().queue(data);

注意:创建上传通道的时候,使用了“输出”通道,因此数据上传时被标记为是“输出”通道的数据;在“实时数据”工具的“调用者”栏会显示为“输出”通道。

      1. Channel_T结构体

所在命名空间为:Kiyun::LowerComputer::DataUploader

  1. get函数

初始化一个Channel_T对象

函数原型:static Channel_T get(const std::string& device, Direction_E direction)

参数:device:设备名;direction:模式(读/写)

返回值:Channel_T对象

说明:对于同设备名同方向的通道,这个方法会取得同一个对象,不会重复创建。

  1. device函数

获取设备名称

函数原型:const std::string& device() const

参数:无

返回值:设备名称

  1. direction函数

获取设备的读写模式

函数原型:Direction_E direction() const

参数:无

返回值:读写模式

      1. Data_T结构体
  1. 构造函数

构造Data_T结构体

原型:

Data_T(

    Channel_T channel,

    uint64_t time, // 时间为0表示当前时刻,相当于 Data_T::now()

    std::vector<char>&& content

);

参数:channel:通道;time:时间戳;content:数据内容。

      1. UploadService_T类
  1. get函数1

获取UploadService_T对象

函数原型:static UploadService_T get()

参数:无

返回值:UploadService_T对象

  1. get函数2

获取UploadService_T对象

函数原型:static UploadService_T get(const std::string& addr)

参数:addr:ip地址

返回值:UploadService_T对象

  1. isValid函数

检测UploadService_T对象是否有效

函数原型:bool isValid()

参数:无

返回值:true:有效;false:无效

  1. queue函数

数据入列

函数原型:void queue(Data_T data)

参数:data:上传数据

返回值:无

      1. 综合运用示例

上传服务对象可以直接取得,通道对象和数据对象都可以当场构造。另外,Data_T 对象提供了可变参数的构造函数,提供了语法上的额外便利。所以,上传代码可以这样写:

uint32_t 操作码 = 12345678;

uint16_t 数据头 = 0x0A05;

vector<char> 数据 { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };

uint32_t 数据尾 = 0x050A050A;

UploadService_T::get().queue({ // 花括号隐式构造一个 Data_T 对象

    Channel_T::get(com.path(), Channel_T::Direction_E::WRITE_e),

    0, // 当前时间

    操作码, 数据头, 数据, 数据尾, (int32_t)0, (uint8_t)10

});

注意:为了准确控制上传数据的字节数,要非常明确的指定数据的类型。比如在数据尾之后上传的0和10,做了强制类型转换,否则它们的长度由编译器决定,不一定是想要的结果。

    1.  应用支持-数据采集方案

数据采集API配合ETest数据采集方案进行使用。在进行测试设计时,完成仿真模型中的接口类型和协议的设计之后,可以在PC规划界面添加VX类型的客户端,然后在该客户端进行“数据采集方案”的配置;同时,在下位机脚本中使用类似“外围系统.read(协议对象)”或者“外围系统.write(协议对象)”的方式进行接口数据的存取,则ETest环境自动生成代码,将数据采集方案中勾选的数据协议字段进行上传到上位机,可以在上位机工具的“实时数据查看”工具中进行查看,也可以在“历史数据查看”工具中进行查看和分析。

      1. 使用方法简介

例如:某个测试方案中进行仿真模型设计如下图所示。

  • 5 ETest仿真模型举例

PC规划中的数据采集方案设计如下图所示:

  • 6 数据采集方案举例

可以创建实时任务,内容如下所示:

外围系统_2 outSys;  //初始化一个外围系统对象

//协议对象初始化,注意为” Protocol_5_T()”和” Protocol_2_T()”相比ETest界面//多了字符”_T”

auto pro5 = Protocol_5_T().发送时间(1).采集时间(0).信号幅值(0.5);

auto pro2 = Protocol_2_T();

if(!outSys.write(pro5)){

      KYIO(err) << "Data Collect Protocol_5_T failed.";

      return -1;

}

Timer_T::delay(1);

if(!outSys.read(pro2)){

      KYIO(err) << "Data Collect Protocol_2_T failed.";

      return -1;

}     

说明:

系统自动生成名称同外围系统名称一样的类型“外围系统_2”;

外围系统outSys有如下四个接口可供调用:

outSys.write(Protocol_5_T);

outSys.read(Protocol_5_T);

outSys.write(Protocol_2_T);

outSys.read(Protocol_2_T);

即对于每一个协议类会有一个write和read接口,且接口的返回值为bool型变量。但对AD和DO接口,只有read接口可用;对DA和DI接口,只有write接口可用。

AD通道有两个read接口可供调用:

read(协议类型);       //AD单次采集

read(协议类型, int);  //AD连续采集,第二个参数表示采集频率为250K时采集的时间长度(以秒钟为单位);

另外需要在源文件中包含头文件: #include "DataCollect.hpp"

引入命名空间:using namespace Kiyun::DataCollect;

      1. 范例

如下:

#define MC_APPNAME "2COM_TEST"

/** 系统固定头文件 */

#include <Rasl/rasl.hpp>

#include <DataUploader/DataUploader.hpp>

#include "rasl-dpd/user.hpp"

#include "UserChannels.rasi"

#include "DataCollect.hpp"

#include <boost/circular_buffer.hpp>

namespace {

  using namespace std;

  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;

void f_sync_wr();   //串口"发送端(com/0/0)"同步写数据,串口"接收端(com/0/1)同步读数据"

}

int Main(){

  KYIO(out) << "Hello, COM!";

  if(!发送端.isValid() || !接收端.isValid()){

      KYIO(err) << "Channel is invalid, please check!!!";

      return -1;

  }  

  f_sync_wr();

  return 0;

}

namespace {

  void f_sync_wr(){

  

    外围系统_2 outSys;

    auto pro5 = Protocol_5_T().发送时间(1).采集时间(0).信号幅值(0.5);

    auto pro2 = Protocol_2_T();

    int times=25;

    while (times >0){

      //同步写测试

      if(!outSys.write(pro5)){

         KYIO(err) << "Data Collect Protocol_5_T failed.";

         return;

      }

      Timer_T::delay(1);

      if(!outSys.read(pro2)){

         KYIO(err) << "Data Collect Protocol_2_T failed.";

         return;

      }     

      times--; 

    }

   }

}

      1. 模型间通信数据采集

模型间通信的数据采集和接口的数据采集API的使用方法一样。只需在ETest的模型间通信的采集方法配置界面进行配置。

首先进行模型间通信的通道和协议配置。

  • 7 模型间通信通道配置

然后进行采集方案的配置。如图所示。

  • 8 模型间通信采集配置

最后编写实时脚本,进行数据收发和上传。

//初始化两个外围系统对象

外围系统_1 outSys_1;

外围系统_3 outSys_3;

//协议对象初始化

auto pro = Protocol_外围系统_1_1_T();

if(!outSys_1.write(pro)){

      KYIO(err) << "Data Collect Protocol_外围系统_1_1_T failed.";

      return;

}

Timer_T::delay(1);

if(!outSys_3.read(pro)){

      KYIO(err) << "Data Collect Protocol_外围系统_1_1_T failed.";

      return;

}     

    1.  应用支持-实时参数设置

对于一些仿真任务,可以在运行中让用户修改一些运行参数,调整任务的运行状态。对于这种任务,可以使用“实时参数设置”功能。

实时参数设置支持在实时任务运行过程中,使用ETest中的“实时参数调节”工具修改任务内部变量的值。本节介绍此功能的使用方法。

      1. 使用方法及范例

进行实时参数调节的方法为:

  1. 首先设计一个实时任务,任务需要运行一段时间,其中用到若干参数(变量)。
  2. 将需要调节的变量定义为全局变量。
  3. 设计kiyunParameterSet函数,函数内部可以进行必要的检查等操作。该函数在变量被修改之前和之后都会被调用一次。
  4. 运行该实时任务。
  5. 使用Etest的工具“实时参数调节”,打开实时任务,显示可以调节的变量。给出希望修改的变量值,点击“发送”修改变量值。

实时任务如以下范例所示。

#define MC_APPNAME "SETP-TEST"

#include <Rasl/rasl.hpp>

#include "rasl-dpd/user.hpp"

#include "UserChannels.rasi"

namespace {

    using namespace std;

    using namespace Kiyun::LowerComputer::Rasl;

    using namespace Kiyun::LowerComputer::Rasl::Frame;

    Os::Semaphore_T f_终止信号;

}

namespace Kiyun { namespace LowerComputer { namespace Rasl { namespace Test {

    char  g_字符 = 'a';

    short g_短整 = 256;

    int   g_整数 = 70000;

    long  g_长整 = 100;

    long long g_大整 = 200;

    float g_浮点 = 0.1;

    double g_双精;

}}}}

unsigned char  g_u字符 = 100;

unsigned short g_u短整;

unsigned int   g_u整数;

unsigned long  g_u长整;

unsigned long long g_u大整;

string g_字符串 = "abcdefg";

Frequency_T g_频率 = 1.5_hz;

Timeout_T g_时长 = 0.5_s;

int kiyunMain() {

    f_终止信号.wait(); // 等待信号退出

    return 0;

}

extern "C"

bool kiyunParameterSet(bool beforeSet, void* paramAddr, const string& value) {

    using namespace Kiyun::LowerComputer::Rasl::Test;

    if(beforeSet)

        KYIO(log) << "Set values,not check: " << paramAddr << " => " << value;

    else {

        if(false) {}

#define WATCH_VALUE(n) \

        else if(&g_##n == paramAddr) \

            KYIO(out) << #n "新值 : " << g_##n;

   WATCH_VALUE(字符)

        WATCH_VALUE(短整)

        WATCH_VALUE(整数)

        WATCH_VALUE(长整)

        WATCH_VALUE(大整)

        WATCH_VALUE(u字符)

        WATCH_VALUE(u短整)

        WATCH_VALUE(u整数)

        WATCH_VALUE(u长整)

        WATCH_VALUE(u大整)

        WATCH_VALUE(字符串)

        WATCH_VALUE(频率)

        WATCH_VALUE(时长)

    }

    if(g_字符串 == "exit") // 字符串值修改为 exit 表示退出

        f_终止信号.notifyOne();

    return true;

}

      1. 支持的数据类型

所有C数值类型(整型、浮点);

RASL运行库中的频率(Kiyun::LowerComputer::Rasl::Frame::Frequency_T);

周期类型(Kiyun::LowerComputer::Rasl::Frame::Period_T);

C++ STL 字符串;

      1. 变量作用域

实时参数设置支持用户自定义的全局变量(不包括匿名空间(namespace)中的变量)值的修改。如范例中所示,支持修改所有全局变量,包括在命名空间中的全局变量,也支持类的静态变量。

注意:在匿名命名空间中的变量不是全局变量,不支持调参。

      1. 运行条件

该实时任务中可以包含”kiyunParameterSet” 函数,如范例中所示,调参功能会在每个变量设置前、设置后分别调用一次”kiyunParameterSet” 函数。在变量设置前以’beforeSet’’true’调用,在变量设置完成后以’beforeSet’’false’调用。在变量设置前,如果 kiyunParameterSet 函数返回 false,表示“不允许设置参数”,则参数不变。否则参数被设置为指定的值。

如果实时任务中没有kiyunParameterSet 函数,则直接设置参数。

      1. 取值范围

支持’int64_t’和’uint64_t’,但取值范围依然只是32位;

支持128位’long double’,但取值范围依然只是64位;

    1.  应用支持-内容附件使用

ETest实时API提供方法能够对ETest项目文件中的“内容附件”进行使用。本节介绍这些数据的使用方法。

      1. 使用方法及范例

ETest使用内容附件的API主要包括两个类:DataStore_T和DataLine_T。

首先使用DataLine_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;                                 //协议命名空间
    using namespace  Kiyun::LowerComputer::Rasl::Data;
    using namespace std;
    
    //本地变量、本地函数声明
    //Os::Semaphore_T f_exitSem;
}
void getData()
{
    uint64_t start = Ticker_T::now();
    uint64_t count = 0;
    //自定义路径,一般无需调用以及修改
    //DataStore_T::setPath("userData");
    auto dataStore = DataStore_T::get("内容附件.测试目录_1.文本文件_1");
  for (const auto& line : dataStore)
    {
        KYIO(log) << line.get<string>(0)
            << "  " << line.get<string>(1)
            << "  " << line.get<string>(2)
            << "  " << line.get<string>(3)
            << "  " << line.get<string>(4);
        ++count;
    }

    DataStore_T dataStore2("内容附件.测试目录_1.二进制文件_1");
    vector<char> buf;
    size_t bytesCount = 0;
    count = 0;
    while (true)
    {
        buf = dataStore2.getNextBlock(1024 * 10);//10K
        if(buf.size() == 0) break;//EOF
        //处理buf
        bytesCount += buf.size();
    }
    MCIO(log) << "bytesCount =" << bytesCount;

    uint64_t finish = Ticker_T::now();
    KYIO(log) << "count =" << count;
    KYIO(log) << "time =" << static_cast<double>((finish - start) / 1e6) << "s";
}

void printLine(const DataLine_T & line)
{
    KYIO(log) << line.get<string>(0);
    KYIO(log) << line.get<uint32_t>(1);
    KYIO(log) << line.get<int>(2);
    KYIO(log) << line.get<float>(3);
    auto buf = line.get<vector<char>>(4);//to buf
    for (auto it : buf) printf("0x%x ", it);
    printf("\n");
}
void testLine()
{
    DataLine_T line = "abcABC,12312,-1234,-12.981344,0x616263646566676869";
    printLine(line);
}


//主函数入口
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";      //发送到控制台

    /*测试代码*/
      testLine();
      getData();

    //f_exitSem.wait();
    return 0;
}

bool Exit(Os::Task_T) {
    //返回值为true,表示能中止正在运行的实时任务
    //f_exitSem.notifyOne();
    //return true;
    
    return false;  //默认情况下该实时任务不能被中止
}

      1. DataStore_T

代表一个内容附件对象。所属命名空间为Kiyun::LowerComputer::Rasl::Data。

1) get函数

函数原型:    static DataStore_T get(const char * file)

函数功能:获取数据文件的容器

参数说明:file:数据文件名

返回值:文件容器对象

2) setPath函数

函数原型:    static void setPath(const char * path)

函数功能:设置FTP相对文件路径(默认为userData,即FTP根目录下的userData目录)

参数说明:path:相对路径

返回值:  无

注:一般无需设置,如需自定义路径,则setPath需在导入数据文件之前进行调用,否则自定义路径将无法生效。

3) getNextLine函数

函数原型:const char * getNextLine()

函数功能:从文件容器中获取一行字符串

参数说明:无

返回值:字符串指针,为NULL则表示读到文件尾。

4) getNextBlock函数

函数原型:std::vector<char> getNextBlock(size_t blockLen = cc_maxBlock)

函数功能:从文件容器中获取一个字节块

参数说明:blockLen:字节块大小,默认字节块大小cc_maxBlock = 4M

返回值:字节容器,容器size为0则表示读到文件尾。

5) begin函数

函数原型:Iterator_T begin()

函数功能:返回一个当前容器中起始行的迭代器

参数说明:无

返回值:行迭代器

6) end函数

函数原型:Iterator_T end()

函数功能:返回一个当前容器中末尾行的迭代器

参数说明:path:相对路径

返回值:行迭代器

      1. DataLine_T

代表一个内容附件中的一行数据。所属命名空间为Kiyun::LowerComputer::Rasl::Data。

1) c_str

函数原型:const char* c_str() const

函数功能:获取当前行的字符串指针

参数说明:无

返回值:字符串指针

2) at

函数原型:const char* at(int index)const

函数功能:返回当前行的指定逗号分割字符串指针

参数说明:列序列号

返回值:字符串指针

3) size

函数原型:size_t size() const

函数功能:返回当前行所包含的列数

参数说明: 无

返回值:总共包含的列数

4) length

函数原型:size_t length() const

函数功能:返回当前行所包含的字符个数

参数说明: 无

返回值:字符个数

5) empty

函数原型:bool empty() const

函数功能:是否为空行

参数说明: 无

返回值:true:空行,false:不为空行

6) get

函数原型:template<typename T> T get(int index = 0) const

函数功能:获取列并进行格式化转换

参数说明: 列序列号,默认为0

返回值:列元素

注:可转换成如下类型:

有(无)符号整形,浮点型,string,char,char *,const char* ,vector<char>

7)  get<vector<char>>

函数原型:template<> vector<char> get(int index = 0) const

函数功能:获取列并可将hex字符串转换成buf

参数说明: 列序列号,默认为0

返回值:列元素的buf

注:可将hex字符串转换成字节流,字符串必须以0x或者0X开头,如0x010203,否则将不会对字符串进行hex转换。

    1.  通用类型

Etest实时脚本中的API中共同用到了一些类型,在此对这样类型进行详细介绍。

      1. Path_T类型

Path_T类型所属命名空间为:Kiyun::LowerComputer::Rasl::Device

类型定义为:

namespace Mince { namespace LowerComputer { namespace Rasl { namespace Device {

    struct Path_T {

        inline Path_T(const char* pathStr) : Path_T(std::string(pathStr)) {}

        Path_T(std::string&& pathStr);

        Path_T(const std::string& pathStr);

        Path_T(const std::string& className, int cardId, int channelId);

        Path_T(const std::string& className, int channelId);

        const std::string className() const noexcept;

        const std::string path() const noexcept;

        int cardId() const noexcept; // 返回 -1 表示没有

        int channelId() const noexcept; // 返回 -1 表示没有

        inline operator std::string () const { return i_path; }

        std::string i_path;

    };

}}}}

      1. Error_T

Error_T类型所属命名空间为:Kiyun::LowerComputer::Rasl

类型定义为:

typedef boost::system::error_code Error_T;

有关方法请查阅 boost 库参考手册。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值