System.tag.browse

Hi all,

Can the system.tag.browse function be used on a expression for example to bind a tag to a label or value display?

system.tag.browse is a script language function. These are different to expression language functions and can’t be mixed.

In addition, you don’t “bind a tag to a label” - you bind one of the label’s properties (usually the text) to a tag.

What is the problem you’re trying to solve?

You can bind script functions to a component either using the runScript expression function or using a script transform in Perspective. But binding to the tag.browse function seems like a bad idea. As @Transistor asked, perhaps let us know what you’re trying to solve instead and we can offer alternative solutions to the issue

Hey guys, here is why Philip and I want to use System.Tag.Browse in an expression:

I need to modify the instances property of a Flex Repeater component based on the results of a System Tag Browse. The purpose of this whole thing is to automate the output of a Flex Repeater component. The core data needed to automate the flex repeater lives within the tag path data. This is why I’m trying to execute a System.Tag.Browse function within an expression connected to the flex repeater.

If you know of a better way to do this, then I’m open to suggestions. Much appreciated.

Try this:

  • Create a parameter on the view to be used in the flex repeater.
  • On the flex repeater component either
    • create a binding on each instance of the flex repeater instance or
    • write a script to set the view parameter.

Ok, so you have different sets of tags in these different folders and therefore need to query the folder to know what to display in the flex repeater?

In that case a browse is really your only option unless you have that info available somewhere else like a dataset tag or sql table. It sounds like you’ll have a trigger (eg the tagpath to browse) that should trigger the browse and hence update your repeater items, so I would use a property binding using this tagpath and attach a script transform to do your browse. Then create your flex instances list of dicts using the results

@Transistor - Yeah, this was basically my plan. Create a view level parameter and then store the specific tag path data into this parameter. Then have a script on the instance property of the flex repeater to build the instance property data based on the list of data in this view parameter. However, I’m still stuck at figuring out how to run the System.Tag.Browse script and pass that data to a parameter.

@nminchin - I don’t have the information anywhere else. I thought about storing the data in SQL, but I realized the complex folder (device) hierarchy information I need is already part of the tag paths so this is why I’m trying to figure out how to run the System.Tag.Browse from expression.

@Transistor & @nminchin - Is the only way to do this is to write a project level script function that contains the System.Tag.Browse and then reference this script using a “RunScript” function? If so, I attempted to do this and still was unsuccessful, but maybe I had a syntax problem. I could try again and post my script here to have you look at it.

Nope. Bind the flex repeater’s props.instances to your tag path property (the property that changes when you want to browse a different folder and hence produce different flex repeater instances) and add a script transform to it which would do the browse and the creation of the list of dicts that props.instances expects.

Something like:

tagpath = value
tags = system.tag.browse(tagpath, filter={'tagType'='AtomicTag'})
tagPaths = [tag['fullPath'] for tag in tags]

instances = [{'tagPath': tagPath} for tagPath in tagPaths]
return instances
1 Like

Note that an absolute performance cliff lies down this road. Browsing is a fairly expensive operation. You’re browsing your folder paths N * M times, where N is the number of Perspective sessions you have and M is the number of these components you have (since this binding will also run on initialization).

If this is something you don’t expect to change often, I would strongly encourage you to look into “pre-caching” this information on the gateway somewhere - you can still use browse, but maybe once an hour in a gateway scheduled script or something, rather than constantly per session.

4 Likes

I think the most important question here is : What will make the data in the repeater change ? Is it user triggered (dropdown, button, text field…) or does it depend on a general condition (a tag’s value, the result of a scheduled script…).

As @PGriffith said, depending on what the trigger is and how often you expect it to change, there might be different ways to do this that would avoid doing a full browse too often.

@PGriffith and @nminchin - Thanks for the advice. I’m doing as you suggested and implementing the system.tag.browse in a Gateway Events script to minimize performance impact. I would ultimately run this script once a day and pass the data to a mySQL database.

However, I’m struggling to pass the tag.browse results to the mySQL database. I have a couple questions that I’m hoping you have answers to.

  1. When I do a system.tag.browse the results all have a ‘u’ in front of tags. Here’s example:
    u’hasChildren’: True, u’name’: u’DCI01’, u’tagType’:Folder}, —> Why is this ‘u’ being inserted into the JSON?

  2. When I try to SET this data in the mySQL database into a JSON datatype column I keep getting a log error saying “You have an error in your SQL syntax”. below is the result of my expression string.
    system.db.runUpdateQuery(UPDATE sitedetails SET jsonTags = ‘: [{u’fullPath’: [Ignition_…

If I replace the 'jsonTags = with something simple like 'jsonTags = ‘I am awesome’ then the script executes fine. So this tells me that the mySQL string for some reason doesn’t like whatever the tag.browse function returns. Any help here is appreciated.

system.tag.browse returns something that looks like a Python list of Python dictionaries, which itself looks like a JSON structure, but neither are exactly equivalent. (The u prefix on strings is a giveaway that it’s not actually JSON, but in fact the string representation of Python data).

The immediate answer is to use system.util.jsonEncode to translate the Python data structure into a plain JSON string, which you should then be able to store in your DB.
However, you’ll probably want to ‘massage’ the data a bit to extract only the fields you particularly care about before encoding it; the direct output of jsonEncode will probably have lots of details and nested information you don’t really need for this use case.

@Trent_Derr
I imagine all you want to put into SQL is a list of paths to the tags:

tags = system.tag.browse(...)
tagpaths = [str(tag['fullPath']) for tag in tags] # this will produce a list of tagpaths
# you may want to convert the list into a CSV for SQL
tagpaths_csv = ','.join(tagpaths)
2 Likes

‘u’ for Unicode, as far as I’m aware.

Thanks. With everyone’s help I was able to get the first part of my automation done. Here is the script near final script for collecting the tag paths.

import json

#Set the tag browse path and filter parameters
filter = {‘tagType’:‘Folder’,‘recursive’:True}

#TODO - Instead of hardcoding the siteID and basePath, fetch the list of siteIDs from the database and iterate through each site
siteID = ‘H4683’
basePath = “[Ignition_HOST01_MQTT_Engine]Edge Nodes/…”

tags = system.tag.browse(path = basePath, filter = filter)
tagpaths = [str(tag[‘fullPath’]).replace(basePath,‘’) for tag in tags]
tagpaths_csv = ‘,’.join(tagpaths)

#system.file.writeFile(r"C:\myExports\myExport.csv", tagpaths_csv)
rowsChanged = system.db.runUpdateQuery(“UPDATE sitesdetails SET tagFolders = ‘%s’ WHERE siteID = ‘%s’” %(tagpaths_csv, siteID),‘db’)

My next task is to create the change script on the flex repeater that will search through these tags to gather specific folder parameters for the flex repeater instances. My plan is iterate through with regex searches, but not sure if that’s possible.