サービスポートサンプル(SimpleService)を作る
Simple Wiki Based Contents Management System
ソフトウェア関連 >> RTコンポーネント関連 >> eSEAT2 >> サービスポートサンプル(SimpleService)を作る

サービスポートサンプル(SimpleService)を作る

OpenRTM-aistのサービスポートは、内部ではCORBAの関数呼び出しにほぼマッピングされており、非同期のデータ通信やリモート処理を行う枠組みです。eSEATでは、これまで、サービスポートのサポートを行っていませんでしたが、2017年11月の改修で、サービスポートを記述できるようにしました。ここでは、OpenRTM-aistのサンプルとして提供されているSampleServiceと同等の動作を行うeSEATコンポーネントを作成したいと思います。
なお、サービスポートの詳細に関しては、OpenRTM-aistオフィシャルサイトを参照してください。

IDLファイルをコンパイル

まず最初に、サービスインターフェースを定義するIDLファイルをコンパイルします。
SampleServiceでは、MyService.idlに下記のように定義されています。
module SimpleService {
  typedef sequence<string> EchoList;
  typedef sequence<float> ValueList;
  interface MyService
  {
        string echo(in string msg);
        EchoList get_echo_history();
        void set_value(in float value);
        float get_value();
        ValueList get_value_history();
  };
};
このファイルのコンパイルには、omniildを使用します。
# omniidl -bpython MyService.idl
これによって、
  • SimpleService/__init__.py
  • SimpeService__POA/__init__.py
  • MyService_idl.py
の3つのファイルが生成されます。
Comsumerサービスポートのみであれば、これで十分なのですが、Providerサービスポートを実装する場合には、実装ファイルが必要になります。ここでは、MyService_Impl.py にMyServiceSVC_implクラスを下記のように定義します。
class MyServiceSVC_impl(SimpleService__POA.MyService):
  def __init__(self):
    self._echoList = []
    self._valueList = []
    self._value = 0
    return

  def __del__(self):
    pass

  def echo(self, msg):
    self._echoList.append(msg)
    print "Message: ", msg
    return msg

  def get_echo_history(self):
    return self._echoList

  def set_value(self, value):
    self._valueList.append(msg)
    self._value = value
    return

  def get_value(self):
    return float(self._value)

  def get_value_history(self):
    return self._valueList

サービスポートの作成と呼出し

eSEATコンポーネントにサービスポートを付加するには、adaptorタグを使います。サービスポートを設定するには、type, name, interface, if_class の4つの属性が必要になります。サービスポート設定用の属性は、下記のとおりです。
属性説明
type'provider'(サービス提供)または 'comsumer'(サービス要求)のどちらかの値
nameサービスポート名(識別子)
interfaceサービスポートのインターフェース。「インターフェース型+'|'+インターフェース名」で表された文字列
if_classインターフェースのクラス:'provider'の場合は、サービスの実装クラス、'consumer'の場合には、サービスインターフェース名となります。
この値が '.'で区切られていた場合には、'.'の前の文字列がモジュール名となり自動的に'import'します
ここで扱っているMyServiceの場合、
プロバイダ
<adaptor name="MyService_P" type="provider" interface="MyService|myservice0" if_class="MyService_Impl.MyServiceSVC_impl" />
コンシューマ
<adaptor name="MyService_C" type="consumer" interface="MyService|myservice0" if_class="SimpleService.MyService" />
と記述することができます。プロバイダの設定でif_class属性値が "MyService_Impl.MyServiceSVC_impl"となっていますが、この場合には、サービスポート生成時に 'import MyService_Impl'が自動実行されますので、この記述のみで設定完了となります。コンシューマの場合には、この記述では、サービス要求のポート設定のみですので、実際にサービスを呼び出すには、<script>タグの中で callService というメソッド呼出しを行う必要があります。例えば、"MyService_C"のポートを介して、"echo"というサービスを呼び出す場合には、
seat.callService("MyService_C", "echo", "Hello")
となります。'seat'は、eSEATコンポーネントを表す変数であり、callServiceメソッドの第一引数がサービスポート名、第二引数がサービス名、第三引数以降は、呼び出すサービスへ与える引数となります。
ただし、callServiceメソッドは、サービスの呼出し後、サービスが終了するまで処理がブロックされます。ノンブロックの呼出しを行いたい場合には、サービス呼出しをファンクタ変換し、callServiceAsyncメソッドを使ってサービスを呼び出します。
以上でサービスポートの作成と呼出しの説明は終了です。
下記に、SimpleIO.seatmlにSimpleService.MyServiceのサービスポートを追加した例を示します。このseatmlファイルでeSEATを起動すれば、SimpleService.MyServiceのコンシューマポートとプロバイダポートが追加されたRTCが起動します。

 <?xml version="1.0" encoding="UTF-8"?>
 <seatml>
   <general name="SimpleService">

    <adaptor name="str_out" type="rtcout" datatype="TimedString" />
    <adaptor name="str_in" type="rtcin" datatype="TimedString" />

    <adaptor name="MyService_C" type="consumer" interface="MyService|myservice0" if_class="SimpleService.MyService"  />
    <adaptor name="MyService_P" type="provider" interface="MyService|myservice0" if_class="MyService_Impl.MyServiceSVC_impl" />
  </general>

  <state name="main_mode">

    <label text="SimpleIO" colspan="4" bg_color="blue" />

    <brk />
    <label text="Input:" />
    <input id="textIn" width="50" colspan="2">
        <message sendto="str_out" input="main_mode:textIn" />
    </input>
    <button label="Send">
        <message sendto="str_out" input="main_mode:textIn" />
    </button>
    <brk />
    <button label="Echo">
      <script>
        seat.callService("MyService_C", "echo", seat.getEntry("main_mode:textIn"))
        seat.appendText("main_mode:textOut", "\n==Finish Echo==")
      </script>
    </button>
    <button label="SetValue">
      <script>
         seat.callService("MyService_C", "set_value", float(seat.getEntry("main_mode:textIn")))
      </script>

    <button label="GetValue">
      <script>
        retval = seat.callService("MyService_C", "get_value")
        seat.appendText("main_mode:textOut", "\n==="+str(retval)+"===")
      </script>
    </button>

    <brk />
  
    <label text="Coming Text:" />
    <text id="textOut" width="50" height="5" colspan="3" >
	    INIT String...
    </text>
    <brk />
    <button label="Clear">
      <script>
        seat.clearText("main_mode:textOut")
      </script>
    </button>
 
    <rule source="str_in">
      <script>
        seat.appendText("main_mode:textOut", rtc_in_data.data+"\n")
      </script>
    </rule>
  </state>
 </seatml>