Get UDT dropConfig References via Script

Is there a way to provide a UDT Type path, and get the relevant dropConfig references to it via script?

I know this is technically possible via some java methods I am sure, but not 100% where to start to find it.

Essentially we need to version some UDT definitions and corresponding views, and we are aiming to not have to store custom props on the UDT definitions that refer to a hardcoded view name. Feels a bit clunky.

1 Like

I'm thinking this might be a good place to start:

1 Like

I looked there, but I think that is how you create a new tag drop config and not retrieve one? I tried playing with that for a while but I couldn't find any way to retrieve it based off UDT types

I think you need to obtain an instance of TagDropConfig, using something like this (slightly different in 8.1, this is 8.3):

        TagDropConfig dropConfig =
            Optional.ofNullable(designerContext.getProject())
                .flatMap(p -> p.getSingletonResource(TagDropConfig.RESOURCE_TYPE))
                .flatMap(Resource::getData)
                .filter(ImmutableBytes::hasBytes)
                .map(data -> GSON.fromJson(data.getBytesAsReader(), TagDropConfig.class))
                .orElse(TagDropConfig.createDefault());

And then ask it for the thing you want via com.inductiveautomation.perspective.common.config.tagdrop.TagDropConfig#findComponentsFor?

Disclaimer: I have never really understood how to set up tag drop in Perspective :laughing:

1 Like

One other thing to keep in mind is that because tags are gateway resources and views are project resources, your results could be different based on the project you reference. If you're wanting to store this as part of the UDT, you'll need to make sure it also references the associated project. could store it as a string formatted similar to OPC tag paths [Project]Folder/View

Okay, I got some progress here, but am struggling to build the NodeBrowseInfo and NodeDescription that the findComponentsFor method needs.

from com.inductiveautomation.ignition.common.tags.config.types import TagObjectType
from com.inductiveautomation.ignition.common.tags.browsing import NodeBrowseInfo, NodeDescription
from com.inductiveautomation.ignition.common.tags.paths import BasicTagPath

tag_path_str = "[default]_types_/MyFolder/MyUdtDefinition"
tag_path = BasicTagPath(tag_path_str)
node_desc = (NodeDescription.newBuilder()
    .fullPath(tag_path)
    .objectType(TagObjectType.UdtType)
    .build())
node_browse_info = NodeBrowseInfo(tag_path, node_desc)

Both of these give empty tag descriptions when printed, and therefore dont return anything in the list. Any ideas?

>>> node_desc
TagDescription [name=null, dataType=null, currentValue=null, objectType=UdtType, subTypeId=null, attributes=null, hasChildren=false, displayFormat=null, tooltip=null]
>>> node_browse_info
TagDescription [name=null, dataType=null, currentValue=null, objectType=UdtType, subTypeId=null, attributes=null, hasChildren=false, displayFormat=null, tooltip=null]

I also tried with an instance's path and TagObjectType.UdtInstance. Still no luck

For completeness, here is the whole script so far
from com.inductiveautomation.perspective.common.config.tagdrop import TagDropConfig
from com.inductiveautomation.ignition.common import TypeUtilities
from com.inductiveautomation.ignition.designer import IgnitionDesigner
from com.inductiveautomation.ignition.common.tags.browsing import NodeBrowseInfo, NodeDescription
from com.inductiveautomation.ignition.common.tags.paths import BasicTagPath
from com.inductiveautomation.ignition.common.tags.config.types import TagObjectType
from java.util import Optional
from org.python.core import PyString
from java.util.function import Function, Predicate

# Helper for Java Function
class PyFunction(Function):
    def __init__(self, func):
        self.func = func
    def apply(self, arg):
        return self.func(arg)

# Helper for Java Predicate
class PyPredicate(Predicate):
    def __init__(self, func):
        self.func = func
    def test(self, arg):
        return self.func(arg)

designerContext = IgnitionDesigner.getFrame().context
dropConfig = (Optional.ofNullable(designerContext.getProject())
    .flatMap(PyFunction(lambda p: p.getSingletonResource(TagDropConfig.RESOURCE_TYPE)))
    .flatMap(PyFunction(lambda r: r.getData()))
    .filter(PyPredicate(lambda data: data.hasBytes()))
    .map(PyFunction(lambda data: TypeUtilities.gsonToPy(TypeUtilities.pyToGson(PyString(data.getBytesAsReader().readString())))))
    .map(PyFunction(lambda pyObj: TagDropConfig.fromJson(pyObj.toString())))
    .orElse(TagDropConfig.createDefault()))

tag_path_str = "[default]_types_/MyFolder/MyUdtDefinition"
tag_path = BasicTagPath(tag_path_str)
node_desc = (NodeDescription.newBuilder()
    .fullPath(tag_path)
    .objectType(TagObjectType.UdtType)
    .build())
node_browse_info = NodeBrowseInfo(tag_path, node_desc)

components = dropConfig.findComponentsFor(node_browse_info)

print components # returns []

You might have a better time calling the underlying browseAsync method yourself to get a NodeDescription back:
com.inductiveautomation.ignition.common.tags.model.TagManager#browseAsync(com.inductiveautomation.ignition.common.tags.model.TagPath, com.inductiveautomation.ignition.common.browsing.BrowseFilter) returns a CompletableFuture<Results<NodeDescription>>.