using Common.Attributes; using DeviceCommand.Base; using Prism.Events; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Windows.Input; using TSMasterCAN; using UIShare.GlobalVariable; using UIShare.PubEvent; using UIShare.UIViewModel; using UIShare.ViewModelBase; namespace MonitorModule.ViewModels.Dialogs { public class ValueLimitViewModel : DialogViewModelBase { #region 属性 private string _Title = "报警设置"; public string Title { get { return _Title; } set { SetProperty(ref _Title, value); } } private ValueLimitVM _SelectValueLimitVM; public ValueLimitVM SelectValueLimitVM { get => _SelectValueLimitVM; set => SetProperty(ref _SelectValueLimitVM, value); } private ObservableCollection _ValueLimitList; public ObservableCollection ValueLimitList { get => _ValueLimitList; set => SetProperty(ref _ValueLimitList, value); } //private ObservableCollection _DeviceSingleList = new(); //public ObservableCollection DeviceSingleList //{ // get => _DeviceSingleList; // set => SetProperty(ref _DeviceSingleList, value); //} //private string _SelectCurve; //public string SelectCurve //{ // get => _SelectCurve; // set => SetProperty(ref _SelectCurve, value); //} #endregion #region 命令 public ICommand SaveCommand { get; set; } public ICommand CloseCommand { get; set; } //public ICommand SelectSignalCommand { get; set; } public ICommand DeleteCommand { get; set; } #endregion private GlobalInfo _globalInfo { get; set; } private SystemConfig _systemConfig { get; set; } private IScopedProvider _scope { get; set; } private DeviceManager? _deviceManager; /// SignalName → CAN信号标识,用于DBC加载/卸载时清理 private readonly Dictionary _canSignalMap = new(); private SubscriptionToken? _dbcLoadedToken; private SubscriptionToken? _dbcUnloadedToken; public ValueLimitViewModel(IContainerProvider containerProvider) : base(containerProvider) { _globalInfo = containerProvider.Resolve(); _eventAggregator = containerProvider.Resolve(); SaveCommand = new DelegateCommand(OnSave); CloseCommand = new DelegateCommand(OnClose); //SelectSignalCommand = new DelegateCommand(SelectSignal); DeleteCommand = new DelegateCommand(Deletel); } #region 命令处理 private void Deletel() { if (SelectValueLimitVM == null) { return; } else { ValueLimitList.Remove(SelectValueLimitVM); SelectValueLimitVM = null; } } private void InitData() { ValueLimitList = new ObservableCollection(); //DeviceSingleList = new ObservableCollection(); _canSignalMap.Clear(); // 1. 加载已保存的 ValueLimitList(保留用户设置) if (_systemConfig?.ValueLimitList != null) { foreach (var item in _systemConfig.ValueLimitList) ValueLimitList.Add(item); } // 2. 发现设备方法信号并加入 ValueLimitList / DeviceSingleList foreach (var (displayName, fingerprint, methodName) in DiscoverDeviceSignals()) { //DeviceSingleList.Add(displayName); EnsureValueLimit(fingerprint, displayName, displayName, methodName); } // 3. 发现 CAN 信号并加入 ValueLimitList,同时收集有效信号名 var validCanSignalNames = new HashSet(); foreach (var (displayName, fingerprint, methodName) in DiscoverCanSignals()) { _canSignalMap[displayName] = (fingerprint, methodName); EnsureValueLimit(fingerprint, displayName, displayName, methodName); validCanSignalNames.Add(displayName); } // 4. 清理已不存在于 DBC 中的 CAN 信号(Fingerprint 以 "CAN" 开头且不在有效集合中) var staleCanSignals = ValueLimitList .Where(x => !string.IsNullOrEmpty(x.Fingerprint) && x.Fingerprint.StartsWith("CAN") && !validCanSignalNames.Contains(x.SignalName)) .ToList(); foreach (var item in staleCanSignals) ValueLimitList.Remove(item); } /// /// 发现当前作用域所有可监测的设备方法信号(与 MonitorViewModel 逻辑一致)。 /// 返回 (DisplayName, Fingerprint, MethodName) 列表。 /// private IEnumerable<(string DisplayName, string Fingerprint, string MethodName)> DiscoverDeviceSignals() { if (_deviceManager?.DeviceMap == null) yield break; var instanceToFp = BuildInstanceToFingerprint(); foreach (var kvp in _deviceManager.DeviceMap) { string deviceName = kvp.Key; var device = kvp.Value; if (!instanceToFp.TryGetValue(device, out string? fingerprint)) continue; var methods = device.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => { if (m.GetCustomAttribute() == null) return false; if (m.ReturnType != typeof(Task)) return false; var parms = m.GetParameters(); return parms.Length == 0 || (parms.Length == 1 && parms[0].ParameterType == typeof(CancellationToken)); }); foreach (var method in methods) { var attr = method.GetCustomAttribute(); string displayName = !string.IsNullOrEmpty(attr?.Description) ? $"{deviceName}.{attr.Description}" : $"{deviceName}.{method.Name}"; yield return (displayName, fingerprint, method.Name); } } } /// /// 构建设备实例 → 硬件指纹的反向查找表。 /// private Dictionary BuildInstanceToFingerprint() { var map = new Dictionary(ReferenceEqualityComparer.Instance); if (_globalInfo?.HardwarePool == null) return map; foreach (var poolEntry in _globalInfo.HardwarePool) { var lazy = poolEntry.Value; if (lazy.IsValueCreated && lazy.Value != null) map[lazy.Value] = poolEntry.Key; } return map; } /// /// 发现当前已加载 DBC 中的 CAN 信号(以 ConfigurationList 为白名单)。 /// 返回 (DisplayName, Fingerprint, MethodName) 列表。 /// private IEnumerable<(string DisplayName, string Fingerprint, string MethodName)> DiscoverCanSignals() { if (_deviceManager?.CANFD == null) yield break; if (_systemConfig?.ConfigurationList == null) yield break; var msgDb = DBCParse.MsgDatabase; string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint(); foreach (var cfg in _systemConfig.ConfigurationList) { if (string.IsNullOrEmpty(cfg.SignalName)) continue; if (cfg.Channel < 0 || cfg.Channel >= msgDb.Count) continue; string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, (uint)cfg.Channel); string methodName = CANSignalBroadcaster.BuildMethodName((uint)cfg.MessageID, cfg.SignalName); string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName); if (ExistsInDbc(methodName, msgDb[cfg.Channel])) yield return (displayName, fingerprint, methodName); } } /// /// 判断指定的 MethodName 是否存在于该通道的 DBC 消息数据库中。 /// private static bool ExistsInDbc(string methodName, List<_Msg_> messages) { if (string.IsNullOrEmpty(methodName)) return false; int dotIndex = methodName.IndexOf('.'); if (dotIndex <= 0 || dotIndex >= methodName.Length - 1) return false; if (!uint.TryParse(methodName.Substring(0, dotIndex), System.Globalization.NumberStyles.HexNumber, null, out uint msgId)) return false; string signalName = methodName.Substring(dotIndex + 1); return messages.Any(m => m.msg_id == msgId && m.signal_Name != null && m.signal_Name.Contains(signalName)); } /// /// 确保 ValueLimitList 中存在指定信号;不存在则添加默认值。 /// private void EnsureValueLimit(string fingerprint, string displayName, string signalName, string methodName = "") { if (ValueLimitList.Any(x => x.SignalName == signalName)) return; ValueLimitList.Add(new ValueLimitVM { DisplayName = displayName, Fingerprint = fingerprint, SignalName = signalName, MethodName = methodName, Upper = 9999, Lower = -9999, UpperExtreme = 9999, LowerExtreme = -9999, }); } /// /// 从 ValueLimitList 和 DeviceSingleList 中移除指定信号。 /// private void RemoveValueLimit(string signalName) { var item = ValueLimitList.FirstOrDefault(x => x.SignalName == signalName); if (item != null) ValueLimitList.Remove(item); //if (DeviceSingleList.Contains(signalName)) // DeviceSingleList.Remove(signalName); } //private void SelectSignal() //{ // if (ValueLimitList.Where(x => x.SignalName == SelectCurve).Count() > 0) // { // return; // } // ValueLimitList.Add(new ValueLimitVM // { // SignalName = SelectCurve, // Upper = 9999, // Lower = -9999, // UpperExtreme = 9999, // LowerExtreme = -9999, // }); //} private void OnClose() { RequestClose.Invoke(); } private void OnSave() { _systemConfig.ValueLimitList = ValueLimitList; ConfigService.Save(_systemConfig); } #endregion public override void OnDialogOpened(IDialogParameters parameters) { // 1. 优先执行基类的打开逻辑(如果基类有需要初始化的通用事务) base.OnDialogOpened(parameters); // 2. 解析参数 if (parameters != null && parameters.ContainsKey("Scope")) // 修复:Prism 中应使用 ContainsKey { _scope = parameters.GetValue("Scope"); _systemConfig = _scope.Resolve(); _deviceManager = _scope.Resolve(); // 3. 订阅 DBC 加载/卸载事件 _dbcLoadedToken = _eventAggregator.GetEvent() .Subscribe(OnDbcLoaded, ThreadOption.UIThread); _dbcUnloadedToken = _eventAggregator.GetEvent() .Subscribe(OnDbcUnloaded, ThreadOption.UIThread); // 4. 初始化数据 InitData(); } } public override void OnDialogClosed() { base.OnDialogClosed(); // 取消 DBC 事件订阅,避免内存泄漏 if (_dbcLoadedToken != null) _eventAggregator.GetEvent().Unsubscribe(_dbcLoadedToken); if (_dbcUnloadedToken != null) _eventAggregator.GetEvent().Unsubscribe(_dbcUnloadedToken); } /// /// DBCLoadedEvent 回调:清理该通道中不存在于 DBC 的 CAN 信号限制项。 /// private void OnDbcLoaded(DBCLoadedArgs args) { if (args.Scope != _systemConfig?.Title) return; if (_deviceManager?.CANFD == null) return; int channel = (int)args.Channel; var msgDb = DBCParse.MsgDatabase; if (channel < 0 || channel >= msgDb.Count) return; string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint(); string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, args.Channel); // 1. 移除该通道中已失效的 CAN 信号限制项 var staleSignals = _canSignalMap .Where(kvp => kvp.Value.Fingerprint == fingerprint && !ExistsInDbc(kvp.Value.MethodName, msgDb[channel])) .Select(kvp => kvp.Key) .ToList(); foreach (var signalName in staleSignals) { RemoveValueLimit(signalName); _canSignalMap.Remove(signalName); } // 2. 根据 ConfigurationList 补充新加载的有效信号 if (_systemConfig?.ConfigurationList != null) { foreach (var cfg in _systemConfig.ConfigurationList.Where(c => c.Channel == channel)) { if (string.IsNullOrEmpty(cfg.SignalName)) continue; string methodName = CANSignalBroadcaster.BuildMethodName((uint)cfg.MessageID, cfg.SignalName); string displayName = CANSignalBroadcaster.BuildDisplayName(cfg.MessageName, cfg.SignalName); if (ExistsInDbc(methodName, msgDb[channel])) { _canSignalMap[displayName] = (fingerprint, methodName); //if (!DeviceSingleList.Contains(displayName)) // DeviceSingleList.Add(displayName); EnsureValueLimit(fingerprint, displayName, displayName, methodName); } } } } /// /// DBCUnloadedEvent 回调:删除该通道所有 CAN 信号的限制项。 /// private void OnDbcUnloaded(DBCUnloadedArgs args) { if (args.Scope != _systemConfig?.Title) return; string canDeviceFingerprint = _deviceManager.GetCanDeviceFingerprint(); string fingerprint = CANSignalBroadcaster.BuildFingerprint(canDeviceFingerprint, args.Channel); var toRemove = _canSignalMap .Where(kvp => kvp.Value.Fingerprint == fingerprint) .Select(kvp => kvp.Key) .ToList(); foreach (var signalName in toRemove) { RemoveValueLimit(signalName); _canSignalMap.Remove(signalName); } } } }