CAN驱动修改优化
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TSMasterCAN;
|
||||
using TSMaster;
|
||||
namespace UIShare.GlobalVariable
|
||||
{
|
||||
public class CANMonitoringService
|
||||
{
|
||||
public bool IsStopped { get; set; } = false;
|
||||
public bool IsInitialized { get; set; } = false;
|
||||
public bool IsRegistered { get; set; } = false;
|
||||
private SystemConfig _systemConfig { get; set; }
|
||||
public CANMonitoringService(SystemConfig systemConfig)
|
||||
{
|
||||
_systemConfig = systemConfig;
|
||||
CANSignalBroadcaster.RegisterService(this);
|
||||
IsRegistered = true;
|
||||
}
|
||||
public int Init(CancellationToken ct = default)
|
||||
{
|
||||
int re = 0;
|
||||
if (IsInitialized == false)
|
||||
{
|
||||
re = CAN.Init(_systemConfig.TSMasterName);
|
||||
}
|
||||
if (IsInitialized == false)
|
||||
{
|
||||
re = CAN.RegisterListener(MonitorEvent);
|
||||
if (re == 0) IsInitialized = true;
|
||||
}
|
||||
return re;
|
||||
}
|
||||
public ConcurrentDictionary<string, TLIBCANFD> RealTimeMessages { get; set; } = new();
|
||||
public ConcurrentDictionary<string, double> RealTimeSignals { get; set; } = new();
|
||||
public void MonitorEvent(ref int AObj, ref TLIBCANFD AData)
|
||||
{
|
||||
if (IsStopped) return;
|
||||
byte[] oldbytes = null;
|
||||
|
||||
//记录至实时报文数据库
|
||||
var cpAData = AData;
|
||||
cpAData.FData = ArrayPool<byte>.Shared.Rent(64);
|
||||
Array.Copy(AData.FData, 0, cpAData.FData, 0, cpAData.FData.Length);
|
||||
var MessageName = $"{AData.FIdxChn}/{AData.FIdentifier}";
|
||||
if (RealTimeMessages.TryGetValue(MessageName, out var v))
|
||||
{
|
||||
oldbytes = v.FData;
|
||||
}
|
||||
RealTimeMessages[MessageName] = cpAData;
|
||||
|
||||
//记录至DBC数据库
|
||||
var dbcfind = DBCParse.MsgDatabase[AData.FIdxChn].FirstOrDefault(s => s.msg_id == cpAData.FIdentifier);
|
||||
if (dbcfind != null)
|
||||
{
|
||||
Array.Copy(cpAData.FData, 0, dbcfind.ACANFD.FData, 0, cpAData.FData.Length);
|
||||
dbcfind.ACANFD.FTimeUS = cpAData.FTimeUS;
|
||||
dbcfind.ACANFD.FIsFD = cpAData.FIsFD;
|
||||
dbcfind.ACANFD.FIsBRS = cpAData.FIsBRS;
|
||||
dbcfind.ACANFD.FIsESI = cpAData.FIsESI;
|
||||
dbcfind.ACANFD.FIsError = cpAData.FIsError;
|
||||
dbcfind.ACANFD.FIsRemote = cpAData.FIsRemote;
|
||||
dbcfind.ACANFD.FIsExt = cpAData.FIsExt;
|
||||
}
|
||||
|
||||
if (oldbytes is not null) ArrayPool<byte>.Shared.Return(oldbytes);
|
||||
|
||||
var id = AData.FIdentifier;
|
||||
var ch = AData.FIdxChn;
|
||||
|
||||
var canSignalList = _systemConfig.ConfigurationList.Where(s => s.MessageID == id && s.Channel == ch).ToArray();
|
||||
|
||||
if (canSignalList.Length > 0)
|
||||
{
|
||||
var find = DBCParse.MsgDatabase[ch].First(s => s.msg_id == id);
|
||||
foreach (var item in canSignalList)
|
||||
{
|
||||
double Signalre = double.NaN;
|
||||
var re = CAN.GetSignalValue(ref cpAData, item.MessageName, item.SignalName, ref Signalre);
|
||||
//Debug.Assert(re == 0);
|
||||
var SinalName = $"{AData.FIdxChn}/{item.MessageName}/{item.SignalName}";
|
||||
RealTimeSignals[SinalName] = Signalre;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止监测:从广播器注销并清理实时数据。
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
IsStopped = true;
|
||||
if (IsRegistered)
|
||||
{
|
||||
CANSignalBroadcaster.UnregisterService(this);
|
||||
IsRegistered = false;
|
||||
}
|
||||
RealTimeSignals.Clear();
|
||||
RealTimeMessages.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +1,191 @@
|
||||
using Model.Models;
|
||||
using Prism.Events;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TSMasterCAN;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using UIShare.PubEvent;
|
||||
using UIShare.UIViewModel;
|
||||
|
||||
namespace UIShare.GlobalVariable
|
||||
{
|
||||
/// <summary>
|
||||
/// CAN 信号广播器(全局单例):
|
||||
/// 统一订阅 GlobalInfo.CanPool 中所有已实例化的 ZLGCANFD 设备,
|
||||
/// 将 DBC 解码后的信号值广播给所有引用该 CAN 设备的作用域,
|
||||
/// 并同步检查各作用域 ValueLimitList 的超限报警。
|
||||
/// 不再订阅 CAN 实体实例的事件,改为从所有已注册的 <see cref="CANMonitoringService"/>
|
||||
/// 的 RealTimeSignals 字典中轮询读取信号值,并通过 <see cref="HardwareDataReportedEvent"/> 广播。
|
||||
/// </summary>
|
||||
public class CANSignalBroadcaster : IDisposable
|
||||
public class CANSignalBroadcaster
|
||||
{
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly GlobalInfo _globalInfo;
|
||||
private bool _disposed;
|
||||
private readonly SystemConfig _systemConfig;
|
||||
|
||||
/// <summary>已订阅解码事件的 CANFD 实例 → 委托 映射</summary>
|
||||
private readonly Dictionary<CAN, Action<uint, DBCMessage>> _handlers = new();
|
||||
public CANSignalBroadcaster(GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
||||
/// <summary>全局已注册的 CANMonitoringService 实例列表(静态,跨作用域共享)</summary>
|
||||
private static readonly ConcurrentDictionary<CANMonitoringService, byte> _registeredServices = new();
|
||||
|
||||
private CancellationTokenSource? _cts;
|
||||
private Task? _broadcastTask;
|
||||
|
||||
/// <summary>广播轮询间隔(毫秒)</summary>
|
||||
public int PollingIntervalMs { get; set; } = 100;
|
||||
|
||||
public CANSignalBroadcaster(SystemConfig systemConfig, IEventAggregator eventAggregator)
|
||||
{
|
||||
_globalInfo = globalInfo;
|
||||
_systemConfig = systemConfig;
|
||||
_eventAggregator = eventAggregator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 扫描 GlobalInfo.CanPool 中已创建的 CANFD 实例并订阅解码事件。
|
||||
/// 幂等:已订阅的实例不会重复订阅。
|
||||
/// </summary>
|
||||
public void Discover()
|
||||
#region 服务注册(供 CANMonitoringService 调用)
|
||||
|
||||
/// <summary>注册一个 CANMonitoringService,使其信号纳入广播</summary>
|
||||
public static void RegisterService(CANMonitoringService service)
|
||||
{
|
||||
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;
|
||||
}
|
||||
_registeredServices.TryAdd(service, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动广播器(目前 Discover 已完成订阅,此处保留以兼容 HardwareDataBroadcaster 的使用模式)。
|
||||
/// </summary>
|
||||
/// <summary>注销一个 CANMonitoringService</summary>
|
||||
public static void UnregisterService(CANMonitoringService service)
|
||||
{
|
||||
_registeredServices.TryRemove(service, out _);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 启动 / 停止
|
||||
|
||||
/// <summary>启动广播(幂等:多次调用只启动一次)</summary>
|
||||
public void Start()
|
||||
{
|
||||
Discover();
|
||||
if (_broadcastTask != null && !_broadcastTask.IsCompleted) return;
|
||||
|
||||
_cts = new CancellationTokenSource();
|
||||
_broadcastTask = Task.Run(() => BroadcastLoop(_cts.Token));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DBC 解码回调:提取所有信号,向引用该 CAN 设备的作用域广播,并检查报警。
|
||||
/// </summary>
|
||||
private void OnDbcMessageDecoded(string canFingerprint, uint channel, DBCMessage msg)
|
||||
/// <summary>停止广播</summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_cts?.Cancel();
|
||||
_broadcastTask = null;
|
||||
}
|
||||
|
||||
string signalFingerprint = BuildFingerprint(canFingerprint, channel);
|
||||
var now = DateTime.Now;
|
||||
/// <summary>兼容旧接口:Discover 已无需执行任何操作</summary>
|
||||
public void Discover() { }
|
||||
|
||||
// 获取引用该 CAN 设备的所有作用域
|
||||
var scopes = GetScopesForFingerprint(canFingerprint);
|
||||
if (scopes.Count == 0) return;
|
||||
#endregion
|
||||
|
||||
for (int i = 0; i < msg.nSignalCount; i++)
|
||||
#region 广播核心逻辑
|
||||
|
||||
/// <summary>
|
||||
/// 轮询所有已注册的 CANMonitoringService 的 RealTimeSignals,
|
||||
/// 结合 ConfigurationList 映射出 MessageID/Channel,广播 HardwareDataReportedEvent。
|
||||
/// </summary>
|
||||
private async Task BroadcastLoop(CancellationToken ct)
|
||||
{
|
||||
while (!ct.IsCancellationRequested)
|
||||
{
|
||||
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)
|
||||
try
|
||||
{
|
||||
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
|
||||
{
|
||||
Scope = scope,
|
||||
HardwareFingerprint = signalFingerprint,
|
||||
MethodName = methodName,
|
||||
Value = physicalValue,
|
||||
Time = now
|
||||
});
|
||||
// 预构建信号名 → 配置 的映射(避免内层循环重复查找)
|
||||
var configMap = BuildConfigMap();
|
||||
|
||||
string MonitorStatus = ValueLimitAlarmHelper.CheckAlarm(scope, signalFingerprint, methodName, physicalValue, _globalInfo);
|
||||
if (MonitorStatus != "" && MonitorStatus != "未报警")
|
||||
string scope = _systemConfig.Title;
|
||||
|
||||
foreach (var service in _registeredServices.Keys)
|
||||
{
|
||||
_eventAggregator.GetEvent<AlarmEvent>().Publish((scope,canFingerprint, MonitorStatus));
|
||||
if (service.IsStopped) continue;
|
||||
|
||||
foreach (var kvp in service.RealTimeSignals)
|
||||
{
|
||||
if (ct.IsCancellationRequested) return;
|
||||
|
||||
// 信号 Key 格式: "{channel}/{MessageName}/{SignalName}"
|
||||
if (!TryParseSignalKey(kvp.Key, out int channel, out string? messageName, out string? signalName))
|
||||
continue;
|
||||
|
||||
// 从配置映射中查找对应的 MessageID
|
||||
if (!configMap.TryGetValue($"{channel}/{messageName}/{signalName}", out var cfg))
|
||||
continue;
|
||||
|
||||
string canFingerprint = $"CAN:{channel}";
|
||||
string fingerprint = BuildFingerprint(canFingerprint, (uint)channel);
|
||||
string methodName = BuildMethodName((uint)cfg.MessageID, signalName);
|
||||
|
||||
// 广播信号值
|
||||
_eventAggregator.GetEvent<HardwareDataReportedEvent>().Publish(new HardwareReportArgs
|
||||
{
|
||||
Scope = scope,
|
||||
HardwareFingerprint = fingerprint,
|
||||
MethodName = methodName,
|
||||
Value = kvp.Value,
|
||||
Time = DateTime.Now
|
||||
});
|
||||
|
||||
// 报警检查
|
||||
string alarmStatus = ValueLimitAlarmHelper.CheckAlarm(fingerprint, methodName, kvp.Value, _systemConfig);
|
||||
if (!string.IsNullOrEmpty(alarmStatus) && alarmStatus != "未报警")
|
||||
{
|
||||
_eventAggregator.GetEvent<AlarmEvent>().Publish((scope, canFingerprint, alarmStatus));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(PollingIntervalMs, ct);
|
||||
}
|
||||
catch (OperationCanceledException) { break; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LoggerHelper.Error($"CANSignalBroadcaster 广播异常: {ex.Message}");
|
||||
await Task.Delay(1000, ct); // 异常后等待一段时间再重试
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>获取指定硬件指纹当前被哪些作用域引用</summary>
|
||||
private List<string> GetScopesForFingerprint(string fingerprint)
|
||||
/// <summary>
|
||||
/// 从 SystemConfig.ConfigurationList 构建 "channel/MessageName/SignalName" → CANSignalConfig 的映射
|
||||
/// </summary>
|
||||
private Dictionary<string, CANSignalConfig> BuildConfigMap()
|
||||
{
|
||||
if (_globalInfo.DeviceAndScopeDic.TryGetValue(fingerprint, out var lazy))
|
||||
var map = new Dictionary<string, CANSignalConfig>(StringComparer.OrdinalIgnoreCase);
|
||||
if (_systemConfig?.ConfigurationList == null) return map;
|
||||
|
||||
foreach (var cfg in _systemConfig.ConfigurationList)
|
||||
{
|
||||
var scopeList = lazy.Value;
|
||||
lock (scopeList) return scopeList.ToList();
|
||||
if (string.IsNullOrEmpty(cfg.SignalName) || string.IsNullOrEmpty(cfg.MessageName)) continue;
|
||||
string key = $"{cfg.Channel}/{cfg.MessageName}/{cfg.SignalName}";
|
||||
map.TryAdd(key, cfg); // 第一个匹配的优先
|
||||
}
|
||||
return new List<string>();
|
||||
return map;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成 CAN 信号的 DisplayName 格式:"{MessageName}.{SignalName}"
|
||||
/// </summary>
|
||||
/// <summary>解析信号 Key: "{channel}/{MessageName}/{SignalName}"</summary>
|
||||
private static bool TryParseSignalKey(string key, out int channel, out string? messageName, out string? signalName)
|
||||
{
|
||||
channel = 0;
|
||||
messageName = null;
|
||||
signalName = null;
|
||||
|
||||
var parts = key.Split('/');
|
||||
if (parts.Length < 3) return false;
|
||||
|
||||
if (!int.TryParse(parts[0], out channel)) return false;
|
||||
messageName = parts[1];
|
||||
signalName = parts[2];
|
||||
return !string.IsNullOrEmpty(messageName) && !string.IsNullOrEmpty(signalName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 静态工具方法(保持向后兼容)
|
||||
|
||||
/// <summary>生成 CAN 信号的 DisplayName 格式:"{MessageName}.{SignalName}"</summary>
|
||||
public static string BuildDisplayName(string messageName, string signalName)
|
||||
{
|
||||
return $"{messageName}.{signalName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成 CAN 信号的 MethodName 格式:"{MessageId:X}.{SignalName}"
|
||||
/// </summary>
|
||||
/// <summary>生成 CAN 信号的 MethodName 格式:"{MessageId:X}.{SignalName}"</summary>
|
||||
public static string BuildMethodName(uint messageId, string signalName)
|
||||
{
|
||||
return $"{messageId:X}.{signalName}";
|
||||
@@ -140,17 +200,6 @@ namespace UIShare.GlobalVariable
|
||||
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();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ using System.Reflection;
|
||||
using TSMasterCAN;
|
||||
using UIShare.PubEvent;
|
||||
using UIShare.UIViewModel;
|
||||
using TSMasterCAN;
|
||||
|
||||
namespace UIShare.GlobalVariable
|
||||
{
|
||||
@@ -25,6 +24,7 @@ namespace UIShare.GlobalVariable
|
||||
{
|
||||
private object _lockObj = new object();
|
||||
public SystemConfig _systemConfig { get; set; }
|
||||
public CANMonitoringService _CANMonitoringService { get; set; }
|
||||
private readonly GlobalInfo _globalInfo;
|
||||
private readonly string _scopeName;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
@@ -35,7 +35,6 @@ namespace UIShare.GlobalVariable
|
||||
|
||||
/// <summary>类名 → Type 的反射缓存(仅扫描一次)。</summary>
|
||||
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
|
||||
public CAN CANFD { get; set; }
|
||||
public IOBoardGroup IOGroup { get; set; }
|
||||
|
||||
public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo, IEventAggregator eventAggregator)
|
||||
@@ -43,11 +42,17 @@ namespace UIShare.GlobalVariable
|
||||
_systemConfig = systemConfig;
|
||||
_globalInfo = globalInfo;
|
||||
_eventAggregator = eventAggregator;
|
||||
_CANMonitoringService=new CANMonitoringService(_systemConfig);
|
||||
// 用 SystemConfig.Title 作为作用域唯一标识,无需反查 ConfigDic
|
||||
_scopeName = _systemConfig.Title;
|
||||
InitDevices();
|
||||
InitCAN();
|
||||
}
|
||||
|
||||
public void InitCAN()
|
||||
{
|
||||
var re1 = _CANMonitoringService.Init();
|
||||
}
|
||||
/// <summary>
|
||||
/// 根据设备配置提取唯一的硬件指纹字符串。
|
||||
/// <para>Tcp → "Tcp:IP:Port";Serial → "Serial:PortName";无法识别则返回空字符串。</para>
|
||||
@@ -98,55 +103,6 @@ namespace UIShare.GlobalVariable
|
||||
{
|
||||
if (config == null || !config.IsEnabled) continue;
|
||||
|
||||
// CAN 设备:ZLGCANFD 不实现 IBaseInterface,通过 SystemConfig.CANFD 单独管理,
|
||||
// 按指纹从全局 CanPool 创建/复用实例,并注册作用域引用计数。
|
||||
if (string.Equals(config.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var fingerprint = ExtractHardwareFingerprint(config);
|
||||
if (string.IsNullOrEmpty(fingerprint))
|
||||
{
|
||||
LoggerHelper.Warn($"设备 [{config.DeviceName}] 无法提取硬件指纹(连接方式={config.ConnectionType}),已跳过。");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.CANConfig == null)
|
||||
{
|
||||
LoggerHelper.Warn($"设备 [{config.DeviceName}] 缺少 CAN 连接参数,已跳过。");
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 按指纹全局唯一创建同星 CAN 实例(maxChannels 默认 4,对应 USBCANFD-400U)
|
||||
// 波特率与终端电阻从 CANConfigVM 传入,初始化并启动通道 时直接使用
|
||||
var canLazy = _globalInfo.CanPool.GetOrAdd(fingerprint, key => new Lazy<CAN>(() =>
|
||||
new CAN(config.CANConfig.DeviceType, config.CANConfig.DeviceIndex, 4,
|
||||
config.CANConfig.ABitBaud, config.CANConfig.DBitBaud, config.CANConfig.EnableTerminalResistance)));
|
||||
|
||||
_systemConfig.CANFD = canLazy.Value;
|
||||
CANFD = canLazy.Value;
|
||||
|
||||
// 注册作用域引用计数
|
||||
if (!string.IsNullOrEmpty(_scopeName))
|
||||
{
|
||||
var scopeList = _globalInfo.DeviceAndScopeDic.GetOrAdd(fingerprint,
|
||||
_ => new Lazy<List<string>>(() => new List<string>())).Value;
|
||||
lock (scopeList)
|
||||
{
|
||||
if (!scopeList.Contains(_scopeName))
|
||||
scopeList.Add(_scopeName);
|
||||
}
|
||||
}
|
||||
|
||||
LoggerHelper.Info($"已加载 CAN 设备 [{config.DeviceName}] 指纹={fingerprint}(通过 SystemConfig.CANFD 管理)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 [{config.DeviceName}] 实例化失败:{ex.Message}");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(config.DeviceType) ||
|
||||
!_deviceTypeMap.TryGetValue(config.DeviceType, out var deviceType))
|
||||
{
|
||||
@@ -302,7 +258,7 @@ namespace UIShare.GlobalVariable
|
||||
// CAN 设备:直接关闭 CAN 卡
|
||||
if (info != null && string.Equals(info.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await CloseCanAsync(info);
|
||||
CloseCan();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -325,7 +281,7 @@ namespace UIShare.GlobalVariable
|
||||
|
||||
lock (_lockObj)
|
||||
{
|
||||
if (DeviceMap.Count == 0 && (CANFD == null)) return;
|
||||
if (DeviceMap.Count == 0 ) return;
|
||||
|
||||
foreach (var kvp in DeviceMap)
|
||||
{
|
||||
@@ -338,17 +294,7 @@ namespace UIShare.GlobalVariable
|
||||
}
|
||||
}
|
||||
|
||||
// CAN 设备:直接关闭 CAN 卡
|
||||
if (CANFD != null)
|
||||
{
|
||||
var canInfo = _systemConfig?.DeviceList?
|
||||
.FirstOrDefault(d => d != null && string.Equals(d.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase));
|
||||
if (canInfo != null)
|
||||
{
|
||||
tasks.Add(CloseCanAsync(canInfo));
|
||||
}
|
||||
}
|
||||
|
||||
CAN.DisConnect();
|
||||
await Task.WhenAll(tasks);
|
||||
LoggerHelper.Info("所有设备已执行关闭操作。");
|
||||
}
|
||||
@@ -456,26 +402,11 @@ namespace UIShare.GlobalVariable
|
||||
/// </summary>
|
||||
private async Task<bool> ConnectCanAsync(DeviceInfoVM info)
|
||||
{
|
||||
string name = info.DeviceName ?? "CAN";
|
||||
|
||||
try
|
||||
{
|
||||
if (CANFD == null)
|
||||
{
|
||||
LoggerHelper.Warn($"CAN 设备 [{name}] 尚未实例化,无法连接。");
|
||||
info.IsConnected = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.IsConnected)
|
||||
{
|
||||
LoggerHelper.Info($"CAN 设备 [{name}] 已连接,跳过。");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ok = await Task.Run(() =>
|
||||
{
|
||||
if (!CANFD.打开设备()) return false;
|
||||
// 自动加载 DBC 文件
|
||||
if (_systemConfig?.DBCAutoLoadList != null)
|
||||
{
|
||||
@@ -495,8 +426,7 @@ namespace UIShare.GlobalVariable
|
||||
LoggerHelper.Warn($"CAN 通道 {item.DBCChannel} 自动加载 DBC 失败:文件不存在 [{item.DBCFilePath}]");
|
||||
continue;
|
||||
}
|
||||
CANFD.初始化并启动通道((uint)item.DBCChannel);
|
||||
bool loadOk = CANFD.加载通道DBC文件((uint)item.DBCChannel, item.DBCFilePath);
|
||||
bool loadOk = CAN.LoadDBC(item.DBCFilePath,new int[] { item.DBCChannel },out _ )==0;
|
||||
if (loadOk)
|
||||
{
|
||||
LoggerHelper.Info($"CAN 通道 {item.DBCChannel} 已自动加载 DBC:{item.DBCFilePath}");
|
||||
@@ -518,16 +448,16 @@ namespace UIShare.GlobalVariable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CAN.Connect() != 0) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
info.IsConnected = ok;
|
||||
|
||||
if (ok)
|
||||
LoggerHelper.Info($"CAN 设备 [{name}] 连接成功,已初始化 {CANFD.DBCParser.MaxChannels} 个通道。");
|
||||
LoggerHelper.Info($"CAN 设备 连接成功");
|
||||
else
|
||||
LoggerHelper.Warn($"CAN 设备 [{name}] 连接失败。");
|
||||
LoggerHelper.Warn($"CAN 设备连接失败。");
|
||||
|
||||
return ok;
|
||||
}
|
||||
@@ -535,7 +465,7 @@ namespace UIShare.GlobalVariable
|
||||
{
|
||||
info.IsConnected = false;
|
||||
var inner = ex.InnerException?.Message ?? ex.Message;
|
||||
LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 [{name}] 连接异常:{inner}");
|
||||
LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 连接异常:{inner}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -543,36 +473,9 @@ namespace UIShare.GlobalVariable
|
||||
/// <summary>
|
||||
/// 关闭 CAN 卡
|
||||
/// </summary>
|
||||
private async Task CloseCanAsync(DeviceInfoVM info)
|
||||
private void CloseCan()
|
||||
{
|
||||
string name = info.DeviceName ?? "CAN";
|
||||
|
||||
try
|
||||
{
|
||||
if (CANFD == null)
|
||||
{
|
||||
info.IsConnected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.IsConnected)
|
||||
{
|
||||
LoggerHelper.Info($"CAN 设备 [{name}] 本就处于断开状态。");
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.Run(() => CANFD.关闭CAN卡设备());
|
||||
LoggerHelper.Info($"CAN 设备 [{name}] 已成功关闭连接。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var inner = ex.InnerException?.Message ?? ex.Message;
|
||||
LoggerHelper.Error($"CAN 设备 [{name}] 关闭连接时出现异常: {inner}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
info.IsConnected = false;
|
||||
}
|
||||
CAN.DisConnect();
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, Type> BuildDeviceTypeMap()
|
||||
@@ -620,6 +523,9 @@ namespace UIShare.GlobalVariable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// 停止 CAN 信号监测服务
|
||||
_CANMonitoringService?.Stop();
|
||||
|
||||
if (string.IsNullOrEmpty(_scopeName)) return;
|
||||
|
||||
// 遍历当前作用域用到的所有指纹,逐一移除本作用域的引用
|
||||
@@ -655,16 +561,6 @@ namespace UIShare.GlobalVariable
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试从 CanPool 取出并销毁
|
||||
if (_globalInfo.CanPool.TryRemove(fingerprint, out var canLazy))
|
||||
{
|
||||
if (canLazy.IsValueCreated)
|
||||
{
|
||||
try { canLazy.Value.Dispose(); }
|
||||
catch { /* 销毁时忽略异常 */ }
|
||||
LoggerHelper.Info($"指纹 [{fingerprint}] 无作用域引用,已销毁 CAN 设备实例。");
|
||||
}
|
||||
}
|
||||
|
||||
// 同步清除 DeviceAndScopeDic 中的空条目
|
||||
_globalInfo.DeviceAndScopeDic.TryRemove(fingerprint, out _);
|
||||
|
||||
@@ -578,11 +578,7 @@ namespace UIShare
|
||||
{
|
||||
try
|
||||
{
|
||||
if(targetType.Name== "ZLGCANFD")
|
||||
{
|
||||
instance = _deviceManager.CANFD;
|
||||
}
|
||||
else if (targetType.Name == "IOBoardGroup")
|
||||
if (targetType.Name == "IOBoardGroup")
|
||||
{
|
||||
instance = _deviceManager.IOGroup;
|
||||
}
|
||||
|
||||
@@ -20,12 +20,22 @@ namespace UIShare.GlobalVariable
|
||||
{
|
||||
if (globalInfo?.ConfigDic == null) return "";
|
||||
if (!globalInfo.ConfigDic.TryGetValue(scope, out var systemConfig)) return "";
|
||||
if (systemConfig.ValueLimitList == null) return "";
|
||||
return CheckAlarm(fingerprint, methodName, value, systemConfig);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接根据 SystemConfig 的 ValueLimitList 检查当前值是否超限。
|
||||
/// 适用于不持有 GlobalInfo 引用的场景(如 CANSignalBroadcaster)。
|
||||
/// </summary>
|
||||
public static string CheckAlarm(string fingerprint, string methodName, double value, SystemConfig systemConfig)
|
||||
{
|
||||
if (systemConfig?.ValueLimitList == null) return "";
|
||||
|
||||
var limit = systemConfig.ValueLimitList.FirstOrDefault(x =>
|
||||
x.Fingerprint == fingerprint && x.MethodName == methodName);
|
||||
if (limit == null) return "";
|
||||
if (value > limit.UpperExtreme)
|
||||
|
||||
if (value > limit.UpperExtreme)
|
||||
{
|
||||
limit.IsAlarm = true;
|
||||
limit.AlarmSatus = AlarmStatus.超上极限;
|
||||
|
||||
Reference in New Issue
Block a user