Opc read values efficiency gains or better approaches

I am interacting with an Allen Bradley PLC to gather data from equipment it is controlling. The basic setup is the PLC gathers information from the process and alerts Ignition with a handshake tag. Ignition stores data in database and resets the handshake. Only five values are exchanged (three of them are strings that could be about 900 bytes long). The code below executing in a tag change scrip is my current approach. I'm looking for advice on any efficiencies or completely different approaches. The turn around on this handshake is a few seconds and it's really adding up in the process time. I've thought of splitting the db write off in a separate thread so I can get right back to the PLC. However, it seems that it's the OPC reads/writes that are dominating the time.

def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
	
	# if the send tag change isn't from a restart and has just changed to true
	if((initialChange != True) and (currentValue.value == True)):
		# read the sent seqence number, increment it, return it and acknowledgement that we are in process
		sequenceResponse = system.opc.readValue("Ignition OPC UA Server", "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Cmd.SeqNum").value + 1
		taglist_1 = ["[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.Ready",
				   "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.Done",
				   "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.AckNum",
				   "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.AckSend",
				   "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.IP"]
		valuelist_1 = [False,False,sequenceResponse,True,True]
		system.opc.writeValues("Ignition OPC UA Server", taglist_1, valuelist_1)
		
		# capture the job number and weld data with direct opc reads from PLC
		taglist_2 = ["[Z1000_PLC]Program:Supervisor_PalletData.p_WeldData[0].JobNumber",
		   "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldData[0].WeldSummary",
	 	   "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldData[0].VoltageWF",
	       "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldData[0].CurrentWF",
	       "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldData[0].WeldX",
	       "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldData[0].WeldY"]
		
		qualifiedList = system.opc.readValues("Ignition OPC UA Server",taglist_2)
		
		jobNumber = qualifiedList[0].getValue()
		weldSummary = qualifiedList[1].getValue()
		voltageWF = qualifiedList[2].getValue()
		currentWF = qualifiedList[3].getValue()
		weldX = qualifiedList[4].getValue()
		weldY = qualifiedList[5].getValue()
		
		# insert values into the weld database
		parameters = {"welderID":40,"jobNumber":str(jobNumber),"weldSummary":str(weldSummary),"voltageWF":str(voltageWF),"currentWF":str(currentWF),"weldX":float(weldX),"weldY":float(weldY)}
		system.db.runNamedQuery("SPM_LineControl","Insert_Weld_Raw",parameters)
			
		# signal that we are no longer in process, done and ready for next weld
		taglist_3 = ["[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.IP",
					 "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.Done",
					 "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.AckSend",
					 "[Z1000_PLC]Program:Supervisor_PalletData.p_WeldReq[0].Stat.Ready"]
		valuelist_3 = [False,True,False,True]
		system.opc.writeValues("Ignition OPC UA Server", taglist_3, valuelist_3)

That is a tag's valueChange event, which is not a safe place to use any system.opc.* functions--you risk locking up all tag event processing. Similarly, it isn't safe to do DB operations either, except for fire-and-forget inserts through the Store and Forward system. Finally, it is usually racy to use a single handshake tag--use two, one only written by the PLC, and an echo that is only written by Ignition.

The correct place for such busy and dangerous code is a project library script function called from a tag change event in a project's gateway events. (Not on the tag.)

Finally, you need to investigate how well your device is actually performing, comms-wise. While AB Logix PLCs tend to support way more comms than other brands, in my experience, it is rather easy to screw that up with poor architecture (in the PLC) and with certain kinds of newbie mistakes (drag-n-drop of all the PLC tags from OPC browser to Ignition Tag Browser being #1).

Do you even know if your PLC comms are overloaded? (Device diagnostics.)

Some reading (follow links, too):

Phil,

Thanks for the quick response and advice, as always. I will move the code to the project library and initiate it from a gateway tag change. Vendor is responsible for the PLC side but I'm headed off to read the thread that you linked and maybe we can shore this up. Hopefully I can find some efficiencies along the way as this interaction is bogging down the whole process.

Reading a KB or two of mostly strings should take a dozen or so milliseconds, and following writes would be similarly quick. I suspect your difficulty with this task is because your comms are already badly overloaded.

Is interrogating the PLC an option?

I find it interesting that the PLC requires a handshake here at all…just to ensure that the recent data/job is stored to a DB? Any other reason?

PLC is talking to a welder to gather information that gets overridden with each weld. The handshake is to let Ignition know there is data to offload, before the next weld overrides it. The PLC could buffer this but going in we didn't anticipate the interaction taking very long.