Gateway Script executing on tag value change

Good day, I am trying to get a gateway script to execute via tag value change. I can get it to run when I use a boolean, but I need a memory tag that contains text to execute it. The tag gets changed when my RFID antenna reads the RFID tag.

Here is what I have so far, can anyone help me troubleshoot this?

Now heres the script:

import datetime
python_time = datetime.datetime.now()
sql_datetime = python_time.strftime('%Y-%m-%d %H:%M:%S')
#START of data parse for WS to YSF for ticket number
if system.tag.readBlocking("[default]RFID/Waste Scale/Green Light")[0].value == True:
	TID = system.tag.readBlocking("[default]RFID/Waste Scale/TagID")[0].value #'A2163400' test tag number

	dataset = system.db.runPrepQuery("SELECT TOP 1 * FROM WasteBarrelStatus WHERE TID = ? ORDER BY TIME DESC", [TID], 'IgnitionHud')

	Time = sql_datetime #Time weight was recorded into table
	Status = dataset[0][8]
	MachineNum = dataset[0][6]
	FinishLot = 'O5177I22' #dataset[0][4] test lot
	Dept = dataset[0][5]
	BarrelType = dataset[0][7]
	PosNum = dataset[0][3]
	TID = dataset[0][0]
	BarrelNum = dataset[0][1]
	ComponentNum = dataset[0][10]
	Denier = dataset[0][11]
	GrossWeight = int(system.tag.readBlocking("[default]RFID/Waste Scale/Scale Weight Lbs")[0].value) #200

#END of data parse for WS to YSF for ticket number
	
#START of WS to YSF for ticket number

	if Status == 'newFill':
		system.tag.writeBlocking("[default]RFID/Waste Scale/Red Light", 0)
	wsdata = system.ws.runWebService("YSF/RecordInProcessQA",
	None,
	{
	  'recordInProcessDetailRequest': {
	    'userName': "IGNITION",
	    'department': Dept,             
	# x   'productionDate': "2023-05-23",
	#    'shift': "J",                    #Use Start or completion shift
			    'machine': MachineNum,
	# x    'employeeNumber': "",
	    'finishedLot': FinishLot,
	# x    'scheduledLot': "",
	# x    'component': "",
	    'reasonCode': "003",             # Asp Waste Code = 003
	# x    'positionDesc': "",
	    'buggyType': BarrelType,
	# x    'buggyWeight': 0,
    	'cones': 0,                        # Leave as zero
    	'detailClass': "wast",            # must be lower case
    	'grossWeight': GrossWeight,     
	# x    'netWeight': 0
  	}
	})

	Status = 'Weighed'
	TicketNum = wsdata.getChild(0).toDict()['recordInProcessDetailResponse']['ticketNumber']
#END of WS to YSF for ticket number
	system.db.runPrepUpdate("INSERT INTO WasteBarrelStatus (Time, Status, GrossWeight, MachineNum, FinishLot, Dept, BarrelType, PosNum, TID, BarrelNum, ComponentNum, Denier, TicketNum) Values(?,?,?,?,?,?,?,?,?,?,?,?,?)",[Time, Status, GrossWeight, MachineNum, FinishLot, Dept, BarrelType, PosNum, TID, BarrelNum, ComponentNum, Denier, TicketNum])

#	else:
#		system.tag.writeBlocking("[default]RFID/Waste Scale/Red Light", 1)

As stated before I can get this to run either with a boolean driving it or executing in the script console, just not when the tag value changes.

I'd start with the obvious thing, if the same script works in the script console:
Decorate your code with logger statements so you can see where in the execution flow things are dropping out. Maybe a string comparison isn't what you expect (leading/trailing spaces? capitalization?) or a value isn't quite matching what you think it is. Initialize a logger at the top of the script with logger = system.util.getLogger("myscriptdebug") and then call into it with logger.info("someString").

As a minor aside...

Prefer the system.date functions over Python's datetime for best interop with other Java parts, such as database insertions.
And also

Prefer reading all the tags involved in an operation in one call to system.tag.readBlocking(). You're paying way, way more overhead for the serial calls than you are to read an "unnecessary" tag or two in the first read.

1 Like

when running the logger in script, I just watch the gateway logs and see what pops up, correct?

Yep, when the script is running on the gateway, system.util.logger messages will show up directly in the gateway log UI + the wrapper log, if you prefer to watch that.

I added logs to the code in several places, the logger returned this:

that logger is here:

logger = system.util.getLogger("RFIDscriptdebug")
import datetime
python_time = datetime.datetime.now()
sql_datetime = python_time.strftime('%Y-%m-%d %H:%M:%S')
#START of data parse for WS to YSF for ticket number
logger.info("RFID_debug_1")
if system.tag.readBlocking("[default]RFID/Waste Scale/Green Light")[0].value == True:

Does that mean Im not seeing that tag be true?

Could it be that when my TagID value changes my GreenLight is still false? It changes to True a few seconds after to gather a stable weight signal. Thats why its not running through the script?

Quite possible. Even if they change together in the PLC, you can't be sure of the order they will be updated by the OPC subscription. When dealing with synchronization issues, the general solution is for everything except the trigger itself to be obtained with system.opc.readValues(), not any kind of tag read.

It did seem to be a sync problem. I added a "while" loop until the GreenLight became true. After it became True the script ran like it should have.

I really appreciate you guys letting me bounce questions around.

That's a huge no-no for tag events. You can lock up your gateway's tag system.

How so? I dont wanna lock nothing up, lol

This looks like a project tag change script, so while still not recommended, not as dangerous as doing it in a tag event script.

1 Like

Tag events run on a small thread pool (three, by default). If you have three tag events running simultaneous, either spinning or sleeping, no other tag events will run at all.

Not wrong, just not applicable.

Do note that if your green light tag never becomes true, you will be burning CPU indefinitely. If you were trying to monitor multiple tags with one script, the other tag changes would be blocked.

You should use system.opc.readValues(). Really. (This is basically what happens when you set a SQL Bridge transaction group to OPC Read mode. The trigger is still subscribed, but everything else gets a fresh read after the trigger.)

Thanks, I will make the appropriate changes.

Prefer reading all the tags involved in an operation in one call to system.tag.readBlocking() . You're paying way, way more overhead for the serial calls than you are to read an "unnecessary" tag or two in the first read.

Hi @PGriffith , can you explain this a bit more? I do readBlocking instructions all the time to single tags. What do you mean reading all tags involved? If I have a Ignition UDT, you mean its better to read the whole UDT at once if I will be using multiple tags from it? If so, how then do you reference the elements in the Ignition UDT? TIA.

Within the context of a single script, try to read all the individual tags you can in the largest "chunk" possible. That is, instead of running a loop from station 1 to 10, reading the motor speed of each station inside the loop and acting on it, read all 10 motor speeds in one operation and then loop over the resulting values.

Ignition UDTs are basically just folders of tags with some window dressing. They don't represent any optimization inside of Ignition with regard to bulk reading or anything; all the tags inside a UDT are (basically) exactly the same as independent Ignition tags.

3 Likes

Hi @PGriffith thanks for the response. Just to make sure I am tracking with you, when you say to read the largest chunk possible, do you mean something like below (if not what is a better way to read "chunks":

Yes, that's the way.

2 Likes

Although I'll add that that particular example is needlessly verbose. I think I've had this discussion with the training department before and lost, but basically for index in range(len(something)) is almost never needed in Python; instead you should just iterate over the sequence object directly:

values = system.tag.readBlocking(paths)
for value in values:
    print value
3 Likes

Also, when there aren't too many values, I typically assign to reasonable local variable names (called "unpacking" in python). Like so:

....
alm1, alm2, alm3 = [x.value for x in values]

print alm1
print alm2
print alm3
3 Likes