using System; using System.Buffers; using System.Diagnostics; using System.IO.Ports; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace RIZO_Helper.Tools { public sealed class ComScanHelper : IDisposable { private readonly SerialPort _serialPort; private readonly StringBuilder _receiveBuffer = new StringBuilder(); private readonly Regex _lineSplitter = new Regex(@"\r\n|\n|\r", RegexOptions.Compiled); private readonly object _lockObject = new object(); private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource(); private bool _isDisposed; private int _isProcessingData; private int _disposeFlag; private readonly int DisposeTimeout = 500; // 处置超时时间(毫秒) public event EventHandler? DataReceived; public event EventHandler? ErrorOccurred; public bool IsOpen => _serialPort.IsOpen; public string PortName => _serialPort.PortName; public int BytesToRead => _serialPort.BytesToRead; public ComScanHelper(string portName = "COM1", int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One, int readBufferSize = 4096) { _serialPort = new SerialPort { PortName = portName, BaudRate = baudRate, Parity = parity, DataBits = dataBits, StopBits = stopBits, ReadTimeout = 500, WriteTimeout = 500, Encoding = Encoding.UTF8, ReadBufferSize = readBufferSize, DiscardNull = false, NewLine = "\n" }; } public async Task OpenAsync() { if (_isDisposed) throw new ObjectDisposedException(nameof(ComScanHelper)); lock (_lockObject) { if (_serialPort.IsOpen) return true; } try { await Task.Run(() => _serialPort.Open(), _disposeCts.Token); _serialPort.DataReceived += OnSerialDataReceived; return true; } catch (InvalidOperationException ex) { Debug.WriteLine($"打开串口 {_serialPort.PortName} 失败: {ex.Message}"); return false; } catch (OperationCanceledException) when (_disposeCts.IsCancellationRequested) { return false; } catch (Exception ex) { ErrorOccurred?.Invoke(this, ex); Debug.WriteLine($"打开串口 {_serialPort.PortName} 失败: {ex.Message}"); return false; } } public async Task CloseAsync() { if (_isDisposed) return; await Task.Run(() => Close(), _disposeCts.Token); } public void Close() { if (_isDisposed) return; lock (_lockObject) { if (!_serialPort.IsOpen) return; try { _serialPort.DataReceived -= OnSerialDataReceived; _serialPort.DiscardInBuffer(); _serialPort.DiscardOutBuffer(); _serialPort.Close(); } catch (Exception ex) { ErrorOccurred?.Invoke(this, ex); Debug.WriteLine($"关闭串口 {_serialPort.PortName} 失败: {ex.Message}"); } } } public async Task SendDataAsync(string data, CancellationToken cancellationToken = default) { if (_isDisposed) throw new ObjectDisposedException(nameof(ComScanHelper)); lock (_lockObject) { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开"); } try { byte[] buffer = Encoding.UTF8.GetBytes(data); await _serialPort.BaseStream.WriteAsync(buffer, 0, buffer.Length, cancellationToken); await _serialPort.BaseStream.FlushAsync(cancellationToken); return true; } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { return false; } catch (Exception ex) { ErrorOccurred?.Invoke(this, ex); return false; } } public async Task ReadLineAsync(CancellationToken cancellationToken = default) { if (_isDisposed) throw new ObjectDisposedException(nameof(ComScanHelper)); lock (_lockObject) { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开"); } try { return await Task.Run(() => { cancellationToken.ThrowIfCancellationRequested(); return _serialPort.ReadLine(); }, cancellationToken); } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { return null; } catch (Exception ex) { ErrorOccurred?.Invoke(this, ex); return null; } } private void OnSerialDataReceived(object sender, SerialDataReceivedEventArgs e) { if (Interlocked.Exchange(ref _isProcessingData, 1) != 0) return; try { if (_isDisposed || !_serialPort.IsOpen) return; ProcessSerialData(); } catch (Exception ex) { ErrorOccurred?.Invoke(this, ex); } finally { Interlocked.Exchange(ref _isProcessingData, 0); } } private void ProcessSerialData() { int bytesToRead = _serialPort.BytesToRead; if (bytesToRead <= 0) return; byte[] buffer = ArrayPool.Shared.Rent(bytesToRead); try { int bytesRead = _serialPort.Read(buffer, 0, bytesToRead); if (bytesRead <= 0) return; string newData = _serialPort.Encoding.GetString(buffer, 0, bytesRead); ProcessReceivedData(newData); } finally { ArrayPool.Shared.Return(buffer); } } private void ProcessReceivedData(string newData) { if (string.IsNullOrEmpty(newData)) return; lock (_receiveBuffer) { _receiveBuffer.Append(newData); string bufferContent = _receiveBuffer.ToString(); string[] lines = _lineSplitter.Split(bufferContent); for (int i = 0; i < lines.Length - 1; i++) { string line = lines[i].Trim(); if (!string.IsNullOrEmpty(line)) PostDataReceived(line); } _receiveBuffer.Clear(); _receiveBuffer.Append(lines[lines.Length - 1]); } } private void PostDataReceived(string data) { try { EventHandler? handler = DataReceived; if (handler != null) { Task.Run(() => { try { handler(this, data); } catch (Exception ex) { ErrorOccurred?.Invoke(this, ex); Debug.WriteLine($"事件处理失败: {ex.Message}"); } }, _disposeCts.Token); } } catch (OperationCanceledException) when (_disposeCts.IsCancellationRequested) { // 忽略取消操作 } catch (Exception ex) { ErrorOccurred?.Invoke(this, ex); } } public void Dispose() { if (Interlocked.Exchange(ref _disposeFlag, 1) != 0) return; try { DisposeAsync().GetAwaiter().GetResult(); } catch (Exception ex) { Debug.WriteLine($"同步Dispose期间发生异常: {ex}"); ErrorOccurred?.Invoke(this, ex); } } private void Dispose(bool disposing) { if (Interlocked.Exchange(ref _disposeFlag, 1) != 0) return; _isDisposed = true; _disposeCts.Cancel(); try { if (disposing) { // 同步执行异步释放方法 using var cts = new CancellationTokenSource(DisposeTimeout); var disposeTask = DisposeAsync(); try { Task.WaitAny(disposeTask, Task.Delay(Timeout.Infinite, cts.Token)); } catch (AggregateException ex) { foreach (var innerEx in ex.InnerExceptions) { if (!(innerEx is TaskCanceledException)) throw innerEx; } } if (!disposeTask.IsCompleted) { Debug.WriteLine("异步释放操作超时,资源可能未完全释放"); } } else { // 非托管资源清理逻辑 try { if (_serialPort.IsOpen) { _serialPort.DataReceived -= OnSerialDataReceived; _serialPort.Close(); } } catch (Exception ex) { Debug.WriteLine($"析构函数关闭串口时发生异常: {ex.Message}"); } _serialPort.Dispose(); _disposeCts.Dispose(); } } catch (Exception ex) { Debug.WriteLine($"释放资源时发生异常: {ex.Message}"); } } public async Task DisposeAsync() { if (Interlocked.Exchange(ref _disposeFlag, 1) != 0) return; _isDisposed = true; _disposeCts.Cancel(); try { using var timeoutCts = new CancellationTokenSource(DisposeTimeout); var closeTask = CloseAsync(); await Task.WhenAny(closeTask, Task.Delay(Timeout.Infinite, timeoutCts.Token)); if (!closeTask.IsCompleted) { timeoutCts.Cancel(); Debug.WriteLine("关闭串口操作超时,正在强制终止"); } } catch (OperationCanceledException) { Debug.WriteLine("关闭串口操作被取消"); } catch (Exception ex) { ErrorOccurred?.Invoke(this, ex); Debug.WriteLine($"关闭串口时发生异常: {ex.Message}"); } finally { _disposeCts.Dispose(); _serialPort.Dispose(); } } ~ComScanHelper() { Dispose(false); } } }