using Model.Models; using Prism.Events; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using UIShare.PubEvent; using UIShare.UIViewModel; namespace UIShare.GlobalVariable { /// /// CAN 信号广播器(全局单例,支持多作用域): /// 从所有已注册的 的 RealTimeSignals 字典中轮询读取信号值, /// 按每个已注册作用域的 ConfigurationList 分别映射,向对应作用域广播 HardwareDataReportedEvent。 /// /// 一拖三场景下 CAN 硬件共享,但各工位的 DBC/ConfigurationList 不同, /// 因此广播器必须遍历所有已注册作用域的配置分别广播。 /// /// public class CANSignalBroadcaster { private readonly IEventAggregator _eventAggregator; /// /// 作用域注册表:scopeName → (SystemConfig, 预构建的 ConfigMap)。 /// 每个工位的 MonitorViewModel 在初始化时调用 注册自己的配置。 /// private readonly ConcurrentDictionary ConfigMap)> _scopeRegistry = new(); /// 全局已注册的 CANMonitoringService 实例列表(静态,跨作用域共享) private static readonly ConcurrentDictionary _registeredServices = new(); private CancellationTokenSource? _cts; private Task? _broadcastTask; /// 广播轮询间隔(毫秒) public int PollingIntervalMs { get; set; } = 100; public CANSignalBroadcaster(SystemConfig systemConfig, IEventAggregator eventAggregator) { _eventAggregator = eventAggregator; // 构造时自动注册第一个作用域(向后兼容) RegisterScope(systemConfig.Title, systemConfig); } #region 服务注册(供 CANMonitoringService 调用) /// 注册一个 CANMonitoringService,使其信号纳入广播 public static void RegisterService(CANMonitoringService service) { _registeredServices.TryAdd(service, 0); } /// 注销一个 CANMonitoringService public static void UnregisterService(CANMonitoringService service) { _registeredServices.TryRemove(service, out _); } #endregion #region 启动 / 停止 /// 启动广播(幂等:多次调用只启动一次) public void Start() { if (_broadcastTask != null && !_broadcastTask.IsCompleted) return; _cts = new CancellationTokenSource(); _broadcastTask = Task.Run(() => BroadcastLoop(_cts.Token)); } /// 停止广播 public void Stop() { _cts?.Cancel(); _broadcastTask = null; } /// /// 注册一个作用域的 CAN 配置。 /// 每个工位在 MonitorViewModel 初始化时调用此方法,将自己的 SystemConfig 注册进来, /// 广播器会在每轮广播中为该作用域独立映射信号并广播。 /// /// 作用域名称(SystemConfig.Title) /// 该作用域的 SystemConfig public void RegisterScope(string scopeName, SystemConfig config) { if (string.IsNullOrEmpty(scopeName) || config == null) return; var configMap = BuildConfigMap(config); _scopeRegistry.AddOrUpdate(scopeName, (config, configMap), (_, _) => (config, configMap)); } /// 注销一个作用域(工位销毁时调用) public void UnregisterScope(string scopeName) { if (!string.IsNullOrEmpty(scopeName)) _scopeRegistry.TryRemove(scopeName, out _); } /// 兼容旧接口:Discover 已无需执行任何操作 public void Discover() { } #endregion #region 广播核心逻辑 /// /// 轮询所有已注册的 CANMonitoringService 的 RealTimeSignals, /// 遍历所有已注册作用域的配置分别映射并广播 HardwareDataReportedEvent。 /// private async Task BroadcastLoop(CancellationToken ct) { while (!ct.IsCancellationRequested) { try { // 快照当前作用域注册表,避免枚举期间被修改 var scopeSnapshot = _scopeRegistry.ToArray(); foreach (var scopeEntry in scopeSnapshot) { if (ct.IsCancellationRequested) return; string scopeName = scopeEntry.Key; var scopeConfig = scopeEntry.Value.Config; var configMap = scopeEntry.Value.ConfigMap; foreach (var service in _registeredServices.Keys) { if (service.IsStopped) continue; foreach (var kvp in service.RealTimeSignals) { if (ct.IsCancellationRequested) return; // 信号 Key 格式: "{channel}/{MessageName}/{SignalName}" if (!TryParseSignalKey(kvp.Key, out int channel, out string? messageName, out string? signalName)) continue; // 从该作用域的配置映射中查找对应的 MessageID if (!configMap.TryGetValue($"{channel}/{messageName}/{signalName}", out var cfg)) continue; string canFingerprint = $"CAN:{channel}"; string fingerprint = BuildFingerprint(canFingerprint, (uint)channel); string methodName = BuildMethodName((uint)cfg.MessageID, signalName); // 广播信号值到对应作用域 _eventAggregator.GetEvent().Publish(new HardwareReportArgs { Scope = scopeName, HardwareFingerprint = fingerprint, MethodName = methodName, Value = kvp.Value, Time = DateTime.Now }); // 报警检查(使用该作用域自己的配置) string alarmStatus = ValueLimitAlarmHelper.CheckAlarm(fingerprint, methodName, kvp.Value, scopeConfig); if (!string.IsNullOrEmpty(alarmStatus) && alarmStatus != "未报警") { _eventAggregator.GetEvent().Publish((scopeName, canFingerprint, alarmStatus)); } } } } await Task.Delay(PollingIntervalMs, ct); } catch (OperationCanceledException) { break; } catch (Exception ex) { Logger.LoggerHelper.Error($"CANSignalBroadcaster 广播异常: {ex.Message}"); await Task.Delay(1000, ct); } } } /// /// 从 SystemConfig.ConfigurationList 构建 "channel/MessageName/SignalName" → CANSignalConfig 的映射 /// private static Dictionary BuildConfigMap(SystemConfig systemConfig) { var map = new Dictionary(StringComparer.OrdinalIgnoreCase); if (systemConfig?.ConfigurationList == null) return map; foreach (var cfg in systemConfig.ConfigurationList) { if (string.IsNullOrEmpty(cfg.SignalName) || string.IsNullOrEmpty(cfg.MessageName)) continue; string key = $"{cfg.Channel}/{cfg.MessageName}/{cfg.SignalName}"; map.TryAdd(key, cfg); // 第一个匹配的优先 } return map; } /// 解析信号 Key: "{channel}/{MessageName}/{SignalName}" private static bool TryParseSignalKey(string key, out int channel, out string? messageName, out string? signalName) { channel = 0; messageName = null; signalName = null; var parts = key.Split('/'); if (parts.Length < 3) return false; if (!int.TryParse(parts[0], out channel)) return false; messageName = parts[1]; signalName = parts[2]; return !string.IsNullOrEmpty(messageName) && !string.IsNullOrEmpty(signalName); } #endregion #region 静态工具方法(保持向后兼容) /// 生成 CAN 信号的 DisplayName 格式:"{MessageName}.{SignalName}" public static string BuildDisplayName(string messageName, string signalName) { return $"{messageName}.{signalName}"; } /// 生成 CAN 信号的 MethodName 格式:"{MessageId:X}.{SignalName}" public static string BuildMethodName(uint messageId, string signalName) { return $"{messageId:X}.{signalName}"; } /// /// 生成 CAN 信号的 Fingerprint 格式:"{canDeviceFingerprint}:{channel}" /// canDeviceFingerprint 来自 DeviceManager.ExtractHardwareFingerprint,如 "CAN:0" /// public static string BuildFingerprint(string canDeviceFingerprint, uint channel) { return $"{canDeviceFingerprint}:{channel}"; } #endregion } }