diff --git a/CapMachine.Model/CANLIN/CANConfigExd.cs b/CapMachine.Model/CANLIN/CANConfigExd.cs
index 7213492..d44149b 100644
--- a/CapMachine.Model/CANLIN/CANConfigExd.cs
+++ b/CapMachine.Model/CANLIN/CANConfigExd.cs
@@ -38,6 +38,11 @@ namespace CapMachine.Model.CANLIN
[Column(Name = "DbcPath", IsNullable = false, StringLength = 500)]
public string? DbcPath { get; set; }
+ ///
+ /// 调度表是否启用
+ ///
+ [Column(Name = "SchEnable")]
+ public bool SchEnable { get; set; }
/////
diff --git a/CapMachine.Model/CANLIN/CANFdConfigExd.cs b/CapMachine.Model/CANLIN/CANFdConfigExd.cs
new file mode 100644
index 0000000..4425e27
--- /dev/null
+++ b/CapMachine.Model/CANLIN/CANFdConfigExd.cs
@@ -0,0 +1,53 @@
+using FreeSql.DataAnnotations;
+
+namespace CapMachine.Model.CANLIN
+{
+ ///
+ /// CAN和LIN的配置信息数据
+ ///
+ [Table(Name = "CANFdConfigExd")]
+ public class CANFdConfigExd
+ {
+ ///
+ /// 主键
+ ///
+ [Column(IsPrimary = true, IsIdentity = true)]
+ public long Id { get; set; }
+
+ ///
+ /// 数据波特率
+ ///
+ [Column(Name = "DataBaudRate")]
+ public int DataBaudRate { get; set; }
+
+ ///
+ /// 仲裁波特率
+ ///
+ [Column(Name = "ArbBaudRate")]
+ public int ArbBaudRate { get; set; }
+
+ ///
+ /// CAN FD标准 ISO是否启用
+ ///
+ [Column(Name = "ISOEnable")]
+ public bool ISOEnable { get; set; }
+
+ ///
+ /// 终端电阻 是否启用
+ ///
+ [Column(Name = "ResEnable")]
+ public bool ResEnable { get; set; }
+
+ ///
+ /// 周期
+ ///
+ [Column(Name = "Cycle")]
+ public int Cycle { get; set; }
+
+ ///
+ /// Dbc文件路径
+ ///
+ [Column(Name = "DbcPath", IsNullable = false, StringLength = 500)]
+ public string? DbcPath { get; set; }
+ }
+}
diff --git a/CapMachine.Model/CANLIN/CANScheduleConfig.cs b/CapMachine.Model/CANLIN/CANScheduleConfig.cs
new file mode 100644
index 0000000..5ea3d66
--- /dev/null
+++ b/CapMachine.Model/CANLIN/CANScheduleConfig.cs
@@ -0,0 +1,55 @@
+using FreeSql.DataAnnotations;
+
+namespace CapMachine.Model.CANLIN
+{
+ ///
+ /// 调度表的配置
+ /// 其实这些调度表是在DBC中有的,但是图莫斯的驱动没有读取到这些信息
+ /// 那么我们在系统层面进行操作和保存这些信息
+ ///
+ [Table(Name = "CANScheduleConfig")]
+ public class CANScheduleConfig
+ {
+ ///
+ /// 主键
+ ///
+ [Column(IsPrimary = true, IsIdentity = true)]
+ public long Id { get; set; }
+
+ ///
+ /// 消息名称
+ ///
+ [Column(Name = "MsgName")]
+ public string? MsgName { get; set; }
+
+ ///
+ /// 消息的周期
+ ///
+ [Column(Name = "Cycle")]
+ public int Cycle { get; set; }
+
+ ///
+ /// 发送方式
+ ///
+ [Column(Name = "OrderSend")]
+ public int OrderSend { get; set; }
+
+ ///
+ /// 调度表的Index
+ /// //约定每帧对应一个调度表,预设5个调度表,每个调度表对应一个帧
+ /// 0-4这个范围的设置Index
+ ///
+ [Column(Name = "SchTabIndex")]
+ public int SchTabIndex { get; set; }
+
+
+
+
+ ///
+ /// ///////////////////////////////////////////导航属性///////////////////////////////////////////////////////
+ ///
+
+ public long CanLinConfigProId { get; set; }
+ public CanLinConfigPro? CanLinConfigPro { get; set; }
+ }
+}
diff --git a/CapMachine.Model/CANLIN/CanLinConfigPro.cs b/CapMachine.Model/CANLIN/CanLinConfigPro.cs
index 249ecb8..48f1f48 100644
--- a/CapMachine.Model/CANLIN/CanLinConfigPro.cs
+++ b/CapMachine.Model/CANLIN/CanLinConfigPro.cs
@@ -44,6 +44,25 @@ namespace CapMachine.Model.CANLIN
public List? CanLinConfigContents { get; set; }
+ ///
+ /// ///////////////////////////////////////////导航属性///////////////////////////////////////////////////////
+ ///
+ ///CAN 的调度表配置模式
+ public List? CanScheduleConfigs { get; set; }
+
+ ///
+ /// ///////////////////////////////////////////导航属性///////////////////////////////////////////////////////
+ ///
+ ///LIN 的调度表配置模式
+ public List? LinScheduleConfigs { get; set; }
+
+
+ ///
+ /// ///////////////////////////////////////////导航属性 LIN 一对一///////////////////////////////////////////////////////
+ ///
+ public long CANFdConfigExdId { get; set; } // 外键字段,必要
+ public CANFdConfigExd CANFdConfigExd { get; set; }
+
///
/// ///////////////////////////////////////////导航属性 CAN 一对一///////////////////////////////////////////////////////
@@ -52,12 +71,12 @@ namespace CapMachine.Model.CANLIN
public CANConfigExd CANConfigExd { get; set; }
-
-
///
/// ///////////////////////////////////////////导航属性 LIN 一对一///////////////////////////////////////////////////////
///
public long LINConfigExdId { get; set; } // 外键字段,必要
public LINConfigExd LINConfigExd { get; set; }
+
+
}
}
diff --git a/CapMachine.Model/CANLIN/CanLinEmun.cs b/CapMachine.Model/CANLIN/CanLinEmun.cs
index 42802ed..1a4c264 100644
--- a/CapMachine.Model/CANLIN/CanLinEmun.cs
+++ b/CapMachine.Model/CANLIN/CanLinEmun.cs
@@ -15,10 +15,16 @@ namespace CapMachine.Model.CANLIN
/// CAN
///
CAN = 1,
+
///
/// LIN
///
LIN = 2,
+
+ ///
+ /// CANFD
+ ///
+ CANFD = 3,
}
///
diff --git a/CapMachine.Model/CANLIN/CanLinRWConfig.cs b/CapMachine.Model/CANLIN/CanLinRWConfig.cs
index ca6e539..148c576 100644
--- a/CapMachine.Model/CANLIN/CanLinRWConfig.cs
+++ b/CapMachine.Model/CANLIN/CanLinRWConfig.cs
@@ -61,5 +61,13 @@ namespace CapMachine.Model.CANLIN
public long CanLinConfigProId { get; set; }
public CanLinConfigPro? CanLinConfigPro { get; set; }
+
+
+ public long LogicRuleId { get; set; }
+ ///
+ /// CanLinConfig的逻辑转换规则
+ /// 比如:速度下发的数据SV是4000,但是下发到CAN的值是40,可能是其他的逻辑转换规则,这里就是保存其中的逻辑规则
+ ///
+ public LogicRule? LogicRule { get; set; }
}
}
diff --git a/CapMachine.Model/CANLIN/LINScheduleConfig.cs b/CapMachine.Model/CANLIN/LINScheduleConfig.cs
new file mode 100644
index 0000000..496ea73
--- /dev/null
+++ b/CapMachine.Model/CANLIN/LINScheduleConfig.cs
@@ -0,0 +1,53 @@
+using FreeSql.DataAnnotations;
+
+namespace CapMachine.Model.CANLIN
+{
+ ///
+ /// 调度表的配置
+ /// 其实这些调度表是在DBC中有的,但是图莫斯的驱动没有读取到这些信息
+ /// 那么我们在系统层面进行操作和保存这些信息
+ ///
+ [Table(Name = "LINScheduleConfig")]
+ public class LINScheduleConfig
+ {
+ ///
+ /// 主键
+ ///
+ [Column(IsPrimary = true, IsIdentity = true)]
+ public long Id { get; set; }
+
+ ///
+ /// 消息名称
+ ///
+ [Column(Name = "MsgName")]
+ public string? MsgName { get; set; }
+
+ ///
+ /// 消息的周期
+ ///
+ [Column(Name = "Cycle")]
+ public int Cycle { get; set; }
+
+ ///
+ /// 调度表的Index
+ /// LDF中可能有多个调度器
+ ///
+ [Column(Name = "SchTabIndex")]
+ public int SchTabIndex { get; set; }
+
+ ///
+ /// 调度表的名称
+ /// LDF中可能有多个调度器名称
+ ///
+ [Column(Name = "SchTabName")]
+ public int SchTabName { get; set; }
+
+
+ ///
+ /// ///////////////////////////////////////////导航属性///////////////////////////////////////////////////////
+ ///
+
+ public long CanLinConfigProId { get; set; }
+ public CanLinConfigPro? CanLinConfigPro { get; set; }
+ }
+}
diff --git a/CapMachine.Model/LogicRule.cs b/CapMachine.Model/LogicRule.cs
new file mode 100644
index 0000000..18675e1
--- /dev/null
+++ b/CapMachine.Model/LogicRule.cs
@@ -0,0 +1,52 @@
+using CapMachine.Model.CANLIN;
+using FreeSql.DataAnnotations;
+
+namespace CapMachine.Model
+{
+ ///
+ /// 逻辑转换规则模型
+ ///
+ [Table(Name = "LogicRule")]
+ public class LogicRule
+ {
+ ///
+ /// 主键ID
+ ///
+ [Column(IsPrimary = true, IsIdentity = true)]
+ public long Id { get; set; }
+
+ ///
+ /// 规则名称
+ ///
+ [Column(Name = "Name", StringLength = 50)]
+ public string? Name { get; set; }
+
+ ///
+ /// 规则描述
+ ///
+ [Column(Name = "Description", StringLength = 200)]
+ public string? Description { get; set; }
+
+ ///
+ /// 规则表达式
+ ///
+ [Column(Name = "Expression", StringLength = 500)]
+ public string? Expression { get; set; }
+
+ ///
+ /// 适用的参数类型(如:转速、功率等)
+ ///
+ [Column(Name = "ParameterType", StringLength = 50)]
+ public string? ParameterType { get; set; }
+
+ [Column(ServerTime = DateTimeKind.Local, CanUpdate = false)]
+ public DateTime CreateTime { get; set; }
+
+ ///
+ /// ///////////////////////////////////////////导航属性///////////////////////////////////////////////////////
+ ///
+
+ public List? CanLinRWConfigs { get; set; }
+
+ }
+}
diff --git a/CapMachine.Wpf/App.xaml.cs b/CapMachine.Wpf/App.xaml.cs
index b55b988..22dc887 100644
--- a/CapMachine.Wpf/App.xaml.cs
+++ b/CapMachine.Wpf/App.xaml.cs
@@ -104,6 +104,7 @@ namespace CapMachine.Wpf
////注册设备服务
//containerRegistry.RegisterSingleton();
containerRegistry.RegisterSingleton();
+ containerRegistry.RegisterSingleton();
containerRegistry.RegisterSingleton();
containerRegistry.RegisterSingleton();
containerRegistry.RegisterSingleton();
@@ -155,6 +156,7 @@ namespace CapMachine.Wpf
containerRegistry.RegisterForNavigation();
containerRegistry.RegisterForNavigation();
containerRegistry.RegisterForNavigation();
+ containerRegistry.RegisterForNavigation();
containerRegistry.RegisterForNavigation();
@@ -173,6 +175,9 @@ namespace CapMachine.Wpf
containerRegistry.RegisterDialog();
containerRegistry.RegisterDialog();
containerRegistry.RegisterDialog();
+ containerRegistry.RegisterDialog();
+ containerRegistry.RegisterDialog();
+ containerRegistry.RegisterDialog();
containerRegistry.RegisterDialog();
diff --git a/CapMachine.Wpf/CanDrive/CanCmdData.cs b/CapMachine.Wpf/CanDrive/CanCmdData.cs
index 58baba8..c7a8b74 100644
--- a/CapMachine.Wpf/CanDrive/CanCmdData.cs
+++ b/CapMachine.Wpf/CanDrive/CanCmdData.cs
@@ -1,4 +1,5 @@
-using System;
+using CapMachine.Wpf.Dtos;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -31,5 +32,17 @@ namespace CapMachine.Wpf.CanDrive
/// 没有的话,则给默认值
///
public double SignalCmdValue { get; set; }
+
+ /////
+ ///// 逻辑规则Id
+ /////
+ //public long LogicRuleId { get; set; }
+
+ ///
+ /// CanLinConfig的逻辑转换规则
+ /// 比如:速度下发的数据SV是4000,但是下发到CAN的值是40,可能是其他的逻辑转换规则,这里就是保存其中的逻辑规则
+ ///
+ public LogicRuleDto? LogicRuleDto { get; set; }
+
}
}
diff --git a/CapMachine.Wpf/CanDrive/CanFD/DBCParserByFD.cs b/CapMachine.Wpf/CanDrive/CanFD/DBCParserByFD.cs
new file mode 100644
index 0000000..0967da2
--- /dev/null
+++ b/CapMachine.Wpf/CanDrive/CanFD/DBCParserByFD.cs
@@ -0,0 +1,57 @@
+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
+{
+ ///
+ /// 和DBCParser一样,但用于处理USB设备的DBC文件解析。
+ /// DBCParserByFD类用于处理USB设备的DBC文件解析。
+ ///
+ public class DBCParserByFD
+ {
+ public const Int32 DBC_PARSER_OK = 0;//没有错误
+ public const Int32 DBC_PARSER_FILE_OPEN = (-1);//打开文件出错
+ public const Int32 DBC_PARSER_FILE_FORMAT = (-2);//文件格式错误
+ public const Int32 DBC_PARSER_DEV_DISCONNECT = (-3);//设备未连接
+ public const Int32 DBC_PARSER_HANDLE_ERROR = (-4);//LDF Handle错误
+ public const Int32 DBC_PARSER_GET_INFO_ERROR = (-5);//获取解析后的数据出错
+ public const Int32 DBC_PARSER_DATA_ERROR = (-6);//数据处理错误
+ public const Int32 DBC_PARSER_SLAVE_NACK = (-7);//从机未响应数据
+
+ [DllImport("USB2XXX.dll")]
+ public static extern UInt64 DBC_ParserFile(int DevHandle, StringBuilder pDBCFileName);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_GetMsgQuantity(UInt64 DBCHandle);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_GetMsgName(UInt64 DBCHandle, int index, StringBuilder pMsgName);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_GetMsgSignalQuantity(UInt64 DBCHandle, StringBuilder pMsgName);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_GetMsgSignalName(UInt64 DBCHandle, StringBuilder pMsgName, int index, StringBuilder pSignalName);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_GetMsgPublisher(UInt64 DBCHandle, StringBuilder pMsgName, StringBuilder pPublisher);
+ //设置信号值
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_SetSignalValue(UInt64 DBCHandle, StringBuilder pMsgName, StringBuilder pSignalName, double Value);
+ //获取信号值
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_GetSignalValue(UInt64 DBCHandle, StringBuilder pMsgName, StringBuilder pSignalName, double[] pValue);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_GetSignalValueStr(UInt64 DBCHandle, StringBuilder pMsgName, StringBuilder pSignalName, StringBuilder pValueStr);
+
+ //将CAN消息数据填充到信号里面
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_SyncCANMsgToValue(UInt64 DBCHandle, IntPtr pCANMsg, int MsgLen);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_SyncCANFDMsgToValue(UInt64 DBCHandle, IntPtr pCANFDMsg, int MsgLen);
+ //将信号数据填充到CAN消息里面
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_SyncValueToCANMsg(UInt64 DBCHandle, StringBuilder pMsgName, IntPtr pCANMsg);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 DBC_SyncValueToCANFDMsg(UInt64 DBCHandle, StringBuilder pMsgName, IntPtr pCANFDMsg);
+ }
+}
diff --git a/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs b/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs
new file mode 100644
index 0000000..c637cbb
--- /dev/null
+++ b/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs
@@ -0,0 +1,1107 @@
+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.Concurrent;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Windows.Interop;
+
+namespace CapMachine.Wpf.CanDrive
+{
+ ///
+ /// Toomoss CANFD
+ ///
+ public class ToomossCanFD : BindableBase
+ {
+ private readonly IContainerProvider ContainerProvider;
+
+ ///
+ /// 实例化函数
+ ///
+ public ToomossCanFD(IContainerProvider containerProvider)
+ {
+ ContainerProvider = containerProvider;
+ HighSpeedDataService = ContainerProvider.Resolve();
+ LogService = ContainerProvider.Resolve();
+ //Stopwatch.Frequency表示高精度计时器每秒的计数次数(ticks/秒)每毫秒的ticks数 = 每秒的ticks数 ÷ 1000
+ TicksPerMs = Stopwatch.Frequency / 1000.0;
+ }
+
+ ///
+ /// HighSpeedDataService 实例
+ ///
+ public HighSpeedDataService HighSpeedDataService { get; set; }
+
+ public ILogService LogService { get; set; }
+
+ ///
+ /// 开始CAN的驱动
+ ///
+ public void StartCanDrive()
+ {
+ try
+ {
+ IsExistsDllFile();
+ ScanDevice();
+ OpenDevice();
+ GetDeviceInfo();
+ GetCANConfig();
+ InitCAN();
+ }
+ catch (Exception ex)
+ {
+ LogService.Error(ex.Message);
+ System.Windows.MessageBox.Show($"{ex.Message}", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
+ }
+ }
+
+ ///
+ /// 开始Dbc文件写入
+ ///
+ public ObservableCollection StartDbc(string DbcPath)
+ {
+ DBC_Parser(DbcPath);
+ return ListCanFdDbcModel;
+ }
+
+ ///
+ /// Dbc消息集合
+ ///
+ public ObservableCollection ListCanFdDbcModel { get; set; } = new ObservableCollection();
+
+
+ #region 公共变量
+
+ ///
+ /// 仲裁波特率
+ ///
+ public uint ArbBaudRate { get; set; } = 500000;
+
+ ///
+ /// 数据波特率
+ ///
+ public uint DataBaudRate { get; set; } = 2000000;
+
+ ///
+ /// CAN FD标准 ISO是否启用
+ ///
+ public bool ISOEnable { get; set; } = true;
+
+ ///
+ /// 终端电阻 是否启用
+ ///
+ public bool ResEnable { get; set; } = true;
+
+ ///
+ /// 更新配置
+ ///
+ public void UpdateConfig(uint arbBaudRate, uint dataBaudRate, bool iSOEnable, bool resEnable, ushort sendCycle)
+ {
+ ArbBaudRate = arbBaudRate;
+ DataBaudRate = dataBaudRate;
+ ISOEnable = iSOEnable;
+ ResEnable = resEnable;
+ SendCycle = sendCycle;
+ }
+
+ ///
+ /// 设备固件信息
+ ///
+ public USB_DEVICEByFD.DEVICE_INFO DevInfo = new USB_DEVICEByFD.DEVICE_INFO();
+
+ ///
+ /// CAN Config
+ ///
+ USB2CANFD.CANFD_INIT_CONFIG CANConfig = new USB2CANFD.CANFD_INIT_CONFIG();
+
+ ///
+ /// 诊断信息
+ ///
+ public USB2CANFD.CANFD_DIAGNOSTIC CurCANFD_DIAGNOSTIC = new USB2CANFD.CANFD_DIAGNOSTIC();
+
+ ///
+ /// CANFD总线错误信息
+ ///
+ public USB2CANFD.CANFD_BUS_ERROR CurCANFD_BUS_ERROR = new USB2CANFD.CANFD_BUS_ERROR();
+
+ ///
+ /// DBC Handle
+ ///
+ public UInt64 DBCHandle { get; set; }
+
+ ///
+ /// 扫描设备Handle集合
+ ///
+ public Int32[] DevHandles { get; set; } = new Int32[20];
+
+ ///
+ /// 扫描设备Handle
+ ///
+ public Int32 DevHandle { get; set; } = 0;
+
+ ///
+ /// Write CAN Index
+ ///
+ public Byte WriteCANIndex { get; set; } = 0;
+
+ ///
+ /// Read CAN Index
+ ///
+ public Byte ReadCANIndex { get; set; } = 0;
+
+
+ private bool _OpenState;
+ ///
+ /// 打开设备的状态
+ ///
+ public bool OpenState
+ {
+ get { return _OpenState; }
+ set { _OpenState = value; RaisePropertyChanged(); }
+ }
+
+ private bool _DbcParserState;
+ ///
+ /// DBC解析的状态
+ ///
+ public bool DbcParserState
+ {
+ get { return _DbcParserState; }
+ set { _DbcParserState = value; RaisePropertyChanged(); }
+ }
+
+ ///
+ /// 扫描到设备个数
+ ///
+ public Int32 DevNum { get; set; }
+
+ public Int32 ret { get; set; }
+
+ public string dllFilePath { get; set; } = "USB2XXX.dll";
+
+
+ ///
+ /// 消息值Pt
+ ///
+ public IntPtr msgPt { get; set; }
+
+ #endregion
+
+ ///
+ /// ******************【1】*********************
+ /// 是否存在Dll文件
+ ///
+ ///
+ public bool IsExistsDllFile()
+ {
+ if (!File.Exists(dllFilePath))
+ {
+ Console.WriteLine("请先将USB2XXX.dll和libusb-1.0.dll文件复制到exe程序文件输出目录下!");
+ Console.WriteLine("dll文件在‘usb2can_lin_pwm_example/sdk/libs/windows’目录下!");
+ Console.WriteLine("程序是32位的就复制‘x86’目录下文件,程序是64位的就复制‘x86_64’目录下文件!");
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// ******************【2】*********************
+ /// 扫描查找设备,并将每个设备的唯一设备号存放到数组中,后面的函数需要用到
+ ///
+ ///
+ public bool ScanDevice()
+ {
+ DevNum = USB_DEVICEByFD.USB_ScanDevice(DevHandles);
+ if (DevNum <= 0)
+ {
+ Console.WriteLine("No device connected!");
+
+ return false;
+ }
+ else
+ {
+ Console.WriteLine("Have {0} device connected!", DevNum);
+ DevHandle = DevHandles[0];//获取第一个设备的设备号
+ return true;
+ }
+ }
+
+ ///
+ /// ******************【3】*********************
+ /// 打开设备
+ ///
+ ///
+ public bool OpenDevice()
+ {
+ //打开设备
+ OpenState = USB_DEVICEByFD.USB_OpenDevice(DevHandle);
+ if (!OpenState)
+ {
+ Console.WriteLine("Open device error!");
+ return false;
+ }
+ else
+ {
+ Console.WriteLine("Open device success!");
+ return true;
+ }
+ }
+
+ ///
+ /// ******************【4】*********************
+ /// 获取设备的固件信息
+ ///
+ ///
+ public bool GetDeviceInfo()
+ {
+ //获取固件信息
+ StringBuilder FuncStr = new StringBuilder(256);
+ OpenState = USB_DEVICEByFD.DEV_GetDeviceInfo(DevHandle, ref DevInfo, FuncStr);
+ if (!OpenState)
+ {
+ Console.WriteLine("Get device infomation error!");
+ return false;
+ }
+ else
+ {
+ Console.WriteLine("Firmware Info:");
+ Console.WriteLine(" Name:" + Encoding.Default.GetString(DevInfo.FirmwareName));
+ Console.WriteLine(" Build Date:" + Encoding.Default.GetString(DevInfo.BuildDate));
+ Console.WriteLine(" Firmware Version:v{0}.{1}.{2}", (DevInfo.FirmwareVersion >> 24) & 0xFF, (DevInfo.FirmwareVersion >> 16) & 0xFF, DevInfo.FirmwareVersion & 0xFFFF);
+ Console.WriteLine(" Hardware Version:v{0}.{1}.{2}", (DevInfo.HardwareVersion >> 24) & 0xFF, (DevInfo.HardwareVersion >> 16) & 0xFF, DevInfo.HardwareVersion & 0xFFFF);
+ Console.WriteLine(" Functions:" + DevInfo.Functions.ToString("X8"));
+ Console.WriteLine(" SerialNumber:" + DevInfo.SerialNumber[0] + DevInfo.SerialNumber[1] + DevInfo.SerialNumber[2]);
+ Console.WriteLine(" Functions String:" + FuncStr);
+ StringBuilder DLLBuildDate = new StringBuilder(256);
+ USB_DEVICEByFD.DEV_GetDllBuildTime(DLLBuildDate);
+ Console.WriteLine(" DLL Build Date:" + DLLBuildDate);
+
+ return true;
+ }
+ }
+
+ ///
+ /// ******************【5】*********************
+ /// 获取设备Config配置
+ ///
+ public void GetCANConfig()
+ {
+ //获取CAN波特率参数
+ ret = USB2CANFD.CANFD_GetCANSpeedArg(DevHandle, ref CANConfig, ArbBaudRate, DataBaudRate);
+ if (ret != USB2CANFD.CANFD_SUCCESS)
+ {
+ Console.WriteLine("Get CAN Speed failed!");
+ return;
+ }
+ else
+ {
+ Console.WriteLine("Get CAN Speed Success!");
+ }
+
+ }
+
+ ///
+ /// ******************【6】*********************
+ /// 初始化CAN
+ ///
+ public void InitCAN()
+ {
+ //初始化CAN
+ CANConfig.ISOCRCEnable = ISOEnable == true ? (byte)1 : (byte)0;//使能ISOCRC
+ CANConfig.ResEnable = ResEnable == true ? (byte)1 : (byte)0;//使能终端电阻
+ ret = USB2CANFD.CANFD_Init(DevHandle, WriteCANIndex, ref CANConfig);
+ if (ret != USB2CANFD.CANFD_SUCCESS)
+ {
+ Console.WriteLine("Config CAN failed!");
+ return;
+ }
+ else
+ {
+ Console.WriteLine("WriteCANIndex Config CAN Success!");
+ }
+
+ ret = USB2CANFD.CANFD_Init(DevHandle, ReadCANIndex, ref CANConfig);
+ if (ret != USB2CANFD.CANFD_SUCCESS)
+ {
+ Console.WriteLine("Config CAN failed!");
+ return;
+ }
+ else
+ {
+ Console.WriteLine("ReadCANIndex Config CAN Success!");
+ }
+ Console.WriteLine("");
+ }
+
+ ///
+ /// ******************【7】*********************
+ /// DBC解析
+ ///
+ public void DBC_Parser(string Path)
+ {
+ //解析DBC文件
+ DBCHandle = DBCParserByFD.DBC_ParserFile(DevHandle, new StringBuilder(Path));
+ if (DBCHandle == 0)
+ {
+ Console.WriteLine("Parser DBC File error!");
+ DbcParserState = false;
+ return;
+ }
+ else
+ {
+ Console.WriteLine("Parser DBC File success!");
+ }
+
+ ListCanFdDbcModel.Clear();
+
+ //打印DBC里面报文和信号相关信息
+ int DBCMsgNum = DBCParserByFD.DBC_GetMsgQuantity(DBCHandle);
+ for (int i = 0; i < DBCMsgNum; i++)
+ {
+ StringBuilder MsgName = new StringBuilder(32);
+ DBCParserByFD.DBC_GetMsgName(DBCHandle, i, MsgName);
+ Console.WriteLine("Msg.Name = {0}", MsgName);
+ int DBCSigNum = DBCParserByFD.DBC_GetMsgSignalQuantity(DBCHandle, MsgName);
+ StringBuilder Publisher = new StringBuilder(32);
+ DBCParserByFD.DBC_GetMsgPublisher(DBCHandle, MsgName, Publisher);
+ Console.Write("Signals:");
+ for (int j = 0; j < DBCSigNum; j++)
+ {
+ StringBuilder SigName = new StringBuilder(32);
+ DBCParserByFD.DBC_GetMsgSignalName(DBCHandle, MsgName, j, SigName);
+ Console.Write("{0} ", SigName);
+
+ //增加信息数据
+ ListCanFdDbcModel.Add(new CanDbcModel()
+ {
+ MsgName = MsgName.ToString(),
+ MsgId = "",
+ SignalName = SigName.ToString(),
+ SignalDesc = "",
+ SignalUnit = "",
+ SignalRtValue = "",
+ Publisher = Publisher.ToString()
+ });
+ }
+ Console.WriteLine("");
+ }
+
+ //Dbc解析成功
+ DbcParserState = true;
+ }
+
+ ///
+ /// 发送CAN数据
+ ///
+ public void SendCanMsg(List CmdData)
+ {
+ 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 msgPt = 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), msgPt);
+ CanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
+ Index++;
+ }
+
+ //设置信号值
+ //DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder("msg_moto_speed"), new StringBuilder("moto_speed"), 2412);
+ //DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder("msg_oil_pressure"), new StringBuilder("oil_pressure"), 980);
+ //DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder("msg_speed_can"), new StringBuilder("speed_can"), 120);
+
+ //通过DBC写入数据后生成CanMsg
+ //将信号值填入CAN消息里面
+
+
+ //IntPtr msgPt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
+ //DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder("msg_moto_speed"), msgPt);
+ //CanMsg[0] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
+
+ //DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder("msg_oil_pressure"), msgPt);
+ //CanMsg[1] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
+
+ //DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder("msg_speed_can"), msgPt);
+ //CanMsg[2] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
+
+ //释放申请的临时缓冲区
+ Marshal.FreeHGlobal(msgPt);
+ Console.WriteLine("");
+ //发送CAN数据
+ int SendedNum = USB2CANFD.CANFD_SendMsg(DevHandle, WriteCANIndex, CanMsg, (Int32)CanMsg.Length);
+ if (SendedNum >= 0)
+ {
+ Console.WriteLine("Success send frames:{0}", SendedNum);
+ }
+ else
+ {
+ Console.WriteLine("Send CAN data failed! {0}", SendedNum);
+ }
+ }
+
+
+ #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;
+ LogService.Info("定时发送延迟过大,重新校准时间");
+ 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);
+ //CanMsg[0].Flags = 5;
+
+ //发送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)
+ {
+ // 任务被取消,正常退出
+ IsSendOk = false;
+ break;
+ }
+ catch (Exception ex)
+ {
+ LogService.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);
+ // 清理其他可能的资源
+ Console.WriteLine("CAN周期发送任务已结束,资源已清理");
+ IsSendOk = false;
+ }
+ finally
+ {
+ // 确保在任何情况下(正常退出、异常、取消)都会停止计时器
+ Stopwatcher.Stop();
+ IsSendOk = false;
+ }
+
+ }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+ }
+
+ ///
+ /// 获取CAN消息数组中指定位置的数据
+ ///
+ /// 源CAN消息数组
+ /// 要获取的数据索引位置
+ /// 返回指定位置数据的新数组
+ private USB2CANFD.CANFD_MSG[] GetCanMsg(USB2CANFD.CANFD_MSG[] sourceCanMsg, int index)
+ {
+ // 参数检查
+ if (sourceCanMsg == null || sourceCanMsg.Length == 0 || index < 0 || index >= sourceCanMsg.Length)
+ {
+ return new USB2CANFD.CANFD_MSG[0];
+ }
+
+ // 创建单个元素的新数组
+ USB2CANFD.CANFD_MSG[] targetMsg = new USB2CANFD.CANFD_MSG[1];
+ targetMsg[0] = sourceCanMsg[index];
+
+ // 由于CANFD_MSG包含数组字段,需要手动初始化数据数组
+ targetMsg[0].Data = new byte[64];
+ // 复制数据
+ Array.Copy(sourceCanMsg[index].Data, targetMsg[0].Data, sourceCanMsg[index].Data.Length);
+
+ return targetMsg;
+ }
+
+ ///
+ /// 修改停止发送的方法
+ ///
+ public void StopCycleSendMsg()
+ {
+ IsCycleSend = false;
+ CycleSendCts?.Cancel();
+ }
+
+ #endregion
+
+
+ #region 并行多线程发送报文
+
+ ///
+ /// 并行精确周期发送CAN数据
+ ///
+ 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();
+
+ // 创建线程同步对象,确保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;
+ LogService.Info($"消息{msgName}定时发送延迟过大,重新校准时间");
+ }
+
+ // 使用锁确保DBC操作和发送的线程安全
+ lock (dbcLock)
+ {
+ // 构建CAN消息
+ IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)));
+
+ try
+ {
+ // 为信号赋值
+ foreach (var signal in signals)
+ {
+ CAN_DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder(msgName),
+ new StringBuilder(signal.SignalName), signal.SignalCmdValue);
+ }
+
+ // 同步值到CAN消息
+ CAN_DBCParser.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;
+ LogService.Info($"消息{msgName}发送失败: {sendedNum}");
+ }
+ }
+ finally
+ {
+ // 确保释放非托管资源
+ Marshal.FreeHGlobal(msgPtSend);
+ }
+ }
+ }
+ catch (TaskCanceledException)
+ {
+ LogService.Info($"消息{msgName}发送任务被取消");
+ break;
+ }
+ catch (Exception ex)
+ {
+ LogService.Info($"消息{msgName}发送异常: {ex.Message}");
+ await Task.Delay(10, token);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogService.Info($"消息{msgName}发送线程异常: {ex.Message}");
+ }
+ finally
+ {
+ LogService.Info($"消息{msgName}发送线程已退出");
+ }
+ }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+
+ sendTasks.Add(sendTask);
+ }
+
+ // 创建监控任务,监控所有发送任务的状态
+ CycleSendTask = Task.Factory.StartNew(async () =>
+ {
+ try
+ {
+ // 等待所有任务完成或取消
+ await Task.WhenAll(sendTasks.ToArray());
+ }
+ catch (Exception ex)
+ {
+ LogService.Info($"并行发送监控任务异常: {ex.Message}");
+ }
+ finally
+ {
+ LogService.Info("并行发送任务已全部结束");
+ IsSendOk = false;
+ }
+ }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+ }
+
+ #endregion
+
+
+
+ private bool _IsCycleRevice;
+ ///
+ /// 是否循环接收数据
+ ///
+ public bool IsCycleRevice
+ {
+ get { return _IsCycleRevice; }
+ set { _IsCycleRevice = value; RaisePropertyChanged(); }
+ }
+
+
+ private bool _IsCycleSend;
+ ///
+ /// 是否循环发送数据
+ ///
+ public bool IsCycleSend
+ {
+ get { return _IsCycleSend; }
+ set { _IsCycleSend = value; RaisePropertyChanged(); }
+ }
+
+ ///
+ /// 循环发送数据
+ ///
+ public ushort SendCycle { get; set; } = 200;
+
+ ///
+ /// 循环接受数据
+ ///
+ public ushort ReviceCycle { get; set; } = 200;
+
+
+ ///
+ /// 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;
+ }
+ }
+ }
+
+
+ private bool _IsReviceOk;
+ ///
+ /// 接收报文是否OK
+ ///
+ public bool IsReviceOk
+ {
+ get { return _IsReviceOk; }
+ set
+ {
+ if (_IsReviceOk != value)
+ {
+ RaisePropertyChanged();
+ _IsReviceOk = value;
+ }
+ }
+ }
+
+
+ ///
+ /// 要发送的数据
+ ///
+ public List CmdData { get; set; } = new List();
+
+ ///
+ /// 循环获取CAN消息
+ ///
+ public void StartCycleReviceCanMsg()
+ {
+ CycleReviceTask = Task.Run(async () =>
+ {
+ //var ret = USB2CANFD.CANFD_StartGetMsg(DevHandle, ReadCANIndex);
+ while (IsCycleRevice)
+ {
+ await Task.Delay(ReviceCycle);
+ try
+ {
+ //另外一个CAN通道读取数据
+ USB2CANFD.CANFD_MSG[] CanMsgBuffer = new USB2CANFD.CANFD_MSG[128];
+ IntPtr msgPtRead = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)) * CanMsgBuffer.Length);
+ 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++)
+ {
+ CanMsgBuffer[i] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure((IntPtr)(msgPtRead + i * Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG))), typeof(USB2CANFD.CANFD_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].DLC; j++)
+ {
+ Console.Write("{0} ", CanMsgBuffer[i].Data[j].ToString("X2"));
+ }
+ Console.WriteLine("");
+ //报文给高速记录的服务
+ HighSpeedDataService.AppendOrUpdateMsg(new Models.HighSpeed.CommMsg()
+ {
+ Category = "CANFD",
+ MsgInfo = "0x" + CanMsgBuffer[i].ID.ToString("X8"),
+ MsgData = BitConverter.ToString(CanMsgBuffer[i].Data),
+ Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
+ });
+ }
+ }
+ else if (CanNum == 0)
+ {
+ IsReviceOk = false;
+ Console.WriteLine("No CAN data!");
+ }
+ else
+ {
+ IsReviceOk = false;
+ Console.WriteLine("Get CAN data error!");
+ }
+ Console.WriteLine("");
+
+ //将CAN消息数据填充到信号里面
+ DBCParserByFD.DBC_SyncCANFDMsgToValue(DBCHandle, msgPtRead, CanNum);
+
+ //循环获取消息的数据
+ foreach (var item in ListCanFdDbcModel)
+ {
+ //有配置的名称的,认为是有用的,则需要读取数据
+ //if (!string.IsNullOrEmpty(item.Name))
+ //{
+ //CAN_DBCParser.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;
+ item.SignalRtValue = ValueDouble[0].ToString();
+ //Console.Write(ValueSb.ToString());
+ //}
+ }
+
+ //释放数据缓冲区,必须释放,否则程序运行一段时间后会报内存不足
+ Marshal.FreeHGlobal(msgPtRead);
+ Thread.Sleep(10);
+
+ ////获取信号值并打印出来
+ //StringBuilder ValueStr = new StringBuilder(32);
+ //DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("TX1"), new StringBuilder("COM_current_Power"), ValueStr);
+ //Console.WriteLine("COM_current_Power = {0}", ValueStr);
+ //DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("TX1"), new StringBuilder("COM_Curr_dc"), ValueStr);
+ //Console.WriteLine("COM_Curr_dc = {0}", ValueStr);
+ }
+ catch (Exception ex)
+ {
+ IsReviceOk = false;
+ LogService.Error(ex.Message);
+ //LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}");
+ }
+ }
+
+
+ IsReviceOk = false;
+ });
+ }
+
+ ///
+ /// 接受CAN消息
+ ///
+ public void ReciveCanMsg()
+ {
+ //另外一个CAN通道读取数据
+ USB2CANFD.CANFD_MSG[] CanMsgBuffer = new USB2CANFD.CANFD_MSG[10];
+ msgPt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)) * CanMsgBuffer.Length);
+ int CanNum = USB2CANFD.CANFD_GetMsg(DevHandle, ReadCANIndex, msgPt, CanMsgBuffer.Length);
+ if (CanNum > 0)
+ {
+ Console.WriteLine("Read CanMsgNum = {0}", CanNum);
+ for (int i = 0; i < CanNum; i++)
+ {
+ CanMsgBuffer[i] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure((IntPtr)(msgPt + i * Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG))), typeof(USB2CANFD.CANFD_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].DLC; j++)
+ {
+ Console.Write("{0} ", CanMsgBuffer[i].Data[j].ToString("X2"));
+ }
+ Console.WriteLine("");
+ }
+ }
+ else if (CanNum == 0)
+ {
+ Console.WriteLine("No CAN data!");
+ }
+ else
+ {
+ Console.WriteLine("Get CAN data error!");
+ }
+ Console.WriteLine("");
+
+ //将CAN消息数据填充到信号里面
+ DBCParserByFD.DBC_SyncCANFDMsgToValue(DBCHandle, msgPt, CanNum);
+ //获取信号值并打印出来
+ StringBuilder ValueStr = new StringBuilder(32);
+ DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_moto_speed"), new StringBuilder("moto_speed"), ValueStr);
+ Console.WriteLine("moto_speed = {0}", ValueStr);
+ DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_oil_pressure"), new StringBuilder("oil_pressure"), ValueStr);
+ Console.WriteLine("oil_pressure = {0}", ValueStr);
+ DBCParserByFD.DBC_GetSignalValueStr(DBCHandle, new StringBuilder("msg_speed_can"), new StringBuilder("speed_can"), ValueStr);
+ Console.WriteLine("speed_can = {0}", ValueStr);
+
+ }
+
+
+ ///
+ /// 关闭设备
+ ///
+ public void CloseDevice()
+ {
+ //关闭设备
+ USB_DEVICEByFD.USB_CloseDevice(DevHandle);
+ OpenState = false;
+ DbcParserState = false;
+ IsCycleRevice = false;
+ IsCycleSend = false;
+ }
+
+ }
+}
diff --git a/CapMachine.Wpf/CanDrive/USB2CANFD.cs b/CapMachine.Wpf/CanDrive/CanFD/USB2CANFD.cs
similarity index 89%
rename from CapMachine.Wpf/CanDrive/USB2CANFD.cs
rename to CapMachine.Wpf/CanDrive/CanFD/USB2CANFD.cs
index 67a278e..fd1b13c 100644
--- a/CapMachine.Wpf/CanDrive/USB2CANFD.cs
+++ b/CapMachine.Wpf/CanDrive/CanFD/USB2CANFD.cs
@@ -119,16 +119,24 @@ namespace CapMachine.Wpf.CanDrive
[DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_Init(Int32 DevHandle, Byte CANIndex, ref CANFD_INIT_CONFIG pCanConfig);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CANFD_Init2(Int32 DevHandle, Byte CANIndex, Int32 BaudRateNBTBps, Int32 BaudRateDBTBps, Byte EnResistor, Byte ISOCRCEnable);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_StartGetMsg(Int32 DevHandle, Byte CANIndex);
[DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_StopGetMsg(Int32 DevHandle, Byte CANIndex);
[DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_SendMsg(Int32 DevHandle, Byte CANIndex, CANFD_MSG[] pCanSendMsg, Int32 SendMsgNum);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CANFD_SendMsgWithTime(Int32 DevHandle, Byte CANIndex, CANFD_MSG[] pCanSendMsg, Int32 SendMsgNum);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_GetMsg(Int32 DevHandle, Byte CANIndex, IntPtr pCanGetMsg, Int32 BufferSize);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CANFD_ClearMsg(Int32 DevHandle, Byte CANIndex);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_SetFilter(Int32 DevHandle, Byte CANIndex, ref CANFD_FILTER_CONFIG pCanFilter, Byte Len);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CANFD_FilterList_Init(Int32 DevHandle, Byte CANIndex, UInt32[] pIDList, Byte IDListLen);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_GetDiagnostic(Int32 DevHandle, Byte CANIndex, ref CANFD_DIAGNOSTIC pCanDiagnostic);
[DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_GetBusError(Int32 DevHandle, Byte CANIndex, ref CANFD_BUS_ERROR pCanBusError);
@@ -139,7 +147,13 @@ namespace CapMachine.Wpf.CanDrive
[DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_StartSchedule(Int32 DevHandle, Byte CANIndex, Byte MsgTabIndex, Byte TimePrecMs, Byte OrderSend);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CANFD_UpdateSchedule(Int32 DevHandle, Byte CANIndex, Byte MsgTabIndex, Byte MsgIndex, CANFD_MSG[] pCanMsg, Byte MsgNum);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_StopSchedule(Int32 DevHandle, Byte CANIndex);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int64 CANFD_GetStartTime(Int32 DevHandle, Byte CANIndex);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 CANFD_ResetStartTime(Int32 DevHandle, Byte CANIndex);
[DllImport("USB2XXX.dll")]
public static extern Int32 CANFD_SetRelay(Int32 DevHandle, Byte RelayState);
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/CanDrive/ToomossCan.cs b/CapMachine.Wpf/CanDrive/ToomossCan.cs
index 258c6fe..82ce026 100644
--- a/CapMachine.Wpf/CanDrive/ToomossCan.cs
+++ b/CapMachine.Wpf/CanDrive/ToomossCan.cs
@@ -1,6 +1,11 @@
-using CapMachine.Wpf.Models.Tag;
+using CapMachine.Core;
+using CapMachine.Model.CANLIN;
+using CapMachine.Wpf.Dtos;
+using CapMachine.Wpf.Models.Tag;
using CapMachine.Wpf.Services;
using HslCommunication;
+using ImTools;
+using NLog;
using NPOI.OpenXmlFormats.Wordprocessing;
using Prism.Ioc;
using Prism.Mvvm;
@@ -33,6 +38,7 @@ namespace CapMachine.Wpf.CanDrive
{
ContainerProvider = containerProvider;
HighSpeedDataService = ContainerProvider.Resolve();
+ LoggerService = ContainerProvider.Resolve();
//Stopwatch.Frequency表示高精度计时器每秒的计数次数(ticks/秒)每毫秒的ticks数 = 每秒的ticks数 ÷ 1000
TicksPerMs = Stopwatch.Frequency / 1000.0;
@@ -57,6 +63,11 @@ namespace CapMachine.Wpf.CanDrive
///
public HighSpeedDataService HighSpeedDataService { get; set; }
+ ///
+ /// Logger 实例
+ ///
+ public ILogService LoggerService { get; set; }
+
///
/// 开始Dbc文件写入
///
@@ -123,6 +134,7 @@ namespace CapMachine.Wpf.CanDrive
///BufferSize 存储CAN消息缓冲区大小。
///返回值:
//大于等于0表示从CAN适配器内部成功读取到的CAN消息帧数,若返回值小于0则说明调用该函数失败。
+ /// 目前是WriteCANIndex和ReadCANIndex需要相同
///
public Byte WriteCANIndex { get; set; } = 0;
@@ -353,19 +365,19 @@ namespace CapMachine.Wpf.CanDrive
{
StringBuilder MsgName = new StringBuilder(32);
CAN_DBCParser.DBC_GetMsgName(DBCHandle, i, MsgName);
- //Console.WriteLine("Msg.Name = {0}", MsgName);
+ Console.WriteLine("Msg.Name = {0}", MsgName);
int DBCSigNum = CAN_DBCParser.DBC_GetMsgSignalQuantity(DBCHandle, MsgName);
StringBuilder Publisher = new StringBuilder(32);
CAN_DBCParser.DBC_GetMsgPublisher(DBCHandle, MsgName, Publisher);
long MsgId;
MsgId = CAN_DBCParser.DBC_GetMsgIDByName(DBCHandle, MsgName);
- //Console.Write("Signals:");
+ Console.Write("Signals:");
for (int j = 0; j < DBCSigNum; j++)
{
StringBuilder SigName = new StringBuilder(32);
CAN_DBCParser.DBC_GetMsgSignalName(DBCHandle, MsgName, j, SigName);
- //Console.Write("{0} ", SigName);
+ Console.Write("{0} ", SigName);
//增加信息数据
ListCanDbcModel.Add(new CanDbcModel()
@@ -379,7 +391,7 @@ namespace CapMachine.Wpf.CanDrive
Publisher = Publisher.ToString()
});
}
- //Console.WriteLine("");
+ Console.WriteLine("");
}
//Dbc解析成功
@@ -429,11 +441,11 @@ namespace CapMachine.Wpf.CanDrive
int SendedNum = USB2CAN.CAN_SendMsg(DevHandle, WriteCANIndex, CanMsg, (uint)CanMsg.Length);
if (SendedNum >= 0)
{
- //Console.WriteLine("Success send frames:{0}", SendedNum);
+ Console.WriteLine("Success send frames:{0}", SendedNum);
}
else
{
- //Console.WriteLine("Send CAN data failed! {0}", SendedNum);
+ Console.WriteLine("Send CAN data failed! {0}", SendedNum);
}
}
@@ -480,7 +492,48 @@ namespace CapMachine.Wpf.CanDrive
///
private static Task CycleSendTask { get; set; }
+ ///
+ /// 定时扫描更新数据 扫描Task
+ ///
+ private static Task CycleUpdateCmdTask { 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;
+ }
+ //RaisePropertyChanged();
+ }
+ }
+
+ private bool _IsReviceOk;
+ ///
+ /// 接收报文是否OK
+ ///
+ public bool IsReviceOk
+ {
+ get { return _IsReviceOk; }
+ set
+ {
+ if (_IsReviceOk != value)
+ {
+ RaisePropertyChanged();
+ _IsReviceOk = value;
+ }
+ }
+ }
///
/// 要发送的数据
@@ -530,11 +583,11 @@ namespace CapMachine.Wpf.CanDrive
int SendedNum = USB2CAN.CAN_SendMsg(DevHandle, WriteCANIndex, CanMsg, (uint)CanMsg.Length);
if (SendedNum >= 0)
{
- //Console.WriteLine("Success send frames:{0}", SendedNum);
+ Console.WriteLine("Success send frames:{0}", SendedNum);
}
else
{
- //Console.WriteLine("Send CAN data failed! {0}", SendedNum);
+ Console.WriteLine("Send CAN data failed! {0}", SendedNum);
}
}
catch (Exception ex)
@@ -546,97 +599,6 @@ namespace CapMachine.Wpf.CanDrive
}
- ///
- /// 循环获取CAN消息
- ///
- public void StartCycleReviceCanMsg()
- {
- CycleReviceTask = Task.Run(async () =>
- {
- while (IsCycleRevice)
- {
- await Task.Delay(ReviceCycle);
- try
- {
- //另外一个CAN通道读取数据
- USB2CAN.CAN_MSG[] CanMsgBuffer = new USB2CAN.CAN_MSG[128];
- //申请数据缓冲区
- IntPtr msgPtRead = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)) * CanMsgBuffer.Length);
- int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, ReadCANIndex, msgPtRead, CanMsgBuffer.Length);
- if (CanNum > 0)
- {
- Console.WriteLine("Read CanMsgNum = {0}", CanNum);
- for (int i = 0; i < CanNum; i++)
- {
- //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")
- });
- }
- }
- else if (CanNum == 0)
- {
- //Console.WriteLine("No CAN data!");
- }
- else
- {
- //Console.WriteLine("Get CAN data error!");
- }
- Console.WriteLine("");
-
- //将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);
- item.SignalRtValueSb = ValueSb;
- Console.Write(ValueSb.ToString());
- //}
- }
-
- //释放数据缓冲区,必须释放,否则程序运行一段时间后会报内存不足
- 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)
- {
- //LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}");
- }
- }
- });
- }
-
-
#region 精确发送报文数据
// 添加取消标记源字段用于停止任务
@@ -725,6 +687,7 @@ namespace CapMachine.Wpf.CanDrive
// 严重延迟,重新校准
NextExecutionTime = Stopwatcher.ElapsedTicks;
Console.WriteLine("定时发送延迟过大,重新校准时间");
+ LoggerService.Info($"定时发送延迟过大,重新校准时间");
}
// 使用Stopwatch记录实际的执行间隔,而不是DateTime
@@ -770,12 +733,12 @@ namespace CapMachine.Wpf.CanDrive
if (SendedNum >= 0)
{
//Console.WriteLine("Success send frames:{0}", SendedNum);
- //IsSendOk = true;
+ IsSendOk = true;
}
else
{
//Console.WriteLine("Send CAN data failed! {0}", SendedNum);
- //IsSendOk = false;
+ IsSendOk = false;
}
}
@@ -783,32 +746,43 @@ namespace CapMachine.Wpf.CanDrive
}
catch (TaskCanceledException)
{
+ LoggerService.Info($"精确周期发送CAN数据-任务被取消,正常退出");
// 任务被取消,正常退出
+ IsSendOk = false;
break;
}
catch (Exception ex)
{
Console.WriteLine($"CAN周期发送异常: {ex.Message}");
// 短暂暂停避免异常情况下CPU占用过高
+ IsSendOk = false;
await Task.Delay(10, token);
+
+ LoggerService.Info($"精确周期发送CAN数据-{ex.Message}");
}
}
+
+ IsSendOk = false;
}
catch (Exception ex)
{
// 确保在任何情况下(正常退出、异常、取消)都会停止计时器
Stopwatcher.Stop();
-
+ LoggerService.Info($"精确周期发送CAN数据-{ex.Message}");
// 清理其他可能的资源
Console.WriteLine("CAN周期发送任务已结束,资源已清理");
+ IsSendOk = false;
}
finally
{
// 确保在任何情况下(正常退出、异常、取消)都会停止计时器
Stopwatcher.Stop();
+ LoggerService.Info("精确周期发送CAN数据-正常退出、异常、取消)都会停止计时器");
+ IsSendOk = false;
}
}, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+
}
@@ -823,7 +797,451 @@ namespace CapMachine.Wpf.CanDrive
#endregion
+ #region 调度表发送报文
+ private bool _SchEnable;
+ ///
+ /// 调度表使能
+ ///
+ public bool SchEnable
+ {
+ get { return _SchEnable; }
+ set
+ {
+ _SchEnable = value;
+ RaisePropertyChanged();
+ }
+ }
+
+
+ ///
+ /// 调度表的发送的报文数据
+ ///
+ private USB2CAN.CAN_MSG[] SchCanMsg { get; set; }
+
+ ///
+ /// 依据消息的分组
+ ///
+ private IEnumerable> GroupMsg { get; set; }
+
+ ///
+ /// 调度表集合数据
+ /// 总共3个调度表,第一个表里面包含3帧数据,第二个调度表包含6帧数据,第三个调度表包含11帧数据
+ /// Byte[] MsgTabNum = new Byte[3] { 3, 6, 11 };
+ ///
+ private Byte[] MsgTabNum { get; set; }
+
+ ///
+ /// 调度表发送的次数集合
+ /// 第一个调度表循环发送数据,第二个调度表循环发送数据,第三个调度表只发送3次
+ /// UInt16[] SendTimes = new UInt16[3] { 0xFFFF, 0xFFFF, 3 };
+ ///
+ private UInt16[] SendTimes { get; set; }
+
+ ///
+ /// 预设的调度表的个数 常值
+ ///
+ private const int MsgTabCount = 5;
+
+ ///
+ /// 定时更新时间
+ ///
+ private int UpdateCycle = 100;
+
+ ///
+ /// CNA 调度表的配置信息
+ ///
+ public List ListCANScheduleConfig { get; set; }
+
+ Random random = new Random();
+
+ ///
+ /// 更新数据 测试用废弃了
+ ///
+ public void UpdateValue()
+ {
+ //通过DBC进行对消息赋值
+ IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)));
+ int Index = 0;
+ //循环给MSG赋值数据
+ foreach (var itemMsg in GroupMsg)
+ {
+ foreach (var itemSignal in itemMsg)
+ {
+ itemSignal.SignalCmdValue = random.Next(0, 100);
+ 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);
+ SchCanMsg[Index] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_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 = USB2CAN.CAN_SetSchedule(DevHandle, WriteCANIndex, SchCanMsg, MsgTabNum, SendTimes, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
+ var ret = USB2CAN.CAN_UpdateSchedule(DevHandle, WriteCANIndex, 0, 0, SchCanMsg, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
+ if (ret == USB2CAN.CAN_SUCCESS)
+ {
+ Console.WriteLine("Update CAN Schedule Success");
+ }
+ else
+ {
+ Console.WriteLine("Update CAN Schedule Error ret = {0}", ret);
+ return;
+ }
+ }
+
+ ///
+ /// 开始调度表执行
+ ///
+ public void StartSchedule()
+ {
+ if (CmdData.Count() == 0) return;
+
+ //依据报文进行分组
+ GroupMsg = CmdData.GroupBy(x => x.MsgName)!;
+ //初始化调度表要发送的消息结构
+ SchCanMsg = new USB2CAN.CAN_MSG[GroupMsg.Count()];
+ for (int i = 0; i < GroupMsg.Count(); i++)
+ {
+ SchCanMsg[i] = new USB2CAN.CAN_MSG();
+ SchCanMsg[i].Data = new Byte[64];
+ }
+
+ //通过DBC进行对消息赋值
+ 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);
+ //每个分组就是一个帧指令/消息数据
+ SchCanMsg[Index] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_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 = USB2CAN.CAN_SetSchedule(DevHandle, WriteCANIndex, SchCanMsg, MsgTabNum, SendTimes, MsgTabCount);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
+ if (ret == USB2CAN.CAN_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 = USB2CAN.CAN_StartSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)100, (byte)ListCANScheduleConfig.FirstOrDefault()!.OrderSend);
+ if (ret == USB2CAN.CAN_SUCCESS)
+ {
+ Console.WriteLine($"Start CAN Schedule 1 Success,SchTabIndex:{(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 = USB2CAN.CAN_StartSchedule(DevHandle, WriteCANIndex, (byte)CANScheduleConfig.SchTabIndex, (byte)CANScheduleConfig.Cycle, (byte)CANScheduleConfig.OrderSend);
+ // if (ret == USB2CAN.CAN_SUCCESS)
+ // {
+ // Console.WriteLine($"Start CAN Schedule 1 Success,SchTabIndex:{(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;
+
+ }
+
+
+ ///
+ /// 停止调度表
+ ///
+ public void StopSchedule()
+ {
+ ret = USB2CAN.CAN_StopSchedule(DevHandle, WriteCANIndex);//启动第一个调度表,表里面的CAN帧并行发送
+ if (ret == USB2CAN.CAN_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;
+ }
+
+
+ }
+
+ ///
+ /// 循环使用的
+ ///
+ private int CycleUpdateIndex = 0;
+
+ ///
+ /// 循环更新调度表的指令数据
+ /// 定时更新数据到调度表中
+ ///
+ public void StartCycleUpdateCmd()
+ {
+ CycleUpdateCmdTask = Task.Run(async () =>
+ {
+ while (IsCycleSend)
+ {
+ await Task.Delay(UpdateCycle);
+ try
+ {
+
+ //通过DBC进行对消息赋值
+ IntPtr msgPtSend = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CAN.CAN_MSG)));
+ CycleUpdateIndex = 0;
+ //循环给MSG赋值数据,顺序是固定的,跟初始时设置是一样的
+ foreach (var itemMsg in GroupMsg)
+ {
+ foreach (var itemSignal in itemMsg)
+ {
+ //itemSignal.SignalCmdValue = random.Next(0, 100); //仿真测试数据使用
+ 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);
+ SchCanMsg[CycleUpdateIndex] = (USB2CAN.CAN_MSG)Marshal.PtrToStructure(msgPtSend, typeof(USB2CAN.CAN_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 = USB2CAN.CAN_UpdateSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)(0), SchCanMsg, (byte)SchCanMsg.Count());//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
+ if (ret == USB2CAN.CAN_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)
+ //{
+ // //USB2CAN.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 = USB2CAN.CAN_UpdateSchedule(DevHandle, WriteCANIndex, (byte)0, (byte)(itemMsgSchConfig.MsgIndex), SchCanMsg, 1);//配置调度表,该函数耗时可能会比较长,但是只需要执行一次即可
+ // if (ret == USB2CAN.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;
+ });
+ }
+
+
+ #endregion
+
+ ///
+ /// 循环获取CAN消息
+ ///
+ public void StartCycleReviceCanMsg()
+ {
+ CycleReviceTask = Task.Run(async () =>
+ {
+ while (IsCycleRevice)
+ {
+ await Task.Delay(ReviceCycle);
+ try
+ {
+ //另外一个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)
+ {
+ IsReviceOk = true;
+ Console.WriteLine("Read CanMsgNum = {0}", CanNum);
+ for (int i = 0; i < CanNum; i++)
+ {
+ //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")
+ });
+ }
+ }
+ else if (CanNum == 0)
+ {
+ IsReviceOk = false;
+ Console.WriteLine("No CAN data!");
+ }
+ else
+ {
+ IsReviceOk = false;
+ Console.WriteLine("Get CAN data error!");
+ }
+ //Console.WriteLine("");
+
+ //将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;
+ item.SignalRtValue = ValueDouble[0].ToString();
+ //Console.Write(ValueSb.ToString());
+ //}
+ }
+
+ //释放数据缓冲区,必须释放,否则程序运行一段时间后会报内存不足
+ 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("接收出现异常");
+ }
+ //finally
+ //{
+ // IsReviceOk = false;
+ //}
+ }
+ IsReviceOk = false;
+ });
+ }
///
/// 接受CAN消息
@@ -884,6 +1302,10 @@ namespace CapMachine.Wpf.CanDrive
DbcParserState = false;
IsCycleRevice = false;
IsCycleSend = false;
+ if (SchEnable)
+ {
+ StopSchedule();
+ }
}
}
diff --git a/CapMachine.Wpf/CanDrive/ToomossCanFD.cs b/CapMachine.Wpf/CanDrive/ToomossCanFD.cs
deleted file mode 100644
index a84245b..0000000
--- a/CapMachine.Wpf/CanDrive/ToomossCanFD.cs
+++ /dev/null
@@ -1,511 +0,0 @@
-using CapMachine.Wpf.Models.Tag;
-using NPOI.OpenXmlFormats.Wordprocessing;
-using Prism.Mvvm;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using System.Windows.Interop;
-
-namespace CapMachine.Wpf.CanDrive
-{
- ///
- /// Toomoss CAN
- ///
- public class ToomossCanFD : BindableBase
- {
- ///
- /// 实例化函数
- ///
- public ToomossCanFD()
- {
-
- }
-
- ///
- /// 开始CAN的驱动
- ///
- public void StartCanDrive()
- {
- IsExistsDllFile();
- ScanDevice();
- OpenDevice();
- GetDeviceInfo();
- GetCANConfig();
- InitCAN();
-
- }
-
- ///
- /// 开始Dbc文件写入
- ///
- public void StartDbc(string DbcPath)
- {
- DBC_Parser(DbcPath);
- }
-
- ///
- /// Dbc消息集合
- ///
- public ObservableCollection ListCanDbcModel { get; set; } = new ObservableCollection();
-
-
- #region 公共变量
-
- ///
- /// 设备固件信息
- ///
- public USB_DEVICE.DEVICE_INFO DevInfo = new USB_DEVICE.DEVICE_INFO();
-
- ///
- /// CAN Config
- ///
- USB2CANFD.CANFD_INIT_CONFIG CANConfig = new USB2CANFD.CANFD_INIT_CONFIG();
-
- ///
- /// DBC Handle
- ///
- public UInt64 DBCHandle { get; set; }
-
- ///
- /// 扫描设备Handle集合
- ///
- public Int32[] DevHandles { get; set; } = new Int32[20];
-
- ///
- /// 扫描设备Handle
- ///
- public Int32 DevHandle { get; set; } = 0;
- ///
- /// Write CAN Index
- ///
- public Byte WriteCANIndex { get; set; } = 0;
-
- ///
- /// Read CAN Index
- ///
- public Byte ReadCANIndex { get; set; } = 1;
-
-
- private bool _OpenState;
- ///
- /// 打开设备的状态
- ///
- public bool OpenState
- {
- get { return _OpenState; }
- set { _OpenState = value; RaisePropertyChanged(); }
- }
-
-
- ///
- /// 扫描到设备个数
- ///
- public Int32 DevNum { get; set; }
-
- public Int32 ret { get; set; }
-
- public string dllFilePath { get; set; } = "USB2XXX.dll";
-
-
- ///
- /// 消息值Pt
- ///
- public IntPtr msgPt { get; set; }
-
- #endregion
-
- ///
- /// ******************【1】*********************
- /// 是否存在Dll文件
- ///
- ///
- public bool IsExistsDllFile()
- {
- if (!File.Exists(dllFilePath))
- {
- Console.WriteLine("请先将USB2XXX.dll和libusb-1.0.dll文件复制到exe程序文件输出目录下!");
- Console.WriteLine("dll文件在‘usb2can_lin_pwm_example/sdk/libs/windows’目录下!");
- Console.WriteLine("程序是32位的就复制‘x86’目录下文件,程序是64位的就复制‘x86_64’目录下文件!");
- return false;
- }
- return true;
- }
-
- ///
- /// ******************【2】*********************
- /// 扫描查找设备,并将每个设备的唯一设备号存放到数组中,后面的函数需要用到
- ///
- ///
- public bool ScanDevice()
- {
- DevNum = USB_DEVICE.USB_ScanDevice(DevHandles);
- if (DevNum <= 0)
- {
- Console.WriteLine("No device connected!");
-
- return false;
- }
- else
- {
- Console.WriteLine("Have {0} device connected!", DevNum);
- DevHandle = DevHandles[0];//获取第一个设备的设备号
- return true;
- }
- }
-
- ///
- /// ******************【3】*********************
- /// 打开设备
- ///
- ///
- public bool OpenDevice()
- {
- //打开设备
- OpenState = USB_DEVICE.USB_OpenDevice(DevHandle);
- if (!OpenState)
- {
- Console.WriteLine("Open device error!");
- return false;
- }
- else
- {
- Console.WriteLine("Open device success!");
- return true;
- }
- }
-
- ///
- /// ******************【4】*********************
- /// 获取设备的固件信息
- ///
- ///
- public bool GetDeviceInfo()
- {
- //获取固件信息
- StringBuilder FuncStr = new StringBuilder(256);
- OpenState = USB_DEVICE.DEV_GetDeviceInfo(DevHandle, ref DevInfo, FuncStr);
- if (!OpenState)
- {
- Console.WriteLine("Get device infomation error!");
- return false;
- }
- else
- {
- Console.WriteLine("Firmware Info:");
- Console.WriteLine(" Name:" + Encoding.Default.GetString(DevInfo.FirmwareName));
- Console.WriteLine(" Build Date:" + Encoding.Default.GetString(DevInfo.BuildDate));
- Console.WriteLine(" Firmware Version:v{0}.{1}.{2}", (DevInfo.FirmwareVersion >> 24) & 0xFF, (DevInfo.FirmwareVersion >> 16) & 0xFF, DevInfo.FirmwareVersion & 0xFFFF);
- Console.WriteLine(" Hardware Version:v{0}.{1}.{2}", (DevInfo.HardwareVersion >> 24) & 0xFF, (DevInfo.HardwareVersion >> 16) & 0xFF, DevInfo.HardwareVersion & 0xFFFF);
- Console.WriteLine(" Functions:" + DevInfo.Functions.ToString("X8"));
- Console.WriteLine(" Functions String:" + FuncStr);
- StringBuilder DLLBuildDate = new StringBuilder(256);
- USB_DEVICE.DEV_GetDllBuildTime(DLLBuildDate);
- Console.WriteLine(" DLL Build Date:" + DLLBuildDate);
-
- return true;
- }
- }
-
- ///
- /// ******************【5】*********************
- /// 获取设备Config配置
- ///
- public void GetCANConfig()
- {
- //获取CAN波特率参数
- ret = USB2CANFD.CANFD_GetCANSpeedArg(DevHandle, ref CANConfig, 500000, 2000000);
- if (ret != USB2CANFD.CANFD_SUCCESS)
- {
- Console.WriteLine("Get CAN Speed failed!");
- return;
- }
- else
- {
- Console.WriteLine("Get CAN Speed Success!");
- }
-
- }
-
- ///
- /// ******************【6】*********************
- /// 初始化CAN
- ///
- public void InitCAN()
- {
- //初始化CAN
-
- ret = USB2CANFD.CANFD_Init(DevHandle, WriteCANIndex, ref CANConfig);
- if (ret != USB2CANFD.CANFD_SUCCESS)
- {
- Console.WriteLine("Config CAN failed!");
- return;
- }
- else
- {
- Console.WriteLine("WriteCANIndex Config CAN Success!");
- }
-
- ret = USB2CANFD.CANFD_Init(DevHandle, ReadCANIndex, ref CANConfig);
- if (ret != USB2CAN.CAN_SUCCESS)
- {
- Console.WriteLine("Config CAN failed!");
- return;
- }
- else
- {
- Console.WriteLine("ReadCANIndex Config CAN Success!");
- }
- Console.WriteLine("");
- }
-
- ///
- /// ******************【7】*********************
- /// DBC解析
- ///
- public void DBC_Parser(string Path)
- {
- //解析DBC文件
- DBCHandle = CANFD_DBCParser.DBC_ParserFile(DevHandle, new StringBuilder(Path));
- if (DBCHandle == 0)
- {
- Console.WriteLine("Parser DBC File error!");
- return;
- }
- else
- {
- Console.WriteLine("Parser DBC File success!");
- }
-
- ListCanDbcModel.Clear();
-
- //打印DBC里面报文和信号相关信息
- int DBCMsgNum = CANFD_DBCParser.DBC_GetMsgQuantity(DBCHandle);
- for (int i = 0; i < DBCMsgNum; i++)
- {
- StringBuilder MsgName = new StringBuilder(32);
- CANFD_DBCParser.DBC_GetMsgName(DBCHandle, i, MsgName);
- Console.WriteLine("Msg.Name = {0}", MsgName);
- int DBCSigNum = CANFD_DBCParser.DBC_GetMsgSignalQuantity(DBCHandle, MsgName);
- StringBuilder Publisher = new StringBuilder(32);
- CANFD_DBCParser.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);
- Console.Write("{0} ", SigName);
-
- //增加信息数据
- ListCanDbcModel.Add(new CanDbcModel()
- {
- MsgName = MsgName.ToString(),
- MsgId = "",
- SignalName = SigName.ToString(),
- SignalDesc = "",
- SignalUnit = "",
- SignalRtValue = "",
- Publisher = Publisher.ToString()
- });
- }
- Console.WriteLine("");
- }
- }
-
- ///
- /// 发送CAN数据
- ///
- public void SendCanMsg(List CmdData)
- {
- 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 msgPt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
- int Index = 0;
- //循环给MSG赋值数据
- foreach (var itemMsg in GroupMsg)
- {
- foreach (var itemSignal in itemMsg)
- {
- CANFD_DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
- }
- CANFD_DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder(itemMsg.Key), msgPt);
- CanMsg[Index] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
- Index++;
- }
-
- //设置信号值
- //DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder("msg_moto_speed"), new StringBuilder("moto_speed"), 2412);
- //DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder("msg_oil_pressure"), new StringBuilder("oil_pressure"), 980);
- //DBCParser.DBC_SetSignalValue(DBCHandle, new StringBuilder("msg_speed_can"), new StringBuilder("speed_can"), 120);
-
- //通过DBC写入数据后生成CanMsg
- //将信号值填入CAN消息里面
-
-
- //IntPtr msgPt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)));
- //DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder("msg_moto_speed"), msgPt);
- //CanMsg[0] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
-
- //DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder("msg_oil_pressure"), msgPt);
- //CanMsg[1] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
-
- //DBCParser.DBC_SyncValueToCANFDMsg(DBCHandle, new StringBuilder("msg_speed_can"), msgPt);
- //CanMsg[2] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure(msgPt, typeof(USB2CANFD.CANFD_MSG));
-
- //释放申请的临时缓冲区
- Marshal.FreeHGlobal(msgPt);
- Console.WriteLine("");
- //发送CAN数据
- int SendedNum = USB2CANFD.CANFD_SendMsg(DevHandle, WriteCANIndex, CanMsg, (Int32)CanMsg.Length);
- if (SendedNum >= 0)
- {
- Console.WriteLine("Success send frames:{0}", SendedNum);
- }
- else
- {
- Console.WriteLine("Send CAN data failed! {0}", SendedNum);
- }
- }
-
- ///
- /// 是否循环接收数据
- ///
- public bool IsCycleRevice { get; set; }
-
- ///
- /// CycleRevice扫描Task
- ///
- private static Task CycleReviceTask { get; set; }
-
- ///
- /// 循环获取CAN消息
- ///
- public void StartCycleReviceCanMsg()
- {
- CycleReviceTask = Task.Run(async () =>
- {
- while (IsCycleRevice)
- {
- await Task.Delay(1000);
- try
- {
- //另外一个CAN通道读取数据
- USB2CANFD.CANFD_MSG[] CanMsgBuffer = new USB2CANFD.CANFD_MSG[10];
- msgPt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)) * CanMsgBuffer.Length);
- int CanNum = USB2CANFD.CANFD_GetMsg(DevHandle, ReadCANIndex, msgPt, CanMsgBuffer.Length);
- if (CanNum > 0)
- {
- Console.WriteLine("Read CanMsgNum = {0}", CanNum);
- for (int i = 0; i < CanNum; i++)
- {
- CanMsgBuffer[i] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure((IntPtr)(msgPt + i * Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG))), typeof(USB2CANFD.CANFD_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].DLC; j++)
- {
- Console.Write("{0} ", CanMsgBuffer[i].Data[j].ToString("X2"));
- }
- Console.WriteLine("");
- }
- }
- else if (CanNum == 0)
- {
- Console.WriteLine("No CAN data!");
- }
- else
- {
- Console.WriteLine("Get CAN data error!");
- }
- Console.WriteLine("");
-
- //将CAN消息数据填充到信号里面
- CANFD_DBCParser.DBC_SyncCANFDMsgToValue(DBCHandle, msgPt, CanNum);
- //获取信号值并打印出来
- StringBuilder ValueStr = new StringBuilder(32);
- CANFD_DBCParser.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);
- Console.WriteLine("COM_Curr_dc = {0}", ValueStr);
- }
- catch (Exception ex)
- {
- //LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}");
- }
- }
- });
- }
-
- ///
- /// 接受CAN消息
- ///
- public void ReciveCanMsg()
- {
- //另外一个CAN通道读取数据
- USB2CANFD.CANFD_MSG[] CanMsgBuffer = new USB2CANFD.CANFD_MSG[10];
- msgPt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG)) * CanMsgBuffer.Length);
- int CanNum = USB2CANFD.CANFD_GetMsg(DevHandle, ReadCANIndex, msgPt, CanMsgBuffer.Length);
- if (CanNum > 0)
- {
- Console.WriteLine("Read CanMsgNum = {0}", CanNum);
- for (int i = 0; i < CanNum; i++)
- {
- CanMsgBuffer[i] = (USB2CANFD.CANFD_MSG)Marshal.PtrToStructure((IntPtr)(msgPt + i * Marshal.SizeOf(typeof(USB2CANFD.CANFD_MSG))), typeof(USB2CANFD.CANFD_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].DLC; j++)
- {
- Console.Write("{0} ", CanMsgBuffer[i].Data[j].ToString("X2"));
- }
- Console.WriteLine("");
- }
- }
- else if (CanNum == 0)
- {
- Console.WriteLine("No CAN data!");
- }
- else
- {
- Console.WriteLine("Get CAN data error!");
- }
- Console.WriteLine("");
-
- //将CAN消息数据填充到信号里面
- CANFD_DBCParser.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);
- Console.WriteLine("moto_speed = {0}", ValueStr);
- CANFD_DBCParser.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);
- Console.WriteLine("speed_can = {0}", ValueStr);
-
- }
-
-
- ///
- /// 关闭设备
- ///
- public void CloseDevice()
- {
- //关闭设备
- USB_DEVICE.USB_CloseDevice(DevHandle);
- OpenState = false;
- }
-
- }
-}
diff --git a/CapMachine.Wpf/CanDrive/usb2can.cs b/CapMachine.Wpf/CanDrive/usb2can.cs
index 672bc5c..427625a 100644
--- a/CapMachine.Wpf/CanDrive/usb2can.cs
+++ b/CapMachine.Wpf/CanDrive/usb2can.cs
@@ -101,14 +101,22 @@ namespace CapMachine.Wpf.CanDrive
[DllImport("USB2XXX.dll")]
public static extern Int32 CAN_Init(Int32 DevHandle, Byte CANIndex, ref CAN_INIT_CONFIG pCanConfig);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CAN_Init2(Int32 DevHandle, Byte CANIndex, Int32 BaudRateBps, Byte EnResistor);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CAN_Filter_Init(Int32 DevHandle, Byte CANIndex, ref CAN_FILTER_CONFIG pFilterConfig);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CAN_FilterList_Init(Int32 DevHandle, Byte CANIndex, UInt32[] pIDList, Byte IDListLen);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CAN_StartGetMsg(Int32 DevHandle, Byte CANIndex);
[DllImport("USB2XXX.dll")]
public static extern Int32 CAN_StopGetMsg(Int32 DevHandle, Byte CANIndex);
[DllImport("USB2XXX.dll")]
public static extern Int32 CAN_SendMsg(Int32 DevHandle, Byte CANIndex, CAN_MSG[] pCanSendMsg, UInt32 SendMsgNum);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CAN_SendMsgSynch(Int32 DevHandle, Byte CANIndex, CAN_MSG[] pCanSendMsg, UInt32 SendMsgNum);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 CAN_SendMsgWithTime(Int32 DevHandle, Byte CANIndex, CAN_MSG[] pCanSendMsg, UInt32 SendMsgNum);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CAN_GetMsg(Int32 DevHandle, Byte CANIndex, IntPtr pCanGetMsg);
[DllImport("USB2XXX.dll")]
public static extern Int32 CAN_GetMsgWithSize(Int32 DevHandle, Byte CANIndex, IntPtr pCanGetMsg, Int32 BufferSize);
@@ -121,9 +129,17 @@ namespace CapMachine.Wpf.CanDrive
[DllImport("USB2XXX.dll")]
public static extern Int32 CAN_StartSchedule(Int32 DevHandle, Byte CANIndex, Byte MsgTabIndex, Byte TimePrecMs, Byte OrderSend);
[DllImport("USB2XXX.dll")]
+ public static extern Int32 CAN_UpdateSchedule(Int32 DevHandle, Byte CANIndex, Byte MsgTabIndex, Byte MsgIndex, CAN_MSG[] pCanMsg, Byte MsgNum);
+ [DllImport("USB2XXX.dll")]
public static extern Int32 CAN_StopSchedule(Int32 DevHandle, Byte CANIndex);
[DllImport("USB2XXX.dll")]
public static extern Int32 CAN_SetRelay(Int32 DevHandle, Byte RelayState);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 CAN_Stop(Int32 DevHandle, Byte CANIndex);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int64 CAN_GetStartTime(Int32 DevHandle, Byte CANIndex);
+ [DllImport("USB2XXX.dll")]
+ public static extern Int32 CAN_ResetStartTime(Int32 DevHandle, Byte CANIndex);
//CAN Bootloader相关函数
[DllImport("USB2XXX.dll")]
diff --git a/CapMachine.Wpf/CapMachine.Wpf.csproj b/CapMachine.Wpf/CapMachine.Wpf.csproj
index 92a0e4d..30c1ad4 100644
--- a/CapMachine.Wpf/CapMachine.Wpf.csproj
+++ b/CapMachine.Wpf/CapMachine.Wpf.csproj
@@ -668,6 +668,7 @@
+
diff --git a/CapMachine.Wpf/Dtos/CANConfigExdDto.cs b/CapMachine.Wpf/Dtos/CANConfigExdDto.cs
index 7018cf1..665acbe 100644
--- a/CapMachine.Wpf/Dtos/CANConfigExdDto.cs
+++ b/CapMachine.Wpf/Dtos/CANConfigExdDto.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace CapMachine.Wpf.Dtos
{
- public class CANConfigExdDto:BindableBase
+ public class CANConfigExdDto : BindableBase
{
///
/// 主键
@@ -43,5 +43,15 @@ namespace CapMachine.Wpf.Dtos
get { return _DbcPath; }
set { _DbcPath = value; RaisePropertyChanged(); }
}
+
+ private bool _SchEnable;
+ ///
+ /// 调度表是否启用
+ ///
+ public bool SchEnable
+ {
+ get { return _SchEnable; }
+ set { _SchEnable = value; RaisePropertyChanged(); }
+ }
}
}
diff --git a/CapMachine.Wpf/Dtos/CANFdConfigExdDto.cs b/CapMachine.Wpf/Dtos/CANFdConfigExdDto.cs
new file mode 100644
index 0000000..8d4d873
--- /dev/null
+++ b/CapMachine.Wpf/Dtos/CANFdConfigExdDto.cs
@@ -0,0 +1,77 @@
+using Prism.Mvvm;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.Dtos
+{
+ public class CANFdConfigExdDto : BindableBase
+ {
+ ///
+ /// 主键
+ ///
+ public long Id { get; set; }
+
+ private int _DataBaudRate;
+ ///
+ /// 数据波特率
+ ///
+ public int DataBaudRate
+ {
+ get { return _DataBaudRate; }
+ set { _DataBaudRate = value; RaisePropertyChanged(); }
+ }
+
+ private int _ArbBaudRate;
+ ///
+ /// 仲裁波特率
+ ///
+ public int ArbBaudRate
+ {
+ get { return _ArbBaudRate; }
+ set { _ArbBaudRate = value; RaisePropertyChanged(); }
+ }
+
+ private bool _ISOEnable;
+ ///
+ /// ISO 使能
+ ///
+ public bool ISOEnable
+ {
+ get { return _ISOEnable; }
+ set { _ISOEnable = value; RaisePropertyChanged(); }
+ }
+
+ private bool _ResEnable;
+ ///
+ /// 终端电阻使能
+ ///
+ public bool ResEnable
+ {
+ get { return _ResEnable; }
+ set { _ResEnable = value; RaisePropertyChanged(); }
+ }
+
+ private int _Cycle;
+ ///
+ /// 周期
+ ///
+ public int Cycle
+ {
+ get { return _Cycle; }
+ set { _Cycle = value; RaisePropertyChanged(); }
+ }
+
+ private string? _DbcPath;
+ ///
+ /// Dbc文件路径
+ ///
+ public string? DbcPath
+ {
+ get { return _DbcPath; }
+ set { _DbcPath = value; RaisePropertyChanged(); }
+ }
+ }
+}
diff --git a/CapMachine.Wpf/Dtos/CANScheduleConfigDto.cs b/CapMachine.Wpf/Dtos/CANScheduleConfigDto.cs
new file mode 100644
index 0000000..c19970f
--- /dev/null
+++ b/CapMachine.Wpf/Dtos/CANScheduleConfigDto.cs
@@ -0,0 +1,83 @@
+using CapMachine.Model.CANLIN;
+using Prism.Mvvm;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.Dtos
+{
+ ///
+ /// CANScheduleConfigDto
+ ///
+ public class CANScheduleConfigDto : BindableBase
+ {
+ ///
+ /// 主键
+ ///
+ public long Id { get; set; }
+
+ private string? _MsgName;
+ ///
+ /// 消息名称/帧名称
+ ///
+ public string? MsgName
+ {
+ get { return _MsgName; }
+ set { _MsgName = value; RaisePropertyChanged(); }
+ }
+
+ private int _Cycle;
+ ///
+ /// 周期
+ ///
+ public int Cycle
+ {
+ get { return _Cycle; }
+ set { _Cycle = value; RaisePropertyChanged(); }
+ }
+
+ private int _OrderSend;
+ ///
+ /// 发送方式
+ ///
+ public int OrderSend
+ {
+ get { return _OrderSend; }
+ set { _OrderSend = value; RaisePropertyChanged(); }
+ }
+
+ private int _SchTabIndex;
+ ///
+ /// 调度表的Index序列
+ ///
+ public int SchTabIndex
+ {
+ get { return _SchTabIndex; }
+ set { _SchTabIndex = value; RaisePropertyChanged(); }
+ }
+
+ ///
+ /// 在更新调度表数据时,我们有一个整体的帧数据指令集合,但是这些帧数据集合,分属于不同的调度表,
+ /// 这个在开始时生成整体的帧数据指令集合才会把这个MsgIndex分配上,这个不需要保存到数据库中,对接使用
+ ///
+ public int MsgIndex { get; set; }
+
+ ///
+ /// 程序的ID
+ ///
+ public long CanLinConfigProId { get; set; }
+
+
+ private CanLinConfigPro _CanLinConfigPro;
+ ///
+ /// 所属的程序
+ ///
+ public CanLinConfigPro CanLinConfigPro
+ {
+ get { return _CanLinConfigPro; }
+ set { _CanLinConfigPro = value; RaisePropertyChanged(); }
+ }
+ }
+}
diff --git a/CapMachine.Wpf/Dtos/CanLinRWConfigDto.cs b/CapMachine.Wpf/Dtos/CanLinRWConfigDto.cs
index 034b598..ff40a6b 100644
--- a/CapMachine.Wpf/Dtos/CanLinRWConfigDto.cs
+++ b/CapMachine.Wpf/Dtos/CanLinRWConfigDto.cs
@@ -47,7 +47,7 @@ namespace CapMachine.Wpf.Dtos
set { _MsgFrameName = value; RaisePropertyChanged(); }
}
-
+
private string? _SignalName;
///
/// 配置项值
@@ -70,7 +70,12 @@ namespace CapMachine.Wpf.Dtos
set { _DefautValue = value; RaisePropertyChanged(); }
}
-
+ public long LogicRuleId { get; set; }
+ ///
+ /// CanLinConfig的逻辑转换规则
+ /// 比如:速度下发的数据SV是4000,但是下发到CAN的值是40,可能是其他的逻辑转换规则,这里就是保存其中的逻辑规则
+ ///
+ public LogicRuleDto? LogicRuleDto { get; set; }
}
}
diff --git a/CapMachine.Wpf/Dtos/LINScheduleConfigDto.cs b/CapMachine.Wpf/Dtos/LINScheduleConfigDto.cs
new file mode 100644
index 0000000..380b7eb
--- /dev/null
+++ b/CapMachine.Wpf/Dtos/LINScheduleConfigDto.cs
@@ -0,0 +1,82 @@
+using CapMachine.Model.CANLIN;
+using Prism.Mvvm;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.Dtos
+{
+ public class LINScheduleConfigDto : BindableBase
+ {
+ ///
+ /// 主键
+ ///
+ public long Id { get; set; }
+
+ private string? _MsgName;
+ ///
+ /// 消息名称/帧名称
+ ///
+ public string? MsgName
+ {
+ get { return _MsgName; }
+ set { _MsgName = value; RaisePropertyChanged(); }
+ }
+
+ private int _Cycle;
+ ///
+ /// 周期
+ ///
+ public int Cycle
+ {
+ get { return _Cycle; }
+ set { _Cycle = value; RaisePropertyChanged(); }
+ }
+
+
+ private int _SchTabIndex;
+ ///
+ /// 调度表的Index序列
+ ///
+ public int SchTabIndex
+ {
+ get { return _SchTabIndex; }
+ set { _SchTabIndex = value; RaisePropertyChanged(); }
+ }
+
+ private string? _SchTabName;
+ ///
+ /// 调度表的名称
+ ///
+ public string? SchTabName
+ {
+ get { return _SchTabName; }
+ set { _SchTabName = value; RaisePropertyChanged(); }
+ }
+
+
+ ///
+ /// 在更新调度表数据时,我们有一个整体的帧数据指令集合,但是这些帧数据集合,分属于不同的调度表,
+ /// 这个在开始时生成整体的帧数据指令集合才会把这个MsgIndex分配上,这个不需要保存到数据库中,对接使用
+ ///
+ public int MsgIndex { get; set; }
+
+ ///
+ /// 程序的ID
+ ///
+ public long CanLinConfigProId { get; set; }
+
+
+ private CanLinConfigPro _CanLinConfigPro;
+ ///
+ /// 所属的程序
+ ///
+ public CanLinConfigPro CanLinConfigPro
+ {
+ get { return _CanLinConfigPro; }
+ set { _CanLinConfigPro = value; RaisePropertyChanged(); }
+ }
+ }
+}
diff --git a/CapMachine.Wpf/Dtos/LogicRuleDto.cs b/CapMachine.Wpf/Dtos/LogicRuleDto.cs
new file mode 100644
index 0000000..7392007
--- /dev/null
+++ b/CapMachine.Wpf/Dtos/LogicRuleDto.cs
@@ -0,0 +1,73 @@
+using Prism.Mvvm;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.Dtos
+{
+ ///
+ /// 逻辑转换规则模型
+ ///
+ public class LogicRuleDto : BindableBase
+ {
+ ///
+ /// 主键ID
+ ///
+ public int Id { get; set; }
+
+
+ private string _name = string.Empty;
+ ///
+ /// 规则名称
+ ///
+ public string Name
+ {
+ get { return _name; }
+ set { SetProperty(ref _name, value); }
+ }
+
+
+ private string _description = string.Empty;
+ ///
+ /// 规则描述
+ ///
+ public string Description
+ {
+ get { return _description; }
+ set { SetProperty(ref _description, value); }
+ }
+
+ private string _expression = string.Empty;
+ ///
+ /// 规则表达式
+ ///
+ public string Expression
+ {
+ get { return _expression; }
+ set { _expression = value; RaisePropertyChanged(); }
+ }
+
+
+ private string _parameterType = string.Empty;
+ ///
+ /// 适用的参数类型(如:转速、功率等)
+ ///
+ public string ParameterType
+ {
+ get { return _parameterType; }
+ set { SetProperty(ref _parameterType, value); }
+ }
+
+ private DateTime _CreateTime;
+ ///
+ /// 适用的参数类型(如:转速、功率等)
+ ///
+ public DateTime CreateTime
+ {
+ get { return _CreateTime; }
+ set { _CreateTime = value; RaisePropertyChanged(); }
+ }
+ }
+}
diff --git a/CapMachine.Wpf/LinDrive/ToomossLin.cs b/CapMachine.Wpf/LinDrive/ToomossLin.cs
index 13fba95..de48544 100644
--- a/CapMachine.Wpf/LinDrive/ToomossLin.cs
+++ b/CapMachine.Wpf/LinDrive/ToomossLin.cs
@@ -1,11 +1,13 @@
using CapMachine.Wpf.CanDrive;
using CapMachine.Wpf.Services;
+using ImTools;
using Microsoft.VisualBasic;
using Prism.Ioc;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@@ -53,8 +55,18 @@ namespace CapMachine.Wpf.LinDrive
{
ContainerProvider = containerProvider;
HighSpeedDataService = ContainerProvider.Resolve();
+ LoggerService = ContainerProvider.Resolve();
+
+
+ //Stopwatch.Frequency表示高精度计时器每秒的计数次数(ticks/秒)每毫秒的ticks数 = 每秒的ticks数 ÷ 1000
+ TicksPerMs = Stopwatch.Frequency / 1000.0;
}
+ ///
+ /// Logger 实例
+ ///
+ public ILogService LoggerService { get; set; }
+
///
/// HighSpeedDataService 实例
///
@@ -77,7 +89,7 @@ namespace CapMachine.Wpf.LinDrive
IsExistsDllFile();
ScanDevice();
OpenDevice();
-
+
}
private bool _IsCycleRevice;
@@ -266,7 +278,7 @@ namespace CapMachine.Wpf.LinDrive
//当前帧为主机读数据帧
Console.WriteLine("[MR]Frame[{0}].Name={1},Publisher={2}", i, FrameName, PublisherName);
}
-
+
//读取信号信息
int SignalNum = LDFParser.LDF_GetFrameSignalQuantity(LDFHandle, FrameName);
for (int j = 0; j < SignalNum; j++)
@@ -310,6 +322,7 @@ namespace CapMachine.Wpf.LinDrive
LDFParser.LDF_SetSignalValue(LDFHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 1);
+ //LDFParser.LDF_ExeSchToBus(LDFHandle, new StringBuilder(itemMsg.Key), 1);
}
}
catch (Exception ex)
@@ -337,14 +350,36 @@ namespace CapMachine.Wpf.LinDrive
await Task.Delay(ReviceCycle);
try
{
- //主机读操作,读取从机返回的数据值
- foreach (var item in ListLinLdfModel)
+ var GroupMsg = ListLinLdfModel.GroupBy(x => x.MsgName);
+ foreach (var itemMsg in GroupMsg)
{
- LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(item.MsgName), 1);
- LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ReadValueStr);
- item.SignalRtValueSb = ReadValueStr;
+ var data = itemMsg.FirstOrDefault();
+ //非主机发送的指令帧就可以读取数据,因为函数 LDF_ExeFrameToBus【执行帧到总线,若该帧的发布者是主机,那么就是主机写数据,否则就是主机读数据】
+ //那么主机发送和读取都是同一个函数。会导致跟Master主机也会在这里执行写入,导致冲突,出现错误。所以做一个排出
+ if (data != null && !data.IsMasterFrame!.Contains("是"))
+ {
+ //主机读操作,读取从机返回的数据值
+ LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 1);
+ foreach (var itemSignal in itemMsg)
+ {
+ //LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 1);
+ LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), ReadValueStr);
+ itemSignal.SignalRtValueSb = ReadValueStr;
+ }
+ //报文给高速记录的服务
+ //HighSpeedDataService.AppendOrUpdateMsg(new Models.HighSpeed.CommMsg()
+ //{
+ // Category = "LIN",
+ // MsgInfo = "0x" + CanMsgBuffer[i].ID.ToString("X8"),
+ // MsgData = BitConverter.ToString(CanMsgBuffer[i].Data),
+ // Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
+ //});
+ }
+
}
+ IsReviceOk = true;
+
//StringBuilder ValueStr = new StringBuilder(64);
//LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder("ID_DATA"), 1);
//LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder("ID_DATA"), new StringBuilder("Supplier_ID"), ValueStr);
@@ -357,9 +392,11 @@ namespace CapMachine.Wpf.LinDrive
}
catch (Exception ex)
{
- //LogService.Info($"时间:{DateTime.Now.ToString()}-【Meter】-{ex.Message}");
+ IsReviceOk = false;
+ LoggerService.Info($"{ex.Message}");
}
}
+ IsReviceOk = false;
});
}
@@ -385,7 +422,18 @@ namespace CapMachine.Wpf.LinDrive
//主机写操作,发送数据给从机
LDFParser.LDF_SetSignalValue(LDFHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
}
- LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 1);
+ //【0】参数注意
+ LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 0);
+
+ //读取当前的指令帧数据,LDF_ExeFrameToBus执行后就可以读取本身的数据
+ foreach (var item in ListLinLdfModel)
+ {
+ if (CmdData.Any(a => a.MsgName == item.MsgName))
+ {
+ LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ReadValueStr);
+ item.SignalRtValueSb = ReadValueStr;
+ }
+ }
}
////主机写操作,发送数据给从机
@@ -406,6 +454,274 @@ namespace CapMachine.Wpf.LinDrive
}
+ private bool _IsSendOk;
+ ///
+ /// 发送报文是否OK
+ ///
+ public bool IsSendOk
+ {
+ get { return _IsSendOk; }
+ set
+ {
+ if (_IsSendOk != value)
+ {
+ RaisePropertyChanged();
+ _IsSendOk = value;
+ }
+ }
+ }
+
+
+ private bool _IsReviceOk;
+ ///
+ /// 接收报文是否OK
+ ///
+ public bool IsReviceOk
+ {
+ get { return _IsReviceOk; }
+ set
+ {
+ if (_IsReviceOk != value)
+ {
+ RaisePropertyChanged();
+ _IsReviceOk = value;
+ }
+ }
+ }
+
+
+ #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("定时发送延迟过大,重新校准时间");
+
+ LoggerService.Info("定时发送延迟过大,重新校准时间");
+ }
+
+ // 使用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);
+ foreach (var itemMsg in GroupMsg)
+ {
+ foreach (var itemSignal in itemMsg)
+ {
+ //主机写操作,发送数据给从机
+ LDFParser.LDF_SetSignalValue(LDFHandle, new StringBuilder(itemMsg.Key), new StringBuilder(itemSignal.SignalName), itemSignal.SignalCmdValue);
+ }
+ //【0】参数注意
+ LDFParser.LDF_ExeFrameToBus(LDFHandle, new StringBuilder(itemMsg.Key), 0);
+
+ //读取当前的指令帧数据,LDF_ExeFrameToBus执行后就可以读取本身的数据
+ foreach (var item in ListLinLdfModel)
+ {
+ if (CmdData.Any(a => a.MsgName == item.MsgName))
+ {
+ LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder(item.MsgName), new StringBuilder(item.SignalName), ReadValueStr);
+ item.SignalRtValueSb = ReadValueStr;
+ }
+ }
+ }
+
+ IsSendOk = true;
+ }
+
+ }
+ catch (TaskCanceledException)
+ {
+ IsSendOk = false;
+ // 任务被取消,正常退出
+ break;
+ }
+ catch (Exception ex)
+ {
+ IsSendOk = false;
+ Console.WriteLine($"LIN周期发送异常: {ex.Message}");
+ // 短暂暂停避免异常情况下CPU占用过高
+ await Task.Delay(10, token);
+ }
+ }
+
+ IsSendOk = false;
+ }
+ catch (Exception ex)
+ {
+ IsSendOk = false;
+ // 确保在任何情况下(正常退出、异常、取消)都会停止计时器
+ Stopwatcher.Stop();
+ LoggerService.Info("接收出现异常");
+ // 清理其他可能的资源
+ Console.WriteLine("LIN周期发送任务已结束,资源已清理");
+ }
+ finally
+ {
+ IsSendOk = false;
+ // 确保在任何情况下(正常退出、异常、取消)都会停止计时器
+ Stopwatcher.Stop();
+ }
+
+ }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
+ }
+
+
+ ///
+ /// 修改停止发送的方法
+ ///
+ public void StopCycleSendMsg()
+ {
+ IsCycleSend = false;
+ CycleSendCts?.Cancel();
+ }
+
+ #endregion
+
+ #region 调度器发送报文
+
+ private bool _SchEnable;
+ ///
+ /// 调度表使能
+ ///
+ public bool SchEnable
+ {
+ get { return _SchEnable; }
+ set
+ {
+ _SchEnable = value;
+ RaisePropertyChanged();
+ }
+ }
+
+ ///
+ /// 调度表数量
+ ///
+ public int SchCount { get; set; }
+
+
+ ///
+ /// 获取调度表数量
+ ///
+ ///
+ public int GetSchCount()
+ {
+ SchCount = LDFParser.LDF_GetSchQuantity(LDFHandle);
+ return SchCount;
+ }
+
+
+
+ public void LinTest()
+ {
+ GetSchCount();
+
+ for (int i = 0; i < SchCount; i++)
+ {
+ StringBuilder pSchName = new StringBuilder();
+ var Ret = LDFParser.LDF_GetSchName(LDFHandle, i, pSchName);
+ if (Ret <= 0)
+ {
+ Console.WriteLine($"{pSchName.ToString()}");
+ }
+ else
+ {
+ Console.WriteLine("执行失败");
+ }
+ }
+
+ }
+
+ #endregion
+
///
/// 关闭设备
///
diff --git a/CapMachine.Wpf/MapperProfile/CANFdConfigExdProfile.cs b/CapMachine.Wpf/MapperProfile/CANFdConfigExdProfile.cs
new file mode 100644
index 0000000..abafe8f
--- /dev/null
+++ b/CapMachine.Wpf/MapperProfile/CANFdConfigExdProfile.cs
@@ -0,0 +1,19 @@
+using AutoMapper;
+using CapMachine.Model.CANLIN;
+using CapMachine.Wpf.Dtos;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.MapperProfile
+{
+ public class CANFdConfigExdProfile : Profile
+ {
+ public CANFdConfigExdProfile()
+ {
+ CreateMap().ReverseMap();
+ }
+ }
+}
diff --git a/CapMachine.Wpf/MapperProfile/CANScheduleConfigProfile.cs b/CapMachine.Wpf/MapperProfile/CANScheduleConfigProfile.cs
new file mode 100644
index 0000000..b03cc2e
--- /dev/null
+++ b/CapMachine.Wpf/MapperProfile/CANScheduleConfigProfile.cs
@@ -0,0 +1,14 @@
+using AutoMapper;
+using CapMachine.Model.CANLIN;
+using CapMachine.Wpf.Dtos;
+
+namespace CapMachine.Wpf.MapperProfile
+{
+ public class CANScheduleConfigProfile : Profile
+ {
+ public CANScheduleConfigProfile()
+ {
+ CreateMap().ReverseMap();
+ }
+ }
+}
diff --git a/CapMachine.Wpf/MapperProfile/LINScheduleConfigProfile.cs b/CapMachine.Wpf/MapperProfile/LINScheduleConfigProfile.cs
new file mode 100644
index 0000000..7654552
--- /dev/null
+++ b/CapMachine.Wpf/MapperProfile/LINScheduleConfigProfile.cs
@@ -0,0 +1,14 @@
+using AutoMapper;
+using CapMachine.Model.CANLIN;
+using CapMachine.Wpf.Dtos;
+
+namespace CapMachine.Wpf.MapperProfile
+{
+ public class LINScheduleConfigProfile : Profile
+ {
+ public LINScheduleConfigProfile()
+ {
+ CreateMap().ReverseMap();
+ }
+ }
+}
diff --git a/CapMachine.Wpf/MapperProfile/LogicRuleProfile.cs b/CapMachine.Wpf/MapperProfile/LogicRuleProfile.cs
new file mode 100644
index 0000000..51e2843
--- /dev/null
+++ b/CapMachine.Wpf/MapperProfile/LogicRuleProfile.cs
@@ -0,0 +1,19 @@
+using AutoMapper;
+using CapMachine.Model;
+using CapMachine.Wpf.Dtos;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.MapperProfile
+{
+ public class LogicRuleProfile : Profile
+ {
+ public LogicRuleProfile()
+ {
+ CreateMap().ReverseMap();
+ }
+ }
+}
diff --git a/CapMachine.Wpf/Models/CanLinRunStateModel.cs b/CapMachine.Wpf/Models/CanLinRunStateModel.cs
index 2241dcf..b013e53 100644
--- a/CapMachine.Wpf/Models/CanLinRunStateModel.cs
+++ b/CapMachine.Wpf/Models/CanLinRunStateModel.cs
@@ -33,6 +33,10 @@ namespace CapMachine.Wpf.Models
SelectedCanLinMsg = "CAN";
CanLinRunState=true;
break;
+ case CanLinEnum.CANFD:
+ SelectedCanLinMsg = "CANFd";
+ CanLinRunState = true;
+ break;
case CanLinEnum.Lin:
SelectedCanLinMsg = "LIN";
CanLinRunState = true;
diff --git a/CapMachine.Wpf/Models/ComEnum.cs b/CapMachine.Wpf/Models/ComEnum.cs
index 8f7b3e9..b02b82e 100644
--- a/CapMachine.Wpf/Models/ComEnum.cs
+++ b/CapMachine.Wpf/Models/ComEnum.cs
@@ -79,6 +79,11 @@ namespace CapMachine.Wpf.Models
/// Lin
///
Lin = 2,
+
+ ///
+ /// CAN FD
+ ///
+ CANFD = 3,
}
}
}
diff --git a/CapMachine.Wpf/PrismEvent/LogicRuleChangeEvent.cs b/CapMachine.Wpf/PrismEvent/LogicRuleChangeEvent.cs
new file mode 100644
index 0000000..7721b9d
--- /dev/null
+++ b/CapMachine.Wpf/PrismEvent/LogicRuleChangeEvent.cs
@@ -0,0 +1,14 @@
+using Prism.Events;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CapMachine.Wpf.PrismEvent
+{
+ public class LogicRuleChangeEvent : PubSubEvent
+ {
+
+ }
+}
diff --git a/CapMachine.Wpf/ProPars/ProParsSongZhiHelper.cs b/CapMachine.Wpf/ProPars/ProParsSongZhiHelper.cs
index 500f31c..6dc25ca 100644
--- a/CapMachine.Wpf/ProPars/ProParsSongZhiHelper.cs
+++ b/CapMachine.Wpf/ProPars/ProParsSongZhiHelper.cs
@@ -39,7 +39,7 @@ namespace CapMachine.Wpf.ProPars
new PlcParsData(){ Name="冷凝器循环水温",EnName="Cond1Temp",SetAddressInfo=AddressInfo.OK,Step=150,Steps=new List(),Ratio=10,Unit="",ValueStartAddress=1074,MinStartAddress=1066,SecStartAddress=1068,CycleStartAddress=1072,SlopConstAddress=1070 },
new PlcParsData(){ Name="吸气混合器温度",EnName="OS2Temp",Step=150,SetAddressInfo=AddressInfo.NoCycle,Steps=new List(),Ratio=10,Unit="" ,ValueStartAddress=1088,MinStartAddress=1080,SecStartAddress=1082 },//??
new PlcParsData(){ Name="HV电压",EnName="HVVol",Step=150,SetAddressInfo=AddressInfo.OK,Steps=new List(),Ratio=10,Unit="" ,ValueStartAddress=1102,MinStartAddress=1094,SecStartAddress=1096,CycleStartAddress=1100,SlopConstAddress=1198 },
- new PlcParsData(){ Name="LV电压",EnName="LVVol",Step=150,SetAddressInfo=AddressInfo.NoCycle,Steps=new List(),Ratio=10,Unit="" ,ValueStartAddress=1114,MinStartAddress=1106,SecStartAddress=1108},
+ new PlcParsData(){ Name="LV电压",EnName="LVVol",Step=150,SetAddressInfo=AddressInfo.NoCycle,Steps=new List(),Ratio=1,Unit="" ,ValueStartAddress=1114,MinStartAddress=1106,SecStartAddress=1108},
new PlcParsData(){ Name="压缩机环境温度",EnName="EnvTemp",Step=150,SetAddressInfo=AddressInfo.OK,Steps=new List(),Ratio=10,Unit="" ,ValueStartAddress=1128,MinStartAddress=1120,SecStartAddress=1122,CycleStartAddress=1126,SlopConstAddress=1124 },
new PlcParsData(){ Name="压缩机环境湿度",EnName="EnvRH",Step=150,SetAddressInfo=AddressInfo.NoCycle,Steps=new List(),Ratio=10,Unit="" ,ValueStartAddress=1142,MinStartAddress=1134,SecStartAddress=1136},
diff --git a/CapMachine.Wpf/Services/CanDriveService.cs b/CapMachine.Wpf/Services/CanDriveService.cs
index a2c6820..ded2774 100644
--- a/CapMachine.Wpf/Services/CanDriveService.cs
+++ b/CapMachine.Wpf/Services/CanDriveService.cs
@@ -1,5 +1,6 @@
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.CanDrive;
+using CapMachine.Wpf.Dtos;
using ImTools;
using Prism.Ioc;
using Prism.Mvvm;
@@ -20,15 +21,17 @@ namespace CapMachine.Wpf.Services
{
public HighSpeedDataService HighSpeedDataService { get; }
+ public LogicRuleService LogicRuleService { get; }
///
/// 实例化函数
///
- public CanDriveService(HighSpeedDataService highSpeedDataService, IContainerProvider containerProvider)
+ public CanDriveService(HighSpeedDataService highSpeedDataService, IContainerProvider containerProvider, LogicRuleService logicRuleService)
{
ToomossCanDrive = new ToomossCan(containerProvider);
//高速数据服务
HighSpeedDataService = highSpeedDataService;
+ LogicRuleService = logicRuleService;
//ToomossCanDrive.StartCanDrive();
}
@@ -97,12 +100,38 @@ namespace CapMachine.Wpf.Services
///
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();
+ ///
+ /// CNA 调度表的配置信息
+ ///
+ public List ListCANScheduleConfig { get; set; }
+
///
/// 增加发送的指令数据
///
@@ -124,6 +153,18 @@ namespace CapMachine.Wpf.Services
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;
}
@@ -139,14 +180,26 @@ namespace CapMachine.Wpf.Services
///
public void UpdateSpeedCmdData(double SpeedData)
{
+ //if (SpeedCanCmdData != null)
+ //{
+ // SpeedCanCmdData.SignalCmdValue = SpeedData;
+ //}
if (SpeedCanCmdData != null)
{
- SpeedCanCmdData.SignalCmdValue = SpeedData;
+ //首先是否判断是有斜率
+ if (SpeedCanCmdData.LogicRuleDto == null)
+ {
+ //没有启动逻辑规则处理
+ SpeedCanCmdData.SignalCmdValue = SpeedData;
+ }
+ else
+ {
+ //LogicRuleService.ApplyExpressionFast(SpeedData, SpeedCanCmdData.LogicRuleDto);
+ SpeedCanCmdData.SignalCmdValue = LogicRuleService.ApplyExpressionFast(SpeedData, SpeedCanCmdData.LogicRuleDto);
+ //Console.WriteLine($"实时转换后转速值:{SpeedCanCmdData.SignalCmdValue}-SV值:{SpeedData}");
+ }
+
}
- //if (EnableCanCmdData != null)
- //{
- // EnableCanCmdData.SignalCmdValue = 1;
- //}
}
///
@@ -162,6 +215,67 @@ namespace CapMachine.Wpf.Services
}
+ ///
+ /// 更新压缩机的功率限制
+ ///
+ ///
+ 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 驱动
///
@@ -199,9 +313,39 @@ namespace CapMachine.Wpf.Services
{
if (CmdData.Count > 0)
{
- ToomossCanDrive.IsCycleSend = true;
+
ToomossCanDrive.CmdData = CmdData;
- ToomossCanDrive.StartCycleSendMsg();
+
+ if (ToomossCanDrive.SchEnable)
+ {
+
+ //使用调度表的话,需要在调度表中配置信息后才可以进行操作
+ var GroupMsg = ToomossCanDrive.CmdData.GroupBy(a => a.MsgName).ToList();
+ foreach (var itemMsg in GroupMsg)
+ {
+ if (!ListCANScheduleConfig.ToList().Any(a => a.MsgName == itemMsg.Key))
+ {
+ System.Windows.MessageBox.Show($"你使能了调度表,但是调度表中没有设置【{itemMsg.Key}】信息,请设置后再操作", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
+ return;
+ }
+ }
+
+ if (ListCANScheduleConfig == null && ListCANScheduleConfig!.Count() == 0)
+ {
+ System.Windows.MessageBox.Show("调度表配置为空数据,请检查!将无法发送数据", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand);
+ return;
+ }
+ ToomossCanDrive.ListCANScheduleConfig = ListCANScheduleConfig!;
+ ToomossCanDrive.StartSchedule();
+ ToomossCanDrive.StartCycleUpdateCmd();
+ }
+ else
+ {
+ ToomossCanDrive.StartPrecisionCycleSendMsg();
+ //ToomossCanDrive.StartCycleSendMsg();
+ }
+
+ ToomossCanDrive.IsCycleSend = true;
}
else
{
@@ -210,7 +354,15 @@ namespace CapMachine.Wpf.Services
}
else
{
- ToomossCanDrive.IsCycleSend = false;
+ if (ToomossCanDrive.SchEnable)
+ {
+ ToomossCanDrive.IsCycleSend = false;
+ ToomossCanDrive.StopSchedule();
+ }
+ else
+ {
+ ToomossCanDrive.IsCycleSend = false;
+ }
}
}
@@ -230,7 +382,7 @@ namespace CapMachine.Wpf.Services
if (ListCanDbcModel.Count > 0)
{
ToomossCanDrive.IsCycleRevice = true;
- ToomossCanDrive.StartPrecisionCycleSendMsg();
+ ToomossCanDrive.StartCycleReviceCanMsg();
}
else
{
@@ -277,7 +429,9 @@ namespace CapMachine.Wpf.Services
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;
}
diff --git a/CapMachine.Wpf/Services/CanFdDriveService.cs b/CapMachine.Wpf/Services/CanFdDriveService.cs
new file mode 100644
index 0000000..42e8901
--- /dev/null
+++ b/CapMachine.Wpf/Services/CanFdDriveService.cs
@@ -0,0 +1,414 @@
+using CapMachine.Model.CANLIN;
+using CapMachine.Wpf.CanDrive;
+using CapMachine.Wpf.Models;
+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 LogicRuleService LogicRuleService { get; }
+
+ ///
+ /// 实例化函数
+ ///
+ public CanFdDriveService(HighSpeedDataService highSpeedDataService, IContainerProvider containerProvider, LogicRuleService logicRuleService)
+ {
+ ToomossCanFDDrive = new ToomossCanFD(containerProvider);
+ //高速数据服务
+ HighSpeedDataService = highSpeedDataService;
+ LogicRuleService = logicRuleService;
+
+ //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)
+ {
+ if (SpeedCanCmdData.LogicRuleDto == null)
+ {
+ //没有启动逻辑规则处理
+ SpeedCanCmdData.SignalCmdValue = SpeedData;
+ return;
+ }
+
+ SpeedCanCmdData.SignalCmdValue = LogicRuleService.ApplyExpressionFast(SpeedData, SpeedCanCmdData.LogicRuleDto);
+ }
+ //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)
+ {
+ if (PwLimitCanCmdData.LogicRuleDto == null)
+ {
+ PwLimitCanCmdData.SignalCmdValue = PwLimit;
+ return;
+ }
+ PwLimitCanCmdData.SignalCmdValue = LogicRuleService.ApplyExpressionFast(PwLimit, PwLimitCanCmdData.LogicRuleDto!);
+ }
+ }
+
+
+ ///
+ /// 更新 PTC使能信号
+ ///
+ ///
+ public void UpdateCapPTCEnableCmdData(bool IsEnable)
+ {
+ if (PTCEnableCanCmdData != null)
+ {
+ PTCEnableCanCmdData.SignalCmdValue = IsEnable ? 1 : 0;
+ }
+ }
+
+ ///
+ /// 更新 PTC功率 信号
+ ///
+ ///
+ public void UpdateCapPTCPwCmdData(double PTCPw)
+ {
+ if (PTCPwCanCmdData != null)
+ {
+ if (PTCPwCanCmdData.LogicRuleDto == null)
+ {
+ PTCPwCanCmdData.SignalCmdValue = PTCPw;
+ return;
+ }
+ PTCPwCanCmdData.SignalCmdValue = LogicRuleService.ApplyExpressionFast(PTCPw, PTCPwCanCmdData.LogicRuleDto!);
+ }
+ }
+
+ ///
+ /// 更新 PTC水流量 信号
+ ///
+ ///
+ public void UpdateCapPTCFlowCmdData(double Flow)
+ {
+ if (PTCFlowCanCmdData != null)
+ {
+ if (PTCFlowCanCmdData.LogicRuleDto == null)
+ {
+ PTCFlowCanCmdData.SignalCmdValue = Flow;
+ return;
+ }
+ PTCFlowCanCmdData.SignalCmdValue = LogicRuleService.ApplyExpressionFast(Flow, PTCFlowCanCmdData.LogicRuleDto!);
+ }
+ }
+
+ ///
+ /// 更新 PTC水温 信号
+ ///
+ ///
+ public void UpdateCapPTCWaterTempCmdData(double WaterTemp)
+ {
+ if (PTCWaterTempCanCmdData != null)
+ {
+ if (PTCWaterTempCanCmdData.LogicRuleDto == null)
+ {
+ PTCWaterTempCanCmdData.SignalCmdValue = WaterTemp;
+ return;
+ }
+ PTCWaterTempCanCmdData.SignalCmdValue = LogicRuleService.ApplyExpressionFast(WaterTemp, PTCWaterTempCanCmdData.LogicRuleDto!);
+ }
+ }
+
+ ///
+ /// 发送消息给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 12.3;
+ }
+ 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 2300;
+ }
+ return 0;
+ }
+
+ #endregion
+
+ }
+}
diff --git a/CapMachine.Wpf/Services/LogicRuleService.cs b/CapMachine.Wpf/Services/LogicRuleService.cs
new file mode 100644
index 0000000..5adc573
--- /dev/null
+++ b/CapMachine.Wpf/Services/LogicRuleService.cs
@@ -0,0 +1,578 @@
+using AutoMapper;
+using CapMachine.Core;
+using CapMachine.Model;
+using CapMachine.Wpf.Dtos;
+using DynamicExpresso;
+using FreeSql;
+using FreeSql.DataAnnotations;
+using Prism.Mvvm;
+using Syncfusion.Windows.Shared;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace CapMachine.Wpf.Services
+{
+ ///
+ /// 逻辑服务 - 用于处理数据逻辑转换
+ ///
+ public class LogicRuleService : BindableBase
+ {
+ private readonly ILogService LogService;
+ private readonly IFreeSql FreeSql;
+
+ ///
+ /// 逻辑转换规则集合
+ ///
+ public ObservableCollection LogicRuleDtos { get; private set; }
+
+ ///
+ /// DynamicExpresso解释器
+ ///
+ private Interpreter CurInterpreter { get; set; }
+ public IMapper Mapper { get; }
+
+ ///
+ /// 规则表达式缓存(高性能访问)
+ /// Key: 规则名称, Value: 已编译的Lambda表达式
+ ///private readonly ConcurrentDictionary> _expressionCache;
+ ///
+ private readonly ConcurrentDictionary> _expressionCache;
+
+ /////
+ ///// 创建包含指定值的参数数组
+ /////
+ //private Parameter[] CreateParameters(double value)
+ //{
+ // return new Parameter[]
+ // {
+ // new Parameter("value", typeof(double), value)
+ // };
+ //}
+
+ ///
+ /// 创建包含指定值的参数
+ ///
+ private Parameter CreateParameters(double value)
+ {
+ return new Parameter("value", typeof(double), value);
+
+ }
+
+ ///
+ /// 实例化函数
+ ///
+ public LogicRuleService(ILogService logService, IFreeSql freeSql, IMapper mapper)
+ {
+ LogService = logService;
+ FreeSql = freeSql;
+ Mapper = mapper;
+
+ // 初始化集合
+ LogicRuleDtos = new ObservableCollection();
+
+ _expressionCache = new ConcurrentDictionary>();
+
+ // 初始化DynamicExpresso解释器
+ CurInterpreter = new Interpreter();
+
+ try
+ {
+ // 从数据库加载规则
+ LoadRulesFromDatabase();
+
+ }
+ catch (Exception ex)
+ {
+ LogService.Error($"初始化逻辑服务失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 从数据库加载规则
+ ///
+ private void LoadRulesFromDatabase()
+ {
+ try
+ {
+ // 从数据库加载规则
+ var dbRules = FreeSql.Select().OrderBy(a => a.Id).ToList();
+ if (dbRules != null && dbRules.Count > 0)
+ {
+ LogicRuleDtos.Clear();
+ _expressionCache.Clear(); // 清空表达式缓存
+
+ foreach (var rule in dbRules)
+ {
+ LogicRuleDtos.Add(Mapper.Map(rule));
+ }
+
+ // 预编译所有表达式
+ WarmUpExpressionCache();
+
+ //LogService.Info($"已从数据库加载并预编译 {dbRules.Count} 条逻辑规则");
+ }
+ else
+ {
+ LogService.Warn("数据库中没有逻辑规则");
+ }
+ }
+ catch (Exception ex)
+ {
+ LogService.Error($"从数据库加载逻辑规则失败: {ex.Message}");
+ throw; // 重新抛出异常以便调用者处理
+ }
+ }
+
+
+ #region 增删改查LogicRule
+
+
+ ///
+ /// 添加新规则
+ ///
+ /// 规则对象
+ public void AddRule(LogicRule rule)
+ {
+ if (string.IsNullOrWhiteSpace(rule.Name))
+ {
+ MessageBox.Show("规则名称不能为空");
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(rule.Expression))
+ {
+ MessageBox.Show("规则表达式不能为空");
+ return;
+ }
+
+ if (LogicRuleDtos.Where(a => a.Name == rule.Name).Any())
+ {
+ MessageBox.Show("已经有另一个相同名称的规则了");
+ return;
+ }
+
+ // 验证表达式是否有效
+ if (!ValidateExpression(rule.Expression))
+ {
+ MessageBox.Show("规则表达式验证失败");
+ return;
+ }
+
+ if (InsertRuleToDb(rule, out LogicRule resultInsert))
+ {
+ //此时的resultInsert有新增的ID
+ // 添加规则
+ LogicRuleDtos.Add(Mapper.Map(resultInsert));
+
+ // 预编译并缓存表达式
+ CacheExpression(Mapper.Map(resultInsert));
+ }
+ else
+ {
+ MessageBox.Show("增加数据失败!");
+ return;
+ }
+ //LogService.Info($"添加新规则: {rule.Name}");
+ }
+
+ ///
+ /// 保存规格到数据库中
+ ///
+ ///
+ public bool InsertRuleToDb(LogicRule rule, out LogicRule ResultInsert)
+ {
+ try
+ {
+ // 插入规则到数据库
+ var result = FreeSql.Insert(rule).ExecuteInserted();
+
+ // 检查影响的行数
+ bool success = result.Count > 0;
+
+ if (success)
+ {
+ ResultInsert = result.FirstOrDefault()!;
+ return success;
+ //LogService.Info($"成功插入规则: {rule.Name}");
+
+ // 刷新内存中的规则集合
+ //LoadRulesFromDatabase();
+ }
+ else
+ {
+ LogService.Warn($"插入规则失败: {rule.Name},没有行受影响");
+ ResultInsert = null;
+ return success;
+ }
+
+
+ }
+ catch (Exception ex)
+ {
+ LogService.Error($"插入规则到数据库时发生异常: {ex.Message}");
+ ResultInsert = null;
+ return false;
+ }
+ }
+
+ ///
+ /// 更新规则
+ ///
+ /// 规则对象
+ public void UpdateRule(LogicRule ruleOld, LogicRule rule)
+ {
+ if (string.IsNullOrWhiteSpace(rule.Name))
+ {
+ MessageBox.Show("规则名称不能为空");
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(rule.Expression))
+ {
+ MessageBox.Show("规则表达式不能为空");
+ return;
+ }
+
+
+ // 验证表达式是否有效
+ if (!ValidateExpression(rule.Expression))
+ {
+ MessageBox.Show("规则表达式验证失败");
+ return;
+ }
+
+ if (UpdateRuleToDb(rule))
+ {
+ // 更新缓存
+ var updatedDto = LogicRuleDtos.FirstOrDefault(r => r.Id == rule.Id);
+ var insertIndex = LogicRuleDtos.ToList().FindIndex(x => x.Id > rule.Id);
+ if (updatedDto != null)
+ {
+ //移除数据
+ LogicRuleDtos.Remove(updatedDto);
+ if (insertIndex == -1)
+ {
+ LogicRuleDtos.Add(Mapper.Map(rule));
+ }
+ else
+ {
+ // 在找到的位置插入
+ LogicRuleDtos.Insert(insertIndex - 1, Mapper.Map(rule));
+ }
+
+ //有可能更改的是名称,那么新名称的话在_expressionCache中肯定是找不到的,可以用之前的ruleOld
+ _expressionCache.TryRemove(ruleOld.Name!, out _);
+ CacheExpression(updatedDto);
+ }
+ }
+
+ //LogService.Info($"更新规则: {rule.Name}");
+ }
+
+ ///
+ /// 更新规格到数据库中
+ ///
+ ///
+ public bool UpdateRuleToDb(LogicRule rule)
+ {
+ try
+ {
+ // 更新规则到数据库
+ var result = FreeSql.Update()
+ .Set(a => a.Name, rule.Name)
+ .Set(a => a.Description, rule.Description)
+ .Set(a => a.Expression, rule.Expression)
+ .Set(a => a.ParameterType, rule.ParameterType)
+ .Where(r => r.Id == rule.Id)
+ .ExecuteUpdated();
+
+ // 检查影响的行数
+ bool success = result.Count() > 0;
+
+ if (success)
+ {
+ //LogService.Info($"成功更新规则: {rule.Name}");
+
+ }
+ else
+ {
+ //LogService.Warn($"更新规则失败: {rule.Name},没有行受影响");
+ }
+
+ return success;
+ }
+ catch (Exception ex)
+ {
+ //LogService.Error($"更新规则到数据库时发生异常: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// 删除规则
+ ///
+ /// 规则名称
+ public void DeleteRule(string ruleName)
+ {
+ var rule = LogicRuleDtos.FirstOrDefault(r => r.Name == ruleName);
+ if (rule == null)
+ {
+ MessageBox.Show("找不到删除的数据");
+ return;
+ }
+
+ var Count = FreeSql.Delete().Where(r => r.Id == rule.Id).ExecuteAffrows();
+ if (Count > 0)
+ {
+ // 删除规则
+ LogicRuleDtos.Remove(rule);
+ // 从缓存中移除表达式
+ _expressionCache.TryRemove(ruleName, out _);
+ }
+
+ //LogService.Info($"删除规则: {ruleName}");
+ }
+
+ ///
+ /// 验证表达式是否有效
+ ///
+ /// 表达式字符串
+ /// 表达式是否有效
+ private bool ValidateExpression(string expression)
+ {
+ try
+ {
+ // 创建测试解释器并直接设置变量
+ var interpreter = new Interpreter()
+ .Reference(typeof(Math)) // 引用Math类
+ .SetVariable("Math", typeof(Math))
+ .SetVariable("value", 100); // 直接设置值变量
+
+ // 尝试编译表达式
+ var lambda = interpreter.Parse(expression);
+
+ // 尝试执行表达式
+ var result = lambda.Invoke();
+
+ // 检查结果是否为数值类型
+ if (result is double doubleResult)
+ {
+ // 验证结果不是NaN或Infinity
+ if (double.IsNaN(doubleResult) || double.IsInfinity(doubleResult))
+ {
+ //LogService.Warn($"表达式执行结果无效 (NaN或Infinity): {expression}");
+ return false;
+ }
+ }
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ LogService.Warn($"验证表达式失败: {expression}, 错误: {ex.Message}");
+ return false;
+ }
+ }
+
+
+ #endregion
+
+
+ ///
+ /// 根据参数类型获取适用的规则
+ ///
+ /// 参数类型
+ /// 适用的规则列表
+ public IEnumerable GetRulesByParameterType(string parameterType)
+ {
+ return LogicRuleDtos.Where(r => r.ParameterType == parameterType);
+ }
+
+ ///
+ /// 获取指定名称的规则
+ ///
+ /// 规则名称
+ /// 规则对象
+ public LogicRuleDto GetRuleByName(string ruleName)
+ {
+ var rule = LogicRuleDtos.FirstOrDefault(r => r.Name == ruleName);
+ if (rule == null)
+ {
+ throw new KeyNotFoundException($"找不到名为 {ruleName} 的规则");
+ }
+ return rule;
+ }
+
+
+ ///
+ /// 将规则表达式预编译并缓存到字典中
+ ///
+ /// 要缓存的规则
+ private void CacheExpression(LogicRuleDto rule)
+ {
+ if (string.IsNullOrEmpty(rule.Name) || string.IsNullOrEmpty(rule.Expression))
+ {
+ return; // 防止空名称或空表达式
+ }
+
+ try
+ {
+ // ****如下步骤很重要,尝试了很多次才正常运行****
+
+ // 使用已有的CreateParameters方法创建参数声明
+ var parameter = CreateParameters(0); // 值不重要,只是为了获取参数声明
+ // 只解析一次,得到 Lambda 对象
+ var lambda = CurInterpreter.Parse(rule.Expression, parameter); // 只定义参数类型,不传值
+ // 编译为强类型委托
+ var compiledFunc = lambda.Compile>();
+
+ // 存入缓存
+ _expressionCache[rule.Name] = compiledFunc;
+ }
+ catch (Exception ex)
+ {
+ LogService.Error($"缓存规则 {rule.Name} 表达式失败: {ex.Message}");
+ }
+ }
+
+ ///
+ /// 预热表达式缓存 - 预编译所有规则
+ ///
+ private void WarmUpExpressionCache()
+ {
+ foreach (var rule in LogicRuleDtos)
+ {
+ if (!_expressionCache.ContainsKey(rule.Name))
+ {
+ CacheExpression(rule);
+ }
+ }
+ LogService.Info($"已预编译 {_expressionCache.Count} 条规则表达式");
+ }
+
+ ///
+ /// 根据规则应用表达式(高性能版本)
+ ///
+ /// 输入值
+ /// 规则对象
+ /// 转换后的输出值
+ public double ApplyExpression(double value, LogicRuleDto rule)
+ {
+ try
+ {
+ // 尝试从缓存获取预编译的表达式
+ if (!_expressionCache.TryGetValue(rule.Name, out var compiledExpression))
+ {
+ // 如果缓存中没有,则编译并添加到缓存
+ CacheExpression(rule);
+
+ // 再次尝试获取
+ if (!_expressionCache.TryGetValue(rule.Name, out compiledExpression))
+ {
+ // 初始解析表达式(只需执行一次)
+ string expressionText = rule.Expression;
+
+ // 强类型函数
+ Func compiledFunc = (v) =>
+ {
+ // 高性能版本:直接根据公式计算
+ try
+ {
+ if (expressionText.Contains("value"))
+ {
+ var parameters = CreateParameters(v);
+ var lambda = CurInterpreter.Parse(expressionText, parameters);
+ return (double)lambda.Invoke();
+ }
+ else
+ {
+ // 简单表达式使用直接求值
+ return v / 100.0; // 默认处理
+ }
+ }
+ catch
+ {
+ // 出错时返回原值
+ return v;
+ }
+ };
+ // 再次尝试缓存
+ CacheExpression(rule);
+
+ return compiledFunc(value);
+ }
+ }
+
+ // 创建包含实际值的参数
+ var parameter = new Parameter("value", typeof(double), value);
+ // 使用预编译的表达式执行计算(高性能)
+ var convertedValue = (double)compiledExpression.Invoke(value);
+
+ return convertedValue;
+ }
+ catch (Exception ex)
+ {
+ //LogService.Error($"应用规则 {rule.Name} 失败: {ex.Message}");
+ return value; // 出错时返回原始值
+ }
+ }
+
+
+ ///
+ /// 快速的执行数据
+ ///
+ ///
+ ///
+ ///
+ public double ApplyExpressionFast(double value, LogicRuleDto rule)
+ {
+ try
+ {
+ if (!_expressionCache.TryGetValue(rule.Name, out var CurActiveFunc))
+ {
+ CacheExpression(rule);
+ if (!_expressionCache.TryGetValue(rule.Name, out CurActiveFunc))
+ return value;
+ }
+
+ // 直接调用委托,无反射开销
+ return CurActiveFunc(value);
+ }
+ catch
+ {
+ return value;
+ }
+ }
+
+
+ ///
+ /// 如果需要对多个数据应用相同规则,考虑实现批处理版本:
+ ///
+ ///
+ ///
+ ///
+ public IEnumerable ApplyExpressionBatch(IEnumerable values, LogicRuleDto rule)
+ {
+ if (!_expressionCache.TryGetValue(rule.Name, out var func))
+ {
+ CacheExpression(rule);
+ if (!_expressionCache.TryGetValue(rule.Name, out func))
+ return values;
+ }
+
+ // 使用并行处理大量数据
+ return values.AsParallel().Select(v =>
+ {
+ try { return func(v); }
+ catch { return v; }
+ });
+ }
+
+ }
+}
diff --git a/CapMachine.Wpf/Services/MachineRtDataService.cs b/CapMachine.Wpf/Services/MachineRtDataService.cs
index 2e75282..74ae66a 100644
--- a/CapMachine.Wpf/Services/MachineRtDataService.cs
+++ b/CapMachine.Wpf/Services/MachineRtDataService.cs
@@ -34,6 +34,7 @@ namespace CapMachine.Wpf.Services
public AlarmService AlarmService { get; }
public ConfigService ConfigService { get; }
public CanDriveService CanDriveService { get; }
+ public CanFdDriveService CanFdDriveService { get; }
public LinDriveService LinDriveService { get; }
public SysRunService SysRunService { get; }
@@ -112,7 +113,7 @@ namespace CapMachine.Wpf.Services
///
///
public MachineRtDataService(IEventAggregator eventAggregator, AlarmService alarmService, ConfigService configService,
- CanDriveService canDriveService, LinDriveService linDriveService, SysRunService sysRunService)//, AlarmService alarmService
+ CanDriveService canDriveService, CanFdDriveService canFdDriveService, LinDriveService linDriveService, SysRunService sysRunService)//, AlarmService alarmService
{
//ConcurrentDictionary keyValuePairs = new ConcurrentDictionary();
@@ -128,6 +129,7 @@ namespace CapMachine.Wpf.Services
AlarmService = alarmService;
ConfigService = configService;
CanDriveService = canDriveService;
+ CanFdDriveService = canFdDriveService;
LinDriveService = linDriveService;
SysRunService = sysRunService;
@@ -886,20 +888,20 @@ namespace CapMachine.Wpf.Services
///
public bool SysReset()
{
- var FindData = ListHandSwitchData.Where(a => a.Name == "复位");
- if (FindData.Count() > 0)
+ //var FindData = ListHandSwitchData.Where(a => a.Name == "复位");
+ //if (FindData.Count() > 0)
+ //{
+ var Result = SiemensDrive.Write("M0.2", true);
+ if (Result.IsSuccess)
{
- var Result = SiemensDrive.Write(FindData.First().ActionAddress, true);
- if (Result.IsSuccess)
- {
- return true;
- }
- else
- {
- return false;
- }
+ return true;
}
- return false;
+ else
+ {
+ return false;
+ }
+ //}
+ //return false;
}
///
@@ -1177,6 +1179,9 @@ namespace CapMachine.Wpf.Services
SiemensDrive.Write(itemTag.Value.PVAddress, (short)CanDriveService.GetDbcSpeedValueBySpeedName("通讯转速"));
//itemTag.Value.EngPvValue = 0;
break;
+ case CanLinEnum.CANFD:
+ SiemensDrive.Write(itemTag.Value.PVAddress, (short)CanFdDriveService.GetDbcSpeedValueBySpeedName("通讯转速"));
+ break;
case CanLinEnum.Lin:
//通信转速 Dbc中间配置名称的转速数据读取出来 给PLC
SiemensDrive.Write(itemTag.Value.PVAddress, (short)LinDriveService.GetLdfSpeedValueBySpeedName("通讯转速"));
@@ -1195,6 +1200,9 @@ namespace CapMachine.Wpf.Services
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(CanDriveService.GetDbcValueByName("通讯母线电压") * itemTag.Value.Precision));
//itemTag.Value.EngPvValue = 0;
break;
+ case CanLinEnum.CANFD:
+ SiemensDrive.Write(itemTag.Value.PVAddress, (short)CanFdDriveService.GetDbcValueByName("通讯母线电压"));
+ break;
case CanLinEnum.Lin:
//通信转速 Dbc中间配置名称的转速数据读取出来 给PLC
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(LinDriveService.GetLdfValueByName("通讯母线电压") * itemTag.Value.Precision));
@@ -1213,6 +1221,9 @@ namespace CapMachine.Wpf.Services
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(CanDriveService.GetDbcValueByName("通讯母线电流") * itemTag.Value.Precision));
//itemTag.Value.EngPvValue = 0;
break;
+ case CanLinEnum.CANFD:
+ SiemensDrive.Write(itemTag.Value.PVAddress, (short)CanFdDriveService.GetDbcValueByName("通讯母线电流"));
+ break;
case CanLinEnum.Lin:
//通信转速 Dbc中间配置名称的转速数据读取出来 给PLC
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(LinDriveService.GetLdfValueByName("通讯母线电流") * itemTag.Value.Precision));
@@ -1231,6 +1242,9 @@ namespace CapMachine.Wpf.Services
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(CanDriveService.GetDbcValueByName("通讯相电流") * itemTag.Value.Precision));
//itemTag.Value.EngPvValue = 0;
break;
+ case CanLinEnum.CANFD:
+ SiemensDrive.Write(itemTag.Value.PVAddress, (short)CanFdDriveService.GetDbcValueByName("通讯相电流"));
+ break;
case CanLinEnum.Lin:
//通信转速 Dbc中间配置名称的转速数据读取出来 给PLC
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(LinDriveService.GetLdfValueByName("通讯相电流") * itemTag.Value.Precision));
@@ -1249,6 +1263,9 @@ namespace CapMachine.Wpf.Services
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(CanDriveService.GetDbcValueByName("通讯功率") * itemTag.Value.Precision));
//itemTag.Value.EngPvValue = 0;
break;
+ case CanLinEnum.CANFD:
+ SiemensDrive.Write(itemTag.Value.PVAddress, (short)CanFdDriveService.GetDbcValueByName("通讯功率"));
+ break;
case CanLinEnum.Lin:
//通信转速 Dbc中间配置名称的转速数据读取出来 给PLC
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(LinDriveService.GetLdfValueByName("通讯功率") * itemTag.Value.Precision));
@@ -1267,6 +1284,9 @@ namespace CapMachine.Wpf.Services
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(CanDriveService.GetDbcValueByName("通讯芯片温度") * itemTag.Value.Precision));
//itemTag.Value.EngPvValue = 0;
break;
+ case CanLinEnum.CANFD:
+ SiemensDrive.Write(itemTag.Value.PVAddress, (short)CanFdDriveService.GetDbcValueByName("通讯芯片温度") * itemTag.Value.Precision);
+ break;
case CanLinEnum.Lin:
//通信转速 Dbc中间配置名称的转速数据读取出来 给PLC
SiemensDrive.Write(itemTag.Value.PVAddress, (short)(LinDriveService.GetLdfValueByName("通讯芯片温度") * itemTag.Value.Precision));
@@ -1301,6 +1321,13 @@ namespace CapMachine.Wpf.Services
itemTag.Value.EngPvValue = CanDriveService.GetDbcValueByName(itemTag.Value.NameNoUnit);
}
break;
+ case CanLinEnum.CANFD:
+ if (itemTag.Value.Group == "CANLIN")
+ {
+ //回读CAN通信的DBC集合数据到集合中
+ itemTag.Value.EngPvValue = CanFdDriveService.GetDbcValueByName(itemTag.Value.NameNoUnit);
+ }
+ break;
case CanLinEnum.Lin:
//LIN数据读取 //回读LIN通信的数据到集合中
//LIN组合并且在循环接收数据中时,才读取数据
@@ -1338,6 +1365,11 @@ namespace CapMachine.Wpf.Services
CanDriveService.UpdateSpeedCmdData(itemTag!.Value.EngSvValue);
//itemTag.Value.EngPvValue = 0;
break;
+ case CanLinEnum.CANFD:
+ //获取PLC的SV数据 更新SV的速度值到压缩机
+ CanFdDriveService.UpdateSpeedCmdData(itemTag!.Value.EngSvValue);
+ //itemTag.Value.EngPvValue = 0;
+ break;
case CanLinEnum.Lin:
//获取PLC的SV数据 更新SV的速度值到压缩机
LinDriveService.UpdateSpeedCmdData(itemTag!.Value.EngSvValue);
@@ -1412,6 +1444,11 @@ namespace CapMachine.Wpf.Services
CanDriveService.UpdateCapEnableCmdData(item.State);
//itemTag.Value.EngPvValue = 0;
break;
+ case CanLinEnum.CANFD:
+ //获取PLC的使能状态,更新到CAN的使能状态
+ CanFdDriveService.UpdateCapEnableCmdData(item.State);
+ //itemTag.Value.EngPvValue = 0;
+ break;
case CanLinEnum.Lin:
//获取PLC的使能状态,更新到LIN的使能状态
LinDriveService.UpdateCapEnableCmdData(item.State);
diff --git a/CapMachine.Wpf/Services/NavigationMenuService.cs b/CapMachine.Wpf/Services/NavigationMenuService.cs
index db0c4eb..4c8ef7d 100644
--- a/CapMachine.Wpf/Services/NavigationMenuService.cs
+++ b/CapMachine.Wpf/Services/NavigationMenuService.cs
@@ -40,6 +40,10 @@ namespace CapMachine.Wpf.Services
new NavigationItem("SuperHeatCool","过热度/过冷度配置","DialogSuperHeatCoolConfigView"),
//new NavigationItem("Palette","过冷度",""),
}),
+ new NavigationItem("", "规则设置","",new ObservableCollection()
+ {
+ new NavigationItem("Rule","规则转换","DialogLogicRuleView"),
+ }),
//new NavigationItem("", "PID设置","",new ObservableCollection()
//{
// new NavigationItem("Circle","转速PID",""),
@@ -54,7 +58,7 @@ namespace CapMachine.Wpf.Services
new NavigationItem("FormatColorText", "操作手册",""),
new NavigationItem("Barcode", "版本更新信息",""),
}),
-
+
}));
//MenuItems.Add(new NavigationItem("", "通信配置", "", new ObservableCollection()
//{
@@ -73,6 +77,7 @@ namespace CapMachine.Wpf.Services
//}));
MenuItems.Add(new NavigationItem("", "工艺过程", "MonitorView"));
MenuItems.Add(new NavigationItem("", "CAN配置", "CANConfigView"));
+ MenuItems.Add(new NavigationItem("", "CANFD配置", "CANFDConfigView"));
MenuItems.Add(new NavigationItem("", "LIN配置", "LINConfigView"));
MenuItems.Add(new NavigationItem("", "工艺参数", "ProConfigView"));
MenuItems.Add(new NavigationItem("", "工艺曲线", "RealTimeChartView"));
diff --git a/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs b/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs
index 5b2c0dd..d32eca0 100644
--- a/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs
+++ b/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs
@@ -37,7 +37,7 @@ namespace CapMachine.Wpf.ViewModels
{
public CANConfigViewModel(IDialogService dialogService, IFreeSql freeSql,
IEventAggregator eventAggregator, IRegionManager regionManager, SysRunService sysRunService,
- ComActionService actionService,
+ ComActionService actionService, LogicRuleService logicRuleService,
ConfigService configService, CanDriveService canDriveService,
IMapper mapper, MachineRtDataService machineRtDataService)
{
@@ -47,6 +47,7 @@ namespace CapMachine.Wpf.ViewModels
RegionManager = regionManager;
SysRunService = sysRunService;
ComActionService = actionService;
+ LogicRuleService = logicRuleService;
ConfigService = configService;
CanDriveService = canDriveService;
Mapper = mapper;
@@ -55,25 +56,64 @@ namespace CapMachine.Wpf.ViewModels
//MachineDataService = machineDataService;
DialogService = dialogService;
+ EventAggregator.GetEvent().Subscribe(LogicRuleChangeEventCall);
+
+ //数据波特率
+ DataBaudRateCbxItems = new ObservableCollection()
+ {
+ new CbxItems(){ Key="100000",Text="100 Kbps"},
+ new CbxItems(){ Key="125000",Text="125 Kbps"},
+ new CbxItems(){ Key="200000",Text="200 Kbps"},
+ new CbxItems(){ Key="250000",Text="250 Kbps"},
+ new CbxItems(){ Key="400000",Text="400 Kbps"},
+ new CbxItems(){ Key="500000",Text="500 Kbps"},
+ new CbxItems(){ Key="666000",Text="666 Kbps"},
+ new CbxItems(){ Key="800000",Text="800 Kbps"},
+ new CbxItems(){ Key="1000000",Text="1.0 Mbps"},
+
+ new CbxItems(){ Key="1500000",Text="1.5 Mbps"},
+ new CbxItems(){ Key="2000000",Text="2.0 Mbps"},
+ new CbxItems(){ Key="3000000",Text="3.0 Mbps"},
+ new CbxItems(){ Key="4000000",Text="4.0 Mbps"},
+ new CbxItems(){ Key="5000000",Text="5.0 Mbps"},
+ new CbxItems(){ Key="6700000",Text="6.7 Mbps"},
+ new CbxItems(){ Key="8000000",Text="8.0 Mbps"},
+ new CbxItems(){ Key="10000000",Text="10.0 Mbps"},
+ };
+
WriteNameCbxItems = new ObservableCollection()
{
new CbxItems(){ Key="转速",Text="转速"},
new CbxItems(){ Key="功率限制",Text="功率限制"},
new CbxItems(){ Key="使能",Text="使能"},
new CbxItems(){ Key="Anti_Sleep",Text="Anti_Sleep"},
+
+ new CbxItems(){ Key="PTC使能",Text="PTC使能"},
+ new CbxItems(){ Key="PTC功率",Text="PTC功率"},
+ new CbxItems(){ Key="PTC水流量",Text="PTC水流量"},
+ new CbxItems(){ Key="PTC水温",Text="PTC水温"},
};
ReadNameCbxItems = new ObservableCollection()
{
- new CbxItems(){ Key="通讯转速",Text="通讯转速"},
- new CbxItems(){ Key="通讯母线电压",Text="通讯母线电压"},
- new CbxItems(){ Key="通讯母线电流",Text="通讯母线电流"},
- new CbxItems(){ Key="通讯相电流",Text="通讯相电流"},
- new CbxItems(){ Key="通讯功率",Text="通讯功率"},
- new CbxItems(){ Key="通讯芯片温度",Text="通讯芯片温度"},
+ new CbxItems(){ Key="通讯Cmp转速",Text="通讯Cmp转速"},
+ new CbxItems(){ Key="通讯Cmp母线电压",Text="通讯Cmp母线电压"},
+ new CbxItems(){ Key="通讯Cmp母线电流",Text="通讯Cmp母线电流"},
+ new CbxItems(){ Key="通讯Cmp逆变器温度",Text="通讯Cmp逆变器温度"},
+ new CbxItems(){ Key="通讯Cmp相电流",Text="通讯Cmp相电流"},
+ new CbxItems(){ Key="通讯Cmp功率",Text="通讯Cmp功率"},
+ new CbxItems(){ Key="通讯Cmp芯片温度",Text="通讯Cmp芯片温度"},
+
+ new CbxItems(){ Key="通讯PTC入水温度",Text="通讯PTC入水温度"},
+ new CbxItems(){ Key="通讯PTC出水温度",Text="通讯PTC出水温度"},
+ new CbxItems(){ Key="通讯PTC峰值电流",Text="通讯PTC峰值电流"},
+ new CbxItems(){ Key="通讯PTC母线电流",Text="通讯PTC母线电流"},
+ new CbxItems(){ Key="通讯PTC膜温",Text="通讯PTC膜温"},
+ new CbxItems(){ Key="通讯PTC模块温度",Text="通讯PTC模块温度"},
};
InitLoadCanConfigPro();
-
+ //初始化写规则下拉框
+ InitWriteRuleCbx();
}
@@ -82,6 +122,7 @@ namespace CapMachine.Wpf.ViewModels
///
public IFreeSql FreeSql { get; }
public IEventAggregator EventAggregator { get; }
+ public LogicRuleService LogicRuleService { get; }
public IRegionManager RegionManager { get; }
public SysRunService SysRunService { get; }
public ComActionService ComActionService { get; }
@@ -96,6 +137,39 @@ namespace CapMachine.Wpf.ViewModels
public IDialogService DialogService { get; }
+ #region 规则
+
+
+ ///
+ /// 逻辑更改事件
+ ///
+ ///
+ ///
+ private void LogicRuleChangeEventCall(string msg)
+ {
+ //InitWriteRuleCbx();
+ }
+
+ ///
+ /// 初始化写规则下拉框
+ ///
+ private void InitWriteRuleCbx()
+ {
+ WriteRuleCbxItems = new ObservableCollection();
+ //选择的读写规则
+ foreach (var itemRule in LogicRuleService.LogicRuleDtos)
+ {
+ WriteRuleCbxItems.Add(new CbxItems()
+ {
+ Key = itemRule.Id.ToString(),
+ Text = itemRule.Name
+ });
+ }
+ }
+
+
+ #endregion
+
#region CanConfigPro
@@ -107,7 +181,8 @@ namespace CapMachine.Wpf.ViewModels
//CAN配置集合数据
canLinConfigPros = FreeSql.Select().Where(a => a.CANLINInfo == CANLIN.CAN)
.Include(a => a.CANConfigExd)
- .IncludeMany(a => a.CanLinConfigContents)
+ .IncludeMany(a => a.CanLinConfigContents, then => then.Include(b => b.LogicRule))//,then=> then.Include(b=>b.LogicRule) //.IncludeMany(a => a.CanLinConfigContents)
+ .IncludeMany(a => a.CanScheduleConfigs)//
.ToList();
ListCanLinConfigPro = new ObservableCollection(canLinConfigPros);
@@ -116,7 +191,7 @@ namespace CapMachine.Wpf.ViewModels
SelectCanLinConfigPro = canLinConfigPros.Where(a => a.Id == SelectCanLinConfigPro.Id).FirstOrDefault()!;
//无数据就返回
if (SelectCanLinConfigPro == null) return;
-
+
SelectedCANConfigExdDto = Mapper.Map(SelectCanLinConfigPro!.CANConfigExd);
//配置信息
@@ -135,6 +210,7 @@ namespace CapMachine.Wpf.ViewModels
MsgName = item.MsgFrameName,
SignalName = item.SignalName,
SignalCmdValue = double.TryParse(item.DefautValue, out double result) == true ? result : 0,
+ LogicRuleDto = Mapper.Map(item.LogicRule),
});
//CanDriveService.CmdData.Add(new CanCmdData()
//{
@@ -145,11 +221,26 @@ namespace CapMachine.Wpf.ViewModels
//});
}
}
+ else
+ {
+ ListWriteCanLinRWConfigDto = new ObservableCollection();
+ }
+
var ReadData = SelectCanLinConfigPro.CanLinConfigContents!.Where(a => a.RWInfo == RW.Read).ToList();
if (ReadData != null && ReadData.Count > 0)
{
ListReadCanLinRWConfigDto = new ObservableCollection(Mapper.Map>(ReadData));
}
+ else
+ {
+ ListReadCanLinRWConfigDto = new ObservableCollection();
+ }
+ //调度表配置信息
+ if (SelectCanLinConfigPro.CanScheduleConfigs != null && SelectCanLinConfigPro.CanScheduleConfigs.Count() > 0)
+ {
+ ListCANScheduleConfigDto = new ObservableCollection(Mapper.Map>(SelectCanLinConfigPro.CanScheduleConfigs));
+ }
+
//匹配选中的SelectCanLinConfigPro.CanLinConfigContents和ListCanDbcModel
MatchSeletedAndCanDbcModel();
@@ -488,6 +579,7 @@ namespace CapMachine.Wpf.ViewModels
MsgName = item.MsgFrameName,
SignalName = item.SignalName,
SignalCmdValue = double.TryParse(item.DefautValue, out double result) == true ? result : 0,
+ LogicRuleDto = Mapper.Map(item.LogicRule),
});
//CanDriveService.CmdData.Add(new CanCmdData()
@@ -500,14 +592,33 @@ namespace CapMachine.Wpf.ViewModels
}
}
+ else
+ {
+ ListWriteCanLinRWConfigDto = new ObservableCollection();
+ }
var ReadData = SelectCanLinConfigPro.CanLinConfigContents!.Where(a => a.RWInfo == RW.Read).ToList();
if (ReadData != null && ReadData.Count > 0)
{
ListReadCanLinRWConfigDto = new ObservableCollection(Mapper.Map>(ReadData));
}
+ else
+ {
+ ListReadCanLinRWConfigDto = new ObservableCollection();
+ }
SelectCanLinConfigProConfigName = SelectCanLinConfigPro.ConfigName;
+ //调度表配置信息
+ if (SelectCanLinConfigPro.CanScheduleConfigs != null && SelectCanLinConfigPro.CanScheduleConfigs.Count() > 0)
+ {
+ ListCANScheduleConfigDto = new ObservableCollection(Mapper.Map>(SelectCanLinConfigPro.CanScheduleConfigs));
+ }
+ else
+ {
+ ListCANScheduleConfigDto = new ObservableCollection();
+ }
+
+
return;
}
//先判断是否是正确的集合数据,防止DataGrid的数据源刷新导致的触发事件
@@ -695,7 +806,7 @@ namespace CapMachine.Wpf.ViewModels
}
//(par as SelectionChangedEventArgs)!.AddedItems[0] == null
- if ((par as SelectionChangedEventArgs)!.AddedItems.Count==0)
+ if ((par as SelectionChangedEventArgs)!.AddedItems.Count == 0)
{
return;
}
@@ -790,6 +901,57 @@ namespace CapMachine.Wpf.ViewModels
set { _SelectedCANConfigExdDto = value; RaisePropertyChanged(); }
}
+ private ObservableCollection _DataBaudRateCbxItems;
+ ///
+ /// CAN 数据波特率
+ ///
+ public ObservableCollection DataBaudRateCbxItems
+ {
+ get { return _DataBaudRateCbxItems; }
+ set { _DataBaudRateCbxItems = value; RaisePropertyChanged(); }
+ }
+
+ private DelegateCommand