ソフトウェア関連 >> RTコンポーネント関連 >> eSEAT_v2.5 >> SEATMLファイルの書き方 >> RTコンポーネントの作成(eSEAT) >> eSEATでSimpleService
eSEATでSimpleService
SeqIOに引き続き、SimpleServiceのサンプルをSEATMLで作成していきます。
作成するMyService_providerとMyService_consumerは、以下のようになっています。
作成するMyService_providerとMyService_consumerは、以下のようになっています。
- MyService_provider
- 下のIDLで記述されたSimpleService::MyServiceのサービスインターフェースを持つRTC。提供するサービスは''echo'': 引数のメッセージを規定回数(10回)表示''get_echo_history'': echoサービスを呼び出した履歴を返す''set_value'':引数の数値を記憶する''get_value'':記憶した数値を返す''get_value_history'':set_valueサービスの呼び出し履歴を返す
- MyService_consumer
- コンソールからMySerivceのサービスの呼び出しコマンドを入力し、結果を表示する
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();
};
};
MyService_providerを実装する
まずは、ひな形のSEATMLファイルをMyService_provider.seatmlといファイルにコピーします。
# cd ~/work
# source /usr/local/eSEAT/setup.bash
# gen_seatml MyService_provider
また、上記のIDLファイルをMyService.idlという名前で保存しておきます。
次に、適当なエディタで MyService_provider.seatmlを開き、コンポーネント名の変更とサービスポートの追加を行います。サービスを実装したクラス名を MyServiceSVC_impl としおきます。
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="MyService_provider">
<adaptor name="myservice0" type="provider"
interface="SimpleService::MyService|SimpleService.MyService"
if_class ="MyServiceSVC_impl" />
</general>
<state name="main_state">
</state>
</seatml>
次に、MyServiceSVC_implクラスを実装していきます。このクラスの実装は、SEATML内に直接記述する方法と、別ファイルに実装する方法があります。
ここでは、SEATML内に記述していきます。
ここでは、SEATML内に記述していきます。
eSEATでは、SEATMLの記述順で評価していますので、MyServiceSVC_impl定義は、adaptorの前に記述する必要があります。
MyServiceSVC_implは、Pythonで実装しますので、<script>要素で下のように定義します。
MyServiceSVC_implは、Pythonで実装しますので、<script>要素で下のように定義します。
<script import="SimpleService__POA" >
class seq_print:
def __init__(self):
self._cnt = 0
return
def __call__(self, val):
print( self._cnt, ": ", val)
self._cnt += 1
return
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( "MyService::echo() was called.")
for i in range(10):
print( "Message: ", msg )
time.sleep(1)
print( "MyService::echo() was finished." )
return msg
def get_echo_history(self):
print( "MyService::get_echo_history() was called.")
func=seq_print()
for val in self._echoList:
func(val)
return self._echoList
def set_value(self, value):
self._valueList.append(value)
self._value = value
print( "MyService::set_value() was called." )
print( "Current value: ", self._value )
return
def get_value(self):
print( "MyService::get_value() was called." )
print( "Current value: ", self._value )
return float(self._value)
def get_value_history(self):
print( "MyService::get_value_history() was called." )
func=seq_print()
for val in self._valueList:
func(val)
return self._valueList
</script>
この定義は、ほぼSimpleServiceProvider.pyのものなので、OpenRTM_aistモジュールで定義された便利な機能を展開したものになっています。また、<script>要素には、import属性を設定することができ、eSEAT内部では、 'import SimpleService, SimpleService__POA'が実行されることになります。
最後に、MyService.idlをコンパイルします。IDLファイルのコンパイルには、idlcompile.sh または idlcompile.batを使います。
# idlcompile.sh MyService.idl
このスクリプトは、下記の操作と同じです。
# mkdir rtm
# omniidl -bpython -Crtm MyService.idl
以上で、ServiceportのProviderの設定は終了です。
eSEATでは、eSEATの実行ディレクトリの下の 'rtm' というディレクトリにPythonモジュールのimportパスを設定していますので、IDLファイルのコンパイル時に ''-Crtm'' として出力場所を指定しています。もちろん、 PYTHONPAYH でしている場所に出力しても問題ありません。
サービスの実装を別ファイルにする(参考)
上の例では、サービスの実装をSEATMLに記述しましたが、MyServiceSVC_impl等を別ファイルに記述して、adaptorを生成することもできます。この方法は、<script>要素のexecfile属性で読み込み方法と<adaptor>要素のimple_file属性を使う方法があります。
まず、MyServiceSVC_implの実装ファイルMyService.pyを下記のように作成します。
import SimpleService__POA
class seq_print:
def __init__(self):
self._cnt = 0
return
def __call__(self, val):
print( self._cnt, ": ", val)
self._cnt += 1
return
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( "MyService::echo() was called.")
for i in range(10):
print( "Message: ", msg )
time.sleep(1)
print( "MyService::echo() was finished." )
return msg
def get_echo_history(self):
print( "MyService::get_echo_history() was called.")
func=seq_print()
for val in self._echoList:
func(val)
return self._echoList
def set_value(self, value):
self._valueList.append(value)
self._value = value
print( "MyService::set_value() was called." )
print( "Current value: ", self._value )
return
def get_value(self):
print( "MyService::get_value() was called." )
print( "Current value: ", self._value )
return float(self._value)
def get_value_history(self):
print( "MyService::get_value_history() was called." )
func=seq_print()
for val in self._valueList:
func(val)
これは、SEATMLで記述したものに ''import SimpleService__POA''を追記したものです。このファイルを用いた場合に、SimpleSerive_provider.seatmlは、下記の2通りの書き方があります。
- <script>要素を利用
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="MyService_provider">
<script execfile="MyService.py" />
<adaptor name="myservice0" type="provider"
interface="SimpleService::MyService|SimpleService.MyService"
if_class ="MyServiceSVC_impl" />
</general>
<state name="main_state">
</state>
</seatml>
- <adaptor>要素を利用
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="MyService_provider">
<adaptor name="myservice0" type="provider"
impl_file="MyService.py"
interface="SimpleService::MyService|SimpleService.MyService"
if_class ="MyServiceSVC_impl" />
</general>
<state name="main_state">
</state>
</seatml>
MyService_consumerを実装する
次に、Comsumerを実装します。まずは、ひな形のSEATMLファイルをMyService_consumer.seatmlといファイルにコピーしましょう。
# cd ~/work
# source /usr/local/eSEAT/setup.bash
# gen_seatml MyService_consumer
次に、適当なエディタで MyService_consumer.seatmlを開き、コンポーネント名の変更とサービスポートの追加を行います。
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="MyService_consumer">
<adaptor name="myservice0" type="consumer"
interface="SimpleService::MyService|SimpleService.MyService" />
</general>
<state name="main_state">
</state>
</seatml>
次に、providerに対する呼び出し部分を実装します。今回の実装は、MyServiceConsumer.pyと同じ機能を実装します。
MyServiceConsumerと同じように<onexec>要素で実装していきます。
<onexec>要素の記述の前に、functorクラスとパラメータの初期化(onInitializedの部分)を定義します。
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="MyService_consumer">
<adaptor name="myservice0" type="consumer"
interface="SimpleService::MyService|SimpleService.MyService" />
<script>
seat._async_echo = None
seat._result = [None]
</script>
<script>
class echo_functor:
def __init__(self, msg, result):
self._msg = msg
self._result = result
return
def __call__(self, obj):
try:
if omniORB.CORBA.is_nil(obj):
print( "No service connected." )
else:
self._result[0] = obj.echo(self._msg)
except:
pass
class seq_print:
def __init__(self):
self._cnt = 0
return
def __call__(self, val):
print( self._cnt, ": ", val )
self._cnt += 1
return
</script>
</general>
<state name="main_state">
</state>
</seatml>
最後に、<onexec>要素(コンソールからのコマンド入力部分)を追加します。
<onexec>
<script>
print( "\n" )
print( "Command list: " )
print( " echo [msg] : echo message." )
print( " set_value [value]: set value." )
print( " get_value : get current value." )
print( " get_echo_history : get input messsage history." )
print( " get_value_history: get input value history." )
print( "> ", end="")
args = str(sys.stdin.readline())
argv = args.split()
argv[-1] = argv[-1].rstrip("\n")
if seat._async_echo and seat._async_echo.finished():
print( "echo() finished: ", seat._result[0] )
seat._async_echo = None
if argv[0] == "echo" and len(argv) > 1:
if not seat._async_echo:
retmsg = ""と
func = echo_functor(argv[1], seat._result)
seat._async_echo = seat.callServiceAsync('myservice0', func)
else:
print( "echo() still invoking" )
elif argv[0] == "set_value" and len(argv) > 1:
val = float(argv[1])
seat.callService('myservice0','set_value', val)
print( "Set remote value: ", val )
elif argv[0] == "get_value":
retval = seat.callService('myservice0', 'get_value')
print( "Current remote value: ", retval )
elif argv[0] == "get_echo_history":
OpenRTM_aist.CORBA_SeqUtil.for_each(seat.callSeとrvice('myservice0', 'get_echo_history'),
seq_print())
elif argv[0] == "get_value_history":
OpenRTM_aist.CORBA_SeqUtil.for_each(seat.callService('myservice0', 'get_value_history'),
seq_print())
</script>
</onexec>
この実装では、Providerのサービスの呼び出しに、callServiceとcallAsycServiceの2つのメソッドを使っています。前者は、通常のRPCのように応答を待つ場合で、後者は、Providerの実行を待たずに終了し、あとで結果を受け取るという形式です。
この実装でもSimpleIOのConsoleInと同様に、コンソールからの入力待ちになっています。
以上で、ProviderとConsumerの実装は終了です。
MyService_providerとMyService_consumerの動作を確認する
作成したMyService_provider.seatmlとMyService_consumer.seatmlを起動させて、動作の確認を行います。
ターミナルを2つ起動し、下記のコマンドを入力します。
ターミナル1:
# cd ~/work
# eSEAT MyService_provider.seatml
ターミナル2:
# cd ~/work
# eSEAT MyService_consumer.seatml
次に、RT System Editorまたはrtshellを使用して、2つのコンポーネントの接続を行ってください。ここでは、rtshellでの接続例を示します。
# rtcon /localhost/MyService_provider.rtc:myservice0 /localhost/MyService_consumer.rtc:myservice0
最後に、2つのコンポーネントをアクティベートします。
# rtact /localhost/MyService_provider.rtc
# rtact /localhost/MyService_consumer.rtc
ここまで行えば、MyService_consumerを起動したターミナル2からコマンドを入力して、動作確認をしてください。正常に動作していれば、MyService_providerとMyService_consumerをディアクティベートして、終了して下さい。
# rtdeact /localhost/MyService_provider.rtc
# rtdeact /localhost/MyService_consumer.rtc
# rtexit /localhost/MyService_provider.rtc
# rtexit /localhost/MyService_consumer.rtc
資料