using System;
|
using System.Collections.Generic;
|
using System.IO;
|
using System.Linq;
|
using System.Text;
|
using System.Threading;
|
using System.Threading.Tasks;
|
using System.Windows;
|
using System.Windows.Media;
|
using System.Windows.Threading;
|
using Aging.Infrastructure.Drivers;
|
using CanSimulator;
|
using log4net;
|
using Plugin.DeviceConfig.Entity;
|
using Plugin.DeviceConfig.Model;
|
using Plugin.DeviceDebug.Entity;
|
using Plugin.DeviceDebug.Model;
|
using Plugin.DeviceDebug.Panels;
|
|
namespace Plugin.IntegrationTest
|
{
|
public partial class MainWindow : Window
|
{
|
private int _passed;
|
private int _failed;
|
private int _totalSteps;
|
private int _currentStep;
|
private string _testDir = string.Empty;
|
private string _logFilePath = string.Empty;
|
private readonly StringBuilder _logBuffer = new();
|
|
// 嵌入的插件实例
|
private Plugin.DeviceConfig.DeviceConfig? _deviceConfigPlugin;
|
private Plugin.DeviceDebug.DeviceDebug? _deviceDebugPlugin;
|
private static readonly ILog _testLog = LogManager.GetLogger("IntegrationTest");
|
|
// CAN 模拟器
|
private CanBusSimulator? _canBus;
|
private CanDebugPanel? _canDebugPanel;
|
|
public MainWindow()
|
{
|
InitializeComponent();
|
}
|
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
{
|
InitializePlugins();
|
InitializeCanSimulator();
|
}
|
|
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
{
|
// 清理 CAN 模拟器
|
_canBus?.Dispose();
|
CanDriverAdapter.ResetAllBuses();
|
|
// 不删除测试目录,保留数据以便重启后恢复
|
// (如需手动重置,使用工具栏中的 "重置数据" 按钮)
|
}
|
|
/// <summary>
|
/// 初始化两个插件并嵌入到对应 Tab 中
|
/// </summary>
|
private void InitializePlugins()
|
{
|
// 使用固定目录保存数据(重启后不丢失)
|
string appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
_testDir = Path.Combine(appData, "PluginIntegrationTest");
|
Directory.CreateDirectory(_testDir);
|
_logFilePath = Path.Combine(_testDir, "test_result.log");
|
|
txtStatus.Text = $"数据目录: {_testDir}";
|
|
try
|
{
|
// 初始化仪器仪表注册插件
|
_deviceConfigPlugin = new Plugin.DeviceConfig.DeviceConfig();
|
_deviceConfigPlugin.SetBaseFolder(_testDir);
|
_deviceConfigPlugin.SetLogger(_testLog);
|
_deviceConfigPlugin.InitFinish();
|
deviceConfigHost.Child = _deviceConfigPlugin;
|
|
// 初始化硬件调试插件
|
_deviceDebugPlugin = new Plugin.DeviceDebug.DeviceDebug();
|
_deviceDebugPlugin.SetBaseFolder(_testDir);
|
_deviceDebugPlugin.SetLogger(_testLog);
|
_deviceDebugPlugin.InitFinish();
|
deviceDebugHost.Child = _deviceDebugPlugin;
|
|
// 预置 CAN 测试设备(方便直接验证驱动与面板)
|
SeedPresetCanDevices();
|
|
// 刷新插件内部列表(需手动调用插件方法)
|
_deviceConfigPlugin.ForceRefreshForTest();
|
_deviceDebugPlugin.ForceRefreshForTest();
|
|
txtStatus.Text = "插件初始化完成 - 可切换 Tab 手动测试";
|
Log("[INFO] 插件初始化成功");
|
Log($"[INFO] 测试目录: {_testDir}");
|
Log("[INFO] DeviceConfig 已嵌入到 '仪器仪表注册' Tab");
|
Log("[INFO] DeviceDebug 已嵌入到 '硬件调试' Tab");
|
Log("[INFO] 预置测试设备: CAN-001, CAN-002 (Transport=Can)");
|
Log("");
|
Log("[TIP] 使用流程:");
|
Log(" 1. 先切换到 '仪器仪表注册' Tab,添加设备、通道、Slot映射等");
|
Log(" 2. 已预置 CAN-001 (CAN_BUS_1:0) 和 CAN-002 (CAN_BUS_1:1) 供测试");
|
Log(" 3. 切换到 '硬件调试' Tab,选择 CAN-001 会自动打开 CAN 面板并连接总线");
|
Log(" 4. 切换到 'CAN 模拟器' Tab 可手动控制总线或另一个节点发送数据");
|
Log(" 5. 可随时点击 '运行自动化测试' 验证核心逻辑");
|
}
|
catch (Exception ex)
|
{
|
txtStatus.Text = $"插件初始化失败: {ex.Message}";
|
Log($"[ERROR] 插件初始化失败: {ex.Message}");
|
Log(ex.StackTrace ?? "");
|
}
|
}
|
|
/// <summary>
|
/// 预置 CAN 测试设备(仅当不存在时插入,避免覆盖用户修改)
|
/// </summary>
|
private void SeedPresetCanDevices()
|
{
|
try
|
{
|
var model = new DeviceConfigModel();
|
string dbPath = Path.Combine(_testDir, "data.db");
|
model.InitDatabase(dbPath);
|
|
int inserted = 0;
|
|
// CAN-001
|
if (model.GetDeviceById("CAN-001") == null)
|
{
|
model.SaveDevice(new DeviceDefinition
|
{
|
DeviceId = "CAN-001",
|
Kind = DeviceKind.CanDevice,
|
DriverType = "CAN",
|
Transport = TransportType.Can,
|
Endpoint = "CAN_BUS_1:0",
|
EffectiveVersion = "V1.0"
|
});
|
inserted++;
|
}
|
|
// CAN-002
|
if (model.GetDeviceById("CAN-002") == null)
|
{
|
model.SaveDevice(new DeviceDefinition
|
{
|
DeviceId = "CAN-002",
|
Kind = DeviceKind.CanDevice,
|
DriverType = "CAN",
|
Transport = TransportType.Can,
|
Endpoint = "CAN_BUS_1:1",
|
EffectiveVersion = "V1.0"
|
});
|
inserted++;
|
}
|
|
Log(inserted > 0
|
? $"[INFO] 预置 CAN 设备: 新增 {inserted} 个"
|
: "[INFO] 预置 CAN 设备已存在,未覆盖");
|
}
|
catch (Exception ex)
|
{
|
Log($"[WARN] 预置 CAN 设备失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 重置测试数据(删除本地目录中的 db 文件)
|
/// </summary>
|
private void ResetTestData()
|
{
|
try
|
{
|
if (MessageBox.Show("确定要删除本地保存的设备数据吗?\n此操作不可恢复!",
|
"确认重置", MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
|
{
|
return;
|
}
|
|
// 释放插件后删除数据
|
_deviceConfigPlugin = null;
|
_deviceDebugPlugin = null;
|
|
foreach (var file in Directory.GetFiles(_testDir, "*.db", SearchOption.AllDirectories))
|
{
|
try { File.Delete(file); } catch { }
|
}
|
|
GC.Collect();
|
GC.WaitForPendingFinalizers();
|
|
MessageBox.Show("数据已重置,请重新启动程序。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
}
|
catch (Exception ex)
|
{
|
MessageBox.Show($"重置失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
}
|
}
|
|
private void BtnClear_Click(object sender, RoutedEventArgs e)
|
{
|
txtLog.Clear();
|
txtSummary.Text = "就绪 - 请切换到对应 Tab 进行手动测试";
|
txtPassed.Text = "";
|
txtFailed.Text = "";
|
progressBar.Value = 0;
|
_passed = 0;
|
_failed = 0;
|
_currentStep = 0;
|
btnRun.IsEnabled = true;
|
}
|
|
private void BtnReset_Click(object sender, RoutedEventArgs e)
|
{
|
ResetTestData();
|
}
|
|
private async void BtnRun_Click(object sender, RoutedEventArgs e)
|
{
|
btnRun.IsEnabled = false;
|
txtLog.Clear();
|
_passed = 0;
|
_failed = 0;
|
_currentStep = 0;
|
|
_totalSteps = CountTestSteps();
|
UpdateProgress();
|
|
Log("========================================");
|
Log(" 仪器仪表注册 & 硬件调试 集成测试");
|
Log($" 时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
|
Log("========================================");
|
Log("");
|
|
// 为自动化测试创建独立的数据库目录
|
string autoTestDir = Path.Combine(_testDir, "auto_test_" + Guid.NewGuid().ToString("N").Substring(0, 8));
|
Directory.CreateDirectory(autoTestDir);
|
|
await Task.Run(() => RunAllTests(autoTestDir));
|
|
Log("");
|
Log("========================================");
|
string result = _failed == 0 ? "全部通过 ✓" : $"有 {_failed} 项失败 ✗";
|
Log($" 测试结果: {result}");
|
Log($" 通过: {_passed} 失败: {_failed} 总计: {_passed + _failed}");
|
Log("========================================");
|
|
Dispatcher.Invoke(() =>
|
{
|
txtSummary.Text = _failed == 0 ? "✓ 自动化测试全部通过" : $"✗ 有 {_failed} 项测试失败";
|
txtSummary.Foreground = _failed == 0
|
? System.Windows.Media.Brushes.Green
|
: System.Windows.Media.Brushes.Red;
|
txtPassed.Text = $"通过: {_passed}";
|
txtFailed.Text = $"失败: {_failed}";
|
btnRun.IsEnabled = true;
|
|
// 复制日志到输出目录
|
try
|
{
|
string appDir = AppDomain.CurrentDomain.BaseDirectory;
|
string copyPath = Path.Combine(appDir, "test_result.log");
|
File.Copy(_logFilePath, copyPath, overwrite: true);
|
}
|
catch { }
|
});
|
}
|
|
private int CountTestSteps() => 22;
|
|
private void RunAllTests(string testDir)
|
{
|
string configDbPath = Path.Combine(testDir, "device_config.db");
|
string debugDbPath = Path.Combine(testDir, "device_debug.db");
|
|
// ===== 第一部分:仪器仪表注册 =====
|
RunSection("第一部分:仪器仪表注册 (DeviceConfig)");
|
|
var configModel = TestInit("初始化设备配置数据库", () =>
|
{
|
var model = new DeviceConfigModel();
|
model.InitDatabase(configDbPath);
|
return model;
|
});
|
|
if (configModel == null)
|
{
|
SkipRemainingSection(9, "DeviceConfig 数据库初始化失败");
|
}
|
else
|
{
|
var device = TestInit("创建设备定义(电源设备)", () =>
|
{
|
var dev = new DeviceDefinition
|
{
|
DeviceId = "PS-001",
|
Kind = DeviceKind.PowerSupply,
|
DriverType = "SCPI",
|
Transport = TransportType.Tcp,
|
Endpoint = "192.168.1.100:5025",
|
EffectiveVersion = "V1.0",
|
CreatedAt = DateTime.Now,
|
UpdatedAt = DateTime.Now
|
};
|
configModel.SaveDevice(dev);
|
return dev;
|
});
|
|
TestStep("读取设备定义并验证", () =>
|
{
|
var read = configModel.GetDeviceById("PS-001");
|
Assert(read != null, "设备应存在");
|
Assert(read!.Kind == DeviceKind.PowerSupply, "种类应为PowerSupply");
|
Assert(read.Transport == TransportType.Tcp, "传输方式应为Tcp");
|
Assert(read.Endpoint == "192.168.1.100:5025", "端点地址应匹配");
|
});
|
|
TestStep("更新设备定义", () =>
|
{
|
Assert(device != null, "设备对象不应为空");
|
device!.Endpoint = "192.168.1.200:5025";
|
device.EffectiveVersion = "V2.0";
|
configModel.SaveDevice(device);
|
var updated = configModel.GetDeviceById("PS-001");
|
Assert(updated!.Endpoint == "192.168.1.200:5025", "端点应已更新");
|
Assert(updated.EffectiveVersion == "V2.0", "版本应已更新");
|
});
|
|
TestStep("添加设备通道", () =>
|
{
|
for (int i = 1; i <= 4; i++)
|
{
|
var ch = new DeviceChannel
|
{
|
DeviceId = "PS-001",
|
ChannelId = i,
|
Capability = i <= 2 ? "Voltage" : "Current",
|
MaxVoltage = 30.0,
|
MaxCurrent = 5.0,
|
Limits = $"{{\"min_v\":0,\"max_v\":30,\"min_i\":0,\"max_i\":5}}",
|
SessionKey = ""
|
};
|
configModel.SaveChannel(ch);
|
}
|
var channels = configModel.GetChannelsByDevice("PS-001");
|
Assert(channels.Count == 4, $"应有4个通道,实际{channels.Count}个");
|
});
|
|
TestStep("添加Slot映射", () =>
|
{
|
var mapping = new SlotDeviceMapping
|
{
|
SlotId = "SLOT-01",
|
Capability = "Power",
|
Version = "V2.0",
|
DeviceId = "PS-001",
|
ChannelId = 1,
|
MappingVersion = "MV-001"
|
};
|
configModel.SaveSlotMapping(mapping);
|
var mappings = configModel.GetSlotMappings();
|
Assert(mappings.Count == 1, "应有1条Slot映射");
|
Assert(mappings[0].DeviceId == "PS-001", "映射设备ID应匹配");
|
});
|
|
TestStep("创建配置版本", () =>
|
{
|
var ver = new DeviceConfigVersion
|
{
|
VersionId = "VER-001",
|
Status = VersionStatus.Draft,
|
Hash = configModel.ComputeConfigHash(),
|
Author = "TestUser",
|
Timestamp = DateTime.Now,
|
DiffDescription = "初始配置"
|
};
|
configModel.SaveConfigVersion(ver);
|
var versions = configModel.GetConfigVersions();
|
Assert(versions.Count == 1, "应有1个版本");
|
Assert(versions[0].Status == VersionStatus.Draft, "版本状态应为Draft");
|
Assert(!string.IsNullOrEmpty(versions[0].Hash), "哈希不应为空");
|
});
|
|
TestStep("冲突检测(应无冲突)", () =>
|
{
|
var conflicts = configModel.DetectConflicts();
|
Assert(conflicts.Count == 0, $"不应有冲突,实际: {string.Join("; ", conflicts)}");
|
});
|
|
TestStep("添加第二个设备并检测IP冲突", () =>
|
{
|
var dev2 = new DeviceDefinition
|
{
|
DeviceId = "PS-002",
|
Kind = DeviceKind.PowerSupply,
|
DriverType = "SCPI",
|
Transport = TransportType.Tcp,
|
Endpoint = "192.168.1.200:5025",
|
EffectiveVersion = "V1.0",
|
CreatedAt = DateTime.Now,
|
UpdatedAt = DateTime.Now
|
};
|
configModel.SaveDevice(dev2);
|
|
var dev3 = new DeviceDefinition
|
{
|
DeviceId = "PS-003",
|
Kind = DeviceKind.PowerSupply,
|
DriverType = "ModbusTCP",
|
Transport = TransportType.Tcp,
|
Endpoint = "192.168.1.200:5025",
|
EffectiveVersion = "V1.0",
|
CreatedAt = DateTime.Now,
|
UpdatedAt = DateTime.Now
|
};
|
configModel.SaveDevice(dev3);
|
|
var conflicts = configModel.DetectConflicts();
|
Assert(conflicts.Count > 0, "应检测到IP冲突");
|
Log($" [冲突] {conflicts[0]}");
|
});
|
|
TestStep("删除设备和清理", () =>
|
{
|
configModel.DeleteDevice("PS-003");
|
configModel.DeleteChannel("PS-001", 4);
|
var allDevices = configModel.GetAllDevices();
|
Assert(allDevices.Count == 2, $"删除后应有2个设备,实际{allDevices.Count}");
|
var channels = configModel.GetChannelsByDevice("PS-001");
|
Assert(channels.Count == 3, $"删除通道后应有3个,实际{channels.Count}");
|
});
|
}
|
|
Log("");
|
|
// ===== 第二部分:硬件调试 =====
|
RunSection("第二部分:硬件调试 (DeviceDebug)");
|
|
var debugModel = TestInit("初始化调试数据库", () =>
|
{
|
var model = new DeviceDebugModel();
|
model.InitDatabase(debugDbPath);
|
return model;
|
});
|
|
DebugSessionManager? sessionMgr = null;
|
if (debugModel == null)
|
{
|
SkipRemainingSection(8, "DeviceDebug 数据库初始化失败");
|
}
|
else
|
{
|
sessionMgr = TestInit("创建会话管理器", () =>
|
{
|
return new DebugSessionManager(debugModel, resourceManager: null);
|
});
|
|
if (sessionMgr == null)
|
{
|
SkipRemainingSection(7, "会话管理器创建失败");
|
}
|
else
|
{
|
string? sessionId = null;
|
TestStep("打开调试会话(ReadOnly)", () =>
|
{
|
var session = sessionMgr.OpenSession("PS-001", "OP001", "张工", "V2.0");
|
sessionId = session.SessionId;
|
Assert(!string.IsNullOrEmpty(sessionId), "会话ID不应为空");
|
Assert(session.State == DebugSessionState.ReadOnly, "初始状态应为ReadOnly");
|
Assert(session.DeviceId == "PS-001", "设备ID应匹配");
|
Log($" 会话ID: {sessionId}");
|
});
|
|
if (sessionId == null)
|
{
|
SkipRemainingSection(6, "打开会话失败");
|
}
|
else
|
{
|
TestStep("请求维护模式(ReadOnly → AcquiringMaintenance)", () =>
|
{
|
bool result = sessionMgr.RequestMaintenance(sessionId!);
|
Assert(result, "请求维护应成功");
|
var state = sessionMgr.GetSessionState(sessionId!);
|
Assert(state == DebugSessionState.AcquiringMaintenance, $"状态应为AcquiringMaintenance,实际{state}");
|
});
|
|
TestStep("激活维护模式(AcquiringMaintenance → MaintenanceActive)", () =>
|
{
|
sessionMgr.ActivateMaintenance(sessionId!);
|
var state = sessionMgr.GetSessionState(sessionId!);
|
Assert(state == DebugSessionState.MaintenanceActive, $"状态应为MaintenanceActive,实际{state}");
|
});
|
|
TestStep("验证命令执行权限", () =>
|
{
|
var cmd = new PowerSetCommand { Voltage = 12.5, CurrentLimit = 3.0, Output = true };
|
bool allowed = sessionMgr.ValidateCommand(sessionId!, cmd);
|
Assert(allowed, "MaintenanceActive状态下应允许命令执行");
|
|
var readOnlySession = sessionMgr.OpenSession("PS-002", "OP002", "李工", "V1.0");
|
bool readOnlyAllowed = sessionMgr.ValidateCommand(readOnlySession.SessionId, cmd);
|
Assert(!readOnlyAllowed, "ReadOnly状态下应拒绝命令执行");
|
});
|
|
TestStep("记录调试操作审计", () =>
|
{
|
var op = new DebugOperation
|
{
|
SessionId = sessionId!,
|
CommandType = "PowerSet",
|
Parameters = "{\"voltage\":12.5,\"current\":3.0,\"output\":true}",
|
Outcome = "Success",
|
DeviceReadback = "V=12.48,I=2.99",
|
ErrorCode = "",
|
ErrorMessage = ""
|
};
|
debugModel.RecordOperation(op);
|
|
var ops = debugModel.GetOperationsBySession(sessionId!);
|
Assert(ops.Count == 1, $"应有1条操作记录,实际{ops.Count}");
|
Assert(ops[0].CommandType == "PowerSet", "命令类型应匹配");
|
Assert(ops[0].Outcome == "Success", "结果应为Success");
|
});
|
|
TestStep("关闭会话(MaintenanceActive → Closing → Closed)", () =>
|
{
|
sessionMgr.CloseSession(sessionId!);
|
var state = sessionMgr.GetSessionState(sessionId!);
|
Assert(state == DebugSessionState.Closed, $"状态应为Closed,实际{state}");
|
|
var session = debugModel.GetSession(sessionId!);
|
Assert(session != null, "会话记录应仍存在");
|
Assert(session!.ClosedAt.HasValue, "关闭时间应已设置");
|
});
|
|
TestStep("故障和恢复测试", () =>
|
{
|
var faultSession = sessionMgr.OpenSession("PS-001", "OP001", "张工", "V2.0");
|
string faultId = faultSession.SessionId;
|
sessionMgr.RequestMaintenance(faultId);
|
sessionMgr.ActivateMaintenance(faultId);
|
|
sessionMgr.FaultSession(faultId, "通信超时");
|
var state = sessionMgr.GetSessionState(faultId);
|
Assert(state == DebugSessionState.Faulted, $"状态应为Faulted,实际{state}");
|
|
sessionMgr.MarkRecoveryRequired(faultId);
|
state = sessionMgr.GetSessionState(faultId);
|
Assert(state == DebugSessionState.RecoveryRequired, $"状态应为RecoveryRequired,实际{state}");
|
});
|
}
|
}
|
}
|
|
Log("");
|
|
// ===== 第三部分:跨模块联动验证 =====
|
RunSection("第三部分:跨模块联动验证");
|
|
if (configModel == null || debugModel == null)
|
{
|
SkipRemainingSection(4, "前置模块初始化失败,跳过联动测试");
|
}
|
else
|
{
|
TestStep("设备配置 → 调试会话数据一致性", () =>
|
{
|
var configDevice = configModel.GetDeviceById("PS-001");
|
Assert(configDevice != null, "配置中应存在PS-001");
|
|
var activeSessions = debugModel.GetActiveSessionsByDevice("PS-001");
|
Assert(activeSessions.Count > 0, "PS-001应有活跃会话记录");
|
Assert(activeSessions.All(s => s.DeviceId == "PS-001"), "所有会话应引用PS-001");
|
});
|
|
TestStep("配置版本哈希一致性", () =>
|
{
|
var currentHash = configModel.ComputeConfigHash();
|
var versions = configModel.GetConfigVersions();
|
Assert(versions.Count > 0, "应有版本记录");
|
|
string originalHash = versions[0].Hash;
|
Log($" 原始哈希: {originalHash.Substring(0, 16)}...");
|
Log($" 当前哈希: {currentHash.Substring(0, 16)}...");
|
Log($" 哈希变化: {(originalHash != currentHash ? "是(配置已更新)" : "否")}");
|
Assert(true, "哈希计算流程正常");
|
});
|
|
TestStep("操作审计完整性验证", () =>
|
{
|
var allSessions = debugModel.GetAllSessions();
|
int totalOps = 0;
|
foreach (var s in allSessions)
|
{
|
var ops = debugModel.GetOperationsBySession(s.SessionId);
|
totalOps += ops.Count;
|
}
|
Assert(totalOps > 0, $"应有操作审计记录,实际{totalOps}条");
|
Log($" 总会话数: {allSessions.Count}, 总操作记录: {totalOps}");
|
});
|
|
TestStep("设备组模板功能验证", () =>
|
{
|
var template = new DeviceGroupTemplate
|
{
|
TemplateId = "TPL-001",
|
DeviceType = "PowerSupply",
|
DriverType = "SCPI",
|
Transport = "Tcp",
|
DeviceCount = 4,
|
ChannelsPerDevice = 4,
|
AddressRule = "192.168.1.{0}",
|
SlotStart = 1
|
};
|
configModel.SaveGroupTemplate(template);
|
var templates = configModel.GetGroupTemplates();
|
Assert(templates.Count == 1, "应有1个模板");
|
Assert(templates[0].DeviceCount == 4, "设备数量应为4");
|
});
|
}
|
}
|
|
#region CAN 模拟器
|
|
/// <summary>
|
/// 初始化 CAN 模拟器
|
/// </summary>
|
private void InitializeCanSimulator()
|
{
|
try
|
{
|
// 创建 CAN 调试面板并嵌入到 Tab 中
|
_canDebugPanel = new CanDebugPanel();
|
canSimulatorHost.Child = _canDebugPanel;
|
|
Log("[INFO] CAN 模拟器已嵌入到 'CAN 模拟器' Tab");
|
}
|
catch (Exception ex)
|
{
|
Log($"[ERROR] CAN 模拟器初始化失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 启动总线按钮点击
|
/// </summary>
|
private void CanStartBtn_Click(object sender, RoutedEventArgs e)
|
{
|
try
|
{
|
string busName = canBusNameInput.Text.Trim();
|
if (string.IsNullOrEmpty(busName))
|
{
|
MessageBox.Show("请输入总线名称", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
return;
|
}
|
|
// 通过 DriverFactory 创建 CAN 驱动来启动总线
|
var driver = DriverFactory.CreateDriver("CAN_SIM_0", "CAN", $"{busName}:0");
|
driver.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult();
|
|
// 获取总线实例
|
var buses = CanDriverAdapter.GetAllBuses();
|
if (buses.TryGetValue(busName, out var bus))
|
{
|
_canBus = bus;
|
bus.Start();
|
|
// 更新 UI
|
canBusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50"));
|
canBusStatusText.Text = $"运行中 ({busName})";
|
canStartBtn.IsEnabled = false;
|
canStopBtn.IsEnabled = true;
|
|
Log($"[CAN] 总线 {busName} 已启动");
|
}
|
}
|
catch (Exception ex)
|
{
|
Log($"[CAN] 启动总线失败: {ex.Message}");
|
MessageBox.Show($"启动失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
}
|
}
|
|
/// <summary>
|
/// 停止总线按钮点击
|
/// </summary>
|
private void CanStopBtn_Click(object sender, RoutedEventArgs e)
|
{
|
try
|
{
|
_canBus?.Stop();
|
|
// 更新 UI
|
canBusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E"));
|
canBusStatusText.Text = "已停止";
|
canStartBtn.IsEnabled = true;
|
canStopBtn.IsEnabled = false;
|
|
Log("[CAN] 总线已停止");
|
}
|
catch (Exception ex)
|
{
|
Log($"[CAN] 停止总线失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 重置所有按钮点击
|
/// </summary>
|
private void CanResetBtn_Click(object sender, RoutedEventArgs e)
|
{
|
try
|
{
|
CanDriverAdapter.ResetAllBuses();
|
_canBus = null;
|
|
// 更新 UI
|
canBusIndicator.Fill = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9E9E9E"));
|
canBusStatusText.Text = "未启动";
|
canStartBtn.IsEnabled = true;
|
canStopBtn.IsEnabled = false;
|
|
Log("[CAN] 所有总线已重置");
|
}
|
catch (Exception ex)
|
{
|
Log($"[CAN] 重置失败: {ex.Message}");
|
}
|
}
|
|
#endregion
|
|
#region 测试辅助方法
|
|
private T? TestInit<T>(string name, Func<T> action) where T : class
|
{
|
_currentStep++;
|
UpdateProgress();
|
try
|
{
|
var result = action();
|
_passed++;
|
LogStep("PASS", name);
|
return result;
|
}
|
catch (Exception ex)
|
{
|
_failed++;
|
LogStep("FAIL", name, ex.Message);
|
return default;
|
}
|
}
|
|
private void TestStep(string name, Action action)
|
{
|
_currentStep++;
|
UpdateProgress();
|
try
|
{
|
action();
|
_passed++;
|
LogStep("PASS", name);
|
}
|
catch (Exception ex)
|
{
|
_failed++;
|
LogStep("FAIL", name, ex.Message);
|
}
|
}
|
|
private void Assert(bool condition, string message)
|
{
|
if (!condition)
|
throw new Exception($"断言失败: {message}");
|
}
|
|
private void RunSection(string title)
|
{
|
Log($"--- {title} ---");
|
Log("");
|
}
|
|
private void SkipRemainingSection(int count, string reason)
|
{
|
for (int i = 0; i < count; i++)
|
{
|
_currentStep++;
|
_failed++;
|
UpdateProgress();
|
LogStep("FAIL", $"[SKIP] 依赖失败: {reason}");
|
}
|
}
|
|
private void LogStep(string status, string name, string? error = null)
|
{
|
string line = status == "PASS"
|
? $" [PASS] {name}"
|
: $" [FAIL] {name} → {error}";
|
Log(line);
|
}
|
|
private void Log(string message)
|
{
|
_logBuffer.AppendLine(message);
|
Dispatcher.Invoke(() =>
|
{
|
txtLog.AppendText(message + Environment.NewLine);
|
txtLog.ScrollToEnd();
|
});
|
if (!string.IsNullOrEmpty(_logFilePath))
|
{
|
try { File.AppendAllText(_logFilePath, message + Environment.NewLine); }
|
catch { }
|
}
|
}
|
|
private void UpdateProgress()
|
{
|
Dispatcher.Invoke(() =>
|
{
|
if (_totalSteps > 0)
|
{
|
double pct = (_currentStep * 100.0) / _totalSteps;
|
progressBar.Value = Math.Min(pct, 100);
|
}
|
});
|
}
|
|
#endregion
|
}
|
}
|