Commit 945b36d20eeb986348da190cc79595fdbf715cdb

Authored by haribo
1 parent 0a0c921f
Exists in master and in 1 other branch dev

Généricité des caméras : simulateurs, controlleurs et celery tasks

Implémentation de la simulation d'alerte sur la page principale des
alertes
Quelques fix sur le workflow
simulators/Camera.py 0 → 100644
... ... @@ -0,0 +1,114 @@
  1 +import socket
  2 +from enum import Enum
  3 +import time
  4 +import os
  5 +from random import randint
  6 +from Device import Device
  7 +
  8 +IMAGES_FOLDER = '../src/misc/images'
  9 +
  10 +EXPOSURE_TIME = 5
  11 +SHUTTER_TIME = 3
  12 +COOLER_TIME = 10
  13 +
  14 +
  15 +class Camera(Device):
  16 +
  17 + def __init__(self, camera_name):
  18 + super().__init__(camera_name)
  19 +
  20 + ''' I will just fill the attributes with their names and params without regarding them '''
  21 + self.attributes = {}
  22 +
  23 + if camera_name == "CameraVIS":
  24 + self.attributes["FILENAME"] = "default_vis_" + str(randint(1, 10000))
  25 + else:
  26 + self.attributes["FILENAME"] = "default_nir_" + str(randint(1, 10000))
  27 +
  28 + self.status = "IDLE"
  29 + self.shutter_status = "CLOSE"
  30 +
  31 + def loop(self):
  32 + # à gérer : la réception des paramètres, le temps de shutter (et son statut), le temps de start, le stop, le abort, la création d'images
  33 +
  34 + cooler_timer = 0
  35 + shutter_timer = 0
  36 + exposure_timer = 0
  37 +
  38 + while True:
  39 + try:
  40 + conn, addr = self.sock.accept()
  41 + except socket.error as e:
  42 + print("There was a socket error: " + repr(e))
  43 + break
  44 +
  45 + data = conn.recv(128).decode()
  46 +
  47 + # print("Received: " + data)
  48 +
  49 + answer = ""
  50 + cut_data = data.split(" ")
  51 +
  52 + if cooler_timer != 0 and time.time() > cooler_timer:
  53 + cooler_timer = 0
  54 + status = "IDLE"
  55 + print("Ended cooling")
  56 + if shutter_timer != 0 and time.time() > shutter_timer:
  57 + shutter_timer = 0
  58 + print("Ended shutter")
  59 + if exposure_timer > 0 and time.time() > exposure_timer:
  60 + exposure_timer = -1
  61 + print("Ended exposure")
  62 + with open(os.path.join(IMAGES_FOLDER, self.attributes["FILENAME"]), 'w'):
  63 + pass
  64 +
  65 + if len(cut_data) < 2:
  66 + print("Invalid message: " + data)
  67 + else:
  68 + cmd_type = cut_data[0]
  69 + cmd = cut_data[1]
  70 + args = cut_data[2:]
  71 + if cmd_type == "SET":
  72 + if cmd == "FILENAME":
  73 + if len(args) > 0:
  74 + self.attributes["FILENAME"] = " ".join(args)
  75 + else:
  76 + print("An argument is needed for the FILENAME command")
  77 + else:
  78 + self.attributes[cmd] = args
  79 + elif cmd_type == "GET":
  80 + if cmd == "STATUS":
  81 + answer = self.status
  82 + elif cmd == "TEMPERATURE":
  83 + answer = "GET TEMPERATURE answer not implemented"
  84 + elif cmd == "SETUP":
  85 + answer = "GET SETUP answer not implemented"
  86 + elif cmd == "TIMER":
  87 + if exposure_timer > 0:
  88 + answer = str(int(exposure_timer - time.time()))
  89 + else:
  90 + answer = str(exposure_timer)
  91 + else:
  92 + answer = "Invalid cmd for GET: " + cmd
  93 + print(answer)
  94 + elif cmd_type == "DO":
  95 + if cmd == "START":
  96 + exposure_timer = time.time() + EXPOSURE_TIME
  97 + elif cmd == "STOP":
  98 + exposure_timer = -1
  99 + elif cmd == "ABORT":
  100 + exposure_timer = -1
  101 + elif cmd == "COOLER":
  102 + cooler_timer = time.time() + COOLER_TIME
  103 + elif cmd == "SHUTTER":
  104 + shutter_timer = time.time() + SHUTTER_TIME
  105 + else:
  106 + print("Invalid cmd for GET: " + cmd)
  107 + else:
  108 + print("Invalid message: " + data)
  109 +
  110 + if len(answer) > 0:
  111 + conn.send(bytes(answer, "UTF-8"))
  112 + # print("send: " + answer)
  113 +
  114 + conn.close()
... ...
simulators/CameraNIR.py
... ... @@ -3,7 +3,7 @@ from enum import Enum
3 3 import time
4 4 import os
5 5 from random import randint
6   -from Device import Device
  6 +from Camera import Camera
7 7  
8 8 IMAGES_FOLDER = '../src/misc/images'
9 9  
... ... @@ -12,7 +12,7 @@ SHUTTER_TIME = 3
12 12 COOLER_TIME = 10
13 13  
14 14  
15   -class CameraNIR(Device):
  15 +class CameraNIR(Camera):
16 16  
17 17 def __init__(self):
18 18 super().__init__("CameraNIR")
... ... @@ -40,7 +40,7 @@ class CameraNIR(Device):
40 40  
41 41 data = conn.recv(128).decode()
42 42  
43   - print("Received: " + data)
  43 + # print("Received: " + data)
44 44  
45 45 answer = ""
46 46 cut_data = data.split(" ")
... ... @@ -105,7 +105,7 @@ class CameraNIR(Device):
105 105  
106 106 if len(answer) > 0:
107 107 conn.send(bytes(answer, "UTF-8"))
108   - print("send: " + answer)
  108 + # print("send: " + answer)
109 109  
110 110 conn.close()
111 111  
... ...
simulators/CameraVIS.py
1   -import socket
2   -from enum import Enum
3   -import time
4   -import os
5   -from random import randint
6   -from Device import Device
  1 +from Camera import Camera
7 2  
8   -IMAGES_FOLDER = '../src/misc/images'
9   -
10   -EXPOSURE_TIME = 5
11   -SHUTTER_TIME = 3
12   -COOLER_TIME = 10
13   -
14   -
15   -class CameraVIS(Device):
  3 +class CameraVIS(Camera):
16 4 def __init__(self):
17 5 super().__init__("CameraVIS")
18 6  
19   - ''' I will just fill the attributes with their names and params without regarding them '''
20   - self.attributes = {}
21   - self.attributes["FILENAME"] = "default_vis_" + str(randint(1, 10000))
22   -
23   - self.status = "IDLE"
24   - self.shutter_status = "CLOSE"
25   -
26   - def loop(self):
27   - # à gérer : la réception des paramètres, le temps de shutter (et son statut), le temps de start, le stop, le abort, la création d'images
28   -
29   - cooler_timer = 0
30   - shutter_timer = 0
31   - exposure_timer = 0
32   -
33   - while True:
34   - try:
35   - conn, addr = self.sock.accept()
36   - except socket.error as e:
37   - print("There was a socket error: " + repr(e))
38   - break
39   -
40   - data = conn.recv(128).decode()
41   -
42   - print("Received: " + data)
43   -
44   - answer = ""
45   - cut_data = data.split(" ")
46   -
47   - if cooler_timer != 0 and time.time() > cooler_timer:
48   - cooler_timer = 0
49   - status = "IDLE"
50   - print("Stopped cooling")
51   - if shutter_timer != 0 and time.time() > shutter_timer:
52   - shutter_timer = 0
53   - print("Stopped shutter")
54   - if exposure_timer != 0 and time.time() > exposure_timer:
55   - exposure_timer = 0
56   - print("Stopped exposure")
57   - with open(os.path.join(IMAGES_FOLDER, self.attributes["FILENAME"]), 'w'):
58   - pass
59   -
60   - if len(cut_data) < 2:
61   - print("Invalid message: " + data)
62   - else:
63   - cmd_type = cut_data[0]
64   - cmd = cut_data[1]
65   - args = cut_data[2:]
66   - if cmd_type == "SET":
67   - if cmd == "FILENAME":
68   - if len(args) > 0:
69   - self.attributes["FILENAME"] = " ".join(args)
70   - else:
71   - print("An argument is needed for the FILENAME command")
72   - else:
73   - self.attributes[cmd] = args
74   - elif cmd_type == "GET":
75   - if cmd == "STATUS":
76   - answer = self.status
77   - elif cmd == "TEMPERATURE":
78   - answer = "GET TEMPERATURE answer not implemented"
79   - elif cmd == "SETUP":
80   - answer = "GET SETUP answer not implemented"
81   - elif cmd == "TIMER":
82   - answer = "GET TIMER answer not implemented"
83   - else:
84   - answer = "Invalid cmd for GET: " + cmd
85   - print(answer)
86   - elif cmd_type == "DO":
87   - if cmd == "START":
88   - exposure_timer = time.time() + EXPOSURE_TIME
89   - elif cmd == "STOP":
90   - exposure_timer = 0
91   - elif cmd == "ABORT":
92   - exposure_timer = 0
93   - elif cmd == "COOLER":
94   - cooler_timer = time.time() + COOLER_TIME
95   - elif cmd == "SHUTTER":
96   - shutter_timer = time.time() + SHUTTER_TIME
97   - else:
98   - print("Invalid cmd for GET: " + cmd)
99   - else:
100   - print("Invalid message: " + data)
101   -
102   - if len(answer) > 0:
103   - conn.send(bytes(answer, "UTF-8"))
104   - print("send: " + answer)
105   -
106   - conn.close()
107   -
108 7 cam = CameraVIS()
109 8 cam.loop()
... ...
simulators/Telescope.py
... ... @@ -31,7 +31,7 @@ class Telescope(Device):
31 31  
32 32 data = conn.recv(128).decode()
33 33  
34   - print("Received: " + data)
  34 + # print("Received: " + data)
35 35  
36 36 '''
37 37 Pour le moment, les messages sont de taille variable, contenant :
... ... @@ -93,7 +93,7 @@ class Telescope(Device):
93 93  
94 94 if len(answer) > 0:
95 95 conn.send(bytes(answer, "UTF-8"))
96   - print("send: " + answer)
  96 + # print("send: " + answer)
97 97 conn.close()
98 98  
99 99 tel = Telescope()
... ...
src/alert_manager/tasks.py
... ... @@ -19,9 +19,6 @@ import majordome.TaskManager
19 19 from decimal import Decimal
20 20  
21 21  
22   -IP = '127.0.0.1'
23   -PORT = 31569
24   -
25 22 TIMESTAMP_JD = 2440587.500000
26 23 VOEVENTS_PATH = "alert_manager/events_received"
27 24  
... ... @@ -40,16 +37,16 @@ class alert_listener(Task):
40 37 '''
41 38  
42 39 print("alert listener started")
43   - if settings.SIMULATION == False:
44   - self.old_files = [
45   - f for f in os.listdir(VOEVENTS_PATH) if isfile(join(VOEVENTS_PATH, f))]
46   - while True:
47   - fresh_events = self.get_fresh_events()
48   - for event in fresh_events:
49   - self.analyze_event(event)
50   - time.sleep(1)
51   - else:
52   - self.run_simulation()
  40 + Log.objects.create(agent="Alert manager", message="Start alert manager")
  41 +
  42 + self.old_files = [
  43 + f for f in os.listdir(VOEVENTS_PATH) if isfile(join(VOEVENTS_PATH, f))]
  44 +
  45 + while True:
  46 + fresh_events = self.get_fresh_events()
  47 + for event in fresh_events:
  48 + self.analyze_event(event)
  49 + time.sleep(1)
53 50  
54 51 def get_fresh_events(self):
55 52 '''
... ... @@ -65,6 +62,7 @@ class alert_listener(Task):
65 62  
66 63 for event in diff:
67 64 print("New file found : %s" % (event,))
  65 + Log.objects.create(agent="Alert manager", message="New file found : %s" % (event,))
68 66 fresh_events.append(event)
69 67 self.old_files = files
70 68 return fresh_events
... ... @@ -101,6 +99,7 @@ class alert_listener(Task):
101 99 alert.strat = StrategyObs.objects.filter(is_default=True)[0]
102 100 name = "GRB - " + str(alert.trig_id)
103 101  
  102 + Log.objects.create(agent="Alert manager", message="Creating alert from file : %s ..." % (alert.strat.xml_file,))
104 103 sb = StrategyBuilder()
105 104 sb.create_request_from_strategy(alert.strat.xml_file, pyros_user, scientific_program, name)
106 105 req = sb.validate()
... ... @@ -110,6 +109,7 @@ class alert_listener(Task):
110 109 seq.save()
111 110 alert.request = req
112 111 alert.save()
  112 + Log.objects.create(agent="Alert manager", message="Alert created.")
113 113  
114 114  
115 115 def get_alert_attributes(self, voevent, event_file):
... ... @@ -158,75 +158,3 @@ class alert_listener(Task):
158 158 return None
159 159  
160 160 return alert
161   -
162   - def run_simulation(self):
163   - '''
164   - Uses a socket to block until a message is received (simulation of a VOEvent reception)
165   - '''
166   - print("run")
167   - self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
168   - self.server_socket.setsockopt(
169   - socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
170   - self.server_socket.setblocking(True)
171   - self.server_socket.bind((IP, PORT))
172   - self.server_socket.listen(12)
173   - while True:
174   - print("Task en attente de signal")
175   - self.server_socket.accept()
176   - print("signal reçu")
177   - self.alert_received_simulation()
178   -
179   - def alert_received_simulation(self):
180   - '''
181   - IMPORTANT : To do simulation, settings.SIMULATION must be set True
182   - Simulates a VOEvent message by reading sequences into a file
183   - Creates a request with them
184   - '''
185   -
186   - # je supprime les sequences et requetes déjà existantes
187   - majordome.TaskManager.delete_pending_alert()
188   - Plan.objects.all().delete()
189   - Album.objects.all().delete()
190   - Sequence.objects.all().delete()
191   - Alert.objects.all().delete()
192   - Request.objects.all().delete()
193   - ScheduleHasSequences.objects.all().delete()
194   -
195   - Log.objects.create(agent='Alert manager', message='Alert received')
196   -
197   - for file_name in os.listdir(observation_manager.tasks.IMAGES_FOLDER):
198   - if file_name != "empty":
199   - file_path = os.path.join(
200   - observation_manager.tasks.IMAGES_FOLDER, file_name)
201   - os.unlink(file_path)
202   -
203   - print("All deleted")
204   -
205   - # j'ouvre le fichier des séquences de simulation, et je le lis ligne par ligne
206   - with open("alert_manager/simulation_sequences", 'r') as sequences_file:
207   - sequences = sequences_file.readlines()
208   -
209   - print("File read")
210   -
211   - Log.objects.create(
212   - agent='Alert manager', message='Simulation sequences file read')
213   -
214   - sequences = [sequence.strip('\n') for sequence in sequences]
215   - request_builder = RequestBuilder()
216   - request_builder.start_new_request(PyrosUser.objects.all()[0], ScientificProgram.objects.all()[0], True, name="Simulation_request")
217   - for sequence in sequences:
218   - sequence_array = sequence.split(" ")
219   - id_seq = sequence_array[0]
220   - priority = int(sequence_array[2])
221   - ''' transforms the duration (seconds) in days (86,400s in a day)'''
222   - duration = Decimal(float(sequence_array[4]) / 86400.0)
223   - jd1 = Decimal("%.8f" % float(sequence_array[5]))
224   - jd2 = Decimal("%.8f" % float(sequence_array[6]))
225   -
226   - request_builder.add_sequence(priority, jd1, jd2, name="Simulation_" + id_seq, duration=duration)
227   -
228   - print("Parsé")
229   - request_builder.validate_request()
230   - print("Validé")
231   - Alert.objects.create(request=request_builder.request, strategyobs=StrategyObs.objects.all()[0])
232   - Log.objects.create(agent='Alert manager', message='Request built')
... ...
src/alert_manager/templates/alert_manager/alert_simulation.html deleted
... ... @@ -1,28 +0,0 @@
1   - {% extends "base.html" %}
2   -
3   - {% load substract %}
4   - {% load jdconverter %}
5   -
6   - {% block title %}
7   - Alert simulation
8   - {% endblock %}
9   -
10   - {% block content %}
11   -
12   - <div style="margin-left:35px;margin-right:35px">
13   -
14   -
15   - <br>
16   - <center>
17   - <div class="row">
18   - <a href="{% url "alert_simulation_start" %}" class="btn btn-lg btn-info">
19   - Simulate an alert !
20   - </a>
21   - </div>
22   - </center>
23   -
24   -
25   -
26   - </div><!-- /#page-wrapper -->
27   -
28   - {% endblock %}
29 0 \ No newline at end of file
src/alert_manager/templates/alert_manager/alerts.html
... ... @@ -21,10 +21,19 @@
21 21 {% endfor %}
22 22 </ul>
23 23 </div>
  24 +
24 25  
25 26 </div>
26 27  
27 28 <div class="row">
  29 + <h3>Alert simulation</h3>
  30 +
  31 + <div>
  32 + <a href="{% url 'start_simulation' %}" class="btn btn-primary">Trigger an alert</a>
  33 + </div>
  34 + </div>
  35 +
  36 + <div class="row">
28 37 <h3>Alerts list :</h3>
29 38  
30 39 <div class="table-responsive">
... ...
src/alert_manager/tests.py
... ... @@ -75,12 +75,12 @@ class AlertListenerTestsCelery(TestCase):
75 75 if os.path.isfile(TEST_FILE_PATH):
76 76 os.remove(TEST_FILE_PATH)
77 77 print("================== DELETE FILE ==================")
78   - time.sleep(8)
  78 + time.sleep(3)
79 79  
80 80 print("================== COPY FILE ==================")
81 81 shutil.copyfile(os.path.join(VOEVENTS_TO_SEND_PATH, TEST_FILE),
82 82 TEST_FILE_PATH)
83   - time.sleep(8)
  83 + time.sleep(4)
84 84  
85 85 self.assertEqual(Alert.objects.count(), 1)
86 86 alert = Alert.objects.all()[0]
... ...
src/alert_manager/urls.py
... ... @@ -2,8 +2,7 @@ from django.conf.urls import url
2 2 from . import views
3 3  
4 4 urlpatterns = [
5   - url(r'^simulation$', views.alert_simulation, name="alert_simulation"),
6   - url(r'^simulation/start$', views.alert_simulation_start, name="alert_simulation_start"),
  5 + url(r'^start_simulation', views.start_simulation, name="start_simulation"),
7 6 url(r'^change_obs_strategy/(?P<alert_id>\d+)$', views.change_obs_strategy, name="change_obs_strategy"),
8 7 url(r'^change_obs_strategy_validate/(?P<alert_id>\d+)$', views.change_obs_strategy_validate, name="change_obs_strategy_validate"),
9 8 url(r'^alerts$', views.alerts_list, name='alerts_list'),
... ...
src/alert_manager/views.py
... ... @@ -2,32 +2,46 @@ from django.shortcuts import render
2 2 from common.models import *
3 3 from alert_manager.StrategyBuilder import StrategyBuilder
4 4 import socket
5   -from alert_manager import tasks as am_tasks
6 5 import majordome
7 6 from django.contrib.auth.decorators import login_required
  7 +import alert_manager.tasks
8 8  
9   -@login_required
10   -def alert_simulation(request):
11   - '''
12   - Opens a page with a button to simulate an alert
13   - '''
14   - return render(request, "alert_manager/alert_simulation.html", {})
  9 +import os, time, shutil
  10 +
  11 +TEST_FILE = "unittest_voevent.xml"
  12 +
  13 +TEST_FILE_PATH = os.path.join(alert_manager.tasks.VOEVENTS_PATH, TEST_FILE)
  14 +
  15 +VOEVENTS_TO_SEND_PATH = "alert_manager/events_to_send"
15 16  
16 17  
17 18 @login_required
18   -def alert_simulation_start(request):
  19 +def start_simulation(request):
19 20 '''
20 21 Called when the alert simulation button is pressed
21   - Opens a connection on the alert_listener simulation socket to unlock it (so that the workflow starts)
  22 + Deletes then copies the test voevent file from the events_received folder
22 23 '''
23   - Log.objects.all().delete()
24   - clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
25   - try:
26   - clientsocket.connect((am_tasks.IP, am_tasks.PORT))
27   - Log.objects.create(agent='Alert manager', message='Start simulation')
28   - except Exception as e:
29   - print("exception : ", e)
30   - return render(request, "alert_manager/alert_simulation.html", {})
  24 +
  25 + if os.path.isfile(TEST_FILE_PATH):
  26 + os.remove(TEST_FILE_PATH)
  27 + print("================== DELETE FILE ==================")
  28 + time.sleep(3)
  29 +
  30 + print("================== COPY FILE ==================")
  31 + shutil.copyfile(os.path.join(VOEVENTS_TO_SEND_PATH, TEST_FILE),
  32 + TEST_FILE_PATH)
  33 + alerts = Alert.objects.order_by("-request__created")
  34 + if StrategyObs.objects.filter(is_default=True).exists():
  35 + current_default = StrategyObs.objects.filter(is_default=True)[0].name
  36 + btn_type = "btn-primary"
  37 + else:
  38 + current_default = "Warning: please choose a default strategy"
  39 + btn_type = "btn-danger"
  40 + strat_list = StrategyObs.objects.all()
  41 +
  42 + success = True
  43 + message = "Simulation started successfully"
  44 + return render(request, "alert_manager/alerts.html", locals())
31 45  
32 46  
33 47 @login_required
... ...
src/common/RequestBuilder.py
... ... @@ -4,38 +4,9 @@ import scheduler.tasks
4 4  
5 5 from decimal import Decimal
6 6  
7   -from django.conf import settings
8   -
9 7  
10 8 class RequestBuilder():
11 9  
12   - def __init__(self):
13   - '''
14   - Might do nothing if this is not a simulation
15   - '''
16   - if settings.SIMULATION == False:
17   - pass
18   - else:
19   - ''' creating dependencies in DB if they don't exist '''
20   - if PyrosUser.objects.count() == 0:
21   - if Country.objects.count() == 0:
22   - country = Country.objects.create(name="default")
23   - else:
24   - country = Country.objects.get()
25   - if UserLevel.objects.count() == 0:
26   - user_level = UserLevel.objects.create(name="default")
27   - else:
28   - user_level = UserLevel.objects.get()
29   - if User.objects.count() == 0:
30   - user = User.objects.create(username="default")
31   - else:
32   - user = User.objects.get()
33   - PyrosUser.objects.create(
34   - country=country, user_level=user_level, user=user, quota=1000)
35   -
36   - if ScientificProgram.objects.count() == 0:
37   - ScientificProgram.objects.create(name="default")
38   -
39 10 def start_new_request(self, pyros_user, scientific_program, is_alert, name="default"):
40 11 '''
41 12 This function MUST be called to build a request.
... ... @@ -138,15 +109,14 @@ class RequestBuilder():
138 109 raise RuntimeError(
139 110 "There must be at least one sequence in a request")
140 111  
141   - if settings.SIMULATION == False:
142   - sequences_having_album = list(set([
143   - seq_id for album, seq_id in self.albums.values()]))
144   - if len(sequences_having_album) != len(self.sequences):
145   - raise RuntimeError("All sequences must have at least one album")
146   - albums_having_plan = list(set([
147   - album_id for plan, album_id in self.plans.values()]))
148   - if len(albums_having_plan) != len(self.albums):
149   - raise RuntimeError("All albums must have at least one plan")
  112 + sequences_having_album = list(set([
  113 + seq_id for album, seq_id in self.albums.values()]))
  114 + if len(sequences_having_album) != len(self.sequences):
  115 + raise RuntimeError("All sequences must have at least one album")
  116 + albums_having_plan = list(set([
  117 + album_id for plan, album_id in self.plans.values()]))
  118 + if len(albums_having_plan) != len(self.albums):
  119 + raise RuntimeError("All albums must have at least one plan")
150 120  
151 121 self.request.save()
152 122 for sequence in self.sequences.values():
... ... @@ -159,20 +129,17 @@ class RequestBuilder():
159 129 plan.album = self.albums[album_id][0]
160 130 plan.save()
161 131  
162   - if settings.SIMULATION == False:
163   - ''' computes the duration of all sequences '''
164   - for sequence in self.sequences.values():
165   - max_duration = 0
166   - for album in sequence.albums.all():
167   - duration = sum([plan.duration for plan in album.plans.all()])
168   - max_duration = duration if duration > max_duration else max_duration
169   - sequence.duration = max_duration
170   - sequence.save()
171   -
172   - if settings.CELERY_TEST == False:
173   - pass
174   - # TODO: uncomment
175   - # mettre une condition de temps pour lancer le scheduling (il faut être nuit -2 min au minimum)
176   - # scheduler.tasks.scheduling.delay(first_schedule=True, alert=self.request.is_alert) # TODO : changer le first_schedule ...
  132 + ''' computes the duration of all sequences '''
  133 + for sequence in self.sequences.values():
  134 + max_duration = 0
  135 + for album in sequence.albums.all():
  136 + duration = sum([plan.duration for plan in album.plans.all()])
  137 + max_duration = duration if duration > max_duration else max_duration
  138 + sequence.duration = max_duration
  139 + sequence.save()
  140 +
  141 + # TODO: uncomment
  142 + # mettre une condition de temps pour lancer le scheduling (il faut être nuit -2 min au minimum)
  143 + scheduler.tasks.scheduling.delay(first_schedule=True, alert=self.request.is_alert) # TODO : changer le first_schedule ...
177 144  
178 145 return self.request
... ...
src/dashboard/templates/dashboard/system_logs.html
... ... @@ -22,7 +22,7 @@
22 22 </div>
23 23  
24 24 <div class="row">
25   - <div class="col-lg-4">
  25 + <div class="col-lg-4" style="overflow:scroll">
26 26 <div class="fixed" style="color: #00CC00;">
27 27 <ul>
28 28 {% for log in alert_logs %}
... ... @@ -32,7 +32,7 @@
32 32 </div>
33 33 </div>
34 34  
35   - <div class="col-lg-4">
  35 + <div class="col-lg-4" style="overflow:scroll">
36 36 <div class="fixed" style="color: #00FFFF;">
37 37 {% for log in scheduler_logs %}
38 38 <li>{{log.created}} : {{log.message}}</li> {% endfor %}
... ... @@ -40,7 +40,7 @@
40 40 </div>
41 41 </div>
42 42  
43   - <div class="col-lg-4">
  43 + <div class="col-lg-4" style="overflow:scroll">
44 44 <div class="fixed" style="color: #FF4500;">
45 45 {% for log in majordome_logs %}
46 46 <li>{{log.created}} : {{log.message}}</li> {% endfor %}
... ... @@ -69,7 +69,7 @@
69 69 </div>
70 70  
71 71 <div class="row">
72   - <div class="col-lg-4">
  72 + <div class="col-lg-4" style="overflow:scroll">
73 73  
74 74 <div class="fixed" style="color: #FFD700;">
75 75 {% for log in obs_logs %}
... ... @@ -81,7 +81,7 @@
81 81  
82 82  
83 83  
84   - <div class="col-lg-4">
  84 + <div class="col-lg-4" style="overflow:scroll">
85 85  
86 86 <div class="fixed" style="color: #FF00FF;">
87 87 {% for log in analyzer_logs %}
... ... @@ -92,7 +92,7 @@
92 92 </div>
93 93  
94 94  
95   - <div class="col-lg-4">
  95 + <div class="col-lg-4" style="overflow:scroll">
96 96  
97 97 <div class="fixed" style="color: #FF9933;">
98 98 {% for log in monitoring_logs %}
... ...
src/devices/Device.py
... ... @@ -91,14 +91,23 @@ class DeviceController():
91 91 if len(args) not in msg[1]:
92 92 raise ValueError("Bad number of arguments")
93 93  
94   - for arg in args:
95   - if type(arg) != msg[2]:
96   - raise TypeError("Bad type of argument: expected %s" % (msg[2],))
  94 + if type(msg[2]) is str:
  95 + if msg[2] not in self.enums.keys():
  96 + raise TypeError("Enum %s doesn't exist. Please check the grammar.json file." % (msg[2],))
  97 + enum = self.enums[msg[2]]
  98 + for arg in args:
  99 + if arg not in enum:
  100 + raise TypeError("Bad value '%s' for enum '%s'" % (arg, msg[2]))
  101 + else:
  102 + for arg in args:
  103 + if type(arg) != msg[2]:
  104 + raise TypeError("Bad type of argument: expected %s" % (msg[2],))
  105 +
97 106  
98 107 self.init_socket()
99 108 message = "SET " + cmd + " " + " ".join([str(arg) for arg in args])
100 109 self.sock.send(bytes(message, "UTF-8"))
101   - print("set ", cmd)
  110 + # print("set ", cmd)
102 111  
103 112  
104 113 def get(self, cmd):
... ... @@ -111,10 +120,10 @@ class DeviceController():
111 120 self.init_socket()
112 121 message = "GET " + cmd
113 122 self.sock.send(bytes(message, "UTF-8"))
114   - print("get ", cmd)
  123 + # print("get ", cmd)
115 124 # TODO: gérer le timeout
116 125 ret = self.sock.recv(1024).decode()
117   - print("return : ", ret)
  126 + # print("return : ", ret)
118 127 return ret
119 128  
120 129  
... ... @@ -129,11 +138,19 @@ class DeviceController():
129 138 if len(args) not in msg[1]:
130 139 raise ValueError("Bad number of arguments")
131 140  
132   - for arg in args:
133   - if type(arg) != msg[2]:
134   - raise TypeError("Bad type of argument: expected %s" % (msg[2],))
  141 + if type(msg[2]) is str:
  142 + if msg[2] not in self.enums.keys():
  143 + raise TypeError("Enum %s doesn't exist. Please check the grammar.json file." % (msg[2],))
  144 + enum = self.enums[msg[2]]
  145 + for arg in args:
  146 + if arg not in enum:
  147 + raise TypeError("Bad value '%s' for enum '%s'" % (arg, msg[2]))
  148 + else:
  149 + for arg in args:
  150 + if type(arg) != msg[2]:
  151 + raise TypeError("Bad type of argument: expected %s" % (msg[2],))
135 152  
136 153 self.init_socket()
137 154 message = "DO " + cmd + " " + " ".join([str(arg) for arg in args])
138 155 self.sock.send(bytes(message, "UTF-8"))
139   - print("do ", cmd)
  156 + # print("do ", cmd)
... ...
src/misc/images/GRB - 532871_strat1_000 0 → 100644
src/misc/images/GRB - 532871_strat1_000_analyzed 0 → 100644
src/misc/images/GRB - 532871_strat1_001 0 → 100644
src/misc/images/GRB - 532871_strat1_001_analyzed 0 → 100644
src/misc/images/GRB - 532871_strat1_010 0 → 100644
src/misc/images/GRB - 532871_strat1_100 0 → 100644
src/misc/images/GRB - 532871_strat1_100_analyzed 0 → 100644
src/misc/images/GRB - 532871_strat1_101 0 → 100644
src/misc/images/GRB - 532871_strat1_101_analyzed 0 → 100644
src/misc/images/GRB - 532871_strat1_102 0 → 100644
src/misc/images/GRB - 532871_strat1_102_analyzed 0 → 100644
src/observation_manager/tasks.py
... ... @@ -11,26 +11,33 @@ from devices import Telescope
11 11 import time
12 12 import os
13 13  
14   -class execute_plan_vis(Task):
  14 +class execute_plan(Task):
15 15 '''
16   - Gives the orders to the instruments to retrieve the image(s) of a plan VIS.
17   - Send the images to the analyzer
  16 + Super class for execute_plan_vis / _nir
18 17 '''
19 18  
20   - def run(self, plan_id, countdown):
  19 + def run(self, plan_id, countdown, type):
  20 + '''
  21 + :param type: VIS or NIR
  22 + :param countdown: Time to wait before execution start
  23 + '''
21 24  
22 25 if countdown > 0:
23 26 time.sleep(countdown)
24 27 TaskId.objects.filter(task_id=self.request.id).delete()
25 28  
26   - message = 'Start plan ' + str(plan_id) + ' execution'
27   - Log.objects.create(agent='Observation manager', message=message)
28   - print("execute_plan VIS : ", plan_id)
29   -
  29 + self.type = type
30 30 plan = Plan.objects.get(id=plan_id)
31 31  
  32 + message = 'Start plan ' + plan.name + ' execution'
  33 + Log.objects.create(agent='Observation manager', message=message)
  34 + print("execute_plan " + self.type + " : ", plan.name)
  35 +
32 36 self.tel = Telescope.TelescopeController()
33   - cam = VIS.VISCameraController()
  37 + if self.type == "VIS":
  38 + cam = VIS.VISCameraController()
  39 + else:
  40 + cam = NIR.NIRCameraController()
34 41  
35 42 self.set_camera(cam, plan)
36 43 self.wait_camera_ready(cam)
... ... @@ -43,7 +50,7 @@ class execute_plan_vis(Task):
43 50 time.sleep(1)
44 51 # Penser qu'un plan peut créer plusieurs images (et analyser image par image)
45 52 analysis.delay(plan_id)
46   - message = 'Finished plan ' + str(plan_id) + ' execution'
  53 + message = 'Finished plan ' + plan.name + ' execution'
47 54 Log.objects.create(agent='Observation manager', message=message)
48 55  
49 56 def set_camera(self, cam, plan):
... ... @@ -59,8 +66,12 @@ class execute_plan_vis(Task):
59 66 cam.set("READOUT_FREQUENCY", 20.0)
60 67 cam.set("FILTER", "H")
61 68  
62   - cam.set("EXPOSURE", 180)
63   - cam.set("BINNING", 300, 300)
  69 + if self.type == "VIS":
  70 + cam.set("EXPOSURE", 180)
  71 + cam.set("BINNING", 300, 300)
  72 + else:
  73 + cam.set("NB_IMAGES", 28)
  74 +
64 75  
65 76 def wait_camera_ready(self, cam):
66 77 '''
... ... @@ -98,83 +109,27 @@ class execute_plan_vis(Task):
98 109 time.sleep(1)
99 110  
100 111  
101   -class execute_plan_nir(Task):
  112 +
  113 +
  114 +class execute_plan_vis(execute_plan):
102 115 '''
103   - Gives the orders to the instruments to retrieve the image(s) of a plan NIR.
  116 + Gives the orders to the instruments to retrieve the image(s) of a plan VIS.
104 117 Send the images to the analyzer
105 118 '''
106 119  
107 120 def run(self, plan_id, countdown):
108   - if countdown > 0:
109   - time.sleep(countdown)
110   - TaskId.objects.filter(task_id=self.request.id).delete()
111   -
112   - message = 'Start plan ' + str(plan_id) + ' execution'
113   - Log.objects.create(agent='Observation manager', message=message)
114   - print("execute_plan NIR : ", plan_id)
115   -
116   - plan = Plan.objects.get(pk=plan_id)
  121 + super().run(plan_id, countdown, "VIS")
117 122  
118   - self.tel = Telescope.TelescopeController()
119   - cam = NIR.NIRCameraController()
120   -
121   - self.set_camera(cam, plan)
122   - self.wait_camera_ready(cam)
123   -
124   - cam.do("START")
125   -
126   - st = self.wait_camera_finished(cam)
127   -
128   - time.sleep(1)
129 123  
130   - analysis.delay(plan_id)
131   - message = 'Finished plan ' + str(plan_id) + ' execution'
132   - Log.objects.create(agent='Observation manager', message=message)
133   -
134   - def set_camera(self, cam, plan):
135   -
136   - cam.set("WINDOW", 0, 100, 10, 100)
137   - cam.set("READMODE", "Ramp")
138   - cam.set("FILENAME", plan.name)
139   - cam.set("HEADER", {})
140   - cam.set("READOUT_FREQUENCY", 20.0)
141   - cam.set("FILTER", "H")
142   -
143   - cam.set("NB_IMAGES", 28)
144   -
145   - def wait_camera_ready(self, cam):
146   - '''
147   - Loop to wait for the configuration to be done
148   - '''
149   - st = 0
150   - while st == 0:
151   - st_tel = self.tel.get("STATUS")
152   -
153   - st_cam = cam.get("STATUS")
154   -
155   - st = 1
156   -
157   - if st_tel != "IDLE" or st_cam != "IDLE":
158   - st = 0
159   -
160   -
161   - def wait_camera_finished(self, cam):
162   - '''
163   - Loop to wait for the observation to be finished
164   - '''
  124 +class execute_plan_nir(execute_plan):
  125 + '''
  126 + Gives the orders to the instruments to retrieve the image(s) of a plan NIR.
  127 + Send the images to the analyzer
  128 + '''
165 129  
166   - countdown = int(cam.get("TIMER"))
167   - while countdown > 5:
168   - time.sleep(5)
169   - countdown = int(cam.get("TIMER"))
  130 + def run(self, plan_id, countdown):
  131 + super().run(plan_id, countdown, "NIR")
170 132  
171   - st = 0
172   - while st == 0:
173   - timer = int(cam.get("TIMER"))
174   - if timer == -1:
175   - st = 1
176   - else:
177   - time.sleep(1)
178 133  
179 134  
180 135 class create_calibrations(Task):
... ...
src/pyros/settings.py
... ... @@ -46,10 +46,6 @@ SECRET_KEY = &#39;0*@w)$rq4x1c2w!c#gn58*$*u$w=s8uw2zpr_c3nj*u%qlxc23&#39;
46 46 # SECURITY WARNING: don't run with debug turned on in production!
47 47 DEBUG = True
48 48  
49   -# TODO: enelever le système actuel de simulation d'alerte, et le refaire sur la page principale des alertes
50   -# Supprimer le vue associée (et le template)
51   -SIMULATION = False
52   -
53 49 ALLOWED_HOSTS = ['localhost']
54 50  
55 51  
... ...
src/scripts/delete_all_requests.sh
... ... @@ -10,7 +10,7 @@ MYSQL=`grep &quot;MYSQL = &quot; pyros/settings.py`
10 10 MYSQL=${MYSQL##* }
11 11 MYSQL=${MYSQL%?}
12 12  
13   -REQUEST="delete from plan ; delete from album ; delete from sequence ; delete from alert ; delete from request ; delete from schedule_has_sequences ; delete from schedule ;"
  13 +REQUEST="delete from plan ; delete from album ; delete from sequence ; delete from alert ; delete from request ; delete from schedule_has_sequences ; delete from schedule ; delete from log"
14 14  
15 15 if [ "$MYSQL" == "True" ]; then
16 16 mysql -u root -e "$REQUEST" pyros
... ...