五大通信庫,圍攻莫迪康
前面寫過一篇關(guān)于西門子PLC通信的文章,很多小伙伴對通信相關(guān)的內(nèi)容比較感興趣,今天給大家分享一下,關(guān)于ModbusTCP通信的內(nèi)容。
前記
本文主要以C#作為編程語言,結(jié)合目前市場上常用的5種通信庫,分別與臺達DVP-32 PLC進行通信研究,并對研究的結(jié)論進行分享。
通信庫簡介
一、EasyModbus
簡介:EasyModbus支持Modbus TCP, Modbus UDP and Modbus RTU,開源協(xié)議為MIT。
二、SharpModbus
簡介:SharpModbus是一個開源的Modbus工具,支持ModbusTCP與ModbusRTU,開源協(xié)議為MIT。
三、NModbus4
簡介:NModbus4是一個開源的Modbus通信庫,開源協(xié)議為MIT。
四、HslCommunication
簡介:HslCommunication是一個商業(yè)通信庫,涵蓋各種不同設備的通信。
五、xktComm
簡介:xktComm是一個商業(yè)通信庫,涵蓋西門子、歐姆龍、三菱、基恩士等PLC,也支持Modbus及OPC通信。
通信庫開發(fā)
針對各種庫創(chuàng)建了一個枚舉類型:
? public enum ModbusLib ? ?{ ? ? ? ?EasyModbus, ? ? ? ?SharpModbus, ? ? ? ?NModbus4, ? ? ? ?hslCommunication, ? ? ? ?xktComm ? ?}
針對每個庫創(chuàng)建一個通信對象:
? ? ? //創(chuàng)建對象 ? ? ? ? ? ? ? ?//EasyModbus ? ? ? ?private ModbusClient easyModbus = new ModbusClient(); ? ? ? ?//SharpModbus ? ? ? ?private ModbusMaster sharpModbus; ? ? ? ?//NModbus4 ? ? ? ?private ModbusIpMaster nModbus; ? ? ? ?//HslCommunication ? ? ? ?private ModbusTcpNet hsl; ? ? ? ?//xktComm ? ? ? ?private ModbusTcp xktModbus = new ModbusTcp();
針對各個庫實現(xiàn)連接方法:
? ? ? private bool Connect(ModbusLib modbusLib) ? ? ? ?{ ? ? ? ? ? ?try ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?switch (modbusLib) ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ?case ModbusLib.EasyModbus: ? ? ? ? ? ? ? ? ? ? ? ?easyModbus.Connect(ipAddress, port); ? ? ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ? ? ?case ModbusLib.SharpModbus: ? ? ? ? ? ? ? ? ? ? ? ?sharpModbus = ModbusMaster.TCP(ipAddress, port); ? ? ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ? ? ?case ModbusLib.NModbus4: ? ? ? ? ? ? ? ? ? ? ? ?TcpClient tcpClient = new TcpClient(); ? ? ? ? ? ? ? ? ? ? ? ?tcpClient.Connect(ipAddress, port); ? ? ? ? ? ? ? ? ? ? ? ?nModbus = ModbusIpMaster.CreateIp(tcpClient); ? ? ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ? ? ?case ModbusLib.hslCommunication: ? ? ? ? ? ? ? ? ? ? ? ?hsl = new ModbusTcpNet(ipAddress, port); ? ? ? ? ? ? ? ? ? ? ? ?return hsl.ConnectServer().IsSuccess; ? ? ? ? ? ? ? ? ? ?case ModbusLib.xktComm: ? ? ? ? ? ? ? ? ? ? ? ?return xktModbus.Connect(ipAddress, port); ? ? ? ? ? ? ? ? ? ?default: ? ? ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ?} ? ? ? ? ? ?} ? ? ? ? ? ?catch (Exception) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?return false; ? ? ? ? ? ?} ? ? ? ? ? ?return true; ? ? ? ?}
針對各個庫實現(xiàn)斷開連接方法
? ? ? private void DisConnect(ModbusLib modbusLib) ? ? ? ?{ ? ? ? ? ? ?switch (modbusLib) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?case ModbusLib.EasyModbus: ? ? ? ? ? ? ? ? ? ?easyModbus.Disconnect(); ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ?case ModbusLib.SharpModbus: ? ? ? ? ? ? ? ? ? ?sharpModbus.Dispose(); ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ?case ModbusLib.NModbus4: ? ? ? ? ? ? ? ? ? ?nModbus.Dispose(); ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ?case ModbusLib.hslCommunication: ? ? ? ? ? ? ? ? ? ?hsl.ConnectClose(); ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ?case ModbusLib.xktComm: ? ? ? ? ? ? ? ? ? ?xktModbus.DisConnect(); ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ? ? ?default: ? ? ? ? ? ? ? ? ? ?break; ? ? ? ? ? ?} ? ? ? ?}
針對各個庫實現(xiàn)一個讀取變量的方法:
? ? ? public bool ReadSingleReg(ModbusLib modbusLib) ? ? ? ?{ ? ? ? ? ? ?try ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?switch (modbusLib) ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ?case ModbusLib.EasyModbus: ? ? ? ? ? ? ? ? ? ? ? ?return easyModbus.ReadHoldingRegisters(0, 1).Length == 1; ? ? ? ? ? ? ? ? ? ?case ModbusLib.SharpModbus: ? ? ? ? ? ? ? ? ? ? ? ?return sharpModbus.ReadHoldingRegisters(1, 0, 1).Length == 1; ? ? ? ? ? ? ? ? ? ?case ModbusLib.NModbus4: ? ? ? ? ? ? ? ? ? ? ? ?return nModbus.ReadHoldingRegisters(0, 1).Length == 1; ? ? ? ? ? ? ? ? ? ?case ModbusLib.hslCommunication: ? ? ? ? ? ? ? ? ? ? ? ?return hsl.ReadInt16("0").IsSuccess; ? ? ? ? ? ? ? ? ? ?case ModbusLib.xktComm: ? ? ? ? ? ? ? ? ? ? ? ?return xktModbus.ReadKeepReg(0, 1).Length == 2; ? ? ? ? ? ? ? ? ? ?default: ? ? ? ? ? ? ? ? ? ? ? ?return false; ? ? ? ? ? ? ? ?} ? ? ? ? ? ?} ? ? ? ? ? ?catch (Exception) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?return false; ? ? ? ? ? ?} ? ? ? ?}
通信功能開發(fā)
由于每個庫實現(xiàn)的功能都一樣,所以開發(fā)了一個控件,將相關(guān)接口預留出來,后續(xù)直接調(diào)用即可。

控件提供了啟動(Start)、停止(Stop)事件,提供了Modbus庫類型,可以選擇設置,也可以通過屬性對通信結(jié)果和耗時時間進行賦值。
使用也非常簡單,直接拖到界面上,設置好庫類型,綁定上Start和Stop事件即可。
測試的核心代碼如下:
? ? ? private void modbusCtl1_Start(object sender, EventArgs e) ? ? ? ?{ ? ? ? ? ? ?if (sender is ModbusCtl control) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?ModbusLib modbusLib = control.ModbusType; ? ? ? ? ? ? ? ?if (Connect(modbusLib)) ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ?cts = new CancellationTokenSource(); ? ? ? ? ? ? ? ? ? ?stopwatch.Restart(); ? ? ? ? ? ? ? ? ? ?int successCount = 0;//成功次數(shù) ? ? ? ? ? ? ? ? ? ?int totalCount = 0;//總次數(shù) ? ? ? ? ? ? ? ? ? ?Task.Run(() => ? ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ? ?while (!cts.IsCancellationRequested) ? ? ? ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ? ? ? ?if (ReadSingleReg(modbusLib)) ? ? ? ? ? ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?successCount++; ? ? ? ? ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ? ? ? ? ? ?totalCount++; ? ? ? ? ? ? ? ? ? ? ? ? ? ?if (successCount != 0 && totalCount != 0) ? ? ? ? ? ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?control.Result = successCount.ToString() + "/" + totalCount.ToString(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?control.CommRate = stopwatch.ElapsedMilliseconds / successCount; ? ? ? ? ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ? ?}, cts.Token); ? ? ? ? ? ? ? ? ? ?cts.Token.Register(() => ? ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ? ?stopwatch.Stop(); ? ? ? ? ? ? ? ? ? ? ? ?Thread.Sleep(100); ? ? ? ? ? ? ? ? ? ? ? ?DisConnect(modbusLib); ? ? ? ? ? ? ? ? ? ?}); ? ? ? ? ? ? ? ?} ? ? ? ? ? ?} ? ? ? ?}
臺達PLC測試結(jié)果如下:

ModbusSlave測試結(jié)果如下:

西門子1200PLC測試結(jié)果如下:

西門子1500PLC測試結(jié)果如下:

通過以上測試發(fā)現(xiàn),ModbusTCP通信周期與ModbusTCP服務器(PLC硬件)緊密關(guān)聯(lián),甚至可以達到毫秒級。
可能有的小伙伴會感到疑惑,為什么SharpModbus連接臺達PLC那么慢,連接其他PLC都正常呢?