4diac运行时Fort源码分析(1)-功能块应用的执行环境

28 篇文章 38 订阅

4diac 是IEC61499 的开源项目,它由开发环境4diac-ide,运行时forte ,功能块库(LIB) 和系统实例构成。这个项目已经持续开发了十几年,不过依然只是用于学术研究,如果要应用工业领域,需要进一步扩展和完善。而且主要是4diac 的运行时Forte的扩展和完善工作尤为重要。如果打算做完善和扩展等二次开发工作的话。阅读,分析Forte 的源代码是必不可少的艰巨任务。网络上对forte 软件架构的介绍性文章非常的少。我们打算开始来尝试分析和阅读forte 源代码,并陆陆续续地向读者介绍。

概述

最新版本的Forte 是1.12.0版本,可以从4diac的网站上下载

https://www.eclipse.org/4diac/en_dow.php

forte 软件包是一个由C++ 编写的程序,其中使用了大量C++的类来定义IEC61499 的功能块,模型,通信接口,过程接口,和运行环境。分析Forte源代码需要熟悉C++程序设计的许多特性。

forte 软件包括了下面几个主要的文件夹

核心core

顾名思义,该目录中包含了forte 的核心程序。

 内部还包括了下面几个子目录

通信功能块cominfra

输入输出程序IO

数据类型datatypes

lua

fmi

实用程序utils

模块 module

包含各种扩展模块,例如扩展的通信协议http,modbus,MQTT,opc_ua,tsn,wagokbu等,以及各种特定的过程接口,例如树莓派的PiFace,raspberry_sps接口板的过程接口类程序。用户编写的功能块类型也放在在module目录下。

标准功能块库stdfblib

标准功能库的实现。

适配程序(arch)

 与不同操作系统的适配程序

核心程序

IEC61499 运行时最为重要的是基于事件网络的IEC61499 应用的执行。从Forte 如何执行IEC61499 应用的脉络来研究Forte核心程序

功能块网络(FBN)的执行

当我们分析IEC611499 运行时的源代码时,也许最迫切想了解的就是功能块网络(FBN)的应用是如何被执行的。可以有许多的实现的方法,关键是要实现系统的实时(Realtime Execution)执行。在计算机操作系统中,有许多的实现实时调度的方法。比如,我们可以将IEC61499 功能块执行转换称为PLC 中循环扫描的方式。为每个资源安排一个任务,该任务循环地扫描每个功能块,看它们是否被输入事件触发,一旦功能块被输入事件触发,就执行内部状态图和算法,并根据执行的结果输出相应的输出事件和数据。反复循环直到系统没有新的被触发功能块,便暂时挂起任务。这种盲目地扫描所有功能块的方法增加了许多无用的开销。

我们在这里介绍forte 采用事件链方式的实时执行功能块算法。

事件链(EventChain)

在介绍事件链调度算法之前,首先来分析IEC61499 功能块网络执行的行为以及一些基本概念。

ES-FB和ESK

通过事件连接发送事件的功能块称为:Event-Source Function Blocks (ESFB).,不产生更进一步输出事件的功能块称为Event-Sink (ESK)

时间链从某个ESFB 产生的事件开始,到一个ESK 结束。

responder-FB

能被服务(硬件) 触发的功能块称为响应功能块

 

事件链(EventChain)

有了ES-FB和ESK 的概念,我们来定义事件链。事件链是从某个ES-FB 产生事件到ESK 的FB 执行链。

 

当外部事件发生,会触发 ES-FB 执行,然后它产生了事件,触发FB1 执行,最后触发ESK-FB 执行,它不再产生输出事件,于是,该EC 执行完毕。

事件链(EC)的基本行为

  -EC 能具有实时限制(比如 deadline)

- 不同的EC能够触发同一个FB

-EC能够分裂和并行执行分支

-实时限制的EC可以包含没有实时限制的分支

-EC的长度可以根据内部状态和触发FB输入数据而变化

 

下图是事件链的例子。

 

在上面的图中,有三个ES-FB,所以就会有三个独立的事件链EC1,EC2和EC3。

 

在实现IEC61499运行环境时,为了实现实时运行环境,我们遇到一个主要的问题是什么样的计算单元是一个任务(TASK)?这是计算机并发计算中最基本的概念。我们可以将一个设备为一个任务,也可以一个资源一个任务,甚至是一个功能块一个任务。采用什么样的算法能兼顾实时性和效率呢,这是设计实时运行环境必须考虑的重要问题。

 

我们通过分析FB 的执行和事件通知流可以观察到,所有的执行路径源自于ES-FB,所以每个ES-FB赋予一个任务是一个合适的方式。

 

图中有两个重要的部分:

EEM –外部事件管理

顾名思义,它响应和处理外部事件。比如来自数字IO,定时器和网络的请求

  ECX- 事件链执行程序

调度算法

当一个FB 接收外部事件,当它建立时,要通过管理命令在EEM 中注册,这样的话,EEM可以将进入的外部事件丢给注册的FB

外部事件触发后,会通过一个ECX来执行,每个ECX都保留一个EC-Execution-List(EC-EL)。该列表是一个FIFO类型的队列,存放了所有事件输入端有事件等待执行的FB (的引用),所以等待执行。在下列情形下一个FB将添加到EC-EL 的末端

当一个外部事件产生,EEM添加ES_FB 到EC-EL .

当一个EC 队列中的FB 发送一个输出事件。他将添加接收FB(receiver-FB)到EC-EL

 当EC-EL 空了,这个任务被挂起。这表明所有的原始外部事件以及所有后续事件都递交了,触发的FB被执行,所有EC 到达了ESK。

Fort 中事件链执行算法的实现

设备,容器和资源类

我们知道,IEC61499 中的设备中包含了一个或者多个资源,如果没有显式地声明类,device具有资源的特性。resource 是一个能够独立运行的单元。相互之间不受干扰。这概念类似于软件技术中的容器。

 

我们也从结构化分层模模型出发,研究C++类的相互关系。

在Forte的是现在中并没有完全按照设备,资源的架构类定义相关的类,而是由容器CResource 类派生出资源类CResource类。并且在CResource 类基础上添加了设备的特征,所以CDevice 是一个CResource的派生类。

 设备类是具有了设备特征的资源类。设备,资源,和容器三个类的关系如下:

 

CFBContainer类(core/fbcontainer.h/CFBContainer)

容器具有如下特征

-获取名称

-获取容器中的功能块

-向容器添加功能块

-获取功能块列表

-建立功能块

-删除功能块

-获取功能块容器

CResource类(core/resource.h/CResource)

作为CFBContainer 的派生类,CResource 类添加下列特征

  • 执行管理命令(executeMGMCommand)
  • 获取包含该资源的设备(getDevice)
  • 获取资源的事件链执行器线程(getResourceEventExecution)
  • 改变执行器状态
  • 向指定的功能块数据输入写值(writeValue)
  • 获取监控处理器(getMonitorHandle)
  • 获取lua引擎
  • 执行事件(executeEvent)
  • 建立连接(createConnection)
  •  

CDevice类(core/device.h/CDevice)

作为CResource 的派生类,CDevice  添加了如下特征:

-获取功能块类型ID

-启动设备(startDevice)

-执行管理命令(executeMGMCommand)

-改变执行器状态(changeFBExecutionState)

-获取设备执行程序(getDeviceExecution)

-获取时钟(getTimer)

事件链数据结构

事件列表项(conn.h/ CConnectionPoint  类)

记录功能块的事件端口,有两个重要参数

  • 指向功能块的指针(CFunctionBlock)*mFB
  • 端口ID(TPortId)mportId

事件列表(ecet.h/EventList)

  功能块网络中的所有事件端口的列表。

 

 

事件链执行线程(CEventChainExecutionThread )类

执行事件链的线程。它在资源类和CDeviceExecution 类中引用。它内部维护一个事件列表的数据结构。

 

事件链执行线程是线程的一个派生类,具有下列特征

-启动事件链(startEventChain)

- 添加事件项(addEventEntry)

-改变执行状态

-执行线程,等待完成(joinEventChainExecutionThread)

-设置Deadline

-主循环mainRun

mainRun 是主循环,它循环地读取EventList ,调用功能块类的receiveInputEvent 函数。

设备执行类(devexec.h/CDeviceExecution)

 

这是在CDevice 中类中引用的类,这个类处理外部事件的处理。它相当于前面讨论过的EEM。

它的内部维护一个外部事件信息的数据结构

  struct SEventHandlerElement {

        bool mOccured; //!<flag indicating that the external event has occurred between the last invocation.

        CExternalEventHandler *mHandler; //!< pointer to the external event handler instance.

};

SEventHandlerElement   mRegisteredEventHandlers[cg_unNumberOfHandlers];

所有的外部事件功能块,都将会注册外部事件处理器。当外部事件产生时,通过CDeviceExecution 类的startNewEventChain(pointerToTargetFB)方法发送这个外部事件。在这个方法中启动一个CEventChainExecutionThread 线程,开始运行以这个外部事件开头的事件链。

 

与外部事件相关的类

外部事件是forte 内部隐藏的事件,在4DIAC IDE 中看不见。它通常用于通信硬件和功能块之间的通信。例如,E_CYCLE 功能块就会用到这样的功能。它注册到CTimerHandle类,周期性地获取外部事件。

CEventSourceFB类

CExternalEventHandler类

  处理进入的中断和类似的外部事件。是所有Forte 网络和IO 模块的基类,

接收外部事件

接收外部事件的功能块不是从CFunctionBlock 类派生,而是从CEventSourceFB类派生。CEventFB 能够响应外部事件。在访问外部事件类(例如一个定时触发器FORTE_SIBF_T_FF3)的executeEvent的方法中注册外部事件:

void FORTE_SIBF_T_FF3::executeEvent(int pa_nEIID){

  switch(pa_nEIID){

       case cg_nExternalEventID:

              Q() = !Q();

         sendOutputEvent(scm_nEventEOID);

         break;

       case scm_nEventSTOPID:

         if(m_bActive){

              CTimerHandler::sm_poFORTETimer->unregisterTimedFB(this);

              m_bActive = false;

         }

         break;

       case scm_nEventSTARTID:

         if(!m_bActive){

              CTimerHandler::sm_poFORTETimer->registerTimedFB(&m_stTimeListEntry, DT());

              m_bActive = true;

         }

         break;

       default:

         break;

  }

}

发送外部事件

 如果你设计一个通信/IO功能块类需要发送外部事件,你的类需要从CExternalEventHandler派生,在构造函数中,需要注册你的类:

m_nExtEvHandID = sm_poDeviceExecution->registerExternalEventHandler(this);

然后,你可以使用下列方式发送外部事件

if (sm_poDeviceExecution->extEvHandlerIsAllowed(m_nExtEvHandID)) {

sm_poDeviceExecution->startNewEventChain(pointerToTargetFB);

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值