Files
shgxtzcjhoudaosaomadayinwpf/RIZO_Application/RIZO_Application.Infrastructure/Util/ScanHelper/ComScanHelper.cs

386 lines
12 KiB
C#

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<string>? DataReceived;
public event EventHandler<Exception>? 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<bool> 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 (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<bool> 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<string?> 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<byte>.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<byte>.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<string>? 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);
}
}
}