CAN FD 调度表初步更改

This commit is contained in:
2026-01-14 17:55:45 +08:00
parent 427cdc5305
commit 4d16b474c6
19 changed files with 2130 additions and 186 deletions

View File

@@ -1,10 +1,13 @@
using CapMachine.Wpf.CanDrive.CanFD;
using CapMachine.Wpf.Dtos;
using CapMachine.Wpf.Models.Tag;
using CapMachine.Wpf.Services;
using ImTools;
using NPOI.OpenXmlFormats.Wordprocessing;
using Prism.Ioc;
using Prism.Mvvm;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
@@ -28,11 +31,12 @@ namespace CapMachine.Wpf.CanDrive
/// <summary>
/// 实例化函数
/// </summary>
public ToomossCanFD(IContainerProvider containerProvider)
public ToomossCanFD(IContainerProvider containerProvider, ILogService logService)
{
ContainerProvider = containerProvider;
LoggerService = logService;
HighSpeedDataService = ContainerProvider.Resolve<HighSpeedDataService>();
LogService = ContainerProvider.Resolve<ILogService>();
//LogService = ContainerProvider.Resolve<ILogService>();
//Stopwatch.Frequency表示高精度计时器每秒的计数次数ticks/秒每毫秒的ticks数 = 每秒的ticks数 ÷ 1000
TicksPerMs = Stopwatch.Frequency / 1000.0;
}
@@ -42,7 +46,7 @@ namespace CapMachine.Wpf.CanDrive
/// </summary>
public HighSpeedDataService HighSpeedDataService { get; set; }
public ILogService LogService { get; set; }
public ILogService LoggerService { get; set; }
/// <summary>
/// 开始CAN的驱动
@@ -60,7 +64,7 @@ namespace CapMachine.Wpf.CanDrive
}
catch (Exception ex)
{
LogService.Error(ex.Message);
LoggerService.Error(ex.Message);
System.Windows.MessageBox.Show($"{ex.Message}", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
}
}
@@ -482,6 +486,11 @@ namespace CapMachine.Wpf.CanDrive
private static readonly Random _random = new Random();
/// <summary>
/// 定时扫描更新数据 扫描Task
/// </summary>
private static Task CycleUpdateCmdTask { get; set; }
/// <summary>
/// 精确周期发送CAN数据
/// </summary>
@@ -549,6 +558,7 @@ namespace CapMachine.Wpf.CanDrive
//此时重置NextExecutionTime为当前时间避免连续的延迟累积
// 严重延迟,重新校准
NextExecutionTime = Stopwatcher.ElapsedTicks;
LoggerService.Info("定时发送延迟过大,重新校准时间");
Console.WriteLine("定时发送延迟过大,重新校准时间");
}
@@ -611,29 +621,35 @@ namespace CapMachine.Wpf.CanDrive
catch (TaskCanceledException)
{
// 任务被取消,正常退出
IsSendOk = false;
break;
}
catch (Exception ex)
{
LogService.Error(ex.Message);
LoggerService.Error(ex.Message);
Console.WriteLine($"CAN周期发送异常: {ex.Message}");
// 短暂暂停避免异常情况下CPU占用过高
IsSendOk = false;
await Task.Delay(10, token);
}
}
IsSendOk = false;
}
catch (Exception ex)
{
// 确保在任何情况下(正常退出、异常、取消)都会停止计时器
Stopwatcher.Stop();
LogService.Error(ex.Message);
LoggerService.Error(ex.Message);
// 清理其他可能的资源
Console.WriteLine("CAN周期发送任务已结束资源已清理");
IsSendOk = false;
}
finally
{
// 确保在任何情况下(正常退出、异常、取消)都会停止计时器
Stopwatcher.Stop();
IsSendOk = false;
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
@@ -677,6 +693,651 @@ namespace CapMachine.Wpf.CanDrive
#endregion
#region 线
/// <summary>
/// 并行精确周期发送CAN数据
/// </summary>
public void StartParallelPrecisionCycleSendMsg()
{
// 创建取消标记源
var cancellationTokenSource = new CancellationTokenSource();
var token = cancellationTokenSource.Token;
// 保存取消标记,以便在停止时使用
CycleSendCts = cancellationTokenSource;
// 初始化计时基准
TicksPerMs = Stopwatch.Frequency / 1000.0;
// 分组消息,按照消息名称进行分组
var groupedMessages = CmdData.GroupBy(x => x.MsgName).ToList();
// 创建发送任务列表
var sendTasks = new List<Task>();
// 创建线程同步对象确保DBC操作和发送操作的线程安全
object dbcLock = new object();
// 为每组消息创建单独的发送任务
foreach (var messageGroup in groupedMessages)
{
var msgName = messageGroup.Key;
var signals = messageGroup.ToList();
// 为每个消息组创建一个发送任务
var sendTask = Task.Factory.StartNew(async () =>
{
try
{
// 设置线程优先级
Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
// 每个消息使用独立的计时器
var messageStopwatch = new Stopwatch();
messageStopwatch.Start();
// 计算该消息的发送周期(可以为不同消息设置不同周期)
// 这里可以从配置或参数中获取特定消息的周期
long cycleInTicks = (long)(SendCycle * TicksPerMs);
long nextExecutionTime = 0;
while (IsCycleSend && !token.IsCancellationRequested)
{
try
{
// 计算下一次执行时间
nextExecutionTime += cycleInTicks;
// 获取当前时间
long currentTime = messageStopwatch.ElapsedTicks;
long delayTicks = nextExecutionTime - currentTime;
// 精确等待逻辑
if (delayTicks > 0)
{
int delayMs = (int)(delayTicks / TicksPerMs);
if (delayMs <= 20)
{
SpinWait.SpinUntil(() => messageStopwatch.ElapsedTicks >= nextExecutionTime);
}
else
{
await Task.Delay(delayMs - 20, token);
SpinWait.SpinUntil(() => messageStopwatch.ElapsedTicks >= nextExecutionTime);
}
}
// 校准时间,处理严重延迟情况
if (messageStopwatch.ElapsedTicks >= nextExecutionTime + cycleInTicks)
{
nextExecutionTime = messageStopwatch.ElapsedTicks;
LoggerService.Info($"消息{msgName}定时发送延迟过大,重新校准时间");
}
// 使用锁确保DBC操作和发送的线程安全
lock (dbcLock)
{
// 构建CAN消息
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)));
try
{
// 为信号赋值
foreach (var signal in signals)
{
DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(msgName),
new StringBuilder(signal.SignalName), signal.SignalCmdValue);
}
// 同步值到CAN消息
DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(msgName), msgPtSend);
var canMsg = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_MSG));
// 直接发送此消息,无需通过队列
USB2CAN.CAN_MSG[] canMsgArray = new USB2CAN.CAN_MSG[] { canMsg };
int sendedNum = USB2CAN.CAN_SendMsg(DevHandle, WriteCANIndex, canMsgArray, 1);
// 更新发送状态
if (sendedNum >= 0)
{
// 发送成功
IsSendOk = true;
}
else
{
IsSendOk = false;
LoggerService.Info($"消息{msgName}发送失败: {sendedNum}");
}
}
finally
{
// 确保释放非托管资源
Marshal.FreeHGlobal(msgPtSend);
}
}
}
catch (TaskCanceledException)
{
LoggerService.Info($"消息{msgName}发送任务被取消");
break;
}
catch (Exception ex)
{
LoggerService.Info($"消息{msgName}发送异常: {ex.Message}");
await Task.Delay(10, token);
}
}
}
catch (Exception ex)
{
LoggerService.Info($"消息{msgName}发送线程异常: {ex.Message}");
}
finally
{
LoggerService.Info($"消息{msgName}发送线程已退出");
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
sendTasks.Add(sendTask);
}
// 创建监控任务,监控所有发送任务的状态
CycleSendTask = Task.Factory.StartNew(async () =>
{
try
{
// 等待所有任务完成或取消
await Task.WhenAll(sendTasks.ToArray());
}
catch (Exception ex)
{
LoggerService.Info($"并行发送监控任务异常: {ex.Message}");
}
finally
{
LoggerService.Info("并行发送任务已全部结束");
IsSendOk = false;
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
#endregion
#region
private bool _SchEnable;
/// <summary>
/// 调度表使能
/// </summary>
public bool SchEnable
{
get { return _SchEnable; }
set
{
_SchEnable = value;
RaisePropertyChanged();
}
}
/// <summary>
/// 调度表的发送的报文数据
/// </summary>
private USB2CANFD.CANFD_MSG[] SchCanMsg { get; set; }
/// <summary>
/// 依据消息的分组
/// </summary>
private IEnumerable<IGrouping<string, CanCmdData>> GroupMsg { get; set; }
/// <summary>
/// 调度表集合数据
/// 总共3个调度表第一个表里面包含3帧数据第二个调度表包含6帧数据第三个调度表包含11帧数据
/// Byte[] MsgTabNum = new Byte[3] { 3, 6, 11 };
/// </summary>
private Byte[] MsgTabNum { get; set; }
/// <summary>
/// 调度表发送的次数集合
/// 第一个调度表循环发送数据第二个调度表循环发送数据第三个调度表只发送3次
/// UInt16[] SendTimes = new UInt16[3] { 0xFFFF, 0xFFFF, 3 };
/// </summary>
private UInt16[] SendTimes { get; set; }
/// <summary>
/// 预设的调度表的个数 常值
/// </summary>
private const int MsgTabCount = 5;
/// <summary>
/// 定时更新时间
/// </summary>
private int UpdateCycle { get; set; } = 100;
/// <summary>
/// CNA 调度表的配置信息
/// </summary>
public List<CANFdScheduleConfigDto> ListCANScheduleConfig { get; set; }
Random random = new Random();
/// <summary>
/// 监控数据
/// 查找问题用,平时不用
/// </summary>
//public MonitorValueLog monitorValueLog { get; set; }
/// <summary>
/// 更新数据 测试用废弃了
/// </summary>
public void UpdateValue()
{
//通过DBC进行对消息赋值
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
int Index = 0;
//循环给MSG赋值数据
foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
itemSignal.SignalCmdValue = random.Next(0, 100);
DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
SchCanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG));
Index++;
}
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
//释放申请的临时缓冲区
Marshal.FreeHGlobal(msgPtSend);
////总共3个调度表第一个表里面包含3帧数据第二个调度表包含6帧数据第三个调度表包含11帧数据
//MsgTabNum = new Byte[1] { 1 };
////第一个调度表循环发送数据第二个调度表循环发送数据第三个调度表只发送3次
//SendTimes = new UInt16[1] { 0xFFFF };
//var ret = USB2CANFD.CAN_SetSchedule(DevHandle, WriteCANIndex, SchCanMsg, MsgTabNum, SendTimes, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
var ret = USB2CANFD.CANFD_UpdateSchedule(DevHandle, WriteCANIndex, 0, 0, SchCanMsg, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
if (ret == USB2CANFD.CANFD_SUCCESS)
{
Console.WriteLine("Update CAN Schedule Success");
}
else
{
Console.WriteLine("Update CAN Schedule Error ret = {0}", ret);
return;
}
}
/// <summary>
/// 开始调度表执行
/// </summary>
public void StartSchedule()
{
if (CmdData.Count() == 0) return;
//依据报文进行分组
GroupMsg = CmdData.GroupBy(x => x.MsgName)!;
//初始化调度表要发送的消息结构
SchCanMsg = new USB2CANFD.CANFD_MSG[GroupMsg.Count()];
for (int i = 0; i < GroupMsg.Count(); i++)
{
SchCanMsg[i] = new USB2CANFD.CANFD_MSG();
SchCanMsg[i].Data = new Byte[64];
}
//通过DBC进行对消息赋值
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
int Index = 0;
//循环给MSG赋值数据
foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
//每个分组就是一个帧指令/消息数据
SchCanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG));
//分配当前消息帧在集合中的位置信息到ListCANScheduleConfig中方便实时更新时定位帧的位置
if (ListCANScheduleConfig.Any(a => a.MsgName == itemMsg.Key))
{
//把帧的位置给ListCANScheduleConfig方便更新时定位数据
ListCANScheduleConfig.FindFirst(a => a.MsgName == itemMsg.Key).MsgIndex = Index;
//设置当前这个报文的在调度表中的发送周期
SchCanMsg[Index].TimeStamp = (uint)ListCANScheduleConfig.FindFirst(a => a.MsgName == itemMsg.Key).Cycle;
}
Index++;
}
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
//释放申请的临时缓冲区
Marshal.FreeHGlobal(msgPtSend);
//********就是可以设置多个调度表放那里,但是运行时同一个时刻只能运行调度表其中的一个 ********
//****** 控制报文SchCanMsg和调度器中第一个调度器中的报文集合和要更新的报文集合都是同一个变量SchCanMsg ********
// *** SchCanMsg的Index序号和ListCANScheduleConfig的MsgIndex是一样的 ***
//图莫斯的Sample总共3个调度表第一个表里面包含3帧数据第二个调度表包含6帧数据第三个调度表包含11帧数据
//预设5个调度表但是我们只用其中第一个调度表第一个调度表中包括多少消息帧由系统的控制指令的帧的分布决定SchCanMsg.Count()是所需要的控制发送的帧,都放到第一个调度表中
MsgTabNum = new Byte[MsgTabCount] { (byte)SchCanMsg.Count(), 1, 1, 1, 1 };
//0xFFFF调度表循环发送数据X调度表循环发送的次数
//设置每个调度表的发送方式,约定全部为循环发送
SendTimes = new UInt16[MsgTabCount] { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF };
//SchCanMsg:需要发送的消息集合MsgTabNum调度表集合数据SendTimes发送次数集合数据调度表个数MsgTabCount
var ret = USB2CANFD.CANFD_SetSchedule(DevHandle, WriteCANIndex, SchCanMsg, MsgTabNum, SendTimes, MsgTabCount);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
if (ret == USB2CANFD.CANFD_SUCCESS)
{
Console.WriteLine("Set CAN Schedule Success");
}
else
{
Console.WriteLine("Set CAN Schedule Error ret = {0}", ret);
LoggerService.Info($"Set CAN Schedule Error; 返回错误代码:{ret}");
return;
}
//约定使用Index=0 1号调度器因为同一个时刻只能有一个调度器工作把所有的报文都要放到这个调度器中
//CAN_MSG的TimeStamp就是这个报文发送的周期由调度器协调
ret = USB2CANFD.CANFD_StartSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)100, (byte)ListCANScheduleConfig.FirstOrDefault()!.OrderSend);
if (ret == USB2CANFD.CANFD_SUCCESS)
{
Console.WriteLine($"Start CAN Schedule 1 SuccessSchTabIndex{(byte)0} - Cycle{(byte)100} - OrderSend{(byte)1}");
}
else
{
Console.WriteLine("Start CAN Schedule 1 Error ret = {0}", ret);
LoggerService.Info($"Start CAN Schedule 1 Error;");
return;
}
//foreach (var itemGroupMsg in GroupMsg)
//{
// if (itemGroupMsg == null) continue;
// if (ListCANScheduleConfig.Any(a => a.MsgName!.Contains(itemGroupMsg.Key)))
// {
// var CANScheduleConfig = ListCANScheduleConfig.FindFirst(a => a.MsgName!.Contains(itemGroupMsg.Key));
// //配置表里面包括这个报文消息内容
// ret = USB2CANFD.CAN_StartSchedule(DevHandle, WriteCANIndex, (byte)CANScheduleConfig.SchTabIndex, (byte)CANScheduleConfig.Cycle, (byte)CANScheduleConfig.OrderSend);
// if (ret == USB2CANFD.CAN_SUCCESS)
// {
// Console.WriteLine($"Start CAN Schedule 1 SuccessSchTabIndex{(byte)CANScheduleConfig.SchTabIndex} - Cycle{(byte)CANScheduleConfig.Cycle} - OrderSend{(byte)CANScheduleConfig.OrderSend}");
// }
// else
// {
// Console.WriteLine("Start CAN Schedule 1 Error ret = {0}", ret);
// LoggerService.Info($"Start CAN Schedule 1 Error;消息名称:{CANScheduleConfig.MsgName}");
// return;
// }
// }
// else
// {
// LoggerService.Info($"调度表配置未发现对应的消息报文信息;报文信息{itemGroupMsg.Key}");
// }
//}
//走到这里说明调度表执行的是OK的
//IsSendOk = true;
}
/// <summary>
/// 停止调度表
/// </summary>
public void StopSchedule()
{
ret = USB2CANFD.CANFD_StopSchedule(DevHandle, WriteCANIndex);//启动第一个调度表,表里面的CAN帧并行发送
if (ret == USB2CANFD.CANFD_SUCCESS)
{
IsSendOk = false;
Console.WriteLine("Stop CAN Schedule");
LoggerService.Info($"Stop CAN Schedule");
}
else
{
Console.WriteLine("Start CAN Schedule Error ret = {0}", ret);
LoggerService.Info($"Stop CAN Schedule");
return;
}
}
/// <summary>
/// 循环使用的
/// </summary>
private int CycleUpdateIndex = 0;
/// <summary>
/// 循环更新调度表的指令数据
/// 定时更新数据到调度表中
/// </summary>
public void StartCycleUpdateCmd()
{
CycleUpdateCmdTask = Task.Run(async () =>
{
while (IsCycleSend)
{
await Task.Delay(UpdateCycle);
try
{
//通过DBC进行对消息赋值
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
CycleUpdateIndex = 0;
//循环给MSG赋值数据顺序是固定的跟初始时设置是一样的
foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
//itemSignal.SignalCmdValue = random.Next(0, 100); //仿真测试数据使用
DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
SchCanMsg[CycleUpdateIndex] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG));
CycleUpdateIndex++;
}
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
//释放申请的临时缓冲区
Marshal.FreeHGlobal(msgPtSend);
//CAN_UpdateSchedule 官网解释
// ---MsgTabIndex CAN调度表索引号
// ---MsgIndex 开始更新帧起始索引,若起始索引大于调度表帧数,则将帧添加到调度表后面
// ---pCanMsg 需要更新的CAN帧指针
// ---MsgNum pCanMsgTab里面包含的有效帧数
//CAN_UpdateSchedule中的MsgIndex表示当前的调度器中的帧Index序号
//因为调度表中的帧集合和控制帧的集合和要更新的帧集合都是同一个集合SchCanMsg
//默认1号调度表一个更新所有的帧数据
var ret = USB2CANFD.CANFD_UpdateSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)(0), SchCanMsg, (byte)SchCanMsg.Count());//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
if (ret == USB2CANFD.CANFD_SUCCESS)
{
IsSendOk = true;
Console.WriteLine($"Update CAN Schedule Success -- SchTabIndex{(byte)0} -- MsgIndex{(byte)(0)} ");
}
else
{
IsSendOk = false;
Console.WriteLine($"Update CAN Schedule Error ret = {ret} -- SchTabIndex{(byte)0} -- MsgIndex{(byte)(0)}");
//return;
}
//一个报文帧一个报文帧进行更新数据
////配置信息 默认启用1号调度器,MsgTabIndex=0
//foreach (var itemMsgSchConfig in ListCANScheduleConfig)
//{
// //USB2CANFD.CAN_MSG[] SchCanMsg1=new CAN_MSG[1];
// //SchCanMsg1[0] = SchCanMsg[itemMsgSchConfig.MsgIndex];
// // MsgTabIndex CAN调度表索引号 ;MsgIndex 开始更新帧起始索引,若起始索引大于调度表帧数,则将帧添加到调度表后面, ;
// // pCanMsg 需要更新的CAN帧指针,消息数据 ; MsgNum pCanMsgTab里面包含的有效帧数一个调度表对应一个帧/消息即为1 (byte)(itemMsgSchConfig.MsgIndex+0)
// var ret = USB2CANFD.CAN_UpdateSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)(itemMsgSchConfig.MsgIndex), SchCanMsg, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
// if (ret == USB2CANFD.CAN_SUCCESS)
// {
// Console.WriteLine($"Update CAN Schedule Success -- SchTabIndex{(byte)itemMsgSchConfig.SchTabIndex} -- MsgIndex{(byte)(itemMsgSchConfig.MsgIndex)} ");
// }
// else
// {
// Console.WriteLine($"Update CAN Schedule Error ret = {ret} -- SchTabIndex{(byte)itemMsgSchConfig.SchTabIndex} -- MsgIndex{(byte)(itemMsgSchConfig.MsgIndex)}");
// //return;
// }
//}
}
catch (Exception ex)
{
IsSendOk = false;
LoggerService.Info($"时间:{DateTime.Now.ToString()}-【MSG】-{ex.Message}");
}
}
IsSendOk = false;
});
}
/// <summary>
/// 加载要发送的数据
/// 一般是激活后才注册事件
/// </summary>
/// <param name="cmdData"></param>
public void LoadCmdDataToDrive(List<CanCmdData> cmdData)
{
// Unsubscribe from events on the old CmdData items
if (CmdData != null && CmdData.Count > 0)
{
foreach (var cmd in CmdData)
{
cmd.CanCmdDataChangedHandler -= CmdData_CanCmdDataChangedHandler;
}
}
// Set the new data and subscribe to events
CmdData = cmdData;
foreach (var cmd in cmdData)
{
cmd.CanCmdDataChangedHandler += CmdData_CanCmdDataChangedHandler;
}
}
/// <summary>
/// 指令数据发生变化执行方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CmdData_CanCmdDataChangedHandler(object? sender, string e)
{
UpdateSchDataByCmdDataChanged();
}
/// <summary>
/// 指令数据发生变化执行更新调度表锁
/// </summary>
private readonly object SchUpdateLock = new object();
/// <summary>
/// 指令数据发生变化执行方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UpdateSchDataByCmdDataChanged()
{
try
{
if (!IsCycleSend) return;
if (!SchEnable) return;
// 基础防御:确保 DBC/ 调度表 / 分组已经初始化
if (DBCHandle == 0 || SchCanMsg == null || GroupMsg == null)
{
return;
}
lock (SchUpdateLock)
{
//通过DBC进行对消息赋值
IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
int CycleUpdateIndex = 0;
//循环给MSG赋值数据顺序是固定的跟初始时设置是一样的
foreach (var itemMsg in GroupMsg)
{
foreach (var itemSignal in itemMsg)
{
//itemSignal.SignalCmdValue = random.Next(0, 100); //仿真测试数据使用
var SetSignalValue = DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
//monitorValueLog.UpdateValue1(SetSignalValue);
}
var SyncValueToCanMsg = DBCParserByFD.DBC_SyncValueToCANMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
//monitorValueLog.UpdateValue2(SyncValueToCanMsg);
SchCanMsg[CycleUpdateIndex] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG));
CycleUpdateIndex++;
}
//通过DBC写入数据后生成CanMsg
//将信号值填入CAN消息里面
//释放申请的临时缓冲区
Marshal.FreeHGlobal(msgPtSend);
//CAN_UpdateSchedule 官网解释
// ---MsgTabIndex CAN调度表索引号
// ---MsgIndex 开始更新帧起始索引,若起始索引大于调度表帧数,则将帧添加到调度表后面
// ---pCanMsg 需要更新的CAN帧指针
// ---MsgNum pCanMsgTab里面包含的有效帧数
//CAN_UpdateSchedule中的MsgIndex表示当前的调度器中的帧Index序号
//因为调度表中的帧集合和控制帧的集合和要更新的帧集合都是同一个集合SchCanMsg
//默认1号调度表一个更新所有的帧数据
var ret = USB2CANFD.CANFD_UpdateSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)(0), SchCanMsg, (byte)SchCanMsg.Count());//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
if (ret == USB2CANFD.CANFD_SUCCESS)
{
IsSendOk = true;
//Console.WriteLine($"Update CAN Schedule Success -- SchTabIndex{(byte)0} -- MsgIndex{(byte)(0)} ");
//monitorValueLog.UpdateValue3(ret);
}
else
{
IsSendOk = false;
//Console.WriteLine($"Update CAN Schedule Error ret = {ret} -- SchTabIndex{(byte)0} -- MsgIndex{(byte)(0)}");
//monitorValueLog.UpdateValue3(ret);
LoggerService.Info($"更新调度表失败,错误码:{ret}");
//return;
}
}
}
catch (Exception ex)
{
IsSendOk = false;
LoggerService.Info($"时间:{DateTime.Now.ToString()}-【MSG】-{ex.Message}");
}
}
#endregion
private bool _IsCycleRevice;
/// <summary>
/// 是否循环接收数据
@@ -739,6 +1400,25 @@ namespace CapMachine.Wpf.CanDrive
}
}
private bool _IsReviceOk;
/// <summary>
/// 接收报文是否OK
/// </summary>
public bool IsReviceOk
{
get { return _IsReviceOk; }
set
{
if (_IsReviceOk != value)
{
RaisePropertyChanged();
_IsReviceOk = value;
}
}
}
/// <summary>
/// 要发送的数据
/// </summary>
@@ -751,7 +1431,7 @@ namespace CapMachine.Wpf.CanDrive
{
CycleReviceTask = Task.Run(async () =>
{
//var ret = USB2CANFD.CANFD_StartGetMsg(DevHandle, ReadCANIndex);
//var ret = USB2CANFDFD.CANFD_StartGetMsg(DevHandle, ReadCANIndex);
while (IsCycleRevice)
{
await Task.Delay(ReviceCycle);
@@ -763,6 +1443,7 @@ namespace CapMachine.Wpf.CanDrive
int CanNum = USB2CANFD.CANFD_GetMsg(DevHandle, ReadCANIndex, msgPtRead, CanMsgBuffer.Length);
if (CanNum > 0)
{
IsReviceOk = true;
Console.WriteLine("Read CanMsgNum = {0}", CanNum);
for (int i = 0; i < CanNum; i++)
{
@@ -787,10 +1468,12 @@ namespace CapMachine.Wpf.CanDrive
}
else if (CanNum == 0)
{
IsReviceOk = false;
Console.WriteLine("No CAN data!");
}
else
{
IsReviceOk = false;
Console.WriteLine("Get CAN data error!");
}
Console.WriteLine("");
@@ -804,7 +1487,7 @@ namespace CapMachine.Wpf.CanDrive
//有配置的名称的,认为是有用的,则需要读取数据
//if (!string.IsNullOrEmpty(item.Name))
//{
//CAN_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ValueSb);
//DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ValueSb);
//double[] ValueDouble;
DBCParserByFD.DBC_GetSignalValue(DBCHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ValueDouble);
//item.SignalRtValueSb = ValueSb;
@@ -826,10 +1509,14 @@ namespace CapMachine.Wpf.CanDrive
}
catch (Exception ex)
{
LogService.Error(ex.Message);
//LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}");
IsReviceOk = false;
LoggerService.Error(ex.Message);
//LoggerService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}");
}
}
IsReviceOk = false;
});
}
@@ -893,6 +1580,25 @@ namespace CapMachine.Wpf.CanDrive
DbcParserState = false;
IsCycleRevice = false;
IsCycleSend = false;
if (SchEnable)
{
StopSchedule();
}
// 确保定时器发送被停止并释放资源
//try { StopTimerCycleSendMsg(); } catch { }
// 等待接收任务结束后释放非托管缓冲区,避免并发释放
try
{
var task = CycleReviceTask;
if (task != null && !task.IsCompleted)
{
task.Wait(TimeSpan.FromMilliseconds(ReviceCycle + 500));
}
}
catch { }
}
}