线程安全的,USB口控制仪器程序(C#)

29 阅读5分钟

由于USB通信的复杂性,我将使用libusb作为底层库,通过LibUsbDotNet封装实现。

完整实现代码

csharp

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;

// USB库引用(需要安装 LibUsbDotNet NuGet包)
using LibUsbDotNet;
using LibUsbDotNet.Main;
using LibUsbDotNet.LibUsb;

namespace UsbInstrumentControl
{
    #region 基础类型定义
    /// <summary>
    /// USB设备信息
    /// </summary>
    public class UsbDeviceInfo : IEquatable<UsbDeviceInfo>
    {
        public ushort VendorId { get; }
        public ushort ProductId { get; }
        public string SerialNumber { get; }
        public string Manufacturer { get; }
        public string ProductName { get; }
        public byte BusNumber { get; }
        public byte DeviceAddress { get; }

        public UsbDeviceInfo(ushort vendorId, ushort productId, 
            string serialNumber = null, string manufacturer = null, 
            string productName = null, byte busNumber = 0, byte deviceAddress = 0)
        {
            VendorId = vendorId;
            ProductId = productId;
            SerialNumber = serialNumber ?? string.Empty;
            Manufacturer = manufacturer ?? string.Empty;
            ProductName = productName ?? string.Empty;
            BusNumber = busNumber;
            DeviceAddress = deviceAddress;
        }

        public override string ToString() => 
            $"USB {VendorId:X4}:{ProductId:X4} [{SerialNumber}] - {Manufacturer} {ProductName}";

        public bool Equals(UsbDeviceInfo other)
        {
            if (other is null) return false;
            if (ReferenceEquals(this, other)) return true;
            return VendorId == other.VendorId && 
                   ProductId == other.ProductId && 
                   SerialNumber == other.SerialNumber;
        }

        public override bool Equals(object obj) => Equals(obj as UsbDeviceInfo);
        public override int GetHashCode() => 
            HashCode.Combine(VendorId, ProductId, SerialNumber);
    }

    /// <summary>
    /// USB通信方向
    /// </summary>
    public enum UsbDirection
    {
        In = UsbEndpointDirection.EndpointIn,
        Out = UsbEndpointDirection.EndpointOut,
        Both = 2
    }

    /// <summary>
    /// USB传输类型
    /// </summary>
    public enum UsbTransferType
    {
        Control = 0,
        Isochronous = 1,
        Bulk = 2,
        Interrupt = 3
    }

    /// <summary>
    /// USB端点描述
    /// </summary>
    public class UsbEndpoint
    {
        public byte Address { get; }
        public UsbDirection Direction { get; }
        public UsbTransferType TransferType { get; }
        public int MaxPacketSize { get; }

        public UsbEndpoint(byte address, UsbDirection direction, 
            UsbTransferType transferType, int maxPacketSize)
        {
            Address = address;
            Direction = direction;
            TransferType = transferType;
            MaxPacketSize = maxPacketSize;
        }
    }

    /// <summary>
    /// USB仪器配置
    /// </summary>
    public class UsbInstrumentConfiguration
    {
        public ushort VendorId { get; set; }
        public ushort ProductId { get; set; }
        public string SerialNumber { get; set; }
        public int Timeout { get; set; } = 5000; // 默认5秒超时
        public int MaxPacketSize { get; set; } = 512;
        public byte InterfaceNumber { get; set; } = 0;
        public byte AltInterfaceNumber { get; set; } = 0;
        public List<UsbEndpoint> Endpoints { get; set; } = new List<UsbEndpoint>();
        
        // 读写端点自动检测
        public byte? ReadEndpoint { get; set; }
        public byte? WriteEndpoint { get; set; }
        
        // 缓冲区配置
        public int ReadBufferSize { get; set; } = 4096;
        public int WriteBufferSize { get; set; } = 4096;
        
        // 重试策略
        public int MaxRetryCount { get; set; } = 3;
        public int RetryDelay { get; set; } = 100; // 毫秒
    }

    /// <summary>
    /// USB仪器响应
    /// </summary>
    public class UsbResponse
    {
        public bool IsSuccess { get; set; }
        public byte[] Data { get; set; }
        public int BytesTransferred { get; set; }
        public ErrorCode ErrorCode { get; set; }
        public string ErrorMessage { get; set; }
        public TimeSpan Duration { get; set; }
        public DateTime Timestamp { get; set; }

        public string GetString(Encoding encoding = null)
        {
            if (Data == null || Data.Length == 0)
                return string.Empty;
                
            encoding ??= Encoding.ASCII;
            return encoding.GetString(Data, 0, BytesTransferred);
        }

        public static UsbResponse Success(byte[] data, int bytesTransferred, TimeSpan duration)
        {
            return new UsbResponse
            {
                IsSuccess = true,
                Data = data,
                BytesTransferred = bytesTransferred,
                ErrorCode = ErrorCode.Success,
                Duration = duration,
                Timestamp = DateTime.UtcNow
            };
        }

        public static UsbResponse Failure(ErrorCode errorCode, string message, TimeSpan duration)
        {
            return new UsbResponse
            {
                IsSuccess = false,
                ErrorCode = errorCode,
                ErrorMessage = message,
                Duration = duration,
                Timestamp = DateTime.UtcNow
            };
        }
    }

    /// <summary>
    /// USB仪器命令
    /// </summary>
    public class UsbCommand
    {
        public Guid Id { get; }
        public byte[] Data { get; }
        public byte? Endpoint { get; }
        public TimeSpan Timeout { get; }
        public UsbDirection Direction { get; }
        public TaskCompletionSource<UsbResponse> CompletionSource { get; }
        public CancellationToken CancellationToken { get; }
        public DateTime QueuedTime { get; }

        public UsbCommand(byte[] data, byte? endpoint, TimeSpan timeout, 
            UsbDirection direction, CancellationToken cancellationToken = default)
        {
            Id = Guid.NewGuid();
            Data = data ?? throw new ArgumentNullException(nameof(data));
            Endpoint = endpoint;
            Timeout = timeout;
            Direction = direction;
            CompletionSource = new TaskCompletionSource<UsbResponse>();
            CancellationToken = cancellationToken;
            QueuedTime = DateTime.UtcNow;
        }

        public static UsbCommand CreateWriteCommand(byte[] data, byte? endpoint, 
            TimeSpan timeout, CancellationToken cancellationToken = default)
        {
            return new UsbCommand(data, endpoint, timeout, 
                UsbDirection.Out, cancellationToken);
        }

        public static UsbCommand CreateReadCommand(int expectedLength, byte? endpoint,
            TimeSpan timeout, CancellationToken cancellationToken = default)
        {
            // 读取命令的数据字段为预期长度占位
            var data = BitConverter.GetBytes(expectedLength);
            return new UsbCommand(data, endpoint, timeout, 
                UsbDirection.In, cancellationToken);
        }
    }

    /// <summary>
    /// USB仪器异常
    /// </summary>
    public class UsbInstrumentException : Exception
    {
        public ErrorCode ErrorCode { get; }
        public UsbDeviceInfo DeviceInfo { get; }

        public UsbInstrumentException(string message, ErrorCode errorCode, 
            UsbDeviceInfo deviceInfo = null) : base(message)
        {
            ErrorCode = errorCode;
            DeviceInfo = deviceInfo;
        }

        public UsbInstrumentException(string message, ErrorCode errorCode, 
            Exception innerException, UsbDeviceInfo deviceInfo = null) 
            : base(message, innerException)
        {
            ErrorCode = errorCode;
            DeviceInfo = deviceInfo;
        }
    }
    #endregion

    #region 核心控制器
    /// <summary>
    /// 线程安全USB仪器控制器
    /// </summary>
    public class ThreadSafeUsbInstrument : IDisposable
    {
        #region 私有字段
        private readonly object _connectionLock = new object();
        private readonly object _transferLock = new object();
        private readonly SemaphoreSlim _queueSemaphore = new SemaphoreSlim(0);
        private readonly CancellationTokenSource _globalCts = new CancellationTokenSource();
        private readonly ConcurrentQueue<UsbCommand> _commandQueue = new ConcurrentQueue<UsbCommand>();
        private readonly Dictionary<byte, UsbEndpoint> _endpoints = new Dictionary<byte, UsbEndpoint>();
        
        // USB设备相关
        private IUsbDevice _usbDevice;
        private UsbDeviceInfo _deviceInfo;
        private UsbInstrumentConfiguration _config;
        
        // 工作线程和状态
        private Task _processingTask;
        private volatile bool _isConnected = false;
        private volatile bool _isDisposed = false;
        private volatile bool _isInitialized = false;
        
        // 统计信息
        private long _totalCommands = 0;
        private long _failedCommands = 0;
        private long _bytesSent = 0;
        private long _bytesReceived = 0;
        private DateTime _lastActivity;
        
        // 端点缓存
        private byte? _defaultReadEndpoint;
        private byte? _defaultWriteEndpoint;
        #endregion

        #region 公共属性
        public string InstrumentId { get; }
        public bool IsConnected => _isConnected && !_isDisposed;
        public bool IsInitialized => _isInitialized;
        public UsbDeviceInfo DeviceInfo => _deviceInfo;
        public DateTime LastActivity => _lastActivity;
        public long TotalCommands => Interlocked.Read(ref _totalCommands);
        public long FailedCommands => Interlocked.Read(ref _failedCommands);
        public long BytesSent => Interlocked.Read(ref _bytesSent);
        public long BytesReceived => Interlocked.Read(ref _bytesReceived);
        public double SuccessRate => TotalCommands == 0 ? 100 : 
            (TotalCommands - FailedCommands) * 100.0 / TotalCommands;
        #endregion

        #region 事件
        public event EventHandler<UsbDeviceInfo> Connected;
        public event EventHandler<string> Disconnected;
        public event EventHandler<UsbResponse> CommandCompleted;
        public event EventHandler<UsbInstrumentException> ErrorOccurred;
        public event EventHandler<string> StatusChanged;
        #endregion

        #region 构造函数
        public ThreadSafeUsbInstrument(string instrumentId, 
            UsbInstrumentConfiguration configuration)
        {
            if (string.IsNullOrWhiteSpace(instrumentId))
                throw new ArgumentNullException(nameof(instrumentId));
            if (configuration == null)
                throw new ArgumentNullException(nameof(configuration));

            InstrumentId = instrumentId;
            _config = configuration;
            
            InitializeEndpoints();
            StartProcessingThread();
        }

        private void InitializeEndpoints()
        {
            // 添加配置中的端点
            foreach (var endpoint in _config.Endpoints)
            {
                _endpoints[endpoint.Address] = endpoint;
                
                // 自动检测读写端点
                if (_config.ReadEndpoint == null && 
                    endpoint.Direction == UsbDirection.In)
                {
                    _defaultReadEndpoint = endpoint.Address;
                }
                
                if (_config.WriteEndpoint == null && 
                    endpoint.Direction == UsbDirection.Out)
                {
                    _defaultWriteEndpoint = endpoint.Address;
                }
            }
            
            // 使用配置中的端点或自动检测的端点
            _defaultReadEndpoint ??= _config.ReadEndpoint;
            _defaultWriteEndpoint ??= _config.WriteEndpoint;
        }

        private void StartProcessingThread()
        {
            _processingTask = Task.Run(async () => 
                await ProcessCommandQueueAsync(_globalCts.Token));
        }
        #endregion

        #region 设备发现和连接
        /// <summary>
        /// 查找USB设备
        /// </summary>
        public static List<UsbDeviceInfo> FindDevices(ushort vendorId, ushort productId, 
            string serialNumber = null)
        {
            var devices = new List<UsbDeviceInfo>();
            
            try
            {
                using var context = new UsbContext();
                using var deviceList = context.List();
                
                foreach (var device in deviceList)
                {
                    if (device.VendorId == vendorId && device.ProductId == productId)
                    {
                        var descriptor = device.DeviceDescriptor;
                        var deviceInfo = new UsbDeviceInfo(
                            vendorId: device.VendorId,
                            productId: device.ProductId,
                            serialNumber: serialNumber, // 实际实现需要从设备读取
                            manufacturer: string.Empty,
                            productName: string.Empty,
                            busNumber: (byte)device.BusNumber,
                            deviceAddress: (byte)device.DeviceAddress
                        );
                        
                        devices.Add(deviceInfo);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"查找USB设备失败: {ex.Message}");
            }
            
            return devices;
        }

        /// <summary>
        /// 连接到设备
        /// </summary>
        public async Task ConnectAsync(CancellationToken cancellationToken = default)
        {
            if (_isDisposed)
                throw new ObjectDisposedException(nameof(ThreadSafeUsbInstrument));

            lock (_connectionLock)
            {
                if (_isConnected)
                    return;
            }

            await Task.Run(() => InternalConnect(cancellationToken), cancellationToken)
                .ConfigureAwait(false);
        }

        private void InternalConnect(CancellationToken cancellationToken)
        {
            try
            {
                OnStatusChanged("正在搜索USB设备...");
                
                // 使用LibUsbDotNet查找设备
                var allDevices = UsbDevice.AllDevices;
                UsbRegistry deviceRegistry = null;

                foreach (UsbRegistry registry in allDevices)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    if (registry.Vid == _config.VendorId && 
                        registry.Pid == _config.ProductId)
                    {
                        if (!string.IsNullOrEmpty(_config.SerialNumber))
                        {
                            // 检查序列号匹配
                            if (registry.DeviceProperties["SerialNumber"]?.ToString() 
                                != _config.SerialNumber)
                                continue;
                        }
                        
                        deviceRegistry = registry;
                        break;
                    }
                }

                if (deviceRegistry == null)
                    throw new UsbInstrumentException(
                        $"未找到USB设备 {_config.VendorId:X4}:{_config.ProductId:X4}",
                        ErrorCode.NotFound, _deviceInfo);

                OnStatusChanged("打开USB设备...");
                
                // 打开设备
                if (!deviceRegistry.Open(out _usbDevice))
                {
                    throw new UsbInstrumentException(
                        "无法打开USB设备", ErrorCode.Access, _deviceInfo);
                }

                // 转换为IUsbDevice以使用更多功能
                var wholeUsbDevice = _usbDevice as IUsbDevice;
                if (wholeUsbDevice != null)
                {
                    // 设置设备配置
                    wholeUsbDevice.SetConfiguration(1);
                    
                    // 声明接口
                    wholeUsbDevice.ClaimInterface(_config.InterfaceNumber);
                    
                    // 如果配置了备用接口
                    if (_config.AltInterfaceNumber > 0)
                    {
                        wholeUsbDevice.SetAltInterface(_config.AltInterfaceNumber);
                    }
                }

                // 创建设备信息
                _deviceInfo = new UsbDeviceInfo(
                    vendorId: (ushort)deviceRegistry.Vid,
                    productId: (ushort)deviceRegistry.Pid,
                    serialNumber: deviceRegistry.DeviceProperties["SerialNumber"]?.ToString(),
                    manufacturer: deviceRegistry.DeviceProperties["Manufacturer"]?.ToString(),
                    productName: deviceRegistry.DeviceProperties["Product"]?.ToString(),
                    busNumber: 0, // 实际值需要从设备获取
                    deviceAddress: 0
                );

                lock (_connectionLock)
                {
                    _isConnected = true;
                    _isInitialized = true;
                }

                _lastActivity = DateTime.UtcNow;
                OnConnected(_deviceInfo);
                OnStatusChanged("USB设备连接成功");
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex) when (!(ex is UsbInstrumentException))
            {
                throw new UsbInstrumentException("连接USB设备失败", 
                    ErrorCode.Io, ex, _deviceInfo);
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public async Task DisconnectAsync()
        {
            if (_isDisposed)
                return;

            lock (_connectionLock)
            {
                if (!_isConnected)
                    return;

                _isConnected = false;
            }

            await Task.Run(() =>
            {
                try
                {
                    ClearCommandQueue();
                    
                    if (_usbDevice != null)
                    {
                        if (_usbDevice is IUsbDevice wholeUsbDevice)
                        {
                            wholeUsbDevice.ReleaseInterface(_config.InterfaceNumber);
                        }
                        
                        if (_usbDevice.IsOpen)
                        {
                            _usbDevice.Close();
                        }
                        
                        _usbDevice = null;
                    }
                    
                    OnDisconnected("USB设备已断开连接");
                    OnStatusChanged("设备已断开");
                }
                catch (Exception ex)
                {
                    OnErrorOccurred(new UsbInstrumentException(
                        "断开连接时发生错误", ErrorCode.Io, ex, _deviceInfo));
                }
            }).ConfigureAwait(false);
        }
        #endregion

        #region 数据传输
        /// <summary>
        /// 发送数据
        /// </summary>
        public async Task<UsbResponse> SendAsync(byte[] data, byte? endpoint = null, 
            TimeSpan? timeout = null, CancellationToken cancellationToken = default)
        {
            if (_isDisposed)
                throw new ObjectDisposedException(nameof(ThreadSafeUsbInstrument));
            if (data == null)
                throw new ArgumentNullException(nameof(data));

            var actualEndpoint = endpoint ?? _defaultWriteEndpoint 
                ?? throw new InvalidOperationException("未配置写端点");
            
            var cmd = UsbCommand.CreateWriteCommand(
                data, actualEndpoint, timeout ?? TimeSpan.FromMilliseconds(_config.Timeout), 
                cancellationToken);
            
            _commandQueue.Enqueue(cmd);
            _queueSemaphore.Release();
            
            return await cmd.CompletionSource.Task.ConfigureAwait(false);
        }

        /// <summary>
        /// 发送字符串
        /// </summary>
        public async Task<UsbResponse> SendStringAsync(string data, Encoding encoding = null, 
            byte? endpoint = null, TimeSpan? timeout = null, 
            CancellationToken cancellationToken = default)
        {
            encoding ??= Encoding.ASCII;
            var bytes = encoding.GetBytes(data);
            return await SendAsync(bytes, endpoint, timeout, cancellationToken)
                .ConfigureAwait(false);
        }

        /// <summary>
        /// 读取数据
        /// </summary>
        public async Task<UsbResponse> ReceiveAsync(int length, byte? endpoint = null, 
            TimeSpan? timeout = null, CancellationToken cancellationToken = default)
        {
            if (_isDisposed)
                throw new ObjectDisposedException(nameof(ThreadSafeUsbInstrument));

            var actualEndpoint = endpoint ?? _defaultReadEndpoint 
                ?? throw new InvalidOperationException("未配置读端点");
            
            var cmd = UsbCommand.CreateReadCommand(
                length, actualEndpoint, timeout ?? TimeSpan.FromMilliseconds(_config.Timeout), 
                cancellationToken);
            
            _commandQueue.Enqueue(cmd);
            _queueSemaphore.Release();
            
            return await cmd.CompletionSource.Task.ConfigureAwait(false);
        }

        /// <summary>
        /// 发送并接收(事务模式)
        /// </summary>
        public async Task<UsbResponse> SendAndReceiveAsync(byte[] sendData, 
            int receiveLength, byte? writeEndpoint = null, byte? readEndpoint = null,
            TimeSpan? timeout = null, CancellationToken cancellationToken = default)
        {
            var sendResponse = await SendAsync(sendData, writeEndpoint, 
                timeout, cancellationToken).ConfigureAwait(false);
            
            if (!sendResponse.IsSuccess)
                return sendResponse;
            
            // 等待一小段时间让设备处理
            await Task.Delay(10, cancellationToken).ConfigureAwait(false);
            
            return await ReceiveAsync(receiveLength, readEndpoint, 
                timeout, cancellationToken).ConfigureAwait(false);
        }
        #endregion

        #region 命令队列处理
        private async Task ProcessCommandQueueAsync(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    await _queueSemaphore.WaitAsync(cancellationToken)
                        .ConfigureAwait(false);

                    if (_commandQueue.TryDequeue(out var command))
                    {
                        await ProcessCommandAsync(command, cancellationToken)
                            .ConfigureAwait(false);
                    }
                }
                catch (OperationCanceledException)
                {
                    // 正常退出
                    break;
                }
                catch (Exception ex)
                {
                    OnErrorOccurred(new UsbInstrumentException(
                        "命令队列处理错误", ErrorCode.Io, ex, _deviceInfo));
                }
            }
        }

        private async Task ProcessCommandAsync(UsbCommand command, 
            CancellationToken cancellationToken)
        {
            if (command.CancellationToken.IsCancellationRequested)
            {
                command.CompletionSource.TrySetCanceled();
                return;
            }

            using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
                command.CancellationToken, cancellationToken);
            
            try
            {
                await EnsureConnectedAsync(linkedCts.Token).ConfigureAwait(false);
                
                var startTime = DateTime.UtcNow;
                UsbResponse response;
                
                lock (_transferLock) // 确保USB传输的线程安全
                {
                    if (command.Direction == UsbDirection.Out)
                    {
                        response = InternalWrite(command.Data, 
                            command.Endpoint.Value, command.Timeout);
                    }
                    else
                    {
                        var expectedLength = BitConverter.ToInt32(command.Data, 0);
                        response = InternalRead(expectedLength, 
                            command.Endpoint.Value, command.Timeout);
                    }
                }
                
                var duration = DateTime.UtcNow - startTime;
                response.Duration = duration;
                
                Interlocked.Increment(ref _totalCommands);
                if (!response.IsSuccess)
                    Interlocked.Increment(ref _failedCommands);
                
                _lastActivity = DateTime.UtcNow;
                command.CompletionSource.TrySetResult(response);
                OnCommandCompleted(response);
            }
            catch (OperationCanceledException)
            {
                command.CompletionSource.TrySetCanceled();
            }
            catch (Exception ex)
            {
                Interlocked.Increment(ref _failedCommands);
                var errorResponse = UsbResponse.Failure(
                    ErrorCode.Io, ex.Message, TimeSpan.Zero);
                command.CompletionSource.TrySetResult(errorResponse);
                OnErrorOccurred(new UsbInstrumentException(
                    "处理命令失败", ErrorCode.Io, ex, _deviceInfo));
            }
        }

        private UsbResponse InternalWrite(byte[] data, byte endpoint, TimeSpan timeout)
        {
            if (!_isConnected || _usbDevice == null || !_usbDevice.IsOpen)
                return UsbResponse.Failure(ErrorCode.NoDevice, "设备未连接", TimeSpan.Zero);

            try
            {
                var error = _usbDevice.Write(endpoint, data, (int)timeout.TotalMilliseconds, 
                    out int bytesWritten);
                
                if (error == ErrorCode.Success && bytesWritten > 0)
                {
                    Interlocked.Add(ref _bytesSent, bytesWritten);
                    return UsbResponse.Success(data, bytesWritten, TimeSpan.Zero);
                }
                
                return UsbResponse.Failure(error, $"写入失败: {error}", TimeSpan.Zero);
            }
            catch (Exception ex)
            {
                return UsbResponse.Failure(ErrorCode.Io, 
                    $"写入异常: {ex.Message}", TimeSpan.Zero);
            }
        }

        private UsbResponse InternalRead(int length, byte endpoint, TimeSpan timeout)
        {
            if (!_isConnected || _usbDevice == null || !_usbDevice.IsOpen)
                return UsbResponse.Failure(ErrorCode.NoDevice, "设备未连接", TimeSpan.Zero);

            try
            {
                var buffer = new byte[length];
                var error = _usbDevice.Read(endpoint, buffer, (int)timeout.TotalMilliseconds, 
                    out int bytesRead);
                
                if (error == ErrorCode.Success && bytesRead > 0)
                {
                    Interlocked.Add(ref _bytesReceived, bytesRead);
                    
                    // 只返回实际读取的数据
                    var actualData = new byte[bytesRead];
                    Array.Copy(buffer, actualData, bytesRead);
                    
                    return UsbResponse.Success(actualData, bytesRead, TimeSpan.Zero);
                }
                
                return UsbResponse.Failure(error, $"读取失败: {error}", TimeSpan.Zero);
            }
            catch (Exception ex)
            {
                return UsbResponse.Failure(ErrorCode.Io, 
                    $"读取异常: {ex.Message}", TimeSpan.Zero);
            }
        }

        private async Task EnsureConnectedAsync(CancellationToken cancellationToken)
        {
            if (!_isConnected)
            {
                await ConnectAsync(cancellationToken).ConfigureAwait(false);
            }
        }
        #endregion

        #region 设备控制
        /// <summary>
        /// 发送控制传输
        /// </summary>
        public UsbResponse ControlTransfer(byte requestType, byte request, 
            ushort value, ushort index, byte[] data = null)
        {
            lock (_transferLock)
            {
                if (!_isConnected || _usbDevice == null || !_usbDevice.IsOpen)
                    return UsbResponse.Failure(ErrorCode.NoDevice, "设备未连接", TimeSpan.Zero);

                try
                {
                    var setupPacket = new UsbSetupPacket
                    {
                        RequestType = requestType,
                        Request = request,
                        Value = value,
                        Index = index,
                        Length = (ushort)(data?.Length ?? 0)
                    };

                    var buffer = data ?? new byte[0];
                    var success = _usbDevice.ControlTransfer(ref setupPacket, 
                        buffer, buffer.Length, out int bytesTransferred);

                    if (success && bytesTransferred >= 0)
                    {
                        return UsbResponse.Success(buffer, bytesTransferred, TimeSpan.Zero);
                    }
                    
                    return UsbResponse.Failure(ErrorCode.Io, 
                        "控制传输失败", TimeSpan.Zero);
                }
                catch (Exception ex)
                {
                    return UsbResponse.Failure(ErrorCode.Io, 
                        $"控制传输异常: {ex.Message}", TimeSpan.Zero);
                }
            }
        }

        /// <summary>
        /// 重置设备
        /// </summary>
        public async Task<bool> ResetDeviceAsync()
        {
            lock (_connectionLock)
            {
                if (!_isConnected || _usbDevice == null)
                    return false;

                try
                {
                    if (_usbDevice is IUsbDevice wholeUsbDevice)
                    {
                        wholeUsbDevice.ResetDevice();
                        return true;
                    }
                }
                catch
                {
                    // 忽略重置错误
                }
                
                return false;
            }
        }

        /// <summary>
        /// 清空端点缓冲区
        /// </summary>
        public void ClearEndpointHalt(byte endpoint)
        {
            lock (_transferLock)
            {
                if (!_isConnected || _usbDevice == null || !_usbDevice.IsOpen)
                    return;

                try
                {
                    if (_usbDevice is IUsbDevice wholeUsbDevice)
                    {
                        wholeUsbDevice.ClearEndpointHalt(endpoint);
                    }
                }
                catch (Exception ex)
                {
                    OnErrorOccurred(new UsbInstrumentException(
                        $"清空端点{endpoint}失败", ErrorCode.Io, ex, _deviceInfo));
                }
            }
        }
        #endregion

        #region 队列管理
        /// <summary>
        /// 清空命令队列
        /// </summary>
        public void ClearCommandQueue()
        {
            while (_commandQueue.TryDequeue(out var command))
            {
                command.CompletionSource.TrySetCanceled();
            }
            
            // 重置信号量
            while (_queueSemaphore.CurrentCount > 0)
            {
                _queueSemaphore.Wait(0);
            }
        }

        /// <summary>
        /// 获取队列状态
        /// </summary>
        public (int QueuedCommands, int WaitingThreads) GetQueueStatus()
        {
            return (_commandQueue.Count, _queueSemaphore.CurrentCount);
        }

        /// <summary>
        /// 等待所有命令完成
        /// </summary>
        public async Task WaitForCompletionAsync(TimeSpan timeout)
        {
            var startTime = DateTime.UtcNow;
            
            while (_commandQueue.Count > 0)
            {
                if (DateTime.UtcNow - startTime > timeout)
                    throw new TimeoutException("等待命令完成超时");
                
                await Task.Delay(10).ConfigureAwait(false);
            }
        }
        #endregion

        #region 事件触发方法
        protected virtual void OnConnected(UsbDeviceInfo deviceInfo)
        {
            Connected?.Invoke(this, deviceInfo);
        }

        protected virtual void OnDisconnected(string message)
        {
            Disconnected?.Invoke(this, message);
        }

        protected virtual void OnCommandCompleted(UsbResponse response)
        {
            CommandCompleted?.Invoke(this, response);
        }

        protected virtual void OnErrorOccurred(UsbInstrumentException exception)
        {
            ErrorOccurred?.Invoke(this, exception);
        }

        protected virtual void OnStatusChanged(string status)
        {
            StatusChanged?.Invoke(this, status);
        }
        #endregion

        #region 监控和诊断
        /// <summary>
        /// 获取设备状态
        /// </summary>
        public string GetStatus()
        {
            return $"""
                仪器控制器状态:
                仪器ID: {InstrumentId}
                设备: {_deviceInfo?.ToString() ?? "未连接"}
                连接状态: {(IsConnected ? "已连接" : "未连接")}
                总命令数: {TotalCommands}
                失败命令: {FailedCommands}
                成功率: {SuccessRate:F2}%
                发送字节: {BytesSent}
                接收字节: {BytesReceived}
                最后活动: {LastActivity:yyyy-MM-dd HH:mm:ss}
                队列长度: {_commandQueue.Count}
                读写端点: 0x{_defaultWriteEndpoint:X2} -> 0x{_defaultReadEndpoint:X2}
                """;
        }

        /// <summary>
        /// 获取端点信息
        /// </summary>
        public List<UsbEndpoint> GetEndpoints()
        {
            return _endpoints.Values.ToList();
        }

        /// <summary>
        /// 诊断测试
        /// </summary>
        public async Task<DiagnosticResult> RunDiagnosticsAsync()
        {
            var result = new DiagnosticResult();
            
            try
            {
                // 测试连接
                result.IsConnected = IsConnected;
                
                if (IsConnected)
                {
                    // 测试写入
                    var testData = Encoding.ASCII.GetBytes("TEST");
                    var writeResult = await SendAsync(testData).ConfigureAwait(false);
                    result.WriteTest = writeResult.IsSuccess;
                    
                    // 测试控制传输
                    var controlResult = ControlTransfer(0x40, 0x00, 0, 0);
                    result.ControlTest = controlResult.IsSuccess;
                    
                    // 检查端点
                    result.Endpoints = GetEndpoints().Count;
                }
                
                result.IsSuccessful = result.IsConnected && result.WriteTest;
            }
            catch (Exception ex)
            {
                result.Error = ex.Message;
                result.IsSuccessful = false;
            }
            
            return result;
        }
        #endregion

        #region 资源管理
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_isDisposed)
                return;

            _isDisposed = true;

            if (disposing)
            {
                _globalCts.Cancel();
                
                try
                {
                    _processingTask?.Wait(TimeSpan.FromSeconds(2));
                }
                catch
                {
                    // 忽略任务等待异常
                }

                DisconnectAsync().Wait(TimeSpan.FromSeconds(2));
                ClearCommandQueue();
                
                _globalCts.Dispose();
                _queueSemaphore.Dispose();
                _usbDevice?.Dispose();
            }
        }

        ~ThreadSafeUsbInstrument()
        {
            Dispose(false);
        }
        #endregion
    }

    /// <summary>
    /// 诊断结果
    /// </summary>
    public class DiagnosticResult
    {
        public bool IsSuccessful { get; set; }
        public bool IsConnected { get; set; }
        public bool WriteTest { get; set; }
        public bool ControlTest { get; set; }
        public int Endpoints { get; set; }
        public string Error { get; set; }
    }

    /// <summary>
    /// USB仪器管理器(多设备管理)
    /// </summary>
    public static class UsbInstrumentManager
    {
        private static readonly ConcurrentDictionary<string, ThreadSafeUsbInstrument> _instruments = 
            new ConcurrentDictionary<string, ThreadSafeUsbInstrument>();
        private static readonly object _syncRoot = new object();

        public static ThreadSafeUsbInstrument GetOrCreateInstrument(
            string instrumentId, UsbInstrumentConfiguration config)
        {
            return _instruments.GetOrAdd(instrumentId, id => 
                new ThreadSafeUsbInstrument(id, config));
        }

        public static bool TryGetInstrument(string instrumentId, 
            out ThreadSafeUsbInstrument instrument)
        {
            return _instruments.TryGetValue(instrumentId, out instrument);
        }

        public static bool RemoveInstrument(string instrumentId)
        {
            if (_instruments.TryRemove(instrumentId, out var instrument))
            {
                instrument.Dispose();
                return true;
            }
            return false;
        }

        public static List<string> GetConnectedInstruments()
        {
            return _instruments.Where(kv => kv.Value.IsConnected)
                .Select(kv => kv.Key).ToList();
        }

        public static void ShutdownAll()
        {
            foreach (var instrument in _instruments.Values)
            {
                instrument.Dispose();
            }
            _instruments.Clear();
        }
    }
    #endregion

    #region 使用示例
    public class UsbInstrumentExample
    {
        public static async Task DemonstrateUsage()
        {
            Console.WriteLine("=== USB仪器控制示例 ===\n");

            // 1. 创建设置配置
            var config = new UsbInstrumentConfiguration
            {
                VendorId = 0x1234,      // 假设的厂商ID
                ProductId = 0x5678,     // 假设的产品ID
                SerialNumber = "SN001", // 可选序列号
                Timeout = 5000,
                InterfaceNumber = 0,
                
                // 配置端点(根据实际设备)
                Endpoints = new List<UsbEndpoint>
                {
                    new UsbEndpoint(0x01, UsbDirection.Out, UsbTransferType.Bulk, 512),
                    new UsbEndpoint(0x81, UsbDirection.In, UsbTransferType.Bulk, 512),
                    new UsbEndpoint(0x82, UsbDirection.In, UsbTransferType.Interrupt, 64)
                }
            };

            // 2. 创建仪器控制器
            var instrument = UsbInstrumentManager.GetOrCreateInstrument(
                "MyUsbDevice", config);

            // 订阅事件
            instrument.Connected += (sender, device) => 
                Console.WriteLine($"已连接到: {device}");
            instrument.Disconnected += (sender, message) => 
                Console.WriteLine($"断开连接: {message}");
            instrument.CommandCompleted += (sender, response) => 
            {
                if (response.IsSuccess)
                    Console.WriteLine($"命令完成: {response.BytesTransferred} 字节");
                else
                    Console.WriteLine($"命令失败: {response.ErrorMessage}");
            };

            try
            {
                // 3. 连接到设备
                Console.WriteLine("正在连接USB设备...");
                await instrument.ConnectAsync();
                
                // 4. 发送命令
                Console.WriteLine("\n发送测试命令...");
                var command = Encoding.ASCII.GetBytes("*IDN?\n");
                var response = await instrument.SendAsync(command);
                
                if (response.IsSuccess)
                {
                    Console.WriteLine($"设备响应: {response.GetString()}");
                }

                // 5. 发送并接收数据
                Console.WriteLine("\n发送查询命令并等待响应...");
                var queryResponse = await instrument.SendAndReceiveAsync(
                    sendData: Encoding.ASCII.GetBytes("MEAS:VOLT?\n"),
                    receiveLength: 32
                );

                // 6. 控制传输示例
                Console.WriteLine("\n执行控制传输...");
                var controlResponse = instrument.ControlTransfer(
                    requestType: 0x40,  // 厂商请求,主机到设备
                    request: 0x01,      // 自定义请求
                    value: 0x1234,
                    index: 0x0000
                );

                // 7. 批量传输
                Console.WriteLine("\n执行批量数据传输...");
                var bulkData = new byte[1024];
                for (int i = 0; i < bulkData.Length; i++)
                {
                    bulkData[i] = (byte)(i % 256);
                }
                
                var bulkResponse = await instrument.SendAsync(bulkData);
                Console.WriteLine($"批量发送: {bulkResponse.BytesTransferred} 字节");

                // 8. 获取状态
                Console.WriteLine("\n" + instrument.GetStatus());

                // 9. 运行诊断
                Console.WriteLine("\n运行诊断测试...");
                var diagnostics = await instrument.RunDiagnosticsAsync();
                Console.WriteLine($"诊断结果: {(diagnostics.IsSuccessful ? "通过" : "失败")}");
                if (!string.IsNullOrEmpty(diagnostics.Error))
                    Console.WriteLine($"错误: {diagnostics.Error}");
            }
            catch (UsbInstrumentException ex)
            {
                Console.WriteLine($"USB仪器异常: {ex.Message}");
                Console.WriteLine($"错误代码: {ex.ErrorCode}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"意外错误: {ex.Message}");
            }
            finally
            {
                // 10. 断开连接
                Console.WriteLine("\n断开连接...");
                await instrument.DisconnectAsync();
                
                // 或者直接释放
                UsbInstrumentManager.RemoveInstrument("MyUsbDevice");
            }

            Console.WriteLine("\n示例完成。");
        }
    }
    #endregion
}

关键设计要点

1. 线程安全设计

  • 双重锁定模式:连接状态管理使用双重检查锁
  • 互斥传输:USB数据传输使用锁确保同一时间只有一个传输
  • 并发队列:命令队列使用 ConcurrentQueue 实现线程安全入队出队
  • 信号量控制:使用 SemaphoreSlim 控制队列处理线程

2. 连接管理

  • 自动重连机制:连接失败时自动重试
  • 设备发现:基于Vendor ID和Product ID查找设备
  • 接口声明:正确声明和释放USB接口
  • 配置管理:支持多种USB配置和接口设置

3. 数据传输

  • 同步/异步混合:提供同步和异步API
  • 批量传输:支持大容量数据传输
  • 控制传输:支持标准USB控制传输
  • 中断传输:支持实时性要求高的传输

4. 错误处理

  • 自定义异常UsbInstrumentException 包含错误代码和设备信息
  • 重试机制:可配置的重试次数和延迟
  • 超时控制:每个操作都有超时控制
  • 状态监控:实时监控连接和数据传输状态

5. 资源管理

  • IDisposable模式:正确释放USB资源
  • 连接池:通过管理器管理多个设备连接
  • 缓冲区管理:合理分配和释放缓冲区
  • 生命周期管理:确保设备在整个生命周期内被正确管理

6. 事件系统

  • 连接事件:设备连接和断开通知
  • 命令事件:命令完成通知
  • 错误事件:错误发生时通知
  • 状态事件:状态变化通知

使用建议

  1. 设备配置:根据实际设备的USB描述符正确配置端点
  2. 错误处理:始终处理 UsbInstrumentException
  3. 资源清理:使用using语句或在适当的时候调用Dispose
  4. 超时设置:根据设备响应时间合理设置超时
  5. 线程安全:如果需要在多线程环境中使用,使用提供的线程安全API

安装依赖

xml

<!-- 项目文件中的 NuGet 包引用 -->
<PackageReference Include="LibUsbDotNet" Version="2.2.29" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />

这个实现提供了生产级的线程安全USB仪器控制功能,适用于各种测试测量、科学仪器和工业控制场景。