using DeviceCommand.Devices; using Prism.Commands; using Prism.Ioc; using System; using System.IO; using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using UIShare.GlobalVariable; using UIShare.ViewModelBase; using static DeviceCommand.Devices.SDS2000X_HD; namespace DeviceEditModule.ViewModels { /// /// SDS2000X_HD 数字存储示波器控制面板 ViewModel。 /// public class SDS2000X_HDViewModel : NavigateViewModelBase, IDisposable { #region 私有字段 private readonly DeviceManager _deviceManager; private SDS2000X_HD? _device; private CancellationTokenSource? _cts; #endregion #region 设备信息属性 private string _deviceName = "SDS2000X_HD"; public string DeviceName { get => _deviceName; set => SetProperty(ref _deviceName, value); } private bool _isConnected; public bool IsConnected { get => _isConnected; set => SetProperty(ref _isConnected, value); } private bool _isBusy; public bool IsBusy { get => _isBusy; set => SetProperty(ref _isBusy, value); } #endregion #region 输入参数属性 private int _channel = 1; /// 当前操作通道(1~4)。 public int Channel { get => _channel; set => SetProperty(ref _channel, value); } private string _channelUnit = "V"; /// 通道物理单位(V 电压 / A 电流)。 public string ChannelUnit { get => _channelUnit; set => SetProperty(ref _channelUnit, value); } public string[] ChannelUnitOptions { get; } = { "V", "A" }; private double _voltsPerDiv = 1.0; /// 垂直档位(V/div 或 A/div)。 public double VoltsPerDiv { get => _voltsPerDiv; set => SetProperty(ref _voltsPerDiv, value); } private ChannelCoupling _selectedCoupling = ChannelCoupling.DC; /// 通道耦合方式(DC/AC/GND,测纹波需 AC)。 public ChannelCoupling SelectedCoupling { get => _selectedCoupling; set => SetProperty(ref _selectedCoupling, value); } public ChannelCoupling[] CouplingOptions { get; } = (ChannelCoupling[])Enum.GetValues(typeof(ChannelCoupling)); private string _bandwidthLimit = "20M"; /// 通道带宽限制(20M/200M/FULL,测纹波常用 20M)。 public string BandwidthLimit { get => _bandwidthLimit; set => SetProperty(ref _bandwidthLimit, value); } public string[] BandwidthLimitOptions { get; } = { "20M", "200M", "FULL" }; private double _timeBase = 0.001; /// 水平时基档位(s/div)。 public double TimeBase { get => _timeBase; set => SetProperty(ref _timeBase, value); } private TriggerMode _selectedTriggerMode = TriggerMode.AUTO; /// 触发模式(AUTO/NORMal/SINGle)。 public TriggerMode SelectedTriggerMode { get => _selectedTriggerMode; set => SetProperty(ref _selectedTriggerMode, value); } public TriggerMode[] TriggerModeOptions { get; } = (TriggerMode[])Enum.GetValues(typeof(TriggerMode)); private string _triggerSource = "C1"; /// 边沿触发源(C1~C4/EX/LINE)。 public string TriggerSource { get => _triggerSource; set => SetProperty(ref _triggerSource, value); } private double _triggerLevel = 0.0; /// 边沿触发电平(V)。 public double TriggerLevel { get => _triggerLevel; set => SetProperty(ref _triggerLevel, value); } private TriggerSlope _selectedTriggerSlope = TriggerSlope.RISing; /// 边沿触发斜率(RISing/FALLing/ALTernate,测泄放时间用 FALLing)。 public TriggerSlope SelectedTriggerSlope { get => _selectedTriggerSlope; set => SetProperty(ref _selectedTriggerSlope, value); } public TriggerSlope[] TriggerSlopeOptions { get; } = (TriggerSlope[])Enum.GetValues(typeof(TriggerSlope)); private double _cursorX1 = 0.0; /// X1 光标位置(s)。 public double CursorX1 { get => _cursorX1; set => SetProperty(ref _cursorX1, value); } private double _cursorX2 = 0.001; /// X2 光标位置(s)。 public double CursorX2 { get => _cursorX2; set => SetProperty(ref _cursorX2, value); } private string _saveFilePath = Path.Combine(AppContext.BaseDirectory, "Screenshots", "SDS2000X_HD.png"); /// 屏幕截图保存路径(保存时自动追加时间戳)。 public string SaveFilePath { get => _saveFilePath; set => SetProperty(ref _saveFilePath, value); } #endregion #region 测量结果属性 private double _measuredVpp; public double MeasuredVpp { get => _measuredVpp; set => SetProperty(ref _measuredVpp, value); } private double _measuredFrequency; public double MeasuredFrequency { get => _measuredFrequency; set => SetProperty(ref _measuredFrequency, value); } private double _measuredRms; public double MeasuredRms { get => _measuredRms; set => SetProperty(ref _measuredRms, value); } private double _cursorDeltaT; /// X1/X2 光标时间差 ΔT(s)。 public double CursorDeltaT { get => _cursorDeltaT; set => SetProperty(ref _cursorDeltaT, value); } private string _responseLog = string.Empty; /// 命令响应日志(最新消息在顶部)。 public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); } #endregion #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 SetTriggerModeCommand { get; } // 通道垂直控制 public ICommand SetChannelOnCommand { get; } public ICommand SetChannelOffCommand { get; } public ICommand SetChannelUnitCommand { get; } public ICommand SetVoltsDivCommand { get; } public ICommand SetChannelCouplingCommand { get; } public ICommand SetBandwidthLimitCommand { get; } // 水平与触发控制 public ICommand SetTimeBaseCommand { 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(); // 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("设备已重置 (*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} 已关闭"); })); 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"); })); 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}"); })); // 光标测量 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 () => { CursorDeltaT = await _device!.查询光标X时间差(Ct()); AppendLog($"光标时间差 ΔT = {CursorDeltaT:0.######E+0} s"); })); // 基本测量(查询前先将测量源切换到当前通道) QueryVppCommand = new DelegateCommand(async () => await Exec(async () => { MeasuredVpp = await QuerySimpleValueAsync("PKPK"); AppendLog($"C{Channel} Vpp: {MeasuredVpp:0.######} {ChannelUnit}"); })); QueryFrequencyCommand = new DelegateCommand(async () => await Exec(async () => { MeasuredFrequency = await QuerySimpleValueAsync("FREQ"); AppendLog($"C{Channel} Freq: {MeasuredFrequency:0.######} Hz"); })); QueryRmsCommand = new DelegateCommand(async () => await Exec(async () => { 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(); } #region 初始化 / Navigation public void Initialize(string? deviceName = null) { SDS2000X_HD? found = null; string? foundName = null; if (deviceName != null && _deviceManager.DeviceMap.TryGetValue(deviceName, out var d) && d is SDS2000X_HD s) { found = s; foundName = deviceName; } else { foreach (var kv in _deviceManager.DeviceMap) { if (kv.Value is SDS2000X_HD sds) { found = sds; foundName = kv.Key; break; } } } _device = found; DeviceName = foundName ?? "SDS2000X_HD (未找到)"; IsConnected = _device?.IsConnected ?? false; AppendLog(found != null ? $"已关联设备 [{DeviceName}],连接状态:{(IsConnected ? "已连接" : "未连接")}" : "未在 DeviceManager 中找到 SDS2000X_HD 设备,请先初始化设备配置。"); } public override void OnNavigatedTo(NavigationContext context) { var name = context.Parameters.GetValue("DeviceName"); Initialize(name); } #endregion #region 辅助 private CancellationToken Ct() => (_cts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token; /// /// 将基本测量源切换到当前通道后,查询指定测量项的实时数值。 /// /// 测量参数名称(如: "PKPK"、"FREQ"、"RMS") /// 测量项提取到的实时数值 private async Task QuerySimpleValueAsync(string type) { await _device!.设置基本测量源(Channel, Ct()); return await _device.查询基本测量值(type, Ct()); } private async Task Exec(Func action) { if (_device == null) { AppendLog("错误:未关联到设备实例,请检查设备配置。"); return; } if (IsBusy) return; IsBusy = true; try { await action(); IsConnected = _device.IsConnected; } catch (OperationCanceledException) { AppendLog("命令超时或已取消。"); } catch (Exception ex) { AppendLog($"错误:{ex.Message}"); } finally { IsBusy = false; } } private void AppendLog(string message) { var line = $"[{DateTime.Now:HH:mm:ss}] {message}"; ResponseLog = ResponseLog.Length > 4000 ? line + "\n" + ResponseLog[..3000] : line + "\n" + ResponseLog; } #endregion public void Dispose() { _cts?.Cancel(); _cts?.Dispose(); } } }