Files
ACP/PowerAdaptabilityModule/ViewModels/PowerAdaptabilityViewModel.cs
T
2026-09-22 11:33:07 +08:00

638 lines
30 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using DeviceCommand.Devices;
using OxyPlot;
using OxyPlot.Axes;
using OxyPlot.Legends;
using OxyPlot.Series;
using Prism.Commands;
using Prism.Ioc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using UIShare.GlobalVariable;
using UIShare.ViewModelBase;
namespace PowerAdaptabilityModule.ViewModels
{
/// <summary>
/// 电源适应性测试 ViewModel(Chroma61800 交流源一拖三):
/// 左侧为设备命令面板(三相输出设置 + LIST 模式),右侧为 A/B/C 三相实时波形图表。
/// </summary>
public class PowerAdaptabilityViewModel : NavigateViewModelBase, IDisposable
{
#region 常量
/// <summary>采样周期(毫秒)</summary>
private const int SampleIntervalMs = 1000;
/// <summary>合成正弦波的分段数(0~360°,每 2° 一个点,共 181 点)</summary>
private const int SinePoints = 180;
/// <summary>三相相位偏移(°):A=0,B=-120,C=+120</summary>
private static readonly double[] PhaseOffsetDeg = { 0, -120, 120 };
/// <summary>三相曲线配色:A 红、B 绿、C 蓝(三张图统一,便于对照)</summary>
private static readonly OxyColor[] PhaseColors =
{ OxyColor.FromRgb(0xE5, 0x39, 0x35), OxyColor.FromRgb(0x43, 0xA0, 0x47), OxyColor.FromRgb(0x1E, 0x88, 0xE5) };
#endregion
#region 私有字段
private readonly DeviceManager _dm;
private readonly GlobalInfo _globalInfo;
private Chroma61800? _dev;
private CancellationTokenSource? _cmdCts;
private CancellationTokenSource? _sampleCts;
// 三相正弦曲线:按物理量分组(电压 / 电流 / 功率),每组含 A/B/C 三相
private readonly LineSeries _voltA, _voltB, _voltC;
private readonly LineSeries _currA, _currB, _currC;
private readonly LineSeries _powA, _powB, _powC;
#endregion
#region 设备状态属性
private string _deviceName = "Chroma61800";
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); }
private bool _isSampling;
/// <summary>是否正在采集波形数据</summary>
public bool IsSampling { get => _isSampling; set => SetProperty(ref _isSampling, value); }
#endregion
#region 三相图表(按物理量分图:电压 / 电流 / 功率,每图叠加 A/B/C 三相正弦)
/// <summary>三相电压波形图表</summary>
public PlotModel VoltagePlot { get; }
/// <summary>三相电流波形图表</summary>
public PlotModel CurrentPlot { get; }
/// <summary>三相功率波形图表</summary>
public PlotModel PowerPlot { get; }
#endregion
#region 实时测量值
private double _voltageA; public double VoltageA { get => _voltageA; set => SetProperty(ref _voltageA, value); }
private double _currentA; public double CurrentA { get => _currentA; set => SetProperty(ref _currentA, value); }
private double _voltageB; public double VoltageB { get => _voltageB; set => SetProperty(ref _voltageB, value); }
private double _currentB; public double CurrentB { get => _currentB; set => SetProperty(ref _currentB, value); }
private double _voltageC; public double VoltageC { get => _voltageC; set => SetProperty(ref _voltageC, value); }
private double _currentC; public double CurrentC { get => _currentC; set => SetProperty(ref _currentC, value); }
private double _frequencyNow;
/// <summary>当前输出频率 (Hz)</summary>
public double FrequencyNow { get => _frequencyNow; set => SetProperty(ref _frequencyNow, value); }
private double _powerNow;
/// <summary>当前真实输出功率 (W)</summary>
public double PowerNow { get => _powerNow; set => SetProperty(ref _powerNow, value); }
#endregion
#region 输入参数(三相输出设置)
private double _voltage = 220;
/// <summary>待设置的交流电压 (V)</summary>
public double Voltage { get => _voltage; set => SetProperty(ref _voltage, value); }
private double _frequency = 50;
/// <summary>待设置的输出频率 (Hz)</summary>
public double Frequency { get => _frequency; set => SetProperty(ref _frequency, value); }
private string _waveform = "SINE";
/// <summary>波形选择</summary>
public string Waveform { get => _waveform; set => SetProperty(ref _waveform, value); }
private string _couplingMode = "AC";
/// <summary>输出耦合模式 (AC / DC / ACDC)</summary>
public string CouplingMode { get => _couplingMode; set => SetProperty(ref _couplingMode, value); }
private string _threePhaseRelation = "BALANCE";
/// <summary>三相关系 (INDEPEND / SAMEFREQ / BALANCE)</summary>
public string ThreePhaseRelation { get => _threePhaseRelation; set => SetProperty(ref _threePhaseRelation, value); }
#endregion
#region 输入参数(LIST 模式)
private string _listCoupling = "NONE";
/// <summary>列表耦合模态 (ALL / NONE)</summary>
public string ListCoupling { get => _listCoupling; set => SetProperty(ref _listCoupling, value); }
private string _listTrigger = "AUTO";
/// <summary>列表触发形态 (AUTO / MANUAL / EXCITE)</summary>
public string ListTrigger { get => _listTrigger; set => SetProperty(ref _listTrigger, value); }
private int _listCount = 1;
/// <summary>列表执行次数 (0 ~ 65535)</summary>
public int ListCount { get => _listCount; set => SetProperty(ref _listCount, value); }
private string _listTimeBase = "TIME";
/// <summary>列表时间基础 (TIME / CYCLE)</summary>
public string ListTimeBase { get => _listTimeBase; set => SetProperty(ref _listTimeBase, value); }
private string _listVoltAcStartText = "";
/// <summary>AC 起始电压序列(逗号分隔,如 0,50,100,150)</summary>
public string ListVoltAcStartText { get => _listVoltAcStartText; set => SetProperty(ref _listVoltAcStartText, value); }
private string _listVoltAcEndText = "";
/// <summary>AC 结束电压序列(逗号分隔)</summary>
public string ListVoltAcEndText { get => _listVoltAcEndText; set => SetProperty(ref _listVoltAcEndText, value); }
private string _listFreqStartText = "";
/// <summary>起始频率序列(逗号分隔,如 50,60,50)</summary>
public string ListFreqStartText { get => _listFreqStartText; set => SetProperty(ref _listFreqStartText, value); }
private string _listFreqEndText = "";
/// <summary>结束频率序列(逗号分隔)</summary>
public string ListFreqEndText { get => _listFreqEndText; set => SetProperty(ref _listFreqEndText, value); }
private string _listDwellText = "";
/// <summary>静止时间序列(ms,逗号分隔,如 500,500,500)</summary>
public string ListDwellText { get => _listDwellText; set => SetProperty(ref _listDwellText, value); }
private string _listDegrText = "";
/// <summary>相位角度序列(°,逗号分隔)</summary>
public string ListDegrText { get => _listDegrText; set => SetProperty(ref _listDegrText, value); }
private string _listShapeText = "";
/// <summary>波形缓冲顺序(A/B,逗号分隔,如 A,B,A)</summary>
public string ListShapeText { get => _listShapeText; set => SetProperty(ref _listShapeText, value); }
#endregion
#region 下拉选项
public IReadOnlyList<string> WaveformOptions { get; } = new[] { "SINE", "SQUA", "CSIN" };
public IReadOnlyList<string> CouplingOptions { get; } = new[] { "AC", "DC", "ACDC" };
public IReadOnlyList<string> RelationOptions { get; } = new[] { "INDEPEND", "SAMEFREQ", "BALANCE" };
public IReadOnlyList<string> ListCouplingOptions { get; } = new[] { "ALL", "NONE" };
public IReadOnlyList<string> ListTriggerOptions { get; } = new[] { "AUTO", "MANUAL", "EXCITE" };
public IReadOnlyList<string> ListTimeBaseOptions { get; } = new[] { "TIME", "CYCLE" };
#endregion
#region 日志
private string _responseLog = "";
/// <summary>操作与采样日志</summary>
public string ResponseLog { get => _responseLog; set => SetProperty(ref _responseLog, value); }
#endregion
#region 命令
public ICommand QueryIdnCommand { get; }
public ICommand ResetCommand { get; }
public ICommand OutputOnCommand { get; }
public ICommand OutputOffCommand { get; }
public ICommand RelayOnCommand { get; }
public ICommand RelayOffCommand { get; }
public ICommand ApplyCouplingCommand { get; }
public ICommand SetThreePhaseModeCommand { get; }
public ICommand SetSinglePhaseModeCommand { get; }
public ICommand ApplyRelationCommand { get; }
public ICommand SetVoltageCommand { get; }
public ICommand SetFrequencyCommand { get; }
public ICommand SetWaveformCommand { get; }
public ICommand SetFixedModeCommand { get; }
public ICommand EnterListModeCommand { get; }
public ICommand ApplyListBasicCommand { get; }
public ICommand ApplyListSequencesCommand { get; }
public ICommand ListStartCommand { get; }
public ICommand ListStopCommand { get; }
public ICommand StartSamplingCommand { get; }
public ICommand StopSamplingCommand { get; }
public ICommand ClearChartsCommand { get; }
#endregion
public PowerAdaptabilityViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_dm = containerProvider.Resolve<DeviceManager>();
_globalInfo = containerProvider.Resolve<GlobalInfo>();
// 初始化三相图表(按物理量分图,每图叠加 A/B/C 三相正弦)
VoltagePlot = BuildSinePlot("三相电压", "V");
CurrentPlot = BuildSinePlot("三相电流", "A");
PowerPlot = BuildSinePlot("三相功率", "W");
(_voltA, _voltB, _voltC) = AddThreePhaseSeries(VoltagePlot);
(_currA, _currB, _currC) = AddThreePhaseSeries(CurrentPlot);
(_powA, _powB, _powC) = AddThreePhaseSeries(PowerPlot);
#region 命令初始化
QueryIdnCommand = new DelegateCommand(async () =>
await Exec(async () => Log("IDN: " + await _dev!.查询设备信息Async(Ct()))));
ResetCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.复位Async(Ct()); Log("设备已复位"); }));
OutputOnCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置输出开关Async(true, Ct()); Log("输出已开启"); }));
OutputOffCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置输出开关Async(false, Ct()); Log("输出已关闭"); }));
RelayOnCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置输出继电器Async(true, Ct()); Log("输出继电器已开启"); }));
RelayOffCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置输出继电器Async(false, Ct()); Log("输出继电器已关闭"); }));
ApplyCouplingCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置耦合模式Async(CouplingMode, Ct()); Log($"耦合模式={CouplingMode}"); }));
SetThreePhaseModeCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置单三相模式Async(true, Ct()); Log("已切换为三相模式"); }));
SetSinglePhaseModeCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置单三相模式Async(false, Ct()); Log("已切换为单相模式"); }));
ApplyRelationCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置三相关系Async(ThreePhaseRelation, Ct()); Log($"三相关系={ThreePhaseRelation}"); }));
SetVoltageCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置交流电压Async(Voltage, Ct()); Log($"交流电压={Voltage} V"); }));
SetFrequencyCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置频率Async(Frequency, Ct()); Log($"输出频率={Frequency} Hz"); }));
SetWaveformCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置波形Async(Waveform, Ct()); Log($"波形={Waveform}"); }));
SetFixedModeCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置操作模态Async("FIXED", Ct()); Log("已切换为 FIXED 固定模式"); }));
EnterListModeCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.设置操作模态Async("LIST", Ct()); Log("已切换为 LIST 模式"); }));
ApplyListBasicCommand = new DelegateCommand(async () => await Exec(async () =>
{
await _dev!.设置列表耦合模态Async(ListCoupling, Ct());
await _dev!.设置列表触发形态Async(ListTrigger, Ct());
await _dev!.设置列表执行次数Async(ListCount, Ct());
await _dev!.设置列表时间基础Async(ListTimeBase, Ct());
Log($"LIST 基础配置:耦合={ListCoupling} 触发={ListTrigger} 次数={ListCount} 时基={ListTimeBase}");
}));
ApplyListSequencesCommand = new DelegateCommand(async () => await Exec(async () =>
{
int applied = 0;
var vAcStart = ParseDoubles(ListVoltAcStartText);
if (vAcStart.Length > 0) { await _dev!.设置列表AC起始电压顺序Async(vAcStart, Ct()); applied++; }
var vAcEnd = ParseDoubles(ListVoltAcEndText);
if (vAcEnd.Length > 0) { await _dev!.设置列表AC结束电压顺序Async(vAcEnd, Ct()); applied++; }
var fStart = ParseDoubles(ListFreqStartText);
if (fStart.Length > 0) { await _dev!.设置列表起始频率顺序Async(fStart, Ct()); applied++; }
var fEnd = ParseDoubles(ListFreqEndText);
if (fEnd.Length > 0) { await _dev!.设置列表结束频率顺序Async(fEnd, Ct()); applied++; }
var dwell = ParseDoubles(ListDwellText);
if (dwell.Length > 0) { await _dev!.设置列表静止时间顺序Async(dwell, Ct()); applied++; }
var degr = ParseDoubles(ListDegrText);
if (degr.Length > 0) { await _dev!.设置列表相位角度顺序Async(degr, Ct()); applied++; }
var shapes = ParseShapes(ListShapeText);
if (shapes.Length > 0) { await _dev!.设置列表波形顺序Async(shapes, Ct()); applied++; }
Log(applied > 0 ? $"已下发 {applied} 组列表序列" : "没有可下发的列表序列(请先填写序列文本框)");
}));
ListStartCommand = new DelegateCommand(async () => await Exec(async () =>
{
await _dev!.设置操作模态Async("LIST", Ct());
await _dev!.触发列表执行Async(true, Ct());
Log("LIST 已开始执行");
}));
ListStopCommand = new DelegateCommand(async () =>
await Exec(async () => { await _dev!.触发列表执行Async(false, Ct()); Log("LIST 已停止执行"); }));
StartSamplingCommand = new DelegateCommand(StartSampling);
StopSamplingCommand = new DelegateCommand(StopSampling);
ClearChartsCommand = new DelegateCommand(ClearCharts);
#endregion
Initialize();
}
#region 初始化 / 导航
/// <summary>
/// 查找 Chroma61800 设备实例:优先当前作用域 DeviceManager,回退全局 HardwarePool(跨作用域共享实例)。
/// </summary>
private void Initialize()
{
Chroma61800? found = null;
string? fn = null;
foreach (var kv in _dm.DeviceMap)
{
if (kv.Value is Chroma61800 it) { found = it; fn = kv.Key; break; }
}
if (found == null)
{
foreach (var kv in _globalInfo.HardwarePool)
{
if (kv.Value.IsValueCreated && kv.Value.Value is Chroma61800 c) { found = c; fn = kv.Key; break; }
}
}
_dev = found;
DeviceName = fn ?? "Chroma61800 (未找到)";
IsConnected = _dev?.IsConnected ?? false;
Log(found != null
? $"已关联设备 [{DeviceName}],连接:{(IsConnected ? "已连接" : "未连接")}"
: "未找到 Chroma61800 设备,请先在设置界面添加并启用该设备");
}
public override void OnNavigatedTo(NavigationContext navigationContext)
{
Initialize();
}
public override void OnNavigatedFrom(NavigationContext navigationContext)
{
// 离开页面时停止采样,避免后台持续占用设备通信
StopSampling();
}
#endregion
#region 数据采集
private void StartSampling()
{
if (_dev == null) { Log("未关联 Chroma61800 设备,无法采集"); return; }
if (IsSampling) return;
IsSampling = true;
_sampleCts = new CancellationTokenSource();
var token = _sampleCts.Token;
_ = Task.Run(() => SamplingLoopAsync(token), token);
Log($"开始采集(周期 {SampleIntervalMs} ms)");
}
private void StopSampling()
{
if (!IsSampling) return;
IsSampling = false;
try { _sampleCts?.Cancel(); } catch { }
_sampleCts?.Dispose();
_sampleCts = null;
Log("停止采集");
}
/// <summary>
/// 采样循环:逐相读取交流电压/电流,合成波形;并刷新频率与功率实时值。
/// <para>说明:Chroma61800 类现有测量方法面向"活动相位",三相逐一调用读取;
/// 如设备需要按相切换后读取,可在每相读取前补充相位选择命令。</para>
/// </summary>
private async Task SamplingLoopAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
try
{
if (_dev != null && _dev.IsConnected)
{
// A 相
double vA = await _dev.读取交流电压Async(token);
double iA = await _dev.读取交流电流Async(token);
// B 相
double vB = await _dev.读取交流电压Async(token);
double iB = await _dev.读取交流电流Async(token);
// C 相
double vC = await _dev.读取交流电压Async(token);
double iC = await _dev.读取交流电流Async(token);
// 设备级:频率与功率
double freq = await _dev.读取频率Async(token);
double power = await _dev.读取真实功率Async(token);
var dispatcher = Application.Current?.Dispatcher;
if (dispatcher != null)
{
_ = dispatcher.BeginInvoke(new Action(() =>
{
RegenerateWaveforms(vA, iA, vB, iB, vC, iC, freq, power);
IsConnected = _dev.IsConnected;
}));
}
}
}
catch (OperationCanceledException) { break; }
catch (Exception ex)
{
Log($"采样异常:{ex.Message}");
}
try { await Task.Delay(SampleIntervalMs, token); }
catch (OperationCanceledException) { break; }
}
}
/// <summary>
/// 根据实测 RMS 值合成三相正弦波并刷新三张图表(UI 线程调用)。
/// <para>横轴为相位角 0~360°,纵轴对称显示正负半周;电压/电流峰值 = √2 × RMS,
/// 三相相位互差 120°(A=0,B=-120,C=+120)。设备仅提供 RMS 标量,故为示意波形。</para>
/// </summary>
private void RegenerateWaveforms(double vA, double iA, double vB, double iB,
double vC, double iC, double freq, double power)
{
// 电压三相
RegenerateSine(_voltA, vA, PhaseOffsetDeg[0]);
RegenerateSine(_voltB, vB, PhaseOffsetDeg[1]);
RegenerateSine(_voltC, vC, PhaseOffsetDeg[2]);
// 电流三相
RegenerateSine(_currA, iA, PhaseOffsetDeg[0]);
RegenerateSine(_currB, iB, PhaseOffsetDeg[1]);
RegenerateSine(_currC, iC, PhaseOffsetDeg[2]);
// 功率三相(设备仅提供总有功,平衡三相每相 ≈ 总功率 / 3,按正弦示意呈现正负半周)
double pPhase = power / 3.0;
RegenerateSine(_powA, pPhase, PhaseOffsetDeg[0]);
RegenerateSine(_powB, pPhase, PhaseOffsetDeg[1]);
RegenerateSine(_powC, pPhase, PhaseOffsetDeg[2]);
// 实时数值
VoltageA = vA; CurrentA = iA;
VoltageB = vB; CurrentB = iB;
VoltageC = vC; CurrentC = iC;
FrequencyNow = freq; PowerNow = power;
// 纵轴对称范围(取该图三相峰值最大值并留 15% 余量,确保正负半周完整可见)
SetSymmetricAxis(VoltagePlot, vA, vB, vC);
SetSymmetricAxis(CurrentPlot, iA, iB, iC);
SetSymmetricAxis(PowerPlot, pPhase);
VoltagePlot.InvalidatePlot(true);
CurrentPlot.InvalidatePlot(true);
PowerPlot.InvalidatePlot(true);
}
/// <summary>按 RMS 幅值与相位偏移,生成 0~360° 一个完整周期的正弦曲线点(峰值 = √2 × RMS)。</summary>
private static void RegenerateSine(LineSeries series, double rms, double phaseDeg)
{
series.Points.Clear();
double peak = Math.Sqrt(2) * rms;
double phaseRad = phaseDeg * Math.PI / 180.0;
for (int i = 0; i <= SinePoints; i++)
{
double deg = 360.0 * i / SinePoints;
double rad = deg * Math.PI / 180.0 + phaseRad;
series.Points.Add(new DataPoint(deg, peak * Math.Sin(rad)));
}
}
/// <summary>设置纵轴对称范围(-peak ~ +peak);无输出时回退到 ±1 避免坐标轴退化。</summary>
private static void SetSymmetricAxis(PlotModel plot, params double[] rmsValues)
{
double maxRms = rmsValues.Max();
double peak = Math.Sqrt(2) * maxRms * 1.15;
if (peak <= 0) peak = 1;
if (plot.Axes.OfType<LinearAxis>().FirstOrDefault() is { } axis)
{
axis.Minimum = -peak;
axis.Maximum = peak;
}
}
private void ClearCharts()
{
foreach (var s in new[] { _voltA, _voltB, _voltC, _currA, _currB, _currC, _powA, _powB, _powC })
{
s.Points.Clear();
}
VoltagePlot.InvalidatePlot(true);
CurrentPlot.InvalidatePlot(true);
PowerPlot.InvalidatePlot(true);
Log("已清空波形数据");
}
#endregion
#region 图表构建
/// <summary>
/// 构建正弦波图表:横轴为相位角 0~360°,纵轴为对应物理量(对称显示正负半周),含图例。
/// </summary>
/// <param name="title">图表标题(如“三相电压”)</param>
/// <param name="unit">纵轴单位(V / A / W)</param>
private static PlotModel BuildSinePlot(string title, string unit)
{
var pm = new PlotModel
{
Title = $"{title} ({unit})",
PlotAreaBorderColor = OxyColors.LightGray,
Background = OxyColors.White
};
pm.Axes.Add(new LinearAxis
{
Position = AxisPosition.Bottom,
Title = "相位角 (°)",
Minimum = 0,
Maximum = 360,
MajorStep = 45,
MinorStep = 15,
StringFormat = "0",
MajorGridlineStyle = LineStyle.Dot,
MinorGridlineStyle = LineStyle.None
});
pm.Axes.Add(new LinearAxis
{
Position = AxisPosition.Left,
Title = unit,
Minimum = -1,
Maximum = 1,
MajorGridlineStyle = LineStyle.Dot,
MinorGridlineStyle = LineStyle.None
});
pm.Legends.Add(new Legend
{
LegendPosition = LegendPosition.RightTop,
LegendBackground = OxyColor.FromAColor(200, OxyColors.White),
LegendBorder = OxyColors.LightGray
});
return pm;
}
/// <summary>向图表添加 A/B/C 三相正弦曲线(红/绿/蓝,与三张图配色一致)。</summary>
private static (LineSeries A, LineSeries B, LineSeries C) AddThreePhaseSeries(PlotModel plot)
{
var a = NewPhaseSeries("A 相", PhaseColors[0]);
var b = NewPhaseSeries("B 相", PhaseColors[1]);
var c = NewPhaseSeries("C 相", PhaseColors[2]);
plot.Series.Add(a);
plot.Series.Add(b);
plot.Series.Add(c);
return (a, b, c);
}
/// <summary>创建一条相曲线(无标记点、平滑线条)。</summary>
private static LineSeries NewPhaseSeries(string title, OxyColor color) => new LineSeries
{
Title = title,
Color = color,
StrokeThickness = 1.8,
MarkerType = MarkerType.None
};
#endregion
#region 辅助
/// <summary>创建 10 秒超时的取消令牌(命令用)。</summary>
private CancellationToken Ct() => (_cmdCts = new CancellationTokenSource(TimeSpan.FromSeconds(10))).Token;
/// <summary>统一的设备命令执行包装:防重入、异常捕获与日志。</summary>
private async Task Exec(Func<Task> action)
{
if (_dev == null) { Log("错误:未找到 Chroma61800 设备实例,请检查设备配置"); return; }
if (IsBusy) return;
IsBusy = true;
try
{
await action();
IsConnected = _dev.IsConnected;
}
catch (OperationCanceledException) { Log("命令超时或已取消"); }
catch (Exception ex) { Log($"错误:{ex.Message}"); }
finally { IsBusy = false; }
}
/// <summary>线程安全的日志输出(自动切回 UI 线程,保留最近内容)。</summary>
private void Log(string message)
{
var dispatcher = Application.Current?.Dispatcher;
if (dispatcher != null && !dispatcher.CheckAccess())
{
_ = dispatcher.BeginInvoke(new Action(() => Log(message)));
return;
}
var line = $"[{DateTime.Now:HH:mm:ss}] {message}";
ResponseLog = ResponseLog.Length > 6000
? line + "\n" + ResponseLog[..4000]
: line + "\n" + ResponseLog;
}
/// <summary>解析逗号/空格/分号分隔的数值序列。</summary>
private static double[] ParseDoubles(string text)
{
if (string.IsNullOrWhiteSpace(text)) return Array.Empty<double>();
return text.Split(new[] { ',', ',', ' ', '\t', ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => double.TryParse(s.Trim(), out double v) ? (double?)v : null)
.Where(v => v.HasValue)
.Select(v => v!.Value)
.ToArray();
}
/// <summary>解析波形缓冲顺序(A/B)序列。</summary>
private static string[] ParseShapes(string text)
{
if (string.IsNullOrWhiteSpace(text)) return Array.Empty<string>();
return text.Split(new[] { ',', ',', ' ', '\t', ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim().ToUpperInvariant())
.ToArray();
}
#endregion
public void Dispose()
{
StopSampling();
try { _cmdCts?.Cancel(); } catch { }
_cmdCts?.Dispose();
}
}
}