Files
HC_APTBS/ViewModels/SingleFlowChartViewModel.cs
LucianoDev 4891eb6812 feat: redesign bench calibration (factor/offset), add Ttank/P2 displays, fix sensor calibration
- Replace P1-P6 rational transfer function with factor/offset model for bench params
- Add explicit rx/tx direction flags in bench XML configuration
- Add T.Tank (BenchTemp) and P2 (AnalogSensor2) to temperature/pressure display
- Apply SensorConfiguration calibration to pressure channels, fix empty sensors.xml fallback
- Add live value labels to flowmeter charts
- Hide pump live values and PSG encoder standalone label
- Add K-Line connection state model, improve KWP service and status displays
- Restructure .claude/skills into subdirectory format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 21:25:30 +02:00

127 lines
4.1 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>Most recent sample value, displayed as a numeric label alongside the chart.</summary>
[ObservableProperty] private double _currentValue;
/// <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);
CurrentValue = 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>();
}
}
}