We use these a lot in the oil and gas industry (majority is continuous process with PID loops), and my recommendation is to only read the necessary values at the faster rate and everything else leased at a slower rate. I try to have any tags that are displayed on main screens/windows or historized as direct at 1000ms, and everything else that’s on a faceplate at like 60,000ms with a lease rate of 2000-5000ms for settings/config/tuning parameters. We have 240 in the current project I’m working on, and we have 0-2% overload with a total of 53k tags on the PLC overall.
This is a continuous process and the PID_ENHANCED works very well for their process, so migrating to the older PID ladder instruction is more likely than not a non-starter.
I may just create a UDT that Ignition reads and writes to, and then wrap that around the PIDE instruction. It just becomes a pain when you want to tune the PID you have to make sure you are putting tuning constants into the outer UDT rather than the instruction.
We are esentially only pulling the current status, current sp, current cv, and writing to the SP, P/I/D and like 2 configuration values. So a UDT as a go between isn't a totally bad thing.
Consider making a UDT that exactly mimics the real structure of that data type, but completely readable, and use CPS to copy from the real PID to the duplicate. The duplicate will be optimizable. Use the divert write tag actors in my toolkit to cause relevant writes to the Ignition tags to be re-routed to the actual OPC Item paths.
We use a wrapper that maps the PIDE to a UDT.
Edit: What Phil said
Does your UDT map every data point in the instruction? Or just the ones that you have on the popup?
There is quite a bit of data in those instructions, and our typical use is only a handful.
Actually it’s more correct to describe it the other way around; we map our UDT into the PIDE.
It’s designed to be algorithm neutral, you can use it with whatever control strategy you want (PIDE, custom PI controller, unique cross limiting logic, etc.) as long as you can defined a way to map the PV, SP, CV, and gains into the algorithm. We also handle SP/CV ramping/clamping and the CommandSource logic ourselves.
This allows use to use a standard faceplate for many different control strategies.
Similar metrics with same rules on another project of mine, accessing PID_ENHANCED tags directly.
Not sure how you could do this as the members of the PIDE would need to be bidirectional. Some values like SP would need to be copied into PIDE. Other values like CV, error, etc would need to be copied out of PIDE.
For me, I just use a UDT and map in/out of the PIDE like any other IO mapping.
What Phil’s module can do is allow you to do your COP/CPS of the PIDE data into a UDT in the PLC, then read all that data efficiently. Then the tag actor setting can redirect writes so that they go direct to the PIDE tag rather than the UDT.
In the ladder, use CPS to copy the entire structure, one way only. Subscribe to the duplicate in Ignition. Write directly to the original PIDE data, either with system.opc.write*()
, or by:
This is one of the use-cases that inspired the creation of that tag actor.
Traditionally we just have an Ignition UDT that reads the data that we need out of the PID_ENHANCED.
If I have up to 100 of these instructions, what is the recommendation for getting data into and out of these instructions efficiently?
Sounds like you already have your answer? Why can’t you do what you traditionally do?
Well, we have an Ignition UDT that reads directly into the PID_ENHANCED block, and we traditionally only have 2 or 3 in a project. With this project having substantially more than 2 or 3, I wanted to get opinions on a more efficient way to read/write into the PID_ENHANCED block.
Yeah, it's not efficient, but in our case, it didn't impact overall comms loading, so we stuck with reading them directly. I think because we have everything else reading efficiently, it didn't make a big difference overall, and I prefer to leave the native stuff alone if I can, otherwise someone (usually a local tech) will come in and add a PID loop or something afterward and won't be able to get it talking because they missed the wrapper function or COP instruction that's mapping that data over to a custom UDT. I like to keep things simple where I can because I know I'm not the only one that will ever work on it.
I misunderstood.
I guess it depends a lot on how much and how often you’re reading from the PIDE data blocks. The main reason reading directly from an instruction like that is generally not a good idea is because it blocks the Ignition driver from efficiently reading the data. Each element of each PIDE will be its’ own read operation. While normal UDTs with all of their members exposed with read/write access can be read with a single read operation. If you’re reading, say 90% of a block all the time and you need to read it fast, it’s going to present more of a problem than if you’re only calling up most of that data in a configuration view and you have it in a leased tag group. What’s really going to make the difference is how many of those PIDE members are going to need to be visible on a screen at the same time.
In my experience, the numbers you’re most interested in are the Process Variable, the Setpoint, and the Output. Usually these tags are separate from the PIDE tags anyway. So in that case, I’d set up Ignition to read those external tags instead of the actual PIDE members. The rest of it, the gains, deadband, etc., those aren’t typically values you put front-and-center. So for those, you can theoretically put them in a leased tag group where they’re read very slowly when no bindings are active, and then read more quickly when they are. Keep in mind though, leased tags aren’t a magic bullet and carry their own set of problems. Switching scan rates can take up to 8 seconds, and if you put all of these tags in a single device connection, you could see huge delays in responsiveness when you pull up the relevant view. That may actually be fine if they’re just settings the user changes. You can perhaps set the tag group to read after write, so that any changed values will be read back immediately. Again, there are pitfalls involved with that method, too, so be careful.
There's a few ways to tackle it. Honestly, the simplest way may be to create another AOI just for bridging Ignition and the PIDE instruction. You would create a UDT with only what you need in your Ignition project. You then create a parameterized subroutine (which would be my preference since it’d be more memory-efficient. You wouldn’t need a set of internal tags for every single instance), or an AOI that reads and writes data between an instance of that UDT and the PIDE elements themselves. Then you set up Ignition to only read and write with the UDT you created. You gain all the comms efficiency that way.