CAN修复

This commit is contained in:
“hsc”
2026-10-26 10:46:44 +08:00
parent e877dc9a90
commit 9cb139b208
12 changed files with 1462 additions and 167 deletions
+81 -7
View File
@@ -48,10 +48,39 @@ namespace UIShare.GlobalVariable
_CANMonitoringService=new CANMonitoringService(_systemConfig);
// 用 SystemConfig.Title 作为作用域唯一标识,无需反查 ConfigDic
_scopeName = _systemConfig.Title;
AcquireCanScope();
InitDevices();
InitCAN();
}
/// <summary>本工位(作用域)是否已登记 CAN 卡引用,保证 Acquire/Release 成对且幂等</summary>
private bool _canScopeAcquired;
/// <summary>
/// 登记本工位对 CAN 卡的引用。
/// <para>CAN 卡是一拖多共享硬件,与 HardwarePool 中的设备一样需要按作用域引用计数。</para>
/// </summary>
private void AcquireCanScope()
{
if (_canScopeAcquired) return;
CAN.AcquireScope(_scopeName);
_canScopeAcquired = true;
}
/// <summary>
/// 注销本工位对 CAN 卡的引用。
/// <para>
/// 只有最后一个工位释放时才真正停止循环报文、断开总线并释放 TSMaster 驱动;
/// 仍有其他工位在用时只做引用减一,避免误伤其他工位。
/// </para>
/// </summary>
private void ReleaseCanScope()
{
if (!_canScopeAcquired) return;
_canScopeAcquired = false;
CAN.ReleaseScope(_scopeName);
}
public void InitCAN()
{
var re1 = _CANMonitoringService.Init();
@@ -112,6 +141,13 @@ namespace UIShare.GlobalVariable
{
if (config == null || !config.IsEnabled) continue;
// CAN 卡由 ConnectCanAsync 独立管理(静态 TSMaster 驱动 + 作用域引用计数),
// 不参与 HardwarePool 的设备实例池,这里直接跳过避免误报"未知设备类型"
if (string.Equals(config.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (string.IsNullOrWhiteSpace(config.DeviceType) ||
!_deviceTypeMap.TryGetValue(config.DeviceType, out var deviceType))
{
@@ -314,11 +350,18 @@ namespace UIShare.GlobalVariable
// 先停止健康监控,避免重连定时器与关闭操作冲突
StopHealthMonitor();
// CAN 卡按作用域引用计数释放:仅最后一个工位关闭时才停循环报文、断总线并释放驱动
ReleaseCanScope();
List<Task> tasks = new List<Task>();
lock (_lockObj)
{
if (DeviceMap.Count == 0 ) return;
if (DeviceMap.Count == 0)
{
LoggerHelper.Info("所有设备已执行关闭操作。");
return;
}
foreach (var kvp in DeviceMap)
{
@@ -331,7 +374,6 @@ namespace UIShare.GlobalVariable
}
}
CAN.DisConnect();
await Task.WhenAll(tasks);
LoggerHelper.Info("所有设备已执行关闭操作。");
}
@@ -453,9 +495,16 @@ namespace UIShare.GlobalVariable
try
{
// 重新连接时恢复本工位对 CAN 卡的引用(之前可能被主动关闭过)
AcquireCanScope();
// 驱动若曾被最后一个工位释放,这里重新初始化并把报文回调挂回原生库
InitCAN();
bool ok = await Task.Run(() =>
{
// 自动加载 DBC 文件
// 一拖多共享 CAN 卡:同一通道 + 同一文件只加载一次,
// 其他工位已经加载过时直接跳过,避免重复下发并重复追加 DBCParse 报文库
if (_systemConfig?.DBCAutoLoadList != null)
{
foreach (var item in _systemConfig.DBCAutoLoadList)
@@ -474,11 +523,22 @@ namespace UIShare.GlobalVariable
LoggerHelper.Warn($"CAN 通道 {item.DBCChannel} 自动加载 DBC 失败:文件不存在 [{item.DBCFilePath}]");
continue;
}
bool loadOk = CAN.LoadDBC(item.DBCFilePath,new int[] { item.DBCChannel },out _ )==0;
var dbcChannels = new int[] { item.DBCChannel };
bool alreadyLoaded = CAN.IsDbcLoaded(item.DBCFilePath, dbcChannels);
bool loadOk = CAN.LoadDBCOnce(item.DBCFilePath, dbcChannels, out _) == 0;
if (loadOk)
{
LoggerHelper.Info($"CAN 通道 {item.DBCChannel} 已自动加载 DBC:{item.DBCFilePath}");
// 发布 DBC 加载完成事件,通知监控系统刷新信号列表
if (alreadyLoaded)
{
LoggerHelper.Info($"CAN 通道 {item.DBCChannel} DBC 已由其他工位加载,跳过重复加载:{item.DBCFilePath}");
}
else
{
LoggerHelper.Info($"CAN 通道 {item.DBCChannel} 已自动加载 DBC:{item.DBCFilePath}");
}
// 即使跳过加载,也要通知本工位监控系统刷新信号列表
_eventAggregator.GetEvent<DBCLoadedEvent>().Publish(new DBCLoadedArgs
{
Channel = (uint)item.DBCChannel,
@@ -519,11 +579,22 @@ namespace UIShare.GlobalVariable
}
/// <summary>
/// 关闭 CAN 卡
/// 关闭 CAN 卡:按作用域引用计数释放。
/// <para>
/// CAN 卡为一拖多共享硬件,这里只注销本工位的引用;
/// 仅当最后一个工位释放时,<see cref="CAN.ReleaseScope"/> 才停止循环报文、断开总线并释放驱动。
/// </para>
/// </summary>
private void CloseCan()
{
CAN.DisConnect();
try
{
ReleaseCanScope();
}
catch (Exception ex)
{
LoggerHelper.Error($"关闭 CAN 卡异常(已隔离):{ex.Message}");
}
}
private static IReadOnlyDictionary<string, Type> BuildDeviceTypeMap()
@@ -612,6 +683,9 @@ namespace UIShare.GlobalVariable
// 停止 CAN 信号监测服务
_CANMonitoringService?.Stop();
// CAN 卡按作用域引用计数释放:最后一个工位销毁时才停循环报文、断总线并释放驱动
ReleaseCanScope();
if (string.IsNullOrEmpty(_scopeName)) return;
// 遍历当前作用域用到的所有指纹,逐一移除本作用域的引用