CAN修复
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user