Bundles several feature streams that have been iterating on the working tree: - Developer Tools page (Debug-only via DEVELOPER_TOOLS symbol): hosts the identification card, manual KWP write + transaction log, ROM/EEPROM dump card with progress banner and completion message, persisted custom-commands library, persisted EEPROM passwords library. New service primitives: IKwpService.SendRawCustomAsync / ReadEepromAsync / ReadRomEepromAsync. Persistence mirrors the Clients XML pattern in two new files (custom_commands.xml, eeprom_passwords.xml). - Auto-test orchestrator (IAutoTestOrchestrator + AutoTestState): linear K-Line read -> unlock -> bench-on -> test sequence with snackbar UI and progress dialog VM, gated on dashboard alarms. - BIP-STATUS display: BipDisplayViewModel + BipDisplayView, RAM read at 0x0106 via IKwpService.ReadBipStatusAsync; status definitions in BipStatusDefinition. - Tests page redesign: TestSectionCard + PhaseTileView replacing the old TestPlanView/TestRunningView/TestDoneView/TestPreconditionsView/ TestSectionView controls and their VMs. - Pump command sliders: Fluent thick-track style with overhang thumb, click-anywhere-and-drag, mouse-wheel adjustment. - Window startup: app.manifest declares PerMonitorV2 DPI awareness, MainWindow installs a WM_GETMINMAXINFO hook in OnSourceInitialized and maximizes there (after the hook is in place) so the app fits the work area exactly on any display configuration. - Misc: PercentToPixelsConverter, seed_aliases.py one-shot pump-alias importer, tools/Import-BipStatus.ps1, kline_eeprom_spec.md and dump-functions reference docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
145 lines
6.0 KiB
C#
145 lines
6.0 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Windows.Media;
|
|
using System.Windows.Threading;
|
|
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>BIP-STATUS display (PSG5-PI pumps only; hidden via HasDefinition for others).</summary>
|
|
public BipDisplayViewModel BipDisplay => Root.BipDisplay;
|
|
|
|
/// <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; }
|
|
|
|
private readonly DispatcherTimer _rpmChartTimer;
|
|
|
|
// ── 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,
|
|
smoothScroll: true);
|
|
|
|
_rpmChartTimer = new DispatcherTimer
|
|
{
|
|
Interval = HzToInterval(root.Config.Settings.RpmChartUpdateHz)
|
|
};
|
|
_rpmChartTimer.Tick += (_, _) => RpmChart.AddValue(Root.PumpRpm);
|
|
_rpmChartTimer.Start();
|
|
|
|
// Per-frame viewport slide — produces smooth continuous leftward motion independent
|
|
// of data cadence. Safe to leave subscribed for the VM's lifetime (one DateTime.UtcNow
|
|
// and two property sets per render frame).
|
|
CompositionTarget.Rendering += OnRenderFrame;
|
|
|
|
RefreshDerivedFlags();
|
|
|
|
Root.PropertyChanged += OnRootPropertyChanged;
|
|
Root.SettingsSaved += OnSettingsSaved;
|
|
Root.PumpIdentification.PumpChanged += _ => RefreshDerivedFlags();
|
|
}
|
|
|
|
private void OnRenderFrame(object? sender, EventArgs e)
|
|
{
|
|
int hz = Root.Config.Settings.RpmChartUpdateHz;
|
|
if (hz <= 0) hz = 15;
|
|
RpmChart.UpdateViewport(DateTime.UtcNow, 1000.0 / hz);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
private void OnSettingsSaved()
|
|
{
|
|
_rpmChartTimer.Interval = HzToInterval(Root.Config.Settings.RpmChartUpdateHz);
|
|
}
|
|
|
|
private static System.TimeSpan HzToInterval(int hz) =>
|
|
System.TimeSpan.FromMilliseconds(hz > 0 ? 1000.0 / hz : 1000.0 / 15);
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|