From 5bbdd8b9c5d4efe254eca27c9cbe075b3af95cb7 Mon Sep 17 00:00:00 2001 From: aparajit-pratap Date: Mon, 27 Nov 2023 21:10:22 -0500 Subject: [PATCH] DYN-6394: Add legacy trace warning when opening workspace (#14628) * remove coreclr-ncalc references * show legacy trace warning * cleanup * get workspace version correctly * update warning * add warning for xml DYNs * add unit test * cleanup * review comments * update tests * revert test fixture change * refactor, add test * cleanup * rename * review comments * update tests * update tests * revert unchanged test file --- src/DynamoCore/Engine/EngineController.cs | 9 +- src/DynamoCore/Graph/Nodes/NodeCategories.cs | 16 +- .../Workspaces/CustomNodeWorkspaceModel.cs | 19 - .../Graph/Workspaces/HomeWorkspaceModel.cs | 22 +- .../Workspaces/SerializationConverters.cs | 44 +- .../Graph/Workspaces/WorkspaceModel.cs | 98 +--- src/DynamoCore/Migration/Migration.cs | 18 - src/DynamoCore/Models/DynamoModel.cs | 34 +- src/DynamoCore/Models/DynamoModelEvents.cs | 6 +- .../Properties/Resources.Designer.cs | 9 + .../Properties/Resources.en-US.resx | 3 + src/DynamoCore/Properties/Resources.resx | 5 +- .../ViewModels/Core/DynamoViewModel.cs | 4 +- test/DynamoCoreTests/CallsiteTests.cs | 58 +++ test/DynamoCoreTests/UtilityTests.cs | 6 +- ...g_customNodes_modified_multiple_pre3.0.dyn | 478 ++++++++++++++++++ 16 files changed, 633 insertions(+), 196 deletions(-) create mode 100644 test/core/callsite/element_binding_customNodes_modified_multiple_pre3.0.dyn diff --git a/src/DynamoCore/Engine/EngineController.cs b/src/DynamoCore/Engine/EngineController.cs index dbb7325a7e8..63b9b665a95 100644 --- a/src/DynamoCore/Engine/EngineController.cs +++ b/src/DynamoCore/Engine/EngineController.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; using Dynamo.Engine.CodeCompletion; using Dynamo.Engine.CodeGeneration; using Dynamo.Engine.NodeToCode; @@ -10,6 +9,7 @@ using Dynamo.Graph.Workspaces; using Dynamo.Logging; using Dynamo.Scheduler; +using Dynamo.Utilities; using ProtoCore.AST.AssociativeAST; using ProtoCore.DSASM.Mirror; using ProtoCore.Mirror; @@ -44,6 +44,11 @@ public class EngineController : LogSourceBase, IAstNodeContainer, IDisposable /// internal static event Action VMLibrariesReset; + /// + /// Dynamo version in which the current workspace was last created or modified. + /// + internal Version CurrentWorkspaceVersion { get; set; } + /// /// This event is fired when is completed. /// @@ -153,6 +158,8 @@ public EngineController(LibraryServices libraryServices, string geometryFactoryF syncDataManager = new SyncDataManager(); VerboseLogging = verboseLogging; + + CurrentWorkspaceVersion = AssemblyHelper.GetDynamoVersion(); } /// diff --git a/src/DynamoCore/Graph/Nodes/NodeCategories.cs b/src/DynamoCore/Graph/Nodes/NodeCategories.cs index 54c06acdd8b..47f94374d94 100644 --- a/src/DynamoCore/Graph/Nodes/NodeCategories.cs +++ b/src/DynamoCore/Graph/Nodes/NodeCategories.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -7,7 +7,6 @@ using System.Xml; using Dynamo.Configuration; using Dynamo.Engine; -using Dynamo.Graph.Nodes.CustomNodes; using Dynamo.Library; using ProtoCore; @@ -282,7 +281,7 @@ internal static string GetDocumentXmlPath(XmlDocument document) /// Returns a dictionary of deserialized node-data-list pairs /// loaded from the given XmlDocument. internal static IEnumerable>> - LoadTraceDataFromXmlDocument(XmlDocument document) + LoadTraceDataFromXmlDocument(XmlDocument document, out bool containsLegacyTraceData) { if (document == null) throw new ArgumentNullException("document"); @@ -301,7 +300,10 @@ where childNode.Name.Equals(sessionXmlTagName) var loadedData = new Dictionary>(); if (!query.Any()) // There's no data, return empty dictionary. + { + containsLegacyTraceData = false; return loadedData; + } XmlElement sessionElement = query.ElementAt(0); foreach (XmlElement nodeElement in sessionElement.ChildNodes) @@ -313,14 +315,12 @@ where childNode.Name.Equals(sessionXmlTagName) var callsiteId = string.Empty; if (child.HasAttribute(Configurations.CallSiteID)) { - callsiteId = child.GetAttribute(Configurations.CallSiteID); + containsLegacyTraceData = true; + return loadedData; } - var traceData = child.InnerText; - callsiteTraceData.Add(new CallSite.RawTraceData(callsiteId, traceData)); } - loadedData.Add(guid, callsiteTraceData); } - + containsLegacyTraceData = false; return loadedData; } diff --git a/src/DynamoCore/Graph/Workspaces/CustomNodeWorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/CustomNodeWorkspaceModel.cs index cc3dc6bd1ab..add23d7f767 100644 --- a/src/DynamoCore/Graph/Workspaces/CustomNodeWorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/CustomNodeWorkspaceModel.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Xml; using Dynamo.Engine; using Dynamo.Graph.Annotations; using Dynamo.Graph.Nodes; @@ -315,23 +314,5 @@ public override void Save(string newPath, bool isBackup = false, EngineControlle base.Save(newPath, isBackup, engine); } - - [Obsolete("Method will be deprecated in Dynamo 3.0.")] - protected override bool PopulateXmlDocument(XmlDocument document) - { - if (!base.PopulateXmlDocument(document)) - return false; - - var root = document.DocumentElement; - if (root == null) - return false; - - var guid = CustomNodeDefinition != null ? CustomNodeDefinition.FunctionId : Guid.NewGuid(); - root.SetAttribute("ID", guid.ToString()); - root.SetAttribute("Description", Description); - root.SetAttribute("Category", Category); - - return true; - } } } diff --git a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs index d5a3274e26c..fece34b574f 100644 --- a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs @@ -1,10 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Runtime.Serialization; -using System.Xml; using Dynamo.Core; using Dynamo.Engine; using Dynamo.Events; @@ -664,24 +661,7 @@ internal void StopPeriodicEvaluation() } #endregion - - [Obsolete("Method will be deprecated in Dynamo 3.0.")] - protected override bool PopulateXmlDocument(XmlDocument document) - { - if (!base.PopulateXmlDocument(document)) - return false; - - var root = document.DocumentElement; - if (root == null) - return false; - - root.SetAttribute("RunType", RunSettings.RunType.ToString()); - root.SetAttribute("RunPeriod", RunSettings.RunPeriod.ToString(CultureInfo.InvariantCulture)); - root.SetAttribute("HasRunWithoutCrash", HasRunWithoutCrash.ToString(CultureInfo.InvariantCulture)); - - return true; - } - + private void PulseMakerRunStarted() { var nodesToUpdate = Nodes.Where(n => n.CanUpdatePeriodically); diff --git a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs index 9529b9a147f..4fa4b7dda3d 100644 --- a/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs +++ b/src/DynamoCore/Graph/Workspaces/SerializationConverters.cs @@ -679,27 +679,38 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist #region Restore trace data // Trace Data Dictionary> loadedTraceData = new Dictionary>(); + bool containsLegacyTraceData = false; // Restore trace data if bindings are present in json if (obj["Bindings"] != null && obj["Bindings"].Children().Count() > 0) { - JEnumerable bindings = obj["Bindings"].Children(); + var wrc = serializer.Converters.First(c => c is WorkspaceReadConverter) as WorkspaceReadConverter; - // Iterate through bindings to extract nodeID's and bindingData (callsiteId & traceData) - foreach (JToken entity in bindings) + if (wrc.engine.CurrentWorkspaceVersion < new Version(3, 0, 0)) { - Guid nodeId = Guid.Parse(entity["NodeId"].ToString()); - string bindingString = entity["Binding"].ToString(); - - // Key(callsiteId) : Value(traceData) - Dictionary bindingData = JsonConvert.DeserializeObject>(bindingString); - List callsiteTraceData = new List(); + containsLegacyTraceData = true; + } + else + { + JEnumerable bindings = obj["Bindings"].Children(); - foreach (KeyValuePair pair in bindingData) + // Iterate through bindings to extract nodeID's and bindingData (callsiteId & traceData) + foreach (JToken entity in bindings) { - callsiteTraceData.Add(new CallSite.RawTraceData(pair.Key, pair.Value)); - } + Guid nodeId = Guid.Parse(entity["NodeId"].ToString()); + string bindingString = entity["Binding"].ToString(); + + // Key(callsiteId) : Value(traceData) + Dictionary bindingData = + JsonConvert.DeserializeObject>(bindingString); + List callsiteTraceData = new List(); - loadedTraceData.Add(nodeId, callsiteTraceData); + foreach (KeyValuePair pair in bindingData) + { + callsiteTraceData.Add(new CallSite.RawTraceData(pair.Key, pair.Value)); + } + + loadedTraceData.Add(nodeId, callsiteTraceData); + } } } #endregion @@ -725,7 +736,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist if (obj.TryGetValue(nameof(HomeWorkspaceModel.Thumbnail), StringComparison.OrdinalIgnoreCase, out JToken thumbnail)) homeWorkspace.Thumbnail = thumbnail.ToString(); - // GraphDocumentaionLink + // GraphDocumentationLink if (obj.TryGetValue(nameof(HomeWorkspaceModel.GraphDocumentationURL), StringComparison.OrdinalIgnoreCase, out JToken helpLink)) { if (Uri.TryCreate(helpLink.ToString(), UriKind.Absolute, out Uri uri)) @@ -738,6 +749,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist // If there is a active linter serialized in the graph we set it to the active linter else set the default None. SetActiveLinter(obj); + ws = homeWorkspace; } @@ -746,7 +758,9 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist ws.ExternalFiles = externalFiles; if (obj.TryGetValue(nameof(WorkspaceModel.Author), StringComparison.OrdinalIgnoreCase, out JToken author)) ws.Author = author.ToString(); - + + ws.ContainsLegacyTraceData = containsLegacyTraceData; + return ws; } diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index bb04951472d..f11681ff4fe 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -8,7 +8,6 @@ using System.Reflection; using System.Threading; using System.Xml; -using Dynamo.Configuration; using Dynamo.Core; using Dynamo.Engine; using Dynamo.Engine.CodeGeneration; @@ -233,6 +232,11 @@ internal int CurrentPasteOffset } } + /// + /// This is true only if the workspace contains legacy SOAP formatted binding data. + /// + internal bool ContainsLegacyTraceData { get; set; } + internal bool ScaleFactorChanged = false; /// @@ -1280,9 +1284,6 @@ public Rect2D Rect get { return new Rect2D(x, y, width, height); } } - //TODO(Steve): This probably isn't needed inside of WorkspaceModel -- MAGN-5714 - internal Version WorkspaceVersion { get; set; } - /// /// Implements property. /// @@ -1372,7 +1373,6 @@ protected WorkspaceModel( IsReadOnly = DynamoUtilities.PathHelper.IsReadOnlyPath(fileName); LastSaved = DateTime.Now; - WorkspaceVersion = AssemblyHelper.GetDynamoVersion(); undoRecorder = new UndoRedoRecorder(this); NodeFactory = factory; @@ -2062,94 +2062,6 @@ private void SerializeElementResolver(XmlDocument xmlDoc) root.AppendChild(mapElement); } - [Obsolete("Method will be deprecated in Dynamo 3.0.")] - protected virtual bool PopulateXmlDocument(XmlDocument xmlDoc) - { - try - { - var root = xmlDoc.DocumentElement; - root.SetAttribute("Version", WorkspaceVersion.ToString()); - root.SetAttribute("X", X.ToString(CultureInfo.InvariantCulture)); - root.SetAttribute("Y", Y.ToString(CultureInfo.InvariantCulture)); - root.SetAttribute("ScaleFactor", ScaleFactor.ToString(CultureInfo.InvariantCulture)); - root.SetAttribute("Name", Name); - root.SetAttribute("Description", Description); - - SerializeElementResolver(xmlDoc); - - var elementList = xmlDoc.CreateElement("Elements"); - //write the root element - root.AppendChild(elementList); - - foreach (var dynEl in Nodes.Select(el => el.Serialize(xmlDoc, SaveContext.Save))) - elementList.AppendChild(dynEl); - - //write only the output connectors - var connectorList = xmlDoc.CreateElement("Connectors"); - //write the root element - root.AppendChild(connectorList); - - foreach (var el in Nodes) - { - foreach (var port in el.OutPorts) - { - foreach ( - var c in - port.Connectors.Where(c => c.Start != null && c.End != null)) - { - var connector = xmlDoc.CreateElement(c.GetType().ToString()); - connectorList.AppendChild(connector); - connector.SetAttribute("start", c.Start.Owner.GUID.ToString()); - connector.SetAttribute("start_index", c.Start.Index.ToString()); - connector.SetAttribute("end", c.End.Owner.GUID.ToString()); - connector.SetAttribute("end_index", c.End.Index.ToString()); - connector.SetAttribute(nameof(ConnectorModel.IsHidden), c.IsHidden.ToString()); - - if (c.End.PortType == PortType.Input) - connector.SetAttribute("portType", "0"); - } - } - } - - //save the notes - var noteList = xmlDoc.CreateElement("Notes"); //write the root element - root.AppendChild(noteList); - foreach (var n in Notes) - { - var note = n.Serialize(xmlDoc, SaveContext.Save); - noteList.AppendChild(note); - } - - //save the annotation - var annotationList = xmlDoc.CreateElement("Annotations"); - root.AppendChild(annotationList); - foreach (var n in annotations) - { - var annotation = n.Serialize(xmlDoc, SaveContext.Save); - annotationList.AppendChild(annotation); - } - - //save the presets into the dyn file as a seperate element on the root - var presetsElement = xmlDoc.CreateElement("Presets"); - root.AppendChild(presetsElement); - foreach (var preset in Presets) - { - var presetState = preset.Serialize(xmlDoc, SaveContext.Save); - presetsElement.AppendChild(presetState); - } - - OnSaving(xmlDoc); - - return true; - } - catch (Exception ex) - { - Log(ex.Message); - Log(ex.StackTrace); - return false; - } - } - internal void SendModelEvent(Guid modelGuid, string eventName, int value) { var retrievedModel = GetModelInternal(modelGuid); diff --git a/src/DynamoCore/Migration/Migration.cs b/src/DynamoCore/Migration/Migration.cs index cfea89aef8d..a3114f13274 100644 --- a/src/DynamoCore/Migration/Migration.cs +++ b/src/DynamoCore/Migration/Migration.cs @@ -517,24 +517,6 @@ internal static Version VersionFromString(string version) return new Version(ver.Major, ver.Minor, ver.Build, 0); } - /// - /// Call this method to obtain the version of current WorkspaceModel. - /// Note that the revision number is dropped as both "0.7.0.1234" - /// should be treated as the same version as "0.7.0.5678", and no file - /// migration should take place. - /// - /// The WorkspaceModel to get the Version from. - /// - /// Returns the Version object representing the workspace - /// version with the revision set to 0. - /// - internal static Version VersionFromWorkspace(WorkspaceModel workspace) - { - // Ignore revision number. - var ver = workspace.WorkspaceVersion; - return new Version(ver.Major, ver.Minor, ver.Build, 0); - } - /// /// Call this method to determine if migration should take place /// for the input DYN/DYF file based on the given version numbers. diff --git a/src/DynamoCore/Models/DynamoModel.cs b/src/DynamoCore/Models/DynamoModel.cs index 4c3034ada9f..1be01a36080 100644 --- a/src/DynamoCore/Models/DynamoModel.cs +++ b/src/DynamoCore/Models/DynamoModel.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using System.Xml; @@ -2317,6 +2316,10 @@ private bool OpenJsonFile( var currentHomeSpace = Workspaces.OfType().FirstOrDefault(); currentHomeSpace.UndefineCBNFunctionDefinitions(); + // This is to handle the case of opening a JSON file that does not have a version string + EngineController.CurrentWorkspaceVersion = dynamoPreferences.Version == + null ? AssemblyHelper.GetDynamoVersion() : new Version(dynamoPreferences.Version); + // TODO, QNTM-1108: WorkspaceModel.FromJson does not check a schema and so will not fail as long // as the fileContents are valid JSON, regardless of if all required data is present or not workspace = WorkspaceModel.FromJson( @@ -2333,16 +2336,16 @@ private bool OpenJsonFile( workspace.FileName = string.IsNullOrEmpty(filePath) ? "" : filePath; workspace.FromJsonGraphId = string.IsNullOrEmpty(filePath) ? WorkspaceModel.ComputeGraphIdFromJson(fileContents) : ""; workspace.ScaleFactor = dynamoPreferences.ScaleFactor; + + if (!IsTestMode) + { + if (workspace.ContainsLegacyTraceData) + { + OnRequestNotification(Resources.LegacyTraceDataWarning, true); + } + } - // NOTE: This is to handle the case of opening a JSON file that does not have a version string - // This logic may not be correct, need to decide the importance of versioning early JSON files - string versionString = dynamoPreferences.Version; - if (versionString == null) - versionString = AssemblyHelper.GetDynamoVersion().ToString(); - workspace.WorkspaceVersion = new System.Version(versionString); - - HomeWorkspaceModel homeWorkspace = workspace as HomeWorkspaceModel; - if (homeWorkspace != null) + if (workspace is HomeWorkspaceModel homeWorkspace) { homeWorkspace.EnableLegacyPolyCurveBehavior ??= PreferenceSettings.Instance.DefaultEnableLegacyPolyCurveBehavior; @@ -2465,12 +2468,19 @@ private bool OpenXmlHomeWorkspace( { var nodeGraph = NodeGraph.LoadGraphFromXml(xmlDoc, NodeFactory); Guid deterministicId = GuidUtility.Create(GuidUtility.UrlNamespace, workspaceInfo.Name); + + var loadedTraceData = Utils.LoadTraceDataFromXmlDocument(xmlDoc, out var containsLegacyTraceData); + if (!IsTestMode) + { + if (containsLegacyTraceData) OnRequestNotification(Resources.LegacyTraceDataWarning, true); + } + var newWorkspace = new HomeWorkspaceModel( deterministicId, EngineController, Scheduler, NodeFactory, - Utils.LoadTraceDataFromXmlDocument(xmlDoc), + loadedTraceData, nodeGraph.Nodes, nodeGraph.Notes, nodeGraph.Annotations, @@ -3213,7 +3223,7 @@ public void ClearCurrentWorkspace() //don't save the file path CurrentWorkspace.FileName = ""; CurrentWorkspace.HasUnsavedChanges = false; - CurrentWorkspace.WorkspaceVersion = AssemblyHelper.GetDynamoVersion(); + EngineController.CurrentWorkspaceVersion = AssemblyHelper.GetDynamoVersion(); this.LinterManager?.SetDefaultLinter(); diff --git a/src/DynamoCore/Models/DynamoModelEvents.cs b/src/DynamoCore/Models/DynamoModelEvents.cs index 2860fd24ad4..3d5143e9130 100644 --- a/src/DynamoCore/Models/DynamoModelEvents.cs +++ b/src/DynamoCore/Models/DynamoModelEvents.cs @@ -547,12 +547,12 @@ internal void OnRequestPythonReset(string pythonEngine) /// /// This event is used to raise a toast notification from the DynamoViewModel /// - internal event Action RequestNotification; - internal void OnRequestNotification(string notification) + internal event Action RequestNotification; + internal void OnRequestNotification(string notification, bool stayOpen = false) { if (RequestNotification != null) { - RequestNotification(notification); + RequestNotification(notification, stayOpen); } } diff --git a/src/DynamoCore/Properties/Resources.Designer.cs b/src/DynamoCore/Properties/Resources.Designer.cs index f523c41ad4c..31864038d8b 100644 --- a/src/DynamoCore/Properties/Resources.Designer.cs +++ b/src/DynamoCore/Properties/Resources.Designer.cs @@ -1117,6 +1117,15 @@ public static string InvalidStartOrEndOfRange { } } + /// + /// Looks up a localized string similar to This workspace contains element binding data in a legacy format that is no longer supported in Dynamo 3.0 and higher versions. Element binding data will be saved in the new format the next time you run and save this workspace.. + /// + public static string LegacyTraceDataWarning { + get { + return ResourceManager.GetString("LegacyTraceDataWarning", resourceCulture); + } + } + /// /// Looks up a localized string similar to Build error for library: {0}. /// diff --git a/src/DynamoCore/Properties/Resources.en-US.resx b/src/DynamoCore/Properties/Resources.en-US.resx index b40c51c4764..ad2ca83d86c 100644 --- a/src/DynamoCore/Properties/Resources.en-US.resx +++ b/src/DynamoCore/Properties/Resources.en-US.resx @@ -908,4 +908,7 @@ This package likely contains an assembly that is blocked. You will need to load Formula node has been deprecated. It has been automatically migrated to a CodeBlock node. Note that results may vary after the migration depending on lacing options selected on the original Formula node. Appropriate replication guides might need to be applied to the CodeBlock node script. + + This workspace contains element binding data in a legacy format that is no longer supported in Dynamo 3.0 and higher versions. Element binding data will be saved in the new format the next time you run and save this workspace. + \ No newline at end of file diff --git a/src/DynamoCore/Properties/Resources.resx b/src/DynamoCore/Properties/Resources.resx index 77d3d8340dd..4306d13dc3a 100644 --- a/src/DynamoCore/Properties/Resources.resx +++ b/src/DynamoCore/Properties/Resources.resx @@ -911,4 +911,7 @@ This package likely contains an assembly that is blocked. You will need to load Formula node has been deprecated. It has been automatically migrated to a CodeBlock node. Note that results may vary after the migration depending on lacing options selected on the original Formula node. Appropriate replication guides might need to be applied to the CodeBlock node script. - \ No newline at end of file + + This workspace contains element binding data in a legacy format that is no longer supported in Dynamo 3.0 and higher versions. Element binding data will be saved in the new format the next time you run and save this workspace. + + diff --git a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs index d1f5ff0b286..75678c56443 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs @@ -1907,9 +1907,9 @@ private void model_ComputeModelDeserialized() /// Create a toast notification with a notification sent by the DynamoModel /// /// - private void model_RequestNotification(string notification) + private void model_RequestNotification(string notification, bool stayOpen = false) { - this.MainGuideManager.CreateRealTimeInfoWindow(notification); + this.MainGuideManager.CreateRealTimeInfoWindow(notification, stayOpen); } /// diff --git a/test/DynamoCoreTests/CallsiteTests.cs b/test/DynamoCoreTests/CallsiteTests.cs index 13ac3e61b9c..e01c3e3a3b7 100644 --- a/test/DynamoCoreTests/CallsiteTests.cs +++ b/test/DynamoCoreTests/CallsiteTests.cs @@ -6,11 +6,13 @@ using System.Reflection; using System.Runtime.Serialization; using CoreNodeModels.Input; +using DSCore; using Dynamo.Engine; using Dynamo.Graph.Nodes; using Dynamo.Graph.Nodes.CustomNodes; using Dynamo.Graph.Nodes.ZeroTouch; using Dynamo.Graph.Workspaces; +using Dynamo.Models; using FFITarget; using NUnit.Framework; using ProtoCore; @@ -334,5 +336,61 @@ public void CanDetectLegacyTraceFormat() "PFNPQVAtRU5WOkVudmVsb3BlIHhtbG5zOnhzaT0iaHR0cDovL3d3dyY2hlbWFzLm1pY3Jvc29mdC5jb20vc29hcC9lbmNvZGluZy"; Assert.True(CheckIfTraceDataIsLegacySOAPFormat(legacyTraceData)); } + + [Test] + public void CanWarnAboutLegacyTraceData() + { + DynamoModel.IsTestMode = false; + var counter = 99; + CurrentDynamoModel.RequestNotification += (_, _) => { counter++; }; + + // Dyn file contains SOAP formatted trace data. + var ws = Open(TestDirectory, callsiteDir, + "element_binding_customNodes_modified_multiple_pre3.0.dyn"); + + DynamoModel.IsTestMode = true; + Assert.AreEqual(100, counter); + } + + [Test] + public void JsonTraceSerializationTest() + { + // Contains trace data in old SOAP format + var filePath = Path.Combine(TestDirectory, + @"core\callsite\element_binding_customNodes_modified_multiple_pre3.0.dyn"); + + OpenModel(filePath); + var model = CurrentDynamoModel; + + if (((HomeWorkspaceModel)model.CurrentWorkspace).RunSettings.RunType == Dynamo.Models.RunType.Manual) + { + RunCurrentModel(); + } + var json = model.CurrentWorkspace.ToJson(model.EngineController); + Assert.That(json, Is.Not.Null.Or.Empty); + + var obj = Data.ParseJSON(json) as Dictionary; + var bindings = obj["Bindings"]; + + var list = (bindings as IEnumerable).ToList(); + Assert.AreEqual(3, list.Count); + int i = 0; + foreach (var elem in list) + { + var traces = ((elem as Dictionary)["Binding"] as Dictionary).Values; + if (i == 0 || i == 2) + { + Assert.AreEqual(2, traces.Count); + } + else Assert.AreEqual(1, traces.Count); + + foreach (var trace in traces) + { + // Assert that new bindings are not in SOAP format. + Assert.False(CallSite.CheckIfTraceDataIsLegacySOAPFormat(trace as string)); + } + ++i; + } + } } } diff --git a/test/DynamoCoreTests/UtilityTests.cs b/test/DynamoCoreTests/UtilityTests.cs index 23a8a0c1f94..a65f43b2ddc 100644 --- a/test/DynamoCoreTests/UtilityTests.cs +++ b/test/DynamoCoreTests/UtilityTests.cs @@ -300,14 +300,14 @@ public void LoadTraceDataFromXmlDocument00() Assert.Throws(() => { // Test method call without a valid XmlDocument. - Graph.Nodes.Utilities.LoadTraceDataFromXmlDocument(null); + Graph.Nodes.Utilities.LoadTraceDataFromXmlDocument(null, out _); }); Assert.Throws(() => { // Test XmlDocument without a document element. XmlDocument document = new XmlDocument(); - Graph.Nodes.Utilities.LoadTraceDataFromXmlDocument(document); + Graph.Nodes.Utilities.LoadTraceDataFromXmlDocument(document, out _); }); } @@ -321,7 +321,7 @@ public void LoadTraceDataFromXmlDocument01() { XmlDocument document = new XmlDocument(); document.AppendChild(document.CreateElement("RootElement")); - outputs = Graph.Nodes.Utilities.LoadTraceDataFromXmlDocument(document); + outputs = Graph.Nodes.Utilities.LoadTraceDataFromXmlDocument(document, out _); }); Assert.IsNotNull(outputs); diff --git a/test/core/callsite/element_binding_customNodes_modified_multiple_pre3.0.dyn b/test/core/callsite/element_binding_customNodes_modified_multiple_pre3.0.dyn new file mode 100644 index 00000000000..b68bf9aa877 --- /dev/null +++ b/test/core/callsite/element_binding_customNodes_modified_multiple_pre3.0.dyn @@ -0,0 +1,478 @@ +{ + "Uuid": "ce4b7a61-2ef7-46ba-965c-e5a69dc6d6a1", + "IsCustomNode": false, + "Description": null, + "Name": "element_binding_customNodes_modified_multiple", + "ElementResolver": { + "ResolutionMap": {} + }, + "Inputs": [], + "Outputs": [], + "Nodes": [ + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Function, DynamoCore", + "FunctionSignature": "9c65f412-0637-4601-ba90-9de2f539224f", + "FunctionType": "Graph", + "NodeType": "FunctionNode", + "Id": "a3cceaa601de4485beb72eea39a46f65", + "Inputs": [ + { + "Id": "a83f8affbd294adab130d8f0af11f68d", + "Name": "someInput", + "Description": "var[]..[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "d959a76782cb472bbe5dae220136d396", + "Name": "out2", + "Description": "return value", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "28031b992d1f4923959eb2b0562a545d", + "Name": "out1", + "Description": "return value", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "testing nested element binding" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "FFITarget.WrapperObject.ID", + "Id": "da39dbe5f59649b18c2fb6ca54acba7b", + "Inputs": [ + { + "Id": "57657d3625f54b7c8fc3b6e5612618a7", + "Name": "wrapperObject", + "Description": "FFITarget.WrapperObject", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "537d03bde70b47d0b02b879668df989d", + "Name": "int", + "Description": "int", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "WrapperObject.ID: int" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "FFITarget.WrapperObject.ID", + "Id": "2366239164a9441a8c4dcd981d9cf542", + "Inputs": [ + { + "Id": "7957f66d0e56481e9c7673de24179110", + "Name": "wrapperObject", + "Description": "FFITarget.WrapperObject", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "f45a46c44e7a48a793862160ab38365c", + "Name": "int", + "Description": "int", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "WrapperObject.ID: int" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CodeBlockNodeModel, DynamoCore", + "NodeType": "CodeBlockNode", + "Code": "100;", + "Id": "247d1b8751c64f8b8ac88e37cad98dff", + "Inputs": [], + "Outputs": [ + { + "Id": "12a69844634441d08df8ff2d1b3b0c42", + "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" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "FFITarget.WrapperObject.ID", + "Id": "342f96575f8942c890867d88495fb0db", + "Inputs": [ + { + "Id": "7178d1186aee4a20b03be1e20ad0da64", + "Name": "wrapperObject", + "Description": "FFITarget.WrapperObject", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "b06f4f43fa7749b5856af632006d7806", + "Name": "int", + "Description": "int", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "WrapperObject.ID: int" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "FFITarget.WrapperObject.WrapperObject@int", + "Id": "e464dfe1dfcf43698eb5e8efc11d1aae", + "Inputs": [ + { + "Id": "165c688d1cdf43eb863f95e12721700c", + "Name": "x", + "Description": "int", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "94b68073cf42449abbd5ecb25fb6fcbc", + "Name": "WrapperObject", + "Description": "WrapperObject", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "WrapperObject.WrapperObject (x: int): WrapperObject" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.CustomNodes.Function, DynamoCore", + "FunctionSignature": "9c65f412-0637-4601-ba90-9de2f539224f", + "FunctionType": "Graph", + "NodeType": "FunctionNode", + "Id": "7d799d548e0344aab8debfb468b50166", + "Inputs": [ + { + "Id": "0a321e2157fe45268bcf46eed08e8cd3", + "Name": "someInput", + "Description": "var[]..[]", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "2aa9090e1843495295c4ce3cddd9459c", + "Name": "out2", + "Description": "return value", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + }, + { + "Id": "af8d68ecd22b45a1afe7c12190cf3300", + "Name": "out1", + "Description": "return value", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "testing nested element binding" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "FFITarget.WrapperObject.ID", + "Id": "08448232ee094aad8280e9a99ed44f46", + "Inputs": [ + { + "Id": "8adfa0547e0d4784b59d352c45a9b4b9", + "Name": "wrapperObject", + "Description": "FFITarget.WrapperObject", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "c7ee12320e614211bf681715342f894a", + "Name": "int", + "Description": "int", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "WrapperObject.ID: int" + }, + { + "ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore", + "NodeType": "FunctionNode", + "FunctionSignature": "FFITarget.WrapperObject.ID", + "Id": "8cfce012280342f3bd688520d68a7f66", + "Inputs": [ + { + "Id": "d5cfa698e209433a8c4efd66b711d98c", + "Name": "wrapperObject", + "Description": "FFITarget.WrapperObject", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Outputs": [ + { + "Id": "bad1c4ea3f9242f2b2a7c9da5c5d6a66", + "Name": "int", + "Description": "int", + "UsingDefaultValue": false, + "Level": 2, + "UseLevels": false, + "KeepListStructure": false + } + ], + "Replication": "Auto", + "Description": "WrapperObject.ID: int" + } + ], + "Connectors": [ + { + "Start": "d959a76782cb472bbe5dae220136d396", + "End": "57657d3625f54b7c8fc3b6e5612618a7", + "Id": "3490d7fe3a8143f8b0da79608cec2db7" + }, + { + "Start": "28031b992d1f4923959eb2b0562a545d", + "End": "7957f66d0e56481e9c7673de24179110", + "Id": "fead1997c72544b79c6085f0047faf4c" + }, + { + "Start": "12a69844634441d08df8ff2d1b3b0c42", + "End": "165c688d1cdf43eb863f95e12721700c", + "Id": "5ca50e2c395e4304b14c86c45480de32" + }, + { + "Start": "b06f4f43fa7749b5856af632006d7806", + "End": "a83f8affbd294adab130d8f0af11f68d", + "Id": "79b1c4ec60014662896beb5cd6f9e219" + }, + { + "Start": "b06f4f43fa7749b5856af632006d7806", + "End": "0a321e2157fe45268bcf46eed08e8cd3", + "Id": "eeeff16f2f3f4eb583e82b66ef370777" + }, + { + "Start": "94b68073cf42449abbd5ecb25fb6fcbc", + "End": "7178d1186aee4a20b03be1e20ad0da64", + "Id": "fe46ec09fbe24401934b70dec0521e7a" + }, + { + "Start": "2aa9090e1843495295c4ce3cddd9459c", + "End": "d5cfa698e209433a8c4efd66b711d98c", + "Id": "bf0d54c88f41487e984f4ec706685b2c" + }, + { + "Start": "af8d68ecd22b45a1afe7c12190cf3300", + "End": "8adfa0547e0d4784b59d352c45a9b4b9", + "Id": "b2e0d33b549f4ad2a8f432ae785116c0" + } + ], + "Dependencies": [ + "9c65f412-0637-4601-ba90-9de2f539224f" + ], + "Bindings": [ + { + "NodeId": "a3cceaa6-01de-4485-beb7-2eea39a46f65", + "Binding": { + "__func_9c65f41206374601ba909de2f539224f_InClassDecl-1_InFunctionScope-1_Instance0_a3cceaa6-01de-4485-beb7-2eea39a46f65;WrapperObject_InClassDecl-1_InFunctionScope1000_Instance0_54062f33-ce19-49a3-a373-5d4a253e26a5": "PFNPQVAtRU5WOkVudmVsb3BlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOlNPQVAtRU5DPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyIgeG1sbnM6U09BUC1FTlY9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3NvYXAvZW52ZWxvcGUvIiB4bWxuczpjbHI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vc29hcC9lbmNvZGluZy9jbHIvMS4wIiBTT0FQLUVOVjplbmNvZGluZ1N0eWxlPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyI+CjxTT0FQLUVOVjpCb2R5Pgo8YTE6Q2FsbFNpdGVfeDAwMkJfVHJhY2VTZXJpYWxpc2VySGVscGVyIGlkPSJyZWYtMSIgeG1sbnM6YTE9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vY2xyL25zYXNzZW0vUHJvdG9Db3JlL1Byb3RvQ29yZSUyQyUyMFZlcnNpb24lM0QyLjIuMC40NTY1JTJDJTIwQ3VsdHVyZSUzRG5ldXRyYWwlMkMlMjBQdWJsaWNLZXlUb2tlbiUzRG51bGwiPgo8TnVtYmVyT2ZFbGVtZW50cz4xPC9OdW1iZXJPZkVsZW1lbnRzPgo8QmFzZS0wX0hhc0RhdGE+dHJ1ZTwvQmFzZS0wX0hhc0RhdGE+CjxCYXNlLTBfRGF0YSBpZD0icmVmLTMiPlBGTlBRVkF0UlU1V09rVnVkbVZzYjNCbElIaHRiRzV6T25oemFUMGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TVM5WVRVeFRZMmhsYldFdGFXNXpkR0Z1WTJVaUlIaHRiRzV6T25oelpEMGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TVM5WVRVeFRZMmhsYldFaUlIaHRiRzV6T2xOUFFWQXRSVTVEUFNKb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5emIyRndMMlZ1WTI5a2FXNW5MeUlnZUcxc2JuTTZVMDlCVUMxRlRsWTlJbWgwZEhBNkx5OXpZMmhsYldGekxuaHRiSE52WVhBdWIzSm5MM052WVhBdlpXNTJaV3h2Y0dVdklpQjRiV3h1Y3pwamJISTlJbWgwZEhBNkx5OXpZMmhsYldGekxtMXBZM0p2YzI5bWRDNWpiMjB2YzI5aGNDOWxibU52WkdsdVp5OWpiSEl2TVM0d0lpQlRUMEZRTFVWT1ZqcGxibU52WkdsdVoxTjBlV3hsUFNKb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5emIyRndMMlZ1WTI5a2FXNW5MeUkrQ2p4VFQwRlFMVVZPVmpwQ2IyUjVQZ284WVRFNlNVUkliMnhrWlhJZ2FXUTlJbkpsWmkweElpQjRiV3h1Y3pwaE1UMGlhSFIwY0RvdkwzTmphR1Z0WVhNdWJXbGpjbTl6YjJaMExtTnZiUzlqYkhJdmJuTmhjM05sYlM5R1JrbFVZWEpuWlhRdlJrWkpWR0Z5WjJWMEpUSkRKVEl3Vm1WeWMybHZiaVV6UkRJdU1pNHdMalExTVRZbE1rTWxNakJEZFd4MGRYSmxKVE5FYm1WMWRISmhiQ1V5UXlVeU1GQjFZbXhwWTB0bGVWUnZhMlZ1SlRORWJuVnNiQ0krQ2p4cGJuUkpSRDR4TVRFeE1URThMMmx1ZEVsRVBnbzhMMkV4T2tsRVNHOXNaR1Z5UGdvOEwxTlBRVkF0UlU1V09rSnZaSGsrQ2p3dlUwOUJVQzFGVGxZNlJXNTJaV3h2Y0dVKzwvQmFzZS0wX0RhdGE+CjxCYXNlLTBfSGFzTmVzdGVkRGF0YT5mYWxzZTwvQmFzZS0wX0hhc05lc3RlZERhdGE+CjwvYTE6Q2FsbFNpdGVfeDAwMkJfVHJhY2VTZXJpYWxpc2VySGVscGVyPgo8L1NPQVAtRU5WOkJvZHk+CjwvU09BUC1FTlY6RW52ZWxvcGU+Cg==", + "__func_9c65f41206374601ba909de2f539224f_InClassDecl-1_InFunctionScope-1_Instance0_a3cceaa6-01de-4485-beb7-2eea39a46f65;WrapperObject_InClassDecl-1_InFunctionScope1000_Instance0_3f04f5f4-f54d-4071-af6f-7d5db4e7706e": "PFNPQVAtRU5WOkVudmVsb3BlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOlNPQVAtRU5DPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyIgeG1sbnM6U09BUC1FTlY9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3NvYXAvZW52ZWxvcGUvIiB4bWxuczpjbHI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vc29hcC9lbmNvZGluZy9jbHIvMS4wIiBTT0FQLUVOVjplbmNvZGluZ1N0eWxlPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyI+CjxTT0FQLUVOVjpCb2R5Pgo8YTE6Q2FsbFNpdGVfeDAwMkJfVHJhY2VTZXJpYWxpc2VySGVscGVyIGlkPSJyZWYtMSIgeG1sbnM6YTE9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vY2xyL25zYXNzZW0vUHJvdG9Db3JlL1Byb3RvQ29yZSUyQyUyMFZlcnNpb24lM0QyLjIuMC40NTY1JTJDJTIwQ3VsdHVyZSUzRG5ldXRyYWwlMkMlMjBQdWJsaWNLZXlUb2tlbiUzRG51bGwiPgo8TnVtYmVyT2ZFbGVtZW50cz4xPC9OdW1iZXJPZkVsZW1lbnRzPgo8QmFzZS0wX0hhc0RhdGE+dHJ1ZTwvQmFzZS0wX0hhc0RhdGE+CjxCYXNlLTBfRGF0YSBpZD0icmVmLTMiPlBGTlBRVkF0UlU1V09rVnVkbVZzYjNCbElIaHRiRzV6T25oemFUMGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TVM5WVRVeFRZMmhsYldFdGFXNXpkR0Z1WTJVaUlIaHRiRzV6T25oelpEMGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TVM5WVRVeFRZMmhsYldFaUlIaHRiRzV6T2xOUFFWQXRSVTVEUFNKb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5emIyRndMMlZ1WTI5a2FXNW5MeUlnZUcxc2JuTTZVMDlCVUMxRlRsWTlJbWgwZEhBNkx5OXpZMmhsYldGekxuaHRiSE52WVhBdWIzSm5MM052WVhBdlpXNTJaV3h2Y0dVdklpQjRiV3h1Y3pwamJISTlJbWgwZEhBNkx5OXpZMmhsYldGekxtMXBZM0p2YzI5bWRDNWpiMjB2YzI5aGNDOWxibU52WkdsdVp5OWpiSEl2TVM0d0lpQlRUMEZRTFVWT1ZqcGxibU52WkdsdVoxTjBlV3hsUFNKb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5emIyRndMMlZ1WTI5a2FXNW5MeUkrQ2p4VFQwRlFMVVZPVmpwQ2IyUjVQZ284WVRFNlNVUkliMnhrWlhJZ2FXUTlJbkpsWmkweElpQjRiV3h1Y3pwaE1UMGlhSFIwY0RvdkwzTmphR1Z0WVhNdWJXbGpjbTl6YjJaMExtTnZiUzlqYkhJdmJuTmhjM05sYlM5R1JrbFVZWEpuWlhRdlJrWkpWR0Z5WjJWMEpUSkRKVEl3Vm1WeWMybHZiaVV6UkRJdU1pNHdMalExTVRZbE1rTWxNakJEZFd4MGRYSmxKVE5FYm1WMWRISmhiQ1V5UXlVeU1GQjFZbXhwWTB0bGVWUnZhMlZ1SlRORWJuVnNiQ0krQ2p4cGJuUkpSRDR5TWpJeU1qSThMMmx1ZEVsRVBnbzhMMkV4T2tsRVNHOXNaR1Z5UGdvOEwxTlBRVkF0UlU1V09rSnZaSGsrQ2p3dlUwOUJVQzFGVGxZNlJXNTJaV3h2Y0dVKzwvQmFzZS0wX0RhdGE+CjxCYXNlLTBfSGFzTmVzdGVkRGF0YT5mYWxzZTwvQmFzZS0wX0hhc05lc3RlZERhdGE+CjwvYTE6Q2FsbFNpdGVfeDAwMkJfVHJhY2VTZXJpYWxpc2VySGVscGVyPgo8L1NPQVAtRU5WOkJvZHk+CjwvU09BUC1FTlY6RW52ZWxvcGU+Cg==" + } + }, + { + "NodeId": "e464dfe1-dfcf-4369-8eb5-e8efc11d1aae", + "Binding": { + "WrapperObject_InClassDecl-1_InFunctionScope-1_Instance0_e464dfe1-dfcf-4369-8eb5-e8efc11d1aae": "PFNPQVAtRU5WOkVudmVsb3BlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOlNPQVAtRU5DPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyIgeG1sbnM6U09BUC1FTlY9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3NvYXAvZW52ZWxvcGUvIiB4bWxuczpjbHI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vc29hcC9lbmNvZGluZy9jbHIvMS4wIiBTT0FQLUVOVjplbmNvZGluZ1N0eWxlPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyI+DQo8U09BUC1FTlY6Qm9keT4NCjxhMTpDYWxsU2l0ZV94MDAyQl9UcmFjZVNlcmlhbGlzZXJIZWxwZXIgaWQ9InJlZi0xIiB4bWxuczphMT0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS9jbHIvbnNhc3NlbS9Qcm90b0NvcmUvUHJvdG9Db3JlJTJDJTIwVmVyc2lvbiUzRDIuMi4wLjQ1NjUlMkMlMjBDdWx0dXJlJTNEbmV1dHJhbCUyQyUyMFB1YmxpY0tleVRva2VuJTNEbnVsbCI+DQo8TnVtYmVyT2ZFbGVtZW50cz4xPC9OdW1iZXJPZkVsZW1lbnRzPg0KPEJhc2UtMF9IYXNEYXRhPnRydWU8L0Jhc2UtMF9IYXNEYXRhPg0KPEJhc2UtMF9EYXRhIGlkPSJyZWYtMyI+UEZOUFFWQXRSVTVXT2tWdWRtVnNiM0JsSUhodGJHNXpPbmh6YVQwaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNUzlZVFV4VFkyaGxiV0V0YVc1emRHRnVZMlVpSUhodGJHNXpPbmh6WkQwaWFIUjBjRG92TDNkM2R5NTNNeTV2Y21jdk1qQXdNUzlZVFV4VFkyaGxiV0VpSUhodGJHNXpPbE5QUVZBdFJVNURQU0pvZEhSd09pOHZjMk5vWlcxaGN5NTRiV3h6YjJGd0xtOXlaeTl6YjJGd0wyVnVZMjlrYVc1bkx5SWdlRzFzYm5NNlUwOUJVQzFGVGxZOUltaDBkSEE2THk5elkyaGxiV0Z6TG5odGJITnZZWEF1YjNKbkwzTnZZWEF2Wlc1MlpXeHZjR1V2SWlCNGJXeHVjenBqYkhJOUltaDBkSEE2THk5elkyaGxiV0Z6TG0xcFkzSnZjMjltZEM1amIyMHZjMjloY0M5bGJtTnZaR2x1Wnk5amJISXZNUzR3SWlCVFQwRlFMVVZPVmpwbGJtTnZaR2x1WjFOMGVXeGxQU0pvZEhSd09pOHZjMk5vWlcxaGN5NTRiV3h6YjJGd0xtOXlaeTl6YjJGd0wyVnVZMjlrYVc1bkx5SStEUW84VTA5QlVDMUZUbFk2UW05a2VUNE5DanhoTVRwSlJFaHZiR1JsY2lCcFpEMGljbVZtTFRFaUlIaHRiRzV6T21FeFBTSm9kSFJ3T2k4dmMyTm9aVzFoY3k1dGFXTnliM052Wm5RdVkyOXRMMk5zY2k5dWMyRnpjMlZ0TDBaR1NWUmhjbWRsZEM5R1JrbFVZWEpuWlhRbE1rTWxNakJXWlhKemFXOXVKVE5FTWk0eUxqQXVORFV4TmlVeVF5VXlNRU4xYkhSMWNtVWxNMFJ1WlhWMGNtRnNKVEpESlRJd1VIVmliR2xqUzJWNVZHOXJaVzRsTTBSdWRXeHNJajROQ2p4cGJuUkpSRDR6UEM5cGJuUkpSRDROQ2p3dllURTZTVVJJYjJ4a1pYSStEUW84TDFOUFFWQXRSVTVXT2tKdlpIaytEUW84TDFOUFFWQXRSVTVXT2tWdWRtVnNiM0JsUGcwSzwvQmFzZS0wX0RhdGE+DQo8QmFzZS0wX0hhc05lc3RlZERhdGE+ZmFsc2U8L0Jhc2UtMF9IYXNOZXN0ZWREYXRhPg0KPC9hMTpDYWxsU2l0ZV94MDAyQl9UcmFjZVNlcmlhbGlzZXJIZWxwZXI+DQo8L1NPQVAtRU5WOkJvZHk+DQo8L1NPQVAtRU5WOkVudmVsb3BlPg0K" + } + }, + { + "NodeId": "7d799d54-8e03-44aa-b8de-bfb468b50166", + "Binding": { + "__func_9c65f41206374601ba909de2f539224f_InClassDecl-1_InFunctionScope-1_Instance0_7d799d54-8e03-44aa-b8de-bfb468b50166;WrapperObject_InClassDecl-1_InFunctionScope1000_Instance0_54062f33-ce19-49a3-a373-5d4a253e26a5": "PFNPQVAtRU5WOkVudmVsb3BlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOlNPQVAtRU5DPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyIgeG1sbnM6U09BUC1FTlY9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3NvYXAvZW52ZWxvcGUvIiB4bWxuczpjbHI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vc29hcC9lbmNvZGluZy9jbHIvMS4wIiBTT0FQLUVOVjplbmNvZGluZ1N0eWxlPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyI+CjxTT0FQLUVOVjpCb2R5Pgo8YTE6Q2FsbFNpdGVfeDAwMkJfVHJhY2VTZXJpYWxpc2VySGVscGVyIGlkPSJyZWYtMSIgeG1sbnM6YTE9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vY2xyL25zYXNzZW0vUHJvdG9Db3JlL1Byb3RvQ29yZSUyQyUyMFZlcnNpb24lM0QyLjIuMC40NTY1JTJDJTIwQ3VsdHVyZSUzRG5ldXRyYWwlMkMlMjBQdWJsaWNLZXlUb2tlbiUzRG51bGwiPgo8TnVtYmVyT2ZFbGVtZW50cz4xPC9OdW1iZXJPZkVsZW1lbnRzPgo8QmFzZS0wX0hhc0RhdGE+dHJ1ZTwvQmFzZS0wX0hhc0RhdGE+CjxCYXNlLTBfRGF0YSBpZD0icmVmLTMiPlBGTlBRVkF0UlU1V09rVnVkbVZzYjNCbElIaHRiRzV6T25oemFUMGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TVM5WVRVeFRZMmhsYldFdGFXNXpkR0Z1WTJVaUlIaHRiRzV6T25oelpEMGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TVM5WVRVeFRZMmhsYldFaUlIaHRiRzV6T2xOUFFWQXRSVTVEUFNKb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5emIyRndMMlZ1WTI5a2FXNW5MeUlnZUcxc2JuTTZVMDlCVUMxRlRsWTlJbWgwZEhBNkx5OXpZMmhsYldGekxuaHRiSE52WVhBdWIzSm5MM052WVhBdlpXNTJaV3h2Y0dVdklpQjRiV3h1Y3pwamJISTlJbWgwZEhBNkx5OXpZMmhsYldGekxtMXBZM0p2YzI5bWRDNWpiMjB2YzI5aGNDOWxibU52WkdsdVp5OWpiSEl2TVM0d0lpQlRUMEZRTFVWT1ZqcGxibU52WkdsdVoxTjBlV3hsUFNKb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5emIyRndMMlZ1WTI5a2FXNW5MeUkrQ2p4VFQwRlFMVVZPVmpwQ2IyUjVQZ284WVRFNlNVUkliMnhrWlhJZ2FXUTlJbkpsWmkweElpQjRiV3h1Y3pwaE1UMGlhSFIwY0RvdkwzTmphR1Z0WVhNdWJXbGpjbTl6YjJaMExtTnZiUzlqYkhJdmJuTmhjM05sYlM5R1JrbFVZWEpuWlhRdlJrWkpWR0Z5WjJWMEpUSkRKVEl3Vm1WeWMybHZiaVV6UkRJdU1pNHdMalExTVRZbE1rTWxNakJEZFd4MGRYSmxKVE5FYm1WMWRISmhiQ1V5UXlVeU1GQjFZbXhwWTB0bGVWUnZhMlZ1SlRORWJuVnNiQ0krQ2p4cGJuUkpSRDR6TXpNek16TThMMmx1ZEVsRVBnbzhMMkV4T2tsRVNHOXNaR1Z5UGdvOEwxTlBRVkF0UlU1V09rSnZaSGsrQ2p3dlUwOUJVQzFGVGxZNlJXNTJaV3h2Y0dVKzwvQmFzZS0wX0RhdGE+CjxCYXNlLTBfSGFzTmVzdGVkRGF0YT5mYWxzZTwvQmFzZS0wX0hhc05lc3RlZERhdGE+CjwvYTE6Q2FsbFNpdGVfeDAwMkJfVHJhY2VTZXJpYWxpc2VySGVscGVyPgo8L1NPQVAtRU5WOkJvZHk+CjwvU09BUC1FTlY6RW52ZWxvcGU+Cg==", + "__func_9c65f41206374601ba909de2f539224f_InClassDecl-1_InFunctionScope-1_Instance0_7d799d54-8e03-44aa-b8de-bfb468b50166;WrapperObject_InClassDecl-1_InFunctionScope1000_Instance0_3f04f5f4-f54d-4071-af6f-7d5db4e7706e": "PFNPQVAtRU5WOkVudmVsb3BlIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzZD0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEiIHhtbG5zOlNPQVAtRU5DPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyIgeG1sbnM6U09BUC1FTlY9Imh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3NvYXAvZW52ZWxvcGUvIiB4bWxuczpjbHI9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vc29hcC9lbmNvZGluZy9jbHIvMS4wIiBTT0FQLUVOVjplbmNvZGluZ1N0eWxlPSJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy9zb2FwL2VuY29kaW5nLyI+CjxTT0FQLUVOVjpCb2R5Pgo8YTE6Q2FsbFNpdGVfeDAwMkJfVHJhY2VTZXJpYWxpc2VySGVscGVyIGlkPSJyZWYtMSIgeG1sbnM6YTE9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vY2xyL25zYXNzZW0vUHJvdG9Db3JlL1Byb3RvQ29yZSUyQyUyMFZlcnNpb24lM0QyLjIuMC40NTY1JTJDJTIwQ3VsdHVyZSUzRG5ldXRyYWwlMkMlMjBQdWJsaWNLZXlUb2tlbiUzRG51bGwiPgo8TnVtYmVyT2ZFbGVtZW50cz4xPC9OdW1iZXJPZkVsZW1lbnRzPgo8QmFzZS0wX0hhc0RhdGE+dHJ1ZTwvQmFzZS0wX0hhc0RhdGE+CjxCYXNlLTBfRGF0YSBpZD0icmVmLTMiPlBGTlBRVkF0UlU1V09rVnVkbVZzYjNCbElIaHRiRzV6T25oemFUMGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TVM5WVRVeFRZMmhsYldFdGFXNXpkR0Z1WTJVaUlIaHRiRzV6T25oelpEMGlhSFIwY0RvdkwzZDNkeTUzTXk1dmNtY3ZNakF3TVM5WVRVeFRZMmhsYldFaUlIaHRiRzV6T2xOUFFWQXRSVTVEUFNKb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5emIyRndMMlZ1WTI5a2FXNW5MeUlnZUcxc2JuTTZVMDlCVUMxRlRsWTlJbWgwZEhBNkx5OXpZMmhsYldGekxuaHRiSE52WVhBdWIzSm5MM052WVhBdlpXNTJaV3h2Y0dVdklpQjRiV3h1Y3pwamJISTlJbWgwZEhBNkx5OXpZMmhsYldGekxtMXBZM0p2YzI5bWRDNWpiMjB2YzI5aGNDOWxibU52WkdsdVp5OWpiSEl2TVM0d0lpQlRUMEZRTFVWT1ZqcGxibU52WkdsdVoxTjBlV3hsUFNKb2RIUndPaTh2YzJOb1pXMWhjeTU0Yld4emIyRndMbTl5Wnk5emIyRndMMlZ1WTI5a2FXNW5MeUkrQ2p4VFQwRlFMVVZPVmpwQ2IyUjVQZ284WVRFNlNVUkliMnhrWlhJZ2FXUTlJbkpsWmkweElpQjRiV3h1Y3pwaE1UMGlhSFIwY0RvdkwzTmphR1Z0WVhNdWJXbGpjbTl6YjJaMExtTnZiUzlqYkhJdmJuTmhjM05sYlM5R1JrbFVZWEpuWlhRdlJrWkpWR0Z5WjJWMEpUSkRKVEl3Vm1WeWMybHZiaVV6UkRJdU1pNHdMalExTVRZbE1rTWxNakJEZFd4MGRYSmxKVE5FYm1WMWRISmhiQ1V5UXlVeU1GQjFZbXhwWTB0bGVWUnZhMlZ1SlRORWJuVnNiQ0krQ2p4cGJuUkpSRDQwTkRRME5EUThMMmx1ZEVsRVBnbzhMMkV4T2tsRVNHOXNaR1Z5UGdvOEwxTlBRVkF0UlU1V09rSnZaSGsrQ2p3dlUwOUJVQzFGVGxZNlJXNTJaV3h2Y0dVKzwvQmFzZS0wX0RhdGE+CjxCYXNlLTBfSGFzTmVzdGVkRGF0YT5mYWxzZTwvQmFzZS0wX0hhc05lc3RlZERhdGE+CjwvYTE6Q2FsbFNpdGVfeDAwMkJfVHJhY2VTZXJpYWxpc2VySGVscGVyPgo8L1NPQVAtRU5WOkJvZHk+CjwvU09BUC1FTlY6RW52ZWxvcGU+Cg==" + } + } + ], + "View": { + "Dynamo": { + "ScaleFactor": 1.0, + "HasRunWithoutCrash": true, + "IsVisibleInDynamoLibrary": true, + "Version": "2.2.0.4565", + "RunType": "Manual", + "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 + }, + "NodeViews": [ + { + "ShowGeometry": true, + "Name": "element_binding_nested", + "Id": "a3cceaa601de4485beb72eea39a46f65", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 400.5, + "Y": 419.25 + }, + { + "ShowGeometry": true, + "Name": "WrapperObject.ID_111111", + "Id": "da39dbe5f59649b18c2fb6ca54acba7b", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 825.5, + "Y": 286.25 + }, + { + "ShowGeometry": true, + "Name": "WrapperObject.ID_222222", + "Id": "2366239164a9441a8c4dcd981d9cf542", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 817.5, + "Y": 437.25 + }, + { + "ShowGeometry": true, + "Name": "Code Block", + "Id": "247d1b8751c64f8b8ac88e37cad98dff", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": -189.0, + "Y": 287.0 + }, + { + "ShowGeometry": true, + "Name": "WrapperObject.ID", + "Id": "342f96575f8942c890867d88495fb0db", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": -59.5, + "Y": 490.25 + }, + { + "ShowGeometry": true, + "Name": "WrapperObject.WrapperObject", + "Id": "e464dfe1dfcf43698eb5e8efc11d1aae", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": -56.5, + "Y": 393.25 + }, + { + "ShowGeometry": true, + "Name": "element_binding_nested", + "Id": "7d799d548e0344aab8debfb468b50166", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 400.5, + "Y": 558.25 + }, + { + "ShowGeometry": true, + "Name": "WrapperObject.ID_444444", + "Id": "08448232ee094aad8280e9a99ed44f46", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 837.5, + "Y": 744.25 + }, + { + "ShowGeometry": true, + "Name": "WrapperObject.ID_333333", + "Id": "8cfce012280342f3bd688520d68a7f66", + "IsSetAsInput": false, + "IsSetAsOutput": false, + "Excluded": false, + "X": 835.5, + "Y": 624.25 + } + ], + "Annotations": [], + "X": -435.0, + "Y": -220.0, + "Zoom": 1.0 + } +} \ No newline at end of file