以前のブログ投稿では、動的ライブラリを呼び出すIEC61499関数ブロックの利点について大まかに説明しています。このブログ投稿では、私の実装方法と発生した問題を紹介しています。
達成した結果
私の考えを検証するために、FB_CALL_X2と呼ばれる単純な関数ブロックが実装されました。この関数ブロックには、関数名、2つのパラメーター、および結果が含まれています。
nameは呼び出された関数の名前であり、2つの部分で構成されます。形式は次のとおりです。ライブラリ名。関数名。たとえば、この例では、OpenLibaray.addとOpenLibaray.subの2つの関数があります。
実験プロジェクトでは、単純な動的ライブラリOpenLibarayを作成しました。このライブラリには、追加、サブ、乗算、除算の3つの関数があります。加算、減算、乗算、除算を実現します。これらは2つのパラメーターで構成されているため、関数ブロックはFB_CALL_X2と呼ばれます。
FB_X2機能ブロックを実装した後、次のIEC61499アプリケーションを作成しました。
写真から、このプログラムは正しく実行されます。
実装の詳細
動的関数ブロック呼び出しのプロセス
- CALL関数ブロックはINITイベントを受信すると、動的ライブラリの名前(OpenLibaray)と関数の名前(addまたはsub)をデータ入力NAMEから抽出します。動的関数ブロックライブラリ(./OpenLibaray.so)をロードします。明らかに、実行時に、コンパイルされた動的ライブラリをforte.exeと同じディレクトリに配置する必要があります。
- CALL関数ブロックはREQイベントを受信すると、データ入力param1とparam2の値を読み取り、動的ライブラリ内の関数を呼び出します。
- 関数の戻り値は、RESULTデータ出力端子に出力されます。
発生した問題
発生する問題は、関数の入力タイプと出力タイプをどのように処理するかです。動的ライブラリを呼び出すLinuxのメカニズムでは、最初にdlsymを介して関数の呼び出しエントリアドレスとパラメータアドレスを取得してから、この関数を呼び出します。といった
typedef int(* PFUNC_PUSH)(int、int);
PFUNC_ADD add =(PFUNC_PUSH)dlsym(handle、"add");
結果=追加(param1、param2)
これが問題です。関数を呼び出すときは、関数パラメーターと戻り値のタイプを判別し、関数テンプレートを定義する必要があります。ファンクションブロックプログラムは、すべてのファンクションパラメータと戻り値のタイプを認識しているわけではありません。これが必要な場合は、関数ブロックparam1、param2、および結果のタイプを指定する必要があります。これは十分な柔軟性がありません。
そこで、ASN.1データエンコーディングについて考えました。実際、機能ブロック間のデータ転送を実現する方法は、ASN.1エンコーディングを使用することです。
また、ダイナミックライブラリ関数のパラメータにはASN.1エンコーディング方式を採用しました。したがって、動的ライブラリ関数の呼び出しメソッドは次のようになります。
typedef int(* PFUNC)(char *、char *、char *);
PFUNC sub =(PFUNC)dlsym(handle、Name);
sub(param1、param2、result);
以下はDynamic.cプログラムです
#include<stdio.h>
#include <iostream>
#include <list>
#include <string>
#include <dlfcn.h>
#include <cstring>
using namespace std;
typedef int (*PFUNC)(char *,char *,char *);
struct lib {
string Name;
void * handle;
};
std::list <lib> libList;
void * openlibaray(string libName){
cout<<"OpenLibaray:"<<libName<<endl;
for (std::list<lib>::iterator it = libList.begin(); it != libList.end(); it++){
if ((*it).Name==libName) {
return (*it).handle;
}
}
//Open Dynmaic Lib
string Path="./"+libName+".so";
cout<<"Open Libaray:"<<Path<<endl;
void *handle = dlopen(Path.c_str(),RTLD_LAZY| RTLD_DEEPBIND |RTLD_GLOBAL); //加载动态库
if(handle==NULL)
{
printf("dlopen fail:%s\n",dlerror());
return handle;
}
libList.push_back({libName,handle});
return handle;
}
bool callLibaray(void * handle,const char * Name, char * a, char * b,char * r){
cout<<"call Libaray:"<<Name<<endl;
PFUNC sub = (PFUNC)dlsym(handle,Name);
if(sub==NULL)
{
printf("dlsym fail:%s\n",dlerror());
return false;
}
sub(a,b,r);
return true;
}
もちろん、動的ライブラリの関数は、ASN.1コードから実際のデータを抽出する処理プログラムを追加する必要があります。といった:
#include <cstring>
#include<stdio.h>
#include <iostream>
#include <string>
using namespace std;
extern "C" {
void add(char * a,char * b,char * r){
int a_val=(a[1]<<8)|a[2];
int b_val=(b[1]<<8)|b[2];
int r_val= a_val+b_val;
r[1]=(r_val>>8)&0xff;
r[2]=r_val &0xff;
}
int sub(char * a,char * b,char * r){
int a_val=(a[1]<<8)|a[2];
int b_val=(b[1]<<8)|b[2];
int r_val= a_val-b_val;
r[1]=(r_val>>8)&0xff;
r[2]=r_val &0xff;
}
int multiply(char * a,char * b,char * r){
int a_val=(a[1]<<8)|a[2];
int b_val=(b[1]<<8)|b[2];
int r_val= a_val*b_val;
r[1]=(r_val>>8)&0xff;
r[2]=r_val &0xff;
}
int divide(char * a,char * b,char * r){
int a_val=(a[1]<<8)|a[2];
int b_val=(b[1]<<8)|b[2];
int r_val= a_val/b_val;
r[1]=(r_val>>8)&0xff;
r[2]=r_val &0xff;
}
}
結論として
実践により、FB_CALL関数ブロックを介してC ++動的ライブラリを呼び出すことには次の利点があることが証明されています。
- ランタイムと拡張機能ブロックを分離し、機能ブロックの開発、展開、更新を簡素化します。
- 動的ライブラリを提供することにより、関数ライブラリを提供できます。第三者の知的財産権を保護
- 動的に更新する方法を提供します
さらなる研究の方向性
エラー出力をCALL機能ブロックに追加する必要がありますか?
動的ライブラリにクラスとスレッドを含める方法。
ファンクションブロックプログラムを動的プログラムライブラリにカプセル化する可能性。