LabVIEWを触ったことがない方に向けて、それなりのプログラムが書けるようになるところまで基本的な事柄を解説していこうという試みです。
シリーズ25回目として機能的グローバル変数の紹介です。
この記事は、以下のような方に向けて書いています。
- 機能的グローバル変数ってなに?
- 機能的グローバル変数はどうやって使うの?
- 機能的グローバル変数の使用例を知りたい
もし上記のことに興味があるよ、という方には参考にして頂けるかもしれません。
なお、前回の記事はこちらです。
機能的グローバル変数とは
前回までの記事で、キューやノーティファイアといった、ループ間でデータを受け渡すための手法を扱いました。
これらは、ローカル変数やグローバル変数をなるべく使わず、かつ効率よくループ間でデータを受け渡しするためにはどうすればいいかということで紹介してきました。
対して今回扱う「機能的グローバル変数」は「グローバル変数」と付いていて、使用する機会が少ない・・・のかと思いきや、結構便利でよく使われます。
通常のグローバル変数は単なる「箱」であり、ブロックダイアグラムがなかったことからもわかる通り、データの出し入れ(読み書き)しか行いません。これに対し、機能的グローバル変数(英語でFunctional Global Variableなので以降はFGVと書きます)は名前の通り、任意の機能を持ったグローバル変数です。
ではその最も簡単な形を以下に紹介します。このFGVは、入力された文字の末尾に「_new」という文字を追加する機能を持っています。
え、これはVIのように見えるけど・・・?と思った方。正解です。FGVの正体はVIです。
正体がわかったところで、このブロックダイアグラム、とても違和感があると思いませんか?全体をWhileループで囲っているくせに、条件端子には定数でTRUEが配線されています。つまり、繰り返しを行うかと思いきや、一回しか実行されない形となっています。
「じゃあWhileループ無くていいじゃん」と思うかもしれませんが、FGVの本質はそこではなく、シフトレジスタにあります。よくみると、このWhileループ、シフトレジスタが初期化されていません。そのため、例えばこのWhileループが何回か実行される場合、シフトレジスタには前回実行された際の値が残っているということになります。ここがFGVのミソになります。
実際にFGVを使用したプログラムの例を挙げてみます。
もちろん、これは単なる例として見せているだけなので実際上記のようにプログラムを書くことはないと思います。
要は、ブロックダイアグラム上でいくらでも「機能」をつけることができながら、シフトレジスタによって値の保存ができ、さらにそもそもはサブVIとしての扱いなのでグローバル変数と同じく様々なVI、場所から呼び出せるもの、それがFGVなのです。
機能的グローバル変数の条件
先ほどの図で紹介した機能的グローバル変数、その特徴は、条件端子にTrueが配線されたWhileループと初期化されていないシフトレジスタ、ケースストラクチャ、そして列挙体だと思います。Whileループやシフトレジスタ、ケースストラクチャに列挙体・・・これらを見て何か思い出しませんか?
そう、ステートマシンですね。ステートマシンの基本的な要素としても、これらの要素が関わってきていました。違いとしては、Whileループの条件端子にTrueの定数を配線するか、特定の条件を配線するかの違いになります。
Whileループの条件端子が定数でTRUEとなっているだけでFGVとしての役割が確立し、そこそこ活躍します。
ただし、FGVは「見えないところ」でもう一つ条件があります。それはVIプロパティの設定です。Ctrl + IでVIプロパティを開き、カテゴリから実行を選んだときに左側に表示される項目で、非再入実行が選択されている必要があります(デフォルトでは非再入実行となっていますが)。
この機能的グローバル変数、値を保持して、メインのVIのいろいろな場所で呼び出されて使用される際にその保持した値を更新したり読み出すことができなくてはいけません。
その際に、VIの設定が非再入実行ではなく再入実行の設定になっていると、あるところで呼び出したサブVIを別の場所で呼び出した際に異なるインスタンスになってしまい機能を活かせません。
デフォルトではこの設定はどのVIであっても非再入実行になっているので特に意識されなかったりしますが、もし思った通りに動作しない場合にはこの設定が正しいかを確認する必要があります。
FGVの使いどころ
さて、それではもう少し具体的にFGVの役割を知るために使われ方の例を挙げてみます。
FGVはそれ単体だけではあまり意味を持たないため、実際にプログラムの中で使用される様子を通してご紹介するために二つのプログラムのシナリオを見ていきます。
まず初めのプログラムでは、ユーザーのフロントパネル上での操作によって、任意の制限値幅の乱数を足したり引いたり、あるいは履歴をリセットしながら数値の値を変えるような動作を考えます。
機能的グローバル変数には、それらの動作に応じて
- リセット
- 乱数を足す
- 乱数を引く
- そのまま
という機能を持たせました。各機能は下のように定義しています。
これをメインVIの中で呼び出すことになります。イベントストラクチャを使用してそれぞれのボタン操作をイベント(ブールの値変更イベント)として検知、各FGVの機能を呼び出します。
これで望みのプログラムの完成です。
メインVIの中で直接値に手を加えないのもポイントです。余計な操作をすることで値を期待しない形で書き換えるようなことがなく、またメインVIのブロックダイアグラムもすっきりします。
もう一つ、別の例として、時間をカウントするFGVを用意してみます(個人的にはこのパターンのFGVをよく使用します)。
こちらは少し条件を多めにして、メインのプログラムの動作としてこんな条件を想定します。
- 開始ボタンを押すことでデータが表示され始められ、開始してからの経過時間を表示する
- 一時停止ボタンを押すと、データの表示が止まり、経過時間の表示の更新も止まる
- もし一時停止ボタンをまた押すとデータの表示が再開され、経過時間の表示も続きから再開される
- もし運転停止ボタンが押されると、データの表示が止まり、経過時間表示もリセットされる
- 停止ボタンを押すことでプログラムが終了する
- どのフロントパネル上の動作も50ミリ秒以内に反応するようにする
このような動作を行うプログラムを、今度はイベントストラクチャを使用しないで書いてみます。
なお、フロントパネル上のボタンのうち、一時停止のボタン以外はラッチ動作で、一時停止のボタンはスイッチ動作としています。
動作時の様子も以下に示しておきます。
さて、このプログラムはどのように書けばいいかもうわかるでしょうか?一例を紹介します。(ステートマシンのデザインパターンを採用しました)
まずはFGVです。このFGVが呼び出された時点での時間を知り、前に呼び出されたときの時間から引き算をすることでFGVが呼び出される間の経過時間を測れるようにしています。また、累計時間のデータも保存しています。
このFGVを使用したメインVIは例えば以下のような構造にします。
メインVIのステートマシンのステートの数がFGVのステートの数と異なるのは、同じステートを使用したりそもそもFGVを使用しないものもあるのが理由です。メインVIの待機のステート以外は以下のようにすると望みの動作をします。
FGVの難しいところは、作るプログラムによってFGVに求められる機能が全然異なることです。また、メインVIの状態によってさらに条件をつけてFGV側で処理を行う場合もあるので、FGVが絡んだプログラムはあらかじめしっかり設計をしたうえで作らないと余計に複雑になることがあるので注意します。
さて、この記事で機能的グローバル変数の説明も終わりました。ここまでで紹介してきた内容だけでも様々なプログラムが書けるはずです。
ということで次回は、これまでのまずこれシリーズの知識を総動員してLabVIEWでゲームを作るということを考えます。その名も反射神経ゲームで以下のルールとします。
- ユーザーがスタートボタンを押すとゲームスタート
- ゲームスタート後、ランダムなタイミングでLEDが点灯したらすぐにボタンを押す
- ボタンを押すと再びLEDが消え、またLEDが点灯するのを待つ。これを合計3回繰り返す
- LEDがついてからボタンを押すまでの時間が測られ、累計時間が表示される
- 3回のゲームのそれぞれが終わると、それぞれが終了したことを表わすブールが光る。また状況説明として今何回目なのかを文字列表示器に表す
- もしLEDがついていないときにボタンを押してしまうとペナルティ。200ミリ秒が追加される
このゲームでは、3回の合計で累計時間が少なければ少ないほど反射神経が鋭い、ということになります。フロントパネルとしては以下のような感じでしょうか。
動作時の様子の一例を以下に挙げます。
上記の動作を行うためのプログラムとしてどのように書けばいいかを考えます。もちろん答えは一つではないと思うので、次回私が紹介するのはほんの一例だと思います。もし興味がある方は一度自分でプログラムを組んでから次回の記事を確認してみてください。
もしよろしければ次の記事も見ていってもらえると嬉しいです。
ここまで読んでいただきありがとうございました。
コメント