From 4971e9dfbdccf3d469bd0578ac84748fc9ee5e86 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Mon, 13 Nov 2023 08:36:57 -0500 Subject: [PATCH 1/3] DYN-6365 Promote Gate node from GD Extension to Dynamo Core. (#14528) * Gate node * Add Tests * Bad format * Remame file * Update graphic --------- Co-authored-by: Craig Long --- .../CoreNodeModels/CoreNodeModelsImages.resx | 44 +++ src/Libraries/CoreNodeModels/Logic/Gate.cs | 72 +++++ .../Properties/Resources.Designer.cs | 38 +++ .../Properties/Resources.en-US.resx | 14 + .../CoreNodeModels/Properties/Resources.resx | 14 + .../NodeViewCustomizations/Gate.cs | 70 +++++ .../CoreNodeModelWpfResources.Designer.cs | 18 ++ .../CoreNodeModelWpfResources.en-US.resx | 6 + .../Properties/CoreNodeModelWpfResources.resx | 6 + .../web/library/layoutSpecs.json | 3 + test/DynamoCoreWpfTests/GateTests.cs | 75 +++++ .../MarkdownGeneratorCommandTests.cs | 2 +- .../CoreNodeModels.Logic.Gate.md | 5 + .../Dynamo_Nodes_Documentation.json | 6 + test/UI/UIGateNode.dyn | 284 ++++++++++++++++++ 15 files changed, 656 insertions(+), 1 deletion(-) create mode 100644 src/Libraries/CoreNodeModels/Logic/Gate.cs create mode 100644 src/Libraries/CoreNodeModelsWpf/NodeViewCustomizations/Gate.cs create mode 100644 test/DynamoCoreWpfTests/GateTests.cs create mode 100644 test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.Logic.Gate.md create mode 100644 test/UI/UIGateNode.dyn diff --git a/src/Libraries/CoreNodeModels/CoreNodeModelsImages.resx b/src/Libraries/CoreNodeModels/CoreNodeModelsImages.resx index efb40687d82..4e4ea041b86 100644 --- a/src/Libraries/CoreNodeModels/CoreNodeModelsImages.resx +++ b/src/Libraries/CoreNodeModels/CoreNodeModelsImages.resx @@ -1376,6 +1376,50 @@ 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 \ No newline at end of file diff --git a/src/Libraries/CoreNodeModels/Logic/Gate.cs b/src/Libraries/CoreNodeModels/Logic/Gate.cs new file mode 100644 index 00000000000..b4cbe24448e --- /dev/null +++ b/src/Libraries/CoreNodeModels/Logic/Gate.cs @@ -0,0 +1,72 @@ +using CoreNodeModels.Properties; +using Dynamo.Graph.Nodes; +using Newtonsoft.Json; +using ProtoCore.AST.AssociativeAST; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreNodeModels.Logic +{ + [NodeName("Gate")] + [NodeDescription(nameof(Resources.GateDescription), typeof(Resources))] + [NodeCategory(BuiltinNodeCategories.LOGIC)] + [NodeSearchTags(nameof(Resources.GateSearchTags), typeof(Resources))] + [InPortNames(">")] + [InPortTypes("object")] + [InPortDescriptions(nameof(Resources.GateInPortToolTip), nameof(Resources))] + [OutPortNames(">")] + [OutPortTypes("object")] + [OutPortDescriptions(nameof(Resources.GateOutPortToolTip), nameof(Resources))] + [IsDesignScriptCompatible] + public class Gate : NodeModel + { + private bool value; + + [JsonProperty("InputValue")] + public virtual bool Value + { + get + { + return value; + } + set + { + if (!this.value.Equals(value)) + { + this.value = value; + ClearDirtyFlag(); + OnNodeModified(); + RaisePropertyChanged(nameof(Value)); + } + } + } + + [JsonConstructor] + private Gate(IEnumerable inPorts, IEnumerable outPorts) : base(inPorts, outPorts) + { + ShouldDisplayPreviewCore = false; + } + + public Gate() + { + Value = false; + RegisterAllPorts(); + ShouldDisplayPreviewCore = false; + } + + public override IEnumerable BuildOutputAst(List inputAstNodes) + { + // Check that node can run + if (!Value) + { + return new[] + {AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), AstFactory.BuildNullNode())}; + } + + return new[] { AstFactory.BuildAssignment(GetAstIdentifierForOutputIndex(0), inputAstNodes[0]) }; + } + } +} diff --git a/src/Libraries/CoreNodeModels/Properties/Resources.Designer.cs b/src/Libraries/CoreNodeModels/Properties/Resources.Designer.cs index ea596bb04a3..cc057625533 100644 --- a/src/Libraries/CoreNodeModels/Properties/Resources.Designer.cs +++ b/src/Libraries/CoreNodeModels/Properties/Resources.Designer.cs @@ -664,6 +664,44 @@ public static string FunctionComposeDescription { } } + /// + /// Looks up a localized string similar to Block data passing through this node to downstream nodes. Node returns the input data only if the node value is set to Open. + /// + ///In Generative Design workflows, this node should be used to control and block the behavior of nodes that will place elements in Revit. + /// + public static string GateDescription { + get { + return ResourceManager.GetString("GateDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data to pass to a downstream node with gated controls.. + /// + public static string GateInPortToolTip { + get { + return ResourceManager.GetString("GateInPortToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data. + /// + public static string GateOutPortToolTip { + get { + return ResourceManager.GetString("GateOutPortToolTip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to gate;stop;data. + /// + public static string GateSearchTags { + get { + return ResourceManager.GetString("GateSearchTags", resourceCulture); + } + } + /// /// Looks up a localized string similar to Conditional statement. /// diff --git a/src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx b/src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx index a00f3e418b6..7be14186dc6 100644 --- a/src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx +++ b/src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx @@ -641,4 +641,18 @@ Default value: {0} Nodes that query data + + Block data passing through this node to downstream nodes. Node returns the input data only if the node value is set to Open. + +In Generative Design workflows, this node should be used to control and block the behavior of nodes that will place elements in Revit + + + Data to pass to a downstream node with gated controls. + + + Data + + + gate;stop;data + \ No newline at end of file diff --git a/src/Libraries/CoreNodeModels/Properties/Resources.resx b/src/Libraries/CoreNodeModels/Properties/Resources.resx index eae12ee8b4d..f6b0f1b5437 100644 --- a/src/Libraries/CoreNodeModels/Properties/Resources.resx +++ b/src/Libraries/CoreNodeModels/Properties/Resources.resx @@ -641,4 +641,18 @@ Default value: {0} Nodes that query data + + Block data passing through this node to downstream nodes. Node returns the input data only if the node value is set to Open. + +In Generative Design workflows, this node should be used to control and block the behavior of nodes that will place elements in Revit + + + Data to pass to a downstream node with gated controls. + + + Data + + + gate;stop;data + \ No newline at end of file diff --git a/src/Libraries/CoreNodeModelsWpf/NodeViewCustomizations/Gate.cs b/src/Libraries/CoreNodeModelsWpf/NodeViewCustomizations/Gate.cs new file mode 100644 index 00000000000..09c22ad23ed --- /dev/null +++ b/src/Libraries/CoreNodeModelsWpf/NodeViewCustomizations/Gate.cs @@ -0,0 +1,70 @@ +using CoreNodeModels; +using CoreNodeModels.Logic; +using CoreNodeModels.Properties; +using Dynamo.Controls; +using Dynamo.UI; +using Dynamo.Wpf; +using Dynamo.Wpf.Properties; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; + +namespace CoreNodeModelsWpf.Nodes +{ + public class GateNodeViewCustomization : INodeViewCustomization + { + public void CustomizeView(Gate model, NodeView nodeView) + { + //add a text box to the input grid of the control + var rbTrue = new RadioButton(); + var rbFalse = new RadioButton(); + rbTrue.Style = rbFalse.Style = (Style)SharedDictionaryManager.DynamoModernDictionary["RadioButton"]; + + //use a unique name for the button group + //so other instances of this element don't get confused + string groupName = Guid.NewGuid().ToString(); + rbTrue.GroupName = groupName; + rbFalse.GroupName = groupName; + + rbTrue.Content = CoreNodeModelWpfResources.GateOpen; + rbTrue.Padding = new Thickness(0, 0, 12, 0); + rbFalse.Content = CoreNodeModelWpfResources.GateClose; + rbFalse.Padding = new Thickness(0); + var wp = new WrapPanel + { + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Stretch, + Margin = new Thickness(10, 5, 10, 0), + Orientation = Orientation.Horizontal + }; + + wp.Children.Add(rbTrue); + wp.Children.Add(rbFalse); + nodeView.inputGrid.Children.Add(wp); + + rbFalse.DataContext = model; + rbTrue.DataContext = model; + + var rbTrueBinding = new Binding("Value") { Mode = BindingMode.TwoWay, }; + rbTrue.SetBinding(ToggleButton.IsCheckedProperty, rbTrueBinding); + + var rbFalseBinding = new Binding("Value") + { + Mode = BindingMode.TwoWay, + Converter = new InverseBoolDisplay() + }; + rbFalse.SetBinding(ToggleButton.IsCheckedProperty, rbFalseBinding); + } + + public void Dispose() + { + // Nothing to dispose + } + } +} diff --git a/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.Designer.cs b/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.Designer.cs index fcabe7ea18d..f02f659b981 100644 --- a/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.Designer.cs +++ b/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.Designer.cs @@ -530,6 +530,24 @@ public static string DateTimeInputToolTip { } } + /// + /// Looks up a localized string similar to Close. + /// + public static string GateClose { + get { + return ResourceManager.GetString("GateClose", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open. + /// + public static string GateOpen { + get { + return ResourceManager.GetString("GateOpen", resourceCulture); + } + } + /// /// Looks up a localized string similar to Display. /// diff --git a/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.en-US.resx b/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.en-US.resx index 40c96277d06..677cd69669a 100644 --- a/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.en-US.resx +++ b/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.en-US.resx @@ -315,4 +315,10 @@ Dictionary<string, List<List<double>>> Drag to modify the size of the chart + + Close + + + Open + diff --git a/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.resx b/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.resx index f436f098a9b..1bed61050db 100644 --- a/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.resx +++ b/src/Libraries/CoreNodeModelsWpf/Properties/CoreNodeModelWpfResources.resx @@ -315,4 +315,10 @@ Dictionary<string, List<List<double>>> Drag to modify the size of the chart + + Close + + + Open + \ No newline at end of file diff --git a/src/LibraryViewExtensionWebView2/web/library/layoutSpecs.json b/src/LibraryViewExtensionWebView2/web/library/layoutSpecs.json index 4af5af4d265..12e7aa65744 100644 --- a/src/LibraryViewExtensionWebView2/web/library/layoutSpecs.json +++ b/src/LibraryViewExtensionWebView2/web/library/layoutSpecs.json @@ -877,6 +877,9 @@ "iconUrl": "", "elementType": "group", "include": [ + { + "path": "Core.Logic.Gate" + }, { "path": "BuiltIn.LoopWhile" }, diff --git a/test/DynamoCoreWpfTests/GateTests.cs b/test/DynamoCoreWpfTests/GateTests.cs new file mode 100644 index 00000000000..a5d4fee74aa --- /dev/null +++ b/test/DynamoCoreWpfTests/GateTests.cs @@ -0,0 +1,75 @@ +using CoreNodeModels.Input; +using CoreNodeModels.Logic; +using Dynamo; +using Dynamo.Graph.Workspaces; +using Dynamo.Tests; +using DynamoCoreWpfTests.Utility; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SystemTestServices; + +namespace DynamoCoreWpfTests +{ + internal class GateTests : DynamoModelTestBase + { + protected override void GetLibrariesToPreload(List libraries) + { + libraries.Add("FunctionObject.ds"); + libraries.Add("DesignScriptBuiltin.dll"); + libraries.Add("BuiltIn.ds"); + libraries.Add("DSCPython.dll"); + libraries.Add("FFITarget.dll"); + } + + [Test] + public void SettingNodeAsOpenPassesData() + { + string openPath = Path.Combine(TestDirectory, @"UI\UIGateNode.dyn"); + RunModel(openPath); + + Guid gateNodeGuid = Guid.Parse("0a4e291d93a84260bd9f37fde3158d83"); + var gateNode = CurrentDynamoModel.CurrentWorkspace.NodeFromWorkspace(gateNodeGuid) as Gate; + + Assert.AreEqual(false, gateNode.Value); + + //Test that the downstream connected node IsNull is true + AssertPreviewValue("448ad21ed4af42b4ae71c45961a173ff", true); + + //Open the gate node + gateNode.Value = true; + + BeginRun(); + + //Test that the downstream connected node IsNull is now not null + AssertPreviewValue("448ad21ed4af42b4ae71c45961a173ff", false); + } + + [Test] + public void SettingNodeAsClosedBlocksData() + { + string openPath = Path.Combine(TestDirectory, @"UI\UIGateNode.dyn"); + RunModel(openPath); + + Guid gateNodeGuid = Guid.Parse("e323a525bab546199c3635ce33c8c46b"); + var gateNode = CurrentDynamoModel.CurrentWorkspace.NodeFromWorkspace(gateNodeGuid) as Gate; + + Assert.AreEqual(true, gateNode.Value); + + //Test that the downstream connected node IsNull is false + AssertPreviewValue("414438cdd24f4d74b08e462e0b17ddff", false); + + //Open the gate node + gateNode.Value = false; + + BeginRun(); + + //Test that the downstream connected node IsNull is now not null + AssertPreviewValue("414438cdd24f4d74b08e462e0b17ddff", true); + } + } +} diff --git a/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs b/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs index 224ccc97630..ac07d1cee09 100644 --- a/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs +++ b/test/Tools/NodeDocumentationMarkdownGeneratorTests/MarkdownGeneratorCommandTests.cs @@ -164,7 +164,7 @@ public void ProducesCorrectOutputFromCoreDirectory_preloadedbinaries() FromDirectoryCommand.HandleDocumentationFromDirectory(opts); var generatedFileNames = tempDirectory.GetFiles().Select(x => x.Name); - Assert.AreEqual(683, generatedFileNames.Count()); + Assert.AreEqual(684, generatedFileNames.Count()); } [Test] diff --git a/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.Logic.Gate.md b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.Logic.Gate.md new file mode 100644 index 00000000000..301a2792dc7 --- /dev/null +++ b/test/Tools/docGeneratorTestFiles/TestMdOutput_CoreNodeModels/CoreNodeModels.Logic.Gate.md @@ -0,0 +1,5 @@ +## Gate - Documentation +This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=2.13.0.2212, Culture=neutral, PublicKeyToken=null. + +For more information about adding documentation to nodes see https://github.com/DynamoDS/Dynamo/wiki/Create-and-Add-Custom-Documentation-to-Nodes + diff --git a/test/Tools/docGeneratorTestFiles/sampledictionarycontent/Dynamo_Nodes_Documentation.json b/test/Tools/docGeneratorTestFiles/sampledictionarycontent/Dynamo_Nodes_Documentation.json index 0e7d0c9c70c..4287f1060c6 100644 --- a/test/Tools/docGeneratorTestFiles/sampledictionarycontent/Dynamo_Nodes_Documentation.json +++ b/test/Tools/docGeneratorTestFiles/sampledictionarycontent/Dynamo_Nodes_Documentation.json @@ -4342,5 +4342,11 @@ "dynFile": ["TrimWhitespace"], "folderPath": "String/Modify/Action", "inDepth": "TrimWhitespace will remove all the whitespace of an input string. In the example below, we start with the string ' Hello World ', which has several leading and trailing spaces. By using TrigWhitespace, we can remove the spaces at the beginning and end of the string, as well as the space between 'Hello' and 'World'." +}, { + "Name": "Gate", + "imageFile": ["Gate"], + "dynFile": ["Gate"], + "folderPath": "Script/Control Flow/Action", + "inDepth": "Block data passing through this node to downstream nodes. Node returns the input data only if the node value is set to Open.\n\nIn Generative Design workflows, this node should be used to control and block the behavior of nodes that will place elements in Revit" } ] diff --git a/test/UI/UIGateNode.dyn b/test/UI/UIGateNode.dyn new file mode 100644 index 00000000000..b7c5b55b09e --- /dev/null +++ b/test/UI/UIGateNode.dyn @@ -0,0 +1,284 @@ +{ + "Uuid": "e9ad9db0-00f2-4e95-b50c-e42e1855418d", + "IsCustomNode": false, + "Description": "", + "Name": "UIGateNode", + "ElementResolver": { + "ResolutionMap": {} + }, + "Inputs": [], + "Outputs": [], + "Nodes": [ + { + "ConcreteType": "CoreNodeModels.Logic.Gate, CoreNodeModels", + "InputValue": false, + "Id": "0a4e291d93a84260bd9f37fde3158d83", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "0f1887f8f60b455eb25c729b5f619e12", + "Name": ">", + "Description": "GateInPortToolTip", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "087cdec73a4748009a7cc7be02c5ffd7", + "Name": ">", + "Description": "GateOutPortToolTip", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Block data passing through this node to downstream nodes. Node returns the input data only if the node value is set to Open.\r\n\r\nIn Generative Design workflows, this node should be used to control and block the behavior of nodes that will place elements in Revit" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "Id": "d463c2fca2f5452e99c58dd22ab2a394", + "NodeType": "CodeBlockNode", + "Inputs": [], + "Outputs": [ + { + "Id": "621f8d2e5ae24c1b91a6991cd796929e", + "Name": "", + "Description": "Value of expression at line 1", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Allows for DesignScript code to be authored directly", + "Code": "1;" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "Id": "448ad21ed4af42b4ae71c45961a173ff", + "NodeType": "FunctionNode", + "Inputs": [ + { + "Id": "d3178b4aafb24c97a67e769f0278c517", + "Name": "object", + "Description": "Object to test.\n\nvar", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "049e5b4ac54f4c2d802c08b2b2009dca", + "Name": "bool", + "Description": "Whether object is null.", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "FunctionSignature": "DSCore.Object.IsNull@var", + "Replication": "Auto", + "Description": "Determines if the given object is null.\n\nObject.IsNull (object: var): bool" + }, + { + "ConcreteType": "CoreNodeModels.Logic.Gate, CoreNodeModels", + "InputValue": true, + "Id": "e323a525bab546199c3635ce33c8c46b", + "NodeType": "ExtensionNode", + "Inputs": [ + { + "Id": "a59f1ddbd73f4c9890e8cb2923163b3f", + "Name": ">", + "Description": "GateInPortToolTip", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "83a18e7d7e164a8fb3ae41056183f817", + "Name": ">", + "Description": "GateOutPortToolTip", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Disabled", + "Description": "Block data passing through this node to downstream nodes. Node returns the input data only if the node value is set to Open.\r\n\r\nIn Generative Design workflows, this node should be used to control and block the behavior of nodes that will place elements in Revit" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "Id": "414438cdd24f4d74b08e462e0b17ddff", + "NodeType": "FunctionNode", + "Inputs": [ + { + "Id": "d86d0d29d74c4f66a9cc6cafb74449d7", + "Name": "object", + "Description": "Object to test.\n\nvar", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "2f84f1e8426649ec83709678b20e02a3", + "Name": "bool", + "Description": "Whether object is null.", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "FunctionSignature": "DSCore.Object.IsNull@var", + "Replication": "Auto", + "Description": "Determines if the given object is null.\n\nObject.IsNull (object: var): bool" + } + ], + "Connectors": [ + { + "Start": "087cdec73a4748009a7cc7be02c5ffd7", + "End": "d3178b4aafb24c97a67e769f0278c517", + "Id": "e66a9e01e5784fafa24f40c5f5e891a3", + "IsHidden": "False" + }, + { + "Start": "621f8d2e5ae24c1b91a6991cd796929e", + "End": "0f1887f8f60b455eb25c729b5f619e12", + "Id": "ffc85ba6b53b452b802dcd791f3d9612", + "IsHidden": "False" + }, + { + "Start": "621f8d2e5ae24c1b91a6991cd796929e", + "End": "a59f1ddbd73f4c9890e8cb2923163b3f", + "Id": "ab382aa0a02f409ca2d4dfe1bd8056b7", + "IsHidden": "False" + }, + { + "Start": "83a18e7d7e164a8fb3ae41056183f817", + "End": "d86d0d29d74c4f66a9cc6cafb74449d7", + "Id": "4ef87edee3044cdfaa28680559bd3a96", + "IsHidden": "False" + } + ], + "Dependencies": [], + "NodeLibraryDependencies": [], + "Thumbnail": "", + "GraphDocumentationURL": null, + "ExtensionWorkspaceData": [ + { + "ExtensionGuid": "28992e1d-abb9-417f-8b1b-05e053bee670", + "Name": "Properties", + "Version": "3.0", + "Data": {} + }, + { + "ExtensionGuid": "DFBD9CC0-DB40-457A-939E-8C8555555A9D", + "Name": "Generative Design", + "Version": "6.0", + "Data": {} + } + ], + "Author": "", + "Linting": { + "activeLinter": "None", + "activeLinterId": "7b75fb44-43fd-4631-a878-29f4d5d8399a", + "warningCount": 0, + "errorCount": 0 + }, + "Bindings": [], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": true, + "IsVisibleInDynamoLibrary": true, + "Version": "3.0.0.5795", + "RunType": "Automatic", + "RunPeriod": "1000" + }, + "Camera": { + "Name": "_Background Preview", + "EyeX": -17.0, + "EyeY": 24.0, + "EyeZ": 50.0, + "LookX": 12.0, + "LookY": -13.0, + "LookZ": -58.0, + "UpX": 0.0, + "UpY": 1.0, + "UpZ": 0.0 + }, + "ConnectorPins": [], + "NodeViews": [ + { + "Id": "0a4e291d93a84260bd9f37fde3158d83", + "Name": "Gate", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 296.0, + "Y": 252.75 + }, + { + "Id": "d463c2fca2f5452e99c58dd22ab2a394", + "Name": "Code Block", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 49.78802839937305, + "Y": 251.9470070998433 + }, + { + "Id": "448ad21ed4af42b4ae71c45961a173ff", + "Name": "Object.IsNull", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 622.7480758949821, + "Y": 252.24250536875752 + }, + { + "Id": "e323a525bab546199c3635ce33c8c46b", + "Name": "Gate", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 293.89968576502383, + "Y": 417.7784871033631 + }, + { + "Id": "414438cdd24f4d74b08e462e0b17ddff", + "Name": "Object.IsNull", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "ShowGeometry": true, + "X": 620.6477616600059, + "Y": 417.2709924721206 + } + ], + "Annotations": [], + "X": 30.64969663596358, + "Y": 143.65060857084856, + "Zoom": 0.6627597805684056 + } +} \ No newline at end of file From 40e4357d3533538f33b877dd21086bc59b9c63a1 Mon Sep 17 00:00:00 2001 From: Craig Long Date: Mon, 13 Nov 2023 10:28:19 -0500 Subject: [PATCH 2/3] Add Dynamo Preference Non-UI Flag for legacy behavior (#14575) * Add flag * Update comment * UpdateTests --------- Co-authored-by: Craig Long Co-authored-by: Michael Kirschner --- src/DynamoCore/Configuration/PreferenceSettings.cs | 7 +++++++ .../Configuration/PreferenceSettingsTests.cs | 4 ++++ test/settings/DynamoSettings-NewSettings.xml | 1 + 3 files changed, 12 insertions(+) diff --git a/src/DynamoCore/Configuration/PreferenceSettings.cs b/src/DynamoCore/Configuration/PreferenceSettings.cs index 2a86d8290a9..cf035afe617 100644 --- a/src/DynamoCore/Configuration/PreferenceSettings.cs +++ b/src/DynamoCore/Configuration/PreferenceSettings.cs @@ -835,6 +835,12 @@ public string IronPythonResolveTargetVersion #region Dynamo Player and Generative Design settings + /// + /// Enable legacy behavior for Dynamo Player to allow renamed Watch nodes to be seen as graph output. + /// This flag is for use in the 2024 product release year and can removed for 2025 + /// + public bool EnableDynamoPlayerRenamedWatchAsOutput { get; set; } + /// /// Collections of folders used by individual Dynamo Player or Generative Design as entry points. /// @@ -906,6 +912,7 @@ public PreferenceSettings() ViewExtensionSettings = new List(); GroupStyleItemsList = new List(); ReadNotificationIds = new List(); + EnableDynamoPlayerRenamedWatchAsOutput = false; DynamoPlayerFolderGroups = new List(); backupLocation = string.Empty; } diff --git a/test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs b/test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs index 9545df367c1..c710f6e602e 100644 --- a/test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs +++ b/test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs @@ -70,6 +70,7 @@ public void TestSettingsSerialization() Assert.AreEqual(settings.ViewExtensionSettings.Count, 0); Assert.AreEqual(settings.DefaultRunType, RunType.Automatic); Assert.AreEqual(settings.DynamoPlayerFolderGroups.Count, 0); + Assert.AreEqual(settings.EnableDynamoPlayerRenamedWatchAsOutput, false); Assert.AreEqual(settings.Locale, "Default"); // Save @@ -89,6 +90,7 @@ public void TestSettingsSerialization() Assert.AreEqual(settings.ViewExtensionSettings.Count, 0); Assert.AreEqual(settings.DefaultRunType, RunType.Automatic); Assert.AreEqual(settings.DynamoPlayerFolderGroups.Count, 0); + Assert.AreEqual(settings.EnableDynamoPlayerRenamedWatchAsOutput, false); Assert.AreEqual(settings.Locale, "Default"); // Change setting values @@ -136,6 +138,7 @@ public void TestSettingsSerialization() } } }); + settings.EnableDynamoPlayerRenamedWatchAsOutput = true; settings.Locale = "zh-CN"; @@ -173,6 +176,7 @@ public void TestSettingsSerialization() Assert.AreEqual(styleItemsList.HexColorString, "000000"); Assert.AreEqual(settings.DynamoPlayerFolderGroups.Count, 1); Assert.AreEqual(settings.DynamoPlayerFolderGroups[0].Folders.Count, 1); + Assert.AreEqual(settings.EnableDynamoPlayerRenamedWatchAsOutput, true); Assert.AreEqual(settings.Locale, "zh-CN"); } diff --git a/test/settings/DynamoSettings-NewSettings.xml b/test/settings/DynamoSettings-NewSettings.xml index 2a7bde2b2cd..686957ac2d4 100644 --- a/test/settings/DynamoSettings-NewSettings.xml +++ b/test/settings/DynamoSettings-NewSettings.xml @@ -128,6 +128,7 @@ 1 + true From f0cd05c3fdb4723c12d848f0691f0e3148fdcbef Mon Sep 17 00:00:00 2001 From: jesusalvino <96534278+jesusalvino@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:45:26 -0500 Subject: [PATCH 3/3] DYN-6311 checksum generation (#14542) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add the Checksum property * Naming and Test file * Checksum function of a graph and storage in the preferences for later comparisons * Checksum generation - Rrefactoring * Assignation of the checksum properties refactored --------- Co-authored-by: Jesus Alfredo Alviño Co-authored-by: Aaron (Qilong) <173288704@qq.com> --- .../Configuration/GraphChecksumItem.cs | 17 +++ src/DynamoCore/Configuration/IPreferences.cs | 6 + .../Configuration/PreferenceSettings.cs | 6 + .../Graph/Workspaces/WorkspaceModel.cs | 2 +- .../ViewModels/Core/DynamoViewModel.cs | 43 ++++++- .../ViewModels/Core/WorkspaceViewModel.cs | 113 +++++++++++++++++- .../Configuration/PreferenceSettingsTests.cs | 12 ++ test/settings/DynamoSettings-NewSettings.xml | 6 + 8 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 src/DynamoCore/Configuration/GraphChecksumItem.cs diff --git a/src/DynamoCore/Configuration/GraphChecksumItem.cs b/src/DynamoCore/Configuration/GraphChecksumItem.cs new file mode 100644 index 00000000000..a00b4538800 --- /dev/null +++ b/src/DynamoCore/Configuration/GraphChecksumItem.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.ObjectModel; +using Dynamo.Core; +using Dynamo.Properties; + +namespace Dynamo.Configuration +{ + /// + /// Represents the stringified version of the nodes connections from a graph + /// + public class GraphChecksumItem + { + public string GraphId { get; set; } + + public string Checksum { get; set; } + } +} diff --git a/src/DynamoCore/Configuration/IPreferences.cs b/src/DynamoCore/Configuration/IPreferences.cs index e3d79c34c9a..7798668f18b 100644 --- a/src/DynamoCore/Configuration/IPreferences.cs +++ b/src/DynamoCore/Configuration/IPreferences.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Dynamo.Configuration; using Dynamo.Graph.Connectors; namespace Dynamo.Interfaces @@ -146,6 +147,11 @@ public interface IPreferences /// Background preview name /// Active state to set void SetIsBackgroundPreviewActive(string name, bool value); + + /// + /// Return a list of GraphChecksumItems + /// + List GraphChecksumItemsList { get; set; } } /// diff --git a/src/DynamoCore/Configuration/PreferenceSettings.cs b/src/DynamoCore/Configuration/PreferenceSettings.cs index cf035afe617..17bb1b7078e 100644 --- a/src/DynamoCore/Configuration/PreferenceSettings.cs +++ b/src/DynamoCore/Configuration/PreferenceSettings.cs @@ -458,6 +458,11 @@ public bool DisableTrustWarnings /// private List trustedLocations { get; set; } = new List(); + /// + /// Return a list of GraphChecksumItems + /// + public List GraphChecksumItemsList { get; set; } + // This function is used to deserialize the trusted locations manually // so that the TrustedLocation propertie's setter does not need to be public. private List DeserializeTrustedLocations(XmlNode preferenceSettingsElement) @@ -915,6 +920,7 @@ public PreferenceSettings() EnableDynamoPlayerRenamedWatchAsOutput = false; DynamoPlayerFolderGroups = new List(); backupLocation = string.Empty; + GraphChecksumItemsList = new List(); } /// diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index b31a1025553..99ffc2c06c2 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -2013,7 +2013,7 @@ internal bool IsValidForFDX { get { - return !HasErrors && !HasNoneGeometryRelatedWarnings; + return Nodes.Count() > 1 && !HasErrors && !HasNoneGeometryRelatedWarnings; } } diff --git a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs index 8ccc4f74f39..5985e801a57 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs @@ -2090,15 +2090,52 @@ internal bool CanSaveAs(object parameters) return (parameters != null); } + /// + /// Indicates if the graph has been changed substantially bearing in mind the connections of its nodes and store the checksum value of the graph in the preferences to later comparison + /// + /// + private bool HasSubstantialCheckSum() + { + bool substantialChecksum = false; + string graphId = Model.CurrentWorkspace.Guid.ToString(); + + GraphChecksumItem checksumItem = PreferenceSettings.GraphChecksumItemsList.Where(i => i.GraphId == graphId).FirstOrDefault(); + if (checksumItem != null) + { + if (checksumItem.Checksum != currentWorkspaceViewModel.Checksum) + { + PreferenceSettings.GraphChecksumItemsList.Remove(checksumItem); + PreferenceSettings.GraphChecksumItemsList.Add(new GraphChecksumItem() { GraphId = graphId, Checksum = currentWorkspaceViewModel.Checksum }); + substantialChecksum = true; + } + } + else + { + PreferenceSettings.GraphChecksumItemsList.Add(new GraphChecksumItem() { GraphId = graphId, Checksum = currentWorkspaceViewModel.Checksum }); + substantialChecksum = true; + } + return substantialChecksum; + } + private void InternalSaveAs(string path, SaveContext saveContext, bool isBackup = false) { try { Model.Logger.Log(String.Format(Properties.Resources.SavingInProgress, path)); CurrentSpaceViewModel.Save(path, isBackup, Model.EngineController, saveContext); - if (!isBackup) AddToRecentFiles(path); - if (currentWorkspaceViewModel?.IsHomeSpace ?? true) - Model.Logger.Log("The Workspace is valid for FDX : " + (HomeSpace.HasRunWithoutCrash && Model.CurrentWorkspace.IsValidForFDX).ToString()); + + if (!isBackup) + { + AddToRecentFiles(path); + + if ((currentWorkspaceViewModel?.IsHomeSpace ?? true) && HomeSpace.HasRunWithoutCrash && Model.CurrentWorkspace.IsValidForFDX && currentWorkspaceViewModel.Checksum != string.Empty) + { + Model.Logger.Log("The Workspace is valid for FDX"); + Model.Logger.Log("The Workspace id is : " + currentWorkspaceViewModel.Model.Guid.ToString()); + Model.Logger.Log("The Workspace checksum is : " + currentWorkspaceViewModel.Checksum); + Model.Logger.Log("The Workspace has Substantial checksum, so is ready to send to FDX : " + HasSubstantialCheckSum().ToString()); + } + } } catch (Exception ex) { diff --git a/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs index 0850e474490..aa64e8920fe 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/WorkspaceViewModel.cs @@ -357,6 +357,106 @@ public bool IsHomeSpace get { return Model == DynamoViewModel.HomeSpace; } } + /// + /// Returns the Json representation of the current graph + /// + [JsonIgnore] + internal JObject JsonRepresentation { get; set; } + + /// + /// Returns the stringified representation of the connected nodes + /// + [JsonIgnore] + public string Checksum + { + get + { + List nodeInfoConnections = new List(); + JObject jsonWorkspace = JsonRepresentation; + var nodes = jsonWorkspace["Nodes"]; + + List nodeIds = new List(); + foreach (JObject node in nodes) + { + var nodeProperties = node.Children(); + JProperty id = nodeProperties.FirstOrDefault(x => x.Name == "Id"); + nodeIds.Add(id.Value.ToString()); + } + + nodeIds.Sort(); + + foreach (string nodeId in nodeIds) + { + List outputIds = new List(); + var node = jsonWorkspace["Nodes"].Where(t => t.Value("Id") == nodeId).Select(t => t).FirstOrDefault(); + var outputsProperty = node.Children().FirstOrDefault(x => x.Name == "Outputs"); + var outputs = (JArray)outputsProperty.Value; + int outputIndex = 1; + + foreach (JObject output in outputs) + { + var outputProperties = output.Children(); + JProperty outputId = outputProperties.FirstOrDefault(x => x.Name == "Id"); + outputIds.Add(outputId.Value.ToString()); + + var connectorsProperty = jsonWorkspace["Connectors"].Where(t => t.Value("Start") == outputId.Value.ToString()); + + foreach (var connector in connectorsProperty) + { + var connectorProperties = connector.Children(); + JProperty endProperty = connectorProperties.FirstOrDefault(x => x.Name == "End"); + string inputId = (String)endProperty.Value; + + var outputConnectedNode = GetNodeByInputId(inputId, jsonWorkspace); + nodeInfoConnections.Add(nodeId + "|[" + outputIndex.ToString() + "|" + outputConnectedNode.Item1 + "|" + outputConnectedNode.Item2.ToString() + "]"); + } + outputIndex++; + } + } + return nodeInfoConnections.Count > 0 ? string.Join(",", nodeInfoConnections) : string.Empty; + } + } + + Tuple GetNodeByInputId(string inputId, JObject jsonWorkspace) + { + var nodes = jsonWorkspace["Nodes"]; + + string nodeId = string.Empty; + int connectedInputIndex = 1; + bool foundNode = false; + + foreach (var node in nodes) + { + if (!foundNode) + { + nodeId = string.Empty; + connectedInputIndex = 1; + + var nodeProperties = node.Children(); + JProperty nodeProperty = nodeProperties.FirstOrDefault(x => x.Name == "Id"); + nodeId = (String)nodeProperty.Value; + + JProperty nodeInputs = nodeProperties.FirstOrDefault(x => x.Name == "Inputs"); + var inputs = (JArray)nodeInputs.Value; + + foreach (JObject input in inputs) + { + var inputProperties = input.Children(); + JProperty connectedNodeInputId = inputProperties.FirstOrDefault(x => x.Name == "Id"); + + if ((String)connectedNodeInputId.Value == inputId) + { + foundNode = true; + break; + } + connectedInputIndex++; + } + } + } + + return new Tuple(nodeId, connectedInputIndex); + } + [JsonIgnore] public bool HasUnsavedChanges { @@ -652,19 +752,26 @@ internal void Save(string filePath, bool isBackup = false, EngineController engi } } } - - saveContent = GuidUtility.UpdateWorkspaceGUIDs(jo.ToString()); + saveContent = GuidUtility.UpdateWorkspaceGUIDs(jo.ToString()); } else { saveContent = jo.ToString(); - } + } + File.WriteAllText(filePath, saveContent); // Handle Workspace or CustomNodeWorkspace related non-serialization internal logic // Only for actual save, update file path and recent file list + // The assignation of the JsonRepresentation and Guid is only for the checksum flow, it will grab info only from .dyn files if (!isBackup) { + if (Path.GetExtension(filePath).Equals(".dyn")) + { + JsonRepresentation = JObject.Parse(saveContent); + DynamoViewModel.Workspaces[0].Model.Guid = new Guid(JsonRepresentation.Properties().First(p => p.Name == "Uuid").Value.ToString()); + } + Model.FileName = filePath; Model.OnSaved(); } diff --git a/test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs b/test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs index c710f6e602e..c35151802dd 100644 --- a/test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs +++ b/test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs @@ -358,6 +358,18 @@ PreferencesComparison comparePrefenceSettings(PreferenceSettings defaultSettings propertiesWithDifferentValue.Add(destinationPi.Name); } } + else if (destinationPi.PropertyType == typeof(List)) + { + if (((List)sourcePi.GetValue(newGeneralSettings, null)).Count == + ((List)destinationPi.GetValue(defaultSettings, null)).Count) + { + propertiesWithSameValue.Add(destinationPi.Name); + } + else + { + propertiesWithDifferentValue.Add(destinationPi.Name); + } + } else { if (newValue?.ToString() == oldValue?.ToString()) diff --git a/test/settings/DynamoSettings-NewSettings.xml b/test/settings/DynamoSettings-NewSettings.xml index 686957ac2d4..1699decee44 100644 --- a/test/settings/DynamoSettings-NewSettings.xml +++ b/test/settings/DynamoSettings-NewSettings.xml @@ -46,6 +46,12 @@ C:\ProgramData\Dynamo\Dynamo Core\2.16\packages true + + + a6f6ab04-752c-4b17-8360-2e563bea7c23 + 588c8174ea314ee3945243a19457796f|[1|f9ec27ad6b584c0babf7055a787df6ad|1] + + C:\ E:\