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,167 @@
# 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.

347
docs/ui-structure.md Normal file
View File

@@ -0,0 +1,347 @@
# HC_APTBS — UI Structure Reference
Defines the top-level page structure, operator intent, content scope, and design
guidelines for each page. Use this as the authoritative reference when designing
or implementing any page or UserControl.
---
## Guiding principles
- **Operator intent first.** Navigation reflects what the operator is trying to
accomplish, not which components exist or which service owns a feature.
- **Separation of concerns.** Bench hardware and ECU diagnostics are logically
different domains and live on different pages — never mix them.
- **Progressive disclosure.** Dashboard is read-first. Controls deepen as the
operator navigates inward. Settings are last and guarded.
- **State-driven flow.** The Test page is not a free-form panel — it guides the
operator through a linear sequence: Plan → Preconditions → Running → Done.
- **Safety is visible.** Alarms, interlocks, and precondition failures are
surfaced prominently, never hidden in status bars or tooltips.
---
## Navigation layout
```
┌──────┬─────────────────────────────────────────┐
│ │ │
│ ⌂ │ PAGE CONTENT │
│ ⚙B │ │
│ ⚡E │ │
│ ▶T │ │
│ ■R │ │
│ ── │ │
│ ⚙S │ │
└──────┴─────────────────────────────────────────┘
```
Left sidebar, icon + label, collapsed to icon-only below a width threshold.
Settings pinned to the bottom, separated by a divider.
Global status bar at the top of the content area (not the sidebar):
CAN heartbeat indicator · K-Line session indicator · alarm count badge ·
logged-in user · app version.
---
## Pages
### 1. Dashboard
**Intent:** Confirm the bench is ready before doing anything.
**Design tone:** Read-first. Dense information, minimal interaction. Should be
readable at a glance from across the workbench.
**Content:**
| Section | Detail |
|---------|--------|
| Bench state | RPM (live), T-in, T-out, T-tank, P1, P2, QDelivery |
| Connection status | CAN: last frame timestamp / "offline"; K-Line: session open / closed |
| Active alarms | List with criticality badge (critical / warning / info). Empty state = green banner. |
| Test summary | Active test name + current phase, or last completed test name + overall pass/fail |
| Quick actions | **Start Test** (→ Test page), **Stop**, **Emergency Stop** |
**Guidelines:**
- E-Stop is the only destructive control allowed here. Style it as red, always
visible, never hidden by scroll.
- No configuration inputs on this page.
- Alarm list shows the full description text, not just a bit number.
- Offline indicators use a distinct color (e.g. amber) separate from alarm red.
- Quick-action buttons are disabled with a tooltip when prerequisites are unmet
(e.g. Start Test disabled if no pump selected or CAN offline).
**Existing components to embed:** none — this page is new.
---
### 2. Bench
**Intent:** Manually drive the hardware. Feels like an HMI panel.
**Design tone:** Control-heavy, tactile. Large inputs, clear feedback, live
values always visible alongside their controls.
**Content:**
| Group | Controls / Displays |
|-------|---------------------|
| RPM / Drive | Target RPM input, ramp rate, Start/Stop drive, direction toggle |
| Temperature | T-in / T-out / T-tank live readings, cooler relay, heater relay, PID setpoint |
| Pressure / Flow | P1, P2 live gauges, QDelivery, QOver, oil pump relay |
| Encoder / Angle | PSG, INJ, Manual angle live displays, Lock Angle input |
| Relay outputs | DepositCooler, DepositHeater, Counter, Pulse4Signal, Flasher — toggle buttons with state indicator |
| Live plots | Flowmeter chart (QDelivery, QOver vs time), pressure trace — always rendering |
**Guidelines:**
- No ECU concepts on this page (no ME, FBKW, DFI, K-Line).
- Relay buttons must show current state (on/off) as well as the toggle action.
- Live plots persist while the operator adjusts controls — they are not
collapsible or tab-hidden.
- RPM input should warn (not block) if above configured safety limit
(`AppSettings.MaxRpm`).
- Oil pump interlock: if oil pump relay is off and RPM > threshold, show
a dismissible inline warning (not a blocking dialog).
**Existing components to embed:**
- `BenchControlView` (RPM, direction, oil pump)
- `BenchParamConfigView` (bench parameter live readings)
- `FlowmeterChartView` (always-on in manual context)
- `AngleDisplayView` (encoder monitoring)
---
### 3. ECU
**Intent:** Diagnose and interact with the pump's electronic control unit via
K-Line. Logically separate from the mechanical bench.
**Design tone:** Diagnostic tool. Tabular data, structured forms, raw values
accessible but not leading.
**Sub-sections (tabs or left sub-nav within the page):**
#### 3a. Identification
- Pump model search/select from database
- K-Line read: serial number, model reference, SW version
- BIP status
- "Read ECU" button — requires K-Line session open
#### 3b. DTCs
- Fault code list (code, description, freeze-frame if available)
- Read DTCs / Clear DTCs actions
- Error history log with timestamps
#### 3c. Live Data
- ECU variable table: ME, FBKW, Temp, Tein, Status word
- Status word bit grid (bit index, label, state, color-coded per
`PumpStatusDefinition`)
- Refresh rate selector
#### 3d. Adaptation
- ME / FBKW / PreIn sliders (manual ECU output control)
- DFI read / write / auto-adjust
- Requires user authentication before write actions
#### 3e. Unlock *(Ford VP44 only)*
- Unlock type selector (Type 1 / Type 2)
- Phase 1 countdown (600.5 s) with progress bar
- Phase 2 TestUnlock with live status
- Verification result display
- Full UI spec in `docs/gap-ford-unlock-ui.md`
**Guidelines:**
- K-Line session state (open / closed) is always shown at the top of this page.
If closed, write actions are disabled with a "Session not open" tooltip.
- Adaptation and Unlock sub-sections require authenticated user
(`UserCheckDialog`).
- Raw K-Line bytes are shown only in a collapsible "Engineering" panel — not
in the primary view.
- Pump selection (3a) is a prerequisite for everything else. If no pump is
selected, show a banner on all other sub-sections.
**Existing components to embed:**
- `PumpIdentificationView` → 3a
- `StatusDisplayView` → 3c
- `DfiManageView` → 3d
- `PumpControlView` (ME/FBKW/PreIn) → 3d
---
### 4. Test
**Intent:** Run the automated test procedure. Procedural and task-oriented.
**Design tone:** Wizard-like linear flow. The operator should always know where
they are in the sequence and what comes next.
**State machine — sub-sections are sequential, not freely navigable:**
```
[Plan] ──► [Preconditions] ──► [Running] ──► [Done]
(blocked if (blocked (summary,
checklist until all → Results)
fails) checks pass)
```
#### 4a. Plan
- Test suite dropdown (selects from pump's `TestDefinition` list)
- Phase list: enable/disable individual phases, view tolerance bands
- Estimated duration
- "Next: Check Preconditions" button
#### 4b. Preconditions
Enforced checklist before the sequence starts. Each item is checked
automatically where possible, or confirmed manually:
| Check | Source |
|-------|--------|
| Pump selected | ECU page — pump DB record loaded |
| CAN bus live | `PcanAdapter` heartbeat |
| K-Line session open | `KwpService` state |
| RPM = 0 | `BenchParameterNames.RPM` |
| Oil pump on | Relay state |
| No critical alarms | Alarm list |
| User authenticated | If test requires auth |
Start button is disabled until all required checks pass. Failed checks
show inline fix links (e.g. "Go to Bench → start oil pump").
#### 4c. Running
- Phase timeline: vertical step list, current phase highlighted
- Live measurement table: parameter, current value, tolerance band, pass/fail
- Embedded: `FlowmeterChartView` + `AngleDisplayView` (visible during run)
- Controls: **Pause**, **Abort**, **Retry current phase**, **Skip phase**
- Running timer (elapsed + estimated remaining)
#### 4d. Done
- Inline summary: overall pass/fail, phase-by-phase verdict
- "View Full Results" → navigates to Results page
- "Run Again" → back to 4a (Plan) with same configuration
**Guidelines:**
- Back-navigation within the state machine is allowed (Plan ↔ Preconditions)
but not once Running has started unless the test is aborted.
- Abort requires confirmation dialog.
- QOver zero-flow safety: if `QOver == 0` while RPM > 300 and oil pump is on,
trigger emergency stop and display a blocking alert (see Known gaps in
CLAUDE.md — HIGH priority).
- Pump parameters (ME/FBKW/PreIn) must be zeroed between phases
(see Known gaps — HIGH priority).
- Per-sample real-time UI callback must update the live measurement table
during Running (see Known gaps — HIGH priority).
**Existing components to embed:**
- `TestPanelView` → 4a (Plan)
- `TestDisplayView` → 4c (Running)
- `FlowmeterChartView` → 4c (Running, inline)
- `AngleDisplayView` → 4c (Running, inline)
- `ResultDisplayView` → 4d (Done, summary) + Results page (full)
---
### 5. Results
**Intent:** Review, compare, and document completed tests.
**Design tone:** Data-focused. Clean tables, exportable. No live hardware data.
**Content:**
| Section | Detail |
|---------|--------|
| History list | Completed tests: date, pump model, serial, overall pass/fail |
| Result detail | Selected test: per-phase table, per-parameter pass/fail, measured vs tolerance |
| Trend / comparison | Same pump over multiple runs — delta on key parameters (future) |
| Report preview | PDF rendered inline or in a preview pane |
| Export | PDF export, notes/observations field before export |
**Guidelines:**
- History is read-only. No controls that affect the bench or ECU.
- PDF export requires a report dialog (operator name, client, notes) before
generating. Reuse `ReportDialog` / `ReportViewModel`.
- Observations/notes field must be present before PDF generation
(see Known gaps in CLAUDE.md — MEDIUM priority).
- Empty state (no completed tests this session): show a prompt to run a test.
- Test history persistence across sessions requires a future storage layer
(not yet implemented — scope to session-only for now).
**Existing components to embed:**
- `ResultDisplayView` (full detail view)
- `ReportDialog` (triggered by Export action)
---
### 6. Settings
**Intent:** Infrequent configuration. Not for daily operators unless necessary.
**Design tone:** Form-based. Grouped, labeled, validated. No live hardware
data visible here.
**Sub-sections:**
| Section | Content |
|---------|---------|
| Machine | CAN bitrate, K-Line COM port, encoder mode, motor direction |
| Sensors | ADC calibration per channel (min/max voltage → engineering units) |
| Limits & Protection | Safety RPM max, alarm criticality reactions, PID tuning constants |
| Alarms | Alarm bit definitions, descriptions, criticality levels |
| Users | User list, roles, password management (future: hashed) |
| Report | Company name, logo, branding colors, default template |
| Storage | Config file paths, backup/restore |
**Guidelines:**
- Changes are not applied until "Save" is clicked — no live-apply side effects.
- Sensor calibration changes require confirmation (affects all future readings).
- User management (`UserManageDialog`) is accessible from here.
- Settings are not accessible while a test is Running (nav item disabled or
shows a "test in progress" tooltip).
- All inputs use `CultureInfo.InvariantCulture` for double parsing (see Rules
in CLAUDE.md).
**Existing components to embed:**
- `SettingsDialog` content (migrate from dialog to full page)
- `DfiManageView` is in ECU > Adaptation, not here
---
## Component placement summary
| UserControl / Dialog | Page | Sub-section |
|----------------------|------|-------------|
| `BenchControlView` | Bench | RPM / Drive + Relays |
| `BenchParamConfigView` | Bench | Pressure / Flow readings |
| `FlowmeterChartView` | Bench (manual) · Test (running) | Live plots · 4c Running |
| `AngleDisplayView` | Bench · Test (running) | Encoder group · 4c Running |
| `PumpIdentificationView` | ECU | 3a Identification |
| `StatusDisplayView` | ECU | 3c Live Data |
| `DfiManageView` | ECU | 3d Adaptation |
| `PumpControlView` | ECU | 3d Adaptation |
| `TestPanelView` | Test | 4a Plan |
| `TestDisplayView` | Test | 4c Running |
| `ResultDisplayView` | Test (summary) · Results (full) | 4d Done · detail view |
| `ReportDialog` | Results | Export action |
| `UserCheckDialog` | ECU (auth gate) · Test (auth gate) | on write actions |
| `UserManageDialog` | Settings | Users sub-section |
| `KlineErrorsDialog` | ECU | triggered on DTC read errors |
| `ProgressDialog` | ECU · Test | long K-Line operations, unlock phase |
| `UnlockProgressDialog` | ECU | 3e Unlock |
| `OilPumpConfirmDialog` | Bench · Test (preconditions) | oil pump interlock |
| `RpmSafetyWarningDialog` | Bench · Test | RPM limit breach |
| `VoltageWarningDialog` | Bench | analog sensor out of range |
---
## Pages to build (status)
| Page | Status | Notes |
|------|--------|-------|
| Dashboard | **Not started** | New page, new ViewModel |
| Bench | `BenchPage.xaml` exists | Expand with live plots, angle view |
| ECU | `PumpPage.xaml` exists | Rename, add sub-sections 3b3e |
| Test | `TestsPage.xaml` exists | Add preconditions state machine |
| Results | **Not started** | New page, new ViewModel |
| Settings | Partial (`SettingsDialog`) | Migrate from dialog to full page |