# Gap: Ford Unlock Progress UI — RESOLVED ## Status: Implemented The unlock progress dialog, service refactoring, and K-Line fast unlock are fully implemented. ## What was done ### 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 ### 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 ### 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) ### 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 ### Type 1 (CAN IDs 0x700 + 0x300) | Phase | ID | Data | Interval | |-------|----|------|----------| | 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 | ### Type 2 (CAN IDs 0x700 + 0x500) | Phase | ID | Data | Interval | |-------|----|------|----------| | Msg1 | 0x700 | `00 00 00 B2 00 00 00 00` | 500 ms | | Msg2 | 0x500 | `00 00 00 00 78 00 00 00` | 50 ms | | 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 (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 ### 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. ## 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.