Hatu123456
@Hatu123456

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

VB.netでPLCとUDP通信をしたいのですが・・・

質問失礼します。
Vb.netでPLCとUDP通信しようと思い以下のプログラミングを組みました。
以下のプログラムはタイマーに設定された時間毎にM3000の値を読み出しTextbox2に表示するといったプログラミングです。
しかし、いざ動かしてみるとフリーズしてしまいます。UDPではなくTCPにすると動くのですがUDPで作り上げたいのでUDPのまま動かしたいです。
https://momomo-97.com/communicate-with-mitsubishi-plc-using-vb-net-mc-protocol/#google_vignette
上記を参考にUDPにしようとたらうまくいきませんでした。

何が原因かわかる方いましたらアドバイス等していただけると幸いです。


Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar
Public Class Form1
    '送信メッセージ作成
    Dim T As Date
    Dim _socket As Socket =
    New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
    Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim ipAddress As String = "192.168.xxx.xxx"     'アドレス番号指定
        Dim portNo As Integer = xxxx              'ポート番号指定
        _socket.Connect(ipAddress, portNo)          '接続先
    End Sub
    
    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
        _socket.Close() 'フォームを閉じると同時にソケット通信を終了
    End Sub
    Private Function SendAndRecieve(sendMessage As String()) As Byte() 'ソケット通信を行う
        '送信メッセージを変換 String⇒Byte
        Dim byteSendMessage As Byte() = StringsToBytes(sendMessage)
        'メッセージ送信
        _socket.Send(byteSendMessage, byteSendMessage.GetLength(0), SocketFlags.None)
        '応答メッセージを受信
        Dim byteReciveMessage As Byte()
        Dim reciveSize As Integer = 0
        Do
            byteReciveMessage = New Byte(_socket.Available - 1) {}
            reciveSize =
                _socket.Receive(byteReciveMessage, byteReciveMessage.GetLength(0), SocketFlags.None)
        Loop While reciveSize = 0
        Return byteReciveMessage
    End Function
    Private Function StringsToBytes(src() As String) As Byte()
        Dim returnBytes(src.Length - 1) As Byte
        '1要素ずつ変換
        Dim i As Integer
        For i = 0 To src.Length - 1
            returnBytes(i) = Convert.ToByte(src(i), 16)
        Next
        Return returnBytes
    End Function

    Public Sub M3000_Tick(sender As Object, e As EventArgs) Handles M3000.Tick   'M3000番を一定時間ごとに読み取る
       
        '送信メッセージ作成
        Dim sendMessage(20) As String
        'サブヘッダ
        sendMessage(0) = "50" '50で固定
        sendMessage(1) = "00" '00で固定
        'アクセス経路
        sendMessage(2) = "00" 'ネットワーク番号
        sendMessage(3) = "FF" ' PC番号
        sendMessage(4) = "FF" '要求先ユニットI/O番号
        sendMessage(5) = "03" '要求先ユニットI/O番号
        sendMessage(6) = "00" '要求先ユニット局番号
        '要求データ長(2Byte)
        sendMessage(7) = "0C" '要求データ長34 12点⇒000C
        sendMessage(8) = "00" '要求データ長12 12点⇒000C
        '監視タイマ(2Byte)
        sendMessage(9) = "10"
        sendMessage(10) = "00"
        '要求データ
        sendMessage(11) = "01" 'コマンド 読取0401⇒04"01"
        sendMessage(12) = "04" 'コマンド 読取0401⇒"04"01
        sendMessage(13) = "01" 'サブコマンド ビット読取0001⇒00"01"
        sendMessage(14) = "00" 'サブコマンド ビット読取0001⇒"00"01
        sendMessage(15) = "B8" 'デバイス番号56 0⇒0000"B8" M3000は16進数でBB8
        sendMessage(16) = "0B" 'デバイス番号34 0⇒00"0B"00
        sendMessage(17) = "00" 'デバイス番号12 0⇒"00"0000
        sendMessage(18) = "90" 'デバイスコード M⇒90
        sendMessage(19) = "01" '読取点数34 1点⇒00"01"
        sendMessage(20) = "00" '読取点数12 1点⇒"00"01
        'メッセージ送信&受信
        Dim byteReciveMessage = SendAndRecieve(sendMessage)
        Dim M3000 = Hex(byteReciveMessage(11)).PadLeft(2, "0").Substring(0, 1)
        TextBox2.Text = M3000

0

3Answer

受信処理のループに問題はありませんか?

vb.net
vbCopyDo
    byteReciveMessage = New Byte(_socket.Available - 1) {}
    reciveSize = _socket.Receive(byteReciveMessage, byteReciveMessage.GetLength(0), SocketFlags.None)
Loop While reciveSize = 0

このコードは、データが受信されるまで無限ループする可能性があり、これがフリーズの主な原因ではないでしょうか?
UDPはパケットロスの可能性があるため、タイムアウト処理が必要です。

0Like

こんな感じではいかがでしょうか?

vb.net
Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Public Class Form1
    Private _socket As Socket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
    Private Const TIMEOUT_MS As Integer = 1000  ' タイムアウト時間(1秒)

    Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim ipAddress As String = "192.168.xxx.xxx"
        Dim portNo As Integer = xxxx
        _socket.Connect(ipAddress, portNo)
        
        ' 受信タイムアウトを設定
        _socket.ReceiveTimeout = TIMEOUT_MS
    End Sub

    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
        _socket.Close()
    End Sub

    Private Function SendAndRecieve(sendMessage As String()) As Byte()
        Try
            Dim byteSendMessage As Byte() = StringsToBytes(sendMessage)
            _socket.Send(byteSendMessage, byteSendMessage.GetLength(0), SocketFlags.None)

            ' 受信バッファサイズを固定
            Dim byteReciveMessage(1024) As Byte
            Dim reciveSize As Integer = 0

            Try
                reciveSize = _socket.Receive(byteReciveMessage, byteReciveMessage.Length, SocketFlags.None)
                If reciveSize > 0 Then
                    ' 実際に受信したサイズにバッファを切り詰める
                    Dim actualData(reciveSize - 1) As Byte
                    Array.Copy(byteReciveMessage, actualData, reciveSize)
                    Return actualData
                End If
            Catch ex As SocketException
                MessageBox.Show("受信タイムアウトが発生しました。")
                Return Nothing
            End Try

        Catch ex As Exception
            MessageBox.Show("通信エラーが発生しました: " & ex.Message)
            Return Nothing
        End Try

        Return Nothing
    End Function

    Private Function StringsToBytes(src() As String) As Byte()
        Dim returnBytes(src.Length - 1) As Byte
        For i As Integer = 0 To src.Length - 1
            returnBytes(i) = Convert.ToByte(src(i), 16)
        Next
        Return returnBytes
    End Function

    Public Sub M3000_Tick(sender As Object, e As EventArgs) Handles M3000.Tick
        ' 送信メッセージ作成(変更なし)
        Dim sendMessage(20) As String
        ' ... (既存のメッセージ作成コード)

        ' メッセージ送信&受信
        Dim byteReciveMessage = SendAndRecieve(sendMessage)
        If byteReciveMessage IsNot Nothing AndAlso byteReciveMessage.Length > 11 Then
            Dim M3000 = Hex(byteReciveMessage(11)).PadLeft(2, "0").Substring(0, 1)
            TextBox2.Text = M3000
        End If
    End Sub
End Class

・タイムアウト処理の追加
_socket.ReceiveTimeout = TIMEOUT_MS でタイムアウト時間を設定
受信処理で例外処理を追加し、タイムアウト時にメッセージを表示

・エラーハンドリングの強化
Try-Catch構文を使用して通信エラーを適切に処理
受信データのNull判定を追加

・受信バッファの管理改善
固定サイズのバッファを使用
実際に受信したサイズに合わせてデータを切り詰め

・無限ループの除去
Do-Loopを除去し、単一の受信処理に変更

0Like

Comments

  1. @Hatu123456

    Questioner

    さかいさまコメントしていただきありがとうございます。
    様々なご指摘大変ありがとうございます。
    現在仕事でプログラミングに触れられる状況ではないため変更して試せないのですが、明日には変更して試せそうなので明日の正午ごろには結果を報告させていただきます!

  2. @Hatu123456

    Questioner

    さかいさまの指摘していただいたとおりに変更したところ無事に通信でき、値の取得にも成功しました!
    また質問になるのですがこのプログラミングで下記の画像のように通信することは可能であると考えられますか?
    タイトルなし.png

無事改善されてよかったです!

表の構成であれば、同一サブネット内での通信となるため、基本的に通信は可能です。Pingが通るかまずは確認してみると良いですね。

0Like

Comments

  1. @Hatu123456

    Questioner

    試してみたところ通信に成功しました!説明やご指摘いただき本当にありがとうございました!

  2. 通信できてよかったです。
    ご報告ありがとうございました:relaxed:

Your answer might help someone💌