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();
+ }
+ }
///
/// 修改停止发送的方法