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