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; /// /// 字节序 /// public EndingConst.ByteSeq ByteSeq { get; set; } = EndingConst.ByteSeq.AB; /// /// 字序 /// public EndingConst.WordSeq WordSeq { get; set; } = EndingConst.WordSeq.CD; public int Duration { get; set; } = 1000; public bool Connected { get; set; } /// /// 自动重连开关 /// 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 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 result = ModbusTcpNet.Read("x=3;" + registerNo, (ushort)length); if (result.IsSuccess) return result.Content; return null; } public byte[]? BatchReadInputRegister(int registerNo, int length) { OperateResult result = ModbusTcpNet.Read("x=4;" + registerNo, (ushort)length); if (result.IsSuccess) return result.Content; return null; } /// /// 读取线圈,需要指定起始地址,如果富文本地址不指定,默认使用的功能码是 0x01
/// 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. ///
/// 起始地址,格式为"1234" /// 带有成功标志的bool对象 public OperateResult ReadCoil(string address) => ModbusTcpNet.ReadBool(address); /// /// 批量的读取线圈,需要指定起始地址,读取长度,如果富文本地址不指定,默认使用的功能码是 0x01
/// 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. ///
/// 起始地址,格式为"1234" /// 读取长度 /// 带有成功标志的bool数组对象 public OperateResult ReadCoil(string address, ushort length) { return ModbusTcpNet.ReadBool(address, length); } /// /// 读取输入线圈,需要指定起始地址,如果富文本地址不指定,默认使用的功能码是 0x02
/// 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. ///
/// 起始地址,格式为"1234" /// 带有成功标志的bool对象 public OperateResult ReadDiscrete(string address) { return ModbusTcpNet.ReadDiscrete(address); } /// /// 批量的读取输入点,需要指定起始地址,读取长度,如果富文本地址不指定,默认使用的功能码是 0x02
/// 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 ///
/// 起始地址,格式为"1234" /// 读取长度 /// 带有成功标志的bool数组对象 public OperateResult ReadDiscrete(string address, ushort length) { return ModbusTcpNet.ReadDiscrete(address, length); } /// /// 将数据写入到Modbus的寄存器上去,需要指定起始地址和数据内容,如果富文本地址不指定,默认使用的功能码是 0x10
/// 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 ///
/// 起始地址,比如"100","x=4;100","s=1;100","s=1;x=4;100" /// 写入的数据,长度根据data的长度来指示 /// 返回写入结果 /// 富地址格式,支持携带站号信息,功能码信息,具体参照类的示例代码 /// /// 此处演示批量写入的示例 /// /// public OperateResult Write(string address, byte[] value) { return ModbusTcpNet.Write(address, value); } public bool WriteValue(ModbusProperty 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 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; } /// /// 写寄存器中的一个bit /// /// /// /// /// public bool WriteRegisterOneBit(int addr, int location, bool boolResult) { if (!Connected) return false; var result = false; ushort registerValue1 = 0x1234; ushort mask = 0x1234; OperateResult 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; } }