From 11bfe74d2d380a76de6e9d56c69b8db7a9dcf81a Mon Sep 17 00:00:00 2001 From: Nico Melone Date: Tue, 4 Feb 2020 16:24:17 -0600 Subject: [PATCH] Added alert buffers and reminders --- data_points.py | 75 +++++++++++++++++++++++++++++--------------------- driver.py | 9 ++++-- 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/data_points.py b/data_points.py index 2a2dc33..90d223c 100644 --- a/data_points.py +++ b/data_points.py @@ -5,7 +5,7 @@ from pycomm.ab_comm.clx import Driver as clx from pycomm.cip.cip_base import CommError, DataError class DataPoint(object): - def __init__(self,changeThreshold=0,guaranteed=3600, name="datapoint",alertThreshold=[],alertCondition=[],alertResponse=[],alertContact=[], alertName=[]): + def __init__(self,changeThreshold=0,guaranteed=3600, name="datapoint",alertThreshold=[],alertCondition=[],alertResponse=[],alertContact=[], alertName=[],alertBuffer=[],alertReminder=[]): self.value = None self.lastvalue = None self.lastsend = 0 @@ -17,7 +17,11 @@ class DataPoint(object): self.alertResponse = alertResponse self.alertContact = alertContact self.alertName = alertName - self.alerted = [False] * len(self.alertThreshold) + self.alertBuffer = alertBuffer + self.alertReminder = alertReminder + self.alerted = [False] * len(self.alertCondition) + self.reminderTimer = [0] * len(self.alertCondition) + self.alertTimer = [0] * len(self.alertCondition) def checkSend(self,value): @@ -35,44 +39,53 @@ class DataPoint(object): "eq": "value == threshold", "gte": "value >= threshold", "lte": "value <= threshold", - "not": "value != threshold" + "not": "value != threshold", + + } + + oppositeConditions = { + "gt": "lt", + "lt": "gt", + "gte": "lte", + "lte": "gte", + "eq": "not", + "not": "eq" } - """for thres,cond,name,contact,alerted in zip(self.alertThreshold,self.alertCondition, self.alertName, self.alertContact,self.alerted): - #check value for alert threshold send back first alarmed value - evalVars = { - "value": value, - "threshold": thres - } - func = conditions.get(cond) - if func == None: - print("Not an available function: {}".format(cond)) - else: - if eval(func, evalVars) and not alerted: - - return {"alert": name} - else: - self.alerted = False - return None -""" for x in range(len(self.alerted)): #check value for alert threshold send back first alarmed value evalVars = { "value": value, - "threshold": self.alertThreshold[x] + "threshold": self.alertThreshold[x][0] } func = conditions.get(self.alertCondition[x]) if func == None: print("Not an available function: {}".format(self.alertCondition[x])) else: result = eval(func,evalVars) - if result and not self.alerted[x]: - self.alerted[x] = True - return {"alert": self.alertName[x], "contact": self.alertContact[x]} - elif result and self.alerted[x]: - pass - else: - self.alerted[x] = False + if result and not self.alerted[x]:#threshold crossed but not already alerted + if self.alertTimer[x] != 0 and time.time() - self.alertTimer[x] > self.alertBuffer[x]:#timer started and crossed threshold + self.alerted[x] = True + self.alertTimer[x] = 0 + self.reminderTimer[x] = time.time() + return {"alert": self.alertName[x] + " alarm", "contact": self.alertContact[x], "response": self.alertResponse[x]} + elif self.alertTimer[x] == 0:#timer not started yet + self.alertTimer[x] = time.time() + elif result and self.alerted[x]:#threshold crossed and already on alert + if self.alertReminder[x] and time.time() - self.reminderTimer[x] > self.alertReminder[x]: #reminder threshold crossed + self.reminderTimer[x] = time.time() + return {"alert": self.alertName[x] + " reminder", "contact": self.alertContact[x], "response": self.alertResponse[x]} + elif not result and self.alerted[x]:#threshold not crossed but previously alerted + evalVars["threshold"] = self.alertThreshold[x][1] + func = conditions.get(oppositeConditions.get(self.alertCondition[x])) + if eval(func,evalVars):#untrigger threshold crossed + if self.alertTimer[x] != 0 and time.time() - self.alertTimer[x] > self.alertBuffer[x]:#timer started and crossed threshold + self.alerted[x] = False + self.alertTimer[x] = 0 + #Send cleared alarm message + return {"alert": self.alertName[x] + " alarmed cleared", "contact": self.alertContact[x], "response": self.alertResponse[x]} + elif self.alertTimer[x] == 0:#timer not started + self.alertTimer[x] = time.time() return None class modbusDataPoint(DataPoint): @@ -90,8 +103,8 @@ class modbusDataPoint(DataPoint): pass class plcDataPoint(DataPoint): - def __init__(self,changeThreshold,guaranteed,name,plcIP='192.168.1.10',plcType='Micro800',tag=None,alertThreshold=[],alertCondition=[],alertResponse=[],alertContact=[], alertName=[]): - DataPoint.__init__(self,changeThreshold,guaranteed,name,alertThreshold,alertCondition,alertResponse,alertContact,alertName) + def __init__(self,changeThreshold,guaranteed,name,plcIP='192.168.1.10',plcType='Micro800',tag=None,alertThreshold=[],alertCondition=[],alertResponse=[],alertContact=[], alertName=[],alertBuffer=[],alertReminder=[]): + DataPoint.__init__(self,changeThreshold,guaranteed,name,alertThreshold,alertCondition,alertResponse,alertContact,alertName,alertBuffer,alertReminder) self.plcIP = plcIP self.plcType = plcType self.tag = tag @@ -112,7 +125,7 @@ class plcDataPoint(DataPoint): except CommError as cerr: print("Error: {}".format(cerr)) - return False + return False, None def write(self): pass diff --git a/driver.py b/driver.py index 0efba22..8e5f7e0 100644 --- a/driver.py +++ b/driver.py @@ -75,7 +75,9 @@ def run(config, device, port, host, rootCAPath): response = config['PLCData'][key]["alert"]["response"] contact = config['PLCData'][key]["alert"]["contact"] alertName = config['PLCData'][key]["alert"]["name"] - datapoint = plcDataPoint(changeThreshold,guaranteed,str(name),plcIP=str(plcIP),plcType=str(plcType),tag=str(tag),alertThreshold=threshold,alertCondition=condition,alertResponse=response,alertContact=contact, alertName=alertName) + buffer = config['PLCData'][key]["alert"]["buffer"] + reminder = config['PLCData'][key]["alert"]["reminder"] + datapoint = plcDataPoint(changeThreshold,guaranteed,str(name),plcIP=str(plcIP),plcType=str(plcType),tag=str(tag),alertThreshold=threshold,alertCondition=condition,alertResponse=response,alertContact=contact, alertName=alertName,alertBuffer=buffer,alertReminder=reminder) else: datapoint = plcDataPoint(changeThreshold,guaranteed,str(name),plcIP=str(plcIP),plcType=str(plcType),tag=str(tag)) datapoints.append(datapoint) @@ -98,13 +100,14 @@ def run(config, device, port, host, rootCAPath): val,alertMessage = datapoint.read() if alertMessage != None: alertMessage["location"] = locationID - filelogger.info("Publishin: {}\nTo Topic: {}".format(alertMessage,alm_topic)) + alertMessage["timestamp"] = datetime.now().isoformat() + filelogger.info("Publishing: {}\tTo Topic: {}".format(alertMessage,alm_topic)) myAWSIoTMQTTClient.publish(alm_topic,json.dumps(alertMessage),1) if datapoint.checkSend(val): message[datapoint.name] = val if message: message["timestamp"] = datetime.now().isoformat() - filelogger.info("Publishing: {}\nTo Topic: {}".format(message,dt_topic)) + filelogger.info("Publishing: {}\tTo Topic: {}".format(message,dt_topic)) myAWSIoTMQTTClient.publish(dt_topic, json.dumps(message),1) time.sleep(5)