開発中のC言語ラッパーツールです.
ラッパーといっても「Hey Yo!」とかいうやつではなく,純粋にC言語の関数でOpenRTM-aistの機能がよびだせるようにするためのラッピングツールです.
全体概要
RTMAdapterというツールを開発しました.RTMAdapterはC++版のC言語の関数を被せただけのライブラリです.
ただ,独自データ型などを利用したい場合に備えて,データ型に依存する部分を分離し,後から別ライブラリで参照できるように工夫がなされています.興味がある人はソースコードをごらんください.
データ型に依存した部分,現場ではデータポートのみですが,この部分のコードはRTCDataTypeAdapterというツールを使って,IDLファイルから自動生成します.
インストール
OpenRTM-aist C++ 1.1.1 RELEASE版で動作確認をしています.主にOpenRTM-aist 1.1.1 RELEASE C++版とVisual Studio 12 2013でのテストを行っていますが,gccでもチェックしています.
RTMAdapterライブラリをインストールし,データ型のC言語ラッパーを自動生成するためのRTCDataTypeAdapterというPythonで書かれたツールをインストールします.
環境構築
必要な環境は,
OpenRTM-aist 1.1.1 C++ Release 32bit版
CMake3.2
Python2.7 (32bit)
PyYaml
Visual Studio 2013 Community
です.
ソースコードからのインストール
ソースコードをダウンロードします.
ソースコードはこちら
展開したフォルダのCMakeLists.txtをCMakeGUIに渡します.
RTCのビルド同様に,buildフォルダを作ってからconfigure -> generateとします.
この時コンパイラはVisual Studio 12 2013を選択してください.
buildフォルダに生成されたslnファイルをVisual Studio 2013で開き,Release版をビルドします.
ビルド後にINSTALLプロジェクトをbuildすると,C:¥Program Files (x86)¥RTMAdapter¥フォルダにライブラリがインストールされます.
このとき,dllはSysWow64フォルダにもインストールされますので,必要なDLLはすでにインストールされているPCのPATHに入っています.
以降はCMakeでfind_package(RTMAdapter)で見つかるようになります.
同様にDebug版もビルドしてINSTALLしておくと良いと思います.Debug版はライブラリ名の後にdの文字が入るようになっています.
データポート用ライブラリを作る
データポートは利用するデータ型に依存したコード・バイナリになっており,
本ツールでは,データ型ごとにスタブと呼ばれるコードおよびDLLを作ります.BasicDataTypeにおいても同様です.
まず,RTCDataTypeAdapterをインストールします.
ソースコードはこちら
Pythonで組まれています.このツールのインストールには,idl_parserというライブラリが必要です.IDLのパーサー.これもysugaが作りました.
ソースコードはこちら
どちらもこのコマンドでインストールできるはずです.
$ python setup.py install
さて,インストールされるとgenerate_adaptor.pyというスクリプトがインストールされます.
BasicDataType.idlを%RTM_ROO%/rtm/idlフォルダあたりから持ってきて,
$ generate_adaptor.py BasicDataType.idl -I "%RTM_ROOT%/rtm/idl" -bc -o output
というコマンドでoutputというフォルダにBasicDataTypeというプロジェクトが生成されます.
-Iはインクルードディレクトリで他のIDLが配置されているディレクトリ.-oは出力フォルダ.-bはバックエンドで今回はC言語なのでcです.
あとはそれをCMakeして,Visual Studioでビルドします.
同様にINSTALLをビルドするとC:¥Program Files (x86)¥BasicDataTypeフォルダにインストールされます.
このとき,BasicDataType.dllはSysWow64フォルダにもインストールされますので,必要なDLLはすでにインストールされているPCのPATHに入っています.
デバッグ版もビルドしておくと便利でしょう.
テスト
サンプルプロジェクトをここに置いておきます.
https://github.com/sugarsweetrobotics/RTMAdapterTest
CMakeしてビルドすればRTMAdapterTestという実行ファイルが出来上がります.
もともともomniORBやRTMのヘッダーを読み込まないのでビルドがめちゃくちゃ早いです.ただ,Cに変換してある関数のみしか使えないので,Cで使うためのツールというよりは,
この関数をさらに別の言語やツールでラッピングするためのツールとして開発しています.
test.cの中身は以下のようになります.
main関数ではマネージャを起動して,my_module_init関数を登録しています.managerがactivateされるとmy_module_init関数が呼ばれてRTCがcreateされます.
RTMの名前も変えることができるのですが,それはヘッダーを見てもらえれば分かると思います.
作成したRTCを引数としてすぐにinitialize関数を呼び,RTCにデータポートを追加しています.
さらにon_activatedやon_executeのコールバックを登録します.これでRTCがアクティブ化されるとコールバック関数が呼ばれるようになります.
#include#include #include "RTMAdapter-1.0/manager_adapter.h" #include "RTMAdapter-1.0/coil_adapter.h" #include "BasicDataType-1.0/BasicDataType.h" RTC_t rtc; // -------------- RTC Functions ------------------- int initialize(RTC_t rtc); int on_activated(int ec_id); int on_deactivated(int ec_id); int on_execute(int ec_id); // -------------- Module Initialization ------------- void my_module_init(Manager_t m) { Manager_RTMAdapter_init(m); if (rtc = Manager_createComponent(m, "RTMAdapter") == RESULT_INVALID_RTC) { printf("#Error. Invalid RTC ID.\n"); return; } initialize(rtc); RTC_onActivated_listen(rtc, on_activated); RTC_onDeactivated_listen(rtc, on_deactivated); RTC_onExecute_listen(rtc, on_execute); } // ------------ Main Function -------------------- int main(int argc, char* argv[]) { Manager_t m = Manager_initManager(argc, argv); Manager_init(m, argc, argv); Manager_setModuleInitProc(m, my_module_init); Manager_activateManager(m); Manager_runManager(m, 0); return 0; } //------------------------ RTC Code --------------------------- DataType_t _d_in_RTC_TimedLong, _d_out_RTC_TimedLong; Port_t _in_RTC_TimedLongIn, _out_RTC_TimedLongOut; DataType_t _d_in_RTC_TimedDouble, _d_out_RTC_TimedDouble; Port_t _in_RTC_TimedDoubleIn, _out_RTC_TimedDoubleOut; DataType_t _d_in_RTC_TimedString, _d_out_RTC_TimedString; Port_t _in_RTC_TimedStringIn, _out_RTC_TimedStringOut; DataType_t _d_in_RTC_TimedLongSeq, _d_out_RTC_TimedLongSeq; Port_t _in_RTC_TimedLongSeqIn, _out_RTC_TimedLongSeqOut; DataType_t _d_in_RTC_TimedDoubleSeq, _d_out_RTC_TimedDoubleSeq; Port_t _in_RTC_TimedDoubleSeqIn, _out_RTC_TimedDoubleSeqOut; /** * Initialize RTC. * on_initialize can not be captured in current version. * Please initialize RTC in my_module_init proc. */ int initialize(RTC_t rtc) { _d_in_RTC_TimedLong = RTC_TimedLong_create(); _in_RTC_TimedLongIn = InPort_RTC_TimedLong_create("RTC_TimedLong_in", _d_in_RTC_TimedLong); if (RTC_addInPort(rtc, "RTC_TimedLong_in", _in_RTC_TimedLongIn) < 0) { return -1; } _d_out_RTC_TimedLong = RTC_TimedLong_create(); _out_RTC_TimedLongOut = OutPort_RTC_TimedLong_create("RTC_TimedLong_out", _d_out_RTC_TimedLong); if (RTC_addOutPort(rtc, "RTC_TimedLong_out", _out_RTC_TimedLongOut) < 0) { return -1; } _d_in_RTC_TimedDouble = RTC_TimedDouble_create(); _in_RTC_TimedDoubleIn = InPort_RTC_TimedDouble_create("RTC_TimedDouble_in", _d_in_RTC_TimedDouble); if (RTC_addInPort(rtc, "RTC_TimedDouble_in", _in_RTC_TimedDoubleIn) < 0) { return -1; } _d_out_RTC_TimedDouble = RTC_TimedDouble_create(); _out_RTC_TimedDoubleOut = OutPort_RTC_TimedDouble_create("RTC_TimedDouble_out", _d_out_RTC_TimedDouble); if (RTC_addOutPort(rtc, "RTC_TimedDouble_out", _out_RTC_TimedDoubleOut) < 0) { return -1; } _d_in_RTC_TimedString = RTC_TimedString_create(); _in_RTC_TimedStringIn = InPort_RTC_TimedString_create("RTC_TimedString_in", _d_in_RTC_TimedString); if (RTC_addInPort(rtc, "RTC_TimedString_in", _in_RTC_TimedStringIn) < 0) { return -1; } _d_out_RTC_TimedString = RTC_TimedString_create(); _out_RTC_TimedStringOut = OutPort_RTC_TimedString_create("RTC_TimedString_out", _d_out_RTC_TimedString); if (RTC_addOutPort(rtc, "RTC_TimedString_out", _out_RTC_TimedStringOut) < 0) { return -1; } _d_in_RTC_TimedLongSeq = RTC_TimedLongSeq_create(); _in_RTC_TimedLongSeqIn = InPort_RTC_TimedLongSeq_create("RTC_TimedLongSeq_in", _d_in_RTC_TimedLongSeq); if (RTC_addInPort(rtc, "RTC_TimedLongSeq_in", _in_RTC_TimedLongSeqIn) < 0) { return -1; } _d_out_RTC_TimedLongSeq = RTC_TimedLongSeq_create(); _out_RTC_TimedLongSeqOut = OutPort_RTC_TimedLongSeq_create("RTC_TimedLongSeq_out", _d_out_RTC_TimedLongSeq); if (RTC_addOutPort(rtc, "RTC_TimedLongSeq_out", _out_RTC_TimedLongSeqOut) < 0) { return -1; } _d_in_RTC_TimedDoubleSeq = RTC_TimedDoubleSeq_create(); _in_RTC_TimedDoubleSeqIn = InPort_RTC_TimedDoubleSeq_create("RTC_TimedDoubleSeq_in", _d_in_RTC_TimedDoubleSeq); if (RTC_addInPort(rtc, "RTC_TimedDoubleSeq_in", _in_RTC_TimedDoubleSeqIn) < 0) { return -1; } _d_out_RTC_TimedDoubleSeq = RTC_TimedDoubleSeq_create(); _out_RTC_TimedDoubleSeqOut = OutPort_RTC_TimedDoubleSeq_create("RTC_TimedDoubleSeq_out", _d_out_RTC_TimedDoubleSeq); if (RTC_addOutPort(rtc, "RTC_TimedDoubleSeq_out", _out_RTC_TimedDoubleSeqOut) < 0) { return -1; } return 0; } int on_activated(int ec_id) { printf("on_activated called.\n"); return 0; } int on_deactivated(int ec_id) { printf("on_deactivated called.\n"); return 0; } int on_execute(int ec_id) { int32_t flag; static int i; uint32_t sec, nsec; InPort_RTC_TimedLong_isNew(_in_RTC_TimedLongIn, &flag); if (flag) { InPort_read(_in_RTC_TimedLongIn, &flag); int32_t ld; RTC_TimedLong_get(_d_in_RTC_TimedLong, &sec, &nsec, &ld); printf("TimedLong : %d %d %d\n", sec, nsec, ld); } RTC_TimedLong_set(_d_out_RTC_TimedLong, 0, 0, i); OutPort_write(_out_RTC_TimedLongOut); InPort_RTC_TimedDouble_isNew(_in_RTC_TimedDoubleIn, &flag); if (flag) { InPort_read(_in_RTC_TimedDoubleIn, &flag); double dd; RTC_TimedDouble_get(_d_in_RTC_TimedDouble, &sec, &nsec, &dd); printf("TimedDouble : %d %d %f\n", sec, nsec, dd); } RTC_TimedDouble_set(_d_out_RTC_TimedDouble, 0, 0, i*0.1); OutPort_write(_out_RTC_TimedDoubleOut); InPort_RTC_TimedString_isNew(_in_RTC_TimedStringIn, &flag); if (flag) { InPort_read(_in_RTC_TimedStringIn, &flag); char stringBuf[256]; RTC_TimedString_get(_d_in_RTC_TimedString, &sec, &nsec, stringBuf); printf("TimedString : %d %d %s\n", sec, nsec, stringBuf); } char stringBuffer[256]; sprintf(stringBuffer, "TimedString Data is %d", i); RTC_TimedString_set(_d_out_RTC_TimedString, 0, 0, stringBuffer); OutPort_write(_out_RTC_TimedStringOut); InPort_RTC_TimedLongSeq_isNew(_in_RTC_TimedLongSeqIn, &flag); if (flag) { InPort_read(_in_RTC_TimedLongSeqIn, &flag); uint32_t len; RTC_TimedLongSeq_data_getLength(_d_in_RTC_TimedLongSeq, &len); int32_t* data = malloc(len * sizeof(int32_t)); RTC_TimedLongSeq_get(_d_in_RTC_TimedLongSeq, &sec, &nsec, data, &len); printf("TimedLongSeq : \n"); for (int i = 0; i < len; i++) { printf(" - %d\n", data[i]); } free(data); } RTC_TimedLongSeq_data_setLength(_d_out_RTC_TimedLongSeq, 3); int32_t ldata[3] = { i, i + 1, i + 2 }; RTC_TimedLongSeq_set(_d_out_RTC_TimedLongSeq, 0, 0, ldata, 3); OutPort_write(_out_RTC_TimedLongSeqOut); InPort_RTC_TimedDoubleSeq_isNew(_in_RTC_TimedDoubleSeqIn, &flag); if (flag) { InPort_read(_in_RTC_TimedDoubleSeqIn, &flag); uint32_t len; RTC_TimedDoubleSeq_data_getLength(_d_in_RTC_TimedDoubleSeq, &len); double* data = malloc(len * sizeof(double)); RTC_TimedDoubleSeq_get(_d_in_RTC_TimedDoubleSeq, &sec, &nsec, data, &len); printf("TimedDoubleSeq : \n"); for (int i = 0; i < len; i++) { printf(" - %f\n", data[i]); } free(data); } RTC_TimedDoubleSeq_data_setLength(_d_out_RTC_TimedDoubleSeq, 3); double ddata[3] = { i*0.1, (i + 1)*0.1, (i + 2)*0.1 }; RTC_TimedDoubleSeq_set(_d_out_RTC_TimedDoubleSeq, 0, 0, ddata, 3); OutPort_write(_out_RTC_TimedDoubleSeqOut); i++; return 0; }
解説
このツールを使うと,CMakeでデータポートのライブラリだけを参照すればRTCを作れるようになります.
たとえば,BasicDataType.idlを変換してデータ型用関数を生成すると以下のようになります.
DATAADAPTER_API DataType_t RTC_TimedLong_create(); DATAADAPTER_API Result_t RTC_TimedLong_set(DataType_t d, uint32_t tm_sec, uint32_t tm_nsec, int32_t data); DATAADAPTER_API Result_t RTC_TimedLong_get(DataType_t d, uint32_t* tm_sec, uint32_t* tm_nsec, int32_t* data); DATAADAPTER_API Port_t InPort_RTC_TimedLong_create(char* name, DataType_t d); DATAADAPTER_API Result_t InPort_RTC_TimedLong_isNew(Port_t port, int32_t* flag); DATAADAPTER_API Port_t OutPort_RTC_TimedLong_create(char* name, DataType_t d);
プリミティブなデータ型のみで構造体にアクセスできるようにしてあります.最近のツールだと,構造体をサポートしているツールも少なくないのですが,この方法が一番面倒が少ないと思ったのでこうなっています.複雑なデータ型だとアクセスが大変です.