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>
This commit is contained in:
@@ -52,9 +52,12 @@ namespace HC_APTBS.ViewModels
|
||||
});
|
||||
|
||||
// Start loading the pump as soon as the identifier is read from ROM,
|
||||
// before the full K-Line read completes.
|
||||
_kwp.PumpIdentified += (pumpId) => App.Current.Dispatcher.Invoke(() =>
|
||||
// before the full K-Line read completes. Use BeginInvoke so the K-Line
|
||||
// background worker thread returns immediately and the read keeps progressing
|
||||
// even if the UI thread is momentarily blocked (e.g. modal voltage warning).
|
||||
_kwp.PumpIdentified += (pumpId) => App.Current.Dispatcher.BeginInvoke(() =>
|
||||
{
|
||||
_log.Info(LogId, $"PumpIdentified handler: '{pumpId}'");
|
||||
KlinePumpId = pumpId;
|
||||
AutoSelectPumpByKlineId(pumpId);
|
||||
});
|
||||
@@ -83,6 +86,13 @@ namespace HC_APTBS.ViewModels
|
||||
/// </summary>
|
||||
public event Action<PumpDefinition?>? PumpChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Raised at the end of a successful K-Line read with the matched pump ID and
|
||||
/// the ECU serial number (may be empty if the ECU did not return one). Used by
|
||||
/// the parent ViewModel to detect a physical pump swap when the model ID is unchanged.
|
||||
/// </summary>
|
||||
public event Action<string /*pumpId*/, string /*serial*/>? KlineReadCompleted;
|
||||
|
||||
/// <summary>Populates the pump ID list from the configuration database.</summary>
|
||||
public void LoadPumpIds()
|
||||
{
|
||||
@@ -99,6 +109,7 @@ namespace HC_APTBS.ViewModels
|
||||
|
||||
private void LoadPump(string pumpId)
|
||||
{
|
||||
_log.Info(LogId, $"LoadPump: {pumpId}");
|
||||
var pump = _config.LoadPump(pumpId);
|
||||
if (pump == null)
|
||||
{
|
||||
@@ -217,12 +228,18 @@ namespace HC_APTBS.ViewModels
|
||||
KlineConnectError = connectErr ?? string.Empty;
|
||||
});
|
||||
|
||||
// Pump auto-selection now happens via the PumpIdentified event
|
||||
// mid-read, so there is no need to call AutoSelectPumpByKlineId here.
|
||||
// Pump auto-selection by pumpID/alias/substring already happened mid-read
|
||||
// via the PumpIdentified event. If still unmatched, try ModelReference now
|
||||
// that the full ECU text has been read (ModelRef arrives later than pumpID).
|
||||
if (CurrentPump == null && !string.IsNullOrEmpty(modelRef))
|
||||
App.Current.Dispatcher.Invoke(() => TryAutoSelectByModelRef(modelRef!));
|
||||
|
||||
// Attach K-Line info to the (now possibly auto-selected) pump.
|
||||
if (CurrentPump != null)
|
||||
CurrentPump.KlineInfo = info;
|
||||
|
||||
// Notify parent VM so it can detect physical pump swaps when the model ID is unchanged.
|
||||
KlineReadCompleted?.Invoke(CurrentPump?.Id ?? string.Empty, serial ?? string.Empty);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -252,29 +269,75 @@ namespace HC_APTBS.ViewModels
|
||||
|
||||
/// <summary>
|
||||
/// Tries to match a K-Line pump identifier to a pump in the database and auto-select it.
|
||||
/// If the K-Line ID is directly in the pump list, select it. Otherwise, try to find
|
||||
/// a pump whose ID is contained in the K-Line identifier string.
|
||||
/// Resolution order:
|
||||
/// <list type="number">
|
||||
/// <item>Exact match — K-Line ID equals a canonical pump ID.</item>
|
||||
/// <item>Alias match — K-Line ID is listed under a pump's <c><Aliases><KlineId></c> entries.</item>
|
||||
/// <item>Substring match — pump ID appears inside the K-Line ident string (legacy fallback for noisy ROM reads).</item>
|
||||
/// </list>
|
||||
/// ModelReference-based equivalence is handled separately after the full K-Line read completes
|
||||
/// (see <see cref="TryAutoSelectByModelRef"/>).
|
||||
/// </summary>
|
||||
private void AutoSelectPumpByKlineId(string klinePumpId)
|
||||
{
|
||||
// Direct match — the K-Line ID is itself a pump ID in the database.
|
||||
if (PumpIds.Contains(klinePumpId))
|
||||
var trimmed = (klinePumpId ?? string.Empty).Trim();
|
||||
if (trimmed.Length == 0)
|
||||
{
|
||||
App.Current.Dispatcher.Invoke(() => SelectedPumpId = klinePumpId);
|
||||
_log.Warning(LogId, "AutoSelectPumpByKlineId: empty K-Line identifier.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Substring match — the K-Line ident string may contain the pump ID.
|
||||
// 1. Direct match — the K-Line ID is itself a pump ID in the database.
|
||||
foreach (var id in PumpIds)
|
||||
{
|
||||
if (klinePumpId.Contains(id, StringComparison.OrdinalIgnoreCase))
|
||||
if (string.Equals(id, trimmed, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
App.Current.Dispatcher.Invoke(() => SelectedPumpId = id);
|
||||
_log.Info(LogId, $"Auto-selected '{id}' from K-Line id '{trimmed}' (exact match).");
|
||||
SelectedPumpId = id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_log.Warning(LogId, $"K-Line pump ID '{klinePumpId}' not found in pump database.");
|
||||
// 2. Alias match — the K-Line ID is registered as an alias under a canonical pump.
|
||||
var aliased = _config.FindPumpIdByKlineAlias(trimmed);
|
||||
if (!string.IsNullOrEmpty(aliased))
|
||||
{
|
||||
_log.Info(LogId, $"Auto-selected '{aliased}' from K-Line id '{trimmed}' (alias KlineId).");
|
||||
SelectedPumpId = aliased;
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Substring match — the K-Line ident string may contain the pump ID.
|
||||
foreach (var id in PumpIds)
|
||||
{
|
||||
if (trimmed.Contains(id, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_log.Info(LogId, $"Auto-selected '{id}' from K-Line id '{trimmed}' (substring match).");
|
||||
SelectedPumpId = id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_log.Warning(LogId,
|
||||
$"K-Line pump ID '{trimmed}' not found in pump database ({PumpIds.Count} candidates).");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Late-stage fallback used when the full K-Line read has completed and no pump was
|
||||
/// matched by ID/alias/substring. Looks up the canonical pump ID by ModelReference alias
|
||||
/// (e.g. <c>ME190297C150</c>). No-op if a pump is already selected.
|
||||
/// </summary>
|
||||
private void TryAutoSelectByModelRef(string modelRef)
|
||||
{
|
||||
if (CurrentPump != null) return;
|
||||
var trimmed = (modelRef ?? string.Empty).Trim();
|
||||
if (trimmed.Length == 0) return;
|
||||
|
||||
var canonical = _config.FindPumpIdByModelRef(trimmed);
|
||||
if (string.IsNullOrEmpty(canonical)) return;
|
||||
|
||||
_log.Info(LogId, $"Auto-selected '{canonical}' from ModelRef '{trimmed}' (alias ModelRef).");
|
||||
SelectedPumpId = canonical;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user