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; } } } }