using FluentModbus; using ProtocalGeneral; using System.Net; using System.Runtime.InteropServices; namespace ModBusTcp; public class Modbus_Tcp : IModBus { private ModbusTcpClient? _master; private IPEndPoint? EndPoint; private string? _name; private string _ip = string.Empty; private ushort _port; private ITcpConnectNority? _connect; public bool Initialize(string name, ITcpConnectNority tcpConnect) { if (!string.IsNullOrEmpty(this._name)) return false; this._name = name; this._name ??= "Default"; this._connect = tcpConnect; return true; } public bool Open(string ip, ushort port, bool notify = true) { if (this._master is not null) return false; if (!IPAddress.TryParse(ip, out IPAddress? address) || address is null) return false; try { IPEndPoint endPoint = new(address, port); ModbusTcpClient client = new() { ReadTimeout = 500, ConnectTimeout = 500 }; if (notify) client.Connect(endPoint); this._ip = ip; this._port = port; this.EndPoint = endPoint; this._master = client; } catch { return false; } return true; } public byte[]? GetBuffer(ushort index, ushort count, byte slaveAddress = 1) { if (this._master is null) return null; try { lock (this) { Span bytes = this._master.ReadHoldingRegisters(0x00, index, count); if (bytes == null) return null; return bytes.ToArray(); } } catch { TryReconnect(); return null; } } public unsafe bool SetFloat(ushort index, float value, byte slaveAddress = 1) { if (this._master is null) return false; byte[] bytes = new byte[4]; float* pSource = &value; byte* pByte = (byte*)pSource; bytes[0] = pByte[3]; bytes[1] = pByte[2]; bytes[2] = pByte[1]; bytes[3] = pByte[0]; try { lock (this) this._master.WriteMultipleRegisters(0x00, (int)index, bytes); } catch { return false; } return true; } public unsafe bool SetValue(ushort index, T value, byte slaveAddress = 1) where T : struct { if (this._master is null) return false; int size = Marshal.SizeOf(); if (size % 4 != 0) return false; byte[] bytes = new byte[size]; #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type T* pSource = &value; #pragma warning restore CS8500 byte* pByte = (byte*)pSource; for (int i = 0; i < size; i += 4) { bytes[i] = pByte[i + 3]; bytes[i + 1] = pByte[i + 2]; bytes[i + 2] = pByte[i + 1]; bytes[i + 3] = pByte[i]; } try { lock (this) this._master.WriteMultipleRegisters(0x00, (int)index, bytes); } catch { return false; } return true; } public unsafe bool SetUshort(ushort index, ushort value, byte slaveAddress = 1) { if (this._master is null) return false; ushort* pUshort = &value; byte* pByte = (byte*)pUshort; byte[] result = [pByte[1], pByte[0]]; try { lock (this) this._master.WriteSingleRegister(0x00, index, result); } catch { return false; } return true; } public bool Close() { this._master?.Dispose(); this._master = null; this.IsCanceled = true; return true; } private volatile bool IsCanceled = false; private readonly Semaphore semaphore = new(1, 1); private void TryReconnect() { if (!semaphore.WaitOne(0)) return; this._master?.Disconnect(); this._connect?.DisConnect(this._ip, this._port); Task.Factory.StartNew(() => { IsCanceled = false; this._master ??= new() { ReadTimeout = 500, ConnectTimeout = 500 }; try { while (true) { if (this.IsCanceled) return; try { this._master.Connect(EndPoint!); break; } catch { Thread.Sleep(1000); } } this._connect?.Connect(this._ip, this._port); } finally { semaphore.Release(); } }); } }