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.

314 lines
11 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using HslCommunication;
using HslCommunication.ModBus;
using HybirdFrameworkCore.Const;
using HybirdFrameworkCore.Utils;
using log4net;
namespace HybirdFrameworkDriver.ModbusTcpMaster;
public class ModbusTcpMaster
{
public delegate void MyReadAction(ModbusTcpMaster str);
private static readonly ILog Log = LogManager.GetLogger(typeof(ModbusTcpMaster));
private ModbusTcpNet? ModbusTcpNet;
public string Ip { get; set; } = "127.0.0.1";
public int Port { get; set; } = 502;
/// <summary>
/// 字节序
/// </summary>
public EndingConst.ByteSeq ByteSeq { get; set; } = EndingConst.ByteSeq.AB;
/// <summary>
/// 字序
/// </summary>
public EndingConst.WordSeq WordSeq { get; set; } = EndingConst.WordSeq.CD;
public int Duration { get; set; } = 1000;
public bool Connected { get; set; }
/// <summary>
/// 自动重连开关
/// </summary>
public bool AutoReConnect { get; set; }
public string connectId { get; set; }
public MyReadAction? ReadAction { get; set; }
private bool StopFlag { get; set; }
private ILog GetLog()
{
return Log;
}
public bool Connect()
{
Thread.Sleep(Duration);
GetLog().Info($"begin to connect {Ip}:{Port}");
try
{
if (ModbusTcpNet == null)
{
ModbusTcpNet = new ModbusTcpNet(Ip, Port);
var result = ModbusTcpNet.ConnectServer();
connectId = ModbusTcpNet.ConnectionId;
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();
}
}
}
}
catch (Exception e)
{
GetLog().Error($"connect {Ip}:{Port} exception {e.Message}");
if (AutoReConnect)
{
return Connect();
}
}
return Connected;
}
public void StartListen()
{
StopListen();
StopFlag = false;
var readThread = new Thread(ReadFunc)
{
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
{
ReadAction(this);
Thread.Sleep(Duration);
}
catch (Exception e)
{
Log.Error(e);
}
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);
if (result.IsSuccess) return result.Content;
return null;
}
public byte[]? BatchReadHolderRegister(int registerNo, int length)
{
OperateResult<byte[]> result = ModbusTcpNet.Read("x=3;" + registerNo, (ushort)length);
if (result.IsSuccess) return result.Content;
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);
}
/// <summary>
/// 将数据写入到Modbus的寄存器上去需要指定起始地址和数据内容如果富文本地址不指定默认使用的功能码是 0x10<br />
/// To write data to Modbus registers, you need to specify the start address and data content. If the rich text address is not specified, the default function code is 0x10
/// </summary>
/// <param name="address">起始地址,比如"100""x=4;100""s=1;100","s=1;x=4;100"</param>
/// <param name="value">写入的数据长度根据data的长度来指示</param>
/// <returns>返回写入结果</returns>
/// <remarks>富地址格式,支持携带站号信息,功能码信息,具体参照类的示例代码</remarks>
/// <example>
/// 此处演示批量写入的示例
/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Modbus\Modbus.cs" region="WriteExample1" title="Write示例" />
/// </example>
public OperateResult Write(string address, byte[] value)
{
return ModbusTcpNet.Write(address, value);
}
public bool WriteValue<T>(ModbusProperty<T> property)
{
var value = property.Value;
if (value == null)
{
GetLog().Warn($"write property{property.RegisterNo} null value");
return false;
}
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;
switch (dataType)
{
case ModbusDataType.Register:
if (setValue.Length < length * 2)
{
//byte 需要读取寄存器中的内容
if (typeof(T) == typeof(byte))
{
OperateResult<byte[]> readResultRegister = ModbusTcpNet.Read("x=3;" + registerNo, 1);
if (readResultRegister.IsSuccess)
{
if (ByteSeq == EndingConst.ByteSeq.AB)
{
readResultRegister.Content[1] = setValue[0];
}
else
{
readResultRegister.Content[0] = setValue[0];
}
operateResult = ModbusTcpNet.Write("x=16;" + registerNo, readResultRegister.Content);
result = operateResult.IsSuccess;
}
}
//其他类型 String 占用几个寄存器 直接补0
else
{
var preWriteCont = BitUtls.ProcessEnding(setValue, ByteSeq, WordSeq);
operateResult = ModbusTcpNet.Write("x=16;" + registerNo, preWriteCont);
result = operateResult.IsSuccess;
}
}
else if (setValue.Length == length * 2)
{
var preWriteCont = BitUtls.ProcessEnding(setValue, ByteSeq, WordSeq);
operateResult = ModbusTcpNet.Write("x=16;" + registerNo, preWriteCont);
result = operateResult.IsSuccess;
}
break;
case ModbusDataType.Bit:
result = WriteRegisterOneBit(registerNo, start, Convert.ToBoolean(value));
break;
}
return result;
}
/// <summary>
/// 写寄存器中的一个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;
var result = false;
ushort registerValue1 = 0x1234;
ushort mask = 0x1234;
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
{
mask = (ushort)~(1 << location);
registerValue1 &= mask;
}
var writeResult = ModbusTcpNet.Write("x=6;" + addr, registerValue1);
if (writeResult.IsSuccess) result = true;
}
return result;
}
}