【入門編】Windows Formsにおけるシリアル通信(RS-232C / USB)の非同期制御:計測器連携アプリのタイムアウトとロスト対策 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

現場のエンジニア諸君、ようこそ。

Excel VBAの「マクロの記録」から一歩踏み出し、Visual Basic (.NET)の世界へ足を踏み入れた君たちを歓迎する。製造現場やラボで、計測器やバーコードリーダーとPCを繋ぐ……この領域は、いわば「物理的な世界とデジタルの世界の境界線」だ。

ここで最も多い悲劇は、「通信の応答を待っている間に画面がフリーズし、そのまま強制終了する」という事故だ。UIが固まるのは、開発者としての敗北を意味する。今日は、現場で絶対に止まらない、堅牢な非同期通信の極意を伝授しよう。

—

1. なぜ「UIスレッド」を止めてはいけないのか?

Windows Formsにおいて、ボタンをクリックした時に走るコードは「UIスレッド」という一本のレールの上で動いている。もしここで `SerialPort.ReadLine()` のように、データが来るまで待機する命令を書くとどうなるか。

その間、Windowsは画面の再描画も、ボタンのクリック判定もできなくなる。これが「フリーズ」の正体だ。

極意: 通信は「裏のレール(バックグラウンド)」で走らせ、データが届いた時だけ「表のレール」に報告させる。これが非同期設計の鉄則だ。

—

2. 堅牢なシリアル通信クラスの雛形

まずは、UIを一切汚さない「通信専用クラス」の設計を見てほしい。これを使えば、メイン画面は軽快そのものだ。

Imports System.IO.Ports

Public Class SerialManager
‘ 通信ポートのインスタンス
Private WithEvents _serialPort As SerialPort

‘ データ受信をUIに通知するためのイベント
Public Event DataReceived(data As String)

Public Sub OpenPort(portName As String)
_serialPort = New SerialPort(portName, 9600, Parity.None, 8, StopBits.One)
_serialPort.ReadTimeout = 2000 ‘ 2秒反応がなければタイムアウト
_serialPort.Open()
End Sub

‘ イベントハンドラ:データが届くと自動的に呼ばれる(これが裏スレッド)
Private Sub _serialPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles _serialPort.DataReceived
Try
‘ データを読み込み
Dim rawData As String = _serialPort.ReadLine()
‘ UIスレッドへデータを投げる
RaiseEvent DataReceived(rawData)
Catch ex As TimeoutException
‘ タイムアウト処理(ここをログに記録するのがプロの技)
End Try
End Sub
End Class

—

3. 「ロスト対策」と「再接続」の魔法

現場の環境は過酷だ。ケーブルが抜ける、ノイズで通信が途切れることは日常茶飯事。「繋がっている前提」でコードを書くのは素人のやることだ。

再接続の自動化(Heartbeatの概念)

もしデータが5秒間来なければ「切断された」とみなして再接続を試みる、というロジックをタイマーで組むのが、現場で生き残るための生存戦略だ。

‘ メインフォームでの実装イメージ
Private Sub Timer_CheckConnection_Tick(sender As Object, e As EventArgs) Handles Timer_CheckConnection.Tick
‘ 最後にデータを受け取ってからの経過時間を監視
If (DateTime.Now – _lastReceivedTime).TotalSeconds > 5 Then
‘ 5秒以上無音なら再接続処理を走らせる
ReconnectPort()
End If
End Sub

—

4. 陥りやすい罠:クロススレッド・エラー

初学者が必ず突き当たる壁がある。それは、「バックグラウンドで受信したデータを、直接テキストボックスに表示しようとして落ちる」という現象だ。

Windows Formsのルールでは、「UIの部品(TextBox等)を作った本人(メインスレッド)以外が、その部品を触ってはいけない」という鉄の掟がある。これを回避するには `Invoke` を使う。

修正コードの例:

‘ データ受信イベントを受け取った時
Private Sub OnDataReceived(data As String) Handles _serialManager.DataReceived
‘ UIスレッド以外から呼ばれた場合、Invokeを使ってメインスレッドへ処理を委譲する
If Me.InvokeRequired Then
Me.Invoke(Sub() UpdateUI(data))
Else
UpdateUI(data)
End If
End Sub

Private Sub UpdateUI(data As String)
txtDisplay.AppendText(data & vbCrLf)
End Sub

—

5. 最後に:エンジニアとしての心構え

計測器連携アプリは、ただコードを書けばいいのではない。「物理的な断線」という不確実性をコードでいかに制御するかが問われる。

1. タイムアウトを必ず設定せよ: 無限待ちはアプリの死を招く。
2. 例外を握りつぶすな: `Try-Catch` でログを残し、なぜ止まったかを後から追えるようにせよ。
3. UIは常に軽く: ユーザーを待たせるな。

VB.NETは古臭い言語だなんて言う奴もいるが、WindowsというOSの深淵に最も寄り添える、現場最強の言語の一つだ。このアーキテクチャをマスターすれば、君はもう「マクロ屋」ではない。真の業務自動化エンジニアだ。

さあ、次は君自身のコードで、物理世界と対話してみよう。何か詰まったら、いつでも戻ってきなさい。応援しているよ。

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