Defining Service
on OpenRTM-aist C++ for Ubuntu
Ubuntu OS上のOpenRTM-aist C++版でサービスポートの基本的な使用方法を解説します。
▶ 更新履歴
1. はじめに
RTコンポーネント間の通信には、これまでデータポートを使用してきました。
RTミドルウェアでは、この他にサービスポートと呼ばれるポートを使って通信することができます。
サービスポートを利用するRTCはプロバイダとコンシューマに分かれます。
プロバイダはある機能を提供し、コンシューマはその機能を利用します。
簡単にいえば、プロバイダが持つ関数を、コンシューマが呼び出し利用する仕組みがサービスポートです。
サービスポートを使用したRTコンポーネントの作成手順は以下のようになります。
(1) サービが記述されたIDL(インターフェイス定義言語)ファイルの作成
(2) インターフェイスの実装
それでは早速、RTコンポーネントを作成してみましょう。
5. おわりに
これでOpenRTM-aist C++版で、サービスポートの基本的な使用方法を学べました。
データポートと違い、双方向のデータの通信が可能であること、データポートで表現できない概念を扱えることが利点です。
このように、サービスは適切に用いれば非常に強力な道具となります。
しかし、サービスポートは、他のRTCと交互性を保ちにくという欠点もあります。
サービスは、データポートで表現できない場合のみ使用するよう心がけて下さい。
3. サービスを利用したRTCの作成
先ほど定義したMyServiceを利用したRTCを作成します。
今回は、TrialServiceProviderとTrialServiceConsumerを作成します。
TrialServiceProviderはサービスの機能を確認するための簡単な演算を行う関数を提供します。
TrialServiceConsumerはTrialServiceProviderが提供する関数を利用します。
いつものように、RTC Builderを使用して各RTCを作成します。
4. 動作確認
作成したRTCを実行し、動作確認を行って下さい。
このとき、サービスを利用したRTCは以下の手順に従ってActivateしてください。
①TrialServiceProvider RTCとTrialServiceConsumer RTCを接続する。
②TrialServiceProvider RTCをActivateする。
③TrialServiceConsumer RTCをActivateする。
このように、プロバイダと接続し、プロバイダがActive状態になってからでないと、コンシューマは正常に実行できません。
これは、コンシューマはプロバイダの関数を呼び出し、処理を委託しているためです。
処理の委託時に、プロバイダと接続され、プロバイダがActive状態である必要があります。
それでは、作成したRTCの動作を見て行きましょう。
TrialServiceProvider RTCはコンソールに値を出力していないので、何も表示されません。
ここでは、TrialServiceConsumer RTCに注目し、in、out、inoutの方向属性の意味を考えましょう。
-
module Trial
-
{
-
interface MyService
-
{
-
long foo(in long arg);
-
void bar(out long arg);
-
void baz(inout long arg);
-
};
-
};
-
1-9行目
Trialというモジュールの中に、MyServiceというサービスを定義します。
モジュールの中には複数の型や、サービスを定義することができます。
わかりやすいサービス名を設定し、サービスを定義して下さい。
-
5行目
fooという関数を定義します。
関数は以下のように定義します。
戻り値の型 関数名(方向属性 型 変数名, …)
関数の定義にしたがい、long foo(in long arg)を読むと以下のようになります。
・戻り値 long
・関数名 foo
・方向属性 in
・変数名 arg
基本的には普通の関数と一緒ですが、C++言語では見慣れないのは、変数の方向属性だと思います。
この方向属性は3種類あります。
・in
・out
・inout
MyServiceにはこれらの方向属性in、out、inoutをそれぞれ備えた、関数が定義されています。
実際に動作させながら、その機能を確認します。
-
6行目
out方向属性の引数を持つ、bar関数を定義しています。
-
7行目
inout方向属性の引数を持つ、baz関数を定義しています。
このように、IDLファイルにはサービスに属する関数の定義を記述します。
ここで使用できる変数の型は、以下の様なCORBAの基本型と、基本型を組み合わせて定義した型のみです。
2. IDLファイルの作成
独自データ型のときに、少し触れましたがサービスもIDLファイルに定義します。
サービスを記述したIDLファイルを作成しましょう。
2.1 保存ディレクトリの作成
IDLファイルを作成する前に、「MyIDL」というディレクトリを作成して下さい。
ここでは、以下の場所に作成したして説明します。
~/openrtm/MyIDL
IDLファイルは一度作成したら何度も使用することになるので、頻繁に移動する必要がない場所に保存してください。
2.2 TrialService.idlの作成
TrialService.idlというIDLファイルを作成します。
以下のコードを、適当なテキストエディタで入力してください。
3.1 TrialServiceProviderの作成
TrialServiceProviderのテンプレートの設定は以下のようになります。
-
モジュール名 TrialServiceProvider
-
ベンダ名 あなたの名前
-
モジュールカテゴリ TEST
-
実行周期 10
-
アクションコールバック
onExecute
-
サービスポート
ポート名 ProviderPort
インターフェース名 MyService
方向 Provided
IDLファイル TrialService.idlの格納場所のパス
インターフェース型 Trial::MyService
-
言語 C++
サービスポートは、サービスポートタブに切り替えて設定します。
具体的なサービスポートの設定方法を以下に示します。
①のAdd Portボタンを押して、サービスポートを追加します。
次に、②のテキストボックスにポート名を入力します。
# Filter a list to remove all strings matching the regex in _pattern. The
# output is placed in the variable pointed at by _output.
macro(FILTER_LIST _list _pattern _output)
set(${_output})
foreach(_item ${@_list@})
if("${_item}" MATCHES ${_pattern})
set(${_output} ${${_output}} ${_item})
endif("${_item}" MATCHES ${_pattern})
endforeach(_item)
endmacro(FILTER_LIST)
これで、いつものようにCMakeを用いてビルド環境の設定が行えます。
ここで作成しているのはプロバイダ側のRTCなので、インターフェイスの実装をここで行います。
TrialServiceProvider/srcディレクトリ内のTrialServiceSVC_impl.cppにインターフェイスの実装を記述します。
TrialServiceSVC_impl.cppの コードを以下に示します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
また、出力時に関数内に記述されてるifdefなどは全て消してください。
-
CORBA::Long MyServiceSVC_impl::foo(CORBA::Long arg)
-
{
-
return arg * 10;
-
}
-
-
void MyServiceSVC_impl::bar(CORBA::Long& arg)
-
{
-
arg = 10;
-
}
-
-
void MyServiceSVC_impl::baz(CORBA::Long& arg)
-
{
-
arg *= 10;
-
}
-
1-4行目
foo関数の実体を記述します。
foo関数は、Long型の戻り値ちと、Long型の変数を持っています。
この変数argの方向属性はinでした。
-
3行目
引数argに10を掛けた結果を戻します。
-
6-9行目
bar関数の実体を記述します。
bar関数は、Long型の変数を持っています。
この変数argの方向属性はoutでした。
先ほどと違い、引数がリファレンスであることに注目してください。
-
8行目
引数argに10を代入します。
-
11-14行目
baz関数の実体を記述します。
baz関数は、Long型の変数を持っています。
この変数argの方向属性はinoutでした。
inoutも引数がリファレンスであることに注目してください。
-
11行目
引数argに10を掛けます。
以上のコーディングが終了したら、~/workspace/TrialServiceProvider/build/srcにあるMakefileを使用してmakeを行います。
今回は、TrialServiceProvider.cppには何も記述しません。
無事ビルドできた場合は、~/workspace/TrialServiceProvider/build/srcに「TrialServiceProviderComp」が生成されているはずです。
~/workspace/TrialServiceProviderにある「TrialServiceProvider.conf」と「rtc.conf」を、
実行ファイルがある~/workspace/TrialServiceProvider/build/srcにコピーして下さい。
これで、TrialServiceProvider RTCの完成です。
3.2 TrialServiceConsumerの作成
TrialServiceConsumerのテンプレートの設定は以下のようになります。
-
モジュール名 TrialServiceConsumer
-
ベンダ名 あなたの名前
-
モジュールカテゴリ TEST
-
実行周期 10
-
アクションコールバック
onExecute
-
サービスポート
ポート名 ConsumerPort
インターフェース名 MyService
方向 Required
IDLファイル TrialService.idlの格納場所のパス
インターフェース型 Trial::MyService
-
言語 C++
サービスポートは、先ほどと同様の方法で設定してください。
*インターフェース名やインターフェイスの方向を間違えないように注意してください。
テンプレートを出力したら、先ほどと同様にcmakeファイルを編集してください。
編集後、CMakeを用いてソリューションファイルを作成してください。
TrialServiceConsumer.cppの コードを以下に示します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
-
RTC::ReturnCode_t TrialServiceConsumer::onExecute(RTC::UniqueId ec_id)
-
{
-
long arg_foo = 1;
-
long return_foo = m_MyService->foo(arg_foo);
-
-
std::cout << "return_foo: " << return_foo << std::endl;
-
std::cout << "arg_foo: " << arg_foo << std::endl;
-
-
long arg_bar = 2;
-
m_MyService->bar(arg_bar);
-
std::cout << "arg_bar: " << arg_bar << std::endl;
-
-
long arg_baz = 3;
-
m_MyService->baz(arg_baz);
-
std::cout << "arg_baz: " << arg_baz << std::endl;
-
-
return RTC::RTC_OK;
-
}
-
3-7行目
foo関数を呼び出しとそれに伴う処理を行います。
-
3行目
foo関数に与える引数arg_fooを定義し、1で初期化します。
-
4行目
foo関数の戻り値を格納する変数return_fooを定義し、foo関数の戻り値で初期しています。
サービスに定義された関数は以下のように呼び出します。
m_インターフェース名->関数名(引数…)
インターフェース名はIDLファイルに定義したものではなく、RTC Builderに入力したインターフェース名です。
-
6行目
return_fooの値をコンソールに出力します。
-
7行目
arg_fooの値をコンソールに出力します。
-
9-11行目
bar関数を呼び出しとそれに伴う処理を行います。
-
9行目
bar関数に与える引数arg_barを定義し、2で初期化します。
-
10行目
bar関数を呼び出します。
-
11行目
arg_barの値をコンソールに出力します。
-
13-15行目
baz関数を呼び出しとそれに伴う処理を行います。
-
13行目
baz関数に与える引数arg_bazを定義し、3で初期化します。
-
14行目
baz関数を呼び出します。
-
15行目
arg_bazの値をコンソールに出力します。
以上のコーディングが終了したら、~/workspace/TrialServiceConsumer/build/srcにあるMakefileを使用してmakeを行います。
無事ビルドできた場合は、~/workspace/TrialServiceConsumer/build/srcに「TrialServiceConsumerComp」が生成されているはずです。
~/workspace/TrialServiceConsumerにある「TrialServiceConsumer.conf」と「rtc.conf」を、
実行ファイルがある~/workspace/TrialServiceConsumer/build/srcにコピーして下さい。
これで、TrialServiceConsumer RTCの完成です。


TrialServiceConsumer RTC
TrialServiceProvider RTC
short
long
unsinged short
unsinged long
float
double
char
wchar
boolean
octet
longlong
ulonglong
sequence<T>
short型整数
long型整数
符号なしshort型整数
符号なしlong型整数
単精度浮動小数点
倍精度浮動小数点
文字型
wchar文字型
bool型
octet型
longlong型整数
符号なしlonglong型整数
Tのsequence型
入力したら、コードはTrialService.idlというファイル名で、MyIDLディレクトリ内に保存してください。
*拡張子はtxtではなくidlで保存してください。
これで、サービスを定義したIDLファイルの完成です。

これで、サービスが設定できます。
設定後、いつもの様にテンプレートを出力してください。
ここで、いつもであればCMakeを使用してビルド環境の設定を行います。
しかし、独自定義のIDLファイルを使用する場合は、cmakeファイルを編集する必要があります。
-
TrialServiceProviderフォルダ内の「utils.cmake」
下記にutils.cmakeのコードを抜粋して示します。
「foreach(_item ${${_list}})」の部分を赤字のように書き換えて下さい。

①のAdd Interfaceボタンを押して、インターフェースを追加します。
②のドロップダウンリストから、Providedを選択します。
③のテキストボックスにインターフェース名を入力します。
*インターフェース名はプロバイダとコンシューマで同じ名前である必要があります。
④のBrowsボタンを押して、IDLファイルが格納されている場所を選択します。
IDLファイルが無事読み込まれれば、⑤のドロップダウンボタンからインターフェース型を選択します。
TrialServiceConsumer RTCのコンソールには以下の様に出力されています。
return_foo: 10
arg_foo: 1
arg_bar: 10
arg_baz: 30
この結果を元に、もう一度関数の定義と、実装を確認しましょう。
まずは、foo関数です。
-
// long foo(in long arg) foo関数の定義
-
-
CORBA::Long MyServiceSVC_impl::foo(CORBA::Long arg)
-
{
-
return arg * 10;
-
}
foo関数は引数を10倍した値を戻す関数です。
arg_fooの値がfoo関数に渡され、return_fooに10倍された値が渡されているのがわかります。
また、渡したのは値だけなので、関数呼び出し後にarg_fooの値は変化していません。
つまり、方向属性inを指定すると、変数の値を渡せます。
次に、bar関数です。
-
// void bar(out long arg) bar関数の定義
-
-
void MyServiceSVC_impl::bar(CORBA::Long& arg)
-
{
-
arg = 10;
-
}
bar関数は引数に10を代入する関数です。
arg_barがbar関数に渡され、引数argに10が代入されているのがわかります。
関数呼び出し後にarg_barの値は10に変更されています。
このように、方向属性outを指定すると、引数に渡した変数のリファレンスが渡せます。
ただし、outは受け取り専用なので、変数の値は渡すことはできません。
最後に、baz関数です。
-
// void baz(inout long arg) baz関数の定義
-
-
void MyServiceSVC_impl::baz(CORBA::Long& arg)
-
{
-
arg *= 10;
-
}
baz関数は引数に10を掛ける関数です。
arg_bazがbaz関数に渡され、引数argに引数argが10倍された値が代入されているのがわかります。
関数呼び出し後にarg_barの値は30に変更されています。
このように、方向属性inoutを指定すると、引数に渡した変数のリファレンスが渡せます。
outと異なり、変数の値を渡すことができるので、3 * 10となり、30が代入されています。
これで、サービスの定義や実装、特に方向属性の意味がわかったと思います。
方向属性の意味をまとめると、以下のようになります。
-
in
値渡し
-
out
参照渡し(値は受け取りのみ)
-
inout
参照渡し