How can you make One shot-esque script?

I have some script that emails out some data once a particular OPC tag is true. OPC tag is linked to a button and to a PLC input so that the data can be emailed whether it is triggered from pressing button in ignition screen or PLC. Pressing button works flawlessly but when the PLC triggers (with a One shot) nothing happens like there is not enough time in 1 scan to process. And without the one shot, one second in time emails out 5+times. Im wondering if this is a scan class issue or is there some script or function that I can write that will only allow the email script to run once even if the bit stays true until going back to 0.

OPC is not meant to follow the speed of the PLC. So if the PLC just has an input for a split second, Ignition will not be able to pick it up.

The only solution is to let the requester (PLC or button) set the bit high, trigger on the bit becoming high, and let the handler (the code that sends the mail) set the bit back to false.

Probably with some light polling attached to recover from failing communications (f.e. failing to set the bit back to false).

Don’t use a bit. Use a short integer. Have the PLC code increment it once per button press or once per other logic trigger. Monitor this integer with a gateway tag change event script. In Ignition, have an additional integer memory tag holding the last email trigger value. In the event script, check the new value from the PLC against the memory tag–if different, write the new value to the memory tag and proceed to send the emails.

If the PLC needs an acknowledge, make the memory tag an OPC tag instead, pointing at another integer in the PLC. That one must never be changed by PLC code.

No races. Doesn’t write to a register or bit or memory tag from multiple directions (avoid that like the plague, because it is a plague).


I had a similar situation where I was unable to change the PLC logic and was stuck with a bit to trigger the event when it changed to 1. In this case I used a memory tag to hold the last value read from the PLC.
In my event script if the PLC tag is 1 and the memory tag is 0, then update the memory tag to 1 and trigger the email. If the memory tag is already 1, then ignore it. If the PLC tag changes back to 0, then reset the memory tag to 0 and wait for the next 1 from the PLC.

I like pturmel solution, which would be more robust, especilly if acknowledgement is needed or events are triggered quite quickly, but this has been working well for a year without any problems.


Thanks for all your input guys but I found a fix in a small loophole. I understand what was mentioned about ignition not running at the same speed as the PLC. With that I found that the common element is time and that I could come up with a way to allow the script run once with my timed boolean running for 2 seconds(more than enough time for the plc to communicate the state to ignition). Using the event ‘property change’ scripting I have that bit trigger a memory bit in ignition that carries out the emailing script. By placing a NC bit in the PLC timing logic, I have the last piece of the email script set that NC bit true and then waiting 5 seconds. This allows the PLC bit to trigger on long enough for ignition to get the state and then kill it once the script completes. The wait logic allows for the script to complete the email out until PLC timer is completely off. Working like a champ now. I have started applying this to other components and works great.

Just did the following:
import time

tag = currentValue.value

#Script only processes on high property change not low
if tag == 1:



You could do this in a tag change script checking that the new value is true (so it doesn’t also execute when value changes to false) to run the script one shot without the time.sleep (avoid using sleep).

Yeah my issue was more so that once the script ran, it would process 3 times while the bit was true. This was sending multiple instances of my email out so the sleep function allows the script to process that while the bit goes low and then does not warrant multiple sends. Why do you recommend not using the sleep timer?

Sleep freezes the thread it is on (for instance, in the client, you can freeze the GUI). If you must use it (you don’t need it for what you’re doing here), put it in it’s own asynchronous thread.

Tag change scripts fire only when the tag changes (they do not repeat while it is true). The only issue is they’ll fire when the bit goes true, and when it goes false. To get just the true edge (so your code runs once each time tag becomes true), start your code in a gateway event tag change script something like:

# Ignore change due to subscription and only run on change from false to true.
if not initialChange and event.getValue():
	# Do something.

sleep on a tag change also has some nasty side effects, like stopping other tag updates.

So it’s best not to sleep in a tag script.

The best option is what @pturmel suggested. Just let the PLC increment an integer, and react on change. During the change script, you can find out the previous and the current value, so you can check if they’re really different, of if the change just comes from a connection loss of configuration update.

1 Like