tasks.py 6.56 KB
from __future__ import absolute_import
from django.conf import settings
from common.models import *
from celery.task import Task
import scheduler.tasks
import alert_manager.tasks
import observation_manager.tasks
import time

from devices.Telescope import TelescopeController
from devices.CameraVIS import VISCameraController
from devices.CameraNIR import NIRCameraController
from devices.PLC import PLCController

import time

TIMER_CHECK = 10  # in seconds

class monitoring(Task):
    '''
        Infinite task created at the program's start.
        It initilize all the external connections, and starts the alert_listener.

        This is the place to put the starting configurations.

        Once the starting configurations are done, it becomes a loop that checks the PLC and instruments status.
        It also handles the beginning and the end of the night, recalculating them at each end of night.
    '''


    def run(self):
        self.configure_instruments()
        self.update_software_versions()
        self.get_night_start_end()
        self.wait_devices_ready()

        # TODO: décommenter pour lancer un scheduling
        # scheduler.tasks.scheduling(first_schedule=True, alert=False, night_start=self.night_start, night_end=self.night_end)
        alert_manager.tasks.alert_listener.delay()

        self.timers_loop()

    def configure_instruments(self):
        '''
            Creates the communication objects for each instrument, and give them the basic configurations.
        '''
        self.tel = TelescopeController()
        self.vis_camera = VISCameraController()
        self.nir_camera = NIRCameraController()
        self.plc = PLCController()

        self.vis_camera.do("COOLER", 1.0, -150.0)
        self.nir_camera.do("COOLER", 1.0, -150.0)

        self.tel.do("HOMING")
        self.tel.do("DOORS", True)
        # TODO: dire au plc d'ouvrir le dome

    def update_software_versions(self):
        '''
            Reads the softwares versions in the settings.py, store them in the DB and send them to the IC.
        '''
        versions = settings.MODULES_VERSIONS

        for module, version in versions.items():
            same_module_versions = Version.objects.filter(module_name=module)
            if same_module_versions.count() == 0:
                Version.objects.create(module_name=module, version=version)
            elif same_module_versions.order_by("-created")[0].version != version:
                Version.objects.create(module_name=module, version=version)

        # TODO: envoyer les versions à l'IC

    def get_night_start_end(self):
        '''
            Computes the beginning and the end of the following (or current) night
        '''
        # TODO: utiliser un logiciel by AK pour stocker en local le début et la fin de la nuit (on est peut-être dedans)
        self.night_start = time.time() + 180 / 86400
        self.night_end = time.time() + 360 / 86400
        pass

    def wait_devices_ready(self):
        '''
            Loop to wait for the device to be idle avec the starting configurations.
        '''

        nir_st = ""
        vis_st = ""
        tel_st = ""

        while nir_st != "IDLE" or vis_st != "IDLE" and tel_st != "IDLE":
            nir_st = self.nir_camera.get("STATUS")
            vis_st = self.vis_camera.get("STATUS")
            tel_st = self.tel.get("STATUS")
            # TODO: rajouter le fait que le dome doit être ouvert (PLC)

        print("Devices ready !")

    def timers_loop(self):
        '''
            Infinite loop for the different timers :
                - Every TIMER_CHECK seconds, check PLC and instruments status (+ analyse them and send them to the IC)
                - 2 minutes before the night start, make a scheduling
                - At the end of the night, do calibration files and computes the next night limits + make a scheduling with the new schedule
        '''

        timer_status = TIMER_CHECK

        ''' Set night start timer to 1 day, then compute the real ones if the current time isn't during the night '''
        timer_night_start = 86400

        night_start_seconds = self.night_start * 3600 * 24
        night_end_seconds = self.night_end * 3600 * 24

        if night_start_seconds - 120 > time.time():
            timer_night_start = night_start_seconds - 120 - time.time()

        timer_night_end = night_end_seconds - time.time()

        timers = {"status": timer_status, "night_start": timer_night_start, "night_end": timer_night_end}

        while True:
            minimal_timer = min(timers, key=timers.get)
            ''' Wait for the nearest timer '''
            time.sleep(timers[minimal_timer])
            ''' Update the timers '''
            timers = {key: value - timers[minimal_timer] for key, value in timers.items()}

            ''' Then check what timers are <= 0 '''
            for timer_name, timer_value in timers.items():
                if timer_value <= 0:
                    if timer_name == "status":

                        status_tel = self.tel.get("STATUS")
                        status_nir = self.nir_camera.get("STATUS")
                        status_vis = self.vis_camera.get("STATUS")
                        status_plc = self.plc.get("STATUS")

                        # TODO: stocker les statuts & les envoyer à l'IC

                        timers["status"] = TIMER_CHECK

                        self.analyze_plc_status()

                    elif timer_name == "night_start":
                        scheduler.tasks.scheduling.delay(first_schedule=False, alert=False)
                        timers["night_start"] = 86400
                    elif timer_name == "night_end":
                        # TODO: faire un majordome.system_pause (fin de nuit)
                        observation_manager.tasks.create_calibrations.delay()
                        self.get_night_start_end()
                        scheduler.tasks.scheduling(first_schedule=True, alert=False, night_start=self.night_start, night_end=self.night_end)
                        timers["night_start"] = self.night_start * 3600 * 24 - time.time() - 120
                        timers["night_end"] = self.night_end * 3600 * 24 - time.time()

    def analyze_plc_status(self):
        '''
           Reads the status in DB, and fill missing fields (maybe ?)
           Determines the obs conditions and compare them with the previous ones to know if they changed
           Create a task to stop the system if there is a security problem
        '''

        pass
        # TODO: toute la fct
        # On calcule le nouveau seeing, et si il y a eu du changement, on crée une tâche de majordome.change_obs_conditions