using System.Windows; using System.Windows.Media; using HC_APTBS.Infrastructure.Logging; using Wpf.Ui.Appearance; using HC_APTBS.Infrastructure.Pcan; using HC_APTBS.Models; using HC_APTBS.Services; using HC_APTBS.Services.Impl; using HC_APTBS.ViewModels; using Microsoft.Extensions.DependencyInjection; using Peak.Can.Basic; namespace HC_APTBS; /// /// Application entry-point and DI container host. /// /// /// Registers all services, ViewModels, and the main window with /// , then resolves and shows /// on startup. The container is disposed when the application exits. /// /// public partial class App : Application { private ServiceProvider? _serviceProvider; protected override async void OnStartup(StartupEventArgs e) { base.OnStartup(e); // Apply WPF-UI theme and accent colour before any UI is constructed. ApplicationThemeManager.Apply(ApplicationTheme.Light); ApplicationAccentColorManager.Apply(Color.FromRgb(0x21, 0x96, 0xF3)); var services = new ServiceCollection(); ConfigureServices(services); _serviceProvider = services.BuildServiceProvider(); // Load the persisted language dictionary before any VM or view reads a string. _ = _serviceProvider.GetRequiredService(); // Wire runtime-warning hooks on pure Model classes before any config is loaded. // Keeps the Models layer DI-free while still routing warnings through IAppLogger. var logger = _serviceProvider.GetRequiredService(); CanBusParameter.WarningLogger = logger.Warning; SensorConfiguration.WarningLogger = logger.Warning; // Initialise the ViewModel (loads pump IDs, starts refresh timer, connects CAN). var mainVm = _serviceProvider.GetRequiredService(); await mainVm.InitialiseAsync(); var window = _serviceProvider.GetRequiredService(); window.Show(); } protected override void OnExit(ExitEventArgs e) { _serviceProvider?.Dispose(); base.OnExit(e); } // ── Service registration ────────────────────────────────────────────────── private static void ConfigureServices(IServiceCollection services) { // ── Infrastructure ──────────────────────────────────────────────────── services.AddSingleton(); // PcanAdapter requires a channel handle and baudrate read from configuration. services.AddSingleton(sp => { var cfg = sp.GetRequiredService(); var logger = sp.GetRequiredService(); var channel = cfg.Bench.Channel; // Default to 500 kbps; the channel can switch at runtime via ICanService.SwitchBaudrate. return new PcanAdapter(channel, TPCANBaudrate.PCAN_BAUD_500K, logger); }); services.AddSingleton(); // ── Application services ────────────────────────────────────────────── services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); // Auto-test orchestrator. Factory indirection breaks the Orchestrator↔MainViewModel // construction cycle (orchestrator needs IAutoTestHost, MainViewModel implements it). services.AddSingleton>(sp => () => sp.GetRequiredService()); services.AddSingleton(); // ── ViewModels ──────────────────────────────────────────────────────── services.AddSingleton(); // ── Views ───────────────────────────────────────────────────────────── services.AddSingleton(); } }