Creating Complex UDT Definitions with DataTypes with Java SDK

Hi, I am looking to automatically create UDT Definitions via the Java SDK in Ignition 8.3 in a ManagedTagProvider. (io.ia.sdk.modl version 0.4.0 using a GatewayHook)

I instantiate my ManagedTagProvider with the TagProviderMeta.FLAG_PROVIDES_COMPLEX_TYPES flag set to true, and instantiate my UDT

Definition with the following:

        provider = manager.getOrCreateManagedProvider(getProviderConfig());

        BasicTagConfiguration model = new BasicTagConfiguration();
        model.setName("MyUDT"); //setName always seems superfluous as configureTag sets name based on path
        model.setType(TagObjectType.UdtType);

        BasicTagConfiguration subFolder = new BasicTagConfiguration();
        subFolder.setName("SubFolder");
        subFolder.setType(TagObjectType.Folder);

        BasicTagConfiguration subTag1 = new BasicTagConfiguration();
        subTag1.setName("SubTag1");
        subTag1.setType(TagObjectType.AtomicTag);
        subTag1.set(WellKnownTagProps.DataType, DataType.Float4);
        subTag1.set(WellKnownTagProps.TagGroup, "Default");

        BasicTagConfiguration subTag2 = new BasicTagConfiguration();
        subTag2.setName("SubTag2");
        subTag2.setType(TagObjectType.AtomicTag);
        subTag2.set(WellKnownTagProps.DataType, DataType.Float8);
        subTag2.set(WellKnownTagProps.TagGroup, "Default");

        model.addChild(subFolder);
        subFolder.addChild(subTag1); //add child seems superfluous, based on path

        provider.configureTag("MyUDT", model);
        provider.configureTag("_types_/MyUDT/SubFolder", subFolder);
        provider.configureTag("_types_/MyUDT/SubFolder/SubTag1", subTag1);
        provider.configureTag("_types_/MyUDT/SubFolder/SubTag2", subTag2);

It does successfully create the hierarchy as seen below:


However the DataType field is missing from the tag properties


But it is populated into a custom prg property.

I have also tried various other methods of instantiating tags, notably:
TagConfigurationBuilder, TagConfigurationModel, UdtConfigDefinition
which all produced similar results.

Any help or guidance on the correct process would be greatly appreciated. Thank you!

To clarify, I have also set hasDataTypes to truein my ManagedTagProviderConfigurationand the issue persists.

This is somewhat of a perennial topic on this forum, so I'm hijacking your thread in the hopes that I can save future travelers some of their time.

Configuring ManagedTagProvider for UDT support

Ensure that our ManagedTagProviderConfiguration is configured with (at least) the following properties.

var mtpConfig = 
    new ManagedTagProviderConfiguration
        .Builder("mtp")                  // name of our provider
        .hasDataTypes(true)              // enables FLAG_PROVIDES_COMPLEX_TYPES
        .build();

var tagManager = gatewayContext.getTagManager();
ManagedTagProvider mtp = tagManager.getOrCreateManagedProvider(mtpConfig)

Declaring UDT members

This step is concerned exclusively with the declaration of UDT members. Do not attempt to declare relationships between members until all members are instantiated. This way, there is no need to worry about the sequencing of declaration / instantiation.

// configuration for a UDT definition tag MyUdt
var myUdtPath = TagPathParser.parse("[mtp]_types_/MyUdt");
var myUdtConfig = BasicTagConfiguration.createNew(myUdtPath);
myUdtConfig.setType(TagObjectType.UdtType);

// configuration for a memory Float4 tag at MyUdt/SubTag1
var subTag1Path = TagPathParser.parse("[mtp]_types_/MyUdt/SubTag1");
var subTag1Config = BasicTagConfiguration.createNew(subTag1Path);
subTag1Config.setType(TagObjectType.AtomicTag);
subTag1Config.set(WellKnownTagProps.DataType, DataType.Float4);
subTag1Config.set(WellKnownTagProps.TagGroup, "Default");
subTag1Config.set(WellKnownTagProps.ValueSource, WellKnownTagProps.MEMORY_TAG_TYPE);
subTag1Config.set(WellKnownTagProps.Value, BasicQualifiedValue.fromObject(0)); // default value

// configuration for a Folder tag at MyUdt/SubFolder
var subFolderPath = TagPathParser.parse("[mtp]_types_/MyUdt/SubFolder");
var subFolderConfig = BasicTagConfiguration.createNew(subFolderPath);
subFolderConfig.setType(TagObjectType.Folder);

// configuration for an OPC Int4 tag at MyUdt/SubFolder/SubTag2
var subTag2Path = TagPathParser.parse("[mtp]_types_/MyUdt/SubFolder/SubTag2");
var subTag2Config = BasicTagConfiguration.createNew(subTag2Path);
subTag2Config.setType(TagObjectType.AtomicTag);
subTag2Config.set(WellKnownTagProps.DataType, DataType.String);
subTag2Config.set(WellKnownTagProps.TagGroup, "Default");
subTag2Config.set(WellKnownTagProps.ValueSource, TagManager.DATA_SOURCE_OPC);
subTag2Config.set(WellKnownTagProps.Value, BasicQualifiedValue.fromObject(0)); // default value
subTag2Config.set(TagProp.OPCServer, "Ignition OPC UA Server");
subTag2Config.set(TagProp.OPCItemPath, "i=2259"); // i=2259, gateway status code

Guidance & Notes

  • Use the BasicTagConfiguration.createNew() function in place of the BasicTagConfiguration() constructor.
  • Ensure that all atomic tags declare each of the following property types
    • WellKnownTagProps.DataType
    • WellKnownTagProps.TagGroup
    • WellKnownTagProps.ValueSource
    • WellKnownTagProps.Value (if you want to establish default values)
  • I have had extensive issues with TagConfigurationBuilder, and thus I do not recommend using it at all. This was not for a lack of effort, as it is sorely missed. I am highly invested in seeing this changed, so I will continue to investigate and share my findings in a follow-up post.
  • @rhys.nickerson's observations about the setName and addChild functions are correct - they're just helper functions which manipulate the caller's internal TagPath member. They don't perform linking or have any side-effects. Use these with caution, as they will make "invisible" changes to your tag paths which can be difficult to keep track of.

Linking, saving, and the trouble with configureTag()

The managed-tag-provider sample demonstrates the use of ManagedTagProvider::configureTag() for transforming TagConfiguration objects into actual tags. This works flawlessly when restricted to atomic tags, but becomes troublesome once UDTs are introduced.

configureTag()'s internal handling of UDT definitions/instances assumes that the UDT's tag hierarchy is explicitly declared through the use of the EncodingProps.Tags ("tags") tag property. Specifically, it is assumed that any tag with children provides a List<TagPath> object with entries for all immediate children via the "tags" tag property. If this sounds familiar, it's because this is how JSON tag imports/exports are structured. The internals behind this process borrow a significant amount of code from that subsystem, and as such, the most SDK-compliant way to perform this operation is to modify the structure of our configuration data to match.

By hand-populating the "tags" properties for any tags with children, we are able to provide configureTag() and its callees with the information they are expecting. From there, we can load a full UDT definition, including all of its descendant tags, by passing just the UDT definition's TagConfiguration object into configureTag(). The descendant tags will be aggregated during the execution of configureTag() and initialized in the gateway.

Here's what that looks like for the previously declared components:

// populate "tags" property for tags with children
subFolderConfig.set(EncodingProps.Tags, List.of(subTag2Config));
myUdtConfig.set(EncodingProps.Tags, List.of(subFolderConfig, subTag1Config));
mtp.configureTag("[mtp]_types_/MyUdt", myUdtConfig);

Creating UDT instances

Once the UDT definitions are squared away, creating UDT instances is very straightforward.

// declare and bootstrap an instance of MyUdt called MyUdtInstance
TagPath myUdtInstancePath = TagPathParser.parse("[mtp]MyUdtInstance");
TagConfiguration myUdtInstanceConfig = BasicTagConfiguration.createNew(myUdtInstancePath);
myUdtInstanceConfig.setType(TagObjectType.UdtInstance);
myUdtInstanceConfig.set(WellKnownTagProps.TypeId, "MyUdt");
mtp.configureTag("[mtp]MyUdtInstance", myUdtInstanceConfig);

Complete, working example (as of Ignition 8.3.2)

var mtpConfig = 
   new ManagedTagProviderConfiguration
       .Builder("mtp")                  // name of our provider
       .hasDataTypes(true)              // enables FLAG_PROVIDES_COMPLEX_TYPES
       .build();

var tagManager = gatewayContext.getTagManager();
ManagedTagProvider mtp = tagManager.getOrCreateManagedProvider(mtpConfig)

// configuration for a UDT definition tag MyUdt
var myUdtPath = TagPathParser.parse("[mtp]_types_/MyUdt");
var myUdtConfig = BasicTagConfiguration.createNew(myUdtPath);
myUdtConfig.setType(TagObjectType.UdtType);

// configuration for a memory Float4 tag at MyUdt/SubTag1
var subTag1Path = TagPathParser.parse("[mtp]_types_/MyUdt/SubTag1");
var subTag1Config = BasicTagConfiguration.createNew(subTag1Path);
subTag1Config.setType(TagObjectType.AtomicTag);
subTag1Config.set(WellKnownTagProps.DataType, DataType.Float4);
subTag1Config.set(WellKnownTagProps.TagGroup, "Default");
subTag1Config.set(WellKnownTagProps.ValueSource, WellKnownTagProps.MEMORY_TAG_TYPE);
subTag1Config.set(WellKnownTagProps.Value, BasicQualifiedValue.fromObject(0)); // default value

// configuration for a Folder tag at MyUdt/SubFolder
var subFolderPath = TagPathParser.parse("[mtp]_types_/MyUdt/SubFolder");
var subFolderConfig = BasicTagConfiguration.createNew(subFolderPath);
subFolderConfig.setType(TagObjectType.Folder);

// configuration for an OPC Int4 tag at MyUdt/SubFolder/SubTag2
var subTag2Path = TagPathParser.parse("[mtp]_types_/MyUdt/SubFolder/SubTag2");
var subTag2Config = BasicTagConfiguration.createNew(subTag2Path);
subTag2Config.setType(TagObjectType.AtomicTag);
subTag2Config.set(WellKnownTagProps.DataType, DataType.String);
subTag2Config.set(WellKnownTagProps.TagGroup, "Default");
subTag2Config.set(WellKnownTagProps.ValueSource, TagManager.DATA_SOURCE_OPC);
subTag2Config.set(TagProp.OPCServer, "Ignition OPC UA Server");
subTag2Config.set(TagProp.OPCItemPath, "i=2259"); // i=2259, gateway status code

// populate "tags" property for tags with children
subFolderConfig.set(EncodingProps.Tags, List.of(subTag1Config));
myUdtConfig.set(EncodingProps.Tags, List.of(subFolderConfig, subTag2Config));
mtp.configureTag("[mtp]_types_/MyUdt", myUdtConfig);

// declare and bootstrap an instance of MyUdt called MyUdtInstance
TagPath myUdtInstancePath = TagPathParser.parse("[mtp]MyUdtInstance");
TagConfiguration myUdtInstanceConfig = BasicTagConfiguration.createNew(myUdtInstancePath);
myUdtInstanceConfig.setType(TagObjectType.UdtInstance);
myUdtInstanceConfig.set(WellKnownTagProps.TypeId, "MyUdt");
mtp.configureTag("[mtp]MyUdtInstance", myUdtInstanceConfig);
2 Likes

Fantastic write-up, thank you so much.

I’m reading this and wanting to know more about your MTP module…