using System; using System.Threading.Tasks; using System.Windows; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using HC_APTBS.Services; namespace HC_APTBS.ViewModels { /// /// ViewModel for the DFIManageDisplay user control. /// /// /// Exposes the current DFI (injection timing offset) value, a slider for manual /// adjustment, KWP version selection, and commands to read and write DFI via K-Line. /// Auto-mode is respected by the bench service when executing a DFI test phase. /// /// public sealed partial class DfiManageViewModel : ObservableObject { // ── Services ────────────────────────────────────────────────────────────── private readonly IKwpService _kwp; private readonly IConfigurationService _config; private readonly ILocalizationService _loc; private const string LogId = "DfiManageViewModel"; // ── Constructor ─────────────────────────────────────────────────────────── /// Initialises the ViewModel with the required services. public DfiManageViewModel(IKwpService kwpService, IConfigurationService configService, ILocalizationService loc) { _kwp = kwpService; _config = configService; _loc = loc; // Update the slider and LCD display in real time when the DFI is // read during a full K-Line read (PumpIdentificationViewModel flow). _kwp.DfiRead += (dfi) => SetDfi(dfi); } // ── DFI display ─────────────────────────────────────────────────────────── /// Current DFI value read from the ECU (displayed in the label). [ObservableProperty] private double _currentDfi; /// /// Slider position in integer hundredths of a DFI unit (range –300 to 300). /// Divide by 100 to get the actual DFI value. /// [ObservableProperty] [NotifyPropertyChangedFor(nameof(SliderDfiValue))] private int _sliderRaw; /// The DFI value represented by the current slider position. public double SliderDfiValue => Math.Round(SliderRaw / 100.0, 2); // ── Options ─────────────────────────────────────────────────────────────── /// /// KWP protocol version index (0, 1, or 2) used when writing DFI. /// Corresponds to the three known VP44 authentication variants. /// [ObservableProperty] private int _versionIndex = 1; /// /// When the bench service may perform automatic DFI /// correction during test phases; when it skips them. /// [ObservableProperty] private bool _isAutoMode = true; // ── Operation state ─────────────────────────────────────────────────────── /// True while a read or write K-Line operation is in progress. [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(ReadDfiCommand))] [NotifyCanExecuteChangedFor(nameof(WriteDfiCommand))] private bool _isBusy; /// Progress percentage (0–100) for the current K-Line operation. [ObservableProperty] private int _progressPercent; /// Verbose status message for the current K-Line operation. [ObservableProperty] private string _progressMessage = string.Empty; // ── Public API ──────────────────────────────────────────────────────────── /// /// Sets the displayed DFI value and repositions the slider to match. /// Safe to call from any thread. /// public void SetDfi(double dfi) { double rounded = Math.Round(dfi, 2); Application.Current.Dispatcher.Invoke(() => { CurrentDfi = rounded; SliderRaw = (int)(rounded * 100); }); } /// /// Clears the DFI display and slider so the previous pump's value is not /// shown stale until a new K-Line read populates it. Called on pump change. /// public void Reset() { CurrentDfi = 0; SliderRaw = 0; } // ── Commands ────────────────────────────────────────────────────────────── /// Reads the current DFI value from the ECU over K-Line. [RelayCommand(CanExecute = nameof(CanOperate))] private async Task ReadDfiAsync() { string? port = _kwp.DetectKLinePort(); if (string.IsNullOrEmpty(port)) { MessageBox.Show(_loc.GetString("Error.KLineNotFound"), _loc.GetString("Error.KLineTitle"), MessageBoxButton.OK, MessageBoxImage.Warning); return; } IsBusy = true; _kwp.ProgressChanged += OnProgress; try { string dfiStr = await _kwp.ReadDfiAsync(port); if (double.TryParse(dfiStr, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out double val)) SetDfi(val); } finally { _kwp.ProgressChanged -= OnProgress; IsBusy = false; ProgressPercent = 0; ProgressMessage = string.Empty; } } /// Writes the slider DFI value to the ECU and reads back the result. [RelayCommand(CanExecute = nameof(CanOperate))] private async Task WriteDfiAsync() { string? port = _kwp.DetectKLinePort(); if (string.IsNullOrEmpty(port)) { MessageBox.Show(_loc.GetString("Error.KLineNotFound"), _loc.GetString("Error.KLineTitle"), MessageBoxButton.OK, MessageBoxImage.Warning); return; } float value = (float)SliderDfiValue; int version = VersionIndex; IsBusy = true; _kwp.ProgressChanged += OnProgress; try { string dfiStr = await _kwp.WriteDfiAsync(port, value, version); if (double.TryParse(dfiStr, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out double val)) SetDfi(val); } finally { _kwp.ProgressChanged -= OnProgress; IsBusy = false; ProgressPercent = 0; ProgressMessage = string.Empty; } } private bool CanOperate() => !IsBusy; // ── Helpers ─────────────────────────────────────────────────────────────── private void OnProgress(int pct, string msg) { Application.Current.Dispatcher.Invoke(() => { ProgressPercent = pct; ProgressMessage = msg; }); } } }