[RTC][Jython]RTC Builder を使って Jython で動くRTコンポーネントを作る
OpenRTM-aist http://openrtm.org/openrtm/ja は、C++, Java, Python 版が用意されていますが、今回は Jython, すなわち JVMで動くPython の上で動作するRTコンポーネントを作成します。
(OpenRTM-aist をインストール済みで、RTC Builderを使って OpenRTM-aist-python 向けのRTコンポーネントを作れる人向け)
Python RTC の作成
まず RTC Builderで、普通にPython版を作ります。
RTC Builder でプロジェクトを作成し、設定項目を以下のように埋めます。
(IDLには、Python版のサンプルのSimpleService に含まれる、MyService.idl をコピーして使います)
基本タブ
- モジュール名: MyServiceProvider
- バージョン: 1.0.0
- ベンダ名: You & Me
- モジュールカテゴリ: Test
サービスポートタブ
- ポート名: MyService
- インターフェース名: myservice0
- 方向: Provided
- IDLファイル: /home/you/workspace/JythonRTC/MyService.idl
- インターフェース型: SimpleService::MyService
- インターフェース名: myservice0
idlコンパイル
通常の Python RTC と違って、idlのコンパイルには idlj を使います。
% idlj -fall MyService.idl
% cd SimpleService
% javac *.java
注:MyServicePOA.java の操作は、未チェックまたは安全ではありません。
注:詳細については、-Xlint:unchecked オプションを指定して再コンパイルしてください。
警告メッセージが出ますが、気にする必要はありません。
MyServiceProvider.py の修正
生成されたソースを修正します。
まず第1行目、shebang行の
#!/usr/bin/env python
import 部
# Import RTM module import RTC import OpenRTM_aist
これを、
import jp.go.aist.rtm.RTC OpenRTM_aist = jp.go.aist.rtm.RTC OpenRTM_aist.__dict__['Properties'] = jp.go.aist.rtm.RTC.util.Properties OpenRTM_aist.__dict__['CorbaPort'] = jp.go.aist.rtm.RTC.port.CorbaPort
このように直します。
こうすることで、OpenRTM-aist-Pythonと、OpenRTM-aist-Java の API の差異を吸収することができ、OpenRTM_aist ではじまるクラス名を修正する必要がなくなります。
とくに特殊変数 __dict__ を使うことで、OpenRTM_aist に Properties や CorbaPort プロパティを追加できるあたりは Python をはじめとする軽量言語の、トリッキーで便利なところです。
MyServiceProvider
- onInitialize
self._MyServicePort.registerProvider("myservice0", "SimpleService.MyService", self._myservice0)
第2引数を "SimpleService.MyService" から "MyService" に変更します。(これは、RTCのサンプルのMyServiceConsumerと接続するため)
また、
return RTC.RTC_OK
返り値 RTC.RTC_OK を、self.super__onInitialize() に変更します。
return self.super__onInitialize()
コメントアウトされているメソッドについては省略しますが、これらのコメントアウトを解除して利用するときは、同様に返り値を スーパークラスメソッド呼び出しに変更しなくてはなりません。
- MyServiceNewFunc, MyServiceDeleteFunc の追加
コメントアウトされている MyServiceProvider::onRateChanged の定義と、MyServiceInit 関数の定義の間にふたつのクラス MyServiceNewFunc, MyServiceDeleteFunc の定義を追加します。
class MyServiceNewFunc(OpenRTM_aist.RtcNewFunc): def createRtc(self, manager): return MyServiceProvider(manager) class MyServiceDeleteFunc(OpenRTM_aist.RtcDeleteFunc): def deleteRtc(self, rtcBase): rtcBase = None
createRtc メソッドが、MyServiceProvider を生成して返すところが重要になります。
ほかのコンポーネントを Jython 化する場合、この部分を対象に合わせて適宜読み替えてください。
- MyServiceProviderInit 関数の修正
profile = OpenRTM_aist.Properties(defaults_str=myserviceprovider_spec)
default_str=を削除します。
manager.registerFactory(profile, MyServiceProvider, OpenRTM_aist.Delete)
第2引数 MyServiceProvider と、第3引数 OpenRTM_aist.Delete を、
それぞれ MyServiceNewFunc() と MyServiceDeleteFunc() に変更します。これらはコンストラクタなので、末尾の()を忘れてはいけません。
MyServiceProviderInit関数(修正後)
def MyServiceProviderInit(manager): profile = OpenRTM_aist.Properties(myserviceprovider_spec) manager.registerFactory(profile, MyServiceNewFunc(), MyServiceDeleteFunc())
- MyModuleInit 関数を MyModuleInitProc クラスにパック
def MyModuleInit(manager):
この行の前に以下の行を入れます。
class MyModuleInitProc(OpenRTM_aist.ModuleInitProc):
そして def MyModuleInit... から、comp = managerr.createComponent... までの空行を含む4行をタブ一つ分右へずらし、メソッド名の頭の M を小文字の m にして、第一引数 self を追加します。
MyModuleInitProc クラス(修正後)
class MyModuleInitProc(OpenRTM_aist.ModuleInitProc): def myModuleInit(self, manager): MyServiceProviderInit(manager) # Create a component comp = manager.createComponent("MyServiceProvider")
くれぐれも、第一引数 self を忘れないで。
- main関数の修正
mgr.setModuleInitProc(MyModuleInit)
引数 MyModuleInit を、MyModuleInitProc() に変更します。
mgr.setModuleInitProc(MyModuleInitProc())
Python では関数オブジェクトを渡していますが、Java では ModuleInitProc のサブクラスのインスタンスを渡します。
MyServiceProvider.py の修正はここまでです。
MyServide_idl_example.py の修正
import 部
もともとの2行
import CORBA, PortableServer import SimpleService, SimpleService__POA
これを削除し、
import time from SimpleService import MyServicePOA
に変更します。
time モジュールは、Jython化と直接関係はなく、単にあとの MyService_i.echo の定義でtime.sleep を使うためにいれたものです。
MyService_i 定義
class MyService_i (SimpleService__POA.MyService):
この行のスーパークラスを、SimpleService__POA.MyService から、MyServicePOA に変更します。
class MyService_i (MyServicePOA):
__init__, echo 等のインスタンスメソッドは普通に実装します。(以下、実装例)
class MyService_i (MyServicePOA): def __init__(self): self._echoList = [] self._valueList = [] self._myValue = 0 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." for i in range(len(self._echoList)): print repr(i) + ": " + self._echoList[i] return self._echoList def set_value(self, value): self._valueList.append(value) self._myValue = value print "MyService::set_value() was called." print "Current value: ", self._myValue def get_value(self): print "MyService::get_value() was called." print "Current value: ", self._myValue return float(self._myValue) def get_value_history(self): print "MyService::get_value_history() was called.",len(self._valueList) for i in range(len(self._valueList)): print repr(i) + ": " + repr(self._valueList[i]) return self._valueList
注意点として、string やリストを返す場合はそのままでいいのですが、float 値を返すメソッドでは return float(value) とした方がいいようです。
また、こちらにも直接コマンドとして呼ばれたときのためのメインコードがありますが、削除しても問題ありません。