ステートマシンは、LabVIEWの開発者がアプリケーションを迅速に構築するためによく使用する基本アーキテクチャの1つです。ステートマシンアーキテクチャを使用すると、状態図またはフローチャートによる複雑な条件判断アルゴリズムを実装できます。ステートマシンは固有のLabVIEW関数を使用して実装できます。アーキテクチャに別途ツールキットやモジュールを追加する必要はありません。
この記事では、ステートマシンとは何かについて説明し、ユースケースの例、ステートマシンのいくつかの概念的な例、ステートマシンのコード例を紹介します。
ステートマシンとは、前回のステートの値またはユーザ入力に応じて動的なステートへのフローを可能にするプログラミングアーキテクチャのことです。
このアーキテクチャは、以下で構成されるアプリケーションに適してします。
ステートは、プログラムが全体的なタスクを実行している間のプログラム内のステータスとして定義できます。ステートの例としては、初期化中、待機中、計算の実行中、ステータスの確認中などがあります。
論理ステートメントは、いつ新しいステートに遷移するか、どのステートに遷移するかを決めるのに役立ちます。イベントを使用すると、あるステートから次のステートへの遷移をトリガできます。イベントは、プログラムイベントやユーザ定義 (ボタンの押下など) にすることができます。
ステートマシンの各ステートは、独自の動作を行い、他のステートを呼び出します。ステートの通信は、何らかの条件またはシーケンスに依存します。状態図をLabVIEWプログラミングアーキテクチャに変換するには、以下のインフラストラクチャが必要です。
ステートの区別が明白なアプリケーションでは、ステートマシンを使用します。ステートでは、次のステートや複数のステートに遷移したり、プロセスフローを終了させたりできます。ステートマシンでは、ユーザの入力やステート内の計算によって、次にどのステートに進むかを決定します。多くのアプリケーションでは、「初期化」ステートと、その次に多数の動作を実行するデフォルトステートが必要です。実行できる動作は、前の入力、現在の入力、さらにステートに依存します。その後「シャットダウン」ステートを使用してクリーンアップ動作を実行できます。
条件判断アルゴリズムを実装する優れた機能に加えて、ステートマシンはアプリケーションプラニングを関数の形で表したものでもあります。アプリケーションが複雑化すればするほど、より適切な設計が必要になります。状態図とフローチャートは有用なツールであり、場合によっては設計プロセスに欠かすことができません。ステートマシンはアプリケーションプラニングにおいて有用なだけでなく、作成においても簡単だというメリットがあります。
たとえば、以下のアプリケーションにはステートマシンパターンが有効です。
ユーザインタフェースの実装に使用できる別のデザインパターンとして、キューメッセージハンドラがあります。キューメッセージハンドラは、より洗練されたステートマシンであり、柔軟性が高まりますが、複雑さも増します。
効果的なステートマシンを作成するには、設計者が (1) 考えられるステートのリストを作成する必要があります。このリストを使用して、設計者は (2) 各ステートが別のステートとどのように関係するのかを計画できます。そして、状態図を (3) LabVIEWグラフィカルプログラミングアーキテクチャに変換できます。
この例では、大砲が危険なほど熱くならないように連続的に発射するアプリケーションを作成したいとします。
(1) 考えられるステートのリストを作成する
まず、タスクで考えられるすべてのステートのリストを作成します。大砲を継続的に発射するタスクを実行するには、次のことが必要です。
(2) 状態図でのステートの関係をマッピングする
次に、これらのステートが互いにどのように関係しているかを考慮し、状態図を作成します。状態図を作成するときは、ある統計から次の統計にプログラムが遷移する原因となるものを考慮します。それは自動的な遷移でしょうか。ユーザからの外部トリガがありますか。遷移は計算の結果に基づいているでしょうか。
たとえば、初期化ステートと他のステートとの関係を考えてみましょう。
ステートが互いにどのように関係しているかを十分に考えながら、これらのステート間における遷移ロジックの定義を始めたことに注意してください。コードで使用するプログラミングロジックは、(1) 考えられる遷移ステートの数、そして (2) ロジックで考慮される入力の数に依存します。コード遷移の選択肢については、以下の遷移コードの例のセクションで説明します。
引き続き完全な状態図ができるまで、ステートが互いにどのように関係しているかを調べます。状態図には、すべてのステートとそれらの間の関係が含まれることになります (図1)。この図では、ステート (楕円形のノード) は、制御プロセスがそのステートにあるときに実行される操作を表しています。一方、遷移 (矢印) は単に、プロセスがいつどのようにして、あるステートから別のステートに遷移できるのかを表しています。
図1:大砲の発射の状態図
各ステート間の関係を参考にして、あるステートから次のステートへの遷移に必要なロジックをプログラミングできます。
(3) LabVIEWでステートマシンを構築する
状態図で定義したステートとそれらの関係は、LabVIEWでコーディングアーキテクチャに変換することができます (図2)。状態図 (図1) のステート間のフローは、ループによって実装されます。個々のステートはケースストラクチャのケースに置き換えられます。前の図の各ステートは、ケースストラクチャのサブダイアグラムに対応します。各ステートは次の動作を実行します。
Whileループのシフトレジスタは現在のステートを保持し、それをケースストラクチャの入力に供給します。
図2:ステートマシン
測定アプリケーションにおけるこのテンプレートの使用例については、プロジェクトを作成ダイアログボックスでシングルショット測定サンプルプロジェクトを参照してください。
1. ユーザがアプリケーションの停止を意図した場合にのみシャットダウンコードを実行する。
2. シャットダウンコードを必ず最後まで実行する。
作業を始める方法については、『Modify the Simple State Machine LabVIEW Template』チュートリアルを参照してください。
テンプレートのカスタマイズを始める前に、以下の点を明確にします。
次にどのステートに遷移するかを決定するには、以下で説明するさまざまな方法があります。
サンプルの図は「初期化」ステートを示していますが、これらの遷移の可能性はどのステートにも適用できることに注意してください。
1対1:常にステートAからステートBに遷移する場合は、ロジックをプログラムする必要はなく、次のケースの名前 (ケースB) をシフトレジスタに出力するだけです。
図3a:可能な遷移ステートは1つのみ
1対2:ステートAからステートBまたはステートCに遷移する可能性がある場合は、選択関数を使用して、インジケータのステータスを評価できます。何らかの評価を実行して、どのステートに遷移させるかを決定する必要があります。たとえば図3bでは、ユーザの停止ボタンの入力によって、電源投入ステートから遷移するかシャットダウンに進むかが決定されていることがわかります。
図3b:可能な遷移ステートは2つ
配列を使用した1対多:遷移できる複数のステートがある場合は、列挙定数に関連付けられたブール領域を使用して遷移をプログラムできます。たとえば図3cでは、何らかのコードが実行され、その結果によって遷移が決定されます。遷移の出力はブール値の配列です。ブール配列は、遷移可能なステートのリストを含む列挙定数と相互に関係しています。指標配列関数を使用すると、ブール配列の最初の「True」ブールのインデックスが出力されます。次に、部分配列関数を使用して、相互に関係する列挙定数から適切なものを引き出すことができます。
図3c
ヒント:配列のインデックスは0から始まり、列挙体のインデックスは1から始まります。ブール配列と列挙定数を相互に関連付けるには、インクリメント関数を使用してそのオフセットを修正します。
Whileループを使用した1対多:複数の遷移ステートが考えられる別の選択肢として、ケース内側でのwhileループの使用があります。ブールステータスがTrueに設定されて停止ボタンがトリガされるまで、whileループの内側のコードが続行されます。これにより、トリガイベントが発生するまでコードを効果的に実行できます。
図3dは、内側ループとケースストラクチャを使用して次のステートに遷移する「初期化」ステートを示しています。内側のケースストラクチャには、現在のステートを離れる遷移ごとにダイアグラムが1つずつ含まれています。内側のケースストラクチャの各ケースには2つの出力があります。1つは遷移が必要かどうかを指定するブール値で、もう1つは遷移先のステートを指定する列挙定数です。ケースストラクチャへの入力としてループインデックスを使用することにより、このコードは、「True」のブール出力を持つダイアグラムが見つかるまで、各遷移ケースを1つずつ効果的に実行します。「True」のブール出力が見つかると、遷移先となる新しいステートがケースから出力されます。この方法は前の方法よりも少し複雑に見えるかもしれませんが、ループインデックスの出力を列挙体に「キャスト」することで、遷移に名前を追加する機能を提供できます。こうした利点により、遷移コードに「自動ドキュメント」を追加できます。
図3d
コードの冗長化
問題:ステートマシンの作成で最も難しいのは、状態図において考えられるステートを区別することです。たとえば、自動販売機の状態図 (図4) では、「応答を待機」ステートを設けて、投入されたコインの種類に応じて、あるステートから別のステートに遷移するのではなく、0、5、10、15、20、25、30、35、40、45、50セントのステートを持たせることもできました。その場合は、まったく同じケースダイアグラムで11個の異なる状態が作成されることになります。コードが冗長になると、大規模なアプリケーションで大きな問題を引き起こす可能性があります。
解決策:異なるステートに同じケースダイアグラムがある場合は、それらを1つのステートに結合してみてください。たとえば、コードの冗長性を避けるために、「応答を待機」ステートを作成します。
列挙体の使用
問題:列挙体は、ステートマシンのケースセレクタとして広く利用されています。ユーザがこの列挙体に対してステートの追加または削除をしようとすると、その列挙体のコピーにつながっている残りの連結線が壊れます。このことは、列挙体を使用したステートマシンの実装を難しくする最も一般的な妨げの1つになっています。
解決策: この問題については以下の2つの解決策が考えられます。
1.変更を加えた列挙体からすべての列挙体がコピーされると、連結線の壊れはなくなる。
2.列挙体を使用して新しいコントロールを作成し、サブメニューから「typedef」を選択する。typedefを選択することで、ユーザがステートを追加または削除した場合に列挙体のすべてのコピーが自動的に更新される。
LabVIEWのステートマシンやその他の高度なアーキテクチャについてご興味のある方は、LabVIEW 実践集中コース 2 カスタマー トレーニングコースをご検討ください。