Is there an inverse of the Advanced -> tag() scripting function, one that can write by reference?

I have long since figured out the (hacky) way of reading tags "indirectly" using the tag() method in the expression language. Is there the opposite, that would let me write by reference? Do I need to make a basic script that calls system.tag.writeBlocking on the referenced tag and then uses runScript to execute it on an event trigger? I would expect this to be slow and have stability questions compared to using a builtin system, if one exists.

What I really want is tag(tagPath) to actually be tag(tagPath, [writeValue]) where it optionally will write the value to the tag instead of returning the value.

There's not, and expressions having side effects is discouraged in general. runScript is an escape hatch to be used with caution.

So there is basically no way to have a dynamically referenced tag system with writing capability that is supported?

I.e. if I have 5 identical systems that have "system1" "system2"... names, where each system recieves a block of PLC Modbus registers that are structurally identical and only vary with the name of the folder they are in, I can't just duplicate the tag provider and adjust their reference to modbus input tags by name?

I haven't been following your multi post dynamic tag saga close enough to say yes or no to that.

1 Like

Bidirectional tag bindings in user interfaces do this. Expressions cannot. Derived tags and reference tags are also bidirectional, and the latter can be easily reconfigured.

Derived tags and reference tags are incapable of dynamic tag pathing, no?

No, but you can make a UDT that has that repeating structure, and make multiple, identical PLC instances in one tag provider.

(You are really hung up on having per-project tag providers. It runs against the grain of Ignition's architecture.)

1 Like

Both kinds can have configuration-time dynamic pathing by including them in UDTs. Which are the foundation elements of Ignition's dynamic configuration capabilities.

Actually, I have been trying very very hard to avoid the per-project tag provider, it is not until literally today that I even tried to implement a project-specific tag provider. Since I can't seem to use that to fetch the project name for steering, it is not useful to me.

Can you show me an example of how this works? I have tried every which way I can think of and it will not resolve the value of the dynamic tag in a Reference or a Derived tag, I have to use tag() inside of an expression tag.

configuration-time dynamic references. Anything else is crazy talk within tags, and belongs in user interfaces.

1 Like

configuration-time dynamic references. Anything else is crazy talk within tags, and belongs in user interfaces.

Ah, that sucks. I have that solved for my first bridge layer but I am trying to implement the "user interface" layer which is a lot more handmade and not something that I can trivially script into a configuration without some kind of bootstrapped "read the stuff that developers wrote and rewrite it all so that it can be duplicated" type of procedure.

Basically, I want to duplicate our system so we can run two in parallel and I don't want to manually update every single tag reference by changing a 1 to a 2 in a path in order to do it. I've solved this at the binding level in Vision but I am clearly struggling to abstract the actual memory layer of Ignition which is exceptionally frustrating as a python developer.

If you use UDTs, you can have a parameter for that part of the path, and change it in one place for the entire UDT instance. That is what they are for.

I recommend to take a look at my Spreadsheet Import Tool on the Exchange, including its example UDT definitions and the example spreadsheets that drive instance creation/replacement.

If you intend to run two PLCs in parallel, make two UDT instances that are parameterized to point into the PLCs correctly.

Then you make your UI take a tag path parameter that points at one or the other UDT instance path. Key everything in your UI off of indirect bindings with that parameter. Pass that parameter inward to subsystems, possibly with added path name fragments.

If you use UDTs, you can have a parameter for that part of the path, and change it in one place for the entire UDT instance. That is what they are for .

I have that, what I am missing is the part of the UDT that lets me write back to the referenced tags.

I can read them

But this doesn't work to write them because there is no write equivalent to tag()

Then you make your UI take a tag path parameter that points at one or the other UDT instance path. Key everything in your UI off of indirect bindings with that parameter. Pass that parameter inward to subsystems, possibly with added path name fragments.

Are you effectively suggesting I offload the "find the source tag and write to it" logic all the way into the actual bindings of components in the gui?

Get rid of tag(), too. Make your UDT point at the PLC based on its parameters. Make the member names and inner folders/nested UDTs fit the structure you need for your UI.

Get rid of the runtime indirection in your tags.

1 Like

Yes, but every such "find" is simple concatenation of the outer tag path with a constant string for the inner reference. Then your indirect tag bindings both bring data to your UI for display and provide the write path.

If your PLCs have the same structure, then all tags and folders (or nested UDTs) should have the same names, except the outermost fragment. That outermost fragment is what you pass to the outermost view of your UI. (Or Window, in Vision.)

This is a sort of example of my system architecture. My goal is to have all my "variables" available for GUI developers to be able to make an interface, while keeping as little strange logic in the HMI itself as possible. I am developing a platform for R&D applications where we iterate quite a lot and systems will sort of start as copies and then mutate over time. I do not want any of the controls to have the possibility to accidentally grab the wrong PLC, so I am trying to keep everything as abstracted as possible such that there are a few high level switches that control the flow.

Notably missing from the graphic is the fact that our PLCs expose blocks of Modbus memory that I have attached to via the OPCUA configuration in the Gateway interface:

Good luck. Your architecture implies that a PLC change is not a configuration change. The Modbus mappings in IA's driver are a crutch that even the author that driver recommends against using. Just make manual OPC Item Paths in your OPC tags.

{Do take a moment, perhaps when you are frustrated, to look at my Spreadsheet Import Tool.}

Your architecture implies that a PLC change is not a configuration change.

I don't think I understand what you mean by this -- when I change something in my PLC (and more importantly where those changes affect where data hits the modbus interface), I have to run my script to reconfigure my tags to align with the new PLC arrangement. What does "configuration change" mean to you?