I’m using ignition to log data from a heat treatment furnace controller via ModbusTCP. We read three variables.
- Temperature (float)
- BatchStatus (running or not)
- BatchNbr (integer)
The operator presses the start button on the machine (hardware). The BatchStatus flag goes high and the BatchNbr variable is incremented by 1. The machine runs its cycle and at the end, BatchStatus goes false.
I have written a script which populates a simple SQL table with the BatchNbr and the start and stop
timestamps. Using this lookup table, I can then query ignition’s tag historian for the temperature profile associated with the heat treatment batch. This is used by the reporting module to print out a cert for the batch. The script is currently triggered from value change on BatchStatus. This all works fine. Sort of.
Occasionally there is a delay between the BatchStatus going high and the BatchNbr incrementing. I’m not sure if this is a charactestic of the machine or ignition’s polling rate. Anyway, this results in a race condition. If BatchNbr increments first, this causes no problems. However, if BatchNbr increments after BatchStaus, the new row in my lookup table is populated with the batch number of the previous run.
I have thought about triggering based on BatchNbr, but this doesn’t change at the end of the run - only at the start of a run.
I have made this work using a 1 second sleep which is not ideal. See code below. When the BatchStatus flag goes high, the script waits for 1 second to allow the BatchNbr variable to be incremented. The SQL commands are then generated and executed. This works well but, having read a bit about it, I’m not keen on leaving the sleep function in place.
I’ve tried invoke later with no luck, but I understand that this does not work on gateway scripts (client only).
- Does anyone have any suggestions for improving the sleep section of this code? An improvement would be to have some logic which determines if the BatchNbr variable has also changed
- Which is preferable, adding the script to Gateway Events or leaving it as a Tag Event Script in the tag editor?
def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents): """ Fired whenever the current value changes in value or quality. Arguments: tag: The source tag object. A read-only wrapper for obtaining tag properties. tagPath: The full path to the tag (String) previousValue: The previous value. This is a "qualified value", so it has value, quality, and timestamp properties. currentValue: The current value. This is a "qualified value", so it has value, quality, and timestamp properties. initialChange: A boolean flag indicating whether this event is due to the first execution or initial subscription. missedEvents: A flag indicating that some events have been skipped due to event overflow. """ from time import sleep # This will pause execution of the script for 1 second. This delay allows the batch number to # update as there is often a delay of a few ms after the batch status flag goes high. sleep(1) # Set the table tableTime = "STS_HT_Time" Database = "hamjet_report" # Read the system time timeNow = system.tag.read("[System]Gateway/CurrentDateTime").value # Read the new (or current) batch number. batchNow = system.tag.read("[HamJet_Tags]Stainless Steel/Heat Treatment/BatchNbr").value # If the batch status has changed to 1 (from 0) then... if (currentValue.value == 1 & currentValue.quality.isGood()): #set the arguments for the SQL query, args = [batchNow, timeNow] # Build the query string command = "INSERT INTO " + tableTime + " (BatchNbr, StartTime) VALUES (? ,?)" # Run the query system.db.runPrepUpdate(command, args, Database) # If the batch status has changed to 0 (from 1) then... elif (currentValue.value == 0 & currentValue.quality.isGood()): #set the arguments for the SQL query, args = [timeNow, batchNow] # Build the query string command = "UPDATE " + tableTime + " SET StopTime = ? WHERE BatchNbr = ?" # Run the query system.db.runPrepUpdate(command, args, Database)