この記事で扱っていること
- 設計書からプログラムのベースを作る方法
を紹介しています。
注意:すべてのエラーを確認しているわけではないので、記事の内容を実装する際には自己責任でお願いします。また、エラー配線は適当な部分があるので適宜修正してください。
LabVIEWには、VIスクリプトという機能があります。
この機能を使用することで、「プログラムからプログラムを作る」つまり、「viを作る」ためのviを作ることができます。
この機能の活用の例は、例えばプログラムのテンプレートを作るような場合が挙げられます。
本記事では、テキストファイルに書いた、ステートマシンプログラムの設計図を読み込み、そのステートマシンに必要なケースと列挙体を自動的に生成するようなプログラムの例を紹介します。
VIスクリプトを扱えればこのようなプログラムも書ける、という例として参考にしてもらえればと思います。
どんな結果になるか
フロントパネルには、設計書(csvファイルを想定)のファイルパス制御器と、生成したVIを保存するフォルダのパス制御器、そしてVIの名前を書く文字列制御器があります。
また、生成後にメインVIを開くかどうかを指定するブール制御器も付けています。
用意する設計書となるファイルについては、これから作るステートマシンのプログラムで使うステートのラベルを1列目に、それぞれのステートがケースストラクチャで表された際の、ケースのサブダイアグラムラベルを2列目に書いておきます。
サブダイアグラムラベルとは、簡単に言えばそのケースがどういう役割をするかを説明するもの、です。

プログラムを実行すると、テキストファイルに書いた通りのステートとサブダイアグラムラベルがついたステートマシンのベースが出来上がります。

実際にVIを見てみると、一般的なステートマシンとしての列挙体やWhileループ、ケースストラクチャとシフトレジスタが組みあがっているのがわかります。
あとはここをベースに実際の処理を各ケースの中に書いていくだけですね。

プログラムの構造
VIスクリプトをふんだんに駆使したプログラムになっています。
VIスクリプトについては、使用するのに設定を行う必要があります。
デフォルトではこの機能は無効になっているので、LabVIEWのツールバーの「ツール」の最下部にある「オプション」を開いて、VIサーバのカテゴリでVIスクリプトに関する設定を有効にしておきます。
こうすることで、関数パレットにVIスクリプトの機能が表れて使えるようになります。

プログラムはそこそこ大きいです。
VIスクリプトを使用することでVIを生成しているのですが、そのVIの中に配置するWhileループなり各種端子なりワイヤ配線なりを全て指定する必要があってそれらの指定をリファレンスで管理するのですが、少し配線を間違えると意図したとおりのVIを生成できないので注意してください。

最初の方のパートでは、新規VIを作成し、フロントパネルやブロックダイアグラムのサイズの指定を行っています(サイズは適当に決めます)。
また、プロパティノードでブロックダイアグラムを開くかどうかを指定することができるのでこの指定を「終了後メインvi開く?」のブール制御器で行います。
生成するVIに必要な列挙体の作り方は以下の図を参考にしてみてください。
Whileループも必要になりますが、今回は列挙体とは別個に配置することになるため、列挙体の作成もWhileループの作成も全てVI自体のリファレンスを使用しています。

次のパートでWhileループにシフトレジスタを設定したり、ケースストラクチャを用意しています。
今回はケースストラクチャはWhileループの中に配置するため、ケースストラクチャを生成する際に指定するリファレンスはWhileループのリファレンスを配線しています。
上の図と照らしてながら配線を間違えないように注意してください。
ケースストラクチャに用意するケースは、素のケースストラクチャが持っている2つのケース以外に用意するため、Add Frameでケースの数を増やす場合には「必要なケース数-2」の数だけ増やすことになり、今回はForループのカウント端子にその指定を行っています。

次のパートでは、各ケースのリファレンスを使用してそれぞれのケースのラベルを指定するとともにサブダイアグラムラベルの指定も行っています。
そしてケースストラクチャの中に配置した列挙体がケースストラクチャの右側の出力トンネルに配線されるようにしています。
この時点ではまだ、一つのケースにある列挙体しか出力トンネルにつないでいない状態となります。

最後のパートでは、全てのケースの中の列挙体を出力トンネルにつないだり、その出力トンネルとWhileループのシフトレジスタをつないでいます。
最後の最後では、VIを保存するためにインボークノードで「保存」メソッドを使用しています。

各4つのパートを示した図は縮尺を合わせているので、ワイヤ配線を間違えないように注意しながらプログラムを組んでみてください。
生成したVIを含んだプロジェクトも生成する場合
実際には、VI単体で生成するというよりは、プロジェクトエクスプローラを用意してここに生成したVIが登録されているような状態の方が実用的かと思います。
そのため、フロントパネルにVIの生成先パスの代わりにプロジェクトエクスプローラの生成先パスを指定するようにし、プロジェクト名とメインVI名を同じにするように指定する形でプログラムを書き換えてみます。

追加する部分は、あまり多くなく、上で紹介したプログラムの最後の方に、以下の図の赤枠の内容を加えるだけです。

列挙体をタイプ定義にする場合
ステートマシンを作ると考えた際に上のプログラムで都合が悪いのは、列挙体がタイプ定義になっていない点です。
そこで、別途再帰処理を用いたサブVIを用意して、列挙体をタイプ定義したものに置き換えることを考えます。

まずそもそものプログラムでタイプ定義された列挙体を用意すること自体は、以下の図の赤枠の内容を追加すればできるようになります。
タイプ定義した列挙体は、新規VIの関数を使用して、メインVIとは別個にファイル(ctl)を作る操作となるため少しややこしいです。
Style IDというプロパティを渡している部分がありますが、下の図のようにすることで、メインVI用に生成した(タイプ定義していない)列挙体と、別個に作成するタイプ定義した列挙体の内容を同じくするためにこのように組んでいます。

あとは、プログラムの最後の方にさらに以下の図の赤枠部分の内容を追加する形で修正を加えます。
途中でreplacer_recursive.viというサブVIがありますが、このサブVIが、列挙体をタイプ定義した列挙体に置換する役割を果たしています。

replacer_recursive.viは以下のようなフロントパネルを持ちます。
メインVIからは、VIパス(生成するVIのパス)とタイプ定義した列挙体のパスを入力します。

ブロックダイアグラムは以下のようになっています。
このプログラムの説明は別の記事で紹介しているのでここでは詳しくは扱いません。

一応フロントパネルとブロックダイアグラムはこちらに載せているので、再帰処理について知っているよという方は別記事に飛ばなくても以下に示した図の通り組んでもらえれば分かるかなと思います。
再帰処理って何?な方は、さらに別記事でどのようなものか、どのような設定が必要かを紹介しています。

今回はWhileループやケースストラクチャしかストラクチャ要素がないため、以下の図のケースが再帰処理VIに含まれていれば十分かと思います。

本記事では、設計書からプログラムのベースを作る方法を紹介しました。
今回の例ではテキストファイルから読み取っていますが、もちろんユーザーがその場でステートマシンを定義するという方法もあり得るかと思います。
例えばユーザーインタフェースとなるサブVIを用意し、そこに2次元の文字列配列を用意して、今回テキストファイルで書いたような内容を配列に書いてこれを読み込ませるようにするといった感じですね。
VIスクリプトを積極的に使用するアプリケーションというのはあまりないかもしれませんが、応用例の一つとして参考になればうれしいです。
ここまで読んでいただきありがとうございました。
コメント