IEC61499 功能块调用C++ 动态程序库的实现(2)

使用IEC61499 开发应用的人都会有一个感触-“功能块到用时方显少”。所以用户开发功能块的必需的。理论上,IEC61499 标准提供了定义功能块类型的方式。比如在4DIAC 中,可以定义功能块类型。它包括可以定义基本功能块,服务接口功能块和复合功能块。而功能块内部算法可以使用ST 语言来编写,或者Lua 语言来。实际上,设计完成了功能块类型后,要将该功能块Export 成为运行时的C++ 代码,和运行时一起从新编译之后,运行时才真正支持定义的功能块(在4DIAC Forte 中称为外部模块(external module))。这种方法是有缺陷的。首先它并不是动态功能块,需要和运行时一起编译。真正实现动态功能块的方式只能使用解释性语言来编写算法(比如lua ,或者java,JavaScript)而解释性语言的弊端是明显的,执行效率不高,不适合实时控制的要求。另外,如果涉及硬件底层的控制,解释性语言也是无能为力。在linux OS 下,还有另外一种方式实现动态远程部署,那就是容器技术。然而,docker 是一个庞然大物,在小型嵌入式系统中并不合适。

为了寻求更加合适的方式实现动态功能块。我们做了多种努力,最终选择了动态库的技术来实现IEC61499动态功能块。

动态功能块的通用格式

我们设计了一个CALL 功能块,它的主要功能是调用一个动态库中的一个子程序。

例如:下面是一个带两个参数的库函数调用功能块

其中param1,和param2 是函数的输入值,result 的函数的结果

name 由库名称和函数名称组合而成,例如 libmath.add 表示是动态库libmath 中的add 函数。

 

更为复杂的考虑

函数的内部变量如何考虑

   我们知道,函数无法访问外部变量,也不能保留内部变量。我们使用类来保留内部变量。

例子:

下面这个例子是动态库中定义了一个类。然后使用外部函数来调用这个类。

fifo.cpp

#include<stdio.h>
#include "fifo.h"
FB::FB(){

}
FIFO::FIFO(){
    inPos=0;
    outPos=0;
    counter=0;
}
void FIFO::pushData(int value){
    printf("pushData\n");
     buffer[inPos++]=value;
     if (inPos==32) 
     inPos=0;
     counter++;
}
int FIFO::popData() {
   int value;
   printf("popData\n");
   value=buffer[outPos++];
   if (outPos==32) outPos=0;
   counter--;
   return value;
}

 
extern "C" { 
FB * init(){
   printf("init\n");
    return (new FIFO());
}

 
void pushData(FB * handle,int val){
     FIFO * h;
     h=(FIFO *)handle;
     h->pushData(val);
}
 
 int popData(FB * handle){
     FIFO * h;
     h=(FIFO *)handle;
   return h->popData();
}
}

注意:extern "C" 很重要。否则运行./dlopen_test会报undefined symbol dlsym的错误,因为c++编译后的文件会把函数名改名(为了实现重载功能),用extern "C"声明后,就会使用c的方式进行编译,编译后的文件中仍然是定义的函数名,可以通过nm来看so中函数的名称。

冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了,相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。

但是,在另外的例子中,我编译成静态库,或者 在编译时链接动态,好像加了extern “C" 反而不行,出现  undefined reference to 的错误。去掉了却好了。有点诡异。嗨

fifo.h

#include<stdio.h>
class FB {
    public:
      FB();
};
class FIFO :public FB {
    public:
       FIFO();
       void pushData(int value);
       int popData();
    private:
      int buffer[32];
      int counter;
      int inPos,outPos;

};
FB * init();
void pushData(FB * handle,int val);
int popData(FB * handle);

编译

 g++ fifo.cpp  -fPIC  -shared  -o libfifo.so   

主程序

#include<stdio.h>
#include<dlfcn.h>
#include "fifo.h"
typedef FB *(*PFUNC_INIT)(); 
typedef void (*PFUNC_PUSH)(FB* handle,int);
typedef int(*PFUNC_POP)(FB* handle);        //给函数指针定义一个别名   PFUNC_SHOW = int(*)(int)
 
int main()
{
     printf("Dynamic lib test\n");
    void *handle = dlopen("./libfifo.so",RTLD_LAZY| RTLD_DEEPBIND |RTLD_GLOBAL);  //加载动态库
        if(handle==NULL)
        {
            printf("dlopen fail:%s\n",dlerror());
            return -1;
        }
    PFUNC_INIT init = (PFUNC_INIT)dlsym(handle,"init"); 
    if(init==NULL)
    {
        printf("dlsym fail:%s\n",dlerror());
        return -1;
    } 
    PFUNC_PUSH push = (PFUNC_PUSH)dlsym(handle,"pushData");  
       if(push==NULL)
    {
        printf("dlsym fail:%s\n",dlerror());
        return -1;
    }         
    PFUNC_POP pop = (PFUNC_POP)dlsym(handle,"popData");  
       if(pop==NULL)
    {
        printf("dlsym fail:%s\n",dlerror());
        return -1;
    } 
    FB * h=init();
    push(h,1);
    push(h,2);
    int val=pop(h);
    printf("result=%d\n",val);
    val=pop(h);
    printf("result=%d\n",val);
    dlclose(handle);
    return 0;
}

编译

g++ fifoTest.cpp -ldl -o fifoTest  

运行结果

yao@DESKTOP-U0S0G7O:/mnt/f/yao2020/c++2020/dynamiclib$ ./fifoTest
Dynamic lib test
init
pushData
pushData
popData
result=1
popData
result=2

在这里我们看到,在main.中通过函数参数来传递 类的指针(FB * h)。而且FB 只是一个空的类,FIFO 使用FB 作为基类。在主程序中只是使用FB 指针类传递FIFO类的实例的地址。也就是说,如果动态库中的所有类,都使用FB 作为基类,那么就可以使用FB指针来作为动态库中类的指针。在C++中可以使用基类指针来访问派生类。

同样地,在动态库的函数中,也可以运行一个线程。

使用动态库实现动态功能块的开发是可型的方法。你说呢?

猜你喜欢

转载自blog.csdn.net/yaojiawan/article/details/108874766