Currently I have a UDT that I use to record the status of certain valve states based on if another machine is running or not. I use this state to write the start and end time to a SQL database that is needed for permit compliance. On top of this I have alerts set to send messages when the state has been high for a period of time so that the facilities crew can address the issue. I also have an alert set to the low state. Unfortunately, the default state is low, and there are often periods in which the high state has only been active for 10 to 15 seconds. Nothing needs to be done in these scenarios, but it ends up causing a lot of nuisance alarms. I think I’m just having a mental block on getting this figure out, but if someone has a good idea on how to eliminate that 10-15 second alarm, I’m open to ideas. I could in theory delay it going high for 15 seconds, but not really sure the best way to execute that. I’d rather not change any PLC code for this. I’m also putting my UDT in here as well as the tag change script.
def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
if not initialChange:
def work():
import time
time.sleep(10)
if currentValue.value is True:
# Save Current Timestamp to Start Time
bypassStartTimestamp = currentValue.timestamp
system.tag.writeBlocking(["[.]Start Bypass"], [bypassStartTimestamp])
# Clear Out End Timestamp
system.tag.writeBlocking(["[.]End Bypass"], [None])
else:
# Collect Data for Entry
bypassEndTimestamp = currentValue.timestamp
bypassStartTimestamp = system.tag.readBlocking(["[.]Start Bypass"])[0].value
oven = system.tag.readBlocking(["[.]Oven Name"])[0].value
minutesElapsed = system.date.minutesBetween(bypassStartTimestamp, bypassEndTimestamp)
# Write Data to the Database bypass_events table
system.db.runNamedQuery("generic", "Bypass", {"start_time": bypassStartTimestamp, "end_time": bypassEndTimestamp, "oven": oven, "minutes": minutesElapsed})
system.tag.writeBlocking(["[.]End Bypass"], [bypassEndTimestamp])
{
"name": "Bypass Event",
"tagType": "UdtType",
"tags": [
{
"name": "Oven Name",
"tagType": "AtomicTag",
"valueSource": "memory"
},
{
"dataType": "DateTime",
"formatString": "yyyy-MM-dd h:mm:ss aa",
"name": "Start Bypass",
"tagType": "AtomicTag",
"valueSource": "memory"
},
{
"dataType": "DateTime",
"formatString": "yyyy-MM-dd h:mm:ss aa",
"name": "End Bypass",
"tagType": "AtomicTag",
"valueSource": "memory"
},
{
"alarms": [
{
"CustomEmailMessage": {
"bindType": "Expression",
"value": "{InstanceName} + \" is in bypass and needs to be adressed immediately. Please ensure RTO is on and roof dampers are in proper position\""
},
"CustomEmailSubject": {
"bindType": "Expression",
"value": "{InstanceName} + \" is in bypass and needs to be adressed immediately. Please ensure RTO is on and roof dampers are in proper position\""
},
"CustomSmsMessage": {
"bindType": "Expression",
"value": "{InstanceName} + \" is in bypass and needs to be adressed immediately. Please ensure RTO is on and roof dampers are in proper position\""
},
"activeCondition": true,
"activePipeline": "generic/RTO Bypass",
"enabled": true,
"mode": "Equality",
"name": "Alarm",
"setpointA": 1.0,
"timeOnDelaySeconds": 600.0,
"voip.customMessage": {
"bindType": "Expression",
"value": "{InstanceName} + \" has been in bypass for 3 hours\""
}
},
{
"CustomEmailMessage": {
"bindType": "Expression",
"value": "{InstanceName} + \" is out of bypass. No more action is required.\""
},
"CustomEmailSubject": {
"bindType": "Expression",
"value": "{InstanceName} + \" is out of bypass. No more action is required.\""
},
"CustomSmsMessage": {
"bindType": "Expression",
"value": "{InstanceName} + \" is out of bypass. No more action is required.\""
},
"activePipeline": "generic/RTO Bypass OVer",
"deadband": 0.0,
"mode": "Equality",
"name": "Alarm 1",
"setpointA": 0.0,
"timeOnDelaySeconds": 10.0
}
],
"dataType": "Boolean",
"eventScripts": [
{
"eventid": "valueChanged",
"script": "\tif not initialChange: \n\t\t\n\t\tdef work():\n\t\t\timport time\n\t\t\ttime.sleep(10)\n\t\t\n\t\tif currentValue.value is True: \n\t\n\t\t\t# Save Current Timestamp to Start Time \n\t\t\tbypassStartTimestamp \u003d currentValue.timestamp \n\t\t\tsystem.tag.writeBlocking([\"[.]Start Bypass\"], [bypassStartTimestamp]) \n\t\n\t\t\t# Clear Out End Timestamp \n\t\t\tsystem.tag.writeBlocking([\"[.]End Bypass\"], [None]) \n\t\n\t\telse: \n\t\t\t# Collect Data for Entry \n\t\t\tbypassEndTimestamp \u003d currentValue.timestamp \n\t\t\tbypassStartTimestamp \u003d system.tag.readBlocking([\"[.]Start Bypass\"])[0].value \n\t\t\toven \u003d system.tag.readBlocking([\"[.]Oven Name\"])[0].value\n\t\t\tminutesElapsed \u003d system.date.minutesBetween(bypassStartTimestamp, bypassEndTimestamp) \n\t\n\t\t\t# Write Data to the Database bypass_events table \n\t\t\tsystem.db.runNamedQuery(\"Home_Market_Food\", \"Bypass\", {\"start_time\": bypassStartTimestamp, \"end_time\": bypassEndTimestamp, \"oven\": oven, \"minutes\": minutesElapsed}) \n\t\t\tsystem.tag.writeBlocking([\"[.]End Bypass\"], [bypassEndTimestamp])"
}
],
"executionMode": "FixedRate",
"historyEnabled": true,
"historyProvider": "SQLSever_WinAuth",
"name": "Bypass Status",
"tagType": "AtomicTag",
"value": false,
"valueSource": "expr"
}
]
}