177 lines
6.7 KiB
C#
177 lines
6.7 KiB
C#
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<bool> 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<ushort[]> 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<bool[]> 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<ushort[]> 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();
|
|
}
|
|
}
|
|
}
|
|
}
|