Best way to read multiple tags and bind them to a table

Hello all,

I am trying to read the tags of a folder all at once and bind a table’s data to the tag values. So then a column of that table gets filled with the tag values.
image
There may be a ton of ways to do this but what is the best and most efficient way? As I have 100 tags in that folder and I do not want to read each manually (the picture is just for the demonstration as I have more tags).

In the previous versions of ignition there was a command called “system.tag.readAll” which has been deleted from Ignition 8.1. Is there any command similar to that one?

Also I am using perspective

It was not "deleted" it was depricated / replaced by system.tag.readBlocking. You can provide this function with a list of tag paths, just as you would have system.tag.readAll in earlier versions.

You just need to build the list of paths

parentPath = 'pathToFolder/Zone/'
tagPaths = [parentPath + 'Zone{0}'.format(tag) for tag in range(1,101)]

values = system.tag.readBlocking(tagPaths)

Then you can build the dataset:


headers = ['Zone','Value']
data = []
for tag in range(len(values)):
    data.append(['Zone{0}'.format(tag),values[tag]])

tableData = system.dataset.toDataSet(headers,data)
1 Like

Thank you @lrose for the information.
Where should I build the list of paths? inside another tag or table data expression binding?
also for the part:

headers = ['Zone','Value']
data = []
for tag in range(len(values):
    data.append(['Zone{0}'.format(tag),values[tag]])

tableData = system.dataset.toDataSet(headers,data)

Is this going to be written as a transform script format?

for perspective

results = system.tag.browse(path = '[provider]zone', filter = {'tagType':'AtomicTag'})
tags = []
for result in results.getResults():
     tags.append([result['name'], result['fullPath']])
values = system.tag.readBlocking([t[1] for t in tags])
data = []
for e, t in enumerate(tags):
    data.append({'zone':t[0], 'value':values[e].value})

Hope it works, I don’t have a designer on hand

1 Like

Thank you @jespinmartin1. I have not used tag.browse before and I was wondering where to actually put this script. It does not work in the table’s expression binding.

That's up to you.

Of course that won't work in a expression binding. You may want to add a Script Transform

1 Like

Before you decide where to put the script, you need to decide when it needs to be updated. Only on window open, on button-push, tag change, every 5 seconds when the table is visible…

2 Likes

@jespinmartin1’s code will work, but personally I would avoid using system.tag.browse if possible. In this instance the tag path is mostly static so it’s easy enough to build a list of tag paths without the added abstraction you get with the browse function.

The reason I would avoid the function is because the execution time can be pretty long depending on the complexity and depth of your tag structure. You should avoid potentially long running tasks on the UI at all costs.

This isn’t something that is really specific to just perspective or vision.

Determining the best place for the script as @dkhayes117 stated is really dependent upon the project structure and the required outcome. Personally I would be tempted to delegate this to a gateway event of some type, perhaps a timer event, then writing the result to a memory tag of type dataset. Then it should be as simple as binding the tag in the view.

2 Likes

Thank you for the information @lrose. The tag path is not static not it can be changed and each tag may have different names. I tried @jespinmartin1 code on a button event handler but that did not work., I am going to post the script here.

Sorry, I used the word static loosely in this case. I assumed that the vast majority of the tag path did not change. If the only part of the path that changes is the tag name itself, I would consider that to be static for all intents and purposes. No need to browse to find the paths they can easily be generated.

I tried this on a button event handler and it did not work:

	results = system.tag.browse(path = '[default]Zone', filter = {'tagType':'AtomicTag'})
	tags = []
	for result in results.getResults():
	     tags.append([result['name'], result['fullPath']])
	values = system.tag.readBlocking([t[1] for t in tags])
	data = []
	for e, t in enumerate(tags):
	    data.append({'zone':t[0], 'value':values[e].value})

I get TypeError: expected a str error.

I also tried this version:

	results = system.tag.browse("[default]Zone")
	tags = []
	for result in results.getResults():
	     tags.append(result['fullPath'])
	headers = ['Zone','Value']
	data = []
	for tag in range(len(tags)):
	    data.append(['Zone{0}'.format(tag),tags[tag]])	
	tableData = system.dataset.toDataSet(headers,data)
	#system.perspective.print(tableData)
	self.getSibling("Table_0").props.data=tableData

And this is the result I get:

image

first of all zone number does not match the cone values and I would like to see the zone values instead of their tag path. tried to use system.tag.readBlocking but I got errors.

You have to use system.tag.readBlocking at some point, otherwise you will not have the zone values to put into the dataset. What errors did you get?

1 Like

TypeError: ‘com.inductiveautomation.ignition.common.tags.paths.BasicTagPath’ object is unsubscriptable

Parya, you are very close.

In your first for statement, you don’t have to invoke .getResults() method. “results” is already a dictionary with the data you want. Check results object contents here: system.tag.browse - Ignition User Manual 8.1 - Ignition Documentation
You can actually bind that results object directly to a table dataset prop to monitor in runtime what you are getting, and then can use the same for to build/shape a dataset with the data you really need. In your case, “name” and “value”.

1 Like

Thank you very much, Daniel. I was also able to read the tag values with the hint @lrose provided.

	parentPath = '[default]Zone/'
	tagPaths = [parentPath + 'zone{0}'.format(tag) for tag in range(1,10)]
	
	for i in range(len(tagPaths)):
		values = system.tag.readBlocking(tagPaths)[i].value
		system.perspective.print(values)
2 Likes

Through this on there and you’ll be done. I would still probably put this in a Gateway Event script and write it to a tag, though.

headers = ['Zone','Value']
data = []
for tag in range(len(values)):
    data.append(['Zone{0}'.format(tag),values[tag].value])

tableData = system.dataset.toDataSet(headers,data)
1 Like

I get this error when I add them together

TypeError: object of type 'int' has no len()

Your script in full should look as follows:

parentPath = '[default]Zone/'
tagPaths = [parentPath + 'Zone{0}'.format(tag) for tag in range(1,10)]

values = system.tag.readBlocking(tagPaths)

headers = ['Zone','Value']
data = []
for tag in range(len(values)):
    data.append(['Zone{0}'.format(tag + 1),values[tag].value])

tableData = system.dataset.toDataSet(headers,data)

Notice that I am only making one call to system.tag.readBlocking where as in your code you are making 9. The entire point of building a list of tag paths is to only call the function once. Then I am using the length of the values list provided from the readBlocking function to loop and pull the individual values.

2 Likes

I also tried this:

	results = system.tag.browse("[default]Zone").results
	tags = []
	for result in results:
	     tags.append(result['value'])
	headers = ['Zone','Value']
	data = []
	for tag in range(len(tags)):
	    data.append(['Zone{0}'.format(tag),tags[tag]])	
	tableData = system.dataset.toDataSet(headers,data)
	#system.perspective.print(tableData)
	self.getSibling("Table_0").props.data=tableData

and this is the result I got:

image

Now I have to take the first value which I am working on it right now

Thank you very much @lrose. That solved the issue!
However, to learn what the script is doing here I have two questions:

  1. what does Zone{0} mean here? I tried to change that to 1to match my values with zone numbers so that zone 0 is not equal to zone 1 value and I get IndexError: tuple index out of range error
  1. I do not quite understand this part either.

Thank you so much for always being so helpful :slight_smile: