dw做網(wǎng)站怎么連接gif圖片東莞做網(wǎng)站優(yōu)化
前言
此前,我寫過一個VB.net環(huán)境下與西門子PLC通訊案例的博文:
VisaulStudio2022下用VB.net實現(xiàn)socket與西門子PLC進(jìn)行通訊案例(優(yōu)化版)
最近項目上會用到匯川PLC比較多,正好有個項目有上位機(jī)通訊需求,于是就自己這邊先測試了一下匯川PLC和上位機(jī)通過socket進(jìn)行數(shù)據(jù)通訊。
配置:
平臺:windows
工具:visual studio 2022
語言:VB.net
通訊協(xié)議:socket
PLC型號:匯川Eazy521系列
固件版本:6.1.2.0
PLC軟件:匯川Autoshop V4.8.2.4
其實,與PLC進(jìn)行通訊來說,上位機(jī)側(cè)的程序幾乎不用改變,主要是PLC端的使用有些不同,像西門子、信捷的PLC,在進(jìn)行socket通訊時,雖然使用協(xié)議都是一樣的,但不同廠家,其編程指令和PLC程序?qū)懗鰜硎遣灰恢碌?#xff0c;其中還涉及數(shù)據(jù)類型的轉(zhuǎn)換。
本篇將側(cè)重于介紹匯川PLC這邊的設(shè)置及編程,上位機(jī)將在之前的基礎(chǔ)上稍作修改。
一、PLC側(cè)設(shè)置
匯川PLC進(jìn)行socket協(xié)議通訊的指令,在它的工具箱-通訊指令-以太網(wǎng)指令下:
大致介紹下指令:
1、TCP_listen:PLC作為服務(wù)器端時,監(jiān)聽客戶端指令
2、TCP_accept:PLC作為服務(wù)器端時,在開啟了監(jiān)聽模式后,用于監(jiān)測客戶端的連接指令。如果檢測到有客戶端請求連接,則會產(chǎn)生一個connected_socket,并存儲在隊列里,這個connected_socket就是客戶端的請求,后面的收發(fā)數(shù)據(jù),都用它進(jìn)行。
3、TCP_connect:PLC作為客戶端時,與服務(wù)器連接的指令。
4、TCP_receive:當(dāng)連接成功建立后,使用此指令從遠(yuǎn)程設(shè)備接收數(shù)據(jù)。
5、TCP_send:同上,發(fā)送數(shù)據(jù)。
6、TCP_close:用于關(guān)閉監(jiān)聽或者連接。
下面,以PLC作為服務(wù)器端為例,來說明一下指令具體如何使用:
1、TCP_listen:
如上圖,這個指令的觸發(fā)位Execute,其實使用上升沿脈沖即可,當(dāng)觸發(fā)后,Active位會變成ON。
其中,左側(cè)的socket接口,根據(jù)匯川官方文檔,是應(yīng)該填寫sSocekt數(shù)據(jù)類型的,詳細(xì)如下:
但是目前Autoshop軟件中目前還不支持自定義這種數(shù)據(jù)類型,所以要用INT[20]數(shù)組來代替,實例中如下:
port接口是填寫本地端口后,可以直接填寫常數(shù),如2000,在PLC中表示為K2000,即十進(jìn)制2000。也可以使用變量,只要將數(shù)據(jù)類型設(shè)置為DINT即可。但官方文檔有提示,盡量不要使用特殊端口號,如502。
至于右側(cè)的其他狀態(tài)位如buzy,error,errorid,只要監(jiān)控指令運行狀態(tài)的,按照數(shù)據(jù)類型,分配個變量就行了,后面的其他指令,都會有這些變量,就不再多做說明。
2、TCP_accept:
當(dāng)監(jiān)聽開啟后,就需要用accept來監(jiān)測客戶端的連接請求。
如上圖,這個指令左側(cè)有一個ListeningSocket參數(shù),這里填寫和TCP_listen指令一樣的數(shù)據(jù)即可。
其右側(cè)有一個Connected位,如果客戶端發(fā)起了連接請求,且無故障,那么這個位將會變?yōu)镺N。同時,右側(cè)還有一個Connected_socket參數(shù),這個參數(shù)也是sSocket類型,此處也是用INT[20]數(shù)組來代替。但要注意,這個和TCP_listen不能是同一個,這個相當(dāng)于是服務(wù)器端監(jiān)聽到了客戶端,產(chǎn)生的客戶端反饋。
accept指令可以多次使用,以接收多個客戶端的連接。本例只按照一個客戶端連說明。
3、TCP_receive:
此指令用于接收遠(yuǎn)程設(shè)備(本例中就是客戶端)發(fā)送的數(shù)據(jù),其左側(cè)接口有一個Socket參數(shù),此處填寫前面accept指令右側(cè)的Connected_socket一樣的數(shù)據(jù)即可。
接著是Buffer,即數(shù)據(jù)接收過來,存放在哪里?
你可以自己新建一個buffer區(qū),buffer區(qū)是一個連續(xù)數(shù)組,匯川PLC支持兩種數(shù)據(jù)類型:
INT[0…n]或者Byte[0…n],使用哪種看自己需要,一般常用byte即可。
接下來是Size參數(shù),因為我們不一定需要所有發(fā)送過來的數(shù)據(jù),所以可設(shè)置接收數(shù)據(jù)長度,但要注意,Size的值不能超過buffer區(qū)的大小。
右側(cè)參數(shù),done代表指令接收完成,可以使用這個狀態(tài)位來編寫一些邏輯程序,如數(shù)據(jù)傳送等。
還有一個ReceivedSize參數(shù),表示實際接收的數(shù)據(jù)長度,雖然設(shè)置了數(shù)據(jù)接收長度,但發(fā)過來的數(shù)據(jù)可能小于設(shè)置值,比如設(shè)置了10字節(jié),但發(fā)送過來的只有2個字節(jié),這個長度就是實際長度,會顯示在ReceivedSize中。
4、TCP_send:
此指令用于發(fā)送數(shù)據(jù),與TCP_receive指令類似,其左側(cè)的Socket參數(shù),填寫accept指令右側(cè)的Connected_socket一樣的參數(shù)。
buffer填寫自定義的發(fā)送數(shù)據(jù)緩沖區(qū),Size為發(fā)送長度,右側(cè)的sentsize標(biāo)識實際發(fā)送的數(shù)據(jù)長度。
5、TCP_close:
關(guān)閉指令非常簡單,用于關(guān)閉socket連接,但要注意,本例是將PLC作為服務(wù)器端,所以關(guān)閉連接時,除了要關(guān)閉服務(wù)器的監(jiān)聽socket,還需要關(guān)閉客戶端的請求socket(假如已成功建立連接)。
以上就是匯川PLC端的程序編寫,匯川的官方文檔也給了示例,但實際使用時,會涉及數(shù)據(jù)轉(zhuǎn)換問題,這里提一個注意點:
我這邊使用時,需要在PLC這邊將浮點數(shù)(32位)發(fā)送到上位機(jī),但我們在前面說了,TCP_send的buffer區(qū)只能是Byte[]或者INT[]這兩種,所以,就需要將浮點數(shù)先轉(zhuǎn)為整數(shù)類型或者字節(jié)類型。
但是,如果你直接使用匯川的INT或者DINT這個指令,它會將浮點數(shù)轉(zhuǎn)換為四舍五入后的整數(shù),并不是真正的無損轉(zhuǎn)換。
我在指令文檔找了半天,才發(fā)現(xiàn)匯川要實現(xiàn)將浮點數(shù)完全轉(zhuǎn)換為字節(jié),需要使用MCPY這個指令。
官方解釋:
該指令是較為高階的應(yīng)用,需謹(jǐn)慎使用!
該指令是數(shù)據(jù)復(fù)制操作,數(shù)據(jù)本身無變化,可以實現(xiàn)內(nèi)存復(fù)制拷貝;如果使用巧妙,可以實現(xiàn)類型變換的操
作。
n是待復(fù)制的數(shù)據(jù)的長度,以字節(jié)為單位,比如,兩個16位數(shù)據(jù)賦值給一個32位數(shù)據(jù),n=4;把兩個32位整
數(shù),復(fù)制給同樣大小的結(jié)構(gòu)體或16位整數(shù)數(shù)組,n=8;
當(dāng)操作數(shù)S或D為位元件時,位元件的地址必須按字節(jié)對齊,否則會發(fā)生尋址錯誤;例如,MCPY M1 M15
K1,會報“無效變量地址,訪問的變量不存在”錯誤。
實際應(yīng)用:
這樣轉(zhuǎn)換后的數(shù)據(jù),在通過socket發(fā)送到上位機(jī)后,上位機(jī)端使用字節(jié)轉(zhuǎn)浮點指令,如python中可以使用:
data_float=struct.unpack('f',datarecv[0:4])[0]
轉(zhuǎn)換后和PLC發(fā)送的數(shù)據(jù)是一致的。
二、上位機(jī)側(cè)程序
上位機(jī)側(cè)采用的是VB.net語言編寫,其中socket通訊方面使用的是System.Net.Sockets庫,直接導(dǎo)入即可:
Imports System.Net.Sockets
說一下幾個關(guān)鍵步驟:
1、socket連接
Private Sub socket_client_connect() Tryip = 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'如果連接成功,狀態(tài)變?yōu)榫G色huayuan(PictureBox1, Color.LimeGreen)'如果連接成功,則啟動數(shù)據(jù)接收,為了防止阻塞線程,采用新線程Dim th1 As New Thread(AddressOf socket_client_recvievedata) '多線程th1.Start() '線程啟動,防止socket阻塞End IfCatch ex As ExceptionConsole.WriteLine(ex.Message)MsgBox(ex.Message + vbCrLf +"請檢查IP設(shè)置或目標(biāo)PLC是否保持通訊正常?",MsgBoxStyle.OkOnly, "tips!")End TryEnd Sub
使用socket.connect()函數(shù)向服務(wù)器發(fā)送連接請求,可以使用socket.connected狀態(tài)位來判斷連接是否成功。
2、socket接收
Private Sub socket_client_recvievedata() '定義接收區(qū)字節(jié)數(shù)組Dim recbyt(1024) As Byte'循環(huán)接收While TrueTryDim 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)'以下是對所接收的所有數(shù)據(jù)進(jìn)行解析,可以根據(jù)實際情況變化'下面的僅作為參考'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)tt1.temp_float1 = BitConverter.ToSingle(recbyt, 0)'Console.WriteLine(recdata1)'將解析的數(shù)據(jù)通過委托的方式傳遞給主線程,以更新主線程的UIDim socket_invoke_1 As New socket_invoke(AddressOf socket_recv_data_invoke)Me.Invoke(socket_invoke_1, tt1)Catch ex As ExceptionExit SubMsgBox(ex.Message, MsgBoxStyle.OkOnly, "tips!")End TryEnd WhileEnd Sub
接收數(shù)據(jù)使用socket.receive(),為了循環(huán)接收,可以使用while指令,但是如果直接使用,會阻塞主線程即UI,所以最好在新線程中使用。注意上述代碼中的后面幾行:
'將解析的數(shù)據(jù)通過委托的方式傳遞給主線程,以更新主線程的UI
Dim socket_invoke_1 As New socket_invoke(AddressOf socket_recv_data_invoke) Me.Invoke(socket_invoke_1, tt1)
此處采用的是.Net中的委托,即為了不阻塞主線程,我們采用委托的形式,將socket循環(huán)接收的數(shù)據(jù)發(fā)送給UI,這樣既可以傳送數(shù)據(jù),又不會阻塞線程。
3、socket發(fā)送數(shù)據(jù)
發(fā)送數(shù)據(jù)則非常簡單,使用socket.send()即可。
Private Sub socket_client_senddata() TryIf soc_client.Connected Thensoc_client.Send(sendbytes_client, 200, SocketFlags.None)End IfCatch ex As ExceptionMsgBox(ex.Message, MsgBoxStyle.YesNoCancel, "tips!")End TryEnd Sub
視頻演示
匯川PLC與上位機(jī)通訊演示