Files
ACP/MonitorModule/ViewModels/Dialogs/ValueLimitViewModel.cs
2026-07-29 14:20:32 +08:00

399 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<ValueLimitVM> _ValueLimitList;
public ObservableCollection<ValueLimitVM> ValueLimitList
{
get => _ValueLimitList;
set => SetProperty(ref _ValueLimitList, value);
}
//private ObservableCollection<string> _DeviceSingleList = new();
//public ObservableCollection<string> 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;
/// <summary>SignalName → CAN信号标识用于DBC加载/卸载时清理</summary>
private readonly Dictionary<string, (string Fingerprint, string MethodName)> _canSignalMap = new();
private SubscriptionToken? _dbcLoadedToken;
private SubscriptionToken? _dbcUnloadedToken;
public ValueLimitViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
_globalInfo = containerProvider.Resolve<GlobalInfo>();
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
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<ValueLimitVM>();
//DeviceSingleList = new ObservableCollection<string>();
_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<string>();
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);
}
/// <summary>
/// 发现当前作用域所有可监测的设备方法信号(与 MonitorViewModel 逻辑一致)。
/// 返回 (DisplayName, Fingerprint, MethodName) 列表。
/// </summary>
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<MonitorableAttribute>() == null) return false;
if (m.ReturnType != typeof(Task<string>)) 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<MonitorableAttribute>();
string displayName = !string.IsNullOrEmpty(attr?.Description)
? $"{deviceName}.{attr.Description}"
: $"{deviceName}.{method.Name}";
yield return (displayName, fingerprint, method.Name);
}
}
}
/// <summary>
/// 构建设备实例 → 硬件指纹的反向查找表。
/// </summary>
private Dictionary<object, string> BuildInstanceToFingerprint()
{
var map = new Dictionary<object, string>(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;
}
/// <summary>
/// 发现当前已加载 DBC 中的 CAN 信号(以 ConfigurationList 为白名单)。
/// 返回 (DisplayName, Fingerprint, MethodName) 列表。
/// </summary>
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);
}
}
/// <summary>
/// 判断指定的 MethodName 是否存在于该通道的 DBC 消息数据库中。
/// </summary>
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));
}
/// <summary>
/// 确保 ValueLimitList 中存在指定信号;不存在则添加默认值。
/// </summary>
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,
});
}
/// <summary>
/// 从 ValueLimitList 和 DeviceSingleList 中移除指定信号。
/// </summary>
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<IScopedProvider>("Scope");
_systemConfig = _scope.Resolve<SystemConfig>();
_deviceManager = _scope.Resolve<DeviceManager>();
// 3. 订阅 DBC 加载/卸载事件
_dbcLoadedToken = _eventAggregator.GetEvent<DBCLoadedEvent>()
.Subscribe(OnDbcLoaded, ThreadOption.UIThread);
_dbcUnloadedToken = _eventAggregator.GetEvent<DBCUnloadedEvent>()
.Subscribe(OnDbcUnloaded, ThreadOption.UIThread);
// 4. 初始化数据
InitData();
}
}
public override void OnDialogClosed()
{
base.OnDialogClosed();
// 取消 DBC 事件订阅,避免内存泄漏
if (_dbcLoadedToken != null)
_eventAggregator.GetEvent<DBCLoadedEvent>().Unsubscribe(_dbcLoadedToken);
if (_dbcUnloadedToken != null)
_eventAggregator.GetEvent<DBCUnloadedEvent>().Unsubscribe(_dbcUnloadedToken);
}
/// <summary>
/// DBCLoadedEvent 回调:清理该通道中不存在于 DBC 的 CAN 信号限制项。
/// </summary>
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);
}
}
}
}
/// <summary>
/// DBCUnloadedEvent 回调:删除该通道所有 CAN 信号的限制项。
/// </summary>
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);
}
}
}
}