监控逻辑

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

@@ -85,6 +85,7 @@ namespace ADP
containerRegistry.RegisterScoped<DeviceManager>();
containerRegistry.RegisterSingleton<GlobalInfo>();
containerRegistry.RegisterSingleton<HardwareDataBroadcaster>();
containerRegistry.RegisterSingleton<CANSignalBroadcaster>();
//注册AutoMapper
var config = new MapperConfiguration(
cfg => cfg.AddProfile<AutoMapperProfile>(),

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"

View File

@@ -1,6 +1,8 @@
using Model.Models;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIShare.PubEvent;
using ZLGUSBCANFD;
@@ -8,78 +10,108 @@ using ZLGUSBCANFD;
namespace UIShare.GlobalVariable
{
/// <summary>
/// CAN 信号广播器:订阅 ZLGCANFD.OnDbcMessageDecoded 事件,
/// 将解码后的 DBC 信号值通过 HardwareDataReportedEvent 广播给监控系统。
/// DeviceManager 创建和管理,生命周期与 CAN 设备绑定。
/// CAN 信号广播器(全局单例):
/// 统一订阅 GlobalInfo.CanPool 中所有已实例化的 ZLGCANFD 设备,
/// DBC 解码后的信号值广播给所有引用该 CAN 设备的作用域,
/// 并同步检查各作用域 ValueLimitList 的超限报警。
/// </summary>
public class CANSignalBroadcaster : IDisposable
{
private readonly IEventAggregator _eventAggregator;
private readonly string _scopeName;
private ZLGCANFD? _canfd;
private readonly GlobalInfo _globalInfo;
private bool _disposed;
public CANSignalBroadcaster(IEventAggregator eventAggregator, string scopeName)
/// <summary>已订阅解码事件的 CANFD 实例 → 委托 映射</summary>
private readonly Dictionary<ZLGCANFD, Action<uint, ZDBC.DBCMessage>> _handlers = new();
public CANSignalBroadcaster(GlobalInfo globalInfo, IEventAggregator eventAggregator)
{
_globalInfo = globalInfo;
_eventAggregator = eventAggregator;
_scopeName = scopeName;
}
/// <summary>
/// 绑定 CANFD 实例并订阅解码事件。
/// 扫描 GlobalInfo.CanPool 中已创建的 CANFD 实例并订阅解码事件。
/// 幂等:已订阅的实例不会重复订阅。
/// </summary>
public void Bind(ZLGCANFD canfd)
public void Discover()
{
Unbind();
_canfd = canfd;
_canfd.OnDbcMessageDecoded += OnDbcMessageDecoded;
}
if (_disposed) return;
/// <summary>
/// 取消订阅。
/// </summary>
public void Unbind()
{
if (_canfd != null)
foreach (var kvp in _globalInfo.CanPool)
{
_canfd.OnDbcMessageDecoded -= OnDbcMessageDecoded;
_canfd = null;
string fingerprint = kvp.Key;
var lazy = kvp.Value;
if (!lazy.IsValueCreated || lazy.Value == null) continue;
var canfd = lazy.Value;
if (_handlers.ContainsKey(canfd)) continue;
// 使用闭包捕获指纹,回调时即可区分不同 CAN 卡
Action<uint, ZDBC.DBCMessage> handler = (channel, msg) => OnDbcMessageDecoded(fingerprint, channel, msg);
canfd.OnDbcMessageDecoded += handler;
_handlers[canfd] = handler;
}
}
/// <summary>
/// DBC 解码回调:提取所有信号并广播
/// 启动广播器(目前 Discover 已完成订阅,此处保留以兼容 HardwareDataBroadcaster 的使用模式)
/// </summary>
private void OnDbcMessageDecoded(uint channel, ZDBC.DBCMessage msg)
public void Start()
{
Discover();
}
/// <summary>
/// DBC 解码回调:提取所有信号,向引用该 CAN 设备的作用域广播,并检查报警。
/// </summary>
private void OnDbcMessageDecoded(string canFingerprint, uint channel, ZDBC.DBCMessage msg)
{
if (_disposed) return;
string fingerprint = $"CAN:{channel}";
string signalFingerprint = BuildFingerprint(channel);
var now = DateTime.Now;
// 获取引用该 CAN 设备的所有作用域
var scopes = GetScopesForFingerprint(canFingerprint);
if (scopes.Count == 0) return;
for (int i = 0; i < msg.nSignalCount; i++)
{
var signal = msg.vSignals[i];
string signalName = Encoding.Default.GetString(signal.strName).TrimEnd('\0');
if (string.IsNullOrEmpty(signalName)) continue;
// 计算物理值physical = raw * factor + offset
double physicalValue = signal.nRawvalue * signal.nFactor + signal.nOffset;
string methodName = BuildMethodName(msg.nID, signalName);
// MethodName 格式:"{MessageId:X}.{SignalName}"
string methodName = $"{msg.nID:X}.{signalName}";
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
foreach (var scope in scopes)
{
Scope = _scopeName,
HardwareFingerprint = fingerprint,
MethodName = methodName,
Value = physicalValue,
Time = now
});
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
{
Scope = scope,
HardwareFingerprint = signalFingerprint,
MethodName = methodName,
Value = physicalValue,
Time = now
});
ValueLimitAlarmHelper.CheckAlarm(scope, signalFingerprint, methodName, physicalValue, _globalInfo);
}
}
}
/// <summary>获取指定硬件指纹当前被哪些作用域引用</summary>
private List<string> GetScopesForFingerprint(string fingerprint)
{
if (_globalInfo.DeviceAndScopeDic.TryGetValue(fingerprint, out var lazy))
{
var scopeList = lazy.Value;
lock (scopeList) return scopeList.ToList();
}
return new List<string>();
}
/// <summary>
/// 生成 CAN 信号的 DisplayName 格式:"{MessageName}.{SignalName}"
/// </summary>
@@ -108,7 +140,13 @@ namespace UIShare.GlobalVariable
{
if (_disposed) return;
_disposed = true;
Unbind();
foreach (var kvp in _handlers)
{
if (kvp.Key != null)
kvp.Key.OnDbcMessageDecoded -= kvp.Value;
}
_handlers.Clear();
}
}
}

View File

@@ -27,9 +27,6 @@ namespace UIShare.GlobalVariable
private readonly string _scopeName;
private readonly IEventAggregator _eventAggregator;
/// <summary>CAN 信号广播器:将 DBC 解码后的信号值广播给监控系统</summary>
private CANSignalBroadcaster? _canSignalBroadcaster;
/// <summary>按 DeviceName 索引的设备字典,便于业务层按名取实例。</summary>
public IDictionary<string, IBaseInterface> DeviceMap { get; private set; }
= new Dictionary<string, IBaseInterface>(StringComparer.OrdinalIgnoreCase);
@@ -439,10 +436,6 @@ namespace UIShare.GlobalVariable
bool ok = await Task.Run(() =>
{
if (!CANFD.()) return false;
// 启动 CAN 信号广播器
_canSignalBroadcaster = new CANSignalBroadcaster(_eventAggregator, _scopeName);
_canSignalBroadcaster.Bind(CANFD);
// 自动加载 DBC 文件
if (_systemConfig?.DBCAutoLoadList != null)
{
@@ -528,10 +521,6 @@ namespace UIShare.GlobalVariable
return;
}
// 先释放 CAN 信号广播器,取消事件订阅,防止关闭过程中仍触发回调
_canSignalBroadcaster?.Dispose();
_canSignalBroadcaster = null;
await Task.Run(() => CANFD.CAN卡设备());
LoggerHelper.Info($"CAN 设备 [{name}] 已成功关闭连接。");
}
@@ -591,10 +580,6 @@ namespace UIShare.GlobalVariable
public void Dispose()
{
// 释放 CAN 信号广播器
_canSignalBroadcaster?.Dispose();
_canSignalBroadcaster = null;
if (string.IsNullOrEmpty(_scopeName)) return;
// 遍历当前作用域用到的所有指纹,逐一移除本作用域的引用

View File

@@ -179,6 +179,9 @@ namespace UIShare.GlobalVariable
Value = value,
Time = now
});
// 同步检查该作用域 ValueLimitList 是否超限
ValueLimitAlarmHelper.CheckAlarm(scope, entry.Fingerprint, entry.MethodName, value, _globalInfo);
}
}
catch

View File

@@ -0,0 +1,56 @@
using System.Linq;
using UIShare.UIViewModel;
namespace UIShare.GlobalVariable
{
/// <summary>
/// 值限制报警检查辅助类:供各广播器在采样到信号值时统一判断是否超限。
/// </summary>
public static class ValueLimitAlarmHelper
{
/// <summary>
/// 根据指定作用域的 ValueLimitList 检查当前值是否超限,并更新报警状态。
/// </summary>
/// <param name="scope">作用域名称</param>
/// <param name="fingerprint">硬件指纹</param>
/// <param name="methodName">方法名/信号标识</param>
/// <param name="value">当前采样值</param>
/// <param name="globalInfo">全局信息</param>
public static void CheckAlarm(string scope, string fingerprint, string methodName, double value, GlobalInfo globalInfo)
{
if (globalInfo?.ConfigDic == null) return;
if (!globalInfo.ConfigDic.TryGetValue(scope, out var systemConfig)) return;
if (systemConfig.ValueLimitList == null) return;
var limit = systemConfig.ValueLimitList.FirstOrDefault(x =>
x.Fingerprint == fingerprint && x.MethodName == methodName);
if (limit == null) return;
if (value > limit.Upper)
{
limit.IsAlarm = true;
limit.AlarmSatus = AlarmStatus.;
}
else if (value < limit.Lower)
{
limit.IsAlarm = true;
limit.AlarmSatus = AlarmStatus.;
}
else if(value > limit.UpperExtreme)
{
limit.IsAlarm = true;
limit.AlarmSatus = AlarmStatus.;
}
else if (value < limit.LowerExtreme)
{
limit.IsAlarm = true;
limit.AlarmSatus = AlarmStatus.;
}
else
{
limit.IsAlarm = false;
limit.AlarmSatus = AlarmStatus.;
}
}
}
}

View File

@@ -6,6 +6,8 @@ namespace UIShare.UIViewModel
public class ValueLimitVM : BindableBase
{
private string _signalName = string.Empty;
private string _fingerprint = string.Empty;
private string _methodName = string.Empty;
private double _upper;
private double _lower;
private double _upperExtreme;
@@ -14,7 +16,7 @@ namespace UIShare.UIViewModel
private AlarmStatus _alarmStatus = AlarmStatus.;
/// <summary>
/// 信号名
/// 信号名(显示用)
/// </summary>
public string SignalName
{
@@ -22,6 +24,24 @@ namespace UIShare.UIViewModel
set => SetProperty(ref _signalName, value);
}
/// <summary>
/// 硬件指纹(物理设备唯一标识,用于超限匹配)
/// </summary>
public string Fingerprint
{
get => _fingerprint;
set => SetProperty(ref _fingerprint, value);
}
/// <summary>
/// 方法名(唯一标识,用于超限匹配)
/// </summary>
public string MethodName
{
get => _methodName;
set => SetProperty(ref _methodName, value);
}
/// <summary>
/// 上限
/// </summary>