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);