複数の制御器で値変更を行う

Tips

スポンサーリンク

この記事で扱っていること

  • 複数の制御器で値変更を行う方法

を紹介しています。

LabVIEWプログラムの特徴は、データのやりとりをワイヤと呼ばれる線で結んでデータフローを表現したり処理を実装していくことにあります。

このワイヤ配線には、原則として「複数の入力を受け付けない」決まりがあります。

一つの表示器に、複数の制御器や定数をつなぐことはできない、というものですね。

ただ、アプリケーションのニーズによっては、どうしても複数の箇所から一つの場所に値を入力したい場合があると思います。

そんな場合への対応方法としては、ローカル変数を用いるやり方がありますが、表示器(および値を受け取る側)に対してローカル変数を複数箇所用いるのは競合状態の原因にもなりあまり得策ではありません。

(一方で、制御器に対してローカル変数を用いるのは、制御器の初期化等、どうしても必要になる場面だったりそれ以外の方法がないという場面があるので、必要以上に使用しないのであれば、有用な使い方と言えます。)

もう少し工夫をするとなるとローカル変数を使用しない方法も考えられるので、今回は競合を起こさないようにローカル変数を使用する場合と使用しない場合とで、複数の制御器で値の変更を行う方法の例を紹介します。

スポンサーリンク

どんな結果になるか

ちょっと凝った作りとしていますが、タブの中に制御器があったり、タブの外に制御器があるような場面を想定し、これらから数値や文字列の表示器にアクセスすることを考えます。

一つの箇所で値を変更すると、表示器はもちろん、操作しなかった他の制御器も値が変更されます。

データタイプに制限はないので、数値以外の、例えば文字列データでも同じような仕組みを作れます。

プログラムの構造

複数の制御器から一つの表示器にアクセスする、これを表示器のローカル変数を使用しないで行う、そのために、キューを使用して表示器へのアクセスをイベントから行うようにしています。

そのため、全体的な構造としてはイベント駆動の生産者・消費者プログラムとなっています。

二つのループがありますが、それらが始まる前にキューを用意しています。

キューで扱うデータタイプは、文字列とバリアントをまとめたクラスタ、としています。

この部分の文字列は、列挙体でもいいと思います。

ループが始まる前に「init」という文字列を入れておくことで、イベントが起こらなくても下のWhileループのinitステートが実行されます。

今回、initステートでは特に何もしていません。

記事後半で紹介するプログラムではこのinitステートを使用するのでその前振り、と思ってください。

もちろん、実際に何か初期化処理として実装したいことがあればこの中に書いておきます。

数値の値変更イベントが起きたら、その数値(下の図では「新規値」から値を得ることで、どの数値制御器の値が変わったとしてもその値を取得できるようにしています)をキューで扱っているクラスタにあるバリアント(ここではバリアントに「data」という名前をつけています)に配線しています。

なので、同じイベント内には、そのイベントで値を変更しうる制御器全ての値変更イベントをまとめて登録する必要があります。

また、クラスタの文字列データとしてはnumchangeという文字を入れておきます。

こうすることで下のWhileループでnumchangeのステートを実行でき、そこで「バリアントからデータに変換」関数を使用してバリアントを数値に変換、これを表示器と、全ての制御器のローカル変数に配線しています。

こうすることで、どの数値制御器の値が変わっても、指定した全ての数値制御器のローカル変数も一緒に値を変えられる、というわけです。

文字列の方も理屈は同じです。

なお、この方法の場合、イベントの発生元となった制御器(つまり既に値が変わっている制御器)に対してもローカル変数で値を書くという無駄がありますが、この無駄を防ぐのは必要以上にプログラムが複雑になるので、なるべく簡単に実装するとしたらこの形になると思います。

ここまでの図で「あれ、フロントパネルにある制御器の名前と、ブロックダイアグラムにある対応する制御器の名称が違わない?」と気づいた方もいるかと思います。(例えばブロックダイアグラム上の数値制御器には全て「.numresult」とつけているがフロントパネル上の表記にそんなのついていないのがわかるかと思います)

これも、記事後半で紹介するプログラムの布石なのですが、ちなみにこの違いは、「ラベル」表示か「キャプション」表示かという指定の違いとして表れています。

ブロックダイアグラムに表示されるのは「ラベル」なのですが、フロントパネルに表示させるのは「ラベル」以外に、これとは異なる名称でも受け付けられる「キャプション」というものがあり、今回はキャプションを表示することで、フロントパネルの表示が奇妙な(?)名称にならないようにしています。

ローカル変数を使用しない場合

ローカル変数を使用する方法の弱点を挙げるとすると、当然ですがローカル変数を作る必要があります。当たり前ですが。

プログラムを作ってそのままその後全く機能追加などの修正を行わないような場合にはまぁそれでもいいのですが、新たに制御器を配置する必要があった場合に問題となることがあります。

特に大規模なプログラムになるとローカル変数を使用するプログラムでは、データの流れを追いにくいのが厄介です。

むしろブロックダイアグラムはいじらずに修正できればそれの方が簡単、ということで、工夫をしていきます。

使用するのは、ローカル変数ではなく、リファレンスになります。

ただし、リファレンス取得に関しては制御器のラベル名に規則を設けて、その規則に合致した制御器以外は、他の制御器が変更されても値を変えないようにします。

例として以下のような状況を考えます。

この図で、my numberという名前がついた数値制御器は、それ以外の数値制御器の値が変わっても影響を受けないようにしています。

リファレンスを使用するこのようなプログラムは、手動でリファレンスを用意するのではなく、プログラム的にリファレンスを用意するようにしています。

構造としては前半で紹介したイベントベースの生産者・消費者プログラムですが、initステートにおいて、必要な制御器のリファレンスをプログラム的に取得するようにしています。

上のプログラムのget_ref.viがリファレンスをとるサブVIで、中身は以下のようになっています。

メインVIから配線しているkeywordをラベル名に持つ制御器のリファレンスを取るという操作をしています。

この関係上、フロントパネル上で「一つの表示器にアクセスしたい複数の制御器」すべてに対して、ラベル名に規則(この場合、名前に「.numresult」とつける)を設けています。

こうすることで、後からプログラムに制御器を追加する場合でも、この規則に沿った制御器を配置すれば、プログラムの書き換えなしに、自動的にその制御器のリファレンスを取得できるようにしています。

命名規則は何でもよく、今回は数値制御器に対しては(キャプション名)+「.numresult」、文字列制御器に対しては(キャプション名)+「.stringresult」をキーワードとしています。

Forループの中の処理の最初に.tabのパターンがあるかを探しているのは、タブ制御器の中の制御器や表示器は一度タブ制御器のPagesリファレンスを取得した上でないとリファレンスが扱ないためです。

また、Forループの出口、refの配列を出している部分のトンネルモードは「連結」でかつ「条件」を指定しているのにも注意してください。

init以外のステートに対しては、イベントストラクチャの方で「新規値」以外に「制御Ref」から「どの制御器の値が変更されたか」の文字列も取得してこれをキューのクラスタの「data」バリアントにクラスタとして入力しています。(バリアントはクラスタも受け付けるので、「クラスタの中にクラスタが入っている」という状態になります)

下のWhileループでは、キューから受け取ったクラスタデータをバンドル解除し、そのうちのクラスタ部分にある文字列(つまり、値の変更が起きた制御器の名前。ここではキャプションをとっています)をサブVIに渡しています。

サブVIであるdata_modify.viの中身は以下の通りで、メインVIから渡された文字列、つまり「値が変わった制御器」以外のリファレンスをプロパティノードに渡し、値プロパティを変更しています。

メインVIから、値(数値や文字列)を渡しているので、これで、既に値が変更された制御器以外も値を変更できるようになります。

ローカル変数の時には、値が変わった制御器もまたローカル変数で値を書き込んでいたので、それと比べると多少は効率がいいかなと思います。

同じ理屈で文字列制御器に対しても同様なことが行えます。

get_ref.viもdata_modify.viも、扱うデータタイプ(数値か文字列か等)に関係なく同じサブVIが使えるのがミソです。

本記事では、複数の制御器で値変更を行う方法を紹介しました。

本来はローカル変数やリファレンスを使用して値を変更するというプログラムはパフォーマンスに影響が出るため避けるべきですが、どうしても複数の箇所から値を変更したい、という場合には本記事で紹介したような書き方が有効となることもあると思うので、参考になればうれしいです。

ここまで読んでいただきありがとうございました。

コメント

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