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
}
}