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>
This commit is contained in:
@@ -101,9 +101,6 @@ namespace HC_APTBS.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The non-modal unlock progress window, if open.</summary>
|
||||
private UnlockProgressDialog? _unlockDlg;
|
||||
|
||||
/// <summary>Remembers the last authenticated username to pre-fill the next auth dialog.</summary>
|
||||
private string _lastAuthenticatedUser = string.Empty;
|
||||
|
||||
@@ -380,14 +377,11 @@ namespace HC_APTBS.ViewModels
|
||||
|
||||
_unlockCts = new CancellationTokenSource();
|
||||
CurrentUnlockVm = new UnlockProgressViewModel(_unlock, pump.UnlockType, _unlockCts, _loc);
|
||||
_unlockDlg = new UnlockProgressDialog(_unlockVm!)
|
||||
{ Owner = Application.Current.MainWindow };
|
||||
CurrentUnlockVm.RequestClose += CloseUnlockDialog;
|
||||
|
||||
// Start unlock in background — ViewModel tracks via event subscriptions.
|
||||
var unlockTask = _unlock.UnlockAsync(pump, _unlockCts.Token);
|
||||
_ = unlockTask.ContinueWith(_ => { }, TaskContinuationOptions.OnlyOnFaulted);
|
||||
|
||||
_unlockDlg.Show(); // Non-modal — user can continue working.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -409,15 +403,10 @@ namespace HC_APTBS.ViewModels
|
||||
|
||||
if (_unlockVm != null)
|
||||
{
|
||||
_unlockVm.RequestClose -= CloseUnlockDialog;
|
||||
_unlockVm.Dispose();
|
||||
CurrentUnlockVm = null;
|
||||
}
|
||||
|
||||
if (_unlockDlg != null)
|
||||
{
|
||||
_unlockDlg.ForceClose();
|
||||
_unlockDlg = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── CAN connection ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -1,33 +1,19 @@
|
||||
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>Identifies the sub-section shown inside the Pump navigation page.</summary>
|
||||
public enum PumpSubPage
|
||||
{
|
||||
/// <summary>§3.a — Pump selection and K-Line ECU read.</summary>
|
||||
Identification = 0,
|
||||
/// <summary>§3.b — Diagnostic Trouble Codes.</summary>
|
||||
Dtcs = 1,
|
||||
/// <summary>§3.c — Live pump CAN readings and status words.</summary>
|
||||
LiveData = 2,
|
||||
/// <summary>§3.d — DFI calibration and ME/FBKW/PreIn manual control (auth-gated).</summary>
|
||||
Adaptation = 3,
|
||||
/// <summary>§3.e — Ford VP44 immobilizer unlock (visible only when required).</summary>
|
||||
Unlock = 4
|
||||
}
|
||||
|
||||
/// <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 sub-page navigation, banner flags,
|
||||
/// and the Adaptation auth gate. 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>
|
||||
/// <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
|
||||
{
|
||||
@@ -36,31 +22,29 @@ namespace HC_APTBS.ViewModels.Pages
|
||||
|
||||
// ── Child VM façades ──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Pump selector and K-Line read (§3.a).</summary>
|
||||
/// <summary>Pump selector and K-Line read.</summary>
|
||||
public PumpIdentificationViewModel Identification => Root.PumpIdentification;
|
||||
|
||||
/// <summary>Diagnostic Trouble Code list (§3.b).</summary>
|
||||
/// <summary>Diagnostic Trouble Code list.</summary>
|
||||
public DtcListViewModel DtcList { get; }
|
||||
|
||||
/// <summary>DFI management (§3.d).</summary>
|
||||
/// <summary>DFI management.</summary>
|
||||
public DfiManageViewModel DfiViewModel => Root.DfiViewModel;
|
||||
|
||||
/// <summary>Manual pump control sliders (§3.d).</summary>
|
||||
/// <summary>Manual pump control sliders.</summary>
|
||||
public PumpControlViewModel PumpControl => Root.PumpControl;
|
||||
|
||||
/// <summary>First pump status display — Status word (§3.c).</summary>
|
||||
/// <summary>First pump status display — Status word.</summary>
|
||||
public StatusDisplayViewModel StatusDisplay1 => Root.StatusDisplay1;
|
||||
|
||||
/// <summary>Second pump status display — Empf3 word (§3.c).</summary>
|
||||
/// <summary>Second pump status display — Empf3 word.</summary>
|
||||
public StatusDisplayViewModel StatusDisplay2 => Root.StatusDisplay2;
|
||||
|
||||
/// <summary>Current immobilizer unlock VM (§3.e). Null when no unlock is in progress for this pump.</summary>
|
||||
/// <summary>Current immobilizer unlock VM. Null when no unlock is in progress.</summary>
|
||||
public UnlockProgressViewModel? UnlockVm => Root.CurrentUnlockVm;
|
||||
|
||||
// ── Navigation state ──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Currently selected Pump sub-section.</summary>
|
||||
[ObservableProperty] private PumpSubPage _selectedSubPage = PumpSubPage.Identification;
|
||||
/// <summary>Real-time RPM chart (120-sample rolling window).</summary>
|
||||
public SingleFlowChartViewModel RpmChart { get; }
|
||||
|
||||
// ── Banner flags (derived from Root state) ────────────────────────────────
|
||||
|
||||
@@ -81,13 +65,12 @@ namespace HC_APTBS.ViewModels.Pages
|
||||
MainViewModel root,
|
||||
DtcListViewModel dtcList)
|
||||
{
|
||||
Root = root;
|
||||
Root = root;
|
||||
DtcList = dtcList;
|
||||
RpmChart = new SingleFlowChartViewModel("RPM", new SKColor(0x21, 0x96, 0xF3), maxSamples: 120);
|
||||
|
||||
// Initialise derived flags from the current Root state.
|
||||
RefreshDerivedFlags();
|
||||
|
||||
// Keep the derived flags in sync with Root changes.
|
||||
Root.PropertyChanged += OnRootPropertyChanged;
|
||||
Root.PumpIdentification.PumpChanged += _ => RefreshDerivedFlags();
|
||||
}
|
||||
@@ -104,19 +87,23 @@ namespace HC_APTBS.ViewModels.Pages
|
||||
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;
|
||||
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));
|
||||
|
||||
// Drop any stale DTCs from the previous pump.
|
||||
DtcList.Reset();
|
||||
RpmChart.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using HC_APTBS.Models;
|
||||
|
||||
@@ -64,6 +65,9 @@ namespace HC_APTBS.ViewModels
|
||||
/// <summary>16 bit indicators for bits 0–15 of the current status word.</summary>
|
||||
public ObservableCollection<BitIndicatorViewModel> Bits { get; } = new();
|
||||
|
||||
/// <summary>Number of bits currently active (set to 1) in the status word.</summary>
|
||||
public int ActiveCount => Bits.Count(b => b.IsActive);
|
||||
|
||||
/// <summary>
|
||||
/// Fired when a bit that is flagged as an error transitions to active.
|
||||
/// The argument is the bit position (0-based).
|
||||
@@ -119,6 +123,8 @@ namespace HC_APTBS.ViewModels
|
||||
if (isSet && statusBit.Enabled)
|
||||
ErrorBitDetected?.Invoke(index);
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(ActiveCount));
|
||||
}
|
||||
|
||||
/// <summary>Resets all indicators to the default green / inactive state.</summary>
|
||||
@@ -130,6 +136,8 @@ namespace HC_APTBS.ViewModels
|
||||
Bits[i].Color = "#26C200";
|
||||
Bits[i].Description = $"Bit {i}";
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(ActiveCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user