From b544d5cccd76735e034c8055918a50927b437474 Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期二, 10 十一月 2026 08:29:04 +0800
Subject: [PATCH] fix: 维护模式重复进入失败与按钮灰死 + Broker 并发验证用例

---
 Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs |  558 ++++++++-----------------------------------------------
 1 files changed, 88 insertions(+), 470 deletions(-)

diff --git a/Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs b/Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs
index aa8a60b..59b314b 100644
--- a/Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs
+++ b/Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs
@@ -1,44 +1,25 @@
-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
 {
     /// <summary>
-    /// CAN 甯ф樉绀烘ā鍨�
-    /// </summary>
-    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;
-    }
-
-    /// <summary>
-    /// CanDebugPanel.xaml 鐨勪氦浜掗�昏緫
+    /// CAN 璋冭瘯闈㈡澘澶氶�氶亾澹筹細浠� TabControl 鎵胯浇鑻ュ共 <see cref="CanChannelView"/>锛�
+    /// 鏀寔鍚屽睆骞跺垪澶氫釜 CAN 閫氶亾鐙珛鏀跺彂锛堥獙鏀跺満鏅� 4锛夈��
+    /// 瀵瑰淇濇寔涓庢棫鍗曢潰鏉夸竴鑷寸殑灞炴�у绾︼紙DeviceId/InitialBusName/InitialNodeIndex/
+    /// AutoConnectOnLoad/ExistingDriver/IsMaintenanceMode/OnCommandExecute锛夛紝
+    /// DeviceDebug 閫夎澶囨椂杩欎簺閰嶇疆琚浆鍙戝埌"璁惧閫氶亾"锛堥涓� Tab锛夈��
     /// </summary>
     public partial class CanDebugPanel : UserControl
     {
         private static readonly ILog log = LogManager.GetLogger(typeof(CanDebugPanel));
 
-        /// <summary>
-        /// 鏄惁涓虹淮鎶ゆā寮忥紙鎺у埗杈撳叆鍙敤鎬э級
-        /// </summary>
         public static readonly DependencyProperty IsMaintenanceModeProperty =
             DependencyProperty.Register(nameof(IsMaintenanceMode), typeof(bool), typeof(CanDebugPanel),
-                new PropertyMetadata(false));
+                new PropertyMetadata(false, OnMaintenanceModeChanged));
 
         public bool IsMaintenanceMode
         {
@@ -46,498 +27,135 @@
             set => SetValue(IsMaintenanceModeProperty, value);
         }
 
-        /// <summary>
-        /// 鍛戒护鎵ц浜嬩欢
-        /// </summary>
+        private static void OnMaintenanceModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+        {
+            var panel = (CanDebugPanel)d;
+            if (panel._deviceChannel != null)
+                panel._deviceChannel.IsMaintenanceMode = (bool)e.NewValue;
+        }
+
+        /// <summary>鍛戒护鎵ц浜嬩欢锛堜换涓�閫氶亾瑙﹀彂鍧囦笂鎶涳紝渚� DeviceDebug 缁熶竴澶勭悊锛�</summary>
         public event EventHandler<DebugCommand>? OnCommandExecute;
 
-        /// <summary>
-        /// 鎺ユ敹甯у垪琛�
-        /// </summary>
-        public ObservableCollection<CanFrameDisplay> ReceivedFrames { get; } = new();
+        private CanChannelView? _deviceChannel;
+        private int _manualSeq;
 
-        /// <summary>
-        /// CAN 椹卞姩閫傞厤鍣�
-        /// </summary>
-        private CanDriverAdapter? _driver;
-
-        /// <summary>
-        /// 杞瀹氭椂鍣紙鐢ㄤ簬妫�鏌ユ帴鏀堕偖绠憋級
-        /// </summary>
-        private DispatcherTimer? _pollTimer;
-
-        /// <summary>
-        /// 鏄惁宸茶繛鎺�
-        /// </summary>
-        private bool _isConnected;
-
-        /// <summary>
-        /// 璁惧ID锛堜粠 DeviceDebug 浼犲叆锛岄潰鏉胯嚜鍔ㄦ嫾鎺� deviceId锛�
-        /// </summary>
-        public string DeviceId
-        {
-            get => _deviceId;
-            set
-            {
-                _deviceId = value;
-                if (!string.IsNullOrEmpty(value))
-                {
-                    // 灏嗚澶嘔D鍓嶇紑鎷兼帴鍒伴粯璁よ澶囨爣璇嗕腑锛岄伩鍏嶄笌妯℃嫙鍣ㄩ潰鏉块噸鍚�
-                    UpdateDeviceIdDisplay();
-                }
-            }
-        }
+        // 寰呰浆鍙戝埌璁惧閫氶亾鐨勯厤缃紙鍙兘鏃╀簬閫氶亾鍒涘缓锛�
         private string _deviceId = string.Empty;
-
-        /// <summary>
-        /// 浠庤澶� Endpoint 瑙f瀽鍑虹殑鎬荤嚎鍚嶇О
-        /// </summary>
-        public string InitialBusName { get; set; }
-
-        /// <summary>
-        /// 浠庤澶� Endpoint 瑙f瀽鍑虹殑鑺傜偣缂栧彿
-        /// </summary>
-        public int? InitialNodeIndex { get; set; }
-
-        /// <summary>
-        /// 鏄惁鑷姩杩炴帴锛堜粠 DeviceDebug 鎵撳紑鏃�=true锛�
-        /// </summary>
-        public bool AutoConnectOnLoad { get; set; }
+        private string _initialBusName;
+        private int? _initialNodeIndex;
+        private bool _autoConnectOnLoad;
+        private Aging.Infrastructure.Contracts.IDeviceDriver? _existingDriver;
 
         public CanDebugPanel()
         {
             InitializeComponent();
-            receiveList.ItemsSource = ReceivedFrames;
-            InitializePollTimer();
-            Loaded += CanDebugPanel_Loaded;
         }
 
-        private void CanDebugPanel_Loaded(object sender, RoutedEventArgs e)
+        /// <summary>璁惧ID锛堣浆鍙戝埌璁惧閫氶亾锛�</summary>
+        public string DeviceId
         {
-            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);
-            }
+            get => _deviceId;
+            set { _deviceId = value; EnsureDeviceChannel().DeviceId = value; }
         }
 
-        private void UpdateDeviceIdDisplay()
+        /// <summary>鎬荤嚎鍚嶇О锛堣浆鍙戝埌璁惧閫氶亾锛�</summary>
+        public string InitialBusName
         {
-            if (!string.IsNullOrEmpty(_deviceId) && busNameInput != null)
-            {
-                // 淇濈暀榛樿鎬荤嚎鍚嶏紝浠呰褰� deviceId 浠ヤ緵鏃ュ織浣跨敤
-                log.Info($"[CanDebugPanel] 鍏宠仈璁惧: {_deviceId}");
-            }
+            get => _initialBusName;
+            set { _initialBusName = value; EnsureDeviceChannel().InitialBusName = value; }
         }
 
-        /// <summary>
-        /// 鍒濆鍖栬疆璇㈠畾鏃跺櫒
-        /// </summary>
-        private void InitializePollTimer()
+        /// <summary>鑺傜偣缂栧彿锛堣浆鍙戝埌璁惧閫氶亾锛�</summary>
+        public int? InitialNodeIndex
         {
-            _pollTimer = new DispatcherTimer
-            {
-                Interval = TimeSpan.FromMilliseconds(100) // 100ms 杞涓�娆�
-            };
-            _pollTimer.Tick += PollTimer_Tick;
+            get => _initialNodeIndex;
+            set { _initialNodeIndex = value; EnsureDeviceChannel().InitialNodeIndex = value; }
         }
 
-        /// <summary>
-        /// 杞瀹氭椂鍣ㄥ洖璋� - 浠呮洿鏂扮粺璁′俊鎭紙甯ф樉绀虹敱浜嬩欢椹卞姩锛�
-        /// </summary>
-        private void PollTimer_Tick(object? sender, EventArgs e)
+        /// <summary>鍔犺浇鍗宠嚜鍔ㄨ繛鎺ワ紙杞彂鍒拌澶囬�氶亾锛�</summary>
+        public bool AutoConnectOnLoad
         {
-            if (!_isConnected || _driver == null) return;
-
-            try
-            {
-                // 鏇存柊缁熻
-                UpdateStats();
-            }
-            catch (Exception ex)
-            {
-                log.Error($"杞缁熻鏇存柊澶辫触: {ex.Message}");
-            }
+            get => _autoConnectOnLoad;
+            set { _autoConnectOnLoad = value; EnsureDeviceChannel().AutoConnectOnLoad = value; }
         }
 
-        /// <summary>
-        /// 杩炴帴鎸夐挳鐐瑰嚮
-        /// </summary>
-        private void ConnectBtn_Click(object sender, RoutedEventArgs e)
+        /// <summary>澶栭儴宸叉敞鍐岄┍鍔紙杞彂鍒拌澶囬�氶亾锛屽叡浜澶囧鐢ㄥ涓婚�氳矾锛�</summary>
+        public Aging.Infrastructure.Contracts.IDeviceDriver? ExistingDriver
         {
-            try
-            {
-                DoConnect();
-            }
-            catch (Exception ex)
-            {
-                log.Error($"CAN 杩炴帴澶辫触: {ex.Message}", ex);
-                MessageBox.Show($"杩炴帴澶辫触: {ex.Message}", "閿欒", MessageBoxButton.OK, MessageBoxImage.Error);
-                UpdateConnectionStatus(false);
-            }
+            get => _existingDriver;
+            set { _existingDriver = value; EnsureDeviceChannel().ExistingDriver = value; }
         }
 
-        /// <summary>
-        /// 瀹為檯杩炴帴閫昏緫锛堜粠 UI 鎴栬嚜鍔ㄨ繛鎺ヨ皟鐢級
-        /// </summary>
-        private void DoConnect()
+        /// <summary>鎯版�у垱寤�"璁惧閫氶亾"锛堥涓� Tab锛夛紝骞舵寕鎺ュ懡浠や簨浠朵笌褰撳墠缁存姢鎬併��</summary>
+        private CanChannelView EnsureDeviceChannel()
         {
-            if (_isConnected) return;
+            if (_deviceChannel != null) return _deviceChannel;
 
-            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}");
+            _deviceChannel = new CanChannelView();
+            WireChannel(_deviceChannel, MakeHeader(_deviceId, _initialBusName, _initialNodeIndex));
+            // 搴旂敤宸插埌杈剧殑缁存姢鎬�
+            _deviceChannel.IsMaintenanceMode = IsMaintenanceMode;
+            var devTab = MakeTab(_deviceChannel);
+            devTab.Header = MakeHeader(_deviceId, _initialBusName, _initialNodeIndex);
+            channelTabs.Items.Add(devTab);
+            return _deviceChannel;
         }
 
-        /// <summary>
-        /// 椹卞姩甯ф帴鏀朵簨浠跺鐞� - 鍗虫椂鏄剧ず鎺ユ敹鍒扮殑甯�
-        /// </summary>
-        private void OnDriverFrameReceived(object? sender, CanSimulator.CanFrame frame)
+        private void WireChannel(CanChannelView view, string header)
         {
-            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);
-            }
+            view.OnCommandExecute += (s, e) => OnCommandExecute?.Invoke(this, e);
         }
 
-        /// <summary>
-        /// 鏂紑鎸夐挳鐐瑰嚮
-        /// </summary>
-        private void DisconnectBtn_Click(object sender, RoutedEventArgs e)
+        private TabItem MakeTab(CanChannelView view)
         {
-            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);
-            }
+            // 澶嶇敤瑙嗗浘鑷韩鐨勬爣棰樺尯涓嶆樉绀鸿澶囧悕锛岃繖閲岀敤 Tab 澶存爣娉ㄩ�氶亾韬唤
+            return new TabItem { Content = view };
         }
 
-        /// <summary>
-        /// 娣锋潅妯″紡鎸夐挳鐐瑰嚮
-        /// </summary>
-        private void PromiscuousBtn_Click(object sender, RoutedEventArgs e)
+        private static string MakeHeader(string deviceId, string bus, int? node)
         {
-            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}");
-            }
+            string id = string.IsNullOrEmpty(deviceId) ? "閫氶亾" : deviceId;
+            if (!string.IsNullOrEmpty(bus))
+                return $"{id} @{bus}:{node}";
+            return id;
         }
 
-        /// <summary>
-        /// 缁存姢妯″紡鍒囨崲鎸夐挳鐐瑰嚮
-        /// </summary>
-        private void MaintenanceToggleBtn_Click(object sender, RoutedEventArgs e)
+        /// <summary>鎵嬪姩娣诲姞涓�涓彲缂栬緫鐨勭┖鐧介�氶亾锛圠ocal TCP 鐩磋繛锛岀敤鎴疯嚜濉�荤嚎/鑺傜偣鍚庤繛鎺ワ級銆�</summary>
+        private void AddChannelBtn_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("缁存姢妯″紡宸插叧闂紙鍙鐩戞帶锛�");
-            }
+            var view = new CanChannelView { AutoConnectOnLoad = false };
+            _manualSeq++;
+            string header = $"CAN 閫氶亾 #{_manualSeq}";   // 鎵嬪姩閫氶亾鍏蜂綋鎬荤嚎/鑺傜偣鐢辩敤鎴烽〉鍐呭~鍐欙紝鏍囩涓嶉璁�
+            WireChannel(view, header);
+            var tab = MakeTab(view);
+            tab.Header = header;
+            channelTabs.Items.Add(tab);
+            channelTabs.SelectedItem = tab;
+            log.Info($"[CanDebugPanel] 鎵嬪姩娣诲姞閫氶亾 {header}");
         }
 
-        /// <summary>
-        /// 鑷姩鍝嶅簲妯″紡鍒囨崲鎸夐挳鐐瑰嚮
-        /// </summary>
-        private void AutoResponseToggleBtn_Click(object sender, RoutedEventArgs e)
+        /// <summary>鍏抽棴褰撳墠閫変腑鐨勯�氶亾锛堣澶囬�氶亾涓嶅彲鍏抽棴锛岄伩鍏嶈閫�鍑� DeviceDebug 浼氳瘽瑙嗗浘锛夈��</summary>
+        private void CloseChannelBtn_Click(object sender, RoutedEventArgs e)
         {
-            if (!_isConnected || _driver == null) return;
-
-            try
+            if (channelTabs.SelectedItem is not TabItem tab) return;
+            if (tab.Content == _deviceChannel)
             {
-                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("鑷姩鍝嶅簲妯″紡宸插叧闂�");
-                }
+                MessageBox.Show("璁惧閫氶亾涓嶅彲鍦ㄦ鍏抽棴锛岃浠庤澶囬�夋嫨鍒囨崲銆�", "鎻愮ず",
+                    MessageBoxButton.OK, MessageBoxImage.Information);
+                return;
             }
-            catch (Exception ex)
-            {
-                log.Error($"鍒囨崲鑷姩鍝嶅簲妯″紡澶辫触: {ex.Message}");
-                MessageBox.Show($"鍒囨崲澶辫触: {ex.Message}", "閿欒", MessageBoxButton.OK, MessageBoxImage.Error);
-            }
+            if (tab.Content is CanChannelView view)
+                view.DisconnectInternalSafe();
+            channelTabs.Items.Remove(tab);
         }
 
-        /// <summary>
-        /// 鏇存柊杩炴帴鐘舵�佹樉绀�
-        /// </summary>
-        private void UpdateConnectionStatus(bool connected)
+        /// <summary>鏇存柊璁惧閫氶亾 Tab 澶达紙鍦ㄩ厤缃綈澶囧悗璋冪敤涓�娆″嵆鍙級銆�</summary>
+        public void RefreshDeviceHeader()
         {
-            _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;
-            }
-        }
-
-        /// <summary>
-        /// 鏇存柊缁熻淇℃伅
-        /// </summary>
-        private void UpdateStats()
-        {
-            if (_driver == null) return;
-
-            try
-            {
-                var status = _driver.GetStatus();
-                var response = _driver.QueryAsync("STATUS", CancellationToken.None).GetAwaiter().GetResult();
-                // 瑙f瀽鐘舵��: "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
-            {
-                // 蹇界暐缁熻鏇存柊澶辫触
-            }
-        }
-
-        /// <summary>
-        /// 浠庣姸鎬佸瓧绗︿覆涓彁鍙栨暟鍊煎��
-        /// </summary>
-        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";
-        }
-
-        /// <summary>
-        /// 娣诲姞鎺ユ敹甯�
-        /// </summary>
-        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);
-            }
-        }
-
-        /// <summary>
-        /// 鍙戦�佹寜閽偣鍑�
-        /// </summary>
-        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);
-            }
-        }
-
-        /// <summary>
-        /// 娓呯┖鎸夐挳鐐瑰嚮
-        /// </summary>
-        private void ClearBtn_Click(object sender, RoutedEventArgs e)
-        {
-            try
-            {
-                ReceivedFrames.Clear();
-                log.Info("娓呯┖ CAN 鎺ユ敹璁板綍");
-            }
-            catch (Exception ex)
-            {
-                log.Error($"娓呯┖鎺ユ敹璁板綍澶辫触: {ex.Message}", ex);
-            }
+            if (_deviceChannel == null || channelTabs.Items.Count == 0) return;
+            if (channelTabs.Items[0] is TabItem t)
+                t.Header = MakeHeader(_deviceId, _initialBusName, _initialNodeIndex);
         }
     }
 }

--
Gitblit v1.9.1