この記事では、LabVIEWプログラムの動作を確認するうえで使えるテクニック、アプローチを紹介しています。
自分が書いた、あるいは他の人が書いたプログラムがよくわからない、また、書いたプログラムが大きくなっていて複雑で意図したとおりに動作しているかわからない、期待したとおりの動作をしていないけれどデバッグとしてどこから手を付けていいかわからない・・・
これらの場合の打開策の一つとして、プログラムの実際の流れを可視化することが有効な時があります。
LabVIEWで元から使えるツールを使用する以外でもプログラムとしてそういった仕組みを仕込むやり方も紹介します。
動作確認の重要性
プログラムというのは、自分で書いたものであれ他の人が書いたものであれ、綺麗に書かれていないと流れを追うのが困難な時が多いかと思います。
自分で書いたものだとしても、半年も経って振り返ってみれば「なんでここでこんな処理しているんだろう」とか「この処理の後はどこに進むんだ?」といった具合に、迷うことがあります。
一方で、整理整頓されていて、素晴らしく良く書けているプログラムであれば比較的短時間で中身の処理を追っていくことがたやすく、ましてや「グラフィカルプログラミング」ができるLabVIEWでは(ちゃんと書けば)プログラムが理解しやすい、というのも事実です。
実際には、頑張れば流れを追うことができないことはない場合がほとんどですが、それはある程度LabVIEWプログラムの構造を理解している人の話であって、学習したての初心者の方にとっては苦痛な作業かもしれません。
あるノードや関数に対して入力が揃ってからその処理が実行される、といったLabVIEWの原則だけ知っていてもなかなか全体を把握できずに先に進めなくて頭を抱えてしまい、LabVIEWを見るのも嫌になる・・・ではもったいないです。
そんなときにプログラムを理解するうえで有効となりうる方法の一つが、実際に動かす、というアプローチかと思います。
何はともあれ実際に動かしてみて、その流れを追ってみよう、という話です。
人から引き継いだプログラムなんかは特に、全体の流れの把握から入って大まかな概要を知ることが理解への近道だったりしますし、意図した動作をしない時に、実は処理の流れ(順番)が違っていた、といったことに気づきやすいのは、やはり実際に動かしたときです。
動かしたとして、どうやってその流れを追うのか、という方法について以下で例を紹介していきます。
プログラムの処理の流れの追い方
LabVIEWでプログラムの動作を確かめる方法としては例えば以下のような方法が考えられます。
- 愚直にワイヤをたどる
- 実行のハイライトを使用する
- 「ダイアログ」の関数を使う
- デスクトップ実行トレースツールキットを使い「ユーザ定義のトレースイベントを生成」で実行の順番を確かめる
- ファイルに保存する
これら5つの方法によるプログラムの動作確認方法についてそれぞれ紹介していきます。
今回は、この記事を見ながら実際に試しやすいように、LabVIEWをインストールしたと同時にインストールされるサンプルを例にとっていきます。
サンプルは、LabVIEWのツールバーのヘルプメニューから「サンプルを検索」で開くNIサンプルファインダから探せます。
サンプルプログラムをそのまま編集してしまうと次回以降同じサンプルを開いた際にそのまま編集後のプログラムが開くことになるため、サンプルを開いたら別名で保存してそちらを編集するようにしてください。
サンプルファインダには、プログラムファイル(vi)の状態かプロジェクトファイル(lvproj)の状態で項目が並んでいます。
どちらも別名保存すればいいのですが、プロジェクトの場合には依存項目もまとめて複製しておくと便利です。
愚直にワイヤをたどる
まずは一番シンプルな方法、それが、愚直にワイヤをたどる方法、です。
LabVIEWの強みの一つは、プログラムの書き方にあり、データの受け渡しに必ずワイヤを使用することになるため、このワイヤをたどっていけばプログラムの流れを追うことができる点です。
(・・・のですが、実際には単純にワイヤを追うだけではプログラム全体の概要をつかめない場合もあり、そういった場合には次以降で紹介する方法を駆使することになります。)
実際にワイヤをたどる上で有用な機能は、ワイヤのハイライト機能と検索機能です。
ワイヤのハイライトとは、ワイヤをダブルクリック、あるいはトリプルクリックすることで、そのワイヤが途中で分岐していたとしても途切れていない限りワイヤ全体を点線でハイライトしてくれる機能で、プログラムがあまり整理されておらずかつブロックダイアグラムの広範に広がっている場合にどこがつながっているかを確認するために使用できます。
また、複数のワイヤが入り組んでいてデータの流れがわかりづらいような場合も役に立ちます。
サンプルのプログラムは整理されて作られているものが多いですが、例えばNIサンプルファインダでPIDと検索して出てくる「PIDをオフラインでオートチューニングする」のサンプルで使用されているサブVIである「Plant System.vi」の中ではクラスタからバンドル解除されてワイヤが多く使用されていますが、こういった場合にワイヤを見失わないようにすることができます。
また、ローカル変数やグローバル変数があるようなプログラムについては、検索機能を使用することでどの部分とどの部分が関連しているかを見つけやすくなります。
以下はローカル変数の例ですが、右クリックで検索を選ぶと、そのローカル変数の基になった端子を探せたり、同じ端子に対してローカル変数が複数作られている場合には、他のローカル変数の一覧を表示させることができます。
通常LabVIEWでは変数を使用するとワイヤが「途切れる」ためデータの流れを読むには都合が悪いのですが、検索機能を使用することでデータのつながりをたどることはできるので、この機能をうまく活用できるかどうかで読みやすさが全然変わったりします。
グローバル変数の場合には、同じ検索機能で「グローバル定義」と「グローバルリファレンス」が選べます。
グローバル定義、とは、グローバル変数の大元であるファイル(拡張子は.viですがブロックダイアグラムをもたない)を表示させる機能で、グローバルリファレンス、はそのグローバル変数が他の場所でも使用されている場合一覧表示する機能です。
ワイヤをたどれるところはたどって、それでも難しい、あるいはプログラムが大きすぎてたどり切れない、という場合には、以下で紹介する他の方法も検討していきます。
実行のハイライトを使用する
次に、デバッグツールである実行のハイライトを使用する方法です。
手短な例ですが、State Machine.lvprojサンプルにあるState Machine Fundamentals.viを例にとります。
このサンプルは、ユーザーがフロントパネル上で起こしたイベント(ブールボタンをクリック)に応じてそれぞれの処理を実行するためのステートに移る、シンプルなプログラムです。
実行のハイライトは、ブロックダイアグラム上の電球マークで選択できます。
LabVIEW 2023 Q3からは、実行のハイライトを使用時の速度を切り替えられるようになりました。(下の画像は2023 Q3より古いのでその表示はないですが)
実行のハイライトを有効にした状態で、プログラムを実行します。
プログラム全体の動作がゆっくりになり、今現在どのような処理がどこで実行されているかをアニメーションとして表示してくれます。
また、各ワイヤを流れるデータの値も確認することができます。
比較的小規模のプログラムや、大きなプログラムの部分的な流れの確認については実行のハイライトが便利です。
そもそもはデバッグツールなので、各ワイヤに流れているデータを確認できるだけでも有用なのですが、複数のループに処理がまたがるような作りのプログラムだとデータの流れを追うのが難しくなるので注意が必要です。
実行のハイライトを含めたデバッグ方法について他にも知りたいという方は関連記事を見てみてください。
「ダイアログ」の関数を使う
次のアプローチは、ダイアログの関数を使用する方法です。
これにより、強制的に今どのような状態であるかを通知することができ、順番がわかりやすくなります。
実はこの方法は最初に紹介した実行のハイライトの話で例に出していたステートマシンのサンプルで既に使用されています。
同サンプルプログラムを実行したら特定のメッセージを表示するダイアログが出てきていたはずですが、これはダイアログを表示するための関数を使用しているからです。
このサンプルのように、それぞれのケースでダイアログの関数とそのメッセージを表示させることで、今どのステートにいるかといったことがわかります。
ステートを表す列挙体に表示器をつけてフロントパネル上に表示させるというのもいいのですが、各ステートが一瞬で過ぎてしまうと表示を確認するのが難しくなる一方、ダイアログの関数であれば確実にそれぞれのダイアログが出てプログラムが止まるので、見逃す、ということはなくなります。
表示器に履歴付きプローブをおくと履歴は見えますが、それぞれのステートが一瞬で終わると追いにくくなるのは同じですし、プローブ上では列挙体は数値として扱われるのでステート名が確認できません。
開発時に正しく意図したとおりに動作が進んでいくかを確認するためにメッセージを表示する機能をあらかじめ持たしておく、というのも有効な場合があります。
ステートマシンのテンプレートプロジェクトは、メッセージデータをクラスタとして保持し各ステートでその値を参照するという書き方になっているので参考になると思います。
テンプレートプロジェクトを確認する場合には、新規でプロジェクトを作る画面から選択します。
このテンプレートは、先ほどのNIサンプルファインダにあったサンプルと動作自体は似ていますが、作りが若干異なり、こちらのテンプレートの方がステートマシンを作り始めるベースプログラムとしやすい構造になっています。
ただし注意点としては、短期間で繰り返しを行うようなループの中にこのダイアログの関数を置いてしまうと、延々とダイアログが表示され続け、プログラムを止めることができなくなってしまいます。
そのため、ある程度は事前に「ここがずっと繰り返し処理が続きそうだな」という仕組みを何となくでも把握しておかないと、ダイアログ地獄にはまってしまうことになります。
そんなときの対処のために、一定回数同じダイアログを連続して表示する場合にはそれ以降そのダイアログは表示させない、といった作りのダイアログ関数になるようにサブVIを使用する手もあります。
ユーザ定義のトレースイベントを使う
上のダイアログの関数を使う方法だといちいちダイアログが表示されて面倒だ、という場合には、デスクトップ実行トレースツールキットを使用して、プログラムの流れをいちいち妨げずに実行の流れを知ることができます。
今回は、NIサンプルファインダの基本機能フォルダにあるループおよびストラクチャフォルダ内の「キューメッセージハンドラの基礎」のサンプルを例にとります。
このサンプルは、先ほどまでのステートマシンのサンプルと似たような動作をするのですが、二つのループに分かれ、ループ間でメッセージのやり取りを介して処理を進めていく構造になっています。
まずはサンプルに何も手を加えずにデスクトップ実行トレースツールキットを使っていきます。
デスクトップ実行トレースツールキットはプロファイルの項目から選択して開けます。
使用する場合は必ずデスクトップ実行トレースツールキットを実行してからプログラムを実行するようにします。
フロントパネル上でいくつか操作を行い、結果を見てみます。
以下は、フロントパネル上で動作1と動作2のボタンを一回ずつ押して停止させた場合の結果ですが、操作の内容によってトレース結果は当然変わります。
ではこのトレース結果に対してプログラムの流れが追いやすくなるように修正を加えていきます。
そのために使用するのが、ユーザー定義のトレースイベントを生成関数です。
この関数を使用することで、トレース結果に任意の文字列や数値の情報を追加することができます。
今回は以下のようにこの関数を使用していきます。
イベントストラクチャでは、イベントケースの中でこの関数を使用しています。
イベントケースの外側(でWhileループの内側)に配置してもトレース結果に表すこと自体はできるのですが、本来は「イベントが検出されてからそのイベントに応じた処理が実行される」という順番なのに、「イベントに応じた処理が実行されてからイベントが検出される」かのような表示になるので、以下の図のようにイベントストラクチャの中に置いておくようにします。
もう一つのループでもこの関数を使用していますが、こちらの場合には処理後に「どのような処理を行った」かという情報をトレース結果に載せられればいいので、ケースストラクチャの外に関数を置いて、そこに各ケースから異なる文字列を与えています。
この状態でプログラムを実行します。
先ほど同様、フロントパネル上のボタンを一回ずつ押して停止させたときの結果が以下のような感じになるのですが、プログラムが実行された順に各操作が表示されるため、どのタイミング(順番)でどの処理が実行されたかが一目でわかります。
今回は単に各ケースの名前をトレース結果に表示しただけですが、工夫次第でさらに見やすく加工することができます。
このデスクトップ実行トレースツールキットはviの状態であってもEXEの状態であっても使用することができるので、アプリケーションとして完成させた後の動作をチェックするのにも利用できます。
もっと詳しく知りたいよ、という方は以下の記事を参考にしてみてください。
これにより、プログラムの流れを可視化できるのが利点なのですが、LabVIEWの最上位エディションではないとデスクトップ実行トレースツールキットそのものを使用できないのが弱点となります。
ファイルに保存する
最後に、プログラムの動作をファイルに保存するといった方法も紹介します。
この方法はプログラムの流れの確認だけでなく、アプリケーションが予期せぬ挙動を示す場合のデバッグログとしても有効となります。
プログラムは、引き続き「キューメッセージハンドラの基礎」をベースとします。
ファイルに保存するためのサブVIは、機能的グローバル変数の形にしています。
ループが複数あってそれぞれに対してログを取る前提とする場合、ループの数と同じだけサブVIを用意しますが、それらの中身はほとんど同じで、initケースで指定するファイル名を変えるくらいです。
これらのサブVIを下の図のようにそれぞれのループに対して配置しています。
書き込むデータは何でもいいのですが、プログラムの流れを確認するのであればケースの名前が簡便です。
また、必要に応じてそのケースで扱っている特徴的なデータやパラメータを書き込む文字列に指定することもできます。
以下ではメッセージ処理ループのUpdate Displayケースにて表示する文字列のデータもログするようにしています。
実行した結果の一例を以下に示しています。
ループごとにファイルが分かれるのでイベントの発生順などに対応付けは必要ですが、それぞれのループ単体で見た時の挙動を知るのはこの方法が便利です。
一方でプログラム全体の流れを知るのであれば、一つのファイルに保存した方が見やすいので、そのような場合の実装例も紹介します。
用意するサブVIはキューを使用しています。
キューで渡すデータの種類としてはループを識別するための文字列とメッセージデータである文字列なので、これらのクラスタをキューのデータタイプとしています。
メインVIにて以下のように実装します。
ファイルに保存するループはメインVIに配置して、各ループの様々なケース上に配置したサブVIからのデータを受けとります。
機能的グローバル変数の形にしなくても各ループにワイヤを通せばいいのですが、それだと見た目がごちゃごちゃしてしまう可能性があるので注意が必要です。
プログラムを実行すると以下のように一つのファイルにそれぞれのループで何の処理が実行されたかを記録できます。
この方法は処理ログ、デバッグログにも有効なのですが、デザインテンプレートの種類によってはこの方法が取りづらく、実装が大変になったり、プログラムの実行速度によってはログファイルのサイズが大きくなってしまうのが弱点です。
キューメッセージハンドラのようなプログラムの場合には、各ループにてステートマシンの構造を持っていればそのループごとにファイル保存の形式でどのような順番で処理が実行されたかを記録することはできますが、見やすくするには工夫が必要になります。
それぞれ一長一短あり、どの方法でプログラムの処理を追っていこうか、悩むのもいい経験になると思いますが、ループが複数あるようなプログラムの場合にはデスクトップ実行トレースツールキットやファイル保存の方法が便利です。
本記事では、プログラムの動作確認の方法として実際にプログラムを動かして処理の流れを見るための例を紹介しました。
他の人のプログラムを見る、引き継いだプログラムを理解する、デバッグを行う、あらゆる場面で活用しうるアプローチかと思いますので、参考になればうれしいです。
ここまで読んでいただきありがとうございました。
コメント