using System; using System.IO.Ports; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DeviceCommand.Base { public class Serial_Port : ISerialPort { public string PortName { get; set; } = "COM1"; public int BaudRate { get; set; } = 9600; public int DataBits { get; set; } = 8; public StopBits StopBits { get; set; } = StopBits.One; public Parity Parity { get; set; } = Parity.None; public int ReadTimeout { get; set; } = 3000; public int WriteTimeout { get; set; } = 3000; private SerialPort _serialPort; public bool IsConnected => _serialPort?.IsOpen ?? false; protected readonly SemaphoreSlim commLock = new(1, 1); public Serial_Port() { _serialPort = new SerialPort(); } /// /// 通过 一次性配置串口通信参数。 /// public Serial_Port(SerialPortConfig config) : this() { if (config == null) return; ConfigureDevice(config.PortName, config.BaudRate, config.DataBits, config.StopBits, config.Parity, config.ReadTimeout, config.WriteTimeout); } 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 virtual 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(); return true; } finally { commLock.Release(); } } public virtual void Close() { if (_serialPort.IsOpen) _serialPort.Close(); } // 内部无锁发送方法,供原子组合操作调用 private async Task LoglessSendAsync(string data, CancellationToken ct) { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开。"); byte[] bytes = Encoding.UTF8.GetBytes(data); using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); if (WriteTimeout > 0) cts.CancelAfter(WriteTimeout); await _serialPort.BaseStream.WriteAsync(bytes, 0, bytes.Length, cts.Token); } public async Task SendAsync(string data, CancellationToken ct = default) { await commLock.WaitAsync(ct); try { await LoglessSendAsync(data, ct); } finally { commLock.Release(); } } // 内部无锁读取方法,利用 BaseStream 挂起线程,高性能不吃 CPU private async Task LoglessReadAsync(string delimiter, CancellationToken ct) { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开。"); delimiter ??= "\n"; var sb = new StringBuilder(); byte[] buffer = new byte[1024]; using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); if (ReadTimeout > 0) cts.CancelAfter(ReadTimeout); while (!cts.Token.IsCancellationRequested) { // 核心优化:利用流异步挂起,替代原先的 BytesToRead 循环延时 int bytesRead = await _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length, cts.Token); if (bytesRead == 0) continue; 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("读取数据超时"); } public async Task ReadAsync(string delimiter = "\n", CancellationToken ct = default) { await commLock.WaitAsync(ct); try { return await LoglessReadAsync(delimiter, ct); } finally { commLock.Release(); } } // 核心优化:保证多线程环境下发送和等待回包是一个原子过程 public async Task WriteReadAsync(string command, string delimiter = "\n", CancellationToken ct = default) { await commLock.WaitAsync(ct); try { await LoglessSendAsync(command, ct); return await LoglessReadAsync(delimiter, ct); } finally { commLock.Release(); } } public void Dispose() { _serialPort?.Dispose(); commLock?.Dispose(); } } }