Passing tagpath to generic sidebar for display of live values

Hi all,

Do you have a best practice way to do this? currently the way I’m doing it based on which component on the screen is selected I pass the tag to a client tag, the ‘sidebar’ template reads the tagpath from the client tag and I have some custom methods to read the tags in that tagpath. I have custom properties which have a runScript expression calling the custom method to populate the dataset. Once I have the dataset I use template repeater to display in the sidebar. I don’t think this is the most efficient and there must be a better way, it worked well during development but now the UI is extremely slow and hardly works.

Thanks!

I'm gonna guess you are calling readStates() from the bolded propertyChange event?

You are browsing tags inside an event. Not only does browsing call out to the gateway, it is notoriously slow. Inside an event, that freezes your UI. Even scripted tag reads (other than client tags) should not be done in event scripts.

Use indirect binding to read your dynamically selected tag, using a string property for the whole tagpath. Do your tag browsing in a background thread (invokeAsynchronous) and pass the results (tagpath) back to the foreground UI with invokeLater.

2 Likes

I’m calling readStates() from an internal property which builds the dataset:

Now I have a dataSet with {key:value} pairings and I display that on the sidebar with template repeater.

Inside the propertyChange I call the script again which catches any property changes if any of the state tags change.

Can you provide an example of the async and invokelater, I was trying to get that to work before but I don’t believe I was implementing properly.

Code: {tags = system.tag.browseTagsSimple(states, “ASC”)
data =
for tag in tags:
if “Spare” not in tag.name:
data.append([tag.name, system.tag.read(tag.path).value])
tempDataSet = system.dataset.toDataSet([‘Key’, ‘Value’], data)
print ‘readStates’
return tempDataSet}

Thanks.

Not sure if this is the same as what you’re trying to do but I have a tag browser where users can select up to 5 tags and see them charted in real time on a rolling configurable amount of time. The 5 limit is arbitrary - the chart just needs the same number of dataset properties.

If that’s what you’re after I could post more info.

You really need to put this into preformatted text (using code surrounded by pairs of ```) on the forum to preserve formatting (in particular tabs) and make your code readable:

tags = system.tag.browseTagsSimple(states, “ASC”)
data = []
for tag in tags:
    if “Spare” not in tag.name:
        data.append([tag.name, system.tag.read(tag.path).value])
tempDataSet = system.dataset.toDataSet([‘Key’, ‘Value’], data)
print ‘readStates’
return tempDataSet

ok thanks

def readStates(self, states, refresh=none)
     //invoke later, not exactly sure how to call this
     def browseTags
          tags = system.tag.browseTagsSimple(states, “ASC”)
          data = []
          for tag in tags:
               if “Spare” not in tag.name:
                  data.append([tag.name, system.tag.read(tag.path).value]) tempDataSet = 
              system.dataset.toDataSet([‘Key’, ‘Value’], data)
     system.util.invokeAsynchronous(browseTags)

print ‘readStates’
return tempDataSet
//also unsure about calling async at the end of the function
system.util.invokeAsynchronous(readStates(self, states))

Would it make sense to call async on the internal frame opened of the sidebar or something like I have started above?

You have syntax and logix errors. Your def browseTags needs parentheses and a colon. You aren't assigning the returned dataset from toDataSet() to a variable, so it goes in the bitbucket beside your laptop. (It's also indented one too far--it should be run after the loop.) When you have it assigned to a variable, you'll need an invokeLater() to safely assign it to a component.

Also, you can't pass any parameters to the function called in invoke*. See my helper functions in later.py for alternatives:

{ If you search this forum for invokeLater or invokeAsynchronous, you'll find many more examples. }

Thanks, so if I can't pass the required tagpath that needs to be browsed into the function then I don't think the invoke* will work for this case

Look at the examples for the alternate techniques for passing information into these functions, or just use the functions in later.py that do it for you.

Arguments don’t need to be passed into this function though, do they @pturmel? They’re available to the inside of the function as they exist when the function is created.

E. G.

states =... 
refresh = none
def readStates():
     #invoke later, not exactly sure how to call this
     def browseTags() :
          tags = system.tag.browseTagsSimple(states, “ASC”)
          data = []
          for tag in tags:
               if “Spare” not in tag.name:
                  data.append([tag.name, system.tag.read(tag.path).value]) tempDataSet = 
              system.dataset.toDataSet([‘Key’, ‘Value’], data)
     system.util.invokeAsynchronous(browseTags)

print ‘readStates’
return tempDataSet
#also unsure about calling async at the end of the function
system.util.invokeAsynchronous(readStates)

Yes, in this case. My later.py was modified to take advantage of such closures where applicable. Note that the variables in question must be named in the inner functions to be captured in the outer scope. Indirect access to outer variables doesn't work.