diff --git a/ZLGUSBCANFD/USBCANFD.cs b/ZLGUSBCANFD/USBCANFD.cs index b211716..2888c5f 100644 --- a/ZLGUSBCANFD/USBCANFD.cs +++ b/ZLGUSBCANFD/USBCANFD.cs @@ -40,6 +40,10 @@ namespace ZLGUSBCANFD // 每次 设置报文 会累积写入此表,发送时以 DBC 初始值为底再叠加此表覆盖值 private readonly ConcurrentDictionary<(uint 通道号, uint 帧ID), Dictionary> _signalOverrides = new ConcurrentDictionary<(uint, uint), Dictionary>(); + // 最新解码报文缓存:Key = (通道号, 帧ID),Value = 最近一次 DBC 解码成功的报文快照(含各信号原始值) + // 接收线程解码成功时写入,供 获取报文信号值 主动查询最新信号物理值 + private readonly ConcurrentDictionary<(uint 通道号, uint 帧ID), ZDBC.DBCMessage> _latestDecodedMessages = new ConcurrentDictionary<(uint, uint), ZDBC.DBCMessage>(); + // 动态硬件参数 private readonly uint _deviceType; // 76: USBCANFD-400U private readonly uint _deviceIndex; // 设备索引 @@ -300,6 +304,13 @@ namespace ZLGUSBCANFD _dbcHandles[通道号] = 0; _isDbcLoadedArray[通道号] = false; DBCParser.MsgDatabase[(int)通道号].Clear(); + + // 同步清理该通道的最新解码报文缓存,避免残留旧信号值 + foreach (var key in _latestDecodedMessages.Keys) + { + if (key.通道号 == 通道号) + _latestDecodedMessages.TryRemove(key, out _); + } } } } @@ -344,6 +355,8 @@ namespace ZLGUSBCANFD if (_isDbcLoadedArray[channelIndex] && ZDBC.ZDBC_Decode(_dbcHandles[channelIndex], ptrDbcMsg, framePtr, 1, 0)) { var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage)); + // 缓存最新解码报文快照,供 获取报文信号值 查询 + _latestDecodedMessages[(channelIndex, msg.nID)] = msg; OnDbcMessageDecoded?.Invoke(channelIndex, msg); } } @@ -369,6 +382,8 @@ namespace ZLGUSBCANFD if (_isDbcLoadedArray[channelIndex] && ZDBC.ZDBC_Decode(_dbcHandles[channelIndex], ptrDbcMsg, framePtr, 1, 1)) { var msg = (ZDBC.DBCMessage)Marshal.PtrToStructure(ptrDbcMsg, typeof(ZDBC.DBCMessage)); + // 缓存最新解码报文快照,供 获取报文信号值 查询 + _latestDecodedMessages[(channelIndex, msg.nID)] = msg; OnDbcMessageDecoded?.Invoke(channelIndex, msg); } } @@ -1023,6 +1038,71 @@ namespace ZLGUSBCANFD #endregion + #region 4.2 报文信号查询 + + /// + /// 获取指定通道最新一帧 DBC 报文中某个信号的物理值(帧 ID 支持 string 兼容模式)。 + /// + /// 返回值为最近一次接收并解码成功的报文快照:物理值 = 原始值 × 因子 + 偏移, + /// 与监控界面广播的数值口径一致。若报文尚未收到、DBC 未加载、报文或信号名称不存在, + /// 将记录错误日志并返回 0。 + /// + /// + /// CAN/CANFD 通道索引 + /// 对应报文的帧 ID(可以是 "26"、"0x1A"、"1A") + /// DBC 中定义的英文信号名称(不区分大小写) + /// 信号物理值;失败时返回 0 + public virtual double 获取报文信号值(uint 通道号, string 帧IDStr, string 信号名称) + { + if (!TryParseFrameId(帧IDStr, out uint 帧ID)) + { + LoggerHelper.Error($"[ZLGCANFD] 获取报文信号值失败: 无法解析的帧 ID '{帧IDStr}'"); + return 0; + } + + if (通道号 >= _maxChannels) + { + LoggerHelper.Error($"[ZLGCANFD] 获取报文信号值失败: 通道号 {通道号} 越界(有效范围 0~{_maxChannels - 1})"); + return 0; + } + + if (!_isDbcLoadedArray[通道号]) + { + LoggerHelper.Error($"[ZLGCANFD] 获取报文信号值失败: 通道 {通道号} 尚未加载 DBC 文件"); + return 0; + } + + if (string.IsNullOrWhiteSpace(信号名称)) + { + LoggerHelper.Error("[ZLGCANFD] 获取报文信号值失败: 信号名称为空"); + return 0; + } + + // 查找该通道该帧 ID 最近一次解码成功的报文快照 + if (!_latestDecodedMessages.TryGetValue((通道号, 帧ID), out var msg)) + { + LoggerHelper.Error($"[ZLGCANFD] 获取报文信号值失败: 通道 {通道号} 未收到帧 ID 0x{帧ID:X} 的报文(尚未接收或 DBC 中无此报文)"); + return 0; + } + + // 在报文信号集合中查找目标信号(不区分大小写,与 设置报文 的信号匹配口径一致) + for (int i = 0; i < msg.nSignalCount; i++) + { + var signal = msg.vSignals[i]; + string name = Encoding.Default.GetString(signal.strName).TrimEnd('\0'); + if (string.Equals(name, 信号名称, StringComparison.OrdinalIgnoreCase)) + { + // 物理值 = 原始值 × 因子 + 偏移(与 CANSignalBroadcaster 广播公式一致) + return signal.nRawvalue * signal.nFactor + signal.nOffset; + } + } + + LoggerHelper.Error($"[ZLGCANFD] 获取报文信号值失败: 帧 ID 0x{帧ID:X} 的报文中不存在信号 '{信号名称}'"); + return 0; + } + + #endregion + #region 5. 辅助工具子系统 ///