using IBKRTrader.Core.Database.Migrations; using IBKRTrader.Core.Logging; using IBKRTrader.Core.Modules; using IBKRTrader.Core.Settings; using IBKRTrader.Core.Workers; using IBKRTrader.UI; namespace IBKRTrader; /// /// Launcher – das Basis-Fenster der Anwendung. /// Enthält die Core-Panels (Workers, Logs, Settings) und eine Modul-Liste, /// aus der jedes Modul als eigenständiges Fenster geöffnet wird. /// public partial class LauncherForm : Form { private readonly LoggingService _logger; private readonly WorkerEngine _workerEngine; private readonly SettingsService _settings; private readonly CoreMigrations _migrations; private readonly IBKRMigrations _ibkrMigrations; private readonly ModuleRegistry _modules; private readonly WindowManager _windows; private readonly IServiceProvider _services; private LogPanelController? _logPanel; public LauncherForm( LoggingService logger, WorkerEngine workerEngine, SettingsService settings, CoreMigrations migrations, IBKRMigrations ibkrMigrations, ModuleRegistry modules, WindowManager windows, IServiceProvider services) { InitializeComponent(); _logger = logger; _workerEngine = workerEngine; _settings = settings; _migrations = migrations; _ibkrMigrations = ibkrMigrations; _modules = modules; _windows = windows; _services = services; } // ─── Form-Events ────────────────────────────────────────────────────────── protected override void OnLoad(EventArgs e) { base.OnLoad(e); InitializeLogPanel(); InitializeWorkerList(); InitializeSettingsGrid(); InitializeModulePanel(); _ = StartupAsync(); } protected override void OnFormClosing(FormClosingEventArgs e) { base.OnFormClosing(e); // Offene Modul-Fenster schließen, dann Worker sauber beenden. _windows.CloseAll(); _workerEngine.StopAllAsync().GetAwaiter().GetResult(); } // ─── Initialisierung ────────────────────────────────────────────────────── private void InitializeLogPanel() { // RichTextBox auf Dock.Fill setzen rtb_logs.Dock = DockStyle.Fill; _logPanel = new LogPanelController(rtb_logs, _logger); // LogLevel aus Settings setzen var levelStr = _settings.Settings.Logging.Level; if (Enum.TryParse(levelStr, true, out var level)) _logger.SetMinLevel(level); } private void InitializeWorkerList() { WorkerListBindingSource.Setup(dgv_workerlist, _workerEngine.WorkerInfos); } private void InitializeSettingsGrid() { pg_settings.SelectedObject = _settings.Settings; } /// Baut je Modul eine Karte mit „Fenster öffnen"-Button. private void InitializeModulePanel() { flp_modules.FlowDirection = FlowDirection.LeftToRight; flp_modules.WrapContents = true; flp_modules.Controls.Clear(); foreach (var module in _modules.Modules) flp_modules.Controls.Add(BuildModuleCard(module)); } private Control BuildModuleCard(IModule module) { var card = new Panel { Width = 320, Height = 150, Margin = new Padding(8), BorderStyle = BorderStyle.FixedSingle }; var title = new Label { Text = $"{module.DisplayName} [{module.Key}]", Font = new Font(Font.FontFamily, 11f, FontStyle.Bold), Location = new Point(10, 10), AutoSize = true }; var desc = new Label { Text = module.Description, Location = new Point(10, 42), Size = new Size(300, 60), AutoEllipsis = true }; var open = new Button { Text = "Fenster öffnen", Location = new Point(10, 108), Width = 140, Tag = module }; open.Click += (_, _) => OpenModuleWindow(module); var version = new Label { Text = $"v{module.Version}", Location = new Point(240, 113), AutoSize = true, ForeColor = SystemColors.GrayText }; card.Controls.Add(title); card.Controls.Add(desc); card.Controls.Add(open); card.Controls.Add(version); return card; } /// Öffnet (oder fokussiert) das eigenständige Fenster eines Moduls. private void OpenModuleWindow(IModule module) { try { _windows.OpenOrFocus(module.Key, () => module.CreateWindow(_services)); } catch (Exception ex) { _logger.Error(module.Key, $"{module.DisplayName}: Fenster konnte nicht geöffnet werden.", ex); MessageBox.Show(this, $"Fenster für '{module.DisplayName}' konnte nicht geöffnet werden:\n{ex.Message}", "Modul-Fenster", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } // ─── Async Startup ──────────────────────────────────────────────────────── private async Task StartupAsync() { _logger.Info("Core", "=== IBKRTrader startet ==="); _logger.Info("Core", $"Version: 1.0.0 | .NET {Environment.Version}"); // DB-Verbindung testen und Core-Migrationen laufen lassen try { _logger.Info("Core", "Verbinde mit Datenbank..."); await _migrations.RunAsync(); await _ibkrMigrations.RunAsync(); } catch (Exception ex) { _logger.Error("Core", "Core-Datenbankfehler beim Start.", ex); } // Modul-Migrationen: über die Registry iterieren (Core kennt kein Modul). foreach (var module in _modules.Modules) { try { await module.InitializeAsync(_services); } catch (Exception ex) { _logger.Error(module.Key, $"{module.DisplayName}: Initialisierung fehlgeschlagen.", ex); } } // Worker starten await _workerEngine.StartAllAsync(); _logger.Info("Core", "IBKRTrader bereit."); UpdateStatusBar("Bereit"); } // ─── StatusBar ──────────────────────────────────────────────────────────── private void UpdateStatusBar(string text) { if (statusStrip1.InvokeRequired) statusStrip1.BeginInvoke(() => UpdateStatusBar(text)); else { // Vorhandenes Label nutzen oder neues anlegen if (statusStrip1.Items.Count == 0) statusStrip1.Items.Add(new ToolStripStatusLabel()); statusStrip1.Items[0].Text = $"Status: {text} | {DateTime.Now:HH:mm:ss}"; } } }