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>
167 lines
7.0 KiB
C#
167 lines
7.0 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|