Looking for best practices when making Ignition UDTs, Views/Templates, creating tags, etc

Fairly new to ignition, so for a lot of things, I've figured out one of the many ways to achieve what I'm looking to do, but I have no idea if my methods are within the "best practices" or if they're ill-advised. Looking for some clarity, reasoning, and possible alternate methods for the following topics.

Background: I'm creating ignition projects that provide system overviews for conveyor systems. This primarily consists of multiple screens with conveyor layout shown with colored indication for conveyor status (idle, running forward, faulted, etc.), conveyor HOA status (Auto, Manual, Off), photoeye and other sensor status (blocked, clear, jammed, etc.). Conveyors and devices can be clicked to call popups for more detailed information as well as Manual controls of conveyors and devices. We use UDTs in the PLC for most things shown on the HMI (motor udt, photoeye udt, etc.) which contain tags for the statuses, manual control buttons, descriptions, etc. Example below

  1. My first question is in regards to tag creation in ignition. Instead of just browsing my PLC and pulling in the tags that I need and having them organized in folders in the ignition tag browser, I've made a UDT in ignition for each device in the PLC and I just instantiate that with the multi-instance wizard. Each having a parameter for PLCName (for if the system has multiple PLCs) and a parameter for the device's PLC tag name. Then, within the UDT definition, I use these parameters to build an OPC path and append the additional member tag designation and use that as the OPC item path for each piece of data I need to use in ignition. Example below:

My PLC UDTs have nested UDT members as well along with my ignition tags (e.g. I have an operator controls UDT that holds all HOA status and user control tags; my motor and solenoid and other controllable device UDTs have a member of type operator controls UDT.

With these nested UDTs, my tags get pretty large (although all the tags are used at one point or another whether it's on popups or for alarming purposes).

I think the way I'm doing this is fine except I seem to be overloading my OPC connection, which could be for multiple reasons and I'll touch on it more later.

One important note on the UDTs is that my tags for HMI pushbutton input are in a leased tag group that updates at 250ms when leased in order to get a quicker response time for button presses so the HMI doesn't feel laggy. More on this later.

  1. My Templates/views are another spot where I'm not sure if I'm going about things the proper way. My first projects were in vision and now I'm on my first perspective project. I have templates/views for each of my devices so I can just instantiate them (like a global object in FTView). The way I'm currently passing in data (which I believe may be ill-advised) is I created parameters of the UDT type o that device (e.g. photoeye template has parameter of type photoeye UDT). This is for ease of development to be able to drag and drop tags to create devices on the layout screens. I also have some popup views that I have UDT type parameters. I did run into issues doing this on the popup, so I made every popup have an input parameter of type xyzUDT and then have a custom property also of type xyzUDT. All of my UDTs include a member that holds the path to that tag, so the custom property UDT just has an indirect binding to the tag path member of the parameter UDT. Example below:

I'm afraid this passing of a whole UDT as a parameter to my layout device objects might be what is causing my overload. My visible piece of conveyor only really needs the status and HOA status to display properly. All the other tags in the UDT are really only useful once the popup is called. So, I think because every single conveyor and device on screen is leasing every tag in the UDT (most of which don't need leased until their popup is called), it's causing my overload issue, but I'm not sure of the best way to prevent that without a major overhaul to my templates/views.

Here is my current device connection status showing an overload of the 250ms group although I really shouldn't need anything leased that fast unless I'm on a popup or the main page with some buttons (which I'm not currently, I'm only viewing the page shown in my first picture).

Any and all input on any of these topics is greatly appreciated. Like I said, I'm learning a lot about ignition having done 2 vision projects and now my first perspective project, but I still need a lot of help to know when best practices. I don't know a ton about OPC servers, so keys to optimizing the connection would be very helpful. We're looking to put together a standard on our PLC side to allow easy development on the ignition side, so I'm trying not to get too far into that before figuring out the best approach.
Thanks

Hi Chanse,

  1. I recommend using a UDT parameter that is the complete OPC Item path prefix for the target PLC tag instance. If merging from multiple PLC tags into a single Ignition UDT, make two parameters like that.

  2. Under no circumstances should you ever use Ignition UDT-type properties in your user interfaces. Always parameterize your windows/templates/pages/views with tagpath string parameters, then use indirect tag binding within your UI to reference the named members.

  • You show your device performance page. This appears to be an Allen-Bradley. You have some reading to do:

{ Lots of cross links in that topic. Worth reading most of them. }

1 Like

The reason for this, is, as you surmised:

Vision's property model is way too simplistic to track "observed" metrics. If you've got a UDT bound, we're going to poll that whole UDT, at that binding's rate, no matter which members of that are actually used. Hence Phil's advice.

Unfortunately,

There is no such way.

Is there a reason why using one parameter with the whole OPC path (minus the member designation, ".Status.HMI") is any different that separating the PLC name and PLC tag name and building the OPC path. You're appending the ".Status.HMI" or whatever else in the member definition anyways. Below is how currently have it.

To be clear, this is referring to both a view's parameters as well as its custom properties? If this is the case, then I guess my understanding is that I should never really define anything as an Ignition UDT type anywhere other than in the tag explorer when creating a tag? Ignition UDTs and custom properties on things seem like super convenient development features, but this seems to take away most of that convenience, but I guess if performance suffers otherwise, there's nothing you can do.

I've read through that and a lot of your other posts on the matter, though a lot of it does go over my head a bit. First, I'm not referencing any AOI-defined UDTs from the PLC. Everything is a separate device-specific UDT used by each AOI, so I should be good there. It seems like I need to keep everything at the same tag rate if they're in the same UDT as the slower tags(which they are). If I need faster response from my buttons, I'll look into separating the User control buttons and statuses into their own separate UDT used by my AOIs in the PLC. To be clear in this topic, if any part of a PLC tag is accessed by the OPC UA server, it pulls the entire UDT tag (the entire highest level base tag), right? It doesn't ever just get the tag data from members of a tag (even if the members themselves are nested UDTs)?

It seems like my fixes are fairly straightforward, but will require some redevelopment and will make ignition not quite as easy to develop (since now I'll have a lot more basic properties and indirect tag bindings). Please let me know if I'm missing something. I appreciate all your help!

I had run into some other issues with using UDT-type parameters in templates and views, but I found workarounds by putting the tag path into a member of my UDTs and using that for an indirect binding where needed to alleviate some problems. I wish I had known earlier that this was actually also causing connection overload. It's good to actually get more of a straight answer why this is detrimental practice as opposed to just inconvenient.

Thanks for the input!

It reduces proliferation of UDT parameters, and leave open the possibility of using OPC client connections instead of IA drivers with the same data type.

Correct.

Good intentions, terrible side effects. Just don't do it.

Not quite:

  • The IA driver applies some heuristics as to how much of the UDT is being accessed before switching to read the whole UDT.
  • My alternate driver reads the whole UDT if two or more members are accessed, and the whole UDT fits in the connection buffer.
  • If any member of a UDT (or AOI) is marked "no external access", then the entire UDT cannot be read as a block, and all members must be accessed individually.
  • A nested UDT that is accessed by itself will be directly targeted, not the unused outer tag elements.

Before I get too deep into rebuilding my views, could you clarify this for me? In every Ignition UDT I have made, I have a member called _TagPath which is just bound to the built in parameter PathToParentFolder for each tag.

This has come in handy in certain situations. Now, if I'm changing my view parameters to be a string tag address instead of an Ignition UDT, can I bind that string parameter to this single member of each tag to get that string? Like so:

Or will doing even that binding to just that sting member of the UDT cause the entire UDT to be leased?

Here is the new parameter within my view with the custom properties now indirectly bound to just the status tags I need for animation using that tag address.

I think I'm on the right track, but just looking for some confirmation. I guess within my conveyor view, I'm referencing UDT members anyways, so if doing that does make the whole tag leased, then I'm leasing the whole thing regardless of how I get the address to the view parameter.

Thanks!

In Vision, if you make a custom property of type SomeUDT, then when you bind that to some tag to bring in a real value, you're subscribing to all members of SomeUDTInstance.

Perspective works fundamentally differently (in part to remove this footgun).

If you are directly subscribing to individual members of a UDT, then you're just dealing with the individual member tags. The only complications are when you use the somewhat exotic "virtual" props available on UDTs, such as reading/binding to Path/To/Some/UDT.jsonValues, which will implicitly read all the inner members at whatever the binding rate is, but unless you're doing that, you have nothing to worry about.

This is actually showing an overload on the 1000ms group as well. Anything above 0 means it's overloaded.

In perspective, you can still link your template views to your UDTs by using the dropConfig.udts[x].params set to path
This will copy the UDT instance's tag path into the param you specify, thus giving you the dev convenience without the client penalty of UDT Type params