Files
HC_APTBS/ViewModels/PumpIdentificationViewModel.cs
2026-04-11 12:45:18 +02:00

245 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HC_APTBS.Models;
using HC_APTBS.Services;
namespace HC_APTBS.ViewModels
{
/// <summary>
/// ViewModel for the pump identification user control.
///
/// <para>
/// Owns the pump selector (dropdown), K-Line ECU read command,
/// and all read-only K-Line fields (DFI, serial number, software versions, etc.).
/// After a successful K-Line read the pump is auto-selected from the database
/// using the ECU's pump identifier — matching the old-source behaviour where
/// <c>OnPumpConnectClick</c> would call <c>LoadPump</c> after reading.
/// </para>
/// </summary>
public sealed partial class PumpIdentificationViewModel : ObservableObject
{
// ── Services ──────────────────────────────────────────────────────────────
private readonly IKwpService _kwp;
private readonly IConfigurationService _config;
private readonly IAppLogger _log;
private const string LogId = "PumpIdentVM";
// ── Constructor ───────────────────────────────────────────────────────────
/// <summary>Initialises the ViewModel with the required services.</summary>
public PumpIdentificationViewModel(
IKwpService kwpService,
IConfigurationService configService,
IAppLogger logger)
{
_kwp = kwpService;
_config = configService;
_log = logger;
// Wire KWP progress events to local properties.
_kwp.ProgressChanged += (pct, msg) => App.Current.Dispatcher.Invoke(() =>
{
ProgressPercent = pct;
ProgressMessage = msg;
});
}
// ── Pump selection ────────────────────────────────────────────────────────
/// <summary>List of available pump IDs loaded from the pump database.</summary>
public ObservableCollection<string> PumpIds { get; } = new();
/// <summary>Currently selected pump ID in the dropdown.</summary>
[ObservableProperty]
private string? _selectedPumpId;
/// <summary>Currently loaded pump definition.</summary>
[ObservableProperty]
private PumpDefinition? _currentPump;
/// <summary>
/// Raised when <see cref="CurrentPump"/> changes so the parent ViewModel
/// can react (e.g. register CAN parameters, update test display).
/// </summary>
public event Action<PumpDefinition?>? PumpChanged;
/// <summary>Populates the pump ID list from the configuration database.</summary>
public void LoadPumpIds()
{
PumpIds.Clear();
foreach (var id in _config.GetPumpIds())
PumpIds.Add(id);
}
partial void OnSelectedPumpIdChanged(string? value)
{
if (string.IsNullOrEmpty(value)) return;
LoadPump(value);
}
private void LoadPump(string pumpId)
{
var pump = _config.LoadPump(pumpId);
if (pump == null)
{
_log.Warning(LogId, $"Pump {pumpId} not found in database.");
return;
}
CurrentPump = pump;
PumpChanged?.Invoke(pump);
_log.Info(LogId, $"Loaded pump: {pumpId}");
}
// ── K-Line display properties ─────────────────────────────────────────────
/// <summary>K-Line read progress percentage (0100).</summary>
[ObservableProperty] private int _progressPercent;
/// <summary>K-Line read progress status message.</summary>
[ObservableProperty] private string _progressMessage = string.Empty;
/// <summary>True while a K-Line read is in progress.</summary>
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ReadKlineCommand))]
private bool _isReading;
/// <summary>DFI calibration angle read from ECU EEPROM.</summary>
[ObservableProperty] private string _klineDfi = "-";
/// <summary>Pump identifier string read from ECU ROM.</summary>
[ObservableProperty] private string _klinePumpId = string.Empty;
/// <summary>ECU serial number (EEPROM 0x0080).</summary>
[ObservableProperty] private string _klineSerialNumber = string.Empty;
/// <summary>Model reference from ECU identification text.</summary>
[ObservableProperty] private string _klineModelRef = string.Empty;
/// <summary>Data record from ECU identification text.</summary>
[ObservableProperty] private string _klineDataRecord = string.Empty;
/// <summary>Pump control field from ECU identification text (V2+ pumps).</summary>
[ObservableProperty] private string _klinePumpControl = string.Empty;
/// <summary>Customer change index read from ECU ROM.</summary>
[ObservableProperty] private string _klineModelIndex = string.Empty;
/// <summary>Software version 1 from ECU identification text.</summary>
[ObservableProperty] private string _klineSwVersion1 = string.Empty;
/// <summary>Software version 2 from ECU identification text (V2+ pumps).</summary>
[ObservableProperty] private string _klineSwVersion2 = string.Empty;
/// <summary>Fault code text returned by the ECU.</summary>
[ObservableProperty] private string _klineErrors = string.Empty;
/// <summary>Connection error message (empty when OK, auto-collapsed in the UI).</summary>
[ObservableProperty] private string _klineConnectError = string.Empty;
// ── Commands ──────────────────────────────────────────────────────────────
/// <summary>Reads all pump ECU data over K-Line in a background task.</summary>
[RelayCommand(CanExecute = nameof(CanReadKline))]
private async Task ReadKlineAsync()
{
// Always freshly detect the FTDI device — never rely on a stored config value.
// This matches the old source where Config.GetKLinePortName() was called every time.
var port = _kwp.DetectKLinePort();
if (string.IsNullOrEmpty(port))
{
App.Current.Dispatcher.Invoke(() =>
KlineConnectError = "No K-Line device found");
return;
}
// Use the pump's KWP version if one is selected; default to 0 otherwise.
int kwpVersion = CurrentPump?.KwpVersion ?? 0;
IsReading = true;
try
{
var info = await _kwp.ReadAllInfoAsync(port, kwpVersion);
info.TryGetValue(KlineKeys.Dfi, out string? dfi);
info.TryGetValue(KlineKeys.Errors, out string? errors);
info.TryGetValue(KlineKeys.PumpId, out string? pumpId);
info.TryGetValue(KlineKeys.SerialNumber, out string? serial);
info.TryGetValue(KlineKeys.ModelReference, out string? modelRef);
info.TryGetValue(KlineKeys.ModelIndex, out string? modelIndex);
info.TryGetValue(KlineKeys.SwVersion1, out string? sw1);
info.TryGetValue(KlineKeys.SwVersion2, out string? sw2);
info.TryGetValue(KlineKeys.DataRecord, out string? dataRecord);
info.TryGetValue(KlineKeys.PumpControl, out string? pumpControl);
info.TryGetValue(KlineKeys.ConnectError, out string? connectErr);
info.TryGetValue(KlineKeys.Result, out string? result);
App.Current.Dispatcher.Invoke(() =>
{
KlineDfi = dfi ?? "-";
KlineErrors = errors ?? string.Empty;
KlinePumpId = pumpId ?? string.Empty;
KlineSerialNumber = serial ?? string.Empty;
KlineModelRef = modelRef ?? string.Empty;
KlineModelIndex = modelIndex ?? string.Empty;
KlineSwVersion1 = sw1 ?? string.Empty;
KlineSwVersion2 = sw2 ?? string.Empty;
KlineDataRecord = dataRecord ?? string.Empty;
KlinePumpControl = pumpControl ?? string.Empty;
KlineConnectError = connectErr ?? string.Empty;
});
// Auto-select pump from K-Line pump ID — matches old source behaviour
// where OnPumpConnectClick would call LoadPump(pumpId) after reading.
if (result == "1" && !string.IsNullOrEmpty(pumpId))
{
AutoSelectPumpByKlineId(pumpId);
}
// Attach K-Line info to the (now possibly auto-selected) pump.
if (CurrentPump != null)
CurrentPump.KlineInfo = info;
}
finally
{
IsReading = false;
}
}
private bool CanReadKline() => !IsReading;
// ── Helpers ───────────────────────────────────────────────────────────────
/// <summary>
/// Tries to match a K-Line pump identifier to a pump in the database and auto-select it.
/// If the K-Line ID is directly in the pump list, select it. Otherwise, try to find
/// a pump whose ID is contained in the K-Line identifier string.
/// </summary>
private void AutoSelectPumpByKlineId(string klinePumpId)
{
// Direct match — the K-Line ID is itself a pump ID in the database.
if (PumpIds.Contains(klinePumpId))
{
App.Current.Dispatcher.Invoke(() => SelectedPumpId = klinePumpId);
return;
}
// Substring match — the K-Line ident string may contain the pump ID.
foreach (var id in PumpIds)
{
if (klinePumpId.Contains(id, StringComparison.OrdinalIgnoreCase))
{
App.Current.Dispatcher.Invoke(() => SelectedPumpId = id);
return;
}
}
_log.Warning(LogId, $"K-Line pump ID '{klinePumpId}' not found in pump database.");
}
}
}