diff --git a/TuneUp/TuneUpWindowViewModel.cs b/TuneUp/TuneUpWindowViewModel.cs index b590fe9..83d30e1 100644 --- a/TuneUp/TuneUpWindowViewModel.cs +++ b/TuneUp/TuneUpWindowViewModel.cs @@ -433,11 +433,6 @@ internal void DisableProfiling() private void CurrentWorkspaceModel_EvaluationStarted(object sender, EventArgs e) { - // Store nodes in temporary HashSets to batch the updates and avoid immediate UI refreshes. - tempProfiledNodesLatestRun = ProfiledNodesLatestRun.ToHashSet(); - tempProfiledNodesPreviousRun = ProfiledNodesPreviousRun.ToHashSet(); - tempProfiledNodesNotExecuted = ProfiledNodesNotExecuted.ToHashSet(); - IsRecomputeEnabled = false; foreach (var node in nodeDictionary.Values) { @@ -452,7 +447,7 @@ private void CurrentWorkspaceModel_EvaluationStarted(object sender, EventArgs e) // Move to CollectionPreviousRun if (node.State == ProfiledNodeState.ExecutedOnPreviousRun) { - MoveNodeToTempCollection(node, tempProfiledNodesPreviousRun); + MoveNodeToCollection(node, ProfiledNodesPreviousRun); } } executedNodesNum = 1; @@ -464,27 +459,13 @@ private void CurrentWorkspaceModel_EvaluationCompleted(object sender, Dynamo.Mod Task.Run(() => { IsRecomputeEnabled = true; + CalculateGroupNodes(); + UpdateExecutionTime(); + UpdateTableVisibility(); uiContext.Post(_ => { - // Swap references instead of clearing and re-adding nodes - ProfiledNodesLatestRun.Clear(); - foreach (var node in tempProfiledNodesLatestRun) - { - ProfiledNodesLatestRun.Add(node); - } - ProfiledNodesPreviousRun.Clear(); - foreach (var node in tempProfiledNodesPreviousRun) - { - ProfiledNodesPreviousRun.Add(node); - } - ProfiledNodesNotExecuted.Clear(); - foreach (var node in tempProfiledNodesNotExecuted) - { - ProfiledNodesNotExecuted.Add(node); - } - RaisePropertyChanged(nameof(ProfiledNodesCollectionLatestRun)); RaisePropertyChanged(nameof(ProfiledNodesCollectionPreviousRun)); RaisePropertyChanged(nameof(ProfiledNodesCollectionNotExecuted)); @@ -495,15 +476,6 @@ private void CurrentWorkspaceModel_EvaluationCompleted(object sender, Dynamo.Mod ProfiledNodesCollectionLatestRun.View?.Refresh(); ProfiledNodesCollectionPreviousRun.View?.Refresh(); ProfiledNodesCollectionNotExecuted.View?.Refresh(); - - // Update execution time and table visibility - UpdateExecutionTime(); - UpdateTableVisibility(); - - // Clear temporary collections - tempProfiledNodesLatestRun = new HashSet(); - tempProfiledNodesPreviousRun = new HashSet(); - tempProfiledNodesNotExecuted = new HashSet(); }, null); }); } @@ -514,19 +486,23 @@ private void CurrentWorkspaceModel_EvaluationCompleted(object sender, Dynamo.Mod /// private void UpdateExecutionTime() { - // After each evaluation, manually update execution time column(s) - // Calculate total execution times using rounded node execution times, not exact values. - int totalLatestRun = ProfiledNodesLatestRun - .Where(n => n.WasExecutedOnLastRun && !n.IsGroup && !n.IsGroupExecutionTime) - .Sum(r => r?.ExecutionMilliseconds ?? 0); - int previousLatestRun = ProfiledNodesPreviousRun - .Where(n => !n.WasExecutedOnLastRun && !n.IsGroup && !n.IsGroupExecutionTime) - .Sum(r => r?.ExecutionMilliseconds ?? 0); - - // Update latest and previous run times - latestGraphExecutionTime = totalLatestRun.ToString(); - previousGraphExecutionTime = previousLatestRun.ToString(); - totalGraphExecutionTime = (totalLatestRun + previousLatestRun).ToString(); + // Reset execution time + uiContext.Send( + x => + { // After each evaluation, manually update execution time column(s) + // Calculate total execution times using rounded node execution times, not exact values. + int totalLatestRun = ProfiledNodesLatestRun + .Where(n => n.WasExecutedOnLastRun && !n.IsGroup && !n.IsGroupExecutionTime) + .Sum(r => r?.ExecutionMilliseconds ?? 0); + int previousLatestRun = ProfiledNodesPreviousRun + .Where(n => !n.WasExecutedOnLastRun && !n.IsGroup && !n.IsGroupExecutionTime) + .Sum(r => r?.ExecutionMilliseconds ?? 0); + + // Update latest and previous run times + latestGraphExecutionTime = totalLatestRun.ToString(); + previousGraphExecutionTime = previousLatestRun.ToString(); + totalGraphExecutionTime = (totalLatestRun + previousLatestRun).ToString(); + }, null); RaisePropertyChanged(nameof(TotalGraphExecutionTime)); RaisePropertyChanged(nameof(LatestGraphExecutionTime)); @@ -540,50 +516,59 @@ private void UpdateExecutionTime() /// private void CalculateGroupNodes() { - // Clean the collections from all group and time nodesB - foreach (var node in groupDictionary.Values) + Task.Run(() => { - RemoveNodeFromState(node, node.State, GetTempCollectionFromState); - - if (groupModelDictionary.TryGetValue(node.GroupGUID, out var groupNodes)) + // Apply all removals and additions on the UI thread + uiContext.Post(_ => { - groupNodes.Remove(node); - } - } - groupDictionary.Clear(); + // Clean the collections from all group and time nodes + foreach (var node in groupDictionary.Values) + { + RemoveNodeFromStateCollection(node, node.State); - // Create group and time nodes for latest and previous runs - CreateGroupNodesForCollection(tempProfiledNodesLatestRun); - CreateGroupNodesForCollection(tempProfiledNodesPreviousRun); + if (groupModelDictionary.TryGetValue(node.GroupGUID, out var groupNodes)) + { + groupNodes.Remove(node); + } + } + groupDictionary.Clear(); - // Create group nodes for not executed - var processedNodesNotExecuted = new HashSet(); + // Create group and time nodes for latest and previous runs + CreateGroupNodesForCollection(ProfiledNodesLatestRun); + CreateGroupNodesForCollection(ProfiledNodesPreviousRun); - // Create a copy of ProfiledNodesNotExecuted to iterate over - var profiledNodesCopy = tempProfiledNodesNotExecuted.ToList(); + // Create group nodes for not executed + var processedNodesNotExecuted = new HashSet(); - foreach (var pNode in profiledNodesCopy) - { - if (pNode.GroupGUID != Guid.Empty && !processedNodesNotExecuted.Contains(pNode)) - { - // get the other nodes from this group - var nodesInGroup = tempProfiledNodesNotExecuted - .Where(n => n.GroupGUID == pNode.GroupGUID) - .ToList(); + // Create a copy of ProfiledNodesNotExecuted to iterate over + var profiledNodesCopy = ProfiledNodesNotExecuted.ToList(); - foreach (var node in nodesInGroup) + foreach (var pNode in profiledNodesCopy) { - processedNodesNotExecuted.Add(node); + if (pNode.GroupGUID != Guid.Empty && !processedNodesNotExecuted.Contains(pNode)) + { + // get the other nodes from this group + var nodesInGroup = ProfiledNodesNotExecuted + .Where(n => n.GroupGUID == pNode.GroupGUID) + .ToList(); + + foreach (var node in nodesInGroup) + { + processedNodesNotExecuted.Add(node); + } + + // create new group node + var pGroup = CreateAndRegisterGroupNode(pNode); + uiContext.Send(_ => ProfiledNodesNotExecuted.Add(pGroup), null); + } } - // create new group node - var pGroup = CreateAndRegisterGroupNode(pNode); - tempProfiledNodesNotExecuted.Add(pGroup); - } - } + RefreshGroupNodeUI(); + }, null); + }); } - private void CreateGroupNodesForCollection(HashSet collection) + private void CreateGroupNodesForCollection(ObservableCollection collection) { int executionCounter = 1; var processedNodes = new HashSet(); @@ -660,7 +645,7 @@ internal void OnNodeExecutionEnd(NodeModel nm) { profiledNode.ExecutionOrderNumber = executedNodesNum++; // Move to collection LatestRun - MoveNodeToTempCollection(profiledNode, tempProfiledNodesLatestRun); + MoveNodeToCollection(profiledNode, ProfiledNodesLatestRun); } } @@ -890,7 +875,7 @@ private void CurrentWorkspaceModel_NodeRemoved(NodeModel node) node.NodeExecutionEnd -= OnNodeExecutionEnd; node.PropertyChanged -= OnNodePropertyChanged; - RemoveNodeFromState(profiledNode, profiledNode.State, GetObservableCollectionFromState); + RemoveNodeFromStateCollection(profiledNode, profiledNode.State); //Recalculate the execution times UpdateExecutionTime(); @@ -1000,7 +985,7 @@ private void CurrentWorkspaceModel_GroupRemoved(AnnotationModel group) // Remove the group and time nodes foreach (var node in gNodes) { - RemoveNodeFromState(node, node.State, GetObservableCollectionFromState); + RemoveNodeFromStateCollection(node, node.State); groupDictionary.Remove(node.NodeGUID); } @@ -1172,11 +1157,6 @@ private void InitializeCollectionsAndDictionaries() ProfiledNodesPreviousRun?.Clear(); ProfiledNodesNotExecuted?.Clear(); - // Clear temporary collections - tempProfiledNodesLatestRun = new HashSet(); - tempProfiledNodesPreviousRun = new HashSet(); - tempProfiledNodesNotExecuted = new HashSet(); - // Reset execution time stats LatestGraphExecutionTime = PreviousGraphExecutionTime = TotalGraphExecutionTime = defaultExecutionTime; @@ -1185,6 +1165,13 @@ private void InitializeCollectionsAndDictionaries() ProfiledNodesPreviousRun = ProfiledNodesPreviousRun ?? new ObservableCollection(); ProfiledNodesNotExecuted = ProfiledNodesNotExecuted ?? new ObservableCollection(); + collectionMapping = new Dictionary, CollectionViewSource> + { + { ProfiledNodesLatestRun, ProfiledNodesCollectionLatestRun }, + { ProfiledNodesPreviousRun, ProfiledNodesCollectionPreviousRun }, + { ProfiledNodesNotExecuted, ProfiledNodesCollectionNotExecuted } + }; + nodeDictionary = new Dictionary(); groupDictionary = new Dictionary(); groupModelDictionary = new Dictionary>(); @@ -1285,16 +1272,6 @@ private ObservableCollection GetObservableCollectionFromS else return ProfiledNodesNotExecuted; } - /// - /// Returns the appropriate ObservableCollection based on the node's profiling state. - /// - private HashSet GetTempCollectionFromState(ProfiledNodeState state) - { - if (state == ProfiledNodeState.ExecutedOnCurrentRun) return tempProfiledNodesLatestRun; - else if (state == ProfiledNodeState.ExecutedOnPreviousRun) return tempProfiledNodesPreviousRun; - else return tempProfiledNodesNotExecuted; - } - /// /// Updates the group visibility, refreshes the collection view, and applies appropriate sorting for the given nodes. /// @@ -1429,26 +1406,33 @@ private void SortCollectionViewForProfiledNodesCollection(ObservableCollection

- /// Moves a node between HashSets, removing it from all HashSets and adding it to the target HashSet if provided. + /// Moves a node between collections, removing it from all collections and adding it to the target collection if provided. /// - private void MoveNodeToTempCollection(ProfiledNodeViewModel profiledNode, HashSet targetCollection) + private void MoveNodeToCollection(ProfiledNodeViewModel profiledNode, ObservableCollection targetCollection) { - var collections = new[] { tempProfiledNodesLatestRun, tempProfiledNodesPreviousRun, tempProfiledNodesNotExecuted }; - - foreach (var collection in collections) + Task.Run(() => { - collection?.Remove(profiledNode); - } + uiContext.Post(_ => + { + var collections = new[] { ProfiledNodesLatestRun, ProfiledNodesPreviousRun, ProfiledNodesNotExecuted }; + + foreach (var collection in collections) + { + collection?.Remove(profiledNode); + } - targetCollection?.Add(profiledNode); + targetCollection?.Add(profiledNode); + }, null); + }); } ///

/// Removes a node from the appropriate collection based on its state. /// - private void RemoveNodeFromState(ProfiledNodeViewModel pNode, ProfiledNodeState state, Func getCollectionFunc) where T : ICollection + private void RemoveNodeFromStateCollection(ProfiledNodeViewModel pNode, ProfiledNodeState state) { - var collection = getCollectionFunc(state); + var collection = GetObservableCollectionFromState(state); + collection?.Remove(pNode); } @@ -1516,6 +1500,17 @@ private void RefreshUIAfterReset() UpdateTableVisibility(); } + /// + /// Refreshes the UI after group nodes are re-calculated + /// + private void RefreshGroupNodeUI() + { + ApplyCustomSorting(ProfiledNodesCollectionLatestRun); + RaisePropertyChanged(nameof(ProfiledNodesCollectionLatestRun)); + ApplyCustomSorting(ProfiledNodesCollectionPreviousRun); + RaisePropertyChanged(nameof(ProfiledNodesCollectionPreviousRun)); + } + #endregion #region Dispose or setup