from __future__ import absolute_import from django.conf import settings from common.models import * # (EP) OLD task base class from celery.task import Task # NEW task base class, but DOES NOT WORK (because celery3 and not 4 ?) !!! #from celery import Task from devices.PLC import PLCController from utils.JDManipulator import * import json import utils.Logger as L import majordome.tasks from celery import app, task import alert_manager.tasks from celery import shared_task log = L.setupLogger("MonitoringTaskLogger", "Monitoring") # EP #DEBUG_FILE = False DEBUG_FILE = True ''' Infinite task created at the program's start. Checks the plc status, parse it, analyse it, store it in db ''' class Monitoring(Task): timers = {} functions = {} state = None plcController = None majordome = None alert_manager = None alert_task = None majordome_task = None timer_status = None timer_tasks = None status_plc = None def run(self): print ("AGENT ENV: startup...") self.createTask() # (TRY TO) Connect to PLC # (non blocking if could not connect) self.setContext() print ("AGENT ENV: config PLC is (ip={}, port={})".format(self.plcController.ip, self.plcController.port)) # (EP) self.setTime() self.setTimers() print("AGENT ENV: my timers (check env status every {}s, check other agents every {}s)".format(self.timer_status, self.timer_tasks)) self.setTasks() print("AGENT ENV: Other Agents id read from DB (majordome={}, alert={})".format(self.majordome_task, self.alert_task)) self.loop() def createTask(self): try: # (EP) NO find() method available from Django ORM, c'est du n'importe quoi ce code (lastminute.com ?) !!!! #TaskId.objects.find(task="monitoring").delete() TaskId.objects.filter(task="monitoring").delete() except Exception as e: log.info(str(e)) # (EP) return always if no monitoring task to delete, and so monitoring task id is never saved !!! #return 1 TaskId.objects.create(task_id=self.request.id, task="monitoring") return 0 def setTasks(self): try: self.majordome_task = TaskId.objects.get(task="majordome") self.alert_task = TaskId.objects.get(task="alert_manager") except Exception as e: self.majordome_task = None self.alert_task = None return 0 def setContext(self): self.plcController = PLCController() self.state = "RUNNING" return (0) def setTimers(self): self.timer_status = 2 self.timer_tasks = 5 self.timers = {"timer_status": self.timer_status, "timer_tasks": self.timer_tasks} # (EP) "tasks": self.timer_tasks} # (EP) self.functions = {"timer_status": self.handleTimerStatus, self.functions = {"timer_status": self.handleStatus, "timer_tasks": self.handleTasks} return (0) def logDB(self, message: str): Log.objects.create(agent='Monitoring', message=message) ''' Function called by the main loop to check if the majordome and alert_manager are running ''' def handleTasks(self): # Reset timer total duration # (EP) self.timers["tasks"] = self.timer_tasks self.timers["timer_tasks"] = self.timer_tasks # TODO check majordome and alert_manager status if self.majordome_task is None: try: # (EP) bugfix !!! #self.monitoring_task = TaskId.objects.get(task="majordome") self.majordome_task = TaskId.objects.get(task="majordome") except Exception as e: if settings.USE_CELERY: try: # Mind this will raise an Exception if RABBITMQ is not started ! majordome.tasks.Majordome.apply_async() if settings.DEBUG and DEBUG_FILE: log.info(str(e)) except Exception as e2: print("YOU MUST START RABBITMQ before running PyROS !") exit() else: print("USE_CELERY is false => do not create Majordome task") if self.alert_task is None: try: self.alert_task = TaskId.objects.get(task="alert_manager") except Exception as e: # Mind this will raise an Exception if RABBITMQ is not started ! if settings.USE_CELERY: try: alert_manager.tasks.AlertListener.apply_async() if settings.DEBUG and DEBUG_FILE: log.info(str(e)) except Exception as e2: print("YOU MUST START RABBITMQ before running PyROS !") exit() else: print("USE_CELERY is false => do not create AlertListener task") # EP non car 0 = false #return 0 return True ''' Check if all devices info are consitants ''' def isStatusValid(self): return True ''' Extract content from the status dictionary ''' def extractFromDict(self, status): synthesis = {} devices = status["devices"] #print(devices) #is_safe_str = status["is_safe"] #mode = status["mode"] for device in devices: if device['valid'] == "yes": for value in device["device_values"]: synthesis[value["name"]] = value["value"] synthesis[value["name"] + "_unit"] = value["unit"] return synthesis # TODO ATTENTION SI DEUX DEVICES ONT LE MEME NOM # TODO SAVE SYNTHESIS def saveContent(self, content): devices = content[0]["devices"] print("saveContent") for device in devices: if device['valid'] == "yes": status = PlcDeviceStatus() try: database_device = PlcDevice.objects.get(name=device["device_name"]) except Exception as e: # plc = Plc.objects.first() database_device = PlcDevice.objects.create(name=device["name"]) status.device = database_device for value in device["device_values"]: status.setValue(value["name"], value["value"], value["unit"]) if value["name"] == "OutsideTemp": print(value["name"], value["value"]) #status.setValue("mode", mode) #status.setValue("is_safe", is_safe_str) status.save() ''' Parse status returned by plc ''' def parseStatus(self, status_plc): try: status = {} dict = json.loads(status_plc) if dict[0]["entity_name"] == "PLC_STATUS": if self.isStatusValid(): status = self.extractFromDict(dict[0]) self.saveContent(dict) else: # TODO HANDLE ERROR HERE pass except Exception as e: if DEBUG_FILE and settings.DEBUG: log.info(str(e)) self.status_plc = {} return 1 self.status_plc = status return 0 ''' Check if string is ouside ''' def isOutside(self, key): if key.find('Outside') != -1 or key.find('Rain') != -1\ or key.find("Wind") != -1 or key.find("Sky") != -1: return True elif key == "Pressure": return True elif key == "DewPoint": return True return False def isInside(self, key): if key.find('Inside') != -1 or key.find('Door') != -1: return True elif key == "status": return True elif key == "current": return True return False ''' Save status returned by plc ''' def saveStatus(self): outside = WeatherWatch() inside = SiteWatch() for key, value in self.status_plc.items(): if self.isOutside(key): outside.setAttribute(key, value) elif self.isInside(key): inside.setAttribute(key, value) outside.setGlobalStatus() inside.setGlobalStatus() outside.save() inside.save() #return 0 ''' Function called by the main loop to handle the plc status ''' def handleStatus(self): # Reset timer total duration self.timers["timer_status"] = self.timer_status status_plc = self.plcController.getStatus() #print("status ->" + status_plc) # Error while READING status ? if (self.plcController.isError(status_plc)): if (settings.DEBUG and DEBUG_FILE): log.info("Invalid PLC status returned (while reading) : " + str(status_plc)) Log.objects.create(message="Invalid PLC status returned (while reading) : ", agent="Monitoring") # EP Non car 1 = true #return (1) return False # (EP) if parseStatus() = THERE WAS AN ERROR !!! # Error while PARSING status ? if self.parseStatus(status_plc): if (settings.DEBUG and DEBUG_FILE): log.info("Invalid PLC status returned (while parsing) : " + str(status_plc)) Log.objects.create(message="Invalid PLC status returned (while parsing) : ", agent="Monitoring") # EP Non car 1 = true #return (1) return False print("Status received from PLC (read and parsed ok):") print(status_plc) #return self.saveStatus() #print(datetime.datetime.now()) Log.objects.create(message="Status received from PLC (read and parsed ok):", agent="Monitoring") self.saveStatus() return True ''' Main loop ''' def loop(self): i = 0 while (self.state != "SHUTDOWN"): print("-") print("-") print("-") print ("AGENT Monitoring (ENV): iteration "+str(i)+", (my state is " + self.state+") :") minimal_timer = min(self.timers, key=self.timers.get) time.sleep(self.timers[minimal_timer]) self.timers = {key: value - self.timers[minimal_timer] for key, value in self.timers.items()} for timer_name, timer_value in self.timers.items(): if (timer_value <= 0): if (timer_name in self.functions): # Handle the timer function self.functions[timer_name]() if (settings.DEBUG and DEBUG_FILE): log.info("Timer : " + str(timer_name) + " executed by monitoring") else: if (settings.DEBUG and DEBUG_FILE): log.info("Timer : " + str(timer_name) + "is not known by the monitoring") self.logDB("Timer " + str(timer_name) + " unknown") # EP : pas au bon endroit !!! #if (settings.DEBUG and DEBUG_FILE): #log.info("Timer : " + str(timer_name) + " executed by monitoring") i+=1 return (0) if (__name__ == "__main__"): m = Monitoring() m.run()