Files
ACP/UIShare/GlobalVariable/CANMonitoringService.cs
T
2026-10-26 10:46:44 +08:00

135 lines
6.0 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 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;
using Logger;
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)
{
// 多工位共用同一个 TSMaster 实例:首次工位已完成 initialize_lib_tsmaster,
// 后续工位的 CAN.Init 会直接复用首次结果(返回 0),因此这里不能再用
// “初始化未成功就跳过”的判断,否则第二个工位永远注册不到报文回调。
var initRe = CAN.Init(_systemConfig.TSMasterName);
if (initRe != 0)
{
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)
{
try
{
if (IsStopped) return;
// 通道越界防护:多通道/异常帧的 FIdxChn 可能超出 DBC 数据库(12 通道)范围
if (AData.FIdxChn >= DBCParse.MsgDatabase.Count) return;
if (AData.FData == null) return;
byte[] oldbytes = null;
//记录至实时报文数据库
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))
{
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;
}
RealTimeSignals.Clear();
RealTimeMessages.Clear();
}
}
}