From 268f4baf9984397fbc441d9efc93af6210af0681 Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Thu, 14 May 2026 17:33:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A525001=20can?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CapMachine.Wpf/CanDrive/ToomossCan.cs | 261 +++++++++++++++++++++++++- 1 file changed, 259 insertions(+), 2 deletions(-) diff --git a/CapMachine.Wpf/CanDrive/ToomossCan.cs b/CapMachine.Wpf/CanDrive/ToomossCan.cs index 6993c97..f2197f9 100644 --- a/CapMachine.Wpf/CanDrive/ToomossCan.cs +++ b/CapMachine.Wpf/CanDrive/ToomossCan.cs @@ -566,7 +566,7 @@ namespace CapMachine.Wpf.CanDrive /// /// 循环获取CAN消息(使用预分配缓冲区,避免每次循环内存分配/释放) /// - public void StartCycleReviceCanMsg() + public void StartCycleReviceCanMsg1() { if (CycleReviceTask != null && !CycleReviceTask.IsCompleted) { @@ -637,6 +637,193 @@ namespace CapMachine.Wpf.CanDrive }); } + + // 保护接收缓冲的并发锁(接收读与关闭释放之间的互斥) + private readonly object RecvBufferSync = new object(); + + //StringBuilder ValueSb = new StringBuilder(16); + double[] ValueDouble = new double[5]; + + // 接收缓冲池(重用,避免每轮分配) + //private IntPtr RecvMsgBufferPtr = IntPtr.Zero; + private int RecvMsgBufferCapacity = 1024; + private readonly int CanMsgSize = Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)); + + // 名称 StringBuilder 缓存(DBC 调用复用,避免频繁分配) + private readonly Dictionary MsgNameSBCache = new Dictionary(StringComparer.Ordinal); + private readonly Dictionary SigNameSBCache = new Dictionary(StringComparer.Ordinal); + + // 控制台调试输出开关(默认关闭,防止日志风暴) + public bool EnableConsoleDebugLog { get; set; } = true; + + // 保护接收缓冲的并发锁(接收读与关闭释放之间的互斥) + //private readonly object RecvBufferSync = new object(); + + private StringBuilder GetMsgSB(string key) => GetCachedSB(MsgNameSBCache, key); + private StringBuilder GetSigSB(string key) => GetCachedSB(SigNameSBCache, key); + + private StringBuilder GetCachedSB(Dictionary cache, string key) + { + key ??= string.Empty; + if (cache.TryGetValue(key, out var sb)) return sb; + var nsb = new StringBuilder(key); + cache[key] = nsb; + return nsb; + } + + /// + /// 启动后台循环接收 CAN 报文,并同步到 DBC 信号实时值。 + /// + /// + /// 关键点: + /// - 使用 CAN_GetMsgWithSize 从设备内部 FIFO 拉取报文; + /// - 使用 作为重用缓冲,避免每轮申请/释放非托管内存导致碎片与性能问题; + /// - 使用 互斥,避免指针并发释放。 + /// + public void StartCycleReviceCanMsg() + { + // 防止重复启动,若已有任务在运行则直接返回 + if (CycleReviceTask != null && !CycleReviceTask.IsCompleted) + { + return; + } + //monitorValueLog + CycleReviceTask = Task.Run(async () => + { + try + { + while (IsCycleRevice) + { + await Task.Delay(ReviceCycle); + try + { + // 另一个CAN通道读取数据(与 CloseDevice 释放互斥,保护指针安全) + IntPtr msgPtRead; + int CanNum; + lock (RecvBufferSync) + { + if (RecvMsgBufferPtr == IntPtr.Zero) + { + RecvMsgBufferPtr = Marshal.AllocHGlobal(CanMsgSize * RecvMsgBufferCapacity); + //LoggerService.Info("申请 RecvMsgBufferPtr"); + } + msgPtRead = RecvMsgBufferPtr; + CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, ReadCANIndex, msgPtRead, RecvMsgBufferCapacity); + //int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, 1, msgPtRead, RecvMsgBufferCapacity);//测试用,CAN卡 CAN1和CAN2 短接时测试用 + //monitorValueLog.UpdateValue4(CanNum); + if (CanNum > 0) + { + IsReviceOk = true; + if (EnableConsoleDebugLog) Console.WriteLine("Read CanMsgNum = {0}", CanNum); + for (int i = 0; i < CanNum; i++) + { + var msgPtr = (IntPtr)(msgPtRead + i * CanMsgSize); + var msg = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtr, typeof(USB2CAN.CAN_MSG)); + + if (EnableConsoleDebugLog) + { + Console.WriteLine("CanMsg[{0}].ID = 0x{1}", i, msg.ID.ToString("X8")); + Console.WriteLine("CanMsg[{0}].TimeStamp = {1}", i, msg.TimeStamp); + Console.Write("CanMsg[{0}].Data = ", i); + for (int j = 0; j < msg.DataLen; j++) + { + Console.Write("{0} ", msg.Data[j].ToString("X2")); + } + Console.WriteLine(""); + } + + //// 报文给高速记录的服务 + //HighSpeedDataService.AppendOrUpdateMsg(new Models.HighSpeed.CommMsg() + //{ + // Category = "CAN", + // MsgInfo = "0x" + msg.ID.ToString("X8"), + // MsgData = BitConverter.ToString(msg.Data), + // Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + //}); + } + } + else if (CanNum == 0) + { + IsReviceOk = false; + if (EnableConsoleDebugLog) Console.WriteLine("No CAN data!"); + } + else + { + IsReviceOk = false; + if (EnableConsoleDebugLog) Console.WriteLine("Get CAN data error!"); + } + // 将CAN消息数据填充到信号里面,用DBC解析数据(仍在锁内,避免指针被并发释放) + var SyncCANMsgToValue = CAN_DBCParser.DBC_SyncCANMsgToValue(DBCHandle, msgPtRead, CanNum); + //monitorValueLog.UpdateValue5(SyncCANMsgToValue); + } + + //循环获取消息的数据 + foreach (var item in ListCanDbcModel) + { + // 复用 StringBuilder 缓存,避免频繁分配 + var msgNameSB = GetMsgSB(item.MsgName); + var sigNameSB = GetSigSB(item.SignalName); + CAN_DBCParser.DBC_GetSignalValue(DBCHandle, msgNameSB, sigNameSB, ValueDouble); + item.SignalRtValue = ValueDouble[0].ToString(); + //Console.Write(ValueSb.ToString()); + } + // 缓冲区在 CloseDevice 或任务退出的 finally 中统一释放,避免频繁申请/释放 + + + } + catch (Exception ex) + { + IsReviceOk = false; + //LoggerService.Info($"CAN指令接收出现异常:{ex.Message}"); + } + //finally + //{ + // IsReviceOk = false; + //} + } + } + finally + { + IsReviceOk = false; + //LoggerService.Info("CAN指令接收 finally结束"); + // 接收任务退出时释放接收缓冲,避免仅停止接收时的缓冲常驻 + lock (RecvBufferSync) + { + if (RecvMsgBufferPtr != IntPtr.Zero) + { + try { Marshal.FreeHGlobal(RecvMsgBufferPtr); } + catch { } + finally { RecvMsgBufferPtr = IntPtr.Zero; } + } + } + } + }); + } + + + private bool _IsReviceOk; + /// + /// 最近一次接收是否成功。 + /// + /// + /// 该状态反映最近一次轮询读取 CAN_GetMsgWithSize 的结果: + /// - CanNum > 0:成功收到数据; + /// - CanNum == 0:本轮无数据; + /// - CanNum < 0:调用失败。 + /// + public bool IsReviceOk + { + get { return _IsReviceOk; } + set + { + if (_IsReviceOk != value) + { + RaisePropertyChanged(); + _IsReviceOk = value; + } + } + } + /// /// 确保接收缓冲区已分配 /// @@ -768,11 +955,60 @@ namespace CapMachine.Wpf.CanDrive //Console.WriteLine($"--当前时间(毫秒): {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"); + //// 执行发送CAN逻辑 + //{ + // SendCanMessagesByDbc(CmdData); + //} + // 执行发送CAN逻辑 { - SendCanMessagesByDbc(CmdData); + + var GroupMsg = CmdData.GroupBy(x => x.MsgName); + USB2CAN.CAN_MSG[] CanMsg = new USB2CAN.CAN_MSG[GroupMsg.Count()]; + for (int i = 0; i < GroupMsg.Count(); i++) + { + CanMsg[i] = new USB2CAN.CAN_MSG(); + CanMsg[i].Data = new Byte[64]; + } + + // 发送构帧临时缓冲:每轮申请/释放,避免与其他线程共享同一指针导致并发问题 + IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG))); + int Index = 0; + //循环给MSG赋值数据 + foreach (var itemMsg in GroupMsg) + { + foreach (var itemSignal in itemMsg) + { + CAN_DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue); + } + CAN_DBCParser.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend); + CanMsg[Index] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_MSG)); + Index++; + } + + //通过DBC写入数据后生成CanMsg + //将信号值填入CAN消息里面 + + // 释放非托管缓冲区(务必释放,否则长时间运行会造成内存泄漏) + Marshal.FreeHGlobal(msgPtSend); + + //发送CAN数据 + int SendedNum = USB2CAN.CAN_SendMsg(DevHandle, WriteCANIndex, CanMsg, (uint)CanMsg.Length); + if (SendedNum >= 0) + { + //Console.WriteLine("Success send frames:{0}", SendedNum); + IsSendOk = true; + } + else + { + //Console.WriteLine("Send CAN data failed! {0}", SendedNum); + IsSendOk = false; + } + } + + } catch (TaskCanceledException) { @@ -801,6 +1037,27 @@ namespace CapMachine.Wpf.CanDrive }, token); } + private bool _IsSendOk; + /// + /// 最近一次发送是否成功。 + /// + /// + /// - 软件循环发送路径:由 CAN_SendMsg 返回值决定; + /// - 调度表更新路径:由 CAN_UpdateSchedule 返回值决定。 + /// + public bool IsSendOk + { + get { return _IsSendOk; } + set + { + if (_IsSendOk != value) + { + RaisePropertyChanged(); + _IsSendOk = value; + } + //RaisePropertyChanged(); + } + } /// /// 修改停止发送的方法