From c53a13e0429855aaeae07da38ff391b160b81a4e Mon Sep 17 00:00:00 2001 From: Jeremy Date: Fri, 27 Jan 2017 17:48:26 +0100 Subject: [PATCH] Updating a lot of things (spotted a bug in scheduler) --- logs/2 | 1 - pyros.py | 39 ++++++++++++++++++++++++++++++++++----- simulators/camera/cameraNIRSimulator.py | 13 ++++++++++++- simulators/camera/cameraVISSimulator.py | 13 ++++++++++++- simulators/resources/routine_request_01.xml | 4 ++-- simulators/resources/routine_request_07.xml | 4 ++-- simulators/resources/routine_request_08.xml | 4 ++-- simulators/resources/routine_request_09.xml | 4 ++-- simulators/resources/routine_request_10.xml | 4 ++-- simulators/telescope/telescopeSimulator.py | 3 +++ src/analyzer/analyses/analysis_1.txt | 0 src/analyzer/analyzer | Bin 0 -> 15648 bytes src/analyzer/analyzer_source/calibrations/dark.fits | 0 src/analyzer/analyzer_source/calibrations/flat.fits | 0 src/analyzer/analyzer_source/main.cpp | 22 ++++++++++++++++++++++ src/analyzer/calibrations/dark.fits | 0 src/analyzer/calibrations/flat.fits | 0 src/analyzer/tasks.py | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------- src/common/models.py | 8 ++++++++ src/devices/Device.py | 5 ++++- src/majordome/tasks.py | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------------------------------------------- src/observation_manager/calibrator | Bin 0 -> 15648 bytes src/observation_manager/calibrator_source/main.cpp | 22 ++++++++++++++++++++++ src/observation_manager/tasks.py | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------------------------------------------- src/pyros/settings.py | 15 +++++++-------- src/pyros/settings.py.bak | 15 +++++++-------- src/scheduler/Scheduler.py | 10 +++++----- src/scheduler/tasks.py | 5 ++++- src/utils/JDManipulator.py | 3 +++ src/utils/StatusManager.py | 16 ++++++++++++++++ 30 files changed, 495 insertions(+), 246 deletions(-) delete mode 100644 logs/2 delete mode 100644 src/analyzer/analyses/analysis_1.txt create mode 100755 src/analyzer/analyzer create mode 100644 src/analyzer/analyzer_source/calibrations/dark.fits create mode 100644 src/analyzer/analyzer_source/calibrations/flat.fits create mode 100644 src/analyzer/analyzer_source/main.cpp delete mode 100644 src/analyzer/calibrations/dark.fits delete mode 100644 src/analyzer/calibrations/flat.fits create mode 100755 src/observation_manager/calibrator create mode 100644 src/observation_manager/calibrator_source/main.cpp create mode 100644 src/utils/StatusManager.py diff --git a/logs/2 b/logs/2 deleted file mode 100644 index 9daeafb..0000000 --- a/logs/2 +++ /dev/null @@ -1 +0,0 @@ -test diff --git a/pyros.py b/pyros.py index a4e1988..b91c854 100644 --- a/pyros.py +++ b/pyros.py @@ -436,6 +436,14 @@ class Pyros(AManager): self.migrate() return 0 + def reset_config(self): + self.changeDirectory("src") + self.replacePatternInFile("CELERY_TEST = True", "CELERY_TEST = False", "pyros/settings.py") + self.replacePatternInFile("SIMULATOR = True", "SIMULATOR = False", "pyros/settings.py") + self.addExecuted(self.current_command, "reset configuration") + self.changeDirectory("..") + return 0 + def unittest(self): self.changeDirectory("src") self.execProcessFromVenv(self.venv_bin + " manage.py test common scheduler routine_manager user_manager alert_manager.tests.TestStrategyChange") @@ -476,13 +484,10 @@ class Pyros(AManager): self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q majordome_q -n pyros@majordome -c 1") self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q scheduling_q --purge -n pyros@scheduling -c 1") - self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q execute_sequence_q --purge -n pyros@execute_sequence -c 1") self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q execute_plan_vis_q --purge -n pyros@execute_plan_vis -c 1") - self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q execute_plan_vis_q --purge -n pyros@execute_plan_nir -c 1") + self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q execute_plan_nir_q --purge -n pyros@execute_plan_nir -c 1") self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q create_calibrations_q --purge -n pyros@create_calibrations -c 1") self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q analysis_q --purge -n pyros@analysis -c 1") - self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q system_status_q --purge -n pyros@system_status -c 1") - self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q change_obs_conditions_q --purge -n pyros@change_obs_conditions -c 1") self.changeDirectory("..") return 0 @@ -520,7 +525,7 @@ class Pyros(AManager): self.printColor(Colors.GREEN, "The simulator run on a temp database : src/testdb.sqlite3") self.printColor(Colors.GREEN, "The simulation will be ended by the task 'simulator herself'") self.printColor(Colors.GREEN, "If you want to shutdown the simulation, please run :") - self.printColor(Colors.GREEN, "CTRL-C or ./pyrosrun.sh kill_simulation") + self.printColor(Colors.GREEN, "CTRL-C or python pyros.py kill_simulation") self.printColor(Colors.GREEN, "If the simulation isn't correctly killed, please switch the variable") self.printColor(Colors.GREEN, "CELERY_TEST in src/pyros/settings.py to false") self.printFullTerm(Colors.WARNING, "SUMMARY") @@ -533,6 +538,10 @@ class Pyros(AManager): self.changeDirectory("..") self.singleWorker("scheduling") self.singleWorker("majordome") + self.singleWorker("execute_plan_vis") + self.singleWorker("execute_plan_nir") + self.singleWorker("create_calibrations") + self.singleWorker("analysis") self.sleep(3) procs = [] self.changeDirectory("simulators") @@ -646,6 +655,20 @@ class Pyros(AManager): self.changeDirectory("..") return 0 + def mysql_on(self): + self.changeDirectory("src") + self.replacePatternInFile("MYSQL = False", "MYSQL = True", "pyros/settings.py") + self.addExecuted(self.current_command, "Switch to mysql") + self.changeDirectory("..") + return 0 + + def mysql_off(self): + self.changeDirectory("src") + self.replacePatternInFile("MYSQL = True", "MYSQL = False", "pyros/settings.py") + self.addExecuted(self.current_command, "Switch to sqlite") + self.changeDirectory("..") + return 0 + def __init__(self, argv): super(Pyros, self).__init__(argv) self.commandMatcher = { @@ -657,9 +680,12 @@ class Pyros(AManager): "clean_logs": self.clean_logs, "test": self.test, "migrate": self.migrate, + "mysql_on": self.mysql_on, + "mysql_off": self.mysql_off, "makemigrations": self.makemigrations, "updatedb": self.updatedb, "unittest": self.unittest, + "reset_config": self.reset_config, "test_all": self.test_all, "celery_on": self.celery_on, "init_database": self.init_database, @@ -681,7 +707,10 @@ class Pyros(AManager): "clean_logs": "clean the log directory", "test": "launch the server tests", "migrate": "execute migrations", + "mysql_on": "switch the database to be used to MYSQL", + "mysql_off": "switch the database to be used usage to SQLITE", "makemigrations": "create new migrations", + "reset_config": "Reset the configuration in settings.py", "help": "Help message", "updatedb": "Update the database", "kill_server": "Kill the web server on port 8000", diff --git a/simulators/camera/cameraNIRSimulator.py b/simulators/camera/cameraNIRSimulator.py index c091339..390352e 100644 --- a/simulators/camera/cameraNIRSimulator.py +++ b/simulators/camera/cameraNIRSimulator.py @@ -10,7 +10,7 @@ from utils.StatusManager import StatusManager class CameraNIRSimulator(Device, StatusManager): status = {"status" : "VALID"} - images_folder = '../../src/misc/images' + images_folder = '../../images_folder/' exposure_time = 5 shutter_time = 3 cooler_timer = 5 @@ -42,6 +42,17 @@ class CameraNIRSimulator(Device, StatusManager): else: answer = "Invalid cmd for GET: " + cmd self.cameraNIRPrint(answer) + elif (cmd_type == "DO"): + if (cmd == "CAPTURE"): + plan_name = args[0] + nb_images = int(args[1]) + duration = args[2] + i = 0 + while i < nb_images: + with open(self.images_folder + plan_name + os.sep + "nir_image_"+str(i), "w") as f: + f.write(plan_name+" "+str(nb_images)+" "+str(duration)) + i += 1 + answer = "CAPTURING" else: self.cameraNIRPrint("Ignored message " + data) diff --git a/simulators/camera/cameraVISSimulator.py b/simulators/camera/cameraVISSimulator.py index 6fbf457..e86f8aa 100644 --- a/simulators/camera/cameraVISSimulator.py +++ b/simulators/camera/cameraVISSimulator.py @@ -10,7 +10,7 @@ from utils.StatusManager import StatusManager class CameraVISSimulator(Device, StatusManager): status = {"status" : "VALID"} - images_folder = '../../src/misc/images' + images_folder = '../../images_folder/' exposure_time = 5 shutter_time = 3 cooler_timer = 5 @@ -42,6 +42,17 @@ class CameraVISSimulator(Device, StatusManager): else: answer = "Invalid cmd for GET: " + cmd self.cameraVISPrint(answer) + elif (cmd_type == "DO"): + if (cmd == "CAPTURE"): + plan_name = args[0] + nb_images = int(args[1]) + duration = args[2] + i = 0 + while i < nb_images: + with open(self.images_folder + plan_name + os.sep + "nir_image_" + str(i), "w") as f: + f.write(plan_name + " " + str(nb_images) + " " + str(duration)) + i += 1 + answer = "CAPTURING" else: self.cameraVISPrint("Ignored message " + data) diff --git a/simulators/resources/routine_request_01.xml b/simulators/resources/routine_request_01.xml index 17c3b39..05e9803 100644 --- a/simulators/resources/routine_request_01.xml +++ b/simulators/resources/routine_request_01.xml @@ -1,8 +1,8 @@ - + - + diff --git a/simulators/resources/routine_request_07.xml b/simulators/resources/routine_request_07.xml index 973c2e9..fb9f955 100644 --- a/simulators/resources/routine_request_07.xml +++ b/simulators/resources/routine_request_07.xml @@ -1,8 +1,8 @@ - + - + diff --git a/simulators/resources/routine_request_08.xml b/simulators/resources/routine_request_08.xml index e67a907..bb7f442 100644 --- a/simulators/resources/routine_request_08.xml +++ b/simulators/resources/routine_request_08.xml @@ -1,8 +1,8 @@ - + - + diff --git a/simulators/resources/routine_request_09.xml b/simulators/resources/routine_request_09.xml index 89ed3e1..d7e218b 100644 --- a/simulators/resources/routine_request_09.xml +++ b/simulators/resources/routine_request_09.xml @@ -1,8 +1,8 @@ - + - + diff --git a/simulators/resources/routine_request_10.xml b/simulators/resources/routine_request_10.xml index 3b4d708..2efd365 100644 --- a/simulators/resources/routine_request_10.xml +++ b/simulators/resources/routine_request_10.xml @@ -1,8 +1,8 @@ - + - + diff --git a/simulators/telescope/telescopeSimulator.py b/simulators/telescope/telescopeSimulator.py index 530d0b8..702cb54 100644 --- a/simulators/telescope/telescopeSimulator.py +++ b/simulators/telescope/telescopeSimulator.py @@ -40,6 +40,9 @@ class TelescopeSimulator(Device, StatusManager): else: answer = "Invalid cmd for GET: " + cmd self.telescopePrint(answer) + elif (cmd_type == "DO"): + if (cmd == "GOTO"): + answer = "NEW POSITION SET TO " + str(args) else: self.telescopePrint("Ignored message " + data) diff --git a/src/analyzer/analyses/analysis_1.txt b/src/analyzer/analyses/analysis_1.txt deleted file mode 100644 index e69de29..0000000 --- a/src/analyzer/analyses/analysis_1.txt +++ /dev/null diff --git a/src/analyzer/analyzer b/src/analyzer/analyzer new file mode 100755 index 0000000..c10ca1a Binary files /dev/null and b/src/analyzer/analyzer differ diff --git a/src/analyzer/analyzer_source/calibrations/dark.fits b/src/analyzer/analyzer_source/calibrations/dark.fits new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/analyzer/analyzer_source/calibrations/dark.fits diff --git a/src/analyzer/analyzer_source/calibrations/flat.fits b/src/analyzer/analyzer_source/calibrations/flat.fits new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/analyzer/analyzer_source/calibrations/flat.fits diff --git a/src/analyzer/analyzer_source/main.cpp b/src/analyzer/analyzer_source/main.cpp new file mode 100644 index 0000000..e9541d9 --- /dev/null +++ b/src/analyzer/analyzer_source/main.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main(int ac, char **av) +{ + if (ac >= 4) + { + std::string folder_source = av[1]; + std::string folder_plan = av[2]; + std::string duration = av[3]; + std::string file_dest; + std::ofstream myfile; + + file_dest = folder_source + "/" + folder_plan + "/analysis/analysis.result"; + myfile.open(file_dest.c_str()); + myfile << "Source folder = " + folder_source << std::endl; + myfile << "Plan folder = " + folder_plan << std::endl; + myfile << "duration = " + duration << std::endl; + return (0); + } + return (1); +} diff --git a/src/analyzer/calibrations/dark.fits b/src/analyzer/calibrations/dark.fits deleted file mode 100644 index e69de29..0000000 --- a/src/analyzer/calibrations/dark.fits +++ /dev/null diff --git a/src/analyzer/calibrations/flat.fits b/src/analyzer/calibrations/flat.fits deleted file mode 100644 index e69de29..0000000 --- a/src/analyzer/calibrations/flat.fits +++ /dev/null diff --git a/src/analyzer/tasks.py b/src/analyzer/tasks.py index 3848aa5..3eea06e 100644 --- a/src/analyzer/tasks.py +++ b/src/analyzer/tasks.py @@ -1,53 +1,81 @@ from __future__ import absolute_import - from celery.task import Task from common.models import * - -import time +from utils.Logger import setupLogger +from django.conf import settings +import subprocess import os -IMAGES_FOLDER = "misc/images" +''' + Gets the folder path and the plan id + it makes an analysis of the calibrated images (in folder "calibrated") +''' + class analysis(Task): - ''' - Gets the name of the image to analyze in parameter - It applies the calibrations, then makes the - ''' + logger = setupLogger("analysis", "analysis") + + def log(self, message: str) -> int: + if settings.DEBUG: + self.logger.info(message) + return 0 - def run(self, plan_id): - # je dois jsute créer mes deux fonctions, une pour les corrections, et l'autre pour les analyses + def logDB(self, message: str) -> int: + Log.objects.create(agent='Analyzer', message=message) + return 0 - plan = Plan.objects.get(id=plan_id) - image_name = plan.name + def execProcess(self, command: str) -> int: + self.process = subprocess.Popen(command, shell=True) + return 0 - print("analysis : ", image_name) + def start(self, plan_id: int, folder: str) -> int: + self.plan_id = plan_id + self.folder = folder + self.path_dir_file = os.path.dirname(os.path.realpath(__file__)) + try: + self.plan = Plan.objects.get(id=self.plan_id) + except: + self.log("analysis not found") + self.logDB(message="Plan with id %d not found"%(self.plan_id)) + return 1 + self.dir_name = self.folder + os.sep + self.plan.name + "_" + str(self.plan_id) + os.sep + "analysis" + self.plan_folder = self.plan.name + "_" + str(self.plan_id) + if self.createDirectory(): + self.log("analysis err dir") + self.logDB(message="Could not create the directory for analysis") + return 1 + self.log("analysis start") + return self.logDB("Starting analysis for plan " + self.plan.name) - new_img = self.apply_calibrations(image_name) - # TODO: Faire ça uniquement si c'est une image provenant d'une alerte - self.do_analysis(new_img) + def createDirectory(self): + try: + if not os.path.isdir(self.dir_name): + os.makedirs(self.dir_name) + except: + return 1 + return 0 - # TODO: virer tout ça - time.sleep(1) - with open(os.path.join(IMAGES_FOLDER, str(image_name) + '_analyzed'), 'w'): - pass - message = 'Analyzed image in ' + str(image_name) - Log.objects.create(agent='Analyzer', message=message) + def changeDirectory(self, path: str): + os.chdir(path) + return 0 + + def execute(self) -> int: + self.changeDirectory(self.path_dir_file) + return self.execProcess("./analyzer "+str(self.folder)+" "+self.plan_folder+" "+str(self.plan.duration)) + + def end(self) -> int: + self.process.wait() + if self.process.returncode == 0: + self.log("analysis executed") + self.logDB(message="Analysis executed successfully for plan " + str(self.plan.name)) + else: + self.log("analysis failed") + self.logDB(message="Could not make an analysis for plan " + str(self.plan.name)) + return self.process.returncode - def apply_calibrations(self, image_name): - ''' - Uses the calibration files to correct the image (changes the image bytes) - Send the new images to the FSC - ''' - # TODO: récupérer les bonne images dans le dossier analyzer/calibrations, et les appliquer - # renvoie le nom de l'image calibrée; penser à vérifier que les fichiers de calibration existent à cet endroit - # TODO: copier l'image dans le bon dossier pour le filesync (lien symbolique) - return "toto.fits" - - def do_analysis(self, new_img): - ''' - Use the scientific softwares to create analysis files (doesn't change the image bytes) - Sends the results to the FSC - ''' - # TODO: utiliser les programmes scientifiques pour générer les analyses - # TODO: copier les résultats des analyses dans le bon dossier pour le filesync (lien symbolique) - pass + def run(self, plan_id, folder): + if self.start(plan_id, folder): + return 1 + self.execute() + self.end() + return 0 diff --git a/src/common/models.py b/src/common/models.py index 9888a85..e23e3ae 100644 --- a/src/common/models.py +++ b/src/common/models.py @@ -452,6 +452,11 @@ class ScheduleHasSequences(models.Model): class SiteWatch(models.Model): + OPEN = "OPEN" + CLOSE = "CLOSE" + ON = "ON" + OFF = "OFF" + updated = models.DateTimeField(blank=True, null=True, auto_now=True) lights = models.CharField(max_length=45, blank=True, null=True) dome = models.CharField(max_length=45, blank=True, null=True) @@ -569,6 +574,9 @@ class Version(models.Model): return (str(self.module_name) + " - " + str(self.version)) class WeatherWatch(models.Model): + WIND_LIMIT = 100 + RAIN_LIMIT = 5 + updated = models.DateTimeField(blank=True, null=True) humid_int = models.FloatField(blank=True, null=True) humid_ext = models.FloatField(blank=True, null=True) diff --git a/src/devices/Device.py b/src/devices/Device.py index 01f24e3..4712d51 100644 --- a/src/devices/Device.py +++ b/src/devices/Device.py @@ -9,6 +9,8 @@ import utils.Logger as L Generic object for the communication with all the devices (need inheritance) ''' +DEBUG_FILE = False + class DeviceController(): __metaclass__ = abc.ABCMeta @@ -124,7 +126,8 @@ class DeviceController(): return (0) def log(self, device_name: str, message: str): - self.logger.info("From device : " + device_name + " -> " + message) + if DEBUG_FILE and settings.DEBUG: + self.logger.info("From device : " + device_name + " -> " + message) return (0) def connect(self): diff --git a/src/majordome/tasks.py b/src/majordome/tasks.py index 861daea..8dbf73f 100644 --- a/src/majordome/tasks.py +++ b/src/majordome/tasks.py @@ -2,9 +2,8 @@ from __future__ import absolute_import from common.models import * from django.core.exceptions import ObjectDoesNotExist import scheduler -import scheduler.tasks +import scheduler.tasks as sched_task from celery.task import Task -from celery import app import observation_manager import observation_manager.tasks from devices.Telescope import TelescopeController @@ -12,8 +11,9 @@ from devices.CameraVIS import VISCameraController from devices.CameraNIR import NIRCameraController from devices.Dome import DomeController from devices.PLC import PLCController -from django.conf import settings +from django.db.models import Q from utils.JDManipulator import * +from utils.StatusManager import * import utils.Logger as L DEBUG_FILE = False @@ -39,7 +39,8 @@ class Majordome(Task): timers = {} functions = {} schedule = None - majordome_status = "STARTING" + available_status = [] + current_status = NORMAL ''' Check if the instrument status is valid @@ -70,7 +71,6 @@ class Majordome(Task): self.updateSoftware() self.setContext() self.setTime() - self.majordome_status = "EXECUTING" self.loop() ''' @@ -96,7 +96,6 @@ class Majordome(Task): self.status_nir = self.nir_camera.get("STATUS") self.status_tel = self.tel.get("STATUS") self.status_dom = self.dom.get("STATUS") - print("Devices ready !") return (0) ''' @@ -152,7 +151,7 @@ class Majordome(Task): Infinite loop according to the majordome behavior ''' def loop(self): - while (self.majordome_status != "SHUTDOWN"): + while (self.current_status != "SHUTDOWN"): minimal_timer = min(self.timers, key=self.timers.get) if (self.timers[minimal_timer] > 0): time.sleep(self.timers[minimal_timer]) @@ -219,7 +218,8 @@ class Majordome(Task): self.next_sequence = None self.schedule = schedule if (self.schedule): - shs_list = self.schedule.shs.filter(status=Sequence.PENDING).order_by('tsp') + shs_list = self.schedule.shs.filter(Q(status=Sequence.PLANNED) | + Q(status=Sequence.PENDING)).order_by('tsp') self.executeSchedule(shs_list) else: self.notifyTelescopeStatus("scheduler") @@ -254,25 +254,21 @@ class Majordome(Task): Function called when a schedule has to be executed ''' def executeSchedule(self, shs_list): - is_prev_planned = False self.logDB("Trying to execute a sequence from current schedule") for shs in shs_list: # shs_list is sorted by tsp - if (shs.sequence.status == Sequence.PLANNED and self.observable(shs.sequence)): - countdown = self.getCountdown(shs) - # if (countdown <= 6): - # TODO CHECK IF OBSERVABLE ELSE RESCHEDULE - if countdown <= JulianSeconds(5): - if self.executing_sequence is None: - if self.switchSequence(): - self.executeSequence(shs, shs.sequence, countdown) + if (self.executableSequence(shs.sequence.status) and self.observable(shs.sequence)): + if self.next_sequence is None: + self.setNextSequence(shs, shs.sequence) + if self.executing_sequence is None: + if self.isValidTimer(self.next_sequence[0]): + if self.executeSequence(self.next_sequence[0], self.next_sequence[1]) == -1: + return -1 + if self.next_sequence[0] != shs and self.next_sequence[1] != shs.sequence: + self.setNextSequence(shs, shs.sequence) else: - self.setNextSequence(shs, shs.sequence, countdown) - elif self.next_sequence is None: - self.setNextSequence(shs, shs.sequence, countdown) + self.next_sequence = None else: return 0 - elif self.next_sequence is None: - self.setNextSequence(shs, shs.sequence, countdown) else: if (settings.DEBUG and DEBUG_FILE): log.info("Sequence cannot be executed : Not observable") @@ -284,41 +280,58 @@ class Majordome(Task): return 0 return 1 + def executableSequence(self, status): + if status == Sequence.PLANNED or status == Sequence.PENDING: + return 1 + return 0 + + def isValidTimer(self, shs): + current_countdown = self.getCountdown(shs) + if (current_countdown <= JulianSeconds(5)): + # Trick to be sure to not start the execution of a task if there is a schedule + try: + task = TaskId.objects.filter(task="scheduling") + if not task: + return 1 + return 0 + except: + return 1 + return 0 + ''' Launch the observation tasks associated to a sequence ''' - def executeSequence(self, shs, sequence, countdown): - if (countdown > JulianSeconds(5)): - if self.next_sequence and self.next_sequence[1].status == Sequence.PENDING: - self.next_sequence[1].status = Sequence.PLANNED - self.next_sequence[1].save() - return 0 - if self.next_sequence and self.next_sequence[1].status == Sequence.PLANNED: - self.next_sequence[1].status = Sequence.PENDING - self.next_sequence[1].save() - + @acceptedStatus(["NORMAL", "MINOR_ERROR"]) + def executeSequence(self, shs, sequence): + log.info("Executing sequence id = " + str(sequence.pk)) self.logDB("Executing sequence") plans_results = [] if sequence.albums.filter(detector__name="Cagire").exists(): if (self.isValidStatus(self.status_nir)): for plan in sequence.albums.get(detector__name="Cagire").plans.all(): - res = observation_manager.tasks.execute_plan_nir.apply_async((plan.id, float(countdown))) - TaskId.objects.create(task_id=res.id, task="execute_plan") + res = observation_manager.tasks.execute_plan_nir.apply_async( + (plan.id, float(self.getCountdown(shs)))) + # JB TODO : is it still usefull ? + # TaskId.objects.create(task_id=res.id, task="execute_plan") plans_results.append(res) else: self.notifyDeviceStatus("Cagire", "Sequence execution", self.status_nir) - sequence.status = Sequence.DEVICE_ERROR + sequence.status = Sequence.CANCELLED + shs.status = Sequence.CANCELLED + shs.save() sequence.save() return (1) if sequence.albums.filter(detector__name="Visible camera").exists(): if (self.isValidStatus(self.status_vis)): for plan in sequence.albums.get(detector__name="Visible camera").plans.all(): - res = observation_manager.tasks.execute_plan_vis.apply_async((plan.id, float(countdown))) - TaskId.objects.create(task_id=res.id, task="execute_plan") + res = observation_manager.tasks.execute_plan_vis.apply_async( + (plan.id, float(self.getCountdown(shs)))) plans_results.append(res) else: self.notifyDeviceStatus("Camera visible", "Sequence execution", self.status_vis) - sequence.status = Sequence.DEVICE_ERROR + sequence.status = Sequence.CANCELLED + shs.status = Sequence.CANCELLED + shs.save() sequence.save() return (1) shs.status = Sequence.EXECUTING @@ -326,60 +339,51 @@ class Majordome(Task): shs.save() sequence.save() self.executing_sequence = [shs, sequence, plans_results] + log.info("Sequence status should be executing = " + str(sequence.status)) return (0) ''' Set the next sequence ''' - def setNextSequence(self, shs, sequence, countdown): + def setNextSequence(self, shs, sequence): + log.info("Setting next sequence id = " + str(sequence.pk)) sequence.status = Sequence.PENDING - self.next_sequence = [shs, sequence, countdown] + shs.status = Sequence.PENDING + self.next_sequence = [shs, sequence] sequence.save() - return (0) - - ''' - Switch sequences - ''' - def switchSequence(self): - self.executing_sequence = None - if (self.next_sequence is not None): - self.executeSequence(self.next_sequence[0], - self.next_sequence[1], self.next_sequence[2]) - self.next_sequence = None - else: - return 1 + shs.save() return (0) ''' Check if the current sequence is finished ''' def handleSequence(self, shs, sequence, executing_plans): - finished = False - error = False + count = 0 - for plan in executing_plans: + for res in executing_plans: try: - if plan.ready() == False: - finished = True + if res.successful() or res.failed(): + count += 1 except Exception as e: - error = True + print(e) shs.status = Sequence.CANCELLED sequence.status = Sequence.CANCELLED shs.save() sequence.save() for rev in executing_plans: - if (not rev.failed() and rev.ready() != True): - app.control.revoke(rev.id) - self.switchSequence() + if (not rev.failed() and not rev.successful()): + rev.revoke(terminate=True) + self.executing_sequence = None return (-1) - if (finished): + if count >= len(executing_plans): sequence.status = Sequence.EXECUTED shs.status = Sequence.EXECUTED sequence.save() shs.save() message = "Finished sequence " + str(sequence.pk) + " execution" + log.info(message) Log.objects.create(agent="Majordome", message=message) - self.switchSequence() + self.executing_sequence = None return (0) ''' @@ -387,6 +391,10 @@ class Majordome(Task): ''' def handleStatus(self): # TODO switch majordome state according to devices status + # if not self.isValidStatus(self.status_tel): + # self.current_status = "ERROR" + # if not self.isValidStatus(self.status_vis) and self.current_status == NORMAL: + # self.current_status = "" telescope = Telescope.objects.first() camera_nir = Detector.objects.get(name="Cagire") camera_vis = Detector.objects.get(name="Visible camera") @@ -394,7 +402,7 @@ class Majordome(Task): # dome = ???.objects.get(name="Dome") # dome.status = self.status_dom # dome.save() - # TODO adapt the status (must be short) + # TODO adapt the status (must be a short string) telescope.status = self.status_tel camera_nir.status = self.status_nir camera_vis.status = self.status_vis @@ -409,6 +417,7 @@ class Majordome(Task): ''' Put the system in Pause ''' + @acceptedStatus(["CRITICAL"]) def systemPause(self, duration, cause: str): self.logDB("System in pause for " + str(duration)) time.sleep(duration) @@ -421,6 +430,16 @@ class Majordome(Task): Function called to do an action with the site status and the wheather status ''' def handlePLC(self, site_status, weather_status): + if site_status.doors == SiteWatch.OPEN: + self.current_status = SITE_CONDITION + self.logDB("Doors are open") + self.stopExecution() + if weather_status.wind > WeatherWatch.WIND_LIMIT: + self.cause = "Wind" + self.logDB("Wind too strong") + self.stopExecution() + if weather_status.rain > WeatherWatch.RAIN_LIMIT: + self.cause = "Rain" return (0) ''' diff --git a/src/observation_manager/calibrator b/src/observation_manager/calibrator new file mode 100755 index 0000000..7e1e18f Binary files /dev/null and b/src/observation_manager/calibrator differ diff --git a/src/observation_manager/calibrator_source/main.cpp b/src/observation_manager/calibrator_source/main.cpp new file mode 100644 index 0000000..2598aa7 --- /dev/null +++ b/src/observation_manager/calibrator_source/main.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main(int ac, char **av) +{ + if (ac >= 4) + { + std::string folder_source = av[1]; + std::string folder_plan = av[2]; + std::string img_number = av[3]; + std::string file_dest; + std::ofstream myfile; + + file_dest = folder_source+"/"+folder_plan+"/calibrator/calibrator"+img_number+".result"; + myfile.open(file_dest.c_str()); + myfile << "Source folder = " + folder_source << std::endl; + myfile << "Source plan = " + folder_plan << std::endl; + myfile << "image number = " + img_number << std::endl; + return (0); + } + return (1); +} diff --git a/src/observation_manager/tasks.py b/src/observation_manager/tasks.py index 976c8ee..e05fd3d 100644 --- a/src/observation_manager/tasks.py +++ b/src/observation_manager/tasks.py @@ -1,135 +1,209 @@ from __future__ import absolute_import from celery.task import Task -from analyzer.tasks import analysis +from django.conf import settings from common.models import * -from devices import CameraVIS as VIS -from devices import CameraNIR as NIR -from devices import Telescope -import time from utils.Logger import setupLogger +from analyzer.tasks import analysis +from utils.JDManipulator import * +from devices.Telescope import TelescopeController +from devices.CameraNIR import NIRCameraController +from devices.CameraVIS import VISCameraController +import time +import subprocess +import os ''' Super class for execute_plan_vis / _nir ''' class execute_plan(Task): - - def run(self, plan_id, countdown, type): - - if countdown > 0: - time.sleep(countdown) - TaskId.objects.filter(task_id=self.request.id).delete() - - self.type = type - plan = Plan.objects.get(id=plan_id) - - message = 'Start plan ' + plan.name + ' execution' + def setTelescope(self): + self.tel = TelescopeController() + return 0 + + def start(self) -> int: + self.setTelescope() + self.logger = setupLogger("Observation"+self.type, "Observation"+self.type) + self.image_count = 0 + # JB TODO : is it still usefull ? + # TaskId.objects.filter(task_id=self.request.id).delete() + if self.countdown > 0: + time.sleep(self.countdown) + try: + self.plan = Plan.objects.get(id=self.plan_id) + self.duration = julianSecondsToSeconds(self.plan.duration) + except: + self.logDB(message="Plan with id %d not found" % (self.plan_id)) + return 1 + self.plan_dir = self.plan.name + "_" + str(self.plan_id) + self.dir_name = settings.OUTPUT_FOLDER + os.sep + self.plan_dir + if self.createDirs(): + self.log("Could not create dirs") + self.logDB(message="Could not create directory " + self.dir_name + " for image storage") + return 1 + self.launchAnalysis() + self.log("Starting execution " + self.type) + return self.logDB('Start plan %s observation from camera %s'%(self.plan.name, str(self.type))) + + def createDirs(self): + try: + if not os.path.isdir(settings.OUTPUT_FOLDER): + os.makedirs(settings.OUTPUT_FOLDER) + if not os.path.isdir(self.dir_name): + os.makedirs(self.dir_name) + except: + return 1 + return 0 + + def log(self, message: str) -> int: + self.logger.info(self.type + ' -> '+ message) + return 0 + + def logDB(self, message: str) -> int: Log.objects.create(agent='Observation manager', message=message) - print("execute_plan " + self.type + " : ", plan.name) - - self.tel = Telescope.TelescopeController() - if self.type == "VIS": - cam = VIS.VISCameraController() - else: - cam = NIR.NIRCameraController() - - self.set_camera(cam, plan) - self.wait_camera_ready(cam) + return 0 - cam.do("START") + def launchAnalysis(self): + analysis.apply_async((self.plan_id, settings.OUTPUT_FOLDER)) + return 0 - st = self.wait_camera_finished(cam) + def launchCalibration(self) -> int: + create_calibrations.apply_async((self.plan_id, settings.OUTPUT_FOLDER, self.image_count)) + return 0 - # TODO: récupérer les vraies images ? je fais quoi ? - time.sleep(1) - # Penser qu'un plan peut créer plusieurs images (et analyser image par image) - analysis.delay(plan_id) - message = 'Finished plan ' + plan.name + ' execution' - Log.objects.create(agent='Observation manager', message=message) - - ''' - Set the camera configuration - ''' - def set_camera(self, cam, plan): - - # TODO: mettre les vraies configurations en fct du plan - cam.set("WINDOW", 0, 100, 10, 100) - cam.set("READMODE", "Ramp") - cam.set("FILENAME", plan.name) - cam.set("HEADER", {}) - cam.set("READOUT_FREQUENCY", 20.0) - cam.set("FILTER", "H") - - if self.type == "VIS": - cam.set("EXPOSURE", 180) - cam.set("BINNING", 300, 300) - else: - cam.set("NB_IMAGES", 28) - - ''' - Loop to wait for the configuration to be done - ''' - def wait_camera_ready(self, cam): - st = 0 - while st == 0: - st_tel = self.tel.get("STATUS") - - st_cam = cam.get("STATUS") - - st = 1 - - # TODO: checker les statuts comme il faut, et repasser à 0 si on a des statuts pas bons - if st_tel != "IDLE" or st_cam != "IDLE": - st = 0 - - ''' - Loop to wait for the observation to be finished - ''' - def wait_camera_finished(self, cam): - countdown = int(cam.get("TIMER")) - while countdown > 5: - time.sleep(5) - countdown = int(cam.get("TIMER")) - - st = 0 - while st == 0: - timer = int(cam.get("TIMER")) - if timer == -1: - st = 1 - else: - time.sleep(1) + def end(self) -> int: + self.log("Finished plan observation") + return self.logDB('Finished plan observation ' + self.plan.name + ' from camera ' + str(self.type)) + def run(self, plan_id: int, countdown: float, type: str) -> int: + self.plan_id = plan_id + self.countdown = countdown + self.type = type + if self.start(): + self.log("fail exec task -> leaving") + return 1 + self.execute() + return self.end() + + def execute(self) -> int: + time = julianSecondsToSeconds(self.plan.duration) + self.log("Sleeping for " + str(int(self.duration))) + time.sleep(int(self.duration)) + return 0 ''' Gives the orders to the instruments to retrieve the image(s) of a plan VIS. - Send the images to the analyzer + Send the images to the calibrator ''' class execute_plan_vis(execute_plan): - logger = setupLogger("PlanVIS", "PlanVIS") + def setCamera(self): + self.vis_camera = VISCameraController() + return 0 - def run(self, plan_id, countdown): - self.log.info("------------------ RUNNING PLAN VIS ----------------------------") + def run(self, plan_id: int, countdown: float) -> int: + self.setCamera() super().run(plan_id, countdown, "VIS") + return 0 + def execute(self) -> int: + # TODO All the comunication protocol with the device + self.tel.do("GOTO " + str(self.plan.position)) + self.vis_camera.do("CAPTURE "+self.plan.name+" "+str(self.plan.nb_images)+" "+str(self.plan.duration)) + self.log("Sleeping for " + str(int(self.duration))) + time.sleep(int(self.duration)) + self.launchCalibration() + return 0 ''' Gives the orders to the instruments to retrieve the image(s) of a plan NIR. - Send the images to the analyzer + Send the images to the calibrator ''' class execute_plan_nir(execute_plan): - log = setupLogger("PlanNIR", "PlanNIR") + def setCamera(self): + self.nir_camera = NIRCameraController() + return 0 - def run(self, plan_id, countdown): + def run(self, plan_id: int, countdown: float) -> int: + self.setCamera() super().run(plan_id, countdown, "NIR") - self.log.info("------------------ RUNNING PLAN NIR ----------------------------") + return 0 + + def execute(self) -> int: + # TODO All the comunication protocol with the device + self.tel.do("GOTO " + str(self.plan.position)) + self.nir_camera.do("CAPTURE "+self.plan.name+" "+str(self.plan.nb_images)+" "+str(self.plan.duration)) + self.log("Sleeping for " + str(int(self.duration))) + time.sleep(int(self.duration)) + self.launchCalibration() + return 0 ''' - Directly make the right calls to the instruments to create the calibration files. - When they are all finished, it creates the 'super' calibration files. + Call a process with a folder and an image number as parameter who will create the + calibration for an image ''' class create_calibrations(Task): - logger = setupLogger("Calibrations", "Calibrations") + logger = setupLogger("calibrations", "calibrations") + + def log(self, message: str) -> int: + self.logger.info(message) + return 0 - def run(self): - self.log.info("------------------ RUNNING CALIBRATIONS ----------------------------") - # TODO: attendre que tout soit idle + def logDB(self, message: str) -> int: + Log.objects.create(agent='Observation manager', message=message) + return 0 + + def execProcess(self, command: str) -> int: + self.process = subprocess.Popen(command, shell=True) + return 0 + + def start(self, plan_id: int, folder: str, image_number: int) -> int: + self.plan_id = plan_id + self.folder = folder + self.image_number = image_number + self.path_dir_file = os.path.dirname(os.path.realpath(__file__)) + try: + self.plan = Plan.objects.get(id=self.plan_id) + self.plan_folder = self.plan.name + "_" + str(self.plan_id) + except: + self.log("not found") + self.logDB(message="Plan with id %d not found"%(self.plan_id)) + return 1 + self.dir_name = self.folder + os.sep + self.plan.name + "_" + str(self.plan_id) + os.sep + "calibrations" + if self.createDirectory(): + self.log("err dir") + self.logDB(message="Could not create folder for calibrations") + return 1 + self.log("start") + return self.logDB("Starting calibration for image " + str(self.image_number)) + + def createDirectory(self): + try: + if not os.path.isdir(self.dir_name): + os.makedirs(self.dir_name) + except: + return 1 + return 0 + + def changeDirectory(self, path: str): + os.chdir(path) + return 0 + + def execute(self) -> int: + self.changeDirectory(self.path_dir_file) + return self.execProcess("./calibrator "+str(self.folder)+" "+self.plan_folder+" "+str(self.image_number)) + + def end(self) -> int: + self.process.wait() + if self.process.returncode == 0: + self.log("executed") + self.logDB(message="Calibration executed successfully for image " + str(self.image_number)) + else: + self.log("failed") + self.logDB(message="Could not calibrate image " + str(self.image_number)) + return self.process.returncode + + def run(self, plan_id: int, folder: str, image_number: int) -> int: + if self.start(plan_id, folder, image_number): + return 1 + self.execute() + return self.end() diff --git a/src/pyros/settings.py b/src/pyros/settings.py index f0b022a..170eecc 100644 --- a/src/pyros/settings.py +++ b/src/pyros/settings.py @@ -29,13 +29,15 @@ MODULES_VERSIONS = { # Set MYSQL to False if you want to use SQLITE # This line MUST NOT be changed at all except from changing True/False # (or install_requirements script will become invalid) -MYSQL = False +MYSQL = True import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Output folder for images +OUTPUT_FOLDER = os.path.join(BASE_DIR, "../images_folder") # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ @@ -114,13 +116,10 @@ FIXTURE_DIRS = ( LOGIN_URL = "/" -# Database -# https://docs.djangoproject.com/en/1.9/ref/settings/#databases - -# EP modif - +# FOR SIMULATOR -> do not touch this variable SIMULATOR = False +# FOR TESTS and SIMULATOR -> do not touch this variable CELERY_TEST = False if not CELERY_TEST: @@ -260,11 +259,11 @@ CELERY_QUEUES = { "monitoring_q": {"exchange": "monitoring_q", "routing_key": "monitoring_q"}, "majordome_q": {"exchange": "majordome_q", "routing_key": "majordome_q"}, "analysis_q": {"exchange": "analysis_q", "routing_key": "analysis_q"}, - "system_status_q": {"exchange": "system_status_q", "routing_key": "system_status_q"}, "scheduling_q": {"exchange": "scheduling_q", "routing_key": "scheduling_q"}, "create_calibrations_q": {"exchange": "create_calibrations_q", "routing_key": "create_calibrations_q"}, + "execute_plan_vis_q": {"exchange": "execute_plan_vis_q", "routing_key": "execute_plan_vis_q"}, + "execute_plan_nir_q": {"exchange": "execute_plan_nir_q", "routing_key": "execute_plan_nir_q"}, } - # "simulator_q": {"exchange": "simulator_q", "routing_key": "simulator_q"}, CELERY_ROUTES = { "alert_manager.tasks.AlertListener": {"queue": "alert_listener_q"}, diff --git a/src/pyros/settings.py.bak b/src/pyros/settings.py.bak index 7b21900..617812f 100644 --- a/src/pyros/settings.py.bak +++ b/src/pyros/settings.py.bak @@ -29,13 +29,15 @@ MODULES_VERSIONS = { # Set MYSQL to False if you want to use SQLITE # This line MUST NOT be changed at all except from changing True/False # (or install_requirements script will become invalid) -MYSQL = False +MYSQL = True import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Output folder for images +OUTPUT_FOLDER = os.path.join(BASE_DIR, "../images_folder") # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ @@ -114,13 +116,10 @@ FIXTURE_DIRS = ( LOGIN_URL = "/" -# Database -# https://docs.djangoproject.com/en/1.9/ref/settings/#databases - -# EP modif - +# FOR SIMULATOR -> do not touch this variable SIMULATOR = True +# FOR TESTS and SIMULATOR -> do not touch this variable CELERY_TEST = False if not CELERY_TEST: @@ -260,11 +259,11 @@ CELERY_QUEUES = { "monitoring_q": {"exchange": "monitoring_q", "routing_key": "monitoring_q"}, "majordome_q": {"exchange": "majordome_q", "routing_key": "majordome_q"}, "analysis_q": {"exchange": "analysis_q", "routing_key": "analysis_q"}, - "system_status_q": {"exchange": "system_status_q", "routing_key": "system_status_q"}, "scheduling_q": {"exchange": "scheduling_q", "routing_key": "scheduling_q"}, "create_calibrations_q": {"exchange": "create_calibrations_q", "routing_key": "create_calibrations_q"}, + "execute_plan_vis_q": {"exchange": "execute_plan_vis_q", "routing_key": "execute_plan_vis_q"}, + "execute_plan_nir_q": {"exchange": "execute_plan_nir_q", "routing_key": "execute_plan_nir_q"}, } - # "simulator_q": {"exchange": "simulator_q", "routing_key": "simulator_q"}, CELERY_ROUTES = { "alert_manager.tasks.AlertListener": {"queue": "alert_listener_q"}, diff --git a/src/scheduler/Scheduler.py b/src/scheduler/Scheduler.py index e69954c..02558f5 100644 --- a/src/scheduler/Scheduler.py +++ b/src/scheduler/Scheduler.py @@ -61,7 +61,7 @@ class Scheduler(IntervalManagement): executing = Sequence.objects.filter(status=Sequence.EXECUTING) if executing: s = Sequence.shs.latest("schedule__created") - adder = s.tsp - secondsToPreciseJulianDate(getPreciseCurrentTime()) + adder = s.tep - secondsToPreciseJulianDate(getPreciseCurrentTime()) except: pass self.schedule.plan_night_start = previous_sched.plan_night_start @@ -183,7 +183,7 @@ class Scheduler(IntervalManagement): else: sequence_placed = self.tryShiftingSequences(sequence, shs) if sequence_placed: - shs.status = Sequence.PENDING + shs.status = Sequence.PLANNED self.decreaseQuota(sequence, sequence.duration) else: if DEBUG_FILE: @@ -194,14 +194,14 @@ class Scheduler(IntervalManagement): def findSequenceBefore(self, interval: Interval): for seq, s in self.sequences: - if s.status == Sequence.PENDING: + if s.status == Sequence.PLANNED: if is_nearby_equal(s.tep, interval.start): return (seq, s) return (None, None) def findSequenceAfter(self, interval: Interval): for seq, s in self.sequences: - if s.status == Sequence.PENDING: + if s.status == Sequence.PLANNED: if is_nearby_equal(s.tsp - self.max_overhead, interval.end): return (seq, s) return (None, None) @@ -314,7 +314,7 @@ class Scheduler(IntervalManagement): return 0 def logSchedule(self) -> int: - sequences = Sequence.objects.filter(shs__status=Sequence.PENDING).order_by('shs__tsp').distinct() + sequences = Sequence.objects.filter(shs__status=Sequence.PLANNED).order_by('shs__tsp').distinct() self.log("There are %d sequence(s) planned" % len(sequences)) for sequence in sequences: s = sequence.shs.latest("schedule__created") diff --git a/src/scheduler/tasks.py b/src/scheduler/tasks.py index 66a782b..93ebab0 100644 --- a/src/scheduler/tasks.py +++ b/src/scheduler/tasks.py @@ -4,14 +4,17 @@ from scheduler.Scheduler import Scheduler from common.models import * from utils.JDManipulator import * from utils.Logger import setupLogger + log = setupLogger("TaskSched", "TaskSched") class scheduling(Task): def run(self, first_schedule=False, alert=False): + task = TaskId.objects.create(task_id=self.request.id, task="scheduling") Log.objects.create(agent='Scheduler', message='Start schedule : ' + str(datetime.datetime.now())) self.scheduler = Scheduler() self.scheduler.setNightLimits(secondsToJulianDate(getNightStart()), secondsToJulianDate(getNightEnd())) self.scheduler.makeSchedule() - Log.objects.create(agent='Scheduler', message='Scheduling finished : ' + str(datetime.datetime.now())) \ No newline at end of file + Log.objects.create(agent='Scheduler', message='Scheduling finished : ' + str(datetime.datetime.now())) + task.delete() \ No newline at end of file diff --git a/src/utils/JDManipulator.py b/src/utils/JDManipulator.py index 6143e7b..9ff030b 100644 --- a/src/utils/JDManipulator.py +++ b/src/utils/JDManipulator.py @@ -39,6 +39,9 @@ def secondsToJulianDate(time_seconds): def secondsToPreciseJulianDate(time_seconds): return (Decimal(time_seconds) / 86400 + Decimal(TIMESTAMP_JD)) +def julianSecondsToSeconds(seconds_julian): + return seconds_julian * 86400 + def julianDateToSeconds(time_julian): return ((time_julian - TIMESTAMP_JD) * 86400) diff --git a/src/utils/StatusManager.py b/src/utils/StatusManager.py new file mode 100644 index 0000000..cbb71fb --- /dev/null +++ b/src/utils/StatusManager.py @@ -0,0 +1,16 @@ +SITE_CONDITION = "SITE_CONDITION" +NORMAL = "NORMAL" +ERROR = "ERROR" +WEATHER_CONDITION = "SEVERE_WEATHER" +MINOR = "MINOR_ERROR" +CRITICAL = "CRITICAL" + +def acceptedStatus(status): + def deco(func): + def tag_decorator(self, *args, **kwargs): + for i in status: + if self.current_status == i: + return func(self, *args, **kwargs) + return -1 + return tag_decorator + return deco -- libgit2 0.21.2