
計測システムを開発していて
1人じゃ足りない
と思ったことはありませんか?もちろん、チームの人数が足りないというケースもありますが、ここでは、アプリケーション内のモジュールなりプロセスなりWhileループなりが1つ、つまり、アプリケーション内の作業者が1人では足りないというケースです。ユーザーの操作を受け付けながら、何台もの計測器を制御しながら厳しい要件のデータ集録を行い、重い解析を行いながら大量のデータを保存する…となると、Whileループ1つでは対応できなくなります。アプリケーション内でもチームを組んで対応することが大切で、チームでやれば1人ではできないこともできるというのは一般的な組織と類似しています。しかし、ただ複数人が作業すればいいわけではなく、チームとして連携をとる必要があります。これが難しいのも一般的な組織と同じですね。そのようなアプリケーションをLabVIEWで開発するにはどうすればいいでしょうか?今回は、ある程度規模の大きいアプリケーションでも対応できるデザインとしてQueued Message Handler (QMH、キューメッセージハンドラ)を紹介します。
前提知識
QMHを理解するには、次のことを知っている必要があります。
- キュー
- State Machineのデザインパターン
- バリアント型の取り扱い方
- ユーザーイベント
ユーザーイベントはQMHに必須ではありませんが、NIのQMHのテンプレートではユーザーイベントを利用しているので、ここでは知っていることを前提とします。
Queued Message Handler (QMH) とは?
Queued Message Handler (QMH) とは、複数のWhileループを互いに連携しながら並列で処理をするためのLabVIEWのデザインです。個人的な感覚ですが、LabVIEWを生業とするプロはQMHやこれをベースにしたものをよく使っている印象です。まあ、Whileループ1個ですむようなものなら大学の実験室なら学生にやらせるでしょうし、大手企業なら自社の社員にやらせるでしょうから、LabVIEWのプロに発注するのは複数のWhileループが必要なめんどくさいものになるので、必然的にQMHをよく使うという感じだと思いますが。私自身もよく使うデザインです。
QMHの特徴としては次の通りです。
- 複数のWhileループ (イベント処理ループとメッセージ処理ループ)を並列に実行できる。
- イベント処理ループはユーザーの操作を受け付ける。
- メッセージ処理ループはイベント処理ループからメッセージを受け取って、実際の処理をする。
- それぞれのWhileループの連携はメッセージキューを用いて行う。
- メッセージキューはメッセージ+データからなる。
- データはバリアントでエンキューし、デキュー後にもとの型に復元する。
と言ったところだと思います。これだけだとProducer/Consumer (生産者/消費者)のデザインと大体同じですが、イベント処理ループ以外のループが対等で互いにメッセージ送受信ができるのがProducer/Consumerとの違いです。メッセージ処理ループにも処理結果などを送ることもできます。
…といっても言葉だけで理解するのは難しいと思うので、実際のテンプレートを見ていきたいと思います。
QMHのテンプレートの開き方
QMHでアプリケーションを開発する場合、次の観点からQMHのテンプレートから始めることがおすすめです。
- QMHのライブラリがLabVIEWプロジェクトに含まれている
- メッセージキュー作成、エンキュー、デキューのサブVIがライブラリにある
- アプリケーション停止処理やエラー検出処理がすでにできている
自分でメッセージキューの処理を作ることもできますが、できているものは使ったほうが楽です。テンプレートはスタート画面 (Getting Started Window) からCreate Projectボタンで開くことができます。
注:上メニューのFile → Newでは選択できません

UTF-8のほうはLabVIEW 2026 Q3で追加されたものです。通常のものとUTF-8版の違いはメッセージの文字列の型が従来のSystem文字列かUTF-8かです。どっちを選ぶかは、過去バージョンとの互換性や正規化処理などのバグのリスクなどを考慮して選ぶのがいいですが、そういうのがなければどっちでもいいです。”Channeled Message Handler”はQMHをChannel Wireというもので再現したものですが、私はChannel Wireについてはよく分かっていませんし、使い方の解説もあまり多くないので、QMHのほうを推奨します。Queued Message Handlerを選んで「Next」を押すと、プロジェクトの名前や保存先を聞かれるので、適当に決めて「Finish」を押すと、LabVIEWプロジェクトができます。


QMHのプロジェクトには次のものが含まれます。
- Project Documentation:QMHについて説明したhtmlファイルがある。実際の運用ではここにいろいろな文書ファイルやヘルプを入れるといいのかもしれません。
- Support VIs:ここにQMHで使うサブVIがいる。
- <接頭語 >Message Queue.lvlib:メッセージキューに関係するVIのライブラリ
- <接頭語 >User Event.lvlib:イベント処理ループに登録するユーザーイベントに関するVIのライブラリ
- <接頭語 >Check Loop Error.vi:エラーを検出するためのVI。エラー検出のコア部分。
- <接頭語 >Check Error – Event Handling Loop.vi:イベント処理ループのエラー処理VI。
- <接頭語 >Check Error – Message Handling Loop.vi:メッセージ処理ループのエラー処理VI。
- Type Definition:QMHのメッセージ処理ループで使いまわすデータクラスタがある。
- <接頭語 >Main.vi:これが実行するVI。
…いろいろありますし、メッセージ処理ループとか何?と思いますが、まずはMain.viを開くことを知っていればいいと思います。
QMHのデザインパターン
Main.viを開いてみます。

…私はこのブロックダイアグラムに慣れていますが、そうでない人には複雑に見えるかもしれません。でも、ここでやっているのはこれだけです。

ここまでにイベント処理ループ、メッセージ処理ループという単語を当たり前のように使っていましたが、そろそろちゃんとした?説明をしたいと思います。
- イベント処理ループ:イベントストラクチャを置いて、イベントの処理をするループ。ここでいうイベント処理とは、通常はイベントをメッセージ処理ループに送ることである。
- メッセージ処理ループ:どこかから受け取ったメッセージに従って処理をするループ。
…これで分かるのかな?どうやってイベント処理ループからメッセージ処理ループにメッセージを伝えているかは実行のハイライト(上にある豆電球アイコン)を有効にして、テンプレートのフロントパネルにある Do SomethingやDo Something Elseボタンを押すと理解できるかもしれません。

テンプレートで「Do Something」を押した場合、次のように処理されます。
- イベント処理ループでDo Somethingボタンの値変更イベントを検出し、メッセージ処理ループに「Something」メッセージを送付する。
- メッセージ処理ループで「Something」メッセージを受け取り、Somethingケースの処理をする。このケースでは、メッセージ処理ループ(自分自身)に「Update Display」メッセージと「”Do Something” button pressed」データを送付する。
- メッセージ処理ループで「Update Display」メッセージを受け取り、Update Displayケースの処理をする。このケースでは、受け取ったデータ(バリアント)を文字列に復元し、Displayの表示器に入れて表示する。

ほかのケースは実行ハイライトのスクリーンショットにある

これでユーザー操作から結果表示までの流れを少しでもつかんでいただければ幸いです。
次にExitの処理を見ていきます。QMHでは、通常はイベント処理ループ→メッセージ処理ループの方向にメッセージを送りますが、Exitの処理では、ユーザーイベントを使ってExitのケースに入ったことをメッセージ処理ループ→イベント処理ループの方向で伝えています。具体的にはこのような流れです。
- イベント処理ループでExitボタンの値変更イベントを検出し、メッセージ処理ループに「Exit」メッセージを送付する。メッセージ送付の際、処理の優先度をHighにしている。
- メッセージ処理ループで「Exit」メッセージを受け取り、Exitケースの処理をする。このケースでは、エラーがあればエラーダイアログを表示し、キューを破棄する。また、ユーザーイベントStop を発生させ、イベント処理ループにExit処理をしたこと(正確にはExitケースを通過したこと)を伝える。その後、メッセージ処理ループを終了する。
- イベント処理ループで<User Event – Stop>のユーザーイベントが発生し、Displayを更新し、イベント処理を終了する。


ここでユーザーイベントを使っています。…まあ、停止するだけならExitボタンを押した時点でイベント処理ループを止めてもいいですが。重要なのは、指示の流れを双方向にすることができるということです。停止の場合は、イベント処理ループがほかのループの停止を見届けてから自分を止める…みたいなことができます。
処理の追加 / エラー処理
次に、新しい処理を追加してみます。今回追加する処理は、エラー処理の流れを確認することもかねて、次のようにします。
- メッセージ名:Generate Error
- データ:エラークラスタ制御器の値
- イベント発生条件:Generate Errorボタンを押したとき
- メッセージ処理:イベント処理ループで受け取ったエラークラスタを復元して、エラー配線する。
手順としてはこのような感じです。
- 制御器をフロントパネルに配置する
- イベント処理ループのイベントストラクチャにボタンの値変更イベントを追加する
- 追加したイベントケースにEnqueue Message.viを配置して配線する。Enqueue Message.viは別のイベントケースからコピーペーストして持ってくると楽。メッセージは「Generate Error」、データは「エラークラスタ」を配線する。
- メッセージ処理ループのケースストラクチャにケースを追加し、ケースのラベルをGenerate Errorにする。テンプレートから始めた場合は「—Copy This Frame—」というケースがあるので、それを複製すると楽。
- Generate Errorの処理を作成する。このとき、手順3で送ったエラークラスタはバリアントとして受け取っているので、バリアントからエラークラスタに変換する。

これを実行し、Error ControlをエラーありにしてからGenerate Errorを押すと、エラーを検出します。

これで新しい処理の追加ができたといえます。QMHのテンプレートでは、エラーを検出すると終了する設定になっています。
エラー検出についてもう少し説明すると、エラーの検出はケースストラクチャの外右側にあるサブVI (Check Error – Message Handling Loop.vi)で行っています。


ここでは、無視するエラーやエラーを検出したときの状態遷移先を決めることができます。エラー処理を変更する場合は、青背景のフリーラベル (#CodeRecommendedのハッシュタグがあるもの) を変える感じです。特に、サブVIのCheck Loop Error.viに定数のTrue (Exit on Error?の入力端子) がつながっていますが、実際のシステムではエラーで必ず終了するわけではないので、個人的にはここはFalseにしたほうがいいと思っています。Trueの場合は即Exitのメッセージを送りますが、FalseにするとErrorのメッセージを送ります。終了したほうがいいエラーならErrorケースからExitに行けばいいわけですし。
別のメッセージ処理ループを追加
QMHのテンプレートではメッセージ処理ループは1つだけですが、QMHではメッセージ処理ループを複数持つことができます。メッセージ処理ループを追加することによって、例えば、大きなデータを保存しながらデータ集録を続けたりすることができます。今回はここまで作ったVIにエラーログを書くループを追加します。…まあ、この程度なら別ループにしなくてもいいですが。
別ループを追加するには次のことをします。
- メッセージキューのタイプ定義クラスタにメッセージキューを追加する
- Create All Message Queues.viに追加したメッセージキューの作成+Initializeメッセージ送信を追加する
- Whileループを追加する
メッセージキューの追加
まずはメッセージキューをタイプ定義に追加します。これはAll Message Queues.ctlを開き、既存の要素(今回の場合はUI)をコピーしてラベル名を変えてからMessage Queuesクラスタに入れて保存するだけです。

既存の要素をコピーすることで、同じメッセージキューの関数を流用できます。
追加したメッセージキューの作成処理
次に、Create All Message Queues.viを編集していきます。

今回はForループで2つメッセージキューを作り、要素0、1をそれぞれUI、Error Logにしています。Initializeメッセージ送信を繰り返し処理に入れていますが、これは好みかもしれません。個人的にはすべてのメッセージ処理ループでInitializeをしたほうがいいと思っています。Initializeのケースを作っておき、やることがなければケース内で何もしなければいいだけですし。
Error Log用メッセージ処理ループの追加
次にError Log用のWhileループをMain.viに追加します。これはUIのメッセージ処理ループのコピーでいいと思います。

上のブロックダイアグラムではコピー後にいらないケースを削除しています。Dataのクラスタはコピーしないほうがいいです。なぜなら、Error LogとUIのメッセージ処理ループでは取り扱うデータが異なるからです。Dataクラスタは新規作成をします。また、本当はケースストラクチャの右にあるエラーを検出するVIもUIのメッセージ処理ループとは別のもの (中身は同じでいい)を使うべきですが(無視するエラーなどが異なる可能性があるため)、今回は手抜きで流用します。本当は別に作るんですよ。
Error Logループを追加することで次の修正をします。
UIのメッセージ処理ループ
- Generate ErrorのケースでError LogループにError Logメッセージとエラーを送る処理を追加。
- ExitケースでError LogループにExitメッセージを送る処理を追加。
Error Logのメッセージ処理ループ
- Initializeでエラーログファイルが存在しなければ作る。そのパスをデータクラスタに入れる。
- Error Logケースでエラーログファイルに書き込む処理を作る。
- ExitケースでUIのメッセージ処理ループにExitを送る。


Error LogループのInitializeケースのスクリーンショットを表示していませんが、該当ケースは存在し、そこでログファイルのパスが存在しなければファイルを作ってDataクラスタに入れています。
Exitケースでは、Error LogループはUIのメッセージ処理ループに終了を伝えます。また、UIのメッセージ処理ループではError Logループとイベント処理ループに終了を伝えます。これにより、どのループが終了のきっかけを作っても全ループが終了するようになります。これでGenerate Errorを押すと、こんなエラーログが記録され、アプリケーションが終了します。

かなり雑ですが、これで複数のメッセージ処理ループができ、ちゃんとエラー処理もできます。
QMHの注意点
QMHを使う際の注意点です。個人的見解も大いに含むので、納得なものをお持ち帰りいただければと思います。
関数パレットにあるMessage Queueの関数はActor Framework用なので、通常のQMHでは使ってはいけない
関数パレットでMessage Queueと検索すると、次の関数がヒットすることがあります。

これは確かにMessage QueueのサブVIではありますが、Actor Frameworkで使っているものです。Actor Frameworkでもこの関数をユーザーが配置する状況はほとんどありません。ここで使っているQMHの.lvlibにあるサブVIとは別物です。QMHでは関数パレットのものは使わずに、QMHの.lvlibにあるものをドラッグ&ドロップしたり、テンプレートにある既存のサブVIをコピーしたりして使うようにします。
メッセージキューリファレンスの持たせ方
QMHのテンプレートでは、All Message Queues.ctlというタイプ定義クラスタで各メッセージキューを持っています。

名前からどのメッセージキューか分かる。
しかし、メッセージキューの型はどのWhileループでも同じなので、次のように思うことがあるかもしれません。
- 1次元配列ならどうか?
- Mapならどうか?
配列でメッセージキューを持たせた場合、配列からメッセージキューを取り出すには指標を手掛かりに取り出すわけですが、あとでブロックダイアグラムを見て指標nが何のメッセージキューか把握できるでしょうか?自分が作ったものでも半年も経てば忘れますし、人が作ったものは理解できません。指標ではコードのドキュメント化がされていないので分からないんですよね。

指標1が何なのか分からない。
Mapの場合はキーを設定するわけですが、正直なところ毎回マップからキーを指定してメッセージキューを取得するのは面倒ですね。それと、ほかにどのようなメッセージキューが存在するか、それはMapを作成するVIを確認しないと分かりません。どうせ、Create All Message Queues.viでまとめて作り、それ以降はメッセージキューの数は増えも減りもしないので、最初からタイプ定義クラスタにするのがいいと私は考えています。

何のメッセージキューか分かるが、
ほかにどんなメッセージキューがあるか分からない。
Initialize/Error/Exitのケースを必ず用意する
これはQMHのテンプレートから始めた場合です。Initializeケースについては、Create All Message Queues.viでエンキューしています。ErrorおよびExitについてはCheck Loop Error.viでエンキューしています。なので、これら3つのケースを用意しておくことを推奨します。…ケースを用意するだけで中身は配線が素通りでもいいですが。
メッセージ処理ループのエラー入力について
メッセージ処理ループのエラー入力をよく見ると、シフトレジスタではなく通常のトンネルになっています。
State Machineのデザインパターンでは、前回のエラーを保持するためにシフトレジスタでWhileループに入力していて、QMHでもそうするべきであると思うかもしれません。しかし、ここはトンネルが適切です。実際にシフトレジスタにすると、エラー発生時にDequeue Message.viが実行されず、Exitケースにいつまでも到達できずに無限ループに陥ります。なので、ここはトンネルが正解です。

ループをさらに増やす場合
ループを増やす場合、MainのVIにループを追加していってもいいですが、増やしすぎるとコードの可読性が下がります。そういう場合はイベント処理ループ+UIのメッセージ処理ループを1つのサブVIに、ほかのループを1つのサブVIにするとすっきりします。こんな感じにするといいです。

余談ですが、私はCLA試験で上記のような設計のVIを作りました。それで点数が確か86%ぐらいだったので、それなりに有効な試験対策になります。Launcher VIを用意して、各QMHのループをサブVIにして呼び出す方式です。UIやERRORと書いてあるサブVIの中でイベント処理ループやメッセージ処理ループが回っている感じです。これならループのサブVIが増えても見通しが立ちやすいですし、うまく設計すればUIの部分だけ変えるというようなモジュールの交換みたいなこともできます。…うまく設計すればですが。
サブVIにする場合、各QMHループをlvlibにするのがおすすめです。そうすれば、VIに名前を付けるときに同じ名前にしても接頭辞にライブラリ名がつくのでクロスリンクや競合といったリスクがなくなります。

ここではLoopの中で各QMHがあり、共通となる部品 (QMHのエンキューなどをするライブラリ、ユーザーイベントのライブラリ、Check Loop Error.vi)はSupport VIsのままという構成です。これは一例なので、各開発者の合うように使えばいいと思います。
各メッセージ処理ループの関係
QMHでは、各メッセージ処理ループの関係が対等です。UIループのイベント処理ループが全体を統括しそうですが、コード上にそのような制約はありません。たくさんのメッセージ処理ループを作った場合、各メッセージ処理ループが好き勝手にエンキューできます。これはチームプレイを考えた場合に最短経路で情報伝達できるというメリットがある反面、バグが発生したときにどんな経路でエンキューされたか把握しにくいという欠点があります。
まとめ
今回はQMHのテンプレートをいろいろ見ていき、QMHの簡単な使い方を紹介しました。QMHやそれをベースとしたデザインパターンはLabVIEWのプロでもよく使うものです。ここで記述した内容がどのくらい分かりやすいか、あまり自信がないですが、役に立てば幸いです。
関連情報
- Queued Message Handlerをよりうまく使うには ~メッセージキューの作成~
- Queued Message Handlerをよりうまく使うには ~ユーザーイベントの活用~
- Queued Message Handlerをよりうまく使うには ~メッセージキューの破棄~
- Queued Message Handlerをよりうまく使うには ~エンキュー~
上記の関連事項を入れたもの
次のステップ

