Files
HC_APTBS/ViewModels/TestDisplayViewModel.cs
LucianoDev 37d099cdbd feat: add Ford VP44 unlock progress dialog, K-Line fast unlock, localization, safety dialogs, and settings
Unlock progress UI:
- UnlockProgressDialog with dark-themed progress ring, phase indicator, elapsed
  time, and cancel/close buttons (non-modal, draggable borderless window)
- UnlockProgressViewModel with event-driven progress tracking via IUnlockService
- Triggers on pump selection (manual or K-Line auto-detect), not test start

UnlockService rewrite:
- Persistent CAN senders that outlive the unlock sequence (StopSenders on pump change)
- Concurrent K-Line fast unlock: awaits session Connected, sends RAM timer shortcut
  ({02 88 02 03 A8 01 00}), verifies via CAN TestUnlock before skipping wait
- Fix Type 1 verification (Value == 0 means unlocked, was inverted)

K-Line fast unlock support:
- IKwpService.TryFastUnlockAsync / KwpService implementation

Additional features:
- ILocalizationService with ES/EN resource dictionaries and runtime switching
- Safety dialogs: VoltageWarning, OilPumpConfirm, RpmSafetyWarning
- SettingsDialog for app configuration
- BenchService enhancements, ConfigurationService improvements, PDF report updates
- All UI strings localized via DynamicResource

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 13:22:48 +02:00

124 lines
4.9 KiB
C#

using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using HC_APTBS.Models;
using HC_APTBS.Services;
namespace HC_APTBS.ViewModels
{
/// <summary>
/// Represents the display state for one phase within the test list UI.
/// Binds to a single row in the test phase grid.
/// </summary>
public sealed partial class PhaseRowViewModel : ObservableObject
{
/// <summary>Display name of the phase.</summary>
[ObservableProperty] private string _name = string.Empty;
/// <summary>True while this phase is actively executing.</summary>
[ObservableProperty] private bool _isActive;
/// <summary>True when the phase has completed and passed all criteria.</summary>
[ObservableProperty] private bool _isPassed;
/// <summary>True when the phase has completed but failed one or more criteria.</summary>
[ObservableProperty] private bool _isFailed;
/// <summary>Whether this phase is enabled for the current test run.</summary>
[ObservableProperty] private bool _isEnabled = true;
/// <summary>Short result string shown in the row (e.g. "PASS", "FAIL", "…").</summary>
[ObservableProperty] private string _resultText = string.Empty;
}
/// <summary>
/// ViewModel for the TestDisplay user control.
///
/// <para>
/// Shows the current test name, the list of phases with their pass/fail status,
/// and the verbose status message from the bench service.
/// </para>
/// </summary>
public sealed partial class TestDisplayViewModel : ObservableObject
{
private readonly ILocalizationService _loc;
/// <summary>Initialises a new test display with a localization service.</summary>
public TestDisplayViewModel(ILocalizationService loc) => _loc = loc;
// ── Properties ────────────────────────────────────────────────────────────
/// <summary>Name of the test currently being executed (e.g. "F", "SVME").</summary>
[ObservableProperty] private string _testName = string.Empty;
/// <summary>Current verbose status line from the bench service.</summary>
[ObservableProperty] private string _statusText = string.Empty;
/// <summary>Estimated total remaining time (seconds) for the entire test sequence.</summary>
[ObservableProperty] private int _remainingSeconds;
/// <summary>True while a test sequence is in progress.</summary>
[ObservableProperty] private bool _isRunning;
/// <summary>Phase rows shown in the phase list.</summary>
public ObservableCollection<PhaseRowViewModel> Phases { get; } = new();
// ── Public API ────────────────────────────────────────────────────────────
/// <summary>
/// Populates the phase list from a <see cref="TestDefinition"/>.
/// Call when the active test changes.
/// </summary>
public void LoadTest(TestDefinition test)
{
TestName = test.Name;
Phases.Clear();
foreach (var phase in test.Phases)
{
Phases.Add(new PhaseRowViewModel
{
Name = phase.Name,
IsEnabled = phase.Enabled,
ResultText = phase.Enabled ? "\u2013" : _loc.GetString("Common.Disabled")
});
}
}
/// <summary>
/// Marks a phase as active (running), clearing any previous active state.
/// </summary>
public void SetActivePhase(string phaseName)
{
StatusText = phaseName;
foreach (var row in Phases)
{
row.IsActive = row.Name == phaseName;
}
}
/// <summary>Updates a phase row with the result of a completed phase.</summary>
public void SetPhaseResult(string phaseName, bool passed)
{
foreach (var row in Phases)
{
if (row.Name != phaseName) continue;
row.IsActive = false;
row.IsPassed = passed;
row.IsFailed = !passed;
row.ResultText = passed ? _loc.GetString("Common.Pass") : _loc.GetString("Common.Fail");
break;
}
}
/// <summary>Clears all phase result states for a fresh test run.</summary>
public void ResetResults()
{
foreach (var row in Phases)
{
row.IsActive = false;
row.IsPassed = false;
row.IsFailed = false;
row.ResultText = row.IsEnabled ? "\u2013" : _loc.GetString("Common.Disabled");
}
}
}
}