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 { /// /// DeviceDebug.xaml 的交互逻辑 /// [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 _deviceList = new(); [ObservableProperty] private string _selectedDevice; [ObservableProperty] private DebugSession _currentSession; [ObservableProperty] private ObservableCollection _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(); } /// /// 强制刷新设备列表(供集成测试预置设备后调用) /// 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("DeviceManager"); _deviceService = container.GetUtil("DeviceService"); _resourceManager = container.GetUtil("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}"); } } /// /// 加载设备列表 /// 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(); 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}"); } } /// /// 解析设备选择字符串,获取 DeviceId /// private string ParseDeviceId(string selection) { if (string.IsNullOrEmpty(selection)) return string.Empty; int idx = selection.IndexOf(" ["); return idx > 0 ? selection.Substring(0, idx) : selection; } /// /// 解析设备选择字符串,获取 Kind /// 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; } /// /// 加载设备详细信息 /// 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); } } /// /// 选择设备时:打开会话(ReadOnly),加载对应面板 /// 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; // 打开会话(ReadOnly) 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}"); } } /// /// 根据设备类型加载对应的调试面板 /// 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}"); } } /// /// 面板命令执行事件处理 /// 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); } } /// /// 通过 DeviceService 执行设备命令 /// 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); } } /// /// 降级模式:无 DeviceService 时的后备处理 /// 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}"); } /// /// 根据命令类型构建实际设备命令字符串 /// 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; } /// /// 序列化命令参数为JSON字符串 /// 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 "{}"; } } /// /// 加载操作日志 /// private void LoadOperationLogs() { try { if (CurrentSession == null) return; var ops = _debugModel.GetOperationsBySession(CurrentSession.SessionId); OperationLogs = new ObservableCollection(ops.Take(100)); } catch (Exception ex) { log.Error("加载操作日志失败", ex); } } /// /// 更新按钮启用状态 /// 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(); // 重新加载面板以更新IsMaintenanceMode 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}"); } } /// /// 从 CAN Endpoint 解析总线名称(格式: 总线名:节点编号) /// 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; } /// /// 从 CAN Endpoint 解析节点编号 /// 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 } }