From 1faae12822fe78f8dcb4f7833261cdccffd4efeb Mon Sep 17 00:00:00 2001 From: hsc Date: Mon, 22 Jun 2026 17:22:21 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E5=A4=87=E6=95=B0=E9=87=8F=E4=B8=8D?= =?UTF-8?q?=E5=AF=B9=E7=AD=89=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UIShare/GlobalVariable/DeviceManager.cs | 54 +++++++++++++++++++++---- UIShare/GlobalVariable/GlobalInfo.cs | 9 ++++- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/UIShare/GlobalVariable/DeviceManager.cs b/UIShare/GlobalVariable/DeviceManager.cs index 7eaaf67..3202aa8 100644 --- a/UIShare/GlobalVariable/DeviceManager.cs +++ b/UIShare/GlobalVariable/DeviceManager.cs @@ -19,6 +19,7 @@ namespace UIShare.GlobalVariable { private object _lockObj = new object(); public SystemConfig _systemConfig { get; set; } + private readonly GlobalInfo _globalInfo; /// 按 DeviceName 索引的设备字典,便于业务层按名取实例。 public IDictionary DeviceMap { get; private set; } @@ -27,11 +28,33 @@ namespace UIShare.GlobalVariable /// 类名 → Type 的反射缓存(仅扫描一次)。 private static readonly IReadOnlyDictionary _deviceTypeMap = BuildDeviceTypeMap(); - public DeviceManager(SystemConfig systemConfig) + public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo) { _systemConfig = systemConfig; + _globalInfo = globalInfo; InitDevices(); } + /// + /// 根据设备配置提取唯一的硬件指纹字符串。 + /// Tcp → "Tcp:IP:Port";Serial → "Serial:PortName";无法识别则返回空字符串。 + /// + private static string ExtractHardwareFingerprint(DeviceInfoVM config) + { + if (string.Equals(config.ConnectionType, "Tcp", StringComparison.OrdinalIgnoreCase) + && config.TcpConfig != null) + { + return $"Tcp:{config.TcpConfig.IPAddress}:{config.TcpConfig.Port}"; + } + + if (string.Equals(config.ConnectionType, "Serial", StringComparison.OrdinalIgnoreCase) + && config.SerialPortConfig != null) + { + return $"Serial:{config.SerialPortConfig.PortName}"; + } + + return string.Empty; + } + private void InitDevices() { DeviceMap = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -51,24 +74,41 @@ namespace UIShare.GlobalVariable try { - IBaseInterface? instance = config.ConnectionType switch + // 第一步:提取硬件指纹,为空则跳过 + var fingerprint = ExtractHardwareFingerprint(config); + if (string.IsNullOrEmpty(fingerprint)) { - "Tcp" => CreateTcpDevice(deviceType, config.TcpConfig), - "Serial" => CreateSerialDevice(deviceType, config.SerialPortConfig), - _ => null - }; + LoggerHelper.Warn($"设备 [{config.DeviceName}] 无法提取硬件指纹(连接方式={config.ConnectionType}),已跳过。"); + continue; + } + + // 第二步:原子化获取或添加 Lazy 包装盒,确保同一指纹全局只创建一个实例 + var lazy = _globalInfo.HardwarePool.GetOrAdd(fingerprint, key => new Lazy(() => + { + return config.ConnectionType switch + { + "Tcp" => CreateTcpDevice(deviceType, config.TcpConfig)!, + "Serial" => CreateSerialDevice(deviceType, config.SerialPortConfig)!, + _ => null! + }; + })); + + // 第三步:安全拆盒,Lazy 内部线程锁保证只实例化一次 + var instance = lazy.Value; if (instance == null) { LoggerHelper.Warn($"设备 [{config.DeviceName}] 连接方式 [{config.ConnectionType}] 不支持,已跳过。"); continue; } + + // 第四步:绑定逻辑名 → 同一物理设备可被多个台架名称映射 if (!string.IsNullOrWhiteSpace(config.DeviceName)) { DeviceMap[config.DeviceName] = instance; } - LoggerHelper.Info($"已加载设备 [{config.DeviceName} / {config.DeviceType} / {config.ConnectionType}]"); + LoggerHelper.Info($"已加载设备 [{config.DeviceName} / {config.DeviceType} / {config.ConnectionType}] 指纹={fingerprint}"); } catch (Exception ex) { diff --git a/UIShare/GlobalVariable/GlobalInfo.cs b/UIShare/GlobalVariable/GlobalInfo.cs index ab5fc5c..fde936c 100644 --- a/UIShare/GlobalVariable/GlobalInfo.cs +++ b/UIShare/GlobalVariable/GlobalInfo.cs @@ -1,4 +1,6 @@ -using System; +using DeviceCommand.Base; +using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; @@ -13,6 +15,10 @@ namespace UIShare.GlobalVariable public Dictionary StepRunningDic { get; set; } public Dictionary ConfigDic { get; set; } public Dictionary ScopeDic { get; set; } + + /// 硬件指纹 → 设备实例的并发池,确保同一物理硬件全局只创建一个驱动实例。 + public ConcurrentDictionary> HardwarePool { get; set; } + public String UserName { get; set; } = "Not Logged in"; public bool IsAdmin { get; set; } = true; public string CurrentOpeningScope; @@ -35,6 +41,7 @@ namespace UIShare.GlobalVariable StepRunningDic = new(); ConfigDic = new(); ScopeDic = new(); + HardwarePool = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); CurrentScope = "default"; }