using System; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; namespace ASTM_D7896_Tester.Services { /// /// 基于 LAN (TCP/IP) 的 TH1963 数字多用表通信服务 /// 支持 SCPI 命令,实现高速批量采集 /// public class Th1963LanService : IDisposable { private TcpClient _tcpClient; private NetworkStream _stream; private bool _disposed = false; /// /// 接收缓冲区大小(字节),建议至少 1MB 以支持大量数据 /// public int ReceiveBufferSize { get; set; } = 2 * 1024 * 1024; // 2 MB /// /// 连接超时(毫秒) /// public int ConnectTimeoutMs { get; set; } = 3000; /// /// 读写超时(毫秒) /// public int ReadWriteTimeoutMs { get; set; } = 5000; /// /// 连接到 TH1963 /// /// 仪器 IP 地址,如 "192.168.1.241" /// 端口号,默认 45454 public async Task ConnectAsync(string ipAddress, int port = 45454) { if (_tcpClient != null && _tcpClient.Connected) return; _tcpClient = new TcpClient(); _tcpClient.ReceiveBufferSize = ReceiveBufferSize; _tcpClient.SendBufferSize = ReceiveBufferSize; var connectTask = _tcpClient.ConnectAsync(ipAddress, port); if (await Task.WhenAny(connectTask, Task.Delay(ConnectTimeoutMs)) != connectTask) throw new TimeoutException($"连接 TH1963 ({ipAddress}:{port}) 超时"); _stream = _tcpClient.GetStream(); _stream.ReadTimeout = ReadWriteTimeoutMs; _stream.WriteTimeout = ReadWriteTimeoutMs; } /// /// 发送 SCPI 命令并等待响应(适用于查询类命令) /// public async Task QueryAsync(string command) { EnsureConnected(); byte[] cmdBytes = Encoding.ASCII.GetBytes(command + "\n"); await _stream.WriteAsync(cmdBytes, 0, cmdBytes.Length); // 读取响应,直到换行符 var responseBuilder = new StringBuilder(); byte[] buffer = new byte[4096]; int bytesRead; while (true) { bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead == 0) break; string chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead); responseBuilder.Append(chunk); if (chunk.Contains("\n")) break; } return responseBuilder.ToString().Trim(); } /// /// 发送命令但不等待响应(适用于设置类命令) /// public async Task SendCommandAsync(string command) { EnsureConnected(); byte[] cmdBytes = Encoding.ASCII.GetBytes(command + "\n"); await _stream.WriteAsync(cmdBytes, 0, cmdBytes.Length); } /// /// 配置为高速直流电压测量(用于批量采集) /// 设置:0.02 PLC(≈400μs积分时间),自动量程,关闭自动归零 /// public async Task ConfigureForHighSpeedDcvAsync() { await SendCommandAsync("CONF:VOLT:DC AUTO"); await SendCommandAsync("VOLT:DC:NPLC 0.02"); // 最快速度 await SendCommandAsync("VOLT:DC:ZERO:AUTO OFF"); // 提高速度 await SendCommandAsync("TRIG:SOUR EXT"); // 外部触发(与PLC同步) await SendCommandAsync("TRIG:DEL:AUTO OFF"); await SendCommandAsync("TRIG:DEL 0"); } /// /// 批量采集指定数量的电压值(使用外部触发,需等待硬件触发信号) /// 调用前必须已执行 ConfigureForHighSpeedDcvAsync 和 SAMP:COUN /// /// 采样点数(最大 5000) /// 电压数组(单位:伏特) public async Task AcquireBatchAsync(int sampleCount) { // 预置采样点数 await SendCommandAsync($"SAMP:COUN {sampleCount}"); // 进入等待触发状态 await SendCommandAsync("INIT"); // 等待采集完成(估算:sampleCount / 1000 秒 + 稳定时间) int waitMs = (int)(sampleCount / 1000.0 * 1000) + 200; await Task.Delay(waitMs); // 取回数据 string response = await QueryAsync("FETCh?"); string[] parts = response.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); double[] values = new double[parts.Length]; for (int i = 0; i < parts.Length; i++) { if (!double.TryParse(parts[i], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out values[i])) throw new Exception($"解析电压值失败: {parts[i]}"); } if (values.Length != sampleCount) throw new Exception($"期望 {sampleCount} 个点,实际收到 {values.Length}"); return values; } /// /// 单次读取当前电压(非批量,用于实时监控) /// public async Task ReadVoltageAsync() { string resp = await QueryAsync("MEAS:VOLT:DC?"); if (double.TryParse(resp, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double value)) return value; throw new Exception($"电压读数无效: {resp}"); } /// /// 复位仪器 /// public async Task ResetAsync() => await SendCommandAsync("*RST"); /// /// 查询仪器标识 /// public async Task IdentifyAsync() => await QueryAsync("*IDN?"); private void EnsureConnected() { if (_tcpClient == null || !_tcpClient.Connected) throw new InvalidOperationException("未连接到 TH1963,请先调用 ConnectAsync"); } // 实现 IDisposable public void Dispose() { if (!_disposed) { _stream?.Close(); _tcpClient?.Close(); _stream?.Dispose(); _tcpClient?.Dispose(); _disposed = true; } } } }