4Diac 图形化编程的执行过程

一 4Diac 介绍

Eclipse 4diac为基于 IEC 61499 标准的分布式工业过程测量和控制系统提供开源基础架构。4diac 包括集成开发环境IDE、运行时Forte、Function Block Libray、Example Projects。IEC 61499 定义了用于开发分布式工业控制解决方案的特定领域建模语言。IEC61499 扩展了IEC61131-1,改进了软件组件的封装,提高了可重用性、 提供了独立于供应商的格式,并简化了对控制器间通信的支持。(官网:https://eclipse.dev/4diac/index.php )

4 Diac IDE基于Eclipse框架,可以集成其他插件到4Diac IDE当中。基于IEC61499的系统采用以应用为中心的设计,每个应用程序通过将所需功能块FB相互连接创建,将不同的FB分布部署在不同的设备上。

运行时Forte,采用C++实现,支持在线重新配置应用程序,并能实现执行IEC61499标准提供的所有功能块类型。其中使用了大量C++类来定义IEC61499的功能块,模型,通信接口,过程接口和运行环境。

4 Diac LIB包含可以在4Diac Forte上使用的FB,IEC61499将FB分为三类:基本功能块BFB、复合功能块CFB、服务接口功能块SIFB(也称SFB)。每个FB包含事件接口和数据接口,接口为数据传输和事件触发提供连接点,主体描述FB的功能。对于BFB来说,功能描述是以执行控制图ECC形式提供;对于CFB来说,功能描述是以FBN的形式提供;对于SIFB来说,这种描述是以服务序列图的形式提供。

图1 4Diac主要构成

二 4Diac IDE 图形化编程

IDE用于编写应用程序,将FB功能块建立连接,将应用程序下发到不同的硬件设备或导出为.fboot 文件,供硬件设备上的Forte调用。图2为一个简易的两数相加的应用程序。

 图2 F_ADD两数相加应用程序

 通过IDE导出.fboot文件,creat Forte boot-files,可以看出该文件为应用程序文件,类似于XML的描述性语言。根据图3,可以看出该文件描述了我们所使用的FB,包括了F_ADD、INT2INT、INT2INT_1和INT2INT_2。Connection Source = 12和Connection Source = 11为输入端口输入的数据,Connection Source 和 Destination 指明了FB之间的连接方式。如果FB分布式部署在不同的设备上,则导出不同硬件设备的.fboot文件,供每个设备上的Forte调用。

 

 图3 .fboot文件内容

 在官网处的documention处也有讲解:

当您使用的 FORTE 启用 FORTE_SUPPORT_BOOTFILE 选项时,FORTE 会在启动时尝试加载一个所谓的引导文件。该引导文件必须与 4diac FORTE 二进制文件位于同一目录,并命名为 forte.fboot。该文件包含设备的 FB 网络配置,并将在 4diac FORTE 启动时自动实例化。要创建此类引导文件,您需要在下载选择视图中选择一个或多个设备或资源,然后从上下文菜单中选择创建 FORTE 引导文件。在向导中,您可以选择要创建启动文件的设备和资源,以及放置这些文件的目录。按 "完成 "后,将为每个设备创建一个引导文件。该引导文件将包含所选资源和资源中的 FB 网络。由于引导文件中只包含所选的资源,因此在开发过程中可以使用更多的资源(如测试代码),而在引导文件中只使用主要资源。引导文件的名称是系统和设备名称的组合点 fboot。使用前需要将其重命名为 forte.fboot。

因此,我的理解是:IDE将我们的图形化编程,转化为.fboot文件,供不同设备上的Forte调用,其中Forte包含了许多FB的库,这些FB主要是以C++形式进行开发。在Forte中src可以找到FB的C++源码。Forte在接收到.fboot文件时,根据.fboot文件调用这些FB,实现具体功能。

 

Forte运行时源码主要包括:适配程序arch(与不同的操作系统适配)、com(一些扩展的通信协议,例如:HTTP、MODBUS、MQTT、OPC_UA等)、核心core(基本通信功能块cominfra、输入输出程序IO、数据类型datatypes、lua、fmi等)、模块module(IEC61161-3功能块、类型转换、各种特定的过程接口,例如树莓派的PiFace,raspberry_sps接口板的过程接口类程序)、stdfblib(标准功能库的实现)

 

 在此,分析一下IEC61131-3当中的F_ADD模块,F_ADD收到输入事件,与输入事件相关的数据输入IN1和IN2被刷新,事件被传递到ECC,内部的加法功能触发执行,完成执行后提供新的输出数据,刷新与输出事件相关的输出数据OUT,发送输出事件。

 F_ADD.h文件:

#ifndef _F_ADD_H_
#define _F_ADD_H_

#include <funcbloc.h>

class FORTE_F_ADD: public CFunctionBlock{
  DECLARE_FIRMWARE_FB(FORTE_F_ADD)

private:
  static const CStringDictionary::TStringId scm_anDataInputNames[];
  static const CStringDictionary::TStringId scm_anDataInputTypeIds[];
  CIEC_ANY_MAGNITUDE &IN1() {
    return *static_cast<CIEC_ANY_MAGNITUDE*>(getDI(0));
  };

  CIEC_ANY_MAGNITUDE &IN2() {
    return *static_cast<CIEC_ANY_MAGNITUDE*>(getDI(1));
  };

  static const CStringDictionary::TStringId scm_anDataOutputNames[];
  static const CStringDictionary::TStringId scm_anDataOutputTypeIds[];
  CIEC_ANY_MAGNITUDE &st_OUT() {
    return *static_cast<CIEC_ANY_MAGNITUDE*>(getDO(0));
  };

  static const TEventID scm_nEventREQID = 0;
  static const TForteInt16 scm_anEIWithIndexes[];
  static const TDataIOID scm_anEIWith[];
  static const CStringDictionary::TStringId scm_anEventInputNames[];

  static const TEventID scm_nEventCNFID = 0;
  static const TForteInt16 scm_anEOWithIndexes[];
  static const TDataIOID scm_anEOWith[];
  static const CStringDictionary::TStringId scm_anEventOutputNames[];

  static const SFBInterfaceSpec scm_stFBInterfaceSpec;

   FORTE_FB_DATA_ARRAY(1, 2, 1, 0);

  void executeEvent(int pa_nEIID);

public:
  FUNCTION_BLOCK_CTOR(FORTE_F_ADD){
  };

  template<typename T> void calculateValue(){
    T &roIn1(static_cast<T&>(IN1()));
    T oIn2;
    oIn2.saveAssign(IN2());
    st_OUT().saveAssign(ADD(roIn1,oIn2));
  }

  virtual ~FORTE_F_ADD(){};
};

#endif //close the ifdef sequence from the beginning of the file

 


F_ADD.cpp文件

#include "F_ADD.h"
#ifdef FORTE_ENABLE_GENERATED_SOURCE_CPP
#include "F_ADD_gen.cpp"
#endif
#include <anyhelper.h>

DEFINE_FIRMWARE_FB(FORTE_F_ADD, g_nStringIdF_ADD)

const CStringDictionary::TStringId FORTE_F_ADD::scm_anDataInputNames[] = {g_nStringIdIN1, g_nStringIdIN2};

const CStringDictionary::TStringId FORTE_F_ADD::scm_anDataInputTypeIds[] = {g_nStringIdANY_MAGNITUDE, g_nStringIdANY_MAGNITUDE};

const CStringDictionary::TStringId FORTE_F_ADD::scm_anDataOutputNames[] = {g_nStringIdOUT};

const CStringDictionary::TStringId FORTE_F_ADD::scm_anDataOutputTypeIds[] = {g_nStringIdANY_MAGNITUDE};

const TForteInt16 FORTE_F_ADD::scm_anEIWithIndexes[] = {0};
const TDataIOID FORTE_F_ADD::scm_anEIWith[] = {0, 1, 255};
const CStringDictionary::TStringId FORTE_F_ADD::scm_anEventInputNames[] = {g_nStringIdREQ};

const TDataIOID FORTE_F_ADD::scm_anEOWith[] = {0, 255};
const TForteInt16 FORTE_F_ADD::scm_anEOWithIndexes[] = {0};
const CStringDictionary::TStringId FORTE_F_ADD::scm_anEventOutputNames[] = {g_nStringIdCNF};

const SFBInterfaceSpec FORTE_F_ADD::scm_stFBInterfaceSpec = {
  1,  scm_anEventInputNames,  scm_anEIWith,  scm_anEIWithIndexes,
  1,  scm_anEventOutputNames,  scm_anEOWith, scm_anEOWithIndexes,  2,  scm_anDataInputNames, scm_anDataInputTypeIds,
  1,  scm_anDataOutputNames, scm_anDataOutputTypeIds,
  0, 0
};


void FORTE_F_ADD::executeEvent(int pa_nEIID){
  if (scm_nEventREQID == pa_nEIID) {
    anyMagnitudeFBHelper<FORTE_F_ADD>(IN1().getDataTypeID(), *this);
    sendOutputEvent(scm_nEventCNFID);
  }
}

anyhelper.h文件

template<class T>
void anyMagnitudeFBHelper(CIEC_ANY::EDataTypeID pa_eDataTypeId, T &pa_roFB){
  switch (pa_eDataTypeId){
    case CIEC_ANY::e_REAL:
#ifdef FORTE_USE_REAL_DATATYPE
        pa_roFB.template calculateValue<CIEC_REAL>();
#else //FORTE_USE_REAL_DATATYPE
        DEVLOG_ERROR("REAL is not compiled in this version of forte and you are still trying to use it. Exiting");
        assert(0);
#endif //FORTE_USE_REAL_DATATYPE
      break;
    case CIEC_ANY::e_LREAL:
#ifdef FORTE_USE_LREAL_DATATYPE
          pa_roFB.template calculateValue<CIEC_LREAL>();
#else //FORTE_USE_LREAL_DATATYPE
          DEVLOG_ERROR("LREAL is not compiled in this version of forte and you are still trying to use it. Exiting");
          assert(0);
#endif //FORTE_USE_LREAL_DATATYPE
      break;
    default:
      if(pa_eDataTypeId <= CIEC_ANY::e_TIME){
#ifdef FORTE_USE_64BIT_DATATYPES
          pa_roFB.template calculateValue<CIEC_LINT>();
#else //FORTE_USE_64BIT_DATATYPES
          pa_roFB.template calculateValue<CIEC_DINT>();
#endif //FORTE_USE_64BIT_DATATYPES
      }
      break;
  }
}

根据F_ADD.cpp文件可以看到,executeEvent函数传入参数为pa_nEIID(当前执行事件ID),判断当前执行事件ID和功能块输入事件ID是否相同,如果相同则执行代码。调用anyMagnitudeFBHelper函数,传入参数为IN1输入的数据类型,*this指向FORTE_F_ADD。

anyMagnitudFBHelper函数中,根据传递的第一个参数IN1数据输入的数据类型,利用分支语句决定调用计算函数。可以看出最后调用的都是Forte_F_ADD.template 中的calculateValue函数。
 calculateValue函数,函数定义中的模板参数T表示数据类型,该函数可以根据不同的数据类型执行计算。
T &roIN1(static_cast<T&>(IN1())); T&roIN1 表示声明了一个类型为T的引用变量roIN1, static_cast<T&>(IN1())是一个静态类型转换操作。这行代码的作用就是将IN1数据输入转换为类型T的引用,并赋值给roIN1。
T oIN2;声明了一个oIN2的变量,类型模板参数为T,用于存储第二个输入数据IN2()的值。
oIn2.saveAssign(IN);将IN2()的数值保存在oIN2。
st_OUT().saveAssign(ADD(roIn1,oIn2));将roIN1与OIN2进行加法计算,并将结果保存在输出数据中。

 

posted @ 2023-07-30 23:21  云端听JAY  阅读(1361)  评论(0编辑  收藏  举报