サービスポートのコンシューマ機能を利用する

RTCは、データポートの他にサービスポートというポートを利用できます。 サービスポートはRTC間でサービス呼び出しを行うためのポートです。 サービスポートには、実装したサービスを他のRTCに公開するプロバイダ機能と、 他のRTCが公開しているサービスを呼び出すコンシューマ機能があります。 PyRTSeamではコンシューマ機能のみをサポートしています。

PyRTSeamでサービスポートのコンシューマ機能を利用する方法は、 データポートでユーザ定義型を利用する方法と似ています。 サービスの型を定義したIDLファイルをコンパイルした後、 プロファイルでポートの定義を記述し、ロジックでサービスを呼び出します。

サービスポートのコンシューマ機能を利用する場合、必要なIDLファイルはプロバイダ側の RTCから提供されますので、IDLファイルを新たに記述する必要はありません。 IDLコンパイルは、 データポートでユーザ定義型を利用する で述べている手順と同様に行うことができます。

呼び出すサービスの定義を記述する

サービスポートのコンシューマ機能を利用するには、rtseam.Profile.__init__の引数 consumersに辞書(サービス名:IDLで定義されたサービスの型)を与えます。

例えば、次のIDLファイル(example.idl)があるとします:

module my_service
{
interface EchoService
{
    string echo(in string data);
};
};

このIDLファイルで定義されているmy_service.EchoServiceを”EchoService0”という サービス名で利用するには次のようにプロファイルを定義します:

PROFILE = rtseam.Profile(consumers={"EchoService0": my_service.EchoService})

辞書に複数のアイテムを入れれば、複数のサービスポートを定義できます。

サービスポートには本来、ポート名、インスタンス名、型名という3つの名前を与えます。 PyRTSeamでは、プロファイルに与えたサービス名(辞書のキー) がポート名とインスタンス名として使われ、 サービスの型の__name__属性が型名として使われます。 前述の例では、ポート名とインスタンス名が”EchoService0”となり、 型名が”EchoService”となります。

もし、型名を、型の__name__属性とは異なる名前にしたい場合、次のように タプルを与えることで変更することができます:

PROFILE = rtseam.Profile(consumers={"EchoService0": (my_service.EchoService, "MyEchoService")})

注意

サービスポートは、インスタンス名、型名がプロバイダ側とコンシューマ側で 完全に一致していないと通信できない仕様になっています。 例えば、プロバイダ側のインスタンス名が”Service0”、型名が”Service”で、 コンシューマ側のインスタンス名が”Service”、型名が”MyService”の場合、 通信できません。 RT System Editor上では、ポートの接続は成功し、 ポート間が接続されているように見えますが、実際には通信できない状態になっています。 PyRTSeamのプロファイルでサービスの定義を記述する際は、インスタンス名、 型名がプロバイダ側とコンシューマ側で一致するように注意してください。

ロジックでサービスを呼び出す

サービスポートのコンシューマ機能を利用するロジックは、引数として 辞書(サービス名:サービス)を受け取るようにします。

PyRTSeamは、RTCのonExecuteでロジックを呼び出す前に、接続されている サービスポートからサービスを取り出し、ロジックの引数に渡します。 例えば、前述のexample.idlで定義されているmy_service.EchoServiceを “EchoService0”というサービス名で利用する場合、RTCのロジックは次のようにします:

def print_echo(input_data):
    if "EchoService" in input_data:
        echo_service = input_data["EchoService"]
        print echo_service.echo("echo message")

ノート

サービスポートとデータポートを併用することもできます。 RTCがサービスポートとデータ入力ポートを両方持っている場合、 ロジックの引数に渡される辞書には、データ入力ポートから 読み込んだデータとサービスの両方が含まれています。

ロジックをクラスで作成する場合は、__call__メソッドで同じようにします。

ソースコード例

SimpleServiceを呼び出すRTC

OpenRTM-aistにサンプルとして付属しているSimpleServiceのMyServiceProviderの サービスを呼び出すRTC:

import rtseam.openrtm

rtseam.openrtm.compile_idl("MyService.idl")
import SimpleService

PROFILE = rtseam.Profile(name="ServiceConsumer",
                         consumers={"myservice0": SimpleService.MyService})

def invoke_echo_service(input_data):
    if "myservice0" in input_data:
        service = input_data["myservice0"]
        print service.echo("hello, world")

rtseam.openrtm.run(PROFILE, invoke_echo_service)