feat: add Ford VP44 unlock progress dialog, K-Line fast unlock, localization, safety dialogs, and settings
Unlock progress UI:
- UnlockProgressDialog with dark-themed progress ring, phase indicator, elapsed
time, and cancel/close buttons (non-modal, draggable borderless window)
- UnlockProgressViewModel with event-driven progress tracking via IUnlockService
- Triggers on pump selection (manual or K-Line auto-detect), not test start
UnlockService rewrite:
- Persistent CAN senders that outlive the unlock sequence (StopSenders on pump change)
- Concurrent K-Line fast unlock: awaits session Connected, sends RAM timer shortcut
({02 88 02 03 A8 01 00}), verifies via CAN TestUnlock before skipping wait
- Fix Type 1 verification (Value == 0 means unlocked, was inverted)
K-Line fast unlock support:
- IKwpService.TryFastUnlockAsync / KwpService implementation
Additional features:
- ILocalizationService with ES/EN resource dictionaries and runtime switching
- Safety dialogs: VoltageWarning, OilPumpConfirm, RpmSafetyWarning
- SettingsDialog for app configuration
- BenchService enhancements, ConfigurationService improvements, PDF report updates
- All UI strings localized via DynamicResource
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,50 +1,37 @@
|
||||
# Gap: Ford Unlock Progress UI
|
||||
# Gap: Ford Unlock Progress UI — RESOLVED
|
||||
|
||||
## Problem
|
||||
The `UnlockService` backend is fully functional (Phase 1 + Phase 2 + verification), but there is no dedicated UI for displaying unlock progress. The old app had `WUnlocker.xaml` — a modal dialog with a visual progress ring and status text.
|
||||
## Status: Implemented
|
||||
|
||||
## Current State
|
||||
- `UnlockService.StatusChanged` fires every 1000ms with `"Unlocking... {pct}% ({MM:SS})"`
|
||||
- `UnlockService.UnlockCompleted` fires once with `true`/`false`
|
||||
- `MainViewModel` subscribes and pipes status into `VerboseStatus` (displayed as plain text somewhere in MainWindow)
|
||||
- No progress bar, no percentage display, no cancel button, no dedicated dialog
|
||||
The unlock progress dialog, service refactoring, and K-Line fast unlock are fully implemented.
|
||||
|
||||
## Old UI Reference (`WUnlocker.xaml`)
|
||||
- Standalone modal `Window` (300x400px), dark background (#FF2B2929), Topmost, centered on owner
|
||||
- Decorative `Ellipse` ring (200x200, #4D4D4D stroke, 10px thick) as the focal point
|
||||
- Inside the ring: large percentage (Courier New 60pt), "P R O G R E S S" label, elapsed time (MM:SS)
|
||||
- `LBLState` at top: live lock/immo status from CAN feedback ("Bloqueada/Desbloqueada")
|
||||
- `LBLVerbose` at bottom: phase description ("Unlocking...", "Testing...", "Sending")
|
||||
- "Cerrar" (Close) button disabled until progress reaches 100%
|
||||
- Window close prevented via `OnWindowClosing` until completion
|
||||
## What was done
|
||||
|
||||
## Spec for New Implementation
|
||||
### 1. UnlockProgressDialog (View + ViewModel)
|
||||
- **`Views/Dialogs/UnlockProgressDialog.xaml`** — Dark-themed non-modal window (#2B2929), borderless with drag support, 200x200 ellipse progress ring, Courier New 60pt percentage, MM:SS elapsed time, phase indicator, linear progress bar, result text (green/red), Cancel + Close buttons
|
||||
- **`Views/Dialogs/UnlockProgressDialog.xaml.cs`** — `ForceClose()` for programmatic close, `OnWindowClosing` prevents user close until `IsComplete`, `OnMouseDrag` for window dragging
|
||||
- **`ViewModels/Dialogs/UnlockProgressViewModel.cs`** — `ObservableObject` + `IDisposable`, parses `StatusChanged` events via regex, marshals to UI thread, `CancelCommand` (Phase 1 only) / `CloseCommand` (after completion), `[NotifyCanExecuteChangedFor]` wiring
|
||||
|
||||
### UnlockDialog.xaml (View)
|
||||
- Modal dialog (MVVM, no code-behind logic)
|
||||
- Progress bar (0-100%) + percentage text
|
||||
- Elapsed time display (MM:SS)
|
||||
- Phase indicator: "Phase 1: Sending unlock signals" / "Phase 2: Testing" / "Verifying..."
|
||||
- Current unlock type indicator (Type 1 / Type 2)
|
||||
- Cancel button (disabled during Phase 2 — it cannot be cancelled once started)
|
||||
- Close button (enabled only after completion)
|
||||
- Result indicator: green checkmark (success) / red X (failed)
|
||||
### 2. UnlockService rewrite (`Services/Impl/UnlockService.cs`)
|
||||
- **Persistent CAN senders** — Start before unlock, run indefinitely until `StopSenders()` is called on pump deselection. Prevents pump from re-locking after unlock.
|
||||
- **Concurrent fast unlock** — While the 600s CAN wait runs with progress reporting, a parallel task awaits K-Line session Connected state, then:
|
||||
1. Checks if pump is already unlocked (via `VerifyUnlock`)
|
||||
2. Sends K-Line fast unlock command (`{0x02, 0x88, 0x02, 0x03, 0xA8, 0x01, 0x00}`) which writes to pump RAM to fast-forward the internal 10 min timer
|
||||
3. Waits 2s, then re-checks `VerifyUnlock`
|
||||
4. If verified → cancels remaining wait, proceeds to Phase 2 immediately
|
||||
- **`IUnlockService.StopSenders()`** — New interface method, called from `MainViewModel.CloseUnlockDialog()` on pump change
|
||||
|
||||
### UnlockViewModel.cs (ViewModel)
|
||||
- `[ObservableProperty] double Progress`
|
||||
- `[ObservableProperty] string ElapsedTime`
|
||||
- `[ObservableProperty] string Phase`
|
||||
- `[ObservableProperty] string Result`
|
||||
- `[ObservableProperty] bool IsComplete`
|
||||
- `[ObservableProperty] bool CanCancel`
|
||||
- `[RelayCommand] Cancel()` — calls `CancellationTokenSource.Cancel()`
|
||||
- Subscribe to `IUnlockService.StatusChanged` — parse percentage from status string
|
||||
- Subscribe to `IUnlockService.UnlockCompleted` — set result and enable close
|
||||
### 3. K-Line fast unlock support
|
||||
- **`IKwpService.TryFastUnlockAsync()`** — New interface method
|
||||
- **`KwpService.TryFastUnlockAsync()`** — Sends custom command over active session, returns true if no NAK (command accepted, not unlock confirmation)
|
||||
|
||||
### Integration
|
||||
- Trigger: button in MainViewModel (currently exists but needs to open the dialog)
|
||||
- The dialog should be shown via a dialog service or `Window.ShowDialog()` from MainViewModel
|
||||
- Marshal all event handlers to UI thread
|
||||
### 4. MainViewModel integration
|
||||
- **Trigger on pump selection** — `OnPumpChanged()` calls `StartUnlockIfRequired()` for pumps with `UnlockType != 0` (both manual and K-Line auto-detect)
|
||||
- **Non-modal window** — `.Show()` instead of `.ShowDialog()`, user can interact with main UI during 10 min unlock
|
||||
- **Test start guards** — `StartTestAsync` blocks if unlock is in progress or failed
|
||||
- **Cleanup on pump change** — `CloseUnlockDialog()` stops senders, cancels CTS, disposes ViewModel, force-closes window
|
||||
|
||||
### 5. Bug fix: Type 1 verification
|
||||
**Fixed.** Old code: `Lock = valor != 0` (non-zero = LOCKED). New code had `return Value != 0` (non-zero = SUCCESS). Changed to `return Value == 0`. Type 2 (`== 0xE4`) was already correct.
|
||||
|
||||
## Protocol Reference
|
||||
|
||||
@@ -54,7 +41,7 @@ The `UnlockService` backend is fully functional (Phase 1 + Phase 2 + verificatio
|
||||
| Msg1 | 0x700 | `B2 00 00 00 00 00 00 00` | 500 ms |
|
||||
| Msg2 | 0x300 | `01 48 50 C3 00 00 00 00` | 50 ms |
|
||||
| TestUnlock states | 0x700 | `B2`, `B6`, `23`, `24` (byte[0]) x2 | 500 ms each |
|
||||
| Verify | TestUnlock param | Success when value != 0 | One-shot |
|
||||
| Verify | TestUnlock param | Success when value == 0 | One-shot |
|
||||
|
||||
### Type 2 (CAN IDs 0x700 + 0x500)
|
||||
| Phase | ID | Data | Interval |
|
||||
@@ -64,11 +51,21 @@ The `UnlockService` backend is fully functional (Phase 1 + Phase 2 + verificatio
|
||||
| TestUnlock states | 0x700 | `B2`, `24`, `24`, `24` (byte[3]) x2 | 500 ms each |
|
||||
| Verify | TestUnlock param | Success when value == 0xE4 | One-shot |
|
||||
|
||||
### K-Line fast unlock (timer shortcut)
|
||||
| Command | `02 88 02 03 A8 01 00` |
|
||||
|---------|------------------------|
|
||||
| Effect | Writes pump RAM to fast-forward the internal 10 min timer |
|
||||
| Prerequisite | Active K-Line session + CAN flood senders already running |
|
||||
| ACK meaning | Command accepted (NOT unlock confirmed — must still verify via CAN TestUnlock) |
|
||||
|
||||
### Duration
|
||||
Phase 1: 600,500 ms (10 min 0.5 sec). Phase 2: ~4 sec (8 messages x 500ms). Total: ~604.5 sec.
|
||||
- Phase 1 (normal): 600,500 ms (10 min 0.5 sec)
|
||||
- Phase 1 (fast unlock): ~2 sec after K-Line ACK (+ K-Line read time)
|
||||
- Phase 2: ~4 sec (8 messages x 500 ms)
|
||||
- CAN senders: persist until pump deselection
|
||||
|
||||
## Known Issue in Unlock Verification
|
||||
The **Type 1 verification logic may be inverted** compared to the old code. Old: `Lock = (valor != 0)` meant non-zero = LOCKED. New: `Value != 0` returned as SUCCESS (unlocked). Needs hardware testing to confirm which is correct.
|
||||
### Critical: CAN sender lifecycle
|
||||
CAN flood messages must be active **before** the fast unlock attempt (otherwise the timer resets instantly) and must **continue running after** unlock completes. Stopping them causes the pump to re-lock. `StopSenders()` is only called when the pump is deselected.
|
||||
|
||||
## Missing Feature: TestImmo Check
|
||||
Old code tracked both `TestUnlock` and `TestImmo` CAN parameters and displayed combined status. New code only checks `TestUnlock`, ignoring `TestImmo` entirely. Consider adding the immobilizer state check for completeness.
|
||||
## Remaining gap: TestImmo check
|
||||
Old code tracked both `TestUnlock` and `TestImmo` CAN parameters and displayed combined status ("Inmovilizada/Liberada | Bloqueada/Desbloqueada"). New code only checks `TestUnlock`. Consider adding immobilizer state display for completeness.
|
||||
|
||||
Reference in New Issue
Block a user