runScript - Expression Tag vs. Binding Expression Property

Hi everyone,

I’m trying to use runScript in an expression tag. I thought it was my project script so I changed it to something simple that just returned a string. Here is my expression:

runScript("system.util.getClientId()", 1000)

That gives me the following error:

com.inductiveautomation.ignition.common.expressions.ExpressionException: Error executing script for runScript() expression: system.util.getClientID()

When I copy the same expression into the expression binding type of a label’s text property it works.

Is there a difference between a tag and a binding property?

Ultimately what I’m trying to do is a system.opc.readValue for properties of a UDT where I pass the OPC item path into a script and therefore avoid having to create those as Ignition tags. I know I could have an OPC tag in my UDT and build it using data type parameters but it would require passing in a ton of parameters that I can get elsewhere in my tag hierarchy. I think this entire problem could also be solved if we had the ability to bind values of data type parameters… :smiley:

Thanks in advance,

You can’t use any expression or data item in an expression tag that resides in a client. The expression tag script runs in the gateway with no client. (No project either.) Only shared scripts and gateway-scope script functions are available.
If what you are doing is truly specific to each client, use a Client Tag.

As Phil mentioned, the system.util.getClientId() function does not exist in the Gateway scope so calling it in a script associated with a tag will not work.


Thank you both. Looks like I’m out of luck then.

Just in case it changes anything, which I don’t think it does, here’s a better explanation of what I’m trying to do. The system.util.getClientID was just the first single line script I thought of that returned a string. I have a data type (Machine) that I plan on using in a bunch of different places including windows of multiple projects and transaction groups so this is something I want on the gateway, not client. The way the PLC tags are structured would make me have to pass in a bunch of parameters to the data type to build the OPC path for each instance of this data type (now around 35 but could get higher). So in the following example, based on how the rest of my tag browser is structured, I can get the instance by passing in only a single integer parameter to the data type:


I’m building the prefix of some OPC tags with the Tag_Prefix expression tag. I want to pass that into a system.opc.readvalue script so the expression of Mode_ID is:

runScript("project.Helper.ReadTag('" + {[.]Tag_Prefix} + "Mode_ID')")

And ReadTag is a project script:

def ReadTag(tagPath): qualifiedValue = system.opc.readValue("Ignition OPC-UA Server", tagPath) PV = qualifiedValue.getValue() return PV

It’s not the end of the world, it just means I need Line_Code, Machine_Code, Machine_ID_Pad and Machine_IX_Pad as data type parameters that need to be manually typed in for each instance.

SQLtag folders don’t have to follow PLC folders. Just make folders with suitably convenient lineID keywords and place the UDTs in them. That’ll give you a simple Tag Prefix for your navigation and indirect tag binding. Then, to create the OPC address for related items, retrieve the OPC path from one of the items in the UDT and strip off the end. That’ll give you the OPC prefix for anything you want to use with system.opc for that line. There’s no need for Tag Prefix to be the same as OPC Prefix.
I still don’t understand how the ClientID in a gateway tag would have helped with this problem.

getClientID was never intended to help with the actual problem. I thought I had an issue with the project script and wanted to try an example of a single line Python script that returned a string to test runScript instead of calling a project script. After your post I realized it was the scope itself and not the project script.

You lost me on your suggestion. I still don’t understand how I can use indirect tag binding or system.opc within a data type property. The only variables I can use in the OPC Item Path are parameters, InstanceName or TagName.

I want to have a data type that allows me to make a change in one place that affects all 30-some instances. These will also be used in numerous transaction groups where it would be easy to export one as XML and do a find/replace on a handful of machine names and IDs to duplicate for the other machines.

Apologies for not making my problem clear and not understanding your proposed solution.

I’m suggesting that you don’t need a separate item in your UDT to represent the OPC path for that UDT’s data in the PLC. Just have at least one item in the UDT be an OPC tag. The OPC path for that item is a readable property of the tag that you can retrieve in your gui and in your transaction group. It doesn’t matter if your UDT uses parameters to build that opc path, the path is retrievable with the .OPCItemPath suffix on that item’s tag path. { Note, you keep using ‘tag path’ when in Ignition, you mean ‘opc path’. That’ll confuse people who are used to Ignition. } Try it: make a label on a gui window, then open the tag browser, drill all the way down to the OPCItemPath, and drag that to the label. Then look at the label’s text binding.
So the UDT only needs items for the live data you always want to read from your line and the items holding static configuration for your customization ideas.
Your windows and transactions then need one tagpath that supplies the location of the UDT. From that, you bind to one of the UDT’s OPC items to obtain the opcpath for anything else.

I feel bad wasting your time as I still don’t understand so no issues if you don’t reply. But here’s one last attempt…

I see the tag reference when doing the binding as you suggested but don’t understand how I can use that in a transaction group.

I have one PLC that represents many machines by keeping them in different programs. Each one of those programs contains a PLC UDT that has the data needed in Ignition. I can get to each of those machine’s PLC UDTs by building the OPC path using other items in my Tag Browser hierarchy by having a few memory tags at the root of the line and then the root of each machine. I want a generic data type to represent each of the machines where some of its properties come from the PLC. This way, if the PLC ever has more data points I want I can add them to the data type instead of for each individual machine (there’s another layer where each “machine” can have multiple “stations” but I think I’ve already made it confusing enough). I then want multiple transaction groups for each machine where the tags in the transaction group reference the tag paths of several UDT properties.

So in the picture I would have an instance of this data type (Machine) in each of the P{XX} folders. For the multiple transaction groups I’ll be creating for each machine I don’t get how to do what you suggest:


Here is the OPC browser. Trying to boil it down, I want an Ignition data type that represents all RXXX_u_PMON PLC UDTs without having to pass in P05_PRC and R101 as Ignition data type parameters since 1) it’s a hassle to manually type each of those in to each instance and 2) I already have them elsewhere:


Thanks again for your time