Skip to content

Commit

Permalink
DYN-7278: Fix dictionary preview by adding support for more types (#1…
Browse files Browse the repository at this point in the history
  • Loading branch information
aparajit-pratap authored Jan 14, 2025
1 parent bf568b0 commit 2636f7f
Show file tree
Hide file tree
Showing 7 changed files with 650 additions and 3 deletions.
35 changes: 33 additions & 2 deletions src/DynamoCoreWpf/Interfaces/IWatchHandler.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
using System;

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Dynamo.Configuration;
using Newtonsoft.Json.Linq;

using Dynamo.Extensions;
using Dynamo.ViewModels;
using Dynamo.Wpf.Properties;
using ProtoCore.DSASM;
using ProtoCore.Mirror;
using ProtoCore.Utils;
using Newtonsoft.Json;

namespace Dynamo.Interfaces
{
Expand Down Expand Up @@ -83,7 +86,18 @@ private WatchViewModel ProcessThing(object value, ProtoCore.RuntimeCore runtimeC

return node;
}
if (value is JObject obj)
{
var dict = ConvertJObjectToDictionary(obj);
var node = new WatchViewModel(dict.Keys.Any() ? WatchViewModel.DICTIONARY : WatchViewModel.EMPTY_DICTIONARY, tag, RequestSelectGeometry, true);

foreach (var e in dict.Keys.Zip(dict.Values, (key, val) => new { key, val }))
{
node.Children.Add(ProcessThing(e.val, runtimeCore, tag + ":" + e.key, showRawData, callback));
}

return node;
}
if (!(value is string) && value is IEnumerable)
{
var list = (value as IEnumerable).Cast<dynamic>().ToList();
Expand Down Expand Up @@ -122,6 +136,23 @@ private WatchViewModel ProcessThing(object value, ProtoCore.RuntimeCore runtimeC
return new WatchViewModel(value, tag, RequestSelectGeometry);
}

private static Dictionary<string, object> ConvertJObjectToDictionary(JObject jObject)
{
var settings = new JsonSerializerSettings
{
// Add any specific settings you need here
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore,
TypeNameHandling = TypeNameHandling.None
};

var json = jObject.ToString();
var dictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);

return dictionary;
}


private WatchViewModel ProcessThing(double value, ProtoCore.RuntimeCore runtimeCore, string tag, bool showRawData, WatchHandlerCallback callback)
{
return new WatchViewModel(value, tag, RequestSelectGeometry);
Expand Down
12 changes: 12 additions & 0 deletions src/DynamoCoreWpf/ViewModels/Preview/WatchViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,12 @@ private static string GetStringFromObject(object obj)
return ((DateTime)obj).ToString(PreferenceSettings.DefaultDateFormat, CultureInfo.InvariantCulture);
case TypeCode.Object:
return ObjectToLabelString(obj);
case TypeCode.Byte:
return ((byte)obj).ToString(CultureInfo.InvariantCulture);
case TypeCode.UInt32:
return ((uint)obj).ToString(CultureInfo.InvariantCulture);
case TypeCode.UInt64:
return ((ulong)obj).ToString(CultureInfo.InvariantCulture);
default:
return (string)obj;
};
Expand Down Expand Up @@ -313,6 +319,12 @@ private string GetDisplayType(object obj)
return nameof(TypeCode.Object);
case TypeCode.String:
return nameof(TypeCode.String);
case TypeCode.Byte:
return nameof(TypeCode.Byte);
case TypeCode.UInt32:
return nameof(TypeCode.UInt32);
case TypeCode.UInt64:
return nameof(TypeCode.UInt64);
case TypeCode.Empty:
return String.Empty;
default:
Expand Down
78 changes: 78 additions & 0 deletions test/DynamoCoreWpfTests/WatchNodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ protected override void GetLibrariesToPreload(List<string> libraries)
libraries.Add("VMDataBridge.dll");
libraries.Add("ProtoGeometry.dll");
libraries.Add("DesignScriptBuiltin.dll");
libraries.Add("DSCoreNodes.dll");
libraries.Add("FunctionObject.ds");
libraries.Add("FFITarget.dll");
base.GetLibrariesToPreload(libraries);
Expand Down Expand Up @@ -363,6 +364,83 @@ public void WatchMultiReturnNodeOrder()
Assert.AreEqual("1", children[3].NodeLabel);
}

[Test]
public void WatchDictionaryByteValuesDisplaysCorrectly()
{
string openPath = Path.Combine(TestDirectory, @"core\watch\WatchDictionaryByteValuesDisplaysCorrectly.dyn");
ViewModel.OpenCommand.Execute(openPath);
ViewModel.HomeSpace.Run();

var watchNode = ViewModel.Model.CurrentWorkspace.FirstNodeFromWorkspace<Watch>();
var watchVM = ViewModel.WatchHandler.GenerateWatchViewModelForData(
watchNode.CachedValue, watchNode.OutPorts.Select(p => p.Name),
ViewModel.Model.EngineController.LiveRunnerRuntimeCore,
watchNode.AstIdentifierForPreview.Name, true);

var list = watchVM.Children;
Assert.AreEqual(1, list.Count);
var children = list[0].Children;
Assert.AreEqual(12, children.Count);
Assert.AreEqual("Byte", children[0].ValueType);
Assert.AreEqual("72", children[0].NodeLabel);
}

[Test]
public void WatchDictionaryUintValuesDisplaysCorrectly()
{
string openPath = Path.Combine(TestDirectory, @"core\watch\WatchDictionaryUintValuesDisplaysCorrectly.dyn");
ViewModel.OpenCommand.Execute(openPath);
ViewModel.HomeSpace.Run();

var watchNode = ViewModel.Model.CurrentWorkspace.FirstNodeFromWorkspace<Watch>();
var watchVM = ViewModel.WatchHandler.GenerateWatchViewModelForData(
watchNode.CachedValue, watchNode.OutPorts.Select(p => p.Name),
ViewModel.Model.EngineController.LiveRunnerRuntimeCore,
watchNode.AstIdentifierForPreview.Name, true);

var list = watchVM.Children;
Assert.AreEqual(1, list.Count);
var children = list[0].Children;
Assert.AreEqual(5, children.Count);
Assert.AreEqual("UInt32", children[0].ValueType);
Assert.AreEqual("3", children[2].NodeLabel);

var uint64node = ViewModel.Model.CurrentWorkspace.GetDSFunctionNodeFromWorkspace("DummyZeroTouchClass.PreviewUint64Dictionary");
var vm = ViewModel.WatchHandler.GenerateWatchViewModelForData(
uint64node.CachedValue, watchNode.OutPorts.Select(p => p.Name),
ViewModel.Model.EngineController.LiveRunnerRuntimeCore,
watchNode.AstIdentifierForPreview.Name, true);

list = vm.Children;
Assert.AreEqual(1, list.Count);
children = list[0].Children;
Assert.AreEqual(5, children.Count);
Assert.AreEqual("UInt64", children[0].ValueType);
Assert.AreEqual("3", children[2].NodeLabel);
}

[Test]
public void WatchDictionaryJSONValuesDisplaysCorrectly()
{

string openPath = Path.Combine(TestDirectory, @"core\watch\WatchDictionaryJSONValuesDisplaysCorrectly.dyn");
ViewModel.OpenCommand.Execute(openPath);
ViewModel.HomeSpace.Run();

var watchNode = ViewModel.Model.CurrentWorkspace.FirstNodeFromWorkspace<Watch>();
var watchVM = ViewModel.WatchHandler.GenerateWatchViewModelForData(
watchNode.CachedValue, watchNode.OutPorts.Select(p => p.Name),
ViewModel.Model.EngineController.LiveRunnerRuntimeCore,
watchNode.AstIdentifierForPreview.Name, true);

var list = watchVM.Children;
Assert.AreEqual(1, list.Count);
var children = list[0].Children;
Assert.AreEqual(2, children.Count);
Assert.AreEqual("String", children[0].ValueType);
Assert.AreEqual("value1", children[0].NodeLabel);
}

[Test]
public void WatchNestedDictionaryPreviewFromMlutiReturnNode()
{
Expand Down
47 changes: 46 additions & 1 deletion test/Engine/FFITarget/DummyZeroTouchClass.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using Dynamo.Graph.Nodes;
using Dynamo.Graph.Nodes;
using System.Text;
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;

namespace FFITarget
{
Expand All @@ -14,5 +18,46 @@ public int FunctionWithoutDescription(int a)
{
return 0;
}

public static Dictionary<string, object> PreviewByteDictionary()
{
// Example base64 encoded string
string encodedString = "SGVsbG8gV29ybGQh";

byte[] decodedBytes = Convert.FromBase64String(encodedString);
var result = new Dictionary<string, object>();
result.Add("decodedBytes", decodedBytes);

return result;
}

public static Dictionary<string, object> PreviewUint32Dictionary()
{
var uintArray = new uint[] { 1, 2, 3, 4, 5 };
var result = new Dictionary<string, object>();
result.Add("uint32List", uintArray);

return result;
}

public static Dictionary<string, object> PreviewUint64Dictionary()
{
var uintArray = new ulong[] { 1, 2, 3, 4, 5 };
var result = new Dictionary<string, object>();
result.Add("uint64List", uintArray);

return result;
}

public static Dictionary<string, object> PreviewJSONDictionary()
{
var json = new JObject();
json.Add("key1", "value1");
json.Add("key2", "value2");
var result = new Dictionary<string, object>();
result.Add("json", json);

return result;
}
}
}
150 changes: 150 additions & 0 deletions test/core/watch/WatchDictionaryByteValuesDisplaysCorrectly.dyn
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
{
"Uuid": "8a5b5d45-b86d-4b1f-9d04-bec0e4fefd1c",
"IsCustomNode": false,
"Description": "",
"Name": "WatchDictionaryByteValuesDisplaysCorrectly",
"ElementResolver": {
"ResolutionMap": {}
},
"Inputs": [],
"Outputs": [],
"Nodes": [
{
"ConcreteType": "CoreNodeModels.Watch, CoreNodeModels",
"WatchWidth": 200.0,
"WatchHeight": 200.0,
"Id": "e72bcbe3c45044399237b0f63ff261cd",
"NodeType": "ExtensionNode",
"Inputs": [
{
"Id": "3f76a802ad484a90b7a466783baad45a",
"Name": "",
"Description": "Node to show output from",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Outputs": [
{
"Id": "fa2127ef2e864999aae8a00dcc13489f",
"Name": "",
"Description": "Node output",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"Replication": "Disabled",
"Description": "Visualizes a node's output"
},
{
"ConcreteType": "Dynamo.Graph.Nodes.ZeroTouch.DSFunction, DynamoCore",
"Id": "95889388fba84e11bc8905dcf80c10ea",
"NodeType": "FunctionNode",
"Inputs": [],
"Outputs": [
{
"Id": "19ff13f87d5d4ac28736af66e0a80e0b",
"Name": "var[]..[]",
"Description": "var[]..[]",
"UsingDefaultValue": false,
"Level": 2,
"UseLevels": false,
"KeepListStructure": false
}
],
"FunctionSignature": "FFITarget.DummyZeroTouchClass.PreviewByteDictionary",
"Replication": "Auto",
"Description": "DummyZeroTouchClass.PreviewByteDictionary ( ): var[]..[]"
}
],
"Connectors": [
{
"Start": "19ff13f87d5d4ac28736af66e0a80e0b",
"End": "3f76a802ad484a90b7a466783baad45a",
"Id": "35653deefde54d4297e35836f25c4282",
"IsHidden": "False"
}
],
"Dependencies": [],
"NodeLibraryDependencies": [
{
"Name": "FFITarget.dll",
"ReferenceType": "ZeroTouch",
"Nodes": [
"95889388fba84e11bc8905dcf80c10ea"
]
}
],
"EnableLegacyPolyCurveBehavior": true,
"Thumbnail": "",
"GraphDocumentationURL": null,
"ExtensionWorkspaceData": [
{
"ExtensionGuid": "28992e1d-abb9-417f-8b1b-05e053bee670",
"Name": "Properties",
"Version": "3.5",
"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.5.0.6885",
"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": "e72bcbe3c45044399237b0f63ff261cd",
"Name": "Watch",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Excluded": false,
"ShowGeometry": true,
"X": 558.0000000000003,
"Y": 134.0
},
{
"Id": "95889388fba84e11bc8905dcf80c10ea",
"Name": "DummyZeroTouchClass.PreviewByteDictionary",
"IsSetAsInput": false,
"IsSetAsOutput": false,
"Excluded": false,
"ShowGeometry": true,
"X": 78.00000000000006,
"Y": 363.2
}
],
"Annotations": [],
"X": 0.0,
"Y": 0.0,
"Zoom": 1.0
}
}
Loading

0 comments on commit 2636f7f

Please sign in to comment.