Commit c53a13e0429855aaeae07da38ff391b160b81a4e
1 parent
ff448d43
Exists in
master
and in
1 other branch
Updating a lot of things (spotted a bug in scheduler)
Showing
28 changed files
with
495 additions
and
246 deletions
Show diff stats
logs/2 deleted
... | ... | @@ -1 +0,0 @@ |
1 | -test |
pyros.py
... | ... | @@ -436,6 +436,14 @@ class Pyros(AManager): |
436 | 436 | self.migrate() |
437 | 437 | return 0 |
438 | 438 | |
439 | + def reset_config(self): | |
440 | + self.changeDirectory("src") | |
441 | + self.replacePatternInFile("CELERY_TEST = True", "CELERY_TEST = False", "pyros/settings.py") | |
442 | + self.replacePatternInFile("SIMULATOR = True", "SIMULATOR = False", "pyros/settings.py") | |
443 | + self.addExecuted(self.current_command, "reset configuration") | |
444 | + self.changeDirectory("..") | |
445 | + return 0 | |
446 | + | |
439 | 447 | def unittest(self): |
440 | 448 | self.changeDirectory("src") |
441 | 449 | self.execProcessFromVenv(self.venv_bin + " manage.py test common scheduler routine_manager user_manager alert_manager.tests.TestStrategyChange") |
... | ... | @@ -476,13 +484,10 @@ class Pyros(AManager): |
476 | 484 | self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q majordome_q -n pyros@majordome -c 1") |
477 | 485 | |
478 | 486 | self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q scheduling_q --purge -n pyros@scheduling -c 1") |
479 | - self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q execute_sequence_q --purge -n pyros@execute_sequence -c 1") | |
480 | 487 | self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q execute_plan_vis_q --purge -n pyros@execute_plan_vis -c 1") |
481 | - self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q execute_plan_vis_q --purge -n pyros@execute_plan_nir -c 1") | |
488 | + self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q execute_plan_nir_q --purge -n pyros@execute_plan_nir -c 1") | |
482 | 489 | self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q create_calibrations_q --purge -n pyros@create_calibrations -c 1") |
483 | 490 | self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q analysis_q --purge -n pyros@analysis -c 1") |
484 | - self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q system_status_q --purge -n pyros@system_status -c 1") | |
485 | - self.execProcessFromVenvAsync(self.venv_cel + " worker -A pyros -Q change_obs_conditions_q --purge -n pyros@change_obs_conditions -c 1") | |
486 | 491 | self.changeDirectory("..") |
487 | 492 | return 0 |
488 | 493 | |
... | ... | @@ -520,7 +525,7 @@ class Pyros(AManager): |
520 | 525 | self.printColor(Colors.GREEN, "The simulator run on a temp database : src/testdb.sqlite3") |
521 | 526 | self.printColor(Colors.GREEN, "The simulation will be ended by the task 'simulator herself'") |
522 | 527 | self.printColor(Colors.GREEN, "If you want to shutdown the simulation, please run :") |
523 | - self.printColor(Colors.GREEN, "CTRL-C or ./pyrosrun.sh kill_simulation") | |
528 | + self.printColor(Colors.GREEN, "CTRL-C or python pyros.py kill_simulation") | |
524 | 529 | self.printColor(Colors.GREEN, "If the simulation isn't correctly killed, please switch the variable") |
525 | 530 | self.printColor(Colors.GREEN, "CELERY_TEST in src/pyros/settings.py to false") |
526 | 531 | self.printFullTerm(Colors.WARNING, "SUMMARY") |
... | ... | @@ -533,6 +538,10 @@ class Pyros(AManager): |
533 | 538 | self.changeDirectory("..") |
534 | 539 | self.singleWorker("scheduling") |
535 | 540 | self.singleWorker("majordome") |
541 | + self.singleWorker("execute_plan_vis") | |
542 | + self.singleWorker("execute_plan_nir") | |
543 | + self.singleWorker("create_calibrations") | |
544 | + self.singleWorker("analysis") | |
536 | 545 | self.sleep(3) |
537 | 546 | procs = [] |
538 | 547 | self.changeDirectory("simulators") |
... | ... | @@ -646,6 +655,20 @@ class Pyros(AManager): |
646 | 655 | self.changeDirectory("..") |
647 | 656 | return 0 |
648 | 657 | |
658 | + def mysql_on(self): | |
659 | + self.changeDirectory("src") | |
660 | + self.replacePatternInFile("MYSQL = False", "MYSQL = True", "pyros/settings.py") | |
661 | + self.addExecuted(self.current_command, "Switch to mysql") | |
662 | + self.changeDirectory("..") | |
663 | + return 0 | |
664 | + | |
665 | + def mysql_off(self): | |
666 | + self.changeDirectory("src") | |
667 | + self.replacePatternInFile("MYSQL = True", "MYSQL = False", "pyros/settings.py") | |
668 | + self.addExecuted(self.current_command, "Switch to sqlite") | |
669 | + self.changeDirectory("..") | |
670 | + return 0 | |
671 | + | |
649 | 672 | def __init__(self, argv): |
650 | 673 | super(Pyros, self).__init__(argv) |
651 | 674 | self.commandMatcher = { |
... | ... | @@ -657,9 +680,12 @@ class Pyros(AManager): |
657 | 680 | "clean_logs": self.clean_logs, |
658 | 681 | "test": self.test, |
659 | 682 | "migrate": self.migrate, |
683 | + "mysql_on": self.mysql_on, | |
684 | + "mysql_off": self.mysql_off, | |
660 | 685 | "makemigrations": self.makemigrations, |
661 | 686 | "updatedb": self.updatedb, |
662 | 687 | "unittest": self.unittest, |
688 | + "reset_config": self.reset_config, | |
663 | 689 | "test_all": self.test_all, |
664 | 690 | "celery_on": self.celery_on, |
665 | 691 | "init_database": self.init_database, |
... | ... | @@ -681,7 +707,10 @@ class Pyros(AManager): |
681 | 707 | "clean_logs": "clean the log directory", |
682 | 708 | "test": "launch the server tests", |
683 | 709 | "migrate": "execute migrations", |
710 | + "mysql_on": "switch the database to be used to MYSQL", | |
711 | + "mysql_off": "switch the database to be used usage to SQLITE", | |
684 | 712 | "makemigrations": "create new migrations", |
713 | + "reset_config": "Reset the configuration in settings.py", | |
685 | 714 | "help": "Help message", |
686 | 715 | "updatedb": "Update the database", |
687 | 716 | "kill_server": "Kill the web server on port 8000", | ... | ... |
simulators/camera/cameraNIRSimulator.py
... | ... | @@ -10,7 +10,7 @@ from utils.StatusManager import StatusManager |
10 | 10 | |
11 | 11 | class CameraNIRSimulator(Device, StatusManager): |
12 | 12 | status = {"status" : "VALID"} |
13 | - images_folder = '../../src/misc/images' | |
13 | + images_folder = '../../images_folder/' | |
14 | 14 | exposure_time = 5 |
15 | 15 | shutter_time = 3 |
16 | 16 | cooler_timer = 5 |
... | ... | @@ -42,6 +42,17 @@ class CameraNIRSimulator(Device, StatusManager): |
42 | 42 | else: |
43 | 43 | answer = "Invalid cmd for GET: " + cmd |
44 | 44 | self.cameraNIRPrint(answer) |
45 | + elif (cmd_type == "DO"): | |
46 | + if (cmd == "CAPTURE"): | |
47 | + plan_name = args[0] | |
48 | + nb_images = int(args[1]) | |
49 | + duration = args[2] | |
50 | + i = 0 | |
51 | + while i < nb_images: | |
52 | + with open(self.images_folder + plan_name + os.sep + "nir_image_"+str(i), "w") as f: | |
53 | + f.write(plan_name+" "+str(nb_images)+" "+str(duration)) | |
54 | + i += 1 | |
55 | + answer = "CAPTURING" | |
45 | 56 | else: |
46 | 57 | self.cameraNIRPrint("Ignored message " + data) |
47 | 58 | ... | ... |
simulators/camera/cameraVISSimulator.py
... | ... | @@ -10,7 +10,7 @@ from utils.StatusManager import StatusManager |
10 | 10 | |
11 | 11 | class CameraVISSimulator(Device, StatusManager): |
12 | 12 | status = {"status" : "VALID"} |
13 | - images_folder = '../../src/misc/images' | |
13 | + images_folder = '../../images_folder/' | |
14 | 14 | exposure_time = 5 |
15 | 15 | shutter_time = 3 |
16 | 16 | cooler_timer = 5 |
... | ... | @@ -42,6 +42,17 @@ class CameraVISSimulator(Device, StatusManager): |
42 | 42 | else: |
43 | 43 | answer = "Invalid cmd for GET: " + cmd |
44 | 44 | self.cameraVISPrint(answer) |
45 | + elif (cmd_type == "DO"): | |
46 | + if (cmd == "CAPTURE"): | |
47 | + plan_name = args[0] | |
48 | + nb_images = int(args[1]) | |
49 | + duration = args[2] | |
50 | + i = 0 | |
51 | + while i < nb_images: | |
52 | + with open(self.images_folder + plan_name + os.sep + "nir_image_" + str(i), "w") as f: | |
53 | + f.write(plan_name + " " + str(nb_images) + " " + str(duration)) | |
54 | + i += 1 | |
55 | + answer = "CAPTURING" | |
45 | 56 | else: |
46 | 57 | self.cameraVISPrint("Ignored message " + data) |
47 | 58 | ... | ... |
simulators/resources/routine_request_01.xml
1 | 1 | <?xml version="1.0" ?> |
2 | 2 | <request submitted="1" relative="1" name="RequestSimulator" scientific_program="GRB" target_type="test"> |
3 | - <sequence duration="200" jd1="15" jd2="60000" name="Sequence1 (200secs)" target_coords="10"> | |
3 | + <sequence duration="10" jd1="15" jd2="60000" name="Sequence1 (200secs)" target_coords="10"> | |
4 | 4 | <album detector="Visible camera" name="alb"> |
5 | - <plan duration="200" filter="First infrared filter" name="New plan" nb_images="5"/> | |
5 | + <plan duration="10" filter="First infrared filter" name="New plan" nb_images="5"/> | |
6 | 6 | </album> |
7 | 7 | </sequence> |
8 | 8 | </request> | ... | ... |
simulators/resources/routine_request_07.xml
1 | 1 | <?xml version="1.0" ?> |
2 | 2 | <request submitted="1" relative="1" name="RequestSimulator" scientific_program="GRB" target_type="test"> |
3 | - <sequence duration="200" jd1="6000" jd2="60000" name="Sequence7 (200secs)" target_coords="10"> | |
3 | + <sequence duration="10" jd1="6000" jd2="60000" name="Sequence7 (200secs)" target_coords="10"> | |
4 | 4 | <album detector="Visible camera" name="alb"> |
5 | - <plan duration="200" filter="First infrared filter" name="New plan" nb_images="5"/> | |
5 | + <plan duration="10" filter="First infrared filter" name="New plan" nb_images="5"/> | |
6 | 6 | </album> |
7 | 7 | </sequence> |
8 | 8 | </request> | ... | ... |
simulators/resources/routine_request_08.xml
1 | 1 | <?xml version="1.0" ?> |
2 | 2 | <request submitted="1" relative="1" name="RequestSimulator" scientific_program="GRB" target_type="test"> |
3 | - <sequence duration="200" jd1="19000" jd2="60000" name="Sequence8 (200secs)" target_coords="10"> | |
3 | + <sequence duration="20" jd1="19000" jd2="60000" name="Sequence8 (200secs)" target_coords="10"> | |
4 | 4 | <album detector="Visible camera" name="alb"> |
5 | - <plan duration="200" filter="First infrared filter" name="New plan" nb_images="5"/> | |
5 | + <plan duration="20" filter="First infrared filter" name="New plan" nb_images="5"/> | |
6 | 6 | </album> |
7 | 7 | </sequence> |
8 | 8 | </request> | ... | ... |
simulators/resources/routine_request_09.xml
1 | 1 | <?xml version="1.0" ?> |
2 | 2 | <request submitted="1" relative="1" name="RequestSimulator" scientific_program="GRB" target_type="test"> |
3 | - <sequence duration="200" jd1="1000" jd2="60000" name="Sequence9 (200secs)" target_coords="10"> | |
3 | + <sequence duration="20" jd1="1000" jd2="60000" name="Sequence9 (200secs)" target_coords="10"> | |
4 | 4 | <album detector="Visible camera" name="alb"> |
5 | - <plan duration="200" filter="First infrared filter" name="New plan" nb_images="5"/> | |
5 | + <plan duration="20" filter="First infrared filter" name="New plan" nb_images="5"/> | |
6 | 6 | </album> |
7 | 7 | </sequence> |
8 | 8 | </request> | ... | ... |
simulators/resources/routine_request_10.xml
1 | 1 | <?xml version="1.0" ?> |
2 | 2 | <request submitted="1" relative="1" name="RequestSimulator" scientific_program="GRB" target_type="test"> |
3 | - <sequence duration="200" jd1="200" jd2="60000" name="Sequence10 (200secs)" target_coords="10"> | |
3 | + <sequence duration="20" jd1="200" jd2="60000" name="Sequence10 (200secs)" target_coords="10"> | |
4 | 4 | <album detector="Visible camera" name="alb"> |
5 | - <plan duration="200" filter="First infrared filter" name="New plan" nb_images="5"/> | |
5 | + <plan duration="20" filter="First infrared filter" name="New plan" nb_images="5"/> | |
6 | 6 | </album> |
7 | 7 | </sequence> |
8 | 8 | </request> | ... | ... |
simulators/telescope/telescopeSimulator.py
... | ... | @@ -40,6 +40,9 @@ class TelescopeSimulator(Device, StatusManager): |
40 | 40 | else: |
41 | 41 | answer = "Invalid cmd for GET: " + cmd |
42 | 42 | self.telescopePrint(answer) |
43 | + elif (cmd_type == "DO"): | |
44 | + if (cmd == "GOTO"): | |
45 | + answer = "NEW POSITION SET TO " + str(args) | |
43 | 46 | else: |
44 | 47 | self.telescopePrint("Ignored message " + data) |
45 | 48 | ... | ... |
No preview for this file type
src/analyzer/analyses/analysis_1.txt renamed to src/analyzer/analyzer_source/calibrations/dark.fits
src/analyzer/calibrations/dark.fits renamed to src/analyzer/analyzer_source/calibrations/flat.fits
... | ... | @@ -0,0 +1,22 @@ |
1 | +#include <iostream> | |
2 | +#include <fstream> | |
3 | + | |
4 | +int main(int ac, char **av) | |
5 | +{ | |
6 | + if (ac >= 4) | |
7 | + { | |
8 | + std::string folder_source = av[1]; | |
9 | + std::string folder_plan = av[2]; | |
10 | + std::string duration = av[3]; | |
11 | + std::string file_dest; | |
12 | + std::ofstream myfile; | |
13 | + | |
14 | + file_dest = folder_source + "/" + folder_plan + "/analysis/analysis.result"; | |
15 | + myfile.open(file_dest.c_str()); | |
16 | + myfile << "Source folder = " + folder_source << std::endl; | |
17 | + myfile << "Plan folder = " + folder_plan << std::endl; | |
18 | + myfile << "duration = " + duration << std::endl; | |
19 | + return (0); | |
20 | + } | |
21 | + return (1); | |
22 | +} | ... | ... |
src/analyzer/calibrations/flat.fits deleted
src/analyzer/tasks.py
1 | 1 | from __future__ import absolute_import |
2 | - | |
3 | 2 | from celery.task import Task |
4 | 3 | from common.models import * |
5 | - | |
6 | -import time | |
4 | +from utils.Logger import setupLogger | |
5 | +from django.conf import settings | |
6 | +import subprocess | |
7 | 7 | import os |
8 | 8 | |
9 | -IMAGES_FOLDER = "misc/images" | |
9 | +''' | |
10 | + Gets the folder path and the plan id | |
11 | + it makes an analysis of the calibrated images (in folder "calibrated") | |
12 | +''' | |
13 | + | |
10 | 14 | |
11 | 15 | class analysis(Task): |
12 | - ''' | |
13 | - Gets the name of the image to analyze in parameter | |
14 | - It applies the calibrations, then makes the | |
15 | - ''' | |
16 | + logger = setupLogger("analysis", "analysis") | |
17 | + | |
18 | + def log(self, message: str) -> int: | |
19 | + if settings.DEBUG: | |
20 | + self.logger.info(message) | |
21 | + return 0 | |
16 | 22 | |
17 | - def run(self, plan_id): | |
18 | - # je dois jsute créer mes deux fonctions, une pour les corrections, et l'autre pour les analyses | |
23 | + def logDB(self, message: str) -> int: | |
24 | + Log.objects.create(agent='Analyzer', message=message) | |
25 | + return 0 | |
19 | 26 | |
20 | - plan = Plan.objects.get(id=plan_id) | |
21 | - image_name = plan.name | |
27 | + def execProcess(self, command: str) -> int: | |
28 | + self.process = subprocess.Popen(command, shell=True) | |
29 | + return 0 | |
22 | 30 | |
23 | - print("analysis : ", image_name) | |
31 | + def start(self, plan_id: int, folder: str) -> int: | |
32 | + self.plan_id = plan_id | |
33 | + self.folder = folder | |
34 | + self.path_dir_file = os.path.dirname(os.path.realpath(__file__)) | |
35 | + try: | |
36 | + self.plan = Plan.objects.get(id=self.plan_id) | |
37 | + except: | |
38 | + self.log("analysis not found") | |
39 | + self.logDB(message="Plan with id %d not found"%(self.plan_id)) | |
40 | + return 1 | |
41 | + self.dir_name = self.folder + os.sep + self.plan.name + "_" + str(self.plan_id) + os.sep + "analysis" | |
42 | + self.plan_folder = self.plan.name + "_" + str(self.plan_id) | |
43 | + if self.createDirectory(): | |
44 | + self.log("analysis err dir") | |
45 | + self.logDB(message="Could not create the directory for analysis") | |
46 | + return 1 | |
47 | + self.log("analysis start") | |
48 | + return self.logDB("Starting analysis for plan " + self.plan.name) | |
24 | 49 | |
25 | - new_img = self.apply_calibrations(image_name) | |
26 | - # TODO: Faire ça uniquement si c'est une image provenant d'une alerte | |
27 | - self.do_analysis(new_img) | |
50 | + def createDirectory(self): | |
51 | + try: | |
52 | + if not os.path.isdir(self.dir_name): | |
53 | + os.makedirs(self.dir_name) | |
54 | + except: | |
55 | + return 1 | |
56 | + return 0 | |
28 | 57 | |
29 | - # TODO: virer tout ça | |
30 | - time.sleep(1) | |
31 | - with open(os.path.join(IMAGES_FOLDER, str(image_name) + '_analyzed'), 'w'): | |
32 | - pass | |
33 | - message = 'Analyzed image in ' + str(image_name) | |
34 | - Log.objects.create(agent='Analyzer', message=message) | |
58 | + def changeDirectory(self, path: str): | |
59 | + os.chdir(path) | |
60 | + return 0 | |
61 | + | |
62 | + def execute(self) -> int: | |
63 | + self.changeDirectory(self.path_dir_file) | |
64 | + return self.execProcess("./analyzer "+str(self.folder)+" "+self.plan_folder+" "+str(self.plan.duration)) | |
65 | + | |
66 | + def end(self) -> int: | |
67 | + self.process.wait() | |
68 | + if self.process.returncode == 0: | |
69 | + self.log("analysis executed") | |
70 | + self.logDB(message="Analysis executed successfully for plan " + str(self.plan.name)) | |
71 | + else: | |
72 | + self.log("analysis failed") | |
73 | + self.logDB(message="Could not make an analysis for plan " + str(self.plan.name)) | |
74 | + return self.process.returncode | |
35 | 75 | |
36 | - def apply_calibrations(self, image_name): | |
37 | - ''' | |
38 | - Uses the calibration files to correct the image (changes the image bytes) | |
39 | - Send the new images to the FSC | |
40 | - ''' | |
41 | - # TODO: récupérer les bonne images dans le dossier analyzer/calibrations, et les appliquer | |
42 | - # renvoie le nom de l'image calibrée; penser à vérifier que les fichiers de calibration existent à cet endroit | |
43 | - # TODO: copier l'image dans le bon dossier pour le filesync (lien symbolique) | |
44 | - return "toto.fits" | |
45 | - | |
46 | - def do_analysis(self, new_img): | |
47 | - ''' | |
48 | - Use the scientific softwares to create analysis files (doesn't change the image bytes) | |
49 | - Sends the results to the FSC | |
50 | - ''' | |
51 | - # TODO: utiliser les programmes scientifiques pour générer les analyses | |
52 | - # TODO: copier les résultats des analyses dans le bon dossier pour le filesync (lien symbolique) | |
53 | - pass | |
76 | + def run(self, plan_id, folder): | |
77 | + if self.start(plan_id, folder): | |
78 | + return 1 | |
79 | + self.execute() | |
80 | + self.end() | |
81 | + return 0 | ... | ... |
src/common/models.py
... | ... | @@ -452,6 +452,11 @@ class ScheduleHasSequences(models.Model): |
452 | 452 | |
453 | 453 | |
454 | 454 | class SiteWatch(models.Model): |
455 | + OPEN = "OPEN" | |
456 | + CLOSE = "CLOSE" | |
457 | + ON = "ON" | |
458 | + OFF = "OFF" | |
459 | + | |
455 | 460 | updated = models.DateTimeField(blank=True, null=True, auto_now=True) |
456 | 461 | lights = models.CharField(max_length=45, blank=True, null=True) |
457 | 462 | dome = models.CharField(max_length=45, blank=True, null=True) |
... | ... | @@ -569,6 +574,9 @@ class Version(models.Model): |
569 | 574 | return (str(self.module_name) + " - " + str(self.version)) |
570 | 575 | |
571 | 576 | class WeatherWatch(models.Model): |
577 | + WIND_LIMIT = 100 | |
578 | + RAIN_LIMIT = 5 | |
579 | + | |
572 | 580 | updated = models.DateTimeField(blank=True, null=True) |
573 | 581 | humid_int = models.FloatField(blank=True, null=True) |
574 | 582 | humid_ext = models.FloatField(blank=True, null=True) | ... | ... |
src/devices/Device.py
... | ... | @@ -9,6 +9,8 @@ import utils.Logger as L |
9 | 9 | Generic object for the communication with all the devices (need inheritance) |
10 | 10 | ''' |
11 | 11 | |
12 | +DEBUG_FILE = False | |
13 | + | |
12 | 14 | |
13 | 15 | class DeviceController(): |
14 | 16 | __metaclass__ = abc.ABCMeta |
... | ... | @@ -124,7 +126,8 @@ class DeviceController(): |
124 | 126 | return (0) |
125 | 127 | |
126 | 128 | def log(self, device_name: str, message: str): |
127 | - self.logger.info("From device : " + device_name + " -> " + message) | |
129 | + if DEBUG_FILE and settings.DEBUG: | |
130 | + self.logger.info("From device : " + device_name + " -> " + message) | |
128 | 131 | return (0) |
129 | 132 | |
130 | 133 | def connect(self): | ... | ... |
src/majordome/tasks.py
... | ... | @@ -2,9 +2,8 @@ from __future__ import absolute_import |
2 | 2 | from common.models import * |
3 | 3 | from django.core.exceptions import ObjectDoesNotExist |
4 | 4 | import scheduler |
5 | -import scheduler.tasks | |
5 | +import scheduler.tasks as sched_task | |
6 | 6 | from celery.task import Task |
7 | -from celery import app | |
8 | 7 | import observation_manager |
9 | 8 | import observation_manager.tasks |
10 | 9 | from devices.Telescope import TelescopeController |
... | ... | @@ -12,8 +11,9 @@ from devices.CameraVIS import VISCameraController |
12 | 11 | from devices.CameraNIR import NIRCameraController |
13 | 12 | from devices.Dome import DomeController |
14 | 13 | from devices.PLC import PLCController |
15 | -from django.conf import settings | |
14 | +from django.db.models import Q | |
16 | 15 | from utils.JDManipulator import * |
16 | +from utils.StatusManager import * | |
17 | 17 | import utils.Logger as L |
18 | 18 | |
19 | 19 | DEBUG_FILE = False |
... | ... | @@ -39,7 +39,8 @@ class Majordome(Task): |
39 | 39 | timers = {} |
40 | 40 | functions = {} |
41 | 41 | schedule = None |
42 | - majordome_status = "STARTING" | |
42 | + available_status = [] | |
43 | + current_status = NORMAL | |
43 | 44 | |
44 | 45 | ''' |
45 | 46 | Check if the instrument status is valid |
... | ... | @@ -70,7 +71,6 @@ class Majordome(Task): |
70 | 71 | self.updateSoftware() |
71 | 72 | self.setContext() |
72 | 73 | self.setTime() |
73 | - self.majordome_status = "EXECUTING" | |
74 | 74 | self.loop() |
75 | 75 | |
76 | 76 | ''' |
... | ... | @@ -96,7 +96,6 @@ class Majordome(Task): |
96 | 96 | self.status_nir = self.nir_camera.get("STATUS") |
97 | 97 | self.status_tel = self.tel.get("STATUS") |
98 | 98 | self.status_dom = self.dom.get("STATUS") |
99 | - print("Devices ready !") | |
100 | 99 | return (0) |
101 | 100 | |
102 | 101 | ''' |
... | ... | @@ -152,7 +151,7 @@ class Majordome(Task): |
152 | 151 | Infinite loop according to the majordome behavior |
153 | 152 | ''' |
154 | 153 | def loop(self): |
155 | - while (self.majordome_status != "SHUTDOWN"): | |
154 | + while (self.current_status != "SHUTDOWN"): | |
156 | 155 | minimal_timer = min(self.timers, key=self.timers.get) |
157 | 156 | if (self.timers[minimal_timer] > 0): |
158 | 157 | time.sleep(self.timers[minimal_timer]) |
... | ... | @@ -219,7 +218,8 @@ class Majordome(Task): |
219 | 218 | self.next_sequence = None |
220 | 219 | self.schedule = schedule |
221 | 220 | if (self.schedule): |
222 | - shs_list = self.schedule.shs.filter(status=Sequence.PENDING).order_by('tsp') | |
221 | + shs_list = self.schedule.shs.filter(Q(status=Sequence.PLANNED) | | |
222 | + Q(status=Sequence.PENDING)).order_by('tsp') | |
223 | 223 | self.executeSchedule(shs_list) |
224 | 224 | else: |
225 | 225 | self.notifyTelescopeStatus("scheduler") |
... | ... | @@ -254,25 +254,21 @@ class Majordome(Task): |
254 | 254 | Function called when a schedule has to be executed |
255 | 255 | ''' |
256 | 256 | def executeSchedule(self, shs_list): |
257 | - is_prev_planned = False | |
258 | 257 | self.logDB("Trying to execute a sequence from current schedule") |
259 | 258 | for shs in shs_list: # shs_list is sorted by tsp |
260 | - if (shs.sequence.status == Sequence.PLANNED and self.observable(shs.sequence)): | |
261 | - countdown = self.getCountdown(shs) | |
262 | - # if (countdown <= 6): | |
263 | - # TODO CHECK IF OBSERVABLE ELSE RESCHEDULE | |
264 | - if countdown <= JulianSeconds(5): | |
265 | - if self.executing_sequence is None: | |
266 | - if self.switchSequence(): | |
267 | - self.executeSequence(shs, shs.sequence, countdown) | |
259 | + if (self.executableSequence(shs.sequence.status) and self.observable(shs.sequence)): | |
260 | + if self.next_sequence is None: | |
261 | + self.setNextSequence(shs, shs.sequence) | |
262 | + if self.executing_sequence is None: | |
263 | + if self.isValidTimer(self.next_sequence[0]): | |
264 | + if self.executeSequence(self.next_sequence[0], self.next_sequence[1]) == -1: | |
265 | + return -1 | |
266 | + if self.next_sequence[0] != shs and self.next_sequence[1] != shs.sequence: | |
267 | + self.setNextSequence(shs, shs.sequence) | |
268 | 268 | else: |
269 | - self.setNextSequence(shs, shs.sequence, countdown) | |
270 | - elif self.next_sequence is None: | |
271 | - self.setNextSequence(shs, shs.sequence, countdown) | |
269 | + self.next_sequence = None | |
272 | 270 | else: |
273 | 271 | return 0 |
274 | - elif self.next_sequence is None: | |
275 | - self.setNextSequence(shs, shs.sequence, countdown) | |
276 | 272 | else: |
277 | 273 | if (settings.DEBUG and DEBUG_FILE): |
278 | 274 | log.info("Sequence cannot be executed : Not observable") |
... | ... | @@ -284,41 +280,58 @@ class Majordome(Task): |
284 | 280 | return 0 |
285 | 281 | return 1 |
286 | 282 | |
283 | + def executableSequence(self, status): | |
284 | + if status == Sequence.PLANNED or status == Sequence.PENDING: | |
285 | + return 1 | |
286 | + return 0 | |
287 | + | |
288 | + def isValidTimer(self, shs): | |
289 | + current_countdown = self.getCountdown(shs) | |
290 | + if (current_countdown <= JulianSeconds(5)): | |
291 | + # Trick to be sure to not start the execution of a task if there is a schedule | |
292 | + try: | |
293 | + task = TaskId.objects.filter(task="scheduling") | |
294 | + if not task: | |
295 | + return 1 | |
296 | + return 0 | |
297 | + except: | |
298 | + return 1 | |
299 | + return 0 | |
300 | + | |
287 | 301 | ''' |
288 | 302 | Launch the observation tasks associated to a sequence |
289 | 303 | ''' |
290 | - def executeSequence(self, shs, sequence, countdown): | |
291 | - if (countdown > JulianSeconds(5)): | |
292 | - if self.next_sequence and self.next_sequence[1].status == Sequence.PENDING: | |
293 | - self.next_sequence[1].status = Sequence.PLANNED | |
294 | - self.next_sequence[1].save() | |
295 | - return 0 | |
296 | - if self.next_sequence and self.next_sequence[1].status == Sequence.PLANNED: | |
297 | - self.next_sequence[1].status = Sequence.PENDING | |
298 | - self.next_sequence[1].save() | |
299 | - | |
304 | + @acceptedStatus(["NORMAL", "MINOR_ERROR"]) | |
305 | + def executeSequence(self, shs, sequence): | |
306 | + log.info("Executing sequence id = " + str(sequence.pk)) | |
300 | 307 | self.logDB("Executing sequence") |
301 | 308 | plans_results = [] |
302 | 309 | if sequence.albums.filter(detector__name="Cagire").exists(): |
303 | 310 | if (self.isValidStatus(self.status_nir)): |
304 | 311 | for plan in sequence.albums.get(detector__name="Cagire").plans.all(): |
305 | - res = observation_manager.tasks.execute_plan_nir.apply_async((plan.id, float(countdown))) | |
306 | - TaskId.objects.create(task_id=res.id, task="execute_plan") | |
312 | + res = observation_manager.tasks.execute_plan_nir.apply_async( | |
313 | + (plan.id, float(self.getCountdown(shs)))) | |
314 | + # JB TODO : is it still usefull ? | |
315 | + # TaskId.objects.create(task_id=res.id, task="execute_plan") | |
307 | 316 | plans_results.append(res) |
308 | 317 | else: |
309 | 318 | self.notifyDeviceStatus("Cagire", "Sequence execution", self.status_nir) |
310 | - sequence.status = Sequence.DEVICE_ERROR | |
319 | + sequence.status = Sequence.CANCELLED | |
320 | + shs.status = Sequence.CANCELLED | |
321 | + shs.save() | |
311 | 322 | sequence.save() |
312 | 323 | return (1) |
313 | 324 | if sequence.albums.filter(detector__name="Visible camera").exists(): |
314 | 325 | if (self.isValidStatus(self.status_vis)): |
315 | 326 | for plan in sequence.albums.get(detector__name="Visible camera").plans.all(): |
316 | - res = observation_manager.tasks.execute_plan_vis.apply_async((plan.id, float(countdown))) | |
317 | - TaskId.objects.create(task_id=res.id, task="execute_plan") | |
327 | + res = observation_manager.tasks.execute_plan_vis.apply_async( | |
328 | + (plan.id, float(self.getCountdown(shs)))) | |
318 | 329 | plans_results.append(res) |
319 | 330 | else: |
320 | 331 | self.notifyDeviceStatus("Camera visible", "Sequence execution", self.status_vis) |
321 | - sequence.status = Sequence.DEVICE_ERROR | |
332 | + sequence.status = Sequence.CANCELLED | |
333 | + shs.status = Sequence.CANCELLED | |
334 | + shs.save() | |
322 | 335 | sequence.save() |
323 | 336 | return (1) |
324 | 337 | shs.status = Sequence.EXECUTING |
... | ... | @@ -326,60 +339,51 @@ class Majordome(Task): |
326 | 339 | shs.save() |
327 | 340 | sequence.save() |
328 | 341 | self.executing_sequence = [shs, sequence, plans_results] |
342 | + log.info("Sequence status should be executing = " + str(sequence.status)) | |
329 | 343 | return (0) |
330 | 344 | |
331 | 345 | ''' |
332 | 346 | Set the next sequence |
333 | 347 | ''' |
334 | - def setNextSequence(self, shs, sequence, countdown): | |
348 | + def setNextSequence(self, shs, sequence): | |
349 | + log.info("Setting next sequence id = " + str(sequence.pk)) | |
335 | 350 | sequence.status = Sequence.PENDING |
336 | - self.next_sequence = [shs, sequence, countdown] | |
351 | + shs.status = Sequence.PENDING | |
352 | + self.next_sequence = [shs, sequence] | |
337 | 353 | sequence.save() |
338 | - return (0) | |
339 | - | |
340 | - ''' | |
341 | - Switch sequences | |
342 | - ''' | |
343 | - def switchSequence(self): | |
344 | - self.executing_sequence = None | |
345 | - if (self.next_sequence is not None): | |
346 | - self.executeSequence(self.next_sequence[0], | |
347 | - self.next_sequence[1], self.next_sequence[2]) | |
348 | - self.next_sequence = None | |
349 | - else: | |
350 | - return 1 | |
354 | + shs.save() | |
351 | 355 | return (0) |
352 | 356 | |
353 | 357 | ''' |
354 | 358 | Check if the current sequence is finished |
355 | 359 | ''' |
356 | 360 | def handleSequence(self, shs, sequence, executing_plans): |
357 | - finished = False | |
358 | - error = False | |
361 | + count = 0 | |
359 | 362 | |
360 | - for plan in executing_plans: | |
363 | + for res in executing_plans: | |
361 | 364 | try: |
362 | - if plan.ready() == False: | |
363 | - finished = True | |
365 | + if res.successful() or res.failed(): | |
366 | + count += 1 | |
364 | 367 | except Exception as e: |
365 | - error = True | |
368 | + print(e) | |
366 | 369 | shs.status = Sequence.CANCELLED |
367 | 370 | sequence.status = Sequence.CANCELLED |
368 | 371 | shs.save() |
369 | 372 | sequence.save() |
370 | 373 | for rev in executing_plans: |
371 | - if (not rev.failed() and rev.ready() != True): | |
372 | - app.control.revoke(rev.id) | |
373 | - self.switchSequence() | |
374 | + if (not rev.failed() and not rev.successful()): | |
375 | + rev.revoke(terminate=True) | |
376 | + self.executing_sequence = None | |
374 | 377 | return (-1) |
375 | - if (finished): | |
378 | + if count >= len(executing_plans): | |
376 | 379 | sequence.status = Sequence.EXECUTED |
377 | 380 | shs.status = Sequence.EXECUTED |
378 | 381 | sequence.save() |
379 | 382 | shs.save() |
380 | 383 | message = "Finished sequence " + str(sequence.pk) + " execution" |
384 | + log.info(message) | |
381 | 385 | Log.objects.create(agent="Majordome", message=message) |
382 | - self.switchSequence() | |
386 | + self.executing_sequence = None | |
383 | 387 | return (0) |
384 | 388 | |
385 | 389 | ''' |
... | ... | @@ -387,6 +391,10 @@ class Majordome(Task): |
387 | 391 | ''' |
388 | 392 | def handleStatus(self): |
389 | 393 | # TODO switch majordome state according to devices status |
394 | + # if not self.isValidStatus(self.status_tel): | |
395 | + # self.current_status = "ERROR" | |
396 | + # if not self.isValidStatus(self.status_vis) and self.current_status == NORMAL: | |
397 | + # self.current_status = "" | |
390 | 398 | telescope = Telescope.objects.first() |
391 | 399 | camera_nir = Detector.objects.get(name="Cagire") |
392 | 400 | camera_vis = Detector.objects.get(name="Visible camera") |
... | ... | @@ -394,7 +402,7 @@ class Majordome(Task): |
394 | 402 | # dome = ???.objects.get(name="Dome") |
395 | 403 | # dome.status = self.status_dom |
396 | 404 | # dome.save() |
397 | - # TODO adapt the status (must be short) | |
405 | + # TODO adapt the status (must be a short string) | |
398 | 406 | telescope.status = self.status_tel |
399 | 407 | camera_nir.status = self.status_nir |
400 | 408 | camera_vis.status = self.status_vis |
... | ... | @@ -409,6 +417,7 @@ class Majordome(Task): |
409 | 417 | ''' |
410 | 418 | Put the system in Pause |
411 | 419 | ''' |
420 | + @acceptedStatus(["CRITICAL"]) | |
412 | 421 | def systemPause(self, duration, cause: str): |
413 | 422 | self.logDB("System in pause for " + str(duration)) |
414 | 423 | time.sleep(duration) |
... | ... | @@ -421,6 +430,16 @@ class Majordome(Task): |
421 | 430 | Function called to do an action with the site status and the wheather status |
422 | 431 | ''' |
423 | 432 | def handlePLC(self, site_status, weather_status): |
433 | + if site_status.doors == SiteWatch.OPEN: | |
434 | + self.current_status = SITE_CONDITION | |
435 | + self.logDB("Doors are open") | |
436 | + self.stopExecution() | |
437 | + if weather_status.wind > WeatherWatch.WIND_LIMIT: | |
438 | + self.cause = "Wind" | |
439 | + self.logDB("Wind too strong") | |
440 | + self.stopExecution() | |
441 | + if weather_status.rain > WeatherWatch.RAIN_LIMIT: | |
442 | + self.cause = "Rain" | |
424 | 443 | return (0) |
425 | 444 | |
426 | 445 | ''' | ... | ... |
No preview for this file type
... | ... | @@ -0,0 +1,22 @@ |
1 | +#include <iostream> | |
2 | +#include <fstream> | |
3 | + | |
4 | +int main(int ac, char **av) | |
5 | +{ | |
6 | + if (ac >= 4) | |
7 | + { | |
8 | + std::string folder_source = av[1]; | |
9 | + std::string folder_plan = av[2]; | |
10 | + std::string img_number = av[3]; | |
11 | + std::string file_dest; | |
12 | + std::ofstream myfile; | |
13 | + | |
14 | + file_dest = folder_source+"/"+folder_plan+"/calibrator/calibrator"+img_number+".result"; | |
15 | + myfile.open(file_dest.c_str()); | |
16 | + myfile << "Source folder = " + folder_source << std::endl; | |
17 | + myfile << "Source plan = " + folder_plan << std::endl; | |
18 | + myfile << "image number = " + img_number << std::endl; | |
19 | + return (0); | |
20 | + } | |
21 | + return (1); | |
22 | +} | ... | ... |
src/observation_manager/tasks.py
1 | 1 | from __future__ import absolute_import |
2 | 2 | from celery.task import Task |
3 | -from analyzer.tasks import analysis | |
3 | +from django.conf import settings | |
4 | 4 | from common.models import * |
5 | -from devices import CameraVIS as VIS | |
6 | -from devices import CameraNIR as NIR | |
7 | -from devices import Telescope | |
8 | -import time | |
9 | 5 | from utils.Logger import setupLogger |
6 | +from analyzer.tasks import analysis | |
7 | +from utils.JDManipulator import * | |
8 | +from devices.Telescope import TelescopeController | |
9 | +from devices.CameraNIR import NIRCameraController | |
10 | +from devices.CameraVIS import VISCameraController | |
11 | +import time | |
12 | +import subprocess | |
13 | +import os | |
10 | 14 | |
11 | 15 | ''' |
12 | 16 | Super class for execute_plan_vis / _nir |
13 | 17 | ''' |
14 | 18 | class execute_plan(Task): |
15 | - | |
16 | - def run(self, plan_id, countdown, type): | |
17 | - | |
18 | - if countdown > 0: | |
19 | - time.sleep(countdown) | |
20 | - TaskId.objects.filter(task_id=self.request.id).delete() | |
21 | - | |
22 | - self.type = type | |
23 | - plan = Plan.objects.get(id=plan_id) | |
24 | - | |
25 | - message = 'Start plan ' + plan.name + ' execution' | |
19 | + def setTelescope(self): | |
20 | + self.tel = TelescopeController() | |
21 | + return 0 | |
22 | + | |
23 | + def start(self) -> int: | |
24 | + self.setTelescope() | |
25 | + self.logger = setupLogger("Observation"+self.type, "Observation"+self.type) | |
26 | + self.image_count = 0 | |
27 | + # JB TODO : is it still usefull ? | |
28 | + # TaskId.objects.filter(task_id=self.request.id).delete() | |
29 | + if self.countdown > 0: | |
30 | + time.sleep(self.countdown) | |
31 | + try: | |
32 | + self.plan = Plan.objects.get(id=self.plan_id) | |
33 | + self.duration = julianSecondsToSeconds(self.plan.duration) | |
34 | + except: | |
35 | + self.logDB(message="Plan with id %d not found" % (self.plan_id)) | |
36 | + return 1 | |
37 | + self.plan_dir = self.plan.name + "_" + str(self.plan_id) | |
38 | + self.dir_name = settings.OUTPUT_FOLDER + os.sep + self.plan_dir | |
39 | + if self.createDirs(): | |
40 | + self.log("Could not create dirs") | |
41 | + self.logDB(message="Could not create directory " + self.dir_name + " for image storage") | |
42 | + return 1 | |
43 | + self.launchAnalysis() | |
44 | + self.log("Starting execution " + self.type) | |
45 | + return self.logDB('Start plan %s observation from camera %s'%(self.plan.name, str(self.type))) | |
46 | + | |
47 | + def createDirs(self): | |
48 | + try: | |
49 | + if not os.path.isdir(settings.OUTPUT_FOLDER): | |
50 | + os.makedirs(settings.OUTPUT_FOLDER) | |
51 | + if not os.path.isdir(self.dir_name): | |
52 | + os.makedirs(self.dir_name) | |
53 | + except: | |
54 | + return 1 | |
55 | + return 0 | |
56 | + | |
57 | + def log(self, message: str) -> int: | |
58 | + self.logger.info(self.type + ' -> '+ message) | |
59 | + return 0 | |
60 | + | |
61 | + def logDB(self, message: str) -> int: | |
26 | 62 | Log.objects.create(agent='Observation manager', message=message) |
27 | - print("execute_plan " + self.type + " : ", plan.name) | |
28 | - | |
29 | - self.tel = Telescope.TelescopeController() | |
30 | - if self.type == "VIS": | |
31 | - cam = VIS.VISCameraController() | |
32 | - else: | |
33 | - cam = NIR.NIRCameraController() | |
34 | - | |
35 | - self.set_camera(cam, plan) | |
36 | - self.wait_camera_ready(cam) | |
63 | + return 0 | |
37 | 64 | |
38 | - cam.do("START") | |
65 | + def launchAnalysis(self): | |
66 | + analysis.apply_async((self.plan_id, settings.OUTPUT_FOLDER)) | |
67 | + return 0 | |
39 | 68 | |
40 | - st = self.wait_camera_finished(cam) | |
69 | + def launchCalibration(self) -> int: | |
70 | + create_calibrations.apply_async((self.plan_id, settings.OUTPUT_FOLDER, self.image_count)) | |
71 | + return 0 | |
41 | 72 | |
42 | - # TODO: récupérer les vraies images ? je fais quoi ? | |
43 | - time.sleep(1) | |
44 | - # Penser qu'un plan peut créer plusieurs images (et analyser image par image) | |
45 | - analysis.delay(plan_id) | |
46 | - message = 'Finished plan ' + plan.name + ' execution' | |
47 | - Log.objects.create(agent='Observation manager', message=message) | |
48 | - | |
49 | - ''' | |
50 | - Set the camera configuration | |
51 | - ''' | |
52 | - def set_camera(self, cam, plan): | |
53 | - | |
54 | - # TODO: mettre les vraies configurations en fct du plan | |
55 | - cam.set("WINDOW", 0, 100, 10, 100) | |
56 | - cam.set("READMODE", "Ramp") | |
57 | - cam.set("FILENAME", plan.name) | |
58 | - cam.set("HEADER", {}) | |
59 | - cam.set("READOUT_FREQUENCY", 20.0) | |
60 | - cam.set("FILTER", "H") | |
61 | - | |
62 | - if self.type == "VIS": | |
63 | - cam.set("EXPOSURE", 180) | |
64 | - cam.set("BINNING", 300, 300) | |
65 | - else: | |
66 | - cam.set("NB_IMAGES", 28) | |
67 | - | |
68 | - ''' | |
69 | - Loop to wait for the configuration to be done | |
70 | - ''' | |
71 | - def wait_camera_ready(self, cam): | |
72 | - st = 0 | |
73 | - while st == 0: | |
74 | - st_tel = self.tel.get("STATUS") | |
75 | - | |
76 | - st_cam = cam.get("STATUS") | |
77 | - | |
78 | - st = 1 | |
79 | - | |
80 | - # TODO: checker les statuts comme il faut, et repasser à 0 si on a des statuts pas bons | |
81 | - if st_tel != "IDLE" or st_cam != "IDLE": | |
82 | - st = 0 | |
83 | - | |
84 | - ''' | |
85 | - Loop to wait for the observation to be finished | |
86 | - ''' | |
87 | - def wait_camera_finished(self, cam): | |
88 | - countdown = int(cam.get("TIMER")) | |
89 | - while countdown > 5: | |
90 | - time.sleep(5) | |
91 | - countdown = int(cam.get("TIMER")) | |
92 | - | |
93 | - st = 0 | |
94 | - while st == 0: | |
95 | - timer = int(cam.get("TIMER")) | |
96 | - if timer == -1: | |
97 | - st = 1 | |
98 | - else: | |
99 | - time.sleep(1) | |
73 | + def end(self) -> int: | |
74 | + self.log("Finished plan observation") | |
75 | + return self.logDB('Finished plan observation ' + self.plan.name + ' from camera ' + str(self.type)) | |
100 | 76 | |
77 | + def run(self, plan_id: int, countdown: float, type: str) -> int: | |
78 | + self.plan_id = plan_id | |
79 | + self.countdown = countdown | |
80 | + self.type = type | |
81 | + if self.start(): | |
82 | + self.log("fail exec task -> leaving") | |
83 | + return 1 | |
84 | + self.execute() | |
85 | + return self.end() | |
86 | + | |
87 | + def execute(self) -> int: | |
88 | + time = julianSecondsToSeconds(self.plan.duration) | |
89 | + self.log("Sleeping for " + str(int(self.duration))) | |
90 | + time.sleep(int(self.duration)) | |
91 | + return 0 | |
101 | 92 | |
102 | 93 | ''' |
103 | 94 | Gives the orders to the instruments to retrieve the image(s) of a plan VIS. |
104 | - Send the images to the analyzer | |
95 | + Send the images to the calibrator | |
105 | 96 | ''' |
106 | 97 | class execute_plan_vis(execute_plan): |
107 | - logger = setupLogger("PlanVIS", "PlanVIS") | |
98 | + def setCamera(self): | |
99 | + self.vis_camera = VISCameraController() | |
100 | + return 0 | |
108 | 101 | |
109 | - def run(self, plan_id, countdown): | |
110 | - self.log.info("------------------ RUNNING PLAN VIS ----------------------------") | |
102 | + def run(self, plan_id: int, countdown: float) -> int: | |
103 | + self.setCamera() | |
111 | 104 | super().run(plan_id, countdown, "VIS") |
105 | + return 0 | |
112 | 106 | |
107 | + def execute(self) -> int: | |
108 | + # TODO All the comunication protocol with the device | |
109 | + self.tel.do("GOTO " + str(self.plan.position)) | |
110 | + self.vis_camera.do("CAPTURE "+self.plan.name+" "+str(self.plan.nb_images)+" "+str(self.plan.duration)) | |
111 | + self.log("Sleeping for " + str(int(self.duration))) | |
112 | + time.sleep(int(self.duration)) | |
113 | + self.launchCalibration() | |
114 | + return 0 | |
113 | 115 | |
114 | 116 | ''' |
115 | 117 | Gives the orders to the instruments to retrieve the image(s) of a plan NIR. |
116 | - Send the images to the analyzer | |
118 | + Send the images to the calibrator | |
117 | 119 | ''' |
118 | 120 | class execute_plan_nir(execute_plan): |
119 | - log = setupLogger("PlanNIR", "PlanNIR") | |
121 | + def setCamera(self): | |
122 | + self.nir_camera = NIRCameraController() | |
123 | + return 0 | |
120 | 124 | |
121 | - def run(self, plan_id, countdown): | |
125 | + def run(self, plan_id: int, countdown: float) -> int: | |
126 | + self.setCamera() | |
122 | 127 | super().run(plan_id, countdown, "NIR") |
123 | - self.log.info("------------------ RUNNING PLAN NIR ----------------------------") | |
128 | + return 0 | |
129 | + | |
130 | + def execute(self) -> int: | |
131 | + # TODO All the comunication protocol with the device | |
132 | + self.tel.do("GOTO " + str(self.plan.position)) | |
133 | + self.nir_camera.do("CAPTURE "+self.plan.name+" "+str(self.plan.nb_images)+" "+str(self.plan.duration)) | |
134 | + self.log("Sleeping for " + str(int(self.duration))) | |
135 | + time.sleep(int(self.duration)) | |
136 | + self.launchCalibration() | |
137 | + return 0 | |
124 | 138 | |
125 | 139 | |
126 | 140 | ''' |
127 | - Directly make the right calls to the instruments to create the calibration files. | |
128 | - When they are all finished, it creates the 'super' calibration files. | |
141 | + Call a process with a folder and an image number as parameter who will create the | |
142 | + calibration for an image | |
129 | 143 | ''' |
130 | 144 | class create_calibrations(Task): |
131 | - logger = setupLogger("Calibrations", "Calibrations") | |
145 | + logger = setupLogger("calibrations", "calibrations") | |
146 | + | |
147 | + def log(self, message: str) -> int: | |
148 | + self.logger.info(message) | |
149 | + return 0 | |
132 | 150 | |
133 | - def run(self): | |
134 | - self.log.info("------------------ RUNNING CALIBRATIONS ----------------------------") | |
135 | - # TODO: attendre que tout soit idle | |
151 | + def logDB(self, message: str) -> int: | |
152 | + Log.objects.create(agent='Observation manager', message=message) | |
153 | + return 0 | |
154 | + | |
155 | + def execProcess(self, command: str) -> int: | |
156 | + self.process = subprocess.Popen(command, shell=True) | |
157 | + return 0 | |
158 | + | |
159 | + def start(self, plan_id: int, folder: str, image_number: int) -> int: | |
160 | + self.plan_id = plan_id | |
161 | + self.folder = folder | |
162 | + self.image_number = image_number | |
163 | + self.path_dir_file = os.path.dirname(os.path.realpath(__file__)) | |
164 | + try: | |
165 | + self.plan = Plan.objects.get(id=self.plan_id) | |
166 | + self.plan_folder = self.plan.name + "_" + str(self.plan_id) | |
167 | + except: | |
168 | + self.log("not found") | |
169 | + self.logDB(message="Plan with id %d not found"%(self.plan_id)) | |
170 | + return 1 | |
171 | + self.dir_name = self.folder + os.sep + self.plan.name + "_" + str(self.plan_id) + os.sep + "calibrations" | |
172 | + if self.createDirectory(): | |
173 | + self.log("err dir") | |
174 | + self.logDB(message="Could not create folder for calibrations") | |
175 | + return 1 | |
176 | + self.log("start") | |
177 | + return self.logDB("Starting calibration for image " + str(self.image_number)) | |
178 | + | |
179 | + def createDirectory(self): | |
180 | + try: | |
181 | + if not os.path.isdir(self.dir_name): | |
182 | + os.makedirs(self.dir_name) | |
183 | + except: | |
184 | + return 1 | |
185 | + return 0 | |
186 | + | |
187 | + def changeDirectory(self, path: str): | |
188 | + os.chdir(path) | |
189 | + return 0 | |
190 | + | |
191 | + def execute(self) -> int: | |
192 | + self.changeDirectory(self.path_dir_file) | |
193 | + return self.execProcess("./calibrator "+str(self.folder)+" "+self.plan_folder+" "+str(self.image_number)) | |
194 | + | |
195 | + def end(self) -> int: | |
196 | + self.process.wait() | |
197 | + if self.process.returncode == 0: | |
198 | + self.log("executed") | |
199 | + self.logDB(message="Calibration executed successfully for image " + str(self.image_number)) | |
200 | + else: | |
201 | + self.log("failed") | |
202 | + self.logDB(message="Could not calibrate image " + str(self.image_number)) | |
203 | + return self.process.returncode | |
204 | + | |
205 | + def run(self, plan_id: int, folder: str, image_number: int) -> int: | |
206 | + if self.start(plan_id, folder, image_number): | |
207 | + return 1 | |
208 | + self.execute() | |
209 | + return self.end() | ... | ... |
src/pyros/settings.py
... | ... | @@ -29,13 +29,15 @@ MODULES_VERSIONS = { |
29 | 29 | # Set MYSQL to False if you want to use SQLITE |
30 | 30 | # This line MUST NOT be changed at all except from changing True/False |
31 | 31 | # (or install_requirements script will become invalid) |
32 | -MYSQL = False | |
32 | +MYSQL = True | |
33 | 33 | |
34 | 34 | import os |
35 | 35 | |
36 | 36 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) |
37 | 37 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
38 | 38 | |
39 | +# Output folder for images | |
40 | +OUTPUT_FOLDER = os.path.join(BASE_DIR, "../images_folder") | |
39 | 41 | |
40 | 42 | # Quick-start development settings - unsuitable for production |
41 | 43 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ |
... | ... | @@ -114,13 +116,10 @@ FIXTURE_DIRS = ( |
114 | 116 | |
115 | 117 | LOGIN_URL = "/" |
116 | 118 | |
117 | -# Database | |
118 | -# https://docs.djangoproject.com/en/1.9/ref/settings/#databases | |
119 | - | |
120 | -# EP modif | |
121 | - | |
119 | +# FOR SIMULATOR -> do not touch this variable | |
122 | 120 | SIMULATOR = False |
123 | 121 | |
122 | +# FOR TESTS and SIMULATOR -> do not touch this variable | |
124 | 123 | CELERY_TEST = False |
125 | 124 | |
126 | 125 | if not CELERY_TEST: |
... | ... | @@ -260,11 +259,11 @@ CELERY_QUEUES = { |
260 | 259 | "monitoring_q": {"exchange": "monitoring_q", "routing_key": "monitoring_q"}, |
261 | 260 | "majordome_q": {"exchange": "majordome_q", "routing_key": "majordome_q"}, |
262 | 261 | "analysis_q": {"exchange": "analysis_q", "routing_key": "analysis_q"}, |
263 | - "system_status_q": {"exchange": "system_status_q", "routing_key": "system_status_q"}, | |
264 | 262 | "scheduling_q": {"exchange": "scheduling_q", "routing_key": "scheduling_q"}, |
265 | 263 | "create_calibrations_q": {"exchange": "create_calibrations_q", "routing_key": "create_calibrations_q"}, |
264 | + "execute_plan_vis_q": {"exchange": "execute_plan_vis_q", "routing_key": "execute_plan_vis_q"}, | |
265 | + "execute_plan_nir_q": {"exchange": "execute_plan_nir_q", "routing_key": "execute_plan_nir_q"}, | |
266 | 266 | } |
267 | - # "simulator_q": {"exchange": "simulator_q", "routing_key": "simulator_q"}, | |
268 | 267 | |
269 | 268 | CELERY_ROUTES = { |
270 | 269 | "alert_manager.tasks.AlertListener": {"queue": "alert_listener_q"}, | ... | ... |
src/pyros/settings.py.bak
... | ... | @@ -29,13 +29,15 @@ MODULES_VERSIONS = { |
29 | 29 | # Set MYSQL to False if you want to use SQLITE |
30 | 30 | # This line MUST NOT be changed at all except from changing True/False |
31 | 31 | # (or install_requirements script will become invalid) |
32 | -MYSQL = False | |
32 | +MYSQL = True | |
33 | 33 | |
34 | 34 | import os |
35 | 35 | |
36 | 36 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) |
37 | 37 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
38 | 38 | |
39 | +# Output folder for images | |
40 | +OUTPUT_FOLDER = os.path.join(BASE_DIR, "../images_folder") | |
39 | 41 | |
40 | 42 | # Quick-start development settings - unsuitable for production |
41 | 43 | # See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ |
... | ... | @@ -114,13 +116,10 @@ FIXTURE_DIRS = ( |
114 | 116 | |
115 | 117 | LOGIN_URL = "/" |
116 | 118 | |
117 | -# Database | |
118 | -# https://docs.djangoproject.com/en/1.9/ref/settings/#databases | |
119 | - | |
120 | -# EP modif | |
121 | - | |
119 | +# FOR SIMULATOR -> do not touch this variable | |
122 | 120 | SIMULATOR = True |
123 | 121 | |
122 | +# FOR TESTS and SIMULATOR -> do not touch this variable | |
124 | 123 | CELERY_TEST = False |
125 | 124 | |
126 | 125 | if not CELERY_TEST: |
... | ... | @@ -260,11 +259,11 @@ CELERY_QUEUES = { |
260 | 259 | "monitoring_q": {"exchange": "monitoring_q", "routing_key": "monitoring_q"}, |
261 | 260 | "majordome_q": {"exchange": "majordome_q", "routing_key": "majordome_q"}, |
262 | 261 | "analysis_q": {"exchange": "analysis_q", "routing_key": "analysis_q"}, |
263 | - "system_status_q": {"exchange": "system_status_q", "routing_key": "system_status_q"}, | |
264 | 262 | "scheduling_q": {"exchange": "scheduling_q", "routing_key": "scheduling_q"}, |
265 | 263 | "create_calibrations_q": {"exchange": "create_calibrations_q", "routing_key": "create_calibrations_q"}, |
264 | + "execute_plan_vis_q": {"exchange": "execute_plan_vis_q", "routing_key": "execute_plan_vis_q"}, | |
265 | + "execute_plan_nir_q": {"exchange": "execute_plan_nir_q", "routing_key": "execute_plan_nir_q"}, | |
266 | 266 | } |
267 | - # "simulator_q": {"exchange": "simulator_q", "routing_key": "simulator_q"}, | |
268 | 267 | |
269 | 268 | CELERY_ROUTES = { |
270 | 269 | "alert_manager.tasks.AlertListener": {"queue": "alert_listener_q"}, | ... | ... |
src/scheduler/Scheduler.py
... | ... | @@ -61,7 +61,7 @@ class Scheduler(IntervalManagement): |
61 | 61 | executing = Sequence.objects.filter(status=Sequence.EXECUTING) |
62 | 62 | if executing: |
63 | 63 | s = Sequence.shs.latest("schedule__created") |
64 | - adder = s.tsp - secondsToPreciseJulianDate(getPreciseCurrentTime()) | |
64 | + adder = s.tep - secondsToPreciseJulianDate(getPreciseCurrentTime()) | |
65 | 65 | except: |
66 | 66 | pass |
67 | 67 | self.schedule.plan_night_start = previous_sched.plan_night_start |
... | ... | @@ -183,7 +183,7 @@ class Scheduler(IntervalManagement): |
183 | 183 | else: |
184 | 184 | sequence_placed = self.tryShiftingSequences(sequence, shs) |
185 | 185 | if sequence_placed: |
186 | - shs.status = Sequence.PENDING | |
186 | + shs.status = Sequence.PLANNED | |
187 | 187 | self.decreaseQuota(sequence, sequence.duration) |
188 | 188 | else: |
189 | 189 | if DEBUG_FILE: |
... | ... | @@ -194,14 +194,14 @@ class Scheduler(IntervalManagement): |
194 | 194 | |
195 | 195 | def findSequenceBefore(self, interval: Interval): |
196 | 196 | for seq, s in self.sequences: |
197 | - if s.status == Sequence.PENDING: | |
197 | + if s.status == Sequence.PLANNED: | |
198 | 198 | if is_nearby_equal(s.tep, interval.start): |
199 | 199 | return (seq, s) |
200 | 200 | return (None, None) |
201 | 201 | |
202 | 202 | def findSequenceAfter(self, interval: Interval): |
203 | 203 | for seq, s in self.sequences: |
204 | - if s.status == Sequence.PENDING: | |
204 | + if s.status == Sequence.PLANNED: | |
205 | 205 | if is_nearby_equal(s.tsp - self.max_overhead, interval.end): |
206 | 206 | return (seq, s) |
207 | 207 | return (None, None) |
... | ... | @@ -314,7 +314,7 @@ class Scheduler(IntervalManagement): |
314 | 314 | return 0 |
315 | 315 | |
316 | 316 | def logSchedule(self) -> int: |
317 | - sequences = Sequence.objects.filter(shs__status=Sequence.PENDING).order_by('shs__tsp').distinct() | |
317 | + sequences = Sequence.objects.filter(shs__status=Sequence.PLANNED).order_by('shs__tsp').distinct() | |
318 | 318 | self.log("There are %d sequence(s) planned" % len(sequences)) |
319 | 319 | for sequence in sequences: |
320 | 320 | s = sequence.shs.latest("schedule__created") | ... | ... |
src/scheduler/tasks.py
... | ... | @@ -4,14 +4,17 @@ from scheduler.Scheduler import Scheduler |
4 | 4 | from common.models import * |
5 | 5 | from utils.JDManipulator import * |
6 | 6 | from utils.Logger import setupLogger |
7 | + | |
7 | 8 | log = setupLogger("TaskSched", "TaskSched") |
8 | 9 | |
9 | 10 | class scheduling(Task): |
10 | 11 | |
11 | 12 | def run(self, first_schedule=False, alert=False): |
12 | 13 | |
14 | + task = TaskId.objects.create(task_id=self.request.id, task="scheduling") | |
13 | 15 | Log.objects.create(agent='Scheduler', message='Start schedule : ' + str(datetime.datetime.now())) |
14 | 16 | self.scheduler = Scheduler() |
15 | 17 | self.scheduler.setNightLimits(secondsToJulianDate(getNightStart()), secondsToJulianDate(getNightEnd())) |
16 | 18 | self.scheduler.makeSchedule() |
17 | - Log.objects.create(agent='Scheduler', message='Scheduling finished : ' + str(datetime.datetime.now())) | |
18 | 19 | \ No newline at end of file |
20 | + Log.objects.create(agent='Scheduler', message='Scheduling finished : ' + str(datetime.datetime.now())) | |
21 | + task.delete() | |
19 | 22 | \ No newline at end of file | ... | ... |
src/utils/JDManipulator.py
... | ... | @@ -39,6 +39,9 @@ def secondsToJulianDate(time_seconds): |
39 | 39 | def secondsToPreciseJulianDate(time_seconds): |
40 | 40 | return (Decimal(time_seconds) / 86400 + Decimal(TIMESTAMP_JD)) |
41 | 41 | |
42 | +def julianSecondsToSeconds(seconds_julian): | |
43 | + return seconds_julian * 86400 | |
44 | + | |
42 | 45 | def julianDateToSeconds(time_julian): |
43 | 46 | return ((time_julian - TIMESTAMP_JD) * 86400) |
44 | 47 | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +SITE_CONDITION = "SITE_CONDITION" | |
2 | +NORMAL = "NORMAL" | |
3 | +ERROR = "ERROR" | |
4 | +WEATHER_CONDITION = "SEVERE_WEATHER" | |
5 | +MINOR = "MINOR_ERROR" | |
6 | +CRITICAL = "CRITICAL" | |
7 | + | |
8 | +def acceptedStatus(status): | |
9 | + def deco(func): | |
10 | + def tag_decorator(self, *args, **kwargs): | |
11 | + for i in status: | |
12 | + if self.current_status == i: | |
13 | + return func(self, *args, **kwargs) | |
14 | + return -1 | |
15 | + return tag_decorator | |
16 | + return deco | ... | ... |