using NModbus; using NModbus.Serial; using System; using System.IO.Ports; using System.Threading; using System.Threading.Tasks; namespace DeviceCommand.Base { public class ModbusRtu:IModbusDevice { public string PortName { get; private set; } = "COM1"; public int BaudRate { get; private set; } = 9600; public int DataBits { get; private set; } = 8; public StopBits StopBits { get; private set; } = StopBits.One; public Parity Parity { get; private set; } = Parity.None; public int ReadTimeout { get; private set; } = 3000; public int WriteTimeout { get; private set; } = 3000; public SerialPort SerialPort { get; private set; } = new SerialPort(); public IModbusMaster Modbus { get; private set; } private readonly SemaphoreSlim _commLock = new(1, 1); public void ConfigureDevice(string portName, int baudRate, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000) { PortName = portName; BaudRate = baudRate; DataBits = dataBits; StopBits = stopBits; Parity = parity; ReadTimeout = readTimeout; WriteTimeout = writeTimeout; } public async Task ConnectAsync(CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { if (SerialPort.IsOpen) SerialPort.Close(); SerialPort.PortName = PortName; SerialPort.BaudRate = BaudRate; SerialPort.DataBits = DataBits; SerialPort.StopBits = StopBits; SerialPort.Parity = Parity; SerialPort.ReadTimeout = ReadTimeout; SerialPort.WriteTimeout = WriteTimeout; SerialPort.Open(); Modbus = new ModbusFactory().CreateRtuMaster(SerialPort); return true; } finally { _commLock.Release(); } } public void Close() { if (SerialPort.IsOpen) SerialPort.Close(); } public async Task WriteSingleRegisterAsync(byte slaveAddress, ushort registerAddress, ushort value, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { var writeTask = Modbus.WriteSingleRegisterAsync(slaveAddress, registerAddress, value).WaitAsync(ct); var timeoutTask = Task.Delay(WriteTimeout, ct); var completedTask = await Task.WhenAny(writeTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException("ModbusRTU写单寄存器超时"); await writeTask; } finally { _commLock.Release(); } } public async Task WriteMultipleRegistersAsync(byte slaveAddress, ushort startAddress, ushort[] values, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { var writeTask = Modbus.WriteMultipleRegistersAsync(slaveAddress, startAddress, values).WaitAsync(ct); var timeoutTask = Task.Delay(WriteTimeout, ct); var completedTask = await Task.WhenAny(writeTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException("ModbusRTU写多寄存器超时"); await writeTask; } finally { _commLock.Release(); } } public async Task ReadHoldingRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { var readTask = Modbus.ReadHoldingRegistersAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct); var timeoutTask = Task.Delay(ReadTimeout, ct); var completedTask = await Task.WhenAny(readTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException("ModbusRTU读取保持寄存器超时"); return await readTask; } finally { _commLock.Release(); } } public async Task WriteSingleCoilAsync(byte slaveAddress, ushort coilAddress, bool value, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { var writeTask = Modbus.WriteSingleCoilAsync(slaveAddress, coilAddress, value).WaitAsync(ct); var timeoutTask = Task.Delay(WriteTimeout, ct); var completedTask = await Task.WhenAny(writeTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException("ModbusRTU写单线圈超时"); await writeTask; } finally { _commLock.Release(); } } public async Task ReadCoilsAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { var readTask = Modbus.ReadCoilsAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct); var timeoutTask = Task.Delay(ReadTimeout, ct); var completedTask = await Task.WhenAny(readTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException("ModbusRTU读取线圈超时"); return await readTask; } finally { _commLock.Release(); } } public async Task ReadInputRegistersAsync(byte slaveAddress, ushort startAddress, ushort numberOfPoints, CancellationToken ct = default) { await _commLock.WaitAsync(ct); try { var readTask = Modbus.ReadInputRegistersAsync(slaveAddress, startAddress, numberOfPoints).WaitAsync(ct); var timeoutTask = Task.Delay(ReadTimeout, ct); var completedTask = await Task.WhenAny(readTask, timeoutTask); if (completedTask == timeoutTask) throw new TimeoutException("ModbusRTU读取输入寄存器超时"); return await readTask; } finally { _commLock.Release(); } } } }