using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using BmsMonitor.App.Can; using BmsMonitor.App.Protocol; namespace BmsMonitor.App; public partial class MainWindow : Window { private const int MaxVisibleFrames = 1000; private readonly ObservableCollection _frames = new(); private readonly BmsProtocolParser _parser = new(); private ICanReceiver? _receiver; private BmsSnapshot _snapshot = new(Faults: Array.Empty()); private long _frameCount; private IReadOnlyList _cellTexts = Array.Empty(); private IReadOnlyList _temperatureTexts = Array.Empty(); public MainWindow() { InitializeComponent(); FrameGrid.ItemsSource = _frames; _cellTexts = new[] { Cell01Text, Cell02Text, Cell03Text, Cell04Text, Cell05Text, Cell06Text, Cell07Text, Cell08Text, Cell09Text, Cell10Text, Cell11Text, Cell12Text, Cell13Text, Cell14Text, Cell15Text, Cell16Text }; _temperatureTexts = new[] { Temp01Text, Temp02Text, Temp03Text, Temp04Text, Temp05Text, Temp06Text, Temp07Text, Temp08Text, Temp09Text, Temp10Text }; } private void ConnectButton_Click(object sender, RoutedEventArgs e) { if (_receiver?.IsOpen == true) { Disconnect(); return; } try { _receiver = new ControlCanReceiver(); _receiver.FrameReceived += Receiver_FrameReceived; _receiver.ReceiveError += Receiver_ReceiveError; var baudRate = (BaudRateCombo.SelectedItem as ComboBoxItem)?.Tag?.ToString() == "500" ? CanBaudRate.Kbps500 : CanBaudRate.Kbps250; _receiver.Open(0, (uint)ChannelCombo.SelectedIndex, baudRate); ConnectButton.Content = "断开"; SetStatus($"已连接 · {baudRate.ToDisplayText()} · 只读监听", true); } catch (DllNotFoundException) { ConnectionFailed("未找到 ControlCAN.dll。请将与程序位数匹配的 DLL 放在程序目录。", "缺少驱动库"); } catch (BadImageFormatException) { ConnectionFailed("ControlCAN.dll 位数与程序不一致,请改用对应的 x86 或 x64 版本。", "DLL 位数不匹配"); } catch (Exception ex) { ConnectionFailed(ex.Message, "连接失败"); } } private void ConnectionFailed(string message, string title) { _receiver?.Dispose(); _receiver = null; MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Warning); } private void Receiver_FrameReceived(object? sender, CanFrame frame) => Dispatcher.BeginInvoke(() => { CounterText.Text = $"接收 {++_frameCount:N0} 帧"; _frames.Insert(0, frame); if (_frames.Count > MaxVisibleFrames) _frames.RemoveAt(_frames.Count - 1); if (_parser.TryApply(frame, _snapshot, out var updated)) { _snapshot = updated; RefreshDashboard(); } }); private void Receiver_ReceiveError(object? sender, string message) => Dispatcher.BeginInvoke(() => SetStatus($"接收异常:{message}", false)); private void RefreshDashboard() { PackVoltageText.Text = Format(_snapshot.PackVoltage, "F1", "V"); PackCurrentText.Text = Format(_snapshot.PackCurrent, "F1", "A"); SocText.Text = _snapshot.Soc is int soc ? $"{soc} %" : "-- %"; BmsStateText.Text = _snapshot.State; MaxCellText.Text = Format(_snapshot.MaximumCellVoltage, "F3", "V"); MinCellText.Text = Format(_snapshot.MinimumCellVoltage, "F3", "V"); MaxTempText.Text = _snapshot.MaximumTemperature is int max ? $"{max} °C" : "-- °C"; MinTempText.Text = _snapshot.MinimumTemperature is int min ? $"{min} °C" : "-- °C"; LowBatteryText.Text = Format(_snapshot.LowVoltageBattery, "F1", "V"); FaultText.Text = _snapshot.Faults is { Count: > 0 } ? string.Join(" · ", _snapshot.Faults) : "无故障"; FaultText.Foreground = _snapshot.Faults is { Count: > 0 } ? Brushes.LightCoral : Brushes.LightGreen; for (var i = 0; i < _cellTexts.Count; i++) _cellTexts[i].Text = FormatAt(_snapshot.CellVoltages, i, "F3", "V"); for (var i = 0; i < _temperatureTexts.Count; i++) _temperatureTexts[i].Text = FormatAt(_snapshot.Temperatures, i, "F1", "°C"); } private static string Format(double? value, string format, string unit) => value.HasValue ? $"{value.Value.ToString(format)} {unit}" : $"-- {unit}"; private static string FormatAt(IReadOnlyList? values, int index, string format, string unit) => values is not null && index < values.Count ? Format(values[index], format, unit) : $"-- {unit}"; private void ClearButton_Click(object sender, RoutedEventArgs e) { _frames.Clear(); _frameCount = 0; CounterText.Text = "接收 0 帧"; } private void SetStatus(string text, bool connected) { StatusText.Text = text; StatusDot.Fill = connected ? Brushes.LimeGreen : Brushes.IndianRed; } private void Disconnect() { if (_receiver is not null) { _receiver.FrameReceived -= Receiver_FrameReceived; _receiver.ReceiveError -= Receiver_ReceiveError; _receiver.Dispose(); _receiver = null; } ConnectButton.Content = "连接"; SetStatus("未连接", false); } private void Window_Closing(object? sender, CancelEventArgs e) => Disconnect(); }