using DeviceCommand.Base; using Logger; using Model.Models; using Prism.Events; using Prism.Ioc; using System; using System.Collections.Generic; using System.IO; using System.IO.Ports; using System.Linq; using System.Reflection; using UIShare.PubEvent; using UIShare.UIViewModel; using ZLGUSBCANFD; namespace UIShare.GlobalVariable { /// /// 设备管理器:根据 反射实例化所有启用的设备, /// 通过 多态统一管理,避免为每种设备单独硬编码字段。 /// public class DeviceManager:IDisposable { private object _lockObj = new object(); public SystemConfig _systemConfig { get; set; } private readonly GlobalInfo _globalInfo; private readonly string _scopeName; private readonly IEventAggregator _eventAggregator; /// 按 DeviceName 索引的设备字典,便于业务层按名取实例。 public IDictionary DeviceMap { get; private set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); /// 类名 → Type 的反射缓存(仅扫描一次)。 private static readonly IReadOnlyDictionary _deviceTypeMap = BuildDeviceTypeMap(); public ZLGCANFD CANFD { get; set; } public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo, IEventAggregator eventAggregator) { _systemConfig = systemConfig; _globalInfo = globalInfo; _eventAggregator = eventAggregator; // 用 SystemConfig.Title 作为作用域唯一标识,无需反查 ConfigDic _scopeName = _systemConfig.Title; InitDevices(); } /// /// 根据设备配置提取唯一的硬件指纹字符串。 /// Tcp → "Tcp:IP:Port";Serial → "Serial:PortName";无法识别则返回空字符串。 /// public 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}"; } if (string.Equals(config.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase) && config.CANConfig != null) { return $"CAN:{config.CANConfig.DeviceIndex}"; } return string.Empty; } /// /// 获取当前作用域配置的 CAN 设备硬件指纹(与 GlobalInfo.CanPool 的 Key 一致)。 /// 如 "CAN:0",其中 0 是 CANConfig.DeviceIndex。 /// public string GetCanDeviceFingerprint() { if (_systemConfig?.DeviceList == null) return string.Empty; var canConfig = _systemConfig.DeviceList.FirstOrDefault(d => d != null && d.IsEnabled && string.Equals(d.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase)); return canConfig != null ? ExtractHardwareFingerprint(canConfig) : string.Empty; } private void InitDevices() { DeviceMap = new Dictionary(StringComparer.OrdinalIgnoreCase); if (_systemConfig?.DeviceList == null) return; foreach (var config in _systemConfig.DeviceList) { if (config == null || !config.IsEnabled) continue; // CAN 设备:ZLGCANFD 不实现 IBaseInterface,通过 SystemConfig.CANFD 单独管理, // 按指纹从全局 CanPool 创建/复用实例,并注册作用域引用计数。 if (string.Equals(config.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase)) { var fingerprint = ExtractHardwareFingerprint(config); if (string.IsNullOrEmpty(fingerprint)) { LoggerHelper.Warn($"设备 [{config.DeviceName}] 无法提取硬件指纹(连接方式={config.ConnectionType}),已跳过。"); continue; } if (config.CANConfig == null) { LoggerHelper.Warn($"设备 [{config.DeviceName}] 缺少 CAN 连接参数,已跳过。"); continue; } try { // 按指纹全局唯一创建 ZLGCANFD 实例(maxChannels 默认 4,对应 USBCANFD-400U) // 波特率与终端电阻从 CANConfigVM 传入,初始化并启动通道 时直接使用 var canLazy = _globalInfo.CanPool.GetOrAdd(fingerprint, key => new Lazy(() => new ZLGCANFD(config.CANConfig.DeviceType, config.CANConfig.DeviceIndex, 4, config.CANConfig.ABitBaud, config.CANConfig.DBitBaud, config.CANConfig.EnableTerminalResistance))); _systemConfig.CANFD = canLazy.Value; CANFD = canLazy.Value; // 注册作用域引用计数 if (!string.IsNullOrEmpty(_scopeName)) { var scopeList = _globalInfo.DeviceAndScopeDic.GetOrAdd(fingerprint, _ => new Lazy>(() => new List())).Value; lock (scopeList) { if (!scopeList.Contains(_scopeName)) scopeList.Add(_scopeName); } } LoggerHelper.Info($"已加载 CAN 设备 [{config.DeviceName}] 指纹={fingerprint}(通过 SystemConfig.CANFD 管理)"); } catch (Exception ex) { LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 [{config.DeviceName}] 实例化失败:{ex.Message}"); } continue; } if (string.IsNullOrWhiteSpace(config.DeviceType) || !_deviceTypeMap.TryGetValue(config.DeviceType, out var deviceType)) { LoggerHelper.Warn($"未识别的设备类型 [{config.DeviceType}],已跳过 [{config.DeviceName}]。"); continue; } try { // 第一步:提取硬件指纹,为空则跳过 var fingerprint = ExtractHardwareFingerprint(config); if (string.IsNullOrEmpty(fingerprint)) { 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; } // 第五步:将当前作用域注册到指纹的作用域列表,用于引用计数与安全销毁 if (!string.IsNullOrEmpty(_scopeName)) { var scopeList = _globalInfo.DeviceAndScopeDic.GetOrAdd(fingerprint, _ => new Lazy>(() => new List())).Value; lock (scopeList) { if (!scopeList.Contains(_scopeName)) scopeList.Add(_scopeName); } } LoggerHelper.Info($"已加载设备 [{config.DeviceName} / {config.DeviceType} / {config.ConnectionType}] 指纹={fingerprint}"); } catch (Exception ex) { var inner = ex.InnerException?.Message ?? ex.Message; LoggerHelper.ErrorWithNotify(_scopeName, $"设备 [{config.DeviceName}] 实例化失败:{inner}"); } } } public async Task ConnectAllDevices(CancellationToken ct = default) { if (_systemConfig?.DeviceList == null) return; var tasks = new List(); foreach (var info in _systemConfig.DeviceList) { if (info == null || !info.IsEnabled) continue; if (string.IsNullOrWhiteSpace(info.DeviceName)) continue; // CAN 设备:直接打开 CAN 卡 if (string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase)) { tasks.Add(ConnectCanAsync(info)); continue; } if (!DeviceMap.TryGetValue(info.DeviceName, out var device)) continue; tasks.Add(ConnectInternalAsync(info, device, ct)); } await Task.WhenAll(tasks); } public async Task ConnectSpecifiedDevice(string deviceName, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(deviceName)) { LoggerHelper.Warn("ConnectSpecifiedDevice:设备名为空。"); return; } var info = _systemConfig?.DeviceList? .FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase)); if (info == null) { LoggerHelper.Warn($"ConnectSpecifiedDevice:未找到设备配置 [{deviceName}]。"); return; } // CAN 设备:直接打开 CAN 卡 if (string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase)) { await ConnectCanAsync(info); return; } if (!DeviceMap.TryGetValue(deviceName, out var device)) { LoggerHelper.Warn($"ConnectSpecifiedDevice:未找到设备 [{deviceName}]。"); return; } await ConnectInternalAsync(info, device, ct); } /// /// 异步关闭指定设备,释放底层连接并更新 UI 状态 /// public async Task CloseDeviceAsync(string deviceName) { if (string.IsNullOrWhiteSpace(deviceName)) return; var info = _systemConfig?.DeviceList? .FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase)); // CAN 设备:直接关闭 CAN 卡 if (info != null && string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase)) { await CloseCanAsync(info); return; } IBaseInterface? device; lock (_lockObj) { if (!DeviceMap.TryGetValue(deviceName, out device)) return; } await CloseInternalAsync(info, device); } /// /// 异步关闭所有设备 /// public async Task CloseAllDevicesAsync() { List tasks = new List(); lock (_lockObj) { if (DeviceMap.Count == 0 && (CANFD == null)) return; foreach (var kvp in DeviceMap) { string deviceName = kvp.Key; var device = kvp.Value; var info = _systemConfig?.DeviceList? .FirstOrDefault(d => d != null && string.Equals(d.DeviceName, deviceName, StringComparison.OrdinalIgnoreCase)); tasks.Add(CloseInternalAsync(info, device)); } } // CAN 设备:直接关闭 CAN 卡 if (CANFD != null) { var canInfo = _systemConfig?.DeviceList? .FirstOrDefault(d => d != null && string.Equals(d.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase)); if (canInfo != null) { tasks.Add(CloseCanAsync(canInfo)); } } await Task.WhenAll(tasks); LoggerHelper.Info("所有设备已执行关闭操作。"); } #region 辅助方法 private async Task CloseInternalAsync(DeviceInfoVM? info, IBaseInterface device) { string name = info?.DeviceName ?? device.GetType().Name; string conn = info?.ConnectionType ?? "?"; try { // 如果设备本身已经是断开状态,直接更新 UI 并返回 if (!device.IsConnected) { if (info != null) info.IsConnected = false; LoggerHelper.Info($"设备 [{name}] 本就处于断开状态。"); return; } await Task.Run(() => device.Close()); LoggerHelper.Info($"设备 [{name}/{conn}] 已成功关闭连接。"); } catch (Exception ex) { var inner = ex.InnerException?.Message ?? ex.Message; LoggerHelper.Error($"设备 [{name}/{conn}] 关闭连接时出现异常: {inner}"); } finally { // 无论关闭时是否抛出异常,均强制同步 UI 状态为未连接 if (info != null) { info.IsConnected = false; } } } private async Task ConnectInternalAsync(DeviceInfoVM? info, IBaseInterface device, CancellationToken ct) { string name = info?.DeviceName ?? device.GetType().Name; string conn = info?.ConnectionType ?? "?"; try { if (device.IsConnected) { if (info != null) info.IsConnected = true; LoggerHelper.Info($"设备 [{name}] 已连接,跳过。"); return; } bool ok = conn switch { "Tcp" => await ConnectTcpAsync(name, device, ct), "Serial" => await ConnectSerialAsync(name, device, ct), _ => false }; if (info != null) info.IsConnected = ok; if (ok) LoggerHelper.Info($"设备 [{name}/{conn}] 连接成功。"); else LoggerHelper.Warn($"设备 [{name}/{conn}] 连接失败。"); } catch (OperationCanceledException) { if (info != null) info.IsConnected = false; LoggerHelper.Warn($"设备 [{name}/{conn}] 连接已取消。"); } catch (Exception ex) { if (info != null) info.IsConnected = false; var inner = ex.InnerException?.Message ?? ex.Message; LoggerHelper.ErrorWithNotify(_scopeName, $"设备 [{name}/{conn}] 连接异常:{inner}"); } } private static async Task ConnectTcpAsync(string name, IBaseInterface device, CancellationToken ct) { if (device is not ITcp tcp) { LoggerHelper.Warn($"设备 [{name}] 配置为 Tcp 但未实现 ITcp,实际类型为 {device.GetType().Name}。"); return false; } return await tcp.ConnectAsync(ct); } private static async Task ConnectSerialAsync(string name, IBaseInterface device, CancellationToken ct) { if (device is not ISerialPort sp) { LoggerHelper.Warn($"设备 [{name}] 配置为 Serial 但未实现 ISerialPort,实际类型为 {device.GetType().Name}。"); return false; } return await sp.ConnectAsync(ct); } /// /// 打开 CAN 卡:打开设备 + 初始化并启动所有通道 + 自动加载 DBC + 启动信号广播 /// private async Task ConnectCanAsync(DeviceInfoVM info) { string name = info.DeviceName ?? "CAN"; try { if (CANFD == null) { LoggerHelper.Warn($"CAN 设备 [{name}] 尚未实例化,无法连接。"); info.IsConnected = false; return false; } if (info.IsConnected) { LoggerHelper.Info($"CAN 设备 [{name}] 已连接,跳过。"); return true; } bool ok = await Task.Run(() => { if (!CANFD.打开设备()) return false; // 自动加载 DBC 文件 if (_systemConfig?.DBCAutoLoadList != null) { foreach (var item in _systemConfig.DBCAutoLoadList) { if (item.DBCChannel < 0 || item.DBCChannel >= 4) continue; if (string.IsNullOrWhiteSpace(item.DBCFilePath)) continue; if (!File.Exists(item.DBCFilePath)) { // 发布 DBC 卸载事件,通知监控系统清除对应信号 _eventAggregator.GetEvent().Publish(new DBCUnloadedArgs { Channel = (uint)item.DBCChannel, Scope = _scopeName }); LoggerHelper.Warn($"CAN 通道 {item.DBCChannel} 自动加载 DBC 失败:文件不存在 [{item.DBCFilePath}]"); continue; } CANFD.初始化并启动通道((uint)item.DBCChannel); bool loadOk = CANFD.加载通道DBC文件((uint)item.DBCChannel, item.DBCFilePath); if (loadOk) { LoggerHelper.Info($"CAN 通道 {item.DBCChannel} 已自动加载 DBC:{item.DBCFilePath}"); // 发布 DBC 加载完成事件,通知监控系统刷新信号列表 _eventAggregator.GetEvent().Publish(new DBCLoadedArgs { Channel = (uint)item.DBCChannel, Scope = _scopeName }); } else { LoggerHelper.Warn($"CAN 通道 {item.DBCChannel} 自动加载 DBC 失败:{item.DBCFilePath}"); _eventAggregator.GetEvent().Publish(new DBCUnloadedArgs { Channel = (uint)item.DBCChannel, Scope = _scopeName }); } } } return true; }); info.IsConnected = ok; if (ok) LoggerHelper.Info($"CAN 设备 [{name}] 连接成功,已初始化 {CANFD.DBCParser.MaxChannels} 个通道。"); else LoggerHelper.Warn($"CAN 设备 [{name}] 连接失败。"); return ok; } catch (Exception ex) { info.IsConnected = false; var inner = ex.InnerException?.Message ?? ex.Message; LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 [{name}] 连接异常:{inner}"); return false; } } /// /// 关闭 CAN 卡 /// private async Task CloseCanAsync(DeviceInfoVM info) { string name = info.DeviceName ?? "CAN"; try { if (CANFD == null) { info.IsConnected = false; return; } if (!info.IsConnected) { LoggerHelper.Info($"CAN 设备 [{name}] 本就处于断开状态。"); return; } await Task.Run(() => CANFD.关闭CAN卡设备()); LoggerHelper.Info($"CAN 设备 [{name}] 已成功关闭连接。"); } catch (Exception ex) { var inner = ex.InnerException?.Message ?? ex.Message; LoggerHelper.Error($"CAN 设备 [{name}] 关闭连接时出现异常: {inner}"); } finally { info.IsConnected = false; } } 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 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; } 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; } public void Dispose() { if (string.IsNullOrEmpty(_scopeName)) return; // 遍历当前作用域用到的所有指纹,逐一移除本作用域的引用 var fingerprintsToRemove = new List(); foreach (var kvp in _globalInfo.DeviceAndScopeDic) { string fingerprint = kvp.Key; var scopeList = kvp.Value.IsValueCreated ? kvp.Value.Value : null; if (scopeList == null) continue; lock (scopeList) { scopeList.Remove(_scopeName); // 引用归零 → 标记为待清理 if (scopeList.Count == 0) fingerprintsToRemove.Add(fingerprint); } } // 对引用归零的指纹:销毁设备实例 + 从全局池中移除 foreach (var fingerprint in fingerprintsToRemove) { // 尝试从 HardwarePool 取出并销毁 if (_globalInfo.HardwarePool.TryRemove(fingerprint, out var lazy)) { if (lazy.IsValueCreated && lazy.Value is IBaseInterface device) { try { device.Close(); } catch { /* 销毁时忽略异常 */ } LoggerHelper.Info($"指纹 [{fingerprint}] 无作用域引用,已销毁设备实例。"); } } // 尝试从 CanPool 取出并销毁 if (_globalInfo.CanPool.TryRemove(fingerprint, out var canLazy)) { if (canLazy.IsValueCreated) { try { canLazy.Value.Dispose(); } catch { /* 销毁时忽略异常 */ } LoggerHelper.Info($"指纹 [{fingerprint}] 无作用域引用,已销毁 CAN 设备实例。"); } } // 同步清除 DeviceAndScopeDic 中的空条目 _globalInfo.DeviceAndScopeDic.TryRemove(fingerprint, out _); } DeviceMap.Clear(); } #endregion } }