CakePHPのXMLレスポンスをストアアプリで一覧表示する

CakePHPのXMLレスポンスをWPFで一覧表示する | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5370

の Windows ストアアプリ版も作ってみます。

本当はListViewを使いたいところだけど、手始めに GridView を使うところから。もっと、WPFのListViewのようなダサいデザインを手軽に作れればいいのですが。どうもうまくいかない。

■データモデルを作る

WPFで作ったソースをそのまま流用しますが、一か所だけ違います。リフレクションでプロパティを取ってくるところで、拡張メソッドの GetTypeInfo を使います。なんで、こんな風にしたのか不明なのですが。

using System.Reflection;

public class IModel
{
    public void FormXml(XElement el)
    {
        // WinRTの場合
        var pis = this.GetType().GetTypeInfo().DeclaredProperties;
        foreach (var pi in pis)
        {
            var vv = el.Descendants(pi.Name).FirstOrDefault();
            if (vv != null)
            {
                var v = vv.Value;
                if (pi.PropertyType == typeof(int))
                    pi.SetValue(this, int.Parse(v));
                else if (pi.PropertyType == typeof(double))
                    pi.SetValue(this, double.Parse(v));
                else if (pi.PropertyType == typeof(string))
                    pi.SetValue(this, v);
            }
        }
    }
}

POJO なクラスはそのままで。

■グリッドビューを作る

VS2013 でグリッドのテンプレートを作ると、きれいな GridView のテンプレートを吐き出してくれるけどカスタムしにくいので、最小限のコードだけにします。

<GridView
    x:Name="itemGridView"
    ItemsSource="{Binding Items}"
    ItemTemplate="{StaticResource StoreTemplate}"
    Margin="13,13,10,10" Grid.Row="1"
    SelectionChanged="itemGridView_SelectionChanged">
</GridView>

データのバインドは「ItemsSource=”{Binding Items}”」なところで。このあたりは WPF と同じ。GirdView に表示するアイテムのテンプレートは、「ItemTemplate=”{StaticResource StoreTemplate}”」で指定してます。既存のデータテンプレートを使うと、バインド名が Title や Subtitle に固定になっているので、ここはカスタムで。StaticResource せずとも、GridView.ItemTemplate を使って以下のようにも書けます。

<GridView
    x:Name="itemGridView"
    ItemsSource="{Binding Items}"
    Margin="13,13,10,10" Grid.Row="1"
    SelectionChanged="itemGridView_SelectionChanged">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Width="200" Height="200">
                <TextBlock Text="{Binding ID}"  FontSize="20" Margin="10,10,10,150"   />
                <TextBlock Text="{Binding Name}" FontSize="20" Margin="10,50,10,97" />
            </Grid>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

1回だけしか使わない場合はこれで十分ですよね。そもそも、StaticResource って、その xaml 内に限るからあまりそこに書く理由はあまり意味ないかも、と思ってたり。

ページリソースに記述する場合はこんな感じで「StoreTemplate」のように名前を付けておきます。デザイン用のデータを読み込んでちまちま修正するパターンもできるので、これはこれで便利だし。他のプロジェクトからコピペする場合も便利。

<Page.Resources>
    <DataTemplate x:Key="StoreTemplate">
        <Grid Width="200" Height="200">
            <TextBlock Text="{Binding ID}"  FontSize="20" Margin="10,10,10,150"   />
            <TextBlock Text="{Binding Name}" FontSize="20" Margin="10,50,10,97" />
        </Grid>
    </DataTemplate>
</Page.Resources>

デザイン用のデータは、VS2013 から JSON 型式で読み込めるようになったのですが、何故か日本語が通りません。UTF-8 にしてもダメなので、なんか XAML デザイナのほうで変なことをやっている気がします。英語(ASCII文字)だけにすると通ります。

■プロパティビューっぽいもの

VS2013が作ってくれるデザインテンプレートでは、アイテムをクリックすると詳細画面に遷移しますが、ここは WPF と同じように同一の画面に表示させます。いちいち画面遷移するのも面倒だし(特に戻るのが面倒)、でかい画面を切り替えるよりも、画面を自前で分割するほうがいいよってことで。

<TextBox 
    Text="{Binding Item.ID}"
    Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="10,13,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="280"/>
<TextBox 
    Text="{Binding Item.Name}"
    Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="10,50,0,0" Grid.Row="1" TextWrapping="Wrap"  VerticalAlignment="Top" Width="280"/>
<TextBox 
    Text="{Binding Item.Sellingcompany.Name}"
    Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="10,87,0,0" Grid.Row="1" TextWrapping="Wrap"  VerticalAlignment="Top" Width="280"/>
<TextBox 
    Text="{Binding Item.Areagroup.Name}"
    Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="10,124,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="280"/>

バインドは、Item の下にプロパティをつける形にしておきます。この構造は、先のデータモデルと合わせておきます。当然名前も一緒にしないとダメ。

■バインド用のモデルを作る

ここは WPF と同じ。

public class BindModel : BindableBase
{
    private ObservableCollection<Model.Store> _Items;
    public ObservableCollection<Model.Store> Items
    {
        get { return _Items; }
        set { this.SetProperty(ref this._Items, value); }
    }

    private Model.Store _Item;
    public Model.Store Item
    {
        get { return _Item; }
        set { this.SetProperty(ref this._Item, value); }
    }
}

■DataContextに設定する

DataContext に設定する方法も同じ。ストアアプリでは、DefaultViewModel がある訳ですが、実質同じことをやるので、どちらかを使えば OK です。

[win8] DefaultViewModel と DataContext は同時に使えない | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3873

に書いた通り「バインディングするところがアレ」なので、DataContext で十分かなと思ったりします。コードレベルで WPF との共有も考えると、業務コード的にはどっちかに統一しておいたほうがよいかも。覚えることが少なくなるし。

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        // モデルをバインドする
        _model = new BindModel();
        this.DataContext = _model;
    }

    private BindModel _model;

    /// <summary>
    /// 一覧を取得
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        var lst = await new Api.ApiStore().Read();
        _model.Items = new ObservableCollection<Model.Store>(lst);
    }
    /// <summary>
    /// アイテムをクリックしたとき
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void itemGridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var item = e.AddedItems[0] as Model.Store;
        _model.Item = item;
    }
}

これで API 呼び出しをして、GridView のアイテムをちまちまとクリック(タブレットの場合はタップ)すると、詳細情報が右のプロパティ一覧に表示されます。

ちなみに、WPFでテキストボックスにバインドをすると初期値が TwoWay(双方向)になっていますが、ストアアプリの場合は OneWay(表示のみ)です。あとストアアプリの場合、それぞれのプロパティに INotifyPropertyChanged をつけないと、TwoWay にならないのが面倒かなと。このあたり VS2012 のグリッドのサンプルにはあったのだけど、VS2013 のグリッドのサンプルにはないんですよね。

GridView の場合は横スクロールなので、お次は ListView を使って縦スクロール(WPFのリスト表示と同じパターン)で作ってみます。

カテゴリー: CakePHP, WPF, WinRT | CakePHPのXMLレスポンスをストアアプリで一覧表示する はコメントを受け付けていません

CakePHPのXMLレスポンスをWPFで一覧表示する

余裕があるうちにCakePHPとWPFの相互運用をまとめていく(準備編) | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/5329

の続きで、今度はWPF側からのコーディングです。
いわゆる、以下な感じな、リストビューとプロパティを表示するマスター画面を想定します。

一覧ボタンを押すと、Web APIにつなげて一覧を取得。リストビューの各行をクリックすると右側のプロパティに詳細な値がぽちぽちと表示されるパターンです。このあたり、WPFなので、適当に Binding を使います。最終的には、Visual Studio のプロパティウィンドウのように自動化を目指したいところなのですが…まあ、業務的には各種のプロパティウィンドウをちまちま作っています。というのも、完全に自動化してしまうと使い勝手が悪いことがある(データベース構造に引きずられる)ことになるので、このあたりは適宜カスタムで。
とはいえ、cake bake で自動化できるぐらいには、受け側の WPF のマスター画面も自動化生成しておきたいところです。

■データモデルを作る

まずは、Model を作ります。お店(Store)クラスは、会社(Sellingcompany)と地域(Areagroup)が紐づいているので、Storeクラスの中にプロパティとして入れてしまいます。このあたりの構造は、CakePHPが返すXMLがフラットな構造(Store, Sellingcompany, Areagroupが並列で存在)に対して、モデルのほうはツリー構造になります。IModel.FormXml メソッドは、XElement から適当にモデルにプロパティを入れ込むための簡易メソッドです。業務のほうは ExDoc を使ってみましたが、試しに XElement.Descendants を使ってパース。

  • Web APIが返す要素名
  • 受け取るときのクラス名
  • 受け取るときのプロパティ名

を大文字小文字も合わせて完全に一致させておくと、この方法が取れます。逆に言えば、それぞれの名前が微妙に違ったリスト面倒なことになるわけですが…まあ、そのときは属性を使って回避するのもよし、ここの FromXmlで書き換えるもよし、というところです。

public class IModel
{
    public void FormXml(XElement el)
    {
        var pis = this.GetType().GetProperties();
        foreach (var pi in pis)
        {
            var vv = el.Descendants(pi.Name).FirstOrDefault();
            if (vv != null)
            {
                var v = vv.Value;
                if (pi.PropertyType == typeof(int))
                    pi.SetValue(this, int.Parse(v));
                else if (pi.PropertyType == typeof(double))
                    pi.SetValue(this, double.Parse(v));
                else if (pi.PropertyType == typeof(string))
                    pi.SetValue(this, v);
            }
        }
    }
}

public class Store : IModel
{
    public int ID { get; set; }
    public int SellingCompanyID { get; set; }
    public int AreaGroupID { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
    public string Person { get; set; }
    public string Tel { get; set; }
    public string EMail { get; set; }
    public string ApplicationProgress { get; set; }
    public int Enabled { get; set; }
    public DateTime modified { get; set; }
    public DateTime created { get; set; }
    // リンク先
    public Areagroup Areagroup { get; set; }
    public Sellingcompany Sellingcompany { get; set; }

    public Store(XElement el)
    {
        base.FormXml(el);
        this.Areagroup = new Areagroup(el.Parent.Descendants("Areagroup").FirstOrDefault());
        this.Sellingcompany = new Sellingcompany(el.Parent.Descendants("Sellingcompany").FirstOrDefault());
    }
}
public class Areagroup : IModel
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public int SortKey { get; set; }
    public int ColorR { get; set; }
    public int ColorG { get; set; }
    public int ColorB { get; set; }
    public int ForeColorBW { get; set; }
    public int ExcelColor { get; set; }
    public int Enabled { get; set; }
    public DateTime modified { get; set; }
    public DateTime created { get; set; }

    public Areagroup(XElement el) { base.FormXml(el); }
}

public class Sellingcompany : IModel
{
    public int ID { get; set; }
    public int ManufacturerID { get; set; }
    public int JurisdictionID { get; set; }
    public string Name { get; set; }
    public string Abbreviation { get; set; }
    public string Code { get; set; }
    public int Enabled { get; set; }
    public DateTime modified { get; set; }
    public DateTime created { get; set; }

    public Sellingcompany(XElement el) { base.FormXml(el); }
}

それぞれのプロパティはちまちまと書いてもいいのですが、Visual Studio 2013 だと「編集」メニューの「型式を選択して貼り付け」→「XMLをクラスとして貼り付ける」を選ぶと簡単に作れます…が、JSONのときとXMLの時と違う出力になるので、なんだかな~という感じなのと、MVVM の Model にするときはちまちまと手作業で変えないといけません。まあ、その時は適当なスクリプトを書けば済むわけですが。
Store クラスのコンストラクタがややこしいことになっているのは、Web APIが返すXML構造とModel.Storeクラスのツリー構造が異なるからです。ここを同じにしてもよいのですが、CakePHPの出力とWPFでバインドをするときの便利さとが異なるの、ここで調節しておくほうがベターです。

■リストビューを作る

左側のリストビューを作ります。DataContextを使ったデータバインドが前提になるので、ちまちまとバインド式を書いてきます。

最低限バインドしておく箇所は、

  • ListView に ItemsSource=”{Binding Items}” でバインド
  • それぞれの列に DisplayMemberBinding=”{Binding ID}” でバインド

ビューにバインドするモデルから Items プロパティで bind させます。そして、それぞれのアイテム(要素)のプロパティにバインドさせるわけですが、DisplayMemberBinding=”{Binding Sellingcompany.Name}” のように、内部もつクラスのプロパティを参照することができます。ツリー構造にしておくと、親オブジェクトをリスト化しておいて、それぞれのプロパティにアクセスができます。

<ListView 
    x:Name=&quot;lv&quot;
    ItemsSource=&quot;{Binding Items}&quot;
    SelectionChanged=&quot;lv_SelectionChanged&quot;
    HorizontalAlignment=&quot;Left&quot; Height=&quot;235&quot; Margin=&quot;10,10,0,0&quot; Grid.Row=&quot;1&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;315&quot;>
    <ListView.View>
        <GridView>
            <GridViewColumn Header=&quot;ID&quot; 
                            DisplayMemberBinding=&quot;{Binding ID}&quot; Width=&quot;50&quot;/>
            <GridViewColumn Header=&quot;名前&quot; 
                            DisplayMemberBinding=&quot;{Binding Name}&quot; Width=&quot;50&quot;/>
            <GridViewColumn Header=&quot;会社名&quot; 
                            DisplayMemberBinding=&quot;{Binding Sellingcompany.Name}&quot; Width=&quot;50&quot;/>
            <GridViewColumn Header=&quot;地域&quot; 
                            DisplayMemberBinding=&quot;{Binding Areagroup.Name}&quot; Width=&quot;50&quot;/>
        </GridView>
    </ListView.View>
</ListView>

プロパティが入れ子になってバインドができるのは便利なのですが、逆に言えばプロパティ名が変わったときには、XAML 部分も直さないといけないという弊害もあります。そのあたりは先の Web API とデータモデルの自動変換のところにも当てはめるわけで、それはそれで割り切りで。業務のほうは、SellingcompanyName という読み取り専用のプロパティを作ったりしてます。

■プロパティビューっぽいものを作る

リストビューをクリックしたときの内容を表示させます。
リストビューと似た形で Item という名前で DataContext へバインドしておきます。この「Item」の部分は、自分で好きなように設定できます。

<TextBox 
    Text=&quot;{Binding Item.ID}&quot;
    Grid.Column=&quot;1&quot; HorizontalAlignment=&quot;Left&quot; Height=&quot;23&quot; Margin=&quot;10,13,0,0&quot; Grid.Row=&quot;1&quot; TextWrapping=&quot;Wrap&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;162&quot;/>
<TextBox 
    Text=&quot;{Binding Item.Name}&quot;
    Grid.Column=&quot;1&quot; HorizontalAlignment=&quot;Left&quot; Height=&quot;23&quot; Margin=&quot;10,41,0,0&quot; Grid.Row=&quot;1&quot; TextWrapping=&quot;Wrap&quot;  VerticalAlignment=&quot;Top&quot; Width=&quot;162&quot;/>
<TextBox 
    Text=&quot;{Binding Item.Sellingcompany.Name}&quot;
    Grid.Column=&quot;1&quot; HorizontalAlignment=&quot;Left&quot; Height=&quot;23&quot; Margin=&quot;10,69,0,0&quot; Grid.Row=&quot;1&quot; TextWrapping=&quot;Wrap&quot;  VerticalAlignment=&quot;Top&quot; Width=&quot;162&quot;/>
<TextBox 
    Text=&quot;{Binding Item.Areagroup.Name}&quot;
    Grid.Column=&quot;1&quot; HorizontalAlignment=&quot;Left&quot; Height=&quot;23&quot; Margin=&quot;10,97,0,0&quot; Grid.Row=&quot;1&quot; TextWrapping=&quot;Wrap&quot; VerticalAlignment=&quot;Top&quot; Width=&quot;162&quot;/>

Text=”{Binding Item.ID}” のところがバインドですね。地域の名前を表示するときは Text=”{Binding Item.Areagroup.Name}” な感じでバインドさせます。
TextBox にバインドさせているのは、編集もできるようにするためです。ただし、不用意に編集されないように読み取り専用のときと編集可能なときのモードをつけるほうがベターです。これは、IsEnabled に対してバインドさせると一気に Textbox を読み取り専用にすることができます。

■バインド用のモデルを作る

WPFのビューにバインドさせるモデルクラスを作ります。INotifyPropertyChanged インターフェースを使うわけですが、Visual Studio 2012 に入っているストアアプリ用の BindableBase.cs をそのまま使っています。このあたりは適宜好み&用途にあわせて。

public class BindModel : BindableBase
{
    private ObservableCollection<Model.Store> _Items;
    public ObservableCollection<Model.Store> Items
    {
        get { return _Items; }
        set { this.SetProperty(ref this._Items, value); }
    }

    private Model.Store _Item;
    public Model.Store Item
    {
        get { return _Item; }
        set { this.SetProperty(ref this._Item, value); }
    }
}

リストのほうは、ObservableCollection を使うのを忘れずに。
本来ならば Model.Store クラスの各プロパティも INotifyPropertyChanged を通すべきなのですが、単なる閲覧の場合は不要なのでこのままで。
Items コレクションがリストビューにバインドするプロパティで、Item プロパティがプロパティウィンドウのほうにバインドするものです。

■DataContextに設定する

ビューの DataContext にモデルを結びつけます。XAMLにStaticResourceで追加してもよいのですが、好みコードで設定しています。

public MainWindow()
{
    InitializeComponent();
    // モデルをバインドする
    _model = new BindModel();
    this.DataContext = _model;
}
private BindModel _model;

/// <summary>
/// 一覧を取得
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private async void Button_Click(object sender, RoutedEventArgs e)
{
    var lst = await new Api.ApiStore().Read();
    _model.Items = new ObservableCollection<Model.Store>(lst);

    // this.lv.ItemsSource = lst;
}

/// <summary>
/// 項目を選択
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
private void lv_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var item = lv.SelectedItem as Model.Store;
    _model.Item = item;
}

リストビューの更新は、_model.Items 経由で、リストビューの行を選択したときは _model.Item 経由で表示させます。
CakePHP で作った Web API の呼び出しが new Api.ApiStore() となっているのは愛嬌です。本来ならば static メソッドにするか、別途 api 用のオブジェクトをキープしておくかという感じなんですが、まあ、new 使ってガシガシと開くところは Java っぽいですね。この new オブジェクトはいつ解放されるのだろうか?ってなことは C++ の時だけ考えます。

■Web APIを呼び出す

Web API の呼び出し部分も定型なので、何かと共通化しておきたいところですが、いまんところべた書きです。どうしても Store のようなクラス名が出てきてしまうので、あまり汎用的には作れません。T4 使って自動生成するぐらいかなと。エラー処理がおおざっぱなのは、所詮マスター画面で使うものだからです。基本はエラーがでないという思想ですね。

public async Task<List<Model.Store>> Read()
{
    var uri = new Uri(SERVER + &quot;/Stores.xml&quot;);
    var lst = new List<Model.Store>();
    try
    {
        HttpClient cl = new HttpClient();
        var xml = await cl.GetStringAsync(uri);
        var doc = XDocument.Parse(xml);
        var coll = doc.Descendants(&quot;item&quot;);
        foreach (var it in coll)
        {
            var el = it.Descendants(&quot;Store&quot;).FirstOrDefault();
            var m = new Model.Store(el);
            lst.Add(m);
        }
    }
    catch ( Exception ex )
    {
        // エラー発生
        Debug.WriteLine(&quot;error:{0}&quot;, ex.Message);
        return lst;
    }
    return lst;
}
public async Task<Model.Store> Read( int id )
{
    var uri = new Uri(SERVER +
        string.Format(&quot;/Stores/view/{0}.xml&quot;, id));
    try
    {
        HttpClient cl = new HttpClient();
        var xml = await cl.GetStringAsync(uri);
        var doc = XDocument.Parse(xml);
        var el = doc.Descendants(&quot;Store&quot;).FirstOrDefault();
        var m = new Model.Store(el);
        return m; 
    }
    catch (Exception ex)
    {
        // エラー発生
        Debug.WriteLine(&quot;error:{0}&quot;, ex.Message);
        return null;
    }
}

Web APIのエラー値を取るためには、もう少しコードを追加しないといけません。
ひとまず、こんな感じで WPF から CakePHP の Web API を呼び出すことができます。

カテゴリー: CakePHP, WPF | CakePHPのXMLレスポンスをWPFで一覧表示する はコメントを受け付けていません

CakePHPのWeb APIレスポンスをJSON型式で返す

業務では使わなかったのですが、JSON型式で返す方法も紹介しておきます。

1. app/Config/routes.php を書き換える

先頭の行に以下を追加します。

	Router::parseExtensions('json');

以下のように並べると、xml と json の両方が使えます。

	Router::parseExtensions('json','xml');

2. app/Controller/AppController.php を書き換える

RequestHandler コンポーネントを有効にすること、ページングの数を多めにとっておきます。

class AppController extends Controller {
	public $components = array('RequestHandler');
	public $paginate = array( 'limit' => 1000 );
}

3. XML 用の View を作成する

Controller で set した値を json/*.ctp ファイルに書いていきます。作るファイルは XML と同じで、index.ctp, view.ctp, add.ctp, edit.ctp になります。json に変換する場合は、json_encode 関数です。

index.ctp は、配列にします。

$items = array('items'=> array('item'=> $stores));
echo json_encode(array('response'=>$items),JSON_UNESCAPED_UNICODE);

view.ctp, add.ctp, edit.ctp は一つだけの要素を返します。

$item = array('item'=> $store);
echo json_encode(array('response'=>$item),JSON_UNESCAPED_UNICODE);

# この部分、なんとか汎用化できないかと思案中。
JSON_UNESCAPED_UNICODE は、マルチバイトを uXXXX 形式にしない、という設定です。こっちのほうがデコードする手間が省けるので。

■実行してみる

一覧の場合は、http://localhost:81/cakephp-2.4.5/Stores.json 、IDを指定して閲覧する場合は http://localhost:81/cakephp-2.4.5/Stores/view/20.json

■おまけ

似た感じでスクリプトも書いておきます。

<?php
$views = array( 
	array('adjustmentjobs', 'adjustmentjob'),
...
	array('stores', 'store'),
	array('workingsituations', 'workingsituation'),
);

foreach ( $views as $v ) 
{
	$v0 = $v[0];
	$v1 = $v[1];
	
	$json0 =<<< END
<?php 
$items = array('items'=> array('item'=> $$v0));
echo json_encode(array('response'=>$items));

END;

	mkdir(&quot;view/$v0/json&quot;);
	$path=&quot;view/$v0/json/index.ctp&quot;;
#	print &quot;$json0n&quot;;
	file_put_contents( $path, $json0 );

	$json1 =<<< END
<?php 
$item = array('item'=> $$v1);
echo json_encode(array('response'=>$item));

END;

#	print &quot;$json1n&quot;;
	file_put_contents( &quot;view/$v0/json/view.ctp&quot;, $json1 );
	file_put_contents( &quot;view/$v0/json/add.ctp&quot;, $json1 );
	file_put_contents( &quot;view/$v0/json/edit.ctp&quot;, $json1 );
}
カテゴリー: CakePHP, WPF | CakePHPのWeb APIレスポンスをJSON型式で返す はコメントを受け付けていません

CakePHPのWeb APIレスポンスをXML型式で返す

CakePHP の通常 View ができたので、XML を返す Web API用のビューを作ります。

[CakePHP] RESTを使ってViewをXML形式で返す | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/4871

と基本は同じです。View の各フォルダに xml フォルダを作成して対応させます。いちいち同じファイルを作らないといけないのが面倒ですが、適当なスクリプトを作って自動化させておくとよいでしょう。

1.app/Config/routes.php を書き換える

先頭の行に以下を追加します。

	Router::parseExtensions('xml');

2.app/Controller/AppController.php を書き換える

RequestHandler コンポーネントを有効にすること、ページングの数を多めにとっておきます。

class AppController extends Controller {
	public $components = array('RequestHandler');
	public $paginate = array( 'limit' => 1000 );
}

デフォルトでは 20 件ぐらいなのですが、XML型式の場合はそれでは不便です。なので1000件をlimitにしておきます。本当はコントローラごとに細かく制限するのがよいのですが、それはシステム特性というとで、ひとまずこうしておくと大丈夫です。この数をもっと大きくしてもいいのですが、レスポンスも悪くなるし、システム的にこのぐらいがしておく、という感じですね。

3.XML 用の View を作成する

Controller で set した値を xml/*.ctp ファイルに書いていきます。CURD すべてに対応するために、index.ctp, view.ctp, add.ctp, edit.ctp を作っておけば OK です。

index.ctp は、配列にします。

$items = array('items'=> array('item'=> $stores));
$xml = Xml::fromArray(array('response'=>$items));
echo $xml->asXML();

view.ctp, add.ctp, edit.ctp は一つだけの要素を返します。

$item = array('item'=> $store);
$xml = Xml::fromArray(array('response'=>$item));
echo $xml->asXML();

response 要素を作って、その中に items あるいは item を作っていますが、これは好みですね。どちらもルート要素はひとつになるので、response は必要ないのですが、受け取る側(この場合は WPF )で、同じ名前のほうがいいだろうと思ってこうしています。
ただし、これだと web api の OK/NG が返しにくいのが難点です。Web API の受け取り側のつくりとしては、

  • API自体の成功/失敗を早めに拾いたい
  • APIレスポンスの型式はできるだけ同じ形式であってほしい。
  • OK/NGのレスポンスはできるだけ同じ形式であってほしい。

というのがあります。XMLをパースするときに、指定の要素があったりなかったりすのは結構面倒です。また、正常時のXML構造と異常時にXML構造は同じであったほうがプログラムが簡単になります。そうい意味では、あらかじめ response の属性として OK/NG をつけたりバージョンをつけるのがよいのですが…今回は、最初の実装時に入れ忘れたのこのままです。
あと、xml/*.ctp 自体は、非常に単純にしておかないとデバッグが面倒なんですよね。なので、できるだけ簡単にするために、こんなスタイルにしてます。

■一覧(index)を実行すると

XML 型式で返す場合は、http://localhost:81/cakephp-2.4.5/Stores.xml のように、最後に「.xml」をつけます。こうすると、$belongsTo などで指定したアソシエーションと一緒にデータを取得できます。

レスポンス自体には一切改行がないので閲覧には不便なので、IEなどのブラウザを利用するとよいです。

■ひとつの要素(view)を実行する

ID を指定して表示したい場合は、http://localhost:81/cakephp-2.4.5/Stores/view/20.xml のように指定します。最後に「.xml」をつければいいので、他の edit や add も同じです。

似た感じに見えますが、一覧の場合は response/items/item の中にそれぞれの要素が入っていて、view の場合は response/item の中に要素が入っています。
この入れ子が面倒な場合は、

$items = array('items'=> array('item'=> $store));
$xml = Xml::fromArray(array('response'=>$items));
echo $xml->asXML();

のように items 要素を入れてしまってもいいと思います。こうすることで、ひとつでも複数の要素でも、response/items/item の階層でデータが取れます。こっちのほうがいいかもしれませんね。

■add/edit はどうするのか?

add も edit もレスポンスとしては view と同じです。が、新規に作成したり編集したりするデータをクライアントから送らないといけません。ブラウザから編集するページは cake bake で自動生成しているので、WPF クライアントからサーバーへ POST するプログラムを作ります。これはちょっと次回へ。

■おまけ

適当ですが、こんな風にスクリプトを書いて、xml/*.ctp を作成します。
app フォルダに allbakexml.php のファイルで保存して実行してください。$views に複数形、単数形の順に書かないとダメなのは、Controller か Model の中身を見るようにすれば汎用化できるかも。

<?php
$views = array( 
	array('adjustmentjobs', 'adjustmentjob'),
...
	array('stores', 'store'),
	array('workingsituations', 'workingsituation'),
);

foreach ( $views as $v ) 
{
	$v0 = $v[0];
	$v1 = $v[1];
	
	$xml0 =<<< END
<?php 
$items = array('items'=> array('item'=> $$v0));
$xml = Xml::fromArray(array('response'=>$items));
echo $xml->asXML();

END;

	mkdir(&quot;view/$v0/xml&quot;);
	$path=&quot;view/$v0/xml/index.ctp&quot;;
#	print &quot;$xml0n&quot;;
	file_put_contents( $path, $xml0 );

	$xml1 =<<< END
<?php 
$item = array('item'=> $$v1);
$xml = Xml::fromArray(array('response'=>$item));
echo $xml->asXML();

END;

#	print &quot;$xml0n&quot;;
	file_put_contents( &quot;view/$v0/xml/view.ctp&quot;, $xml1 );
	file_put_contents( &quot;view/$v0/xml/add.ctp&quot;, $xml1 );
	file_put_contents( &quot;view/$v0/xml/edit.ctp&quot;, $xml1 );
}
カテゴリー: CakePHP, WPF | CakePHPのWeb APIレスポンスをXML型式で返す はコメントを受け付けていません

CakePHPとWPFの相互運用 cake bake を使って MVC を自動生成する

最初に CakePHP の cake bake all を使って Model/View/Controller を一気に作成したいところですが、既存のデータベースから移行する場合、CakePHP の複数形ってのにひっかかります。

データベース上に store というテーブルがあって、これに対応する MVC を作ろうとすると、こんな風に Model.php 内でエラーがでます。CakePHP のテーブル名のルールとして複数形にするわけで、本来ならば「stores」を探しているのですが、このテーブルがありません、ってな具合ですね。

Error: Table stores for model Store was not found in datasource default.
#0 C:xampphtdocscakephp-2.4.5libCakeModelModel.php(3498): Model->setSource('stores')
#1 C:xampphtdocscakephp-2.4.5libCakeModelModel.php(1355): Model->getDataSource()
#2 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandTaskModelTask.php(503): Model->schema(true)
#3 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandTaskModelTask.php(801): ModelTask->doAssociations(Object(Model))
#4 C:xampphtdocscakephp-2.4.5libCakeConsoleCommandBakeShell.php(174): ModelTask->bake(Object(Model), false)
#5 C:xampphtdocscakephp-2.4.5libCakeConsoleShell.php(431): BakeShell->all()
#6 C:xampphtdocscakephp-2.4.5libCakeConsoleShellDispatcher.php(207): Shell->runCommand('all', Array)
#7 C:xampphtdocscakephp-2.4.5libCakeConsoleShellDispatcher.php(66): ShellDispatcher->dispatch()
#8 C:xampphtdocscakephp-2.4.5appConsolecake.php(36): ShellDispatcher::run(Array)
#9 {main}

Model 名は「Store」なんだから、なんとか store テーブルも見つけてくれれば便利じゃないかと思うんですが、これで結構悩みます。
で、テーブル名を変えられる、あるいは新しいシステムの場合はいいんですが、既存のシステムがあったり、それなりの命名規約があたりすると変更は難しいのです。なので、テーブル名はそのまま「store」のままで使いたいですね。

■テーブル名に単数名を使う

そういう場合は、あらかじめ、$useTable に store を指定した Model を作っておきます。そうやって、

<?php
App::uses('AppModel', 'Model');
/**
 * Store Model
 *
 */
class Store extends AppModel {

/**
 * Use table
 *
 * @var mixed False or table name
 */
	public $useTable = 'store';
}
&#91;/code&#93;
<p>
そのあとに、
</p>
<ol>
<li>cake bake Model で Store モデルクラスを作り直す(バリデーションとか)</li>
<li>cake bake all で Store テーブルに対する MVC を作る</li>
</ol>
<p>
という手順を踏みます。こうすると、Store モデルクラスが、「store」テーブルを対象にするという複数形なしの方法で作れます...が、これってコマンドラインでぽちぽち y/n を決めないといけないので、もっといい方法がないものかと思っ、
</p>
[code]
> cake bake Model Store
> cake bake all Store

の2コマンドで一気にできますね。何故か cake bake Model Store のほうは複数系を探さずに単数系のままなので、一気にモデルクラスができます。そのあと、bake all Store でモデル名(テーブル名?)を指定すると、これまた PHPUnit で y を押すだけで、一気に作れます。
なので、「y」だけ書かれた応答ファイルだけ作っておいて次のようなバッチファイルで動かせば大丈夫ですね。

cmd /c cake bake Model Store
cmd /c bake all Store < yes.txt
&#91;/code&#93;
<p>
テーブル全体は適当なperlかPHPでスクリプトを書いてやればOKです。
そうして作成してやると、ひとまず1つのテーブルに対して CRUD ができる MVC が作成されます。
</p>
<a href='http://www.moonmile.net/blog/wp-content/uploads/2014/01/wpid-dworkblogimage20140128_02org1.jpg'><img border='0' src='http://www.moonmile.net/blog/wp-content/uploads/2014/01/wpid-dworkblogimage20140128_02thum1.jpg'/></a>
<p>
この例では、starttime というテーブルに http://localhost:81/cakephp-2.4.5/Starttimes でアクセスをします。コントローラー自体は複数形で生成されるので、元テーブル名と若干ずれてしまうのですが、まあ良しとしましょう。
Person テーブルが People でアクセスするとか、迷う点もあるのですが。
</p>
<p>
■テーブルの外部結合を指定する
</p>
<p>
あらかじめ、テーブルの外部結合をしておけば CakePHP が拾ってくれる?と思うのですが、既存のテーブルには外部結合が設定されていませんでした。まあ、そのまま使うのもよいのですが、せっかく自動生成された View で ID の羅列があるのはデバッグ上面倒くさいです。$belongsTo を使うと Model に外部結合を設定できます。
</p>
<p>
Model クラスに下記な風にアソシエーションの設定を追加します。
アソシエーションの設定は、$belongsTo(多対1)だけを使いました。データ上、親からその子を検索するというのがなくて、子テーブルから親の名前が頻繁に発生するからです。このあたりは、システムの特性に合わせてですね。
</p>
[code lang="php"]
class Store extends AppModel {
	public $belongsTo = array(
	    'Areagroup' => array(
	        'className'    => 'Areagroup',
	        'foreignKey'   => 'AreaGroupID'
	    ),
	    'Sellingcompany' => array(
	        'className'    => 'Sellingcompany',
	        'foreignKey'   => 'SellingCompanyID'
	    )
	);

この例では、Storeテーブルが、AreagroupテーブルとSellingcompanyテーブルに外部結合しているという設定です。子であるStore(お店)が、Areagroup(地域)やSellingcompany(会社)に含まれているというスタイルが直観的に書けます。あとはいろいろ調べたのですが、あまり必要はないでしょう。必要であれば適宜クエリを書いて取ってきてしまいます。
というのも、親から子を探す時にたいていは検索条件を入れるので、単純な $hasMany ではうまくいかないのです。適切な設定をしたり適切なモデルを作ってもよいですが、そうなると特定の検索条件ごとにモデルが必要になってしまいモデルクラス自体が氾濫してしまいます。なので、普通に ER 図にできる範囲でよいかと。

アソシエーションの設定が終わったら View だけを書き換えます。

cake bake View Store

とすると Store モデルに関する View だけを書き換えます。cake bake all をするとせっかく設定した $belongsTo が消えてしまうので注意してください。

アソシエーションをつけたときには、指定先のページが開くリンクが作られます。

■Viewでリンク先の名称を変える

先ほどの SellingCompanyID(会社ID)はわかるのですが、これが数値のままだと編集しづらいですね。これは View のほうを直接修正します。 $belongsTo で設定したときは、$store[‘Sellingcompany’][‘ID’] のように外部連携先のテーブル名とIDが設定されています。ページを開くときは、IDで指定するのですが画面表示の場合は、
$store[‘Sellingcompany’][‘Name’] のように名前を設定しておきます。

<td>
  <?php echo $this->Html->link($store['Sellingcompany']['Name'], array('controller' => 'sellingcompanies', 'action' => 'view', $store['Sellingcompany']['ID'])); ?>
</td>
<td>
  <?php echo $this->Html->link($store['Areagroup']['Name'], array('controller' => 'areagroups', 'action' => 'view', $store['Areagroup']['ID'])); ?>
</td>

こうすることで、会社名と地域名をが表示できます。

実際は、CakePHP は Web API のバックエンドとしか使っていないので、この View は使われません。ですが、最初のデバッグ時には有効なのと、簡単なデータ編集ならばこれで十分なのでそのままにしておきます。

カテゴリー: CakePHP, WPF | CakePHPとWPFの相互運用 cake bake を使って MVC を自動生成する はコメントを受け付けていません

余裕があるうちにCakePHPとWPFの相互運用をまとめていく(準備編)

去年末にリリースした、某予約システムですが、CakePHPとWPFの相互運用をしています。この発想は、手始めに Silverlight+WCFの組み合わせで実験 | Moonmile Solutions Blog からありました。サーバー側にLAMP環境を据えて、クライアントでWindowsを使うという仕組みですね。当時から、Web APIはあったのですが、結合の部分はWCFのほうがよいのではないか?と思ってWindows寄りに考えていました。最近は、ASP.NET MVC、Web APIの流れで、WCFを直接扱うよりもRESTfulで送ればよいという流れがあって、艦これ諜報員の場合も単純なJSONで戻してくるので「諜報」が楽ってのもあります。

仕事なので、サーバーに何を使うのかの選定から始めるわけですが、コストの問題やらサーバーのプログラマ調達の問題などがあって、

  • サーバーにLinux+MySQLを使う
  • クライアントにWindowsを使う
  • クライアントにブラウザを使う

というパターンになっています。クライアントがWindowsだけならば、直接MySQLにアクセスしてもよいのですが、ブラウザも含めるとなるとやり取りは、HTMLかjQuery+JSONかってパターンですよね。そこそこ古いブラウザも含めるとなると、HTMLをべたべたに返してやる必要があります。また、もともとのデータベースがMySQLだったこともあって、そのまま移行。レンタルサーバーの運用コストを考えてLinuxを使う、といったところです。まあ、結果論でいえば、Windows Server+ASP.NETの組み合わせでもよかったのでは?と思うときもありますが、それは結果論で。

さて、サーバーの選定と大まかな仕組みを決めたところなのですが、パフォーマンス的にどのくらいかというと、某予約システムでは、1000件/日程度のアクセスしかないので、それほどのパフォーマンスはいりません。これが1000件/秒とか1000件/分とかだったら考え直すのでしょうが、ここはあっさりCakePHPを選択しました。CakePHPがいかに遅いとはいえ(実際遅いのですが)このぐらいのスピードだと耐えられるでしょう。しかし、実装してきてわかったのですが、Web APIをふつうのApplication APIのようにバシバシ呼び出しとレスポンスが悪くなります。当たり前ですね。間にHTTP通信を絡めてしまうし、エンドのCakePHP自体のレスポンスもあるし。なので、ある程度は、APIの呼び出し回数は考慮しないといけません。

image

本来は、ブラウザからCakePHPへアクセスしたかったのですが、諸事情があってブラウザからOriginal に作成した MVC をアクセスしています。ブラウザでの予約は、不特定多数の一般的なお客さんなので、Viewの部分はそれなりにリッチに書かなければいけません。なので、CakePHPの吐き出すソースは何に使うというとおもにWindowsクライアントからのWeb API呼び出しに使います。また、本来ならば Original MVC から直接MySQLへのアクセスを禁止するほうがよいのですが、SELECTに限って許しています。これも諸事情の関係ですね。

なので、CakePHPのView部分に関しては、いろいろ豊富な方法があるのですが一切使っていません。あくまで Web APIに徹する(Windowsクライアントからの呼び出しに応答する)役目を負わせています。

CakePHPからの応答がXML型式になっていますが、これはJSONでも構いません。当時、WindowsクライアントでJSONをパースする方法が(私的に)見当たらなかったのでXMLにしたのですが、実は DataContractJsonSerializer クラス (System.Runtime.Serialization.Json) を使えばOKです。Xml系のオブジェクトになるので、自前の ExDoc と組み合わせて使っています。XMLの構造=クラス構造とはしたくなかった≒面倒だった、というのも理由のひとつですが、システム的にクライアントとサーバーのアップデートは「同時には行えない」ことが分かっていたので、APIからの構造が変わっても古いクライアントでも緩く動く、というシステム構成を作っておくためです。このあたりのテクニックは、cookpad に学んでいます。

Windows クライアントは WPF で作っています。業務用なので Windows フォームでもよかったのですが、先行きタブレット版のストアアプリとかスマートフォン対応を考えると XAML で書いておいたほうがいいですよね。Windows クライアントのほうは社内業務用なので、インストーラ付きでガシガシ作れる状態です。ただし、Windows 7対応ということで、まだWindows 8にはなっていません。

WPF アプリのほうは、MVVM パターンを使っています。なので、なにかの機能を入れて WPF クライアントを拡張しようとすると MVVM+MVC の苦行パターンにはまります。この辛さは半端ではありません。去年の夏の1か月は暑いさなかこの「苦行」をしていました。マスター系のクライアントも MVVM で作っていたので、10テーブルぐらいのマスター画面を WPF で、あと諸々の業務用画面を WPF でという具合ですね。それに対応する CakePHP の Controller をちまちま作るという感じす。もちろん、CakePHP は bake を使って自動生成させるのですが、定型的なブラウザのマスター画面ならいいのですが、ちょっと気の利いたテーブル構成(複数テーブルっていう意味で)とちょっと気の利いたマスター画面を作ろうと思うと、手作業で追加が発生するんですね。当然ですが。まあ、それでも、そこそこのコード量を自動的に吐き出してくれるので、そこそこのスピードでマスター画面が作成できたのはよかったかと。

そのあたりも含めて、CakePHPとWPFの相互運用をテーマにぼちぼちと書き下していく予定です。ちなみに、機能拡張の部分はそれぞれが分離されているので非常に楽です。CakePHPのWeb APIになっているので、ブラウザでAPIの結果をXML型式で確認できるとかデバッグも多少楽になっています。複雑なSQLをどう解くのか(どうテストするのか?)という点では、PHPUnitを使うのか、Web API経由でMSTestを使うのかという問題もありますが、いまのところ MSTest 一本でやっています。

カテゴリー: CakePHP, WPF | 余裕があるうちにCakePHPとWPFの相互運用をまとめていく(準備編) はコメントを受け付けていません

リモートデスクトップ接続すると画面が真っ黒になる、ときの対策(おそらく自分限定)

えらく嵌ったのでメモ書きとして。Windows 7ではでなくて、確かWindows 8でもなでなくて、Winodws 8.1で出ているのでドライバーの問題のような気がするのですが、原因は定かではない。けど、対処の仕方は明らかに「Intel HD Graphics 3000」ドライバー が悪さをしていると、と。追記 2014/02/21
コメントにある通り、「AMD Radeon 6600M and 6700M Seriese」のほうがダメなようです。「「AMD Radeon 6600M and 6700M Seriese を無効」することで、リモートデスクトップ&外部端子がうまく動きます。

■現象

リモートデスクトップ接続をしたときに画面が真っ黒になります。真っ黒になるのはいくつか原因があるよう、ビットマップキャッシュの問題もあるのですが、今回は違って接続先のグラフィックドライバーが変なことをやっているようです。画面は真っ黒ですが、マウスやキーは効いています。強制的に切断すると、なんとなく開いたウィンドウがでてきます。

うちの vaio のノート VPCSB は Windows 8.1 をクリーンインストールすると、ディスプレィアダプタが2つ見つかります。内部ディスプレイ用と外部端子用なんですかね?謎なのですが、2つあります。

image

でもって、Windows 8.1 はクリーンインストール時に、ネットワークから「自動的に」最新のグラフィックドライバーをダウンロードして「勝手に」インストールします。ええ、auto にインストールしてくれるのは便利なので、最適化されるのはいいのですが、ここでインストールされてしまう「AMD Radeon 6600M and 6700M Seriese」が問題なのです。

image

このノート自体、型が古いので(出た当時はWindows 7だったし)8.1 に対応しきれないから、なのかもしれまんせんが、メモリは 8GB 積んだし、CPU は Corei5 なのでそこそこ開発にも使えます。なので、最近 SSD 256 MB に乗せ換えて(何故か)英語版 Windows 7 で動かしいたのですが…まあ、これが別件の不具合で使い物にならなくなったんですよね。いや、それはまあ、VMWare 上に戻せばいいわけで、復旧させているわけですが。

■対処

で、Windows 8.1 をクリーンインストールしたはずなのに、コントロールパネルの「プログラムと機能」を開くと、こっそり?「インテル HD グラフィックス・ドライバー」がインストールされているわけで、これをアンインストールすると、無事リモートデスクトップで接続できるようになります。そもそも、グラフィックドライバーが2つ表示されるのが変な感じがするので、なんらかの競合っぽいのですが、まあアンインストールしてもOK。

image

ただし、なんらかの電源の制御も入っているらしくノートの蓋を閉じたときの制御が「電源コントロール」から消えてしまいます

試しに サポート: インテル® HD グラフィックス 3000/2000 内蔵第 2 世代インテル® Core™ プロセッサー から最新のバージョンをダウンロードして適用してもダメでした。そもそも、Windows 8.1 が自動でダウンロードしてくるのが、9.17.* ぐらいまで一緒なので、そこそこ最新なはずなんですよね。このあたりはよくわかりません。 → 「AMD Radeon 6600M and 6700M Seriese」を無効かして、「Intel HD Graphics 3000」だけの残すようにするとうまく動きます。Intel HD Graphics 3000 のほうは、外部出力端子のドライバーのようです。

私の場合、ノートブックに対してリモートデスクトップ接続することが多いので、これができないと不便なんですよね。ノートのキーボードを使うよりもデスクトップから使ったほうが効率が良いというのもあるのですが、ファイル移動とか資料のコピーとか設定なんかもリモートデスクトップ経由なので。

 

カテゴリー: 雑談 | 4件のコメント

余裕があるから F# で数独を解くを発表してみる

.NETラボ 勉強会 2014年1月 : .NET Lab の発表スライドです。

1週間ほど前にプログラムを作ったので、それを整理して発表すれば…と思ったのですが、直前に仕事先の不具合と前日の風邪であえなくダウン。スライドを作ったのは、当日の午前中です。ええ、いつものことなんですが。

当日発表に使ったデモコードは、http://sdrv.ms/1aWXJZH からダウンロードできます。

F#については初心者なので、突っ込みどころ満載なのですが(というほど内容も書いてありませんが)、その分、デモなところでC#プログラマから見てF#を使うと便利そうなところを紹介できと思います。ここ1か月間集中的に使ってみてわかったのは、

  • F# intaractive を使って、トライ&エラーを繰り返してプログラムが作りやすい
  • 中途のパラメータを手作業で入れながら、プログラムを実行しやすい
  • .NETのライブラリを簡単に呼び出せる

ところが(私にとっての)利点です。この手のロジックを作るとき、トライ&エラーはスクリプト言語(私の場合Perl)を使ってみたり、C++でCppUnitを作って何度も繰り返したりするのですが、F#で作ると…というか、Visual Studio の F# Interactive との組み合わせが非常に便利です。スクリプトで書いて、Alt+Enterで実行して、結果を見るという感じで繰り返します。おそらく他の関数型言語(OCamlとかHaskellとか)にも似た環境はあると思うのですが、Maxima と同じ感覚です。

そんな訳で、引き続きパズル系を解く/作るに使うのとFEM作りへ。

カテゴリー: F#, 勉強会 | 余裕があるから F# で数独を解くを発表してみる はコメントを受け付けていません

有限要素法をF#で実現しようという続きメモ(渡航編)

年末から続けている構造解析をF#で作成しようという話のメモを少し。主に参考書の紹介です。

仕事がら有限要素法を使って計算をしている…部分の修正などを行っているわけですが、有限要素法のコアな部分は触っていないんですよね。なので、専門家が話すテクニカルタームについていけないのが不甲斐ないので、再勉強がてらF#も同時に学習しようというねらいです。

なんで、F#かというと、手元のプログラムがFortranとC++の組み合わせでたまにC#/WPFが混じります。これに組み合わせるとなると、.NET系がよいわけで、ならば頭文字FつながりでF#がよいか、というノリで。いや、実のところは、有限要素法なコアなところを.NETのPCLで作っておけば、タブレットとかストアアプリとか使いやすいだろうという腹づもりです。更にXamarinと組み合わせればそのままiPad上で動くかもしれません。

ちなみに、有限要素法のソフトウェアとしては NASTRAN と ANSYS が2強らしく、これからデータコンバートをすると適当なメッシュデータを作れます。実は Femap の無償版があることを知ったので少ない節点ならばこれで試すのもありですよね。あと ADVENTUREプロジェクト というのがあって、ver.1 ならばメールアドレスのみでソースを見れます。

0から学ぶ…ってほどでもないのです。以下の本からスタートしています。

 

式を Fortran に直したり C++ に直すというよりも、根本的なところを学びたかったので、仕事上実践の部分と基本的なところと2冊用意しました。2年前に買ったのですが、ざっと読み下したままだたんですよね。なので、「実践 有限要素法~」の演習問題のところを正月に鉛筆で解いていました。

そんな中で行列が出てきたので、大学以来の線形代数の本もいくつか。

大学の教科書はもう手元にないので図書館で借りてきています。ざっと読み返して、行列と行列式と逆行列がわかればOKです。固有値はいるのかな?原子力学科のときは使ったのですが、構造解析で必要かどうかはわかりません。ただし、行列式とか逆行列とかは、数式で解くとは限りません。というか、計算機用のアルゴリズムが必要です。

線形代数自体をプログラムで解くときに、一番おもしろかったのがこれです。

行列を解いていくと、そのまま立体の回転とか、画像の変換とかにも使えることが分かります。多変量分析もそうですよね。このあたり、それぞれ別々にツールがあるわけですが、根っこは行列で解くところにあるので相互に別の分野のロジックを持ってくることも可能です。なので、線形代数の特徴を知っておくと応用範囲が広いかな…ってのを今頃知った次第です。

わき道にそれますが、行列の次元の話ついで、超弦理論の本を読みました。非常にわかりやすくて数式がほとんど(3箇所だけ?)出てきません。なぜ超弦理論が10次元(あるいは11次元)なのかが明快に説明されています。超弦(超ひも)論を知ったのは20年前なのですが、それ以降すごい発展を遂げていたんですね。

さて、ここにきてやって F# の本を買いました。ぽちぽちと Kindel で Programing F# 3.0 を読んでいたのですが、手元におくために日本語の本も一冊。

F# で必要な要素を絞り込んでいるので(配列の扱いとループぐらいか使わない)、WEBなどで大丈夫かと思ったのですが、やっぱり拾い読み用に手元に置きたいかなと。行列を扱う場合、2次元配列/Array2Dで扱うか、F# MathProvider にある matrix を使うかと迷うところです。実は、数独の問題を解いたときに知ったのですが、Array2D の場合は、Aij は、A.[j,i]となって x y が逆なんですよね。これは C 言語の二重配列と同じので当たり前といえば当たり前なのですが、数式からそのままプログラムコードに落とすときに間違いやすいところです。matrix のほうは、A.[i,j] のように x y の順になっているので、こっちのほうがいいです。

あと、数式の場合は 1 始まりなのですが、プログラムの場合は 0 始まりになります。ループを使うとき(maxのとき)に気を付ければいいのですが、その脳内変換が面倒ですね。なので、できるだけループを使わず、再帰を使うのが F# らしいかと思っています…が、matrix には Seq 等が使えない(Arrray2D にも使えない)ので、このあたり適当な拡張が必要かなと。

で、現在のところはこれです。

具体的に Visual Basic 2008 を使って有限要素法のソフトを作っていきます。元ネタは Fortran だそうで、それらしいコードなのですが、グラフィック部分が .NET なんですよね。全部実装すると、pre, solver, post の3種類が揃うのでワンセットでかなりのことができそうです。今後は、この VBのコードを F# にコンバートしていこうと思ってます。

カテゴリー: F#, FEM | 有限要素法をF#で実現しようという続きメモ(渡航編) はコメントを受け付けていません

[WPF] 最初に開くXAMLを切り替える

備忘録的に、メモ。
WPFアプリで最初に開く画面は、App.xaml の StartupUri 属性にしてあるのですが、これをコンパイル時に切り替えます。たとえば、DEBUG 時に「MenuWindow.xaml」を開いて RELEASE時に「LoginWindow.xaml」を開くというパターンです。デバッグ時にはログイン画面が面倒だから、飛ばしてしまってメニュー画面を開きたいときに便利です。という自分。

/// <summary>
/// App.xaml の相互作用ロジック
/// </summary>
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
#if DEBUG
        this.StartupUri = new Uri(&quot;MenuWindow.xaml&quot;, UriKind.Relative);
#endif
        base.OnStartup(e);
    }
}

OnStartup イベントをオーバーライドして、StartUri プロパティを書き換えます。この場合は、DEBUG 時には「MenuWindow.xaml」が使われて、RELEASE 時には App.xaml に指定されているページが開かれるというパターンです。
設定ファイルを読み込んで、トップ画面を開くときにも使えるかも。

カテゴリー: WPF | [WPF] 最初に開くXAMLを切り替える はコメントを受け付けていません