以下是完整实现:
csharp
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace InstrumentControl
{
/// <summary>
/// 仪器通信协议类型
/// </summary>
public enum InstrumentProtocol
{
TCP,
UDP,
RawSocket
}
/// <summary>
/// 仪器控制异常
/// </summary>
public class InstrumentControlException : Exception
{
public InstrumentControlException(string message) : base(message) { }
public InstrumentControlException(string message, Exception innerException)
: base(message, innerException) { }
}
/// <summary>
/// 仪器响应数据
/// </summary>
public class InstrumentResponse
{
public bool IsSuccess { get; set; }
public byte[] RawData { get; set; }
public string Message { get; set; }
public TimeSpan ResponseTime { get; set; }
public DateTime Timestamp { get; set; }
public string GetString(Encoding encoding = null)
{
encoding ??= Encoding.ASCII;
return encoding.GetString(RawData);
}
}
/// <summary>
/// 仪器命令封装
/// </summary>
public class InstrumentCommand
{
public string Command { get; }
public byte[] CommandBytes { get; }
public TimeSpan Timeout { get; set; }
public string ExpectedResponsePattern { get; set; }
public TaskCompletionSource<InstrumentResponse> CompletionSource { get; }
public DateTime QueuedTime { get; }
public CancellationToken CancellationToken { get; }
public InstrumentCommand(string command, TimeSpan timeout,
CancellationToken cancellationToken = default)
{
Command = command;
CommandBytes = Encoding.ASCII.GetBytes(command);
Timeout = timeout;
QueuedTime = DateTime.UtcNow;
CompletionSource = new TaskCompletionSource<InstrumentResponse>();
CancellationToken = cancellationToken;
}
public InstrumentCommand(byte[] commandBytes, TimeSpan timeout,
CancellationToken cancellationToken = default)
{
CommandBytes = commandBytes;
Command = Encoding.ASCII.GetString(commandBytes);
Timeout = timeout;
QueuedTime = DateTime.UtcNow;
CompletionSource = new TaskCompletionSource<InstrumentResponse>();
CancellationToken = cancellationToken;
}
}
/// <summary>
/// 线程安全的网口仪器控制器
/// </summary>
public class NetworkInstrumentController : IDisposable
{
#region 私有字段
private readonly object _connectionLock = new object();
private readonly object _sendLock = new object();
private readonly ConcurrentQueue<InstrumentCommand> _commandQueue = new ConcurrentQueue<InstrumentCommand>();
private readonly SemaphoreSlim _queueSemaphore = new SemaphoreSlim(0);
private readonly CancellationTokenSource _globalCancellationTokenSource = new CancellationTokenSource();
private readonly ManualResetEventSlim _connectionEvent = new ManualResetEventSlim(false);
private TcpClient _tcpClient;
private UdpClient _udpClient;
private NetworkStream _networkStream;
private Task _processingTask;
private volatile bool _isConnected = false;
private volatile bool _isDisposed = false;
// 配置参数
private readonly string _host;
private readonly int _port;
private readonly InstrumentProtocol _protocol;
private readonly TimeSpan _defaultTimeout;
private readonly int _receiveBufferSize;
private readonly Encoding _encoding;
private readonly int _maxRetryCount;
private readonly TimeSpan _reconnectInterval;
// 统计信息
private long _totalCommandsSent = 0;
private long _failedCommands = 0;
private DateTime _lastActivityTime;
#endregion
#region 公共属性
public string InstrumentId { get; }
public bool IsConnected => _isConnected && !_isDisposed;
public string ConnectionString => $"{_protocol}://{_host}:{_port}";
public DateTime LastActivityTime => _lastActivityTime;
public long TotalCommandsSent => Interlocked.Read(ref _totalCommandsSent);
public long FailedCommands => Interlocked.Read(ref _failedCommands);
public double SuccessRate => TotalCommandsSent == 0 ? 100 :
(TotalCommandsSent - FailedCommands) * 100.0 / TotalCommandsSent;
#endregion
#region 事件
public event EventHandler<InstrumentResponse> CommandCompleted;
public event EventHandler<string> ConnectionStatusChanged;
public event EventHandler<Exception> ErrorOccurred;
#endregion
#region 构造函数
public NetworkInstrumentController(
string instrumentId,
string host,
int port,
InstrumentProtocol protocol = InstrumentProtocol.TCP,
TimeSpan? defaultTimeout = null,
int receiveBufferSize = 4096,
Encoding encoding = null,
int maxRetryCount = 3,
TimeSpan? reconnectInterval = null)
{
if (string.IsNullOrWhiteSpace(instrumentId))
throw new ArgumentException("Instrument ID cannot be empty", nameof(instrumentId));
if (string.IsNullOrWhiteSpace(host))
throw new ArgumentException("Host cannot be empty", nameof(host));
if (port < 1 || port > 65535)
throw new ArgumentException("Invalid port number", nameof(port));
InstrumentId = instrumentId;
_host = host;
_port = port;
_protocol = protocol;
_defaultTimeout = defaultTimeout ?? TimeSpan.FromSeconds(5);
_receiveBufferSize = receiveBufferSize;
_encoding = encoding ?? Encoding.ASCII;
_maxRetryCount = maxRetryCount;
_reconnectInterval = reconnectInterval ?? TimeSpan.FromSeconds(2);
Initialize();
}
#endregion
#region 初始化
private void Initialize()
{
_processingTask = Task.Run(async () => await ProcessCommandQueueAsync());
_lastActivityTime = DateTime.UtcNow;
}
#endregion
#region 连接管理
/// <summary>
/// 连接到仪器
/// </summary>
public async Task ConnectAsync(CancellationToken cancellationToken = default)
{
if (_isDisposed)
throw new ObjectDisposedException(nameof(NetworkInstrumentController));
lock (_connectionLock)
{
if (_isConnected)
return;
OnConnectionStatusChanged($"正在连接仪器 {InstrumentId}...");
}
try
{
await InternalConnectAsync(cancellationToken).ConfigureAwait(false);
lock (_connectionLock)
{
_isConnected = true;
_connectionEvent.Set();
}
OnConnectionStatusChanged($"仪器 {InstrumentId} 连接成功");
}
catch (Exception ex)
{
OnErrorOccurred(ex);
throw new InstrumentControlException($"连接仪器失败: {ex.Message}", ex);
}
}
/// <summary>
/// 内部连接方法
/// </summary>
private async Task InternalConnectAsync(CancellationToken cancellationToken)
{
switch (_protocol)
{
case InstrumentProtocol.TCP:
_tcpClient = new TcpClient();
await _tcpClient.ConnectAsync(_host, _port, cancellationToken).ConfigureAwait(false);
_tcpClient.ReceiveTimeout = (int)_defaultTimeout.TotalMilliseconds;
_tcpClient.SendTimeout = (int)_defaultTimeout.TotalMilliseconds;
_networkStream = _tcpClient.GetStream();
break;
case InstrumentProtocol.UDP:
_udpClient = new UdpClient();
await _udpClient.ConnectAsync(_host, _port, cancellationToken).ConfigureAwait(false);
break;
case InstrumentProtocol.RawSocket:
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(_host, _port, cancellationToken).ConfigureAwait(false);
_tcpClient = new TcpClient { Client = socket };
_networkStream = new NetworkStream(socket, true);
break;
default:
throw new ArgumentOutOfRangeException(nameof(_protocol));
}
}
/// <summary>
/// 断开连接
/// </summary>
public async Task DisconnectAsync()
{
if (_isDisposed)
return;
lock (_connectionLock)
{
if (!_isConnected)
return;
_isConnected = false;
_connectionEvent.Reset();
OnConnectionStatusChanged($"正在断开仪器 {InstrumentId} 连接...");
}
try
{
// 取消所有待处理命令
while (_commandQueue.TryDequeue(out var command))
{
command.CompletionSource.TrySetCanceled();
}
// 关闭连接
switch (_protocol)
{
case InstrumentProtocol.TCP:
case InstrumentProtocol.RawSocket:
_networkStream?.Close();
_tcpClient?.Close();
break;
case InstrumentProtocol.UDP:
_udpClient?.Close();
break;
}
}
catch (Exception ex)
{
OnErrorOccurred(ex);
}
finally
{
_networkStream = null;
_tcpClient = null;
_udpClient = null;
OnConnectionStatusChanged($"仪器 {InstrumentId} 已断开连接");
}
}
/// <summary>
/// 确保连接
/// </summary>
private async Task EnsureConnectedAsync(CancellationToken cancellationToken)
{
if (!_isConnected)
{
await ConnectAsync(cancellationToken).ConfigureAwait(false);
}
// 检查连接状态
if (_protocol == InstrumentProtocol.TCP || _protocol == InstrumentProtocol.RawSocket)
{
if (_tcpClient?.Client == null || !_tcpClient.Connected)
{
await ReconnectAsync(cancellationToken).ConfigureAwait(false);
}
}
}
/// <summary>
/// 重连
/// </summary>
private async Task ReconnectAsync(CancellationToken cancellationToken)
{
for (int retry = 0; retry < _maxRetryCount; retry++)
{
try
{
await DisconnectAsync().ConfigureAwait(false);
await Task.Delay(_reconnectInterval, cancellationToken).ConfigureAwait(false);
await InternalConnectAsync(cancellationToken).ConfigureAwait(false);
lock (_connectionLock)
{
_isConnected = true;
_connectionEvent.Set();
}
OnConnectionStatusChanged($"仪器 {InstrumentId} 重连成功");
return;
}
catch (Exception ex)
{
OnErrorOccurred(ex);
if (retry == _maxRetryCount - 1)
throw;
}
}
}
#endregion
#region 命令发送
/// <summary>
/// 发送命令(异步)
/// </summary>
public async Task<InstrumentResponse> SendCommandAsync(
string command,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default)
{
if (_isDisposed)
throw new ObjectDisposedException(nameof(NetworkInstrumentController));
var cmd = new InstrumentCommand(command, timeout ?? _defaultTimeout, cancellationToken);
_commandQueue.Enqueue(cmd);
_queueSemaphore.Release();
return await cmd.CompletionSource.Task.ConfigureAwait(false);
}
/// <summary>
/// 发送二进制命令
/// </summary>
public async Task<InstrumentResponse> SendBinaryCommandAsync(
byte[] commandBytes,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default)
{
if (_isDisposed)
throw new ObjectDisposedException(nameof(NetworkInstrumentController));
var cmd = new InstrumentCommand(commandBytes, timeout ?? _defaultTimeout, cancellationToken);
_commandQueue.Enqueue(cmd);
_queueSemaphore.Release();
return await cmd.CompletionSource.Task.ConfigureAwait(false);
}
/// <summary>
/// 批量发送命令
/// </summary>
public async Task<List<InstrumentResponse>> SendCommandsAsync(
IEnumerable<string> commands,
TimeSpan? timeout = null,
CancellationToken cancellationToken = default)
{
var tasks = commands.Select(cmd =>
SendCommandAsync(cmd, timeout, cancellationToken)).ToList();
return await Task.WhenAll(tasks).ConfigureAwait(false);
}
#endregion
#region 命令队列处理
/// <summary>
/// 处理命令队列
/// </summary>
private async Task ProcessCommandQueueAsync()
{
while (!_globalCancellationTokenSource.Token.IsCancellationRequested)
{
try
{
await _queueSemaphore.WaitAsync(_globalCancellationTokenSource.Token).ConfigureAwait(false);
if (_commandQueue.TryDequeue(out var command))
{
await ProcessSingleCommandAsync(command).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
// 正常退出
break;
}
catch (Exception ex)
{
OnErrorOccurred(ex);
}
}
}
/// <summary>
/// 处理单个命令
/// </summary>
private async Task ProcessSingleCommandAsync(InstrumentCommand command)
{
if (command.CancellationToken.IsCancellationRequested)
{
command.CompletionSource.TrySetCanceled();
return;
}
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
command.CancellationToken,
_globalCancellationTokenSource.Token);
using (linkedCts)
{
try
{
// 设置超时
using var timeoutCts = new CancellationTokenSource(command.Timeout);
using var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
linkedCts.Token, timeoutCts.Token);
// 确保连接
await EnsureConnectedAsync(combinedCts.Token).ConfigureAwait(false);
// 发送命令
var response = await SendAndReceiveAsync(command, combinedCts.Token).ConfigureAwait(false);
Interlocked.Increment(ref _totalCommandsSent);
_lastActivityTime = DateTime.UtcNow;
command.CompletionSource.TrySetResult(response);
OnCommandCompleted(response);
}
catch (OperationCanceledException ex)
{
if (command.CancellationToken.IsCancellationRequested)
command.CompletionSource.TrySetCanceled();
else if (ex.CancellationToken == _globalCancellationTokenSource.Token)
command.CompletionSource.TrySetCanceled();
else
{
Interlocked.Increment(ref _failedCommands);
command.CompletionSource.TrySetException(
new InstrumentControlException("命令执行超时", ex));
}
}
catch (Exception ex)
{
Interlocked.Increment(ref _failedCommands);
command.CompletionSource.TrySetException(
new InstrumentControlException("命令执行失败", ex));
OnErrorOccurred(ex);
}
}
}
/// <summary>
/// 发送并接收数据
/// </summary>
private async Task<InstrumentResponse> SendAndReceiveAsync(
InstrumentCommand command,
CancellationToken cancellationToken)
{
var startTime = DateTime.UtcNow;
try
{
byte[] responseData = null;
lock (_sendLock) // 确保同一时间只有一个线程在发送
{
switch (_protocol)
{
case InstrumentProtocol.TCP:
case InstrumentProtocol.RawSocket:
// 发送命令
_networkStream.Write(command.CommandBytes, 0, command.CommandBytes.Length);
// 接收响应
responseData = ReceiveDataFromStream(_networkStream, cancellationToken);
break;
case InstrumentProtocol.UDP:
// UDP 发送
var sentBytes = await _udpClient.SendAsync(
command.CommandBytes, command.CommandBytes.Length).ConfigureAwait(false);
// UDP 接收
var udpResult = await _udpClient.ReceiveAsync()
.WithCancellation(cancellationToken).ConfigureAwait(false);
responseData = udpResult.Buffer;
break;
}
}
var endTime = DateTime.UtcNow;
var responseTime = endTime - startTime;
return new InstrumentResponse
{
IsSuccess = true,
RawData = responseData,
Message = "命令执行成功",
ResponseTime = responseTime,
Timestamp = endTime
};
}
catch (Exception ex)
{
// 标记连接可能已断开
lock (_connectionLock)
{
_isConnected = false;
_connectionEvent.Reset();
}
throw;
}
}
/// <summary>
/// 从流接收数据
/// </summary>
private byte[] ReceiveDataFromStream(NetworkStream stream, CancellationToken cancellationToken)
{
using var memoryStream = new MemoryStream();
var buffer = new byte[_receiveBufferSize];
int bytesRead;
do
{
cancellationToken.ThrowIfCancellationRequested();
bytesRead = stream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, bytesRead);
// 检查是否还有数据可读(非阻塞检查)
if (!stream.DataAvailable)
break;
} while (bytesRead > 0);
return memoryStream.ToArray();
}
#endregion
#region 事件触发方法
protected virtual void OnCommandCompleted(InstrumentResponse response)
{
CommandCompleted?.Invoke(this, response);
}
protected virtual void OnConnectionStatusChanged(string status)
{
ConnectionStatusChanged?.Invoke(this, status);
}
protected virtual void OnErrorOccurred(Exception exception)
{
ErrorOccurred?.Invoke(this, exception);
}
#endregion
#region 监控与诊断
/// <summary>
/// 获取控制器状态
/// </summary>
public string GetStatus()
{
return $"""
仪器控制器状态:
仪器ID: {InstrumentId}
连接状态: {(IsConnected ? "已连接" : "未连接")}
连接地址: {ConnectionString}
协议类型: {_protocol}
总命令数: {TotalCommandsSent}
失败命令: {FailedCommands}
成功率: {SuccessRate:F2}%
最后活动: {LastActivityTime:yyyy-MM-dd HH:mm:ss}
队列长度: {_commandQueue.Count}
""";
}
/// <summary>
/// 清空命令队列
/// </summary>
public void ClearCommandQueue()
{
while (_commandQueue.TryDequeue(out var command))
{
command.CompletionSource.TrySetCanceled();
}
}
/// <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(100).ConfigureAwait(false);
}
}
#endregion
#region 资源清理
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
return;
_isDisposed = true;
if (disposing)
{
_globalCancellationTokenSource.Cancel();
_globalCancellationTokenSource.Dispose();
_processingTask?.Wait(TimeSpan.FromSeconds(5));
_processingTask?.Dispose();
DisconnectAsync().Wait(TimeSpan.FromSeconds(5));
ClearCommandQueue();
_queueSemaphore.Dispose();
_connectionEvent.Dispose();
_networkStream?.Dispose();
_tcpClient?.Dispose();
_udpClient?.Dispose();
}
}
~NetworkInstrumentController()
{
Dispose(false);
}
#endregion
}
#region 扩展方法
public static class TaskExtensions
{
public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task).ConfigureAwait(false))
{
throw new OperationCanceledException(cancellationToken);
}
}
return await task.ConfigureAwait(false);
}
}
/// <summary>
/// 仪器控制器工厂
/// </summary>
public static class InstrumentControllerFactory
{
private static readonly ConcurrentDictionary<string, NetworkInstrumentController> _controllers =
new ConcurrentDictionary<string, NetworkInstrumentController>();
public static NetworkInstrumentController GetOrCreateController(
string instrumentId,
string host,
int port,
InstrumentProtocol protocol = InstrumentProtocol.TCP)
{
return _controllers.GetOrAdd(instrumentId, id =>
new NetworkInstrumentController(id, host, port, protocol));
}
public static bool TryGetController(string instrumentId, out NetworkInstrumentController controller)
{
return _controllers.TryGetValue(instrumentId, out controller);
}
public static bool RemoveController(string instrumentId)
{
if (_controllers.TryRemove(instrumentId, out var controller))
{
controller.Dispose();
return true;
}
return false;
}
}
#endregion
#region 使用示例
public class InstrumentControlExample
{
public static async Task DemonstrateUsage()
{
// 1. 创建仪器控制器
var controller = new NetworkInstrumentController(
instrumentId: "Oscilloscope_001",
host: "192.168.1.100",
port: 5025,
protocol: InstrumentProtocol.TCP,
defaultTimeout: TimeSpan.FromSeconds(10));
// 订阅事件
controller.ConnectionStatusChanged += (sender, status) =>
Console.WriteLine($"连接状态: {status}");
controller.CommandCompleted += (sender, response) =>
Console.WriteLine($"命令完成: {response.Message}, 耗时: {response.ResponseTime.TotalMilliseconds}ms");
controller.ErrorOccurred += (sender, ex) =>
Console.WriteLine($"错误: {ex.Message}");
try
{
// 2. 连接到仪器
await controller.ConnectAsync();
// 3. 发送命令
var response = await controller.SendCommandAsync("*IDN?\n");
if (response.IsSuccess)
{
Console.WriteLine($"仪器标识: {response.GetString()}");
}
// 4. 批量发送命令
var commands = new[]
{
"CONF:VOLT:DC 10,0.001\n",
"READ?\n",
"SYST:ERR?\n"
};
var responses = await controller.SendCommandsAsync(commands);
foreach (var resp in responses)
{
Console.WriteLine($"响应: {resp.GetString()}");
}
// 5. 获取状态
Console.WriteLine(controller.GetStatus());
// 6. 使用工厂模式创建/获取控制器
var factoryController = InstrumentControllerFactory.GetOrCreateController(
"Multimeter_002", "192.168.1.101", 5025);
await factoryController.SendCommandAsync("*RST\n");
}
catch (InstrumentControlException ex)
{
Console.WriteLine($"仪器控制异常: {ex.Message}");
}
finally
{
// 7. 清理资源
await controller.DisconnectAsync();
controller.Dispose();
}
}
}
#endregion
}
设计要点说明:
1. 线程安全实现
- 双重锁机制:连接状态管理使用双重检查锁
- 并发队列:使用
ConcurrentQueue存储命令 - 信号量控制:使用
SemaphoreSlim控制队列处理 - 互斥发送:使用锁确保同一时间只有一个线程发送数据
2. 连接管理
- 自动重连机制(可配置重试次数和间隔)
- 连接状态监控和事件通知
- 支持TCP、UDP和原始Socket协议
3. 错误处理
- 自定义异常类型
InstrumentControlException - 完善的异常捕获和事件通知
- 超时控制和取消支持
4. 异步设计
- 完全基于async/await的异步编程
- 支持CancellationToken取消操作
- 非阻塞的命令队列处理
5. 监控和诊断
- 统计信息(成功率、响应时间等)
- 状态查询方法
- 事件系统用于监控
6. 资源管理
- 实现IDisposable模式
- 正确的资源清理和释放
- 使用工厂模式管理多个控制器实例
7. 扩展性
- 支持多种协议
- 可配置的超时和缓冲区大小
- 支持二进制命令和文本命令
使用建议:
- 单例模式:对于每个仪器,使用工厂模式确保只有一个控制器实例
- 异常处理:始终处理
InstrumentControlException - 资源清理:使用using语句或在适当的时候调用Dispose
- 监控:订阅事件以便监控仪器状态
- 超时设置:根据仪器响应时间合理设置超时
这个实现提供了生产级的线程安全仪器控制功能,适用于各种测试测量和自动化控制场景。