ヘルパーループとは?

Queued Message Handler (QMH) や DQMH を使用していると、次の課題に直面することがあります。

周期処理をどうやるか?

例えば、10秒ごとに現在の状態をログファイルとして保存したり、100 msごとに温度データを取得したりといった周期的に行う処理をどうやるかです。QMHやDQMHでは、メッセージ処理ループ(MHL)で何らかの処理を行うわけですが、処理をさせるにはどこかでメッセージをエンキューさせるのが原則です。やることの種類が少なく、周期的処理が1種類しかないモジュールやMHLなら、イベントストラクチャやデキューにタイムアウトを入れて対応できる(デキューの場合はQMHやDQMHのデキューのサブVIにタイムアウトの入力端子を追加する)かもしれません。

イベントストラクチャのタイムアウトで周期処理をする例
周期処理中はイベントストラクチャのタイムアウトを-1以外にする。

しかし、複数種類の周期処理を独立した周期で行う場合はこれでは対応できません。また、イベントストラクチャやキューの性質上、別のイベントが割り込んでくると周期が乱れます。その別のイベントが周期処理の開始と終了だけならいいですが、それ以外もあると周期処理が1つであってもイベントストラクチャのタイムアウトみたいな手っ取り早いやり方では対応できません。そういうときにどう対応するかというと、「ヘルパーループ」というものを作ります。ここでは、QMHやDQMHにおけるヘルパーループについて記述していきます。

ヘルパーループとは?

ヘルパーループとは、QMHやDQMHでMHLのヘルプをするループです。

…と言われても何を助けるのか、どう助けるのかよく分からないので、ちゃんと説明すると、MHLで周期的処理をするのに用いるループです。

なぜ周期的処理にヘルパーループを用いるかというと、次のような理由が考えられます。

  • MHLではループあたり1つのメッセージキューで処理をする。
  • その1つのキューでいろいろなメッセージを受け取る。
  • 周期的処理をするには該当するメッセージ以外もいつ送られるか不定という前提で周期的にメッセージを送る必要がある。

1つのMHLで取り扱うメッセージの総数が少なく、周期的処理が1つで、その周期的処理のメッセージ以外のメッセージが周期的処理の開始または終了を意味するものなら、EHLのタイムアウトやデキューのタイムアウトを利用すればわざわざ新しいWhileループを用いなくても対応できます。しかし、複数の周期的処理や別のメッセージの割り込みでも関係なく周期処理を続けたい場合は、メッセージを周期的に送る機構を加える必要があります。それがヘルパーループです。

ヘルパーループを使う際に重要な点は、ヘルパーループではメッセージを周期的に送る処理にとどめておき、処理自体はMHLに置くことです。ヘルパーループはあくまでヘルパーなので、処理自体はMHLにやらせます。あと、ヘルパーループで周期を定めたからといって、MHLで呼び出した処理がその周期内に終了するわけではないので、処理時間 < 周期に設定するようにする必要があります。

ヘルパーループの例

実際のヘルパーループを見てみます。今回はQMHで次のようなVIを作成しました。

  • ボタンを押すと1から6の乱数を100msごとに発生させる
作るVIの画面

…これだけならState Machineでも作れるような大したことのないものですが、今回はQMHでヘルパーループを使っていきます。ヘルパーループでは次のことを行います。

  • MHLから周期処理の開始、終了を受け取る。
  • 開始の場合は100msごとにMHLにメッセージを送る。送るメッセージは1種類。

あとはエラー処理とかループの終了処理があればいいです。…これってMHLとだいたい同じなんですよね。実際に作る場合はMHLを追加するような形になります。

作ったVIのブロックダイアグラム

1番のループがヘルパーループです。ほとんどMHLと同じ構造です。違いは2つですね。

  • ヘルパーループは自分のモジュール内で作り、その中だけで利用し、その中で破棄する。
  • デキューにタイムアウト(あるいは周期)の端子がある。

ヘルパーループはMHLのサポートをするものなので、自分のMHL以外で使われると困ります。なので、MHLがあるモジュール内で自分用のヘルパーループのキューの作成、利用、破棄を行います。

あと、デキューにタイムアウトがあるのが通常のメッセージキューのデキューとの違いです。これがあることによって、タイムアウト→タイムアウトの処理を周期的に行うことができます。

Dequeue Helper Message.viの中身
ヘルパーループの周期設定
ヘルパーループのTimeoutケース

このヘルパーループでは、MHLからStartを受け取ったら周期を100msに、Stopを受け取ったら周期を-1(無限待機)に設定します。こうすることで、Start後は100msごとにTimeoutケースが実行され、MHLに”Update Number”というメッセージを送ります。重要なことは、この100msの周期がヘルパーループの周期処理開始、停止(今回の場合はStart、Stopメッセージ)以外の割り込みを受けない点です。これにより、MHLにほかの処理が割り込んでもほぼ周期通りの処理をしてくれます。まあ、メッセージを送ったタイミングでMHLで別の処理中だったらうまくいきませんが。

複数のヘルパーループを使う

周期処理が複数あり、それぞれが非同期な場合は、ヘルパーループを複数個用意して対応できます。今回は上記のQMHにLEDを250msごとに点滅させる処理を追加します。これは通常のMHLの追加と同じですね。

フロントパネル
ヘルパーループが2つある場合のブロックダイアグラム
Helper Loop.viのブロックダイアグラム

ヘルパーループではメッセージと周期以外は同じ処理(指定した周期で決まったメッセージを送る)をするため、同じサブVIを流用することができます。ただ、LabVIEWではサブVIを置いた場合は1つのサブVIを使う場所から共有して呼び出す設定(非再入実行)になっていて、どこかで呼び出されている場合は別の場所から呼び出すことができないので、今回のように複数のヘルパーループを両方使う場合はVIプロパティで設定を変える必要があります。

Helper Loop.viのVIオプション

具体的には、VIプロパティのExecution(実行)のReentrancy(再入実行)を再入実行にします。これで、サブVIが1つでも複数のVI(VIクローン)を実行できます。再入実行にはShared Clone(クローン共有)とPreallocated(クローン事前割り当て)の2つがあります。今回の場合はまあどっちでもいいといえばどっちでもいいですが、ヘルパーループは開始から終了までずっと実行している関係でクローンが2つできるのでクローン共有の利点(クローンを作る必要がなければ作らない)を享受できません。なので、クローン事前割り当てのほうを選択するほうが自然だと考えています。

まとめ

ここではQMHやDQMHで周期的処理を行う方法としてヘルパーループを紹介しました。ここでの実例はQMHのものでしたが、DQMHでも基本的には同じです。ヘルパーループのメッセージキューはモジュール内で作成し、通常のメッセージキューにデキューのタイムアウトを追加してやる感じです。周期処理が複数あってもその分だけヘルパーループを増やせば問題ありません。これを使いこなせばQMHでだいたいのプログラムは作れると思います。

ヘルパーループの数が不定の場合はどうするか?それは別のお話。

タイトルとURLをコピーしました