from __future__ import absolute_import from celery.task import Task from common.RequestBuilder import RequestBuilder from django.conf import settings from django.templatetags.static import static import socket from pyrosapp.models import * import time import voeventparse import os from os.path import isfile, join import observation_manager import majordome.TaskManager from decimal import Decimal IP = '127.0.0.1' PORT = 31569 TIMESTAMP_JD = 2440587.500000 VOEVENTS_PATH = "alert_manager/events_received" class alert_listener(Task): ''' Launched at server start Listens to VOEvents, and create appropriate request when an event is received ''' def run(self): ''' Is called at the beginning of the task Calls the function to get the new events in the VOEVENTS_PATH directory For each event, calls a fonction to analyze it ''' print("alert listener started") if settings.SIMULATION == False: self.old_files = [ f for f in os.listdir(VOEVENTS_PATH) if isfile(join(VOEVENTS_PATH, f))] while True: fresh_events = self.get_fresh_events() for event in fresh_events: self.analyze_event(event) time.sleep(1) else: self.run_simulation() def get_fresh_events(self): ''' Reads in the VOEVENTS_PATH directory to see if there are new events to analyze :returns : A list containing the new files to analyze ''' fresh_events = [] files = [ f for f in os.listdir(VOEVENTS_PATH) if isfile(join(VOEVENTS_PATH, f))] diff = list(set(files).difference(set(self.old_files))) for event in diff: print("New file found : %s" % (event,)) fresh_events.append(event) self.old_files = files return fresh_events def analyze_event(self, event_file): ''' Opens and parse the voevent file Will create the request & the alert object related ''' # TODO: checker si c'est bien un voevent with open(os.path.join(VOEVENTS_PATH, event_file), 'rb') as f: voevent = voeventparse.load(f) request = self.create_related_request(voevent, event_file) def create_related_request(self, voevent, event_file): ''' Creates a request object related to the voevent received. For the moment, it doesn't take care of the voevent content, and hardcode the sequences etc :param voevent: Object resulting of the VOEvent parsing with voeventparse library :returns : The request ''' # TODO: utiliser le VOEvent et les stratégies pour créer une requête logique pyros_user = PyrosUser.objects.get(user__username="haribo") scientific_program = ScientificProgram.objects.get(name="GRB") alert = self.get_alert_attributes(voevent, event_file) if alert != None: # à changer, juste pour test alert.save() return None # à virer, juste pour tester le parsing de VOEvent if alert == None: pass # TODO: handle error (surement un simple return) # Ici, je crée la requete avec le strategy builder # Et si il n'y a pas eu de souci, j'associe ma requete à mon alerte et c'est bon # request_builder = RequestBuilder() # request_builder.start_new_request( # pyros_user, scientific_program, True, name=voevent_id) # seq1 = request_builder.add_sequence(1, 0, 10, name=voevent_id + "_1") # alb11 = request_builder.add_album(seq1, Device.NIR, name=voevent_id + "_11") # request_builder.add_plan( # alb11, Device.NIR_FILTER_1, 120, 5, name=voevent_id + "_111") # alb12 = request_builder.add_album(seq1, Device.VIS, name=voevent_id + "_12") # request_builder.add_plan( # alb12, Device.VIS_FILTER_1, 180, 1, name=voevent_id + "_121") # seq2 = request_builder.add_sequence(1, 0, 10, name=voevent_id + "_2") # alb21 = request_builder.add_album(seq2, Device.NIR, name=voevent_id + "_21") # request_builder.add_plan( # alb21, Device.NIR_FILTER_2, 60, 3, name=voevent_id + "_211") # return request_builder.validate_request() def get_alert_attributes(self, voevent, event_file): ''' Parses the VOEvent to get all the required attributes Creates the alert object Handles errors (missing params, wrong params, ...) :param voevent: Object resulting of the VOEvent parsing with voeventparse library :returns : The alert object ''' ''' The parameters are a dictionnary with the "" s as keys, and a dictionnary of their as values. The nested dictionnaries have the 'name' attribute as keys, and another dictionnary as values, containing {attribute : value} pairs The s without are in the dictionnary with 'None' as key ''' v_params = voeventparse.pull_params(voevent) # Ici, je remplis l'objet alert alert = Alert(voevent_file=event_file) try: alert.trig_id = v_params[None]["TrigID"]["value"] alert.editor = v_params[None]["Packet_Type"]["value"] alert.soln_status = v_params[None]["Soln_Status"]["value"] alert.pkt_ser_num = v_params[None]["Pkt_Ser_Num"]["value"] tjd = Decimal(v_params[None]["Burst_TJD"]["value"]) sod = Decimal(v_params[None]["Burst_SOD"]["value"]) alert.burst_jd = tjd - 13370 - 1 + 2453371 + (sod / 86400) alert.defly_not_grb = int(alert.soln_status[0]) & 0xF0 alert.author = voevent.Who.AuthorIVORN alert.jd_send = time.mktime(time.strptime(str(voevent.Who.Date), "%Y-%m-%dT%H:%M:%S")) / 86400 + TIMESTAMP_JD alert.jd_received = time.time() / 86400 + TIMESTAMP_JD alert.equinox = voevent.WhereWhen.ObsDataLocation.ObservationLocation.AstroCoordSystem alert.burst_ra = voevent.WhereWhen.ObsDataLocation.ObservationLocation.AstroCoords.Position2D.Value2.C1 alert.burst_dec = voevent.WhereWhen.ObsDataLocation.ObservationLocation.AstroCoords.Position2D.Value2.C2 alert.error_radius = voevent.WhereWhen.ObsDataLocation.ObservationLocation.AstroCoords.Position2D.Error2Radius # TODO: faire des checks sur les valeurs récupérées ??? except KeyError or AttributeError as e: print("Error while parsing VOEvent : ", e) return None return alert def run_simulation(self): ''' Uses a socket to block until a message is received (simulation of a VOEvent reception) ''' print("run") self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server_socket.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.server_socket.setblocking(True) self.server_socket.bind((IP, PORT)) self.server_socket.listen(12) while True: # il faudra que tout ça soit dans un try ... print("Task en attente de signal") self.server_socket.accept() print("signal reçu") self.alert_received_simulation() def alert_received_simulation(self): ''' IMPORTANT : To do simulation, settings.SIMULATION must be set True Simulates a VOEvent message by reading sequences into a file Creates a request with them ''' # je supprime les sequences et requetes déjà existantes majordome.TaskManager.delete_pending_alert() Plan.objects.all().delete() Album.objects.all().delete() Sequence.objects.all().delete() Alert.objects.all().delete() Request.objects.all().delete() ScheduleHasSequences.objects.all().delete() Log.objects.create(agent='Alert manager', message='Alert received') for file_name in os.listdir(observation_manager.tasks.IMAGES_FOLDER): if file_name != "empty": file_path = os.path.join( observation_manager.tasks.IMAGES_FOLDER, file_name) os.unlink(file_path) print("All deleted") # j'ouvre le fichier des séquences de simulation, et je le lis ligne par ligne with open("alert_manager/simulation_sequences", 'r') as sequences_file: sequences = sequences_file.readlines() print("File read") Log.objects.create( agent='Alert manager', message='Simulation sequences file read') sequences = [sequence.strip('\n') for sequence in sequences] request_builder = RequestBuilder() request_builder.start_new_request(PyrosUser.objects.all()[0], ScientificProgram.objects.all()[0], True, name="Simulation_request") for sequence in sequences: sequence_array = sequence.split(" ") id_seq = sequence_array[0] priority = int(sequence_array[2]) ''' transforms the duration (seconds) in days (86,400s in a day)''' duration = Decimal(float(sequence_array[4]) / 86400.0) jd1 = Decimal("%.8f" % float(sequence_array[5])) jd2 = Decimal("%.8f" % float(sequence_array[6])) request_builder.add_sequence(priority, jd1, jd2, name="Simulation_" + id_seq, duration=duration) print("Parsé") request_builder.validate_request() print("Validé") Alert.objects.create(request=request_builder.request, strategyobs=StrategyObs.objects.all()[0]) Log.objects.create(agent='Alert manager', message='Request built')