ガンダムUCを観ながらXamarin.Formsをver2.0に上げると、ブルースクリーンになるのか

という訳で、ブルースクリーンになりました。単にガンダムUCをビデオ再生しながら、Visual Studio 2015 で Xamarin.Forms 2.0 にアップデートしながら、Android on Hyper-V でデバッグをしていただけなんでけどね。なぜか、2回ほどやって、2回ともなるからこれが不思議なのです。

「自動的に再移動します」とありますが、なかなか進まないので電源をぶちっと切って、再起動させます。すると、画面が真っ黒になって BIOS 画面すら出ないというパターンに。暫く待っても ping すら通らない冷や汗状態になりました。この写真は、次の日の昼頃に復旧させようとして、またブルースクリーンになった状態なので、前日の夜中に2回、そのあとに3回ほど同じ現象にあたっています。

あとの3回は、

  • Android on Hyper-V を使って、画面キャプチャ中に落ちる。
  • FireFox でツイッターに入力中に落ちる。
  • なんだかわからないが、起動して Core Temp を起動したら落ちる。

とい体たらくです。これはあまりひどいので原因究明をしていった記録を少し。

再起動で立ち上がらない

再起動で BIOS の画面も出ずに起動画面がでないので、ビデオボードの不具合か?と思ったのですが、BIOS すら出ないのは変な感じです。実は Window 10 の場合、高速起動が働いていて BIOS 画面をすっ飛ばしてロゴの画面が表示されます。このときに、ビデオドライバー等に不具合があれば真っ黒になりますね。Windows 10 の場合、低解像度とかにならなくて真っ黒になるのが困りものなのですが…ひとまず、電源コードを抜いて 5 分ぐらい経って起動すると、無事 BIOS 画面が表示されるようになりました。ひとまず、ビデオボードのドライバーは問題なさそうです。

ビデオ再生の不具合か?

ユニコーン UC は、標準の「ビデオ」アプリで再生させているのですが、たまにこのアプリが落ちます。ビデオドライバーの辛みもあって、エラーを起こしたりするのですが、再生によるエラーが原因でブルースクリーンではないようです。

Hyper-V の不具合か?

ブルースクリーンということはカーネル回りで落ちているので(実際「KMODE Exception not handled」になってる)、Hyper-V ともども落ちるということは考えられます。が、先の3回はともかくとして、あとの 2 回は Hyper-V は関係ありません。いや、バックグラウンドで動いているパターンもあるので、これが原因かもしれないのですが、もうちょっと原因を探っていきます。

ブルースクリーンが :( ではない

そういえば、Windows 10 のブルースクリーンは :( の英語パターンなのですが、なぜかこのブルースクリーンは日本語だし :( がありません。なぜか?

CPU のパフォーマンスや、キャッシュ破壊などの OS 絡みのカーネルパニックならば :( の絵が出るはずですよね。ってことは、OS 関係なく、ハード絡みかもしれません。

Core Temp で CPU の温度を見る

ひょっとすると単純な熱暴走なのでは?と思いつつ Core Temp を立ち上げると、CPU の温度が 70度近くになっています。いやいや、冬だしこんなには高くなかったはずだし、そもそも高い水冷式にしたのに、こんなに高くなるはずがありません。ファンが止まってしまったか、それとも水冷式って寿命があるのか?

水冷式のファンを掃除する

空冷式の場合は、ファンにほこりがたまってしまって PC 内が熱くなってしまうことがあるのですが、水冷式の場合は下のように冷却液がめぐっていて、PC 筐体が熱くなることはありません。

だからこその、水冷式なんですけど、70度近くってのはあまりにも上がりすぎ。冷蔵庫のガスが抜けてしまったように冷却液が抜けてしまったのでしょうか?

ひょっとしたら、ファンのビスを外して、ラジエーターの掃除をしました。そう、クーラーのような細かいフィンの間にほこりが溜まっていて冷却効率が悪くなってしまったのではないか、と。

正解は、水冷のラジエーターでした

どうやら、これが正解でした。ラジエーターにたまっていたほこりが、冷却効率を悪くしてしまっていて冷却液が冷えなかったという状態…ううう、これは落とし穴ですね。

image

PC の筐体は、何回か掃除はしていたのですが、ラジエーター自体の目詰まりは考えませんでした。そんなわけで、原因は「ラジエーターの目詰まりによる CPU 熱暴走。そして、ブルースクリーンに至る」というわけです。

カテゴリー: 雑談 | ガンダムUCを観ながらXamarin.Formsをver2.0に上げると、ブルースクリーンになるのか はコメントを受け付けていません

Xamarin.Forms でドラッグを実装しよう(のうりん編)

今年は申年ということで、何故か正月から「のうりん」を読んでいます。申年は Xamarin に関係ありますが「のうりん」には関係ありません。いや、猿の回があったから少しは関係するかも。と、会社を辞/〆て数年経ちますが、フリーでITプログラム屋さんをやっていると月々の収入がえらい上下します。自営業とはいえ小売りや飲食店をやっている訳ではないので、数か月間収入がなかったり、どかっとまとめて入ってきたりします。最初の頃は、この上下が心理的に苦しくて(だからこそ、会社員や契約社員になったりするんですけど…というのは後で気づきました)あれこれ継続的な収入を考えていたのですが、いやいや「アワビ業者」と同じですねと考えてからあれこれすっきりしました。計画的なあれこれはさておき、1年のうちの数回しか収入がないものでそれを1年間で分配すればよいわけです。不確定な年棒制みたいなものですが、程よく貯金(金銭的なバッファ)があれば、多少の浮き沈みは問題ではありません。そんな訳で「のうりん」を読んでいるわけですが、先日のポスター騒ぎをさておき原作を読めば、「ああ、『もやしもん』と『おせん』が混ざっている」わけで、時には『夏子の酒』も入っていたりします。農業あれこれは、板橋区で畑の貸し出しを数年やっただけですが、先に手間をかける、そして収穫するという意味では、フリーのITプログラム屋さんも似たようなものです。農業の場合、農機具を借金して買うパターンになるので農協の話とか台風の話のほうが深刻ですが、自給自足(地産地消)できる分だけ、お金のやり取りが少なくなる分だけお農業のほうが良いかもと思ったり、さすがに午前4時起きで作業を続けるのはちょっと私には無理、と思ったりと読み進めるわけですが。ちなみに、5巻ぐらいになると「おっぱい」の割合が減ります。蘊蓄部分が多くなって、読むスピードが落ちるわけですが、そのあたりのバランスはラノベならではですね。漫画っぽい引きと挿絵の挿入は、他のラノベでもそうなのか分かりませんが、筒井康隆風でもあります。まあ、それ以前に、黄表紙とかタイポグラフィとかあるわけですが。

そんな訳で「のうりん」を読みながら、他人様の時間と自分の作業する時間とをあまり交差させないように、OpenCCPM の構想を立てているところです。OpenCCPM では、

  • タスクカード抽出
  • PERT図
  • ガントチャート

が、ひとつのタスクデータを共有します。いわば、タスクの三面図みたいなものを作るわけで、UML も同じ発想です。これにアジャイル的な変化を組み込む(実は CCPM のプロジェクトバッファは、アジャイル的な変化を畳みこむので考慮しない)ようにします。プロジェクトバッファは、タスクの遅延には強いのですが、タスクそのものの増減には関与しないんですよね。というわけで、IT 特有の時間経過によるタスクの変化(いわゆる状況/要件の変化)を織り込むのが OpenCCPM の目標です。ちなみに CCPM(クリティカルチェーン プロジェクトマネージメント)は、PMBOK に含まれています(発祥は TOC ですがね)。

タスク抽出をタブレットで実装したい

タスク抽出にはいろいろな方法があります。

  • トップダウン方式の WBS
  • ゴールから後戻りをする方式

PMBOK では WBS という作業単位を出していますが、いきなり WBS で出しててはダメです。あれこれ、トップダウン的にプロジェクトマネージメント的にマインドマップ的にあれこれ出しているものがありますが、全部ダメです。プロジェクトマネジメント関係は、以前 TOC を知ったころにあれこれやて考えた時があるのですが、政治的な話に踏み込んでしまうので途中で止めました。もっと技術的な職人的なことに労力を突っ込んだほうがいいだろう、と思ったわけです。その裏ではちょこちょことマネジメント的なテクニックを個人的に使ってはいましたが、外側で使うことはありませんでした。が…どうやら、この歳になって、あれこれとダメダメなマネージメントに引きずられるのが嫌になってきました。ダメダメなのはまあ、いいんですが、ダメダメなスケジュールと進行に自分の時間を浪費させられるのはちょっと嫌~な感じですね。というわけで、少しだけ好き勝手できるようにします。

無駄を省くために、ゴールからタスクを抽出する方法もありますが、実は時間が掛かりすぎます。なにも一から泥縄式にタスクを抽出することはありません。究極に無駄を省くこともできますが、何も最適値である必要はありません。準最適であるだけで十分だし、更に言えば、競争力としては「他の会社でやっているダメダメプロジェクト管理」よりもちょっとだけ良ければいいのです。

ある程度経験があれば、タスク抽出にはテンプレートや以前の経験が便利ということが分かります。逆に言えば、新人 PM には無理です。不可能です。以前やったことがある経験値としてのタスク抽出と、どこかのストーリーで読んだことがある(本を読むことは疑似体験として重要です)タスクを取り入れることは、最適ではなにせよ準最適になり得ます。

さらに、最初のタスク抽出に固辞する必要はなく、状況が変化するのであれば(プロジェクトを進める間に、新人 PM は、新人ではない PM に進化しますよね)、それに従ってタスク自体も組み替えていけばよいわけです。由緒正しいスクラムプロセスの場合は、2週間単位のスプリントで決めますが(最近のスクラムのそれになっている?)ほどよい人数になれば、コミュニケーションコストがかさむし、全体の統括が必要になってきます。「統括」自体は、マイルストーンという形でスケジュールを縛っていくのでプロジェクトのバッファの意味がなくなってきます。そういう欠点も含めて、状況に沿って動く「予測が可能なプロジェクト運営」を目的とします。

で、タスク抽出自体は付箋を使うのが一番よいのですが、手書きだけだと共有ができないし、たくさんのタスクになると検索性も悪くなります。また、タスクを PERT 図はガントチャートに自動的に直すことができません。このあたりも含めて、タブレットでタスク抽出ができるとよいわけです(天井にカメラを据えて、付箋の画面キャプチャ、OCR 認識も考慮したいですよね)。

Xamarin.Forms で Drag する

ふと、大型タブレットを探したのですが、13インチぐらいあるタブレットは、iPad か Windows タブレットになっていますね。何故か Android タブレットは 7 インチぐらいで止まっています。大型タブレットの需要が少ないのもあるのでしょうが、スマートフォーンではないタブレットという分野は閲覧を抜群に上げるために重要な分野です。バッテリー故に重たくなってしまうタブレットですが、液晶の電力を下げたり、薄い有機液晶も出てきているので、5年以内にはでそうです。というか、スマートフォンは無くなるんじゃないかなと思っています。

それはさておき、Java/Objective-C/Swift であれこれするよりは、C# で一括で作ったほうがよいだろうという発想と、MVVM の VM-M を共通化するために View を Xamarin.Forms に渡したほうがよいだろう、という労力的な問題があります。OpenCCPM のツール群は、VM-M のほうに注力するので、できるだけ View は省力化したいのです。マンパワー的に。ちなみに OpenCCPM の作成自体は OpenCCPM で行っています…ということにします。

Xamarin.Forms を使う時に不便なのは、タップしてドラッグ関係の機能がないことです。以前から何故ないんだろう、と思っているわけですが今でもありません。Xamarinでコントロールのドラッグに対応する で作ってみたのは、Windowsストア/Android/iOS の三種類です。これを、Xamarin.Formsでタッチイベントを処理するには?(iOS/Androidの各種ジェスチャー対応) にあるロングタップを参考にして、 OnLongPress をコントロールの Renderer を使って実装すればよいかと思っていたのですが、あれ? GestureDetector.SimpleOnGestureListener って、タップ位置を渡していないよ、なぜかタップの Down はあるけど Move/Up が消されちゃってるよ orz ってことで、どうやら自分で実装しないといけないようです。確かに、Android/iOS とドラッグのやり方は異なるので、実装をどちらに合わせるか悩むところなのですが。

でもって、調べる Android でもドラッグの方法がいくつかあるので、最終的な手順まで計画を立てます。

  1. Xamarin.Android 上で OnDragEvent を使って実装
  2. Xamarin.Android 上で Touch イベントを使って実装
  3. Xamarin.iOS 上で実装
  4. インターフェースを ManipulationDelta 風に変更する
  5. Renderer を使って、Xamarin.Forms にコールバックする

のように手順を踏まないと作れないっぽいです。

という訳で、このあたりからちまちまと。

Android Drag の参照先

カテゴリー: Xamarin | Xamarin.Forms でドラッグを実装しよう(のうりん編) はコメントを受け付けていません

WPF でコントロールをドラッグする

以前、WPF でコントロールのドラッグコードを書いたのですが、業務的に少し書き直してみます。WPFでコントロールをドラッグ(1) では、マウスの down/up/move のイベントを直接つけていますが、コントロールが増えたときには面倒ですね…というか、動的にイベントを追加すればよいのですが。ふと、[WPF]枠なしでリサイズ&ドラッグ移動可能なウィンドウを作る に行き当たると、WindowChrome クラス を使うと枠なしのウィンドウが作れて、MouseLeftButtonDown イベント内に DragMove イベントを呼び出せばよいようです。この方式だと簡単ですね。

というわけで、マウスダウンのイベントだけあらかじめ付けておいて、マウスの移動とアップイベントは動的に追加します。また、クラスの継承ではなくて拡張を使って実装します。

 
public static class ControlDragExtenstions
{
    /// <summary>
    /// コントロールのドラッグを有効にする
    /// 親コントロールが Canvas であること
    /// </summary>
    /// <param name=&quot;sender&quot;></param>
    /// <param name=&quot;e&quot;></param>
    public static void DragMove(this Control t, object sender, MouseButtonEventArgs e)
    {
        var el = sender as Control;
        var canvas = el.Parent as Canvas;
        var dragOffset = e.GetPosition(el);
        el.CaptureMouse();

        MouseEventHandler mouseMove = null;
        MouseButtonEventHandler mouseUp = null;

        mouseMove = new MouseEventHandler((_, __) => {
            Point pt = Mouse.GetPosition(canvas);
            Canvas.SetLeft(el, pt.X - dragOffset.X);
            Canvas.SetTop(el, pt.Y - dragOffset.Y);
        });
        mouseUp = new MouseButtonEventHandler((_, __) => {
            el.ReleaseMouseCapture();
            el.MouseMove -= mouseMove;
            el.MouseUp -= mouseUp;
        });
        el.MouseMove += mouseMove;
        el.MouseLeftButtonUp += mouseUp;
    }
}

利用するときは、次のように sender と MouseButtonEventArgs 引数をそのまま渡します。MouseButtonEventArgs のほうは、マウスの相対位置を調べるのに使います。

 
// マウスダウンのイベント処理
card.MouseLeftButtonDown += (sender, e) =>
{
    this.DragMove(sender, e);
};

 

これを動かすと、こんな感じにドラッグができるようになります。

image

カテゴリー: C#, WPF | WPF でコントロールをドラッグする はコメントを受け付けていません

Windows IoT Core と MVVM の関係

先月の .NETラボで Windows IoT Core のデモをやりましたが、そこで使ったソースコードを公開します。

moonmile/WinIoT
https://github.com/moonmile/WinIoT/tree/master/HelloSample

HelloServo のサンプルは Raspberry Pi では動作しない(他では動作している?)のでなんとも言えませんが、他のコードはほどよく Hello 的に使えるものです。当日はコードの解説はせず、概要と動作だけだったので、ちょっとコードのほうも解説しておきます。

環境を整える

木澤さんの I/O ネタにも突っ込みを入れたのですが、現在 Winodws IoT の開発環境づくりは結構な困難を伴います。有償(とされる)Enterprize 版の場合もこのような状況なのかはわかりませんが、少なくとも開発者が自由に使える無償版は、現状こんな感じです。

  • 最新の Windows IoT Core 10.0.10585 を micro SD カードに焼きこんだ時は、Windows 10.0.10585 + Visual Studio 2015 Update 1 の環境が必要になる。
  • 最新の Windows IoT Core 10.0.10585 には、リモート環境が含まれておらず、直前のバージョンから RDBG フォルダをコピーする必要がある。

Windows IoT Core の最新バージョンは Get Started with Windows IoT から Windows IoT Core Dashboard を使ってインストールします。最初の頃はファイルをダウンロードしてコマンドラインからインストール、というややこしい手順だったのですが、最近のものはえらい変わりました。ついでに言うと、先日の発表をした頃よりも変わっていて、自動的に micro SD カードに焼きこむファイルをダウンロードするようです。
image

ちょっと前の従来のものは Download Windows 10 IoT Core からダウンロードができます。

トップページのドキュメント自体が Windows 10 (version 10.0.10240) or better. となっているので、Windows 10 の細かいバージョンが怪しいのですが、デバッグの関係上 Visual Studio で作成するユニバーサルアプリのバージョンと Raspberry Pi 上で動く Windows IoT Core のバージョンは揃えないといけません。

Visual Studio 2015 から Raspberry Pi へプログラムを送り込むにはリモートデバッグが便利なのですが、何故か10585 版の環境には肝心なリモートデバッグの環境が含まれていません。入れ忘れたのか、有償版には入っていて無償版にはないのか、は定かではありませんが 10585 版はそのままではリモートデバッグができません。このあたりは Windows IoT Core ver.10.0.10586 のデバッグを有効にする | Moonmile Solutions Blog を参考にして、RDBG フォルダをコピーしてください。直前のバージョンの RDBG フォルダは http://1drv.ms/1QqAKz4 にあります。

時計を動かす

Windows IoT Core はユニバーサルアプリ(UWP)で作りますが、これの最大のメリットはユーザーインターフェースのテストを普通の PC で行えることです。

HelloSample プロジェクトは、画面に時刻を表示するアプリですが特に Raspberry Pi の GPIO などをアクセスしている訳ではないので、PC 上で動きます。もちろん、Windows Phone 上でも動きます。

image

また、画面は XAML で作成するので MVVM パターンを使うことができます。HelloSample プロジェクトでは、時計の表示部分を1秒ごとに更新していますが、これは TextBlock を直接更新しているわけではなくて、わざわざ Binding を使っています。この程度だとバインディングを使わなくていいのですが、まあ、先の Xamarin + MVVM の流れでこうしています。

 
public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        this.Loaded += (s, e) =>
        {
            this.DataContext = _vm = new ViewModel(this.Dispatcher);
            _vm.TimeStart();
        };
        this.Unloaded += (s, e) => { _vm.TimeStop(); };
    }
    ViewModel _vm;
}

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    Windows.UI.Core.CoreDispatcher _disp;

    public ViewModel(Windows.UI.Core.CoreDispatcher disp )
    {
        _disp = disp;
    }

    private DateTime _time = DateTime.Now;
    public DateTime Time
    {
        get { return _time; }
        set { this.SetProperty(ref _time, value); }
    }

    Task _task;
    bool _loop = false;
    public void TimeStart()
    {
        _loop = true;
        _task = new Task(async () => {
            while( _loop )
            {
                await _disp.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    this.Time = DateTime.Now;
                });
                await Task.Delay(1000);
            }
        });
        _task.Start();
    }
    public void TimeStop()
    {
        _loop = false;
    }


    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
    {
        if (object.Equals(storage, value)) return false;

        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
        {
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

タイマーは Task クラスで別スレッドにしているので、RunAsync メソッドを使ってスレッド越えをして UI にアクセスする必要があります。このあたり、プロパティアクセスと UI アクセスが混在している VM の弱点でもありますが、逆に言えば、Model にスレッド絡みをいれてしまうと View の結合が強くなってしまうので本末転倒、そうなると ViewModel に押し込むのがベターか、といったところです。

Lチカとタクトスイッチ

HelloLED では、GPIO で Lチカ をするのと、タクトスイッチという物理ボタンをブレッドボードに乗せてスイッチに反応するパターンです。いわば、アウトプットが Lチカで、インプットがタクトスイッチです。

image

アウトプットのほうは、LED だけでなく、液晶ディスプレイとか、モーターへの出力とか、出力全般を扱うための基礎になります。インプットは、温度センサーや、マウスやタッチパネル、角速度センサーの検出などの基礎です。インプットは、OS の割り込みのようなものですが、Windows IoT Core では適当なイベントにして戻してくれます。このイベントの内部動作は、後々詳しく説明したいと思うのですが(結局のところ Windows のイベント駆動と同じなので)、ここでは「便利に」イベントとして返してくれるところに注目してください。ちなみに、Arduino IDE でタクトスイッチを扱うときは意外と面倒です。

public MainPage()
{
    this.InitializeComponent();

    var gpio = GpioController.GetDefault();
    this.ledPin = gpio.OpenPin(LED_PIN);
    this.buttonPin = gpio.OpenPin(BUTTON_PIN);

    ledPin.SetDriveMode(GpioPinDriveMode.Output);
    buttonPin.SetDriveMode(GpioPinDriveMode.InputPullUp);

    ledPin.Write(GpioPinValue.Low);
    _isLED = false;

    buttonPin.ValueChanged += ButtonPin_ValueChanged;

}

private async void  ButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
    await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => {
        // if (buttonPin.Read() == GpioPinValue.High )
        if ( args.Edge == GpioPinEdge.FallingEdge)
        {
            ellipse1.Fill = new SolidColorBrush(Colors.Red);
        }
        else
        {
            ellipse1.Fill = new SolidColorBrush(Colors.Gray);
        }
    });
}

タクトスイッチのボタンイベントは、ValueChanged として扱います。これは Read だけで読み取ると、押している瞬間は電気信号として +/- をうろうろすることがあるからです。タクトスイッチとはいえ、電留が流れる部分はアナログ的な電気接点ですから、物理的に接点がちょっと離れたり押されたりするわけです。ですが、ソフトウェアのほうでは 0/1 のデジタルで判断したいので、途中の揺れの部分は必要ありません。むしろ、0/1 のあたりをうろうろしているのは邪魔ですよね。というわけで、ValueChanged イベントは、一定ので電圧以下から以上になるタイミング、あるいは一定の電圧以上から以下になるタイミングでイベントを発生しています…ってはずなのですが、内部はあとで調べてみます。少なくとも、微妙な接点によるうろいろしたところを削って、0/1 で反応させることができます。

また、一見、UI のボタンイベントのように見えますが、ハードウェアからイベントなのでスレッド越えのときと同じように Dispatcher.RunAsync を使う必要があります。このあたりのスレッドを意識しなければいけないのは、Windows IoT Core の難しいところなのか、不備なのかは謎です…が、ワタクシ的には「不備」ですね。もうちょっと MVVM パターンも含めてスレッド越えを意識しないようなコードにしたいところです。

モーターを動かす

本当はサーボモーターの制御を試したかったのですが、Windows.Devices.Pwm の中身がからっぽっぽいので、急遽ブラシモータに変更しました。Windows.Devices.Pwm のほうはいずれ中身を作っていきたいと思います。

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        this.Loaded += MainPage_Loaded;
        this.Unloaded += (s, e) => _motor.Stop();
    }

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        var gpio = GpioController.GetDefault();
        _motor = new Motor(gpio.OpenPin(22), gpio.OpenPin(27));
    }

    Motor _motor;

    private void clickFront(object sender, RoutedEventArgs e)
    {
        _motor.GoFront();
    }
    private void clickBack(object sender, RoutedEventArgs e)
    {
        _motor.GoBack();
    }
    private void clickStop(object sender, RoutedEventArgs e)
    {
        _motor.Stop();
    }
}

public class Motor
{
    GpioPin _front, _back;
    public Motor( GpioPin front, GpioPin back )
    {
        _front = front;
        _back = back;

        _front.SetDriveMode(GpioPinDriveMode.Output);
        _front.Write(GpioPinValue.Low);
        _back.SetDriveMode(GpioPinDriveMode.Output);
        _back.Write(GpioPinValue.Low);
    }

    public void GoFront()
    {
        _front.Write(GpioPinValue.High);
        _back.Write(GpioPinValue.Low);
    }
    public void GoBack()
    {
        _front.Write(GpioPinValue.Low);
        _back.Write(GpioPinValue.High);
    }
    public void Stop()
    {
        _front.Write(GpioPinValue.Low);
        _back.Write(GpioPinValue.Low);
    }
}

モーターを前後に動かすためには、+ と – を交換すればよいだけです。ただし、ふつうのモーターを動かすためにはある程度の電流が必要になるので、Raspberry Pi の GPIO に直接さして動かすわけにはいきません。別途モータードライバが必要になります。ちなみに、小さなモーター(携帯用の振動モーターなど)は直接さしても動きます。

+/- を逆転させるために、2本のGPIOを使います。それぞれの GPIO に対して HIGH/LOW を逆転させてもよいのですが、自前で Motor クラスを作成しています。この Motor クラスを使って制御ができるのがオブジェクト指向のよいところですよね。ここにハードウェア制御だけでなく、液晶ディスプレイに状態をあらわすような VM を追加していけば、うまく V-VM-M が分離できるてシミュレータで動かしたり、インターネット経由で動かしたりというアスペクト指向的な実装も可能になります。

カテゴリー: C#, RaspberryPi | Windows IoT Core と MVVM の関係 はコメントを受け付けていません

ソフトウェア開発者がハードウェア/IoTをやる意味

12月の .NET ラボ勉強会では「これから始める Windows IoT Core」ということで発表してきました。

[slideshare id=56308093&doc=windowsiotcore-151220031915]

その中で、さらっと流していきましたが「ソフトウェア開発者がIoTをやる意味」のところをもう少し掘り下げて解説しておきます。

垣根を超えるために

私がプログラミングを始めたのは30年以上前ですが、既にソフトウェア開発というジャンルは確立されていました。まだ N88Basic の頃だったり、MS-DOS や DR-DOS だったり、したものの直接「ハードウェア」に触るためには、ハードウェアにアクセスするための馬鹿高いインターフェースボードが必要でした。逆に言えば、ハードウェアとソフトウェアの垣根があって、その馬鹿高いインターフェースを買わない限り直接ハードを触ることはなかったのです。当然、一方では組込みシステムの開発者(当時はアセンブラ)が居たわけで、基板設計とあわせてハードの I/O を地道にたたくハードなソフトウェア屋さんもいらっしゃったわけです。

ともあれ、大学の頃は主に炉心のシミュレーションをやっていたので、ほとんどハードウェアに触ることがありませんでした。学生実験で計測器で測定するぐらいですね。おそらく工学系でも機械工学の方は、もっとハードウェア寄りのことを知っていると思います。

コンピュータ上で何でもできる。インターネットや仮想空間も含めて、コンピュータ内のソフトウェアの世界でかなりのことができます。昨今の人工知能や機械学習、VR なども含めればソフトウェアの世界で突っ切ることも可能な時代になってきています。

逆に言えば、「ハードウェアの世界」と「ソフトウェアの世界」という形で、垣根が現前に存在します。子供の科学などで、少しでも電子回路を組んだことがあれば、半田付けとかコンデンサの使い方とか計測誤差とか、そういう手を使ってアナログ的なところをうまく補完していく方法もあることが分かったのでしょうが、こう 30 年間もソフトウェア内だけでやっていると、ここのところがうまくわからなかったのですよ。

ハードウェアは誤差を許容する世界

ソフトウェアも小数点以下は正確に表せないので、誤差を許容するように作りますが。ハードウェア≒現実な世界では、誤差は範囲を以て対応させます。抵抗値にしても、教科書には 330Ω と書かれていても、だいたい 1kΩ ~ 100Ω ぐらいのを持ってきても大丈夫です。更に半田や銅線の内部抵抗があるので、ぴっちりと計算で決まるわけではありません。抵抗値では2,3倍違ってもだいたい動きます。また、有効桁数は1桁か2桁ぐらいしかありません。ソフトウェアの計算では有効桁数は8桁ぐらい簡単に取れるのですが(doubleの間ならば)のですが、そのあたりもハードウェアとソフトウェアの世界では値の扱いが異なります。

このあたり、即値でも幅があるし、合計値にも幅があります。しかし、RC回路を組んで振動させると分かるのですが、Δt の要素が入ってきて時間軸のある物理現象がかかわってくると、理想状態(定常状態)とは違ったものを見えてきます。また、細かいこところでは誤差が入っていても、全体の値は目の前にある物理現象が絶対なのですがから、合計値が計算で違っていたとしても「誤差」として考えなければいけません。これは、設計をいくらうまくやってもコードの動きが絶対だ、というところと似ています。理論上、あれこれと言い訳をしても、現実で動いているものが絶対的な「現実」ですからね(ああ、量子力学的な説明はまた別なので、これは別途)。

垣根が低くなっている

巷の IoT, Maker Faire などの展覧会に行ってみると、ソフトウェアとは一変した世界があります。到底、ソフトウェア開発者としては到底追いつけないような個人的な出展があって、ちょっと尻込みしてしまうぐらいです。

しかし、一部の高度なロボット技術者の方は別として、ソフトウェア的にはあまり複雑なことをやっている訳ではないことが分かります。データシートというハードウェアの仕様を読み込まないとうまく作れないものがたくさんありますが、ある意味 .NET Framework のような大量なクラスライブラリと同じような形で、ソフトウェア設計の知識を導入すればもう少し扱いやすくなるのではないか?と思われるものもあります。そのあたりは、どちらが主という訳でもなく、相互に乗り入れが可能ではないか、と思っています。

私は Mesh のようなソフトウェアから手軽に扱えるもの、をあえて持っていません。これは、去年の夏から、ロボット戦車みたいなものを作りたかったので、もっとハードウェア寄りな考え方が必要だったので「ソフトウェアから扱う手軽さ」よりも「ハードウェアを扱う手軽さ」が自分にとって必要だったからです。このために、PIC やユニバーサル基板での半田付けなどを試していきました。

そこで、ちょうどよく、ハードウェアの扱いとソフトウェアの扱いとして自分の中で手足になっていきそうなものが、

  • Arduino Nano/Mini
  • Arduino Nano 3.1 (ATmega328P搭載)--販売終了

  • Raspberry Pi 2

商品画像1

  • mbed

です。組み立てのブロック周りとしては LEGO Mindstorms EV3 も良いのですが、いかんせん個人で買うには値段が高いので、あくまで教育用ですね。私の考えている「数を揃える」のが難しいのでちょっと後回しです。

 

これらの 3つは、ある程度値段も安く情報も手に入りやすいものです。通信の有線LAN/WiFi/Bluetooth も扱いやすくなっています。

かつては、PIC とユニバーサル基板で電子工作をしなければいけなかったことを考えると(参考用に、昔の電子工作用の本は数冊買いました)、だんぜん楽です。半田付けをしなくても、ブレッドボードで十分だし、固定したいのであればグルーガンで接着する方法もあります。ブレッドボードも安いものであれば200円前後ですから、試作ならばこれで十分です。かつ、ソフトウェア開発者からハードを触るだけだったら試作で十分です。

Lチカとモーターを回した後に進む場所

最初は、マイコンだけ買って Lチカする訳ですが、それも1時間ほどチカチカさせれば飽きてしまいます(クリスマスツリーとか、LED 制御も色々あるので実は奥が深いのですが)。じゃあ、その後は何か動かすものが必要かな、と思うと機械工学の知識が必要になってきます。アルミ板を曲げたり、切ったり、ドリルで穴をあけたりと専用の工具が必要になってきます。ソフトウェア開発の場合は、パソコンとスマートフォンぐらいで十分だったのですが、色々取り揃えないといけないわけです。

これが垣根になっています。

まあ、昨今の3Dプリンタを買って一気に工作環境を整えてしまうという方法もありますが、初期投資が大変ですよね。それに 3D ソフトウェアを覚えないといけません。

その前にお勧めなのが、既存のおもちゃを作ったり、改造したりすることです。イスペット社のグリッパーアームは5,000円程度と低価格なロボットアームです。関節にはサーボモーターじゃなくて普通のブラシモーターが使われていますが、試しに動かしてみるにはこれで十分でしょう。PC で動かせるバージョンもありますが、あえてコントローラだけ安価なものを買って Arduino などで動かしてみるとよいです。

http://ecx.images-amazon.com/images/I/51NmTEqRgEL.jpg

http://pds.exblog.jp/pds/1/201301/21/06/b0158406_134562.jpg

他にも、ダイソーの100円電車とか、電池で動くおもちゃは大抵改造が可能です。タミヤのギアボックスを買えばプラモデルの戦車を Bluetooth でラジコン化することもできます(Bluetooth には安い HC0-05 のシリアル通信を使えばよいのです)。

そういう安くて簡単なところから入るとよいでしょう。IoT というと、クラウドとセンサーの組み合わせがほとんどで、センサーの類も結構高価なものだったり、サーバーが月額で有料だったりしますが、いえいえ、そういうものを使う必要はありません。センサーの類は中華 Arduino と安価なセンサーの組み合わせで十分だし、サーバーは安価なホスティングサービスを借りて PHP で組む、というパターンでも良いわけです。

その先にあるもの

私のこれからになりますが、ハードウェアの世界を垣間見たら、ソフトウェアの世界に戻ってきましょう。ハードウェアの誤差やΔtの世界を、うまくモデル化してやるのがソフトウェアの役目です。ロボットを扱うための ROS でもよいし、組込みの RTOS を使ってもいいし、スマートフォンやタブレットからうまく制御できる仕組みを使ってもよいでしょう。カメラ機能と画像認識、各種センサーを組み合わせて人工知能的に動く iRobot のような制御も自作できると思います。これを数台作って相互通信させるところが目標です。

ハードウエア屋さんの良いところを持ってきて、うまくソフトウェアでモデル化して、それでハードウェアをうまく制御できるのが目標ですよね。来年こそ暇を見つけて蓮根に行きます。

カテゴリー: 開発 | ソフトウェア開発者がハードウェア/IoTをやる意味 はコメントを受け付けていません

大規模プロジェクトで MVVM を導入するときの注意

先日の JXUG の MVVM の補足 を書いたので、どうせだからもうちょっと突っ込んだ話題を残しておきます。Xamarin.Forms などを使う場合は、そんなに多くの人は関わらないでしょう。勉強会でもちらっと話しましたが、ひとりで MVVM すべてを作ったり、数名で MVVM を分け合ったりするのがほとんです。

せっかく V-VM-M と分かれているのだから、ある程度規模が大きくなれば分担しないといけません。最初に2つのパターンが考えられて、

  • 縦割りにして、画面ごとに MVVM を一貫して作る。
  • 横割りにして、V-VM-M を別々の人に割り当てる。

ことが考えられます。理想的には View をデザイナが受け持ち、VM をプログラマが、M をデータベーススペシャリストが担当するのが良いのですが、人数的な問題や規模的な問題、開発者のスキルのばらつき、遠地なのか近地なのか、同じ会社なのか別会社なのか、という様々な「政治的な」問題が絡み合って、理想的にはできません。となれば、プロジェクトマネジメントも含めて、何らかの妥協点を見つけて、ソフトウェア開発を進めていくことになります。まあ、それこそが「マネジメント」なのですが。

20160114_02

かれこれ5年前の話から

NDA 的には5年経っているので話しても大丈夫なのですが、まあ細かい話は無しにして、Silverlight + MVVM モデルで DataGrid をバインドの落とし穴 をやっていたころです。Silverlight が流行っていて、ActiveX コントロールではなくてブラウザ上に高機能なインターフェースを乗せようとしていた時期ですね。折しも、ちょうど MVVM が発表されたあたりで初期のブームだったのです。

経理系のシステムで、結構規模が大きくかかわっていたのはマネージャ等を除いても30名ぐらいいます。銀行系の大規模とは違うのですが、最近のスマートフォンの開発や、WEB開発に比べれば十分に大きく、そして混乱しやすい人数でもあります。そこに新技術としての Sliverlight と MVVM を同時にぶち込んだところに悲劇があったわけですが…まあ、仕事なのであれこれのトラブルがあってもそれなりに収めるわけなんですがね。

コンサルティング的な立場で入ったはずなのですが、複数の画面を担当することになりました。これもよくありがちで、技術的な点よりも人手でなんとかしようとする人海戦術パターンです。初期の頃から入っていれば設計的になんとかできたのでしょうが、結果的にみれば火消的な時期に入ってしまったため既存の設計に引きずられています。

MVVM パターンのライブラリが別会社だった

ある程度の画面数を超えると、人海戦術的にモノ作りをするパターンにおちいります。まあ、その手のプログラミング自体、労働集約的なところから抜け出せないので、仕方がないといえば仕方ないところです。いまだと、画面数をぐっと減らしてしまうか、共通化することで手数を減らしますよね。あと画面生成を自動化するとか。

そういうものはなくて(今でもそういうものがない会社はたくさんあります)、ひたすら手作業であ画面を作っていきます。Silverlight で画面を作るのですが、画面の部品は別の会社が作っていました。どうやら、ほかのプロジェクトで使ったらしい Sliverlight の画面部品を今回のプロジェクトに流用しようとしたらしく、そこに MVVM のバインドを入れ込んだという訳です。

いやいや、この別会社の部品がひどくて、そもそも Silverlight の Binding は元から揃っているにも関わらず、さらにそれをラップするような同じコントロールをひとつひとつ提供するという技をやってきました。当時、私も MVVM をやり始めたばかりなので、なんでこんな変な形になっているのかわからなかったのですが、あとから考えれば根本的に設計を間違えていたのでしょう。なんだかよくわからない Binding を付加したなんだかよくわからないラップされただけのコントロールで画面を組み合わせると、なんだかよくわからない形で画面部品にアクセスしないといけなくなります。だから、余計混乱するし、別会社のコントロールに不具合が発生するたびにあれこれと時間をロスしていました。

まあ、この手の共通部品は社内で作るのがベストですよね。あるいは、綿密なサンプルと設計をしたうえで社内に持ち込むのがベターです。

画面ごとに縦割りの MVVM は効率が悪い

画面部品を作っている別会社の MVVM の知識も足りないし、まして使う側の社内のメンバの MVVM の知識も足りません。そういう足りない上に、従来の画面ごとに担当を分けていったので更に混乱しています。よくわからない MVVM の知識は、それぞれ V-VM-M に分けて考えてインターフェースをきちっと作ると作業分担をして効率よく進めることができるのですが、従来通り縦割りに MVVM を作りました。このため V-VM-M のすべての知識がないと画面がうまく作れないわけです。

MVVM の社内講習会などはありませんでした。理由としては、MVVM が「解らなくても使いやすい」とされていた画面の部品を別会社から購入したことにより、それをクリアする話だったのですが、実際には VM の Binding が必須なコードになっているので、全然ラップされていません。というか、MVVM の構造を理解せずに画面を作るのであれば、MVVM パターン自体を導入する必要はありませんよね。本末転倒です。

  • MVVM を完全にラップする → 従来のイベントベースでコーディングできる
  • MVVM を使いやすくラップする → View の Binding 記述などを減らす

のどちらかの方法に進むべきだったのですが、結果的にどちらの悪いところを取ってしまって、MVVM をラップしているけども Binding がむき出しなコントロールを使う羽目に陥りました。そんなややこしいことをするならば、そのまま標準コントロールを使ったほうが楽です。

なので、MVVM をある程度の多人数で作る場合には、

  • 全員が MVVM の構造を理解して、画面ごとに縦割りで作る
  • MVVM の概要だけ理解して、インターフェースを守って V-VM-M で担当を分ける

というパターンになります。一見、V-VM-M で分けたほうが効率が良いように見えますが、現実は異なります。というのも、構造的に V-VM-M と分けえるとそれぞれの層に特化することはできるのですが、画面ごとに異なるドメインの知識が必要になってしまいます。このため、プロジェクト全体の業務知識がないと、VM が作れないというところに陥ります。

5名ぐらいで分担している場合は、プロジェクト全体の業務知識はさほどではないのですが、30名レベルになると結構なボリュームになります。分業するならば、業務知識はそれぞれで分担できるほうが効率がよいので、MVVM パターンを画面単位で作るのが良いのですが、これ先に言った通りに MVVM パターン全体の知識が必要となってしまいます。そういうジレンマが MVVM パターンにはあるということを理解して MVVM パターンを導入してください。

テスト可能な VM, M を作る

xUnit を使ってテスト可能なコードを書くことにより、コードの品質は上がるしリファクタリングがやりやすくなります。これも数名で書いている分にはちょこちょこと変えられる程度なのでしょうが、30名レベルになるとかなり混雑してきます。

先のプロジェクトでは Model の部分は WCF を通じて SQL Server にアクセスしていました。このため DBA チームがあって、SQL Server の構造にあった Model を返すのですが、これが曲者です。当然、View は Model に合うような構造になっていないので、VM あたりで構造を変えないといけません。いわゆるインピーダンスミスマッチというやつですね。

MVVM パターンのひとつの回避策としては、画面に即した Model をもうひとつ作り、データベースの Model をうまく組み合わせることです。多少冗長にはなりますが、VM が比較的に簡単になるので、これは有効な手段です。ただし、実装中にコロコロとデータベースのテーブル構造が変わるたびに画面用の Model も変更しないといけないためインテグレーション的に不利なところがあります。

もうひとつのパターンは、VM 内で変換する方法です。もとも View と Model のつなぎを担当する VM なのですから、業務ロジック(画面の操作ロジック)も含めて ViewModel をコーディングしていくことも可能です。ただし、これもデータベースのテーブル構造が変わると VM に手を入れなければならず、さらに VM 自体が画面と密着しているために、テーブル構造の変化が VM –> View にまで伝播してしまうことが多く再テストが大変です。

このようなことから、Model の自動テストだけでなく、ViewModel の自動テストも行えると、上記の2つのパターンの複雑さが多少改善されます。Model だけのテストだと VM の再テストが画面操作からしかできなくなって膨大な打鍵試験が発生します。この打鍵試験を減らすことが xUnit の目的なので、このままは本末転倒です。そこで、自動テストがしやすいように VM を作っているスタイルが必須になります。

まあ、担当した画面だけは自動テストっぽい VM の作りをしていたのですが、もともとそういう文化がない会社だったので、そのあたりはダメダメでした。Excel な設計書をきっちりと書いて、なぜか別の人がコーディングして、なぜか別な人がテストするという従来の方法でやっていたので炎上は必須です、といいますか、炎上していました。設計した会社とコーディングをしていた会社がかなり険悪な状態になっていたんですよね。設計する会社が親会社で、コーディングが子会社だったので猶さらその無茶ぶりな圧力は異常なところがありました。別会社ではありますがパワハラに近いものです。

で、オチはどうなったか?

この話、実はオチがあります。

設計するのが親会社、下請けが子会社だったのですが、ある日突然、子会社が親会社を買収することになってですね、プロジェクト間の立場が一転したのです。へこへことコーディングしていた会社が一夜明けて、設計してた会社の上に立つことになって、進捗会議なども一遍しました。なんだかなー、笑えます。会議のパワハラも、いままでのストレスも相まって、えらいことになってました。

で、私はどうなったかというと、元親会社の契約更新ができなくなって2か月程度でお払い箱ですよ。それ以来、その手の技術支援っぽい派遣には手を付けないことにしています スマイル 安い派遣で働くよりも、うちで地道に勉強していたほうが将来的に有利ってことですね。

カテゴリー: 開発 | 大規模プロジェクトで MVVM を導入するときの注意 はコメントを受け付けていません

JXUG で話した MVVM の活用の解説を

JXUGC #9 Xamarin.Forms Mvvm 実装方法 Teachathon – connpass
http://jxug.connpass.com/event/22840/

にて、田淵さんコードに「マサカリ」を投げまくって来ました。実は、ピアレビューという手法があって、できるだけきめ細かくコードをレビューしていくという手法があります。本来ならばインスペクションの形式を使うのですが、「人を攻撃する」のではなくて、コードのみをバシバシと叩いて向上させる方法ですね。コードを個人の成果物ではなくて、共同の成果物として仕立てあげることが最終目標です。

ひとまず、MVVM とは何ぞ?なり、Xamarin.Forms とは?という話はすっ飛ばして直接コードから入ったのは良かったと思います。ペアプロとか、M-VM-M の分業体制なんかはあんな感じで進めるとうまくいくでしょう。

私の発表したサンプルコードは以下にあるので、ざっと解説をつけておきます。

http://github.com/moonmile/JXUG

プロジェクトの構造

コードにはちょっとだけでも単体テスト用のコードを付けるようにしています。私が TDD を使う目的としては、テストの効率化の他に、「オブジェクト/ライブラリの使い方」を示すためにも作っています。そのクラスをどのように使うのか、かつ、クラスを使う時にどのようなインターフェースにしたら使いづらくはなならないか、の検討用に単体テストコードを使っています。
また、実際にクラスを動かすときのサンプルとして、いきなり Xamarin のコードでは大変(実機でしか動作しないパターンなど)なので、最初に WPF や Windows フォームを使った小さなサンプルを用意します。これでいくつか実験した後で、実際のモバイルコードに直す、あるいは組み合わせていいきます。こうすると、プロジェクトの最後のほうになって実機コードが複雑になっても、実験アプリによって少しテストをしながら、という手法が取れます。

  • /Test/MStopWatch.Test — 単体テストコード
  • /Test/MStopWatch.WPF — WPF による実験コード
  • /ViewModel/MStopWatch.VM — WPF/Xamarin.Forms の共通の VM
  • /ViewModel/MStopWatchFsharp.VM — 試しに F# で書き直した VM
  • MStopWatch — Xamarin.Froms の共通 PCL
  • MStopWatch.Droid — Android 用
  • MStopWatch.iOS — iOS 用
  • MStopWatch.WinPhone — Windows Phone 用

最後の、Droid/iOS/WinPhone は Xamarin.Forms を使うと手を入れずに済みます。シミュレータの場合は、Windows Phone が Hyper-V を使って一番早く動きます。

これで、MVVM パターンを形作るわけですが、いくつかの仕掛けが入っています。ストップウオッチのタイマの場所を何処に置くのかによって VM の書き方が変わります。勉強会のときにも強調しましたが、特に正解があるわけではありません。とあるコードやプロジェクトによって、「そこが最適であろう」という推測はできますが、実際に置かなければならないということではありません。ふさわしい場所がある、というだけです。

ストップウォッチタイマを ViewModel に置く

StopWatchVM.cs

public class StopWatchVM : BindableBase
{
...
    public void Start()
    {
        _now = DateTime.Now;
        _startTime = _now;
        _items.Clear();
        _loop = true;
        Mode = 1;
        _task = new Task(async () => {
            while (_loop)
            {
                await Task.Delay(100);
                _now = DateTime.Now;
                if (OnTimer != null)
                    OnTimer();
                else
                {
                    this.NowSpan = _now - _startTime;   // 画面を更新
                }
            }
        });
        _task.Start();
    }

タイマは、100 msec で動かしています。非常に遅いように見えますが、画面に表示させるときは 100 msec で十分で、Lap ボタンを押したときには改めて DateTime.Now から取っているので正確な時刻が取得できます。このあたりが、表示用の VM とデータとしての Model の違いになりますね。

ここではスレッド越えを許すためにコールバック関数 OnTimer を定義させていますが、Android でもコールバック関数は必要ではありませんでした。NowSpan プロパティと更新すると、INotifyPropertyChanged で画面に通知されます。

ストップウォッチタイマを Model に置く

StopWatchVM2.cs

public class StopWatchModel
{
...    public DateTime StartTime { get; set; }
    public void Start()
    {
        StartTime = Now = DateTime.Now;
        Items.Clear();
        _loop = true;
        _task = new Task(async () => {
            while (_loop)
            {
                await Task.Delay(100);
                Now = DateTime.Now;
                if (OnTimer != null)
                    OnTimer();
            }
        });
        _task.Start();
    }

Model のほうにタイマを用意した例です。この意図としては、計測機器の割り込みイベントや、外部から定期的に割り込みが入るようなパターンを想定しています。この場合、イベントが Model -> VM -> View へと数珠つなぎになるので、MVVM のまま使うよりも Rx のような方法を取ったほうが楽です。

ストップウォッチタイマを View に置く

ちょっとサンプルには書き忘れましたが、View 自身にタイマーを持たせることもできます。ストップウォッチの場合には、

  • 定期的に人の目に触れる View の時刻を切り替える
  • 内部で持つ時刻データを正確に持つ

の2つに分離できることがわかります。このため、内部データは Lap ボタンを押したタイミングで DateTime.Now を取得すればよいわけで、何も定期的に内部データを更新する必要はありません。なので、画面の表示させる View だけタイマー更新を使うという方法が考えられます。これはちょうどゲームの画面更新(スプライト機能など)を行う場合に、描画はキャラの更新タイミングに合わせるのではなくて、垂直同期にあわせるという方法ですね。たいていのゲームは 50fps 程度あれば十分なので、20 msec 程度で更新させれば十分です。
なので、Lap タイムは msec 単位で持っていても、画面更新は 20 msec 単位程度で十分ということです。

View 単体の更新では、WPF の場合は Storyboard の更新タイミングを使う方法もあります。これらは機会を見てサンプルに付け加えていきましょう。

VM を F# で書く

VM や Model に単体テストが入れば開発効率は非常に上がります。画面であれこれテストしたり、インタプリタで一時的なテストを繰り返すよりも、自動テストができる作り方にするのです。

F# で書いた VM の全文が次になります。これらは、単体テスト MStopWatch.Test でテストが可能です。

type StopWatchVM() = 
    let ev = new Event<_,_>()
    let mutable _mode = 0
    let mutable _startTime = DateTime()
    let mutable _now = DateTime()
    let mutable _nowSpan = TimeSpan()
    let mutable _items = new ObservableCollection<LapTime>()
    let mutable _loop = false
    let mutable _task:Task = null

    member this.StartButtonText 
        with get() = 
            match _mode with
            | 0 -> &quot;Start&quot;
            | 1 -> &quot;Stop&quot;
            | 2 -> &quot;Restart&quot;
            | _ -> &quot;&quot;
    member this.Mode 
        with get() = _mode
        and set(value) = 
            if ( _mode <> value ) then
                _mode <- value
                ev.Trigger(this, PropertyChangedEventArgs(&quot;StartButtonText&quot;))
                ev.Trigger(this, PropertyChangedEventArgs(&quot;Mode&quot;))
    member this.Items 
        with get() = _items
        and set(value) = 
            _items <- value
            ev.Trigger(this, PropertyChangedEventArgs(&quot;Items&quot;))
    member this.NowSpan
        with get() = _nowSpan
        and set(value) = 
            _nowSpan <- value
            ev.Trigger(this, PropertyChangedEventArgs(&quot;NowSpan&quot;))


    member this.ClickStart() =
        match _mode with
        | 0 -> this.Start()
        | 1 -> this.Stop()
        | 2 -> this.Reset()
        | _ -> ()
    member this.ClickLap() = this.Lap()

    member this.Start() = 
        _now <- DateTime.Now
        _startTime <- _now
        _items.Clear()
        _loop <- true
        this.Mode <- 1
        _task <- new Task( fun () ->
                while ( _loop ) do
                    ( Async.Sleep(100) |> Async.StartAsTask ).Wait()
                    _now <- DateTime.Now
                    this.NowSpan <- _now - _startTime
            )
        _task.Start()

    member this.Stop() = 
        _now <- DateTime.Now
        this.Items.Add( LapTime( this.Items.Count+1, _now, _now-_startTime))
        _loop <- false
        this.Mode <- 2

    member this.Reset() = 
        _now <- DateTime.Now
        _startTime <- _now
        this.NowSpan <- TimeSpan(0,0,0)
        this.Items.Clear()
        this.Mode <- 0

    member this.Lap() =
        _now <- DateTime.Now
        this.Items.Add( LapTime( this.Items.Count+1, _now, _now-_startTime))

    interface INotifyPropertyChanged with
        [<CLIEvent>]
        member this.PropertyChanged = ev.Publish

VM を単体テストする

Model を自動テスト化すると頑丈なコードが書けます。さらに画面に近い VM をテストするコードを書くことも可能です。

/// <summary>
/// ラップを実行する
/// </summary>
 [TestMethod]
public void TestOneLap()
{
    var vm = new StopWatchVM();
    vm.Start();
    Assert.AreEqual(&quot;Stop&quot;, vm.StartButtonText);
    System.Threading.Thread.Sleep(1000);

    vm.Lap();
    Assert.AreEqual(&quot;Stop&quot;, vm.StartButtonText);
    // ひとつだけ追加されている
    Assert.AreEqual(1, vm.Items.Count);

    System.Threading.Thread.Sleep(1000);
    vm.Stop();
    Assert.AreEqual(&quot;Restart&quot;, vm.StartButtonText);
}

VM の構造を、UI/View から触るメソッドにうまく対応させてやれば、このようにユーザーのアクションをエミュレートできます。最近では Test Cloud のように実機/エミュレータを使って UI ベースのテストをすることも可能です。全ての UI イベントをエミュレートする必要はありませんが、おまかな動作がテストできると、実機を使った打鍵チェックを減らすことができます。

カスタムコントロールの利用

MVVM パターンを使うと、何にでも Binding を使って表そうとしてしまいますが、その分 View が冗長になってしまいます。勉強会でも話しましたが、本来は XAML をデザイナが記述し、コードビハイドをプログラマが記述するという分業ができる、というのが当時の売りでした。ですが、最初の頃に XAML をデザインするにはすべてをコードでみるしかないという状態に陥っていたため、XAML 自体もプログラマが書くようなスタイルになってしまいました。

Xamarin.Forms で、Button クラスを継承して Mode プロパティで表示が変えられるようなカスタムコーントロールを作ります。こうすることで、コントロール自体をより高機能な部品にすることができます。ここではボタンの表示を Mode プロパティで切り替えているだけですが、画像ファイルを張り付けたり、アニメーションをしたりすることができます。これらの動きを全て XAML で書くような Setter な方法もありますが、カスタムコントロールを作ってしまったほうが XAML の View が複雑にならなくて済みます。

public class CustomButton : Button
{
    public static BindableProperty ModeProperty =
        BindableProperty.Create<CustomButton, int>(
            p => p.Mode,
            0,
            defaultBindingMode: BindingMode.TwoWay,
            propertyChanged: (bindable, oldValue, newValue) =>
            {
                var uc = bindable as CustomButton;
                switch (newValue)
                {
                    case 0: uc.Text = &quot;開始&quot;; break;
                    case 1: uc.Text = &quot;停止&quot;; break;
                    case 2: uc.Text = &quot;リセット&quot;; break;
                }
                ((CustomButton)bindable).Mode = newValue;
            });
    public int Mode
    {
        get { return (int)GetValue(ModeProperty); }
        set { SetValue(ModeProperty, value); }
    }
}

WPF の場合は、DependencyProperty を使うため、若干 Xamarin.Forms と書き方が違うので注意が必要です。

class CustomButton : Button 
{
    /// <summary>
    /// モードを指定
    /// </summary>
    public static readonly DependencyProperty ModeProperty =
        DependencyProperty.Register(
            &quot;Mode&quot;,     // プロパティ名
            typeof(int),    // プロパティの型
            typeof(CustomButton),  // コントロールの型
            new FrameworkPropertyMetadata(   // メタデータ                   
                0,
                new PropertyChangedCallback((o, e) =>
                {
                    var uc = o as CustomButton;
                    if (uc != null)
                    {
                        int v = (int)e.NewValue;
                        switch ( v )
                        {
                            case 0: uc.Content = &quot;開始&quot;; break;
                            case 1: uc.Content = &quot;停止&quot;; break;
                            case 2: uc.Content = &quot;リセット&quot;; break;
                        }
                    }
                })));
    // 依存プロパティのラッパー
    public int Mode
    {
        get { return (int)GetValue(ModeProperty); }
        set { SetValue(ModeProperty, value); }
    }
}

ひとつの VM に複数の View を割り当てる

本来ならば、View と VM はきれいに分離するはずなので、動的に View をロードすることも可能です。インターネット経由で View(XAML)をロードすることも可能なのですが、これは結構難しいです。しかし、一定の View のパターンを持っていて、場合によって XAML 全体を切り替えるということができます。
この方法は、権限の違うユーザ(管理ユーザ、一般ユーザー)では画面をダイナミックに切り替える、ということができます。

Xamarin.Forms の MStopWatch プロジェクトには MyPage.xaml と MyPageV.xaml という2つの View があります。VM が同じであっても、インスタンスを生成するときに Page クラスを切り替えることができます。
ちなみに、MyPageV.xaml は、すべて View のコードビハイドにロジックを入れてしまった例です。

こんな感じで、ちょこちょこと業務ノウハウっぽいものも入れてある Xamarin.Forms の MVVM サンプルコードですので、ぜひ活用してください。

カテゴリー: Xamarin | JXUG で話した MVVM の活用の解説を はコメントを受け付けていません

Windows IoT Core ver.10.0.10586 のデバッグを有効にする

土曜日の .NET ラボ勉強会のために、やっとこさ Windows IoT Core の新しいバージョン 10586 を入れて Visual Studio 2015 からデバッグ実行しようと思ったのだけど「できない」。なぜか?と思いつつ探しつつ、結構ややこしい状態になっていたのでメモ書きしておきます。

必要な環境

Windows IoT – Console App Sample
http://ms-iot.github.io/content/en-US/win10/samples/ConsoleApp.htm

を見ると、Windows 10 の 10586 と Visual Studio 20156 Update 1 と Windows IoT Core の 10586 を揃えないといけないようです。結果的には揃えてバージョンが違ったときにはどうなるのかわかりませんが、揃えておきます。

が、揃えたもののデバッグ実行ができません。

新しい環境ではリモートデバッグ(msvsmon.exe)が実行されていない

デバッグメニューの中に「Start」ボタンが増えています。どうやら、普段はリモートデバッグができない環境で、この Start ボタンを押すとリモートデバッグできるようになるらしいのですが…

image

これを押しても、エラーがでます。

Failed to start the Visual Studio Remote Debugger. Make sure the remote debugger is copied to the device using Visual Studio For more information please visit www.windowsondevices.com

image

リンク先を見ても解決しないので、なんだかなー、という感じなのですが、あれこれと調べていくと、そもそもリモートデバッグ環境の /RDBG フォルダがないことが解ります。どうやら、それ以前の 10556 版にはあるのですが、なぜか最新版から落ちている。

解決策は、10556 から RDBG をコピーする

Getting Visual Studio Debugger running on latest build (10586) of Windows 10 IoT – TechNet Articles – United States (English) – TechNet Wiki
http://social.technet.microsoft.com/wiki/contents/articles/32718.getting-visual-studio-debugger-running-on-latest-build-10586-of-windows-10-iot.aspx

あれこれ探した挙句、TechNet に手順が書いてありました。元の 10556 版を入れて、RDBG をローカルにコピーして、最新の 10586 にコピーしなおして、再起動すれば ok。っていう手順なのですが、元のファイルなんかないよー、ってことで、http://1drv.ms/1QqAKz4 に 10556 版の RDBG を置いておきますので、USB メモリにコピーして Windows IoT Core にコピーしてください。直接、SD カードに書き込むと Win IoT から見えないので、RPi に USB メモリを差し込んでコマンドラインでコピーします。

このあと、7. Use the Web UI -> process -> Run command and run the below command:
c:\RDBG\RegisterOneCoreRdbg.cmd と c:\RDBG\StartOneCoreRdbg.cmd を動かします。以前、やったときは「Start」そのままで大丈夫な気がしたのですが、このレジストが必要な模様。

再起動すれば、プロセスに msvsmon.exe が現れて従来通りリモート実行できます。

image

 

MemoryStatus Console Application Sample のサンプルは C++ なのでリモート実行の手順がややこしくなっていますが、ユニバーサルアプリを C# で作れば、「プロジェクト」→「デバッグ」で、対象のリモートコンピュータを指定すれば ok です(デバイスのほうは Windows Phone になるので使いません)。

image

デバッグ実行

こんな風にミニ液晶を使って動かすことができます。

image

土曜日までに、サーボモーターと Lチカを準備する予定。

カテゴリー: Win IoT | 2件のコメント

LEGO Mindstorms EV3+MonoBrick+F# で倒立振子ロボット

F# Advent Calendar 2015 – connpass の 12日目です。

中身的には 倒立振子ロボットを MonoBrick で作成する(F#編) | Moonmile Solutions Blog の延長線上で、何故 F#(関数型言語)でロボット制御をやろうとするのか?ってのをつらつらと書いていきます。

C# 版は、EV3 + MonoBrick + C# なスライドを参照して貰うということで、計算のところを少し突っ込んで行きます。

物理方程式を解く

実は、制御工学を習ったことが無かったので知らなかったのですが、倒立振子ロボットのように「状態の変化に対して少しずつ対応していく」という方式は単純な線形の式で表せます。線形と非線形の違いは、直線近似とそれ以外の複雑な変化を解くという違いにもなりますが、ある意味で物理現象のような「とある時刻 t の状態から、少し異なった t+Δt の状態を予測する」という場合には、線形に近似をしても問題はありません。Δt のサンプリング周期にも依りますが、とある複雑な予測解析を行う場合は、未来の t’ の値を直接計算するよりも Δt の積み重ねのほうが簡単に計算できたりします。

image

これは、よくやる重力の物理方程式で、惑星の動きとかをシミュレーションするときによく使われる方法ですね。太陽と地球の場合は、楕円として計算できるけど、三体問題として太陽・地球・月の場合は複雑に絡み合っているので直接計算はできません。別途 Δt で計算するというアレです。

当然、Δt は近似であるので、その積み重ねによって計算された未来予測値は正しい値とは限らないし、むしろずれています。誤差が累積されますからね。しかし、制御工学のように、その時々の時刻 t’ の値を観測することによって、Δt による線形近似のずれは、その t’ 毎によって補正されるため直近の値のみ正確であればそれでよいのです。このあたりを踏まえると、倒立振子ロボットのように一見、実現不可能ようなもの(私自身が倒立振子ロボットを見たのは、つくば博覧会のときですから30年以上前です。そのときに出品していた、一本足ロボットが現在の iRobo の前身なのですから驚きですね)であっても、意外と簡単な近似だけで行けるものです。

上記の運動方程式をあれこれラグランジュ方程式で解いて、変換すると、

角度θ、φに対するゲインの式

image

予測値に円滑に近づけるための PID 制御

image

の 2つだけで十分なのです。

それぞれの定数(K1, k2 等)を決めるためには複雑な方程式を解かないければいけませんが、最終的に倒立振子ロボットを制御するためのロジックは、上記のような単純な式であらわせます。これが組込みで動くので、Arduino のようなメモリが少ないマイコンでも動くという仕組みです。

科学計算の式をプログラム言語で解く難しさ

ここで注目したのは、最終的な式は簡単に見えるけども、途中の計算は複雑だということです。それぞれの K1 のような定数は、既知の数値のように見えますが、ロボットの重心位置や質量、タイヤの半径や質量、モーターの角速度や逆電流などに関わってくる定数になります。このあたり、科学計算の式をプログラム言語に直すときのむずかしさがあり、その間はギャップがあります。

  • 数式として解いたときの式
  • プログラムコードで実現するコード

の間には結構な隔たりがあります。

image

このような式を、プログラム言語(C言語や、C++、C#,Fortran など)に直していくわけですが、これがなかなか大変です。書き直すときのコツもあるのですが、問題は「式」の表現と「コード」の表現に大きな隔たりがあることです。

仕事上、数式を Fortran、C++ で書き直すことをやっていますが、よく間違えます。ただし、数式として抱えている様々な条件(前提条件なども含む)を写し間違えるあわけですね。その間違い、実際もとの数式に当てはめないとわからなかったり、複数の式が絡み合っているとなかなか見つかりません。

そんな仕事の中でよく使っているのは、F# による検算です。

  1. 数式を書く(与えられる)
  2. F# で書く
  3. C++、Fortran で書き直す。
  4. 3 と 2 の答え合わせを xUnit で行う。

という方法をよくやります。これをやると数式の写し間違えが激減しました。理由は F# が関数型言語で、式の取り扱いが得意だからというのもあるのでしょうが、検算することにより不具合率が減ります。また、この F# で書くコードは、巷の関数型言語特有の複雑さ(モナドとかオプショナルとか)をまったく使わず、ごく普通に式をプログラム言語に直す、というスタイルでしか使っていません。ですが、不具合が激減します。

束縛(バインド)と関数内関数の活用

ここ 2 年間の経験上ではありますが、この不具合率の低さは、

  • 束縛(let)の利用により、できるだけ値を不変にしようとする努力をする
    → 影響範囲が少なくなる
  • その場でしか使わない関数は、関数内関数として定義する。
    → 関数名が短くて済む。影響範囲が狭い。
  • if文、switch/match 文が値を持つ
    → 型システムの利用

なところです。

image

値の束縛は、C言語であれば define 定義すればよいし、C# であれば const にすれば ok です。ですが、F# の場合(関数型言語一般)は、デフォルトが束縛(let)であって、値を変えたいときはわざわざ、mutable にしないといけないという面倒臭さがあります。これが「面倒くさい」ことによって、より値が変化しない束縛(let)のほうにコードが寄ってきて、影響範囲の少ないコードが書きやすくなるという利点が出てきます。

逆に言えば、オブジェクトのプロパティをがりがりと変化させるようなオブジェクト指向とは別の向きを関数型言語は示してくれます。それは相反するものではなくて、その場その場でより使いやすい方向に向ければよいので、オブジェクト指向も関数型言語も相補的な形で使えるでしょう。

そういった場合、倒立振子ロボットのような物理的な制御をする場合には、物理量が不変であるからこそ数式的に関数型言語を使う法が適しているといえます。逆にロボット制御であっても、リモートコントローラーでアクチュエーター(サーボモーターやアームなど)を適切に動かすような主導的な制御はオブジェクト指向/UML を使ったほうがうまく作れるとも言えます。そういうパターンを踏まえて、物理値を逐次観測していてアジャイル的に制御を加えていく制御工学な自立コントロールはなかなか面白い分野になります。

サンプルコード

倒立振子ロボットを F# で書いたコードです。

moonmile/MonoBalancer
https://github.com/moonmile/MonoBalancer

C# と見比べると面白いかも…というか、今は C# から F# に書き直しただけなので、そのうち F# らしいスタイルに直していきましょう。

カテゴリー: F# | LEGO Mindstorms EV3+MonoBrick+F# で倒立振子ロボット はコメントを受け付けていません

Raspberry Pi で Xamarin.Forms を動かそう(Advent編)

この記事は、Xamarin Advent Calendar 2015 の 4日目の記事です。以前、ざっと書き流していたのですが、もうちょっと詳しく書いていきます。

Xamarin が動作するプラットフォームで、相当異色な感じの Android on Raspberry Pi なので、読み物的にお楽しみください。

Raspberry Pi を用意する

そもそも、なぜ Raspberry Pi で Android なんて動かすのか…は、さておき(苦笑)、Raspberry Pi を用意します。Android のインストールに BerryBoot というインストーラー ディストリビューションを使うのですが、バイナリを見る限り最新の RasPi 2 でも、それ以前のものでも大丈夫なようです。

image

動作確認は、Raspberry Pi 2 と5inch ディスプレイで確認しています。Raspberry Pi には HDMI 出力がついているので、普通の HD の液晶ディスプレイでも動きます。が、GPU まわりが遅いらしく 1920×1080 のサイズでは非常に遅くて使い物になりません。この 5inch 液晶ディスプレイだと 800×480 でちょうどいい感じに動きます。ちなみに、あとで設定しますが、config.txt を弄ってディスプレイ解像度を変えた後は、据え置きの液晶ディスプレイでも 800×480 の解像度になるので、800×600 などの適当な荒い解像度にして起動しなおせばよいでしょう。

BerryBoot をダウンロードする

BerryBoot v2.0 – bootloader / universal operating system installer から、RasPi 2 ならば berryboot-20150916-pi2-only.zip を、それ以前の RasPi ならば berryboot-20140814.zip をダウンロードします。

image

最新のファイルは berryboot download | SourceForge.net からダウンロードができます。

micro SD カードに焼きこむ

ダウンロードした zip ファイルを解凍して、micro SD カードに焼きこみます。焼きこむといはいえ、解凍したファイルを micro SD カードにコピーするだけです。micro SD カードは 8GB 程度で十分です。

image

途中で、起動に失敗したり、micro SD カードが見えなくなった&容量が減った場合は、SDFormatter でフォーマットしてください。オプションで、論理サイズ調節を「ON」にしてフォーマットすれば大抵回復します。

image

Android のフォーマットは、Windows から読み込めないので micro SD カードが読み込めなくなった(Windows 標準でフォーマットできなくなった)、あるいは Linux で使っていたら 50MB ぐらいしか容量が無くなった(本来は8GBあるはずなのに)、ときにフォーマットするとよいです。

BerryBoot を起動する

BerryBoot は、最初に起動すると SD カードをフォーマットします。

image

USB キーボードをつなげて、OK を押してください。

RasPi 2 の初期ロットの制限かもしれませんが、HDMI ケーブルをつなげて RasPi に電源を入れるとうまく立ち上がりません。多分、起動時の力不足で立ち上がらなくなってしまうのでしょう。USB キーボード/マウス、有線LAN をつなげっぱなしでも立ち上がらないことが多いので、電源を入れる前は外して、起動しているときに繋げるという技を使っています。

image

こんな感じでフォーマットをします。

Android をインストールする

“Add OS” ボタンで、OS を追加するのですが、OS イメージはインターネットからダウンロードする方式になっているので、有線LAN をつなげておきます。Android は 4.4(KitKat)がダウンロードできます。最新版はないですね…

image

Android 4.4 を選択するとダウンロードが始まります。

image

ダウンロードが終わると、メニューに追加されます。

image

ここでメニューの「Exit」を選択すると、Android が起動されます。

image

いちおう、FullHD 解像度で使えるのですが非常に重いです。これでは実験用には使いづらいので、もうすこし低解像度にします。

config.txt で解像度を調節する

再び、RasPi に電源を入れなおして起動すると、BerryBoot → Android の順に立ち上がります。このときに、BerryBoot メニューで Edit Menu ボタンを押します。このボタンを押さないと、10秒後に自動的に Android が立ち上がるので注意してください。

image

メニューから「>>」に隠れている「Edit config」をクリックして、config.txt を書き換えます。

image

hdmi_group=2
hdmi_mode=1
hdmi_mode=87
hdmi_cvt 800 480 60 6 0 0 0

のように 800×480 で書き換えています。config.txt を書き間違えると画面がでなくなってしまうので、その時は micro SD カードを焼くところから再スタートしてください。

液晶ディスプレイで 800×480 で動かしたときんは、こんな感じになってスムーズに動くようになります。動かすにはマウスが必要ですが。

image

Android on Raspberry Pi を起動する

5 inch ディスプレイをつなげて動かすとこんな感じになります。

image

5 inch 液晶の場合は、電力が十分足りるのかつなぎっぱなしで起動しても大丈夫なのです。Bluetooth は使えないので有線マウスが必須なのが難点ですが。無線マウスは反応がにぶいですが一応動くようです。

adb でデバッグ接続する

やっとこさ、Android の領域に入りました。Android on RasPi の場合、USB デバッグはできるかどうかわからないのですが、既に 5555 ポートが開いていてデバッグモードで動くようになっています。

adb connect <IPアドレス>

でデバッグ接続をします。IP アドレスがどれに割り振られているかを調べるのが大変ですが、Android 起動時の表示にちらっと出てくるので覚えておいてください。

image

これで Visual Studio や Xamarin Studio からデバッグができるようになります。

Visual Studio で Android on RasPi を動かす

Xamarin Studio 2015 で Android プロジェクトを作ってデバッグ起動をします。

image

起動メニューのところに「ARM vexpress」と出るので、これを選択します。

image

Android テンプレートの Hello World プロジェクトが起動できます。

Xamarin.Forms を Android on RasPi で動かす

Xamarin.Forms のサンプルとして My Shoppe をダウンロードして動かしてみましょう。

Visual Studio 2015 Update 1 ではエラーが出てビルドができないので、Xamarin Studio でビルドしています。

image

ビルド実行は、ARM vexpress(API 19) を選択すれば ok です。

image

image

いくつか動かしてみると、画像のロードやテーブルもうまく動いています。これで Android のデバッグも順調にできますね。いや、普通に Android を使ったほうが便利なのですがw

Android on RasPi を何に使うか?

さて、Android が Raspberry Pi で動くことが分かったのですが、これを何につかうか?です。もともと、組込み系の展示会に行ったときに Android 試験ボードを見て、結構高い(2,3万円程度)ので何かで代用できないかな、と思ったところがスタートです。Android 拡張ボードには、ネットワークの他にも Bluetooth や各種ピンが配置されています。Android on RasPi の場合には、Bluetooth ドングルが有効にならないのですが、ピンのほうはなんとかできるのでは?と考えています。UI 部分を Xamarin で作って、内部的には Linux で GPIO 等を操作できればいいですよね。

まあ、5 インチ液晶を載せてしまうとピンが隠れてしまうので駄目なんですが、ピンがつながるようにして、戦車やロボットアームが動かないかな、と考えているところです。Rasbian と同じように /sys/class/gpio/export にアクセスすることで制御が可能だと思うんですけどね。

カテゴリー: Android, RaspberryPi, Xamarin | Raspberry Pi で Xamarin.Forms を動かそう(Advent編) はコメントを受け付けていません