97 lines
5.3 KiB
C#
97 lines
5.3 KiB
C#
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<CanFrame> _frames = new();
|
|
private readonly BmsProtocolParser _parser = new();
|
|
private ICanReceiver? _receiver;
|
|
private BmsSnapshot _snapshot = new(Faults: Array.Empty<string>());
|
|
private long _frameCount;
|
|
private IReadOnlyList<TextBlock> _cellTexts = Array.Empty<TextBlock>();
|
|
private IReadOnlyList<TextBlock> _temperatureTexts = Array.Empty<TextBlock>();
|
|
|
|
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<double?>? 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();
|
|
}
|