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

@@ -41,6 +41,22 @@ namespace HC_APTBS.Services
/// <summary>True when the CAN read thread is running and the channel is open.</summary>
bool IsConnected { get; }
/// <summary>
/// The PCAN channel handle that will be used on the next <see cref="Connect"/> call.
/// Defaults to the channel supplied at construction.
/// Throws <see cref="System.InvalidOperationException"/> when set while <see cref="IsConnected"/> is true.
/// </summary>
TPCANHandle SelectedChannel { get; set; }
// ── Discovery ─────────────────────────────────────────────────────────────
/// <summary>
/// Enumerates PCAN USB channels that are physically attached to the system.
/// Returns an empty list if no adapters are connected or if the PCAN-Basic DLL
/// is unavailable. Never throws.
/// </summary>
System.Collections.Generic.IReadOnlyList<AttachedPcanChannel> EnumerateAttachedChannels();
// ── Lifecycle ─────────────────────────────────────────────────────────────
/// <summary>