Defining Service
on OpenRTM-aist Python for Ubuntu
Ubuntu OS上のOpenRTM-aist Python版でサービスポートの基本的な使用方法を解説します。
▶ 更新履歴
1. はじめに
RTコンポーネント間の通信には、これまでデータポートを使用してきました。
RTミドルウェアでは、この他にサービスポートと呼ばれるポートを使って通信することができます。
サービスポートを利用するRTCはプロバイダとコンシューマに分かれます。
プロバイダはある機能を提供し、コンシューマはその機能を利用します。
簡単にいえば、プロバイダが持つ関数を、コンシューマが呼び出し利用する仕組みがサービスポートです。
サービスポートを使用したRTコンポーネントの作成手順は以下のようになります。
(1) サービが記述されたIDL(インターフェイス定義言語)ファイルの作成
(2) インターフェイスの実装
それでは早速、RTコンポーネントを作成してみましょう。
5. おわりに
これでOpenRTM-aist Python版で、サービスポートに基本的な使用方法を学べました。
データポートと違い、双方向のデータの通信が可能であること、データポートで表現できない概念を扱えることが利点です。
このように、サービスは適切に用いれば非常に強力な道具となります。
しかし、サービスポートは、他の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);
-
long qux(in long arg_1, out long arg_2, inout long arg_3);
-
};
-
};
-
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関数を定義しています。
-
8行目
戻り値と、それぞれin、out、inout方向属性の3種類の引数を持つ、qux関数を定義しています。
このように、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
-
言語 Python
サービスポートは、サービスポートタブに切り替えて設定します。
具体的なサービスポートの設定方法を以下に示します。
①のAdd Portボタンを押して、サービスポートを追加します。
次に、②のテキストボックスにポート名を入力します。
-
# long foo(in long arg)
-
def foo(self, arg):
-
# Must return: result
-
return arg * 10
-
-
# void bar(out long arg)
-
def bar(self):
-
arg = 20
-
-
# Must return: arg
-
return arg
-
-
# void baz(inout long arg)
-
def baz(self, arg):
-
arg *= 30
-
-
# Must return: arg
-
return arg
-
-
# long qux(in long arg_1, out long arg_2, inout long arg_3)
-
def qux(self, arg_1, arg_3):
-
result = arg_1 * arg_3
-
arg_2 = result * 10
-
arg_3 += 1
-
-
# Must return: result, arg_2, arg_3
-
return result, arg_2, arg_3
-
1-4行目
foo関数の実体を記述します。
foo関数は、in方向属性の引数を持っています。
-
3行目
foo関数がreturnすべき値と、その順番が示されています。
-
4行目
引数argに10を掛けた結果をreturnします。
-
6-11行目
bar関数の実体を記述します。
bar関数は、out方向属性の引数を持っています。
-
8行目
引数argに20を代入します。
-
10行目
bur関数がreturnすべき値と、その順番が示されています。
-
11行目
引数argをreturnします。
-
13-18行目
baz関数の実体を記述します。
baz関数は、inoutの方向属性の引数を持っています。
-
15行目
引数argに30を掛けあわせた値を代入します。
-
17行目
qux関数がreturnすべき値と、その順番が示されています。
-
18行目
引数argをreturnします。
-
20-27行目
qux関数の実体を記述します。
qux関数は、戻り値と3つの引数を持っています。
この引数はin、out、inoutの方向属性をそれぞれ持っています。
-
22行目
戻り値resultに、引数arg_1とarg_2を掛けあわせた値を代入します。
-
23行目
引数arg_2に、引数arg_1に10を掛けた値を代入します。
-
24行目
引数arg_3に、引数arg_3に1を足した値を代入します。
-
26行目
qux関数がreturnすべき値と、その順番が示されています。
-
27行目
戻り値resultと、引数arg_2、arg_3をreturnします。
今回は、TrialServiceProvider.pyには何も記述しません。
3.2 TrialServiceConsumerの作成
TrialServiceConsumerのテンプレートの設定は以下のようになります。
-
モジュール名 TrialServiceConsumer
-
ベンダ名 あなたの名前
-
モジュールカテゴリ TEST
-
実行周期 10
-
アクションコールバック
onExecute
-
サービスポート
ポート名 ConsumerPort
インターフェース名 MyService
方向 Required
IDLファイル TrialService.idlの格納場所のパス
インターフェース型 Trial::MyService
-
言語 Python
サービスポートは、先ほどと同様の方法で設定してください。
*インターフェース名やインターフェイスの方向を間違えないように注意してください。
テンプレート生成後は、先ほどと同様にプロジェクトのフォルダ内にIDLコンパイラで生成したフォルダとファイルをコピーします。
通常であれば以下のようになります。
C:\Users\ユーザ名\workspace/TrialServiceConsumerにTrialService_idl.py、Trial、Trial_POAをコピーします。
TrialServiceConsumer.pyの コードを以下に示します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
-
def onExecute(self, ec_id):
-
result_foo = self._MyService._ptr().foo(1)
-
print("result_foo: %d") %(result_foo)
-
-
arg_bar = self._MyService._ptr().bar()
-
print("return_bar: %d") %(arg_bar)
-
-
arg_baz = self._MyService._ptr().baz(3)
-
print("arg_baz: %d") %(arg_baz)
-
-
return_qux = self._MyService._ptr().qux(4, 5)
-
print("return_qux: %s") %(str(return_qux))
-
-
result_qux, arg_qux_1, arg_qux_2 = self._MyService._ptr().qux(4, 5)
-
print("result_qux: %d") %(result_qux)
-
print("arg_qux_1: %d") %(arg_qux_1)
-
print("arg_qux_2: %d") %(arg_qux_2)
-
-
return RTC.RTC_OK
-
2-3行目
foo関数を呼び出しとそれに伴う処理を行います。
-
2行目
foo関数の引数に数値1を与え、戻り値をresult_fooに代入します。
サービスに定義された関数は以下のように呼び出します。
self._インターフェース名._ptr().関数名(引数…)
インターフェース名はIDLファイルに定義したものではなく、RTC Builderに入力したインターフェース名です。
-
3行目
result_fooの値をコンソールに出力します。
-
5-6行目
bar関数を呼び出しとそれに伴う処理を行います。
-
5行目
bar関数の戻り値をarg_barに代入します。
-
6行目
arg_barの値をコンソールに出力します。
-
8-9行目
baz関数を呼び出しとそれに伴う処理を行います。
-
8行目
baz関数の引数に数値3を与え、戻り値をarg_buzに代入します。
-
15行目
arg_bazの値をコンソールに出力します。
-
11-12行目
qux関数を呼び出しとそれに伴う処理を行います。
-
11行目
qux関数の引数に数値4と5を与え、戻り値をreturn_quzに代入します。
-
12行目
return_quzをコンソールに出力します。
詳しくは後述しますが、return_quzはタプルであるため、文字列に変換して表示します。
-
14-17行目
qux関数を再び呼び出し、タプルをアンパックして受け取る方法を示しています。
-
14行目
qux関数の引数に数値4と5を与え、戻り値を複数の変数に代入します。
このように、Pythonのシーケンス型はアンパックして、複数の変数で受け取ることができます。
-
15-17行目
複数の戻り値をそれぞれコンソールに出力します。


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ファイルの完成です。

これで、サービスが設定できます。
設定後、いつもの様にテンプレートを出力してください。
次に、生成したプロジェクトのディレクトリの直下に先ほどIDLコンパイラで生成したフォルダとファイルをコピーします。
通常であれば以下のようになります。
C:\Users\ユーザ名\workspace/TrialServiceProviderにTrialService_idl.py、Trial、Trial_POAをコピーします。
これで、いつものようにコーディングできます。
ここで作成しているのはプロバイダ側のRTCなので、インターフェイスの実装をここで行います。
TrialService_idl_example.pyにインターフェイスの実装を記述します。
このファイルは以下のように命名されRTCのテンプレート出力時に、同時に出力されます。
IDLファイル名_idl_example.py
TrialService_idl_example.pyの コードを以下に示します。
以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。
また、出力時に関数内に記述されてるraiseは消してください。

①のAdd Interfaceボタンを押して、インターフェースを追加します。
②のドロップダウンリストから、Providedを選択します。
③のテキストボックスにインターフェース名を入力します。
*インターフェース名はプロバイダとコンシューマで同じ名前である必要があります。
④のBrowsボタンを押して、IDLファイルが格納されている場所を選択します。
IDLファイルが無事読み込まれれば、⑤のドロップダウンボタンからインターフェース型を選択します。
TrialServiceConsumer RTCのコンソールには以下の様に出力されています。
result_foo: 10
return_bar: 20
arg_baz: 90
return_quz: 90
return_qux: (20, 200, 6)
result_qux: 20
arg_qux_1: 200
arg_qux_2: 6
この結果を元に、もう一度関数の定義と、実装を確認しましょう。
まずは、foo関数です。
-
# long foo(in long arg)
-
def foo(self, arg):
-
# Must return: result
-
return arg * 10
foo関数は引数を10倍した値を戻す関数です。
argの値がfoo関数に渡され、result_fooに10倍された値が渡されているのがわかります。
つまり、方向属性inを指定すると、変数の値を渡せます。
次に、bar関数です。
-
# void bar(out long arg)
-
def bar(self):
-
arg = 20
-
-
# Must return: arg
-
return arg
bar関数は引数argに20を代入し、argをreturnする関数です。
戻り値として、argの値を受け取っているのがわかります。
このように、方向属性outを指定すると、戻り値として渡した変数の値を受け取ります。
次は、baz関数です。
-
# void baz(inout long arg)
-
def baz(self, arg):
-
arg *= 30
-
-
# Must return: arg
-
return arg
baz関数は引数に10を掛ける関数です。
baz関数に渡された値が、10倍されreturnされているのがわかります。
このように、方向属性inoutを指定すると、引数に値を渡せ、戻り値として渡した変数の値を受け取ります。
最後は、qux関数です。
2.3 TrialService.idlのコンパイル
TrialService.idlをIDLコンパイラでコンパイルします。
先ほど、TrialService.idlを保存したディレクトリまで移動し、以下のコマンドを実行します。
コンパイルに成功すると、以下のディレクトリやファイルが生成されます。
・TrialService_idl.py
・Trial
・Trial_POA
qux関数は戻り値と、3つの引数を持つ関数です。
それぞれの戻り値と、引数の挙動はこれまで個別に確認したとおりです。
ここで、注目するのはどのように複数の値をreturnするかです。
returnされた複数の値はタプルに格納されて受け取ります。
格納される順番はreturn時に指定した順番と同じです。
このreturnする変数の順番は7行目のようなテンプレートの出力時に記述されているコメントにしたがってください。
これで、サービスの定義や実装、特に方向属性の意味がわかったと思います。
方向属性の意味をまとめると、以下のようになります。
-
in
値を渡す
-
out
通常の戻り値とは別に値を受け取る
-
inout
値を渡し、通常の戻り値とは別に値を受け取る
Python版では、変数をinするときは引数で値を渡し、out時は戻り値として受け取るという点がC++版と異なるので注意してください。
-
# long qux(in long arg_1, out long arg_2, inout long arg_3)
-
def qux(self, arg_1, arg_3):
-
result = arg_1 * arg_3
-
arg_2 = result * 10
-
arg_3 += 1
-
-
# Must return: result, arg_2, arg_3
-
return result, arg_2, arg_3
$ omniidl -bpython TrialService.idl