Files
HC_APTBS/ViewModels/Pages/PumpPageViewModel.cs
LucianoDev 70be693116 feat: redesign Pump page with Fluent card layout, bottom snackbar, and RPM chart
- Replace sub-nav + HiddenTabsTabControl with 3-column Fluent card layout:
  PumpCommandsCard (vertical ME/FBKW/PreIn sliders) + DfiCalibrationCard /
  PumpLiveDataCard (KPI tiles + RPM rolling chart + redesigned status bytes) /
  PumpIdentificationCard + DtcCard
- Add PumpTopStripView: pump selector, model badge, CAN + K-Line chips
- Move immobilizer unlock to MainWindow bottom snackbar (UnlockSnackbarView):
  auto-close on success after 3 s, persist on failure with manual Dismiss
- Redesign StatusDisplayView to 2×8 rounded 28px tiles with bit index + tooltip
- Add NullToVisibilityConverter; add SnackbarShell, PumpCard, and related styles
- Delete obsolete views: UnlockProgressDialog, UnlockPanelView,
  PumpIdentificationPanelView, PumpLiveDataView, DfiManageView,
  DtcListView, PumpControlView
- PumpPageViewModel: remove PumpSubPage enum, add RpmChart wired to Root.PumpRpm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 14:03:47 +02:00

110 lines
4.6 KiB
C#

using System.ComponentModel;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using HC_APTBS.Models;
using HC_APTBS.ViewModels.Dialogs;
using SkiaSharp;
namespace HC_APTBS.ViewModels.Pages
{
/// <summary>
/// ViewModel for the Pump navigation page.
///
/// <para>Thin façade that groups the pump-related child ViewModels owned by
/// <see cref="MainViewModel"/> and adds banner flags. Holds a <see cref="Root"/>
/// reference so page XAML can bind to MainViewModel-owned properties (PumpRpm,
/// PumpTemp, KLineState, …) via <c>{Binding Root.X}</c>.</para>
/// </summary>
public sealed partial class PumpPageViewModel : ObservableObject
{
/// <summary>Root ViewModel — owns services, live readings, and global commands.</summary>
public MainViewModel Root { get; }
// ── Child VM façades ──────────────────────────────────────────────────────
/// <summary>Pump selector and K-Line read.</summary>
public PumpIdentificationViewModel Identification => Root.PumpIdentification;
/// <summary>Diagnostic Trouble Code list.</summary>
public DtcListViewModel DtcList { get; }
/// <summary>DFI management.</summary>
public DfiManageViewModel DfiViewModel => Root.DfiViewModel;
/// <summary>Manual pump control sliders.</summary>
public PumpControlViewModel PumpControl => Root.PumpControl;
/// <summary>First pump status display — Status word.</summary>
public StatusDisplayViewModel StatusDisplay1 => Root.StatusDisplay1;
/// <summary>Second pump status display — Empf3 word.</summary>
public StatusDisplayViewModel StatusDisplay2 => Root.StatusDisplay2;
/// <summary>Current immobilizer unlock VM. Null when no unlock is in progress.</summary>
public UnlockProgressViewModel? UnlockVm => Root.CurrentUnlockVm;
/// <summary>Real-time RPM chart (120-sample rolling window).</summary>
public SingleFlowChartViewModel RpmChart { get; }
// ── Banner flags (derived from Root state) ────────────────────────────────
/// <summary>True when a pump has been loaded from the database.</summary>
[ObservableProperty] private bool _isPumpSelected;
/// <summary>True when the K-Line session is currently open.</summary>
[ObservableProperty] private bool _isKLineSessionOpen;
/// <summary>True when the K-Line session is in the failed state.</summary>
[ObservableProperty] private bool _isKLineSessionFailed;
/// <summary>True for pumps that require a Ford immobilizer unlock (Type 1 or 2).</summary>
[ObservableProperty] private bool _isUnlockApplicable;
/// <summary>Constructs the page VM and subscribes to relevant Root state changes.</summary>
public PumpPageViewModel(
MainViewModel root,
DtcListViewModel dtcList)
{
Root = root;
DtcList = dtcList;
RpmChart = new SingleFlowChartViewModel("RPM", new SKColor(0x21, 0x96, 0xF3), maxSamples: 120);
RefreshDerivedFlags();
Root.PropertyChanged += OnRootPropertyChanged;
Root.PumpIdentification.PumpChanged += _ => RefreshDerivedFlags();
}
private void OnRootPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(MainViewModel.KLineState):
IsKLineSessionOpen = Root.KLineState == KLineConnectionState.Connected;
IsKLineSessionFailed = Root.KLineState == KLineConnectionState.Failed;
break;
case nameof(MainViewModel.CurrentUnlockVm):
OnPropertyChanged(nameof(UnlockVm));
break;
case nameof(MainViewModel.PumpRpm):
Application.Current.Dispatcher.Invoke(() => RpmChart.AddValue(Root.PumpRpm));
break;
}
}
private void RefreshDerivedFlags()
{
IsPumpSelected = Root.CurrentPump != null;
IsKLineSessionOpen = Root.KLineState == KLineConnectionState.Connected;
IsKLineSessionFailed = Root.KLineState == KLineConnectionState.Failed;
IsUnlockApplicable = Root.CurrentPump != null && Root.CurrentPump.UnlockType != 0;
OnPropertyChanged(nameof(UnlockVm));
DtcList.Reset();
RpmChart.Clear();
}
}
}