Files
ACP/UIShare/GlobalVariable/CANSignalBroadcaster.cs
2026-07-29 14:20:32 +08:00

157 lines
5.8 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 Model.Models;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TSMasterCAN;
using UIShare.PubEvent;
namespace UIShare.GlobalVariable
{
/// <summary>
/// CAN 信号广播器(全局单例):
/// 统一订阅 GlobalInfo.CanPool 中所有已实例化的 ZLGCANFD 设备,
/// 将 DBC 解码后的信号值广播给所有引用该 CAN 设备的作用域,
/// 并同步检查各作用域 ValueLimitList 的超限报警。
/// </summary>
public class CANSignalBroadcaster : IDisposable
{
private readonly IEventAggregator _eventAggregator;
private readonly GlobalInfo _globalInfo;
private bool _disposed;
/// <summary>已订阅解码事件的 CANFD 实例 → 委托 映射</summary>
private readonly Dictionary<CAN, Action<uint, DBCMessage>> _handlers = new();
public CANSignalBroadcaster(GlobalInfo globalInfo, IEventAggregator eventAggregator)
{
_globalInfo = globalInfo;
_eventAggregator = eventAggregator;
}
/// <summary>
/// 扫描 GlobalInfo.CanPool 中已创建的 CANFD 实例并订阅解码事件。
/// 幂等:已订阅的实例不会重复订阅。
/// </summary>
public void Discover()
{
if (_disposed) return;
foreach (var kvp in _globalInfo.CanPool)
{
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, DBCMessage> handler = (channel, msg) => OnDbcMessageDecoded(fingerprint, channel, msg);
canfd.OnDbcMessageDecoded += handler;
_handlers[canfd] = handler;
}
}
/// <summary>
/// 启动广播器(目前 Discover 已完成订阅,此处保留以兼容 HardwareDataBroadcaster 的使用模式)。
/// </summary>
public void Start()
{
Discover();
}
/// <summary>
/// DBC 解码回调:提取所有信号,向引用该 CAN 设备的作用域广播,并检查报警。
/// </summary>
private void OnDbcMessageDecoded(string canFingerprint, uint channel, DBCMessage msg)
{
if (_disposed) return;
string signalFingerprint = BuildFingerprint(canFingerprint, 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;
double physicalValue = signal.nRawvalue * signal.nFactor + signal.nOffset;
string methodName = BuildMethodName(msg.nID, signalName);
foreach (var scope in scopes)
{
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
{
Scope = scope,
HardwareFingerprint = signalFingerprint,
MethodName = methodName,
Value = physicalValue,
Time = now
});
string MonitorStatus = ValueLimitAlarmHelper.CheckAlarm(scope, signalFingerprint, methodName, physicalValue, _globalInfo);
if (MonitorStatus != "" && MonitorStatus != "未报警")
{
_eventAggregator.GetEvent<AlarmEvent>().Publish((scope,canFingerprint, MonitorStatus));
}
}
}
}
/// <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>
public static string BuildDisplayName(string messageName, string signalName)
{
return $"{messageName}.{signalName}";
}
/// <summary>
/// 生成 CAN 信号的 MethodName 格式:"{MessageId:X}.{SignalName}"
/// </summary>
public static string BuildMethodName(uint messageId, string signalName)
{
return $"{messageId:X}.{signalName}";
}
/// <summary>
/// 生成 CAN 信号的 Fingerprint 格式:"{canDeviceFingerprint}:{channel}"
/// canDeviceFingerprint 来自 DeviceManager.ExtractHardwareFingerprint如 "CAN:0"
/// </summary>
public static string BuildFingerprint(string canDeviceFingerprint, uint channel)
{
return $"{canDeviceFingerprint}:{channel}";
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
foreach (var kvp in _handlers)
{
if (kvp.Key != null)
kvp.Key.OnDbcMessageDecoded -= kvp.Value;
}
_handlers.Clear();
}
}
}