2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# WinFormsでVISA計測器制御アプリを作る — 環境構築からハマりポイントまで全部入り

2
Posted at

はじめに — 製造業の現場で計測器制御アプリを作り続けて5年

こんにちは、JodyCraft です。製造業の生産技術職として、C# で業務アプリを作り続けて5年になります。

製造ラインの検査工程って、とにかく計測器が多い。マルチメーター、オシロスコープ、ファンクションジェネレーター……。これらを手作業で操作して、結果をExcelに手入力して、判定して……という作業を毎日やっていると、「いや、これPCで自動化できるでしょ」って思うわけです。

で、実際に作ったら1台あたり3分かかっていた検査が30秒で終わるようになった。手入力ミスもゼロ。このインパクトはかなり大きくて、現場の人にも「これすごいね」と言ってもらえた。

ただ、ここに至るまでの道のりは正直ラクではなかった。VISA(Virtual Instrument Software Architecture) という計測器制御の規格があるんですが、日本語の情報がとにかく少ない。公式ドキュメントは英語、StackOverflowの回答もピンとこない、NuGetパッケージも似た名前のやつが複数あってどれを使えばいいかわからない。

この記事では、そんな「もっと早く知りたかった」情報を全部まとめます。

📋 この記事の前提

  • C# の基本文法がわかる(変数、メソッド、クラスを書ける)
  • Visual Studio がインストール済み(2022 以降推奨)
  • WinForms プロジェクトを作成したことがある
  • 計測器の知識は不要(ゼロから説明します)

この記事を読み終わると、こんなことができるようになります:

  1. VISA 規格の全体像を理解できる
  2. 開発環境を正しく構築できる(ここでハマる人が本当に多い)
  3. C# WinForms から計測器に接続してコマンドを送れる
  4. 実務で使える計測器制御アプリの骨格が作れる

所要時間の目安は、環境構築に30分、サンプルアプリの実装に1〜2時間です。

そもそもVISA って何? — 「計測器のUSBドライバみたいなもの」

VISA は Virtual Instrument Software Architecture の略で、ひとことで言うと 「計測器とPCをつなぐ共通のAPI規格」 です。

計測器とPCの接続方法はいくつかあります。

接続方式 特徴 現場での使用頻度
GPIB 古いけど安定。大量の計測器がある現場で根強い ★★★★☆
USB (USBTMC) ケーブル1本で手軽。新しい計測器はだいたい対応 ★★★★★
LAN (VXI-11/HiSLIP) 離れた場所の計測器を制御できる。工場で便利 ★★★☆☆
RS-232C レガシー機器で使用。設定がちょっと面倒 ★★☆☆☆

これ、接続方式ごとに別のライブラリを使い分けなきゃいけないとしたら地獄ですよね。VISAはその問題を解決してくれる。接続方式が違っても同じコードで計測器を制御できる — これがVISAの最大の価値です。

計測器に送るコマンドは SCPI(Standard Commands for Programmable Instruments) という標準コマンド体系を使います。たとえば *IDN? は「お前誰だ?」と計測器に聞くコマンドで、どのメーカーの計測器でも同じように応答を返してくれます。

正直、最初は「VISAって何?SCPIって何?GPIB?USBTMC?」と略語の洪水で頭がパンクしました。でも、やることは結局3つだけ です。

  1. VISAドライバをインストールする(PCに計測器を認識させる)
  2. VISAアドレスを調べる(どの計測器と話すか指定する)
  3. SCPIコマンドを送る(計測器に指示を出す)

この3つさえ理解すれば、あとは組み合わせるだけ。難しく考える必要はありません。

開発環境を準備する — ここが最大の山場

環境構築はこの記事で最も丁寧に書くセクションです。なぜなら、製造業の現場で計測器制御アプリを作ろうとして最初に挫折するポイントが、ほぼ100%ここだから。コードを書く前に環境でハマって「もういいや」ってなる人を何人も見てきました。

必要なもの一覧

項目 推奨バージョン 備考
Visual Studio 2022 (17.8+) Community版で十分
.NET 8.0 以降 .NET Framework 4.8 でも可
NI-VISA ランタイム 2024 Q3 以降 必須。これがないと何も始まらない
NuGet: Ivi.Visa 最新版 VISA.NETの標準API
NuGet: NationalInstruments.Visa 最新版 NI-VISAの.NET実装
計測器 SCPI対応ならメーカー問わず
接続ケーブル USB/GPIB/LANのいずれか

Step 1: NI-VISA ランタイムをインストールする

ここが最初の関門。

NI-VISA は National Instruments(現 NI)が提供する VISA ドライバで、業界のデファクトスタンダードです。Keysight や菊水などメーカー独自の VISA ドライバもありますが、迷ったら NI-VISA を入れておけば間違いない。実際に5年間使ってきて、NI-VISA で動かなかった計測器はほとんどありません。

  1. NI-VISA ダウンロードページ にアクセス
  2. 最新版をダウンロード(1GB以上あるので時間かかります)
  3. インストーラーを実行。カスタムインストールで「.NET対応」にチェックが入っていることを確認
  4. 再起動を求められたら素直に再起動する

⚠️ ハマりポイント: 複数のVISAドライバを入れない

Keysight IO Libraries と NI-VISA を両方入れると、ドライバが競合して「どちらのVISAで接続するか」が曖昧になり、謎のエラーが出ます。どちらかに統一してください。自分は過去に両方入れてしまい、半日溶かしました。

Step 2: NI MAX で計測器の接続を確認する

NI-VISA をインストールすると NI MAX(Measurement & Automation Explorer) というツールが一緒に入ります。これが地味だけど超重要。

NI MAX を起動して、左のツリーから「デバイスとインターフェース」を開くと、PCに接続されている計測器が一覧表示されます。ここに計測器が表示されていれば、PC側の認識はOK。

表示されない場合は:

  • ケーブルが抜けている(笑い話みたいだけど、工場あるある)
  • USB機器なら、計測器側のUSB設定が「USBTMC」になっているか確認
  • GPIBなら、GPIBカード(NI GPIB-USB-HS 等)のドライバが入っているか確認

NI MAX で計測器を右クリック → 「VISAテストパネルを開く」で、コマンドの送受信テストができます。ここで *IDN? を送って応答が返ってくれば、環境構築は完了。コードを書く前に、必ずここで通信できることを確認してください。 ここを飛ばして「コードが動かない!」と言ってる人が本当に多い。

Step 3: Visual Studio でプロジェクトを作成する

1. Visual Studio を起動
2. 「新しいプロジェクトの作成」→「Windows フォーム アプリ (.NET)」を選択
3. プロジェクト名: VisaInstrumentController(好きな名前でOK)
4. フレームワーク: .NET 8.0
5. 「作成」をクリック

Step 4: NuGet パッケージを追加する

ここでまたハマりやすいポイントがある。VISA関連のNuGetパッケージは似た名前のものが複数あって紛らわしい。

パッケージマネージャーコンソールで以下を実行します。

Install-Package Ivi.Visa
Install-Package NationalInstruments.Visa

もしくは NuGet パッケージマネージャーのGUIから検索してインストールしてもOK。

💡 どのパッケージを使うべきか?

パッケージ名 役割 必要?
Ivi.Visa IVI Foundation 標準の共通API定義 ✅ 必須
NationalInstruments.Visa NI-VISAの.NET実装 ✅ 必須(NI-VISA使用時)
Ivi.Visa.Interop COMベースの旧API ❌ 古い。使わない
VisaNS NI独自の旧API ❌ 非推奨

Ivi.Visa + NationalInstruments.Visa の組み合わせが2025年現在の正解です。 古い記事で VisaNSIvi.Visa.Interop を使ったサンプルを見かけますが、今から新規で使う理由はありません。

計測器との最初の通信 — *IDN? を送ってみよう

さあ、ここからコードを書きます。まず最もシンプルなところから。計測器に *IDN?(識別コマンド)を送って、名前を返してもらいましょう。

VISA アドレスとは

計測器と通信するには、その計測器の「住所」にあたる VISAリソース名(VISAアドレス) が必要です。

USB0::0x0699::0x03A4::C042015::INSTR      ← USBの場合
GPIB0::12::INSTR                           ← GPIBの場合
TCPIP0::192.168.1.100::inst0::INSTR        ← LANの場合

このアドレスは NI MAX のデバイス一覧で確認できます。コードにハードコーディングしてもいいですが、実務ではテキストボックスから入力させるか、接続済みデバイスを列挙して選ばせるのが普通です(後述します)。

最小限のサンプルコード

WinForms のフォームにボタンとテキストボックスを1つずつ配置して、こんなコードを書きます。

using System;
using System.Windows.Forms;
using Ivi.Visa;
using NationalInstruments.Visa;

namespace VisaInstrumentController
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            // VISAアドレスをテキストボックスから取得
            string visaAddress = txtVisaAddress.Text;

            try
            {
                // ResourceManager はVISA通信の入り口
                using var resourceManager = new ResourceManager();

                // 計測器との通信セッションを開く
                using var session = (MessageBasedSession)resourceManager.Open(visaAddress);

                // タイムアウトを設定(ミリ秒)
                session.TimeoutMilliseconds = 3000;

                // *IDN? コマンドを送信
                session.RawIO.Write("*IDN?\n");

                // 応答を読み取る
                string response = session.RawIO.ReadString();

                // 結果を表示
                txtResult.Text = response;
                MessageBox.Show($"接続成功!\n{response}", "成功",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (VisaException ex)
            {
                // VISA固有のエラー
                MessageBox.Show($"VISA エラー: {ex.Message}\nエラーコード: {ex.ErrorCode}",
                    "接続エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"予期しないエラー: {ex.Message}",
                    "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

これをビルドして、txtVisaAddress に NI MAX で確認したアドレスを入力し、ボタンを押す。計測器から KEYSIGHT TECHNOLOGIES,34465A,MY12345678,A.02.14 みたいな文字列が返ってきたら成功!

え、たったこれだけ?って思いますよね。そう、VISAの基本はこれだけOpenWriteReadString の3ステップ。あとはSCPIコマンドを変えるだけで、電圧測定も波形取得もできます。

実用的な計測器制御アプリを作る

最小限のサンプルが動いたら、もう少し実用的な機能を足していきましょう。実際の業務で使うアプリには、最低でも以下の機能が必要です。

  • 接続済みデバイスの自動検出
  • コマンドの送受信(自由に入力できるターミナル的な機能)
  • エラーハンドリング
  • 通信ログの記録

接続済みデバイスを列挙する

VISAアドレスを手入力させるのは不親切だし、タイプミスの原因になる。ComboBox に接続済みの計測器を一覧表示して選ばせるのがベターです。

private void LoadAvailableDevices()
{
    cmbDevices.Items.Clear();

    try
    {
        using var resourceManager = new ResourceManager();
        // 接続されているVISAデバイスを全て検索
        var resources = resourceManager.Find("?*INSTR");

        foreach (var resource in resources)
        {
            cmbDevices.Items.Add(resource);
        }

        if (cmbDevices.Items.Count > 0)
        {
            cmbDevices.SelectedIndex = 0;
            lblStatus.Text = $"{cmbDevices.Items.Count} 台の計測器が見つかりました";
        }
        else
        {
            lblStatus.Text = "計測器が見つかりません。接続を確認してください。";
        }
    }
    catch (Exception ex)
    {
        lblStatus.Text = $"デバイス検索エラー: {ex.Message}";
    }
}

resourceManager.Find("?*INSTR") がポイント。ワイルドカードで、VISA 経由で認識されている全デバイスを返してくれます。フォームの Load イベントや「更新」ボタンの Click イベントでこのメソッドを呼べば、ComboBox にデバイス一覧が入ります。

計測器制御クラスを分離する

ボタンのイベントハンドラに通信処理をベタ書きしていると、計測器が増えたときに破綻します。実務では計測器ごとにクラスを分けるのが鉄則。

using System;
using Ivi.Visa;
using NationalInstruments.Visa;

namespace VisaInstrumentController
{
    /// <summary>
    /// VISA計測器との通信を管理するクラス。
    /// IDisposable を実装して、using 文で確実にリソースを解放する。
    /// </summary>
    public class VisaInstrument : IDisposable
    {
        private MessageBasedSession? _session;
        private readonly ResourceManager _resourceManager;
        private bool _disposed;

        public bool IsConnected => _session != null;
        public string VisaAddress { get; private set; } = string.Empty;

        public VisaInstrument()
        {
            _resourceManager = new ResourceManager();
        }

        /// <summary>
        /// 計測器に接続する
        /// </summary>
        public void Connect(string visaAddress, int timeoutMs = 5000)
        {
            if (IsConnected)
                Disconnect();

            _session = (MessageBasedSession)_resourceManager.Open(visaAddress);
            _session.TimeoutMilliseconds = timeoutMs;
            VisaAddress = visaAddress;
        }

        /// <summary>
        /// 切断する
        /// </summary>
        public void Disconnect()
        {
            _session?.Dispose();
            _session = null;
            VisaAddress = string.Empty;
        }

        /// <summary>
        /// コマンドを送信する(応答なし)
        /// </summary>
        public void Write(string command)
        {
            EnsureConnected();
            _session!.RawIO.Write(command + "\n");
        }

        /// <summary>
        /// コマンドを送信して応答を受け取る
        /// </summary>
        public string Query(string command)
        {
            EnsureConnected();
            _session!.RawIO.Write(command + "\n");
            return _session.RawIO.ReadString().Trim();
        }

        /// <summary>
        /// 計測器の識別情報を取得する
        /// </summary>
        public string GetIdentification()
        {
            return Query("*IDN?");
        }

        /// <summary>
        /// 計測器をリセットする
        /// </summary>
        public void Reset()
        {
            Write("*RST");
        }

        /// <summary>
        /// エラーキューを確認する
        /// </summary>
        public string CheckError()
        {
            return Query("SYST:ERR?");
        }

        private void EnsureConnected()
        {
            if (!IsConnected)
                throw new InvalidOperationException(
                    "計測器に接続されていません。先に Connect() を呼んでください。");
        }

        public void Dispose()
        {
            if (!_disposed)
            {
                Disconnect();
                _resourceManager.Dispose();
                _disposed = true;
            }
        }
    }
}

ここで IDisposable を実装しているのが重要。 VISA のセッションは OS レベルのリソースを握っているので、使い終わったら確実に解放しないとリソースリークする。最初のうちは Dispose を忘れて「2回目の接続が必ず失敗する」という謎の現象に悩まされました。原因は、前回のセッションが解放されずに残っていたから。using 文で囲むか、IDisposable パターンを使う。これは必須です。

フォームから使う

上のクラスを使うと、フォーム側のコードはシンプルになります。

public partial class MainForm : Form
{
    private VisaInstrument? _instrument;

    public MainForm()
    {
        InitializeComponent();
    }

    private void btnConnect_Click(object sender, EventArgs e)
    {
        try
        {
            _instrument = new VisaInstrument();
            _instrument.Connect(cmbDevices.Text);

            string idn = _instrument.GetIdentification();
            AppendLog($"✅ 接続成功: {idn}");

            btnConnect.Enabled = false;
            btnDisconnect.Enabled = true;
        }
        catch (VisaException ex)
        {
            AppendLog($"❌ VISA エラー: {ex.Message}");
        }
    }

    private void btnDisconnect_Click(object sender, EventArgs e)
    {
        _instrument?.Dispose();
        _instrument = null;

        AppendLog("🔌 切断しました");
        btnConnect.Enabled = true;
        btnDisconnect.Enabled = false;
    }

    private void btnSendCommand_Click(object sender, EventArgs e)
    {
        if (_instrument == null || !_instrument.IsConnected)
        {
            AppendLog("⚠️ 先に計測器に接続してください");
            return;
        }

        string command = txtCommand.Text.Trim();
        if (string.IsNullOrEmpty(command)) return;

        try
        {
            // ?で終わるコマンドはクエリ(応答あり)
            if (command.EndsWith("?"))
            {
                string response = _instrument.Query(command);
                AppendLog($">> {command}");
                AppendLog($"<< {response}");
            }
            else
            {
                _instrument.Write(command);
                AppendLog($">> {command}  (送信完了)");
            }
        }
        catch (VisaException ex)
        {
            AppendLog($"❌ コマンド実行エラー: {ex.Message}");
        }
    }

    private void AppendLog(string message)
    {
        string timestamp = DateTime.Now.ToString("HH:mm:ss.fff");
        txtLog.AppendText($"[{timestamp}] {message}{Environment.NewLine}");
    }
}

AppendLog メソッドでログにタイムスタンプを付けているのは、実務で問題が起きたときの調査に役立つから。「14:32:05にコマンド送ったのに14:32:10まで応答が来てない」みたいな情報があると、タイムアウトの設定を見直すべきか判断できます。

実務で使える応用テクニック

ここからは、基本がわかった上での応用編。実際に製造ラインで計測アプリを運用してきた中で「これは最初から知っておきたかった」というテクニックです。

電圧を測定してCSVに保存する

製造業で一番多いユースケースがこれ。マルチメーターで電圧を測って、結果をファイルに残す。

/// <summary>
/// DC電圧を測定して結果を返す
/// </summary>
public double MeasureDcVoltage(VisaInstrument instrument)
{
    // DC電圧測定モードに設定
    instrument.Write("CONF:VOLT:DC AUTO");

    // 測定を実行して結果を取得
    string result = instrument.Query("READ?");

    if (double.TryParse(result, out double voltage))
    {
        return voltage;
    }

    throw new FormatException($"測定値のパースに失敗: '{result}'");
}

/// <summary>
/// 連続測定してCSVに保存する
/// </summary>
public async Task MeasureAndSaveAsync(VisaInstrument instrument,
    string csvPath, int count, int intervalMs)
{
    using var writer = new StreamWriter(csvPath, false, System.Text.Encoding.UTF8);
    writer.WriteLine("No,DateTime,Voltage_V");

    for (int i = 1; i <= count; i++)
    {
        double voltage = MeasureDcVoltage(instrument);
        string line = $"{i},{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff},{voltage:F6}";
        writer.WriteLine(line);

        // UIスレッドをブロックしないように少し待つ
        await Task.Delay(intervalMs);
    }
}

ここでちょっと面白い話なんですが、最初は Thread.Sleep で待ってたんですよ。そしたらUIがフリーズして、現場のオペレーターさんに「固まった!壊れた!」って呼ばれた。await Task.Delay に書き換えたらフリーズしなくなった。WinFormsで時間のかかる処理をするなら async/await は必須です。

タイムアウトの調整

計測器制御で地味に重要なのがタイムアウトの設定。デフォルトだと短すぎて、測定に時間がかかるコマンド(高精度測定とか)がタイムアウトエラーになることがある。

// 通常のコマンド: 3秒で十分
session.TimeoutMilliseconds = 3000;

// 高精度測定や波形取得: 10〜30秒必要なことも
session.TimeoutMilliseconds = 30000;

自分の経験上、デフォルトの3秒で動くコマンドが大半だけど、オシロの波形取得だけは30秒に設定しておかないと安定しなかった。計測器のマニュアルに「最大応答時間」が書いてあるので、それを見て余裕を持たせるのがコツ。

現場でハマったポイント5選

ここからが、公式ドキュメントには載っていない実戦のノウハウ。5年間で遭遇したトラブルの中で、特に多かった5つを紹介します。

1. 「ResourceManager が見つからない」エラー

System.TypeInitializationException:
  The type initializer for 'NationalInstruments.Visa.ResourceManager' threw an exception.

原因: NI-VISA ランタイムがインストールされていない。または、32bit/64bit が合っていない。

解決策: NI-VISA を再インストールする。プロジェクトのターゲットプラットフォームが x64 なのにNI-VISAが32bitしか入っていない(あるいはその逆)パターンが多い。プロジェクトのプロパティ → ビルド → 「プラットフォーム ターゲット」を確認。迷ったら Any CPU にしておけば大体なんとかなります。

2. 2回目の接続で失敗する

原因: 前回のセッションが正しく閉じられていない。

解決策: IDisposable パターンを使って確実にリソースを解放する(前述のコードを参照)。フォームを閉じたときに FormClosing イベントで Dispose を呼ぶのも忘れずに。

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    _instrument?.Dispose();
}

これを入れ忘れて、アプリを閉じて→開き直すと接続できない、を何回やったか数え切れない。

3. 文字化けする

原因: 計測器の応答に改行コード (\n, \r\n) や終端文字が含まれている。

解決策: ReadString() の結果を .Trim() する。それでもダメなら、バイト単位で読んで確認する。

string response = session.RawIO.ReadString().Trim('\n', '\r', ' ');

4. 「VISA resource not found」 — NI MAXには表示されるのに

原因: VISAアドレスのタイプミス、もしくは計測器がスリープモードに入っている。

解決策: NI MAX でもう一度アドレスをコピーして貼り付ける。手入力はミスの元。計測器側のリモート設定が有効になっているかも確認する。一部の計測器は「LOCAL」モードだとVISA経由の通信を拒否します。

5. 計測値が 9.91E+37 になる

これ、初めて見たとき「は?」ってなりました。

原因: 計測器が「測定レンジオーバー」を示す特殊な値。たとえば、10Vレンジで100Vを測ろうとしたとき。

解決策: AUTO レンジを使うか、事前にレンジを適切に設定する。コード上では 9.91E+37 を異常値として検出してハンドリングする。

const double OverRange = 9.9E+37;

double voltage = MeasureDcVoltage(instrument);
if (voltage > OverRange * 0.9)
{
    AppendLog("⚠️ レンジオーバーです。測定レンジを確認してください。");
}

通信の流れを図で理解する

ここまでのコードの裏側で何が起きているか、全体像を図にまとめておきます。

自分たちのC#コードは NI-VISA ランタイム と話しているだけで、GPIB なのか USB なのかは VISA が吸収してくれている。だから接続方式が変わってもアプリのコードを修正する必要がない。この構造を理解しておくと、トラブルの切り分けも早くなります。

設計上のアドバイス — 5年間の現場経験から

最後に、コードの書き方ではなく、アプリ全体の設計で気をつけていることを共有します。

計測器制御の部分とUIの部分は絶対に分離する。 WinForms のイベントハンドラに計測ロジックを直書きすると、テストもできないし再利用もできない。前述の VisaInstrument クラスのように、計測器との通信は独立したクラスにまとめて、フォームからはそのクラスのメソッドを呼ぶだけにする。

自分が実務で作ったアプリは、こんな構成にしています。

VisaInstrumentController/
├── Forms/
│   ├── MainForm.cs          ← UI(ボタン、テキストボックス等)
│   └── SettingsForm.cs      ← 接続設定画面
├── Instruments/
│   ├── VisaInstrument.cs    ← VISA通信の基底クラス
│   ├── Multimeter.cs        ← マルチメーター固有の操作
│   └── Oscilloscope.cs      ← オシロスコープ固有の操作
├── Services/
│   ├── MeasurementService.cs ← 測定フロー制御
│   └── CsvExporter.cs       ← CSV出力
└── Program.cs

計測器ごとにクラスを分けておくと、「今回はKeysightのマルチメーターだけど、次のプロジェクトは菊水の電源」みたいなときに、Instruments フォルダに新しいクラスを追加するだけで済む。SCPIコマンドは計測器ごとに微妙に方言があるので、この分離は実務では必須です。

よくある質問

Q: VISA は無料で使えますか?

A: はい。NI-VISA ランタイムは無料でダウンロード・使用できます。C# の開発環境も Visual Studio Community(無料)で十分です。ライセンス費用はかかりません。

Q: .NET Framework と .NET 8 のどちらで作るべきですか?

A: 新規開発なら .NET 8 を推奨します。Ivi.Visa パッケージは .NET 6 以降に対応しており、パフォーマンスも良いです。ただし、既存の社内ツールが .NET Framework 4.8 で動いている場合は、そちらに合わせても問題ありません。NI-VISA の .NET ライブラリはどちらにも対応しています。

Q: 計測器がなくても開発・テストできますか?

A: NI-VISA にはシミュレーション機能があります。NI MAX でシミュレーションデバイスを追加すれば、実機がなくても通信のテストが可能です。ただし、SCPI コマンドの応答は実機と異なる場合があるので、最終テストは実機で行うことを推奨します。自分の場合は、開発は自宅のPCで行い、最終確認は工場で実機を使ってやっていました。

Q: Keysight や菊水など、NI 以外のメーカーの計測器でも使えますか?

A: 使えます。VISA は規格であり、メーカー固有の仕組みではありません。NI-VISA をインストールすれば、Keysight、Tektronix、菊水、HIOKI など、VISA 対応の計測器なら基本的にどれでも制御できます。自分の職場では NI 以外の計測器のほうが多いですが、全部 NI-VISA 経由で動いています。

Q: WPF ではなく WinForms を使う理由は?

A: 正直、どちらでも計測器制御の部分は同じコードで動きます。WinForms を選ぶ理由は「現場で使う人にとって慣れたUIになる」「学習コストが低い」「.NET 8 でも引き続きサポートされている」あたり。見た目にこだわりたいなら WPF もアリですが、製造現場の検査アプリは「安定して動くこと」が最優先なので、シンプルな WinForms で十分というのが自分の結論です。

おわりに — まずは *IDN? から始めよう

計測器制御と聞くと難しそうに聞こえるかもしれませんが、やっていることは意外とシンプルです。

  1. NI-VISA をインストールする
  2. NI MAX で計測器の接続を確認する
  3. ResourceManager.OpenRawIO.WriteRawIO.ReadString

この3ステップで、計測器と会話できる。あとは SCPI コマンドを変えるだけで、電圧測定も波形取得もできるようになります。

自分も5年前は「VISA って何……?」の状態から始めました。最初に *IDN? の応答が返ってきたときの「おお、つながった!」というあの感動は今でも覚えています。

この記事のコードは全て、自分が実際に製造現場で使ってきたパターンをベースにしています。何かつまずくことがあれば、たぶん自分も同じところでつまずいたことがあるので、コメントやXで聞いてください。

製造業でC#を書いてる人、もっと増えてほしいなと思っています。

この記事が参考になったら「いいね」で応援お願いします!


📝 この記事は Zenn で最初に公開されました。
最新版はZennをご覧ください。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?