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/DeviceDebug.xaml.cs |  668 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 668 insertions(+), 0 deletions(-)

diff --git a/Plugin.DeviceDebug/DeviceDebug.xaml.cs b/Plugin.DeviceDebug/DeviceDebug.xaml.cs
new file mode 100644
index 0000000..dc82505
--- /dev/null
+++ b/Plugin.DeviceDebug/DeviceDebug.xaml.cs
@@ -0,0 +1,668 @@
+using Aging.Infrastructure.Contracts;
+using CommunityToolkit.Mvvm.ComponentModel;
+using HMI.Base;
+using HMI.Core.Util;
+using HMI.UI.Util;
+using log4net;
+using Plugin.DeviceDebug.Entity;
+using Plugin.DeviceDebug.Model;
+using Plugin.DeviceDebug.Panels;
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace Plugin.DeviceDebug
+{
+    /// <summary>
+    /// DeviceDebug.xaml 鐨勪氦浜掗�昏緫
+    /// </summary>
+    [ObservableObject]
+    public partial class DeviceDebug : SimpleMaskControl
+    {
+        private static readonly ILog _log = LogManager.GetLogger(typeof(DeviceDebug));
+        private DeviceDebugModel _debugModel;
+        private DebugSessionManager _sessionManager;
+
+        // 鍩虹璁炬柦灞傛湇鍔�
+        private IDeviceManager? _deviceManager;
+        private IDeviceService? _deviceService;
+        private IResourceManager? _resourceManager;
+
+        // 璁惧閰嶇疆鏁版嵁搴撹矾寰勶紙涓� DeviceConfig 鎻掍欢鍏变韩锛�
+        private string _deviceConfigDbPath;
+
+        [ObservableProperty] private ObservableCollection<string> _deviceList = new();
+        [ObservableProperty] private string _selectedDevice;
+        [ObservableProperty] private DebugSession _currentSession;
+        [ObservableProperty] private ObservableCollection<DebugOperation> _operationLogs = new();
+        [ObservableProperty] private string _connectionStateText = "鏈繛鎺�";
+        [ObservableProperty] private string _sessionStateText = "";
+        [ObservableProperty] private string _reservationStatusText = "";
+        [ObservableProperty] private string _currentDeviceId = "";
+        [ObservableProperty] private string _currentDeviceKind = "";
+        [ObservableProperty] private string _currentEffectiveVersion = "";
+        [ObservableProperty] private string _currentEndpoint = "";
+        [ObservableProperty] private string _currentDriverType = "";
+        [ObservableProperty] private Brush _connectionBrush = new SolidColorBrush(Colors.Gray);
+        [ObservableProperty] private Brush _sessionStateBrush = new SolidColorBrush(Colors.Gray);
+        [ObservableProperty] private bool _canEnterMaintenance;
+        [ObservableProperty] private bool _canExitMaintenance;
+        [ObservableProperty] private bool _hasDevice;
+
+        // 缂撳瓨褰撳墠閫変腑鐨勮澶囩被鍨�
+        private string _currentDeviceKindStr;
+
+        public DeviceDebug()
+        {
+            DataContext = this;
+            InitializeComponent();
+        }
+
+        /// <summary>
+        /// 寮哄埗鍒锋柊璁惧鍒楄〃锛堜緵闆嗘垚娴嬭瘯棰勭疆璁惧鍚庤皟鐢級
+        /// </summary>
+        public void ForceRefreshForTest()
+        {
+            try { LoadDeviceList(); }
+            catch (Exception ex) { _log.Error("ForceRefreshForTest Error.", ex); }
+        }
+
+        public override void InitFinish()
+        {
+            base.InitFinish();
+            try
+            {
+                // 鍒濆鍖栬皟璇曟暟鎹簱
+                _debugModel = new DeviceDebugModel();
+                string dbPath = Path.Combine(BaseFolder, "debug.db");
+                _debugModel.InitDatabase(dbPath);
+
+                // 浠� UtilContainer 鑾峰彇鍩虹璁炬柦鏈嶅姟
+                var container = new UtilContainer();
+                _deviceManager = container.GetUtil<IDeviceManager>("DeviceManager");
+                _deviceService = container.GetUtil<IDeviceService>("DeviceService");
+                _resourceManager = container.GetUtil<IResourceManager>("ResourceManager");
+
+                // 鍒涘缓浼氳瘽绠$悊鍣紙浼犲叆 ResourceManager锛�
+                _sessionManager = new DebugSessionManager(_debugModel, _resourceManager);
+
+                // 璁惧閰嶇疆鏁版嵁搴撹矾寰勶紙涓� DeviceConfig 鍏变韩锛�
+                _deviceConfigDbPath = Path.Combine(BaseFolder, "data.db");
+
+                log.Info("纭欢璋冭瘯妯″潡鍒濆鍖栧畬鎴�");
+            }
+            catch (Exception ex)
+            {
+                log.Error("纭欢璋冭瘯妯″潡鍒濆鍖栧け璐�", ex);
+                ToastHelper.Error($"纭欢璋冭瘯鍒濆鍖栧け璐�: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鍔犺浇璁惧鍒楄〃
+        /// </summary>
+        private void LoadDeviceList()
+        {
+            try
+            {
+                if (!File.Exists(_deviceConfigDbPath))
+                {
+                    log.Warn($"璁惧閰嶇疆鏁版嵁搴撲笉瀛樺湪: {_deviceConfigDbPath}");
+                    ToastHelper.Warning("璁惧閰嶇疆鏁版嵁搴撲笉瀛樺湪锛岃鍏堝湪璁惧閰嶇疆妯″潡涓坊鍔犺澶�");
+                    return;
+                }
+
+                var sqliteHelper = new SqliteHelper(_deviceConfigDbPath);
+                var dt = sqliteHelper.GetDataTable("SELECT DeviceId, Kind FROM DeviceDefinition ORDER BY DeviceId");
+
+                var list = new ObservableCollection<string>();
+                foreach (System.Data.DataRow row in dt.Rows)
+                {
+                    string deviceId = row["DeviceId"].ToString();
+                    string kind = row["Kind"].ToString();
+                    list.Add($"{deviceId} [{kind}]");
+                }
+
+                DeviceList = list;
+                log.Info($"鍔犺浇璁惧鍒楄〃瀹屾垚锛屽叡 {list.Count} 涓澶�");
+            }
+            catch (Exception ex)
+            {
+                log.Error("鍔犺浇璁惧鍒楄〃澶辫触", ex);
+                ToastHelper.Error($"鍔犺浇璁惧鍒楄〃澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 瑙f瀽璁惧閫夋嫨瀛楃涓诧紝鑾峰彇 DeviceId
+        /// </summary>
+        private string ParseDeviceId(string selection)
+        {
+            if (string.IsNullOrEmpty(selection)) return string.Empty;
+            int idx = selection.IndexOf(" [");
+            return idx > 0 ? selection.Substring(0, idx) : selection;
+        }
+
+        /// <summary>
+        /// 瑙f瀽璁惧閫夋嫨瀛楃涓诧紝鑾峰彇 Kind
+        /// </summary>
+        private string ParseDeviceKind(string selection)
+        {
+            if (string.IsNullOrEmpty(selection)) return string.Empty;
+            int start = selection.IndexOf(" [");
+            int end = selection.IndexOf("]");
+            if (start >= 0 && end > start)
+                return selection.Substring(start + 2, end - start - 2);
+            return string.Empty;
+        }
+
+        /// <summary>
+        /// 鍔犺浇璁惧璇︾粏淇℃伅
+        /// </summary>
+        private void LoadDeviceInfo(string deviceId)
+        {
+            try
+            {
+                if (!File.Exists(_deviceConfigDbPath)) return;
+
+                var sqliteHelper = new SqliteHelper(_deviceConfigDbPath);
+                var dt = sqliteHelper.GetDataTable(
+                    $"SELECT DeviceId, Kind, EffectiveVersion, Endpoint, DriverType FROM DeviceDefinition WHERE DeviceId = '{deviceId}'");
+
+                if (dt.Rows.Count > 0)
+                {
+                    var row = dt.Rows[0];
+                    CurrentDeviceId = row["DeviceId"].ToString();
+                    CurrentDeviceKind = row["Kind"].ToString();
+                    CurrentEffectiveVersion = row["EffectiveVersion"]?.ToString() ?? "";
+                    CurrentEndpoint = row["Endpoint"]?.ToString() ?? "";
+                    CurrentDriverType = row["DriverType"]?.ToString() ?? "";
+
+                    // 缂撳瓨璁惧绫诲瀷
+                    _currentDeviceKindStr = CurrentDeviceKind;
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error($"鍔犺浇璁惧淇℃伅澶辫触: {deviceId}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 閫夋嫨璁惧鏃讹細鎵撳紑浼氳瘽锛圧eadOnly锛夛紝鍔犺浇瀵瑰簲闈㈡澘
+        /// </summary>
+        private void OnDeviceSelected()
+        {
+            try
+            {
+                if (string.IsNullOrEmpty(SelectedDevice))
+                {
+                    HasDevice = false;
+                    panelContainer.Content = new TextBlock
+                    {
+                        Text = "璇烽�夋嫨璁惧",
+                        HorizontalAlignment = HorizontalAlignment.Center,
+                        VerticalAlignment = VerticalAlignment.Center,
+                        FontSize = 16,
+                        Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999"))
+                    };
+                    return;
+                }
+
+                string deviceId = ParseDeviceId(SelectedDevice);
+                string kind = ParseDeviceKind(SelectedDevice);
+
+                // 鍔犺浇璁惧璇︾粏淇℃伅
+                LoadDeviceInfo(deviceId);
+
+                HasDevice = true;
+
+                // 鎵撳紑浼氳瘽锛圧eadOnly锛�
+                CurrentSession = _sessionManager.OpenSession(deviceId, "", "operator", CurrentEffectiveVersion);
+
+                // 閫氳繃 DeviceManager 杩炴帴璁惧
+                if (_deviceManager != null)
+                {
+                    try
+                    {
+                        _deviceManager.ConnectAsync(deviceId, CancellationToken.None)
+                            .GetAwaiter().GetResult();
+                        ConnectionStateText = "宸茶繛鎺�";
+                        ConnectionBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50"));
+                    }
+                    catch (Exception ex)
+                    {
+                        log.Warn($"閫氳繃 DeviceManager 杩炴帴璁惧澶辫触: {ex.Message}");
+                        ConnectionStateText = "杩炴帴澶辫触";
+                        ConnectionBrush = new SolidColorBrush(Colors.OrangeRed);
+                    }
+                }
+
+                // 鍔犺浇鎿嶄綔鏃ュ織
+                LoadOperationLogs();
+
+                // 鏇存柊鎸夐挳鐘舵��
+                UpdateButtonStates();
+
+                // 鍔犺浇瀵瑰簲闈㈡澘
+                LoadPanel(kind);
+
+                log.Info($"宸查�夋嫨璁惧: {deviceId}, 绫诲瀷: {kind}");
+            }
+            catch (Exception ex)
+            {
+                log.Error("閫夋嫨璁惧澶辫触", ex);
+                ToastHelper.Error($"閫夋嫨璁惧澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鏍规嵁璁惧绫诲瀷鍔犺浇瀵瑰簲鐨勮皟璇曢潰鏉�
+        /// </summary>
+        private void LoadPanel(string deviceKind)
+        {
+            try
+            {
+                UIElement panel = null;
+
+                switch (deviceKind)
+                {
+                    case "PowerSupply":
+                        var powerPanel = new PowerSupplyDebugPanel();
+                        powerPanel.IsMaintenanceMode = CurrentSession?.State == DebugSessionState.MaintenanceActive;
+                        powerPanel.OnCommandExecute += Panel_OnCommandExecute;
+                        panel = powerPanel;
+                        break;
+                    case "CanDevice":
+                        var canPanel = new CanDebugPanel
+                        {
+                            DeviceId = CurrentDeviceId,
+                            InitialBusName = ParseCanBusName(CurrentEndpoint),
+                            InitialNodeIndex = ParseCanNodeIndex(CurrentEndpoint),
+                            AutoConnectOnLoad = true
+                        };
+                        canPanel.IsMaintenanceMode = CurrentSession?.State == DebugSessionState.MaintenanceActive;
+                        canPanel.OnCommandExecute += Panel_OnCommandExecute;
+                        panel = canPanel;
+                        break;
+                    case "PlcDevice":
+                        var plcPanel = new PlcDebugPanel();
+                        plcPanel.IsMaintenanceMode = CurrentSession?.State == DebugSessionState.MaintenanceActive;
+                        plcPanel.OnCommandExecute += Panel_OnCommandExecute;
+                        panel = plcPanel;
+                        break;
+                    default:
+                        panel = new TextBlock
+                        {
+                            Text = $"涓嶆敮鎸佺殑璁惧绫诲瀷: {deviceKind}",
+                            HorizontalAlignment = HorizontalAlignment.Center,
+                            VerticalAlignment = VerticalAlignment.Center,
+                            FontSize = 14,
+                            Foreground = new SolidColorBrush(Colors.OrangeRed)
+                        };
+                        break;
+                }
+
+                panelContainer.Content = panel;
+            }
+            catch (Exception ex)
+            {
+                log.Error($"鍔犺浇璋冭瘯闈㈡澘澶辫触: {deviceKind}", ex);
+                ToastHelper.Error($"鍔犺浇闈㈡澘澶辫触: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 闈㈡澘鍛戒护鎵ц浜嬩欢澶勭悊
+        /// </summary>
+        private void Panel_OnCommandExecute(object sender, DebugCommand command)
+        {
+            try
+            {
+                if (CurrentSession == null)
+                {
+                    ToastHelper.Warning("璇峰厛閫夋嫨璁惧");
+                    return;
+                }
+
+                // 楠岃瘉鍛戒护鏉冮檺
+                if (!_sessionManager.ValidateCommand(CurrentSession.SessionId, command))
+                {
+                    ToastHelper.Warning("褰撳墠浼氳瘽鐘舵�佷笉鍏佽鎵ц姝ゆ搷浣滐紝璇峰厛杩涘叆缁存姢妯″紡");
+                    return;
+                }
+
+                // 閫氳繃 DeviceService 鎵ц瀹為檯鍛戒护
+                if (_deviceService != null)
+                {
+                    ExecuteViaDeviceService(command);
+                }
+                else
+                {
+                    // 闄嶇骇妯″紡锛氬熀纭�璁炬柦鏈垵濮嬪寲鏃朵繚鐣欏師鏈夎涓�
+                    log.Warn("DeviceService 鏈垵濮嬪寲锛屼娇鐢ㄩ檷绾фā寮忚褰曞懡浠�");
+                    ExecuteFallback(command);
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error($"鎵ц鍛戒护澶辫触: {command.CommandType}", ex);
+                ToastHelper.Error($"鍛戒护鎵ц澶辫触: {ex.Message}");
+
+                // 璁板綍澶辫触鎿嶄綔
+                var operation = new DebugOperation
+                {
+                    SessionId = CurrentSession?.SessionId ?? "",
+                    CommandType = command.CommandType,
+                    Parameters = SerializeParameters(command),
+                    Outcome = "Failed",
+                    ErrorMessage = ex.Message,
+                    Timestamp = DateTime.Now
+                };
+                _debugModel.RecordOperation(operation);
+                OperationLogs.Insert(0, operation);
+            }
+        }
+
+        /// <summary>
+        /// 閫氳繃 DeviceService 鎵ц璁惧鍛戒护
+        /// </summary>
+        private void ExecuteViaDeviceService(DebugCommand command)
+        {
+            try
+            {
+                var ownerToken = new ResourceOwnerToken(
+                    OwnerType.Maintenance,
+                    CurrentSession.SessionId);
+
+                var deviceCommand = BuildDeviceCommand(command);
+
+                var result = _deviceService.ExecuteCommandAsync(
+                    command.DeviceId,
+                    deviceCommand,
+                    ownerToken,
+                    CancellationToken.None).GetAwaiter().GetResult();
+
+                var operation = new DebugOperation
+                {
+                    SessionId = CurrentSession.SessionId,
+                    CommandType = command.CommandType,
+                    Parameters = SerializeParameters(command),
+                    Outcome = result.Success ? "Success" : "Failed",
+                    DeviceReadback = result.Readback ?? "",
+                    ErrorCode = result.ErrorCode ?? "",
+                    ErrorMessage = result.ErrorMessage ?? "",
+                    Timestamp = DateTime.Now
+                };
+
+                _debugModel.RecordOperation(operation);
+                OperationLogs.Insert(0, operation);
+
+                if (result.Success)
+                    ToastHelper.Success($"鍛戒护 {command.CommandType} 鎵ц鎴愬姛");
+                else
+                    ToastHelper.Warning($"鍛戒护鎵ц澶辫触: {result.ErrorMessage}");
+
+                log.Info($"鎵ц鍛戒护: {command.CommandType}, 璁惧: {command.DeviceId}, 缁撴灉: {(result.Success ? "鎴愬姛" : "澶辫触")}");
+            }
+            catch (Exception ex)
+            {
+                log.Error($"DeviceService 鎵ц鍛戒护澶辫触: {ex.Message}", ex);
+                ToastHelper.Error($"璁惧閫氫俊澶辫触: {ex.Message}");
+
+                var operation = new DebugOperation
+                {
+                    SessionId = CurrentSession.SessionId,
+                    CommandType = command.CommandType,
+                    Parameters = SerializeParameters(command),
+                    Outcome = "Failed",
+                    ErrorMessage = ex.Message,
+                    Timestamp = DateTime.Now
+                };
+                _debugModel.RecordOperation(operation);
+                OperationLogs.Insert(0, operation);
+            }
+        }
+
+        /// <summary>
+        /// 闄嶇骇妯″紡锛氭棤 DeviceService 鏃剁殑鍚庡澶勭悊
+        /// </summary>
+        private void ExecuteFallback(DebugCommand command)
+        {
+            var operation = new DebugOperation
+            {
+                SessionId = CurrentSession.SessionId,
+                CommandType = command.CommandType,
+                Parameters = SerializeParameters(command),
+                Outcome = "Success",
+                Timestamp = DateTime.Now
+            };
+
+            _debugModel.RecordOperation(operation);
+            OperationLogs.Insert(0, operation);
+
+            ToastHelper.Success($"鍛戒护 {command.CommandType} 宸插彂閫侊紙闄嶇骇妯″紡锛�");
+            log.Info($"[闄嶇骇妯″紡] 鎵ц鍛戒护: {command.CommandType}, 璁惧: {command.DeviceId}");
+        }
+
+        /// <summary>
+        /// 鏍规嵁鍛戒护绫诲瀷鏋勫缓瀹為檯璁惧鍛戒护瀛楃涓�
+        /// </summary>
+        private string BuildDeviceCommand(DebugCommand command)
+        {
+            if (command is PowerSetCommand psc)
+            {
+                return $"VOLT {psc.Voltage};CURR {psc.CurrentLimit};OUTP {(psc.Output ? "ON" : "OFF")}";
+            }
+            else if (command is PowerMeasureCommand)
+            {
+                return "MEAS:ALL?";
+            }
+            else if (command is CanSendCommand csc)
+            {
+                return $"CAN:SEND {csc.Frame}";
+            }
+            else if (command is PlcWriteCommand pwc)
+            {
+                return $"WRITE:{pwc.IoAddress}:{(pwc.Value ? "TRUE" : "FALSE")}";
+            }
+            return command.CommandType;
+        }
+
+        /// <summary>
+        /// 搴忓垪鍖栧懡浠ゅ弬鏁颁负JSON瀛楃涓�
+        /// </summary>
+        private string SerializeParameters(DebugCommand command)
+        {
+            try
+            {
+                switch (command)
+                {
+                    case PowerSetCommand psc:
+                        return $"{{Voltage:{psc.Voltage},CurrentLimit:{psc.CurrentLimit},Output:{psc.Output}}}";
+                    case PowerMeasureCommand _:
+                        return "{}";
+                    case CanSendCommand csc:
+                        return $"{{Frame:{csc.Frame},Data:{BitConverter.ToString(csc.Data)}}}";
+                    case PlcWriteCommand pwc:
+                        return $"{{IoAddress:{pwc.IoAddress},Value:{pwc.Value}}}";
+                    default:
+                        return "{}";
+                }
+            }
+            catch
+            {
+                return "{}";
+            }
+        }
+
+        /// <summary>
+        /// 鍔犺浇鎿嶄綔鏃ュ織
+        /// </summary>
+        private void LoadOperationLogs()
+        {
+            try
+            {
+                if (CurrentSession == null) return;
+
+                var ops = _debugModel.GetOperationsBySession(CurrentSession.SessionId);
+                OperationLogs = new ObservableCollection<DebugOperation>(ops.Take(100));
+            }
+            catch (Exception ex)
+            {
+                log.Error("鍔犺浇鎿嶄綔鏃ュ織澶辫触", ex);
+            }
+        }
+
+        /// <summary>
+        /// 鏇存柊鎸夐挳鍚敤鐘舵��
+        /// </summary>
+        private void UpdateButtonStates()
+        {
+            if (CurrentSession == null)
+            {
+                CanEnterMaintenance = false;
+                CanExitMaintenance = false;
+                SessionStateText = "";
+                return;
+            }
+
+            var state = CurrentSession.State;
+            CanEnterMaintenance = state == DebugSessionState.ReadOnly;
+            CanExitMaintenance = state == DebugSessionState.MaintenanceActive;
+
+            // 鏇存柊浼氳瘽鐘舵�佹枃鏈拰棰滆壊
+            SessionStateText = state.ToString();
+            SessionStateBrush = state switch
+            {
+                DebugSessionState.ReadOnly => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#2196F3")),
+                DebugSessionState.MaintenanceActive => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50")),
+                DebugSessionState.Faulted => new SolidColorBrush(Colors.Red),
+                DebugSessionState.RecoveryRequired => new SolidColorBrush(Colors.Orange),
+                _ => new SolidColorBrush(Colors.Gray)
+            };
+
+            // 鏇存柊闈㈡澘鐨勭淮鎶ゆā寮�
+            if (panelContainer.Content is PowerSupplyDebugPanel powerPanel)
+                powerPanel.IsMaintenanceMode = state == DebugSessionState.MaintenanceActive;
+            else if (panelContainer.Content is CanDebugPanel canPanel)
+                canPanel.IsMaintenanceMode = state == DebugSessionState.MaintenanceActive;
+            else if (panelContainer.Content is PlcDebugPanel plcPanel)
+                plcPanel.IsMaintenanceMode = state == DebugSessionState.MaintenanceActive;
+        }
+
+        #region 浜嬩欢澶勭悊
+
+        private void SimpleMaskControl_Loaded(object sender, RoutedEventArgs e)
+        {
+            LoadDeviceList();
+            Loaded -= SimpleMaskControl_Loaded;
+        }
+
+        private void DeviceSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            OnDeviceSelected();
+        }
+
+        private void RfhBtn_Click(object sender, RoutedEventArgs e)
+        {
+            LoadDeviceList();
+        }
+
+        private void EnterMaintenanceBtn_Click(object sender, RoutedEventArgs e)
+        {
+            try
+            {
+                if (CurrentSession == null) return;
+
+                // 璇锋眰缁存姢妯″紡
+                if (_sessionManager.RequestMaintenance(CurrentSession.SessionId))
+                {
+                    // 婵�娲荤淮鎶ゆā寮�
+                    _sessionManager.ActivateMaintenance(CurrentSession.SessionId);
+                    UpdateButtonStates();
+
+                    // 閲嶆柊鍔犺浇闈㈡澘浠ユ洿鏂癐sMaintenanceMode
+                    LoadPanel(CurrentDeviceKind);
+
+                    ToastHelper.Success("宸茶繘鍏ョ淮鎶ゆ帶鍒舵ā寮�");
+                    log.Info($"浼氳瘽 {CurrentSession.SessionId} 杩涘叆缁存姢妯″紡");
+                }
+                else
+                {
+                    ToastHelper.Warning("鏃犳硶杩涘叆缁存姢妯″紡锛岃妫�鏌ヨ澶囩姸鎬�");
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("杩涘叆缁存姢妯″紡澶辫触", ex);
+                ToastHelper.Error($"杩涘叆缁存姢妯″紡澶辫触: {ex.Message}");
+            }
+        }
+
+        private void ExitMaintenanceBtn_Click(object sender, RoutedEventArgs e)
+        {
+            try
+            {
+                if (CurrentSession == null) return;
+
+                if (MsgBoxHelper.WarnMessage(Window.GetWindow(this), "纭畾瑕侀��鍑虹淮鎶ゆā寮忓悧锛�") == MessageBoxResult.OK)
+                {
+                    _sessionManager.CloseSession(CurrentSession.SessionId);
+                    CurrentSession = null;
+                    UpdateButtonStates();
+
+                    // 娓呯┖闈㈡澘
+                    panelContainer.Content = new TextBlock
+                    {
+                        Text = "璇烽�夋嫨璁惧",
+                        HorizontalAlignment = HorizontalAlignment.Center,
+                        VerticalAlignment = VerticalAlignment.Center,
+                        FontSize = 16,
+                        Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#999"))
+                    };
+
+                    HasDevice = false;
+                    SelectedDevice = null;
+
+                    ToastHelper.Success("宸查��鍑虹淮鎶ゆā寮�");
+                    log.Info("宸查��鍑虹淮鎶ゆā寮�");
+                }
+            }
+            catch (Exception ex)
+            {
+                log.Error("閫�鍑虹淮鎶ゆā寮忓け璐�", ex);
+                ToastHelper.Error($"閫�鍑虹淮鎶ゆā寮忓け璐�: {ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 浠� CAN Endpoint 瑙f瀽鎬荤嚎鍚嶇О锛堟牸寮�: 鎬荤嚎鍚�:鑺傜偣缂栧彿锛�
+        /// </summary>
+        private string ParseCanBusName(string endpoint)
+        {
+            if (string.IsNullOrEmpty(endpoint)) return "CAN_BUS_1";
+            int idx = endpoint.IndexOf(':');
+            return idx > 0 ? endpoint.Substring(0, idx) : endpoint;
+        }
+
+        /// <summary>
+        /// 浠� CAN Endpoint 瑙f瀽鑺傜偣缂栧彿
+        /// </summary>
+        private int? ParseCanNodeIndex(string endpoint)
+        {
+            if (string.IsNullOrEmpty(endpoint)) return 0;
+            int idx = endpoint.IndexOf(':');
+            if (idx > 0 && idx < endpoint.Length - 1 && int.TryParse(endpoint.Substring(idx + 1), out int n))
+                return n;
+            return 0;
+        }
+
+        #endregion
+    }
+}

--
Gitblit v1.9.1