top of page

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

ロボコンマガジン
  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.     };

  9. };

  • 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などは全て消してください。

  1. CORBA::Long MyServiceSVC_impl::foo(CORBA::Long arg)

  2. {

  3.   return arg * 10;

  4. }

  5.  

  6. void MyServiceSVC_impl::bar(CORBA::Long& arg)

  7. {

  8.   arg = 10;

  9. }

  10.  

  11. void MyServiceSVC_impl::baz(CORBA::Long& arg)

  12. {

  13.   arg *= 10;

  14. }

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

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

  1. RTC::ReturnCode_t TrialServiceConsumer::onExecute(RTC::UniqueId ec_id)

  2. {

  3.   long arg_foo = 1;

  4.   long return_foo = m_MyService->foo(arg_foo);

  5.  

  6.   std::cout << "return_foo: " << return_foo << std::endl;

  7.   std::cout << "arg_foo: " << arg_foo << std::endl;

  8.  

  9.   long arg_bar = 2;

  10.   m_MyService->bar(arg_bar);

  11.   std::cout << "arg_bar: " << arg_bar << std::endl;

  12.  

  13.   long arg_baz = 3;

  14.   m_MyService->baz(arg_baz);

  15.   std::cout << "arg_baz: " << arg_baz << std::endl;

  16.  

  17.   return RTC::RTC_OK;

  18. }

  • 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関数です。
  1. // long foo(in long arg)  foo関数の定義

  2.  

  3. CORBA::Long MyServiceSVC_impl::foo(CORBA::Long arg)

  4. {

  5.   return arg * 10;

  6. }

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

また、渡したのは値だけなので、関数呼び出し後にarg_fooの値は変化していません。

 

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

 

次に、bar関数です。

  1. // void bar(out long arg)  bar関数の定義

  2.  

  3. void MyServiceSVC_impl::bar(CORBA::Long& arg)

  4. {

  5.   arg = 10;

  6. }

 bar関数は引数に10を代入する関数です。
arg_barがbar関数に渡され、引数argに10が代入されているのがわかります。
関数呼び出し後にarg_barの値は10に変更されています。
 
 このように、方向属性outを指定すると、引数に渡した変数のリファレンスが渡せます。
ただし、outは受け取り専用なので、変数の値は渡すことはできません。
 
最後に、baz関数です。
  1. // void baz(inout long arg) baz関数の定義

  2.  

  3. void MyServiceSVC_impl::baz(CORBA::Long& arg)

  4. {

  5.   arg *= 10;

  6. }

 baz関数は引数に10を掛ける関数です。
arg_bazがbaz関数に渡され、引数argに引数argが10倍された値が代入されているのがわかります。
関数呼び出し後にarg_barの値は30に変更されています。
 
 このように、方向属性inoutを指定すると、引数に渡した変数のリファレンスが渡せます。

outと異なり、変数の値を渡すことができるので、3 * 10となり、30が代入されています。

 

 

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

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

 

  • in

値渡し

 

  • out

参照渡し(値は受け取りのみ)

 

  • inout

参照渡し

bottom of page