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
+77 -49
View File
@@ -7,6 +7,7 @@ using System.Text;
using System.Threading.Tasks;
using TSMasterCAN;
using TSMaster;
using Logger;
namespace UIShare.GlobalVariable
{
public class CANMonitoringService
@@ -23,79 +24,106 @@ namespace UIShare.GlobalVariable
}
public int Init(CancellationToken ct = default)
{
int re = 0;
if (IsInitialized == false)
// 多工位共用同一个 TSMaster 实例:首次工位已完成 initialize_lib_tsmaster,
// 后续工位的 CAN.Init 会直接复用首次结果(返回 0),因此这里不能再用
// “初始化未成功就跳过”的判断,否则第二个工位永远注册不到报文回调。
var initRe = CAN.Init(_systemConfig.TSMasterName);
if (initRe != 0)
{
re = CAN.Init(_systemConfig.TSMasterName);
}
if (IsInitialized == false)
{
re = CAN.RegisterListener(MonitorEvent);
if (re == 0) IsInitialized = true;
LoggerHelper.Warn($"CAN 驱动初始化返回 {initRe}({CAN.GetErrorDescription(initRe)}),继续注册报文回调。");
}
// RegisterListener 幂等:重复调用只补充订阅者;驱动被 Release 后再 Init 也能重新挂回原生回调
var 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();
/// <summary>
/// TSMaster 原生库收报文回调(原生线程直接调用)。
/// 回调内抛出的异常一旦跨出原生边界会直接导致进程闪退,
/// 因此整个方法体必须包裹异常隔离,且不做任何可能阻塞的调用。
/// </summary>
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))
try
{
oldbytes = v.FData;
}
RealTimeMessages[MessageName] = cpAData;
if (IsStopped) return;
//记录至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;
}
// 通道越界防护:多通道/异常帧的 FIdxChn 可能超出 DBC 数据库(12 通道)范围
if (AData.FIdxChn >= DBCParse.MsgDatabase.Count) return;
if (AData.FData == null) return;
if (oldbytes is not null) ArrayPool<byte>.Shared.Return(oldbytes);
byte[] oldbytes = null;
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)
//记录至实时报文数据库
var cpAData = AData;
cpAData.FData = ArrayPool<byte>.Shared.Rent(64);
Array.Copy(AData.FData, 0, cpAData.FData, 0, Math.Min(AData.FData.Length, cpAData.FData.Length));
var MessageName = $"{AData.FIdxChn}/{AData.FIdentifier}";
if (RealTimeMessages.TryGetValue(MessageName, out var v))
{
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;
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, Math.Min(cpAData.FData.Length, dbcfind.ACANFD.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 configList = _systemConfig.ConfigurationList;
if (configList == null) return;
var canSignalList = configList.Where(s => s.MessageID == id && s.Channel == ch).ToArray();
if (canSignalList.Length > 0)
{
// DBC 中查不到该报文时静默跳过(配置与 DBC 不一致不应导致进程崩溃)
var find = DBCParse.MsgDatabase[ch].FirstOrDefault(s => s.msg_id == id);
if (find == null) return;
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;
}
}
}
catch (Exception ex)
{
// 原生回调内绝不能向外抛异常:跨原生边界即进程闪退。
LoggerHelper.Error($"CANMonitoringService.MonitorEvent 回调异常(已隔离):{ex.Message}");
}
}
/// <summary>
/// 停止监测:从广播器注销并清理实时数据。
/// 停止监测:注销原生报文回调、从广播器注销并清理实时数据。
/// <para>必须先反注册回调再释放实例,否则原生线程仍会回调到已停止的工位。</para>
/// </summary>
public void Stop()
{
IsStopped = true;
if (IsRegistered)
{
CAN.UnRegisterListener(MonitorEvent);
CANSignalBroadcaster.UnregisterService(this);
IsRegistered = false;
}