Client tags not shown when working in the Project Library

Why aren't client tags shown in the Tag Browser when working in the Project Library?
When I write scripts in the Project Library, I don't see client tags listed in the Tag Browser. I have to manually type the tag paths, which is inconvenient and increases the chance of typos. Is there a reason for this behavior? Is there any way to make client tags visible while working in the Project Library?

The script library is present independently in all scopes, but Vision Client Tags are only present in Vision scope.

The designer tree has to have something selected under Vision for the tag browser to show Vision Client Tags.

The workaround is to float your project library script windows then pick a Vision item in the tree. Your tag browser will then include Vision Client Tags while your project library scripts remain editable. (Right click on the script's tab at the bottom of the editor to find the float option.)

1 Like

I didn't know I could float the Project Library. Thanks for pointing that out! Now at least I can see my tags while editing, which helps. Still, it would be great to have the reference icons or some way to drag and drop the tags directly into the script like we can elsewhere. Thanks again!

Wherever you feel that desire, consider adding a function argument to carry either the tag value or the tag path. In the actual scope of use, where your event is a one-liner to the project library, you can use the convenience methods in that spot to supply the function argument.

Not that you shouldn't have string constants in your project library, but having too many will handcuff your ability to re-use the functions.

In my packing application, the user enters the order number, and Ignition calls a stored procedure to retrieve the order data. About 10 tags are populated with the results. The user then makes a few selections on the screen, which update a few more tags. When they click a button to complete packing, it calls a function in the Project Library that reads these tag values to build a dictionary and send it as a payload to an EWM API.

Given this setup, would it be a good practice to pass all 14 values as arguments to the function? Or is there a better way to handle this?

I presume you put these tags in a folder? Just pass the folder path and let the library function construct the full list for .readBlocking().

I don't have them in a folder because this application has only one window and is used only for packing, so I don't have multiple groups of tags. So it is not a good practice to read one tag at a time? I'm doing this...
orderValue1 = system.tag.readBlocking(['[client]orderValue1'])[0].value
orderValue2 = system.tag.readBlocking(['[client]orderValue2'])[0].value
orderValue3 = system.tag.readBlocking(['[client]orderValue1'])[0].value

Terrible practice.

Less terrible with client tags, because there's no network hop involved, but it's a bad habit to start.

Consider if you were reading three gateway tags. Because you're using readBlocking, you're sending three requests in series - meaning you're paying whatever network latency penalty exists between client and gateway six times, rather than two. And your script is almost certainly running on the main GUI thread, meaning users won't be able to interact with anything in the client while that's all happening.

By far the better practice is to batch your tag reads together into single chunks (if you continue to use blocking reads - going all the way down the async rabbit hole is probably out of scope):

paths = [
	'[client]orderValue1',
	'[client]orderValue2',
	'[client]orderValue3',
]
qvs = system.tag.readBlocking(paths)
orderValue1, orderValue2, orderValue3 = [qv.value for qv in qvs]

I see, it makes perfect sense. Thank you for the explanation! My question remains unanswered regarding whether I should read the tags in the component and pass 14 arguments to the function, or read the tags within the function itself.

I actually tested this the other day as a colleague new to scripting was doing this, reading one tag at a time. I wanted to show him why it was a bad idea, so I read every tag in the system (only 7639, small project, most opc but maybe 10% memory) both ways: in one list and one-by-one from an, albeit, remote designer script console. The results were a bit staggeringly unexpected to be honest:

  • single list read: 650ms
  • 1-by-1 single reads: 843,366 ms (14minutes)

Note: it wouldn't be nearly this bad when running this on the gateway itself, as opposed to across the network through a VPN, but it still won't be great

Edit:
Running the same test direct on the gateway (via Perspective):

  • single list read: 109ms
  • 1-by-1 single reads: 565ms

Still 5x the time

Script
from java.lang.System import nanoTime as nt
tagPaths = system.tag.browse(path='[default]', filter={'tagType': 'AtomicTag', 'recursive': True})
self.custom.tagCount = len(tagPaths)
start = nt()
a = system.tag.readBlocking(str(tag['fullPath']) for tag in tagPaths)
self.custom.all = (nt()-start)/1000.0/1000.0

start = nt()
for tagPath in tagPaths:
	a = system.tag.readBlocking(str(tagPath['fullPath']))
self.custom.single = (nt()-start)/1000.0/1000.0
1 Like

if a function has more than 5 arguments, you're going to call it with arguments in the wrong order/with the wrong keyword params at some point, and 5 is pushing it.

This, but pass the tag paths as a single argument: a list of strings.

4 Likes

I ran your test on my application, and here are the results. It's a small, single-window project with 14 client memory tags, so there's no client-Gateway communication involved when reading them, which aligns with what @PGriffith mentioned.

That said, I now understand the performance difference once network traffic comes into play in a more complex project. I'm glad I asked—thanks to everyone for the feedback!

List read (ms) 1-by-1 read (ms)
1 2
1 3
1 2
1 4
0 1
0 4
1 2
0 2
0 2
0 1
1 Like

If five arguments is already pushing it, what would you suggest for a case where the user clicks a button and the application needs to build a dictionary with 14 values to send to an API? I'm trying to find the right balance between readability and maintainability.

What are the parameters ?
Remember you can pass lists and dicts.

1 Like

build an object that contains those values, pass the object.

2 Likes

Yes, @pturmel already suggested to send a tag path list.

I was answering to the message just above, about building a dict for an API.
What's the situation exactly ?
What data do you need to pass to what ?

Here is the situation. Get 10 values from a database (10 tags). User makes 4 selections (4 tags) and clicks on a button. The button builds a dictionary with all 14 values and sends it to an EWM API.