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.IntegrationTest/MainWindow.xaml.cs | 840 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 840 insertions(+), 0 deletions(-)
diff --git a/Plugin.IntegrationTest/MainWindow.xaml.cs b/Plugin.IntegrationTest/MainWindow.xaml.cs
new file mode 100644
index 0000000..3e80e2d
--- /dev/null
+++ b/Plugin.IntegrationTest/MainWindow.xaml.cs
@@ -0,0 +1,840 @@
+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锛屾坊鍔犺澶囥�侀�氶亾銆丼lot鏄犲皠绛�");
+ 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鏉lot鏄犲皠");
+ 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, "鐗堟湰鐘舵�佸簲涓篋raft");
+ Assert(!string.IsNullOrEmpty(versions[0].Hash), "鍝堝笇涓嶅簲涓虹┖");
+ });
+
+ TestStep("鍐茬獊妫�娴嬶紙搴旀棤鍐茬獊锛�", () =>
+ {
+ var conflicts = configModel.DetectConflicts();
+ Assert(conflicts.Count == 0, $"涓嶅簲鏈夊啿绐侊紝瀹為檯: {string.Join("; ", conflicts)}");
+ });
+
+ TestStep("娣诲姞绗簩涓澶囧苟妫�娴婭P鍐茬獊", () =>
+ {
+ 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("鎵撳紑璋冭瘯浼氳瘽锛圧eadOnly锛�", () =>
+ {
+ var session = sessionMgr.OpenSession("PS-001", "OP001", "寮犲伐", "V2.0");
+ sessionId = session.SessionId;
+ Assert(!string.IsNullOrEmpty(sessionId), "浼氳瘽ID涓嶅簲涓虹┖");
+ Assert(session.State == DebugSessionState.ReadOnly, "鍒濆鐘舵�佸簲涓篟eadOnly");
+ Assert(session.DeviceId == "PS-001", "璁惧ID搴斿尮閰�");
+ Log($" 浼氳瘽ID: {sessionId}");
+ });
+
+ if (sessionId == null)
+ {
+ SkipRemainingSection(6, "鎵撳紑浼氳瘽澶辫触");
+ }
+ else
+ {
+ TestStep("璇锋眰缁存姢妯″紡锛圧eadOnly 鈫� AcquiringMaintenance锛�", () =>
+ {
+ bool result = sessionMgr.RequestMaintenance(sessionId!);
+ Assert(result, "璇锋眰缁存姢搴旀垚鍔�");
+ var state = sessionMgr.GetSessionState(sessionId!);
+ Assert(state == DebugSessionState.AcquiringMaintenance, $"鐘舵�佸簲涓篈cquiringMaintenance锛屽疄闄厈state}");
+ });
+
+ TestStep("婵�娲荤淮鎶ゆā寮忥紙AcquiringMaintenance 鈫� MaintenanceActive锛�", () =>
+ {
+ sessionMgr.ActivateMaintenance(sessionId!);
+ var state = sessionMgr.GetSessionState(sessionId!);
+ Assert(state == DebugSessionState.MaintenanceActive, $"鐘舵�佸簲涓篗aintenanceActive锛屽疄闄厈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("鍏抽棴浼氳瘽锛圡aintenanceActive 鈫� Closing 鈫� Closed锛�", () =>
+ {
+ sessionMgr.CloseSession(sessionId!);
+ var state = sessionMgr.GetSessionState(sessionId!);
+ Assert(state == DebugSessionState.Closed, $"鐘舵�佸簲涓篊losed锛屽疄闄厈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, $"鐘舵�佸簲涓篎aulted锛屽疄闄厈state}");
+
+ sessionMgr.MarkRecoveryRequired(faultId);
+ state = sessionMgr.GetSessionState(faultId);
+ Assert(state == DebugSessionState.RecoveryRequired, $"鐘舵�佸簲涓篟ecoveryRequired锛屽疄闄厈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, "鍝堝笇璁$畻娴佺▼姝e父");
+ });
+
+ 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
+ }
+}
--
Gitblit v1.9.1