tasks.py 10.8 KB
from __future__ import absolute_import

from celery.task import Task
import observation_manager
import observation_manager.tasks
from common.models import *
from devices.Telescope import TelescopeController
from devices.CameraVIS import VISCameraController
from devices.CameraNIR import NIRCameraController
from devices.PLC import PLCController
from django.conf import settings

from utils.JDManipulator import *
import utils.config as L
log = L.setupLogger("MajordomeTaskLogger", "Majordome")

from devices import Telescope as Tel

import time
import datetime
import scheduler
import scheduler.tasks

from decimal import Decimal

TIMESTAMP_JD = 2440587.500000
DAILY_SECOND = 1 / 86400
MAX_WAIT = 180

'''
    Task to handle the execution of ONE sequence
    It receives the shs' id in parameter.
    It prepairs the instruments and cut the sequence in plan. Then the execute_plan tasks are created.

    If possible, it advances the sequence start.
    If the sequence can't start within MAX_WAIT seconds, it relaunches a scheduling
'''
class Majordome(Task):
    loop_speed = 1
    julian_div = 86400


    def setContext(self):
        self.tel = TelescopeController()
        self.vis_camera = VISCameraController()
        self.nir_camera = NIRCameraController()
        self.plc = PLCController()
        return (0)
        # 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)

    '''
        Function called by celery task
        Behavior:
            Init telescope / cameras
            set night limits
            check the software version
            launch the majordome loop
    '''
    def run(self):
        self.updateSoftware()
        self.setTime()
        self.setContext()
        self.loop()

    '''
        Reads the softwares versions in the settings.py, store them in the DB and send them to the IC.
    '''
    def updateSoftware(self):
        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)
        return (0)

    '''
        Loop to wait for the device to be idle avec the starting configurations.
    '''
    def waitDevices(self):
        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")

        print("Devices ready !")
        return (0)

    '''
        Computes the beginning and the end of the following (or current) night
    '''
    def setTime(self):
        self.night_start = getNightStart()
        self.night_end = getNightEnd()
        self.night_start_jd = secondsToJulianDate(getNightStart())
        self.night_end_jd = secondsToJulianDate(getNightEnd())
        self.timer_status = 5
        self.timer_plc = 2
        self.timer_schedule = 1

        if (self.night_start - 120 > getCurrentTime()):
            self.timer_night_start = self.night_start - 120 - getCurrentTime()
        self.timer_night_end = self.night_end - getCurrentTime()

        self.timers = {
            "status": self.timer_status, "environement": self.timer_plc,
            "night_start": self.timer_night_start, "night_end": self.timer_night_end,
            "schedule": self.timer_schedule
        }

        # TODO: utiliser un logiciel by AK pour stocker en local le début et la fin de la nuit (on est peut-être dedans)
        return (0)

    '''
        Infinite loop according to the majordome behavior
    '''
    def loop(self):
        while (True):
            minimal_timer = min(self.timers, key=self.timers.get)
            if (self.timers[minimal_timer] > 0):
                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 == "status":
                        status_tel = self.tel.get("STATUS")
                        status_nir = self.nir_camera.get("STATUS")
                        status_vis = self.vis_camera.get("STATUS")
                        self.handleStatus(status_tel, status_nir, status_vis)
                        self.timers["status"] = self.timer_status
                    elif (timer_name == "environement"):
                        site_status = SiteWatch.objects.latest('updated')
                        weather_status = WeatherWatch.objects.latest('updated')
                        self.handlePLC(site_status, weather_status)
                        self.timers["environement"] = self.timer_plc
                    elif (timer_name == "night_start"):
                        scheduler.tasks.scheduling.delay(first_schedule=False, alert=False)
                        self.timers["night_start"] = getNextNightStart()
                    elif (timer_name == "night_end"):
                        observation_manager.tasks.create_calibrations.delay()
                        self.timers["night_end"] = getNextNightEnd()
                    elif (timer_name == "schedule"):
                        self.schedule = Schedule.objects.latest('created')
                        shs_list = self.schedule.shs.filter(status=Sequence.PENDING)
                        self.executeSchedule(shs_list)
                        self.timers["scheduler"] = self.timer_schedule
                    else:
                        log.info("Timer " + str(timer_name) + "is not known by the Majordome")
                        return (1)
        return (0)

    '''
        Function called when a schedule has to be executed
    '''
    def executeSchedule(self, shs_list):
        return (0)
#        TaskId.objects.filter(task_id=self.request.id).delete()
#         shs = ScheduleHasSequences.objects.get(pk=shs_pk)
#         sequence = shs.sequence
#
#         if sequence.status != Sequence.OBSERVABLE:
#             return
#
#         message = 'Start sequence ' + str(sequence.pk) + ' execution'
#         Log.objects.create(agent='Majordome', message=message)
#
#         print("execute_sequence : ", sequence.pk)
#
#         countdown = self.get_countdown(shs, sequence)
#
#         if countdown > MAX_WAIT * DAILY_SECOND:
#             scheduler.tasks.scheduling.delay(first_schedule=False, alert=False)
#             return
#
#         tel = Tel.TelescopeController()
#
#         tel.set("SPEED", 10.0, 10.0, 10.0)
#         tel.set("COORDS", 104.0, 12.0, 88.0)
#         tel.set("COORDS_FRAME", "Radec")
#         tel.set("TRACKING_SPEED", 0.3, 0.3, 0.3)
#         tel.set("ACCEL", 1.0, 1.0, 1.0)
#         tel.set("ROTATOR", "Tracking")
#         tel.set("FOCUS", 23562.0)
#         tel.set("MOVE_MODE", "GotoTrack")
#
#         tel.do("START")
#
#         countdown = 1  # TODO: à virer, juste pour pouvoir tester
#
#         plans_results = []
#
#         if sequence.albums.filter(detector__name="Cagire").exists():
#             for plan in sequence.albums.get(detector__name="Cagire").plans.all():
#                 res = observation_manager.tasks.execute_plan_nir.apply_async((plan.id, countdown))
#                 TaskId.objects.create(task_id=res.id, task="execute_plan")
#                 plans_results.append(res)
#
#         if sequence.albums.filter(detector__name="Visible camera").exists():
#             for plan in sequence.albums.get(detector__name="Visible camera").plans.all():
#                 res = observation_manager.tasks.execute_plan_vis.apply_async((plan.id, countdown))
#                 TaskId.objects.create(task_id=res.id, task="execute_plan")
#                 plans_results.append(res)
#
#         shs.status = Sequence.EXECUTING
#         sequence.status = Sequence.EXECUTING
#         shs.save()
#         sequence.save()
#
#         for plan_result in plans_results:
#             try:
#                 while plan_result.ready() == False:
#                     print("not finished")
#                     time.sleep(1)
#                 print("result : %s " % (plan_result.status,))
#             except Exception as e:
#                 print("exception : %s" % (str(e),))
#                 shs.status = Sequence.CANCELLED
#                 shs.save()
#                 sequence.status = Sequence.CANCELLED
#                 sequence.save()
#                 # TODO: rendre le quota à l'utilisateur
#                 return
#
#         shs.status = Sequence.EXECUTED
#         sequence.status = Sequence.EXECUTED
#         shs.save()
#         sequence.save()
#         message = 'Finished sequence ' + str(sequence.pk) + ' execution'
#         Log.objects.create(agent='Majordome', message=message)


    '''
        Function called to do an action with the devices status
    '''
    def handleStatus(self, status_tel, status_nir, status_vis):
        return (0)

    '''
        Function called to do an action with the site status and the wheather status
    '''
    def handlePLC(self, site_status, weather_status):
        return (0)

    def get_countdown(self, shs, sequence):
        """
            Gets the time before the expected start of the execution.
            If it is > 10s, tries to get the sequence ahead according the JD1
        """
        countdown = 0

        current_time = Decimal(time.time() / 86400 + TIMESTAMP_JD)
        time_before_exec = shs.tsp - current_time

        if time_before_exec > 10 * DAILY_SECOND:
            time_before_jd1 = sequence.jd1 - current_time
            if time_before_jd1 < 0:
                pass
            elif time_before_jd1 < time_before_exec:
                countdown = time_before_jd1
            else:
                countdown = time_before_exec
        return countdown

class system_pause(Task):
    '''
        Task called by the monitoring in case of problem.
        It stops the system and the instruments.
    '''

    def run(self):
        time.sleep(5)
        print("system_pause")


class system_restart(Task):
    '''
        Task called by the monitoring when there is no more problem.
        Should just make a scheduling.
    '''

    def run(self):
        time.sleep(5)
        print("system_restart")


class change_obs_conditions(Task):
    '''
        Task called by the monitoring when the obs condition have changed.
        It reads them in the DB and changes the sequences status in consequence.
        If needed, relaunches a scheduling
    '''
    def run(self):
        # important : penser à rendre les quotas aux users
        time.sleep(5)
        print("change_obs_conditions")