*2:[http://d.hatena.ne.jp/okazuki/20111209/1323446919:title]でも100倍という性能差になったそうです。*3:1メッセージ読むたびにイベントの登録が解除されるため、データの取りこぼしの可能性があるかも?*4:本来、MVVM的にはこういった処理はModelに入れるべきですが、今回はサンプルということで全部ViewModelに書いてます。Extensionsを使って、ロボットの位置を算出するプログラムを書いてみました。位置を算出するだけではあまり実用的ではありません。Extensionsを適用することを考えてみます。RTミドルウェアは、ロボットソフトウェアの機能(センサやアクチュエータやアルゴリズム)をRTコンポーネントと呼ばれる単位でモジュール化し、それらをネットワークを介して組み合わせてロボットシステムを構築するためのフレームワークです。RTコンポーネントは、自身の状態に応じて周期的な処理を実行し、データポートやサービスポートなどの仕組みを利用して、他のRTコンポーネントと通信を行います。Extensionsがマッチするのではないかと考えてみました。Extensionsを活用するとRTコンポーネントがこんな風に実装できるよというのを紹介します。C++で実装されたOpenRTM-aistとC#のコードを比較しているので、ちょっと分かりにくいですがご容赦を。データポートの同期まずデータポートの同期について考えてみます。RTコンポーネントでは、複数のRTコンポーネントからのデータ入力を待ち受けるということがよくあります。OpenRTM-aistでは、複数のデータ入力を待ちうける場合、公式ページにも書かれているように、on_executeのなかでisNew()でデータの有無を周期的にチェックするのが一般的でしょう。}しかしこの方法では、2つのデータが揃った瞬間ではなく、RTコンポーネントの動作周期のタイミングでしかデータが揃ったことをチェックできません。Extensionsを使うと、2つの入力ポートのデータが揃うまで待ち、そのデータの和を出力ポートに書き込む処理を以下のように簡単に書くことができます。.Subscribe(outport);Zip以外にも、MergeやCombineLeastなどの合成系のメソッドが使えますし、WhereでデータをフィルタリングしたりSelectで加工することもできます。RTコンポーネントの活性化OpenRTM-aistでは、RTコンポーネントを活性化したい場合、通常は以下のようなコードを書くと思います。ec->activate_component(comp);しかし、activate_component()メソッドはイベントを送るだけなので、実際にACTIVE状態に変わったかどうか分かりません。そのため、以下のようにget_component_state()を何度か呼んで状態が変化するまで待つ必要があります。}しかしこの方法では、get_component_stateで何度も通信が行われますし、スリープの時間によっては待ち時間が大きくなってしまいます。OpenRTM-aistでは、ver.1.1からコンポーネントオブザーバという機能が実験的に追加されています。この機能を利用すると、RTコンポーネントの状態変化などの通知を受け取ることができます。OpenRTM-aistでコンポーネントオブザーバの機能を利用するには、rtc.confに以下の記述を追加して、RTコンポーネントを起動するだけです。Extensionsを使って処理してみます。(少々分かりにくいですが、OpenRTM-aistのC++版のRTコンポーネントを、C#のプログラムで監視しているということです)コンポーネントオブザーバからの状態変化通知をStateChangedAsObservableで受け取れるようにすると、RTコンポーネントを活性化して、状態がACTIVEになるまで待つという処理を以下のように書くことができます。obs.First();ここで、もしRTコンポーネントの活性化に失敗して状態が変化しなかった場合、obs.First()のところで待ち続けることになってしまいます。Extensionsでは、以下のようにTimeoutメソッドを使ってタイムアウトを設定することができます。あとは、例外をcatchするなり、Catchメソッドで処理するなり、SubscribeのOnErrorで処理するなりしましょう。obs.Timeout(TimeSpan.FromSeconds(5)).First();スレッド間同期RTコンポーネントでは、一定の周期で動作する処理や、他のコンポーネントから非同期で呼びだされるデータポートやサービスポート、センサの監視やアクチュエータの制御などがそれぞれ別のスレッドで動作しています。ExtensionsではSchedulerという仕組みがあるので、スレッド間の同期がだいぶやりやすくなります。以下のようにRTコンポーネントにExecutionContextSchedulerを持たせ、周期的に動作するOnExecuteと、非同期で呼び出されるデータポートの処理を同じSchedulerで動作させるようにします。ExecutionContextSchedulerは、デフォルトでEventLoopSchedulerを使うようにしてあるので、OnExecuteとデータポートの処理が必ず1つのスレッド上で動作されるようになります。ロックを書く必要はありません。}}RTコンポーネントのテスト先日、Rx-Testingの使い方を紹介しましたが、Rx-Testingを使えばRTコンポーネントのテストも簡単に書けるようになります。入力ポートに入れるデータは、入力するタイミングを任意に指定することができますし、OnExecuteの周期処理も実際の時間を待たなくても実行することができます。入力ポートから入ってきたデータを2倍して出力するコンポーネントのテストは以下のようになります。OutPortから出力されたデータを蓄えるためのもの。InPortに入力するデータ。1秒ごとにデータを送る。}}ReactiveRTMここまでに紹介した機能を持つRTミドルウェアとして、ReactiveRTMという名前でプロトタイプ実装を作ってみました。GitHub一応、OpenRTM-aist-1.1やRTSystemEditorと連携させて動作させることはできますが、実装していない機能も多いので実用には程遠いです。(NuGetに登録もしたいのですが、あまり完成度の高くないものを登録するのもよくないかなと)ちなみに、GUIアプリケーションとの連携サンプルや、IDLを書かずに独自のデータ型でデータポートを使ったり、C#のdynamicを使ってdRubyのようにサービスポートを呼び出すようなサンプルもあるので、興味があれば見てみてください。いえば、Erlang版のOpenRTMなどもありますが、コンセプトは近いのかもしれませんね。C#Rxを使ったプログラミングは楽しいのですが、時間に依存してたり非同期だったりするのでテストを書こうと思うとなかなか難しい。Extensionsでは、テスト用のクラスをいくつか用意してくれています。テスト用のクラスを利用するためには、NuGetからRx-Testingを入れるか、インストーラな人はMicrosoft.Reactive.Testingを参照設定に追加します。ITestableObserverとITestableObservableRxでプログラミングしている時のテスト対象は、何らかのIObserverやIObservableになると思います。IObservableのテストをしたいときは、いつどんな通知が行われたかを確認したいですし、IObserverをテストしたいときは、任意のタイミングで通知できるIObservableが欲しい。そのために、Rx-TestingではITestableObserverとITestableObservableという2種類のインタフェースと、その実装クラスであるMockObserver、HotObservable、ColdObservableというクラスを用意しています。これらはinternalクラスなので、インスタンスはTestSchedulerから作ることになります。MockObserverのインスタンスは以下のように作ります。testScheduler.CreateObserver<int>();IObservableのテストIObservableをテストしたい場合は、テスト対象のIObservableに対して、MockObserverをSubscribeします。・・・testTarget.Subscribe(mockObserver);この状態でtestTargetから通知が行われると、MockObserverのMessagesに通知結果が蓄積されます。蓄積されるのは、Recorded>という型で、通知の時間とその値を保持しています。どんな通知が行われたかは、Notificationのサブクラスの型(Notificatioを継承したOnNextNotification/OnErrorNotification/OnCompletedNotification)で判断することができます。通知結果を確認するためには、ReactiveAssertのAreElementsEqualを使います。期待する結果を用意するために、Recorded>のインスタンスを作るのはちょっと面倒です。そこで、ReactiveTestクラスに、Recorded>を簡単に作るためにOnNext/OnError/OnCompletedというFactoryメソッドが用意されています。テストクラスでReactiveTestを継承すると使いやすいですね。いうわけで、IObservableのテストは以下のように書くことができます。(テストフレームワークにはMSTestを使っています。テスト対象を監視する。}}時間の操作TestSchedulerでは、AdvanceByやAdvanceToメソッドを使って、自由に時間を進めることができます。現実的ではないですが、例えば以下のように1時間に一回通知を行うようなクラスがあったとしましょう。}}通知されるまでスリープしていては、テストに時間がかかりすぎてしまいます。そこで、TestSchedulerを使って時間を操作してしまいましょう。すると、上記のようなクラスのテストも一瞬で実行することができます。}}IObserverのテスト一方、IObserverのテストをしたいときは、任意のタイミングで通知してくれるHotObservable、ColdObservableを使います。それぞれTestSchedulerのCreateHotObservableとCreateColdObservableメソッドで、通知したい時間と値を指定して作ることができます。IObservableが参考になります。これらのクラスは、TestSchedulerの時間を進めると、指定したタイミングで通知してくれます。当然ですが時間は進めることしかできず、過去に戻ろうとすると例外が発生します。IObserverのテストは、こんな感じになります。通知したい時間と値をセットしておく。Extensions(Rx)を使ってプログラミングを書くのがすごく楽しい。今のところRxの使い道としては、GUIアプリケーションや、Webサービスの非同期呼び出しなどが多いでしょうか。Programmingの応用先としては、以下のようなものがあるそうです。ロボット制御とかロボット制御!そう。Programming向き!?じゃあ何か制御プログラムを書いてみるか!とは言っても、ハードウェアを用意したりとかは大変・・・。言語ゲームを参考にして、左右の車輪の速度からロボットの位置を算出するプログラムをRxで書いてみました。左車輪の速度と右車輪の速度を結合、ついでに前回との時間差も付与。x,y,thetaを結合して結果を表示する。}}Rxを使わない場合は、センサ入力と計算処理を別スレッドに分けて、データの受け渡しを排他制御して、計算処理はfor文になったりするので、それと比べるとだんぜんシンプルですね。なにより、積分計算がTimeIntervalとScanでサクっと書けるのに感動しました!なおこのプログラムでは、左右の車輪の速度をSubject.OnNextで与えていますが、実際のロボットの場合はここがセンサ入力になります。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。