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; public int ReceiveBufferSize { get; set; } = 2 * 1024 * 1024; public int ConnectTimeoutMs { get; set; } = 3000; public int ReadWriteTimeoutMs { get; set; } = 5000; // 默认电压量程(伏特),可根据实际信号修改,例如 0.1, 1, 10, 100, 1000 public double DefaultVoltageRange { get; set; } = 10.0; 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; } /// /// 发送命令并等待完整响应(支持多行/大数据量) /// 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[65536]; int bytesRead; bool endReached = false; while (!endReached) { bytesRead = await _stream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead == 0) break; string chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead); responseBuilder.Append(chunk); // 如果收到换行符,再等50ms确认没有更多数据(TCP分包情况) if (chunk.Contains("\n")) { await Task.Delay(50); if (!_stream.DataAvailable) endReached = true; } } 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); } /// /// 配置为高速直流电压测量(BUS触发模式,固定量程,0.02PLC,关闭自归零) /// public async Task ConfigureForHighSpeedDcvAsync() { // 1. 重置到默认状态(可选) await SendCommandAsync("*RST"); await Task.Delay(100); // 2. 固定量程(避免自动量程降低速度) await SendCommandAsync($"VOLT:DC:RANG {DefaultVoltageRange}"); // 3. 设置积分时间 0.02PLC(最快速度) await SendCommandAsync("VOLT:DC:NPLC 0.1"); // 4. 关闭自动归零(提高速度) await SendCommandAsync("VOLT:DC:ZERO:AUTO OFF"); // 5. 触发源设为 BUS(软件触发) await SendCommandAsync("TRIG:SOUR BUS"); // 6. 关闭自动延迟,延迟设为0 await SendCommandAsync("TRIG:DEL:AUTO OFF"); await SendCommandAsync("TRIG:DEL 0"); } /// /// 预置采样点数并进入等待触发状态(发送 INIT) /// public async Task PrepareBatchAsync(int sampleCount) { await SendCommandAsync($"SAMP:COUN {sampleCount}"); await SendCommandAsync("INIT"); // 进入等待触发状态 } /// /// 发送软件触发信号(*TRG),开始采集 /// public async Task TriggerAsync() { await SendCommandAsync("*TRG"); } /// /// 获取批量采集结果(FETCh?) /// public async Task FetchBatchAsync() { string response = await QueryAsync("FETCh?"); return ParseResponse(response); } /// /// 一次性批量采集(简化接口,内部自动完成 SAMP:COUN + READ?) /// 注意:此方法会阻塞直到采集完成,适合不需要分离触发时序的场景 /// public async Task ReadBatchAsync(int sampleCount) { await SendCommandAsync($"SAMP:COUN {sampleCount}"); string response = await QueryAsync("READ?"); return ParseResponse(response); } /// /// 解析逗号分隔的电压值数组 /// private double[] ParseResponse(string response) { if (string.IsNullOrWhiteSpace(response)) throw new Exception("返回数据为空"); 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]}"); } 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"); } public void Dispose() { if (!_disposed) { _stream?.Close(); _tcpClient?.Close(); _stream?.Dispose(); _tcpClient?.Dispose(); _disposed = true; } } } }