Timer script executing multiple times

Hello All,

I tried reaching out to Ignition technical support but looks like they don’t support 7.7.1 anymore. We are having issues with some timer scripts. We run Batch Program on multiple tanks. The active Tank recipes are loaded to a database. We have different operations and Steps withing operations. Operators use UI’s to flow through the operations. PLC holds the information in tags like Operation ID, step etc..A done button for example on the UI tells the PLC that a particular step is completed and moved to next step. Apart from this there are also two scripts, A tag change script that logs to a database every time the operation or step tags change(There is a unique Batch record code) for each step and a Timer Script that continuously browse through the current recipe table in the database and Batch record code. This timer script sometimes executes multiple times within a second which causes skipping within steps and operations sometimes. The issue is intermittent, so I cannot say it is a badly written script. I see that people had issues with Timer scripts executing multiple times. Engineers at our plant explored options to allocate more resources like memory to run Ignition. The issue did not resolve. Please help. I have a Wrapper log that is hard to understand.

Any help is highly appreciated.

Share your complete script. (I can't image anyone being able to help without that.)

1 Like
Script with syntax highlighting
##########################################################
# NAME: goToNextOp
# DESCRIPTION: When an operation has been completed, set that operation
# to "COMPLETE" in the database, signal the PLC to reset the operation
# status and wait until it is finished, then set the next operation in 
# the database to "RUNNING". When a running operation has been found, it
# copies the new operation's information from the database to the PLC
# batch program.
# PARAMETERS: 	PLCbubbleNum (active bubble number in the PLC, if this does
#				not match the bubble number from the database, then the
#				PLC information needs to be updated.)
#				opComplete (trigger from PLC indicating an operation has finished)
# REVISION: 1.3 08/31/17
# REV. NOTE: Added "nextOpStep" to prevent issues where certain functions are
# repeated or triggered out of order.
##########################################################

# send a query to the DB, requesting all running operations
#RU 03/05/2019 -- Update query so that it also obtains all the PAUSED operations
#RU 09/13/2019 -- Update query so that it also obtains all the PAUSED-APP6 operations
table = system.db.runQuery("SELECT * FROM RUNNING WHERE STATUS='RUNNING' or STATUS='PAUSED' or STATUS='PAUSED-APP6'")
commDelay = [False]*75  # if set to true, this means the 'opComplete' bit has
						# already been read, so the batch should not be processed again
batchCleared = False	# indicates batch has been cleared, so OP_ID should not be modified
						
# update cycle number for logging/debugging
cycleNum = system.tag.read("IgnitionStatus/GoToNextOpCycle").value
if cycleNum > 9999:
	cycleNum = 0
else:
	cycleNum = cycleNum + 1
system.tag.write("IgnitionStatus/GoToNextOpCycle",cycleNum)
cycleNum = str(cycleNum)

# CHECK FOR RUNNING OPERATIONS IN THE DATABASE
for row in table: # iterate through all running operations that were found
	vesselID = shared.Functions.vesselID(row[0][1:]) # change vessel name to vessel ID		
	tagPath = "Vessel/Vessel_" + vesselID + "_/Batch/BubbleNum" # set tag path
	PLCbubbleNum = system.tag.read(tagPath).value # get current bubble number from PLC
	nextOpStep = system.tag.read("IgnitionStatus/NextOp_Step_" + vesselID).value # get nextOp step number
	
	# if current bubble number of the operation does not match the db value
	# and steps 1 and 2 have already been completed (or it is the first operation
	# in the batch), then start the next operation.
	if (PLCbubbleNum != row[2]) and (nextOpStep == 3 or row[2] == 1):
		if system.tag.read("Vessel/Vessel_" + vesselID + "_/Batch/OperationComplete" ).value == 0:
			print "Cycle:" + cycleNum + " 3. Bubble: " + str(row[2]) + " started on vessel " + row[0][1:] + ". Old bubble number=" + str(PLCbubbleNum)
			system.tag.write(tagPath,row[2]) # write new bubble number to PLC
			system.tag.write("IgnitionStatus/NextOp_Step_" + vesselID,1) # update nextOp step number
			shared.Functions.startOperation(row,vesselID) # start operation function
		else: # opComplete has not been cleared, do not start the next operation yet
			commDelay[int(vesselID)] = True
			print "Cycle:" + cycleNum + ", waiting on opComplete for vessel " + row[0][1:]
	tagPath2 = "Vessel/Vessel_" + vesselID + "_/Batch/CurrentStep" # set tag path
	if system.tag.read(tagPath2).value == 0: # current step = 0 found, always start on step 1
		print "Step 0 found on vessel " +  vesselID + " - reassigned to step 1."
		system.tag.write(tagPath2,1)
		
	#moved checking for PAUSED operation in a different script
			
	#RU 03/26/2019 Add the following line of code to determine if the lot is an adjustment.	
	current_OPID = system.tag.read("Vessel/Vessel_" + vesselID + "_/Batch/OP_ID")
	
	if current_OPID.value  == 1:
		currentLot = system.tag.read("Vessel/Vessel_" + vesselID + "_/Batch/ProductLot/STRING")
		currentLotLen = system.tag.read("Vessel/Vessel_" + vesselID + "_/Batch/ProductLot/LEN")
		checkAdjPath = "Vessel/Vessel_" + vesselID + "_/Adjusted"
		
		if currentLot.value[currentLotLen.value - 1] == "0":
			system.tag.write(checkAdjPath,0)
		else:
			system.tag.write(checkAdjPath,1)
		
# CHECK FOR COMPLETED OPERATIONS AND MOVE TO NEXT BUBBLE NUMBER
i = 1 # initialize vessel ID index for searching
while i < 74: # look through each vessel
	j = str(i) # convert vessel ID to string
	opComplete = system.tag.read("Vessel/Vessel_" + j + "_/Batch/OperationComplete" ).value
	opID = system.tag.read("Vessel/Vessel_" + j + "_/Batch/OP_ID" ).value
	
	# if an operation is complete, and the PLC has been changed to "loading",
	# and the operation has not already been changed, then continue to next operation
	if opComplete == True and opID == 100 and not commDelay[i]:
	
		# reset batch control tags first
		system.tag.write("Vessel/Vessel_" + j + "_/Batch/OperationComplete",0) # unlatch op complete
		system.tag.write("Vessel/Vessel_" + j + "_/Batch/CurrentStep",1) # reset current step
		system.tag.write("Vessel/Vessel_" + j + "_/Workflow/HMI_PB/HMI_PB_0_",0) # unlatch done pb
		
		vessel = shared.Functions.vesselName(j) # load vessel name
			
		# get the currently completed operation to ensure it exists in the database
		currentOp = system.db.runQuery("SELECT TOP 1 Operation FROM RUNNING WHERE STATUS='RUNNING' AND VESSEL='V" \
		+ vessel +"'") # find the completed operation
		
		nextOpStep = system.tag.read("IgnitionStatus/NextOp_Step_" + j).value # get nextOp step number
		
		if len(currentOp) > 0 and (nextOpStep == 1): # confirm that the operation exists in the database
		
			print "Cycle:" + cycleNum + " 1. Completed operation " + currentOp[0][0] + " found on vessel: " + vessel
				
				# If the completed operation is 'FILL', record filter data	
			if currentOp[0][0] == "Fill":
				print "Fill operation completed, filter data recorded on vessel " + vessel
				shared.Functions.recordFilterData(j)
					
			system.db.runUpdateQuery("UPDATE RUNNING SET STATUS='COMPLETE' WHERE STATUS='RUNNING' AND VESSEL='V" \
			+ vessel +"'") # update completed operation to COMPLETE
			
			nextOp = system.db.runQuery("SELECT TOP 1 * FROM RUNNING WHERE STATUS='PENDING' AND VESSEL='V" \
			+ vessel + "' ORDER BY BUBBLE_NUM ASC") # find next PENDING op
			
			system.tag.write("IgnitionStatus/NextOp_Step_" + j,3) # update nextOp step number
			
			if len(nextOp) > 0: # Additional pending operations exists
				nextOpNum = str(nextOp[0][2])
				system.db.runUpdateQuery("UPDATE RUNNING SET STATUS='RUNNING' WHERE BUBBLE_NUM='" + nextOpNum + \
				"' AND VESSEL='V" + vessel +"'") # set next op to RUNNING
				
				print "Cycle:" + cycleNum + " 2. Set bubble number " + nextOpNum + " to 'Running' on vessel " + vessel + "."
				
			else: # There are no more pending operations, batch is complete
				shared.Functions.clearBatchValues(j) # clear batch values
				batchCleared = True
				system.db.runUpdateQuery("DELETE FROM RUNNING WHERE VESSEL='V" \ # remove the batch
				+ vessel + "'")
				print "Cycle:" + cycleNum + " No more pending operations for vessel: " + vessel + ", batch removed from DB."
			
		else: # batch does not exist, send error indication to PLC/iFIX
			system.tag.write("Vessel/Vessel_" + j + "_/Batch/OP_ID",99) # 99 = operation error condition
			opComplete = False
			system.tag.write("IgnitionAlarms/IgnitionAlarms_1_",vessel)
			print "ERROR: Vessel " + vessel + " has a completed operation but does not exist in the database!"
	
	# set current vessel to 'loading'		
	elif opComplete == True and opID != 100 and not commDelay[i] and not batchCleared:
		system.tag.write("Vessel/Vessel_" + j + "_/Batch/OP_ID",100)
		print "Cycle:" + cycleNum + "OP ID set to 100 ('loading') for vessel " + j + "."
		 
	i += 1 # increment vessel ID counter

Varsha, please see Wiki - how to post code on this forum.

1 Like

Is this a gateway timer event script, a client timer event script, or a Timer component event script?

Anyways, lots of ugly code practices, but no obvious flaw. Possibly in the shared.Functions library script.

1 Like

Its a gateway timer event script