using Aging.Infrastructure.Drivers; using log4net; using Plugin.DeviceDebug.Entity; using System; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; namespace Plugin.DeviceDebug.Panels { /// /// CAN 帧显示模型 /// public class CanFrameDisplay { public DateTime Timestamp { get; set; } public string FrameId { get; set; } = string.Empty; public int Dlc { get; set; } public string Data { get; set; } = string.Empty; public string Direction { get; set; } = string.Empty; // TX / RX public string Source { get; set; } = string.Empty; } /// /// CanDebugPanel.xaml 的交互逻辑 /// public partial class CanDebugPanel : UserControl { private static readonly ILog log = LogManager.GetLogger(typeof(CanDebugPanel)); /// /// 是否为维护模式(控制输入可用性) /// public static readonly DependencyProperty IsMaintenanceModeProperty = DependencyProperty.Register(nameof(IsMaintenanceMode), typeof(bool), typeof(CanDebugPanel), new PropertyMetadata(false)); public bool IsMaintenanceMode { get => (bool)GetValue(IsMaintenanceModeProperty); set => SetValue(IsMaintenanceModeProperty, value); } /// /// 命令执行事件 /// public event EventHandler? OnCommandExecute; /// /// 接收帧列表 /// public ObservableCollection ReceivedFrames { get; } = new(); /// /// CAN 驱动适配器 /// private CanDriverAdapter? _driver; /// /// 轮询定时器(用于检查接收邮箱) /// private DispatcherTimer? _pollTimer; /// /// 是否已连接 /// private bool _isConnected; /// /// 设备ID(从 DeviceDebug 传入,面板自动拼接 deviceId) /// public string DeviceId { get => _deviceId; set { _deviceId = value; if (!string.IsNullOrEmpty(value)) { // 将设备ID前缀拼接到默认设备标识中,避免与模拟器面板重名 UpdateDeviceIdDisplay(); } } } private string _deviceId = string.Empty; /// /// 从设备 Endpoint 解析出的总线名称 /// public string InitialBusName { get; set; } /// /// 从设备 Endpoint 解析出的节点编号 /// public int? InitialNodeIndex { get; set; } /// /// 是否自动连接(从 DeviceDebug 打开时=true) /// public bool AutoConnectOnLoad { get; set; } public CanDebugPanel() { InitializeComponent(); receiveList.ItemsSource = ReceivedFrames; InitializePollTimer(); Loaded += CanDebugPanel_Loaded; } private void CanDebugPanel_Loaded(object sender, RoutedEventArgs e) { Loaded -= CanDebugPanel_Loaded; // 从设备信息初始化总线/节点输入 if (!string.IsNullOrEmpty(InitialBusName)) { busNameInput.Text = InitialBusName; } if (InitialNodeIndex.HasValue) { nodeIndexInput.Text = InitialNodeIndex.Value.ToString(); } // 自动连接 if (AutoConnectOnLoad) { Dispatcher.BeginInvoke(new Action(() => { try { DoConnect(); } catch (Exception ex) { log.Warn($"自动连接失败: {ex.Message}"); } }), DispatcherPriority.Background); } } private void UpdateDeviceIdDisplay() { if (!string.IsNullOrEmpty(_deviceId) && busNameInput != null) { // 保留默认总线名,仅记录 deviceId 以供日志使用 log.Info($"[CanDebugPanel] 关联设备: {_deviceId}"); } } /// /// 初始化轮询定时器 /// private void InitializePollTimer() { _pollTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) // 100ms 轮询一次 }; _pollTimer.Tick += PollTimer_Tick; } /// /// 轮询定时器回调 - 仅更新统计信息(帧显示由事件驱动) /// private void PollTimer_Tick(object? sender, EventArgs e) { if (!_isConnected || _driver == null) return; try { // 更新统计 UpdateStats(); } catch (Exception ex) { log.Error($"轮询统计更新失败: {ex.Message}"); } } /// /// 连接按钮点击 /// private void ConnectBtn_Click(object sender, RoutedEventArgs e) { try { DoConnect(); } catch (Exception ex) { log.Error($"CAN 连接失败: {ex.Message}", ex); MessageBox.Show($"连接失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); UpdateConnectionStatus(false); } } /// /// 实际连接逻辑(从 UI 或自动连接调用) /// private void DoConnect() { if (_isConnected) return; string busName = busNameInput.Text.Trim(); if (!int.TryParse(nodeIndexInput.Text.Trim(), out int nodeIndex)) { throw new ArgumentException("节点编号格式错误"); } // 优先使用 DeviceDebug 传入的设备 ID,否则自动生成一个 string deviceId = string.IsNullOrEmpty(_deviceId) ? $"CAN_Test_{nodeIndex}_{DateTime.Now:HHmmss}" : _deviceId; _driver = new CanDriverAdapter(deviceId, busName, nodeIndex); // 订阅驱动的帧接收事件(即时显示接收到的帧) _driver.DriverFrameReceived += OnDriverFrameReceived; // 连接 _driver.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult(); _isConnected = true; // 模拟器模式下,连接后自动启用维护模式(允许发送) IsMaintenanceMode = true; if (maintenanceToggleBtn != null) { maintenanceToggleBtn.IsChecked = true; maintenanceToggleBtn.IsEnabled = true; } // 更新 UI UpdateConnectionStatus(true); _pollTimer?.Start(); log.Info($"CAN 驱动已连接: Bus={busName}, Node={nodeIndex}, DeviceId={deviceId}"); } /// /// 驱动帧接收事件处理 - 即时显示接收到的帧 /// private void OnDriverFrameReceived(object? sender, CanSimulator.CanFrame frame) { try { // 判断方向:如果帧的来源是当前节点,则为自动响应(显示为 RX) string direction = "RX"; string source = frame.SourceNodeId; AddReceivedFrame( frame.FrameIdHex, frame.DataHex, direction, source ); log.Debug($"[即时显示] 接收帧: {frame}"); } catch (Exception ex) { log.Error($"即时显示帧失败: {ex.Message}", ex); } } /// /// 断开按钮点击 /// private void DisconnectBtn_Click(object sender, RoutedEventArgs e) { try { _pollTimer?.Stop(); if (_driver != null) { _driver.DriverFrameReceived -= OnDriverFrameReceived; _driver.DisconnectAsync().GetAwaiter().GetResult(); _driver.Dispose(); } _driver = null; _isConnected = false; IsMaintenanceMode = false; UpdateConnectionStatus(false); log.Info("CAN 驱动已断开"); } catch (Exception ex) { log.Error($"CAN 断开失败: {ex.Message}", ex); } } /// /// 混杂模式按钮点击 /// private void PromiscuousBtn_Click(object sender, RoutedEventArgs e) { if (!_isConnected || _driver == null) return; try { _driver.QueryAsync("PROMISCUOUS", CancellationToken.None).GetAwaiter().GetResult(); log.Info("已设置混杂模式(接收所有帧)"); MessageBox.Show("已设置混杂模式,将接收所有 CAN 帧", "提示", MessageBoxButton.OK, MessageBoxImage.Information); } catch (Exception ex) { log.Error($"设置混杂模式失败: {ex.Message}"); } } /// /// 维护模式切换按钮点击 /// private void MaintenanceToggleBtn_Click(object sender, RoutedEventArgs e) { bool isOn = maintenanceToggleBtn.IsChecked == true; IsMaintenanceMode = isOn; if (isOn) { maintenanceToggleBtn.Content = "维护模式: 开"; maintenanceToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50")); log.Info("维护模式已开启(可发送 CAN 帧)"); } else { maintenanceToggleBtn.Content = "维护模式: 关"; maintenanceToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E")); log.Info("维护模式已关闭(只读监控)"); } } /// /// 自动响应模式切换按钮点击 /// private void AutoResponseToggleBtn_Click(object sender, RoutedEventArgs e) { if (!_isConnected || _driver == null) return; try { bool isOn = autoResponseToggleBtn.IsChecked == true; // 通过驱动发送命令启用/禁用自动响应 string cmd = isOn ? "AUTO_RESPONSE:ON" : "AUTO_RESPONSE:OFF"; _driver.QueryAsync(cmd, CancellationToken.None).GetAwaiter().GetResult(); if (isOn) { autoResponseToggleBtn.Content = "自动响应: 开"; autoResponseToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF9800")); log.Info("自动响应模式已开启(收到帧将自动回复)"); } else { autoResponseToggleBtn.Content = "自动响应: 关"; autoResponseToggleBtn.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E")); log.Info("自动响应模式已关闭"); } } catch (Exception ex) { log.Error($"切换自动响应模式失败: {ex.Message}"); MessageBox.Show($"切换失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 更新连接状态显示 /// private void UpdateConnectionStatus(bool connected) { _isConnected = connected; if (connected) { statusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50")); statusText.Text = "已连接"; connectBtn.IsEnabled = false; disconnectBtn.IsEnabled = true; promiscuousBtn.IsEnabled = true; maintenanceToggleBtn.IsEnabled = true; autoResponseToggleBtn.IsEnabled = true; busNameInput.IsEnabled = false; nodeIndexInput.IsEnabled = false; } else { statusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E")); statusText.Text = "未连接"; connectBtn.IsEnabled = true; disconnectBtn.IsEnabled = false; promiscuousBtn.IsEnabled = false; maintenanceToggleBtn.IsEnabled = false; autoResponseToggleBtn.IsEnabled = false; busNameInput.IsEnabled = true; nodeIndexInput.IsEnabled = true; } } /// /// 更新统计信息 /// private void UpdateStats() { if (_driver == null) return; try { var status = _driver.GetStatus(); var response = _driver.QueryAsync("STATUS", CancellationToken.None).GetAwaiter().GetResult(); // 解析状态: "STATUS:Node=..., Active=True, Rx=0, Tx=0, Err=0, Mailbox=0|Bus=..." statsText.Text = $"TX: {GetStatValue(response, "Tx")} RX: {GetStatValue(response, "Rx")} Err: {GetStatValue(response, "Err")}"; } catch { // 忽略统计更新失败 } } /// /// 从状态字符串中提取数值值 /// private string GetStatValue(string statusStr, string key) { try { var parts = statusStr.Split(','); foreach (var part in parts) { var kv = part.Trim().Split('='); if (kv.Length == 2 && kv[0].Trim() == key) { return kv[1].Trim(); } } } catch { } return "0"; } /// /// 添加接收帧 /// public void AddReceivedFrame(string frameId, string data, string direction = "RX", string source = "") { try { var frame = new CanFrameDisplay { Timestamp = DateTime.Now, FrameId = frameId, Dlc = data.Split(new[] { ' ', '-' }, StringSplitOptions.RemoveEmptyEntries).Length, Data = data, Direction = direction, Source = source }; Application.Current.Dispatcher.Invoke(() => { ReceivedFrames.Insert(0, frame); // 限制显示数量 if (ReceivedFrames.Count > 500) { var toRemove = ReceivedFrames.Skip(500).ToList(); foreach (var item in toRemove) { ReceivedFrames.Remove(item); } } }); } catch (Exception ex) { log.Error($"添加接收帧失败: {ex.Message}", ex); } } /// /// 发送按钮点击 /// private void SendBtn_Click(object sender, RoutedEventArgs e) { try { if (!IsMaintenanceMode) { MessageBox.Show("当前为只读模式,无法发送数据", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } if (!_isConnected || _driver == null) { MessageBox.Show("请先连接 CAN 总线", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } string frameIdStr = frameIdInput.Text.Trim(); string dataStr = dataInput.Text.Trim(); // 验证帧 ID uint frameId = CanSimulator.CanFrame.ParseFrameId(frameIdStr); // 验证数据 byte[] dataBytes = CanSimulator.CanFrame.ParseData(dataStr); if (dataBytes.Length > 8) { MessageBox.Show("数据长度不能超过 8 字节", "错误", MessageBoxButton.OK, MessageBoxImage.Error); return; } // 通过驱动发送 string command = $"SEND:{frameIdStr}:{dataStr}"; var response = _driver.SendCommandAsync(command, CancellationToken.None).GetAwaiter().GetResult(); // 添加到显示列表(TX) AddReceivedFrame($"0x{frameId:X3}", BitConverter.ToString(dataBytes).Replace("-", " "), "TX", "LOCAL"); log.Info($"发送 CAN 帧: ID=0x{frameId:X3}, Data={BitConverter.ToString(dataBytes)}, Response={response}"); } catch (Exception ex) { log.Error($"发送 CAN 帧失败: {ex.Message}", ex); MessageBox.Show($"发送失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 清空按钮点击 /// private void ClearBtn_Click(object sender, RoutedEventArgs e) { try { ReceivedFrames.Clear(); log.Info("清空 CAN 接收记录"); } catch (Exception ex) { log.Error($"清空接收记录失败: {ex.Message}", ex); } } } }