From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点

---
 Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs |  543 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 543 insertions(+), 0 deletions(-)

diff --git a/Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs b/Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs
new file mode 100644
index 0000000..aa8a60b
--- /dev/null
+++ b/Plugin.DeviceDebug/Panels/CanDebugPanel.xaml.cs
@@ -0,0 +1,543 @@
+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 鐨勪氦浜掗�昏緫
+    /// </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));
+
+        public bool IsMaintenanceMode
+        {
+            get => (bool)GetValue(IsMaintenanceModeProperty);
+            set => SetValue(IsMaintenanceModeProperty, value);
+        }
+
+        /// <summary>
+        /// 鍛戒护鎵ц浜嬩欢
+        /// </summary>
+        public event EventHandler<DebugCommand>? OnCommandExecute;
+
+        /// <summary>
+        /// 鎺ユ敹甯у垪琛�
+        /// </summary>
+        public ObservableCollection<CanFrameDisplay> ReceivedFrames { get; } = new();
+
+        /// <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; }
+
+        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}");
+            }
+        }
+
+        /// <summary>
+        /// 鍒濆鍖栬疆璇㈠畾鏃跺櫒
+        /// </summary>
+        private void InitializePollTimer()
+        {
+            _pollTimer = new DispatcherTimer
+            {
+                Interval = TimeSpan.FromMilliseconds(100) // 100ms 杞涓�娆�
+            };
+            _pollTimer.Tick += PollTimer_Tick;
+        }
+
+        /// <summary>
+        /// 杞瀹氭椂鍣ㄥ洖璋� - 浠呮洿鏂扮粺璁′俊鎭紙甯ф樉绀虹敱浜嬩欢椹卞姩锛�
+        /// </summary>
+        private void PollTimer_Tick(object? sender, EventArgs e)
+        {
+            if (!_isConnected || _driver == null) return;
+
+            try
+            {
+                // 鏇存柊缁熻
+                UpdateStats();
+            }
+            catch (Exception ex)
+            {
+                log.Error($"杞缁熻鏇存柊澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 杩炴帴鎸夐挳鐐瑰嚮
+        /// </summary>
+        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);
+            }
+        }
+
+        /// <summary>
+        /// 瀹為檯杩炴帴閫昏緫锛堜粠 UI 鎴栬嚜鍔ㄨ繛鎺ヨ皟鐢級
+        /// </summary>
+        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}");
+        }
+
+        /// <summary>
+        /// 椹卞姩甯ф帴鏀朵簨浠跺鐞� - 鍗虫椂鏄剧ず鎺ユ敹鍒扮殑甯�
+        /// </summary>
+        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);
+            }
+        }
+
+        /// <summary>
+        /// 鏂紑鎸夐挳鐐瑰嚮
+        /// </summary>
+        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);
+            }
+        }
+
+        /// <summary>
+        /// 娣锋潅妯″紡鎸夐挳鐐瑰嚮
+        /// </summary>
+        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}");
+            }
+        }
+
+        /// <summary>
+        /// 缁存姢妯″紡鍒囨崲鎸夐挳鐐瑰嚮
+        /// </summary>
+        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("缁存姢妯″紡宸插叧闂紙鍙鐩戞帶锛�");
+            }
+        }
+
+        /// <summary>
+        /// 鑷姩鍝嶅簲妯″紡鍒囨崲鎸夐挳鐐瑰嚮
+        /// </summary>
+        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);
+            }
+        }
+
+        /// <summary>
+        /// 鏇存柊杩炴帴鐘舵�佹樉绀�
+        /// </summary>
+        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;
+            }
+        }
+
+        /// <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);
+            }
+        }
+    }
+}

--
Gitblit v1.9.1