using OxyPlot.Series; using OxyPlot; using System.Collections.Concurrent; using Prism.Mvvm; using NCalc; namespace MonitorModule.ViewModels { /// /// 监测通道:一个设备方法对应一个通道。 /// 数据记录(DataPoints)与图表显示(Series)完全分离: /// - IsMonitored=true 时始终记录 DataPoints /// - IsDisplayed=true 时才创建 LineSeries 并绘制 /// public class MonitorChannel : BindableBase { // ===== 标识 ===== public string DeviceName { get; init; } = string.Empty; /// 硬件指纹(物理设备唯一标识,用于匹配广播事件) public string Fingerprint { get; init; } = string.Empty; public string MethodName { get; init; } = string.Empty; public string DisplayName { get; init; } = string.Empty; // ===== 状态 ===== private bool _isMonitored = true; /// 是否在记录数据点(添加后始终为 true) public bool IsMonitored { get => _isMonitored; set => SetProperty(ref _isMonitored, value); } private bool _isDisplayed = true; /// 是否在 OxyPlot 上显示 public bool IsDisplayed { get => _isDisplayed; set => SetProperty(ref _isDisplayed, value); } // ===== 数学变换 ===== private string? _mathExpression; /// 可选数学变换表达式,如 "x*0.001"。为空则 DisplayValue=RawValue public string? MathExpression { get => _mathExpression; set => SetProperty(ref _mathExpression, value); } // ===== 颜色 ===== public OxyColor Color { get; set; } = OxyColors.SteelBlue; // ===== 数据存储 ===== /// 所有采样数据点(线程安全),与 OxyPlot 无关 public ConcurrentQueue<(double Time, double RawValue, double DisplayValue)> DataPoints { get; } = new(); /// 最大缓冲数据点数,超出则丢弃最旧的 public int MaxBuffer { get; set; } = 5000; // ===== OxyPlot Series(仅 IsDisplayed=true 时存在)===== private LineSeries? _series; public LineSeries? Series { get => _series; set => SetProperty(ref _series, value); } // ===== 辅助方法 ===== /// 记录一个数据点:RawValue 经数学变换后得到 DisplayValue,一并入队 public void Record(double time, double rawValue) { double displayValue = rawValue; DataPoints.Enqueue((time, rawValue, displayValue)); // 超出缓冲上限时批量丢弃旧数据 while (DataPoints.Count > MaxBuffer) { DataPoints.TryDequeue(out _); } // 如果正在显示,同步到 Series if (_series != null) { _series.Points.Add(new DataPoint(time, displayValue)); while (_series.Points.Count > 200) { _series.Points.RemoveAt(0); } } } } }