diff --git a/DeviceCommand/Flexible/FModbusRTU.cs b/DeviceCommand/Flexible/FModbusRTU.cs new file mode 100644 index 0000000..d060e81 --- /dev/null +++ b/DeviceCommand/Flexible/FModbusRTU.cs @@ -0,0 +1,193 @@ +using Common.Attributes; +using NModbus; +using NModbus.Serial; +using System; +using System.IO.Ports; +using System.Threading; +using System.Threading.Tasks; + +namespace DeviceCommand.Flexible +{ + [ADPCommand] + 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 + } +} diff --git a/DeviceCommand/Flexible/FModbusTCP.cs b/DeviceCommand/Flexible/FModbusTCP.cs new file mode 100644 index 0000000..85d3e90 --- /dev/null +++ b/DeviceCommand/Flexible/FModbusTCP.cs @@ -0,0 +1,133 @@ +using Common.Attributes; +using NModbus; +using System; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace DeviceCommand.Flexible +{ + [ADPCommand] + public static class FModbusTCP + { + private static readonly SemaphoreSlim _commLock = new(1, 1); + + private static async Task ConnectAsync(string ipAddress, int port, int sendTimeout, int receiveTimeout, CancellationToken ct) + { + var tcpClient = new TcpClient(); + using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); + if (sendTimeout > 0) + cts.CancelAfter(sendTimeout); + + await tcpClient.ConnectAsync(ipAddress, port, cts.Token); + return new ModbusFactory().CreateMaster(tcpClient); + } + + #region Holding Registers + + public static async Task ReadHoldingRegistersAsync( + string ipAddress, + int port, + byte slaveAddress, + ushort startAddress, + ushort numberOfPoints, + int sendTimeout = 3000, + int receiveTimeout = 3000, + CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable; + var result = await ((IModbusMaster)master) + .ReadHoldingRegistersAsync(slaveAddress, startAddress, numberOfPoints) + .WaitAsync(TimeSpan.FromMilliseconds(receiveTimeout), ct); + return result; + } + finally + { + _commLock.Release(); + } + } + + public static async Task WriteSingleRegisterAsync( + string ipAddress, + int port, + byte slaveAddress, + ushort registerAddress, + ushort value, + int sendTimeout = 3000, + int receiveTimeout = 3000, + CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable; + await ((IModbusMaster)master) + .WriteSingleRegisterAsync(slaveAddress, registerAddress, value) + .WaitAsync(TimeSpan.FromMilliseconds(sendTimeout),ct); + } + finally + { + _commLock.Release(); + } + } + + #endregion + + #region Coils + + public static async Task ReadCoilsAsync( + string ipAddress, + int port, + byte slaveAddress, + ushort startAddress, + ushort numberOfPoints, + int sendTimeout = 3000, + int receiveTimeout = 3000, + CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable; + var result = await ((IModbusMaster)master) + .ReadCoilsAsync(slaveAddress, startAddress, numberOfPoints) + .WaitAsync(TimeSpan.FromMilliseconds(receiveTimeout), ct); + return result; + } + finally + { + _commLock.Release(); + } + } + + public static async Task WriteSingleCoilAsync( + string ipAddress, + int port, + byte slaveAddress, + ushort coilAddress, + bool value, + int sendTimeout = 3000, + int receiveTimeout = 3000, + CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var master = await ConnectAsync(ipAddress, port, sendTimeout, receiveTimeout, ct) as IDisposable; + await ((IModbusMaster)master) + .WriteSingleCoilAsync(slaveAddress, coilAddress, value) + .WaitAsync(TimeSpan.FromMilliseconds(sendTimeout),ct); + } + finally + { + _commLock.Release(); + } + } + + #endregion + } +} diff --git a/DeviceCommand/Flexible/FSerialPort.cs b/DeviceCommand/Flexible/FSerialPort.cs new file mode 100644 index 0000000..a0992b4 --- /dev/null +++ b/DeviceCommand/Flexible/FSerialPort.cs @@ -0,0 +1,104 @@ +using Common.Attributes; +using System; +using System.IO.Ports; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace DeviceCommand.Flexible +{ + [ADPCommand] + public static class FSerialPort + { + private static readonly SemaphoreSlim _commLock = new(1, 1); + + private static SerialPort CreatePort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits, int sendTimeout,int receiveTimeout) + { + return new SerialPort(portName, baudRate, parity, dataBits, stopBits) + { + Encoding = Encoding.UTF8, + WriteTimeout = sendTimeout > 0 ? sendTimeout : SerialPort.InfiniteTimeout, + ReadTimeout = receiveTimeout > 0 ? receiveTimeout : SerialPort.InfiniteTimeout + }; + } + + #region 最常用:只发送字符串 + + public static async Task SendAsync(string portName,int baudRate,Parity parity,int dataBits,StopBits stopBits,int sendTimeout,int receiveTimeout,string command,CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var port = CreatePort( + portName, baudRate, parity, dataBits, stopBits, sendTimeout, receiveTimeout); + + port.Open(); + + using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); + if (sendTimeout > 0) + cts.CancelAfter(sendTimeout); + + var bytes = port.Encoding.GetBytes(command); + await port.BaseStream.WriteAsync(bytes, 0, bytes.Length, cts.Token); + await port.BaseStream.FlushAsync(cts.Token); + } + finally + { + _commLock.Release(); + } + } + + #endregion + + #region 最常用:发送字符串并读取字符串 + + public static async Task SendReadAsync(string portName,int baudRate, Parity parity, int dataBits, StopBits stopBits,int sendTimeout, int receiveTimeout,string command,string delimiter = "\n", CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var port = CreatePort( + portName, baudRate, parity, dataBits, stopBits, sendTimeout, receiveTimeout); + + port.Open(); + + // Send + var sendBytes = port.Encoding.GetBytes(command); + await port.BaseStream.WriteAsync(sendBytes, 0, sendBytes.Length, ct); + await port.BaseStream.FlushAsync(ct); + + // Read + delimiter ??= "\n"; + var sb = new StringBuilder(); + byte[] buffer = new byte[256]; + + using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); + if (receiveTimeout > 0) + cts.CancelAfter(receiveTimeout); + + while (!cts.Token.IsCancellationRequested) + { + int read = await port.BaseStream.ReadAsync( + buffer, 0, buffer.Length, cts.Token); + + if (read == 0) + break; + + sb.Append(port.Encoding.GetString(buffer, 0, read)); + + int index = sb.ToString().IndexOf(delimiter, StringComparison.Ordinal); + if (index >= 0) + return sb.ToString(0, index).Trim(); + } + + throw new TimeoutException("串口读取超时"); + } + finally + { + _commLock.Release(); + } + } + + #endregion + } +} diff --git a/DeviceCommand/Flexible/FTCP.cs b/DeviceCommand/Flexible/FTCP.cs new file mode 100644 index 0000000..7f7dcc9 --- /dev/null +++ b/DeviceCommand/Flexible/FTCP.cs @@ -0,0 +1,115 @@ +using Common.Attributes; +using System.Net.Sockets; +using System.Text; + +namespace DeviceCommand.Flexible +{ + [ADPCommand] + public static class FTCP + { + private static readonly SemaphoreSlim _commLock = new(1, 1); + + #region Send + + public static async Task SendAsync(string ipAddress,int port,int sendTimeout, byte[] buffer, CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var client = new TcpClient(); + client.SendTimeout = sendTimeout; + + await client.ConnectAsync(ipAddress, port, ct); + + using NetworkStream stream = client.GetStream(); + await stream.WriteAsync(buffer, 0, buffer.Length, ct).WaitAsync(TimeSpan.FromMilliseconds(sendTimeout)); + } + finally + { + _commLock.Release(); + } + } + + public static Task SendAsync(string ipAddress, int port,int sendTimeout, string text,CancellationToken ct = default) + { + return SendAsync( ipAddress, port, sendTimeout, Encoding.UTF8.GetBytes(text),ct); + } + + #endregion + + #region Read + + public static async Task ReadAsync(string ipAddress,int port,int receiveTimeout,int length,CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var client = new TcpClient(); + client.ReceiveTimeout = receiveTimeout; + + await client.ConnectAsync(ipAddress, port, ct); + + using NetworkStream stream = client.GetStream(); + byte[] buffer = new byte[length]; + int offset = 0; + + using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); + if (receiveTimeout > 0) + cts.CancelAfter(receiveTimeout); + + while (offset < length) + { + int read = await stream.ReadAsync(buffer, offset, length - offset, cts.Token); + if (read == 0) break; + offset += read; + } + + return buffer[..offset]; + } + finally + { + _commLock.Release(); + } + } + + public static async Task ReadLineAsync( string ipAddress, int port, int receiveTimeout, string delimiter = "\n",CancellationToken ct = default) + { + await _commLock.WaitAsync(ct); + try + { + using var client = new TcpClient(); + client.ReceiveTimeout = receiveTimeout; + + await client.ConnectAsync(ipAddress, port, ct); + + using NetworkStream stream = client.GetStream(); + var sb = new StringBuilder(); + byte[] buffer = new byte[1024]; + + using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); + if (receiveTimeout > 0) + cts.CancelAfter(receiveTimeout); + + while (!cts.Token.IsCancellationRequested) + { + int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token); + if (bytesRead == 0) break; + + sb.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); + + int index = sb.ToString().IndexOf(delimiter, StringComparison.Ordinal); + if (index >= 0) + return sb.ToString(0, index).Trim(); + } + + throw new TimeoutException("读取超时或对端关闭"); + } + finally + { + _commLock.Release(); + } + } + + #endregion + } +}