using System;
using System.IO.Ports;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace APS7100TestTool.Services
{
///
/// 连接类型枚举
///
public enum ConnectionType
{
None, // 未连接
SerialPort, // 串口
Ethernet // 网口
}
///
/// SCPI 设备通讯类,支持串口和网口连接
///
public class ScpiDevice : IDisposable
{
private SerialPort? _serialPort;
private TcpClient? _tcpClient;
private NetworkStream? _networkStream;
private ConnectionType _connectionType;
private readonly object _lockObj = new object();
private volatile bool _isDisposed = false;
private volatile bool _isDisconnecting = false;
public bool IsConnected
{
get
{
// 不使用锁,避免阻塞 UI 线程(这些属性读取是线程安全的)
if (_isDisposed || _isDisconnecting) return false;
return _connectionType switch
{
ConnectionType.SerialPort => _serialPort?.IsOpen ?? false,
ConnectionType.Ethernet => _tcpClient?.Connected ?? false,
_ => false
};
}
}
public ConnectionType CurrentConnectionType => _connectionType;
///
/// 通过串口连接设备
///
public bool ConnectSerial(string portName, int baudRate = 9600, int dataBits = 8,
Parity parity = Parity.None, StopBits stopBits = StopBits.One)
{
try
{
lock (_lockObj)
{
Disconnect();
_serialPort = new SerialPort
{
PortName = portName,
BaudRate = baudRate,
DataBits = dataBits,
Parity = parity,
StopBits = stopBits,
Encoding = Encoding.ASCII,
ReadTimeout = 1000, // 减少超时以更快检测断连
WriteTimeout = 1000,
NewLine = "\n"
};
_serialPort.Open();
Thread.Sleep(100); // 等待端口稳定
// 清空缓冲区
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
_connectionType = ConnectionType.SerialPort;
return true;
}
}
catch (Exception ex)
{
throw new Exception($"串口连接失败: {ex.Message}", ex);
}
}
///
/// 连接超时时间(毫秒)
///
public int ConnectionTimeout { get; set; } = 5000;
///
/// 通过网口连接设备
///
public bool ConnectEthernet(string ipAddress, int port = 2268)
{
TcpClient? tempClient = null;
try
{
lock (_lockObj)
{
// 先彻底断开并释放旧连接
ForceDisconnect();
// 等待端口完全释放
Thread.Sleep(100);
tempClient = new TcpClient();
// 设置 LingerState 以立即释放端口
tempClient.LingerState = new System.Net.Sockets.LingerOption(true, 0);
tempClient.NoDelay = true;
// 使用带超时的连接
var connectTask = tempClient.ConnectAsync(ipAddress, port);
if (!connectTask.Wait(ConnectionTimeout))
{
// 超时 - 强制关闭
try { tempClient.Close(); } catch { }
try { tempClient.Dispose(); } catch { }
tempClient = null;
throw new TimeoutException($"连接超时({ConnectionTimeout/1000}秒),请检查设备IP地址和端口是否正确");
}
// 检查连接是否真的成功
if (!tempClient.Connected)
{
try { tempClient.Close(); } catch { }
try { tempClient.Dispose(); } catch { }
tempClient = null;
throw new Exception("连接失败,设备未响应");
}
_tcpClient = tempClient;
tempClient = null; // 转移所有权,防止 finally 中释放
_networkStream = _tcpClient.GetStream();
_networkStream.ReadTimeout = 1000;
_networkStream.WriteTimeout = 1000;
Thread.Sleep(100); // 等待连接稳定
_connectionType = ConnectionType.Ethernet;
return true;
}
}
catch (TimeoutException)
{
throw;
}
catch (AggregateException ae)
{
var innerEx = ae.InnerException ?? ae;
throw new Exception($"网口连接失败: {innerEx.Message}", innerEx);
}
catch (Exception ex)
{
throw new Exception($"网口连接失败: {ex.Message}", ex);
}
finally
{
// 确保临时客户端被释放(如果没有成功转移所有权)
if (tempClient != null)
{
try { tempClient.Close(); } catch { }
try { tempClient.Dispose(); } catch { }
}
}
}
///
/// 强制断开连接并释放所有资源
///
private void ForceDisconnect()
{
_isDisconnecting = true;
try
{
// 关闭网络流
if (_networkStream != null)
{
try { _networkStream.Close(); } catch { }
try { _networkStream.Dispose(); } catch { }
_networkStream = null;
}
// 强制关闭 TCP 连接
if (_tcpClient != null)
{
try
{
// 设置 LingerState 为立即关闭
_tcpClient.LingerState = new System.Net.Sockets.LingerOption(true, 0);
}
catch { }
try { _tcpClient.Close(); } catch { }
try { _tcpClient.Dispose(); } catch { }
_tcpClient = null;
}
// 关闭串口
if (_serialPort != null)
{
try
{
if (_serialPort.IsOpen)
{
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
_serialPort.Close();
}
}
catch { }
try { _serialPort.Dispose(); } catch { }
_serialPort = null;
}
_connectionType = ConnectionType.None;
}
finally
{
_isDisconnecting = false;
}
}
///
/// 连接设备(兼容旧方法)
///
[Obsolete("请使用 ConnectSerial 或 ConnectEthernet")]
public bool Connect(string portName, int baudRate = 9600, int dataBits = 8,
Parity parity = Parity.None, StopBits stopBits = StopBits.One)
{
return ConnectSerial(portName, baudRate, dataBits, parity, stopBits);
}
///
/// 断开连接
///
public void Disconnect()
{
lock (_lockObj)
{
ForceDisconnect();
}
}
///
/// 启用命令调试日志
///
public static bool EnableCommandLog { get; set; } = false;
///
/// 命令日志事件
///
public static event Action? OnCommandLog;
///
/// 发送 SCPI 命令(无返回)
///
public void SendCommand(string command)
{
lock (_lockObj)
{
CheckConnectionState();
try
{
if (EnableCommandLog)
{
OnCommandLog?.Invoke($"[SCPI TX] {command}");
}
if (_connectionType == ConnectionType.SerialPort)
{
if (_serialPort == null || !_serialPort.IsOpen)
throw new InvalidOperationException("串口未打开");
_serialPort.WriteLine(command);
}
else // Ethernet
{
if (_networkStream == null || !_networkStream.CanWrite)
throw new InvalidOperationException("网络流不可写");
byte[] data = Encoding.ASCII.GetBytes(command + "\n");
_networkStream.Write(data, 0, data.Length);
}
Thread.Sleep(20); // 给设备处理时间(减少延迟)
}
catch (ObjectDisposedException)
{
throw new InvalidOperationException("设备连接已关闭");
}
catch (System.IO.IOException ex)
{
throw new InvalidOperationException($"通信错误: {ex.Message}", ex);
}
}
}
///
/// 发送 SCPI 查询命令并获取返回值
///
public string Query(string command)
{
lock (_lockObj)
{
CheckConnectionState();
string response;
try
{
if (EnableCommandLog)
{
OnCommandLog?.Invoke($"[SCPI TX] {command}");
}
if (_connectionType == ConnectionType.SerialPort)
{
if (_serialPort == null || !_serialPort.IsOpen)
throw new InvalidOperationException("串口未打开");
// 串口方式
_serialPort.DiscardInBuffer();
_serialPort.WriteLine(command);
Thread.Sleep(30); // 减少延迟以提高响应速度
response = _serialPort.ReadLine().Trim();
}
else // Ethernet
{
if (_networkStream == null || !_networkStream.CanWrite)
throw new InvalidOperationException("网络流不可用");
// 网口方式
byte[] sendData = Encoding.ASCII.GetBytes(command + "\n");
_networkStream.Write(sendData, 0, sendData.Length);
Thread.Sleep(30); // 减少延迟以提高响应速度
byte[] buffer = new byte[4096];
int bytesRead = _networkStream.Read(buffer, 0, buffer.Length);
response = Encoding.ASCII.GetString(buffer, 0, bytesRead).Trim();
}
}
catch (ObjectDisposedException)
{
throw new InvalidOperationException("设备连接已关闭");
}
catch (System.IO.IOException ex)
{
throw new InvalidOperationException($"通信错误: {ex.Message}", ex);
}
catch (TimeoutException ex)
{
throw new InvalidOperationException($"通信超时: {ex.Message}", ex);
}
return response;
}
}
///
/// 检查连接状态(必须在锁内调用)
///
private void CheckConnectionState()
{
if (_isDisposed)
throw new ObjectDisposedException(nameof(ScpiDevice));
if (_isDisconnecting)
throw new InvalidOperationException("设备正在断开连接");
bool connected = _connectionType switch
{
ConnectionType.SerialPort => _serialPort?.IsOpen ?? false,
ConnectionType.Ethernet => _tcpClient?.Connected ?? false,
_ => false
};
if (!connected)
throw new InvalidOperationException("设备未连接");
}
///
/// 获取可用的串口列表
///
public static string[] GetAvailablePorts()
{
return SerialPort.GetPortNames();
}
public void Dispose()
{
if (!_isDisposed)
{
Disconnect();
_serialPort?.Dispose();
_isDisposed = true;
}
}
}
}