Bundles several feature streams that have been iterating on the working tree: - Developer Tools page (Debug-only via DEVELOPER_TOOLS symbol): hosts the identification card, manual KWP write + transaction log, ROM/EEPROM dump card with progress banner and completion message, persisted custom-commands library, persisted EEPROM passwords library. New service primitives: IKwpService.SendRawCustomAsync / ReadEepromAsync / ReadRomEepromAsync. Persistence mirrors the Clients XML pattern in two new files (custom_commands.xml, eeprom_passwords.xml). - Auto-test orchestrator (IAutoTestOrchestrator + AutoTestState): linear K-Line read -> unlock -> bench-on -> test sequence with snackbar UI and progress dialog VM, gated on dashboard alarms. - BIP-STATUS display: BipDisplayViewModel + BipDisplayView, RAM read at 0x0106 via IKwpService.ReadBipStatusAsync; status definitions in BipStatusDefinition. - Tests page redesign: TestSectionCard + PhaseTileView replacing the old TestPlanView/TestRunningView/TestDoneView/TestPreconditionsView/ TestSectionView controls and their VMs. - Pump command sliders: Fluent thick-track style with overhang thumb, click-anywhere-and-drag, mouse-wheel adjustment. - Window startup: app.manifest declares PerMonitorV2 DPI awareness, MainWindow installs a WM_GETMINMAXINFO hook in OnSourceInitialized and maximizes there (after the hook is in place) so the app fits the work area exactly on any display configuration. - Misc: PercentToPixelsConverter, seed_aliases.py one-shot pump-alias importer, tools/Import-BipStatus.ps1, kline_eeprom_spec.md and dump-functions reference docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
215 lines
12 KiB
C#
215 lines
12 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using HC_APTBS.Models;
|
||
|
||
namespace HC_APTBS.Services
|
||
{
|
||
/// <summary>
|
||
/// Provides KWP2000 / KW1281 diagnostics operations over the ISO K-Line
|
||
/// via an FTDI USB-to-K-Line adapter.
|
||
/// </summary>
|
||
public interface IKwpService
|
||
{
|
||
// ── Session lifecycle ─────────────────────────────────────────────────────
|
||
|
||
/// <summary>Current state of the persistent K-Line session.</summary>
|
||
KLineConnectionState KLineState { get; }
|
||
|
||
/// <summary>
|
||
/// Raised whenever the K-Line session state transitions.
|
||
/// Fires on a background thread; consumers must marshal to the UI thread.
|
||
/// </summary>
|
||
event Action<KLineConnectionState>? KLineStateChanged;
|
||
|
||
/// <summary>
|
||
/// Opens a persistent K-Line session: performs 5-baud slow-init,
|
||
/// reads ECU info, then starts a background keep-alive loop (~1 s interval).
|
||
/// </summary>
|
||
/// <param name="port">FTDI serial number or COM port identifier.</param>
|
||
/// <param name="ct">Cancellation token.</param>
|
||
Task ConnectAsync(string port, CancellationToken ct = default);
|
||
|
||
/// <summary>
|
||
/// Stops the keep-alive loop, sends EndCommunication to the ECU,
|
||
/// and disposes the FTDI interface.
|
||
/// </summary>
|
||
void Disconnect();
|
||
|
||
// ── Progress reporting ────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Raised during long operations to report completion percentage and a status message.
|
||
/// Always marshalled to the UI thread by the implementation.
|
||
/// </summary>
|
||
event Action<int, string>? ProgressChanged;
|
||
|
||
// ── Full ECU read ─────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Connects to the pump ECU over K-Line and reads all available identification data,
|
||
/// fault codes, DFI value, serial number, and software versions.
|
||
/// </summary>
|
||
/// <param name="port">FTDI serial number or COM port identifier.</param>
|
||
/// <param name="pumpVersion">KWP protocol variant (0=V1, 1=V2, 2=V3/V4).</param>
|
||
/// <param name="ct">Cancellation token.</param>
|
||
/// <returns>
|
||
/// Dictionary with keys from <see cref="HC_APTBS.Models.KlineKeys"/>.
|
||
/// The <c>result</c> key is "1" on success, "0" on failure.
|
||
/// </returns>
|
||
Task<Dictionary<string, string>> ReadAllInfoAsync(
|
||
string port, int pumpVersion, CancellationToken ct = default);
|
||
|
||
// ── DTC operations ────────────────────────────────────────────────────────
|
||
|
||
/// <summary>Reads all current diagnostic trouble codes from the ECU.</summary>
|
||
/// <param name="port">FTDI serial number or COM port identifier.</param>
|
||
/// <param name="ct">Cancellation token.</param>
|
||
/// <returns>Formatted fault code string, or "No fault codes".</returns>
|
||
Task<string> ReadFaultCodesAsync(string port, CancellationToken ct = default);
|
||
|
||
/// <summary>Clears all diagnostic trouble codes and returns the post-clear state.</summary>
|
||
/// <param name="port">FTDI serial number or COM port identifier.</param>
|
||
/// <param name="ct">Cancellation token.</param>
|
||
/// <returns>Post-clear fault code string.</returns>
|
||
Task<string> ClearFaultCodesAsync(string port, CancellationToken ct = default);
|
||
|
||
// ── DFI calibration ───────────────────────────────────────────────────────
|
||
|
||
/// <summary>Reads the current DFI calibration angle from EEPROM address 0x0044.</summary>
|
||
/// <param name="port">FTDI serial number or COM port identifier.</param>
|
||
/// <param name="ct">Cancellation token.</param>
|
||
/// <returns>DFI value in degrees as a formatted string.</returns>
|
||
Task<string> ReadDfiAsync(string port, CancellationToken ct = default);
|
||
|
||
/// <summary>
|
||
/// Writes a new DFI calibration angle to EEPROM address 0x0044.
|
||
/// </summary>
|
||
/// <param name="port">FTDI serial number or COM port identifier.</param>
|
||
/// <param name="dfi">Target DFI angle (degrees, range ±1.45°).</param>
|
||
/// <param name="version">KWP protocol variant (selects the authentication password).</param>
|
||
/// <param name="ct">Cancellation token.</param>
|
||
/// <returns>Verified DFI value read back from EEPROM after the write.</returns>
|
||
Task<string> WriteDfiAsync(
|
||
string port, float dfi, int version, CancellationToken ct = default);
|
||
|
||
/// <summary>
|
||
/// Writes DFI to EEPROM, then triggers a pump power cycle via
|
||
/// <see cref="PumpDisconnectRequested"/> / <see cref="PumpReconnectRequested"/>
|
||
/// to activate the new calibration.
|
||
/// </summary>
|
||
Task<string> WriteDfiAndRestartAsync(
|
||
string port, float dfi, int version, CancellationToken ct = default);
|
||
|
||
// ── Device detection ──────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Enumerates connected FTDI USB devices and returns the serial number of the
|
||
/// first one found, or <see langword="null"/> if none are connected.
|
||
/// </summary>
|
||
string? DetectKLinePort();
|
||
|
||
/// <summary>
|
||
/// The FTDI serial number of the device that is currently holding an open
|
||
/// K-Line session, or <see langword="null"/> when no session is active.
|
||
/// </summary>
|
||
string? ConnectedPort { get; }
|
||
|
||
// ── Mid-read notifications ────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Raised during <see cref="ReadAllInfoAsync"/> as soon as the pump identifier
|
||
/// string has been read from ROM, before the full read completes.
|
||
/// Fires on a background thread; consumers must marshal to the UI thread.
|
||
/// </summary>
|
||
event Action<string>? PumpIdentified;
|
||
|
||
/// <summary>
|
||
/// Raised during <see cref="ReadAllInfoAsync"/> when the DFI calibration
|
||
/// value has been read from EEPROM. Parameter is the DFI angle in degrees.
|
||
/// Fires on a background thread; consumers must marshal to the UI thread.
|
||
/// </summary>
|
||
event Action<double>? DfiRead;
|
||
|
||
// ── Fast immobilizer unlock ───────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Attempts a fast immobilizer unlock by sending a KWP custom command
|
||
/// over an existing K-Line session. The RAM address byte written by the
|
||
/// command is selected by <paramref name="unlockType"/>: <c>1</c> → <c>0xA8</c>,
|
||
/// <c>2</c> → <c>0xE8</c>. Any other value is rejected and returns
|
||
/// <see langword="false"/>.
|
||
/// </summary>
|
||
/// <param name="unlockType">Pump unlock variant (1 or 2).</param>
|
||
/// <returns>
|
||
/// <see langword="true"/> when the command was acknowledged,
|
||
/// <see langword="false"/> on NAK, no active session, or unsupported type.
|
||
/// </returns>
|
||
Task<bool> TryFastUnlockAsync(int unlockType);
|
||
|
||
// ── Raw custom KWP packet (developer use) ─────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Sends a raw KWP1281 custom packet over the persistent K-Line session and
|
||
/// returns the bytes of every response packet. The supplied <paramref name="payload"/>
|
||
/// is the title byte plus body — framing (length + counter + end byte) is added
|
||
/// by the lower-level transport.
|
||
///
|
||
/// <para>Returns an empty list when no session is active or the send fails.
|
||
/// Used by the Developer Tools page; never called from production paths.</para>
|
||
/// </summary>
|
||
/// <param name="payload">Packet bytes excluding length/counter/end framing.</param>
|
||
/// <param name="ct">Cancellation token.</param>
|
||
/// <returns>Each response packet's full byte sequence (length…end inclusive).</returns>
|
||
Task<IReadOnlyList<byte[]>> SendRawCustomAsync(byte[] payload, CancellationToken ct = default);
|
||
|
||
/// <summary>
|
||
/// Reads <paramref name="count"/> bytes from EEPROM starting at <paramref name="address"/>
|
||
/// over the persistent K-Line session (KWP <c>ReadEeprom</c> command 0x19).
|
||
/// Returns an empty list when no session is active or the ECU returns NAK.
|
||
/// Used by the Developer Tools page's dumper; max 13 bytes per call.
|
||
/// </summary>
|
||
Task<IReadOnlyList<byte>> ReadEepromAsync(ushort address, byte count, CancellationToken ct = default);
|
||
|
||
/// <summary>
|
||
/// Reads <paramref name="count"/> bytes from ROM/EEPROM starting at <paramref name="address"/>
|
||
/// over the persistent K-Line session (KWP <c>ReadRomEeprom</c> command 0x03).
|
||
/// Valid range 0x0000–0x9FFF. Returns an empty list when no session is active or
|
||
/// the ECU returns NAK. Used by the Developer Tools page's dumper; max 13 bytes per call.
|
||
/// </summary>
|
||
Task<IReadOnlyList<byte>> ReadRomEepromAsync(ushort address, byte count, CancellationToken ct = default);
|
||
|
||
// ── BIP status ────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Reads the 16-bit BIP status word from ECU RAM address <c>0x0106</c>
|
||
/// (<c>ADR-S_BIP_HW_UW</c>) over the persistent K-Line session.
|
||
/// Returns <see langword="null"/> when no session is active or if the read fails.
|
||
/// </summary>
|
||
/// <param name="ct">Cancellation token.</param>
|
||
Task<ushort?> ReadBipStatusAsync(CancellationToken ct = default);
|
||
|
||
/// <summary>
|
||
/// Raised after a successful <see cref="ReadBipStatusAsync"/> call with the
|
||
/// raw 16-bit BIP status word. Fires on a background thread;
|
||
/// consumers must marshal to the UI thread.
|
||
/// </summary>
|
||
event Action<ushort>? BipStatusRead;
|
||
|
||
// ── Power cycle callbacks ─────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Raised when the service needs the bench to cut power to the pump
|
||
/// (e.g. after a DFI write) before reconnecting.
|
||
/// </summary>
|
||
event Action? PumpDisconnectRequested;
|
||
|
||
/// <summary>
|
||
/// Raised after <see cref="PumpDisconnectRequested"/> when the service needs
|
||
/// the bench to restore power and re-establish the K-Line session.
|
||
/// </summary>
|
||
event Action? PumpReconnectRequested;
|
||
}
|
||
}
|