From aec00445952fbf882c2ea29355129b24e8a04111 Mon Sep 17 00:00:00 2001 From: hsc Date: Mon, 6 Jul 2026 11:37:09 +0800 Subject: [PATCH] =?UTF-8?q?CAN=E4=BD=9C=E7=94=A8=E5=9F=9F=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ADP/ViewModels/ShellViewModel.cs | 13 +++- .../ViewModels/SelectCanMessageViewModel.cs | 68 ++++++++++++------- ZLGUSBCANFD/USBCANFD.cs | 8 ++- ZLGUSBCANFD/ZLGDBCParser.cs | 63 +++++++++-------- 4 files changed, 95 insertions(+), 57 deletions(-) diff --git a/ADP/ViewModels/ShellViewModel.cs b/ADP/ViewModels/ShellViewModel.cs index 28128d6..a4aaaf8 100644 --- a/ADP/ViewModels/ShellViewModel.cs +++ b/ADP/ViewModels/ShellViewModel.cs @@ -15,6 +15,7 @@ using UIShare; using UIShare.GlobalVariable; using UIShare.PubEvent; using UIShare.UIViewModel; +using ZLGUSBCANFD; namespace ADP.ViewModels { @@ -212,7 +213,17 @@ namespace ADP.ViewModels #region 命令处理与事件 private void SelectCANMessage() { - _dialogService.ShowDialog("SelectCanMessageView"); + var canfd = CurrentConfig?.CANFD; + if (canfd == null||_globalInfo.CurrentScope=="default") + { + LoggerHelper.Warn("当前作用域未配置 CAN 设备,无法打开报文选择窗口。"); + return; + } + + _dialogService.ShowDialog("SelectCanMessageView", new DialogParameters + { + { "CANFD", canfd } + }, _ => { }); } private void ShowDialogManagerView() diff --git a/CANModule/ViewModels/SelectCanMessageViewModel.cs b/CANModule/ViewModels/SelectCanMessageViewModel.cs index 5a98bbb..3b06b12 100644 --- a/CANModule/ViewModels/SelectCanMessageViewModel.cs +++ b/CANModule/ViewModels/SelectCanMessageViewModel.cs @@ -10,6 +10,10 @@ namespace CANModule.ViewModels { public class SelectCanMessageViewModel : DialogViewModelBase { + #region 字段 + private ZLGCANFD? _canfd; + #endregion + #region 属性 public DialogCloseListener RequestClose { get; set; } @@ -49,20 +53,22 @@ namespace CANModule.ViewModels { if (SetProperty(ref _selectedMessage, value)) { - // 健壮性检查:防范切换通道时清空选择引发的连锁 Bug - if (!string.IsNullOrEmpty(value)) + if (!string.IsNullOrEmpty(value) && _canfd != null) { - // 使用 FirstOrDefault 代替 First,防止找不到匹配项时导致上位机崩溃 - var targetMsg = ZLGDBCParser.MsgDatabase[MessageChannel] - .FirstOrDefault(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value); + var db = _canfd.DBCParser.MsgDatabase; + if (MessageChannel >= 0 && MessageChannel < db.Count) + { + var targetMsg = db[MessageChannel] + .FirstOrDefault(x => $"[0x{x.msg_id:X}]{x.msg_name}" == value); - if (targetMsg != null && targetMsg.signal_Name != null) - { - SignalList = new ObservableCollection(targetMsg.signal_Name); - } - else - { - SignalList = new ObservableCollection(); + if (targetMsg != null && targetMsg.signal_Name != null) + { + SignalList = new ObservableCollection(targetMsg.signal_Name); + } + else + { + SignalList = new ObservableCollection(); + } } } else @@ -80,7 +86,7 @@ namespace CANModule.ViewModels set => SetProperty(ref _selectedSignal, value); } - private int _MessageChannel=-1; + private int _MessageChannel = -1; public int MessageChannel { get => _MessageChannel; @@ -90,17 +96,25 @@ namespace CANModule.ViewModels { _MessageChannel = value; - // 1. 先安全清空旧的选择项和信号列表,断开引用链 + // 1. 先安全清空旧的选择项和信号列表 SelectedMessage = null; SelectedSignal = null; SignalList = new ObservableCollection(); // 2. 刷新当前通道的报文列表 - if (ZLGDBCParser.MsgDatabase != null && ZLGDBCParser.MsgDatabase.Count > value) + if (_canfd != null) { - MessageList = new ObservableCollection( - ZLGDBCParser.MsgDatabase[value].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}") - ); + var db = _canfd.DBCParser.MsgDatabase; + if (db != null && value >= 0 && value < db.Count) + { + MessageList = new ObservableCollection( + db[value].Select(s => $"[0x{s.msg_id:X}]{s.msg_name}") + ); + } + else + { + MessageList = new ObservableCollection(); + } } else { @@ -128,6 +142,13 @@ namespace CANModule.ViewModels LoadCommand = new DelegateCommand(Load); } + #region Dialog 生命周期 + public override void OnDialogOpened(IDialogParameters parameters) + { + _canfd = parameters.GetValue("CANFD"); + } + #endregion + #region 命令处理 private void Close() { @@ -136,7 +157,8 @@ namespace CANModule.ViewModels private void Load() { - ChannelList = new ObservableCollection(Enumerable.Range(0, 4)); + int maxCh = _canfd?.DBCParser.MaxChannels ?? 4; + ChannelList = new ObservableCollection(Enumerable.Range(0, maxCh)); } private void GetSelectedSignal() @@ -152,20 +174,14 @@ namespace CANModule.ViewModels private void GetSelectedMessage() { - // 修复原本误写为 SelectedSignal != null 的 Bug,并增加了边界防错 if (!string.IsNullOrEmpty(SelectedMessage) && SelectedMessage.Contains(']')) { Application.Current.Dispatcher.Invoke(() => { Clipboard.SetDataObject(SelectedMessage); - //var parts = SelectedMessage.Split(']'); - //if (parts.Length > 1) - //{ - // Clipboard.SetDataObject(parts[1]); // 复制纯报文名称(如:"EngineStatus") - //} }); } } #endregion } -} \ No newline at end of file +} diff --git a/ZLGUSBCANFD/USBCANFD.cs b/ZLGUSBCANFD/USBCANFD.cs index e00c8e9..1862831 100644 --- a/ZLGUSBCANFD/USBCANFD.cs +++ b/ZLGUSBCANFD/USBCANFD.cs @@ -34,6 +34,11 @@ namespace ZLGUSBCANFD private readonly uint _deviceIndex; // 设备索引 private readonly int _maxChannels; // 动态通道数 + /// + /// 当前 CAN 卡专属的 DBC 解析器,按通道隔离报文数据库。 + /// + public ZLGDBCParser DBCParser { get; } + /// /// 事件:当接收到 CAN/CANFD 报文并且通过 DBC 成功解析后触发 /// 参数:uint 通道号 (0,1,2,3), ZDBC.DBCMessage 解析后的DBC消息结构体 @@ -51,6 +56,7 @@ namespace ZLGUSBCANFD _dbcHandles = new uint[_maxChannels]; _isDbcLoadedArray = new bool[_maxChannels]; _channelLocks = new object[_maxChannels]; + DBCParser = new ZLGDBCParser(_maxChannels); for (int i = 0; i < _maxChannels; i++) { @@ -185,7 +191,7 @@ namespace ZLGUSBCANFD { _dbcHandles[通道号] = chDbcHandle; _isDbcLoadedArray[通道号] = true; - ZLGDBCParser.ParseAndLoadToDatabase(chDbcHandle, 0); + DBCParser.ParseAndLoadToDatabase(chDbcHandle, (int)通道号); } else { diff --git a/ZLGUSBCANFD/ZLGDBCParser.cs b/ZLGUSBCANFD/ZLGDBCParser.cs index 15385aa..f361398 100644 --- a/ZLGUSBCANFD/ZLGDBCParser.cs +++ b/ZLGUSBCANFD/ZLGDBCParser.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; -using System.Threading.Tasks; namespace ZLGUSBCANFD { @@ -24,18 +23,37 @@ namespace ZLGUSBCANFD CANFD = 1, } + /// + /// DBC 报文解析器(实例类,每个 ZLGCANFD 拥有独立的解析器实例)。 + /// 不同 CAN 卡的作用域各自维护自己的 MsgDatabase,互不干扰。 + /// public class ZLGDBCParser { - // 补齐这个属性!4个通道的静态报文数据库缓存,供 ViewModel 直接读取 - public static List> MsgDatabase { get; set; } = - [.. Enumerable.Range(0, 4).Select(_ => new List<_Msg_>())]; + /// + /// 每个通道的报文数据库缓存,供 ViewModel 直接读取。 + /// 索引 = 通道号 (0 ~ maxChannels-1)。 + /// + public List> MsgDatabase { get; private set; } /// - /// 外部调用:解析特定通道的 DBC 并自动存入对应的 MsgDatabase 缓存中 + /// 最大通道数(与所属 ZLGCANFD 一致)。 /// - /// ZDBC句柄 - /// 通道号索引 (0-11) - public static void ParseAndLoadToDatabase(uint chDbcHandle, int channel) + public int MaxChannels { get; } + + public ZLGDBCParser(int maxChannels = 4) + { + MaxChannels = maxChannels; + MsgDatabase = new List>( + Enumerable.Range(0, maxChannels).Select(_ => new List<_Msg_>()) + ); + } + + /// + /// 解析特定通道的 DBC 并自动存入对应的 MsgDatabase 缓存中。 + /// + /// ZDBC 句柄 + /// 通道号索引 (0 ~ maxChannels-1) + public void ParseAndLoadToDatabase(uint chDbcHandle, int channel) { if (channel < 0 || channel >= MsgDatabase.Count) return; @@ -48,7 +66,7 @@ namespace ZLGUSBCANFD } /// - /// 全手工解析周立功 DBC 句柄下的所有消息和信号 + /// 全手工解析周立功 DBC 句柄下的所有消息和信号。 /// /// 对应通道的 ZDBC 句柄 /// 返回对齐同星业务层的自定义报文列表 @@ -56,43 +74,34 @@ namespace ZLGUSBCANFD { List<_Msg_> msgList = new List<_Msg_>(); - // 显式指定周立功命名空间下的 INVALID_DBC_HANDLE 校验 - if (chDbcHandle == 0 || chDbcHandle == ZLGUSBCANFD.ZDBC.INVALID_DBC_HANDLE) + if (chDbcHandle == 0 || chDbcHandle == ZDBC.INVALID_DBC_HANDLE) return msgList; - // 显式指定周立功命名空间下的结构体大小 - int msgSize = Marshal.SizeOf(typeof(ZLGUSBCANFD.ZDBC.DBCMessage)); + int msgSize = Marshal.SizeOf(typeof(ZDBC.DBCMessage)); IntPtr pMsg = Marshal.AllocHGlobal(msgSize); try { - // 1. 获取周立功 DBC 的第一条报文 - bool hasMessage = ZLGUSBCANFD.ZDBC.ZDBC_GetFirstMessage(chDbcHandle, pMsg); + bool hasMessage = ZDBC.ZDBC_GetFirstMessage(chDbcHandle, pMsg); while (hasMessage) { - // 强制指定周立功底层 DBCMessage 结构映射 - var zlgMsg = (ZLGUSBCANFD.ZDBC.DBCMessage)Marshal.PtrToStructure(pMsg, typeof(ZLGUSBCANFD.ZDBC.DBCMessage)); + var zlgMsg = (ZDBC.DBCMessage)Marshal.PtrToStructure(pMsg, typeof(ZDBC.DBCMessage)); - // 1. 优先在字节数组中找到第一个 \0 的位置 int nullIndex = Array.IndexOf(zlgMsg.strName, (byte)0); _Msg_ myMsg = new _Msg_ { msg_id = (int)zlgMsg.nID, - - // 2. 只翻译 \0 之前的有效字节,彻底解决乱码与规避不必要的内存开销 msg_name = nullIndex >= 0 ? Encoding.Default.GetString(zlgMsg.strName, 0, nullIndex) : Encoding.Default.GetString(zlgMsg.strName).TrimEnd('\0'), - msg_dlc = (byte)zlgMsg.nSize, msg_cyclic = (int)zlgMsg.nCycleTime, - // 如果底层识别出是扩展帧(nExtend==1),这里对齐映射为你的业务类型 msg_type = zlgMsg.nExtend == 1 ? (int)Msg_Type.CANFD : (int)Msg_Type.CAN, signal_Name = new string[zlgMsg.nSignalCount] }; - // 2. 提取该报文下的所有信号名称 + for (int i = 0; i < zlgMsg.nSignalCount; i++) { var signal = zlgMsg.vSignals[i]; @@ -101,20 +110,16 @@ namespace ZLGUSBCANFD } msgList.Add(myMsg); - - // 3. 移向下一条报文 - hasMessage = ZLGUSBCANFD.ZDBC.ZDBC_GetNextMessage(chDbcHandle, pMsg); + hasMessage = ZDBC.ZDBC_GetNextMessage(chDbcHandle, pMsg); } } finally { - // 彻底释放非托管内存,避免上位机高频切换加载 DBC 时导致内存泄漏 Marshal.FreeHGlobal(pMsg); } - // 按 ID 排序,对齐你的树状展现或者二分查找业务逻辑 msgList.Sort((a, b) => a.msg_id.CompareTo(b.msg_id)); return msgList; } } -} \ No newline at end of file +}