From a3ef37e64a69d151276e5932cea757f6b3f08ab4 Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Thu, 9 Oct 2025 21:34:42 +0800 Subject: [PATCH] LIN Schedule --- CapMachine.Model/CANLIN/LINScheduleConfig.cs | 18 +- CapMachine.Wpf/CanDrive/ToomossCan.cs | 3 +- CapMachine.Wpf/CapMachine.Wpf.csproj | 2 +- CapMachine.Wpf/Dtos/LINScheduleConfigDto.cs | 37 ++- CapMachine.Wpf/LinDrive/ToomossLin.cs | 122 +++++++- .../ViewModels/DialogLINSchConfigViewModel.cs | 279 +++++++++++++++--- .../ViewModels/LinConfigViewModel.cs | 71 ++--- CapMachine.Wpf/ViewModels/LinTreeNodes.cs | 89 ++++++ .../Views/DialogLINSchConfigView.xaml | 138 ++++----- 9 files changed, 583 insertions(+), 176 deletions(-) create mode 100644 CapMachine.Wpf/ViewModels/LinTreeNodes.cs diff --git a/CapMachine.Model/CANLIN/LINScheduleConfig.cs b/CapMachine.Model/CANLIN/LINScheduleConfig.cs index 496ea73..3c98fee 100644 --- a/CapMachine.Model/CANLIN/LINScheduleConfig.cs +++ b/CapMachine.Model/CANLIN/LINScheduleConfig.cs @@ -4,7 +4,7 @@ namespace CapMachine.Model.CANLIN { /// /// 调度表的配置 - /// 其实这些调度表是在DBC中有的,但是图莫斯的驱动没有读取到这些信息 + /// 其实这些调度表是在LDF中有的,但是图莫斯的驱动没有读取到这些信息 /// 那么我们在系统层面进行操作和保存这些信息 /// [Table(Name = "LINScheduleConfig")] @@ -17,11 +17,23 @@ namespace CapMachine.Model.CANLIN public long Id { get; set; } /// - /// 消息名称 + /// 是否启用 + /// + [Column(Name = "IsActive")] + public bool IsActive { get; set; } + + /// + /// 消息名称/帧名称 /// [Column(Name = "MsgName")] public string? MsgName { get; set; } + /// + /// 消息名称/帧名称的Index + /// + [Column(Name = "MsgNameIndex")] + public int MsgNameIndex { get; set; } + /// /// 消息的周期 /// @@ -40,7 +52,7 @@ namespace CapMachine.Model.CANLIN /// LDF中可能有多个调度器名称 /// [Column(Name = "SchTabName")] - public int SchTabName { get; set; } + public string? SchTabName { get; set; } /// diff --git a/CapMachine.Wpf/CanDrive/ToomossCan.cs b/CapMachine.Wpf/CanDrive/ToomossCan.cs index f33a2f8..4609201 100644 --- a/CapMachine.Wpf/CanDrive/ToomossCan.cs +++ b/CapMachine.Wpf/CanDrive/ToomossCan.cs @@ -846,7 +846,7 @@ namespace CapMachine.Wpf.CanDrive /// /// 定时更新时间 /// - private int UpdateCycle = 100; + private int UpdateCycle { get; set; } = 100; /// /// CNA 调度表的配置信息 @@ -1121,7 +1121,6 @@ namespace CapMachine.Wpf.CanDrive // Console.WriteLine($"Update CAN Schedule Error ret = {ret} -- SchTabIndex:{(byte)itemMsgSchConfig.SchTabIndex} -- MsgIndex:{(byte)(itemMsgSchConfig.MsgIndex)}"); // //return; // } - //} 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/LINScheduleConfigDto.cs b/CapMachine.Wpf/Dtos/LINScheduleConfigDto.cs index 2630129..68c76a5 100644 --- a/CapMachine.Wpf/Dtos/LINScheduleConfigDto.cs +++ b/CapMachine.Wpf/Dtos/LINScheduleConfigDto.cs @@ -1,10 +1,5 @@ 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 { @@ -15,6 +10,16 @@ namespace CapMachine.Wpf.Dtos /// public long Id { get; set; } + private bool _IsActive; + /// + /// 是否激活 + /// + public bool IsActive + { + get { return _IsActive; } + set { _IsActive = value; RaisePropertyChanged(); } + } + private string? _MsgName; /// /// 消息名称/帧名称 @@ -25,6 +30,16 @@ namespace CapMachine.Wpf.Dtos set { _MsgName = value; RaisePropertyChanged(); } } + private int _MsgNameIndex; + /// + /// 消息名称/帧名称的Index + /// + public int MsgNameIndex + { + get { return _MsgNameIndex; } + set { _MsgNameIndex = value; RaisePropertyChanged(); } + } + private int _Cycle; /// /// 周期 @@ -35,7 +50,6 @@ namespace CapMachine.Wpf.Dtos set { _Cycle = value; RaisePropertyChanged(); } } - private int _SchTabIndex; /// /// 调度表的Index序列 @@ -56,12 +70,11 @@ namespace CapMachine.Wpf.Dtos set { _SchTabName = value; RaisePropertyChanged(); } } - - /// - /// 在更新调度表数据时,我们有一个整体的帧数据指令集合,但是这些帧数据集合,分属于不同的调度表, - /// 这个在开始时生成整体的帧数据指令集合才会把这个MsgIndex分配上,这个不需要保存到数据库中,对接使用 - /// - public int MsgIndex { get; set; } + ///// + ///// 在更新调度表数据时,我们有一个整体的帧数据指令集合,但是这些帧数据集合,分属于不同的调度表, + ///// 这个在开始时生成整体的帧数据指令集合才会把这个MsgIndex分配上,这个不需要保存到数据库中,对接使用 + ///// + //public int MsgIndex { get; set; } /// /// 程序的ID diff --git a/CapMachine.Wpf/LinDrive/ToomossLin.cs b/CapMachine.Wpf/LinDrive/ToomossLin.cs index d325d5b..b3a33e5 100644 --- a/CapMachine.Wpf/LinDrive/ToomossLin.cs +++ b/CapMachine.Wpf/LinDrive/ToomossLin.cs @@ -1,4 +1,5 @@ -using CapMachine.Wpf.CanDrive; +using CapMachine.Model.CANLIN; +using CapMachine.Wpf.CanDrive; using CapMachine.Wpf.Services; using ImTools; using Microsoft.VisualBasic; @@ -379,16 +380,6 @@ namespace CapMachine.Wpf.LinDrive } 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); - //Console.WriteLine("ID_DATA.Supplier_ID={0}", ValueStr); - //LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder("ID_DATA"), new StringBuilder("Machine_ID"), ValueStr); - //Console.WriteLine("ID_DATA.Machine_ID={0}", ValueStr); - //LDFParser.LDF_GetSignalValueStr(LDFHandle, new StringBuilder("ID_DATA"), new StringBuilder("Chip_ID"), ValueStr); - //Console.WriteLine("ID_DATA.Chip_ID={0}", ValueStr); - } catch (Exception ex) { @@ -666,6 +657,7 @@ namespace CapMachine.Wpf.LinDrive #endregion + #region 调度器发送报文 private bool _SchEnable; @@ -682,11 +674,61 @@ namespace CapMachine.Wpf.LinDrive } } + /// /// 调度表数量 /// public int SchCount { get; set; } - + + + /// + /// 获取LIN的LINScheduleConfig + /// + /// + public List GetLINScheduleConfigs() + { + List LINScheduleConfigs = new List(); + SchCount = GetSchCount(); + for (int i = 0; i < SchCount; i++) + { + var SchName = GetSchName(i); + var FrameCount = GetSchFrameCount(SchName); + for (int j = 0; j < FrameCount; j++) + { + LINScheduleConfigs.Add(new LINScheduleConfig() + { + SchTabName = GetSchName(i), + SchTabIndex = i, + MsgName = GetSchFrameName(SchName, j), + MsgNameIndex = j, + Cycle = 10, + IsActive = false, + }); + } + } + + return LINScheduleConfigs; + } + + /// + /// 获取调度的帧的信号个数 + /// + /// + public int GetSchFrameCount(string SchName) + { + return LDFParser.LDF_GetSchFrameQuantity(LDFHandle, new StringBuilder(SchName)); + } + + /// + /// 获取调度的帧的名称 + /// + /// + public string GetSchFrameName(string SchName, int FrameIndex) + { + StringBuilder FrameName = new StringBuilder(); + LDFParser.LDF_GetSchFrameName(LDFHandle, new StringBuilder(SchName), FrameIndex, FrameName); + return FrameName.ToString(); + } /// /// 获取调度表数量 @@ -699,6 +741,62 @@ namespace CapMachine.Wpf.LinDrive } + /// + /// 获取调度表数量 + /// + /// + public string GetSchName(int index) + { + StringBuilder pSchName = new StringBuilder(); + var SchResult = LDFParser.LDF_GetSchName(LDFHandle, index, pSchName); + return pSchName.ToString(); + } + + Random Random=new Random(); + + /// + /// 开始调度表执行 + /// + public void StartSchedule() + { + if (CmdData.Count() == 0) return; + + //防止有多个不同的消息名称/帧,每个帧单独处理发送 + var GroupMsg = CmdData.GroupBy(x => x.MsgName); + foreach (var itemMsg in GroupMsg) + { + foreach (var itemSignal in itemMsg) + { + if (itemSignal.ConfigName.Contains("速")) + { + itemSignal.SignalCmdValue = Random.NextDouble() * 1000; + } + //主机写操作,发送数据给从机 + 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; + } + } + + LDFParser.LDF_ExeSchToBus(LDFHandle, new StringBuilder(itemMsg.Key),1); + LDFParser.LDF_SetSchToTable(LDFHandle, new StringBuilder(itemMsg.Key),1); + } + + + + } + + public void LinTest() { diff --git a/CapMachine.Wpf/ViewModels/DialogLINSchConfigViewModel.cs b/CapMachine.Wpf/ViewModels/DialogLINSchConfigViewModel.cs index 9a407c0..0965d81 100644 --- a/CapMachine.Wpf/ViewModels/DialogLINSchConfigViewModel.cs +++ b/CapMachine.Wpf/ViewModels/DialogLINSchConfigViewModel.cs @@ -1,26 +1,23 @@ -using AutoMapper; +using AutoMapper; using CapMachine.Core; 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 { - public class DialogLINSchConfigViewModel:DialogViewModel + public class DialogLINSchConfigViewModel : DialogViewModel { - public DialogLINSchConfigViewModel(IFreeSql freeSql, IMapper mapper) + public DialogLINSchConfigViewModel(IFreeSql freeSql, IMapper mapper, LinDriveService linDriveService) { Title = "调度表 LIN 配置"; FreeSql = freeSql; Mapper = mapper; + LinDriveService = linDriveService; //默认只能用1号调度器 SchTabIndexCbxItems = new ObservableCollection() @@ -47,10 +44,14 @@ namespace CapMachine.Wpf.ViewModels //}, }; + // 初始化树结构集合 + SchTabTree = new ObservableCollection(); + } public IFreeSql FreeSql { get; } public IMapper Mapper { get; } + public LinDriveService LinDriveService { get; } private string name; /// @@ -113,6 +114,174 @@ namespace CapMachine.Wpf.ViewModels set { _CurSelectedItem = value; RaisePropertyChanged(); } } + //================ TreeView 数据源与选择逻辑 ================ + // 说明: + // 1) SchTabTree 为 XAML 中 TreeView 的 ItemsSource,父节点为调度表 LinSchTabNode,子节点为 LinMsgNode。 + // 2) 仅允许“单选”一个调度表(父节点)。父节点上的 CheckBox 为 TwoWay 绑定 ViewModel 模型的 IsSelected 属性。 + // 3) 我们在 BuildTree() 完成后,通过 AttachNodeHandlers() 给父节点订阅 PropertyChanged, + // 当 IsSelected 变化时,统一走 OnSelectSchTab() 做单选互斥与子节点联动,避免在 XAML 上用行为触发器造成递归。 + private ObservableCollection _SchTabTree; + /// + /// 调度表-消息 树结构数据源(父:SchTabName,子:消息/帧) + /// + public ObservableCollection SchTabTree + { + get { return _SchTabTree; } + set { _SchTabTree = value; RaisePropertyChanged(); } + } + + // 防止选择联动时的递归重入。 + // 在 OnSelectSchTab() 中统一设置 _isUpdatingSelection = true, + // 以避免模型属性变化再次触发 Node_PropertyChanged() / OnSelectSchTab() 形成递归导致 StackOverflow。 + private bool _isUpdatingSelection = false; + + private DelegateCommand _SelectSchTabCmd; + /// + /// 选择/激活 调度表命令(单选),联动选中其下所有消息 + /// + public DelegateCommand SelectSchTabCmd + { + get + { + if (_SelectSchTabCmd == null) + { + _SelectSchTabCmd = new DelegateCommand(OnSelectSchTab); + } + return _SelectSchTabCmd; + } + set { _SelectSchTabCmd = value; } + } + + /// + /// 激活选中的调度表(父节点单选),并联动: + /// 1) 取消其它调度表的选中; + /// 2) 让当前选中调度表的子节点全部选中(仅显示语义,不编辑); + /// 3) 同步底层 ListLINScheduleConfigDto 的 IsActive(只允许一个调度表 Active)。 + /// 通过 _isUpdatingSelection 防重入,避免模型 -> 视图 -> 模型 的递归通知。 + /// + /// + private void OnSelectSchTab(LinSchTabNode node) + { + if (node == null) return; + if (_isUpdatingSelection) return; + try + { + _isUpdatingSelection = true; + + // 单选:当前节点选中时,取消其它调度表选中 + if (node.IsSelected) + { + foreach (var n in SchTabTree) + { + bool isTarget = ReferenceEquals(n, node); + n.IsSelected = isTarget; + foreach (var c in n.Children) + { + c.IsSelected = isTarget; // 子节点选中与父保持一致 + } + } + } + else + { + // 取消选中:其子节点也全部取消 + foreach (var c in node.Children) c.IsSelected = false; + } + + // 同步 DTO 激活状态:按当前树中第一个被选中的调度表名写回 + var active = SchTabTree?.FirstOrDefault(n => n.IsSelected); + foreach (var dto in ListLINScheduleConfigDto) + { + dto.IsActive = active != null && string.Equals(dto.SchTabName, active.SchTabName, StringComparison.Ordinal); + } + } + finally + { + _isUpdatingSelection = false; + } + } + + /// + /// 基于当前 ListLINScheduleConfigDto 构建树结构。 + /// - 将 DTO 以 SchTabName 分组,生成父节点; + /// - 子节点为每条消息/帧(MsgName, MsgNameIndex),IsSelected 与 DTO.IsActive 同步; + /// - 若出现多个分组均被标记 IsActive,则仅保留第一个为选中,其它重置为未选中(保证单选约束); + /// - 构建完成后调用 AttachNodeHandlers() 订阅父节点的 PropertyChanged,实现勾选即联动。 + /// + private void BuildTree() + { + var newTree = new ObservableCollection(); + if (ListLINScheduleConfigDto != null && ListLINScheduleConfigDto.Any()) + { + var groups = ListLINScheduleConfigDto + .GroupBy(x => x.SchTabName ?? string.Empty) + .OrderBy(g => g.Key); + + foreach (var g in groups) + { + var node = new LinSchTabNode + { + SchTabName = g.Key, + IsSelected = g.Any(x => x.IsActive), + Children = new ObservableCollection( + g.Select(x => new LinMsgNode + { + MsgName = x.MsgName ?? string.Empty, + MsgNameIndex = x.MsgNameIndex, + IsSelected = x.IsActive + })) + }; + newTree.Add(node); + } + + // 如果没有任何 IsActive,确保默认不选;若存在多个组被标记,保持第一个,其他置为 false + var activeOnes = newTree.Where(n => n.IsSelected).ToList(); + if (activeOnes.Count > 1) + { + var keep = activeOnes.First(); + foreach (var n in activeOnes.Skip(1)) n.IsSelected = false; + foreach (var dto in ListLINScheduleConfigDto) + { + dto.IsActive = string.Equals(dto.SchTabName, keep.SchTabName, StringComparison.Ordinal); + } + } + } + SchTabTree = newTree; + + // 绑定节点属性变化事件: + // - 父节点 IsSelected 变化 -> Node_PropertyChanged() -> OnSelectSchTab() + // 这样可以不依赖 XAML EventTrigger,减少 UI 侧的递归触发风险。 + AttachNodeHandlers(SchTabTree); + } + + /// + /// 订阅父节点集合的 PropertyChanged。 + /// 注意:子节点当前仅用于显示,不处理其 PropertyChanged。 + /// + private void AttachNodeHandlers(IEnumerable nodes) + { + if (nodes == null) return; + foreach (var n in nodes) + { + n.PropertyChanged -= Node_PropertyChanged; + n.PropertyChanged += Node_PropertyChanged; + if (n.Children != null && n.Children.Any()) + { + // 子节点目前仅做显示,不处理其 PropertyChanged + } + } + } + + /// + /// 父节点属性变更回调:当 IsSelected 变化时,统一交由 OnSelectSchTab() 处理单选与联动。 + /// + private void Node_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(LinSchTabNode.IsSelected)) + { + OnSelectSchTab(sender as LinSchTabNode); + } + } + private DelegateCommand _GridSelectionChangedCmd; /// @@ -169,26 +338,25 @@ namespace CapMachine.Wpf.ViewModels { switch (Par) { - case "Add": - ListLINScheduleConfigDto.Add(new LINScheduleConfigDto - { - CanLinConfigProId = SelectCanLinConfigProId, - Cycle = 100, - SchTabIndex = 0, - }); - break; - case "Delete": - if (CurSelectedItem != null) - { - //直接删除掉,如果没有ID的话,这就不需要删除了 - FreeSql.Delete(CurSelectedItem.Id).ExecuteAffrows(); - ListLINScheduleConfigDto.Remove(CurSelectedItem); + // 禁止新增/删除:来自 LDF 的数据不允许变更 + case "LoadSch": - CurSelectedItem = null; + //通过CAN驱动加载调度表 + if (ListLINScheduleConfigDto != null && ListLINScheduleConfigDto.Count > 0) + { + var messageBoxResult = MessageBox.Show("检测到当前存在数据,下载后将要覆盖当前数据,请你确认?", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Hand); + if (messageBoxResult == MessageBoxResult.OK) + { + LoadSch(); + } + else + { + //不动作 + } } else { - MessageBox.Show("请选中后再进行【删除】操作?", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + LoadSch(); } break; default: @@ -196,6 +364,27 @@ namespace CapMachine.Wpf.ViewModels } } + /// + /// 加载调度表 + /// + private void LoadSch() + { + var ListLINScheduleConfig = LinDriveService.ToomossLinDrive.GetLINScheduleConfigs(); + ListLINScheduleConfigDto.Clear(); + //先清空 + FreeSql.Delete() + .Where(a => a.CanLinConfigProId == SelectCanLinConfigProId) + .ExecuteAffrows(); + + foreach (var item in ListLINScheduleConfig) + { + ListLINScheduleConfigDto.Add(Mapper.Map(item)); + } + // 重新构建树 + BuildTree(); + } + + private DelegateCommand saveCmd; /// /// 保存命令 @@ -231,32 +420,35 @@ namespace CapMachine.Wpf.ViewModels MessageBox.Show("请确认消息名称是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } - if (item.Cycle == 0) - { - MessageBox.Show("请确认周期是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); - return; - } + //if (item.Cycle == 0) + //{ + // MessageBox.Show("请确认周期是否正确", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); + // return; + //} } - //发送的控制帧都放到同一个调度表中,不需要检查了 - ////检查重复设置问题 - //bool isRepeat = ListLINScheduleConfigDto.GroupBy(i => i.MsgName).Any(g => g.Count() > 1); - //if (isRepeat) - //{ - // MessageBox.Show("请确认是否重复设置", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); - // return; - //} + // 保存前:根据树中选中的调度表更新 DTO 的 IsActive,确保只保留一个激活调度表 + var activeNode = SchTabTree?.FirstOrDefault(n => n.IsSelected); + foreach (var dto in ListLINScheduleConfigDto) + { + dto.IsActive = activeNode != null && string.Equals(dto.SchTabName, activeNode.SchTabName, StringComparison.Ordinal); + } - //检查数据是否正常 + //检查数据是否正常并持久化(结构来自 LDF,不允许新增/删除,仅更新激活状态与周期等) foreach (var item in ListLINScheduleConfigDto) { + item.CanLinConfigProId = SelectCanLinConfigProId; + FreeSql.InsertOrUpdate() - .SetSource(Mapper.Map(item)). - ExecuteAffrows(); + .SetSource(Mapper.Map(item)) + .ExecuteAffrows(); } - ListLINScheduleConfigDto = new ObservableCollection(Mapper.Map>(FreeSql.Select().Where(a => a.CanLinConfigProId == SelectCanLinConfigProId).ToList())); + ListLINScheduleConfigDto = new ObservableCollection( + Mapper.Map>(FreeSql.Select().Where(a => a.CanLinConfigProId == SelectCanLinConfigProId).ToList())); + // 重新构建树,反映保存结果 + BuildTree(); DialogParameters pars = new DialogParameters { @@ -315,8 +507,11 @@ namespace CapMachine.Wpf.ViewModels //防止返回的数据为空,就无法增加了 if (ListLINScheduleConfigDto == null) ListLINScheduleConfigDto = new ObservableCollection(); //Name = parameters.GetValue("Name"); - + SelectCanLinConfigProId = parameters.GetValue("SelectCanLinConfigProId"); + + // 初次打开时构建树,并根据已有 IsActive 状态渲染 + BuildTree(); } diff --git a/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs b/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs index 761ad96..12a422a 100644 --- a/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs +++ b/CapMachine.Wpf/ViewModels/LinConfigViewModel.cs @@ -120,7 +120,6 @@ namespace CapMachine.Wpf.ViewModels /// public IDialogService DialogService { get; } - private ObservableCollection _WriteRuleCbxItems; /// /// 写入的规格集合 @@ -232,10 +231,14 @@ namespace CapMachine.Wpf.ViewModels } //调度表配置信息 - if (SelectCanLinConfigPro.CanScheduleConfigs != null && SelectCanLinConfigPro.CanScheduleConfigs.Count() > 0) + if (SelectCanLinConfigPro.LinScheduleConfigs != null && SelectCanLinConfigPro.LinScheduleConfigs.Count() > 0) { ListLINScheduleConfigDto = new ObservableCollection(Mapper.Map>(SelectCanLinConfigPro.LinScheduleConfigs)); } + else + { + ListLINScheduleConfigDto = new ObservableCollection(); + } //匹配选中的SelectCanLinConfigPro.CanLinConfigContents和ListLinLdfModel MatchSeletedAndLinLdfModel(); @@ -601,7 +604,7 @@ namespace CapMachine.Wpf.ViewModels SelectCanLinConfigProConfigName = SelectCanLinConfigPro.ConfigName; //调度表配置信息 - if (SelectCanLinConfigPro.CanScheduleConfigs != null && SelectCanLinConfigPro.CanScheduleConfigs.Count() > 0) + if (SelectCanLinConfigPro.LinScheduleConfigs != null && SelectCanLinConfigPro.LinScheduleConfigs.Count() > 0) { ListLINScheduleConfigDto = new ObservableCollection(Mapper.Map>(SelectCanLinConfigPro.LinScheduleConfigs)); } @@ -1235,8 +1238,6 @@ namespace CapMachine.Wpf.ViewModels InitLoadLinConfigPro(); } - - break; case "Delete": if (SelectedWriteCanLinRWConfigDto != null) @@ -1257,41 +1258,41 @@ namespace CapMachine.Wpf.ViewModels break; case "WriteSch"://调度表 - LinDriveService.ToomossLinDrive.LinTest(); + //LinDriveService.ToomossLinDrive.LinTest(); + LinDriveService.ToomossLinDrive.StartSchedule(); + break; + // + if (LinDriveService.ToomossLinDrive.OpenState) + { + //弹窗 + DialogService.ShowDialog("DialogLINSchConfigView", new DialogParameters() { + {"ListMsg", LinDriveService.CmdData.GroupBy(a=>a.MsgName).Select(a=>a.Key).ToList() }, + { "ListLINScheduleConfigDto",ListLINScheduleConfigDto}, + { "SelectCanLinConfigProId",SelectCanLinConfigPro.Id}, - //var data = LinDriveService.CmdData.GroupBy(a => a.MsgName).Select(a => a.Key).ToList(); + }, (par) => + { + if (par.Result == ButtonResult.OK) + { + ////程序名称 + ListLINScheduleConfigDto = par.Parameters.GetValue>("ReturnValue"); + //把更新后的最新值给当前的主集合中 + SelectCanLinConfigPro.LinScheduleConfigs = Mapper.Map>(ListLINScheduleConfigDto.ToList()); - //if (data != null && data.Count > 0) - //{ - // //弹窗 - // DialogService.ShowDialog("DialogLINSchConfigView", new DialogParameters() { - // {"ListMsg", LinDriveService.CmdData.GroupBy(a=>a.MsgName).Select(a=>a.Key).ToList() }, - // { "ListLINScheduleConfigDto",ListLINScheduleConfigDto}, - // { "SelectCanLinConfigProId",SelectCanLinConfigPro.Id}, + } + else if (par.Result == ButtonResult.Cancel) + { + //取消 - // }, (par) => - // { - // if (par.Result == ButtonResult.OK) - // { - // ////程序名称 - // ListLINScheduleConfigDto = par.Parameters.GetValue>("ReturnValue"); - // //把更新后的最新值给当前的主集合中 - // SelectCanLinConfigPro.LinScheduleConfigs = Mapper.Map>(ListLINScheduleConfigDto.ToList()); + } - // } - // else if (par.Result == ButtonResult.Cancel) - // { - // //取消 - - // } - - // }); - //} - //else - //{ - // System.Windows.MessageBox.Show("未发现写入的执行的命令数据,无法配置?", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand); - //} + }); + } + else + { + System.Windows.MessageBox.Show("未发现写入的执行的命令数据,无法配置?", "提示", System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Hand); + } diff --git a/CapMachine.Wpf/ViewModels/LinTreeNodes.cs b/CapMachine.Wpf/ViewModels/LinTreeNodes.cs new file mode 100644 index 0000000..0fcb8ce --- /dev/null +++ b/CapMachine.Wpf/ViewModels/LinTreeNodes.cs @@ -0,0 +1,89 @@ +using Prism.Mvvm; +using System.Collections.ObjectModel; + +namespace CapMachine.Wpf.ViewModels +{ + /// + /// 调度表节点(父节点)。 + /// 与 XAML 中父级 绑定: + /// - 显示字段: + /// - 选择字段:(TwoWay 绑定至 CheckBox) + /// - 子集合:(用于展开显示消息/帧子节点) + /// + /// 使用 Prism 的 + /// 以避免值未发生变化时重复触发 PropertyChanged 引起的联动递归。 + /// + public class LinSchTabNode : BindableBase + { + /// + /// 调度表名称(父节点标题)。 + /// + private string _SchTabName; + public string SchTabName + { + get => _SchTabName; + set => SetProperty(ref _SchTabName, value); + } + + private bool _IsSelected; + /// + /// 是否被激活/选中(父节点单选)。 + /// 该属性与 XAML 中父节点的 CheckBox 进行 TwoWay 绑定, + /// 其变更由 ViewModel 监听并处理“单选联动”和“子节点跟随”。 + /// + public bool IsSelected + { + get => _IsSelected; + set => SetProperty(ref _IsSelected, value); + } + + /// + /// 子节点集合(消息/帧条目)。 + /// + private ObservableCollection _Children = new ObservableCollection(); + public ObservableCollection Children + { + get => _Children; + set => SetProperty(ref _Children, value); + } + } + + /// + /// 消息/帧 节点(子节点)。 + /// 与 XAML 中父级 绑定: + /// - 显示字段: / + /// - 选择字段:(当前设计作为“显示联动”,非编辑入口) + /// + public class LinMsgNode : BindableBase + { + /// + /// 消息/帧名称。 + /// + private string _MsgName; + public string MsgName + { + get => _MsgName; + set => SetProperty(ref _MsgName, value); + } + + /// + /// 消息/帧 Index。 + /// + private int _MsgNameIndex; + public int MsgNameIndex + { + get => _MsgNameIndex; + set => SetProperty(ref _MsgNameIndex, value); + } + + private bool _IsSelected; + /// + /// 是否被选中(随父节点联动显示)。 + /// + public bool IsSelected + { + get => _IsSelected; + set => SetProperty(ref _IsSelected, value); + } + } +} diff --git a/CapMachine.Wpf/Views/DialogLINSchConfigView.xaml b/CapMachine.Wpf/Views/DialogLINSchConfigView.xaml index 418343d..59a0b1a 100644 --- a/CapMachine.Wpf/Views/DialogLINSchConfigView.xaml +++ b/CapMachine.Wpf/Views/DialogLINSchConfigView.xaml @@ -1,4 +1,4 @@ - @@ -31,7 +32,7 @@ - + + + - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Padding="6" + BorderBrush="#D0D0D0" + BorderThickness="1" + CornerRadius="4"> + + + + + + + + + + + + + + + + + + + + + + + + + + +