From 407f332d7c5f3c1593b3aba01490150159b74e91 Mon Sep 17 00:00:00 2001 From: Ivo Petrov Date: Thu, 18 Jul 2024 11:35:16 +0100 Subject: [PATCH 1/9] temp --- TuneUp/ProfiledNodeViewModel.cs | 65 +++++++- TuneUp/TuneUpWindow.xaml | 284 +++++++++++++++++--------------- TuneUp/TuneUpWindow.xaml.cs | 44 +++++ TuneUp/TuneUpWindowViewModel.cs | 124 +++++++++++++- 4 files changed, 381 insertions(+), 136 deletions(-) diff --git a/TuneUp/ProfiledNodeViewModel.cs b/TuneUp/ProfiledNodeViewModel.cs index 0647e8c..83709eb 100644 --- a/TuneUp/ProfiledNodeViewModel.cs +++ b/TuneUp/ProfiledNodeViewModel.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Windows.Media; using Dynamo.Core; using Dynamo.Graph.Nodes; @@ -16,6 +17,8 @@ public class ProfiledNodeViewModel : NotificationObject /// public static readonly string ExecutionTimelString = "Execution Time"; + public static readonly string GroupNodePrefix = "Group:"; + private string name = String.Empty; /// /// The name of this profiled node. This value can be either an actual @@ -27,7 +30,7 @@ public string Name get { // For virtual row, do not attempt to grab node name - if (!name.Contains(ExecutionTimelString)) + if (!name.Contains(ExecutionTimelString) && !name.StartsWith(GroupNodePrefix)) name = NodeModel?.Name; return name; } @@ -52,6 +55,20 @@ public int? ExecutionOrderNumber } private int? executionOrderNumber; + public int? ExecutionGroupOrderNumber + { + get + { + return executionGroupOrderNumber; + } + set + { + executionGroupOrderNumber = value; + RaisePropertyChanged(nameof(ExecutionGroupOrderNumber)); + } + } + private int? executionGroupOrderNumber; + /// /// The most recent execution time of this node @@ -116,6 +133,43 @@ public ProfiledNodeState State } private ProfiledNodeState state; + public Guid GroupGUID + { + get => groupGIUD; + set + { + groupGIUD = value; + RaisePropertyChanged(nameof(GroupGUID)); + } + } + private Guid groupGIUD; + + public bool IsGroup + { + get => isGroup; + set + { + isGroup = value; + RaisePropertyChanged(nameof(IsGroup)); + } + } + private bool isGroup; + + public SolidColorBrush GroupBackgroundBrush + { + get => groupBackgroundBrush; + set + { + if (value != null) + { + groupBackgroundBrush = value; + RaisePropertyChanged(nameof(GroupBackgroundBrush)); + } + } + } + private SolidColorBrush groupBackgroundBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")); + + /// /// Return the display name of state enum. /// Making this identical property because of datagrid binding @@ -165,5 +219,14 @@ public ProfiledNodeViewModel(string name, TimeSpan exTimeSum, ProfiledNodeState this.ExecutionTime = exTimeSum; State = state; } + + // Constructor for ProfiledAnnotationNodes + public ProfiledNodeViewModel(string name, SolidColorBrush backgroundBrush) + { + NodeModel = null; + this.Name = name; + State = ProfiledNodeState.NotExecuted; + GroupBackgroundBrush = backgroundBrush; + } } } diff --git a/TuneUp/TuneUpWindow.xaml b/TuneUp/TuneUpWindow.xaml index 5d5ad4d..5387eaa 100644 --- a/TuneUp/TuneUpWindow.xaml +++ b/TuneUp/TuneUpWindow.xaml @@ -1,86 +1,94 @@ - + + + - + - + - + - + - + - + - + @@ -145,19 +153,19 @@ - - + + Orientation="Horizontal"> - - + - + Foreground="{StaticResource NodeNameForeground}" + Text="{Binding Name}" /> - - + + - - + + + - + --> + + + + + + + + + diff --git a/TuneUp/TuneUpWindow.xaml.cs b/TuneUp/TuneUpWindow.xaml.cs index 839e7e2..d98357f 100644 --- a/TuneUp/TuneUpWindow.xaml.cs +++ b/TuneUp/TuneUpWindow.xaml.cs @@ -1,9 +1,12 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Media; using Dynamo.Extensions; using Dynamo.Graph.Nodes; using Dynamo.Models; @@ -152,4 +155,45 @@ private void ExportTimes_Click(object sender, RoutedEventArgs e) (NodeAnalysisTable.DataContext as TuneUpWindowViewModel).ExportToCsv(); } } + + #region Converters + + public class ParentToMarginConverter : IValueConverter + { + private static readonly Guid DefaultGuid = Guid.Empty; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + + //return value > 0 ? new System.Windows.Thickness(30, 0, 0, 0) : new System.Windows.Thickness(0, 0, 0, 0); + + if (value is int intValue && intValue != 0) + { + return new System.Windows.Thickness(30, 0, 0, 0); + } + return new System.Windows.Thickness(0, 0, 0, 0); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + public class IsGroupToBrushConverter : IValueConverter + { + private static readonly Guid DefaultGuid = Guid.Empty; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + bool boolValue = (bool)value; + return boolValue ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")) : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AAAAAA")); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + + #endregion } diff --git a/TuneUp/TuneUpWindowViewModel.cs b/TuneUp/TuneUpWindowViewModel.cs index b65eaf0..52355fc 100644 --- a/TuneUp/TuneUpWindowViewModel.cs +++ b/TuneUp/TuneUpWindowViewModel.cs @@ -3,10 +3,12 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using System.DirectoryServices; using System.IO; using System.Linq; using System.Threading; using System.Windows.Data; +using System.Windows.Media; using Dynamo.Core; using Dynamo.Engine.Profiling; using Dynamo.Graph.Nodes; @@ -60,6 +62,7 @@ public class TuneUpWindowViewModel : NotificationObject, IDisposable private bool isTuneUpChecked = false; private ListSortDirection sortDirection; private string sortingOrder; + private Dictionary> groupedNodesDictionary = new Dictionary>(); /// /// Name of the row to display current execution time @@ -246,22 +249,65 @@ internal void ResetProfiledNodes() // Use temporary collections to minimize UI updates var newProfiledNodes = new ObservableCollection(); var newNodeDictionary = new Dictionary(); + var newGroupedNodesDictionary = new Dictionary>(); // Assign the new collection ProfiledNodes = newProfiledNodes; nodeDictionary = newNodeDictionary; + groupedNodesDictionary = newGroupedNodesDictionary; + // List to hold grouped nodes + var nodesInGroups = new HashSet(); + + // Process groups and their nodes + foreach (var group in CurrentWorkspace.Annotations) + { + var groupProfiledNode = new ProfiledNodeViewModel( + $"{ProfiledNodeViewModel.GroupNodePrefix}{group.AnnotationText}", + new SolidColorBrush((Color)ColorConverter.ConvertFromString(group.Background))) + { + IsGroup = true, + GroupGUID = group.GUID + }; + + nodeDictionary[group.GUID] = groupProfiledNode; + ProfiledNodes.Add(groupProfiledNode); + + var groupNodes = new List(); + groupedNodesDictionary[group.GUID] = groupNodes; + + foreach (var node in group.Nodes) + { + if (node is NodeModel nodeModel && nodesInGroups.Add(nodeModel)) + { + var profiledNode = new ProfiledNodeViewModel(nodeModel) + { + GroupGUID = group.GUID + }; + + nodeDictionary[node.GUID] = profiledNode; + ProfiledNodes.Add(profiledNode); + groupNodes.Add(profiledNode); + } + } + } + + // Process standalone nodes foreach (var node in CurrentWorkspace.Nodes) { - var profiledNode = new ProfiledNodeViewModel(node); - nodeDictionary[node.GUID] = profiledNode; - ProfiledNodes.Add(profiledNode); + if (!nodesInGroups.Contains(node)) + { + var profiledNode = new ProfiledNodeViewModel(node); + nodeDictionary[node.GUID] = profiledNode; + ProfiledNodes.Add(profiledNode); + } } ProfiledNodesCollection = new CollectionViewSource(); ProfiledNodesCollection.Source = ProfiledNodes; // Sort the data by execution state ProfiledNodesCollection.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfiledNodeViewModel.StateDescription))); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.GroupGUID), ListSortDirection.Ascending)); ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.State), ListSortDirection.Ascending)); ProfiledNodesCollection.View?.Refresh(); @@ -349,6 +395,7 @@ private void CurrentWorkspaceModel_EvaluationStarted(object sender, EventArgs e) private void CurrentWorkspaceModel_EvaluationCompleted(object sender, Dynamo.Models.EvaluationCompletedEventArgs e) { IsRecomputeEnabled = true; + CalculateGroupNodes(); UpdateExecutionTime(); RaisePropertyChanged(nameof(ProfiledNodesCollection)); RaisePropertyChanged(nameof(ProfiledNodes)); @@ -383,6 +430,77 @@ private void UpdateExecutionTime() RaisePropertyChanged(nameof(TotalGraphExecutiontime)); } + + + + + + + + + /// + /// Calculates and assigns execution order numbers for profiled nodes. + /// Aggregates execution times and updates states for nodes within groups. + /// Ensures nodes are processed only once and maintains the sorted order of nodes. + /// + private void CalculateGroupNodes() + { + int mainExecutionCounter = 1; + var processedNodes = new HashSet(); + var sortedProfiledNodes = ProfiledNodes.OrderBy(node => node.ExecutionOrderNumber).ToList(); + + foreach (var node in sortedProfiledNodes) + { + // if the node is from a group and not yet processed + if (!node.IsGroup && node.GroupGUID != Guid.Empty && !processedNodes.Contains(node)) + { + if (nodeDictionary.TryGetValue(node.GroupGUID, out var groupNode)) + { + if (groupedNodesDictionary.TryGetValue(node.GroupGUID, out var nodesInGroup)) + { + + int groupExecutionCounter = 1; + + foreach (var groupedNode in nodesInGroup) + { + if (processedNodes.Add(groupedNode)) // Adds to HashSet and checks if it was added + { + groupNode.ExecutionTime += groupedNode.ExecutionTime; // Aggregate group execution time + + // Update group state if any node in the group was executed on current run + if (groupedNode.State == ProfiledNodeState.ExecutedOnCurrentRun) + { + groupNode.State = ProfiledNodeState.ExecutedOnCurrentRun; + } + + groupNode.ExecutionOrderNumber = mainExecutionCounter; + groupedNode.ExecutionOrderNumber = mainExecutionCounter; + groupedNode.ExecutionGroupOrderNumber = groupExecutionCounter; + groupExecutionCounter++; + } + } + mainExecutionCounter++; + } + } + } + // if the node is not from a group + else if (!node.IsGroup && processedNodes.Add(node) && !node.Name.Contains(ProfiledNodeViewModel.ExecutionTimelString)) + { + node.ExecutionOrderNumber = mainExecutionCounter++; + } + } + RaisePropertyChanged(nameof(ProfiledNodes)); + } + + + + + + + + + + /// /// Applies the sorting logic to the ProfiledNodesCollection. /// From 63b6128948015554c42292d350843bdb403cefe7 Mon Sep 17 00:00:00 2001 From: Ivo Petrov Date: Thu, 18 Jul 2024 15:24:14 +0100 Subject: [PATCH 2/9] temp --- TuneUp/TuneUpWindow.xaml | 47 ++++++++++++---- TuneUp/TuneUpWindow.xaml.cs | 98 ++++++++++++++++++++++++++------- TuneUp/TuneUpWindowViewModel.cs | 37 +++++++++---- 3 files changed, 139 insertions(+), 43 deletions(-) diff --git a/TuneUp/TuneUpWindow.xaml b/TuneUp/TuneUpWindow.xaml index 5387eaa..f093fb2 100644 --- a/TuneUp/TuneUpWindow.xaml +++ b/TuneUp/TuneUpWindow.xaml @@ -20,6 +20,7 @@ + @@ -206,7 +207,8 @@ SelectionChanged="NodeAnalysisTable_SelectionChanged" SelectionMode="Single" SelectionUnit="FullRow" - Style="{StaticResource DataGridStyle1}"> + Style="{StaticResource DataGridStyle1}" + Sorting="NodeAnalysisTable_Sorting"> @@ -228,20 +230,31 @@ - + - - + + + + private void NodeAnalysisTable_Sorting(object sender, DataGridSortingEventArgs e) { - var viewModel = NodeAnalysisTable.DataContext as TuneUpWindowViewModel; - if (viewModel != null) + //var viewModel = NodeAnalysisTable.DataContext as TuneUpWindowViewModel; + //if (viewModel != null) + //{ + // viewModel.SortingOrder = e.Column.Header switch + // { + // "#" => "number", + // "Name" => "name", + // "Execution Time (ms)" => "time", + // _ => viewModel.SortingOrder + // }; + + // // Set the sorting direction of the datagrid column + // e.Column.SortDirection = viewModel.SortDirection == ListSortDirection.Descending + // ? ListSortDirection.Descending + // : ListSortDirection.Ascending; + + // // Apply custom sorting to ensure total times are at the bottom + // viewModel.ApplySorting(); + // e.Handled = true; + //} + + var dataView = CollectionViewSource.GetDefaultView(NodeAnalysisTable.ItemsSource); + ListSortDirection direction; + + // Determine the new sort direction + if (e.Column.SortDirection == null || e.Column.SortDirection == ListSortDirection.Descending) { - viewModel.SortingOrder = e.Column.Header switch - { - "#" => "number", - "Name" => "name", - "Execution Time (ms)" => "time", - _ => viewModel.SortingOrder - }; - - // Set the sorting direction of the datagrid column - e.Column.SortDirection = viewModel.SortDirection == ListSortDirection.Descending - ? ListSortDirection.Descending - : ListSortDirection.Ascending; - - // Apply custom sorting to ensure total times are at the bottom - viewModel.ApplySorting(); - e.Handled = true; + direction = ListSortDirection.Ascending; + } + else + { + direction = ListSortDirection.Descending; + } + + // Clear previous sort descriptions + dataView.SortDescriptions.Clear(); + + // Apply new sort description based on the column being sorted + if (e.Column.Header.ToString() == "Execution Time (ms)") + { + dataView.SortDescriptions.Add(new SortDescription("ExecutionMilliseconds", direction)); + } + else if (e.Column.Header.ToString() == "Name") + { + dataView.SortDescriptions.Add(new SortDescription("Name", direction)); + } + else if (e.Column.Header.ToString() == "#") + { + dataView.SortDescriptions.Add(new SortDescription("ExecutionOrderNumber", direction)); } + + // Refresh the view to apply the sort + dataView.Refresh(); + e.Column.SortDirection = direction; + + // Mark the event as handled + e.Handled = true; + } private void ExportTimes_Click(object sender, RoutedEventArgs e) @@ -158,15 +196,14 @@ private void ExportTimes_Click(object sender, RoutedEventArgs e) #region Converters + + // TODO: Rename Converters! public class ParentToMarginConverter : IValueConverter { private static readonly Guid DefaultGuid = Guid.Empty; public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - - //return value > 0 ? new System.Windows.Thickness(30, 0, 0, 0) : new System.Windows.Thickness(0, 0, 0, 0); - if (value is int intValue && intValue != 0) { return new System.Windows.Thickness(30, 0, 0, 0); @@ -195,5 +232,24 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu } } + public class GuidToVisibilityConverter : IValueConverter + { + private static readonly Guid DefaultGuid = Guid.Empty; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is Guid guidValue && guidValue != DefaultGuid) + { + return Visibility.Collapsed; + } + return Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } + #endregion } diff --git a/TuneUp/TuneUpWindowViewModel.cs b/TuneUp/TuneUpWindowViewModel.cs index 52355fc..a92819e 100644 --- a/TuneUp/TuneUpWindowViewModel.cs +++ b/TuneUp/TuneUpWindowViewModel.cs @@ -61,7 +61,7 @@ public class TuneUpWindowViewModel : NotificationObject, IDisposable private SynchronizationContext uiContext; private bool isTuneUpChecked = false; private ListSortDirection sortDirection; - private string sortingOrder; + private string sortingOrder = "number"; private Dictionary> groupedNodesDictionary = new Dictionary>(); /// @@ -267,7 +267,7 @@ internal void ResetProfiledNodes() new SolidColorBrush((Color)ColorConverter.ConvertFromString(group.Background))) { IsGroup = true, - GroupGUID = group.GUID + //GroupGUID = group.GUID }; nodeDictionary[group.GUID] = groupProfiledNode; @@ -420,8 +420,8 @@ private void UpdateExecutionTime() ProfiledNodes.Remove(CurrentExecutionTimeRow); ProfiledNodes.Remove(PreviousExecutionTimeRow); // After each evaluation, manually update execution time column(s) - var totalSpanExecuted = new TimeSpan(ProfiledNodes.Where(n => n.WasExecutedOnLastRun).Sum(r => r.ExecutionTime.Ticks)); - var totalSpanUnexecuted = new TimeSpan(ProfiledNodes.Where(n => !n.WasExecutedOnLastRun).Sum(r => r.ExecutionTime.Ticks)); + var totalSpanExecuted = new TimeSpan(ProfiledNodes.Where(n => n.WasExecutedOnLastRun).Where(n => !n.IsGroup).Sum(r => r.ExecutionTime.Ticks)); + var totalSpanUnexecuted = new TimeSpan(ProfiledNodes.Where(n => !n.WasExecutedOnLastRun).Where(n => !n.IsGroup).Sum(r => r.ExecutionTime.Ticks)); ProfiledNodes.Add(new ProfiledNodeViewModel( CurrentExecutionString, totalSpanExecuted, ProfiledNodeState.ExecutedOnCurrentRunTotal)); ProfiledNodes.Add(new ProfiledNodeViewModel( @@ -458,7 +458,7 @@ private void CalculateGroupNodes() { if (groupedNodesDictionary.TryGetValue(node.GroupGUID, out var nodesInGroup)) { - + groupNode.ExecutionTime = TimeSpan.Zero; // reset the Execution Time int groupExecutionCounter = 1; foreach (var groupedNode in nodesInGroup) @@ -512,13 +512,28 @@ public void ApplySorting() ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.State), ListSortDirection.Ascending)); // Sort nodes into execution order and make sure Total execution time is always bottom - var sortDescription = sortingOrder switch + //var sortDescription = sortingOrder switch + //{ + // "time" => new SortDescription(nameof(ProfiledNodeViewModel.ExecutionTime), sortDirection), + // "name" => new SortDescription(nameof(ProfiledNodeViewModel.Name), sortDirection), + // _ => new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), sortDirection), + //}; + //ProfiledNodesCollection.SortDescriptions.Add(sortDescription); + + switch (sortingOrder) { - "time" => new SortDescription(nameof(ProfiledNodeViewModel.ExecutionTime), sortDirection), - "name" => new SortDescription(nameof(ProfiledNodeViewModel.Name), sortDirection), - _ => new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), sortDirection), - }; - ProfiledNodesCollection.SortDescriptions.Add(sortDescription); + case "time": + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionTime), sortDirection)); + break; + case "name": + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.Name), sortDirection)); + break; + case "number": + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), sortDirection)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.IsGroup), ListSortDirection.Descending)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionGroupOrderNumber), sortDirection)); + break; + } } internal void OnNodeExecutionBegin(NodeModel nm) From c311ef24d51d443deff2b7ca7039edfab546b1f0 Mon Sep 17 00:00:00 2001 From: Ivo Petrov Date: Thu, 18 Jul 2024 16:53:28 +0100 Subject: [PATCH 3/9] temp --- TuneUp/ProfiledNodeViewModel.cs | 26 ++++++++ TuneUp/TuneUpWindow.xaml.cs | 102 ++++++++++++++++---------------- TuneUp/TuneUpWindowViewModel.cs | 26 ++++++-- 3 files changed, 98 insertions(+), 56 deletions(-) diff --git a/TuneUp/ProfiledNodeViewModel.cs b/TuneUp/ProfiledNodeViewModel.cs index 83709eb..ee741d6 100644 --- a/TuneUp/ProfiledNodeViewModel.cs +++ b/TuneUp/ProfiledNodeViewModel.cs @@ -88,6 +88,21 @@ public TimeSpan ExecutionTime } private TimeSpan executionTime; + public TimeSpan GroupExecutionTime + { + get + { + return groupExecutionTime; + } + set + { + executionTime = value; + RaisePropertyChanged(nameof(GroupExecutionTime)); + RaisePropertyChanged(nameof(ExecutionMilliseconds)); + } + } + private TimeSpan groupExecutionTime; + /// /// The most recent execution time of this node in milliseconds /// @@ -155,6 +170,17 @@ public bool IsGroup } private bool isGroup; + public string GroupName + { + get => groupName; + set + { + groupName = value; + RaisePropertyChanged(nameof(GroupName)); + } + } + private string groupName; + public SolidColorBrush GroupBackgroundBrush { get => groupBackgroundBrush; diff --git a/TuneUp/TuneUpWindow.xaml.cs b/TuneUp/TuneUpWindow.xaml.cs index 4b170f7..5f9a8a6 100644 --- a/TuneUp/TuneUpWindow.xaml.cs +++ b/TuneUp/TuneUpWindow.xaml.cs @@ -128,63 +128,63 @@ private void RecomputeGraph_Click(object sender, RoutedEventArgs e) /// private void NodeAnalysisTable_Sorting(object sender, DataGridSortingEventArgs e) { - //var viewModel = NodeAnalysisTable.DataContext as TuneUpWindowViewModel; - //if (viewModel != null) - //{ - // viewModel.SortingOrder = e.Column.Header switch - // { - // "#" => "number", - // "Name" => "name", - // "Execution Time (ms)" => "time", - // _ => viewModel.SortingOrder - // }; - - // // Set the sorting direction of the datagrid column - // e.Column.SortDirection = viewModel.SortDirection == ListSortDirection.Descending - // ? ListSortDirection.Descending - // : ListSortDirection.Ascending; - - // // Apply custom sorting to ensure total times are at the bottom - // viewModel.ApplySorting(); - // e.Handled = true; - //} - - var dataView = CollectionViewSource.GetDefaultView(NodeAnalysisTable.ItemsSource); - ListSortDirection direction; - - // Determine the new sort direction - if (e.Column.SortDirection == null || e.Column.SortDirection == ListSortDirection.Descending) + var viewModel = NodeAnalysisTable.DataContext as TuneUpWindowViewModel; + if (viewModel != null) { - direction = ListSortDirection.Ascending; - } - else - { - direction = ListSortDirection.Descending; + viewModel.SortingOrder = e.Column.Header switch + { + "#" => "number", + "Name" => "name", + "Execution Time (ms)" => "time", + _ => viewModel.SortingOrder + }; + + // Set the sorting direction of the datagrid column + e.Column.SortDirection = viewModel.SortDirection == ListSortDirection.Descending + ? ListSortDirection.Descending + : ListSortDirection.Ascending; + + // Apply custom sorting to ensure total times are at the bottom + viewModel.ApplySorting(); + e.Handled = true; } - // Clear previous sort descriptions - dataView.SortDescriptions.Clear(); + //var dataView = CollectionViewSource.GetDefaultView(NodeAnalysisTable.ItemsSource); + //ListSortDirection direction; - // Apply new sort description based on the column being sorted - if (e.Column.Header.ToString() == "Execution Time (ms)") - { - dataView.SortDescriptions.Add(new SortDescription("ExecutionMilliseconds", direction)); - } - else if (e.Column.Header.ToString() == "Name") - { - dataView.SortDescriptions.Add(new SortDescription("Name", direction)); - } - else if (e.Column.Header.ToString() == "#") - { - dataView.SortDescriptions.Add(new SortDescription("ExecutionOrderNumber", direction)); - } + //// Determine the new sort direction + //if (e.Column.SortDirection == null || e.Column.SortDirection == ListSortDirection.Descending) + //{ + // direction = ListSortDirection.Ascending; + //} + //else + //{ + // direction = ListSortDirection.Descending; + //} + + //// Clear previous sort descriptions + //dataView.SortDescriptions.Clear(); + + //// Apply new sort description based on the column being sorted + //if (e.Column.Header.ToString() == "Execution Time (ms)") + //{ + // dataView.SortDescriptions.Add(new SortDescription("ExecutionMilliseconds", direction)); + //} + //else if (e.Column.Header.ToString() == "Name") + //{ + // dataView.SortDescriptions.Add(new SortDescription("Name", direction)); + //} + //else if (e.Column.Header.ToString() == "#") + //{ + // dataView.SortDescriptions.Add(new SortDescription("ExecutionOrderNumber", direction)); + //} - // Refresh the view to apply the sort - dataView.Refresh(); - e.Column.SortDirection = direction; + //// Refresh the view to apply the sort + //dataView.Refresh(); + //e.Column.SortDirection = direction; - // Mark the event as handled - e.Handled = true; + //// Mark the event as handled + //e.Handled = true; } diff --git a/TuneUp/TuneUpWindowViewModel.cs b/TuneUp/TuneUpWindowViewModel.cs index a92819e..645289d 100644 --- a/TuneUp/TuneUpWindowViewModel.cs +++ b/TuneUp/TuneUpWindowViewModel.cs @@ -15,6 +15,7 @@ using Dynamo.Graph.Workspaces; using Dynamo.ViewModels; using Dynamo.Wpf.Extensions; +using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.Win32; namespace TuneUp @@ -268,6 +269,7 @@ internal void ResetProfiledNodes() { IsGroup = true, //GroupGUID = group.GUID + GroupName = group.AnnotationText }; nodeDictionary[group.GUID] = groupProfiledNode; @@ -282,7 +284,8 @@ internal void ResetProfiledNodes() { var profiledNode = new ProfiledNodeViewModel(nodeModel) { - GroupGUID = group.GUID + GroupGUID = group.GUID, + GroupName = group.AnnotationText }; nodeDictionary[node.GUID] = profiledNode; @@ -458,14 +461,14 @@ private void CalculateGroupNodes() { if (groupedNodesDictionary.TryGetValue(node.GroupGUID, out var nodesInGroup)) { - groupNode.ExecutionTime = TimeSpan.Zero; // reset the Execution Time + groupNode.GroupExecutionTime = TimeSpan.Zero; // reset the Execution Time int groupExecutionCounter = 1; foreach (var groupedNode in nodesInGroup) { if (processedNodes.Add(groupedNode)) // Adds to HashSet and checks if it was added { - groupNode.ExecutionTime += groupedNode.ExecutionTime; // Aggregate group execution time + groupNode.GroupExecutionTime += groupedNode.ExecutionTime; // Aggregate group execution time // Update group state if any node in the group was executed on current run if (groupedNode.State == ProfiledNodeState.ExecutedOnCurrentRun) @@ -479,6 +482,7 @@ private void CalculateGroupNodes() groupExecutionCounter++; } } + groupNode.ExecutionTime = groupNode.GroupExecutionTime; mainExecutionCounter++; } } @@ -487,8 +491,18 @@ private void CalculateGroupNodes() else if (!node.IsGroup && processedNodes.Add(node) && !node.Name.Contains(ProfiledNodeViewModel.ExecutionTimelString)) { node.ExecutionOrderNumber = mainExecutionCounter++; + node.GroupExecutionTime = node.ExecutionTime; } } + + //// iterate again to align GroupExecutionTimes + //foreach (var node in processedNodes) + //{ + // var relatedGroup = nodeDictionary[node.GroupGUID]; + // if (relatedGroup != null) + // { node.GroupExecutionTime = relatedGroup.GroupExecutionTime; } + //} + RaisePropertyChanged(nameof(ProfiledNodes)); } @@ -523,10 +537,12 @@ public void ApplySorting() switch (sortingOrder) { case "time": - ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionTime), sortDirection)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.GroupExecutionTime), sortDirection)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.IsGroup), ListSortDirection.Descending)); break; case "name": - ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.Name), sortDirection)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.GroupName), sortDirection)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.IsGroup), ListSortDirection.Descending)); break; case "number": ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), sortDirection)); From f91697cb4fe6641612cc5b9681621d7ecb1b3347 Mon Sep 17 00:00:00 2001 From: Ivo Petrov Date: Fri, 19 Jul 2024 18:45:06 +0100 Subject: [PATCH 4/9] mostly works Group colors do not update Not teste with nested groups --- TuneUp/ProfiledNodeViewModel.cs | 29 +++-- TuneUp/TuneUpWindow.xaml | 70 ++++------ TuneUp/TuneUpWindow.xaml.cs | 83 +++++------- TuneUp/TuneUpWindowViewModel.cs | 220 ++++++++++++++++++++------------ 4 files changed, 206 insertions(+), 196 deletions(-) diff --git a/TuneUp/ProfiledNodeViewModel.cs b/TuneUp/ProfiledNodeViewModel.cs index ee741d6..35a1868 100644 --- a/TuneUp/ProfiledNodeViewModel.cs +++ b/TuneUp/ProfiledNodeViewModel.cs @@ -17,7 +17,7 @@ public class ProfiledNodeViewModel : NotificationObject /// public static readonly string ExecutionTimelString = "Execution Time"; - public static readonly string GroupNodePrefix = "Group:"; + public static readonly string GroupNodePrefix = "Group: "; private string name = String.Empty; /// @@ -55,19 +55,19 @@ public int? ExecutionOrderNumber } private int? executionOrderNumber; - public int? ExecutionGroupOrderNumber + public int? GroupExecutionOrderNumber { get { - return executionGroupOrderNumber; + return groupExecutionOrderNumber; } set { - executionGroupOrderNumber = value; - RaisePropertyChanged(nameof(ExecutionGroupOrderNumber)); + groupExecutionOrderNumber = value; + RaisePropertyChanged(nameof(GroupExecutionOrderNumber)); } } - private int? executionGroupOrderNumber; + private int? groupExecutionOrderNumber; /// @@ -96,9 +96,8 @@ public TimeSpan GroupExecutionTime } set { - executionTime = value; + groupExecutionTime = value; RaisePropertyChanged(nameof(GroupExecutionTime)); - RaisePropertyChanged(nameof(ExecutionMilliseconds)); } } private TimeSpan groupExecutionTime; @@ -181,19 +180,19 @@ public string GroupName } private string groupName; - public SolidColorBrush GroupBackgroundBrush + public SolidColorBrush GroupNodeBackgroundBrush { - get => groupBackgroundBrush; + get => groupNodeBackgroundBrush; set { if (value != null) { - groupBackgroundBrush = value; - RaisePropertyChanged(nameof(GroupBackgroundBrush)); + groupNodeBackgroundBrush = value; + RaisePropertyChanged(nameof(GroupNodeBackgroundBrush)); } } } - private SolidColorBrush groupBackgroundBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")); + private SolidColorBrush groupNodeBackgroundBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")); /// @@ -252,7 +251,9 @@ public ProfiledNodeViewModel(string name, SolidColorBrush backgroundBrush) NodeModel = null; this.Name = name; State = ProfiledNodeState.NotExecuted; - GroupBackgroundBrush = backgroundBrush; + GroupNodeBackgroundBrush = backgroundBrush; + State = state; + IsGroup = true; } } } diff --git a/TuneUp/TuneUpWindow.xaml b/TuneUp/TuneUpWindow.xaml index f093fb2..d6e74b5 100644 --- a/TuneUp/TuneUpWindow.xaml +++ b/TuneUp/TuneUpWindow.xaml @@ -18,9 +18,10 @@ - + + - + @@ -100,7 +101,7 @@ - - - - + - diff --git a/TuneUp/TuneUpWindow.xaml.cs b/TuneUp/TuneUpWindow.xaml.cs index 5f9a8a6..ec7587b 100644 --- a/TuneUp/TuneUpWindow.xaml.cs +++ b/TuneUp/TuneUpWindow.xaml.cs @@ -145,47 +145,9 @@ private void NodeAnalysisTable_Sorting(object sender, DataGridSortingEventArgs e : ListSortDirection.Ascending; // Apply custom sorting to ensure total times are at the bottom - viewModel.ApplySorting(); + viewModel.ApplyCustomSorting(); e.Handled = true; } - - //var dataView = CollectionViewSource.GetDefaultView(NodeAnalysisTable.ItemsSource); - //ListSortDirection direction; - - //// Determine the new sort direction - //if (e.Column.SortDirection == null || e.Column.SortDirection == ListSortDirection.Descending) - //{ - // direction = ListSortDirection.Ascending; - //} - //else - //{ - // direction = ListSortDirection.Descending; - //} - - //// Clear previous sort descriptions - //dataView.SortDescriptions.Clear(); - - //// Apply new sort description based on the column being sorted - //if (e.Column.Header.ToString() == "Execution Time (ms)") - //{ - // dataView.SortDescriptions.Add(new SortDescription("ExecutionMilliseconds", direction)); - //} - //else if (e.Column.Header.ToString() == "Name") - //{ - // dataView.SortDescriptions.Add(new SortDescription("Name", direction)); - //} - //else if (e.Column.Header.ToString() == "#") - //{ - // dataView.SortDescriptions.Add(new SortDescription("ExecutionOrderNumber", direction)); - //} - - //// Refresh the view to apply the sort - //dataView.Refresh(); - //e.Column.SortDirection = direction; - - //// Mark the event as handled - //e.Handled = true; - } private void ExportTimes_Click(object sender, RoutedEventArgs e) @@ -198,32 +160,41 @@ private void ExportTimes_Click(object sender, RoutedEventArgs e) // TODO: Rename Converters! - public class ParentToMarginConverter : IValueConverter + + public class ParentToMarginMultiConverter : IMultiValueConverter { private static readonly Guid DefaultGuid = Guid.Empty; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (value is int intValue && intValue != 0) + if (values.Length == 2 && + values[0] is bool isGroup && + values[1] is Guid groupGuid) { + if (isGroup || groupGuid == DefaultGuid) + { + return new System.Windows.Thickness(0, 0, 0, 0); + } return new System.Windows.Thickness(30, 0, 0, 0); } - return new System.Windows.Thickness(0, 0, 0, 0); + return new System.Windows.Thickness(30, 0, 0, 0); } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } + public class IsGroupToBrushConverter : IValueConverter { - private static readonly Guid DefaultGuid = Guid.Empty; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - bool boolValue = (bool)value; - return boolValue ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")) : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AAAAAA")); + if (value is bool isGroup) + { + return isGroup ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")) : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AAAAAA")); + } + return new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) @@ -232,20 +203,26 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu } } - public class GuidToVisibilityConverter : IValueConverter + public class ParentToVisibilityMultiConverter : IMultiValueConverter { private static readonly Guid DefaultGuid = Guid.Empty; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - if (value is Guid guidValue && guidValue != DefaultGuid) + if (values.Length == 2 && + values[0] is bool isGroup && + values[1] is Guid groupGuid) { + if (isGroup || groupGuid == DefaultGuid) + { + return Visibility.Visible; + } return Visibility.Collapsed; } - return Visibility.Visible; + return Visibility.Collapsed; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } diff --git a/TuneUp/TuneUpWindowViewModel.cs b/TuneUp/TuneUpWindowViewModel.cs index 645289d..168fe33 100644 --- a/TuneUp/TuneUpWindowViewModel.cs +++ b/TuneUp/TuneUpWindowViewModel.cs @@ -3,7 +3,6 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using System.DirectoryServices; using System.IO; using System.Linq; using System.Threading; @@ -11,11 +10,11 @@ using System.Windows.Media; using Dynamo.Core; using Dynamo.Engine.Profiling; +using Dynamo.Graph.Annotations; using Dynamo.Graph.Nodes; using Dynamo.Graph.Workspaces; using Dynamo.ViewModels; using Dynamo.Wpf.Extensions; -using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client; using Microsoft.Win32; namespace TuneUp @@ -63,7 +62,7 @@ public class TuneUpWindowViewModel : NotificationObject, IDisposable private bool isTuneUpChecked = false; private ListSortDirection sortDirection; private string sortingOrder = "number"; - private Dictionary> groupedNodesDictionary = new Dictionary>(); + private Dictionary> groupDictionary = new Dictionary>(); /// /// Name of the row to display current execution time @@ -255,7 +254,7 @@ internal void ResetProfiledNodes() // Assign the new collection ProfiledNodes = newProfiledNodes; nodeDictionary = newNodeDictionary; - groupedNodesDictionary = newGroupedNodesDictionary; + groupDictionary = newGroupedNodesDictionary; // List to hold grouped nodes var nodesInGroups = new HashSet(); @@ -263,21 +262,21 @@ internal void ResetProfiledNodes() // Process groups and their nodes foreach (var group in CurrentWorkspace.Annotations) { - var groupProfiledNode = new ProfiledNodeViewModel( + // Create profiledNode for each annotationModel + var profiledGroup = new ProfiledNodeViewModel( $"{ProfiledNodeViewModel.GroupNodePrefix}{group.AnnotationText}", new SolidColorBrush((Color)ColorConverter.ConvertFromString(group.Background))) { - IsGroup = true, - //GroupGUID = group.GUID - GroupName = group.AnnotationText + GroupName = group.AnnotationText, + GroupGUID = group.GUID }; - nodeDictionary[group.GUID] = groupProfiledNode; - ProfiledNodes.Add(groupProfiledNode); - - var groupNodes = new List(); - groupedNodesDictionary[group.GUID] = groupNodes; + // Record the profiledGroupNode in nodeDictionary + nodeDictionary[group.GUID] = profiledGroup; + ProfiledNodes.Add(profiledGroup); + groupDictionary[group.GUID] = new List(); + // Create profiledNode for each node in the annotationModel foreach (var node in group.Nodes) { if (node is NodeModel nodeModel && nodesInGroups.Add(nodeModel)) @@ -288,14 +287,14 @@ internal void ResetProfiledNodes() GroupName = group.AnnotationText }; + // Record the profiledNode in nodeDictionary nodeDictionary[node.GUID] = profiledNode; ProfiledNodes.Add(profiledNode); - groupNodes.Add(profiledNode); + groupDictionary[group.GUID].Add(profiledNode); } } } - - // Process standalone nodes + // Create profiledNode for each of the standalone nodes foreach (var node in CurrentWorkspace.Nodes) { if (!nodesInGroups.Contains(node)) @@ -303,15 +302,16 @@ internal void ResetProfiledNodes() var profiledNode = new ProfiledNodeViewModel(node); nodeDictionary[node.GUID] = profiledNode; ProfiledNodes.Add(profiledNode); + + profiledNode.GroupName = node.Name; } } ProfiledNodesCollection = new CollectionViewSource(); ProfiledNodesCollection.Source = ProfiledNodes; - // Sort the data by execution state + // Sort the data by execution state and then groupName to ensure nodes fall under groups ProfiledNodesCollection.GroupDescriptions.Add(new PropertyGroupDescription(nameof(ProfiledNodeViewModel.StateDescription))); - ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.GroupGUID), ListSortDirection.Ascending)); - ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.State), ListSortDirection.Ascending)); + ApplyDefaultSorting(); ProfiledNodesCollection.View?.Refresh(); RaisePropertyChanged(nameof(ProfiledNodesCollection)); @@ -405,7 +405,7 @@ private void CurrentWorkspaceModel_EvaluationCompleted(object sender, Dynamo.Mod ProfiledNodesCollection.Dispatcher.InvokeAsync(() => { - ApplySorting(); + ApplyCustomSorting(); ProfiledNodesCollection.View.Refresh(); }); } @@ -433,14 +433,6 @@ private void UpdateExecutionTime() RaisePropertyChanged(nameof(TotalGraphExecutiontime)); } - - - - - - - - /// /// Calculates and assigns execution order numbers for profiled nodes. /// Aggregates execution times and updates states for nodes within groups. @@ -448,110 +440,116 @@ private void UpdateExecutionTime() /// private void CalculateGroupNodes() { - int mainExecutionCounter = 1; + int groupExecutionCounter = 1; + string updatedGroupName = string.Empty; var processedNodes = new HashSet(); var sortedProfiledNodes = ProfiledNodes.OrderBy(node => node.ExecutionOrderNumber).ToList(); - foreach (var node in sortedProfiledNodes) + foreach (var profiledNode in sortedProfiledNodes) { - // if the node is from a group and not yet processed - if (!node.IsGroup && node.GroupGUID != Guid.Empty && !processedNodes.Contains(node)) + // If the node is from a group and not yet processed + if (!profiledNode.IsGroup && profiledNode.GroupGUID != Guid.Empty && !processedNodes.Contains(profiledNode)) { - if (nodeDictionary.TryGetValue(node.GroupGUID, out var groupNode)) + if (nodeDictionary.TryGetValue(profiledNode.GroupGUID, out var profiledGroup)) { - if (groupedNodesDictionary.TryGetValue(node.GroupGUID, out var nodesInGroup)) + if (groupDictionary.TryGetValue(profiledNode.GroupGUID, out var nodesInGroup)) { - groupNode.GroupExecutionTime = TimeSpan.Zero; // reset the Execution Time - int groupExecutionCounter = 1; + profiledGroup.GroupExecutionTime = TimeSpan.Zero; // reset the Execution Time + + // Check if the group has been renamed + var groupModel = CurrentWorkspace.Annotations.FirstOrDefault(g => g.GUID == profiledGroup.GroupGUID); + if (groupModel != null) + { + if (profiledGroup.GroupName != groupModel.AnnotationText) + { + updatedGroupName = groupModel.AnnotationText; + profiledGroup.GroupName = updatedGroupName; + profiledGroup.Name = ProfiledNodeViewModel.GroupNodePrefix + updatedGroupName; + } + } - foreach (var groupedNode in nodesInGroup) + // Iterate through the nodes in the group + foreach (var node in nodesInGroup) { - if (processedNodes.Add(groupedNode)) // Adds to HashSet and checks if it was added + if (processedNodes.Add(node)) // Adds to HashSet and checks if it was added { - groupNode.GroupExecutionTime += groupedNode.ExecutionTime; // Aggregate group execution time + // Update group state, groupExecutionOrder and executionTime of the group + profiledGroup.GroupExecutionTime += node.ExecutionTime; + node.GroupExecutionOrderNumber = groupExecutionCounter; - // Update group state if any node in the group was executed on current run - if (groupedNode.State == ProfiledNodeState.ExecutedOnCurrentRun) + if (node.State == ProfiledNodeState.ExecutedOnCurrentRun) { - groupNode.State = ProfiledNodeState.ExecutedOnCurrentRun; + profiledGroup.State = ProfiledNodeState.ExecutedOnCurrentRun; + } + if (updatedGroupName != string.Empty) + { + profiledGroup.GroupName = updatedGroupName; } - - groupNode.ExecutionOrderNumber = mainExecutionCounter; - groupedNode.ExecutionOrderNumber = mainExecutionCounter; - groupedNode.ExecutionGroupOrderNumber = groupExecutionCounter; - groupExecutionCounter++; } } - groupNode.ExecutionTime = groupNode.GroupExecutionTime; - mainExecutionCounter++; + profiledGroup.ExecutionOrderNumber = groupExecutionCounter; // Displayed by dataGrid + profiledGroup.GroupExecutionOrderNumber = groupExecutionCounter; + groupExecutionCounter++; + profiledGroup.ExecutionTime = profiledGroup.GroupExecutionTime; // Displayed by dataGrid + + // Update the total groupExecutionTime for the purposes of sorting + foreach (var node in nodesInGroup) + { + node.GroupExecutionTime = profiledGroup.GroupExecutionTime; + } } } } - // if the node is not from a group - else if (!node.IsGroup && processedNodes.Add(node) && !node.Name.Contains(ProfiledNodeViewModel.ExecutionTimelString)) + // If the node is not in a group + else if (!profiledNode.IsGroup && processedNodes.Add(profiledNode) && !profiledNode.Name.Contains(ProfiledNodeViewModel.ExecutionTimelString)) { - node.ExecutionOrderNumber = mainExecutionCounter++; - node.GroupExecutionTime = node.ExecutionTime; + profiledNode.ExecutionOrderNumber = groupExecutionCounter; + profiledNode.GroupExecutionOrderNumber = groupExecutionCounter++; + profiledNode.GroupExecutionTime = profiledNode.ExecutionTime; } } - - //// iterate again to align GroupExecutionTimes - //foreach (var node in processedNodes) - //{ - // var relatedGroup = nodeDictionary[node.GroupGUID]; - // if (relatedGroup != null) - // { node.GroupExecutionTime = relatedGroup.GroupExecutionTime; } - //} - RaisePropertyChanged(nameof(ProfiledNodes)); } - - - - - - - - - /// /// Applies the sorting logic to the ProfiledNodesCollection. /// - public void ApplySorting() + public void ApplyCustomSorting() { ProfiledNodesCollection.SortDescriptions.Clear(); - // Sort nodes into execution group ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.State), ListSortDirection.Ascending)); - // Sort nodes into execution order and make sure Total execution time is always bottom - //var sortDescription = sortingOrder switch - //{ - // "time" => new SortDescription(nameof(ProfiledNodeViewModel.ExecutionTime), sortDirection), - // "name" => new SortDescription(nameof(ProfiledNodeViewModel.Name), sortDirection), - // _ => new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), sortDirection), - //}; - //ProfiledNodesCollection.SortDescriptions.Add(sortDescription); - switch (sortingOrder) { case "time": ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.GroupExecutionTime), sortDirection)); ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.IsGroup), ListSortDirection.Descending)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionTime), sortDirection)); break; case "name": ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.GroupName), sortDirection)); ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.IsGroup), ListSortDirection.Descending)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.Name), sortDirection)); break; case "number": - ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), sortDirection)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.GroupExecutionOrderNumber), sortDirection)); ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.IsGroup), ListSortDirection.Descending)); - ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionGroupOrderNumber), sortDirection)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.ExecutionOrderNumber), sortDirection)); break; } } + private void ApplyDefaultSorting() + { + ProfiledNodesCollection.SortDescriptions.Clear(); + // Sort nodes into execution group + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.State), ListSortDirection.Ascending)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.GroupName), ListSortDirection.Ascending)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.IsGroup), ListSortDirection.Descending)); + ProfiledNodesCollection.SortDescriptions.Add(new SortDescription(nameof(ProfiledNodeViewModel.Name), ListSortDirection.Ascending)); + } + internal void OnNodeExecutionBegin(NodeModel nm) { var profiledNode = nodeDictionary[nm.GUID]; @@ -604,6 +602,55 @@ private void CurrentWorkspaceModel_NodeRemoved(NodeModel node) RaisePropertyChanged(nameof(ProfiledNodesCollection)); } + private void CurrentWorkspaceModel_GroupAdded(AnnotationModel group) + { + var profiledGroup = new ProfiledNodeViewModel( + $"{ProfiledNodeViewModel.GroupNodePrefix}{group.AnnotationText}", + new SolidColorBrush((Color)ColorConverter.ConvertFromString(group.Background))) + { + GroupName = group.AnnotationText, + GroupGUID = group.GUID + }; + nodeDictionary[group.GUID] = profiledGroup; + ProfiledNodes.Add(profiledGroup); + groupDictionary[group.GUID] = new List(); + + foreach (var node in group.Nodes) + { + var profiledNode = nodeDictionary[node.GUID] as ProfiledNodeViewModel; + profiledNode.GroupGUID = group.GUID; + profiledNode.GroupName = group.AnnotationText; + profiledNode.GroupExecutionOrderNumber = profiledGroup.GroupExecutionOrderNumber; + + groupDictionary[group.GUID].Add(profiledNode); + } + // Executes for each group when a graph with groups is open while TuneUp is enabled + // Ensures that group nodes are sorted properly and do not appear at the bottom of the DataGrid + ApplyDefaultSorting(); + RaisePropertyChanged(nameof(ProfiledNodesCollection)); + RaisePropertyChanged(nameof(ProfiledNodes)); + } + + private void CurrentWorkspaceModel_GroupRemoved(AnnotationModel group) + { + var groupGUID = group.GUID; + var profiledGroup = nodeDictionary[groupGUID] as ProfiledNodeViewModel; + nodeDictionary.Remove(groupGUID); + ProfiledNodes.Remove(profiledGroup); + + var groupedNodes = groupDictionary[groupGUID]; + foreach (var profiledNode in groupedNodes) + { + profiledNode.GroupGUID = Guid.Empty; + profiledNode.GroupName = string.Empty; + profiledNode.GroupExecutionOrderNumber = 0; + profiledNode.GroupExecutionTime = TimeSpan.Zero; + } + groupDictionary.Remove(groupGUID); + RaisePropertyChanged(nameof(ProfiledNodesCollection)); + RaisePropertyChanged(nameof(ProfiledNodes)); + } + private void OnCurrentWorkspaceChanged(IWorkspaceModel workspace) { // Profiling needs to be enabled per workspace so mark it false after switching @@ -639,6 +686,9 @@ private void ManageWorkspaceEvents(HomeWorkspaceModel workspace, bool subscribe) workspace.NodeRemoved += CurrentWorkspaceModel_NodeRemoved; workspace.EvaluationStarted += CurrentWorkspaceModel_EvaluationStarted; workspace.EvaluationCompleted += CurrentWorkspaceModel_EvaluationCompleted; + workspace.AnnotationAdded += CurrentWorkspaceModel_GroupAdded; + workspace.AnnotationRemoved += CurrentWorkspaceModel_GroupRemoved; + foreach (var node in workspace.Nodes) { @@ -654,6 +704,8 @@ private void ManageWorkspaceEvents(HomeWorkspaceModel workspace, bool subscribe) workspace.NodeRemoved -= CurrentWorkspaceModel_NodeRemoved; workspace.EvaluationStarted -= CurrentWorkspaceModel_EvaluationStarted; workspace.EvaluationCompleted -= CurrentWorkspaceModel_EvaluationCompleted; + workspace.AnnotationAdded -= CurrentWorkspaceModel_GroupAdded; + workspace.AnnotationRemoved -= CurrentWorkspaceModel_GroupRemoved; foreach (var node in workspace.Nodes) { From 2c7e0ae8e67064e7f7ee4231f93109790bb8018f Mon Sep 17 00:00:00 2001 From: Ivo Petrov Date: Mon, 22 Jul 2024 14:37:32 +0100 Subject: [PATCH 5/9] works --- TuneUp/ProfiledNodeViewModel.cs | 105 ++++++++++--------- TuneUp/TuneUpWindow.xaml | 13 +-- TuneUp/TuneUpWindow.xaml.cs | 28 ++--- TuneUp/TuneUpWindowViewModel.cs | 179 ++++++++++++++++---------------- 4 files changed, 157 insertions(+), 168 deletions(-) diff --git a/TuneUp/ProfiledNodeViewModel.cs b/TuneUp/ProfiledNodeViewModel.cs index 35a1868..87aca8b 100644 --- a/TuneUp/ProfiledNodeViewModel.cs +++ b/TuneUp/ProfiledNodeViewModel.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Windows.Media; using Dynamo.Core; +using Dynamo.Graph.Annotations; using Dynamo.Graph.Nodes; namespace TuneUp @@ -43,10 +44,7 @@ public string Name /// public int? ExecutionOrderNumber { - get - { - return executionOrderNumber; - } + get => executionOrderNumber; set { executionOrderNumber = value; @@ -55,12 +53,13 @@ public int? ExecutionOrderNumber } private int? executionOrderNumber; + /// + /// The order number of this group in the most recent graph run. + /// This number is assigned to each node within the group. + /// public int? GroupExecutionOrderNumber { - get - { - return groupExecutionOrderNumber; - } + get => groupExecutionOrderNumber; set { groupExecutionOrderNumber = value; @@ -69,16 +68,12 @@ public int? GroupExecutionOrderNumber } private int? groupExecutionOrderNumber; - /// /// The most recent execution time of this node /// public TimeSpan ExecutionTime { - get - { - return executionTime; - } + get => executionTime; set { executionTime = value; @@ -88,12 +83,12 @@ public TimeSpan ExecutionTime } private TimeSpan executionTime; + /// + /// The total execution time of all node in the group. + /// public TimeSpan GroupExecutionTime { - get - { - return groupExecutionTime; - } + get => groupExecutionTime; set { groupExecutionTime = value; @@ -107,10 +102,7 @@ public TimeSpan GroupExecutionTime /// public int ExecutionMilliseconds { - get - { - return (int)Math.Round(ExecutionTime.TotalMilliseconds); - } + get => (int)Math.Round(ExecutionTime.TotalMilliseconds); } /// @@ -118,10 +110,7 @@ public int ExecutionMilliseconds /// public bool WasExecutedOnLastRun { - get - { - return wasExecutedOnLastRun; - } + get => wasExecutedOnLastRun; set { wasExecutedOnLastRun = value; @@ -135,10 +124,7 @@ public bool WasExecutedOnLastRun /// public ProfiledNodeState State { - get - { - return state; - } + get => state; set { state = value; @@ -147,6 +133,9 @@ public ProfiledNodeState State } private ProfiledNodeState state; + /// + /// The GUID of the group to which this node belongs + /// public Guid GroupGUID { get => groupGIUD; @@ -158,42 +147,52 @@ public Guid GroupGUID } private Guid groupGIUD; - public bool IsGroup + /// + /// The name of the group to which this node belongs + /// This property is also applied to individual nodes and is used when sorting by name + /// + public string GroupName { - get => isGroup; + get => groupName; set { - isGroup = value; - RaisePropertyChanged(nameof(IsGroup)); + groupName = value; + RaisePropertyChanged(nameof(GroupName)); } } - private bool isGroup; + private string groupName; - public string GroupName + /// + /// Indicates if this node is a group + /// + public bool IsGroup { - get => groupName; + get => isGroup; set { - groupName = value; - RaisePropertyChanged(nameof(GroupName)); + isGroup = value; + RaisePropertyChanged(nameof(IsGroup)); } } - private string groupName; + private bool isGroup; - public SolidColorBrush GroupNodeBackgroundBrush + /// + /// The background brush for this node + /// If this node represents a group, it inherits the background color from the associated AnnotationModel + /// + public SolidColorBrush BackgroundBrush { - get => groupNodeBackgroundBrush; + get => backgroundBrush; set { if (value != null) { - groupNodeBackgroundBrush = value; - RaisePropertyChanged(nameof(GroupNodeBackgroundBrush)); + backgroundBrush = value; + RaisePropertyChanged(nameof(BackgroundBrush)); } } } - private SolidColorBrush groupNodeBackgroundBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")); - + private SolidColorBrush backgroundBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#333333")); /// /// Return the display name of state enum. @@ -245,15 +244,19 @@ public ProfiledNodeViewModel(string name, TimeSpan exTimeSum, ProfiledNodeState State = state; } - // Constructor for ProfiledAnnotationNodes - public ProfiledNodeViewModel(string name, SolidColorBrush backgroundBrush) + /// + /// An alternative constructor to represent an annotation model as a group node. + /// + /// the annotation model + public ProfiledNodeViewModel(AnnotationModel group) { NodeModel = null; - this.Name = name; - State = ProfiledNodeState.NotExecuted; - GroupNodeBackgroundBrush = backgroundBrush; - State = state; + Name = $"{GroupNodePrefix}{group.AnnotationText}"; + GroupName = group.AnnotationText; + GroupGUID = group.GUID; + BackgroundBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(group.Background)); IsGroup = true; + State = ProfiledNodeState.NotExecuted; } } } diff --git a/TuneUp/TuneUpWindow.xaml b/TuneUp/TuneUpWindow.xaml index d6e74b5..fa09f6a 100644 --- a/TuneUp/TuneUpWindow.xaml +++ b/TuneUp/TuneUpWindow.xaml @@ -18,10 +18,9 @@ - - + + - @@ -101,7 +100,7 @@ @@ -135,12 +135,19 @@ - + - + - + - + - + - + - + @@ -161,7 +158,7 @@ - + + IsEnabled="{Binding Path=IsRecomputeEnabled}" + Click="ExportTimes_Click"> Run All + x:Name="NodeAnalysisTable" + Grid.Row="1" + ItemsSource="{Binding Path=ProfiledNodesCollection.View}" + Style="{StaticResource DataGridStyle1}" + AutoGenerateColumns="False" + CanUserAddRows="False" + Background="#353535" + FontSize="11" + VerticalAlignment="Center" + SelectionUnit="FullRow" + SelectionMode="Single" + ScrollViewer.CanContentScroll="False" + ScrollViewer.HorizontalScrollBarVisibility="Auto" + ScrollViewer.VerticalScrollBarVisibility="Auto" + CanUserResizeColumns="True" + CanUserSortColumns="True" + HeadersVisibility="Column" + SelectionChanged="NodeAnalysisTable_SelectionChanged" + PreviewMouseDown="NodeAnalysisTable_PreviewMouseDown" + MouseLeave="NodeAnalysisTable_MouseLeave" + Sorting="NodeAnalysisTable_Sorting"> + Margin="3" + FontFamily="{StaticResource ArtifaktElementRegular}" + FontSize="12" + Foreground="{StaticResource NodeNameForeground}" + Text="{Binding Name}" /> @@ -231,10 +228,10 @@ + Header="#" + Binding="{Binding Path=ExecutionOrderNumber}" + IsReadOnly="True" + Width="Auto">