Commit 945b36d20eeb986348da190cc79595fdbf715cdb
1 parent
0a0c921f
Exists in
master
and in
1 other branch
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
Showing
27 changed files
with
267 additions
and
397 deletions
Show diff stats
... | ... | @@ -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/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 = '0*@w)$rq4x1c2w!c#gn58*$*u$w=s8uw2zpr_c3nj*u%qlxc23' |
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 "MYSQL = " 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 | ... | ... |