using DeviceCommand.Device; using Prism.Commands; using Prism.Ioc; using System; using System.Globalization; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using UIShare.GlobalVariable; using UIShare.ViewModelBase; using static DeviceCommand.Device.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 double _voltsPerDiv = 1.0; /// 垂直电压档位(V/div)。 public double VoltsPerDiv { get => _voltsPerDiv; set => SetProperty(ref _voltsPerDiv, value); } private double _offset = 0.0; /// 垂直偏移(V)。 public double Offset { get => _offset; set => SetProperty(ref _offset, value); } private bool _is50Ohm; /// 是否使用 50Ω 输入阻抗,false 为 1MΩ。 public bool Is50Ohm { get => _is50Ohm; set => SetProperty(ref _is50Ohm, value); } private double _timeBase = 0.001; /// 水平时基档位(s/div)。 public double TimeBase { get => _timeBase; set => SetProperty(ref _timeBase, value); } private double _triggerLevel = 0.0; /// 触发电平(V)。 public double TriggerLevel { get => _triggerLevel; set => SetProperty(ref _triggerLevel, value); } private string _triggerSource = "C1"; /// 触发源(C1/C2/C3/C4/EX/LINE)。 public string TriggerSource { get => _triggerSource; set => SetProperty(ref _triggerSource, value); } #endregion #region 测量结果属性 private string _measuredVpp = "—"; public string MeasuredVpp { get => _measuredVpp; set => SetProperty(ref _measuredVpp, value); } private string _measuredFrequency = "—"; public string MeasuredFrequency { get => _measuredFrequency; set => SetProperty(ref _measuredFrequency, value); } private string _measuredRms = "—"; public string MeasuredRms { get => _measuredRms; set => SetProperty(ref _measuredRms, value); } private string _responseLog = string.Empty; /// 命令响应日志(最新消息在顶部)。 public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); } #endregion #region 命令 public ICommand QueryIdentityCommand { get; } public ICommand ResetDeviceCommand { get; } public ICommand RunCommand { get; } public ICommand StopCommand { get; } public ICommand SingleCommand { get; } public ICommand ForceTriggerCommand { get; } public ICommand SetChannelOnCommand { get; } public ICommand SetChannelOffCommand { get; } public ICommand SetVoltsDivCommand { get; } public ICommand SetOffsetCommand { get; } // 🛠️ 补全:设置阻抗的 Command public ICommand SetImpedance50Command { get; } public ICommand SetImpedance1MCommand { get; } public ICommand SetTimeBaseCommand { get; } public ICommand SetTriggerLevelCommand { get; } public ICommand SetTriggerSourceCommand { get; } public ICommand QueryMeasurementsCommand { get; } public ICommand QueryVppCommand { get; } public ICommand QueryFrequencyCommand { get; } public ICommand QueryRmsCommand { get; } #endregion public SDS2000X_HDViewModel(IContainerProvider containerProvider) : base(containerProvider) { _deviceManager = containerProvider.Resolve(); 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("已强制触发"); })); 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Ω"); })); 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}"); })); QueryMeasurementsCommand = new DelegateCommand(async () => await Exec(async () => { // 1. 保留设备原生吐出的完整原始字符串 string rawVpp = await _device!.查询实际电压峰峰值(Channel, Ct()); string rawFreq = await _device!.查询实际频率(Channel, Ct()); string rawRms = await _device!.查询实际电压均方根(Channel, Ct()); // 2. 清洗数据并将科学计数法转为常规小数后赋值给 UI 属性 MeasuredVpp = ParseMeasurement(rawVpp); MeasuredFrequency = ParseMeasurement(rawFreq); MeasuredRms = ParseMeasurement(rawRms); // 3. 在日志中同时体现设备原始报文与解析呈现值,极方便联调 AppendLog($"C{Channel} 测量原始数据 → Vpp:{rawVpp.Trim()} Freq:{rawFreq.Trim()} RMS:{rawRms.Trim()}"); AppendLog($"C{Channel} 界面呈现数值 → Vpp:{MeasuredVpp}V Freq:{MeasuredFrequency}Hz RMS:{MeasuredRms}V"); })); QueryVppCommand = new DelegateCommand(async () => await Exec(async () => { string raw = await _device!.查询实际电压峰峰值(Channel, Ct()); MeasuredVpp = ParseMeasurement(raw); AppendLog($"C{Channel} Vpp: {MeasuredVpp} (Raw: {raw.Trim()})"); })); QueryFrequencyCommand = new DelegateCommand(async () => await Exec(async () => { string raw = await _device!.查询实际频率(Channel, Ct()); MeasuredFrequency = ParseMeasurement(raw); AppendLog($"C{Channel} Freq: {MeasuredFrequency} (Raw: {raw.Trim()})"); })); QueryRmsCommand = new DelegateCommand(async () => await Exec(async () => { string raw = await _device!.查询实际电压均方根(Channel, Ct()); MeasuredRms = ParseMeasurement(raw); AppendLog($"C{Channel} RMS: {MeasuredRms} (Raw: {raw.Trim()})"); })); 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; 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; } /// /// 清洗示波器原始返回的测量字符串(例如 "C1:PAVA RMS,5.57E-03V")并安全转化为无科学计数法的小数形式。 /// /// 设备原始应答数据 /// 可直接绑定到 UI 呈现的字符串数字 private string ParseMeasurement(string rawResponse) { if (string.IsNullOrWhiteSpace(rawResponse)) return "—"; try { string cleanData = rawResponse.Trim(); // 1. 斩断报头,提取逗号后面的具体内容(如 "5.57E-03V" 或 "****") int commaIndex = cleanData.IndexOf(','); if (commaIndex == -1) return "—"; string valStr = cleanData.Substring(commaIndex + 1); var match = Regex.Match(valStr, @"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?"); if (match.Success) { valStr = match.Value; } // 3. 校验并拦截设备未测出时的无效星号 "****" if (valStr.Contains("*") || string.IsNullOrWhiteSpace(valStr)) { return "0"; // 回归为零或 "—",防止触发数据异常 } // 4. 解析科学计数法,并重新以不带科学计数法的小数样式展开 if (double.TryParse(valStr, NumberStyles.Any, CultureInfo.InvariantCulture, out double result)) { // "0.######" 样式会自动消除末尾无用的冗余零,并显示为普通小数(如 0.00557) return result.ToString("0.######", CultureInfo.InvariantCulture); } } catch { // 捕获可能产生的边缘转换故障,保证轮询线程绝不崩溃 } return "—"; } #endregion public void Dispose() { _cts?.Cancel(); _cts?.Dispose(); } } }