diff --git a/CapMachine.Wpf/CanDrive/CANFD_DBCParser.cs b/CapMachine.Wpf/CanDrive/CanFD/DBCParserByFD.cs
similarity index 92%
rename from CapMachine.Wpf/CanDrive/CANFD_DBCParser.cs
rename to CapMachine.Wpf/CanDrive/CanFD/DBCParserByFD.cs
index 1110e58..0967da2 100644
--- a/CapMachine.Wpf/CanDrive/CANFD_DBCParser.cs
+++ b/CapMachine.Wpf/CanDrive/CanFD/DBCParserByFD.cs
@@ -5,9 +5,13 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
-namespace CapMachine.Wpf.CanDrive
+namespace CapMachine.Wpf.CanDrive.CanFD
{
- public class CANFD_DBCParser
+ ///
+ /// 和DBCParser一样,但用于处理USB设备的DBC文件解析。
+ /// DBCParserByFD类用于处理USB设备的DBC文件解析。
+ ///
+ public class DBCParserByFD
{
public const Int32 DBC_PARSER_OK = 0;//没有错误
public const Int32 DBC_PARSER_FILE_OPEN = (-1);//打开文件出错
diff --git a/CapMachine.Wpf/CanDrive/ToomossCanFD.cs b/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs
similarity index 53%
rename from CapMachine.Wpf/CanDrive/ToomossCanFD.cs
rename to CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs
index a84245b..8abde2d 100644
--- a/CapMachine.Wpf/CanDrive/ToomossCanFD.cs
+++ b/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs
@@ -1,5 +1,8 @@
-using CapMachine.Wpf.Models.Tag;
+using CapMachine.Wpf.CanDrive.CanFD;
+using CapMachine.Wpf.Models.Tag;
+using CapMachine.Wpf.Services;
using NPOI.OpenXmlFormats.Wordprocessing;
+using Prism.Ioc;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
@@ -16,18 +19,29 @@ using System.Windows.Interop;
namespace CapMachine.Wpf.CanDrive
{
///
- /// Toomoss CAN
+ /// Toomoss CANFD
///
public class ToomossCanFD : BindableBase
{
+ private readonly IContainerProvider ContainerProvider;
+
///
/// 实例化函数
///
- public ToomossCanFD()
+ public ToomossCanFD(IContainerProvider containerProvider)
{
+ ContainerProvider = containerProvider;
+ HighSpeedDataService = ContainerProvider.Resolve();
+ //Stopwatch.Frequency表示高精度计时器每秒的计数次数(ticks/秒)每毫秒的ticks数 = 每秒的ticks数 ÷ 1000
+ TicksPerMs = Stopwatch.Frequency / 1000.0;
}
+ ///
+ /// HighSpeedDataService 实例
+ ///
+ public HighSpeedDataService HighSpeedDataService { get; set; }
+
///
/// 开始CAN的驱动
///
@@ -45,15 +59,16 @@ namespace CapMachine.Wpf.CanDrive
///
/// 开始Dbc文件写入
///
- public void StartDbc(string DbcPath)
+ public ObservableCollection StartDbc(string DbcPath)
{
DBC_Parser(DbcPath);
+ return ListCanFdDbcModel;
}
///
/// Dbc消息集合
///
- public ObservableCollection ListCanDbcModel { get; set; } = new ObservableCollection();
+ public ObservableCollection ListCanFdDbcModel { get; set; } = new ObservableCollection();
#region 公共变量
@@ -61,7 +76,7 @@ namespace CapMachine.Wpf.CanDrive
///
/// 设备固件信息
///
- public USB_DEVICE.DEVICE_INFO DevInfo = new USB_DEVICE.DEVICE_INFO();
+ public USB_DEVICEByFD.DEVICE_INFO DevInfo = new USB_DEVICEByFD.DEVICE_INFO();
///
/// CAN Config
@@ -145,7 +160,7 @@ namespace CapMachine.Wpf.CanDrive
///
public bool ScanDevice()
{
- DevNum = USB_DEVICE.USB_ScanDevice(DevHandles);
+ DevNum = USB_DEVICEByFD.USB_ScanDevice(DevHandles);
if (DevNum <= 0)
{
Console.WriteLine("No device connected!");
@@ -168,7 +183,7 @@ namespace CapMachine.Wpf.CanDrive
public bool OpenDevice()
{
//打开设备
- OpenState = USB_DEVICE.USB_OpenDevice(DevHandle);
+ OpenState = USB_DEVICEByFD.USB_OpenDevice(DevHandle);
if (!OpenState)
{
Console.WriteLine("Open device error!");
@@ -190,7 +205,7 @@ namespace CapMachine.Wpf.CanDrive
{
//获取固件信息
StringBuilder FuncStr = new StringBuilder(256);
- OpenState = USB_DEVICE.DEV_GetDeviceInfo(DevHandle, ref DevInfo, FuncStr);
+ OpenState = USB_DEVICEByFD.DEV_GetDeviceInfo(DevHandle, ref DevInfo, FuncStr);
if (!OpenState)
{
Console.WriteLine("Get device infomation error!");
@@ -206,7 +221,7 @@ namespace CapMachine.Wpf.CanDrive
Console.WriteLine(" Functions:" + DevInfo.Functions.ToString("X8"));
Console.WriteLine(" Functions String:" + FuncStr);
StringBuilder DLLBuildDate = new StringBuilder(256);
- USB_DEVICE.DEV_GetDllBuildTime(DLLBuildDate);
+ USB_DEVICEByFD.DEV_GetDllBuildTime(DLLBuildDate);
Console.WriteLine(" DLL Build Date:" + DLLBuildDate);
return true;
@@ -253,7 +268,7 @@ namespace CapMachine.Wpf.CanDrive
}
ret = USB2CANFD.CANFD_Init(DevHandle, ReadCANIndex, ref CANConfig);
- if (ret != USB2CAN.CAN_SUCCESS)
+ if (ret != USB2CANFD.CANFD_SUCCESS)
{
Console.WriteLine("Config CAN failed!");
return;
@@ -272,7 +287,7 @@ namespace CapMachine.Wpf.CanDrive
public void DBC_Parser(string Path)
{
//解析DBC文件
- DBCHandle = CANFD_DBCParser.DBC_ParserFile(DevHandle, new StringBuilder(Path));
+ DBCHandle = DBCParserByFD.DBC_ParserFile(DevHandle, new StringBuilder(Path));
if (DBCHandle == 0)
{
Console.WriteLine("Parser DBC File error!");
@@ -283,27 +298,27 @@ namespace CapMachine.Wpf.CanDrive
Console.WriteLine("Parser DBC File success!");
}
- ListCanDbcModel.Clear();
+ ListCanFdDbcModel.Clear();
//打印DBC里面报文和信号相关信息
- int DBCMsgNum = CANFD_DBCParser.DBC_GetMsgQuantity(DBCHandle);
+ int DBCMsgNum = DBCParserByFD.DBC_GetMsgQuantity(DBCHandle);
for (int i = 0; i < DBCMsgNum; i++)
{
StringBuilder MsgName = new StringBuilder(32);
- CANFD_DBCParser.DBC_GetMsgName(DBCHandle, i, MsgName);
+ DBCParserByFD.DBC_GetMsgName(DBCHandle, i, MsgName);
Console.WriteLine("Msg.Name = {0}", MsgName);
- int DBCSigNum = CANFD_DBCParser.DBC_GetMsgSignalQuantity(DBCHandle, MsgName);
+ int DBCSigNum = DBCParserByFD.DBC_GetMsgSignalQuantity(DBCHandle, MsgName);
StringBuilder Publisher = new StringBuilder(32);
- CANFD_DBCParser.DBC_GetMsgPublisher(DBCHandle, MsgName, Publisher);
+ DBCParserByFD.DBC_GetMsgPublisher(DBCHandle, MsgName, Publisher);
Console.Write("Signals:");
for (int j = 0; j < DBCSigNum; j++)
{
StringBuilder SigName = new StringBuilder(32);
- CANFD_DBCParser.DBC_GetMsgSignalName(DBCHandle, MsgName, j, SigName);
+ DBCParserByFD.DBC_GetMsgSignalName(DBCHandle, MsgName, j, SigName);
Console.Write("{0} ", SigName);
//增加信息数据
- ListCanDbcModel.Add(new CanDbcModel()
+ ListCanFdDbcModel.Add(new CanDbcModel()
{
MsgName = MsgName.ToString(),
MsgId = "",
@@ -338,9 +353,9 @@ namespace CapMachine.Wpf.CanDrive
{
foreach (var itemSignal in itemMsg)
{
- CANFD_DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
+ DBCParserByFD.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
- CANFD_DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPt);
+ DBCParserByFD.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPt);
CanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
Index++;
}
@@ -379,16 +394,262 @@ namespace CapMachine.Wpf.CanDrive
}
}
+
+ #region 精确发送报文数据
+
+ // 添加取消标记源字段用于停止任务
+ private CancellationTokenSource CycleSendCts;
+
+ ///
+ /// 计算每毫秒对应的ticks数(只需计算一次)
+ ///
+ private double TicksPerMs;
+
+ // 类成员变量定义 精确记时用
+ private readonly Stopwatch Stopwatcher = new Stopwatch();
+ private long NextExecutionTime;
+ // 计算需要等待的时间
+ private long CurrentTime;
+ private long DelayTicks;
+ private int DelayMs;
+
+ private static readonly Random _random = new Random();
+
+ ///
+ /// 精确周期发送CAN数据
+ ///
+ public void StartPrecisionCycleSendMsg()
+ {
+ // 创建取消标记源 用于控制任务的取消 允许在需要时通过取消令牌来优雅停止任务
+ var cancellationTokenSource = new CancellationTokenSource();
+ var token = cancellationTokenSource.Token;
+
+ // 保存取消标记,以便在停止时使用
+ CycleSendCts = cancellationTokenSource;//将取消标记源保存到类的成员变量CycleSendCts,这样在外部调用停止方法时可以访问它
+ NextExecutionTime = 0;//初始化NextExecutionTime为0,这个变量用于记录下一次执行的目标时间点
+
+ CycleSendTask = Task.Factory.StartNew(async () =>
+ {
+ try
+ {
+ // 设置当前线程为高优先级
+ Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
+
+ // 初始化完成后开始计时
+ Stopwatcher.Restart();
+ // 预先计算固定值
+ long CycleInTicks = (long)(SendCycle * TicksPerMs);
+ //临时测试用
+ //long lastTicks = Stopwatcher.ElapsedTicks;
+ //IsCycleSend
+ while (IsCycleSend && !token.IsCancellationRequested)
+ {
+ try
+ {
+ // 计算下一次执行时间点 ,将当前设置的发送周期SendCycle(毫秒)转换为Stopwatch的计时单位(tick),累加到NextExecutionTime上
+ NextExecutionTime += CycleInTicks; // 转换为Stopwatch计时单位
+
+ // 获取当前时间点,以Stopwatch的tick为单位
+ CurrentTime = Stopwatcher.ElapsedTicks;
+ //计算需要等待的时间,即目标时间点(NextExecutionTime)与当前时间点(CurrentTime)的差值
+ DelayTicks = NextExecutionTime - CurrentTime;
+
+ // 如果还有等待时间,则等待,只有在目标时间点还未到达时才执行等待
+ if (DelayTicks > 0)
+ {
+ ////此时是需要等待的,那么需要等待多久呢, 将需等待的tick数转换回毫秒
+ DelayMs = (int)(DelayTicks / TicksPerMs);
+ //20这个数据是预估和测试的,可能跟Windows抖动误差就是20ms左右,当然可以不用这个IF()判断,直接SpinWait.SpinUntil(() => Stopwatcher.ElapsedTicks >= NextExecutionTime);但是会导致当前独占一个CPU核心线程
+ //所以设置一个20的阈值,20ms以下的延迟使用SpinWait.SpinUntil进行自旋等待,20ms以上的延迟使用Task.Delay进行异步等待,让CPU不至于一直的独占
+ if (DelayMs <= 20)
+ {
+ SpinWait.SpinUntil(() => Stopwatcher.ElapsedTicks >= NextExecutionTime);
+ }
+ else
+ {
+ ////使用Task.Delay进行异步等待,大部分等待时间通过这种方式完成,避免线程阻塞
+ await Task.Delay(DelayMs - 20, token);
+ //// 使用SpinWait.SpinUntil进行精确的微调等待。自旋等待会占用CPU资源,但能提供更高的定时精度,确保在精确的时间点执行
+ ////上面的Task.Delay可能会因为系统调度等原因导致实际执行时间稍晚于预期,因此在这里使用SpinWait.SpinUntil来确保在精确的时间点执行
+ SpinWait.SpinUntil(() => Stopwatcher.ElapsedTicks >= NextExecutionTime);
+ }
+ }
+
+ // 如果已经超过了计划时间,立即执行并重新校准
+ if (Stopwatcher.ElapsedTicks >= NextExecutionTime + CycleInTicks)
+ {
+ //检测是否发生了严重延迟(超过一个周期)。如果当前时间已经超过了下一次计划时间,则说明系统负载过高或其他原因导致无法按时执行,
+ //此时重置NextExecutionTime为当前时间,避免连续的延迟累积
+ // 严重延迟,重新校准
+ NextExecutionTime = Stopwatcher.ElapsedTicks;
+ Console.WriteLine("定时发送延迟过大,重新校准时间");
+ }
+
+ // 使用Stopwatch记录实际的执行间隔,而不是DateTime
+ //Console.WriteLine($"--实际间隔(ms): {(Stopwatcher.ElapsedTicks - lastTicks) / TicksPerMs:F3}, 目标: {SendCycle}");
+ //lastTicks = Stopwatcher.ElapsedTicks;
+
+ //Console.WriteLine($"--当前时间(毫秒): {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
+
+
+ // 执行发送CAN逻辑
+ {
+
+ var GroupMsg = CmdData.GroupBy(x => x.MsgName);
+ USB2CANFD.CANFD_MSG[] CanMsg = new USB2CANFD.CANFD_MSG[GroupMsg.Count()];
+ for (int i = 0; i < GroupMsg.Count(); i++)
+ {
+ CanMsg[i] = new USB2CANFD.CANFD_MSG();
+ CanMsg[i].Data = new Byte[64];
+ }
+
+ 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_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPtSend);
+ CanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CANFD.CANFD_MSG));
+ Index++;
+ }
+
+ //通过DBC写入数据后生成CanMsg
+ //将信号值填入CAN消息里面
+
+ //释放申请的临时缓冲区
+ Marshal.FreeHGlobal(msgPtSend);
+
+ //发送CAN数据
+ int SendedNum = USB2CANFD.CANFD_SendMsg(DevHandle, WriteCANIndex, CanMsg, (Int32)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)
+ {
+ // 任务被取消,正常退出
+ break;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"CAN周期发送异常: {ex.Message}");
+ // 短暂暂停避免异常情况下CPU占用过高
+ await Task.Delay(10, token);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ // 确保在任何情况下(正常退出、异常、取消)都会停止计时器
+ Stopwatcher.Stop();
+
+ // 清理其他可能的资源
+ Console.WriteLine("CAN周期发送任务已结束,资源已清理");
+ }
+ finally
+ {
+ // 确保在任何情况下(正常退出、异常、取消)都会停止计时器
+ Stopwatcher.Stop();
+ }
+
+ }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+ }
+
+
+ ///
+ /// 修改停止发送的方法
+ ///
+ public void StopCycleSendMsg()
+ {
+ IsCycleSend = false;
+ CycleSendCts?.Cancel();
+ }
+
+ #endregion
+
+
+ private bool _IsCycleRevice;
///
/// 是否循环接收数据
///
- public bool IsCycleRevice { get; set; }
+ public bool IsCycleRevice
+ {
+ get { return _IsCycleRevice; }
+ set { _IsCycleRevice = value; RaisePropertyChanged(); }
+ }
+
+
+ private bool _IsCycleSend;
+ ///
+ /// 是否循环发送数据
+ ///
+ public bool IsCycleSend
+ {
+ get { return _IsCycleSend; }
+ set { _IsCycleSend = value; RaisePropertyChanged(); }
+ }
///
- /// CycleRevice扫描Task
+ /// 循环发送数据
+ ///
+ public ushort SendCycle { get; set; } = 100;
+
+ ///
+ /// 循环接受数据
+ ///
+ public ushort ReviceCycle { get; set; } = 500;
+
+
+ ///
+ /// CycleRevice 扫描Task
///
private static Task CycleReviceTask { get; set; }
+ ///
+ /// CycleSend 扫描Task
+ ///
+ private static Task CycleSendTask { get; set; }
+
+ StringBuilder ValueSb = new StringBuilder(16);
+ double[] ValueDouble = new double[5];
+
+ private bool _IsSendOk;
+ ///
+ /// 发送报文是否OK
+ ///
+ public bool IsSendOk
+ {
+ get { return _IsSendOk; }
+ set
+ {
+ if (_IsSendOk != value)
+ {
+ RaisePropertyChanged();
+ _IsSendOk = value;
+ }
+ }
+ }
+
+ ///
+ /// 要发送的数据
+ ///
+ public List CmdData { get; set; } = new List();
+
///
/// 循环获取CAN消息
///
@@ -432,12 +693,12 @@ namespace CapMachine.Wpf.CanDrive
Console.WriteLine("");
//将CAN消息数据填充到信号里面
- CANFD_DBCParser.DBC_SyncCANFDMsgToValue(DBCHandle, msgPt, CanNum);
+ DBCParserByFD.DBC_SyncCANFDMsgToValue(DBCHandle, msgPt, CanNum);
//获取信号值并打印出来
StringBuilder ValueStr = new StringBuilder(32);
- CANFD_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("TX1"), new StringBuilder("COM_current_Power"), ValueStr);
+ DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("TX1"), new StringBuilder("COM_current_Power"), ValueStr);
Console.WriteLine("COM_current_Power = {0}", ValueStr);
- CANFD_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("TX1"), new StringBuilder("COM_Curr_dc"), ValueStr);
+ DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("TX1"), new StringBuilder("COM_Curr_dc"), ValueStr);
Console.WriteLine("COM_Curr_dc = {0}", ValueStr);
}
catch (Exception ex)
@@ -484,14 +745,14 @@ namespace CapMachine.Wpf.CanDrive
Console.WriteLine("");
//将CAN消息数据填充到信号里面
- CANFD_DBCParser.DBC_SyncCANFDMsgToValue(DBCHandle, msgPt, CanNum);
+ DBCParserByFD.DBC_SyncCANFDMsgToValue(DBCHandle, msgPt, CanNum);
//获取信号值并打印出来
StringBuilder ValueStr = new StringBuilder(32);
- CANFD_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_moto_speed"), new StringBuilder("moto_speed"), ValueStr);
+ DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_moto_speed"), new StringBuilder("moto_speed"), ValueStr);
Console.WriteLine("moto_speed = {0}", ValueStr);
- CANFD_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_oil_pressure"), new StringBuilder("oil_pressure"), ValueStr);
+ DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_oil_pressure"), new StringBuilder("oil_pressure"), ValueStr);
Console.WriteLine("oil_pressure = {0}", ValueStr);
- CANFD_DBCParser.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_speed_can"), new StringBuilder("speed_can"), ValueStr);
+ DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_speed_can"), new StringBuilder("speed_can"), ValueStr);
Console.WriteLine("speed_can = {0}", ValueStr);
}
@@ -503,7 +764,7 @@ namespace CapMachine.Wpf.CanDrive
public void CloseDevice()
{
//关闭设备
- USB_DEVICE.USB_CloseDevice(DevHandle);
+ USB_DEVICEByFD.USB_CloseDevice(DevHandle);
OpenState = false;
}
diff --git a/CapMachine.Wpf/CanDrive/USB2CANFD.cs b/CapMachine.Wpf/CanDrive/CanFD/USB2CANFD.cs
similarity index 100%
rename from CapMachine.Wpf/CanDrive/USB2CANFD.cs
rename to CapMachine.Wpf/CanDrive/CanFD/USB2CANFD.cs
diff --git a/CapMachine.Wpf/CanDrive/CanFD/USB_DEVICEByFD.cs b/CapMachine.Wpf/CanDrive/CanFD/USB_DEVICEByFD.cs
new file mode 100644
index 0000000..66b11c1
--- /dev/null
+++ b/CapMachine.Wpf/CanDrive/CanFD/USB_DEVICEByFD.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.CanDrive.CanFD
+{
+ ///
+ /// 和USB_DEVICE一样,但用于处理USB设备的CAN FD协议。
+ ///
+ public class USB_DEVICEByFD
+ {
+ //定义电压输出值
+ public const Byte POWER_LEVEL_NONE = 0; //不输出
+ public const Byte POWER_LEVEL_1V8 = 1; //输出1.8V
+ public const Byte POWER_LEVEL_2V5 = 2; //输出2.5V
+ public const Byte POWER_LEVEL_3V3 = 3; //输出3.3V
+ public const Byte POWER_LEVEL_5V0 = 4; //输出5.0V
+ //设备信息定义
+ public struct DEVICE_INFO
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+ public Byte[] FirmwareName; //固件名称字符串
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+ public Byte[] BuildDate; //固件编译时间字符串
+ public UInt32 HardwareVersion;//硬件版本号
+ public UInt32 FirmwareVersion;//固件版本号
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public UInt32[] SerialNumber; //适配器序列号
+ public UInt32 Functions; //适配器当前具备的功能
+ }
+ //方法定义
+ /**
+ * @brief 初始化USB设备,并扫描设备连接数,必须调用
+ * @param pDevHandle 每个设备的设备号存储地址
+ * @retval 扫描到的设备数量
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 USB_ScanDevice(Int32[] pDevHandle);
+ /**
+ * @brief 打开设备,必须调用
+ * @param DevHandle 设备索引号
+ * @retval 打开设备的状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool USB_OpenDevice(Int32 DevHandle);
+ /**
+ * @brief 关闭设备
+ * @param DevHandle 设备索引号
+ * @retval 关闭设备的状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool USB_CloseDevice(Int32 DevHandle);
+ /**
+ * @brief 复位设备程序,复位后需要重新调用USB_ScanDevice,USB_OpenDevice函数
+ * @param DevHandle 设备索引号
+ * @retval 复位设备的状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool USB_ResetDevice(Int32 DevHandle);
+
+ /**
+ * @brief 检测到USB断开连接后,重新连接设备
+ * @param DevHandle 设备号
+ * @retval 重连状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool USB_RetryConnect(Int32 DevHandle);
+
+ /**
+ * @brief 获取设备信息,比如设备名称,固件版本号,设备序号,设备功能说明字符串等
+ * @param DevHandle 设备索引号
+ * @param pDevInfo 设备信息存储结构体指针
+ * @param pFunctionStr 设备功能说明字符串
+ * @retval 获取设备信息的状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool DEV_GetDeviceInfo(Int32 DevHandle, ref DEVICE_INFO pDevInfo, StringBuilder pFunctionStr);
+ /**
+ * @brief 擦出用户区数据
+ * @param DevHandle 设备索引号
+ * @retval 用户区数据擦出状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool DEV_EraseUserData(Int32 DevHandle);
+
+ /**
+ * @brief 向用户区域写入用户自定义数据,写入数据之前需要调用擦出函数将数据擦出
+ * @param DevHandle 设备索引号
+ * @param OffsetAddr 数据写入偏移地址,起始地址为0x00,用户区总容量为0x10000字节,也就是64KBye
+ * @param pWriteData 用户数据缓冲区首地址
+ * @param DataLen 待写入的数据字节数
+ * @retval 写入用户自定义数据状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool DEV_WriteUserData(Int32 DevHandle, Int32 OffsetAddr, byte[] pWriteData, Int32 DataLen);
+
+ /**
+ * @brief 从用户自定义数据区读出数据
+ * @param DevHandle 设备索引号
+ * @param OffsetAddr 数据写入偏移地址,起始地址为0x00,用户区总容量为0x10000字节,也就是64KBye
+ * @param pReadData 用户数据缓冲区首地址
+ * @param DataLen 待读出的数据字节数
+ * @retval 读出用户自定义数据的状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool DEV_ReadUserData(Int32 DevHandle, Int32 OffsetAddr, byte[] pReadData, Int32 DataLen);
+
+ /**
+ * @brief 设置可变电压输出引脚输出电压值
+ * @param DevHandle 设备索引号
+ * @param PowerLevel 输出电压值
+ * @retval 设置输出电压状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool DEV_SetPowerLevel(Int32 DevHandle, byte PowerLevel);
+ /**
+ * @brief 或者CAN或者LIN的时间戳原始值
+ * @param DevHandle 设备索引号
+ * @param pTimestamp 时间戳指针
+ * @retval 获取时间戳状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool DEV_GetTimestamp(Int32 DevHandle, byte BusType, Int32[] pTimestamp);
+
+ /**
+ * @brief 复位CAN/LIN时间戳,需要在初始化CAN/LIN之后调用
+ * @param DevHandle 设备索引号
+ * @retval 复位时间戳状态
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool DEV_ResetTimestamp(Int32 DevHandle);
+ /**
+ * @brief 获取dll编译日期
+ * @param pDateTime 输出DLL编译日期字符串
+ * @retval 获取dll编译日期字符串
+ */
+ [DllImport("USB2XXX.dll")]
+ public static extern bool DEV_GetDllBuildTime(StringBuilder pDateTime);
+ }
+}
diff --git a/CapMachine.Wpf/Services/CanFdDriveService.cs b/CapMachine.Wpf/Services/CanFdDriveService.cs
new file mode 100644
index 0000000..575f04f
--- /dev/null
+++ b/CapMachine.Wpf/Services/CanFdDriveService.cs
@@ -0,0 +1,382 @@
+using CapMachine.Model.CANLIN;
+using CapMachine.Wpf.CanDrive;
+using ImTools;
+using Prism.Ioc;
+using Prism.Mvvm;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.Services
+{
+ ///
+ /// Can FD驱动服务
+ ///
+ public class CanFdDriveService : BindableBase
+ {
+ public HighSpeedDataService HighSpeedDataService { get; }
+
+ ///
+ /// 实例化函数
+ ///
+ public CanFdDriveService(HighSpeedDataService highSpeedDataService, IContainerProvider containerProvider)
+ {
+ ToomossCanFDDrive = new ToomossCanFD(containerProvider);
+ //高速数据服务
+ HighSpeedDataService = highSpeedDataService;
+
+ //ToomossCanFDDrive.StartCanDrive();
+ }
+
+ ///
+ /// 当前选中的CanLinConfigPro 程序
+ ///
+ public CanLinConfigPro SelectedCanLinConfigPro { get; set; }
+
+
+ ///
+ /// 图莫斯 CAN Drive
+ /// ToomossCanFDDrive
+ ///
+ public ToomossCanFD ToomossCanFDDrive { get; set; }
+
+ ///
+ /// Dbc消息集合
+ /// 包括读取的实时值和数据
+ ///
+ public ObservableCollection ListCanDbcModel { get; set; } = new ObservableCollection();
+
+ ///
+ /// 初始化CAN的配置信息
+ ///
+ public void InitCanConfig(CanLinConfigPro selectedCanLinConfigPro)
+ {
+ //赋值配置数据
+ SelectedCanLinConfigPro = selectedCanLinConfigPro;
+ //为DBC实时数据关联配置的名称
+ foreach (var item in SelectedCanLinConfigPro.CanLinConfigContents)
+ {
+ var FindData = ListCanDbcModel.FindFirst(a => a.SignalName == item.SignalName);
+ if (FindData != null)
+ {
+ FindData.Name = item.Name;
+ }
+ }
+ }
+
+ ///
+ /// 开始DBC 配置文件 加载
+ ///
+ ///
+ public ObservableCollection StartDbc(string Path)
+ {
+ ListCanDbcModel = ToomossCanFDDrive.StartDbc(Path);
+ return ListCanDbcModel;
+ }
+
+
+ #region 程序驱动CAN
+
+ ///
+ /// 转速 指令数据 实例
+ ///
+ private CanCmdData SpeedCanCmdData { get; set; }
+
+ ///
+ /// 功率限制 指令数据 实例
+ ///
+ private CanCmdData PwLimitCanCmdData { get; set; }
+
+ ///
+ /// 使能 指令数据 实例
+ ///
+ private CanCmdData EnableCanCmdData { get; set; }
+
+ ///
+ /// PTC使能 指令数据 实例
+ ///
+ private CanCmdData PTCEnableCanCmdData { get; set; }
+
+ ///
+ /// PTC功率 指令数据 实例
+ ///
+ private CanCmdData PTCPwCanCmdData { get; set; }
+
+ ///
+ /// PTC水流量 指令数据 实例
+ ///
+ private CanCmdData PTCFlowCanCmdData { get; set; }
+
+ ///
+ /// PTC水温 指令数据 实例
+ ///
+ private CanCmdData PTCWaterTempCanCmdData { get; set; }
+
+
+ ///
+ /// 要发送的CAN指令数据
+ /// 在程序配置好后就确定要发送哪些数据
+ ///
+ public List CmdData { get; set; } = new List();
+
+ ///
+ /// 增加发送的指令数据
+ ///
+ ///
+ public void AddCmdData(CanCmdData SendCanCmdData)
+ {
+ //提取常用的实例数据
+ switch (SendCanCmdData.ConfigName)
+ {
+ case "转速":
+ SpeedCanCmdData = SendCanCmdData;
+ break;
+ case "功率限制":
+ PwLimitCanCmdData = SendCanCmdData;
+ break;
+ case "使能":
+ EnableCanCmdData = SendCanCmdData;
+ break;
+ case "Anti_Sleep":
+ //SpeedCanCmdData = SendCanCmdData;
+ break;
+ case "PTC使能":
+ PTCEnableCanCmdData = SendCanCmdData;
+ break;
+ case "PTC功率":
+ PTCPwCanCmdData = SendCanCmdData;
+ break;
+ case "PTC水流量":
+ PTCFlowCanCmdData = SendCanCmdData;
+ break;
+ case "PTC水温":
+ PTCWaterTempCanCmdData = SendCanCmdData;
+ break;
+ default:
+ break;
+ }
+ //添加到发送数据集合
+ CmdData.Add(SendCanCmdData);
+ }
+
+
+ ///
+ /// 更新速度信息
+ /// 默认是启动
+ ///
+ ///
+ public void UpdateSpeedCmdData(double SpeedData)
+ {
+ if (SpeedCanCmdData != null)
+ {
+ SpeedCanCmdData.SignalCmdValue = SpeedData;
+ }
+ //if (EnableCanCmdData != null)
+ //{
+ // EnableCanCmdData.SignalCmdValue = 1;
+ //}
+ }
+
+ ///
+ /// 更新压缩机使能数据
+ ///
+ ///
+ public void UpdateCapEnableCmdData(bool IsEnable)
+ {
+ if (EnableCanCmdData != null)
+ {
+ EnableCanCmdData.SignalCmdValue = IsEnable ? 1 : 0;
+ }
+ }
+
+
+ ///
+ /// 更新压缩机的功率限制
+ ///
+ ///
+ public void UpdateCapPwLimitCmdData(double PwLimit)
+ {
+ if (PwLimitCanCmdData != null)
+ {
+ PwLimitCanCmdData.SignalCmdValue = PwLimit;
+ }
+ }
+
+
+ ///
+ /// 更新 PTC使能信号
+ ///
+ ///
+ public void UpdateCapPTCEnableCmdData(bool IsEnable)
+ {
+ if (PTCEnableCanCmdData != null)
+ {
+ PTCEnableCanCmdData.SignalCmdValue = IsEnable ? 1 : 0;
+ }
+ }
+
+ ///
+ /// 更新 PTC功率 信号
+ ///
+ ///
+ public void UpdateCapPTCPwCmdData(double PTCPw)
+ {
+ if (PTCPwCanCmdData != null)
+ {
+ PTCPwCanCmdData.SignalCmdValue = PTCPw;
+ }
+ }
+
+ ///
+ /// 更新 PTC水流量 信号
+ ///
+ ///
+ public void UpdateCapPTCFlowCmdData(double Flow)
+ {
+ if (PTCFlowCanCmdData != null)
+ {
+ PTCFlowCanCmdData.SignalCmdValue = Flow;
+ }
+ }
+
+ ///
+ /// 更新 PTC水温 信号
+ ///
+ ///
+ public void UpdateCapPTCWaterTempCmdData(double WaterTemp)
+ {
+ if (PTCWaterTempCanCmdData != null)
+ {
+ PTCWaterTempCanCmdData.SignalCmdValue = WaterTemp;
+ }
+ }
+
+ ///
+ /// 发送消息给CAN 驱动
+ ///
+ public void SendMsgToCanDrive(double SpeedData)
+ {
+ if (ToomossCanFDDrive.OpenState)
+ {
+ if (CmdData.Count > 0)
+ {
+ //更新速度信息
+ UpdateSpeedCmdData(SpeedData);
+
+ ToomossCanFDDrive.SendCanMsg(CmdData);
+ }
+ else
+ {
+ System.Windows.MessageBox.Show("未发现配置的数据内容", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
+ }
+ }
+ else
+ {
+ System.Windows.MessageBox.Show("未打开CAN通信,无法发送数据", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
+ }
+ }
+
+
+ ///
+ /// 循环发送数据到CAN
+ ///
+ public void CycleSendMsg()
+ {
+ if (ToomossCanFDDrive.OpenState)
+ {
+ if (ToomossCanFDDrive.IsCycleSend == false)
+ {
+ if (CmdData.Count > 0)
+ {
+ ToomossCanFDDrive.IsCycleSend = true;
+ ToomossCanFDDrive.CmdData = CmdData;
+ //ToomossCanFDDrive.StartCycleSendMsg();
+ ToomossCanFDDrive.StartPrecisionCycleSendMsg();
+ }
+ else
+ {
+ System.Windows.MessageBox.Show("未发现配置的数据内容", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
+ }
+ }
+ else
+ {
+ ToomossCanFDDrive.IsCycleSend = false;
+ }
+
+ }
+ }
+
+
+
+ ///
+ ///循环接收数据
+ ///
+ public void CycleReciveMsg()
+ {
+ if (ToomossCanFDDrive.OpenState)
+ {
+ if (ToomossCanFDDrive.IsCycleRevice == false)
+ {
+ if (ListCanDbcModel.Count > 0)
+ {
+ ToomossCanFDDrive.IsCycleRevice = true;
+ ToomossCanFDDrive.StartCycleReviceCanMsg();
+ }
+ else
+ {
+ System.Windows.MessageBox.Show("未发现配置的数据内容", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
+ }
+ }
+ else
+ {
+ ToomossCanFDDrive.IsCycleRevice = false;
+ }
+ }
+ }
+
+
+ ///
+ /// 获取数据值
+ /// 从DBC中获取数据给数据中心集合
+ ///
+ ///
+ ///
+ public double GetDbcValueByName(string Name)
+ {
+ if (!ToomossCanFDDrive.IsCycleRevice) return 0;
+
+ if (ListCanDbcModel.Any(a => a.Name == Name))
+ {
+ //double.TryParse(ListCanDbcModel.FindFirst(a => a.Name == Name).SignalRtValue, out double Result1);
+ return double.TryParse(ListCanDbcModel.FindFirst(a => a.Name == Name).SignalRtValue.Split(" ")[0], out double Result) == true ? Result : 0;
+ }
+ return 0;
+ }
+
+ ///
+ /// 速度的数据的获取
+ /// 获取速度数据值
+ /// 从DBC中获取Speed数据给数据中心集合
+ ///
+ ///
+ ///
+ public double GetDbcSpeedValueBySpeedName(string Name)
+ {
+ if (!ToomossCanFDDrive.IsCycleRevice) return 0;
+
+ if (ListCanDbcModel.Any(a => a.Name == Name))
+ {
+ //double.TryParse(ListCanDbcModel.FindFirst(a => a.Name == Name).SignalRtValue, out double Result1);
+ //return double.TryParse(ListCanDbcModel.FindFirst(a => a.Name == Name).SignalRtValue.Split(" ")[0], out double Result) == true ? Result : 0;
+ return double.TryParse(ListCanDbcModel.FindFirst(a => a.Name == Name).SignalRtValue.Split(" ")[0], out double Result) == true ? Result : 0;
+ }
+ return 0;
+ }
+
+ #endregion
+
+ }
+}