feat: redesign dashboard with Fluent KPI tiles, connection strip, and devices column
- Replace LCD-style readings with a 3×2 KPI tile grid (Fluent card surfaces, 52pt values) - Add persistent top connection strip with horizontal chips + pump name badge - Add elapsed test timer (DispatcherTimer, mm:ss) to Test Summary card - Restyle Test Summary and Active Alarms with Fluent brushes/iconography - Add Devices column (CAN / K-Line / Bench tiles) between KPI grid and test/alarms - Enumerates attached PCAN USB channels via PCAN_ATTACHED_CHANNELS API - Enumerates FTDI K-Line adapters via existing FtdiInterface helpers - Click to connect/disconnect; confirmation dialog when session active or test running - Hover tint: blue = will connect, red = will disconnect; Bench row is read-only stub - Extend ICanService with SelectedChannel + EnumerateAttachedChannels() - Expose IKwpService.ConnectedPort for active session device tracking - Add DeviceRow button style with MultiDataTrigger hover colour logic - Add 30+ new localization keys (ES + EN) for KPI labels, devices, confirmations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using HC_APTBS.Models;
|
||||
@@ -65,6 +66,14 @@ namespace HC_APTBS.ViewModels
|
||||
|
||||
private CancellationTokenSource? _testCts;
|
||||
|
||||
// ── Test elapsed timer ────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Ticks every second while a test is running to update <see cref="TestElapsed"/>.</summary>
|
||||
private DispatcherTimer? _testTimer;
|
||||
|
||||
/// <summary>UTC start time of the current test; used by the timer to compute elapsed duration.</summary>
|
||||
private DateTime _testStartedUtc;
|
||||
|
||||
// ── Unlock tracking ──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>CTS for the currently running immobilizer unlock, if any.</summary>
|
||||
@@ -139,9 +148,6 @@ namespace HC_APTBS.ViewModels
|
||||
/// <summary>Diagnostic Trouble Code list for the Pump page §3.b sub-section.</summary>
|
||||
public DtcListViewModel DtcList { get; }
|
||||
|
||||
/// <summary>Auth gate for the Pump page §3.d Adaptation sub-section.</summary>
|
||||
public AuthGateViewModel AdaptationAuth { get; }
|
||||
|
||||
// ── Page ViewModels (thin façades over the child VMs above) ───────────────
|
||||
|
||||
/// <summary>Dashboard navigation page VM.</summary>
|
||||
@@ -203,14 +209,12 @@ namespace HC_APTBS.ViewModels
|
||||
AngleDisplay = new AngleDisplayViewModel(configService);
|
||||
DashboardAlarms = new DashboardAlarmsViewModel(configService.Settings.Alarms);
|
||||
DtcList = new DtcListViewModel(kwpService, localizationService, logger);
|
||||
AdaptationAuth = new AuthGateViewModel(configService, localizationService);
|
||||
|
||||
// Page ViewModels are thin façades over the child VMs above; they hold a
|
||||
// reference back to this coordinator so page XAML can bind MainViewModel-owned
|
||||
// values via {Binding Root.X}.
|
||||
DashboardPage = new DashboardPageViewModel(this);
|
||||
DashboardPage = new DashboardPageViewModel(this, canService, kwpService);
|
||||
BenchPage = new BenchPageViewModel(this, benchService, configService);
|
||||
PumpPage = new PumpPageViewModel(this, DtcList, AdaptationAuth);
|
||||
PumpPage = new PumpPageViewModel(this, DtcList);
|
||||
TestsPage = new TestsPageViewModel(this, configService, localizationService);
|
||||
SettingsPage = new SettingsPageViewModel(configService, localizationService);
|
||||
SettingsPage.SettingsSaved += OnSettingsSaved;
|
||||
@@ -548,6 +552,9 @@ namespace HC_APTBS.ViewModels
|
||||
/// <summary>True when the current test results have been saved to a report.</summary>
|
||||
[ObservableProperty] private bool _isTestSaved = true;
|
||||
|
||||
/// <summary>Elapsed time since the current test started. Updated every second; retains last value when idle.</summary>
|
||||
[ObservableProperty] private TimeSpan _testElapsed;
|
||||
|
||||
// ── Commands: test ────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Starts the test sequence for the current pump.</summary>
|
||||
@@ -806,6 +813,14 @@ namespace HC_APTBS.ViewModels
|
||||
{
|
||||
IsTestRunning = true;
|
||||
VerboseStatus = _loc.GetString("Test.Started");
|
||||
|
||||
_testStartedUtc = DateTime.UtcNow;
|
||||
TestElapsed = TimeSpan.Zero;
|
||||
_testTimer = new DispatcherTimer(
|
||||
TimeSpan.FromSeconds(1),
|
||||
DispatcherPriority.Normal,
|
||||
(_, _) => TestElapsed = DateTime.UtcNow - _testStartedUtc,
|
||||
App.Current.Dispatcher);
|
||||
TestPanel.IsRunning = true;
|
||||
TestPanel.ResetResults();
|
||||
ResultDisplay.Clear();
|
||||
@@ -817,6 +832,9 @@ namespace HC_APTBS.ViewModels
|
||||
private void OnTestFinished(bool interrupted, bool success)
|
||||
=> App.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
_testTimer?.Stop();
|
||||
_testTimer = null;
|
||||
|
||||
IsTestRunning = false;
|
||||
LastTestSuccess = !interrupted && success;
|
||||
VerboseStatus = interrupted ? _loc.GetString("Test.Stopped") : (success ? _loc.GetString("Common.Pass") : _loc.GetString("Common.Fail"));
|
||||
|
||||
Reference in New Issue
Block a user