RtStorageリリース

RtStorageというアプリケーションを公開しました。

zoetrope/RtStorage · GitHub

先日のエントリーでは、利用したライブラリのことを書きましたが、今回はアプリケーションの機能について紹介したいと思います。

RtStorageとは

RtStorageはRTミドルウェア(OpenRTM-aist official website | OpenRTM-aist)のためのデータ記録・再生用ツールです。以下のような特徴を持っています。

  • RTコンポーネントのOutPortから出力されたデータをファイルに記録することができます。
  • 保存したデータを、RTコンポーネントのInPortに対して再生することができます。
  • データの再生は保存したときと同じタイミングで行われます。また、任意の位置から再生を開始することができます。
  • 保存されたデータは、いくつかの検索条件で簡単に見つけ出すことができます。
  • IDLファイルを書かなくても、ユーザが独自定義したデータを扱うことができます。
  • 保存されたデータの解析を行うことができます。

RtStorageと同じような機能を持つアプリケーションとしては、rtshellのrtlogと、そのGUIフロントエンドであるrtlogplayerがあります。

想定ユースケース

RtStorageのユースケースとしては、センサコンポーネントと、センサの計測データを使って計算をするコンポーネントを開発するときを考えてみてください。

計算コンポーネントの動作確認をするために、センサを毎回動かすのはかなりめんどくさいんですよね。

ですので、センサコンポーネントのデータどりを先にしておいて、そのデータを使ってじっくりアルゴリズムの検証をするほうがだんぜん楽。

RTコンポーネントの開発を行っている人は、おそらくダミーのコンポーネントを自作して、同じようなことをしているのではないでしょうか?

RtStorageは、データポートのデータ記録とデータ再生を簡単に行うことができます。

コンセプト

RtStorageのコンセプトとしては「簡単に使えること」と「応答性の向上」を目指しています。(実現できているかどうかはともかく・・・)

インストーラを用意していますので.NET Framwork 4 Client ProfileがインストールされているPCであれば、すぐに使い始めることができます。(一応ZIPアーカイブも用意しています。)別途RTミドルウェアをインストールする必要はありません。

また、RTミドルウェアを使うときにはIDLを扱うのが結構難しいのですが、RtStorageではIDLファイルを使う必要はありません。送られてきたデータをバイナリ配列(CDR形式)のままファイルに保存してるので、データ型にまったく依存せず、ユーザー定義型のデータを扱うことができます。

応答性の向上としては、アプリケーション内でCORBA通信を行う箇所や、データベースアクセスをする箇所を非同期で動作させるようにしています。これにより「アプリケーションが固まった」という感覚を受けることなく使えるのではないかと思います。

興味のある方は、こちらからダウンロードできるので使ってみてください。

Downloads · zoetrope/RtStorage · GitHub

今後

現状でも基本的な機能は実現できているので、今後は細かな機能追加をしていこうかと思っています。
例えば、蓄積したデータの閲覧や編集機能などは欲しいと思っています。
また、自動テストに使うことを考えるとCUIで利用する機能も欲しいですかね。


RTミドルウェアコンテストにだしたら、何か賞が取れたりしたかなぁ。

Livet & ReactiveProperty

RtStorageというアプリケーションをつくりました。

このアプリが何するものなのかはさておき(笑)、開発するにあたって利用したLivetとReactivePropertyの感想を書いてみたいと思います。

Livet

まずはLivet。これはWPFMVVMパターンを適用したアプリケーションを開発するためのインフラストラクチャです。

これまでWPFのアプリケーションはいくつか作ったことがあったのですが、インフラなしで開発すると、自分でインフラ的なコードを書かなくちゃいけなくて面倒だったり、ViewModelがViewに依存することでテストコードが書きにくくなったりしてしまうんですよね。

今回はLivetを使って開発してみたのですが、インフラなしの場合と比べるとすごく楽に開発できました。LivetにはMVVMパターンで欲しい機能がきちんとそろっていると思います。
そしてRtStorageのソースコードを見てもらえば分かりますが、コードビハインドは一切書いておらず、ViewModelがViewに依存しているところもないと思います。Livetを使うとこういうコードがとても書きやすくなります。

Livetにはほとんど不満はないのですが、RoutedEventTriggerや、ディレクトリを選択するActionなんかはインフラに含まれてるといいかなと思いました。まぁ、FolderBrowserDialogはWinforms依存なので含めにくいと思いますし、自分で作ってもそれほど大変ではないのですが。

あとは使いたい機能があっても、使い方が分からなくて調査に時間がかかったりしたので、ドキュメントかサンプルコードが充実してくるとうれしいですね。

ReactiveProperty

ReactiveProperty - MVVM Extensions for Rx - Home

つぎはReactiveProperty。これはReactive Extensionsを使って、イベントや非同期処理とWPFのUIをシームレスに連携させるためのライブラリ(という認識でいいのかな?)です。

C# 5.0ではasync/await構文が取り入れられるとか、WinRTでは時間のかかる処理はすべて非同期になっているとか、今後は非同期処理がますます重要になってきそうです。ということもあり、RtStorageではCORBA通信(!)を使っているところやデータベースアクセスの箇所をなるべく非同期にしています。

ReactivePropertyは少しとっつきにくいのですが、CommandとPropertyを非同期でつなげるところがすっきり書けます。そして何よりコードを書いていて楽しい!

LivetのViewModelCommandとReactiveCommandをどのように使い分けるのかはまだ分からないのですが、CommandのCanExecuteの条件が複雑な場合は、ReactiveCommandを使ったほうが書きやすいなと感じています。宣言的に書けるってすばらしい。

その他

RtStorageは非同期に動作するように作っているのですが、コレクションの排他制御周りの挙動がどうも怪しい。ReactivePropertyはver.0.2.0で、Livetは次のver.0.99で、スレッドセーフなコレクションに対応するとのことなので、試してみたいところです。


開発者のお二人にコメントいただけるとか、うれしはずかしです><


わんくま同盟 東京勉強会 #42に行ってきた

久々のブログ更新です。

最近は仕事でSilverlightを使っていることもあり、情報収集も兼ねて、1年ぶりにわんくま同盟の勉強会に参加してきました。

WPFピンポイント講座〜リソース編〜

アプリケーションリソースとイミディエイトリソース、StaticResourceとDynamicResouceの違いの解説が、とても分かりやすかったです。

StaticResourceとDynamicResouceって、今まであまり違いを理解せずに使ってたなぁ。

Silverlightで見てわかるフーリエ変換

入力した数式をどうやって解析しているのか非常に気になったのですが、下記のパーサを利用しているようです。(Silverlight で関数のグラフ表示 | ++C++; // 未確認飛行 C ブログ)

Per Userインストーラを作ってみよう!実践編

前々からWiXは使ってみようと思っているんですが、なかなか手が出ません・・・。

そういえばPer Userインストーラで、GACへアセンブリを登録することってできるんでしょうか?

WPF 4.0 新機能レビュー

Visual Studio 2010のWPFサポート強化はうれしいですね。普段XAMLを手書きしているのでインテリセンス強化は歓迎ですし、プロパティの編集機能の強化でBlend不要かも?

描画内容をビデオメモリにキャッシュすることで、パフォーマンスを大幅向上できるそうです。拡大すると荒くなるというデメリットはあるものの、プロパティの設定だけでパフォーマンス改善できるのは良いですね。

タッチパネルのイベントは使いやすそう。ちょっとタッチパネル買ってみようかな。

標準の.NET Frameworkの指定が、Client Profileになるそうです。配布のしやすさを考えるとうれしいですね。

コーディングしながらあなたのお題に答えます

SilverlightRSSリーダの実演

Expression Blendしばりで作られていたんですが、Blendだけでもちゃんと開発できるものなんですね。

僕はアニメーションやグラデーションだけBlendを使って、他はXAMLを手書きしてるんですが、やっぱりBlendの使い方覚えたほうがよさそうですねー。

Silverlight Toolkitの位置づけ

Silverlight Toolkitは、新しい機能のための実験場だとのこと。

有効性が認められればSDK → Runtimeの順に昇格するそうです。

業務で利用する場合はこの辺を考慮しておかないとですね。ま、ソースコードが公開されているので自己責任で使えと。

SiverlightのWCFSOAP Faultが利用できない問題

これ、僕も3日くらい前にはまりました。

サービス側でFaultExceptionをthrowしても、Silverlight側ではCommunicationExceptionになってしまうんですよね。

解決するためには、サービス側でHTTPのステータスコードで500ではなく200を返すように、ビヘイビアを作成する必要があります。

ただしこれ、type="・・・"を複数行に書くとダメみたいです。


しかし、このようにHTTPのステータスコードを変えてしまうと、他のSOAPとの互換性がなくなってしまうそうです。

そうではない対策を、マイクロソフトの荒井さんがご存じとのことだったのですが、聞きそびれてしまいました(´・ω・`)

INETA Day 2009に行ってきた

INETA Day 2009に行ってきました。

最近は仕事でもWPFを使ってたりするので、WPFSilverlight関連を中心に聴いてきました。

Dynamic Data お手軽プログラミング

Dynamic Dataというコーディングレスでデータベース管理ツールを作れる機能の紹介。

Dynamic Dataを普通に使うだけだといまいち使いにくいので、バリデータの機能をDataGridに適用したり、メタデータを適用して表示名やエラーメッセ時を変更したりする方法を紹介していました。

しかし、最初お客さんが少なくてビックリしました・・・

Prism ではじめる Silverlight LOB アプリケーション開発

Prismって最近よく聞くから、調べてみようと思っていたところだったので、渡りに船でした。

要はWPFSilverlightのアプリケーションを構築するモジュールを、複数人/複数拠点で開発するためのフレームワークなんですね。

  • シェルというメインページを領域に分割し、各領域にモジュールを割り当てていく。
  • モジュールはカタログに登録する。
  • ブートストラッパーでシェルを起動する。
  • モジュール間の通信にはイベントが使えないので、EventAgregatorやRegisterContext、共有サービスを利用する。
  • コマンドの代わりに添付プロパティで使用するDelegateCommandやCompositeCommandが用意されている。

XAMLではなくC#のコードをたくさん書かないといけないので、少々不便なところもありそうですが(特に通信周り)、複数人で開発できるとか試験がしやすくなるのは良さそうですね。

あと、内容には関係ないんだけど、デスクトップに並んでたアイコンがオシャレでよかったなぁ。

Silverlight カスタムコントロール開発

Silverlightでカスタムコントロールを開発する方法の紹介。

僕はたいていUserControlを派生させていたのですが、UserControlから派生する方法はカスタムデザインに向かないらしいです。

なので、大規模なカスタマイズをする場合はControlから派生させる、小規模なカスタマイズの場合は既存のコントロールから派生させるとのこと。

ただし、VSのデザイナの支援は得られないので、手作業でXAMLとコードを結合する必要がある。結合するにはThemes/generics.xamlにデザインを書き、DefaultStyleKeyで指定するか、XamlReaderでXAMLを動的に読み込むか。この辺は将来的に標準でサポートしてもらえるといいなぁ。

あと、テンプレート内に定義されているコントロールを取得するには、OnApplyTemplateをオーバーライドして、GetTemplateChildで取得するとのこと。これは知らなかった。ただし取得結果がnullかもしれないので、ちゃんとチェックする必要があるそうです。

WPF の新しいコントロール

WPFは、だいぶコントロールの種類が充実してきたし、M-V-VMというデザインパターンも出てきたしで、業務で使えるようになってきたよねというお話。

WPF ToolkitWPF Themesの紹介が中心でした。

チャートは今はプレビュー段階で6種類しかないけど、VS2010正式リリースのときには35種類が揃うんじゃないかとのこと。これは楽しみですね。

C# 4.0 と Visual Studio 2010 / .NET Framework 4.0

これはいいなと思った機能を羅列してみます。

  • スタートページ:よく使うプロジェクトをピン止め、カスタマイズできる
  • コードエディタ:拡大縮小、フローティング、呼び出し階層の表示、クラスやプロパティの自動生成
  • アーキテクチャエクスプローラ:クラスやメソッドの依存関係をグラフ化
  • UMLソースコードからシーケンス図の自動生成
  • デバッガ:変数の状態を付箋に貼れる、並行処理のデバッグ機能、デバッグ履歴
  • その他:UIテストや手動テストをサポートする機能

リリースが楽しみ!

ListViewでドラッグアンドドロップ

M-V-VMパターンで添付ビヘイビアを使って、項目の入れ替えができるListViewを作ってみました。ちょっと長いですが。
なお、ViewModelBaseクラスは、WPF Model-View-ViewModel Toolkit 0.1で生成されたものを利用しています。

まずはMainView.xamlです。DataContextにはMainViewModelを関連付けています。

<Window x:Class="DraggableListView.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Behaviors="clr-namespace:DraggableListView.Behaviors"
    Title="Main Window" Height="400" Width="800">
    
    <ListView x:Name="DraggableListView" ItemsSource="{Binding Children}"
              Behaviors:DragAndDropBehavior.IsEnabled="true">
        <ListView.View>
            <GridView>
                <GridView.Columns>
                    <GridViewColumn Header="No" DisplayMemberBinding="{Binding No}" />
                    <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" />
                </GridView.Columns>
            </GridView>
        </ListView.View>
    </ListView>
</Window>

つづいてMainViewModel.csです。ItemViewModelのリストを持っているだけですね。

using System.Collections.ObjectModel;

namespace DraggableListView.ViewModels
{
    public class MainViewModel : ViewModelBase
    {
        public ObservableCollection<ItemViewModel> Children { get; set; }

        public MainViewModel()
        {
            Children = new ObservableCollection<ItemViewModel>()
                           {
                               new ItemViewModel(1, "item1"),
                               new ItemViewModel(2, "item2"),
                               new ItemViewModel(3, "item3"),
                               new ItemViewModel(4, "item4"),
                               new ItemViewModel(5, "item5")
                           };
        }
    }
}

ItemViewModel.csは、NoとNameをプロパティとして持っています。

namespace DraggableListView.ViewModels
{
    public class ItemViewModel : ViewModelBase
    {
        public ItemViewModel(int no, string name)
        {
            No = no;
            Name = name;
        }

        private string _name;
        public string Name
        {
            get{ return _name;}
            set
            {
                if(value != _name)
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
        }

        private int _no;
        public int No
        {
            get { return _no; }
            set
            {
                if (value != _no)
                {
                    _no = value;
                    OnPropertyChanged("No");
                }
            }
        }
    }
}

最後にドラッグアンドドロップの添付ビヘイビア。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using DraggableListView.ViewModels;

namespace DraggableListView.Behaviors
{
    public class DragAndDropBehavior
    {
        public static bool GetIsEnabled(ListView view)
        {
            return (bool)view.GetValue(IsEnabledProperty);
        }

        public static void SetIsEnabled(ListView view, bool value)
        {
            view.SetValue(IsEnabledProperty, value);
        }

        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached(
                "IsEnabled", typeof(bool), typeof(DragAndDropBehavior),
                new UIPropertyMetadata(false, OnIsEnabledChanged));

        static void OnIsEnabledChanged(DependencyObject depObj,
                                       DependencyPropertyChangedEventArgs e)
        {
            var view = depObj as ListView;
            if (view == null)
            {
                return;
            }

            if (e.NewValue is bool == false)
            {
                return;
            }

            bool isEnabled = (bool)e.NewValue;

            if (isEnabled)
            {
                view.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
                view.Drop += OnDrop;
            }
            else
            {
                view.PreviewMouseLeftButtonDown -= OnMouseLeftButtonDown;
                view.Drop -= OnDrop;
            }
        }

        private static void OnDrop(object sender, DragEventArgs e)
        {
            var view = sender as ListView;
            var newIndex = GetItemIndex(view, e.GetPosition(view));
            var source = (ItemViewModel)e.Data.GetData(typeof(ItemViewModel));

            if (newIndex != -1 && source != null)
            {
                MainViewModel vm = (MainViewModel)view.DataContext;
                vm.Children.Remove(source);
                vm.Children.Insert(newIndex, source);
            }
            view.AllowDrop = false; 
        }

        private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var view = sender as ListView;
            var index = GetItemIndex(view, e.GetPosition(view));
            if (index != -1)
            {
                view.SelectedIndex = index;
            }

            if (view.SelectedItem is ItemViewModel)
            {
                view.AllowDrop = true;
                var vm = (ItemViewModel)view.SelectedItem;
                DragDrop.DoDragDrop(view, vm, DragDropEffects.Copy);
            }
        }

        private static int GetItemIndex(ListView view, Point pos)
        {
            var result = VisualTreeHelper.HitTest(view, pos);
            var item = result.VisualHit;
            while (item != null)
            {
                if (item is ListViewItem)
                {
                    break;
                }
                item = VisualTreeHelper.GetParent(item);
            }
            if (item != null)
            {
                return view.Items.IndexOf(((ListViewItem)item).Content);
            }
            return -1;
        }
    }
}

添付ビヘイビアでドラッグアンドドロップ

最近は、M-V-VMパターンの勉強がてらWPFのアプリケーションを作っています。

M-V-VMパターンを使うとコードがスッキリかけるし、何よりViewModelがViewに依存していないおかげで、テストコードが書きやすいのがすばらしいですね。


いろいろな処理がきれいに書けるので、ドラッグアンドドロップでユーザーコントロールを移動するような処理もスッキリ書けないかなと考えていたのですが、添付ビヘイビアが使えそうだと思い試してみました。

まずは、以下のようなドラッグアンドドロップでThumbを移動させるビヘイビアを記述します。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace DragAndDropSample.Behaviors
{
    public class DragAndDropBehavior
    {
        public static bool GetIsEnabled(Thumb thumb)
        {
            return (bool)thumb.GetValue(IsEnabledProperty);
        }

        public static void SetIsEnabled(Thumb thumb, bool value)
        {
            thumb.SetValue(IsEnabledProperty, value);
        }

        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached(
                "IsEnabled", typeof (bool), typeof (DragAndDropBehavior),
                new UIPropertyMetadata(false, OnIsEnabledChanged));

        static void OnIsEnabledChanged(DependencyObject depObj, 
                                       DependencyPropertyChangedEventArgs e)
        {
            Thumb thumb = depObj as Thumb;
            if (thumb == null)
            {
                return;
            }

            if (e.NewValue is bool == false)
            {
                return;
            }

            bool isEnabled = (bool)e.NewValue;

            if (isEnabled)
            {
                thumb.DragStarted += ThumbDragStarted;
                thumb.DragDelta += ThumbDragDelta;
                thumb.DragCompleted += ThumbDragCompleted;
            }
            else
            {
                thumb.DragStarted -= ThumbDragStarted;
                thumb.DragDelta -= ThumbDragDelta;
                thumb.DragCompleted -= ThumbDragCompleted;
            }
        }
        static void ThumbDragStarted(object sender, DragStartedEventArgs e)
        {
            Thumb thumb = sender as Thumb;
            if (thumb == null)
            {
                return;
            }
            thumb.Cursor = Cursors.Hand;
        }

        static void ThumbDragCompleted(object sender, DragCompletedEventArgs e)
        {
            Thumb thumb = sender as Thumb;
            if (thumb == null)
            {
                return;
            }
            thumb.Cursor = Cursors.Arrow;
        }

        static void ThumbDragDelta(object sender, DragDeltaEventArgs e)
        {
            Thumb thumb = sender as Thumb;
            if (thumb == null)
            {
                return;
            }

            double left = (double)thumb.GetValue(Canvas.LeftProperty);
            double top = (double)thumb.GetValue(Canvas.TopProperty);

            thumb.SetValue(Canvas.LeftProperty, left + e.HorizontalChange);
            thumb.SetValue(Canvas.TopProperty, top + e.VerticalChange);
        }
    }
}

あとはXAML側からビヘイビアを指定するだけ。

<Window x:Class="DragAndDropSample.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Behaviors="clr-namespace:DragAndDropSample.Behaviors"
    Title="Main Window" Height="480" Width="640">

    <Canvas>
        <Thumb Behaviors:DragAndDropBehavior.IsEnabled="true"
               Canvas.Left="100" Canvas.Top="100" Height="20" Width="20">
            <Thumb.Template>
                <ControlTemplate TargetType="Thumb">
                    <Rectangle Fill="Blue"/>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
    </Canvas>
</Window>

これでドラッグアンドドロップの処理を再利用できるわけですね。

Intellipadの可能性

Microsoft Oslo May 2009 CTPが公開されていたので、さっそくダウンロードしてみました。

まず、以前作成した文法ファイルは問題なく利用できました。ただし、mg.exeがm.exeに統合され、生成されるファイルが *.mgxファイルから*.mxファイルに変わっています。


さて、今回のリリースではUMLのドメインモデルが追加されていたりしますが、Intellipadの機能が追加されているのも注目です。

文法ファイルを読み込むと、独自のモードが追加されるようになっています。

例えば、idl.mgを読み込むと、idl.mg Modeと、idl.mg Output Modeが追加されます。

idl.mg Modeでは、idl.mgの内容に従って文法チェックや、コメント行の色分けなどを行ってくれます。

idl.mg Output Modeでは、ファイルをパースした結果(M言語)を表示してくれます。

しかも、文法ファイルを書き換えると、即座に他の画面にも反映されるのがうれしい。


あと、以前からあった機能のようですが、ミニバッファからコマンドを打ち込んで検索、置換、ズーム、テストを行うことができたり、Custom Commandを追加することもできるようです。


今後はキーワードハイライトや補完、ビルドなんかができるようになるとうれしいなぁ。オレ様言語用エディタがサクっと用意できるってことですからね。