using Model.Models; using System; using System.IO; using System.IO.Ports; using System.Text; using System.Threading; using System.Threading.Tasks; namespace DeviceCommand.Base { public class Serial_Port : ISerialPort, IDisposable { 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; // 允许底层保留默认设置,控制权交给外层 WaitAsync _serialPort.ReadTimeout = SerialPort.InfiniteTimeout; _serialPort.WriteTimeout = SerialPort.InfiniteTimeout; _serialPort.Open(); return true; } finally { commLock.Release(); } } public virtual void Close() { if (_serialPort?.IsOpen == true) _serialPort.Close(); } private async Task LoglessSendAsync(string data, CancellationToken ct) { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开。"); byte[] bytes = Encoding.UTF8.GetBytes(data); try { if (WriteTimeout > 0) { // 核心修改:使用 WaitAsync 精准控制写入超时 await _serialPort.BaseStream.WriteAsync(bytes, 0, bytes.Length, ct) .WaitAsync(TimeSpan.FromMilliseconds(WriteTimeout), ct) .ConfigureAwait(false); } else { await _serialPort.BaseStream.WriteAsync(bytes, 0, bytes.Length, ct).ConfigureAwait(false); } } catch (TimeoutException ex) { throw new TimeoutException($"串口写入数据超时({WriteTimeout} ms)", ex); } } public async Task SendAsync(string data, CancellationToken ct = default) { await commLock.WaitAsync(ct); try { await LoglessSendAsync(data, ct); } finally { commLock.Release(); } } 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]; try { while (true) { ct.ThrowIfCancellationRequested(); int bytesRead; if (ReadTimeout > 0) { // 核心修改:使用 WaitAsync 精准控制单次异步读取超时 bytesRead = await _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length, ct) .WaitAsync(TimeSpan.FromMilliseconds(ReadTimeout), ct) .ConfigureAwait(false); } else { bytesRead = await _serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length, ct).ConfigureAwait(false); } if (bytesRead == 0) continue; sb.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); string currentText = sb.ToString(); int index = currentText.IndexOf(delimiter, StringComparison.Ordinal); if (index >= 0) { return currentText.Substring(0, index).Trim(); } } } catch (TimeoutException ex) { // 关键防污染策略:串口发生命令错、超时不响应时,WaitAsync 会立功捕获异常 // 我们直接清空串口驱动内部的输入缓冲区,把残余垃圾数据物理抹除,确保下一条指令安全、不掉线 ClearHardwareBuffer(); throw new TimeoutException($"串口读取超时(未收到结束符 '{delimiter}'),等待时间:{ReadTimeout} ms", ex); } } 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 { // 发送新命令前先冲洗一下缓冲区,双重保险 ClearHardwareBuffer(); await LoglessSendAsync(command, ct); return await LoglessReadAsync(delimiter, ct); } finally { commLock.Release(); } } /// /// 物理强制清空串口驱动层及软件层的缓冲区 /// private void ClearHardwareBuffer() { try { if (_serialPort?.IsOpen == true) { _serialPort.DiscardInBuffer(); // 清空硬件接收缓冲区 _serialPort.DiscardOutBuffer(); // 清空硬件发送缓冲区 } } catch { // 忽略清理异常 } } public void Dispose() { if (_serialPort != null) { if (_serialPort.IsOpen) _serialPort.Close(); _serialPort.Dispose(); } commLock?.Dispose(); } // 在 Serial_Port 类中添加以下方法: /// /// 发送原始二进制数据帧(无分隔符,用于IT6720等自定义二进制协议) /// public async Task WriteBytesAsync(byte[] data, CancellationToken ct = default) { await commLock.WaitAsync(ct); try { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开。"); await _serialPort.BaseStream.WriteAsync(data, 0, data.Length, ct).ConfigureAwait(false); } finally { commLock.Release(); } } /// /// 读取指定长度的原始二进制数据帧(无分隔符) /// public async Task ReadBytesAsync(int count, CancellationToken ct = default) { await commLock.WaitAsync(ct); try { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开。"); if (count <= 0) return Array.Empty(); byte[] buffer = new byte[count]; int offset = 0; while (offset < count) { ct.ThrowIfCancellationRequested(); int read = await _serialPort.BaseStream.ReadAsync(buffer, offset, count - offset, ct) .WaitAsync(TimeSpan.FromMilliseconds(ReadTimeout), ct) .ConfigureAwait(false); if (read == 0) throw new EndOfStreamException("串口读取数据流意外终止"); offset += read; } return buffer; } catch (TimeoutException ex) { ClearHardwareBuffer(); throw new TimeoutException($"串口读取 {count} 字节二进制数据超时 ({ReadTimeout} ms)", ex); } finally { commLock.Release(); } } } }