using ScottPlot; using ScottPlot.Plottables; using ScottPlot.WPF; namespace MinicsUI.Helper; public partial class RealtimeDataPlotHelper : ObservableObject, IDisposable { [ObservableProperty] private WpfPlot? _PlotControl; private bool _DisposedValue; private readonly List _HistoryData = []; private readonly List _HistoryTime = []; public Channel? Channel { get; private set; } public void InitPlot(Channel channel) { if (channel is null) return; this.Channel = channel; this._HistoryData.Clear(); this._HistoryTime.Clear(); _HistoryData.AddRange(this.Channel.PVRingBuffer.ReadValues()); _HistoryTime.AddRange(this.Channel.DateTimeRingBuffer.ReadValues()); this.PlotControl = new(); this.PlotControl.Plot.Grid.XAxisStyle.MajorLineStyle.Width = 1f; this.PlotControl.Plot.Grid.YAxisStyle.MajorLineStyle.Width = 1f; this.PlotControl.Plot.FigureBackground.Color = ScottPlot.Colors.Transparent; this.PlotControl.UserInputProcessor.Disable(); this.PlotControl.Plot.RenderManager.RenderStarting += (s, e) => { Tick[] ticks = this.PlotControl.Plot.Axes.Bottom.TickGenerator.Ticks; for (int i = 0; i < ticks.Length; i++) { DateTime dt = DateTime.FromOADate(ticks[i].Position); string label = $"{dt:HH:mm:ss}"; ticks[i] = new Tick(ticks[i].Position, label); } }; this.Channel.OnPVChangedEvent += OnPVChanged; } private void OnPVChanged(float pv, DateTime dateTime) { if (Channel is null) return; if (this.PlotControl is null) return; switch (this._HistoryData.Count) { case >= 120: _HistoryTime.RemoveAt(0); _HistoryTime.Add(dateTime); _HistoryData.RemoveAt(0); _HistoryData.Add(pv); break; default: _HistoryData.Add(pv); _HistoryTime.Add(dateTime); break; } if (this.PlotControl is null) return; this.PlotControl.Plot.Clear(); this.PlotControl.Plot.Add.HorizontalLine(this.Channel.Caps, 1f, ScottPlot.Color.FromHex("FF0000"), LinePattern.Dotted); this.PlotControl.Plot.Add.HorizontalLine(this.Channel.Floor, 1f, ScottPlot.Color.FromHex("FF0000"), LinePattern.Dotted); this.PlotControl.Plot.Add.HorizontalLine(this.Channel.CapsWarning, 1f, ScottPlot.Color.FromHex("FFA500"), LinePattern.Dotted); this.PlotControl.Plot.Add.HorizontalLine(this.Channel.FloorWarning, 1f, ScottPlot.Color.FromHex("FFA500"), LinePattern.Dotted); this.PlotControl.Plot.Add.HorizontalLine(this.Channel.SetPoint, 1f, ScottPlot.Color.FromHex("00FF00"), LinePattern.Dotted); Scatter mainScatter = this.PlotControl.Plot.Add.Scatter(_HistoryTime, _HistoryData, ScottPlot.Color.FromHex("0074c1")); mainScatter.MarkerStyle = MarkerStyle.None; mainScatter.LineWidth = 3f; this.PlotControl.Plot.Axes.DateTimeTicksBottom(); this.PlotControl.Plot.Axes.AutoScale(); this.PlotControl?.Refresh(); } protected virtual void Dispose(bool disposing) { if (!_DisposedValue) { if (disposing) { this.PlotControl = null; } if (this.Channel is not null) this.Channel.OnPVChangedEvent -= OnPVChanged; _DisposedValue = true; } } ~RealtimeDataPlotHelper() { Dispose(disposing: false); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } }