using Common.Attributes; using NModbus; using NModbus.Serial; using System; using System.IO.Ports; using System.Threading; using System.Threading.Tasks; namespace DeviceCommand.Flexible { [ACPCommand] public static class FModbusRTU { private static readonly SemaphoreSlim _commLock = new(1, 1); private static SerialPort CreatePort( string portName, int baudRate, int dataBits, StopBits stopBits, Parity parity, int readTimeout, int writeTimeout) { return new SerialPort(portName, baudRate, parity, dataBits, stopBits) { ReadTimeout = readTimeout > 0 ? readTimeout : SerialPort.InfiniteTimeout, WriteTimeout = writeTimeout > 0 ? writeTimeout : SerialPort.InfiniteTimeout }; } private static IModbusMaster CreateMaster(SerialPort port) { return new ModbusFactory().CreateRtuMaster(port); } #region Holding Register public static async Task ReadHoldingRegistersAsync( string portName, int baudRate, byte slaveAddress, ushort startAddress, ushort numberOfPoints, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { using var port = CreatePort( portName, baudRate, dataBits, stopBits, parity, readTimeout, writeTimeout); port.Open(); var master = CreateMaster(port); using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); if (readTimeout > 0) cts.CancelAfter(readTimeout); return await master .ReadHoldingRegistersAsync(slaveAddress, startAddress, numberOfPoints) .WaitAsync(TimeSpan.FromMilliseconds(readTimeout),cts.Token); } finally { _commLock.Release(); } } public static async Task WriteSingleRegisterAsync( string portName, int baudRate, byte slaveAddress, ushort registerAddress, ushort value, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { using var port = CreatePort( portName, baudRate, dataBits, stopBits, parity, readTimeout, writeTimeout); port.Open(); var master = CreateMaster(port); using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); if (writeTimeout > 0) cts.CancelAfter(writeTimeout); await master .WriteSingleRegisterAsync(slaveAddress, registerAddress, value) .WaitAsync(TimeSpan.FromMilliseconds(writeTimeout),cts.Token); } finally { _commLock.Release(); } } #endregion #region Coil public static async Task ReadCoilsAsync( string portName, int baudRate, byte slaveAddress, ushort startAddress, ushort numberOfPoints, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { using var port = CreatePort( portName, baudRate, dataBits, stopBits, parity, readTimeout, writeTimeout); port.Open(); var master = CreateMaster(port); using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); if (readTimeout > 0) cts.CancelAfter(readTimeout); return await master .ReadCoilsAsync(slaveAddress, startAddress, numberOfPoints) .WaitAsync(TimeSpan.FromMilliseconds(readTimeout),cts.Token); } finally { _commLock.Release(); } } public static async Task WriteSingleCoilAsync( string portName, int baudRate, byte slaveAddress, ushort coilAddress, bool value, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { using var port = CreatePort( portName, baudRate, dataBits, stopBits, parity, readTimeout, writeTimeout); port.Open(); var master = CreateMaster(port); using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); if (writeTimeout > 0) cts.CancelAfter(writeTimeout); await master .WriteSingleCoilAsync(slaveAddress, coilAddress, value) .WaitAsync(TimeSpan.FromMilliseconds(writeTimeout), cts.Token); } finally { _commLock.Release(); } } #endregion } }