initial commit

This commit is contained in:
2026-04-11 12:45:18 +02:00
commit 6e1b929e2f
1246 changed files with 177580 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HC_APTBS.Services;
namespace HC_APTBS.ViewModels.Dialogs
{
/// <summary>
/// ViewModel for the WKlineErrors dialog.
///
/// <para>
/// Displays the raw DTC (Diagnostic Trouble Code) text returned by the VP44 ECU
/// over K-Line and provides commands to read or clear fault codes.
/// </para>
/// </summary>
public sealed partial class KlineErrorsViewModel : ObservableObject
{
// ── Services ──────────────────────────────────────────────────────────────
private readonly IKwpService _kwp;
private readonly IConfigurationService _config;
// ── Constructor ───────────────────────────────────────────────────────────
/// <summary>Initialises the ViewModel and shows the initially known error text.</summary>
public KlineErrorsViewModel(
IKwpService kwpService,
IConfigurationService configService,
string initialErrors = "")
{
_kwp = kwpService;
_config = configService;
ErrorText = initialErrors;
_kwp.ProgressChanged += OnProgress;
}
// ── Properties ────────────────────────────────────────────────────────────
/// <summary>Raw DTC string returned by the ECU, shown in the text box.</summary>
[ObservableProperty] private string _errorText = string.Empty;
/// <summary>True while a K-Line read or clear operation is in progress.</summary>
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(ReadErrorsCommand))]
[NotifyCanExecuteChangedFor(nameof(ClearErrorsCommand))]
private bool _isBusy;
/// <summary>Progress percentage (0100) for the current K-Line operation.</summary>
[ObservableProperty] private int _progressPercent;
/// <summary>Verbose status message for the current K-Line operation.</summary>
[ObservableProperty] private string _progressMessage = string.Empty;
// ── Commands ──────────────────────────────────────────────────────────────
/// <summary>Reads the current fault codes from the ECU.</summary>
[RelayCommand(CanExecute = nameof(CanOperate))]
private async Task ReadErrorsAsync()
{
string? port = GetPort();
if (port == null) return;
IsBusy = true;
try
{
ErrorText = await _kwp.ReadFaultCodesAsync(port);
}
finally
{
IsBusy = false;
ProgressPercent = 0;
ProgressMessage = string.Empty;
}
}
/// <summary>Clears fault codes on the ECU, then reads back the updated list.</summary>
[RelayCommand(CanExecute = nameof(CanOperate))]
private async Task ClearErrorsAsync()
{
string? port = GetPort();
if (port == null) return;
IsBusy = true;
try
{
ErrorText = await _kwp.ClearFaultCodesAsync(port);
// Re-read after clearing to confirm
ErrorText = await _kwp.ReadFaultCodesAsync(port);
}
finally
{
IsBusy = false;
ProgressPercent = 0;
ProgressMessage = string.Empty;
}
}
/// <summary>Closes the dialog.</summary>
[RelayCommand]
private void Close()
{
_kwp.ProgressChanged -= OnProgress;
RequestClose?.Invoke();
}
// ── Events ────────────────────────────────────────────────────────────────
/// <summary>Raised when the dialog should close itself.</summary>
public event System.Action? RequestClose;
// ── Helpers ───────────────────────────────────────────────────────────────
private bool CanOperate() => !IsBusy;
private string? GetPort()
{
string? port = _kwp.DetectKLinePort();
if (!string.IsNullOrEmpty(port)) return port;
MessageBox.Show(
"K-Line device not found. Check that the FTDI adapter is connected.",
"K-Line Error", MessageBoxButton.OK, MessageBoxImage.Warning);
return null;
}
private void OnProgress(int pct, string msg)
{
Application.Current.Dispatcher.Invoke(() =>
{
ProgressPercent = pct;
ProgressMessage = msg;
});
}
}
}

View File

@@ -0,0 +1,36 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace HC_APTBS.ViewModels.Dialogs
{
/// <summary>
/// ViewModel for the WProgressDisplay dialog.
///
/// <para>
/// Shows a progress bar (0100) and a verbose text line during long-running
/// K-Line operations. The dialog is closed by the parent when the operation
/// completes; this ViewModel simply exposes observable properties for binding.
/// </para>
/// </summary>
public sealed partial class ProgressViewModel : ObservableObject
{
/// <summary>Title text shown in the dialog's title bar.</summary>
[ObservableProperty] private string _title = string.Empty;
/// <summary>Current progress value (0100).</summary>
[ObservableProperty] private int _progressPercent;
/// <summary>Verbose status message describing the current step.</summary>
[ObservableProperty] private string _verboseMessage = string.Empty;
/// <summary>
/// Updates both <see cref="ProgressPercent"/> and <see cref="VerboseMessage"/>
/// in a single call. Thread-safe: may be called from any thread because
/// the consumer is expected to marshal to the UI thread before calling.
/// </summary>
public void Update(int percent, string message)
{
ProgressPercent = percent;
VerboseMessage = message;
}
}
}

View File

@@ -0,0 +1,158 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HC_APTBS.Services;
namespace HC_APTBS.ViewModels.Dialogs
{
/// <summary>
/// ViewModel for the WReport dialog.
///
/// <para>
/// Lets the operator fill in client and company information before generating
/// the PDF report. The client list is backed by the
/// <see cref="IConfigurationService"/> and persisted when the dialog is accepted.
/// </para>
/// </summary>
public sealed partial class ReportViewModel : ObservableObject
{
// ── Services ──────────────────────────────────────────────────────────────
private readonly IConfigurationService _config;
// ── Constructor ───────────────────────────────────────────────────────────
/// <summary>Initialises and populates the dialog from configuration.</summary>
public ReportViewModel(IConfigurationService configService)
{
_config = configService;
CompanyName = _config.Settings.CompanyName;
CompanyInfo = _config.Settings.CompanyInfo;
foreach (var name in _config.Clients.Keys)
ClientNames.Add(name);
}
// ── Company ───────────────────────────────────────────────────────────────
/// <summary>Company name shown in the report header.</summary>
[ObservableProperty] private string _companyName = string.Empty;
/// <summary>Company address / contact info shown in the report header.</summary>
[ObservableProperty] private string _companyInfo = string.Empty;
// ── Client ────────────────────────────────────────────────────────────────
/// <summary>List of known client names for the autocomplete combo box.</summary>
public ObservableCollection<string> ClientNames { get; } = new();
/// <summary>Currently selected or typed client name.</summary>
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(SaveClientCommand))]
[NotifyCanExecuteChangedFor(nameof(DeleteClientCommand))]
private string _selectedClientName = string.Empty;
/// <summary>Free-text notes for the selected client.</summary>
[ObservableProperty] private string _clientInfo = string.Empty;
/// <summary>Operator name.</summary>
[ObservableProperty] private string _operatorName = string.Empty;
/// <summary>Free-text observations appended to the report.</summary>
[ObservableProperty] private string _observations = string.Empty;
// ── Dialog result ─────────────────────────────────────────────────────────
/// <summary>True after the user clicks Accept; false if they cancel.</summary>
public bool Accepted { get; private set; }
// ── Commands ──────────────────────────────────────────────────────────────
/// <summary>Saves the current client entry, closes the dialog with Accepted=true.</summary>
[RelayCommand]
private void Accept()
{
SaveCurrentClient();
_config.Settings.CompanyName = CompanyName;
_config.Settings.CompanyInfo = CompanyInfo;
_config.SaveClients();
_config.SaveSettings();
Accepted = true;
RequestClose?.Invoke();
}
/// <summary>Closes the dialog without saving.</summary>
[RelayCommand]
private void Cancel()
{
Accepted = false;
RequestClose?.Invoke();
}
/// <summary>Persists the current client entry and refreshes the client list.</summary>
[RelayCommand(CanExecute = nameof(HasClientName))]
private void SaveClient()
{
SaveCurrentClient();
RefreshClientList();
}
/// <summary>Deletes the currently selected client from the list.</summary>
[RelayCommand(CanExecute = nameof(HasClientName))]
private void DeleteClient()
{
string name = SelectedClientName.Trim();
if (string.IsNullOrEmpty(name)) return;
if (_config.Clients.ContainsKey(name))
_config.Clients.Remove(name);
SelectedClientName = string.Empty;
ClientInfo = string.Empty;
RefreshClientList();
}
/// <summary>
/// Called when the user picks a client name from the combo box.
/// Loads the associated info text.
/// </summary>
public void SelectClient(string name)
{
SelectedClientName = name ?? string.Empty;
if (!string.IsNullOrEmpty(name) && _config.Clients.TryGetValue(name, out var info))
ClientInfo = info;
}
// ── Events ────────────────────────────────────────────────────────────────
/// <summary>Raised when the dialog should close itself.</summary>
public event System.Action? RequestClose;
// ── Helpers ───────────────────────────────────────────────────────────────
private bool HasClientName() => !string.IsNullOrWhiteSpace(SelectedClientName);
private void SaveCurrentClient()
{
string name = SelectedClientName.Trim();
if (string.IsNullOrEmpty(name)) return;
if (_config.Clients.ContainsKey(name))
_config.Clients[name] = ClientInfo;
else
_config.Clients.Add(name, ClientInfo);
}
private void RefreshClientList()
{
ClientNames.Clear();
foreach (var name in _config.Clients.Keys)
ClientNames.Add(name);
}
}
}