diff --git a/src/DynamoCore/Graph/Nodes/NodeModel.cs b/src/DynamoCore/Graph/Nodes/NodeModel.cs index 5791cf91648..b5ac79bd5d4 100644 --- a/src/DynamoCore/Graph/Nodes/NodeModel.cs +++ b/src/DynamoCore/Graph/Nodes/NodeModel.cs @@ -995,6 +995,17 @@ public bool IsFrozen } } + /// + /// A flag indicating whether the node is in transient mode. + /// When a node is in transient mode, the node will not participate in execution, + /// Or saved to the graph. It is only used for previewing the AutoComplete result in the canvas. + /// + internal bool IsTransient + { + get; + set; + } + /// /// The default behavior for ModelBase objects is to not serialize the X and Y /// properties. This overload allows the serialization of the X property diff --git a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs index 10c0b985bbd..029e242172d 100644 --- a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs +++ b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs @@ -862,9 +862,9 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s .Select(outputNode => outputNode.OutputData).ToList(); serializer.Serialize(writer, outputNodeDatas); - // Nodes + // Nodes except for nodes in Transient state writer.WritePropertyName("Nodes"); - serializer.Serialize(writer, ws.Nodes); + serializer.Serialize(writer, ws.Nodes.Where(x => x.IsTransient != true)); // Connectors writer.WritePropertyName("Connectors"); diff --git a/src/DynamoCore/Scheduler/UpdateGraphAsyncTask.cs b/src/DynamoCore/Scheduler/UpdateGraphAsyncTask.cs index 692be1b52e7..2a0a915eafb 100644 --- a/src/DynamoCore/Scheduler/UpdateGraphAsyncTask.cs +++ b/src/DynamoCore/Scheduler/UpdateGraphAsyncTask.cs @@ -255,8 +255,8 @@ protected override TaskMergeInstruction CanMergeWithCore(AsyncTask otherTask) private static IEnumerable ComputeModifiedNodes(WorkspaceModel workspace) { var nodesToUpdate = new List(); - //Get those modified nodes that are not frozen - foreach (var node in workspace.Nodes.Where(n => n.IsModified && !n.IsFrozen)) + //Get those modified nodes that are not frozen or transient states + foreach (var node in workspace.Nodes.Where(n => n.IsModified && !n.IsFrozen && !n.IsTransient)) { GetDownstreamNodes(node, nodesToUpdate); } @@ -273,7 +273,7 @@ private static IEnumerable ComputeModifiedNodes(WorkspaceModel worksp /// private static void GetDownstreamNodes(NodeModel node, ICollection gathered) { - if (gathered.Contains(node) || node.IsFrozen) // Considered this node before, bail.pu + if (gathered.Contains(node) || node.IsFrozen || node.IsTransient) // Considered this node before, bail.pu return; gathered.Add(node); diff --git a/src/DynamoCoreWpf/DynamoCoreWpf.csproj b/src/DynamoCoreWpf/DynamoCoreWpf.csproj index 25a013533bf..5195fb32057 100644 --- a/src/DynamoCoreWpf/DynamoCoreWpf.csproj +++ b/src/DynamoCoreWpf/DynamoCoreWpf.csproj @@ -110,7 +110,6 @@ - @@ -161,6 +160,9 @@ + + + @@ -1041,6 +1043,8 @@ + + diff --git a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt index 7fb2515f640..07a98a3f159 100644 --- a/src/DynamoCoreWpf/PublicAPI.Unshipped.txt +++ b/src/DynamoCoreWpf/PublicAPI.Unshipped.txt @@ -2435,6 +2435,8 @@ Dynamo.ViewModels.NodeViewModel.IsDisplayingLabels.set -> void Dynamo.ViewModels.NodeViewModel.IsExperimental.get -> bool Dynamo.ViewModels.NodeViewModel.IsFrozen.get -> bool Dynamo.ViewModels.NodeViewModel.IsFrozen.set -> void +Dynamo.ViewModels.NodeViewModel.IsTransient.get -> bool +Dynamo.ViewModels.NodeViewModel.IsTransient.set -> void Dynamo.ViewModels.NodeViewModel.IsFrozenExplicitly.get -> bool Dynamo.ViewModels.NodeViewModel.IsInput.get -> bool Dynamo.ViewModels.NodeViewModel.IsInteractionEnabled.get -> bool diff --git a/src/DynamoCoreWpf/UI/Images/NodeStates/transient-64px.png b/src/DynamoCoreWpf/UI/Images/NodeStates/transient-64px.png new file mode 100644 index 00000000000..3e367f6f488 Binary files /dev/null and b/src/DynamoCoreWpf/UI/Images/NodeStates/transient-64px.png differ diff --git a/src/DynamoCoreWpf/UI/Images/NodeStates/transient-light-64px.png b/src/DynamoCoreWpf/UI/Images/NodeStates/transient-light-64px.png new file mode 100644 index 00000000000..3d1535b0d7d Binary files /dev/null and b/src/DynamoCoreWpf/UI/Images/NodeStates/transient-light-64px.png differ diff --git a/src/DynamoCoreWpf/UI/Themes/Modern/DynamoColorsAndBrushes.xaml b/src/DynamoCoreWpf/UI/Themes/Modern/DynamoColorsAndBrushes.xaml index 6e2d8d8472b..6bc264edbb3 100644 --- a/src/DynamoCoreWpf/UI/Themes/Modern/DynamoColorsAndBrushes.xaml +++ b/src/DynamoCoreWpf/UI/Themes/Modern/DynamoColorsAndBrushes.xaml @@ -81,6 +81,7 @@ + diff --git a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs index 3194dace4b5..9d00eab9fe8 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs @@ -225,7 +225,7 @@ internal bool IsDNAClusterPlacementEnabled { get { - return DynamoModel.FeatureFlags?.CheckFeatureFlag("IsDNAClusterPlacementEnabled", false) ?? true; + return DynamoModel.FeatureFlags?.CheckFeatureFlag("IsDNAClusterPlacementEnabled", false) ?? false; } } diff --git a/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs index f2b0ba851a2..9684ec0260f 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/NodeViewModel.cs @@ -584,7 +584,8 @@ public bool NodeHoveringState private static readonly string warningGlyph = "/DynamoCoreWpf;component/UI/Images/NodeStates/alert-64px.png"; private static readonly string errorGlyph = "/DynamoCoreWpf;component/UI/Images/NodeStates/error-64px.png"; private static readonly string infoGlyph = "/DynamoCoreWpf;component/UI/Images/NodeStates/info-64px.png"; - private static readonly string previewGlyph = "/DynamoCoreWpf;component/UI/Images/NodeStates/hidden-64px.png"; + private static readonly string previewGeometryGlyph = "/DynamoCoreWpf;component/UI/Images/NodeStates/hidden-64px.png"; + private static readonly string previewClusterGlyph = "/DynamoCoreWpf;component/UI/Images/NodeStates/transient-light-64px.png"; private static readonly string frozenGlyph = "/DynamoCoreWpf;component/UI/Images/NodeStates/frozen-64px.png"; private static readonly string packageGlyph = "/DynamoCoreWpf;component/UI/Images/NodeStates/package-64px.png"; @@ -622,6 +623,16 @@ public bool IsFrozen } } + /// + /// Return a value indicating whether this node is in transient state. + /// + [JsonIgnore] + public bool IsTransient + { + set { NodeModel.IsTransient = value; } + get { return NodeModel.IsTransient; } + } + /// /// A flag indicating whether the node is set to freeze by the user. /// @@ -1302,8 +1313,9 @@ private void BuildErrorBubble() private static SolidColorBrush infoColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#6AC0E7")); private static SolidColorBrush noPreviewColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#BBBBBB")); private static SolidColorBrush nodeCustomColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#B385F2")); - private static SolidColorBrush nodePreviewColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#BBBBBB")); + private static SolidColorBrush nodePreviewGeometryColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#BBBBBB")); private static SolidColorBrush nodeFrozenOverlayColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#BCD3EE")); + private static SolidColorBrush nodeTransientOverlayColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#D5BCF7")); private static SolidColorBrush nodeInfoColor = (SolidColorBrush)(new BrushConverter().ConvertFrom("#6AC0E7")); /// @@ -1360,10 +1372,10 @@ Pass through all possible states in reverse order if (!this.IsVisible) { - result = nodePreviewColor; + result = nodePreviewGeometryColor; if (result != null) { - ImgGlyphOneSource = previewGlyph; + ImgGlyphOneSource = previewGeometryGlyph; } } @@ -1379,7 +1391,7 @@ Pass through all possible states in reverse order else { ImgGlyphOneSource = packageGlyph; - ImgGlyphTwoSource = previewGlyph; + ImgGlyphTwoSource = previewGeometryGlyph; } } } @@ -1396,10 +1408,28 @@ Pass through all possible states in reverse order else { ImgGlyphOneSource = frozenGlyph; - ImgGlyphTwoSource = previewGlyph; + ImgGlyphTwoSource = previewGeometryGlyph; } } } + + if (this.IsTransient) + { + result = nodeTransientOverlayColor; + if (result != null) + { + if (ImgGlyphOneSource == null) + { + ImgGlyphOneSource = previewClusterGlyph; + } + else + { + ImgGlyphOneSource = previewClusterGlyph; + ImgGlyphTwoSource = previewGeometryGlyph; + } + } + } + if (NodeModel.State == ElementState.Info || NodeModel.State == ElementState.PersistentInfo) { result = nodeInfoColor; diff --git a/src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs index 56fa37f037b..a1797ea8d28 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/PortViewModel.cs @@ -4,9 +4,7 @@ using System.Windows; using System.Windows.Controls.Primitives; using System.Windows.Media; -using Dynamo.Controls; using Dynamo.Graph.Nodes; -using Dynamo.Models; using Dynamo.Search.SearchElements; using Dynamo.UI.Commands; using Dynamo.Utilities; @@ -495,6 +493,8 @@ private void AutoComplete(object parameter) // Handler to invoke Node autocomplete cluster private void AutoCompleteCluster(object parameter) { + // Put a C# timer here to test the cluster placement mock + Stopwatch stopwatch = Stopwatch.StartNew(); var wsViewModel = node.WorkspaceViewModel; wsViewModel.NodeAutoCompleteSearchViewModel.PortViewModel = this; @@ -511,13 +511,43 @@ private void AutoCompleteCluster(object parameter) { return; } + + // Create mock nodes, currently Watch nodes (to avoid potential memory leak from Python Editor), and connect them to the input port + var targetNodeSearchEle = wsViewModel.NodeAutoCompleteSearchViewModel.DefaultResults.ToList()[5]; + targetNodeSearchEle.CreateAndConnectCommand.Execute(wsViewModel.NodeAutoCompleteSearchViewModel.PortViewModel.PortModel); + + var sizeOfMockCluster = 3; + var n = 1; + while (n < sizeOfMockCluster) + { + // Get the last node and connect a new node to it + var node1 = wsViewModel.Nodes.LastOrDefault(); + node1.IsTransient = true; + targetNodeSearchEle.CreateAndConnectCommand.Execute(node1.InPorts.FirstOrDefault().PortModel); + n++; + } + wsViewModel.Nodes.LastOrDefault().IsTransient = true; + + stopwatch.Stop(); // Stop the stopwatch + wsViewModel.DynamoViewModel.Model.Logger.Log($"Cluster Placement Execution Time: {stopwatch.ElapsedMilliseconds} ms"); + + // cluster info display in right side panel if (wsViewModel.DynamoViewModel.IsDNAClusterPlacementEnabled) { try { MLNodeClusterAutoCompletionResponse results = wsViewModel.NodeAutoCompleteSearchViewModel.GetMLNodeClusterAutocompleteResults(); - wsViewModel.OnRequestNodeAutoCompleteViewExtension(results); + + // Process the results and display the preview of the cluster with the highest confidence level + // Leverage some API here to convert topology to actual cluster + results.Results.FirstOrDefault().Topology.Nodes.ToList().ForEach(node => + { + // nothing for now + }); + + // Display the cluster info in the right side panel + // wsViewModel.OnRequestNodeAutoCompleteViewExtension(results); } catch (Exception e) { diff --git a/src/DynamoCoreWpf/ViewModels/Search/NodeAutoCompleteSearchViewModel.cs b/src/DynamoCoreWpf/ViewModels/Search/NodeAutoCompleteSearchViewModel.cs index 592b11c0a58..7899f809817 100644 --- a/src/DynamoCoreWpf/ViewModels/Search/NodeAutoCompleteSearchViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Search/NodeAutoCompleteSearchViewModel.cs @@ -399,31 +399,41 @@ internal void ShowNodeAutocompleMLResults() } } - foreach (var result in results) + OrganizeConfidenceSection(results); + } + } + + /// + /// Compare to low confidence threadhold defined by user can origanize the results into high and low confidence sections. + /// + /// + internal void OrganizeConfidenceSection(List results) + { + foreach (var result in results) + { + if (result.AutoCompletionNodeMachineLearningInfo.ConfidenceScore >= dynamoViewModel.PreferenceSettings.MLRecommendationConfidenceLevel) { - if (result.AutoCompletionNodeMachineLearningInfo.ConfidenceScore >= dynamoViewModel.PreferenceSettings.MLRecommendationConfidenceLevel) - { - FilteredHighConfidenceResults = FilteredHighConfidenceResults.Append(result); - } - else { - FilteredLowConfidenceResults = FilteredLowConfidenceResults.Append(result); - } + FilteredHighConfidenceResults = FilteredHighConfidenceResults.Append(result); } - - // Show low confidence section if there are some results under threshold and feature enabled - DisplayLowConfidence = FilteredLowConfidenceResults.Any() && dynamoViewModel.PreferenceSettings.HideNodesBelowSpecificConfidenceLevel; - - if (!FilteredHighConfidenceResults.Any()) + else { - DisplayAutocompleteMLStaticPage = true; - AutocompleteMLTitle = Resources.AutocompleteLowConfidenceTitle; - AutocompleteMLMessage = Resources.AutocompleteLowConfidenceMessage; - return; + FilteredLowConfidenceResults = FilteredLowConfidenceResults.Append(result); } + } + + // Show low confidence section if there are some results under threshold and feature enabled + DisplayLowConfidence = FilteredLowConfidenceResults.Any() && dynamoViewModel.PreferenceSettings.HideNodesBelowSpecificConfidenceLevel; - // By default, show only the results which are above the threshold - FilteredResults = dynamoViewModel.PreferenceSettings.HideNodesBelowSpecificConfidenceLevel? FilteredHighConfidenceResults : results ; + if (!FilteredHighConfidenceResults.Any()) + { + DisplayAutocompleteMLStaticPage = true; + AutocompleteMLTitle = Resources.AutocompleteLowConfidenceTitle; + AutocompleteMLMessage = Resources.AutocompleteLowConfidenceMessage; + return; } + + // By default, show only the results which are above the threshold + FilteredResults = dynamoViewModel.PreferenceSettings.HideNodesBelowSpecificConfidenceLevel ? FilteredHighConfidenceResults : results; } private MLNodeAutoCompletionResponse GetMLNodeAutocompleteResults(string requestJSON) @@ -528,7 +538,7 @@ internal void ShowLowConfidenceResults() } // Full name and assembly name - private NodeModelTypeId GetInfoFromTypeId(string typeId) + internal NodeModelTypeId GetInfoFromTypeId(string typeId) { if (typeId.Contains(',')) { diff --git a/src/DynamoCoreWpf/ViewModels/Search/NodeSearchElementViewModel.cs b/src/DynamoCoreWpf/ViewModels/Search/NodeSearchElementViewModel.cs index a66808a2070..a5cd8f3805c 100644 --- a/src/DynamoCoreWpf/ViewModels/Search/NodeSearchElementViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Search/NodeSearchElementViewModel.cs @@ -450,7 +450,7 @@ private void AutoLayoutNodes(object sender, EventArgs e) originalNodeId = nodeView.ViewModel.NodeModel.OutputNodes.Values.SelectMany(s => s.Select(t => t.Item2)).Distinct().FirstOrDefault().GUID; newInput = true; } - else if (nodeView.ViewModel.NodeModel.InputNodes.Count() > 0) + else if (nodeView.ViewModel.NodeModel.InputNodes.Count > 0) { originalNodeId = nodeView.ViewModel.NodeModel.InputNodes.Values.Select(s => s.Item2).Distinct().FirstOrDefault().GUID; } diff --git a/src/DynamoCoreWpf/Views/Core/NodeView.xaml b/src/DynamoCoreWpf/Views/Core/NodeView.xaml index 416fd872779..3fea78b8584 100644 --- a/src/DynamoCoreWpf/Views/Core/NodeView.xaml +++ b/src/DynamoCoreWpf/Views/Core/NodeView.xaml @@ -476,6 +476,17 @@ Source="/DynamoCoreWpf;component/UI/Images/NodeStates/frozen-64px.png" Stretch="UniformToFill" /> + + + + + +