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 RealTimeMessages { get; set; } = new(); public ConcurrentDictionary RealTimeSignals { get; set; } = new(); /// /// TSMaster 原生库收报文回调(原生线程直接调用)。 /// 回调内抛出的异常一旦跨出原生边界会直接导致进程闪退, /// 因此整个方法体必须包裹异常隔离,且不做任何可能阻塞的调用。 /// 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.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.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}"); } } /// /// 停止监测:注销原生报文回调、从广播器注销并清理实时数据。 /// 必须先反注册回调再释放实例,否则原生线程仍会回调到已停止的工位。 /// public void Stop() { IsStopped = true; if (IsRegistered) { CAN.UnRegisterListener(MonitorEvent); CANSignalBroadcaster.UnregisterService(this); IsRegistered = false; } RealTimeSignals.Clear(); RealTimeMessages.Clear(); } } }