同步25001 can

This commit is contained in:
2026-05-14 17:33:37 +08:00
parent 2b2097d1ed
commit 268f4baf99

View File

@@ -566,7 +566,7 @@ namespace CapMachine.Wpf.CanDrive
/// <summary> /// <summary>
/// 循环获取CAN消息使用预分配缓冲区避免每次循环内存分配/释放) /// 循环获取CAN消息使用预分配缓冲区避免每次循环内存分配/释放)
/// </summary> /// </summary>
public void StartCycleReviceCanMsg() public void StartCycleReviceCanMsg1()
{ {
if (CycleReviceTask != null && !CycleReviceTask.IsCompleted) 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<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; } = 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<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;
}
/// <summary>
/// 启动后台循环接收 CAN 报文,并同步到 DBC 信号实时值。
/// </summary>
/// <remarks>
/// 关键点:
/// - 使用 CAN_GetMsgWithSize 从设备内部 FIFO 拉取报文;
/// - 使用 <see cref="RecvMsgBufferPtr"/> 作为重用缓冲,避免每轮申请/释放非托管内存导致碎片与性能问题;
/// - 使用 <see cref="RecvBufferSync"/> 与 <see cref="CloseDevice"/> 互斥,避免指针并发释放。
/// </remarks>
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;
/// <summary>
/// 最近一次接收是否成功。
/// </summary>
/// <remarks>
/// 该状态反映最近一次轮询读取 CAN_GetMsgWithSize 的结果:
/// - CanNum &gt; 0成功收到数据
/// - CanNum == 0本轮无数据
/// - CanNum &lt; 0调用失败。
/// </remarks>
public bool IsReviceOk
{
get { return _IsReviceOk; }
set
{
if (_IsReviceOk != value)
{
RaisePropertyChanged();
_IsReviceOk = value;
}
}
}
/// <summary> /// <summary>
/// 确保接收缓冲区已分配 /// 确保接收缓冲区已分配
/// </summary> /// </summary>
@@ -768,11 +955,60 @@ namespace CapMachine.Wpf.CanDrive
//Console.WriteLine($"--当前时间(毫秒): {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"); //Console.WriteLine($"--当前时间(毫秒): {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
//// 执行发送CAN逻辑
//{
// SendCanMessagesByDbc(CmdData);
//}
// 执行发送CAN逻辑 // 执行发送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) catch (TaskCanceledException)
{ {
@@ -801,6 +1037,27 @@ namespace CapMachine.Wpf.CanDrive
}, token); }, token);
} }
private bool _IsSendOk;
/// <summary>
/// 最近一次发送是否成功。
/// </summary>
/// <remarks>
/// - 软件循环发送路径:由 CAN_SendMsg 返回值决定;
/// - 调度表更新路径:由 CAN_UpdateSchedule 返回值决定。
/// </remarks>
public bool IsSendOk
{
get { return _IsSendOk; }
set
{
if (_IsSendOk != value)
{
RaisePropertyChanged();
_IsSendOk = value;
}
//RaisePropertyChanged();
}
}
/// <summary> /// <summary>
/// 修改停止发送的方法 /// 修改停止发送的方法