using BPASmartClient.Helper; //using BPASmartClient.Message; using NModbus; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace BPASmartClient.Modbus { public class ModbusTcp { //private volatile static ModbusTcp _Instance; //public static ModbusTcp GetInstance => _Instance ?? (_Instance = new ModbusTcp()); //private ModbusTcp() { } private ModbusFactory modbusFactory; private IModbusMaster master; private TcpClient tcpClient; private ManualResetEvent mre = new ManualResetEvent(false); public string IPAdress; public int Port; public Action Show { get; set; } public Action ShowEx { get; set; } /// /// 连接plc 原料设备成功 /// public Action ConnectOk { get; set; } public Action ConnectFail { get; set; } public Action Disconnect { get; set; } public bool IsReconnect { get; set; } = true; //true=启用重连,false=禁用重连 /// /// 判断是否连接成功 /// public bool Connected => tcpClient == null ? false : tcpClient.Connected; /// /// ModbusTcp 连接设备 /// /// ip 地址 /// 端口号,默认502 public void ModbusTcpConnect(string ip, int port = 502) { Show?.Invoke($"设备【{ip}:{port}】连接中。。。。"); IPAdress = ip; Port = port; modbusFactory = new ModbusFactory(); Connect(); if (Connected) { master.Transport.ReadTimeout = 2000;//读取超时时间 master.Transport.WriteTimeout = 2000;//写入超时时间 master.Transport.Retries = 10;//重试次数 ConnectOk?.Invoke(); Show?.Invoke($"设备【{ip}:{port}】连接成功"); } else { ConnectFail?.Invoke(); } } public void Dispose() { master.Dispose(); Disconnect?.Invoke(); } private void Connect() { bool ErrorFlag = false; while (!Connected) { try { tcpClient = new TcpClient(IPAdress, Port); //mre.Reset(); //tcpClient.BeginConnect(IPAdress, Port, (res) => { mre.Set(); }, null); //if (!mre.WaitOne(1500, false)) break; master = modbusFactory.CreateMaster(tcpClient); } catch (Exception ex) { if (!ErrorFlag) { ShowEx?.Invoke($"ModbusTcp 连接失败,IP = {IPAdress},Port = {Port}"); ShowEx?.Invoke(ex.ToString()); ErrorFlag = true; } if (!IsReconnect) break; Thread.Sleep(3000); } } if (ErrorFlag && IsReconnect) Show?.Invoke("ModbusTcp 重连成功!"); } /// /// 获取 M 区 和 VW 区的Modbus的地址 /// /// /// public int GetAddress(string address) { if (address == null) return -1; if (address.Length > 0) { if (address.ToUpper().Contains("M") && address.Length >= 4) { var res = address.Substring(1).Split('.'); if (res != null && res.Length == 2) { if (int.TryParse(res[0], out int firstAddress) && int.TryParse(res[1], out int ExitAddress)) { if (ExitAddress >= 0 && ExitAddress <= 7) { return (firstAddress * 8) + 320 + ExitAddress; } } } } else if (address.ToUpper().Contains("VW") && address.Length >= 3) { var res = address.Substring(2); if (res != null && int.TryParse(res, out int tempAddress)) { return (tempAddress / 2) + 100; } } else if (address.ToUpper().Contains("LW") && address.Length >= 3) { var res = address.Substring(2); if (res != null && int.TryParse(res, out int LwAddress)) { return LwAddress; } } else if (address.ToUpper().Contains("D") && address.Length == 5) { try { string head = (Convert.ToInt32(address.Substring(1, 1))).ToString(); int num = Convert.ToInt32(address.Substring(2, 3)); int len = num.ToString().Length; string tail = string.Empty; switch (len) { case 1: if ((Convert.ToInt32(address.Substring(4, 1))).ToString().Length > 1) { tail = "0" + (Convert.ToInt32(address.Substring(4, 1))).ToString(); } else { tail = "00" + (Convert.ToInt32(address.Substring(4, 1))).ToString(); } break; case 2: if ((Convert.ToInt32(address.Substring(3, 2))).ToString().Length > 2) { tail = (Convert.ToInt32(address.Substring(3, 2))).ToString(); } else { tail = "0" + (Convert.ToInt32(address.Substring(3, 2))).ToString(); } break; case 3: tail = (Convert.ToInt32(address.Substring(2, 3))).ToString(); break; } address = head + tail; return Convert.ToInt32(address); } catch (Exception) { //打印日志 } } } return -1; } private void ExceptionHandling(Exception ex) { if (ex.InnerException is SocketException) { if (IsReconnect) { tcpClient = null; Connect(); } Disconnect?.Invoke(); } } public short ReadShort(string address) { var res = Read(address); if (res != null && res is short[] shorts && shorts.Length == 1) { return shorts[0]; } return 0; } public object Read(string address, ushort len = 1, byte slaveAddress = 1) { if (address == null || tcpClient == null) return default(object); ushort startAddress = (ushort)GetAddress(address); CommandType commandType = CommandType.Coils; try { if (address.ToUpper().Contains("M")) { commandType = CommandType.Coils; return master.ReadCoils(slaveAddress, startAddress, len); } else if (address.ToUpper().Contains("VW") || address.ToUpper().Contains("LW") || address.ToUpper().Contains("D")) { commandType = CommandType.HoldingRegisters; return master.ReadHoldingRegisters(slaveAddress, startAddress, len); } else if (address.ToUpper().Contains("I")) { commandType = CommandType.Inputs; return master.ReadInputs(slaveAddress, startAddress, len); } } catch (Exception ex) { ShowEx?.Invoke($"读取地址:【{address}:= {startAddress}】,读取类型:【{commandType.ToString()}】出错,{ex.ToString()}"); ExceptionHandling(ex); } return default(object); } /// /// 写入数据 /// /// /// 写入地址 /// 写入值 /// 重试次数 /// 从站地址 /// public bool Write(string address, T value, int Retries = 1, byte slaveAddress = 1) { int count = 0; if (Retries == 1 || Retries == 0) { return TempWrite(address, value, slaveAddress); } else if (Retries > 1) { bool isok = false; while (count < Retries) { count++; TempWrite(address, value, slaveAddress); var res = Read(address); if (res != null && res.ToString() == value.ToString()) { isok = true; break; } } return isok; } return false; } private bool TempWrite(string address, T value, byte slaveAddress = 1) { if (address == null || tcpClient == null) { Show?.Invoke("写入失败,地址为空或断开连接"); return false; } ushort startAddress = (ushort)GetAddress(address); CommandType commandType = CommandType.Coils; try { if (address.ToUpper().Contains("M")) { commandType = CommandType.Coils; if (value is bool boolValue) master.WriteSingleCoil(slaveAddress, startAddress, boolValue); else if (value is bool[] boolsValue) master.WriteMultipleCoils(slaveAddress, startAddress, boolsValue); } else if (address.ToUpper().Contains("VW") || address.ToUpper().Contains("LW") || address.ToUpper().Contains("D")) { commandType = CommandType.HoldingRegisters; if (value is ushort ushortValue) master.WriteSingleRegister(slaveAddress, startAddress, ushortValue); else if (value is ushort[] ushortsValue) { int len = 100; if (ushortsValue.Length > len) { List ushortLists = new List(); for (int i = 0; i < ushortsValue.Length / len; i++) { ushortLists.Add(ushortsValue.Skip(0).Take(len).ToArray()); } int y = ushortsValue.Length % len; if (y > 0) { ushortLists.Add(ushortsValue.Skip(ushortsValue.Length - y).Take(y).ToArray()); } foreach (var item in ushortLists) { master.WriteMultipleRegisters(slaveAddress, startAddress, item); startAddress += (ushort)item.Length; } } else { master.WriteMultipleRegisters(slaveAddress, startAddress, ushortsValue); } } } else if (address.ToUpper().Contains("I")) { commandType = CommandType.Inputs; } Show?.Invoke($"成功,地址:{address},值:{value}"); return true; } catch (Exception ex) { ShowEx?.Invoke($"写入地址:【{address}:= {startAddress}】,写入类型:【{commandType.ToString()}】出错,{ex.ToString()}"); ExceptionHandling(ex); return false; } } #region 字符串数据读写 /// /// 赋值string /// /// /// /// public void SetString(string StartAddress, string value) { //获取指定的编码不存在的时候需要安装 System.Text.Encoding.CodePages nuget包 //然后使用下面的代码就可以获取了 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var bytes = Encoding.GetEncoding("gb2312")?.GetBytes(value); Write(StartAddress, bytes.BytesToUshorts()); } /// /// 获取string /// /// /// /// public string GetString(string StartAddress, ushort len) { var res = Read(StartAddress, len); if (res != null && res is ushort[] ushorts) { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); return Encoding.GetEncoding("gb2312")?.GetString(ushorts.UshortsToBytes()).Trim(new char[] { '\0' }); } return String.Empty; } #endregion #region 浮点数数据读写 /// /// 赋值Real类型数据 /// /// /// public void SetReal(string StartAddress, float value) { var bytes = BitConverter.GetBytes(value); Write(StartAddress, bytes.BytesToUshorts()); } /// /// 获取float类型数据 /// /// /// public float GetReal(string StartAddress) { var res = Read(StartAddress, 2); if (res != null && res is ushort[] ushorts) { return BitConverter.ToSingle(ushorts.UshortsToBytes(), 0); } return 0; } #endregion #region 整数数据读写 /// /// 赋值Real类型数据 /// /// /// public void SetUint(string StartAddress, uint value) { var bytes = BitConverter.GetBytes(value); Write(StartAddress, bytes.BytesToUshorts()); } /// /// 获取float类型数据 /// /// /// public uint GetUint(string StartAddress) { var res = Read(StartAddress, 2); if (res != null && res is ushort[] ushorts) { return BitConverter.ToUInt32(ushorts.UshortsToBytes(), 0); } return 0; } #endregion #region 批量数据读取 /// /// 读取多个线圈 /// /// /// /// /// public bool[] ReadCoils(ushort startAddress, ushort num, byte slaveAddress = 1) { return master.ReadCoils(slaveAddress, startAddress, num); } /// /// 读取多个输入线圈 /// /// /// /// /// public bool[] ReadInputs(ushort startAddress, ushort num, byte slaveAddress = 1) { return master.ReadInputs(slaveAddress, startAddress, num); } /// /// 读取多个保持寄存器 /// /// /// /// /// public ushort[] ReadHoldingRegisters(ushort startAddress, ushort num, byte slaveAddress = 1) { return master.ReadHoldingRegisters(slaveAddress, startAddress, num); } /// /// 读取多个输入寄存器 /// /// /// /// /// public ushort[] ReadInputRegisters(ushort startAddress, ushort num, byte slaveAddress = 1) { return master.ReadInputRegisters(slaveAddress, startAddress, num); } #endregion #region 批量数据写入 /// /// 写入多个线圈 /// /// /// /// public void WriteMultipleCoils(ushort startAddress, bool[] value, byte slaveAddress = 1) { master.WriteMultipleCoils(slaveAddress, startAddress, value); } /// /// 写入多个寄存器 /// /// /// /// public void WriteMultipleRegisters(ushort startAddress, ushort[] value, byte slaveAddress = 1) { master.WriteMultipleRegisters(slaveAddress, startAddress, value); } #endregion #region 单个数据读取 /// /// 读取单个线圈 /// /// /// /// public bool ReadCoils(ushort startAddress, byte slaveAddress = 1) { var result = master.ReadCoils(slaveAddress, startAddress, 1); if (result == null) return false; if (result.Length == 1) return result[0]; return false; //return master.ReadCoils(slaveAddress, startAddress, 1)[0]; } /// /// 读取单个输入线圈 /// /// /// /// public bool ReadInputs(ushort startAddress, byte slaveAddress = 1) { var result = master.ReadInputs(slaveAddress, startAddress, 1); if (result == null) return false; if (result.Length == 1) return result[0]; return false; //return master.ReadInputs(slaveAddress, startAddress, 1)[0]; } /// /// 读取单个保持寄存器 /// /// /// /// public ushort ReadHoldingRegisters(ushort startAddress, byte slaveAddress = 1) { if (tcpClient == null) return 0; ushort[] result = null; try { result = master.ReadHoldingRegisters(slaveAddress, startAddress, 1); } catch (Exception ex) { ShowEx?.Invoke(ex.ToString()); tcpClient = null; Connect(); } if (result == null) return 0; if (result.Length == 1) return result[0]; return 0; //return master.ReadHoldingRegisters(slaveAddress, startAddress, 1)[0]; } /// /// 读取单个输入寄存器 /// /// /// /// public ushort ReadInputRegisters(ushort startAddress, byte slaveAddress = 1) { var result = master.ReadInputRegisters(slaveAddress, startAddress, 1); if (result == null) return 0; if (result.Length == 1) return result[0]; return 0; //return master.ReadInputRegisters(slaveAddress, startAddress, 1)[0]; } #endregion #region 单个数据写入 /// /// 写入单个线圈 /// /// /// /// public void WriteSingleCoil(ushort startAddress, bool value, byte slaveAddress = 1) { master.WriteSingleCoil(slaveAddress, startAddress, value); } /// /// 写入单个寄存器 /// /// /// /// public void WriteSingleRegister(ushort startAddress, ushort value, byte slaveAddress = 1) { master.WriteSingleRegister(slaveAddress, startAddress, value); } #endregion } public enum CommandType { /// /// 线圈操作 /// Coils, /// /// 输入线圈操作 /// Inputs, /// /// 保持寄存器操作 /// HoldingRegisters, /// /// 输入寄存器操作 /// InputRegisters, } }