|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746 |
- 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;
- using BPA.Helper;
-
- 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<string> Show { get; set; }
- public Action<string> ShowEx { get; set; }
-
- /// <summary>
- /// 连接plc 原料设备成功
- /// </summary>
- public Action ConnectOk { get; set; }
-
- public Action ConnectFail { get; set; }
-
- public Action Disconnect { get; set; }
-
- public bool IsReconnect { get; set; } = false; //true=启用重连,false=禁用重连
-
- /// <summary>
- /// 判断是否连接成功
- /// </summary>
- public bool Connected => tcpClient == null ? false : tcpClient.Connected;
-
- /// <summary>
- /// ModbusTcp 连接设备
- /// </summary>
- /// <param name="ip">ip 地址</param>
- /// <param name="port">端口号,默认502</param>
- 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 = 3000;//读取超时时间
- master.Transport.WriteTimeout = 3000;//写入超时时间
- 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 重连成功!");
- }
-
- /// <summary>
- /// 获取 M 区 和 VW 区的Modbus的地址
- /// </summary>
- /// <param name="address"></param>
- /// <returns></returns>
- public int GetAddress(string address)
- {
- if (address == null) return -1;
- if (address.Length > 0)
- {
- address = address.Trim();
- if (address.ToUpper().Contains("GM") && address.Length >= 3)
- {
- var res = address.Remove(0, 2);
- if (res != null && res.Length > 0)
- {
- return int.Parse(res) + 4096;
- }
- }
- else 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("I") && address.Length >= 2)
- {
- 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) + ExitAddress;
- }
- }
- }
- }
- else if (address.ToUpper().Contains("GI") && address.Length >= 3)
- {
- var res = address.Remove(0, 2);
- if (res != null && res.Length > 0)
- {
- return int.Parse(res);
- }
- }
- else if (address.ToUpper().Contains("LB") && address.Length >= 3)
- {
- var res = address.Substring(2);
- if (res != null && res.Length > 0)
- {
- if (int.TryParse(res, out int firstAddress))
- {
- return firstAddress;
- }
- }
- }
-
- else if ((address.ToUpper().Contains("VW") || address.ToUpper().Contains("VD")) && 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
- {
- //D1001
- 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") || address.ToUpper().Contains("GM"))
- {
- commandType = CommandType.Coils;
- return master.ReadCoils(slaveAddress, startAddress, len);
- }
- else if (address.ToUpper().Contains("VD"))
- {
- commandType = CommandType.HoldingRegisters;
- var var1 = master.ReadHoldingRegisters(slaveAddress, startAddress, len);
- var var2 = master.ReadHoldingRegisters(slaveAddress, (ushort)(startAddress + 1), len);
- int dest = 0;
- dest |= (var2[0] & 0x0000ffff);
- dest = (dest << 16) | (var1[0] & 0x0000ffff);
- return dest;
-
- }
- 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("VD"))
- {
- commandType = CommandType.HoldingRegisters;
- var var1 = master.ReadHoldingRegisters(slaveAddress, startAddress, len);
- var var2 = master.ReadHoldingRegisters(slaveAddress, (ushort)(startAddress + 2), len);
- int dest = 0;
- dest |= (var2[0] & 0x0000ffff);
- dest = (dest << 16) | (var1[0] & 0x0000ffff);
- return dest;
-
- }
- else if (address.ToUpper().Contains("I"))
- {
- commandType = CommandType.Inputs;
- return master.ReadInputs(slaveAddress, startAddress, len);
- }
- else if (address.ToUpper().Contains("LB"))
- {
- commandType = CommandType.Coils;
- return master.ReadCoils(slaveAddress, startAddress, len);
- }
-
- }
- catch (Exception ex)
- {
- ShowEx?.Invoke($"读取地址:【{address}:= {startAddress}】,读取类型:【{commandType.ToString()}】出错,{ex.ToString()}");
- ExceptionHandling(ex);
- }
- return default(object);
- }
-
- /// <summary>
- /// 写入数据
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="address">写入地址</param>
- /// <param name="value">写入值</param>
- /// <param name="Retries">重试次数</param>
- /// <param name="slaveAddress">从站地址</param>
- /// <returns></returns>
- public bool Write<T>(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<T>(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 return false;
- }
- if (address.ToUpper().Contains("GM") && address.Length >= 3)
- {
- 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 return false;
- }
- else if (address.ToUpper().Contains("LB"))
- {
- 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 return false;
- }
- else if (address.ToUpper().Contains("VD"))
- {
- commandType = CommandType.HoldingRegisters;
- if (value is int intValue)
- {
-
- var val1 = (UInt16)intValue;
- var val2 = Convert.ToUInt16(intValue >> 16 & 0x0000ffff);
- master.WriteSingleRegister(slaveAddress, startAddress, val1);
- master.WriteSingleRegister(slaveAddress, (ushort)(startAddress + 1), val2);
- }
- else return false;
-
- }
- 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<ushort[]> ushortLists = new List<ushort[]>();
- 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 return false;
- }
- 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 字符串数据读写
- /// <summary>
- /// 赋值string
- /// </summary>
- /// <param name="StartAddress"></param>
- /// <param name="value"></param>
- /// <returns></returns>
- 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.ToUshorts());
- }
-
- /// <summary>
- /// 获取string
- /// </summary>
- /// <param name="StartAddress"></param>
- /// <param name="len"></param>
- /// <returns></returns>
- 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.ToBytes()).Trim(new char[] { '\0' });
- }
- return String.Empty;
- }
-
- #endregion
-
- #region 浮点数数据读写
- /// <summary>
- /// 赋值Real类型数据
- /// </summary>
- /// <param name="StartAddress"></param>
- /// <param name="value"></param>
- public bool SetReal(string StartAddress, float value, int Retries = 1)
- {
- var bytes = BitConverter.GetBytes(value);
- return Write(StartAddress, bytes.ToUshorts(), Retries);
- }
-
- /// <summary>
- /// 获取float类型数据
- /// </summary>
- /// <param name="StartAddress"></param>
- /// <returns></returns>
- public float GetReal(string StartAddress)
- {
- var res = Read(StartAddress, 2);
- if (res != null && res is ushort[] ushorts)
- {
- return BitConverter.ToSingle(ushorts.ToBytes(), 0);
- }
- return 0;
- }
- #endregion
-
- #region 整数数据读写
- /// <summary>
- /// 赋值Real类型数据
- /// </summary>
- /// <param name="StartAddress"></param>
- /// <param name="value"></param>
- public void SetUint(string StartAddress, uint value)
- {
- var bytes = BitConverter.GetBytes(value);
- Write(StartAddress, bytes.ToUshorts());
- }
-
- /// <summary>
- /// 获取float类型数据
- /// </summary>
- /// <param name="StartAddress"></param>
- /// <returns></returns>
- public uint GetUint(string StartAddress)
- {
- var res = Read(StartAddress, 2);
- if (res != null && res is ushort[] ushorts)
- {
- return BitConverter.ToUInt32(ushorts.ToBytes(), 0);
- }
- return 0;
- }
- #endregion
-
- #region 批量数据读取
- /// <summary>
- /// 读取多个线圈
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="num"></param>
- /// <param name="slaveAddress"></param>
- /// <returns></returns>
- public bool[] ReadCoils(ushort startAddress, ushort num, byte slaveAddress = 1)
- {
- return master.ReadCoils(slaveAddress, startAddress, num);
- }
-
- /// <summary>
- /// 读取多个输入线圈
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="num"></param>
- /// <param name="slaveAddress"></param>
- /// <returns></returns>
- public bool[] ReadInputs(ushort startAddress, ushort num, byte slaveAddress = 1)
- {
- return master.ReadInputs(slaveAddress, startAddress, num);
- }
-
- /// <summary>
- /// 读取多个保持寄存器
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="num"></param>
- /// <param name="slaveAddress"></param>
- /// <returns></returns>
- public ushort[] ReadHoldingRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
- {
- return master.ReadHoldingRegisters(slaveAddress, startAddress, num);
- }
-
- /// <summary>
- /// 读取多个输入寄存器
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="num"></param>
- /// <param name="slaveAddress"></param>
- /// <returns></returns>
- public ushort[] ReadInputRegisters(ushort startAddress, ushort num, byte slaveAddress = 1)
- {
- return master.ReadInputRegisters(slaveAddress, startAddress, num);
- }
- #endregion
-
- #region 批量数据写入
- /// <summary>
- /// 写入多个线圈
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="value"></param>
- /// <param name="slaveAddress"></param>
- public void WriteMultipleCoils(ushort startAddress, bool[] value, byte slaveAddress = 1)
- {
- master.WriteMultipleCoils(slaveAddress, startAddress, value);
- }
-
- /// <summary>
- /// 写入多个寄存器
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="value"></param>
- /// <param name="slaveAddress"></param>
- public void WriteMultipleRegisters(ushort startAddress, ushort[] value, byte slaveAddress = 1)
- {
- master.WriteMultipleRegisters(slaveAddress, startAddress, value);
- }
- #endregion
-
- #region 单个数据读取
- /// <summary>
- /// 读取单个线圈
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="slaveAddress"></param>
- /// <returns></returns>
- 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];
- }
-
- /// <summary>
- /// 读取单个输入线圈
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="slaveAddress"></param>
- /// <returns></returns>
- 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];
- }
-
- /// <summary>
- /// 读取单个保持寄存器
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="slaveAddress"></param>
- /// <returns></returns>
- 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];
- }
-
- /// <summary>
- /// 读取单个输入寄存器
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="slaveAddress"></param>
- /// <returns></returns>
- 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 单个数据写入
- /// <summary>
- /// 写入单个线圈
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="value"></param>
- /// <param name="slaveAddress"></param>
- public void WriteSingleCoil(ushort startAddress, bool value, byte slaveAddress = 1)
- {
- master.WriteSingleCoil(slaveAddress, startAddress, value);
- }
-
- /// <summary>
- /// 写入单个寄存器
- /// </summary>
- /// <param name="startAddress"></param>
- /// <param name="value"></param>
- /// <param name="slaveAddress"></param>
- public void WriteSingleRegister(ushort startAddress, ushort value, byte slaveAddress = 1)
- {
- master.WriteSingleRegister(slaveAddress, startAddress, value);
- }
- #endregion
- }
-
- public enum CommandType
- {
- /// <summary>
- /// 线圈操作
- /// </summary>
- Coils,
- /// <summary>
- /// 输入线圈操作
- /// </summary>
- Inputs,
- /// <summary>
- /// 保持寄存器操作
- /// </summary>
- HoldingRegisters,
- /// <summary>
- /// 输入寄存器操作
- /// </summary>
- InputRegisters,
- }
-
- }
|