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 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)
{
MessageLog.GetInstance.Show($"设备【{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();
MessageLog.GetInstance.Show($"设备【{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)
{
MessageLog.GetInstance.ShowEx($"ModbusTcp 连接失败,IP = {IPAdress},Port = {Port}");
MessageLog.GetInstance.ShowEx(ex.ToString());
ErrorFlag = true;
}
if (!IsReconnect) break;
Thread.Sleep(3000);
}
}
if (ErrorFlag && IsReconnect) MessageLog.GetInstance.Show("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 = "4"+(Convert.ToInt32(address.Substring(1, 1)) - 1).ToString();
string tail = address.Substring(2, 3);
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 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)
{
MessageLog.GetInstance.ShowEx($"读取地址:【{address}:= {startAddress}】,读取类型:【{commandType.ToString()}】出错,{ex.ToString()}");
ExceptionHandling(ex);
}
return default(object);
}
#region 180项目调用
public int GetAddress(string address, string target)
{
if (address == null) return -1;
if (address.Length > 0)
{
if (address.ToUpper().Contains("D") && address.Length == 5)
{
try
{
string head = "4"+(Convert.ToInt32(address.Substring(1, 1)) - 1).ToString();
string tail = address.Substring(2, 3);
address = head + tail;
return Convert.ToInt32(address);
}
catch (Exception)
{
//打印日志
}
}
}
return -1;
}
public object Read(string address, ushort len, string target, byte slaveAddress = 1)
{
if (address == null || tcpClient == null) return default(object);
ushort startAddress = (ushort)GetAddress(address, target);
CommandType commandType = CommandType.HoldingRegisters;
try
{
if (address.ToUpper().Contains("D"))
{
commandType = CommandType.HoldingRegisters;
return master.ReadHoldingRegisters(slaveAddress, startAddress, len);
}
}
catch (Exception ex)
{
MessageLog.GetInstance.ShowEx($"读取地址:【{address}:= {startAddress}】,读取类型:【{commandType.ToString()}】出错,{ex.ToString()}");
ExceptionHandling(ex);
}
return default(object);
}
public void Write(string address, T value, string target, byte slaveAddress = 1)
{
if (address == null || tcpClient == null) return;
ushort startAddress = (ushort)GetAddress(address,target);
CommandType commandType = CommandType.Coils;
try
{
if (address.ToUpper().Contains("D"))
{
commandType = CommandType.HoldingRegisters;
if (value is ushort ushortValue)
{
master.WriteSingleRegister(slaveAddress, startAddress, ushortValue);
}
}
}
catch (Exception ex)
{
MessageLog.GetInstance.ShowEx($"写入地址:【{address}:= {startAddress}】,写入类型:【{commandType.ToString()}】出错,{ex.ToString()}");
ExceptionHandling(ex);
}
}
#endregion
public void Write(string address, T value, byte slaveAddress = 1)
{
if (address == null || tcpClient == null) return;
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;
}
}
catch (Exception ex)
{
MessageLog.GetInstance.ShowEx($"写入地址:【{address}:= {startAddress}】,写入类型:【{commandType.ToString()}】出错,{ex.ToString()}");
ExceptionHandling(ex);
}
}
#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)
{
MessageLog.GetInstance.Show(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,
}
}