Get rid of nuisance alarm

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"
    }
  ]
}

I wouldn't delay your state from going high because you still always want the real data. The better solution is if you go into the tag configuration and then the alarm properties there is a section for time delays. You can add active or clear delays so that the alarm only becomes true if it's high for more than X seconds.

Maybe I didn’t explain it properly. The problem is when it goes high for 10 or 15 seconds and then goes back to low I get that alert. I don’t really need that alert and it will always at some point go low, so even with an active delay on the low, it’ll always end up going low.

Is the low state supposed to be an alarm state?

If both the on and the off states of the valve are alarms, it doesn’t make much sense. If you’re just trying to capture the state into the same alarm journal, maybe set it to diagnostic priority

1 Like

Can you provide a timeline with a few scenarios you’d like to handle? Perhaps a progression of events:

  • State transitions to True : No alarm
  • 10 seconds later, state transitions to False : No alarm
  • Alarm remains off while status is false.
  • …eventually, alarm turns on…