C# Modbut TCP 读写
一、封装基本连接、读写。读写 ReadHoldingRegisters ,当前有四种方案,现在只使用 ReadHoldingRegisters int 类型,需要其他方案自行新增。
public class ModbusClient { private TcpClient? tcpClient; private IModbusMaster? modbusMaster; public DateTime LastConTime; // 最后连接时间 public string Key; public bool Connect(string ipAddress, int port) { try { if(NetworkUtility.IsIPAndPortReachable(ipAddress, port)) { if (tcpClient != null && tcpClient.Connected) { IPEndPoint? remoteEndPoint = (IPEndPoint)tcpClient.Client.RemoteEndPoint; if (remoteEndPoint != null && remoteEndPoint.Address.ToString() == ipAddress && remoteEndPoint.Port == port) { return true; } else { tcpClient = new TcpClient(ipAddress, port); modbusMaster = new ModbusFactory().CreateMaster(tcpClient); } } else { tcpClient = new TcpClient(ipAddress, port); modbusMaster = new ModbusFactory().CreateMaster(tcpClient); } return true; } else { return false; } } catch (Exception ex) { Console.WriteLine("Error connecting to Modbus TCP: " + ex.Message); return false; } } public void Disconnect() { if (tcpClient != null) { tcpClient.Close(); tcpClient = null; } if (modbusMaster != null) { modbusMaster.Dispose(); } } public bool IsConnetc() { if(tcpClient != null) { return tcpClient.Connected; } return false; } /// <summary> /// 读取数据 /// </summary> /// <param name="slaveAddress">SlaveID</param> /// <param name="startAddress">读取的地址位</param> /// <param name="numberOfPoints">读取几位值</param> /// <returns></returns> public ushort[] ReadInputRegisters(byte slaveAddress, ushort startAddress, ushort numberOfPoints) { ushort[] result = { ushort.Parse("999") }; try { if(modbusMaster != null) { return modbusMaster.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints); } } catch (Exception ex) { Console.WriteLine("Error reading input registers: " + ex.Message); } return result; } /// <summary> /// 写入数据 /// </summary> /// <param name="slaveAddress">SlaveID</param> /// <param name="address">写入地址位</param> /// <param name="value">写入值</param> public void WriteSingleRegister(byte slaveAddress, ushort address, ushort value) { try { if(modbusMaster != null) { modbusMaster.WriteSingleRegister(slaveAddress, address, value); } } catch (Exception ex) { LogHelp.LogHelp.NoRepeatLog($"WriteSingleRegister ERR ", ex,10); } } }
检测IP
public class NetworkUtility { /// <summary> /// 判定IP是否OK /// </summary> /// <param name="ipAddress"></param> /// <returns></returns> public static bool IsPingable(string ipAddress) { try { Ping pingSender = new Ping(); PingReply reply = pingSender.Send(ipAddress); return reply.Status == IPStatus.Success; } catch (Exception) { return false; } } /// <summary> /// 判定端口号是否OK /// </summary> /// <param name="ipAddress"></param> /// <param name="port"></param> /// <returns></returns> public static bool IsPortOpen(string ipAddress, int port) { try { using (TcpClient client = new TcpClient()) { client.Connect(ipAddress, port); return true; } } catch (Exception) { return false; } } /// <summary> /// IP端口号是否可以连接 /// </summary> /// <param name="ipAddress"></param> /// <param name="port"></param> /// <returns></returns> public static bool IsIPAndPortReachable(string ipAddress, int port) { bool result = false; bool isPingable = IsPingable(ipAddress); if(!isPingable) { LogHelp.LogHelp.NoRepeatLog($"PING {ipAddress} 失败。",null,30); } else { bool isPortOpen = IsPortOpen(ipAddress, port); if (!isPortOpen) { LogHelp.LogHelp.NoRepeatLog($"PING {ipAddress} Port {port} 失败。", null, 30); } else { result = true; } } return result; } }
二、使用方法
ModbusClient modbusClient = new ModbusClient(); bool ConRestlt = modbusClient.Connect(_IP, _Port); if (ConRestlt) { var read = modbusClient.ReadInputRegisters(1,0,1); modbusClient.WriteSingleRegister(1,0,2); }
三、如果使用以上方案,多线程的时候每次都要连接。那么则使用一下方案。将连接状态缓存,并且定时断开连接
缓存
public class RequestCache { public static Dictionary<string, ModbusClient> ModbusTcpRequset { get; set; } = new Dictionary<string, ModbusClient>(); }
public class ModBusTcpConnect { /// <summary> /// 获取缓存中的连接状态,如果没有连接则尝试连接 /// </summary> /// <param name="_IP"></param> /// <param name="_Port"></param> /// <returns></returns> public static ModbusClient ModBusTCPConnect(string _IP, int _Port) { ModbusClient result = new ModbusClient(); try { string Key = $"{_IP}:{_Port}"; if (RequestCache.ModbusTcpRequset.ContainsKey(Key)) { ModbusClient ModbusClientCache = RequestCache.ModbusTcpRequset[Key]; if (!ModbusClientCache.IsConnetc()) { bool resiltCon = ModbusClientCache.Connect(_IP, _Port); } result = RequestCache.ModbusTcpRequset[Key]; if (result.IsConnetc()) { RequestCache.ModbusTcpRequset[Key].LastConTime = DateTime.Now; } } else { ModbusClient modbusClient = new ModbusClient(); bool ConRestlt = modbusClient.Connect(_IP, _Port); modbusClient.Key = Key; result = modbusClient; if(ConRestlt) { if (!RequestCache.ModbusTcpRequset.ContainsKey(Key)) { RequestCache.ModbusTcpRequset.TryAdd(Key, modbusClient); RequestCache.ModbusTcpRequset[Key].LastConTime = DateTime.Now; } } } } catch (Exception ex) { LogHelp.LogHelp.NoRepeatLog($"ModBusTCPConnect Err ", ex, 10); } return result; } /// <summary> /// 定时断开10分钟没有使用的连接 /// </summary> public void CloseModBusTCPConnect() { try { while (true) { if (RequestCache.ModbusTcpRequset != null) { List<ModbusClient> ModbusClientList = RequestCache.ModbusTcpRequset.Values.ToList(); DateTime dateNow = DateTime.Now; foreach (ModbusClient item in ModbusClientList) { if ((dateNow - Convert.ToDateTime(item.LastConTime)).TotalSeconds > 60 * 10) { item.Disconnect(); if (RequestCache.ModbusTcpRequset.ContainsKey(item.Key)) { RequestCache.ModbusTcpRequset.Remove(item.Key); } } } } Thread.Sleep(1000 * 10); } } catch (Exception ex) { LogHelp.LogHelp.NoRepeatLog($"CloseModBusTCPConnect Err ", ex, 10); throw; } } }
四、使用方案
ModbusClient modbusClient = ModBusTcpConnect.ModBusTCPConnect(IP, Convert.ToInt32(Port)); if (modbusClient.IsConnetc()) { byte slaveAddress = (byte)Convert.ToInt32(areaConfiguration.SlaveID); ushort startAddress = ushort.Parse(Address); ushort numberOfPoints = ushort.Parse(NumberOfPoints); ushort[] registerValues = modbusClient.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints); if (registerValues != null) { result = string.Join(" ", Array.ConvertAll(registerValues, x => x.ToString())); } }
五、使用Modbus Slave 工具作为服务端测试
1、配置基础信息
2、自定义IP或者端口
前面的序号就是 地址位 地址位的值默认是int类型
软件下载地址 :https://wwt.lanzouo.com/ikutE1wlk8oh
ModbusPollSetup 是服务端
ModbusSlaveSetup 是客户端
以上代码是客户端,我们需要安装的是 ModbusPollSetup 服务端