Button with long script inconsistent

Ignition Version 8.1.2

Hello, I have recently started ignition at a new job and the project I am now working on is huge. Over 50 different main screens and 20k+ tags. On one screen there is a momentary button that runs a script that is about 400 lines long. It is a mix of reading tags & OPC values, doing calculations on the values, and then writing the new values back. The script is triggered off the “mouseClicked” event handler. The problem is sometimes the values that the script is supposed to write back never get written, which then will throw an error on the screen it is located on. I am curious if I can get this button working more reliably. If it matters, the script also sets a tag value based on “mouseReleased”.

Any help is appreciated.

Without seeing the script I can only guess at how best to help.

First, this sounds like an Anti-pattern known as the Magic-Pushbutton. Does this type of programming, even the exact same programming, exist in other places in the project?

You should consider moving the code to a project script and generalizing it, such that it can be used in multiple places. Especially if this code is a long running task (note: not determined by the number of lines). If it is a long running task then it should be moved to a background script if it isn’t already. There are numerous threads relating to that topic. Search for system.util.invokeAsynchronous().

Second group all tag reads at the start of the script and all tag writes to the end of the script. Doing single tag reads and writes will greatly increase the execution time of the script.

Finally, you said reading/writing OPC values. I would avoid reading directly from the device unless necessary, off load that work to the gateway.

Without seeing the script its going to be difficult to really help you optimize it.

1 Like

This is the only button in the program that uses a script of this magnitude. It is also not something that is used anywhere other than this screen, so I do not believe it is necessary to have the script running in the background. The script is also only used every few hours at the most.

The script itself is split up. The first half of the script is reading tags and OPC values.
The second half is doing the calculating and writing. Should the calculating be split up as well? Each calculation takes place just before each write. The calculations are simple arithmetic.

The script goes like this.

valueX = system.tag.read(“tagpath”).value (x25 lines)

valueX = system.opc.readValue(serverName, “direct OPC path”).value (x60 lines)

valueX = system.tag.read(“tagpath”).value (x25 lines)

value = value * value / value (example calculation)
system.tag.write(“tagpath”, value) (75x lines)

The last two lines of the script:
value = system.tag.read(“tagpath”).value
system.tag.write(“tagpath”,value)

There are over 60 lines that are reading direct OPC values, so that could definitely have an effect on the performance. I will create tags for those tomorrow and see if performance increases. If this has no effect I will upload the script. I would just like to delete the tag paths/names beforehand for privacy purposes and that may take me a bit.

Thank you for your response.

Any script that needs more than one tag value or OPC item value should be using lists of tag paths or lists of item paths (with the “Blocking” function). So too with the writes–write that all at once.

You will be stunned at the change in performance.

3 Likes

This will not have near the performance increase that combining all of the reads and writes into 1 will.

Your script should look something like:

tagValues = system.tag.readBlocking(['tagPath1','tagPath2', ... ,'tagPathN'])

#do your calculations

system.tag.writeBlocking(['tagPath1','tagPath2',...,'tagPathN'],[writeValue1,writeValue2,...writeValueN])

https://docs.inductiveautomation.com/display/DOC81/system.tag.readBlocking
https://docs.inductiveautomation.com/display/DOC81/system.tag.writeBlocking

I like to create a dictionary that holds all of the read values where the tagName is the key and then modify those values, then I can use that same dictionary at the end to write all of the values back.

1 Like

I would be using the onActionPerformed event, not the mouse events

Oh nice, I didn’t think of that. I guess you are doing something like

myDict = {data...}
system.tag.writeBlocking(myDict.keys(),myDict.values())
2 Likes

Similar, though I’m lazy so I’ve written the scripts to account for a parent path so that I don’t have to type the entire tagPath when referencing a value.

You can find the exact scripts here:

Ah that makes a lot of sense. Will be doing this today and will report on the results when I finish. I’ve sort of inherited this project and there are definitely some rough edges so I’m just working through it piece by piece.

2 Likes

Got the script working. I was able to make a huge reading list and huge writing list. At the very end of the script I had to add an additional read/write because the value being read is being written in the block before it. Much better performance so far from what I can see, but I will gather more feedback as the operators use the button. Thank you for your help. I will definitely be using the readBlocking/writeBlocking for future scripts.

FYI, you can use this code to help narrow down long execution times.

import datetime
startTime = datetime.datetime.now()
print startTime 

'Do stuff'

print datetime.datetime.now()
print datetime.datetime.now() - startTime 

You really ought not to be recommending python’s datetime module. Ignition has a family of date functions for a reason. For timing measurements that aren’t affected by system clock adjustments, use java.lang.System.nanoTime(). It is monotonic and carefully synchronized across CPU cores.

Dude, I think it’s good enough for most purposes, especially what the OP described. But, feel free to post your example on top of mine :slight_smile: