I understand that part. It's just that making two tag libraries is a lot of work, especially since they're both going to be 99% identical except for one variable (the OPC path). That means, every time my customer adds something, they need to make every tag twice. They need to set up all the alarming, history, notification pipes, etc. twice. There's also the issue of increased human error. Now there's the possibility of changing a standard alarm level in one set of tags but not the other.
I just wish there was an efficient way to either inject, store, switch between, etc. JUST the OPC path of a tag and keep every other setting the same.
I'm not sure how to "inherits from the above". I know in UDT instances I can set a Parent tag, but if I'm just doing a regular OPC tag, there's no option to do that that I can see.
The only, possibly "hacky" way that I can think of is to use a Gateway Tag Change Event and script it, the problem would be that depending on the number of tags you have this script could potentially be very long running, and very difficult to manage over time.
The tag change event would look like this:
if not initialChange:
yourScriptLibrary.setOPCItemPaths(newValue.value)
def setOPCItemPaths(deviceName)
#browse to get all tags in the provider that have an OPC Value Source
results = system.tag.browse("[default]",{"valueSource":"opc","recursive":True}).results
tagPaths = [str(result['fullPath']) for result in results]
opcItemPaths = [opcItems[deviceName][tagPath] for tagPath in tagPaths]
tagPaths = [tagPath + 'opcItemPath' for tagPath in tagPaths]
system.tag.writeBlocking(tagPaths,opcItemPaths)
The setOPCItemPaths() function would live in the project library script, in that library there would be a top level dictionary opcItems. It would have two member dictionaries keyed by deviceName. Those dictionaries would have the Ignition Tag Path as the key and the Opc Item Path as the value.
The draw back here is, you have to remember to go and update your dictionary with the proper tag path and opc item paths for both devices anytime you create/edit a tag. Not a great solution but it will meet your criteria, and for a small system might be manageable. All that would be needed in this case to update all of the tags would be to change the trigger tag from one device name to the other (so long as it matches what is in the dictionary).
It sounds like this is a "pick your poison" kind of situation. I think what I'm going to do is just on my displays, point everything to the "PLC Tags" folder. In my master project, I'll have an "AB Tags" and "Schneider Tags" folder, where all the SCADA tags are identical except for the OPC addresses, and then whatever type of PLC an individual project might have, delete the folder they aren't using, and rename the other one to "PLC Tags."
I think no matter what, they're going to have to maintain a dual SOMETHING in the project, whether it be the tag databases or a "hacky" script. They still get the advantages of standardizes tag structures via Ignition UDTs. But if they're using Schneider, they'll need to go through and fill in the Modbus addresses, which they'd have to do at some point, anyway.
Now I wonder if I can have some sort of editable XML or JSON import file where they can make changes in that and just import it. It would take some work, but I bet a utility could be made where it keeps a database of all the tag info (alarms, history, etc.). Maybe an excel spreadsheet with "AB Address" and "Schneider Address" columns, and then some code to massage all that info to spit out ignition tag import files for one or the other PLC.
I guess I'm not following. It sounds like you're saying I should create three UDTs for every single UDT I need--a "Master" and then a version for Schneider and a version for Rockwell--that reference the Master for all the configuration except for the OPC path?
I have follow-up questions/issues if that's what you're saying I should do, but I want to make sure I understand before I go down that rabbit hole.
For each actual UDT instance tag, you instantiate either the Rockwell version or the Schneider version. Make any common configuration, like event scripts or expression tags, in the master UDT. The instances will pick up any such changes, even after they are created.
My customer is looking for something they don't have to develop from scratch every single job, though. They want a "master" app that fits the majority of use cases that they just have to tweak here and there. So what I need to deliver is a (nearly) fully-functional app that can talk to either a Schneider or AB PLC with minimal intervention by the customer's engineering team. Something they can start with and if they need to add a tank here or a pump there, they can.
I think with your way, I'd be instantiating both versions of every UDT every time, because otherwise it'd be up to the customer to build the tag database (hundreds of tags) every time, which they won't find acceptable. In turn, when I develop the screens, what instance of each tag do I point to? Do I make duplicate objects of everything? Do I make every binding an expression that looks at a custom session property? Do I make two sets of views? If I'm doing that, I might as well just make two projects, which the customer has stated they don't want.
Also, the way I'm doing it now, I'm making single UDT definitions, and then I'm making an Allen-Bradley folder and a Schneider folder. In those folders, I'm instantiating all my tags. The Schneider folder is identical, except the tags have overrides for the OPC path. If I make changes to the UDT definition, those changes will automatically populate down to all of the instances, regardless of whether they're Schneider or AB. Only what's overridden in each instance (which is just the OPC path) doesn't change, which is what I want. I've tested this already and it does work.
It seems like the simplest solution for me. Sure, they have to maintain two sets of tags, but once it's locked in and developed, changes to those master tags won't happen very often. The individual projects will have variations, but all they'd have to do is just delete what isn't there, and if they have to add a device, the UDT definitions are there for it, and they can do that easily. They just create a new instance and change the OPC path if they have to. I'm planning on making it so it follows the UDT structure of their Allen-Bradley program, so I can use variables in the OPC path that look at the name and structure of the folders and generates the tag names that way, so at least for one PLC they don't really have to do anything. It's only for the Schneider PLC they'd have to go through and add the modbus registers for anything new or unique to a particular job that they add, but that's unavoidable.
No, only one of the two types would be instantiated. At the same tag path every time. The UI wouldn't change at all.
No, just wrap all tags in an outer triplet of types. On deployment, you create one tag of either Rockwell or Schneider, and that creates the entire tree.
If already deployed, just delete that tag and make the other kind.
Are you saying I make one big master UDT for the whole project, so every tag would actually be inside that definition?
Then, two "slave" UDTs that use the master as the parent type, and then I assign the addresses in the definition, and then instantiate either one or the other depending on which project I'm doing?
If that's what you're saying, I think I see the advantages of that. The entire project is just one
"tag", essentially?
OK. I just tested this out. I made one single UDT master definition and grouped everything inside it. Then I made two "slave" UDT definitions that use the master as a parent definition and overrode the OPC paths. I'm able to add tags to the master and it populates down to the slave UDTs. I can also change which PLC I'm talking to by just changing the Parent UDT type on my single instance from Logix to Schneider. I hope I understood you correctly, because this is probably the direction I want to head in. Thanks for your advice and your patience!