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>
168 lines
7.8 KiB
Markdown
168 lines
7.8 KiB
Markdown
# Gap: Tests page — Pause / Retry-phase / Skip-phase controls
|
|
|
|
## Context
|
|
|
|
The Tests wizard (`Plan → Preconditions → Running → Done`) introduces three controls
|
|
in the Running step's bottom bar:
|
|
|
|
| Control | Purpose |
|
|
|---|---|
|
|
| **Pause** | Temporarily halts phase progression — bench coasts to a safe-RPM hold, keep-alive and CAN traffic continue, timers freeze. |
|
|
| **Retry current phase** | Aborts the current phase (without failing it), zeroes pump outputs, restarts the same phase from conditioning. |
|
|
| **Skip current phase** | Marks the current phase as "skipped" (neither passed nor failed) and advances to the next enabled phase. |
|
|
|
|
At the time of writing these three buttons exist in `Views/UserControls/TestRunningView.xaml`
|
|
rendered `IsEnabled="False"` with a `Test.Running.ComingSoon` tooltip. This document
|
|
captures the full requirements for a follow-up task that wires them to `IBenchService`.
|
|
|
|
## Required `IBenchService` API additions
|
|
|
|
```csharp
|
|
/// <summary>
|
|
/// Pauses the currently running test. The bench holds at idle RPM, oil pump stays on,
|
|
/// keep-alive continues. Phase timers are frozen. No-op if not running.
|
|
/// </summary>
|
|
Task PauseAsync(CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Resumes a paused test. Re-arms phase timers from where they were frozen.
|
|
/// Throws <see cref="InvalidOperationException"/> if not paused.
|
|
/// </summary>
|
|
Task ResumeAsync(CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Aborts the current phase (without marking it failed), zeros pump outputs
|
|
/// (ME/FBKW/PreIn → 0), and restarts the same phase from the beginning of
|
|
/// its conditioning sub-section. No-op if not running.
|
|
/// </summary>
|
|
Task RetryCurrentPhaseAsync(CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Marks the current phase as "skipped" and advances to the next enabled phase.
|
|
/// Pump outputs are zeroed between phases. No-op if at the last enabled phase
|
|
/// (in that case the test completes normally).
|
|
/// </summary>
|
|
Task SkipPhaseAsync(CancellationToken ct = default);
|
|
```
|
|
|
|
All four methods must be callable from a UI thread and must **not** block — they
|
|
queue a transition request onto the existing `BenchService` worker loop and await
|
|
completion via a `TaskCompletionSource`.
|
|
|
|
## Safe-state expectations per operation
|
|
|
|
### Pause
|
|
- RPM: ramp to **idle hold** (configurable, default 600 rpm) using the existing ramp logic.
|
|
- Oil pump relay: stays on.
|
|
- Pump outputs: unchanged (paused at current values).
|
|
- Counter / encoder relays: unchanged.
|
|
- Keep-alive loop: continues.
|
|
- Phase countdown: frozen in place. Timer resumes on `ResumeAsync`.
|
|
- Measurement buffer: continues accumulating samples while paused? **Decision needed** —
|
|
current recommendation is to discard samples received while paused, so tolerance
|
|
evaluation uses only "active" measurement time.
|
|
|
|
### Resume
|
|
- RPM: ramp from idle-hold back to the current phase's target RPM before un-freezing timers.
|
|
- Un-freeze phase timers only **after** RPM is within ±5% of target for ≥500 ms (same
|
|
condition the initial conditioning ramp uses).
|
|
|
|
### Retry
|
|
- Pump outputs: **zeroed immediately** (ME=0, FBKW=0, PreIn=0). This addresses the
|
|
HIGH-priority gap in CLAUDE.md ("Pump parameters (ME/FBKW/PreIn) not zeroed
|
|
between test phases") for the retry path specifically.
|
|
- Phase: same phase re-entered at start of conditioning.
|
|
- Results: any partial samples for the retried phase are discarded. `TestResult.Samples`
|
|
for that phase is cleared.
|
|
- `PhaseChanged` event fires with the same phase name again so the UI re-highlights the card.
|
|
|
|
### Skip
|
|
- Pump outputs: zeroed.
|
|
- Phase disposition: `TestResult` row is inserted with `Status = Skipped` (new enum value
|
|
on `TestResult.Status` or existing `Pass/Fail` with a separate `IsSkipped` flag).
|
|
- Progress: next enabled phase in the same test, or next enabled test if this was the last
|
|
phase. If no enabled phase follows, the test completes as normal (`IsTestRunning = false`).
|
|
|
|
## UI state transitions
|
|
|
|
| Button | Enabled when | Label change |
|
|
|---|---|---|
|
|
| Pause | `IsTestRunning && !IsPaused` | becomes "Resume" (`Test.Running.Resume`) while paused |
|
|
| Retry | `IsTestRunning && !IsPaused` | static — no relabel |
|
|
| Skip | `IsTestRunning && !IsPaused` | static |
|
|
| Abort | always while running (paused or not) | static |
|
|
|
|
When paused, the Retry and Skip buttons are **disabled** to simplify the state machine
|
|
(operator must Resume first, then Retry/Skip). This keeps the BenchService state space
|
|
small (Running ↔ Paused; Retry/Skip only transition inside Running).
|
|
|
|
The **Abort confirm dialog** already used for Abort should be reused for **Skip**
|
|
but with different title/message (`Test.Skip.ConfirmTitle`, `Test.Skip.ConfirmMessage`).
|
|
Retry does **not** require confirmation — it's a non-destructive restart of the current
|
|
phase only.
|
|
|
|
## Interaction with PID temperature control
|
|
|
|
PID loop (`BenchService.TemperatureLoopAsync`) should **continue** during all four
|
|
operations — temperature is an environmental condition, not a test output. The oil-pump
|
|
interlock (`IsOilPumpOn`) must stay satisfied throughout; a Pause that loses the oil
|
|
pump (e.g. operator toggles the relay via the Bench page) must **abort** the test
|
|
through the existing safety path, not silently resume with oil off.
|
|
|
|
## Interaction with the oil-pump safety interlock
|
|
|
|
The "no critical alarms" precondition is evaluated at Preconditions-step entry only.
|
|
Once Running, the existing `DashboardAlarmsViewModel.HasCritical` observer is the
|
|
authoritative safety check. Pause/Retry/Skip do **not** bypass it — any of the four
|
|
operations should fail (throw) if a critical alarm is latched when invoked, surfacing
|
|
the same warning banner the existing Running state already shows.
|
|
|
|
## Localization keys
|
|
|
|
Already added to `Resources/Strings.en.xaml` and `Resources/Strings.es.xaml`:
|
|
|
|
- `Test.Running.Pause`
|
|
- `Test.Running.Resume`
|
|
- `Test.Running.Retry`
|
|
- `Test.Running.Skip`
|
|
- `Test.Running.ComingSoon` (tooltip while buttons remain disabled)
|
|
- `Test.Running.Abort`
|
|
|
|
To add when Skip confirmation lands:
|
|
- `Test.Skip.ConfirmTitle`
|
|
- `Test.Skip.ConfirmMessage`
|
|
- `Test.Skip.Confirm`
|
|
- `Test.Skip.Cancel`
|
|
|
|
## Relation to existing HIGH-priority gap
|
|
|
|
CLAUDE.md ("Pump parameters (ME/FBKW/PreIn) not zeroed between test phases") is the
|
|
broader gap. This document's Retry path implements the zero-between-phases behaviour
|
|
for the retry transition specifically. The general phase-to-phase zeroing in
|
|
`BenchService.RunPhaseAsync` must also be fixed — tracked separately.
|
|
|
|
## Manual verification checklist
|
|
|
|
1. Start a multi-phase test. During a measurement phase click **Pause** — RPM drops
|
|
to idle, countdown freezes, button relabels to "Resume". Flow charts stop
|
|
advancing, oil pump stays on, no critical alarm.
|
|
2. Click **Resume** — RPM ramps back to the phase target, countdown unfreezes from
|
|
the exact value, label returns to "Pause".
|
|
3. During a measurement phase with partial samples, click **Retry** — pump outputs
|
|
zero, phase restarts at conditioning, card highlight refreshes, previous samples
|
|
cleared from `TestResult`.
|
|
4. Click **Skip** on the second of three enabled phases — confirm dialog opens
|
|
(`Test.Skip.ConfirmMessage`). Cancel: test continues. Confirm: phase disposition
|
|
set to Skipped, pump outputs zeroed, next enabled phase starts conditioning.
|
|
5. Skip the final enabled phase — test completes, transitions to Done step.
|
|
6. Trigger a critical alarm mid-Pause — test aborts via the existing critical-alarm
|
|
path, Done step shows "Failed — safety stop".
|
|
7. Toggle language mid-Pause — button text updates to localized "Resume" / "Pause".
|
|
8. Unplug CAN during Pause — CAN-liveness watchdog aborts the test.
|
|
|
|
## Out of scope (acknowledged)
|
|
|
|
- Skipping / retrying a phase while paused (requires Resume first).
|
|
- A Skip-all-remaining-phases shortcut.
|
|
- A "Retry this test" that restarts from the first phase of the current test.
|