LabVIEW グラフ・チャートの凡例名をプログラム的に変更する

サンプル – グラフ・チャート凡例名を変更する.vi

波形チャートやグラフの凡例名はデフォルトで “プロット0” になっていますが、これをプログラム的に変更してみます。

clip_image001[5]

プロパティノードを使用して、

  1. チャートのプロット数を取得
  2. アクティブプロットを0~9まで順次指定し、
  3. 指定したプロット番号のプロット名を変更

という手順です。

clip_image002[4]

プロット数はプロパティの「凡例→行数」で取得。

clip_image003[4]

アクティブプロットはプロパティの「アクティブプロット」で設定。

clip_image004[4]

プロット名はプロパティの「プロット→プロット名」

clip_image005[4]

以上。

LabVIEW で起動したアプリがビルドされているか、開発環境で起動しているかを判断する

Technorati タグ:

LabVIEW で現在の VI がビルドされているか、開発環境で起動しているかを判断したいときがたまにあります。

例えば”Exit ボタン”を押したときに、

  1. ビルドされている(exe) だったらそのままウィンドウを閉じて終了する
  2. VI で実行している(開発中) なら VI の動作を停止するだけにしたい。
    (開発中は VI が閉じてしまうのは避けたい。)

この場合は、以下のコードで「アプリケーション:種類」を取得することで、現在開発環境かビルドかを判別できます。

clip_image001

開発環境で実行している場合

clip_image002

ビルド環境で実行している場合

clip_image003

LabVIEW で時刻を取り扱う

LabVIEW 内で、時刻を扱いたいときはタイムスタンプを取得し、double に変換することで数値として取り扱う事が出来る。

タイムスタンプを double に変換し、さらにタイムスタンプに変換を行うことで、タイムスタンプに戻すことが可能である。

U64 でも可能だが、秒以下のms が切り捨てられなくなってしまう。

clip_image001

clip_image002

LabVIEW から呼ぶことが出来る DLL を生成する(クラスライブラリ)

サンプルプロジェクト (C++, LabVIEW のプロジェクト)

 

前回から時間が空いてしまいましたが、ブログを更新。

更新してなかったですが、何もしていなかったわけではなく Windows8 やもちろん LabVIEW とも毎日のように戯れていました。

前回で、LabVIEWから DLL 内の関数を呼ぶことが出来ましたが、今回はクラスライブラリを呼びたいと思います。

関数とクラスライブラリの違い

当たり前の話ですが「関数を呼ぶ」というのは、C の関数を呼ぶと言うことです。

関数にパラメータを与えて、演算結果を返してもらう分にはこれで十分ですね。

しかし C++ 側でクラスが作ってあって、そのメソッドを LabVIEW で呼びたい場合はどうすればいいのでしょうか?

通常クラスライブラリを使うときは、

  1. クラスのインスタンスを生成
  2. そのインスタンスのメソッド(関数)を呼ぶ。
  3. 戻り値は、メソッドから返される場合もあれば、クラス内に保持される場合もある。

という流れで行ないます。

しかし LabVIEW ではあくまで呼べるのは関数ベースです。

クラスライブラリを呼ぶ方法

LabVIEW からは関数しか呼べません。と言うことは関数経由でクラス生成し、メソッドを呼べば良いことになります。

上記の 1. 2. 3. をクリアする方法を考えてみます。

1 のインスタンスを生成。

まずクラスのインスタンスはポインタな訳ですから、関数の中でクラスを生成し、そのポインタを LabVIEW 返すことでインスタンスの生成は出来そうです。

で、「ライブラリ関数の呼び出しノード」を見てみると、データタイプに「符号付きポインタサイズ整数」があります。プロトタイプで見てみると intptr_t なので、ポインタを受けることは出来そうです。

clip_image001

2 のメソッドを呼ぶ。

「ライブラリ関数の呼び出しノード」のパラメータを見てみると、渡すことが出来るデータタイプに「符号付きポインタサイズ整数」がありますので、取得したポインタを渡すことが出来ます。

では、関数にインスタンスのポインタを渡して、その中でメソッドを呼び、戻り値を受けることが出来そうです。

clip_image002

3 クラス内部の値を受け取る

これも「ライブラリ関数の呼び出しノード」にポインタを渡して、クラス内部の値を返してやれば良さそうです。

クラスライブラリを作る

では早速クラスを呼ぶための準備をします。

クラスを追加する

まずはクラスライブラリを作るために、Visual Studio でクラスを追加します。

クラス名は TestClass で生成すると、TestClass.cpp, TestClass.h がプロジェクトに追加されます。

clip_image003

クラスを実装する

TestClass の仕様は、

  1. クラスメンバ変数として int value を持っている。
  2. Add メソッドに渡された値と value を足し算する。
  3. Sub メソッドに渡された値と value を引き算する。
  4. 足し引きしないで値を設定・取得する

という簡単なクラスにします。

以下実装。

class __declspec(dllexport) は、関数を公開するのと同じく、DLL 内のクラスを外部に公開するために必要な定義です。これを忘れると外部から読んでもエラーになりますので、注意が必要です。

TestClass.h

#pragma once
class __declspec(dllexport) TestClass
{
public:
    int value;
    TestClass(void);
    ~TestClass(void);

    void Set(int value);
    int Get();
    int Add(int value);
    int Sub(int value);
};

TestClass.cpp

#include "StdAfx.h"
#include "TestClass.h"

TestClass::TestClass(void)
{
    this->value = 0;
}

TestClass::~TestClass(void)
{
}

void TestClass::Set(int value)
{
    this->value = value;
}

int TestClass::Get()
{
    return this->value;
}

int TestClass::Add(int value)
{
    this->value += value;
    return this->value;
}

int TestClass::Sub(int value)
{
    this->value -= value;
    return this->value;
}

 

 

クラスのインターフェイスを実装する

クラスが出来たら、LabVIEW からクラスを呼ぶためのインターフェイス関数を作成します。

Visual Studio で、cpp ファイル “LVIF_TestClass.cpp” という名前で追加します。

コードは以下の通り。

#include "StdAfx.h"
#include "TestClass.h"

_declspec(dllexport) TestClass* LVIF_TestClass_GetInstance()
{
    TestClass *param = new TestClass();
    return param;
}

_declspec(dllexport) void LVIF_TestClass_Delete(TestClass *arg)
{
    delete arg;
}

_declspec(dllexport) void LVIF_TestClass_Set(TestClass *arg, int value)
{
    return arg->Set(value);
}

_declspec(dllexport) int LVIF_TestClass_Get(TestClass *arg)
{
    return arg->Get();
}

_declspec(dllexport) int LVIF_TestClass_Add(TestClass *arg, int value)
{
    return arg->Add(value);
}

_declspec(dllexport) int LVIF_TestClass_Sub(TestClass *arg, int value)
{
    return arg->Sub(value);
}

インスタンスを作成しポインタを返す LVIF_TestClass_GetInstance 関数と、

値を設定・取得する LVIF_TestClass_Get, LVIF_TestClass_Set 関数、

Add, Sub メソッドに対応した LVIF_TestClass_Add, LVIF_TestClass_Sub 関数、

インスタンスを削除する LVIF_TestClass_Delete 関数を用意しました。

それぞれ _declspec(dllexport) をつけておきます。

クラスを LabVIEW から呼ぶ

インスタンスの生成

DLL が出来たら、次に LabVIEW からクラスを呼んでみます。まずはインスタンスの生成から。

VI を作成し「ライブラリ関数の呼び出しノード」をブロックダイアグラム上に配置します。右クリック→構成ダイアログで、作成した DLL を選択。

関数名を見ると、先ほどのインターフェイス関数が見えてますね。

まずは GetInstance を選択します。

clip_image004

返り値で、符号付きポインタサイズ整数を選択。

clip_image005

これを実行してみると、何か値が返ってきました。

これが TestClass のポインタですね。

clip_image006

メソッドを呼ぶ

では次に Get, Set をして値を初期化します。

新しい「ライブラリ関数の呼び出しノード」ので、Set 関数を選択。

clip_image007

Set 関数は void のため返り値はデフォルト(void) のまま。

最初の引数でクラスのポインタを渡します。「パス」は必ず「値」を選択します。

clip_image008

2番目の引数で設定する int32 値にしておきます。

clip_image009

Get 関数はこちらのように、int32 を受け取ります。arg1 は Set 関数と同じくポインタを指定しておきます。

clip_image010

実行するとこのように、値を 1 で初期化できました。また生成したポインタと、Set/Get 関数で使用したポインタが同じ事も確認できました。

clip_image011

では次に Add/Sub 関数も足してみたら結果はこの通り、0 で初期化→1を100回加算→1を100回減算→結果 0 という一連の流れをクラスで実行させることが出来ました。

clip_image012

デバッグ方法

このクラスライブラリ呼び出し時に DLL にブレークポイントをしたい場合は、Visual Studio でプロセスにアタッチをすれば可能です。

まずデバッグビルドで DLL を作成し、LabVIEW 側で VI を起動します。

そうしたら Visual Studio のメニュー→デバッグ→プロセスにアタッチ→ LabVIEW.exe を選択し、アタッチボタンを押します。

これでソースコードにブレイクポイントをつけておけば、DLL 実行時にそこで実行が停止しステップ実行など出来ます。

clip_image013

はまり所

c側からの戻り値が bool の場合、LabVIEW で「符号付き8ビット整数」を選択します。32ビットだと戻り値がおかしくなります。

まとめ

  1. クラスのコンストラクタ、メソッドを呼ぶ関数を別途作成する。
  2. クラスのインスタンスポインタを LabVIEW 側で保持することで、クラスの生成・実行は可能
  3. インスタンスの削除を忘れないように
  4. 戻り値の bool は注意。

LabVIEW から呼ぶことが出来る DLL を生成する(C++)

えーと今回は、LabVIEW から C++ で書いたライブラリ (DLL) を呼んでみたいと思います。

DLL に信号処理アルゴリズムが実装されていて、それを LabVIEW から呼びたいという事がたまに発生します。

LabVIEW はグラフの描画が用意なので、DLL のデバッグ時も見やすくて良いですね。

LabVIEW のサンプルを実行する

LabVIEW から DLL を呼ぶサンプルは、

C:\Program Files (x86)\National Instruments\LabVIEW 2010\examples\dll

に4つ程入っていますので、こちらを開いてみると良いと思います・・・・

と思ったら VI はちゃんと動くが、VC のプロジェクトが変換できませんでした。

恐らく私のマシンに VC 64bit コンパイラがインストールされていないからです。VS2010 インストールの時にチェック外したんだった orz。

clip_image001

ここはサンプル無しでやってみます。

DLL を生成する

新規に Visual Studio で DLL を作成してみます。

プロジェクトテンプレートは、VC++ → Win32 → Win32 プロジェクトを選択。

clip_image002

アプリケーションの種類は “DLL” を選択。

今回は ANSI 準拠のプログラミングだけをするので、MFC や ATL も使いません。この辺はお好みで。

clip_image003

プロジェクトが作成できたら、まずプロジェクトプロパティを開き、追加のインクルードディレクトリを設定しておきます。

C:\Program Files (x86)\National Instruments\LabVIEW 2010\cintools

フォルダに NI 提供のヘッダファイルなどが入っているので、こちらは必ずインクルードされるようにしておきます。

clip_image004

cintools の中身clip_image005

ソースコードを生成する

まずは試しで、入力された2つの値を加算して返す関数を実装してみます。

ソリューションエクスプローラ→ソースコードに、プロジェクト名.cpp があるので、開いて以下のコードをコピペします。

// DLL_w_LabVIEW.cpp : DLL アプリケーション用にエクスポートされる関数を定義します。
//

#include "stdafx.h"

#include "extcode.h"
#include <windows.h>

_declspec (dllexport) int Add(int in1, int in2);
_declspec (dllexport) int Add(int in1, int in2)
{
    return(in1 + in2) ;
}

clip_image006

でビルドすると、Debug フォルダに DLL やデバッグシンボル一式が出力されています。これで DLL 生成は OK。

clip_image007

LabVIEW から DLL を呼ぶ

DLL が作成できたら VI から読んでみます。

LabVIEW から DLL を呼ぶには「ライブラリ関数呼び出しノード」を使用します。

関数パレット→コネクティビティ→ライブラリ&実行可能ファイル→ライブラリ関数呼び出しノード

ブロックダイアグラム上にノードを置いたらコンテキストメニューから「構成」を選択し、「ライブラリ名またはパス」に先ほど生成した DLL を選択します。

で、DLL のエクスポート情報に基づいて「関数名」に一覧が表示されるので、そこから呼び出したい関数を選択します。

呼び出し規約はデフォルトの “C” のまま。

スレッドは「任意のスレッド」にしておくと、for ループなどの並列化時には並列化されたスレッドから呼び出させるため、パフォーマンスが向上すると思われます。

ちなみにDLL 内部で GUI を操作しているような場合は、「UI スレッドで実行」にしないと LabVIEW の操作がブロックされ、ハングアップしたようになってしまいます(恐らく)。

例えば DLL 内部で「ファイルを開くダイアログ」のような GUI を使う場合は、必ず UI スレッドで実行するようにしましょう。

clip_image008

パラメータで、関数のシグネチャに合わせた引数・戻り値のタイプを設定します。

clip_image009

今回は 2つの int 引数、1つの int 戻り値なので以下のように設定。

左側リストボックスの「+」を押すと、引数を追加する事が出来ます。

clip_image010

構成が済んだら、ノードに引数と戻り値を受け取る端子が出るようになったので、制御器・表示器を接続します。

clip_image011

実行すると 100+10 = 110 が返ってきました。

clip_image012

配列を DLL に渡す

次は配列を DLL に渡し、結果も配列で受け取ります。

基本的には、

  • 結果の配列も LabVIEW 側で作成して関数にはポインタで渡す
  • 関数内でポインタ先の配列に値を入れる
  • 渡した配列の長さ(要素数)も一緒に渡しておく

という事です。

ソースコード

ソースコードは以下の通り。配列はポインタにしておきます。

#include "stdafx.h"

#include "extcode.h"
#include <math.h>

_declspec(dllexport) void accuArray(double *input,
                                       int input_length,
                                       int *output);
_declspec(dllexport) void accuArray(double *input,
                                       int input_length,
                                       int *output)
{
  int i;

  for(i = 0; i < input_length; i++)
  {
    output[i] = input[i] * 2;
  }
}

 

LabVIEW 側

LabVIEW 側は、入力配列と出力配列の配列形式を「配列データポインタ」にしておくことで、関数にポインタ渡しできるようになります。

clip_image013

結線はこのような形ですね。

clip_image014

クラスタを DLL に渡す

LabVIEW からクラスタを渡すと、DLL 側では構造体ポインタが渡ってきたように見えます。

ソースコード

構造体を定義し、そのポインタを受け渡すようにします。

後はいつも通り構造体へと値を設定すれば大丈夫。

#include "stdafx.h"

#include "extcode.h"

/* LabVIEW created typedef */
typedef struct {
    double DBL;
    long I32;
    char Boolean;
    } TD1;

_declspec(dllexport) void CLUSTERSimple(TD1 *input, TD1 *output);

_declspec(dllexport) void CLUSTERSimple(TD1 *input, TD1 *output)
{
  output->DBL = input->DBL * input->DBL;
  output->I32 = input->I32 / 2;
  if(input->Boolean)
  {
    output->Boolean = FALSE;
  }
  else
  {
    output->Boolean = TRUE;
  }
}

 

LabVIEW 側

配列と違ってクラスタの場合は「タイプに適応」というタイプにしておきます。LabVIEW 側でデータ構造を解析して適当なクラスタに変換してくれるようです。

clip_image015

まとめ

基本的には、LabVIEW から値やポインタもそのまま渡ってきますし、分かっている方なら問題なく使えるかと思います。C# のマーシャリングと同じ要領ですね。

さらに複雑なサンプルは、

C:\Program Files (x86)\National Instruments\LabVIEW 2010\examples\dll\data passing

にあるので、参考になります。

クラスタ構造内の配列にアクセスするなどはコーディングが面倒ですが、複雑なデータ構造を渡す必要が無いのであれば、LabVIEW 側でシンプルな構造にして渡してあげるのが、コードもシンプルになり良さそうです。

めでたし、めでたし。