LIN CAN 的更改

This commit is contained in:
2025-10-15 16:51:15 +08:00
parent fb4bc6d9d7
commit 087b6e9eff
3 changed files with 237 additions and 114 deletions

View File

@@ -1,4 +1,4 @@
using CapMachine.Core;
using CapMachine.Core;
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.Dtos;
using CapMachine.Wpf.Models.Tag;
@@ -12,11 +12,13 @@ using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Interop;
@@ -500,6 +502,32 @@ namespace CapMachine.Wpf.CanDrive
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<string, StringBuilder> MsgNameSBCache = new Dictionary<string, StringBuilder>(StringComparer.Ordinal);
private readonly Dictionary<string, StringBuilder> SigNameSBCache = new Dictionary<string, StringBuilder>(StringComparer.Ordinal);
// 控制台调试输出开关(默认关闭,防止日志风暴)
public bool EnableConsoleDebugLog { get; set; } = false;
// 保护接收缓冲的并发锁(接收读与关闭释放之间的互斥)
private readonly object RecvBufferSync = new object();
private StringBuilder GetCachedSB(Dictionary<string, StringBuilder> 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;
}
private StringBuilder GetMsgSB(string key) => GetCachedSB(MsgNameSBCache, key);
private StringBuilder GetSigSB(string key) => GetCachedSB(SigNameSBCache, key);
private bool _IsSendOk;
/// <summary>
/// 发送报文是否OK
@@ -1258,101 +1286,117 @@ namespace CapMachine.Wpf.CanDrive
/// </summary>
public void StartCycleReviceCanMsg()
{
// 防止重复启动,若已有任务在运行则直接返回
if (CycleReviceTask != null && !CycleReviceTask.IsCompleted)
{
return;
}
CycleReviceTask = Task.Run(async () =>
{
while (IsCycleRevice)
try
{
await Task.Delay(ReviceCycle);
try
while (IsCycleRevice)
{
//另外一个CAN通道读取数据
USB2CAN.CAN_MSG[] CanMsgBuffer = new USB2CAN.CAN_MSG[1024];
//申请数据缓冲区
IntPtr msgPtRead = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)) * CanMsgBuffer.Length);
int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, ReadCANIndex, msgPtRead, CanMsgBuffer.Length);
//int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, 1, msgPtRead, CanMsgBuffer.Length);//测试用CAN卡 CAN1和CAN2 短接时测试用
if (CanNum > 0)
await Task.Delay(ReviceCycle);
try
{
IsReviceOk = true;
Console.WriteLine("Read CanMsgNum = {0}", CanNum);
for (int i = 0; i < CanNum; i++)
// 另一个CAN通道读取数据与 CloseDevice 释放互斥,保护指针安全)
IntPtr msgPtRead;
int CanNum;
lock (RecvBufferSync)
{
if (RecvMsgBufferPtr == IntPtr.Zero)
{
//CanMsgBuffer[i] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure((IntPtr)((UInt32)msgPtRead + i * Marshal.SizeOf(typeof(USB2CAN.CAN_MSG))), typeof(USB2CAN.CAN_MSG)); //有溢出报错
CanMsgBuffer[i] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure((IntPtr)(msgPtRead + i * Marshal.SizeOf(typeof(USB2CAN.CAN_MSG))), typeof(USB2CAN.CAN_MSG));
Console.WriteLine("CanMsg[{0}].ID = 0x{1}", i, CanMsgBuffer[i].ID.ToString("X8"));
Console.WriteLine("CanMsg[{0}].TimeStamp = {1}", i, CanMsgBuffer[i].TimeStamp);
Console.Write("CanMsg[{0}].Data = ", i);
for (int j = 0; j < CanMsgBuffer[i].DataLen; j++)
{
Console.Write("{0} ", CanMsgBuffer[i].Data[j].ToString("X2"));
}
Console.WriteLine("");
//报文给高速记录的服务
HighSpeedDataService.AppendOrUpdateMsg(new Models.HighSpeed.CommMsg()
{
Category = "CAN",
MsgInfo = "0x" + CanMsgBuffer[i].ID.ToString("X8"),
MsgData = BitConverter.ToString(CanMsgBuffer[i].Data),
Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
});
RecvMsgBufferPtr = Marshal.AllocHGlobal(CanMsgSize * RecvMsgBufferCapacity);
}
}
else if (CanNum == 0)
{
IsReviceOk = false;
Console.WriteLine("No CAN data!");
}
else
{
IsReviceOk = false;
Console.WriteLine("Get CAN data error!");
}
//Console.WriteLine("");
msgPtRead = RecvMsgBufferPtr;
CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, ReadCANIndex, msgPtRead, RecvMsgBufferCapacity);
//int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, 1, msgPtRead, RecvMsgBufferCapacity);//测试用CAN卡 CAN1和CAN2 短接时测试用
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));
//将CAN消息数据填充到信号里面用DBC解析数据
CAN_DBCParser.DBC_SyncCANMsgToValue(DBCHandle, msgPtRead, CanNum);
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解析数据仍在锁内避免指针被并发释放
CAN_DBCParser.DBC_SyncCANMsgToValue(DBCHandle, msgPtRead, CanNum);
}
//循环获取消息的数据
foreach (var item in ListCanDbcModel)
{
//有配置的名称的,认为是有用的,则需要读取数据
//if (!string.IsNullOrEmpty(item.Name))
//{
//CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ValueSb);
//double[] ValueDouble;
CAN_DBCParser.DBC_GetSignalValue(DBCHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ValueDouble);
//item.SignalRtValueSb = ValueSb;
// 复用 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 中统一释放,避免频繁申请/释放
//释放数据缓冲区,必须释放,否则程序运行一段时间后会报内存不足
Marshal.FreeHGlobal(msgPtRead);
Thread.Sleep(10);
////获取信号值并打印出来
//StringBuilder ValueStr = new StringBuilder(32);
//CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_moto_speed"), new StringBuilder("moto_speed"), ValueStr);
//Console.WriteLine("moto_speed = {0}", ValueStr);
//CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_oil_pressure"), new StringBuilder("oil_pressure"), ValueStr);
//Console.WriteLine("oil_pressure = {0}", ValueStr);
//CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_speed_can"), new StringBuilder("speed_can"), ValueStr);
//Console.WriteLine("speed_can = {0}", ValueStr);
}
catch (Exception ex)
{
IsReviceOk = false;
LoggerService.Info("接收出现异常");
}
}
catch (Exception ex)
{
IsReviceOk = false;
LoggerService.Info("接收出现异常");
}
//finally
//{
// IsReviceOk = false;
//}
}
}
finally
{
IsReviceOk = false;
// 接收任务退出时释放接收缓冲,避免仅停止接收时的缓冲常驻
lock (RecvBufferSync)
{
if (RecvMsgBufferPtr != IntPtr.Zero)
{
try { Marshal.FreeHGlobal(RecvMsgBufferPtr); }
catch { }
finally { RecvMsgBufferPtr = IntPtr.Zero; }
}
}
}
IsReviceOk = false;
});
}
@@ -1364,43 +1408,55 @@ namespace CapMachine.Wpf.CanDrive
//另外一个CAN通道读取数据
USB2CAN.CAN_MSG[] CanMsgBuffer = new USB2CAN.CAN_MSG[10];
msgPt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)) * CanMsgBuffer.Length);
int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, ReadCANIndex, msgPt, CanMsgBuffer.Length);
if (CanNum > 0)
try
{
Console.WriteLine("Read CanMsgNum = {0}", CanNum);
for (int i = 0; i < CanNum; i++)
int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, ReadCANIndex, msgPt, CanMsgBuffer.Length);
if (CanNum > 0)
{
CanMsgBuffer[i] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure((IntPtr)((UInt32)msgPt + i * Marshal.SizeOf(typeof(USB2CAN.CAN_MSG))), typeof(USB2CAN.CAN_MSG));
Console.WriteLine("CanMsg[{0}].ID = 0x{1}", i, CanMsgBuffer[i].ID.ToString("X8"));
//Console.WriteLine("CanMsg[{0}].TimeStamp = {1}",i,CanMsgBuffer[i].TimeStamp);
Console.Write("CanMsg[{0}].Data = ", i);
for (int j = 0; j < CanMsgBuffer[i].DataLen; j++)
Console.WriteLine("Read CanMsgNum = {0}", CanNum);
for (int i = 0; i < CanNum; i++)
{
Console.Write("{0} ", CanMsgBuffer[i].Data[j].ToString("X2"));
CanMsgBuffer[i] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure((IntPtr)((UInt32)msgPt + i * Marshal.SizeOf(typeof(USB2CAN.CAN_MSG))), typeof(USB2CAN.CAN_MSG));
Console.WriteLine("CanMsg[{0}].ID = 0x{1}", i, CanMsgBuffer[i].ID.ToString("X8"));
//Console.WriteLine("CanMsg[{0}].TimeStamp = {1}",i,CanMsgBuffer[i].TimeStamp);
Console.Write("CanMsg[{0}].Data = ", i);
for (int j = 0; j < CanMsgBuffer[i].DataLen; j++)
{
Console.Write("{0} ", CanMsgBuffer[i].Data[j].ToString("X2"));
}
Console.WriteLine("");
}
Console.WriteLine("");
}
else if (CanNum == 0)
{
Console.WriteLine("No CAN data!");
}
else
{
Console.WriteLine("Get CAN data error!");
}
Console.WriteLine("");
//将CAN消息数据填充到信号里面
CAN_DBCParser.DBC_SyncCANMsgToValue(DBCHandle, msgPt, CanNum);
//获取信号值并打印出来
StringBuilder ValueStr = new StringBuilder(32);
CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_moto_speed"), new StringBuilder("moto_speed"), ValueStr);
Console.WriteLine("moto_speed = {0}", ValueStr);
CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_oil_pressure"), new StringBuilder("oil_pressure"), ValueStr);
Console.WriteLine("oil_pressure = {0}", ValueStr);
CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_speed_can"), new StringBuilder("speed_can"), ValueStr);
Console.WriteLine("speed_can = {0}", ValueStr);
}
finally
{
if (msgPt != IntPtr.Zero)
{
try { Marshal.FreeHGlobal(msgPt); }
catch { }
finally { msgPt = IntPtr.Zero; }
}
}
else if (CanNum == 0)
{
Console.WriteLine("No CAN data!");
}
else
{
Console.WriteLine("Get CAN data error!");
}
Console.WriteLine("");
//将CAN消息数据填充到信号里面
CAN_DBCParser.DBC_SyncCANMsgToValue(DBCHandle, msgPt, CanNum);
//获取信号值并打印出来
StringBuilder ValueStr = new StringBuilder(32);
CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_moto_speed"), new StringBuilder("moto_speed"), ValueStr);
Console.WriteLine("moto_speed = {0}", ValueStr);
CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_oil_pressure"), new StringBuilder("oil_pressure"), ValueStr);
Console.WriteLine("oil_pressure = {0}", ValueStr);
CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_speed_can"), new StringBuilder("speed_can"), ValueStr);
Console.WriteLine("speed_can = {0}", ValueStr);
}
@@ -1419,6 +1475,27 @@ namespace CapMachine.Wpf.CanDrive
{
StopSchedule();
}
// 等待接收任务结束后释放非托管缓冲区,避免并发释放
try
{
var task = CycleReviceTask;
if (task != null && !task.IsCompleted)
{
task.Wait(TimeSpan.FromMilliseconds(ReviceCycle + 500));
}
}
catch { }
// 在锁内安全释放,避免与接收线程并发访问同一指针
lock (RecvBufferSync)
{
if (RecvMsgBufferPtr != IntPtr.Zero)
{
try { Marshal.FreeHGlobal(RecvMsgBufferPtr); }
catch { }
finally { RecvMsgBufferPtr = IntPtr.Zero; }
}
}
}
}