using System; using System.Collections.Concurrent; using System.Windows.Forms; using PolyTraderSharp.Services; namespace PolyTraderSharp.Ui.Views { /// /// Terminal-/Log-Ansicht. Designbar (siehe TerminalView.Designer.cs). Die Laufzeit- /// Abhängigkeit (TerminalLogger) wird per injiziert, damit der /// parameterlose Konstruktor für den VS-Designer nutzbar bleibt. /// public partial class TerminalView : Form { private TerminalLogger? _logger; private readonly ConcurrentQueue _logQueue = new(); private readonly System.Windows.Forms.Timer _uiLogTimer = new() { Interval = 250 }; private bool _autoScroll = true; private EventHandler? _logHandler; public TerminalView() { InitializeComponent(); // Steuerelemente + statische Eigenschaften stammen aus dem Designer. // Hier nur noch Verhalten verdrahten und einen sinnvollen Default wählen. if (cbLogLevel.Items.Count > 0) cbLogLevel.SelectedIndex = 0; btnAutoscroll.Click += (_, _) => ToggleAutoscroll(); _uiLogTimer.Tick += ProcessLogQueue; } /// Verbindet die View mit dem Logger (Laufzeit-DI). public void Initialize(TerminalLogger logger) { _logger = logger; // Jüngste Historie vorladen, damit das Fenster beim Öffnen nicht leer ist. foreach (var e in _logger.GetHistory(TimeSpan.FromMinutes(10))) _logQueue.Enqueue(e); _logHandler = (_, e) => _logQueue.Enqueue(e); _logger.OnLogMessage += _logHandler; _uiLogTimer.Start(); Disposed += OnDisposed; } private void OnDisposed(object? sender, EventArgs e) { _uiLogTimer.Stop(); if (_logger != null && _logHandler != null) _logger.OnLogMessage -= _logHandler; } private void ToggleAutoscroll() { _autoScroll = !_autoScroll; btnAutoscroll.BackColor = _autoScroll ? System.Drawing.Color.LightGreen : System.Drawing.Color.IndianRed; btnAutoscroll.Text = _autoScroll ? "Stop Autoscroll" : "Start Autoscroll"; } private void ProcessLogQueue(object? sender, EventArgs e) { if (_logQueue.IsEmpty || !IsHandleCreated) return; string filter = cbLogLevel.SelectedItem?.ToString() ?? "Alle"; TimeZoneInfo berlinTz = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time"); bool appended = false; int count = 0; const int maxProcess = 500; rtbTerminal.SuspendLayout(); while (count < maxProcess && _logQueue.TryDequeue(out var logEvent)) { count++; if (filter != "Alle" && logEvent.Level.ToString() != filter) continue; DateTime logTime = logEvent.Timestamp.Kind == DateTimeKind.Utc ? TimeZoneInfo.ConvertTimeFromUtc(logEvent.Timestamp, berlinTz) : TimeZoneInfo.ConvertTime(logEvent.Timestamp, berlinTz); string timeStr = $"[{logTime:HH:mm:ss}]"; System.Drawing.Color c = System.Drawing.Color.White; if (logEvent.Level == LogLevel.Error) c = System.Drawing.Color.Red; else if (logEvent.Level == LogLevel.Warning) c = System.Drawing.Color.Yellow; else if (logEvent.Level == LogLevel.Trade) c = System.Drawing.Color.LightGreen; else if (logEvent.Level == LogLevel.TradeReasoning) c = System.Drawing.Color.Orange; rtbTerminal.SelectionStart = rtbTerminal.TextLength; rtbTerminal.SelectionLength = 0; rtbTerminal.SelectionColor = c; rtbTerminal.AppendText($"{timeStr} [{logEvent.Level}] {logEvent.Message}\n"); appended = true; } if (appended) { if (rtbTerminal.TextLength > 80000) { rtbTerminal.Clear(); rtbTerminal.SelectionColor = System.Drawing.Color.LightPink; rtbTerminal.AppendText($"[{DateTime.Now:HH:mm:ss}] [System] Terminal Auto-Clear (RAM Limit erreicht). Vollständige Logs im Ordner /Logs.\n"); } if (_autoScroll) rtbTerminal.ScrollToCaret(); } rtbTerminal.ResumeLayout(); } } }