示波器修改

This commit is contained in:
2026-09-16 14:36:28 +08:00
parent 96680fd4c6
commit 0050cb341b
3 changed files with 512 additions and 302 deletions
@@ -2,7 +2,7 @@ using DeviceCommand.Devices;
using Prism.Commands;
using Prism.Ioc;
using System;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;
@@ -60,30 +60,45 @@ namespace DeviceEditModule.ViewModels
set => SetProperty(ref _channel, value);
}
private string _channelUnit = "V";
/// <summary>通道物理单位(V 电压 / A 电流)。</summary>
public string ChannelUnit
{
get => _channelUnit;
set => SetProperty(ref _channelUnit, value);
}
public string[] ChannelUnitOptions { get; } = { "V", "A" };
private double _voltsPerDiv = 1.0;
/// <summary>垂直电压档位(V/div)。</summary>
/// <summary>垂直档位(V/div 或 A/div)。</summary>
public double VoltsPerDiv
{
get => _voltsPerDiv;
set => SetProperty(ref _voltsPerDiv, value);
}
private double _offset = 0.0;
/// <summary>垂直偏移(V)。</summary>
public double Offset
private ChannelCoupling _selectedCoupling = ChannelCoupling.DC;
/// <summary>通道耦合方式(DC/AC/GND,测纹波需 AC)。</summary>
public ChannelCoupling SelectedCoupling
{
get => _offset;
set => SetProperty(ref _offset, value);
get => _selectedCoupling;
set => SetProperty(ref _selectedCoupling, value);
}
private bool _is50Ohm;
/// <summary>是否使用 50Ω 输入阻抗,false 为 1MΩ。</summary>
public bool Is50Ohm
public ChannelCoupling[] CouplingOptions { get; } =
(ChannelCoupling[])Enum.GetValues(typeof(ChannelCoupling));
private string _bandwidthLimit = "20M";
/// <summary>通道带宽限制(20M/200M/FULL,测纹波常用 20M)。</summary>
public string BandwidthLimit
{
get => _is50Ohm;
set => SetProperty(ref _is50Ohm, value);
get => _bandwidthLimit;
set => SetProperty(ref _bandwidthLimit, value);
}
public string[] BandwidthLimitOptions { get; } = { "20M", "200M", "FULL" };
private double _timeBase = 0.001;
/// <summary>水平时基档位(s/div)。</summary>
public double TimeBase
@@ -92,20 +107,66 @@ namespace DeviceEditModule.ViewModels
set => SetProperty(ref _timeBase, value);
}
private TriggerMode _selectedTriggerMode = TriggerMode.AUTO;
/// <summary>触发模式(AUTO/NORMal/SINGle)。</summary>
public TriggerMode SelectedTriggerMode
{
get => _selectedTriggerMode;
set => SetProperty(ref _selectedTriggerMode, value);
}
public TriggerMode[] TriggerModeOptions { get; } =
(TriggerMode[])Enum.GetValues(typeof(TriggerMode));
private string _triggerSource = "C1";
/// <summary>边沿触发源(C1~C4/EX/LINE)。</summary>
public string TriggerSource
{
get => _triggerSource;
set => SetProperty(ref _triggerSource, value);
}
private double _triggerLevel = 0.0;
/// <summary>触发电平(V)。</summary>
/// <summary>边沿触发电平(V)。</summary>
public double TriggerLevel
{
get => _triggerLevel;
set => SetProperty(ref _triggerLevel, value);
}
private string _triggerSource = "C1";
/// <summary>触发源(C1/C2/C3/C4/EX/LINE)。</summary>
public string TriggerSource
private TriggerSlope _selectedTriggerSlope = TriggerSlope.RISing;
/// <summary>边沿触发斜率(RISing/FALLing/ALTernate,测泄放时间用 FALLing)。</summary>
public TriggerSlope SelectedTriggerSlope
{
get => _triggerSource;
set => SetProperty(ref _triggerSource, value);
get => _selectedTriggerSlope;
set => SetProperty(ref _selectedTriggerSlope, value);
}
public TriggerSlope[] TriggerSlopeOptions { get; } =
(TriggerSlope[])Enum.GetValues(typeof(TriggerSlope));
private double _cursorX1 = 0.0;
/// <summary>X1 光标位置(s)。</summary>
public double CursorX1
{
get => _cursorX1;
set => SetProperty(ref _cursorX1, value);
}
private double _cursorX2 = 0.001;
/// <summary>X2 光标位置(s)。</summary>
public double CursorX2
{
get => _cursorX2;
set => SetProperty(ref _cursorX2, value);
}
private string _saveFilePath = Path.Combine(AppContext.BaseDirectory, "Screenshots", "SDS2000X_HD.png");
/// <summary>屏幕截图保存路径(保存时自动追加时间戳)。</summary>
public string SaveFilePath
{
get => _saveFilePath;
set => SetProperty(ref _saveFilePath, value);
}
#endregion
@@ -133,6 +194,14 @@ namespace DeviceEditModule.ViewModels
set => SetProperty(ref _measuredRms, value);
}
private double _cursorDeltaT;
/// <summary>X1/X2 光标时间差 ΔTs)。</summary>
public double CursorDeltaT
{
get => _cursorDeltaT;
set => SetProperty(ref _cursorDeltaT, value);
}
private string _responseLog = string.Empty;
/// <summary>命令响应日志(最新消息在顶部)。</summary>
public string ResponseLog
@@ -145,91 +214,123 @@ namespace DeviceEditModule.ViewModels
#region
// IEEE 488.2 公共命令
public ICommand QueryIdentityCommand { get; }
public ICommand ResetDeviceCommand { get; }
public ICommand ClearStatusCommand { get; }
public ICommand QueryErrorCommand { get; }
public ICommand QueryOpcCommand { get; }
// 运行与捕获控制
public ICommand RunCommand { get; }
public ICommand StopCommand { get; }
public ICommand SingleCommand { get; }
public ICommand ForceTriggerCommand { get; }
public ICommand SetTriggerModeCommand { get; }
// 通道垂直控制
public ICommand SetChannelOnCommand { get; }
public ICommand SetChannelOffCommand { get; }
public ICommand SetChannelUnitCommand { get; }
public ICommand SetVoltsDivCommand { get; }
public ICommand SetOffsetCommand { get; }
// 🛠️ 补全:设置阻抗的 Command
public ICommand SetImpedance50Command { get; }
public ICommand SetImpedance1MCommand { get; }
public ICommand SetChannelCouplingCommand { get; }
public ICommand SetBandwidthLimitCommand { get; }
// 水平与触发控制
public ICommand SetTimeBaseCommand { get; }
public ICommand SetTriggerLevelCommand { get; }
public ICommand SetTriggerSourceCommand { get; }
public ICommand SetTriggerLevelCommand { get; }
public ICommand SetTriggerSlopeCommand { get; }
// 光标测量
public ICommand EnableManualXCursorCommand { get; }
public ICommand SetCursorX1Command { get; }
public ICommand SetCursorX2Command { get; }
public ICommand QueryCursorDeltaTCommand { get; }
// 基本测量
public ICommand QueryMeasurementsCommand { get; }
public ICommand QueryVppCommand { get; }
public ICommand QueryFrequencyCommand { get; }
public ICommand QueryRmsCommand { get; }
// 屏幕截图
public ICommand SaveScreenshotCommand { get; }
#endregion
public SDS2000X_HDViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_deviceManager = containerProvider.Resolve<DeviceManager>();
// IEEE 488.2 公共命令
QueryIdentityCommand = new DelegateCommand(async () => await Exec(async () => AppendLog("IDN: " + await _device!.(Ct()))));
ResetDeviceCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("设备已重置"); }));
RunCommand = new DelegateCommand(async () => await Exec(async () => { await _device!._RUN(Ct()); AppendLog("已开始捕获"); }));
StopCommand = new DelegateCommand(async () => await Exec(async () => { await _device!._STOP(Ct()); AppendLog("已停止捕获"); }));
SingleCommand = new DelegateCommand(async () => await Exec(async () => { await _device!._SINGLE(Ct()); AppendLog("已触发单次捕获"); }));
ForceTriggerCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("已强制触发"); }));
ResetDeviceCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("设备已重置 (*RST)"); }));
ClearStatusCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Ct()); AppendLog("状态寄存器与错误队列已清除 (*CLS)"); }));
QueryErrorCommand = new DelegateCommand(async () => await Exec(async () => AppendLog("SYST:ERR? → " + await _device!.(Ct()))));
QueryOpcCommand = new DelegateCommand(async () => await Exec(async () => AppendLog("操作完成 (*OPC?): " + (await _device!._OPC(Ct()) ? "是" : "否"))));
// 运行与捕获控制
RunCommand = new DelegateCommand(async () => await Exec(async () => { await _device!._RUN(Ct()); AppendLog("已开始连续捕获"); }));
StopCommand = new DelegateCommand(async () => await Exec(async () => { await _device!._STOP(Ct()); AppendLog("已停止捕获,画面定格"); }));
SetTriggerModeCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(SelectedTriggerMode, Ct()); AppendLog($"触发模式已设为 {SelectedTriggerMode}"); }));
// 通道垂直控制
SetChannelOnCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Channel, true, Ct()); AppendLog($"通道 {Channel} 已开启"); }));
SetChannelOffCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Channel, false, Ct()); AppendLog($"通道 {Channel} 已关闭"); }));
SetVoltsDivCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Channel, VoltsPerDiv, Ct()); AppendLog($"C{Channel} 电压档位已设为 {VoltsPerDiv} V/div"); }));
SetOffsetCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Channel, Offset, Ct()); AppendLog($"C{Channel} 垂直偏移已设为 {Offset} V"); }));
// 🛠️ 绑定:设置 50Ω 和 1MΩ 阻抗控制
SetImpedance50Command = new DelegateCommand(async () => await Exec(async () =>
{
await _device!.(Channel, ImpedanceType.FIFty, Ct());
Is50Ohm = true;
AppendLog($"C{Channel} 输入阻抗已设为 50Ω");
}));
SetImpedance1MCommand = new DelegateCommand(async () => await Exec(async () =>
{
await _device!.(Channel, ImpedanceType.ONEM, Ct());
Is50Ohm = false;
AppendLog($"C{Channel} 输入阻抗已设为 1MΩ");
}));
SetChannelUnitCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Channel, ChannelUnit, Ct()); AppendLog($"C{Channel} 位已设为 {ChannelUnit}"); }));
SetVoltsDivCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Channel, VoltsPerDiv, Ct()); AppendLog($"C{Channel} 垂直档位已设为 {VoltsPerDiv} {ChannelUnit}/div"); }));
SetChannelCouplingCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Channel, SelectedCoupling, Ct()); AppendLog($"C{Channel} 耦合方式已设为 {SelectedCoupling}"); }));
SetBandwidthLimitCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(Channel, BandwidthLimit, Ct()); AppendLog($"C{Channel} 带宽限制已设为 {BandwidthLimit}"); }));
// 水平与触发控制
SetTimeBaseCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(TimeBase, Ct()); AppendLog($"水平时基已设为 {TimeBase} s/div"); }));
SetTriggerLevelCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(TriggerLevel, Ct()); AppendLog($"触发电平已设为 {TriggerLevel} V"); }));
SetTriggerSourceCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.(TriggerSource, Ct()); AppendLog($"触发源已设为 {TriggerSource}"); }));
SetTriggerSourceCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.沿(TriggerSource, Ct()); AppendLog($"边沿触发源已设为 {TriggerSource}"); }));
SetTriggerLevelCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.沿(TriggerLevel, Ct()); AppendLog($"边沿触发电平已设为 {TriggerLevel} V"); }));
SetTriggerSlopeCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.沿(SelectedTriggerSlope, Ct()); AppendLog($"边沿触发斜率已设为 {SelectedTriggerSlope}"); }));
QueryMeasurementsCommand = new DelegateCommand(async () => await Exec(async () =>
// 光标测量
EnableManualXCursorCommand = new DelegateCommand(async () => await Exec(async () => { await _device!.X光标(Ct()); AppendLog("已开启手动 X 轴光标"); }));
SetCursorX1Command = new DelegateCommand(async () => await Exec(async () => { await _device!.X1位置(CursorX1, Ct()); AppendLog($"X1 光标已设为 {CursorX1} s"); }));
SetCursorX2Command = new DelegateCommand(async () => await Exec(async () => { await _device!.X2位置(CursorX2, Ct()); AppendLog($"X2 光标已设为 {CursorX2} s"); }));
QueryCursorDeltaTCommand = new DelegateCommand(async () => await Exec(async () =>
{
// 设备层已自动提取纯数值并返回 double
MeasuredVpp = await _device!.(Channel, Ct());
MeasuredFrequency = await _device!.(Channel, Ct());
MeasuredRms = await _device!.(Channel, Ct());
AppendLog($"C{Channel} 测量结果 → Vpp:{MeasuredVpp:0.######}V Freq:{MeasuredFrequency:0.######}Hz RMS:{MeasuredRms:0.######}V");
CursorDeltaT = await _device!.X时间差(Ct());
AppendLog($"光标时间差 ΔT = {CursorDeltaT:0.######E+0} s");
}));
// 基本测量(查询前先将测量源切换到当前通道)
QueryVppCommand = new DelegateCommand(async () => await Exec(async () =>
{
MeasuredVpp = await _device!.(Channel, Ct());
AppendLog($"C{Channel} Vpp: {MeasuredVpp:0.######} V");
MeasuredVpp = await QuerySimpleValueAsync("PKPK");
AppendLog($"C{Channel} Vpp: {MeasuredVpp:0.######} {ChannelUnit}");
}));
QueryFrequencyCommand = new DelegateCommand(async () => await Exec(async () =>
{
MeasuredFrequency = await _device!.(Channel, Ct());
MeasuredFrequency = await QuerySimpleValueAsync("FREQ");
AppendLog($"C{Channel} Freq: {MeasuredFrequency:0.######} Hz");
}));
QueryRmsCommand = new DelegateCommand(async () => await Exec(async () =>
{
MeasuredRms = await _device!.(Channel, Ct());
AppendLog($"C{Channel} RMS: {MeasuredRms:0.######} V");
MeasuredRms = await QuerySimpleValueAsync("RMS");
AppendLog($"C{Channel} RMS: {MeasuredRms:0.######} {ChannelUnit}");
}));
QueryMeasurementsCommand = new DelegateCommand(async () => await Exec(async () =>
{
await _device!.(Channel, Ct());
MeasuredVpp = await _device.("PKPK", Ct());
MeasuredFrequency = await _device.("FREQ", Ct());
MeasuredRms = await _device.("RMS", Ct());
AppendLog($"C{Channel} 测量结果 → Vpp:{MeasuredVpp:0.######}{ChannelUnit} Freq:{MeasuredFrequency:0.######}Hz RMS:{MeasuredRms:0.######}{ChannelUnit}");
}));
// 屏幕截图
SaveScreenshotCommand = new DelegateCommand(async () => await Exec(async () =>
{
bool ok = await _device!.(SaveFilePath, Ct());
AppendLog(ok ? $"截图已保存:{SaveFilePath}" : "截图获取或保存失败,请检查连接与保存路径。");
}));
Initialize();
@@ -283,6 +384,17 @@ namespace DeviceEditModule.ViewModels
private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
/// <summary>
/// 将基本测量源切换到当前通道后,查询指定测量项的实时数值。
/// </summary>
/// <param name="type">测量参数名称(如: "PKPK"、"FREQ"、"RMS"</param>
/// <returns>测量项提取到的实时数值</returns>
private async Task<double> QuerySimpleValueAsync(string type)
{
await _device!.(Channel, Ct());
return await _device.(type, Ct());
}
private async Task Exec(Func<Task> action)
{
if (_device == null)
@@ -327,4 +439,4 @@ namespace DeviceEditModule.ViewModels
_cts?.Dispose();
}
}
}
}