2018年1月24日水曜日

MPLAB Xでアセンブリリストを見る

私が初めてPICを触ったころ、それは10年以上前になりますが、その時はまだCコンパイラは数百ドルする有料版しかありませんでした。当時中学生だった私は、当然そのような高価なものは買えるわけもなく、一生懸命PICのアセンブリを覚えてプログラムを書いていました。

時代は変わり、PIC以外にもたくさんのマイコンが趣味レベルで使われるようになり、開発環境もC言語が主流になりました。PICも一足遅れて(実用レベルの)Cコンパイラを無償公開し、今ではMicrochipが買収したHi-Tech社のCコンパイラがXCコンパイラとしてPICの基幹コンパイラになっています。

今ではC言語向けに間接参照などの機能を強化したPICがたくさん出てきており、アセンブリで開発する理由は皆無ですが、それでもときどき「このコードはどのような形でアセンブリに展開されるのだろうか」と気になることがあります。

例えばEEPROMアクセス。
EEPROMは、不意な書き込みを防止するため、アンロックシーケンスと呼ばれる一定の手順で命令を実行しないと書き込みできないようになっています。
        BCF     INTCON, GIE
        MOVLW   55h
        MOVWF   EECON2
        MOVLW   AAh
        MOVWF   EECON2
        BSF     EECON1, WR
        BSF     INTCON, GIE
これが代表的なPIC16シリーズにおけるEEPROM書き込みシーケンスの一部です。EECON2には0x55を書き込んだ2サイクル後に0xAAを書き込み、さらに次のサイクルでEECON1のWRビットをセットしなければいけないため、C言語で書いていてもこの部分だけはインラインアセンブリで書かなければならなくなることがあります。
ですが、C言語で書いても次のように書けば上記のアセンブリに展開されることは容易に想像が付きます。
INTCONbits.GIE = 0;
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1;
INTCONbits.GIE = 1;
でもまあ、C言語をどうアセンブリに展開するかはコンパイラの勝手なので、こういう書き方をすれば絶対に展開されるとは限りません。こういう時にどのような形でアセンブリ展開されるか見てみたくなりますよね。

アセンブリリストは…っとその前に、まずはお約束の環境を書いておきます。
  • MPLAB X IDE v4.05 
  • XC8 v1.45

アセンブリリストはメニューの
Window → Debugging → Output → Disassembly Listing File
で開くことができます。
ですがおそらく、最初は次のようなファイルが開かれて、アセンブリリストは出てこないはずです。

悩むほど難しい英語ではないですが、一応拙訳を付けておきます。
アセンブリリストの生成は無効になっています。
下記の情報に従ってプロジェクトをビルドしてください。
  1. プロジェクトのプロパティを開いてください。
  2. 「Conf: [現在の設定]」ノードの下にある「Loading」を選択してください。
  3. 「Load Symbols when Programming or building for production」にチェックを入れてください。
このエディタ上でダブルクリックをすると上記の設定ができます。
最後の1行にしれっと書いてありますが、エディタ上をダブルクリックすればワンタッチでダイアログの当該部分が表示される超便利機能があります。
上のほうにある「Load Symbols when Programming or building for production」にチェックを入れて、OKを押してプロジェクトをリビルドしましょう。
ご覧の通り、ばっちりアセンブリリストが表示されました。

実はXC8には組み込み関数でeeprom_writeとeeprom_readというのがあるので、直接レジスタを叩かなくてもEEPROMの読み書きはできるのですが、その部分のコードも展開されていることが分かります。
PICの違いによるレジスタ名の違いで、EECONxではなくNVMCONxになっていますが、5607行目からアンロックシーケンスが始まっていることが分かります。
書き込みを開始してから完了までの間は割り込みを禁止したままなんですね。CPUは動作しているのでそのようにする理由もあまり無い気もしますが…(割り込み中にEEPROMを読み出すのなら話は別ですが)。

2018年1月17日水曜日

LivetにおけるWPFでのダイアログ表示のいろは

いっつもLivetを使っているはずなのに、いろいろなクラスの名前をすっかり忘れてしまってその都度ググることになってしまっているので、備忘録的にまとめます。

基本

TriggerAction

画面遷移は基本的にTriggerActionを使います。その名の通り、何かしらの引き金によって発動するアクションです。
TriggerActionを発動させる方法はいくつかありますが、イベントで発動させる方法とViewModelから発動させる方法を覚えておけば困らないでしょう。前者はEventTrigger(System.Windows.Interactivityの機能)、後者はMessenger(Livetの機能)を使います。

EventTrigger

こちらは、例えばダイアログ表示ではなくても、Livetのメソッド直接バインディング機能などを使うときに多用することになるでしょう。
<Button Content="Push me" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100" Height="30" >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="ButtonPushed" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>
例えばこんなやつです。ボタンを押すとEventTriggerでClickイベントを拾い、LivetCallMethodActionを発動させるコードです。基本的にレイアウトなどの動きが無いコードになりがちなマークアップ言語に一気に彩りが生まれる記法ですので是非押さえておいてください。

Messenger

LivetにはMessengerという機能があります。ViewModelはMessengerというインスタンスを持っており、MessengerでInteractionMessageクラスのメッセージを送信すると、それをViewで受け取れる(=TriggerActionとして動作する)というものです。
標準のWPFにはこのようにViewModelからViewに何かを働きかける機能がかなり少なく、Livet以外のMVVMライブラリでもたいてい似たような機能は実装されているようです。
<i:Interaction.Triggers>
    <l:InteractionMessageTrigger Messenger="{Binding Messenger}" MessageKey="MethodCaller">
        <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="MessageRaised" />
    </l:InteractionMessageTrigger>
</i:Interaction.Triggers>
これは上記のEventTriggerの例になぞらえて、代わりにMessengerでLivetCallMethodActionを発動させるプログラムです。EventTriggerがInteractionMessageTriggerに代わっただけですので難なく理解できるかと思います。
ちなみに、ViewModelではこのように呼び出します。
Messenger.Raise(new InteractionMessage("MethodCaller"));
InteractionMessageクラスでキーを指定します。これによって、単一のコントロール内に複数のInteractionMessageTriggerを持っていても使い分けることができます。

InteractionMessage

上記のMessengerの項でも少し出てきましたが、MessengerとはViewModelからViewに情報を受け渡すものです。上記の例ではRaiseしかしていないのでMessengerKeyしか渡しませんでしたが、例えばダイアログボックスならば表示テキストやキャプション、アイコンの種類などを伝える必要があるでしょう。そのような場合は、InteractionMessageを継承した派生クラスを作ってパラメーターを持たせるような仕様になっています。それに対応して、TriggerActionの派生クラスであるInteractionMessageActionを継承した派生クラスも作成し、そこで独自のInteractionMessageを受け取れば良いです。

Messengerの例

というわけで、まずはMessengerを使ってダイアログボックスを表示する例を示します。
<i:Interaction.Triggers>
    <l:InteractionMessageTrigger Messenger="{Binding Messenger}" MessageKey="MessageBox">
        <l:InformationDialogInteractionMessageAction />
    </l:InteractionMessageTrigger>
</i:Interaction.Triggers>
Messenger.Raise(new InformationMessage("Message Text", "Caption", MessageBoxImage.Information, "MessageBox"));
ViewとViewModelはこんな感じになります。
InformataionDialogInteractionMessageActionは単一ボタン(=OKボタンのみ)のメッセージボックスを表示するTriggerActionです。
これに対応するInteractionMessageはInformationMessageで、これにメッセージ内容、キャプション内容、アイコン、メッセージキーを含めてMessengerに乗せることでダイアログボックスを表示させることができます。

EventTriggerの例

ところで、ボタンが押されたらそのままメッセージボックスを表示したくなることがあります。ですが、上記のMessengerの例を見ているとViewModelからViewにメッセージを送る必要があるので、ボタン(View)—(メソッド呼び出し)→Messenger発動(ViewModel)—(Messenger)→ダイアログ表示(View)といった具合にViewとViewModelを往復する必要が出てきてしまいます。これではあまりスマートではありません。

ですがそこは流石Livet、ちゃんと方法が用意されています。
InteractionMessageActionクラスにはDirectInteractionMessageプロパティが用意されており、ここに直接InteractionMessageを渡してあげればいい…と思いきや、ここに渡すのはDirectInteractionMessageクラスです。これについては細かくは後述します。
DirectInteractionMessageにはMessageプロパティがあり、こちらにInteractionMessageを渡せばいいです。
さらに、それぞれのクラスのDirectInteractionMessageプロパティ、MessageプロパティはContentPropertyに設定されているので、プロパティを明示しなくても設定できるのでネストが深くなるのも最小限に抑えることができます。
<Button Content="Push me" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100" Height="30" >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click" >
            <l:InformationDialogInteractionMessageAction>
                <l:DirectInteractionMessage>
                    <l:InformationMessage Caption="Caption" Text="Message text" Image="Information" />
                </l:DirectInteractionMessage>
            </l:InformationDialogInteractionMessageAction>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>
こんな感じです。InformataionDialogInteractionMessageActionに直接InformationMessageを渡すことができています。これによって、View単体で(ViewModelを介在せずに)ダイアログを表示することができます。

ダイアログの戻り値

さて、上記は単一ボタンのダイアログを表示していたので戻り値は特に必要ありませんでした。ですが、例えば「はい」「いいえ」で聞くダイアログなどは戻り値を知る必要があります。

Messengerの例

実はこの戻り値はMessengerが持つようにできています。ですので、Messengerの例は特に上記の例とほぼ同じコードになります。
<i:Interaction.Triggers>
    <l:InteractionMessageTrigger Messenger="{Binding Messenger}" MessageKey="MessageBox">
        <l:ConfirmationDialogInteractionMessageAction />
    </l:InteractionMessageTrigger>
</i:Interaction.Triggers>
var msg = new ConfirmationMessage("Is this a MessageBox?", "Caption", MessageBoxImage.Question, MessageBoxButton.YesNoCancel, "MessageBox");
Messenger.Raise(msg);
if(msg.Response == null) {
    // Cancel clicked
} if(msg.Response.Value) {
    // Yes clicked
} else {
    // No clicked
}
bool?型のResponseプロパティに結果が入るので、Messaenger.Raiseから制御が返ってきた後にそれを見れば問題ありません。

EventTriggerの例

さて、ここでまた問題が発生します。当然、ダイアログの戻り値はソフトウェアの制御側(=ViewModel)で受け取りたいですが、EventTriggerで直接TriggerActionを呼び出してしまった場合、ViewModelは介在しないのでそのMessageを受け取るところがありません。

ですがそこは流石Livet、ちゃんと方法が用意されています。
先ほど無駄に挟んでいたように見えたDirectInteractionMessageクラスですが、実は答えはここにあります。これがCallbackMethodTarget/CallbackMethodNameプロパティ及びCallbackCommandプロパティを持っていて、ここでViewModelのメソッドやコマンドをバインディングすることによってViewModelが戻り値を受け取ることができるようになります。
<Button Content="Push me" VerticalAlignment="Center" HorizontalAlignment="Center" Width="100" Height="30" >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click" >
            <l:ConfirmationDialogInteractionMessageAction>
                <l:DirectInteractionMessage CallbackMethodTarget="{Binding}" CallbackMethodName="MessageBoxClosed">
                    <l:ConfirmationMessage Caption="Is this a MessageBox?" Text="Message text" Image="Question" Button="YesNoCancel" />
                </l:DirectInteractionMessage>
            </l:ConfirmationDialogInteractionMessageAction>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>
public void MessageBoxClosed(ConfirmationMessage msg)
{
    if(msg.Response == null) {
        // Cancel clicked
    }
    if(msg.Response.Value) {
        // Yes clicked
    } else {
        // No clicked
    }
}
ViewModelではDirectInteractionMessageに渡していたInteractionMessageをパラメーターにしたメソッドを用意しておく必要があります。これで、ボタンからメッセージボックスを表示し、その結果をViewModelで受け取ることができます。

クラス名対応表

さて、Messaengerとかはもうわかっているからいいよという人はここだけ見てください。
用途 TriggerAction InteractionMessage 備考
メッセージボックス(単一ボタン) InformationDialogInteractionMessageAction InformationMessage
メッセージボックス(複数ボタン) ConfirmationDialogInteractionMessageAction ConfirmationMessage
ファイルを開くダイアログ OpenFileDialogInteractionMessageAction OpeningFileSelectionMessage
ファイルを保存ダイアログ SaveFileDialogInteractionMessageAction SavingFileSelectionMessage
フォルダ選択ダイアログ FolderBrowserDialogInteractionMessageAction FolderSelectionMessage Livet.Extensionsが必要
任意のウィンドウ TransitionInteractionMessageAction TransitionMessage
Livetが持っているダイアログ表示関係のTriggerAction/InteractionMessageはこれで全部のはずです。ほかにも、Windowを最大化/最小化/閉じるなどを実現するWindowInteractionMessageAction/WindowActionMessageなどもありますが、まあこれは自分で調べて使ってみてください。

最後の任意のウィンドウ表示はなかなかに曲者かと思います。特に、これで開くウィンドウに対応するViewModelをどうしたらいいのか、というところでとても悩むことになるかと思います。
まあその辺はまたの機会に。

2018年1月13日土曜日

eventをasync/awaitに変換する

今回のテーマはいわゆるEAPからTAPへの変換です。

Webアクセスなど、長い時間がかかることが見込まれる処理では、あるスレッドがそれにつきっきりになるとほかの処理が一切できず、ユーザーからはソフトがハングアップ(フリーズ)しているように見えてしまいます。そのような状態を防ぐために、非同期処理が必要になってきます。

EAP(Event-based Asynchronous Pattern)は、非同期プログラミングにおいて、処理の開始を行うメソッドと、終了時に呼ばれるイベントからなるパターンです。
例えば、WebClientクラスはこのパターンをサポートしており、Webからのダウンロードを非同期で実行することができます。
WebClient wc = new WebClient();

wc.DownloadStringCompleted += (s, e) => {
    Console.WriteLine(e.Result);
};

wc.DownloadStringAsync(new Uri("https://www.google.co.jp/"));
これはGoogleのトップページのソースを画面に表示するプログラムをEAPで実装したものです。イベントを使っているため、ダウンロードを開始する前にダウンロード後の処理を綴ったハンドラをイベントに登録しておき、その後に処理を開始するという書き方をしなければなりません。プログラムが上から下へ流れないので非常に見にくくなってしまいます。

一方で、TAP(Task-based Asynchronous Pattern)はTaskクラス/Task<T>クラスを使用した非同期パターンです。ライブラリとしてはこの説明だけなのですが、C#では言語機能でもこの非同期パターンに追従していて、C#5.0でasync/await構文が導入されました。これは非同期処理を同期処理であるかのような見た目で実行できる構文で、TAPとセットでこの構文を使うことによって非同期処理の記述がかなりすっきりできるようになりました。
WebClientクラスはこのパターンもサポートしており、上記のプログラムはTAPだと次のように書き換えられます。
WebClient wc = new WebClient();

string result = await wc.DownloadStringTaskAsync("https://www.google.co.jp/");
Console.WriteLine(result);
見てくださいこのシンプルさ。文字列をダウンロードして画面に出力する、というシンプルな流れのみを記述していますが、実際ちゃんと非同期で動作します。人類が古くから慣れ親しんでいる同期パターンと同じように非同期なプログラムを記述できる素晴らしい機能です。


さて、復習はこれくらいで本題に入ります。
WebClientのようにTAPに対応してくれているクラスならいいのですが、世の中にはそうではないクラスもたくさんあります。古いライブラリだったり、そもそももうちょっと汎用的なプログラムでEAPと呼ばれるものでは無かったり。

ズバリ、TaskCompletionSource<T>クラスを使います。
static async Task Main(string[] args)
{
    string result = await DownloadStringTaskAsync("https://www.google.co.jp/");
    Console.WriteLine(result);
}

static Task<string> DownloadStringTaskAsync(string url)
{
    var tcs = new TaskCompletionSource<string>();
    var wc = new WebClient();
    wc.DownloadStringCompleted += (sender, e) => {
        if(e.Error != null)
            tcs.TrySetException(e.Error);
        else if(e.Cancelled)
            tcs.TrySetCanceled();
        else
            tcs.TrySetResult(e.Result);

        wc.Dispose();
    };
    wc.DownloadStringAsync(new Uri(url));
    return tcs.Task;
}
結論から言うとこんな感じです。
TaskCompletionSourceクラスにはTaskプロパティがあり、このプロパティをawaitすることで、TaskCompletionSourceの終了待ちをすることができます。
ではどうやったらTaskCompletionSourceが終了するのかというと、TrySetResultが呼ばれたときになります。これを呼び出すことでawaitでの待機が終了し、パラメーターで渡していた値が結果として返されます。
エラーが発生したり、キャンセルが発生したときは、それぞれTrySetExceptionTrySetCanceledを呼び出せばOKです。


余談ですが、私がこれを欲しくなったのは、SerialPortクラスを触っているときでした。
そもそもシリアルポートは単なる全二重通信ですから、本来はWebClientのように「データを要求して、そのレスポンスが返ってくる」という使い方に限定されるようなものではありません。しかし、「何かしらのコマンドを送るとそれに対応したレスポンスがある」のような実装を行うシチュエーションはそれなりにあり、そのような場合ではTAPでクライアントを作ったほうがスッキリします。
というわけで、SerialPortクラスをTAPでラッピングしようとしたときに使ったのがこの手法でした。