Restore the full bench control panel from the old source with MVVM architecture: - Two-column left panel layout: bench info displays (RPM with target/voltage, temps, pressures, Q-flow, pump live values) and user commands (direction toggle, start/stop with RPM popup and quick-select buttons, oil pump toggle, turn downcounter with CAN send) - PID RPM ramp controller (BenchPidController) with bumpless startup, anti-windup, and derivative-on-measurement for smooth motor speed transitions - Real-time flowmeter charts (LiveChartsCore) for Q-Delivery and Q-Over with tolerance band overlays - Bench/pump CAN liveness detection in PcanAdapter (receive-only IDs) - K-Line connection status indicator (placeholder) - Periodic relay bitmask sender (~21ms) and ElectronicMsg keepalive start on CAN connect, pump sender starts immediately on pump load Fix critical CAN message ID bug: default bench XML values were incorrectly converted from old source (decimal-notation hex parsed as actual hex digits, e.g. "10" -> "A" instead of keeping "10" which parses as 0x10). Corrected all IDs to match hardware: 0x10, 0x11, 0x13, 0x14, 0x15, 0x50, 0x51, 0x55. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
123 lines
4.0 KiB
C#
123 lines
4.0 KiB
C#
using System;
|
|
using System.Collections.ObjectModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using LiveChartsCore;
|
|
using LiveChartsCore.Defaults;
|
|
using LiveChartsCore.SkiaSharpView;
|
|
using LiveChartsCore.SkiaSharpView.Painting;
|
|
using SkiaSharp;
|
|
|
|
namespace HC_APTBS.ViewModels
|
|
{
|
|
/// <summary>
|
|
/// Reusable ViewModel for a single real-time scrolling line chart.
|
|
/// Backed by LiveChartsCore with a fixed-width sample window.
|
|
/// </summary>
|
|
public sealed partial class SingleFlowChartViewModel : ObservableObject
|
|
{
|
|
private const int DefaultMaxSamples = 200;
|
|
|
|
private readonly ObservableCollection<double> _values = new();
|
|
private readonly int _maxSamples;
|
|
|
|
/// <summary>Chart title label.</summary>
|
|
[ObservableProperty] private string _title = string.Empty;
|
|
|
|
/// <summary>Series array bound to the CartesianChart.</summary>
|
|
public ISeries[] Series { get; }
|
|
|
|
/// <summary>X axes for the chart (auto-scrolling, no labels).</summary>
|
|
public Axis[] XAxes { get; }
|
|
|
|
/// <summary>Y axes for the chart.</summary>
|
|
public Axis[] YAxes { get; }
|
|
|
|
/// <summary>
|
|
/// Tolerance band sections overlaid on the chart.
|
|
/// Updated when <see cref="SetTolerance"/> is called.
|
|
/// </summary>
|
|
[ObservableProperty] private RectangularSection[] _sections = Array.Empty<RectangularSection>();
|
|
|
|
/// <summary>
|
|
/// Creates a new chart ViewModel.
|
|
/// </summary>
|
|
/// <param name="title">Display title for the chart.</param>
|
|
/// <param name="lineColor">SKColor for the line series.</param>
|
|
/// <param name="maxSamples">Maximum number of samples before the oldest is dropped.</param>
|
|
public SingleFlowChartViewModel(string title, SKColor lineColor, int maxSamples = DefaultMaxSamples)
|
|
{
|
|
_title = title;
|
|
_maxSamples = maxSamples;
|
|
|
|
Series = new ISeries[]
|
|
{
|
|
new LineSeries<double>
|
|
{
|
|
Values = _values,
|
|
Fill = null,
|
|
GeometrySize = 0,
|
|
Stroke = new SolidColorPaint(lineColor, 2),
|
|
LineSmoothness = 0,
|
|
AnimationsSpeed = TimeSpan.Zero
|
|
}
|
|
};
|
|
|
|
XAxes = new Axis[]
|
|
{
|
|
new Axis
|
|
{
|
|
IsVisible = false,
|
|
AnimationsSpeed = TimeSpan.Zero
|
|
}
|
|
};
|
|
|
|
YAxes = new Axis[]
|
|
{
|
|
new Axis
|
|
{
|
|
AnimationsSpeed = TimeSpan.Zero,
|
|
MinLimit = 0
|
|
}
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Appends a value to the chart. Drops the oldest value when the window is full.
|
|
/// Must be called on the UI thread.
|
|
/// </summary>
|
|
public void AddValue(double value)
|
|
{
|
|
_values.Add(value);
|
|
if (_values.Count > _maxSamples)
|
|
_values.RemoveAt(0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the tolerance band (displayed as a shaded region on the chart).
|
|
/// </summary>
|
|
/// <param name="target">Center value of the tolerance band.</param>
|
|
/// <param name="tolerance">Half-width of the tolerance band.</param>
|
|
public void SetTolerance(double target, double tolerance)
|
|
{
|
|
Sections = new[]
|
|
{
|
|
new RectangularSection
|
|
{
|
|
Yi = target - tolerance,
|
|
Yj = target + tolerance,
|
|
Fill = new SolidColorPaint(SKColors.LimeGreen.WithAlpha(40))
|
|
}
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears all sample data and tolerance bands.
|
|
/// </summary>
|
|
public void Clear()
|
|
{
|
|
_values.Clear();
|
|
Sections = Array.Empty<RectangularSection>();
|
|
}
|
|
}
|
|
}
|