Files
HC_APTBS/ViewModels/BipDisplayViewModel.cs
LucianoDev 827b811b39 feat: developer tools page, auto-test orchestrator, BIP display, tests redesign
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>
2026-05-07 13:59:50 +02:00

167 lines
7.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.ObjectModel;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using HC_APTBS.Models;
using HC_APTBS.Services;
namespace HC_APTBS.ViewModels
{
/// <summary>
/// Represents one row in the BIP-STATUS display.
/// </summary>
public sealed partial class BipRowViewModel : ObservableObject
{
/// <summary>Zero-based index of this BIP definition entry.</summary>
public int Index { get; init; }
/// <summary>16-bit nibble pattern from the CFG (displayed as "0xXXXX").</summary>
public string HexPattern { get; init; } = string.Empty;
/// <summary>Raw hex value — used to re-resolve the description on language change.</summary>
public ushort RawHex { get; init; }
/// <summary>SpecialFunction from the CFG — part of the localization key.</summary>
public int SpecialFunction { get; init; }
/// <summary>Original XML text, used as fallback when no resource key matches.</summary>
public string FallbackDescription { get; init; } = string.Empty;
/// <summary>Human-readable description shown on match; updated on language switch.</summary>
[ObservableProperty] private string _description = string.Empty;
/// <summary>HTML hex colour for the status indicator: green = inactive, red = match detected.</summary>
[ObservableProperty] private string _color = "#26C200";
/// <summary>True when this BIP pattern currently matches the captured word.</summary>
[ObservableProperty] private bool _isActive;
}
/// <summary>
/// ViewModel for the BIP-STATUS display user control.
///
/// <para>
/// Only populated for PSG5-PI pumps (those whose <see cref="PumpDefinition.BipStatus"/>
/// is non-null). When <see cref="HasDefinition"/> is false the view hides itself.
/// </para>
/// </summary>
public sealed partial class BipDisplayViewModel : ObservableObject
{
private readonly ILocalizationService _loc;
// ── Properties ────────────────────────────────────────────────────────────
/// <summary>True when the current pump has BIP definitions; controls view visibility.</summary>
[ObservableProperty] private bool _hasDefinition;
/// <summary>Last raw BIP word received from the ECU (displayed as hex for diagnostics).</summary>
[ObservableProperty] private string _rawValue = "";
/// <summary>Ordered rows for the BIP definition table.</summary>
public ObservableCollection<BipRowViewModel> Rows { get; } = new();
// ── Construction ──────────────────────────────────────────────────────────
/// <summary>
/// Initializes the view model and subscribes to language-change notifications
/// so that row descriptions update automatically when the operator switches language.
/// </summary>
public BipDisplayViewModel(ILocalizationService loc)
{
_loc = loc;
_loc.LanguageChanged += RefreshDescriptions;
}
// ── Public API ────────────────────────────────────────────────────────────
/// <summary>
/// Loads the BIP definition for the selected pump and resets the display.
/// Pass <see langword="null"/> to hide the control (non-PSG5-PI pump selected).
/// Must be called on the UI thread.
/// </summary>
public void LoadDefinition(PumpBipDefinition? bipDef)
{
Rows.Clear();
RawValue = "";
if (bipDef == null || bipDef.Bits.Count == 0)
{
HasDefinition = false;
return;
}
foreach (var d in bipDef.Bits)
{
Rows.Add(new BipRowViewModel
{
Index = d.Index,
HexPattern = $"0x{d.HexPattern:X4}",
RawHex = d.HexPattern,
SpecialFunction = d.SpecialFunction,
FallbackDescription = d.Description,
Description = ResolveDescription(d.HexPattern, d.SpecialFunction, d.Description),
Color = "#26C200",
IsActive = false
});
}
HasDefinition = true;
}
/// <summary>
/// Updates the display with a newly captured BIP status word.
/// Marks matching (and enabled) rows as active.
/// Must be called on the UI thread.
/// </summary>
/// <param name="bipDef">Current pump's BIP definition.</param>
/// <param name="rawWord">Raw 16-bit value read from ECU RAM 0x0106.</param>
public void UpdateBipWord(PumpBipDefinition bipDef, ushort rawWord)
{
RawValue = $"0x{rawWord:X4}";
for (int i = 0; i < Rows.Count && i < bipDef.Bits.Count; i++)
{
var def = bipDef.Bits[i];
var row = Rows[i];
// Bitmask match: all pattern bits must be set in rawWord.
bool matches = def.Enabled && (rawWord & def.HexPattern) == def.HexPattern;
row.IsActive = matches;
row.Color = matches ? "#FF1E1E" : "#26C200";
}
}
/// <summary>Resets all rows to inactive / green without clearing the definitions.</summary>
public void Reset()
{
foreach (var row in Rows)
{
row.IsActive = false;
row.Color = "#26C200";
}
RawValue = "";
}
// ── Helpers ───────────────────────────────────────────────────────────────
/// <summary>
/// Returns the localized description for a BIP entry keyed by (hex, specialFunction),
/// falling back to the raw XML text when no resource key is found.
/// </summary>
private static string ResolveDescription(ushort hex, int sf, string fallback)
{
var key = $"Pump.Bip.Desc.{hex:X4}.{sf}";
return Application.Current.Resources[key]?.ToString() ?? fallback;
}
/// <summary>
/// Re-resolves all row descriptions against the now-active resource dictionary.
/// Called by <see cref="ILocalizationService.LanguageChanged"/>.
/// </summary>
private void RefreshDescriptions()
{
foreach (var row in Rows)
row.Description = ResolveDescription(row.RawHex, row.SpecialFunction, row.FallbackDescription);
}
}
}