From 159187a47531aca20e84d1978ec3f885ae14ccd2 Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Sun, 10 Aug 2025 23:49:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86CAN=E8=B0=83?= =?UTF-8?q?=E5=BA=A6=E8=A1=A8=E5=8F=91=E9=80=81=EF=BC=8C=E4=BD=86=E6=98=AF?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E6=B5=8B=E8=AF=95=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 123 ++++++ CapMachine.Model/CANLIN/CANConfigExd.cs | 5 + CapMachine.Model/CANLIN/CANScheduleConfig.cs | 55 +++ CapMachine.Model/CANLIN/CanLinConfigPro.cs | 6 + CapMachine.Wpf/App.xaml.cs | 3 +- CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs | 176 +++++++++ CapMachine.Wpf/CanDrive/CanFD/USB2CANFD.cs | 14 + CapMachine.Wpf/CanDrive/ToomossCan.cs | 330 +++++++++++++++- CapMachine.Wpf/CanDrive/usb2can.cs | 16 + CapMachine.Wpf/CapMachine.Wpf.csproj | 2 +- CapMachine.Wpf/Dtos/CANConfigExdDto.cs | 10 + CapMachine.Wpf/Dtos/CANMsgDto.cs | 27 ++ CapMachine.Wpf/Dtos/CANScheduleConfigDto.cs | 83 ++++ CapMachine.Wpf/LinDrive/ToomossLin.cs | 3 +- .../MapperProfile/CANScheduleConfigProfile.cs | 14 + CapMachine.Wpf/Services/CanDriveService.cs | 53 ++- .../ViewModels/CANConfigViewModel.cs | 118 +++++- .../ViewModels/DialogCANSchConfigViewModel.cs | 356 ++++++++++++++++++ CapMachine.Wpf/Views/CANConfigView.xaml | 45 ++- .../Views/DialogCANSchConfigView.xaml | 180 +++++++++ .../Views/DialogCANSchConfigView.xaml.cs | 28 ++ 21 files changed, 1618 insertions(+), 29 deletions(-) create mode 100644 CapMachine.Model/CANLIN/CANScheduleConfig.cs create mode 100644 CapMachine.Wpf/Dtos/CANMsgDto.cs create mode 100644 CapMachine.Wpf/Dtos/CANScheduleConfigDto.cs create mode 100644 CapMachine.Wpf/MapperProfile/CANScheduleConfigProfile.cs create mode 100644 CapMachine.Wpf/ViewModels/DialogCANSchConfigViewModel.cs create mode 100644 CapMachine.Wpf/Views/DialogCANSchConfigView.xaml create mode 100644 CapMachine.Wpf/Views/DialogCANSchConfigView.xaml.cs diff --git a/.editorconfig b/.editorconfig index cb53e99..a20d564 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,3 +2,126 @@ # CA1416: 验证平台兼容性 dotnet_diagnostic.CA1416.severity = silent + +[*.cs] +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# 命名样式 + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +csharp_using_directive_placement = outside_namespace:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent +csharp_style_var_elsewhere = false:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion + +[*.vb] +#### 命名样式 #### + +# 命名规则 + +dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion +dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface +dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始 + +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型 +dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 + +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员 +dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法 + +# 符号规范 + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.类型.required_modifiers = + +dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method +dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected +dotnet_naming_symbols.非字段成员.required_modifiers = + +# 命名样式 + +dotnet_naming_style.以_i_开始.required_prefix = I +dotnet_naming_style.以_i_开始.required_suffix = +dotnet_naming_style.以_i_开始.word_separator = +dotnet_naming_style.以_i_开始.capitalization = pascal_case + +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case + +dotnet_naming_style.帕斯卡拼写法.required_prefix = +dotnet_naming_style.帕斯卡拼写法.required_suffix = +dotnet_naming_style.帕斯卡拼写法.word_separator = +dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case + +[*.{cs,vb}] +end_of_line = crlf +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent \ No newline at end of file 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/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 9f3b9ad..eb5521d 100644 --- a/CapMachine.Model/CANLIN/CanLinConfigPro.cs +++ b/CapMachine.Model/CANLIN/CanLinConfigPro.cs @@ -44,6 +44,12 @@ namespace CapMachine.Model.CANLIN public List? CanLinConfigContents { get; set; } + /// + /// ///////////////////////////////////////////导航属性/////////////////////////////////////////////////////// + /// + ///CAN 的调度表配置模式 + public List? CanScheduleConfigs { get; set; } + /// /// ///////////////////////////////////////////导航属性 LIN 一对一/////////////////////////////////////////////////////// diff --git a/CapMachine.Wpf/App.xaml.cs b/CapMachine.Wpf/App.xaml.cs index 8c8127f..4037639 100644 --- a/CapMachine.Wpf/App.xaml.cs +++ b/CapMachine.Wpf/App.xaml.cs @@ -200,7 +200,8 @@ namespace CapMachine.Wpf containerRegistry.RegisterDialog(); containerRegistry.RegisterDialog(); containerRegistry.RegisterDialog(); - + containerRegistry.RegisterDialog(); + containerRegistry.RegisterDialog(); diff --git a/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs b/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs index 2e4fbc6..cfb708a 100644 --- a/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs +++ b/CapMachine.Wpf/CanDrive/CanFD/ToomossCanFD.cs @@ -5,6 +5,7 @@ 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; @@ -549,6 +550,7 @@ namespace CapMachine.Wpf.CanDrive //此时重置NextExecutionTime为当前时间,避免连续的延迟累积 // 严重延迟,重新校准 NextExecutionTime = Stopwatcher.ElapsedTicks; + LogService.Info("定时发送延迟过大,重新校准时间"); Console.WriteLine("定时发送延迟过大,重新校准时间"); } @@ -681,6 +683,180 @@ namespace CapMachine.Wpf.CanDrive #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; /// /// 是否循环接收数据 diff --git a/CapMachine.Wpf/CanDrive/CanFD/USB2CANFD.cs b/CapMachine.Wpf/CanDrive/CanFD/USB2CANFD.cs index 67a278e..fd1b13c 100644 --- a/CapMachine.Wpf/CanDrive/CanFD/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/ToomossCan.cs b/CapMachine.Wpf/CanDrive/ToomossCan.cs index 6b9082f..5bbe60f 100644 --- a/CapMachine.Wpf/CanDrive/ToomossCan.cs +++ b/CapMachine.Wpf/CanDrive/ToomossCan.cs @@ -1,6 +1,10 @@ -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; @@ -35,7 +39,7 @@ namespace CapMachine.Wpf.CanDrive ContainerProvider = containerProvider; HighSpeedDataService = ContainerProvider.Resolve(); LoggerService = ContainerProvider.Resolve(); - + //Stopwatch.Frequency表示高精度计时器每秒的计数次数(ticks/秒)每毫秒的ticks数 = 每秒的ticks数 ÷ 1000 TicksPerMs = Stopwatch.Frequency / 1000.0; } @@ -488,8 +492,13 @@ 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]; + double[] ValueDouble = new double[5]; private bool _IsSendOk; /// @@ -785,6 +794,296 @@ 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 = 500; + + /// + /// 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; + } + Index++; + } + //通过DBC写入数据后生成CanMsg + //将信号值填入CAN消息里面 + //释放申请的临时缓冲区 + Marshal.FreeHGlobal(msgPtSend); + + + //Sample:总共3个调度表,第一个表里面包含3帧数据,第二个调度表包含6帧数据,第三个调度表包含11帧数据 + //约定每帧对应一个调度表,预设5个调度表,每个调度表对应一个帧 + MsgTabNum = new Byte[MsgTabCount] { 1, 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; + } + + 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) + { + 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); + + //配置信息 + 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)(itemMsgSchConfig.SchTabIndex+0), (byte)0, 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) + { + LoggerService.Info($"时间:{DateTime.Now.ToString()}-【MSG】-{ex.Message}"); + } + } + }); + } + + + #endregion /// /// 循环获取CAN消息 @@ -802,24 +1101,25 @@ namespace CapMachine.Wpf.CanDrive 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); + //int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, ReadCANIndex, msgPtRead, CanMsgBuffer.Length); + int CanNum = USB2CAN.CAN_GetMsgWithSize(DevHandle, 1, msgPtRead, CanMsgBuffer.Length); if (CanNum > 0) { IsReviceOk = true; - Console.WriteLine("Read CanMsgNum = {0}", CanNum); + //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(""); + //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() @@ -833,7 +1133,7 @@ namespace CapMachine.Wpf.CanDrive } else if (CanNum == 0) { - IsReviceOk=false; + IsReviceOk = false; Console.WriteLine("No CAN data!"); } else @@ -841,7 +1141,7 @@ namespace CapMachine.Wpf.CanDrive IsReviceOk = false; Console.WriteLine("Get CAN data error!"); } - Console.WriteLine(""); + //Console.WriteLine(""); //将CAN消息数据填充到信号里面,用DBC解析数据 CAN_DBCParser.DBC_SyncCANMsgToValue(DBCHandle, msgPtRead, CanNum); 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 38d28f8..16f359b 100644 --- a/CapMachine.Wpf/CapMachine.Wpf.csproj +++ b/CapMachine.Wpf/CapMachine.Wpf.csproj @@ -1,7 +1,7 @@  - WinExe + Exe net6.0-windows enable enable diff --git a/CapMachine.Wpf/Dtos/CANConfigExdDto.cs b/CapMachine.Wpf/Dtos/CANConfigExdDto.cs index 7018cf1..2dc82df 100644 --- a/CapMachine.Wpf/Dtos/CANConfigExdDto.cs +++ b/CapMachine.Wpf/Dtos/CANConfigExdDto.cs @@ -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/CANMsgDto.cs b/CapMachine.Wpf/Dtos/CANMsgDto.cs new file mode 100644 index 0000000..40db5bc --- /dev/null +++ b/CapMachine.Wpf/Dtos/CANMsgDto.cs @@ -0,0 +1,27 @@ +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CapMachine.Wpf.Dtos +{ + /// + /// CAN报文和帧 + /// + public class CANMsgDto:BindableBase + { + + private string? _MsgName; + /// + /// 消息名称/帧名称 + /// + public string? MsgName + { + get { return _MsgName; } + set { _MsgName = 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/LinDrive/ToomossLin.cs b/CapMachine.Wpf/LinDrive/ToomossLin.cs index ca34c80..a09b8ec 100644 --- a/CapMachine.Wpf/LinDrive/ToomossLin.cs +++ b/CapMachine.Wpf/LinDrive/ToomossLin.cs @@ -577,6 +577,8 @@ namespace CapMachine.Wpf.LinDrive // 严重延迟,重新校准 NextExecutionTime = Stopwatcher.ElapsedTicks; Console.WriteLine("定时发送延迟过大,重新校准时间"); + + LoggerService.Info("定时发送延迟过大,重新校准时间"); } // 使用Stopwatch记录实际的执行间隔,而不是DateTime @@ -585,7 +587,6 @@ namespace CapMachine.Wpf.LinDrive //Console.WriteLine($"--当前时间(毫秒): {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"); - // 执行发送CAN逻辑 { 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/Services/CanDriveService.cs b/CapMachine.Wpf/Services/CanDriveService.cs index 8ad4863..762ecae 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; @@ -126,6 +127,11 @@ namespace CapMachine.Wpf.Services /// public List CmdData { get; set; } = new List(); + /// + /// CNA 调度表的配置信息 + /// + public List ListCANScheduleConfig { get; set; } + /// /// 增加发送的指令数据 /// @@ -307,10 +313,39 @@ namespace CapMachine.Wpf.Services { if (CmdData.Count > 0) { - ToomossCanDrive.IsCycleSend = true; + ToomossCanDrive.CmdData = CmdData; - //ToomossCanDrive.StartCycleSendMsg(); - ToomossCanDrive.StartPrecisionCycleSendMsg(); + ToomossCanDrive.SchEnable = true; + + 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.StartCycleSendMsg(); + } + + ToomossCanDrive.IsCycleSend = true; } else { @@ -319,7 +354,15 @@ namespace CapMachine.Wpf.Services } else { - ToomossCanDrive.IsCycleSend = false; + if (ToomossCanDrive.SchEnable) + { + ToomossCanDrive.IsCycleSend = false; + ToomossCanDrive.StopSchedule(); + } + else + { + ToomossCanDrive.IsCycleSend = false; + } } } @@ -388,7 +431,7 @@ namespace CapMachine.Wpf.Services //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/ViewModels/CANConfigViewModel.cs b/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs index c5c2516..d64097e 100644 --- a/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs +++ b/CapMachine.Wpf/ViewModels/CANConfigViewModel.cs @@ -182,6 +182,7 @@ namespace CapMachine.Wpf.ViewModels canLinConfigPros = FreeSql.Select().Where(a => a.CANLINInfo == CANLIN.CAN) .Include(a => a.CANConfigExd) .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); @@ -225,6 +226,12 @@ namespace CapMachine.Wpf.ViewModels { ListReadCanLinRWConfigDto = new ObservableCollection(Mapper.Map>(ReadData)); } + //调度表配置信息 + if (SelectCanLinConfigPro.CanScheduleConfigs != null && SelectCanLinConfigPro.CanScheduleConfigs.Count() > 0) + { + ListCANScheduleConfigDto = new ObservableCollection(Mapper.Map>(SelectCanLinConfigPro.CanScheduleConfigs)); + } + //匹配选中的SelectCanLinConfigPro.CanLinConfigContents和ListCanDbcModel MatchSeletedAndCanDbcModel(); @@ -584,6 +591,17 @@ namespace CapMachine.Wpf.ViewModels SelectCanLinConfigProConfigName = SelectCanLinConfigPro.ConfigName; + //调度表配置信息 + if (SelectCanLinConfigPro.CanScheduleConfigs != null && SelectCanLinConfigPro.CanScheduleConfigs.Count() > 0) + { + ListCANScheduleConfigDto = new ObservableCollection(Mapper.Map>(SelectCanLinConfigPro.CanScheduleConfigs)); + } + else + { + ListCANScheduleConfigDto = new ObservableCollection(); + } + + return; } //先判断是否是正确的集合数据,防止DataGrid的数据源刷新导致的触发事件 @@ -868,7 +886,7 @@ namespace CapMachine.Wpf.ViewModels private ObservableCollection _DataBaudRateCbxItems; /// - /// CAN FD 数据波特率 + /// CAN 数据波特率 /// public ObservableCollection DataBaudRateCbxItems { @@ -876,6 +894,47 @@ namespace CapMachine.Wpf.ViewModels set { _DataBaudRateCbxItems = value; RaisePropertyChanged(); } } + private DelegateCommand _SchEnableCmd; + /// + /// 调度表的使能 操作的指令 + /// + public DelegateCommand SchEnableCmd + { + set + { + _SchEnableCmd = value; + } + get + { + if (_SchEnableCmd == null) + { + _SchEnableCmd = new DelegateCommand((Par) => SchEnableCmdCall(Par)); + } + return _SchEnableCmd; + } + } + /// + /// 调度表的使能信息 + /// + /// + /// + private void SchEnableCmdCall(object par) + { + var dd = SelectedCANConfigExdDto.SchEnable; + var Result = (bool)par; + if (Result) + { + CanDriveService.ToomossCanDrive.SchEnable = true; + + } + else + { + CanDriveService.ToomossCanDrive.SchEnable = false; + + } + } + + private DelegateCommand _CanOpCmd; /// /// CAN操作的指令 @@ -963,6 +1022,7 @@ namespace CapMachine.Wpf.ViewModels .Set(a => a.DbcPath, SelectedCANConfigExdDto.DbcPath) .Set(a => a.Cycle, SelectedCANConfigExdDto.Cycle) .Set(a => a.BaudRate, SelectedCANConfigExdDto.BaudRate) + .Set(a => a.SchEnable, SelectedCANConfigExdDto.SchEnable) .Where(a => a.Id == SelectedCANConfigExdDto.Id) .ExecuteUpdated(); } @@ -1024,7 +1084,12 @@ namespace CapMachine.Wpf.ViewModels break; case "CycleSend"://循环发送你 - //循环发送数据 + + //有可能加载完毕后不手动改变状态,导致值没有传输,所以这里赋值 + CanDriveService.ToomossCanDrive.SchEnable = SelectedCANConfigExdDto.SchEnable; + //无论有没有调度表,都要把这个配置给CanDriveService + CanDriveService.ListCANScheduleConfig = ListCANScheduleConfigDto.ToList(); + CanDriveService.CycleSendMsg(); //Listen @@ -1099,6 +1164,17 @@ namespace CapMachine.Wpf.ViewModels } + private ObservableCollection _ListCANScheduleConfigDto; + /// + ///调度表集合 + /// + public ObservableCollection ListCANScheduleConfigDto + { + get { return _ListCANScheduleConfigDto; } + set { _ListCANScheduleConfigDto = value; RaisePropertyChanged(); } + } + + //private string _SelectedWriteName; ///// ///// 选中的写入的Name @@ -1244,6 +1320,44 @@ namespace CapMachine.Wpf.ViewModels System.Windows.MessageBox.Show("请选中后再进行【删除】操作?", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand); } + break; + case "WriteSch"://调度表 + + var data = CanDriveService.CmdData.GroupBy(a => a.MsgName).Select(a => a.Key).ToList(); + + if (data != null && data.Count > 0) + { + //弹窗 + DialogService.ShowDialog("DialogCANSchConfigView", new DialogParameters() { + {"ListMsg", CanDriveService.CmdData.GroupBy(a=>a.MsgName).Select(a=>a.Key).ToList() }, + { "ListCANScheduleConfigDto",ListCANScheduleConfigDto}, + { "SelectCanLinConfigProId",SelectCanLinConfigPro.Id}, + + }, (par) => + { + if (par.Result == ButtonResult.OK) + { + ////程序名称 + ListCANScheduleConfigDto = par.Parameters.GetValue>("ReturnValue"); + //把更新后的最新值给当前的主集合中 + SelectCanLinConfigPro.CanScheduleConfigs = Mapper.Map>(ListCANScheduleConfigDto.ToList()); + + } + else if (par.Result == ButtonResult.Cancel) + { + //取消 + + } + + }); + } + else + { + System.Windows.MessageBox.Show("未发现写入的执行的命令数据,无法配置?", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand); + } + + + break; default: break; diff --git a/CapMachine.Wpf/ViewModels/DialogCANSchConfigViewModel.cs b/CapMachine.Wpf/ViewModels/DialogCANSchConfigViewModel.cs new file mode 100644 index 0000000..9543f9b --- /dev/null +++ b/CapMachine.Wpf/ViewModels/DialogCANSchConfigViewModel.cs @@ -0,0 +1,356 @@ +using AutoMapper; +using CapMachine.Core; +using CapMachine.Model; +using CapMachine.Model.CANLIN; +using CapMachine.Wpf.Dtos; +using CapMachine.Wpf.Services; +using Prism.Commands; +using Prism.Services.Dialogs; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace CapMachine.Wpf.ViewModels +{ + /// + /// CAN 调度表配置 + /// + public class DialogCANSchConfigViewModel : DialogViewModel + { + /// + /// 构造函数 + /// + public DialogCANSchConfigViewModel(IFreeSql freeSql, IMapper mapper) + { + Title = "调度表 CAN 配置"; + FreeSql = freeSql; + Mapper = mapper; + + + SendOrderCbxItems = new ObservableCollection() + { + new CbxItems(){ + Key="0", + Text="并行", + }, + new CbxItems(){ + Key="1", + Text="顺序", + }, + }; + + SchTabIndexCbxItems = new ObservableCollection() + { + new CbxItems(){ + Key="0", + Text="0", + }, + new CbxItems(){ + Key="1", + Text="1", + }, + new CbxItems(){ + Key="2", + Text="2", + }, + new CbxItems(){ + Key="3", + Text="3", + }, + new CbxItems(){ + Key="4", + Text="4", + }, + }; + + } + + public IFreeSql FreeSql { get; } + public IMapper Mapper { get; } + + private string name; + /// + /// 名称 + /// + public string Name + { + get { return name; } + set { name = value; RaisePropertyChanged(); } + } + + private ObservableCollection _ListCANScheduleConfigDto = new ObservableCollection(); + /// + /// CAN 调度表数据集合 + /// + public ObservableCollection ListCANScheduleConfigDto + { + get { return _ListCANScheduleConfigDto; } + set { _ListCANScheduleConfigDto = value; RaisePropertyChanged(); } + } + + /// + /// 消息/帧报文信息集合 + /// + public List ListMsg { get; set; } + + private ObservableCollection _MsgCbxItems; + /// + /// 消息名称 集合信息 + /// + public ObservableCollection MsgCbxItems + { + get { return _MsgCbxItems; } + set { _MsgCbxItems = value; RaisePropertyChanged(); } + } + + /// + /// 选中的程序的Id + /// + public long SelectCanLinConfigProId { get; set; } + + private ObservableCollection _SendOrderCbxItems; + /// + /// 发送方式 集合信息 + /// + public ObservableCollection SendOrderCbxItems + { + get { return _SendOrderCbxItems; } + set { _SendOrderCbxItems = value; RaisePropertyChanged(); } + } + + + private ObservableCollection _SchTabIndexCbxItems; + /// + /// 调度器序号 集合信息 + /// + public ObservableCollection SchTabIndexCbxItems + { + get { return _SchTabIndexCbxItems; } + set { _SchTabIndexCbxItems = value; RaisePropertyChanged(); } + } + + private CANScheduleConfigDto _CurSelectedItem; + /// + /// 选中的数据 + /// + public CANScheduleConfigDto CurSelectedItem + { + get { return _CurSelectedItem; } + set { _CurSelectedItem = value; RaisePropertyChanged(); } + } + + + private DelegateCommand _GridSelectionChangedCmd; + /// + /// 选中行数据命令 + /// + public DelegateCommand GridSelectionChangedCmd + { + set + { + _GridSelectionChangedCmd = value; + } + get + { + if (_GridSelectionChangedCmd == null) + { + _GridSelectionChangedCmd = new DelegateCommand((par) => GridSelectionChangedCmdMethod(par)); + } + return _GridSelectionChangedCmd; + } + } + private void GridSelectionChangedCmdMethod(object par) + { + //先判断是否是正确的集合数据,防止DataGrid的数据源刷新导致的触发事件 + var Selecteddata = par as CANScheduleConfigDto; + + if (Selecteddata != null) + { + CurSelectedItem = Selecteddata; + } + } + + //OpCmd + private DelegateCommand _OpCmd; + /// + /// 增加方法命令 + /// + public DelegateCommand OpCmd + { + set + { + _OpCmd = value; + } + get + { + if (_OpCmd == null) + { + _OpCmd = new DelegateCommand((Par) => OpCmdCall(Par)); + } + return _OpCmd; + } + } + + private void OpCmdCall(string Par) + { + switch (Par) + { + case "Add": + ListCANScheduleConfigDto.Add(new CANScheduleConfigDto + { + CanLinConfigProId = SelectCanLinConfigProId, + Cycle = 100, + SchTabIndex = 0, + OrderSend = 1, + }); + break; + case "Delete": + if (CurSelectedItem != null) + { + //直接删除掉,如果没有ID的话,这就不需要删除了 + FreeSql.Delete(CurSelectedItem.Id).ExecuteAffrows(); + ListCANScheduleConfigDto.Remove(CurSelectedItem); + + CurSelectedItem = null; + } + else + { + MessageBox.Show("请选中后再进行【删除】操作?", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + } + break; + default: + break; + } + } + + private DelegateCommand saveCmd; + /// + /// 保存命令 + /// + public DelegateCommand SaveCmd + { + set + { + saveCmd = value; + } + get + { + if (saveCmd == null) + { + saveCmd = new DelegateCommand(() => SaveCmdMethod()); + } + return saveCmd; + } + } + + /// + /// 保存命令方法 + /// + /// + private void SaveCmdMethod() + { + //检查空的数据 + foreach (var item in ListCANScheduleConfigDto) + { + if (string.IsNullOrEmpty(item.MsgName)) + { + MessageBox.Show("请确认消息名称是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + if (item.Cycle == 0) + { + MessageBox.Show("请确认周期是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + if (item.OrderSend >= 2) + { + MessageBox.Show("请确认发送方式是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + } + //检查重复设置问题 + bool isRepeat = ListCANScheduleConfigDto.GroupBy(i => i.MsgName).Any(g => g.Count() > 1); + if (isRepeat) + { + MessageBox.Show("请确认是否重复设置", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + return; + } + + //检查数据是否正常 + foreach (var item in ListCANScheduleConfigDto) + { + FreeSql.InsertOrUpdate() + .SetSource(Mapper.Map(item)). + ExecuteAffrows(); + } + + ListCANScheduleConfigDto = new ObservableCollection(Mapper.Map>(FreeSql.Select().Where(a => a.CanLinConfigProId == SelectCanLinConfigProId).ToList())); + + DialogParameters pars = new DialogParameters + { + { "ReturnValue", ListCANScheduleConfigDto } + }; + + RaiseRequestClose(new DialogResult(ButtonResult.OK, pars)); + } + + private DelegateCommand cancelCmd; + /// + /// 保存命令 + /// + public DelegateCommand CancelCmd + { + set + { + cancelCmd = value; + } + get + { + if (cancelCmd == null) + { + cancelCmd = new DelegateCommand(() => CancelCmdMethod()); + } + return cancelCmd; + } + } + + + /// + /// 取消命令方法 + /// + /// + private void CancelCmdMethod() + { + RaiseRequestClose(new DialogResult(ButtonResult.Cancel)); + } + + /// + /// 窗口打开时的传递的参数 + /// + /// + public override void OnDialogOpened(IDialogParameters parameters) + { + ListMsg = parameters.GetValue>("ListMsg"); + // 转换为CbxItems集合,都是文本内容 + MsgCbxItems = new ObservableCollection( + ListMsg.Select(value => new CbxItems + { + Key = value, + Text = value + })); + + ListCANScheduleConfigDto = parameters.GetValue>("ListCANScheduleConfigDto"); + //防止返回的数据为空,就无法增加了 + if (ListCANScheduleConfigDto == null) ListCANScheduleConfigDto = new ObservableCollection(); + //Name = parameters.GetValue("Name"); + + SelectCanLinConfigProId = parameters.GetValue("SelectCanLinConfigProId"); + } + + + } +} diff --git a/CapMachine.Wpf/Views/CANConfigView.xaml b/CapMachine.Wpf/Views/CANConfigView.xaml index cbec289..b619098 100644 --- a/CapMachine.Wpf/Views/CANConfigView.xaml +++ b/CapMachine.Wpf/Views/CANConfigView.xaml @@ -584,10 +584,10 @@ @@ -620,7 +620,7 @@ @@ -641,6 +641,25 @@ Text="接收" /> + + + + @@ -701,6 +720,24 @@ Margin="0,0,15,0" HorizontalAlignment="Right" Orientation="Horizontal"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +