From f8054e2ad16762f2e9e2c32a6c4609a125201e64 Mon Sep 17 00:00:00 2001 From: michael kirschner Date: Thu, 20 Jan 2022 12:59:18 -0500 Subject: [PATCH 1/7] attempt 1, this works but seems odd. --- .../Graph/Workspaces/HomeWorkspaceModel.cs | 62 ++++++ .../Graph/Workspaces/WorkspaceModel.cs | 61 +---- .../Logging/DynamoAnalyticsClient.cs | 9 +- src/DynamoCore/Logging/Heartbeat.cs | 208 ------------------ src/DynamoCore/Logging/StabilityTracking.cs | 11 - test/DynamoCoreTests/Logging/HeartBeatTest.cs | 97 -------- 6 files changed, 69 insertions(+), 379 deletions(-) delete mode 100644 src/DynamoCore/Logging/Heartbeat.cs delete mode 100644 test/DynamoCoreTests/Logging/HeartBeatTest.cs diff --git a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs index 37045ae53dd..7b9e3c34e2a 100644 --- a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Xml; @@ -16,6 +17,7 @@ using Dynamo.Linting; using Dynamo.Models; using Dynamo.Scheduler; +using DynamoUtilities; using Newtonsoft.Json; using ProtoCore; using ProtoCore.Namespace; @@ -900,6 +902,8 @@ private void OnPreviewGraphCompleted(AsyncTask asyncTask) OnSetNodeDeltaState(deltaComputeStateArgs); } } + + #endregion @@ -988,5 +992,63 @@ internal void CreateNewExtensionData(string uniqueId, string name, string versio var extensionData = new ExtensionData(uniqueId, name, version, data); ExtensionData.Add(extensionData); } + /// + /// Computes the external file references if the Workspace Model is a HomeWorkspaceModel. + /// + /// + internal List GetExternalFiles() + { + var externalFiles = new Dictionary(); + + // If an execution is in progress we'll have to wait for it to be done before we can gather the + // external file references as this implementation relies on the output values of each node. + //instead just bail to avoid blocking the UI. + //TODO Don't really like this solution, this bool only reflects the state of this graph, not the liverunner or engine. + if (!executingTask) + { + foreach (var node in Nodes) + { + externalFilesDictionary.TryGetValue(node.GUID, out var serializedDependencyInfo); + + // Check for the file path string value at each of the output ports of all nodes in the workspace. + foreach (var port in node.OutPorts) + { + var id = node.GetAstIdentifierForOutputIndex(port.Index).Name; + var mirror = EngineController.GetMirror(id); + var data = mirror?.GetData().Data; + + if (data is string dataString && dataString.Contains(@"\")) + { + // Check if the value exists on disk + PathHelper.FileInfoAtPath(dataString, out bool fileExists, out string fileSize); + if (fileExists) + { + var externalFilePath = Path.GetFullPath(dataString); + var externalFileName = Path.GetFileName(dataString); + + if (!externalFiles.ContainsKey(externalFilePath)) + { + externalFiles[externalFilePath] = new DependencyInfo(externalFileName, dataString, ReferenceType.External); + } + + externalFiles[externalFilePath].AddDependent(node.GUID); + externalFiles[externalFilePath].Size = fileSize; + } + // Read the serialized value for that node. + else if (serializedDependencyInfo != null && dataString.Contains(serializedDependencyInfo.Name)) + { + if (!externalFiles.ContainsKey(serializedDependencyInfo.Name)) + { + externalFiles[serializedDependencyInfo.Name] = new DependencyInfo(serializedDependencyInfo.Name, ReferenceType.External); + } + externalFiles[serializedDependencyInfo.Name].AddDependent(node.GUID); + } + } + } + } + } + + return externalFiles.Values.ToList(); + } } } diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index 8a1129bb5b1..75fa87b55e6 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -798,7 +798,11 @@ internal List ExternalFiles { get { - return GetExternalFiles(); + if(this is HomeWorkspaceModel hwm) + { + return hwm.GetExternalFiles(); + } + return new List(); } set { @@ -817,62 +821,9 @@ internal List ExternalFiles } } - private List GetExternalFiles() - { - var externalFiles = new Dictionary(); - - // Computes the external file references if the Workspace Model is a HomeWorkspaceModel. - // The workspace should be executed for the external references to be computed because the node output port values are needed. - if (this is HomeWorkspaceModel homeWorkspaceModel) - { - foreach (var node in nodes) - { - externalFilesDictionary.TryGetValue(node.GUID, out var serializedDependencyInfo); - - // Check for the file path string value at each of the output ports of all nodes in the workspace. - foreach (var port in node.OutPorts) - { - var id = node.GetAstIdentifierForOutputIndex(port.Index).Name; - var mirror = homeWorkspaceModel.EngineController.GetMirror(id); - var data = mirror?.GetData().Data; - - if (data is string dataString && dataString.Contains(@"\")) - { - // Check if the value exists on disk - PathHelper.FileInfoAtPath(dataString, out bool fileExists, out string fileSize); - if (fileExists) - { - var externalFilePath = Path.GetFullPath(dataString); - var externalFileName = Path.GetFileName(dataString); - - if (!externalFiles.ContainsKey(externalFilePath)) - { - externalFiles[externalFilePath] = new DependencyInfo(externalFileName, dataString, ReferenceType.External); - } - - externalFiles[externalFilePath].AddDependent(node.GUID); - externalFiles[externalFilePath].Size = fileSize; - } - // Read the serialized value for that node. - else if (serializedDependencyInfo != null && dataString.Contains(serializedDependencyInfo.Name)) - { - if (!externalFiles.ContainsKey(serializedDependencyInfo.Name)) - { - externalFiles[serializedDependencyInfo.Name] = new DependencyInfo(serializedDependencyInfo.Name, ReferenceType.External); - } - externalFiles[serializedDependencyInfo.Name].AddDependent(node.GUID); - } - } - } - } - } - - return externalFiles.Values.ToList(); - } - private Dictionary nodePackageDictionary = new Dictionary(); private Dictionary localDefinitionsDictionary = new Dictionary(); - private Dictionary externalFilesDictionary = new Dictionary(); + internal Dictionary externalFilesDictionary = new Dictionary(); /// diff --git a/src/DynamoCore/Logging/DynamoAnalyticsClient.cs b/src/DynamoCore/Logging/DynamoAnalyticsClient.cs index dd39ecdeaad..56ea6d8df09 100644 --- a/src/DynamoCore/Logging/DynamoAnalyticsClient.cs +++ b/src/DynamoCore/Logging/DynamoAnalyticsClient.cs @@ -12,7 +12,6 @@ namespace Dynamo.Logging { class DynamoAnalyticsSession : IAnalyticsSession { - private Heartbeat heartbeat; private UsageLog logger; public DynamoAnalyticsSession() @@ -25,8 +24,6 @@ public void Start(DynamoModel model) { StabilityCookie.Startup(); - heartbeat = Heartbeat.GetInstance(model); - logger = new UsageLog("Dynamo", UserId, SessionId); } @@ -38,10 +35,6 @@ public void Dispose() else StabilityCookie.WriteCleanShutdown(); - if (null != heartbeat) - Heartbeat.DestroyInstance(); - heartbeat = null; - if (null != logger) logger.Dispose(); logger = null; @@ -178,7 +171,7 @@ public DynamoAnalyticsClient(DynamoModel dynamoModel) if (Session == null) Session = new DynamoAnalyticsSession(); - //Setup Analytics service, StabilityCookie, Heartbeat and UsageLog. + //Setup Analytics service, StabilityCookie, and UsageLog. Session.Start(dynamoModel); //Dynamo app version. diff --git a/src/DynamoCore/Logging/Heartbeat.cs b/src/DynamoCore/Logging/Heartbeat.cs deleted file mode 100644 index 9d1596db4bd..00000000000 --- a/src/DynamoCore/Logging/Heartbeat.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using Dynamo.Graph.Nodes; -using Dynamo.Models; -using Dynamo.Graph.Workspaces; - -namespace Dynamo.Logging -{ - /// - /// Class to automatically report various metrics of the application usage - /// - internal class Heartbeat - { - private static Heartbeat instance; - private const int WARMUP_DELAY_MS = 5000; - private const int HEARTBEAT_INTERVAL_MS = 60 * 1000; - private readonly DateTime startTime; - private Thread heartbeatThread; - private readonly DynamoModel dynamoModel; - - private readonly AutoResetEvent shutdownEvent = new AutoResetEvent(false); - - private Heartbeat(DynamoModel dynamoModel) - { - // KILLDYNSETTINGS - this is provisional - but we need to enforce that Hearbeat is - // not referencing multiple DynamoModels - this.dynamoModel = dynamoModel; - - startTime = DateTime.Now; - heartbeatThread = new Thread(this.ExecThread); - heartbeatThread.IsBackground = true; - heartbeatThread.Start(); - } - - private void DestroyInternal() - { - System.Diagnostics.Debug.WriteLine("Heartbeat Destory Internal called"); - - shutdownEvent.Set(); // Signal the shutdown event... - - // TODO: Temporary comment out this Join statement. It currently - // causes Dynamo to go into a deadlock when it is shutdown for the - // second time on Revit (that's when the HeartbeatThread is trying - // to call 'GetStringRepOfWorkspaceSync' below (the method has no - // chance of executing, and therefore, will never return due to the - // main thread being held up here waiting for the heartbeat thread - // to end). - // - // heartbeatThread.Join(); // ... wait for thread to end. - - heartbeatThread = null; - } - - public static Heartbeat GetInstance(DynamoModel dynModel) - { - lock (typeof(Heartbeat)) - { - if (instance == null) - instance = new Heartbeat(dynModel); - } - - return instance; - } - - public static void DestroyInstance() - { - lock (typeof(Heartbeat)) - { - if (instance != null) - { - instance.DestroyInternal(); - instance = null; - } - } - } - - private void ExecThread() - { - Thread.Sleep(WARMUP_DELAY_MS); - - while (true) - { - try - { - StabilityCookie.WriteUptimeBeat(DateTime.Now.Subtract(startTime)); - - String usage = PackFrequencyDict(ComputeNodeFrequencies()); - String errors = PackFrequencyDict(ComputeErrorFrequencies()); - - Analytics.LogPiiInfo("Node-usage", usage); - Analytics.LogPiiInfo("Nodes-with-errors", errors); - - DynamoModel.OnRequestDispatcherInvoke( - () => - { - string workspace = dynamoModel.CurrentWorkspace == null ? string.Empty : - dynamoModel.CurrentWorkspace.GetStringRepOfWorkspace(); - Analytics.LogPiiInfo("Workspace", workspace); - }); - - } - catch (Exception e) - { - Debug.WriteLine("Exception in Heartbeat " + e); - } - - // The following call will return "true" if the event is - // signaled, which can only happen when "DestroyInternal" - // is called as the application is shutting down. Otherwise, - // when the wait time elapsed, the loop continues to log - // the next set of information. - // - if (shutdownEvent.WaitOne(HEARTBEAT_INTERVAL_MS)) - break; - } - } - - private string GetVersionString() - { - try - { - string executingAssemblyPathName = System.Reflection.Assembly.GetExecutingAssembly().Location; - FileVersionInfo myFileVersionInfo = FileVersionInfo.GetVersionInfo(executingAssemblyPathName); - return myFileVersionInfo.FileVersion; - } - catch (Exception) - { - return ""; - } - } - - - - /// - /// Turn a frequency dictionary into a string that can be sent - /// - /// - /// - private string PackFrequencyDict(Dictionary frequencies) - { - //@TODO(Luke): Merge with ComputeNodeFrequencies http://adsk-oss.myjetbrains.com/youtrack/issue/MAGN-3842 - StringBuilder sb = new StringBuilder(); - - foreach (String key in frequencies.Keys) - { - sb.Append(key); - sb.Append(":"); - sb.Append(frequencies[key]); - sb.Append(","); - } - - String ret = sb.ToString(); - return ret; - } - - - private Dictionary ComputeNodeFrequencies() - { - //@TODO(Luke): Merge with ComputeNodeFrequencies http://adsk-oss.myjetbrains.com/youtrack/issue/MAGN-3842 - - Dictionary ret = new Dictionary(); - - if (dynamoModel == null) - return ret; - - foreach (var node in dynamoModel.Workspaces.SelectMany(ws => ws.Nodes)) - { - string fullName = node.Name; - if (!ret.ContainsKey(fullName)) - ret[fullName] = 0; - - int count = ret[fullName]; - ret[fullName] = count + 1; - } - - return ret; - } - - private Dictionary ComputeErrorFrequencies() - { - Dictionary ret = new Dictionary(); - - if (dynamoModel == null) - return ret; - - foreach (var node in dynamoModel.Workspaces.SelectMany(ws => ws.Nodes)) - { - if (node.State != ElementState.Error) - continue; - - string fullName = node.Name; - if (!ret.ContainsKey(fullName)) - ret[fullName] = 0; - - int count = ret[fullName]; - ret[fullName] = count + 1; - } - - return ret; - } - - } - -} diff --git a/src/DynamoCore/Logging/StabilityTracking.cs b/src/DynamoCore/Logging/StabilityTracking.cs index b5bebd9716a..955c5ef9011 100644 --- a/src/DynamoCore/Logging/StabilityTracking.cs +++ b/src/DynamoCore/Logging/StabilityTracking.cs @@ -35,8 +35,6 @@ internal static class StabilityCookie // The name of the key must include a valid root. private const string REG_KEY = "HKEY_CURRENT_USER\\Software\\DynamoStability"; private const string SHUTDOWN_TYPE_NAME = "CleanShutdown"; - private const string UPTIME_NAME = "UptimeMS"; - private const string CLEAN_SHUTDOWN_VALUE = "clean"; private const string CRASHING_SHUTDOWN_VALUE = "crashing"; private const string ASSUMING_CRASHING_SHUTDOWN_VALUE = "assumingCrashing"; @@ -58,15 +56,6 @@ public static void WriteCrashingShutdown() Registry.SetValue(REG_KEY, SHUTDOWN_TYPE_NAME, CRASHING_SHUTDOWN_VALUE); } - /// - /// Record that the system has been up for the the length of the time in the timespan - /// - /// - public static void WriteUptimeBeat(TimeSpan timespan) - { - Registry.SetValue(REG_KEY, UPTIME_NAME, ((long)timespan.TotalMilliseconds).ToString()); - } - /// /// Start up and report the status of the last shutdown /// diff --git a/test/DynamoCoreTests/Logging/HeartBeatTest.cs b/test/DynamoCoreTests/Logging/HeartBeatTest.cs deleted file mode 100644 index 9f1ed84102c..00000000000 --- a/test/DynamoCoreTests/Logging/HeartBeatTest.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Dynamo.Graph.Nodes; -using Dynamo.Logging; -using NUnit.Framework; -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; - -namespace Dynamo.Tests.Loggings -{ - [TestFixture] - class HeartBeatTest : DynamoModelTestBase - { - /// - /// This test method will execute using Reflection the private Heartbeat.GetVersionString method - /// - [Test] - [Category("UnitTests")] - public void test_heart_beat_version_string() - { - //Arrange - var heartbeat = Heartbeat.GetInstance(CurrentDynamoModel); - - //Act - //Using reflection we execute the GetVersionString method and store the result - MethodInfo dynMethod = typeof(Heartbeat).GetMethod("GetVersionString", BindingFlags.NonPublic | BindingFlags.Instance); - var fileVersionString = (string)dynMethod.Invoke(heartbeat, null); - - //Assert - //Check that the string response has something and has the right length - Assert.IsFalse(string.IsNullOrEmpty(fileVersionString)); - var versionParts = fileVersionString.Split('.'); - Assert.Greater(versionParts.Length, 0); - foreach(var strPart in versionParts) - { - Assert.IsTrue(Regex.IsMatch(strPart, @"^\d+$")); - } - Heartbeat.DestroyInstance(); - } - - /// - /// This test method set the model = null to execute the ExecThread() method and send an exception - /// - [Test] - [Category("UnitTests")] - public void test_heart_beat_execthread_exception() - { - //Arrange/Act - //The ExecThread() function is executed in a different Thread - //Then If we pass the parameter DynamoModel dynModel as null it will reach the Exception section in ExecThread() - var heartbeat = Heartbeat.GetInstance(null); - - - //Using reflection we execute the ValidateLength method with null in the second parameter - MethodInfo dynMethod = typeof(Heartbeat).GetMethod("GetVersionString", BindingFlags.NonPublic | BindingFlags.Instance); - var fileVersionString = (string)dynMethod.Invoke(heartbeat, null); - - //Assert - Assert.IsFalse(string.IsNullOrEmpty(fileVersionString)); - var versionParts = fileVersionString.Split('.'); - Assert.Greater(versionParts.Length, 0); - - Heartbeat.DestroyInstance(); - } - - /// - /// This test method will assure the the final section of the Heartbeat.ComputeErrorFrequencies() method - /// - [Test] - [Category("UnitTests")] - public void test_heart_beat_compute_error_freq() - { - //Arrange - //This file has a code block with a error - string openPath = Path.Combine(TestDirectory, - @"core\dsevaluation\Test_PortErrorBehavior_CodeBlockErrorsInFile.dyn"); - OpenModel(openPath); - - //We get the code block that has a error state - var cbn = CurrentDynamoModel.CurrentWorkspace.NodeFromWorkspace( - Guid.Parse("dad587d1-acee-445c-890d-98500b408ec6")); - - //Act - //Inside the Heartbeat constructor is called the method but inside a Thread - var heartbeat = Heartbeat.GetInstance(CurrentDynamoModel); - - //Assert - //We just make some validations about the codeblock state and the heartbeat instance - Assert.AreEqual(ElementState.Error, cbn.State); - Assert.IsNotNull(heartbeat); - - Heartbeat.DestroyInstance(); - } - - } -} From 61cec5b690a9dff77d6006ab122331d60fd27547 Mon Sep 17 00:00:00 2001 From: michael kirschner Date: Thu, 20 Jan 2022 19:34:44 -0500 Subject: [PATCH 2/7] switch executingTask for RunEnabled --- src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs index 7b9e3c34e2a..93574fd3807 100644 --- a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs @@ -1003,8 +1003,7 @@ internal List GetExternalFiles() // If an execution is in progress we'll have to wait for it to be done before we can gather the // external file references as this implementation relies on the output values of each node. //instead just bail to avoid blocking the UI. - //TODO Don't really like this solution, this bool only reflects the state of this graph, not the liverunner or engine. - if (!executingTask) + if (RunSettings.RunEnabled) { foreach (var node in Nodes) { From 25d378a089f4d5ce829bb0c9a35e204a9ab6165f Mon Sep 17 00:00:00 2001 From: michael kirschner Date: Fri, 21 Jan 2022 13:04:57 -0500 Subject: [PATCH 3/7] put external files back in wsm --- .../Graph/Workspaces/HomeWorkspaceModel.cs | 57 ------- .../Graph/Workspaces/WorkspaceModel.cs | 161 ++++++++++++------ 2 files changed, 108 insertions(+), 110 deletions(-) diff --git a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs index 93574fd3807..bea672b8ddc 100644 --- a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs @@ -992,62 +992,5 @@ internal void CreateNewExtensionData(string uniqueId, string name, string versio var extensionData = new ExtensionData(uniqueId, name, version, data); ExtensionData.Add(extensionData); } - /// - /// Computes the external file references if the Workspace Model is a HomeWorkspaceModel. - /// - /// - internal List GetExternalFiles() - { - var externalFiles = new Dictionary(); - - // If an execution is in progress we'll have to wait for it to be done before we can gather the - // external file references as this implementation relies on the output values of each node. - //instead just bail to avoid blocking the UI. - if (RunSettings.RunEnabled) - { - foreach (var node in Nodes) - { - externalFilesDictionary.TryGetValue(node.GUID, out var serializedDependencyInfo); - - // Check for the file path string value at each of the output ports of all nodes in the workspace. - foreach (var port in node.OutPorts) - { - var id = node.GetAstIdentifierForOutputIndex(port.Index).Name; - var mirror = EngineController.GetMirror(id); - var data = mirror?.GetData().Data; - - if (data is string dataString && dataString.Contains(@"\")) - { - // Check if the value exists on disk - PathHelper.FileInfoAtPath(dataString, out bool fileExists, out string fileSize); - if (fileExists) - { - var externalFilePath = Path.GetFullPath(dataString); - var externalFileName = Path.GetFileName(dataString); - - if (!externalFiles.ContainsKey(externalFilePath)) - { - externalFiles[externalFilePath] = new DependencyInfo(externalFileName, dataString, ReferenceType.External); - } - - externalFiles[externalFilePath].AddDependent(node.GUID); - externalFiles[externalFilePath].Size = fileSize; - } - // Read the serialized value for that node. - else if (serializedDependencyInfo != null && dataString.Contains(serializedDependencyInfo.Name)) - { - if (!externalFiles.ContainsKey(serializedDependencyInfo.Name)) - { - externalFiles[serializedDependencyInfo.Name] = new DependencyInfo(serializedDependencyInfo.Name, ReferenceType.External); - } - externalFiles[serializedDependencyInfo.Name].AddDependent(node.GUID); - } - } - } - } - } - - return externalFiles.Values.ToList(); - } } } diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index 75fa87b55e6..687d96c4226 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -142,17 +142,17 @@ public override bool Equals(object obj) this.WidthAdjustment == other.WidthAdjustment && this.HeightAdjustment == other.HeightAdjustment; - //TODO try to get rid of these if possible - //needs investigation if we are okay letting them get - //calculated at runtime. currently checking them will fail as we do - //not deserialize them. - - //tolerantDoubleCompare(this.Left, other.Left) && - //tolerantDoubleCompare(this.Top, other.Top) && - //tolerantDoubleCompare(this.InitialTop, other.InitialTop); - //this.Width == other.Width && - //this.Height == other.Height && - //this.TextBlockHeight == other.TextBlockHeight; + //TODO try to get rid of these if possible + //needs investigation if we are okay letting them get + //calculated at runtime. currently checking them will fail as we do + //not deserialize them. + + //tolerantDoubleCompare(this.Left, other.Left) && + //tolerantDoubleCompare(this.Top, other.Top) && + //tolerantDoubleCompare(this.InitialTop, other.InitialTop); + //this.Width == other.Width && + //this.Height == other.Height && + //this.TextBlockHeight == other.TextBlockHeight; } } @@ -487,7 +487,7 @@ private void RegisterConnector(ConnectorModel connector) public event Action ConnectorDeleted; protected virtual void OnConnectorDeleted(ConnectorModel obj) { - + var handler = ConnectorDeleted; if (handler != null) handler(obj); //Check if the workspace is loaded, i.e all the nodes are @@ -574,7 +574,7 @@ private void OnSyncWithDefinitionEnd(NodeModel nodeModel) /// /// A set of input parameter states, this can be used to set the graph to a serialized state. /// - public IEnumerable Presets { get { return presets;} } + public IEnumerable Presets { get { return presets; } } /// /// The date of the last save. @@ -595,7 +595,8 @@ public DateTime LastSaved /// a list of workspace IDs in GUID form public HashSet Dependencies { - get { + get + { dependencies.Clear(); //if the workspace is a main workspace then find all functions and their dependencies if (this is HomeWorkspaceModel) @@ -673,7 +674,7 @@ internal List NodeLibraryDependencies } // If incorrect version of package is installed and not marked for uninstall, // set the state. Otherwise, keep the RequiresRestart state away from overwritten. - else if(packageDependencies[saved].State != PackageDependencyState.RequiresRestart) + else if (packageDependencies[saved].State != PackageDependencyState.RequiresRestart) { packageDependencies[saved].State = PackageDependencyState.IncorrectVersion; } @@ -723,12 +724,12 @@ internal List NodeLocalDefinitions { get { - var nodeLocalDefinitions = new Dictionary(); + var nodeLocalDefinitions = new Dictionary(); foreach (var node in Nodes) { var collected = GetNodePackage(node); - + if (!nodePackageDictionary.ContainsKey(node.GUID) && collected == null) { string localDefinitionName; @@ -798,11 +799,7 @@ internal List ExternalFiles { get { - if(this is HomeWorkspaceModel hwm) - { - return hwm.GetExternalFiles(); - } - return new List(); + return GetExternalFiles(); } set { @@ -821,6 +818,64 @@ internal List ExternalFiles } } + /// + /// Computes the external file references if the Workspace Model is a HomeWorkspaceModel and graph is not running. + /// + /// + internal List GetExternalFiles() + { + var externalFiles = new Dictionary(); + + // If an execution is in progress we'll have to wait for it to be done before we can gather the + // external file references as this implementation relies on the output values of each node. + //instead just bail to avoid blocking the UI. + if (this is HomeWorkspaceModel homeWorkspaceModel && homeWorkspaceModel.RunSettings.RunEnabled) + { + foreach (var node in Nodes) + { + externalFilesDictionary.TryGetValue(node.GUID, out var serializedDependencyInfo); + + // Check for the file path string value at each of the output ports of all nodes in the workspace. + foreach (var port in node.OutPorts) + { + var id = node.GetAstIdentifierForOutputIndex(port.Index).Name; + var mirror = homeWorkspaceModel.EngineController.GetMirror(id); + var data = mirror?.GetData().Data; + + if (data is string dataString && dataString.Contains(@"\")) + { + // Check if the value exists on disk + PathHelper.FileInfoAtPath(dataString, out bool fileExists, out string fileSize); + if (fileExists) + { + var externalFilePath = Path.GetFullPath(dataString); + var externalFileName = Path.GetFileName(dataString); + + if (!externalFiles.ContainsKey(externalFilePath)) + { + externalFiles[externalFilePath] = new DependencyInfo(externalFileName, dataString, ReferenceType.External); + } + + externalFiles[externalFilePath].AddDependent(node.GUID); + externalFiles[externalFilePath].Size = fileSize; + } + // Read the serialized value for that node. + else if (serializedDependencyInfo != null && dataString.Contains(serializedDependencyInfo.Name)) + { + if (!externalFiles.ContainsKey(serializedDependencyInfo.Name)) + { + externalFiles[serializedDependencyInfo.Name] = new DependencyInfo(serializedDependencyInfo.Name, ReferenceType.External); + } + externalFiles[serializedDependencyInfo.Name].AddDependent(node.GUID); + } + } + } + } + } + + return externalFiles.Values.ToList(); + } + private Dictionary nodePackageDictionary = new Dictionary(); private Dictionary localDefinitionsDictionary = new Dictionary(); internal Dictionary externalFilesDictionary = new Dictionary(); @@ -857,9 +912,9 @@ public string Description /// public bool HasUnsavedChanges { - get + get { - if(!string.IsNullOrEmpty(this.FileName)) // if there is a filename + if (!string.IsNullOrEmpty(this.FileName)) // if there is a filename { if (!File.Exists(this.FileName)) // but the filename is invalid { @@ -881,7 +936,7 @@ public bool HasUnsavedChanges /// Returns if current workspace is readonly. /// public bool IsReadOnly - { + { //if the workspace contains xmlDummyNodes it's effectively a readonly graph. get { return isReadOnly || this.containsXmlDummyNodes() || this.containsInvalidInputSymbols(); } set @@ -921,7 +976,7 @@ private void AddNode(NodeModel node) { nodes.Add(node); } - + OnNodeAdded(node); } @@ -1051,7 +1106,7 @@ public double Y RaisePropertyChanged("Y"); } } - + /// /// Get or set the zoom value of the workspace. /// @@ -1329,7 +1384,7 @@ public virtual void Clear() ClearUndoRecorder(); ResetWorkspace(); - + X = 0.0; Y = 0.0; Zoom = 1.0; @@ -1415,7 +1470,7 @@ protected virtual void RegisterNode(NodeModel node) { node.Modified += NodeModified; node.ConnectorAdded += OnConnectorAdded; - node.UpdateASTCollection +=OnToggleNodeFreeze; + node.UpdateASTCollection += OnToggleNodeFreeze; var functionNode = node as Function; if (functionNode != null) @@ -1568,8 +1623,8 @@ internal void AddAnnotation(AnnotationModel annotationModel) internal AnnotationModel AddAnnotation(string text, Guid id) { - var selectedNodes = this.Nodes == null ? null:this.Nodes.Where(s => s.IsSelected); - var selectedNotes = this.Notes == null ? null: this.Notes.Where(s => s.IsSelected); + var selectedNodes = this.Nodes == null ? null : this.Nodes.Where(s => s.IsSelected); + var selectedNotes = this.Notes == null ? null : this.Notes.Where(s => s.IsSelected); if (CheckIfModelExistsInSomeGroup(selectedNodes, selectedNotes)) { @@ -1593,10 +1648,10 @@ internal AnnotationModel AddAnnotation(string titleText, string text, Guid id) } private AnnotationModel CreateAndSubcribeAnnotationModel( - IEnumerable nodes, - IEnumerable notes, - Guid id, - string titel, + IEnumerable nodes, + IEnumerable notes, + Guid id, + string titel, string description = "") { var annotationModel = new AnnotationModel(nodes, notes) @@ -1666,7 +1721,7 @@ private bool CheckIfModelExistsInSomeGroup(IEnumerable selectNodes, I foreach (var group in this.Annotations) { var groupModels = group.Nodes; - + //Selected models minus the ones in the current annotation var modelsExceptGroup = selectedModels.Except(groupModels).ToList(); @@ -1709,7 +1764,7 @@ internal IEnumerable GetSourceNodes() return Nodes.Where( node => - !node.InPorts.Any()||node.InPorts.All(port => !port.Connectors.Any())); + !node.InPorts.Any() || node.InPorts.All(port => !port.Connectors.Any())); } /// @@ -1754,7 +1809,7 @@ internal void IncrementPasteOffset() /// internal bool containsXmlDummyNodes() { - return this.Nodes.OfType().Where(node => node.OriginalNodeContent is XmlElement).Count()> 0; + return this.Nodes.OfType().Where(node => node.OriginalNodeContent is XmlElement).Count() > 0; } /// @@ -2188,11 +2243,11 @@ public static WorkspaceModel FromJson(string json, LibraryServices libraryServic public void UpdateWithExtraWorkspaceViewInfo(ExtraWorkspaceViewInfo workspaceViewInfo) { if (workspaceViewInfo == null) - return; + return; X = workspaceViewInfo.X; Y = workspaceViewInfo.Y; - Zoom = workspaceViewInfo.Zoom; + Zoom = workspaceViewInfo.Zoom; OnCurrentOffsetChanged( this, @@ -2229,7 +2284,7 @@ public void UpdateWithExtraWorkspaceViewInfo(ExtraWorkspaceViewInfo workspaceVie private void LoadNodes(IEnumerable nodeViews) { if (nodeViews == null) - return; + return; foreach (ExtraNodeViewInfo nodeViewInfo in nodeViews) { @@ -2252,9 +2307,9 @@ private void LoadNodes(IEnumerable nodeViews) nodeModel.UpdateValue(new UpdateValueParams("IsVisible", nodeViewInfo.ShowGeometry.ToString())); } else - { - this.Log(string.Format("This graph has a nodeview with id:{0} and name:{1}, but does not contain a matching nodeModel", - guidValue.ToString(),nodeViewInfo.Name) + { + this.Log(string.Format("This graph has a nodeview with id:{0} and name:{1}, but does not contain a matching nodeModel", + guidValue.ToString(), nodeViewInfo.Name) , WarningLevel.Moderate); } } @@ -2263,7 +2318,7 @@ private void LoadNodes(IEnumerable nodeViews) private void LoadLegacyNotes(IEnumerable noteViews) { if (noteViews == null) - return; + return; foreach (ExtraNoteViewInfo noteViewInfo in noteViews) { @@ -2284,7 +2339,7 @@ private void LoadLegacyNotes(IEnumerable noteViews) private void LoadNotesFromAnnotations(IEnumerable annotationViews) { if (annotationViews == null) - return; + return; foreach (ExtraAnnotationViewInfo annotationViewInfo in annotationViews) { @@ -2302,9 +2357,9 @@ private void LoadNotesFromAnnotations(IEnumerable annot FirstOrDefault(x => x.GUID.ToString("N") == annotationViewInfo.PinnedNode); var noteModel = new NoteModel( - annotationViewInfo.Left, - annotationViewInfo.Top, - text, + annotationViewInfo.Left, + annotationViewInfo.Top, + text, annotationGuidValue, pinnedNode); @@ -2319,14 +2374,14 @@ private void LoadNotesFromAnnotations(IEnumerable annot private void LoadConnectorPins(IEnumerable pinInfo) { - if (pinInfo == null) {return;} + if (pinInfo == null) { return; } foreach (ExtraConnectorPinInfo pinViewInfo in pinInfo) { var connectorGuid = IdToGuidConverter(pinViewInfo.ConnectorGuid); var matchingConnector = Connectors.FirstOrDefault(x => x.GUID == connectorGuid); - if (matchingConnector is null) {return;} + if (matchingConnector is null) { return; } matchingConnector.AddPin(pinViewInfo.Left, pinViewInfo.Top); } @@ -2343,7 +2398,7 @@ private void LoadAnnotations(IEnumerable annotationView // Before creating this group we need to create // any group belonging to this group. if (annotationViewInfo.HasNestedGroups && - !annotationQueue.All(x=>x.HasNestedGroups)) + !annotationQueue.All(x => x.HasNestedGroups)) { annotationQueue.Enqueue(annotationViewInfo); continue; @@ -2445,7 +2500,7 @@ private Guid IdToGuidConverter(string id) return deterministicGuid; } - + /// /// Returns a DelayedGraphExecution object. /// From 5c13fda192fbd3a1d7c69a0fc9105c7ecbbcabb9 Mon Sep 17 00:00:00 2001 From: michael kirschner Date: Fri, 21 Jan 2022 13:14:51 -0500 Subject: [PATCH 4/7] int to private --- src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index 687d96c4226..12c4f9bca9c 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -878,7 +878,7 @@ internal List GetExternalFiles() private Dictionary nodePackageDictionary = new Dictionary(); private Dictionary localDefinitionsDictionary = new Dictionary(); - internal Dictionary externalFilesDictionary = new Dictionary(); + private Dictionary externalFilesDictionary = new Dictionary(); /// From 081354f6975475360a92bc4044713d5b71b57658 Mon Sep 17 00:00:00 2001 From: michael kirschner Date: Fri, 21 Jan 2022 13:15:36 -0500 Subject: [PATCH 5/7] remove usings --- src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs index bea672b8ddc..1a327db2eff 100644 --- a/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/HomeWorkspaceModel.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Xml; @@ -17,7 +16,6 @@ using Dynamo.Linting; using Dynamo.Models; using Dynamo.Scheduler; -using DynamoUtilities; using Newtonsoft.Json; using ProtoCore; using ProtoCore.Namespace; From dba9b84efe5dec9ddf4ed05a8d8bb7e00c20fa3f Mon Sep 17 00:00:00 2001 From: michael kirschner Date: Fri, 21 Jan 2022 13:21:26 -0500 Subject: [PATCH 6/7] revert change --- src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index 12c4f9bca9c..9fec60db62b 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -831,7 +831,7 @@ internal List GetExternalFiles() //instead just bail to avoid blocking the UI. if (this is HomeWorkspaceModel homeWorkspaceModel && homeWorkspaceModel.RunSettings.RunEnabled) { - foreach (var node in Nodes) + foreach (var node in nodes) { externalFilesDictionary.TryGetValue(node.GUID, out var serializedDependencyInfo); From 7bc8f847c0fc8b65252cc69a055556d72fd9b346 Mon Sep 17 00:00:00 2001 From: michael kirschner Date: Fri, 21 Jan 2022 13:25:11 -0500 Subject: [PATCH 7/7] revert change --- src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index 9fec60db62b..2a1402b19a8 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -822,7 +822,7 @@ internal List ExternalFiles /// Computes the external file references if the Workspace Model is a HomeWorkspaceModel and graph is not running. /// /// - internal List GetExternalFiles() + private List GetExternalFiles() { var externalFiles = new Dictionary();