监控逻辑

This commit is contained in:
hsc
2026-07-10 11:57:56 +08:00
parent 52d486e2b1
commit db8baed9c7
9 changed files with 435 additions and 97 deletions

View File

@@ -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);
}
}
}

View File

@@ -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. 订阅 HardwareDataReportedEventUI 线程回调)
_subscriptionToken = _eventAggregator.GetEvent<HardwareDataReportedEvent>()

View File

@@ -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"