From eb95de74eda63c2e54d99728729a060ecb01fde4 Mon Sep 17 00:00:00 2001 From: xyy <544939200@qq.com> Date: Sun, 24 May 2026 10:36:57 +0800 Subject: [PATCH] --- Services/FiveHalfDmmService.cs | 115 ++++++++++++++++++--------------- Services/LanScpiSocket.cs | 111 ++++++++++++++++++------------- ViewModels/D7896ViewModel.cs | 4 +- Views/D7896View.xaml | 7 ++ 4 files changed, 140 insertions(+), 97 deletions(-) diff --git a/Services/FiveHalfDmmService.cs b/Services/FiveHalfDmmService.cs index de1dd27..d644a0e 100644 --- a/Services/FiveHalfDmmService.cs +++ b/Services/FiveHalfDmmService.cs @@ -13,9 +13,9 @@ namespace ASTM_D7896_Tester.Services public FiveHalfDmmService(string portName, int baudRate = 115200) { _serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One); - _serialPort.ReadTimeout = 2000; + _serialPort.ReadTimeout = 5000; // 增加超时,批量数据可能需要更长时间 _serialPort.WriteTimeout = 1000; - _serialPort.NewLine = "\n"; + _serialPort.NewLine = "\n"; // 结束符为 LF } public void Open() @@ -32,9 +32,7 @@ namespace ASTM_D7896_Tester.Services public bool IsOpen => _serialPort?.IsOpen == true; - /// - /// 发送命令并等待响应(同步读写,但因在Task.Run中执行,不阻塞UI) - /// + // ========== 核心通信方法 ========== private async Task QueryAsync(string command) { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开"); @@ -42,13 +40,12 @@ namespace ASTM_D7896_Tester.Services { lock (_serialPort) { - // 清空缓冲区 _serialPort.DiscardInBuffer(); _serialPort.DiscardOutBuffer(); - _serialPort.WriteLine(command); System.Diagnostics.Debug.WriteLine($"[发送] {command}"); + // 读取直到遇到换行符(批量数据可能很长,但 ReadLine 会一直读到 \n) string response = _serialPort.ReadLine(); System.Diagnostics.Debug.WriteLine($"[接收] {response}"); return response.Trim(); @@ -56,16 +53,6 @@ namespace ASTM_D7896_Tester.Services }); } - - public string TestCommunication() - { - if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开"); - _serialPort.DiscardInBuffer(); - _serialPort.WriteLine("*IDN?"); - return _serialPort.ReadLine().Trim(); - } - - private async Task SendCommandAsync(string command) { if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开"); @@ -78,71 +65,97 @@ namespace ASTM_D7896_Tester.Services }); } - /// - /// 配置为高速直流电压测量(根据8255文档) - /// + // ========== 配置高速直流电压测量(基于8255文档) ========== public async Task ConfigureHighSpeedDcvAsync() { - // 配置直流电压,自动量程 - await SendCommandAsync("CONFigure:VOLTage:DC AUTO"); - // 设置精度为FAST(高速模式) - await SendCommandAsync("SENSe:VOLTage:DC:RESolution FAST"); - // 使用软件触发 - await SendCommandAsync("TRIGger:SOURce BUS"); - // 关闭自动延迟 - await SendCommandAsync("TRIGger:DELay:AUTO OFF"); - await SendCommandAsync("TRIGger:DELay 0"); + // 1. 复位到已知状态(可选) + await SendCommandAsync("*RST"); + await Task.Delay(100); + + // 2. 固定量程(推荐手动设定,例如 1V,根据实际信号调整) + // 文档中量程可选 200mV, 2V, 20V, 200V, 1000V + await SendCommandAsync("VOLT:DC:RANG 1"); + + // 3. 设置分辨率为 FAST(高速模式) + await SendCommandAsync("VOLT:DC:RES FAST"); + + // 4. 触发源设为 BUS(软件触发) + await SendCommandAsync("TRIG:SOUR BUS"); + + // 5. 关闭自动延迟,延迟设为 0 + await SendCommandAsync("TRIG:DEL:AUTO OFF"); + await SendCommandAsync("TRIG:DEL 0"); } + // ========== 方案一:READ? 一次性批量采集(推荐,稳定可靠) ========== /// - /// 预置采样点数并进入等待触发状态 + /// 批量读取指定数量的电压值(使用 READ? 命令) /// + /// 采样点数,如 400 + /// 电压值数组(单位:伏特) + public async Task ReadBatchAsync(int sampleCount) + { + // 设置采样点数 + await SendCommandAsync($"SAMP:COUN {sampleCount}"); + + // 发送 READ?,仪器将自动完成触发、采集并返回所有结果 + string response = await QueryAsync("READ?"); + + // 解析逗号分隔的数值 + return ParseResponse(response, sampleCount); + } + + // ========== 方案二:INIT + *TRG + FETCh? 流程(保留,以备特殊场景) ========== public async Task PrepareBatchAsync(int sampleCount) { - await SendCommandAsync($"SAMPle:COUNt {sampleCount}"); - await SendCommandAsync("INITiate"); + await SendCommandAsync($"SAMP:COUN {sampleCount}"); + await SendCommandAsync("INIT"); } - /// - /// 发送软件触发信号 - /// public async Task TriggerAsync() { await SendCommandAsync("*TRG"); } - /// - /// 获取批量采集结果 - /// public async Task FetchBatchAsync() { string response = await QueryAsync("FETCh?"); - if (string.IsNullOrWhiteSpace(response)) - throw new Exception("FETCh? 返回空响应"); + return ParseResponse(response, null); // 不检查数量,直接解析 + } + + // ========== 辅助方法 ========== + private double[] ParseResponse(string response, int? expectedCount = null) + { + if (string.IsNullOrWhiteSpace(response)) + throw new Exception("返回数据为空"); - // 文档示例返回值如 "+1.234500E-02,+2.345600E-02" string[] parts = response.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); double[] values = new double[parts.Length]; for (int i = 0; i < parts.Length; i++) { - string valStr = parts[i].Trim(); - if (!double.TryParse(valStr, System.Globalization.NumberStyles.Float, - System.Globalization.CultureInfo.InvariantCulture, out values[i])) + if (!double.TryParse(parts[i].Trim(), + System.Globalization.NumberStyles.Float, + System.Globalization.CultureInfo.InvariantCulture, + out values[i])) { - throw new Exception($"解析失败: {valStr},原始响应: {response}"); + throw new Exception($"解析失败: {parts[i]},原始响应: {response}"); } } + + if (expectedCount.HasValue && values.Length != expectedCount.Value) + System.Diagnostics.Debug.WriteLine($"警告:实际点数 {values.Length},期望 {expectedCount.Value}"); + return values; } - /// - /// 单次读取电压 - /// + // ========== 单次读取(保留兼容) ========== public async Task ReadVoltageAsync() { - string resp = await QueryAsync("MEASure:VOLTage:DC?"); - if (double.TryParse(resp, System.Globalization.NumberStyles.Float, - System.Globalization.CultureInfo.InvariantCulture, out double value)) + 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}"); } diff --git a/Services/LanScpiSocket.cs b/Services/LanScpiSocket.cs index 5bbab30..778333b 100644 --- a/Services/LanScpiSocket.cs +++ b/Services/LanScpiSocket.cs @@ -19,6 +19,9 @@ namespace ASTM_D7896_Tester.Services public int ConnectTimeoutMs { get; set; } = 3000; public int ReadWriteTimeoutMs { get; set; } = 5000; + // 默认电压量程(伏特),可根据实际信号修改,例如 0.1, 1, 10, 100, 1000 + public double DefaultVoltageRange { get; set; } = 1.0; + public async Task ConnectAsync(string ipAddress, int port = 45454) { if (_tcpClient != null && _tcpClient.Connected) @@ -37,6 +40,9 @@ namespace ASTM_D7896_Tester.Services _stream.WriteTimeout = ReadWriteTimeoutMs; } + /// + /// 发送命令并等待完整响应(支持多行/大数据量) + /// public async Task QueryAsync(string command) { EnsureConnected(); @@ -44,19 +50,30 @@ namespace ASTM_D7896_Tester.Services await _stream.WriteAsync(cmdBytes, 0, cmdBytes.Length); var responseBuilder = new StringBuilder(); - byte[] buffer = new byte[4096]; + byte[] buffer = new byte[65536]; int bytesRead; - while (true) + 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); - if (chunk.Contains("\n")) break; + // 如果收到换行符,再等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(); @@ -65,60 +82,76 @@ namespace ASTM_D7896_Tester.Services } /// - /// 配置为高速直流电压测量(外部触发模式) + /// 配置为高速直流电压测量(BUS触发模式,固定量程,0.02PLC,关闭自归零) /// public async Task ConfigureForHighSpeedDcvAsync() { - await SendCommandAsync("CONF:VOLT:DC AUTO"); + // 1. 重置到默认状态(可选) + await SendCommandAsync("*RST"); + await Task.Delay(100); + + // 2. 固定量程(避免自动量程降低速度) + await SendCommandAsync($"VOLT:DC:RANG {DefaultVoltageRange}"); + + // 3. 设置积分时间 0.02PLC(最快速度) await SendCommandAsync("VOLT:DC:NPLC 0.02"); + + // 4. 关闭自动归零(提高速度) await SendCommandAsync("VOLT:DC:ZERO:AUTO OFF"); - await SendCommandAsync("TRIG:SOUR EXT"); + + // 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"); // 等待外部触发 + await SendCommandAsync("INIT"); // 进入等待触发状态 } /// - /// 发送触发信号(软件触发,用于测试,但外部触发模式不使用) - /// 此方法仅用于 BUS 触发模式,当前配置为 EXT 触发,实际触发由硬件信号完成。 - /// 保留此方法以备将来切换触发模式。 + /// 发送软件触发信号(*TRG),开始采集 /// public async Task TriggerAsync() { - // 当前为外部触发模式,不需要软件触发;若需软件触发,请先执行 TRIG:SOUR BUS - // 保留空实现或抛出 NotSupportedException,根据需求选择 - // 为避免编译错误,提供一个空实现 - await Task.CompletedTask; + await SendCommandAsync("*TRG"); } /// - /// 批量采集(假设已经配置并等待外部触发完成) - /// - public async Task AcquireBatchAsync(int sampleCount) - { - await SendCommandAsync($"SAMP:COUN {sampleCount}"); - await SendCommandAsync("INIT"); - int waitMs = (int)(sampleCount / 1000.0 * 1000) + 200; - await Task.Delay(waitMs); - string response = await QueryAsync("FETCh?"); - return ParseResponse(response, sampleCount); - } - - /// - /// 获取批量采集结果(在 PrepareBatchAsync 和外部触发之后调用) + /// 获取批量采集结果(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++) @@ -130,21 +163,9 @@ namespace ASTM_D7896_Tester.Services return values; } - private double[] ParseResponse(string response, int expectedCount) - { - 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 != expectedCount) - throw new Exception($"期望 {expectedCount} 个点,实际收到 {values.Length}"); - return values; - } - + /// + /// 单次读取电压(保留,但不推荐用于高速采集) + /// public async Task ReadVoltageAsync() { string resp = await QueryAsync("MEAS:VOLT:DC?"); diff --git a/ViewModels/D7896ViewModel.cs b/ViewModels/D7896ViewModel.cs index 68dab75..7a9754d 100644 --- a/ViewModels/D7896ViewModel.cs +++ b/ViewModels/D7896ViewModel.cs @@ -239,7 +239,7 @@ public partial class D7896ViewModel : ObservableObject await Task.WhenAll(_th1963Ustd.TriggerAsync(), _fiveHalfUpt.TriggerAsync()); // 等待加热脉冲持续1秒 - await Task.Delay(1000); + await Task.Delay(1); // 停止加热 await _plcService.WriteCoilAsync(_config.PlcRegisterAddresses.StartCommand, false); @@ -250,6 +250,8 @@ public partial class D7896ViewModel : ObservableObject // 获取采集数据 double[] ustd = await _th1963Ustd.FetchBatchAsync(); double[] upt = await _fiveHalfUpt.FetchBatchAsync(); + StandardResistorVoltage = ustd.Average(); + // 构建时间数组(假设采样时间正好1秒,采样点数 = ustd.Length) double[] timeArray = new double[ustd.Length]; diff --git a/Views/D7896View.xaml b/Views/D7896View.xaml index 914c3f2..cdc334d 100644 --- a/Views/D7896View.xaml +++ b/Views/D7896View.xaml @@ -163,6 +163,13 @@ + + + + + + +