feat: implement SavePump/SaveAlarms, fix config round-trip bugs, redesign PDF reports

Config system fixes:
- Implement SavePump() — full XML serialization with insert/update by pump ID
- Add CanBusParameter.ToPumpXml() for legacy P1-P6 pump param format
- Fix LastRotationDirection never loaded in LoadSettings()
- Add SaveAlarms() to ConfigurationService and IConfigurationService
- Remove dead fields AppSettings.Clients and AppSettings.PumpIds

PDF report redesign:
- Professional layout with charts, verdict badges, and tolerance bands
- Add ReportChartRenderer (SVG) and ReportTheme styling constants
- Embed default_logo.png as fallback report logo

Documentation:
- Add gap analysis docs (config validation, ford unlock, missing features)
- Update CLAUDE.md architecture, known gaps, and debt tracking

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-15 15:21:22 +02:00
parent 4891eb6812
commit c617854c09
15 changed files with 1495 additions and 141 deletions

View File

@@ -0,0 +1,117 @@
# Gap: Missing Features
## 1. Localization System (HIGH)
### Problem
The old app supported runtime Spanish/English switching via `R.LoadLanguage()` which swapped WPF `ResourceDictionary` files (`StringResources.xaml` for Spanish, `StringResourcesEN.xaml` for English). The new app has `AppSettings.Language` ("ESP"/"ENG") persisted but NOT used — all UI strings are hardcoded in English.
### Implementation approach
1. Create `Resources/StringResources.xaml` (Spanish) and `Resources/StringResources.en.xaml` (English) with keyed `sys:String` entries
2. Reference strings in XAML via `{DynamicResource key}` (not `StaticResource` — dynamic allows runtime swap)
3. Add `LanguageService` or extension method that merges the correct dictionary into `Application.Current.Resources`
4. Wire language toggle to `AppSettings.Language` change
5. Default resource dictionary should be Spanish (the primary user base)
### Scope
All user-facing text: labels, button text, dialog messages, status messages, report text, error messages. Roughly 200-300 strings.
## 2. User Credential Encryption (HIGH)
### Problem
Users stored as plaintext `user:password` pairs in `config.xml`. Default is `admin:admin`. Old system used AES-256 (Rijndael CBC, PBKDF2 1000 iterations) via `Encrypter.cs`.
### Recommended approach
Don't re-implement the old Rijndael scheme (it used obsolete primitives and weak iteration count). Instead:
1. **Hash passwords** with `Rfc2898DeriveBytes` using HMAC-SHA256, 600,000 iterations, random 16-byte salt
2. Store as `user:salt:hash` in config.xml
3. `ValidateUser()` computes hash of input password with stored salt and compares
4. Migration: on first load of old-format `user:password` entries, hash them and rewrite
This is more secure than the old encrypted-but-reversible approach. Passwords become irreversible.
### Files to modify
- `Models/BenchConfiguration.cs` — change `Users` property format
- `Services/Impl/ConfigurationService.cs``ValidateUser()`, `GetUsers()`, `UpdateUsers()`, `SaveSettings()`
## 3. KlineIDs Auto-Mapping (MEDIUM)
### Problem
Old system remembered K-Line ID → pump ID associations via `Settings.Default.KlineIDs` (comma-separated `klineID:pumpID` string). When a pump was connected and identified via K-Line, the system could auto-select it next time. New system has no equivalent.
### Implementation
1. Add `Dictionary<string, string> KlineIdMap` to `AppSettings`
2. Persist as `<KlineIDs>kline1:pump1,kline2:pump2</KlineIDs>` in config.xml
3. In `KwpService` or `PumpIdentificationViewModel`: after successful K-Line read, save the mapping
4. On pump connection: look up K-Line ID in map, auto-select pump if found
## 4. Test Phase Timer/Countdown Display (MEDIUM)
### Problem
During test execution, conditioning and measurement phases have timed durations (e.g., 10 sec conditioning, 30 sec measurement). The old system showed a visual countdown. The new system fires `VerboseMessage` events with text like "Conditioning: 8s remaining" but there's no dedicated countdown UI.
### Implementation
- Add countdown properties to `TestDisplayViewModel`: `RemainingSeconds`, `PhaseProgress` (0-1), `PhaseName`
- Fire `PhaseTimerTick` event from `BenchService` with remaining seconds
- Display as a circular progress indicator or large countdown text in `TestDisplayView`
## 5. QOver Zero-Flow Safety Check (HIGH — Safety)
### Problem
The old system had a safety check: if `QOver == 0` while RPM > 300 and oil pump is on, trigger emergency stop (leak/blockage detection). This is completely absent from the new codebase.
### Implementation
Add to `BenchService` or the ViewModel's bench monitoring loop:
```csharp
if (qOverValue == 0 && benchRpm > 300 && IsRelayOn(RelayNames.OilPump))
{
EmergencyStop();
_log.Error(LogId, "QOver zero-flow safety triggered: oil flow blocked while motor running");
}
```
Debounce for 2-3 seconds to avoid false positives during startup transients.
## 6. Alarm Bit Recording During Tests (HIGH)
### Problem
`PhaseDefinition` has `ErrorBits` list and `RecordErrorBit(int bit)` method, but no code in `BenchService` ever calls it. The old system tracked which alarm bits fired during each test phase for inclusion in the report.
### Implementation
In `BenchService.MeasurePhaseAsync()`, subscribe to alarm parameter changes. When an alarm bit transitions to active during a measurement phase, call `phase.RecordErrorBit(bit)`. The error bits are already rendered in the PDF report (PdfService checks `phase.ErrorBits`).
## 7. Per-Sample Real-Time UI Callback (MEDIUM)
### Problem
Old system fired `ITestParameterListener.OnValueUpdate` for every measurement sample, enabling live chart updates during test execution. New system silently collects samples with no per-sample event.
### Implementation
Add an event to `IBenchService`:
```csharp
event Action<string, double, double>? MeasurementSampled; // paramName, value, timestamp
```
Fire in `MeasurePhaseAsync` after each sample. ViewModel subscribes to update live charts.
## 8. Pump Parameter Zeroing Between Phases (MEDIUM)
### Problem
Old system called `StopPump()` between phases, which zeroed ME/FBKW/PreIn values. New system calls `SetRpm(0)` but does NOT zero pump injection parameters. This means the pump may continue injecting fuel between phases at the previous phase's setpoint.
### Implementation
Add a `StopPumpParameters()` method to `BenchService` that sets ME, FBKW, and PreIn to 0 and sends the CAN frame. Call it at the end of each phase (currently line 776 in BenchService.cs — after `SetRpm(0)`).
## 9. PDF Report Observations Section (LOW)
### Problem
Old report had an "Observaciones" free-text section at the bottom. New `IPdfService.GenerateReport()` has no observations parameter and no observations rendering.
### Implementation
1. Add `string? observations` parameter to `IPdfService.GenerateReport()`
2. Add a text section at the bottom of the PDF body in `PdfService`
3. Add an observations text box to `ReportDialog`/`ReportViewModel`
## 10. PDF Auto-Open After Generation (LOW)
### Problem
Old code called `Process.Start(filePath)` to open the generated PDF. New code returns the path but doesn't open it.
### Implementation
After `GenerateReport()` returns the path, call `Process.Start(new ProcessStartInfo(path) { UseShellExecute = true })` in the ViewModel.