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