同步25001 can
This commit is contained in:
@@ -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 > 0:成功收到数据;
|
||||||
|
/// - CanNum == 0:本轮无数据;
|
||||||
|
/// - CanNum < 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>
|
||||||
/// 修改停止发送的方法
|
/// 修改停止发送的方法
|
||||||
|
|||||||
Reference in New Issue
Block a user