Commit 729f836f7e2b54e3103a42fd2bf6910b35109f85

Authored by Etienne Pallier
2 parents 0393f729 4778e01f
Exists in master and in 1 other branch dev

Merge branch 'master' of https://gitlab.irap.omp.eu/epallier/pyros

.pydevproject deleted
@@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
1 -<?xml version="1.0" encoding="UTF-8" standalone="no"?>  
2 -<?eclipse-pydev version="1.0"?><pydev_project>  
3 -<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">pyros_venv_py3</pydev_property>  
4 -<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>  
5 -<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">  
6 -<path>/${PROJECT_DIR_NAME}/src</path>  
7 -</pydev_pathproperty>  
8 -<pydev_variables_property name="org.python.pydev.PROJECT_VARIABLE_SUBSTITUTION">  
9 -<key>DJANGO_MANAGE_LOCATION</key>  
10 -<value>src/manage.py</value>  
11 -<key>DJANGO_SETTINGS_MODULE</key>  
12 -<value>pyros.settings</value>  
13 -</pydev_variables_property>  
14 -</pydev_project>  
install/install_requirements.sh
@@ -17,7 +17,7 @@ PORT=8000 @@ -17,7 +17,7 @@ PORT=8000
17 17
18 # String variables 18 # String variables
19 19
20 -ASK_USE_MYSQL="-Do you wish to use mysql in your project ? Only if mysql-server is installed. (Y/N)" 20 +ASK_USE_MYSQL="-Do you wish to use mysql in your project ? Only if mysql-server is installed. (Default: No) (Y/N)"
21 ASK_MYSQL_USERNAME="-MySQL server login (default $USERNAME): " 21 ASK_MYSQL_USERNAME="-MySQL server login (default $USERNAME): "
22 SQL_SCRIPT="CREATE DATABASE IF NOT EXISTS pyros; GRANT USAGE ON *.* TO '$DB_USER'; DROP USER '$DB_USER'; GRANT ALL ON pyros.* TO '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASSWORD'; GRANT ALL ON test_pyros.* TO '$DB_USER'@'localhost'" 22 SQL_SCRIPT="CREATE DATABASE IF NOT EXISTS pyros; GRANT USAGE ON *.* TO '$DB_USER'; DROP USER '$DB_USER'; GRANT ALL ON pyros.* TO '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASSWORD'; GRANT ALL ON test_pyros.* TO '$DB_USER'@'localhost'"
23 SQL_FAIL="-Failed to execute database script, probably due to a wrong username or password" 23 SQL_FAIL="-Failed to execute database script, probably due to a wrong username or password"
src/analyzer/tasks.py
@@ -9,12 +9,17 @@ import os @@ -9,12 +9,17 @@ import os
9 import observation_manager 9 import observation_manager
10 10
11 class analysis(Task): 11 class analysis(Task):
  12 + '''
  13 + Gets the name of the image to analyze in parameter
  14 + It applies the calibrations, then makes the
  15 + '''
12 16
13 def run(self, image_name): 17 def run(self, image_name):
14 # je dois jsute créer mes deux fonctions, une pour les corrections, et l'autre pour les analyses 18 # je dois jsute créer mes deux fonctions, une pour les corrections, et l'autre pour les analyses
15 print("analysis : ", image_name) 19 print("analysis : ", image_name)
16 20
17 new_img = self.apply_calibrations(image_name) 21 new_img = self.apply_calibrations(image_name)
  22 + # TODO: Faire ça uniquement si c'est une image provenant d'une alerte
18 self.do_analysis(new_img) 23 self.do_analysis(new_img)
19 24
20 # TODO: virer tout ça 25 # TODO: virer tout ça
@@ -25,12 +30,20 @@ class analysis(Task): @@ -25,12 +30,20 @@ class analysis(Task):
25 Log.objects.create(agent='Analyzer', message=message) 30 Log.objects.create(agent='Analyzer', message=message)
26 31
27 def apply_calibrations(self, image_name): 32 def apply_calibrations(self, image_name):
  33 + '''
  34 + Uses the calibration files to correct the image (changes the image bytes)
  35 + Send the new images to the FSC
  36 + '''
28 # TODO: récupérer les bonne images dans le dossier analyzer/calibrations, et les appliquer 37 # TODO: récupérer les bonne images dans le dossier analyzer/calibrations, et les appliquer
29 # renvoie le nom de l'image calibrée; penser à vérifier que les fichiers de calibration existent à cet endroit 38 # renvoie le nom de l'image calibrée; penser à vérifier que les fichiers de calibration existent à cet endroit
30 # TODO: copier l'image dans le bon dossier pour le filesync (lien symbolique) 39 # TODO: copier l'image dans le bon dossier pour le filesync (lien symbolique)
31 return "toto.fitz" 40 return "toto.fitz"
32 41
33 def do_analysis(self, new_img): 42 def do_analysis(self, new_img):
  43 + '''
  44 + Use the scientific softwares to create analysis files (doesn't change the image bytes)
  45 + Sends the results to the FSC
  46 + '''
34 # TODO: utiliser les programmes scientifiques pour générer les analyses 47 # TODO: utiliser les programmes scientifiques pour générer les analyses
35 # TODO: copier les résultats des analyses dans le bon dossier pour le filesync (lien symbolique) 48 # TODO: copier les résultats des analyses dans le bon dossier pour le filesync (lien symbolique)
36 pass 49 pass
37 \ No newline at end of file 50 \ No newline at end of file
src/common/Device.py
@@ -2,13 +2,23 @@ from pyrosapp.models import * @@ -2,13 +2,23 @@ from pyrosapp.models import *
2 2
3 3
4 class DeviceObj(): 4 class DeviceObj():
  5 + '''
  6 + Generic object for the communication with all the devices (need inheritance)
  7 + '''
  8 +
5 9
6 def __init__(self): 10 def __init__(self):
  11 + '''
  12 + The messages and their parameters will be defined in the objects that inherits from this class
  13 + '''
7 set_msgs = [] 14 set_msgs = []
8 get_msgs = [] 15 get_msgs = []
9 do_msgs = [] 16 do_msgs = []
10 17
11 def set(self, cmd, *args): 18 def set(self, cmd, *args):
  19 + '''
  20 + Send a SET message to the device
  21 + '''
12 msg = [msg for msg in self.set_msgs if msg[0] == cmd] 22 msg = [msg for msg in self.set_msgs if msg[0] == cmd]
13 if len(msg) == 0: 23 if len(msg) == 0:
14 raise ValueError("Invalid message argument %s" % (cmd,)) 24 raise ValueError("Invalid message argument %s" % (cmd,))
@@ -24,6 +34,9 @@ class DeviceObj(): @@ -24,6 +34,9 @@ class DeviceObj():
24 34
25 35
26 def get(self, cmd): 36 def get(self, cmd):
  37 + '''
  38 + Send a GET message to the device
  39 + '''
27 if cmd not in self.get_msgs: 40 if cmd not in self.get_msgs:
28 raise ValueError("Invalid message argument %s" % (cmd,)) 41 raise ValueError("Invalid message argument %s" % (cmd,))
29 42
@@ -31,6 +44,9 @@ class DeviceObj(): @@ -31,6 +44,9 @@ class DeviceObj():
31 44
32 45
33 def do(self, cmd, *args): 46 def do(self, cmd, *args):
  47 + '''
  48 + Send a DO message to the device
  49 + '''
34 msg = [msg for msg in self.do_msgs if msg[0] == cmd] 50 msg = [msg for msg in self.do_msgs if msg[0] == cmd]
35 if len(msg) == 0: 51 if len(msg) == 0:
36 raise ValueError("Invalid message argument %s" % (cmd,)) 52 raise ValueError("Invalid message argument %s" % (cmd,))
src/majordome/tasks.py
@@ -11,8 +11,18 @@ from DateTime import DateTime @@ -11,8 +11,18 @@ from DateTime import DateTime
11 import scheduler 11 import scheduler
12 12
13 DAILY_SECOND = 1 / 86400 13 DAILY_SECOND = 1 / 86400
  14 +MAX_WAIT = 180
14 15
15 class execute_sequence(Task): 16 class execute_sequence(Task):
  17 + '''
  18 + Task to handle the execution of ONE sequence
  19 + It receives the shs' id in parameter.
  20 + It prepairs the instruments and cut the sequence in plan. Then the execute_plan tasks are created.
  21 +
  22 + If possible, it advances the sequence start.
  23 + If the sequence can't start within MAX_WAIT seconds, it relaunches a scheduling
  24 + '''
  25 +
16 26
17 def run(self, shs_pk): 27 def run(self, shs_pk):
18 # on ne supprime JAMAIS un séquence en cours, donc je peux la virer des tasks dès le début 28 # on ne supprime JAMAIS un séquence en cours, donc je peux la virer des tasks dès le début
@@ -33,7 +43,7 @@ class execute_sequence(Task): @@ -33,7 +43,7 @@ class execute_sequence(Task):
33 43
34 countdown = self.get_countdown(shs, sequence) 44 countdown = self.get_countdown(shs, sequence)
35 45
36 - if countdown > 180 * DAILY_SECOND: 46 + if countdown > MAX_WAIT * DAILY_SECOND:
37 scheduler.tasks.scheduling.delay(first_schedule=True, alert=False) # TODO : changer le first_schedule à False 47 scheduler.tasks.scheduling.delay(first_schedule=True, alert=False) # TODO : changer le first_schedule à False
38 return 48 return
39 49
@@ -116,6 +126,10 @@ class execute_sequence(Task): @@ -116,6 +126,10 @@ class execute_sequence(Task):
116 return countdown 126 return countdown
117 127
118 class system_pause(Task): 128 class system_pause(Task):
  129 + '''
  130 + Task called by the monitoring in case of problem.
  131 + It stops the system and the instruments.
  132 + '''
119 133
120 def run(self): 134 def run(self):
121 time.sleep(5) 135 time.sleep(5)
@@ -123,14 +137,22 @@ class system_pause(Task): @@ -123,14 +137,22 @@ class system_pause(Task):
123 137
124 138
125 class system_restart(Task): 139 class system_restart(Task):
126 - 140 + '''
  141 + Task called by the monitoring when there is no more problem.
  142 + Should just make a scheduling.
  143 + '''
127 def run(self): 144 def run(self):
128 time.sleep(5) 145 time.sleep(5)
129 print("system_restart") 146 print("system_restart")
130 147
131 148
132 class change_obs_conditions(Task): 149 class change_obs_conditions(Task):
133 - 150 + '''
  151 + Task called by the monitoring when the obs condition have changed.
  152 + It reads them in the DB and changes the sequences status in consequence.
  153 + If needed, relaunches a scheduling
  154 + '''
134 def run(self): 155 def run(self):
  156 + # important : penser à rendre les quotas aux users
135 time.sleep(5) 157 time.sleep(5)
136 print("change_obs_conditions") 158 print("change_obs_conditions")
src/monitoring/tasks.py
@@ -16,6 +16,16 @@ import time @@ -16,6 +16,16 @@ import time
16 TIMER_CHECK = 10 # in seconds 16 TIMER_CHECK = 10 # in seconds
17 17
18 class monitoring(Task): 18 class monitoring(Task):
  19 + '''
  20 + Infinite task created at the program's start.
  21 + It initilize all the external connections, and starts the alert_listener.
  22 +
  23 + This is the place to put the starting configurations.
  24 +
  25 + Once the starting configurations are done, it becomes a loop that checks the PLC and instruments status.
  26 + It also handles the beginning and the end of the night, recalculating them at each end of night.
  27 + '''
  28 +
19 29
20 def run(self): 30 def run(self):
21 self.configure_instruments() 31 self.configure_instruments()
@@ -34,6 +44,9 @@ class monitoring(Task): @@ -34,6 +44,9 @@ class monitoring(Task):
34 self.timers_loop() 44 self.timers_loop()
35 45
36 def configure_instruments(self): 46 def configure_instruments(self):
  47 + '''
  48 + Creates the communication objects for each instrument, and give them the basic configurations.
  49 + '''
37 self.tel = TelescopeObj() 50 self.tel = TelescopeObj()
38 self.vis_camera = VISCameraObj() 51 self.vis_camera = VISCameraObj()
39 self.nir_camera = NIRCameraObj() 52 self.nir_camera = NIRCameraObj()
@@ -49,10 +62,16 @@ class monitoring(Task): @@ -49,10 +62,16 @@ class monitoring(Task):
49 # Cette fonction dépendra du moyen de partager les connections des instruments 62 # Cette fonction dépendra du moyen de partager les connections des instruments
50 63
51 def connect_to_plc(self): 64 def connect_to_plc(self):
  65 + '''
  66 + Initialize the connection to the PLC
  67 + '''
52 # TODO: initialiser la co avec le plc et stocker les identifiants de la co dans self 68 # TODO: initialiser la co avec le plc et stocker les identifiants de la co dans self
53 pass 69 pass
54 70
55 def update_software_versions(self): 71 def update_software_versions(self):
  72 + '''
  73 + Reads the softwares versions in the settings.py, store them in the DB and send them to the IC.
  74 + '''
56 versions = settings.MODULES_VERSIONS 75 versions = settings.MODULES_VERSIONS
57 76
58 for module, version in versions.items(): 77 for module, version in versions.items():
@@ -65,12 +84,18 @@ class monitoring(Task): @@ -65,12 +84,18 @@ class monitoring(Task):
65 # TODO: envoyer les versions à l'IC 84 # TODO: envoyer les versions à l'IC
66 85
67 def get_night_start_end(self): 86 def get_night_start_end(self):
  87 + '''
  88 + Computes the beginning and the end of the following (or current) night
  89 + '''
68 # TODO: utiliser un logiciel by AK pour stocker en local le début et la fin de la nuit (on est peut-être dedans) 90 # TODO: utiliser un logiciel by AK pour stocker en local le début et la fin de la nuit (on est peut-être dedans)
69 self.night_start = time.time() + 180 / 86400 91 self.night_start = time.time() + 180 / 86400
70 self.night_end = time.time() + 360 / 86400 92 self.night_end = time.time() + 360 / 86400
71 pass 93 pass
72 94
73 def wait_devices_ready(self): 95 def wait_devices_ready(self):
  96 + '''
  97 + Loop to wait for the device to be idle avec the starting configurations.
  98 + '''
74 # TODO: faire une boucle pour attendre que les devices soient prêts 99 # TODO: faire une boucle pour attendre que les devices soient prêts
75 pass 100 pass
76 101
src/observation_manager/tasks.py
@@ -14,6 +14,10 @@ import os @@ -14,6 +14,10 @@ import os
14 IMAGES_FOLDER = 'simulation_images' 14 IMAGES_FOLDER = 'simulation_images'
15 15
16 class execute_plan_vis(Task): 16 class execute_plan_vis(Task):
  17 + '''
  18 + Gives the orders to the instruments to retrieve the image(s) of a plan VIS.
  19 + Send hte images to the analyzer
  20 + '''
17 21
18 def run(self, plan_id, countdown): 22 def run(self, plan_id, countdown):
19 # print("ex plan : ", self.request.id) 23 # print("ex plan : ", self.request.id)
@@ -48,6 +52,9 @@ class execute_plan_vis(Task): @@ -48,6 +52,9 @@ class execute_plan_vis(Task):
48 Log.objects.create(agent='Observation manager', message=message) 52 Log.objects.create(agent='Observation manager', message=message)
49 53
50 def set_camera(self, cam, plan): 54 def set_camera(self, cam, plan):
  55 + '''
  56 + Set the camera configuration
  57 + '''
51 58
52 cam.filter_wheel.set("FILTER", FilterW.FilterEnum.h) 59 cam.filter_wheel.set("FILTER", FilterW.FilterEnum.h)
53 cam.filter_wheel.do("CHANGE_FILTER") 60 cam.filter_wheel.do("CHANGE_FILTER")
@@ -62,7 +69,9 @@ class execute_plan_vis(Task): @@ -62,7 +69,9 @@ class execute_plan_vis(Task):
62 cam.set("BINNING", 300, 300) 69 cam.set("BINNING", 300, 300)
63 70
64 def wait_camera_ready(self, cam): 71 def wait_camera_ready(self, cam):
65 - 72 + '''
  73 + Loop to wait for the configuration to be done
  74 + '''
66 st = 0 75 st = 0
67 while st == 0: 76 while st == 0:
68 # TODO: uncomment quand on caura la télescope en global 77 # TODO: uncomment quand on caura la télescope en global
@@ -77,7 +86,9 @@ class execute_plan_vis(Task): @@ -77,7 +86,9 @@ class execute_plan_vis(Task):
77 86
78 87
79 def wait_camera_finished(self, cam): 88 def wait_camera_finished(self, cam):
80 - 89 + '''
  90 + Loop to wait for the observation to be finished
  91 + '''
81 # countdown = cam.get("TIMER") 92 # countdown = cam.get("TIMER")
82 # while countdown > 5: 93 # while countdown > 5:
83 # time.sleep(5) 94 # time.sleep(5)
@@ -96,6 +107,10 @@ class execute_plan_vis(Task): @@ -96,6 +107,10 @@ class execute_plan_vis(Task):
96 107
97 108
98 class execute_plan_nir(Task): 109 class execute_plan_nir(Task):
  110 + '''
  111 + Gives the orders to the instruments to retrieve the image(s) of a plan NIR.
  112 + Send the images to the analyzer
  113 + '''
99 114
100 def run(self, plan_id, countdown): 115 def run(self, plan_id, countdown):
101 # print("ex plan : ", self.request.id) 116 # print("ex plan : ", self.request.id)
@@ -181,4 +196,4 @@ class execute_calibrations(Task): @@ -181,4 +196,4 @@ class execute_calibrations(Task):
181 When they are all finished, it creates the 'super' calibration files. 196 When they are all finished, it creates the 'super' calibration files.
182 ''' 197 '''
183 def run(self): 198 def run(self):
184 - pass  
185 \ No newline at end of file 199 \ No newline at end of file
  200 + pass
src/routine_manager/RequestSerializer.py
@@ -84,6 +84,10 @@ class RequestSerializer(): @@ -84,6 +84,10 @@ class RequestSerializer():
84 return "" 84 return ""
85 85
86 def unserialize_request(self, request): 86 def unserialize_request(self, request):
  87 + '''
  88 + Receives an xml.etree request and unserialize it
  89 + '''
  90 +
87 if request.tag != "request": 91 if request.tag != "request":
88 return "Main object should be a request (found %s instead)" % (request.tag,) 92 return "Main object should be a request (found %s instead)" % (request.tag,)
89 possible_attribs = ["name", "scientific_program", "target_type"] 93 possible_attribs = ["name", "scientific_program", "target_type"]
src/scheduler/tasks.py
@@ -35,6 +35,7 @@ class scheduling(Task): @@ -35,6 +35,7 @@ class scheduling(Task):
35 TaskId.objects.create(task_id=res.id, task="execute_sequence") 35 TaskId.objects.create(task_id=res.id, task="execute_sequence")
36 36
37 def determine_schedule_limits(self): 37 def determine_schedule_limits(self):
  38 + # TODO: virer ce système là pour la production
38 sequences = Sequence.objects.all() 39 sequences = Sequence.objects.all()
39 ''' Find the most recurrent night in all sequences ''' 40 ''' Find the most recurrent night in all sequences '''
40 days = {} 41 days = {}