Create momentary button using Vision standard button

I have an existing plc with a reset bit in it. I am creating an equivalent Vision project from an HMI program which uses a momentary button to turn on the reset bit in the plc. The plc doesn't turn the bit off so I have to do it within Ignition. There is an existing standard button I want to add scripting to in order to turn on the bit for a few seconds then turn it back off.

Thinking of using a client event script...any advice on how to do it? Should I create a memory tag that will trigger the client event script, then in the client event script confirm the plc reset bit is off, then turn the plc reset bit on, run a timer delay, then when the delay is complete turn off the plc reset bit and turn off the memory tag?

If you're going to do this (see various forum threads about why it's generally a bad idea)...

Why not just use the momentary button component?
https://docs.inductiveautomation.com/display/DOC81/Vision+-+Momentary+Button

Normally would but dont have the option to create another momentary button for alarm reset so have to figure out another option

I would use datetime memory tag that your Vision client can set to now() to request a pulse. Then I'd use a relatively fast timer event that computes whether the now() is in the two- or three- or five-second window, and writes that to the button tag when it doesn't match.

If you cannot change the PLC code to accept a change to a small integer as the trigger, then this sort of thing is the only solution I'd trust. (I'd normally recommend my EtherNet/IP class 1 driver's adapter mode paired with its custom momentary button, but that requires PLC changes, and in a brand that can scan EtherNet/IP.)

Edit: In case it wasn't clear, the timer event must be a gateway event that runs all the time, not a client event.

Thinking of creating a memory tag that gets activated once the button is pressed and then have a value change script to turn the bit on then turn the bit off. I'd probably have the same issue though because I have to then turn the memory bit off unless I can change the value of the memory bit itself within its own tag event script?

Can you have Ignition read the status of this bit? If it's high then clear the command?

wouldn't that require ignition to continuously monitor this bit? Oh, you mean have the button turn the bit on. Then within the tag itself write a tag change event script that says if the bit is high run a delay timer and then turn the bit off?

The momentary button turns on the reset in the PLC. Read this reset, when it turns on you know the momentary button did it's job, it can turn back off.

No. If you have an OPC tag for then Ignition is going to monitor that reset bit all the time anyways. And no, this isn't a tag event script, nor should your UI button write to the boolean. It should only write the timestamp to the memory tag.

The gateway timer event would examine the memory tag and the OPC tag and current time to decide when to write True and False to the OPC tag, skipping the write when the tag is already in the correct state.

Think of it as resetting a TON timer in Ignition with present of 2, 3, or 5 seconds, when the Ignition .TT bit is connected to the PLC's reset bit.

I got it to work by doing the following:

Write a 1 to the tag using the button.

Then on the tag event script for Value Changed:

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	import time
		
	if not initialChange and currentValue.value == 1:
		time.sleep(3)
		system.tag.writeBlocking("[.]State", 0)

After the bit is turned on via the button the script kicks in and waits three seconds to then turn the bit off.

BAD!

This is going to fail eventually, it will be bound for missed events of all sorts.

I would advise that you read @pturmel's suggestion again and follow it.

When you are doing something in Ignition that belongs in a PLC, you need to operate like a PLC. A gateway timer event in Ignition is the closest thing to a periodic task in a PLC. Generally, PLC tasks examine inputs, compute what the output values should be, and then set those outputs to the freshly computed state unconditionally. We can simplify that slightly by skipping the "set output" if already correct.

Do not use sleep in gateway events, and especially not in tag events (on the tag). You will regret it. (Do a search of this forum.)

2 Likes

So you are suggesting to have a memory tag where the current date and time are read into this tag when the button is pressed.
Then have a gateway event script which would run say every 3 seconds. What exactly would be in the gateway script related to this memory tag? I am missing that part. Could you explain what would be in the gateway event script? Thanks.

Tip: there's no need to keep deleting your posts when making changes. There's a pencil edit icon below each of your posts. Just edit the original instead.

To ping someone just add their username like this: @michael.black.

Thanks. Seems like I was replying to people and when sent it wasn't actually showing me the reply but wanted to make sure it went to that person. @Transistor

No, something like 500ms. (But not faster than the OPC rate for the reset bit.)
The event script would:

  • Read the memory tag and the current reset bit state into local variables.
  • Call system.date.now() to get a millisecond-precision current timestamp.
  • Use system.date.addSeconds() with the memory tag value to get the endpoint of the pulse.
  • If between the timestamps, and the bit is off, turn it on.
  • If not between the timestamps, and the bit is on, turn it off.
3 Likes

I see now. So with the changing of the starting timestamp it will move the range, then while you are in the range it will turn the bit on if off to enable the reset in the plc. Then once you are out of the range the 'on' time is complete and now turn the bit off.

From high level standpoint why would time.sleep function not consistently work?

It ties up a thread that Ignition needs to do other things. It also can't handle someone pressing the button multiple times. And any hiccup could leave the bit on. With the "periodic task" approach, any transient problems due to comms will be corrected when comms are restored.

2 Likes

Took your advice and got it working on script console:

almResetBitState = system.tag.readBlocking('[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State')
print almResetBitState[0].value
startTimeRange = system.tag.readBlocking('[RTP_SistemTek]Charge Car/Button/AlmResetPBTimeStamp')
print startTimeRange[0].value
endTimeRange = system.date.addSeconds(startTimeRange[0].value, 20)
print endTimeRange
currentTime = system.date.now()
print currentTime

if system.date.isBetween(currentTime, startTimeRange[0].value, endTimeRange):
	print "Between"
	if almResetBitState[0].value == 0:
		system.tag.writeBlocking('[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State', 1)
else:
	print "Not Between"
	if almResetBitState[0].value == 1:
		system.tag.writeBlocking('[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State', 0) 

Essentially did a copy/paste onto the gateway event timer script and doesn't work. Even when you are in the time range it is not entering the initial IF statement....thoughts?

logger = system.util.getLogger("myLogger")

#read in alarm reset bit state
bitState = system.tag.readBlocking('[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State')
#logger.info("bitstate: " + str(bitState))
#read in timestamp when alarm acknowledge/reset button was pushed
startTimeRange = system.tag.readBlocking('[RTP_SistemTek]Charge Car/Button/AlmResetPBTimeStamp')
startTime = startTimeRange[0].value
#logger.info("startTime: " + str(startTime))
#calculate end of time range by adding seconds to starting time range
endTimeRange = system.date.addSeconds(startTimeRange[0].value, 20)
#logger.info("endTimeRange: " + str(endTimeRange))
#access current time
currentTime = system.date.now()
#logger.info("currentTime: " + str(currentTime))
#determine if button push is within time range and if so turn the bit on/off based
#   on its current state
isBetween = system.date.isBetween(currentTime, startTime, endTimeRange)
logger.info("isbetween " + str(isBetween))
if system.date.isBetween(currentTime, startTime, endTimeRange):
	logger.info("inside Big If statement" )
#	print "Between"
	if bitState[0].value == 0:
		logger.info("bit state 0 entered")
		system.tag.writeBlocking(['[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State'], [1])
#		logger.info(str(write_code.isGood()))
else:
#	print "Not Between"
#	logger.info("else entered")
	if bitState[0].value == 1:
#		logger.info("bit state 1 entered")
		system.tag.writeBlocking(['[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State'], [0])
#		logger.info(str(write_code.isGood()))

Don't see anything technically wrong with the script, though I would write it like this to, 1.) read all of the tags at once, and 2.) be a bit more pythonic.

logger = system.util.getLogger("myLogger")

bitState,startTime = system.tag.readBlocking(['[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State','[RTP_SistemTek]Charge Car/Button/AlmResetPBTimeStamp'])

endTimeRange = system.date.addSeconds(startTime.value, 20)
currentTime = system.date.now()

isBetween = system.date.isBetween(currentTime, startTime.value, endTimeRange)
logger.info("isbetween " + str(isBetween))
if isBetween and not bitState.value:
	logger.info("bit state 0 entered")
	system.tag.writeBlocking(['[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State'], [1])
elif not isBetween and bitState.value:
	system.tag.writeBlocking(['[RTP_SistemTek]Charge Car/Button/Alarm-Reset_Button/State'], [0])

Your script should work, are you seeing any errors in the Logs?