diff --git a/DeviceCommand/Base/SerialPortConfig.cs b/DeviceCommand/Base/SerialPortConfig.cs new file mode 100644 index 0000000..d1dbd39 --- /dev/null +++ b/DeviceCommand/Base/SerialPortConfig.cs @@ -0,0 +1,27 @@ +using System.IO.Ports; + +namespace DeviceCommand.Base +{ + /// + /// 串口通信参数(DeviceCommand 内部纯数据类,供设备类构造函数使用)。 + /// 与 UIShare.UIViewModel.SerialPortConfigVM 字段一一对应, + /// StopBits / Parity 在此处使用 System.IO.Ports 强类型枚举, + /// 由 DeviceManager 从字符串解析后填入。 + /// + public class SerialPortConfig + { + public string PortName { get; set; } = "COM1"; + + public int BaudRate { get; set; } = 9600; + + public int DataBits { get; set; } = 8; + + public StopBits StopBits { get; set; } = StopBits.One; + + public Parity Parity { get; set; } = Parity.None; + + public int ReadTimeout { get; set; } = 3000; + + public int WriteTimeout { get; set; } = 3000; + } +} diff --git a/DeviceCommand/Base/Serial_Port.cs b/DeviceCommand/Base/Serial_Port.cs index 9c0ed0f..6dd8ea8 100644 --- a/DeviceCommand/Base/Serial_Port.cs +++ b/DeviceCommand/Base/Serial_Port.cs @@ -25,6 +25,15 @@ namespace DeviceCommand.Base _serialPort = new SerialPort(); } + /// + /// 通过 一次性配置串口通信参数。 + /// + public Serial_Port(SerialPortConfig config) : this() + { + if (config == null) return; + ConfigureDevice(config.PortName, config.BaudRate, config.DataBits, config.StopBits, config.Parity, config.ReadTimeout, config.WriteTimeout); + } + public void ConfigureDevice(string portName, int baudRate, int dataBits = 8, StopBits stopBits = StopBits.One, Parity parity = Parity.None, int readTimeout = 3000, int writeTimeout = 3000) { PortName = portName; diff --git a/DeviceCommand/Base/TCP.cs b/DeviceCommand/Base/TCP.cs index 28f5621..e4292db 100644 --- a/DeviceCommand/Base/TCP.cs +++ b/DeviceCommand/Base/TCP.cs @@ -23,6 +23,15 @@ namespace DeviceCommand.Base _tcpClient = new TcpClient(); } + /// + /// 通过 一次性配置 TCP 通信参数。 + /// + public Tcp(TcpConfig config) : this() + { + if (config == null) return; + ConfigureDevice(config.IPAddress, config.Port, config.SendTimeout, config.ReceiveTimeout); + } + public void ConfigureDevice(string ipAddress, int port, int sendTimeout = 3000, int receiveTimeout = 3000) { IPAddress = ipAddress; diff --git a/DeviceCommand/Base/TcpConfig.cs b/DeviceCommand/Base/TcpConfig.cs new file mode 100644 index 0000000..ae1abd3 --- /dev/null +++ b/DeviceCommand/Base/TcpConfig.cs @@ -0,0 +1,17 @@ +namespace DeviceCommand.Base +{ + /// + /// TCP 通信参数(DeviceCommand 内部纯数据类,供设备类构造函数使用)。 + /// 与 UIShare.UIViewModel.TcpConfigVM 字段一一对应,由 DeviceManager 在实例化时填充。 + /// + public class TcpConfig + { + public string IPAddress { get; set; } = "127.0.0.1"; + + public int Port { get; set; } = 502; + + public int SendTimeout { get; set; } = 3000; + + public int ReceiveTimeout { get; set; } = 3000; + } +} diff --git a/DeviceCommand/Devices/IT7800E.cs b/DeviceCommand/Devices/IT7800E.cs index ec57a1d..0f8e32c 100644 --- a/DeviceCommand/Devices/IT7800E.cs +++ b/DeviceCommand/Devices/IT7800E.cs @@ -14,11 +14,10 @@ namespace DeviceCommand.Device private const string ScpiDelimiter = "\n"; /// - /// 构造函数:初始化 IT7800E 交直流电源通信参数 + /// 构造函数:传入 一次性初始化 IT7800E 交直流电源通信参数。 /// - public IT7800E(string ipAddress, int port, int sendTimeout, int receiveTimeout) + public IT7800E(TcpConfig config) : base(config) { - ConfigureDevice(ipAddress, port, sendTimeout, receiveTimeout); } #region 1. IEEE 488.2 公共命令 diff --git a/DeviceCommand/Devices/N36200.cs b/DeviceCommand/Devices/N36200.cs index 99dac04..c4f4eb4 100644 --- a/DeviceCommand/Devices/N36200.cs +++ b/DeviceCommand/Devices/N36200.cs @@ -14,11 +14,10 @@ namespace DeviceCommand.Device private const string ScpiDelimiter = "\n"; /// - /// 构造函数:初始化 N36200/N36300 设备通信参数 + /// 构造函数:传入 一次性初始化 N36200/N36300 设备通信参数。 /// - public N36200(string ipAddress, int port, int sendTimeout, int receiveTimeout) + public N36200(TcpConfig config) : base(config) { - ConfigureDevice(ipAddress, port, sendTimeout, receiveTimeout); } #region 3.1. IEEE 488.2 公共命令 diff --git a/DeviceCommand/Devices/N36600.cs b/DeviceCommand/Devices/N36600.cs index 6579900..33f562d 100644 --- a/DeviceCommand/Devices/N36600.cs +++ b/DeviceCommand/Devices/N36600.cs @@ -22,9 +22,8 @@ namespace DeviceCommand.Device // 手册第 4 页明确规定:每条命令后面都要加结束符 0x0A (\n) private const string SCPIDelimiter = "\n"; - public N36600(string ipAddress, int port, int sendTimeout, int receiveTimeout) + public N36600(TcpConfig config) : base(config) { - ConfigureDevice(ipAddress, port, sendTimeout, receiveTimeout); } /// diff --git a/DeviceCommand/Devices/N69200.cs b/DeviceCommand/Devices/N69200.cs index 2b88a79..2d85091 100644 --- a/DeviceCommand/Devices/N69200.cs +++ b/DeviceCommand/Devices/N69200.cs @@ -14,11 +14,10 @@ namespace DeviceCommand.Device private const string ScpiDelimiter = "\n"; /// - /// 构造函数:初始化 N69200 电子负载通信参数 + /// 构造函数:传入 一次性初始化 N69200 电子负载通信参数。 /// - public N69200(string ipAddress, int port, int sendTimeout, int receiveTimeout) + public N69200(TcpConfig config) : base(config) { - ConfigureDevice(ipAddress, port, sendTimeout, receiveTimeout); } #region 3.1. IEEE 488.2 公共命令 diff --git a/DeviceCommand/Devices/SDS2000X_HD.cs b/DeviceCommand/Devices/SDS2000X_HD.cs index e44dbb0..ba7bd0f 100644 --- a/DeviceCommand/Devices/SDS2000X_HD.cs +++ b/DeviceCommand/Devices/SDS2000X_HD.cs @@ -14,11 +14,11 @@ namespace DeviceCommand.Device private const string ScpiDelimiter = "\n"; /// - /// 构造函数:初始化示波器通信参数 (鼎阳示波器网口 Socket 默认端口通常为 5025) + /// 构造函数:传入 一次性初始化示波器通信参数。 + /// 鼎阳示波器网口 Socket 默认端口通常为 5025,请在配置中设置。 /// - public SDS2000X_HD(string ipAddress, int port = 5025, int sendTimeout = 3000, int receiveTimeout = 3000) + public SDS2000X_HD(TcpConfig config) : base(config) { - ConfigureDevice(ipAddress, port, sendTimeout, receiveTimeout); } #region 1. IEEE 488.2 公共命令 diff --git a/DeviceCommand/Devices/SPAW7000.cs b/DeviceCommand/Devices/SPAW7000.cs index 3bed253..712fcc5 100644 --- a/DeviceCommand/Devices/SPAW7000.cs +++ b/DeviceCommand/Devices/SPAW7000.cs @@ -14,11 +14,10 @@ namespace DeviceCommand.Device private const string ScpiDelimiter = "\n"; /// - /// 构造函数:初始化 SPAW7000 功率分析记录仪通信参数 + /// 构造函数:传入 一次性初始化 SPAW7000 功率分析记录仪通信参数。 /// - public SPAW7000(string ipAddress, int port, int sendTimeout, int receiveTimeout) + public SPAW7000(TcpConfig config) : base(config) { - ConfigureDevice(ipAddress, port, sendTimeout, receiveTimeout); } #region 1. IEEE 488.2 公共命令 diff --git a/MainModule/ViewModels/AutomatedTestingViewModel.cs b/MainModule/ViewModels/AutomatedTestingViewModel.cs index 3392937..d9f4a7d 100644 --- a/MainModule/ViewModels/AutomatedTestingViewModel.cs +++ b/MainModule/ViewModels/AutomatedTestingViewModel.cs @@ -47,7 +47,7 @@ namespace MainModule.ViewModels public ICommand RefreshCommand { get; set; } public ICommand BackToProtocolCommand { get; set; } - public AutomatedTestingViewModel(IContainerProvider container) : base(container) + public AutomatedTestingViewModel(IContainerExtension container) : base(container) { // 每个 AutomatedTestingViewModel 实例创建独立的容器作用域 _scope = container.CreateScope(); @@ -55,7 +55,7 @@ namespace MainModule.ViewModels // 在该作用域内解析 ScopedContext —— 当前作用域唯一 _scopedContext = _scope.Resolve(); _stepRunning = _scope.Resolve(); - _systemConfig = _scope.Resolve();; + _systemConfig = _scope.Resolve(); // 关键:从同一个 _scope 解析 5 个子 VM,DI 会把同一个 ScopedContext 注入它们 CommandTreeVM = _scope.Resolve(); StepsManagerVM = _scope.Resolve(); @@ -64,7 +64,6 @@ namespace MainModule.ViewModels ParametersManagerVM = _scope.Resolve(); RefreshCommand = new DelegateCommand(OnRefresh); BackToProtocolCommand = new DelegateCommand(OnBackToProtocol); - } public void Dispose() diff --git a/UIShare/GlobalVariable/DeviceManager.cs b/UIShare/GlobalVariable/DeviceManager.cs index b69f0f7..a2dc989 100644 --- a/UIShare/GlobalVariable/DeviceManager.cs +++ b/UIShare/GlobalVariable/DeviceManager.cs @@ -1,45 +1,140 @@ using DeviceCommand.Base; -using DeviceCommand.Device; +using Logger; using Prism.Ioc; using System; using System.Collections.Generic; +using System.IO.Ports; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Reflection; +using UIShare.UIViewModel; namespace UIShare.GlobalVariable { + /// + /// 设备管理器:根据 反射实例化所有启用的设备, + /// 通过 多态统一管理,避免为每种设备单独硬编码字段。 + /// public class DeviceManager { public SystemConfig _systemConfig { get; set; } - public IContainerProvider _containerProvider { get; set; } - public IT7800E _iT7800E { get; set; } - public N36200 _n36200 { get; set; } - public N36600 _n36600 { get; set; } - public N69200 _n69200 { get; set; } - public SDS2000X_HD _sDS2000X_HD { get; set; } - public SPAW7000 _sPAW7000 { get; set; } - public IList DeviceList { get; set; } - public DeviceManager(IContainerProvider containerProvider,SystemConfig systemConfig) + public IList DeviceList { get; private set; } = new List(); + + /// 按 DeviceName 索引的设备字典,便于业务层按名取实例。 + public IDictionary DeviceMap { get; private set; } + = new Dictionary(StringComparer.OrdinalIgnoreCase); + + /// 类名 → Type 的反射缓存(仅扫描一次)。 + private static readonly IReadOnlyDictionary _deviceTypeMap = BuildDeviceTypeMap(); + + public DeviceManager(SystemConfig systemConfig) { - _containerProvider = containerProvider; _systemConfig = systemConfig; InitDevices(); } + /// + /// 扫描 所在程序集中所有可实例化的实现类, + /// 以类型名为键建立映射。这样新增设备类无需修改 DeviceManager。 + /// + private static IReadOnlyDictionary BuildDeviceTypeMap() + { + try + { + return typeof(IBaseInterface).Assembly + .GetTypes() + .Where(t => t.IsClass && !t.IsAbstract && typeof(IBaseInterface).IsAssignableFrom(t)) + .ToDictionary(t => t.Name, t => t, StringComparer.OrdinalIgnoreCase); + } + catch (ReflectionTypeLoadException ex) + { + LoggerHelper.Error($"扫描设备类型失败:{ex.Message}"); + return new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } + private void InitDevices() { - foreach(var Config in _systemConfig.DeviceList) + DeviceMap = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (_systemConfig?.DeviceList == null) return; + + foreach (var config in _systemConfig.DeviceList) { - if (Config.ConnectionType == "Tcp") - { + if (config == null || !config.IsEnabled) continue; - } - else if (Config.ConnectionType == "Serial") + if (string.IsNullOrWhiteSpace(config.DeviceType) || + !_deviceTypeMap.TryGetValue(config.DeviceType, out var deviceType)) { + LoggerHelper.Warn($"未识别的设备类型 [{config.DeviceType}],已跳过 [{config.DeviceName}]。"); + continue; + } + try + { + IBaseInterface? instance = config.ConnectionType switch + { + "Tcp" => CreateTcpDevice(deviceType, config.TcpConfig), + "Serial" => CreateSerialDevice(deviceType, config.SerialPortConfig), + _ => null + }; + + if (instance == null) + { + LoggerHelper.Warn($"设备 [{config.DeviceName}] 连接方式 [{config.ConnectionType}] 不支持,已跳过。"); + continue; + } + + DeviceList.Add(instance); + if (!string.IsNullOrWhiteSpace(config.DeviceName)) + { + DeviceMap[config.DeviceName] = instance; + } + + LoggerHelper.Info($"已加载设备 [{config.DeviceName} / {config.DeviceType} / {config.ConnectionType}]"); + } + catch (Exception ex) + { + var inner = ex.InnerException?.Message ?? ex.Message; + LoggerHelper.ErrorWithNotify($"设备 [{config.DeviceName}] 实例化失败:{inner}"); } } } + + /// + /// 实例化 TCP 类设备:将 转为 POCO, + /// 调用设备类的 (TcpConfig) 构造函数。 + /// + private static IBaseInterface? CreateTcpDevice(Type type, TcpConfigVM? vm) + { + vm ??= new TcpConfigVM(); + var cfg = new TcpConfig + { + IPAddress = vm.IPAddress, + Port = vm.Port, + SendTimeout = vm.SendTimeout, + ReceiveTimeout = vm.ReceiveTimeout + }; + return Activator.CreateInstance(type, cfg) as IBaseInterface; + } + + /// + /// 实例化串口类设备:将 转为 POCO, + /// StopBits / Parity 从字符串解析为枚举,调用设备类的 (SerialPortConfig) 构造函数。 + /// + private static IBaseInterface? CreateSerialDevice(Type type, SerialPortConfigVM? vm) + { + vm ??= new SerialPortConfigVM(); + var cfg = new SerialPortConfig + { + PortName = vm.PortName, + BaudRate = vm.BaudRate, + DataBits = vm.DataBits, + StopBits = Enum.TryParse(vm.StopBits, true, out var sb) ? sb : StopBits.One, + Parity = Enum.TryParse(vm.Parity, true, out var pa) ? pa : Parity.None, + ReadTimeout = vm.ReadTimeout, + WriteTimeout = vm.WriteTimeout + }; + return Activator.CreateInstance(type, cfg) as IBaseInterface; + } } }