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:
2026-04-19 22:25:00 +02:00
parent 0280a2fad1
commit 197e9d1775
26 changed files with 1638 additions and 515 deletions

View File

@@ -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"));