feat: page-based navigation shell + Tests page wizard

Replace the monolithic MainWindow with a SelectedPage-driven shell
(Dashboard / Pump / Bench / Tests / Results / Settings). The Tests
page gets the Plan -> Preconditions -> Running -> Done wizard from
ui-structure.md \u00a74, backed by a 7-item precondition gate and
shared sub-views (PhaseCardView / TestSectionView / GraphicIndicatorView)
extracted from the now-deleted monolithic TestPanelView.

New VMs / views:
- Tests wizard: TestPreconditions, PhaseCard, GraphicIndicator,
  TestSection, TestPlan, TestRunning, TestDone
- Dashboard panels: DashboardConnection, DashboardReadings,
  DashboardAlarms, InterlockBanner, ResultHistory
- Pump / bench panels: PumpIdentificationPanel, PumpLiveData,
  UnlockPanel, BenchDriveControl, BenchReadings, RelayBank,
  TemperatureControl, DtcList, AuthGate
- Dialogs: generic ConfirmDialog, UserManageDialog, UserPromptDialog

Supporting changes:
- IsOilPumpOn exposed on MainViewModel for precondition evaluation
- RequiresAuth added to TestDefinition (XML round-trip)
- BipStatusDefinition + CompletedTestRun models
- ~35 new Test.* localization keys (en + es)
- Settings moved from modal dialog to full page
- Pause / Retry / Skip stubs in TestRunningView; full spec in
  docs/gap-test-running-controls.md for follow-up implementation
- docs/ui-structure.md captures the wizard design

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-18 13:11:34 +02:00
parent 37d099cdbd
commit 0280a2fad1
110 changed files with 8008 additions and 1115 deletions

View File

@@ -0,0 +1,69 @@
using System.Globalization;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HC_APTBS.Models;
using HC_APTBS.Services;
namespace HC_APTBS.ViewModels
{
/// <summary>
/// ViewModel for the bench Temperature Control panel:
/// PID setpoint input and heater / deposit cooler / inlet cooler relay toggles.
/// </summary>
public sealed partial class TemperatureControlViewModel : ObservableObject
{
private readonly IBenchService _bench;
private readonly IConfigurationService _config;
/// <summary>Operator-entered PID setpoint in °C (text to allow invariant parsing).</summary>
[ObservableProperty] private string _setpointText = "40";
/// <summary>Tolerance ±°C applied when the setpoint is committed.</summary>
[ObservableProperty] private string _toleranceText = "2";
/// <summary>True when the deposit heater relay is energised.</summary>
[ObservableProperty] private bool _isHeaterOn;
/// <summary>True when the deposit cooler relay is energised.</summary>
[ObservableProperty] private bool _isDepositCoolerOn;
/// <summary>True when the T-in (inlet) cooler relay is energised.</summary>
[ObservableProperty] private bool _isTinCoolerOn;
/// <param name="benchService">Bench service for PID setpoint and relay control.</param>
/// <param name="configService">Configuration service — seeds setpoint from <see cref="AppSettings"/>.</param>
public TemperatureControlViewModel(IBenchService benchService, IConfigurationService configService)
{
_bench = benchService;
_config = configService;
// Seed setpoint from global temperature band midpoint, tolerance from its half-width.
int min = _config.Settings.TempMin;
int max = _config.Settings.TempMax;
int mid = (min + max) / 2;
int tol = (max - min) / 2;
SetpointText = mid.ToString(CultureInfo.InvariantCulture);
ToleranceText = tol.ToString(CultureInfo.InvariantCulture);
}
partial void OnIsHeaterOnChanged(bool value)
=> _bench.SetRelay(RelayNames.DepositHeater, value);
partial void OnIsDepositCoolerOnChanged(bool value)
=> _bench.SetRelay(RelayNames.DepositCooler, value);
partial void OnIsTinCoolerOnChanged(bool value)
=> _bench.SetRelay(RelayNames.TinCooler, value);
/// <summary>Applies the PID setpoint entered by the operator.</summary>
[RelayCommand]
private void ApplySetpoint()
{
if (!double.TryParse(SetpointText, NumberStyles.Float, CultureInfo.InvariantCulture, out double sp))
return;
if (!double.TryParse(ToleranceText, NumberStyles.Float, CultureInfo.InvariantCulture, out double tol))
tol = 2;
_bench.SetTemperatureSetpoint(sp, tol);
}
}
}