Scala で動くRTコンポーネントを作る?
Jython, JRuby ときて、Scala でもRTコンポーネントを作ってみました。
実は Scala を扱うのは初めてで、なかなか勝手がつかめませんでした。
以下のサンプルも動作はするのですが、ひとつだけクリアできなかった問題があります。
RtcDeleteFunc::deleteRtc の引数 rtcBase に null を代入できません。
このままだとMyServiceConsumerImpl インスタンスは解放されないので、今回の Scala RTC サンプルはまあ問題提起みたいなものです。
解決法のわかる方があればアドバイスを。
ExceptionCatcher トレイトは、onInitialize や onExecute で発生したエラーをコンソールに表示するためのものです。
そもそも、RTコンポーネント内のエラーは捕捉しづらい。気がつくとRTシステムエディタ上でRTコンポーネントが真っ赤になって、原因究明にえらい苦労します。
python ではアクション関数ごとにいちいち try 〜 except を書いていたのですが、たった一行の処理に6行も例外処理を書くはめになって非常にしょっぱい。
そこで、トレイトと無名関数の引数渡しを組み合わせて、この部分を簡略化しました。
showIfError {} で処理を囲むことで、{}内で発生した例外をコンソールに表示します。
ただし、MyServiceConsumerImpl のコンストラクタではtry ~ catch が書けなかったり、registerFactory の引数として、RtcXXXFunc を匿名クラスで継承してその場で xxxRtc を実装するあたりではトレイトが使えなかったため、try ~ catch 丸出しになっています。 (ここは普通に継承すべきだったかも……)
/** * MyServiceConsumer.scala */ import SimpleService.MyService import jp.go.aist.rtm.RTC.Manager; import jp.go.aist.rtm.RTC.ModuleInitProc; import jp.go.aist.rtm.RTC.RTObject_impl; import jp.go.aist.rtm.RTC.DataFlowComponentBase; import jp.go.aist.rtm.RTC.RtcDeleteFunc; import jp.go.aist.rtm.RTC.RtcNewFunc; import jp.go.aist.rtm.RTC.port.CorbaConsumer; import jp.go.aist.rtm.RTC.port.CorbaPort; import jp.go.aist.rtm.RTC.util.Properties; import RTC.ReturnCode_t; /* * アクションメソッドでのエラーをコンソールに表示する */ trait ExceptionCatcher { def showIfError[T](f: => T): Unit = { try { Some(f) } catch { case e: Exception => e.printStackTrace() } } } /* * アクションメソッドの実装 */ class MyServiceConsumerImpl(manager: Manager) extends DataFlowComponentBase(manager) with ExceptionCatcher { val myServicePort = new CorbaPort("MyService"); val myservice0Base = new CorbaConsumer(classOf[MyService]) override def onInitialize: ReturnCode_t = { showIfError { myServicePort.registerConsumer("myservice0", "MyService", myservice0Base) addPort(myServicePort) } super.onInitialize() } override def onExecute(ec_id: Int): ReturnCode_t = { showIfError { println("""Command list: echo [msg] : echo message. set_value [value]: set value. get_value : get current value. get_echo_history : get input messsage history. get_value_history: get input value history.""") val line = readLine("> ") if (line == "") return super.onExecute(ec_id) val argv = line.split(" ") val myservice0 = myservice0Base._ptr argv(0) match { case "echo" if argv.length > 1 => { val retmsg = myservice0.echo(argv(1)) println("echo() return: " + retmsg) } case "set_value" if argv.length > 1 => { myservice0.set_value(argv(1).toFloat) printf("Set remote value: %s\n", argv(1)) } case "get_value" => { val retval = myservice0.get_value printf("Current remote value: %f\n", retval) } case "get_echo_history" => { var i: Int = 0 val history = myservice0.get_echo_history history.foreach { h => i += 1 printf("%d: %s\n", i, h) } } case "get_value_history" => { var i: Int = 0 val history = myservice0.get_value_history history.foreach { h => i += 1 printf("%d: %s\n", i, h) } } case _ => println("Invalid command or argument(s).") } } super.onExecute(ec_id) } } object MyServiceConsumer extends ModuleInitProc with ExceptionCatcher { val component_conf = Array( "implementation_id", "MyServiceConsumer", "type_name", "MyServiceConsumer", "description", "MyService Consumer Sample written by Scala", "version", "1.0.0", "vendor", "thegoodbadandugly", "category", "Sample", "activity_type", "DataFlowComponent", "max_instance", "1", "language", "Scala", "lang_type", "compile", "") override def myModuleInit(mgr: Manager) { mgr.registerFactory( new Properties(component_conf), new RtcNewFunc { def createRtc(manager: Manager): RTObject_impl = { try { return new MyServiceConsumerImpl(manager) } catch { case e: Exception => e.printStackTrace() } null } }, new RtcDeleteFunc { def deleteRtc(rtcBase: RTObject_impl): Unit = { try { rtcBase// = null } catch { case e: Exception => e.printStackTrace() } } } ) mgr.createComponent("MyServiceConsumer") } def main(args: Array[String]): Unit = { val manager = Manager.init(args) println("Set module initialization proceduer") manager.setModuleInitProc(MyServiceConsumerComp) println("Activate manager and register to naming service") manager.activateManager println("run the manager in blocking mode") manager.runManager } }
実行
export RTM_JAVA_ROOT=(OpenRTM-aist-Java をインストールしたディレクトリ)/OpenRTM-aist/1.0 export CLASSPATH=.:$RTM_JAVA_ROOT/jar/OpenRTM-aist-1.0.0.jar:$RTM_JAVA_ROOT/jar/commons-cli-1.1.jar scalac MyServiceConsumer.scala scala MyServiceConsumer
MyServiceProvider の方はまた後日。