LabVIEWを触ったことがない方に向けて、それなりのプログラムが書けるようになるところまで基本的な事柄を解説していこうという試みです。
シリーズ17回目としてオリジナルの関数つまりサブVIを作ろうというお話です。
この記事は、以下のような方に向けて書いています。
- LabVIEWで関数を作りたい
- サブVIって何?どんなことに注意するべき?
- 既存のコードの一部をサブVIにしたい
もし上記のことに興味があるよ、という方には参考にして頂けるかもしれません。
なお、前回の記事はこちらです。
オリジナルの関数、サブVIを作る
これまでの回、読んでいただいた方々は以下の内容の基本的なことについて何となくでもわかっていただけたかなと思います。
- 各種データタイプを区別する
- データの保存ができる
- ループで繰り返しの処理ができる
- 条件分岐で場合分けをした処理が書ける
- イベントストラクチャで効率のいいプログラムにできる
実は基本的なアルゴリズムは上記の内容でカバーしているので、これまでの内容だけでもある程度のプログラムは書けてしまえます(もちろんまだまだ紹介していない内容はたくさんあるのですが!)。
実践的にプログラムを作る話はもう少し先で、ここからはいくつかの「知っておくと便利」な事柄について説明していきます。
最終的にプログラムを書くとなったら、かなり多くの処理やアルゴリズムを実装していくことになると思います。
それらの処理については、そのものずばりの関数がLabVIEWの関数パレットにあればいいのですが、中には「これとこれを組み合わせないと実現できない」という処理が出てくると思います。もちろん組み合わせて書くのでも作ることはできるのですが、その場合
- ブロックダイアグラムが大きくなりすぎてすっきりさせたい
- 同じようなコードばかりを書いている
という場面に出くわすことが多々あると思います。そんなときには自分が欲しい処理を実行できるオリジナルの関数を作るのが便利です。これがサブVIになります。他の言語ではサブルーチンと呼ばれたりするものと思います。
例えば、何かコードを作ってから、そのコードを検証するために入力値を模擬することを考えます。その模擬入力値は乱数を使ってプログラム的に用意する場合、複数の値を使用するのであればその都度乱数を用意したほうがいいため、似たようなコードを複数書くことになります。
上の図の例では、赤枠で囲った部分が(定数の違いはありますが)似たようなコードになっています。なので定数の部分は自由に変更できる新たな関数として用意すると見た目もスッキリします。
ちなみに、この「指定範囲の乱数を作る」という関数は、LabVIEW2019以前は自分で作るしかなかったのですが、LabVIEW 2019以降標準関数として加わっています。(それだけ需要があったということかなと思います)
ともあれ、複数の関数を組み合わせて新たな関数を作ることができる、それがサブVIの魅力です。
かしこまって表現していますが、サブVIは「単なるVI」です。サブVIと呼んでいるのは単に役割上こう呼ぶだけで、実態は本当に単なるVIに過ぎないものです。
ただし、メインとなるVIの中で使えるような状態としたVIのことをメインVIと区別してこう呼んでいます。
メインとなるVIの中で使える状態って何?と思うかもしれません。
では、LabVIEWに最初からある、既存の関数のことを考えてみます。この関数をLabVIEWで使用する、とはどういう状態でしょうか?
それは、何か値を入力して結果を出力することができる、という状態です。つまり、サブVIについても「外部から値を入力して、外部に値を出力する」という動作ができればいいということになります。
ということで、サブVIは外部からの入力や出力を受け付けることができるような状態にする必要があります。
LabVIEWの既存の関数も、サブVI状になっているものが数多くあります。例として、信号処理のパレットにある正弦波形の関数を新規VIのブロックダイアグラムについてみてみます。この関数をブロックダイアグラムに置いたらダブルクリックします。すると、ダブルクリックされた関数が右上に表示された別のVIが立ち上がります。
ラベルを見ても詳細ヘルプで見てもSine Waveform.viというようにちゃんと.viの拡張子がついていてVI形式になっていることが分かります。
では、メインVIとやりとりをするためにはどうなっていればいいかですが、サブVI側が制御器や表示器を受け付けられるようになっていればいいわけです。
そのために使用するのが、VIの左上のアイコンの右にあるコネクタペーンと呼ばれるもので、「どこの端子にどの制御器、表示器が紐づいているか」を示しています。
サブVIとしてメインVIと入出力のやりとりができる条件は、このコネクタペーンが用意されているかどうかです。
サブVIの作り方
では早速自分でサブVIを作ってみます。くどいようですがサブVIを作る場合にも関数として必要な条件を満たせばいいわけなので、
- 関数としたい機能を持ったプログラムを作る
- そのプログラムのフロントパネル上で制御器および表示器とコネクタペーンの端子を対応させる
とすればオリジナルの関数としてそのVIを使用できるようになります。
まずは関数としたい機能を持ったプログラムを作ります。ここでは例として、ずっと前に扱った、摂氏と華氏を変換のプログラムを使って「変換関数」として用意することを試したいと思います。
・・・といいたいところなのですが、以前作成したのは華氏から摂氏に変換するプログラムでした。日本では日常で摂氏を使うことが多いので華氏から摂氏に変換する場面はあまり多くないかもしれませんね。ということで摂氏から華氏に変換するプログラムとします。
華氏=1.8×摂氏+32
この式をプログラムに落とし込むのですが、もうこれくらいであれば特に説明不要だと思うので完成形のみ載せておきます。
プログラムが用意出来たところで、次にコネクタペーンの設定を行います。
やり方はとても簡単で、まずはコネクタペーン上で設定したい端子のマス目をクリックします。その後に、その端子と紐づけたい制御器あるいは表示器をクリックします。
すると、コネクタペーンの今設定した端子の色が変わります。この色は、選んだ制御器あるいは表示器のデータタイプの色と一致するはずです。
この操作を全ての端子について行う必要があります。
もし「あれ、表示されている端子の数より制御器、表示器の数が多いぞ?」という場合には
- コネクタペーンのマッピングを別の物にする
- クラスタなどでなるべく関連するデータはひとまとめにする
という処理が考えられます。
マッピングの種類は、コネクタペーンを右クリックしてパターンというところから選べます。ただし、はっきり言って細かいパターンのサブVIはお勧めできません。そのサブVIを使用するときにワイヤの配線がとてもやりづらくなります。
そうするくらいなら、クラスタなどで制御器あるいは表示器の部分をひとまとめにして、そのクラスタをコネクタペーン上で端子と紐づける方が見栄えがすっきりして「それらしい」プログラムになります。
さて、コネクタペーンが用意出来たらもうサブVIとして完成!と言いたいところなのですが、LabVIEWらしさをこれでは活かせません。そう、アイコンの見た目です。
LabVIEWに初めから入っている関数は、どれも見た目で「こういう機能を持っているんだろうな」と視覚的に判断できる絵がついています。
ところが、今用意したサブVIは、デフォルトのアイコンしか表示されていません。
このアイコンも変更することができて、一目で「この関数はこういう機能を持っているんだろうな」ということがわかれば、作った人にも、他の人にも、プログラム全体像が把握しやすくなります。
アイコンの設定は簡単で、アイコンをダブルクリックして表示されるエディタ上で
- 文字を表示させる
- 絵(グリフ)を表示させる
といった編集を行います。グリフには多くの種類があり、それらを自由に配置してオリジナルのアイコンを作ることで見やすくなります。
こうしてコネクタペーンとアイコンの編集を終えたらサブVIの用意は完了です。このサブVIのアイコンを他のVIのブロックダイアグラムにドラッグアンドドロップして使用できるようになりました。
サブVIの注意点
上記でサブVI自体は完成したのですが、いくつか気にしたほうがいい点があります。実際気をつけなくても期待通りの動作をするものが多いのですが、LabVIEWのサブVIとして使うからにはぜひ意識をしたほうがいいこと、になります。
- 適切なルールに沿ってコネクタペーンを設定しよう
- エラーの入出力を用意しよう
- ドキュメントをしっかり残そう
- 再入実行の設定に注意しよう
これらについてみていきますね。
適切なルールに沿ってコネクタペーンを設定しよう
まず、コネクタペーンの配置については、なるべく適切に設定したほうがいいです。「適切」とは、LabVIEWに初めからある関数たちと同じルールでコネクタペーンを設定した状態です。具体的には
- 左側に入力(つまり制御器)、右側に出力(つまり表示器)を用意する
- refnumのようなリファレンスの入出力は左上、右上に用意する
- エラーの入出力は左下、右下に用意する
といったことが挙げられます。
これらを意識することで、プログラムを組んだときに同じ色(種類)のワイヤが見た目上一直線に並び、きれいになります。
なお、リファレンスとはこれまでの内容ではあまり出ていませんでしたが、上の図のようにファイルIOの部分で出てきましたね(refnumのことです)。他にもリファレンスを操作する関数はたくさんあります。
「きれいにする必要あるの?」と思うかもしれません。大いにあります。今までの回でも何度か説明していますが、できる限りプログラムはきれいに組む方がいいです。見やすさ、わかりやすさに直結します。LabVIEWに慣れないうちは最優先事項ではないかもしれませんが、意識することは大切です。
エラーの入出力を用意しよう
次にエラーの入出力を用意しよう、です。適切なルールの部分でも説明しましたが、エラーの入出力は可能な限り用意したほうが好ましいです。
LabVIEWは入力側のノードに全てデータがたどり着いてからその関数を実行するのでした。当然、サブVIであってもこれは同じです。そのときに、関数の実行順番を指定するためにもエラーの入出力が役に立ちます。
また、エラーが起きたときに適切な処理を行わせるためにも意識したほうがいいです。
エラーに対する扱いは上図に示すように「エラーが来たら何も実行せずエラー入力をそのまま出力に渡す」ばかりではなく、作成するプログラムでどう適切に対処するかを考える必要があります。
ドキュメントをしっかり残そう
サブVIを用意する上で忘れがちですが重要なのはドキュメントを残すという点です。
いくらサブVIはアイコンを編集することで見た目上その関数の中身を簡潔に表すことができても、やはり表現には限界があります(よっぼど絵心があればその限りではないかもしれないですが)。
そのために、「このサブVIはこういう動作をするよ」ということを説明する文章を残すことも大切になってきます。より具体的には、LabVIEWに最初からある各関数の詳細ヘルプ(Ctrl + H)を見たときに表示される関数の説明、あれを用意するということです。
これは、「VIプロパティ」から設定できます。
VIプロパティはファイルメニューから飛ぶことができますが、ショートカットが便利です。Ctrl + Iで開くことができます。VIプロパティのウィンドウが開いたら、カテゴリからドキュメントを選択します。
表示されたら、VIの説明という欄にそのVIの説明を書いていきます。どんな動作をするか、入力値や出力値にはどういった値を入れたらいいか、使い方の注意、などを記載することでより分かりやすい関数とすることができます。
また、関数、サブVI全体の説明だけではなく、ブロックダイアグラム上の特定のコードに対して「こういう動作をする」ということを説明するための文字を記載することができます。
ブロックダイアグラム上で何もないところをダブルクリックすると文字を入力できるような枠ができるので、ここに説明を書いてからあとは適切な位置にその枠を移します。
またこの枠の右下にマウスを合わせると小さい四角がでて、これをクリックすることで矢印を出現させることができます。この矢印はブロックダイアグラム上の関数やワイヤに「ひっかける」ことができます。これで、その説明文がどの部分のことを表しているのか、よりはっきり示すことができます。
また、特定の関数が組み合わさった部分に対して説明を加えたいときには、ブロックダイアグラム上で装飾体を使用すると便利なことがあります。
あと、ブロックダイアグラムと比べると優先度は低くなるかもしれませんが、フロントパネル上にも説明の文章を設けた方がわかりやすい場合もあります。ブロックダイアグラム同様、何もない場所でダブルクリックしてテキストを書き込める枠を作って書きこんでいきます。
ここで挙げたポイントは、必ず守らないとプログラムが動作しないということではありません。ただ、守った方がより「美しい」「それっぽい」プログラムを書けるのでぜひ意識してみることをオススメします。
再入実行の設定に注意しよう
サブVIで少し厄介なのがこの再入実行の設定です。同じサブVIを複数個使用している場合に、メインVIの実行速度やタイミングが思った通りではない場合にこの設定を疑う必要があります。
言葉だけではわかりづらいと思うので、あまり実用的ではありませんが説明のために用意した次のプログラムを見てください。
上記のプログラムでは、同じnumgen.viというサブVIが三つWhileループに入っています。そして、ティックカウントとシフトレジスタを用いて、1ループごとにかかっている時間をミリ秒単位で測るようにしています。
さて、このWhileループ、1ループにかかる時間はいくらでしょうか?なお、サブVIはすべてデフォルトの設定で作成されていたとします。
各サブVIには待機関数1000が入っています。つまり1秒待機するということですね。これが3つありますが、それぞれの実行順番に依存関係はありません(エラーワイヤなどで順番が決まっているわけではない状態)。
繰り返し処理の回やタイミングについての話で、各関数は一斉にスタートすると説明してきたのでこれらが一斉に実行されるため、結局ループは1秒ごとに回るように見えます。
ところが、実際にループを動かすと以下のような結果になります。
3秒かかるという結果になりました。3秒って、3つのサブVIでかかっている時間の合計っぽいですよね?
実際その通り、3つのサブVIは(順番そのものは不定ですが)順々に実行されています。
これは、サブVIのデフォルトが「非再入実行」だから、になります。
この非再入実行とは、簡単に言えば、「メインVIのある場所でサブVIが実行されているとき、メインVIの他の場所で同じサブVIを実行しようとしても、最初に実行されているサブVIが終わらないと二つ目以降のサブVIが実行されない」状態になっています。
メインVI上では複数サブVIがあるように見えても、実行されるサブVIの実体は一つなので、それをメインVI上で交互に扱っている、そんなイメージです。この設定をうまく利用したプログラムの書き方(機能的グローバル変数など)もあるのですが、もっとずっと後で取り上げる内容になります。
ただ、プログラムによっては、「順番に実行してほしいわけじゃないし、独立して実行してほしい」という場合もあると思います。
そんな時には、再入実行状態としてあげると順番待ちをしないようになります。
やり方はとても簡単でファイルメニューかあるいはCtrl + Iのショートカットで「VIプロパティ」を開き、「実行」のカテゴリで非再入実行以外を選択するだけです。
二つの選択肢がありますが、要は
- クローン共有による再入実行:一つのサブVIが実行されている間に別の場所で新たに呼び出されるとサブVIのクローンを作って並行して実行することができる。
- クローンの事前割り当てによる再入実行:メインVIにある複数のサブVIに対してあらかじめクローンを用意しておく。
という違いになります。とはいっても初心者のうちはあまりこれらの違いについて気にすることはないと思うので、とにかくどちらかにしておくと、非再入実行ではなくなります。
実際再入実行の状態にしてメインVIを動かすと確かにループにかかる時間は3000ミリ秒ではなく1000ミリ秒になりました。
ただし、再入実行の状態にすると、それだけ「サブVIの実体」が増えることになるのであまりにも増やしすぎるとメモリを圧迫することになりますのでその点は注意する必要があります。
既存のプログラムの一部をサブVIにする
サブVIについて作り方は記載したとおりですが、中にはこんな悩みが出る場合もあると思います。「既に書いたプログラムの一部を関数として使いたいけれど、また新規のVIを用意してその一部分をコピーしてコネクタペーンを設定するのがめんどくさい!」と。
例えば、プログラムの以下の部分をサブVIとして関数化したいと思ったとします。
もちろん、新規VIを用意してコピーしてコネクタペーンを設定して・・・という方法でもできるのですが、LabVIEWでは選択した範囲を自動的にサブVI化するという機能があります。
使い方はとても簡単で、まずサブVIにしたい部分をドラッグアンドドロップで囲みます。そして編集メニューで「選択した範囲をサブVIに変換」するだけで完成です。
これでコネクタペーンの設定まで自動的にやってくれます。とても楽な方法なのですが、さすがにアイコンやドキュメントは自動的に用意されないのでそこは編集する必要があることに注意します。
さて、関数を自由に作ることができてブロックダイアグラムをすっきりさせることができるようになりました。関数にアイコンを付けたり補足の説明をしたりして見やすくすることで、自分が見ても、他の人が見てもわかりやすいプログラムを作ることを心掛けるとデバッグやプログラムの拡張にも役立ちます。
ここら辺の注意事項については補足事項として別記事を用意したので興味がある方は参考にされてみてください。
さて、次回はまた少し毛色の異なる話、プロパティノードとインボークノードを扱います。これらはプログラムの構造の話ではないものの、制御器や表示器の見た目や動作を直接プログラム的に変更できる機能になっています。これを覚えてさらにできることの幅を広げていきましょう。
もしよろしければ次の記事も見ていってもらえると嬉しいです。
ここまで読んでいただきありがとうございました。
コメント