
Queued Message Handler (QMH) のテンプレートでは、イベント処理ループ (Event Handler Loop、EHL) を停止させるのにユーザーイベントを利用しています。しかし、QMHのテンプレートには実はユーザーイベントの使い方におかしい点があります。…まあ、テンプレートを使用するだけなら手を加える必要はないですが。ここでは、そのおかしいところを修正し、ユーザーイベントをより活用する方法を記述していきます。
何がおかしいか?
ユーザーイベントとは、雑に説明すると、開発者(ユーザー)がイベントストラクチャに登録するイベントです。ユーザーイベントをあらかじめ登録し、ユーザーが決めたタイミングでイベントを発生させることで、イベントストラクチャがユーザーイベントを検出するという感じです。QMHではメッセージ処理ループ (Message Handling Loop、MHL)でUser Event – Stopユーザーイベントを発生させ、それをEHLで受け取ることでEHLを終了します。

このブロックダイアグラムでちゃんとEHLは止まりますが、実はQMHのテンプレートのユーザーイベントにはおかしいところがあります。それがこれです。

これはUser Event – Stopユーザーイベントケースです。ここでは左の名前でバンドル解除みたいなノード(Event Data Nodeというらしいです)を選択していますが、この選択欄に”Stop”など値を取得できそうなものがないです。ユーザーイベントではユーザーイベントを発生させたときに値を一緒にイベントストラクチャに送るわけですが、ここでは値を受け取れないですね。まあ、QMHのテンプレートではTrueしか送っていないので、値を受け取れなくても問題はないですが。でも、おかしいのには変わりないので、それを解消したいと思います。
ユーザーイベントの値を受け取れるようにするには?
ユーザーイベントで値を使うには、ユーザーイベントの作成時に型を表す値をラベル付きで入れる必要があります。このラベル名でユーザーイベントを登録する感じですね。QMHではどんなふうに登録しているのか見てみます。

これがUser Event – Stopユーザーイベントの作成部分ですが、作成時に入力しているブールにはラベル名はありますね。それならユーザーイベントのEvent Data NodeにStop Eventという選択肢がありそうですがありませんでした。なんでそうなるのかを見ていきます。

これはQMHのテンプレートにあるCreate User Event – Stop.viにCreate User Event関数をコピーして同じStop Eventを入力端子に入れただけのものです。一見すると出力のUser Event – Stopと新しく作ったStop Eventは同じように見えますが、実はちょっと違います。その違いは、コンテキストヘルプをよく見ると分かります。User Event – Stopのほうは青枠で囲んだブールに特に名前がありませんが、下のStop EventのほうはStop Eventという名前がついています。何で違うのか?これは推測ですが、テンプレートでは次のように作ったと推測できます。
- ラベルのないブールをCreate User Eventに配線してUser Event – Stopを作った。その後、ブールにラベルを付けた。
ユーザーイベントでの変数の名前はCreate User Eventに配線した時点で決まります。なので、型を表す定数を入力する前にラベル名をつける必要があります。たぶんQMHのテンプレートではこの手順を間違えたのでしょう。私の追加したほうでは、最初からラベル名をつけているブール定数をCreate User Eventに入力しているので、ユーザーイベントの値にもブール定数のラベル名と同じStop Eventの名前がついています。ちゃんと名前を付けたユーザーイベントを使うとUser Event – StopユーザーイベントケースでStop Eventが選べるようになります。

…まあ、結局は、ユーザーイベントを作るときは先に型の定数のラベル名を決めたほうがいいですよ…ということです。これはQMH関係なくそうしたほうがいいですね。
ユーザーイベントをより活用するには
QMHのテンプレートではEHLの停止にだけユーザーイベントを利用しています。それだけでもいいですが、例えば、次の場合はユーザーイベントを使うことが有効かもしれません。
- MHLにたくさんのケースがあり、その中の2,3個のケースで同じ波形表示器を更新している。波形はメモリを多く使うため、MHLのデータクラスタには持たせたくない。
この場合、対応する選択肢としては、波形を更新するための専用のメッセージケースをMHLに作ることも有効ですが、ユーザーイベントでイベントストラクチャに飛ばす選択肢もあります。ほかにも、.NETで呼び出したもののCallbackを受け取ったりするときにも使えるかもしれません。QMHでそういった場合は、Create User Event – Stop.viを改造してCreate User Events.viにするのが有効だと考えています。

ユーザーイベントはタイプ定義クラスタにまとめておいた方があとあと楽になるので、タイプ定義クラスタにするのをおすすめします。ただ、最初はタイプ定義クラスタにすることでユーザーイベント破棄のサブVIやStopイベント発生のサブVIが壊れるのでそこの修正は必要です。まあ、大した修正ではないですが。
ユーザーイベントを登録する場合、次の2通りあります。
- 各ユーザーイベントのリファレンスを入れて登録する
- ユーザーイベントのクラスタを入れて登録する

この2つの違いは、イベントストラクチャでのユーザーイベントの表示名ぐらいで、できることは同じです。個人的には、クラスタを入れたほうがあとでユーザーイベントを追加したくなった場合に対応が簡単になるのでクラスタを入れるのをおすすめします。クラスタで登録した場合、イベントストラクチャのケースの編集や追加をするときにこのように表示されます。

クラスタでまとめて登録しても、クラスタ内の各要素のユーザーイベントの処理を作れるわけですね。こうすることで、Stopのユーザーイベントも使えますし、どこかでWaveformのユーザーイベントを発生させれば波形データをEHLで受け取ることができます。

ここではMHLのSomethingケースで適当な波形をユーザーイベントでEHLに送っています。これの重要なところはユーザーイベントを発生させる場所はここ1か所に限定されるものではなく、ほかのイベントケースでもいいですし、サブVI内からでもイベントを発生させられます。極論すると、別のMHLを作って、ここからユーザーイベントを発生させることもできますが、どこからでも好き勝手に送るとバグがあったときに追跡が大変になるので、ちゃんと設計する必要があります。それでもユーザーイベントを発生させているところだけを追えばいいので、ローカル変数とかグローバル変数とか機能的グローバル変数 (Functional Global Variable、FGV)で情報を送る場合に比べればデータの追跡は楽ですが。あとイベント駆動なので、ポーリング不要なのもリソースの無駄遣いを防げます。プロパティノード値やローカル変数などと違い、配線していないのになんか表示器の値が変わるという作成者本人(作成時点の本人ということ。未来の本人は別人)しか理解できないこともユーザーイベントなら防げます。データがどこから飛んできたかは分からないですが。
まとめ
今回はQMHにおけるユーザーイベントについて紹介しました。QMHのテンプレートを使うだけならテンプレートから変える必要はありませんが、大規模になるとユーザーイベントが有効になる場合もあるかもしれません。また、QMHを発展させてフレームワークにしたDQMH (Delacor QMH)ではユーザーイベントで各モジュール(QMHのEHL+MHLを含むMain.vi)から登録したイベントストラクチャにBroadcastしているので、より大きなプロジェクトを作るにはユーザーイベントを使えることが重要になります。Actor Framework (AF)でもActor Coreのメッセージ処理部分がサブVI(正確にはDynamic DispatchサブVI)になっていてその中に表示器を入れられないので、画面用Actor Coreの表示器を更新するにはユーザーイベントを使えたほうがいいと思います。ここで紹介したことによりQMHにおけるユーザーイベントの使い方(?)の一例を理解いただき、大規模なプロジェクトの対応のお役に立てば幸いです。
Github

