LabVIEWでプログラムを組むときに必要になるデザインテンプレートの案を紹介しています。本ブログで紹介している具体的なプログラム例のベースとなる組み方を説明しています。
この記事では比較的単純なステートマシンの実装例を紹介しています。
デザインテンプレートの重要性については以下の記事を参考にしてください。
このデザインに必要な知識
ステートマシンとは、ステート、つまり「状態」を移りながら処理全体の流れを決めていくプログラムの構造の一つです。
本ブログでは色々な記事で、「ステートマシンは便利」ということを書いていますが、このステートマシンは状態遷移図で表せるような処理の流れをプログラムに落とし込むのに便利です。
また、より強力なプログラムデザインである、キューメッセージハンドラ(QMH)を組む際にも必要となる考え方となっているので、初心者だろうと経験者だろうと幅広く活用の機会があると思います。
本記事で紹介するステートマシンは比較的単純な構造のステートマシンの例ですが、必要な知識としては、
- ストレートフローのプログラムで必要な知識
- シフトレジスタ
- タイプ定義された列挙体
が挙げられます。
ストレートフローとは、私が勝手に名付けた形式なので一般用語ではありませんが、要は単純で毎回動作のパターンが固定(条件分岐を使えば多少複雑にはできますが)したもので、以下の記事で紹介しています。
ストレートフロー以外の項目について不安があるという方は、これらをキーワードにネットや書籍を調べたり、以下の記事を参考にしてみてください。
ステートマシンの基本的な構造
ステートマシンの基本的な構造は、「ある回のループの中の処理で、次の回の処理内容を決める」というものです。
この構造を実現するために必要な4要素があり、これらを使っているプログラムは「あ、ステートマシンをベースにして組んでいるんだな」と一目で判断できます。

プログラムは、目的の処理を達成したり、ある特定の状態にならないと終わらないという場合がほとんどであり、そのような状態を維持するために繰り返し構造であるWhileループを使用します。
そして、このWhileループの中で、様々な状態を移りながら処理をしていくのですが、この「状態」の表現に列挙体を、その状態での具体的な処理内容を区別するのにケースストラクチャを、次の回の処理内容を指定するのにシフトレジスタを、それぞれ使用します。
一つのプログラムは、一度作ったらそれで終わり、それ以降ずっとそれを使い続ける、という場合ももちろんあるとは思いますが、後々になって機能追加が必要になることも往々にして起こりえます。
そのような場合に状態の追加を容易に行いやすくするために列挙体をタイプ定義にします。
これらを使用して、状態遷移図を表現します。

具体的に、最も単純なステートマシンの動作とその仕組みを紹介します。
プログラムを動かすと、現在プログラムがどの状態にあるかをポップアップして表示するようにしています。
状態の数は4つだけで、上の図で示したようなInitialize、state1、state2そしてendとしています。

これら状態の数はそのままケースストラクチャのケースの数に対応します。
なので、ケースストラクチャのラベル部分をクリックしたときに表示されるのがそのステートマシンで表現できる「異なる状態」の全てです。
ケースストラクチャの各ケースはタイプ定義した列挙体をケースセレクタ(「?」と書かれた部分)に配線して定義しているので、列挙体の項目数とケースストラクチャのケース数は一致します。

ある状態の中で次の状態としてどの状態になるかをケースストラクチャの内側からシフトレジスタに配線していきます。
つまりここの書き方次第で「どの状態からどの状態に移るか」が決定されます。
最も単純なステートマシンとして、例えば以下のようなブロックダイアグラムとすると、上記の動作が実現できます。

あくまでこれはデモとして「現在の状態を表示する」という機能を各状態に持たせただけの作りとしていますが、実際のプログラムではケースストラクチャの中で様々な「その状態で処理したいこと」を記述していきます。
この例は一番単純なステートマシンであって、ストレートフローのように、毎回決まった動作しかしないプログラムになっていて味気ないですね。
これだと、ステートマシンの恩恵はあまり感じられないので、以下では少しだけひねった形を考えてみます。
ステートマシンの応用(簡単なところから)
フロントパネルにボタンがあり、このボタンが押されることで処理が先に進む、というプログラムを考えてみます。

動作としては以下のような感じですね。
state1の実行後、OKボタンを押さないと先に進まないようにしています。
ユーザーの意思でプログラムの実行のタイミングを制御するという想定です。

ステートマシンとしては、何かユーザーの操作を待つという状態を用意します。
下の図ではwaitというステートを用意し、OKボタンが押されるまで同じ状態(つまりwaitステート)を繰り返すようにしています。

実際これもステートマシンを使わずに書くことはできます。
では次はダイアログを表示させてプログラムがどの状態に移るかユーザーに選択させるタイプのステートマシンの実装を考えます。
選択関数を使用すれば簡単に実装できますね。

動作としては以下のような感じです。
今回は
- state2のボタンを押したらstate2に進みその後state3に進んで終了する
- state3のボタンを押したらstate3に進んで終了する
という形式をとっています。

waitステートで2ボタンダイアログの関数と選択関数とを組み合わせています。

ではもう少し考えて、フロントパネルに3つのボタンがあり、終了ボタンが押されない限り、それぞれのボタンに応じてそれぞれの状態を任意の順番、任意の回数実行できるようにしてみます。
フロントパネルにはそれぞれの状態を指定するためのボタンを用意しておきます。(ボタンの機械的動作はスイッチではなくラッチとしています)

プログラム実行後にstate1を押したらstate1に進み、その後またボタン選択ができるようになります。
state1をまた押したらもちろんstate1に進めますし、state2やstate3のボタンを押したらそれぞれの状態に進めます。

このようなプログラムであっても選択関数を使用して表現することができます。
selectのステートにおいてどのボタンが押されたかによって選択される状態が変わります。

鋭い人は「この作りだと、例えばstate1とstate2が同時に押されたときにstate1に進むという状態が上書きされてしまうので、場合によって意図した動作にならないことがあるのではないか?」と思われる方がいるかもしれません。
確かにその通りで、上記のブロックダイアグラムのwaitステートでは、選択関数を組み合わせている関係上、最終的にシフトレジスタに渡される列挙体の項目の「上書き」が起こりえます。

が、実際の動作としてこの状態は実用上ほぼ考える必要がありません。
なぜなら、各ボタンはラッチ動作としておりselectステートは文字通り一瞬で終了するため、「state1とstate2のボタンを同時に押す」ということがユーザーの操作で実際上不可能だからです。
とはいえ、もし実際のプログラムにおいてこのwaitステートに何か重い処理(時間がかかる処理)を置いていた場合、LabVIEWの原則に照らすと、選択した状態が上書きされてしまうということが限りなく低い確率で起こるということが言えなくもないかなと思います。
プログラムは可能な限り、意図しない動作は起こさない、起こさせないように組むべきです。この考えに基づくと、例えばイベントストラクチャと組み合わせてステートマシンを組んだり、回避方法はいくらでもありますが、そのあたりは少し応用的な使い方になるので別記事で紹介していきます。
まずはこういった比較的単純なステートマシンの形を覚えて、プログラムを組む際にどういった形が当てはまりそうかを考えてから組むといいかなと思います。
この記事では、LabVIEWでプログラムを書く上で幅広いアプリケーションに適用できるステートマシンの応用例を紹介してきました。
この記事で紹介したのは比較的単純なステートマシンですが、他にも応用的なステートマシンの実装はいくつかあり、別の記事で紹介しようと思います。
ここまで読んでいただきありがとうございました。



コメント