Hi,
I have a CompactLogix PLC with an appropriate tag data structure for my Ignition project. I make extensive use of a custom data type in the PLC the reflects a sensor (members are things like scaled value, description, units, raw mA, etc).
Until now I have just pulled the PLC sensor tags (including their members) across from the PLC device by browsing the device. However, I’d like to up my game a little and use a UDT in Ignition to mimic the PLC data structure and define all the members. This is so I only have to make an instance of the sensor itself, and should I decide I want to use another member of the PLC data structure I can just update the UDT.
I understand there may be better management strategies than replicating PLC structures but for the most part this is working for my project and I am fairly invested in this approach this time around.
My sensor type tag instances in the PLC are often nested in wider data structures, sometime they are primary members of a PLC data structure and other times they are nested deeper. When creating an instance of my UDT in Ignition I place it within nested folders that match the PLC’s sensor instance nesting by name and structure. In this way I can use builtin parameters such as PathToParent or PathToTag within my UDT’s member tags to fully describe the (top-down) OPC path to the corresponding tags in the PLC. Because my Sensor UDT instance may be nested several several folder levels, I need to bind either the PathToParent or PathToTag parameter into my UDT members’ opcItemPath to make sure I capture the full top-down PLC path based on the any instance’s nested folder location.
This all works fine, to a point. Wherever I place my UDT instance the opcItemPath of any member tag is correctly defined, whatever the folder path above the instance, the PathToParent bindings inform the member tag of the wider opcItemPath to navigate. The problem I haven’t resolved is that the PathToParent parameter deliniates folder nesting with a forward slash (/) but my PLC (CompactLogix) requires the opcItemPath to deliniate structural nesting by periods (.)
I have tried custom properties, parameters and expression tags within my UDT to nurse the forward slashes returned by PathToParent into periods but I cannot seem to find a combination that allows me to use the PathToParent parameter along with an expression function (such as replace({PathToParentFolder},“/”,“.”) ) and bind this in my opcItemPath. I can do all of these steps in isolation (and combine some) but haven’t found the correct method to effectively get all of them to work together.
I must be missing something simple, I’d be grateful if someone could point out that “something”.
I think what you’re missing can be forgiven because it’s not necessarily obvious, but the built in PathToParent and PathToTag just aren’t that useful for building OPC Item Paths. You should parameterize your UDT in such a manner that you can build the OPC Item Path without them.
They’re useful for other things, like building expressions or other things that reference other Ignition tags, but not necessarily for OPC Item Paths.
Using a UDT is a good idea. Mimic’ing the PLC is not. Your Ignition UDT should include all of the non-OPC tags that are meaningful architecturally for the UI, whatever the placement in the PLC.
Reconsider before you get too deep. Ignition doesn’t make its tag structure replicate PLC structures for good reasons (including that not many drivers are that structured anyways).
If you feel the need to use different poll rates, it is particularly important to break the 1:1 correspondence. The elements of PLC UDTs should all be subscribed at the same rate to optimize well. And doing so with arrays to minimize the top level of tags also helps. Your Ignition UDTs can combine elements from multiple PLC hierarchies at whatever varieties of comms needed while presenting a unified “folder” to your views/pages/windows/templates.
Given my UDT definition won’t know at design time what the path above its instances will be, is there a way to parameterize the UDT so that it can discover that parent path (so as to correctly populate the opcItemPath) without using PathToParent?
Lets say I have a UDT called Sensor with a member OPC tags called CalibratedValue and v. Then I make two instances of it (instances in bold):
Compressor1/SuctionPressure
In this instance my member tags need to resolve their opcItemPath to something like: ns=1;s=[myCompactLogix]Compressor1.SuctionPressure.CalibratedValue
and ns=1;s=[myCompactLogix]Compressor1.SuctionPressure.Setpoint
ColdStore/Freezer1/Evaporator2/ReturnTemperature
In this deeper nested instance the UDT member tags need to resolve their opcItemPath to : ns=1;s=[myCompactLogix]ColdStore.Freezer1.Evaporator2.ReturnTemperature.CalibratedValue
and ns=1;s=[myCompactLogix]ColdStore.Freezer1.Evaporator2.ReturnTemperature.Setpoint
I can get ns=1;s=[myCompactLogix]ColdStore/Freezer1/Evaporator2/ReturnTemperature/CalibratedValue
by binding PathToParent in my opcItemPath (at UDT design time) but this is not understood by the CompactLogix device. Using an expression function in opcItemPath to replace ? with . would fix this but the expression function is not executed, it seems to be interpreted as literal text.
If PathToParent is not intended for opcItemPaths can you suggest another method to discover the path above a UDT instance without having to explicitly describe that path in an alternative notation, instance by instance. I really want to inherit the path directly from the UDT instance’s nested location.
Thanks @pturmel, I think as I go deeper with future Ignition projects I will try to embrace the best practices you have outlined. However, I am in deep on this one.
So sticking with the PLC structure, I am now attempting to use UDTs (and nested UDTs) so as to have the UDT structure fully define the PLC tag structure (and opcItemPath) all the way to the root level of the PLC tags.
Some success here with one level of nesting. Consider the UDT instance Compressor1 below.
Using the opcItemPath binding:
ns=1;s=[AMBDplc]{RootInstanceName}.{InstanceName}.Units
this correctly resolves to
ns=1;s=[AMBDplc]Compressor1.SuctionPressure.Units
when the Compressor UDT definition instantiates the plcParameter as a member tag named SuctionPressure and I create an instance on Compressor as Compressor1.
However, I would need at least two levels of nesting to complete this project.
Changing the opcItemPath of Units (within the plcParameter UDT) to:
ns=1;s=[AMBDplc]{RootInstanceName}.{ParentInstanceName}.{InstanceName}.Units
and nesting Compressor within a higher level UDT (EngineRoom) I was hoping to resolve
ns=1;s=[AMBDplc]EngineRoom.Compressor1.SuctionPressure.Units
but instead got:
ns=1;s=[AMBDplc]EngineRoom.SuctionPressure.SuctionPressure.Units
I had assumed that {ParentInstanceName} would return the name of the instance that an instance belongs to, however, it is returning the name of the instance itself, the same result as {InstanceName}
Units is a member tag of the UDT plcParameter (instatiated as SuctionPressure) in Compressor1 which is instantiated in EngineRoom
For Units {InstanceName} resolves correctly to SuctionPressure,
{ParentInstanceName} resolves again to SuctionPressure (I was hoping Compressor1 to be the resolved parent instance here)
and then {RootInstanceName} leapfrogs the unresolved Compressor1 and correctly resolves to EngineRoom.
Is {ParentInstanceName} behaving as expected? I would have thought this would reach one level up from {InstanceName} so that multiple nestings of UDTs could discover their primary antecedent.
Thanks Justin, I got there in the end with nested UDTs each layer having an Instance_x parameter (bound as .{Instance name}) and Parent_x parameter (bound as {Parent_y}{Instance_y}) where x=0 for the lowest UDT (my plcParameter structure) and y=x+1. x and y increment for each UDT as you climb up to the top or root UDT. The top level UDT drops the period before {Instance name} and it’s Parent_x binding becomes the name of the device.
Down in the plcParameter structure, opcItemPaths are bound as ns=1;s={Parent_0}{Instance_0}.{Tagname}
UDT structure and UDT parameter bindings are thus:
Coldroom (Instance_2={InstanceName}, Parent_2=[AMBDplc] has child
Evaporator1 (Instance_1=.{InstanceName}, Parent_1={Parent_2}{Instance_2} has child
SupplyTemperature (Instance_0=.{InstanceName}, Parent_0={Parent_1}{Instance_1} has tags
Units (opcItemPath=ns=1;s={Parent_0}{Instance_0}.{Tagname})
Calibrated Value (opcItemPath=ns=1;s={Parent_0}{Instance_0}.{Tagname})
Setpoint (opcItemPath=ns=1;s={Parent_0}{Instance_0}.{Tagname} etc
UDT bindings cascade and concatenate such that the UDT instances and their nesting build the final opcItemPath string in the UDTs OPC tags.
Perhaps not pretty or best practice but can save some time if the PLC tag structure already reflects the structure of nested embedded view templates (and the parameters that cascade through them) that you intend to use.
This lends itself to further parent levels _3, _4, _5, etc. I have used explicit subscripts as I understand that parameter bindings to a parameter of a parent object (with the same parameter name) defaults to the instance parameter, not the parent.
I’ll try to follow better practices (once I understand them) the next time.
I'm with you, I'm having the same result and setting up some intermediary parameters to take care of it, but I agree with your assumption, I'd love if someone could chime in as to why this is not the case..