2026-05-20 19:46:52 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.IO.Ports;
|
|
|
|
|
|
using System.Threading.Tasks;
|
2026-05-23 21:18:46 +08:00
|
|
|
|
using System.Linq;
|
2026-05-20 19:46:52 +08:00
|
|
|
|
|
|
|
|
|
|
namespace ASTM_D7896_Tester.Services
|
|
|
|
|
|
{
|
|
|
|
|
|
public class FiveHalfDmmService : IDisposable
|
|
|
|
|
|
{
|
|
|
|
|
|
private SerialPort _serialPort;
|
|
|
|
|
|
private bool _disposed;
|
|
|
|
|
|
|
|
|
|
|
|
public FiveHalfDmmService(string portName, int baudRate = 115200)
|
|
|
|
|
|
{
|
|
|
|
|
|
_serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
|
2026-05-24 10:36:57 +08:00
|
|
|
|
_serialPort.ReadTimeout = 5000; // 增加超时,批量数据可能需要更长时间
|
2026-05-20 19:46:52 +08:00
|
|
|
|
_serialPort.WriteTimeout = 1000;
|
2026-05-24 10:36:57 +08:00
|
|
|
|
_serialPort.NewLine = "\n"; // 结束符为 LF
|
2026-05-20 19:46:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Open()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_serialPort.IsOpen)
|
|
|
|
|
|
_serialPort.Open();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Close()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_serialPort.IsOpen)
|
|
|
|
|
|
_serialPort.Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsOpen => _serialPort?.IsOpen == true;
|
|
|
|
|
|
|
2026-05-24 10:36:57 +08:00
|
|
|
|
// ========== 核心通信方法 ==========
|
2026-05-20 19:46:52 +08:00
|
|
|
|
private async Task<string> QueryAsync(string command)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开");
|
2026-05-23 21:18:46 +08:00
|
|
|
|
return await Task.Run(() =>
|
2026-05-20 19:46:52 +08:00
|
|
|
|
{
|
2026-05-23 21:18:46 +08:00
|
|
|
|
lock (_serialPort)
|
2026-05-20 19:46:52 +08:00
|
|
|
|
{
|
2026-05-23 21:18:46 +08:00
|
|
|
|
_serialPort.DiscardInBuffer();
|
|
|
|
|
|
_serialPort.DiscardOutBuffer();
|
2026-05-20 19:46:52 +08:00
|
|
|
|
_serialPort.WriteLine(command);
|
2026-05-23 21:18:46 +08:00
|
|
|
|
System.Diagnostics.Debug.WriteLine($"[发送] {command}");
|
|
|
|
|
|
|
2026-05-24 10:36:57 +08:00
|
|
|
|
// 读取直到遇到换行符(批量数据可能很长,但 ReadLine 会一直读到 \n)
|
2026-05-23 21:18:46 +08:00
|
|
|
|
string response = _serialPort.ReadLine();
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"[接收] {response}");
|
|
|
|
|
|
return response.Trim();
|
2026-05-20 19:46:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task SendCommandAsync(string command)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_serialPort.IsOpen) throw new InvalidOperationException("串口未打开");
|
2026-05-23 21:18:46 +08:00
|
|
|
|
await Task.Run(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
lock (_serialPort)
|
|
|
|
|
|
{
|
|
|
|
|
|
_serialPort.WriteLine(command);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-05-20 19:46:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-24 10:36:57 +08:00
|
|
|
|
// ========== 配置高速直流电压测量(基于8255文档) ==========
|
2026-05-20 19:46:52 +08:00
|
|
|
|
public async Task ConfigureHighSpeedDcvAsync()
|
|
|
|
|
|
{
|
2026-05-24 10:36:57 +08:00
|
|
|
|
// 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");
|
2026-05-20 19:46:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-24 10:36:57 +08:00
|
|
|
|
// ========== 方案一:READ? 一次性批量采集(推荐,稳定可靠) ==========
|
2026-05-20 19:46:52 +08:00
|
|
|
|
/// <summary>
|
2026-05-24 10:36:57 +08:00
|
|
|
|
/// 批量读取指定数量的电压值(使用 READ? 命令)
|
2026-05-20 19:46:52 +08:00
|
|
|
|
/// </summary>
|
2026-05-24 10:36:57 +08:00
|
|
|
|
/// <param name="sampleCount">采样点数,如 400</param>
|
|
|
|
|
|
/// <returns>电压值数组(单位:伏特)</returns>
|
|
|
|
|
|
public async Task<double[]> ReadBatchAsync(int sampleCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 设置采样点数
|
|
|
|
|
|
await SendCommandAsync($"SAMP:COUN {sampleCount}");
|
|
|
|
|
|
|
|
|
|
|
|
// 发送 READ?,仪器将自动完成触发、采集并返回所有结果
|
|
|
|
|
|
string response = await QueryAsync("READ?");
|
|
|
|
|
|
|
|
|
|
|
|
// 解析逗号分隔的数值
|
|
|
|
|
|
return ParseResponse(response, sampleCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 方案二:INIT + *TRG + FETCh? 流程(保留,以备特殊场景) ==========
|
2026-05-20 19:46:52 +08:00
|
|
|
|
public async Task PrepareBatchAsync(int sampleCount)
|
|
|
|
|
|
{
|
2026-05-24 10:36:57 +08:00
|
|
|
|
await SendCommandAsync($"SAMP:COUN {sampleCount}");
|
|
|
|
|
|
await SendCommandAsync("INIT");
|
2026-05-20 19:46:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task TriggerAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
await SendCommandAsync("*TRG");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<double[]> FetchBatchAsync()
|
|
|
|
|
|
{
|
|
|
|
|
|
string response = await QueryAsync("FETCh?");
|
2026-05-24 10:36:57 +08:00
|
|
|
|
return ParseResponse(response, null); // 不检查数量,直接解析
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 辅助方法 ==========
|
|
|
|
|
|
private double[] ParseResponse(string response, int? expectedCount = null)
|
|
|
|
|
|
{
|
2026-05-23 21:18:46 +08:00
|
|
|
|
if (string.IsNullOrWhiteSpace(response))
|
2026-05-24 10:36:57 +08:00
|
|
|
|
throw new Exception("返回数据为空");
|
2026-05-23 21:18:46 +08:00
|
|
|
|
|
2026-05-20 19:46:52 +08:00
|
|
|
|
string[] parts = response.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
|
double[] values = new double[parts.Length];
|
|
|
|
|
|
for (int i = 0; i < parts.Length; i++)
|
|
|
|
|
|
{
|
2026-05-24 10:36:57 +08:00
|
|
|
|
if (!double.TryParse(parts[i].Trim(),
|
|
|
|
|
|
System.Globalization.NumberStyles.Float,
|
|
|
|
|
|
System.Globalization.CultureInfo.InvariantCulture,
|
|
|
|
|
|
out values[i]))
|
2026-05-23 21:18:46 +08:00
|
|
|
|
{
|
2026-05-24 10:36:57 +08:00
|
|
|
|
throw new Exception($"解析失败: {parts[i]},原始响应: {response}");
|
2026-05-23 21:18:46 +08:00
|
|
|
|
}
|
2026-05-20 19:46:52 +08:00
|
|
|
|
}
|
2026-05-24 10:36:57 +08:00
|
|
|
|
|
|
|
|
|
|
if (expectedCount.HasValue && values.Length != expectedCount.Value)
|
|
|
|
|
|
System.Diagnostics.Debug.WriteLine($"警告:实际点数 {values.Length},期望 {expectedCount.Value}");
|
|
|
|
|
|
|
2026-05-20 19:46:52 +08:00
|
|
|
|
return values;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-24 10:36:57 +08:00
|
|
|
|
// ========== 单次读取(保留兼容) ==========
|
2026-05-20 19:46:52 +08:00
|
|
|
|
public async Task<double> ReadVoltageAsync()
|
|
|
|
|
|
{
|
2026-05-24 10:36:57 +08:00
|
|
|
|
string resp = await QueryAsync("MEAS:VOLT:DC?");
|
|
|
|
|
|
if (double.TryParse(resp,
|
|
|
|
|
|
System.Globalization.NumberStyles.Float,
|
|
|
|
|
|
System.Globalization.CultureInfo.InvariantCulture,
|
|
|
|
|
|
out double value))
|
2026-05-20 19:46:52 +08:00
|
|
|
|
return value;
|
|
|
|
|
|
throw new Exception($"无效响应: {resp}");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_disposed)
|
|
|
|
|
|
{
|
|
|
|
|
|
Close();
|
|
|
|
|
|
_serialPort?.Dispose();
|
|
|
|
|
|
_disposed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|