サービスポートサンプル(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>