You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

321 lines
11 KiB

7 months ago
using HslCommunication;
using HslCommunication.Core;
7 months ago
using HslCommunication.ModBus;
using HybirdFrameworkCore.Utils;
using log4net;
namespace HybirdFrameworkDriver.ModbusTcpMaster;
7 months ago
public class ModbusTcpMaster
{
7 months ago
public delegate void MyReadAction(ModbusTcpMaster str);
private static readonly ILog Log = LogManager.GetLogger(typeof(ModbusTcpMaster));
private ModbusTcpNet? ModbusTcpNet;
7 months ago
7 months ago
public string Ip { get; set; } = "127.0.0.1";
7 months ago
public int Port { get; set; } = 502;
public static DataFormat DataFormat { get; set; } = DataFormat.ABCD;
7 months ago
public int Duration { get; set; } = 1000;
7 months ago
public bool Connected { get; set; }
7 months ago
/// <summary>
/// 自动重连开关
/// </summary>
public bool AutoReConnect { get; set; }
7 months ago
public string connectId { get; set; }
public MyReadAction? ReadAction { get; set; }
7 months ago
7 months ago
private bool StopFlag { get; set; }
7 months ago
7 months ago
private ILog GetLog()
{
return Log;
}
public bool Connect()
{
Thread.Sleep(Duration);
7 months ago
GetLog().Info($"begin to connect {Ip}:{Port}");
try
{
if (ModbusTcpNet == null)
{
ModbusTcpNet = new ModbusTcpNet(Ip, Port);
ModbusTcpNet.DataFormat = DataFormat;
7 months ago
var result = ModbusTcpNet.ConnectServer();
7 months ago
connectId = ModbusTcpNet.ConnectionId;
7 months ago
if (result.IsSuccess)
{
Connected = true;
GetLog().Info($"connect {Ip}:{Port} success");
StartListen();
}
else
{
GetLog().Info($"connect {Ip}:{Port} failed {result.Message}");
if (AutoReConnect)
{
return Connect();
}
7 months ago
}
}
}
catch (Exception e)
{
GetLog().Error($"connect {Ip}:{Port} exception {e.Message}");
if (AutoReConnect)
{
return Connect();
}
7 months ago
}
return Connected;
}
7 months ago
public void StartListen()
{
StopListen();
StopFlag = false;
7 months ago
var readThread = new Thread(ReadFunc)
7 months ago
{
IsBackground = true,
Name = $"{Ip}:{Port}-reader-thread"
};
readThread.Start();
}
public void StopListen()
{
StopFlag = true;
Thread.Sleep(500);
}
private void ReadFunc()
{
if (ReadAction != null)
while (!StopFlag)
try
{
7 months ago
ReadAction(this);
Thread.Sleep(Duration);
}
catch (Exception e)
{
Log.Error(e);
}
7 months ago
GetLog().Info("stop listen");
}
public byte[]? ReadRegister(int registerNo, int start, int length)
{
start = start % 16 == 0 ? start / 16 : start / 16 + 1;
length = length % 8 == 0 ? length / 8 : length / 8 + 1;
OperateResult<byte[]> result = ModbusTcpNet.Read("x=3;" + (registerNo + start), (ushort)length);
7 months ago
if (result.IsSuccess) return result.Content;
7 months ago
return null;
}
public byte[]? BatchReadHolderRegister(int registerNo, int length)
7 months ago
{
OperateResult<byte[]> result = ModbusTcpNet.Read("x=3;" + registerNo, (ushort)length);
7 months ago
if (result.IsSuccess) return result.Content;
7 months ago
return null;
}
public byte[]? BatchReadInputRegister(int registerNo, int length)
{
OperateResult<byte[]> result = ModbusTcpNet.Read("x=4;" + registerNo, (ushort)length);
if (result.IsSuccess) return result.Content;
return null;
}
/// <summary>
/// 读取线圈,需要指定起始地址,如果富文本地址不指定,默认使用的功能码是 0x01<br />
/// To read the coil, you need to specify the start address. If the rich text address is not specified, the default function code is 0x01.
/// </summary>
/// <param name="address">起始地址,格式为"1234"</param>
/// <returns>带有成功标志的bool对象</returns>
public OperateResult<bool> ReadCoil(string address) => ModbusTcpNet.ReadBool(address);
/// <summary>
/// 批量的读取线圈,需要指定起始地址,读取长度,如果富文本地址不指定,默认使用的功能码是 0x01<br />
/// For batch reading coils, you need to specify the start address and read length. If the rich text address is not specified, the default function code is 0x01.
/// </summary>
/// <param name="address">起始地址,格式为"1234"</param>
/// <param name="length">读取长度</param>
/// <returns>带有成功标志的bool数组对象</returns>
public OperateResult<bool[]> ReadCoil(string address, ushort length)
{
return ModbusTcpNet.ReadBool(address, length);
}
/// <summary>
/// 读取输入线圈,需要指定起始地址,如果富文本地址不指定,默认使用的功能码是 0x02<br />
/// To read the input coil, you need to specify the start address. If the rich text address is not specified, the default function code is 0x02.
/// </summary>
/// <param name="address">起始地址,格式为"1234"</param>
/// <returns>带有成功标志的bool对象</returns>
public OperateResult<bool> ReadDiscrete(string address)
{
return ModbusTcpNet.ReadDiscrete(address);
}
/// <summary>
/// 批量的读取输入点,需要指定起始地址,读取长度,如果富文本地址不指定,默认使用的功能码是 0x02<br />
/// To read input points in batches, you need to specify the start address and read length. If the rich text address is not specified, the default function code is 0x02
/// </summary>
/// <param name="address">起始地址,格式为"1234"</param>
/// <param name="length">读取长度</param>
/// <returns>带有成功标志的bool数组对象</returns>
public OperateResult<bool[]> ReadDiscrete(string address, ushort length)
{
return ModbusTcpNet.ReadDiscrete(address, length);
}
7 months ago
public bool WriteValue<T>(ModbusProperty<T> property)
{
7 months ago
var value = property.Value;
7 months ago
if (value == null)
{
GetLog().Warn($"write property{property.RegisterNo} null value");
return false;
}
7 months ago
var result = false;
var dataType = property.Type;
var start = property.Start;
var length = property.Length;
var registerNo = property.RegisterNo - 40000;
var setValue = BitUtls.Value2Bytes(value, property.Scale, property.Offset);
OperateResult operateResult;
7 months ago
switch (dataType)
{
case ModbusDataType.Register:
if (setValue.Length < length * 2)
7 months ago
{
//byte 需要读取寄存器中的内容
if (typeof(T) == typeof(byte))
7 months ago
{
7 months ago
OperateResult<byte[]> readResultRegister = ModbusTcpNet.Read("x=3;" + registerNo, 1);
if (readResultRegister.IsSuccess)
{
switch (DataFormat)
{
case DataFormat.ABCD:
readResultRegister.Content[1] = setValue[0];
break;
case DataFormat.BADC:
readResultRegister.Content[0] = setValue[0];
break;
}
7 months ago
operateResult = ModbusTcpNet.Write("x=16;" + registerNo, readResultRegister.Content);
result = operateResult.IsSuccess;
}
7 months ago
}
//其他类型 String 占用几个寄存器 直接补0
else
{
7 months ago
var preWriteCont = new byte[length * 2];
7 months ago
switch (DataFormat)
{
case DataFormat.ABCD:
7 months ago
for (var i = 0; i < setValue.Length; i++)
if (i % 2 == 0)
preWriteCont[i + 1] = setValue[i];
else
preWriteCont[i - 1] = setValue[i];
7 months ago
break;
case DataFormat.BADC:
Array.Copy(setValue, preWriteCont, setValue.Length);
break;
}
7 months ago
7 months ago
operateResult = ModbusTcpNet.Write("x=16;" + registerNo, preWriteCont);
result = operateResult.IsSuccess;
}
7 months ago
}
else if (setValue.Length == length * 2)
{
7 months ago
var preWriteCont = new byte[setValue.Length];
7 months ago
switch (DataFormat)
{
case DataFormat.ABCD:
7 months ago
for (var i = 0; i < setValue.Length; i++)
if (i % 2 == 0)
preWriteCont[i + 1] = setValue[i];
else
preWriteCont[i - 1] = setValue[i];
7 months ago
break;
case DataFormat.BADC:
//Array.Copy(setValue, preWriteCont, setValue.Length);
break;
}
7 months ago
7 months ago
operateResult = ModbusTcpNet.Write("x=16;" + registerNo, preWriteCont);
result = operateResult.IsSuccess;
}
7 months ago
break;
case ModbusDataType.Bit:
result = WriteRegisterOneBit(registerNo, start, Convert.ToBoolean(value));
7 months ago
break;
}
7 months ago
7 months ago
return result;
}
7 months ago
/// <summary>
7 months ago
/// 写寄存器中的一个bit
/// </summary>
/// <param name="addr"></param>
/// <param name="location"></param>
/// <param name="boolResult"></param>
/// <returns></returns>
public bool WriteRegisterOneBit(int addr, int location, bool boolResult)
{
if (!Connected) return false;
7 months ago
var result = false;
ushort registerValue1 = 0x1234;
ushort mask = 0x1234;
7 months ago
OperateResult<byte[]> readResult = ModbusTcpNet.Read("x=3;" + addr, 1);
if (readResult.IsSuccess)
if (location >= 0 && location <= 15)
{
registerValue1 = ModbusTcpNet.ByteTransform.TransUInt16(readResult.Content, 0);
if (boolResult)
{
mask = (ushort)(1 << location);
registerValue1 |= mask;
}
else
{
7 months ago
mask = (ushort)~(1 << location);
registerValue1 &= mask;
}
7 months ago
7 months ago
var writeResult = ModbusTcpNet.Write("x=6;" + addr, registerValue1);
if (writeResult.IsSuccess) result = true;
}
7 months ago
return result;
}
}