Proposal: system.tag.toggle functionality

It happens from time to time we want to toggle a bit in the tags.

The shortest way of writing this is currently

system.tag.writeBlocking(['path/to/my/tag'],
    [not system.tag.readBlocking(['path/to/my/tag']).value])

This is not so good for a few reasons:

  • The calls are blocking, and involve two tag accesses, so can be slow.
  • The tagpath needs to be given twice
  • The list creation is a bit unneccesary (I never had to toggle two tags at once)

An alternative way is using the async tunctions like this

def handleRead(tagResult):
    system.tag.writeAsync(['path/to/my/tag'], not tagResult[0].value)
system.tag.readAsync(['path/to/my/tag'], handleRead)

This also has issues:

  • it uses more code
  • it still causes two tag accesses
  • you get an extra function the scope which is hard to name and can cause naming collisions with other handlers
  • if you want to wait for the write to be complete, you need to add yet another callback function defined

To me, the toggle situation is so standard, it would be nice to have a toggle function in the tag scripts:

  • It should only take one tag (I can’t think of a situation where I need to toggle multiple tags at onces)
  • It should do a read and write in the server scope, so not cause an extra roundtrip
  • It could accept an optional callback to get back the written value and quality, for those who want to wait for the toggle result

So it could be used like this

system.tag.toggle('path/to/my/tag')

or this

def handleAfterToggle(qv):
    system.gui.showMessageBox("New value is %s" % qv.value)
system.tag.toggle('path/to/my/tag', handleAfterToggle)

What do you guys think of this? Do you also run into this issue? Or is it just me?

There’s always going to be a race between reading and writing, particularly if something else can ask for a toggle simultaneously. Having separate reads and writes makes the timing gap obvious.

If this is a user interface, I like using bidirectional bindings for such things. Then your python is someComponent.someProperty = not someComponent.someProperty. The reading is handled by the binding’s subscription, so only a write generated.

We use it often to trigger table refreshes. When you use a WHERE clause with {triggertag} = {triggertag} it will bind to that tag, and force an update when the trigger tag is toggled. So whenever a user executes a script, the script can force an update for all users. This allows to keep the polling time quite slow, while maintaining a responsive interface.

Another case where we have this, is when creating a right-click menus for toggle buttons (start/stop/reset/…). There we have no bindings, so need to use a script too.

Blegh. I strongly prefer using a broadcast message to clients.

If it's the same information for all users, why not store the data in a dataset tag, and point the table to it?

We usually have different views on the same data for different users.

Users can apply their own filters, operators see data for their own line, and other data than people responsible for the planning, … But the data is all linked together.

And there are urgent updates (like a planner releasing an order should be visible on the operator screen as fast as possible), and there are less urgent updates (like weight counters) where polling at a slow speed is enough.

A combination of slow polling (30 seconds, 1 minute, …) and trigger tags has always given us good results. And I wouldn’t know how to be as versatile with dataset tags or with broadcast messages.