监控逻辑
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
using System;
|
||||
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.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using UIShare.GlobalVariable;
|
||||
using UIShare.PubEvent;
|
||||
using UIShare.UIViewModel;
|
||||
using UIShare.ViewModelBase;
|
||||
using ZLGUSBCANFD;
|
||||
|
||||
namespace MonitorModule.ViewModels.Dialogs
|
||||
{
|
||||
@@ -37,41 +39,48 @@ namespace MonitorModule.ViewModels.Dialogs
|
||||
get => _ValueLimitList;
|
||||
set => SetProperty(ref _ValueLimitList, value);
|
||||
}
|
||||
private ObservableCollection<string> _DeviceSingleList = new();
|
||||
//private ObservableCollection<string> _DeviceSingleList = new();
|
||||
|
||||
public ObservableCollection<string> DeviceSingleList
|
||||
{
|
||||
get => _DeviceSingleList;
|
||||
set => SetProperty(ref _DeviceSingleList, value);
|
||||
}
|
||||
private string _SelectCurve;
|
||||
//public ObservableCollection<string> DeviceSingleList
|
||||
//{
|
||||
// get => _DeviceSingleList;
|
||||
// set => SetProperty(ref _DeviceSingleList, value);
|
||||
//}
|
||||
//private string _SelectCurve;
|
||||
|
||||
public string SelectCurve
|
||||
{
|
||||
get => _SelectCurve;
|
||||
set => SetProperty(ref _SelectCurve, value);
|
||||
}
|
||||
//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 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);
|
||||
//SelectSignalCommand = new DelegateCommand(SelectSignal);
|
||||
DeleteCommand = new DelegateCommand(Deletel);
|
||||
InitData();
|
||||
}
|
||||
#region 命令处理
|
||||
private void Deletel()
|
||||
@@ -90,35 +99,138 @@ namespace MonitorModule.ViewModels.Dialogs
|
||||
|
||||
private void InitData()
|
||||
{
|
||||
//ValueLimitList = new(_systemConfig.ValueLimitList);
|
||||
//foreach (var _PollingRead in _devices.PollingReadDic)
|
||||
//{
|
||||
// string deviceName = _PollingRead.Key;
|
||||
// PollingRead pollingRead = _PollingRead.Value;
|
||||
ValueLimitList = new ObservableCollection<ValueLimitVM>();
|
||||
//DeviceSingleList = new ObservableCollection<string>();
|
||||
_canSignalMap.Clear();
|
||||
|
||||
// var props = pollingRead.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
// .Where(p => p.PropertyType == typeof(double));
|
||||
// 1. 加载已保存的 ValueLimitList(保留用户设置)
|
||||
if (_systemConfig?.ValueLimitList != null)
|
||||
{
|
||||
foreach (var item in _systemConfig.ValueLimitList)
|
||||
ValueLimitList.Add(item);
|
||||
}
|
||||
|
||||
// foreach (var prop in props)
|
||||
// {
|
||||
// DeviceSingleList.Add(deviceName + "." + prop.Name);
|
||||
// }
|
||||
//}
|
||||
//foreach (var Signal in _systemConfig.ConfigurationList)
|
||||
//{
|
||||
// DeviceSingleList.Add($"{Signal.Channel}/{Signal.MessageName}/{Signal.SignalName}");
|
||||
//}
|
||||
// 2. 发现设备方法信号并加入 ValueLimitList / DeviceSingleList
|
||||
foreach (var signal in DiscoverDeviceSignals())
|
||||
{
|
||||
//DeviceSingleList.Add(signal);
|
||||
EnsureValueLimit(signal);
|
||||
}
|
||||
|
||||
// 3. 发现 CAN 信号并加入 ValueLimitList / DeviceSingleList
|
||||
foreach (var (displayName, fingerprint, methodName) in DiscoverCanSignals())
|
||||
{
|
||||
//DeviceSingleList.Add(displayName);
|
||||
_canSignalMap[displayName] = (fingerprint, methodName);
|
||||
EnsureValueLimit(displayName);
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectSignal()
|
||||
/// <summary>
|
||||
/// 发现当前作用域所有可监测的设备方法信号(与 MonitorViewModel 逻辑一致)。
|
||||
/// 返回 DisplayName 列表。
|
||||
/// </summary>
|
||||
private IEnumerable<string> DiscoverDeviceSignals()
|
||||
{
|
||||
if (ValueLimitList.Where(x => x.SignalName == SelectCurve).Count() > 0)
|
||||
if (_deviceManager?.DeviceMap == null) yield break;
|
||||
|
||||
var instanceToFp = BuildInstanceToFingerprint();
|
||||
foreach (var kvp in _deviceManager.DeviceMap)
|
||||
{
|
||||
return;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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?.DBCParser?.MsgDatabase == null) yield break;
|
||||
if (_systemConfig?.ConfigurationList == null) yield break;
|
||||
|
||||
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
||||
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((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 signalName)
|
||||
{
|
||||
if (ValueLimitList.Any(x => x.SignalName == signalName)) return;
|
||||
|
||||
ValueLimitList.Add(new ValueLimitVM
|
||||
{
|
||||
SignalName = SelectCurve,
|
||||
SignalName = signalName,
|
||||
Upper = 9999,
|
||||
Lower = -9999,
|
||||
UpperExtreme = 9999,
|
||||
@@ -126,6 +238,34 @@ namespace MonitorModule.ViewModels.Dialogs
|
||||
});
|
||||
}
|
||||
|
||||
/// <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();
|
||||
@@ -146,7 +286,96 @@ namespace MonitorModule.ViewModels.Dialogs
|
||||
if (parameters != null && parameters.ContainsKey("Scope")) // 修复:Prism 中应使用 ContainsKey
|
||||
{
|
||||
_scope = parameters.GetValue<IScopedProvider>("Scope");
|
||||
_systemConfig= _scope.Resolve<SystemConfig>();
|
||||
_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?.DBCParser?.MsgDatabase == null) return;
|
||||
|
||||
int channel = (int)args.Channel;
|
||||
var msgDb = _deviceManager.CANFD.DBCParser.MsgDatabase;
|
||||
if (channel < 0 || channel >= msgDb.Count) return;
|
||||
|
||||
string fingerprint = CANSignalBroadcaster.BuildFingerprint(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(displayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DBCUnloadedEvent 回调:删除该通道所有 CAN 信号的限制项。
|
||||
/// </summary>
|
||||
private void OnDbcUnloaded(DBCUnloadedArgs args)
|
||||
{
|
||||
if (args.Scope != _systemConfig?.Title) return;
|
||||
|
||||
string fingerprint = CANSignalBroadcaster.BuildFingerprint(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,9 +109,12 @@ namespace MonitorModule.ViewModels
|
||||
};
|
||||
private int _colorIndex;
|
||||
|
||||
/// <summary>广播器(由 UIShare 层注入,每 Scope 一个实例)</summary>
|
||||
/// <summary>设备数据广播器(全局单例)</summary>
|
||||
private HardwareDataBroadcaster? _broadcaster;
|
||||
|
||||
/// <summary>CAN 信号广播器(全局单例)</summary>
|
||||
private CANSignalBroadcaster? _canSignalBroadcaster;
|
||||
|
||||
/// <summary>用于 OxyPlot X 轴相对秒数</summary>
|
||||
private readonly Stopwatch _stopwatch = new();
|
||||
|
||||
@@ -318,6 +321,7 @@ namespace MonitorModule.ViewModels
|
||||
_systemConfig = _scope.Resolve<SystemConfig>();
|
||||
RaisePropertyChanged(nameof(Channels));
|
||||
_broadcaster = _scope.Resolve<HardwareDataBroadcaster>();
|
||||
_canSignalBroadcaster = _scope.Resolve<CANSignalBroadcaster>();
|
||||
IsInitiated = true;
|
||||
|
||||
if (_monitorCTS == null) _monitorCTS = new CancellationTokenSource();
|
||||
@@ -334,6 +338,8 @@ namespace MonitorModule.ViewModels
|
||||
// 4. 启动广播器(Discover + Start)
|
||||
_broadcaster.Discover();
|
||||
_broadcaster.Start();
|
||||
_canSignalBroadcaster.Discover();
|
||||
_canSignalBroadcaster.Start();
|
||||
|
||||
// 5. 订阅 HardwareDataReportedEvent(UI 线程回调)
|
||||
_subscriptionToken = _eventAggregator.GetEvent<HardwareDataReportedEvent>()
|
||||
|
||||
@@ -73,15 +73,15 @@
|
||||
Width="auto" />
|
||||
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.ContextMenu>
|
||||
<!--<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="删除"
|
||||
Foreground="Red"
|
||||
Command="{Binding DeleteCommand}" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
</DataGrid.ContextMenu>-->
|
||||
</DataGrid>
|
||||
<StackPanel Grid.Row="1"
|
||||
<!--<StackPanel Grid.Row="1"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0,10,0,0">
|
||||
@@ -95,7 +95,7 @@
|
||||
<Button Content="添加"
|
||||
Margin="10,0,10,0"
|
||||
Command="{Binding SelectSignalCommand}" />
|
||||
</StackPanel>
|
||||
</StackPanel>-->
|
||||
<StackPanel Grid.Row="1"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
|
||||
Reference in New Issue
Block a user