diff --git a/App.xaml b/App.xaml index 3a364b7..cd96ff6 100644 --- a/App.xaml +++ b/App.xaml @@ -6,6 +6,8 @@ + + diff --git a/App.xaml.cs b/App.xaml.cs index d2f826c..5501868 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -1,6 +1,7 @@ using System.Windows; using HC_APTBS.Infrastructure.Logging; using HC_APTBS.Infrastructure.Pcan; +using HC_APTBS.Models; using HC_APTBS.Services; using HC_APTBS.Services.Impl; using HC_APTBS.ViewModels; @@ -30,6 +31,12 @@ public partial class App : Application ConfigureServices(services); _serviceProvider = services.BuildServiceProvider(); + // Wire runtime-warning hooks on pure Model classes before any config is loaded. + // Keeps the Models layer DI-free while still routing warnings through IAppLogger. + var logger = _serviceProvider.GetRequiredService(); + CanBusParameter.WarningLogger = logger.Warning; + SensorConfiguration.WarningLogger = logger.Warning; + // Initialise the ViewModel (loads pump IDs, starts refresh timer, connects CAN). var mainVm = _serviceProvider.GetRequiredService(); await mainVm.InitialiseAsync(); diff --git a/Converters/EnumToIntConverter.cs b/Converters/EnumToIntConverter.cs new file mode 100644 index 0000000..b6e3bb6 --- /dev/null +++ b/Converters/EnumToIntConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace HC_APTBS.Converters +{ + /// + /// Two-way converter that maps an enum value to/from its underlying value. + /// + /// Used to bind TabControl.SelectedIndex (int) to a ViewModel enum property. + /// Convert and ConvertBack both honor the actual enum type — pass targetType as the + /// enum type when converting back. + /// + public sealed class EnumToIntConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + => value is Enum ? System.Convert.ToInt32(value) : 0; + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is int i && targetType.IsEnum) + return Enum.ToObject(targetType, i); + return System.Windows.Data.Binding.DoNothing; + } + } +} diff --git a/Infrastructure/Pcan/PcanAdapter.cs b/Infrastructure/Pcan/PcanAdapter.cs index 7ea18d5..582a857 100644 --- a/Infrastructure/Pcan/PcanAdapter.cs +++ b/Infrastructure/Pcan/PcanAdapter.cs @@ -237,9 +237,11 @@ namespace HC_APTBS.Infrastructure.Pcan foreach (var param in parameters) { if (param.IsReceive) continue; - uint raw = (uint)param.GetTransmitValue(); - msg.DATA[param.ByteH] = (byte)((raw & 0xFF00) >> 8); - msg.DATA[param.ByteL] = (byte)(raw & 0x00FF); + // Cast to int first so negative values (e.g. FBKW) get proper + // two's complement representation in the 16-bit CAN field. + ushort raw = unchecked((ushort)(int)param.GetTransmitValue()); + msg.DATA[param.ByteH] = (byte)(raw >> 8); + msg.DATA[param.ByteL] = (byte)(raw & 0xFF); } CurrentStatus = PCANBasic.Write(_channel, ref msg); diff --git a/MainWindow.xaml b/MainWindow.xaml index d51d82f..09eabb0 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -5,6 +5,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:HC_APTBS.ViewModels" xmlns:uc="clr-namespace:HC_APTBS.Views.UserControls" + xmlns:pages="clr-namespace:HC_APTBS.Views.Pages" xmlns:models="clr-namespace:HC_APTBS.Models" mc:Ignorable="d" Title="{DynamicResource App.Title}" @@ -15,62 +16,93 @@ Background="#FFEDEDED" Closing="OnWindowClosing"> - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - diff --git a/Views/UserControls/TestPanelView.xaml.cs b/Views/UserControls/TestPanelView.xaml.cs deleted file mode 100644 index 3a056e1..0000000 --- a/Views/UserControls/TestPanelView.xaml.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Windows.Controls; - -namespace HC_APTBS.Views.UserControls -{ - /// - /// Interaction logic for TestPanelView.xaml. - /// All logic resides in . - /// - public partial class TestPanelView : UserControl - { - /// Initialises a new instance of the class. - public TestPanelView() - { - InitializeComponent(); - } - } -} diff --git a/Views/UserControls/TestPlanView.xaml b/Views/UserControls/TestPlanView.xaml new file mode 100644 index 0000000..6f08481 --- /dev/null +++ b/Views/UserControls/TestPlanView.xaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/UserControls/TestPlanView.xaml.cs b/Views/UserControls/TestPlanView.xaml.cs new file mode 100644 index 0000000..3b2d39b --- /dev/null +++ b/Views/UserControls/TestPlanView.xaml.cs @@ -0,0 +1,16 @@ +using System.Windows.Controls; + +namespace HC_APTBS.Views.UserControls +{ + /// + /// Plan step of the Tests wizard — phase enable/disable and duration preview. + /// DataContext is expected to be a . + /// + public partial class TestPlanView : UserControl + { + public TestPlanView() + { + InitializeComponent(); + } + } +} diff --git a/Views/UserControls/TestPreconditionsView.xaml b/Views/UserControls/TestPreconditionsView.xaml new file mode 100644 index 0000000..cbef64e --- /dev/null +++ b/Views/UserControls/TestPreconditionsView.xaml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Views/UserControls/UnlockPanelView.xaml.cs b/Views/UserControls/UnlockPanelView.xaml.cs new file mode 100644 index 0000000..d5391a0 --- /dev/null +++ b/Views/UserControls/UnlockPanelView.xaml.cs @@ -0,0 +1,17 @@ +using System.Windows.Controls; + +namespace HC_APTBS.Views.UserControls +{ + /// + /// Inline unlock panel (Pump page §3.e). DataContext is the + /// shared UnlockProgressViewModel also driving the floating + /// progress dialog. + /// + public partial class UnlockPanelView : UserControl + { + public UnlockPanelView() + { + InitializeComponent(); + } + } +} diff --git a/docs/gap-test-running-controls.md b/docs/gap-test-running-controls.md new file mode 100644 index 0000000..65a6c3a --- /dev/null +++ b/docs/gap-test-running-controls.md @@ -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 +/// +/// 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. +/// +Task PauseAsync(CancellationToken ct = default); + +/// +/// Resumes a paused test. Re-arms phase timers from where they were frozen. +/// Throws if not paused. +/// +Task ResumeAsync(CancellationToken ct = default); + +/// +/// 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. +/// +Task RetryCurrentPhaseAsync(CancellationToken ct = default); + +/// +/// 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). +/// +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. diff --git a/docs/ui-structure.md b/docs/ui-structure.md new file mode 100644 index 0000000..6df79f7 --- /dev/null +++ b/docs/ui-structure.md @@ -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 3b–3e | +| Test | `TestsPage.xaml` exists | Add preconditions state machine | +| Results | **Not started** | New page, new ViewModel | +| Settings | Partial (`SettingsDialog`) | Migrate from dialog to full page |