VisaulStudio2022下用VB.net实现socket与西门子PLC进行通讯案例(优化版)

前言
对于电气工程师来说,不仅要会PLC,还要会上位机。

此前,我写过一个VB.net下雨西门子PLC通讯案例的博文:
VisaulStudio2019下用VB.net实现socket与西门子PLC进行通讯案例
但当时很多东西都理解不深,博文也写的比较浅,但我看有不少收藏,也有些朋友在底下询问,所以,基于这篇文章,我准备更新一下,重写一个VS2022版。

配置:
平台:windows
工具:visual sdutio 2022
语言:VB.net
通讯协议:socket

其中,关于PLC侧的设置,还是和之前一致,我就不细说了,主要关注上位机侧的程序变化。
注:其实,上位机与PLC的通讯,如果使用通用协议,比如串口或者socket,那么无论是哪个品牌的PLC,只要当前型号支持socket,上位机这边是通用的。

一、PLC侧设置:

可以查看之前的博文:
VisaulStudio2019下用VB.net实现socket与西门子PLC进行通讯案例
在此处不多述了。

二、上位机侧程序:

socket协议是分为客户端和服务端的,之前的博文里,我们的介绍是基于将PLC作为服务器端,上位机作为客户端来使用的,这当然是最常用的方式,因为通常情况下,我们都是去获取PLC数据的,但是,如果你非要将PLC设为客户端,也是可以的。
所以,我们下面会从客户端和服务器端两个方面来说明上位机侧的程序。

1 客户端

优化版的效果如下,调整了布局,增加了连接状态监控,以及PLC数据反馈监控(注:PLC反馈数据可以根据实际情况修改,本文仅作示例)
在这里插入图片描述
下面对客户端的程序作说明。

1、socket参数
VB.net中如果要使用socket协议,需要导入socket命名空间。

Imports System.Net.Sockets

定义一个socket对象:

Dim soc_client As Socket 

然后实例化对象:

ip = IPAddress.Parse(TextBox1.Text)    
port = Integer.Parse(TextBox2.Text)
ipe = New IPEndPoint(ip, port)
soc_client = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

如上,new socket即是新建一个socket实例,而ip、port则是socket建立连接时需要的参数,即IP地址和端口号。

soc_client.Connect(ipe)

2、数据接收和发送
当我们建立socket的连接后,可以通过socket.connected这个标志位来判断连接是否成功。

   Try
       soc_client.Connect(ipe)
       Console.WriteLine("connect ok")  
       If soc_client.Connected Then

           '如果连接成功,状态变为绿色
           huayuan(PictureBox1, Color.LimeGreen)


           '如果连接成功,则启动数据接收,为了防止阻塞线程,采用新线程
           Dim th1 As New Thread(AddressOf socket_client_recvievedata)  '多线程
           th1.Start()                               '线程启动,防止socket阻塞

       End If

   Catch ex As Exception

       Console.WriteLine(ex.Message)
       MsgBox(ex.Message + vbCrLf +
              "请检查IP设置或目标PLC是否保持通讯正常?",
              MsgBoxStyle.OkOnly, "tips!")

   End Try

通过判断连接状态,可以在窗体上用颜色变化来表示,此处会用到一个画圆的函数:

   ''' <summary>
   ''' 在PictureBox中画一个圆
   ''' </summary>
   ''' <param name="p"></param>
   ''' <param name="c"></param>
   Private Sub huayuan(p As PictureBox, c As Color)

       Dim b As Bitmap = New Bitmap(p.Width, p.Height)

       Dim g As Graphics = Graphics.FromImage(b)

       Dim mybrush As New SolidBrush(c)

       g.FillEllipse(mybrush, 0, 0, p.Width, p.Height)

       g.Dispose()

       p.Image = b

   End Sub

关于这个huayuan的函数,我写了一个专门的博文介绍:
1、<.Net>使用visual Studio 2022在VB.net中新添自定义画图函数(优化版)
2、使用visual Studio 2019在VB.net中新添自定义画图函数
这两篇文章都可以了解。

总之,这个huayuan函数是用来改变PictureBox的状态(主要是颜色),用以区别连接状态。
在这里插入图片描述
当连接成功后,便可以进行数据的发送和接收,socket是双向同时可进行数据的通讯的。

数据接收:

Dim reclength = soc_client.Receive(recbyt, 200, SocketFlags.None)   

但通常情况,为了能持续接收数据,一般会用到循环,但如果直接循环接收数据,会阻塞主线程即UI,导致窗体卡住,所以,一般会新建线程来接收数据:

'如果连接成功,则启动数据接收,为了防止阻塞线程,采用新线程
Dim th1 As New Thread(AddressOf socket_client_recvievedata)  '多线程     
th1.Start()                               '线程启动,防止socket阻塞

以下是接收数据的一个示例,其中的数据类型可以自己定义,本文仅作示例

  ''' <summary>
 ''' 客户端接收数据
 ''' </summary>
 Private Sub socket_client_recvievedata()

     '定义接收区字节数组
     Dim recbyt(1024) As Byte

     '循环接收
     While True

         Try
             Dim reclength = soc_client.Receive(recbyt, 200, SocketFlags.None)
             'recvdata = Encoding.GetEncoding("gb2312").GetString(recbyt)

             Dim tt1 As Test_data
             'recvdata = Encoding.ASCII.GetString(recbyt, 0, 30)

             '以下是对所接收的所有数据进行解析,可以根据实际情况变化
             '下面的仅作为参考
             'ax1.axis_status = BitConverter.ToInt16(recbyt, 0)
             'ax1.axis_xunhuanmoshi = BitConverter.ToInt16(recbyt, 2)
             'ax1.axis_runing = BitConverter.ToInt16(recbyt, 4)
             'ax1.axis_error = BitConverter.ToInt16(recbyt, 6)

             Console.WriteLine(reclength.ToString())

             tt1.test1 = Encoding.GetEncoding("utf-8").GetString(recbyt)
             tt1.test2 = Encoding.GetEncoding("utf-8").GetString(recbyt)

             'Console.WriteLine(recdata1)

             '将解析的数据通过委托的方式传递给主线程,以更新主线程的UI
             Dim socket_invoke_1 As New socket_invoke(AddressOf socket_recv_data_invoke)

             Me.Invoke(socket_invoke_1, tt1)

         Catch ex As Exception
             Exit Sub
             MsgBox(ex.Message, MsgBoxStyle.OkOnly, "tips!")

         End Try


     End While

 End Sub

要注意到上面的接收函数的后面有这样一段代码:

'将解析的数据通过委托的方式传递给主线程,以更新主线程的UI
Dim socket_invoke_1 As New socket_invoke(AddressOf socket_recv_data_invoke)   

Me.Invoke(socket_invoke_1, ax1, ax2, plc1)

因为我们接收数据,处理完数据后,有一些数据是要传递给主线程并更新UI状态的,所以就需要用到委托。这是用于在两个线程间传递数据的。

此处委托的函数socket_recv_data_invoke如下:

  ''' <summary>
''' 委托处理函数
''' </summary>
Private Sub socket_recv_data_invoke(tt1 As Test_data)

    '以下仅以简单数据更新作为参考,可自行修改此处逻辑
    '此处的数据处理,主要是将socket接收过来的数据,经过处理后,经由委托方式传给主线程即UI
    '用于更新窗体上的Label、button、textbox等控件的内容

    TextBox3.Text = tt1.test1

    TextBox5.Text = tt1.test2
    TextBox6.Text = tt1.test2
    TextBox7.Text = tt1.test2
    TextBox8.Text = tt1.test2

End Sub

本例中,我只是简单写了几个逻辑,其作用是将PLC反馈的数据更新到控件上,比如改变监控状态,改变错误输出,改变轴的数值等。类似于下图这样:
在这里插入图片描述
当然,实际情况需要根据自己的要求来改。
下面看一下演示:
注:这里演示并未使用真实的PLC,而是利用python虚拟了一个服务器端来发送数据,仅作演示效果。
等视频上传完成后添加
已添加

socket客户端通讯演示

完整代码:

Imports System.Text
Imports System.Net.Sockets
Imports System.Net
Imports System.Threading

Public Class Form1


    ''' <summary>
    ''' 测试数据
    ''' </summary>
    Structure Test_data

        Public test1 As String
        Public test2 As String
        Public test3 As String

        Public temp1 As Boolean
        Public temp2 As Boolean

    End Structure

    '定义客户端发送字节缓冲区
    Dim sendbytes_client(1024) As Byte
    '定义客户端接收字节缓冲区
    Dim recvbytes_client(1024) As Byte
    '定义服务器发送字节缓冲区
    Dim sendbytes_server(1024) As Byte
    '定义服务器接收字节缓冲区
    Dim recvbytes_server(1024) As Byte

    Dim ip As IPAddress
    Dim ipe As IPEndPoint
    Dim port As Integer
    Dim host As String
    Dim soc_client As Socket
    Dim soc_server As Socket

    Dim socket_mode As Integer      '设备类型,1=客户端,2=服务器

    Delegate Sub socket_invoke(tt1 As Test_data)
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Me.Text = "PLC通讯工具"
        Me.Size = New Size(840, 600)
        Me.Location = New Point(100, 40)

        TextBox1.Text = "192.168.0.1"
        TextBox2.Text = "2000"

        huayuan(PictureBox1, Color.Gray)



        '默认情况下,设备类型为客户端
        socket_mode = 1

        Label11.Text = "无"


        RadioButton1.Checked = False
        RadioButton2.Checked = True

        RadioButton1.Enabled = False

        init_func()

    End Sub


    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        socket_client_connect()

    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click

        socket_client_disconnect()

    End Sub

    Private Sub RadioButton1_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButton1.CheckedChanged

        If RadioButton1.Checked = True And RadioButton2.Checked = False Then

            Button1.Enabled = False
            Button2.Enabled = False
            Button5.Enabled = True
            Button6.Enabled = True
            socket_mode = 2
            Label11.Text = "服务器"

        ElseIf RadioButton1.Checked = False And RadioButton2.Checked = True Then

            Button1.Enabled = True
            Button2.Enabled = True
            Button5.Enabled = False
            Button6.Enabled = False
            socket_mode = 1
            Label11.Text = "客户端"


        End If

    End Sub

    Private Sub RadioButton2_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButton2.CheckedChanged

        If RadioButton1.Checked = True And RadioButton2.Checked = False Then

            Button1.Enabled = False
            Button2.Enabled = False
            Button5.Enabled = True
            Button6.Enabled = True
            socket_mode = 2
            Label11.Text = "服务器"

        ElseIf RadioButton1.Checked = False And RadioButton2.Checked = True Then

            Button1.Enabled = True
            Button2.Enabled = True
            Button5.Enabled = False
            Button6.Enabled = False
            socket_mode = 1
            Label11.Text = "客户端"


        End If


    End Sub

    ''' <summary>
    ''' 初始化某些参数
    ''' </summary>
    Private Sub init_func()

        ip = IPAddress.Parse(TextBox1.Text)
        port = Integer.Parse(TextBox2.Text)
        ipe = New IPEndPoint(ip, port)
        soc_client = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

    End Sub

    ''' <summary>
    ''' 客户端连接
    ''' </summary>
    Private Sub socket_client_connect()

        Try
            ip = IPAddress.Parse(TextBox1.Text)
            port = Integer.Parse(TextBox2.Text)
            ipe = New IPEndPoint(ip, port)
            soc_client.Connect(ipe)
            Console.WriteLine("connect ok")
            If soc_client.Connected Then

                '如果连接成功,状态变为绿色
                huayuan(PictureBox1, Color.LimeGreen)


                '如果连接成功,则启动数据接收,为了防止阻塞线程,采用新线程
                Dim th1 As New Thread(AddressOf socket_client_recvievedata)  '多线程
                th1.Start()                               '线程启动,防止socket阻塞

            End If

        Catch ex As Exception

            Console.WriteLine(ex.Message)
            MsgBox(ex.Message + vbCrLf +
                   "请检查IP设置或目标PLC是否保持通讯正常?",
                   MsgBoxStyle.OkOnly, "tips!")

        End Try

    End Sub

    ''' <summary>
    ''' 客户端断开连接
    ''' </summary>
    Private Sub socket_client_disconnect()
        Try
            If soc_client.Connected Then

                soc_client.Shutdown(SocketShutdown.Both)

                soc_client.Close()

                soc_client.Dispose()

                If soc_client.Connected = False Then

                    '如果客户端已经断开连接,状态变为灰色
                    huayuan(PictureBox1, Color.Gray)

                End If

            End If

        Catch ex As Exception

            MsgBox(ex.Message, MsgBoxStyle.OkOnly, "tips!")

        End Try

    End Sub

    ''' <summary>
    ''' 客户端接收数据
    ''' </summary>
    Private Sub socket_client_recvievedata()

        '定义接收区字节数组
        Dim recbyt(1024) As Byte

        '循环接收
        While True

            Try
                Dim reclength = soc_client.Receive(recbyt, 200, SocketFlags.None)
                'recvdata = Encoding.GetEncoding("gb2312").GetString(recbyt)

                Dim tt1 As Test_data
                'recvdata = Encoding.ASCII.GetString(recbyt, 0, 30)

                '以下是对所接收的所有数据进行解析,可以根据实际情况变化
                '下面的仅作为参考
                'ax1.axis_status = BitConverter.ToInt16(recbyt, 0)
                'ax1.axis_xunhuanmoshi = BitConverter.ToInt16(recbyt, 2)
                'ax1.axis_runing = BitConverter.ToInt16(recbyt, 4)
                'ax1.axis_error = BitConverter.ToInt16(recbyt, 6)

                Console.WriteLine(reclength.ToString())

                tt1.test1 = Encoding.GetEncoding("utf-8").GetString(recbyt)
                tt1.test2 = Encoding.GetEncoding("utf-8").GetString(recbyt)

                'Console.WriteLine(recdata1)

                '将解析的数据通过委托的方式传递给主线程,以更新主线程的UI
                Dim socket_invoke_1 As New socket_invoke(AddressOf socket_recv_data_invoke)

                Me.Invoke(socket_invoke_1, tt1)

            Catch ex As Exception
                Exit Sub
                MsgBox(ex.Message, MsgBoxStyle.OkOnly, "tips!")

            End Try


        End While

    End Sub

    ''' <summary>
    ''' 委托处理函数
    ''' </summary>
    Private Sub socket_recv_data_invoke(tt1 As Test_data)

        '以下仅以简单数据更新作为参考,可自行修改此处逻辑
        '此处的数据处理,主要是将socket接收过来的数据,经过处理后,经由委托方式传给主线程即UI
        '用于更新窗体上的Label、button、textbox等控件的内容

        TextBox3.Text = tt1.test1

        TextBox5.Text = tt1.test2
        TextBox6.Text = tt1.test2
        TextBox7.Text = tt1.test2
        TextBox8.Text = tt1.test2

    End Sub

    ''' <summary>
    ''' 客户端发送数据
    ''' </summary>
    Private Sub socket_client_senddata()

        Try

            If soc_client.Connected Then

                soc_client.Send(sendbytes_client, 200, SocketFlags.None)

            End If
        Catch ex As Exception

            MsgBox(ex.Message, MsgBoxStyle.YesNoCancel, "tips!")

        End Try

    End Sub

    ''' <summary>
    ''' 在PictureBox中画一个圆
    ''' </summary>
    ''' <param name="p"></param>
    ''' <param name="c"></param>
    Private Sub huayuan(p As PictureBox, c As Color)

        Dim b As Bitmap = New Bitmap(p.Width, p.Height)

        Dim g As Graphics = Graphics.FromImage(b)

        Dim mybrush As New SolidBrush(c)

        g.FillEllipse(mybrush, 0, 0, p.Width, p.Height)

        g.Dispose()

        p.Image = b

    End Sub


End Class

注1:本文是为了便于以后遇到同样问题而作的记录,当然,如果能够同时帮助到其他朋友,那也是非常好的。
注2:本文在前面说到了服务器端和客户端,但本文暂时只是介绍了客户端程序,服务器端程序将在后续更新。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
VB.NET是一种流行的编程语言,它可以通过使用Socket类来实现字符串发送和接收程序。首先,我们需要创建一个Socket对象,并指定要连接的主机和端口号。然后,我们可以使用该Socket对象的Send方法来发送字符串数据,并使用Receive方法来接收字符串数据。 在发送字符串时,我们首先需要将要发送的字符串转换为字节数组,然后再通过Send方法发送。在接收字符串时,我们需要先创建一个足够大的缓冲区来存储接收到的数据,然后通过Receive方法将接收到的字节数组转换为字符串。 接下来是一个简单的示例代码,展示了如何在VB.NET中使用Socket类来实现字符串发送和接收程序: ```vb.net Imports System.Net.Sockets Imports System.Text Module Program Sub Main() Dim clientSocket As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) ' Connect to the server clientSocket.Connect("127.0.0.1", 8888) ' Send data to the server Dim data As Byte() = Encoding.ASCII.GetBytes("Hello server!") clientSocket.Send(data) ' Receive data from the server Dim buffer As Byte() = New Byte(1024) {} Dim bytesReceived As Integer = clientSocket.Receive(buffer) Dim responseData As String = Encoding.ASCII.GetString(buffer, 0, bytesReceived) Console.WriteLine("Received from server: " & responseData) ' Close the socket clientSocket.Close() End Sub End Module ``` 在这个示例中,我们首先创建了一个Socket对象clientSocket,并连接到了本地主机的8888端口。然后,我们使用Send方法发送了一个字符串"Hello server!"到服务器端,并通过Receive方法接收了服务器返回的数据。最后,我们关闭了Socket对象。 这就是如何使用VB.NET中的Socket类来实现简单的字符串发送和接收程序。通过这种方式,我们可以轻松地实现网络通信,并在应用程序之间传递数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

机构师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值