top of page

Defining Service

on OpenRTM-aist Python for Windows

 Windows 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の方向属性の意味を考えましょう。

ロボコンマガジン
  1. module Trial

  2. {

  3.     interface MyService

  4.     {

  5.         long foo(in long arg);

  6.         void bar(out long arg);

  7.         void baz(inout long arg);

  8.         long qux(in long arg_1, out long arg_2, inout long arg_3);

  9.     };

  10. };

  • 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」というフォルダを作成して下さい。

ここでは、以下の場所に作成したして説明します。

 

C:\Users\ユーザ名\Documents\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ボタンを押して、サービスポートを追加します。

次に、②のテキストボックスにポート名を入力します。

  1.     # long foo(in long arg)

  2.     def foo(self, arg):

  3.         # Must return: result

  4.         return arg * 10

  5.    

  6.     # void bar(out long arg)

  7.     def bar(self):

  8.         arg = 20

  9.  

  10.         # Must return: arg        

  11.         return arg

  12.  

  13.     # void baz(inout long arg)

  14.     def baz(self, arg):

  15.         arg *= 30

  16.  

  17.         # Must return: arg        

  18.         return arg

  19.  

  20.     # long qux(in long arg_1, out long arg_2, inout long arg_3)

  21.     def qux(self, arg_1, arg_3):

  22.         result = arg_1 * arg_3

  23.         arg_2 = result * 10

  24.         arg_3 += 1

  25.  

  26.         # Must return: result, arg_2, arg_3

  27.         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の コードを以下に示します。

以下のコードは抜粋となります。生成したテンプレートに当てはめて下さい。

  1.     def onExecute(self, ec_id):

  2.                 result_foo = self._MyService._ptr().foo(1)

  3.                 print("result_foo: %d") %(result_foo)

  4.  

  5.                 arg_bar = self._MyService._ptr().bar()

  6.                 print("return_bar: %d") %(arg_bar)

  7.  

  8.                 arg_baz = self._MyService._ptr().baz(3)

  9.                 print("arg_baz: %d") %(arg_baz)

  10.  

  11.                 return_qux = self._MyService._ptr().qux(4, 5)

  12.                 print("return_qux: %s") %(str(return_qux))

  13.  

  14.                 result_qux, arg_qux_1, arg_qux_2 = self._MyService._ptr().qux(4, 5)

  15.                 print("result_qux: %d") %(result_qux)

  16.                 print("arg_qux_1: %d") %(arg_qux_1)

  17.                 print("arg_qux_2: %d") %(arg_qux_2)

  18.     

  19.         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関数です。
  1.     # long foo(in long arg)

  2.     def foo(self, arg):

  3.         # Must return: result

  4.         return arg * 10

 foo関数は引数を10倍した値を戻す関数です。
argの値がfoo関数に渡され、result_fooに10倍された値が渡されているのがわかります。

 

つまり、方向属性inを指定すると、変数の値を渡せます。

 

次に、bar関数です。

  1.     # void bar(out long arg)

  2.     def bar(self):

  3.        arg = 20

  4.  

  5.         # Must return: arg        

  6.         return arg

 bar関数は引数argに20を代入し、argをreturnする関数です。
戻り値として、argの値を受け取っているのがわかります。
 
 このように、方向属性outを指定すると、戻り値として渡した変数の値を受け取ります。
 
次は、baz関数です。
  1.     # void baz(inout long arg)

  2.     def baz(self, arg):

  3.         arg *= 30

  4.  

  5.         # Must return: arg        

  6.         return arg

 baz関数は引数に10を掛ける関数です。
baz関数に渡された値が、10倍されreturnされているのがわかります。
 
 このように、方向属性inoutを指定すると、引数に値を渡せ、戻り値として渡した変数の値を受け取ります。

 

最後は、qux関数です。

2.3 TrialService.idlのコンパイル

 

 TrialService.idlをIDLコンパイラでコンパイルします。

先ほど作成した「TrialService.idl」をPython 2.6.6のフォルダにコピーします。

デフォルトであればパスは以下のようになります。

 

C:\Python26

 

IDLファイルをコピーしたらコマンドプロンプトを起動します。
コマンドプロンプトに以下のコマンドを入力してください。
Python 2.6.6のフォルダに移動し、IDLコンパイルを行います。

コンパイルに成功すると、以下のフォルダやファイルが生成されます。

 

・TrialService_idl.py

・Trial

・Trial_POA

cd C:\Python26

omniidl.exe -bpython TrialService.idl

 qux関数は戻り値と、3つの引数を持つ関数です。

それぞれの戻り値と、引数の挙動はこれまで個別に確認したとおりです。

ここで、注目するのはどのように複数の値をreturnするかです。

 

returnされた複数の値はタプルに格納されて受け取ります。

格納される順番はreturn時に指定した順番と同じです。

 

このreturnする変数の順番は7行目のようなテンプレートの出力時に記述されているコメントにしたがってください。

 

 

これで、サービスの定義や実装、特に方向属性の意味がわかったと思います。

方向属性の意味をまとめると、以下のようになります。

 

  • in

値を渡す

 

  • out

通常の戻り値とは別に値を受け取る

 

  • inout

値を渡し、通常の戻り値とは別に値を受け取る

 

Python版では、変数をinするときは引数で値を渡し、out時は戻り値として受け取るという点がC++版と異なるので注意してください。

  1.     # long qux(in long arg_1, out long arg_2, inout long arg_3)

  2.     def qux(self, arg_1, arg_3):

  3.         result = arg_1 * arg_3

  4.         arg_2 = result * 10

  5.         arg_3 += 1

  6.  

  7.         # Must return: result, arg_2, arg_3

  8.         return result, arg_2, arg_3

bottom of page