From bd83eb17322571a0b26d3be2db5b47602e530881 Mon Sep 17 00:00:00 2001 From: Quentin Durand Date: Fri, 20 Jul 2018 16:42:28 +0200 Subject: [PATCH] Control command Cagire web + back --- simulators/config/grammar.json | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dashboard/templates/dashboard/observation_status.html | 5 +++++ src/dashboard/templates/dashboard/send_command_cameraNIR.html | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/dashboard/urls.py | 3 +++ src/dashboard/views.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ src/devices/CameraNIR.py | 8 ++++++++ src/devices/CameraNIRRemoteControlDefault.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/devices/CameraVISRemoteControlDefault.py | 8 +++++++- src/devices/Device.py | 2 +- src/misc/static/js/command_control_cameraNIR.js | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 668 insertions(+), 2 deletions(-) create mode 100644 src/dashboard/templates/dashboard/send_command_cameraNIR.html create mode 100644 src/devices/CameraNIRRemoteControlDefault.py create mode 100644 src/misc/static/js/command_control_cameraNIR.js diff --git a/simulators/config/grammar.json b/simulators/config/grammar.json index d2ec2ee..b855bdd 100644 --- a/simulators/config/grammar.json +++ b/simulators/config/grammar.json @@ -245,6 +245,197 @@ "label": "Change the wanted filter", "input_label": [], "help": "Change the wanted filter" + }, + { + "name": "EXPOSURE", + "label": "Exposure time for 1 image in seconds", + "input_label": [ + { + "exposure" : [15, "Int", "Exposure time"] + } + ], + "help": "Exposure time for 1 image in seconds" + }, + { + "name": "BINNING", + "label": "Binning of cells", + "input_label": [ + { + "binning" : [15, "Int", "Binning"] + } + ], + "help": "Set the Binning of cells." + } + ], + "get": [ + { + "name": "STATUS", + "label": "Get camera status values (dict)", + "input_label": [], + "help": "Get camera status values (dict): struct(key, value, type, unit, comment)" + }, + { + "name": "SETUP", + "label": "Get static values for config (dict)", + "input_label": [], + "help": "Get static values for config (eg. limit speed)" + }, + { + "name": "TIMER", + "label": "Get the ime before end of pose", + "input_label": [], + "help": "Time before end of pose (-1 == idle / finished, 0 == readout (recup-ing img))" + } + + ], + "do": [ + { + "name": "COOLER ON", + "label": "Turn On the cooler with the given temperature", + "input_label": [ + { + "temp":[15, "Float", "temperature"] + } + ], + "help": "Turn On the cooler with the given temperature." + }, + { + "name": "COOLER OFF", + "label": "Turn off the cooler", + "input_label": [], + "help": "Turn off the cooler." + }, + { + "name": "DOME SHUTTER OPEN", + "label": "Open the dome shutter", + "input_label": [], + "help": "Open the dome shutter." + }, + { + "name": "DOME SHUTTER CLOSE", + "label": "Close the dome shutter", + "input_label": [], + "help": "Close the dome shutter" + }, + { + "name": "DOME SHUTTER SYNCHRO", + "label": "Dome Shutter Synchro", + "input_label": [], + "help": "Dome Shutter Synchro" + }, + { + "name": "START", + "label": "Start Pose", + "input_label": [], + "help": "Start Pose" + }, + { + "name": "ABORT", + "label": "Stop Pose", + "input_label": [], + "help": "Stop Pose" + }, + { + "name": "STOP", + "label": "End image then stop pose", + "input_label": [], + "help": "End image then stop pose" + } + + ] + + + }, + "CameraNIR" : { + "set": [ + { + "name": "WINDOW", + "label": "Set the window coordinates", + "input_label": [ + { + "x1":[10, "Int", "x left bottom"], + "x2":[10, "Int", "y left bottom"], + "y1":[10, "Int", "x right top corner"], + "y2":[10, "Int", "y right top corner"] + } + ], + "help": "Coordinates of window corners (left bot, right top)" + }, + { + "name": "READMODE [MODE 1]", + "label": "Set Mode of observation (ramp, ...)", + "input_label": [], + "help": "Set the Mode of observation (ramp, ...)" + }, + { + "name": "READMODE [MODE 2]", + "label": "Set Mode of observation (ramp, ...)", + "input_label": [], + "help": "Set the Mode of observation (ramp, ...)" + }, + { + "name": "READMODE [MODE 3]", + "label": "Set Mode of observation (ramp, ...)", + "input_label": [], + "help": "Set the Mode of observation (ramp, ...)" + }, + { + "name": "FILENAME", + "label": "Change the used file", + "input_label": [ + { + "filename" : [256, "Str", "filename"] + } + ], + "help": "Optional, not wanting default file name (reset between each image)" + }, + { + "name": "HEADER", + "label": "Set particular values in the header", + "input_label": [ + { + "header values" : [1024, "Str (dict)", "header values"] + } + ], + "help": "Set particular values in the header" + }, + { + "name": "READOUT_FREQUENCY", + "label": "Readout frequency in pix/s (~= Hz)", + "input_label": [ + { + "readout freq" : [15, "Float", "readout freq pix/s ~= Hz"] + } + ], + "help": "Readout frequency in pix/s (~= Hz)" + }, + { + "name": "FILTER [FILTER 1]", + "label": "Change the wanted filter", + "input_label": [], + "help": "Change the wanted filter" + }, + { + "name": "FILTER [FILTER 2]", + "label": "Change the wanted filter", + "input_label": [], + "help": "Change the wanted filter" + }, + { + "name": "FILTER [FILTER 3]", + "label": "Change the wanted filter", + "input_label": [], + "help": "Change the wanted filter" + }, + { + "name": "NB_IMAGES", + "label": "Number of images in the ramp (x1.4s)", + "input_label": [ + { + "nb_imgs" : [15, "Int", "nb images"] + } + ], + "help": "Set the number of images in the ramp (x1.4s)" } ], "get": [ diff --git a/src/dashboard/templates/dashboard/observation_status.html b/src/dashboard/templates/dashboard/observation_status.html index df647aa..bfa6615 100644 --- a/src/dashboard/templates/dashboard/observation_status.html +++ b/src/dashboard/templates/dashboard/observation_status.html @@ -125,6 +125,11 @@ {% for log in cagire_logs %}
  • {{log.created}} : {{log.message}}
  • {% endfor %} + {% if not config.global_mode %} +
    + +
    + {% endif %}

    diff --git a/src/dashboard/templates/dashboard/send_command_cameraNIR.html b/src/dashboard/templates/dashboard/send_command_cameraNIR.html new file mode 100644 index 0000000..abd6045 --- /dev/null +++ b/src/dashboard/templates/dashboard/send_command_cameraNIR.html @@ -0,0 +1,124 @@ +{% extends "base.html" %} +{% load staticfiles%} + +{% block title %} + Send Command to CAGIRE +{% endblock %} + +{% block content %} + + + + + + + + + + +
    +
    +
    + +
    + + + +
    +

    +
    +
    + + {% csrf_token %} +
    +
    +
    +
    +

    CAGIRE

    +
      +
      +
    +
    +
    +
    + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/src/dashboard/urls.py b/src/dashboard/urls.py index 65e5c58..88ee9bb 100644 --- a/src/dashboard/urls.py +++ b/src/dashboard/urls.py @@ -24,6 +24,9 @@ urlpatterns = [ path('observation_status/send_command_to_telescope/submit_expert', views.submit_command_to_telescope_expert, name='submit_command_to_telescope_expert'), path('observation_status/send_command_to_telescope/submit', views.submit_command_to_telescope, name='submit_command_to_telescope'), path('observation_status/send_command_to_telescope', views.send_command_to_telescope, name='send_command_to_telescope'), + path('observation_status/send_command_to_cameraNIR/submit_expert', views.submit_command_to_cameraNIR_expert, name='submit_command_to_cameraNIR_expert'), + path('observation_status/send_command_to_cameraNIR/submit', views.submit_command_to_cameraNIR, name='submit_command_to_cameraNIR'), + path('observation_status/send_command_to_cameraNIR', views.send_command_to_cameraNIR, name='send_command_to_cameraNIR'), path('observation_status/send_command_to_cameraVIS_1/submit_expert', views.submit_command_to_cameraVIS_1_expert, name='submit_command_to_cameraVIS_1_expert'), path('observation_status/send_command_to_cameraVIS_1/submit', views.submit_command_to_cameraVIS_1, name='submit_command_to_cameraVIS_1'), path('observation_status/send_command_to_cameraVIS_1', views.send_command_to_cameraVIS_1, name='send_command_to_cameraVIS_1'), diff --git a/src/dashboard/views.py b/src/dashboard/views.py index 7dadc09..1112411 100644 --- a/src/dashboard/views.py +++ b/src/dashboard/views.py @@ -19,6 +19,7 @@ from random import randint from devices.Telescope import TelescopeController from devices.TelescopeRemoteControlDefault import TelescopeRemoteControlDefault from devices.CameraVISRemoteControlDefault import CameraVISRemoteControlDefault +from devices.CameraNIRRemoteControlDefault import CameraNIRRemoteControlDefault from django.core.mail import send_mail import time import utils.celme as celme @@ -361,6 +362,53 @@ def submit_command_to_cameraVIS_1_expert(request): +@login_required +@level_required(3) +def send_command_to_cameraNIR(request): + data = "" + with open('../simulators/config/grammar.json') as f: + data = json.load(f, object_pairs_hook=OrderedDict) + json_str = json.dumps(data) + return render(request, "dashboard/send_command_cameraNIR.html", locals()) + + +@login_required +@level_required(3) +def submit_command_to_cameraNIR(request): + if request.method == 'POST': + commands = [request.POST.get("first_command"), request.POST.get("first_param")] + try: #TODO faire un truc plus joli pour gérer les params queqlue soit leur nombre + input_0 = request.POST.get("input_number_0") + input_1 = request.POST.get("input_number_1") + input_2 = request.POST.get("input_number_2") + if input_0: + commands.append(input_0) + if input_1: + commands.append(input_1) + if input_2: + commands.append(input_2) + except Exception: + pass + response = CameraNIRRemoteControlDefault(commands, expert_mode=False).exec_command() + #TODO passer en JS pour send les réponses en AJAX + return redirect('send_command_to_cameraNIR') + +@login_required +@level_required(3) +def submit_command_to_cameraNIR_expert(request): + #import os + if request.method == 'POST': + param = request.POST.get("commande_expert") + if param: + response = CameraNIRRemoteControlDefault(param, expert_mode=True).exec_command() + #os.system("echo \"status :" + response + "\" >> /home/portos/IRAP/pyros/src/status") + return HttpResponse(json.dumps({'message': "Command send OK", 'response': response})) + return HttpResponse(json.dumps({'message': "Missing command data"})) + return redirect('submit_command_to_cameraNIR') + + + + diff --git a/src/devices/CameraNIR.py b/src/devices/CameraNIR.py index 937ae93..4ba17e3 100644 --- a/src/devices/CameraNIR.py +++ b/src/devices/CameraNIR.py @@ -5,7 +5,11 @@ from .Device import DeviceController ''' Device controller for NIRCamera. This class must implement set, get and do functions (DeviceController is an abstract) + ''' + +#TODO: refactoriser cette classe, la seule méthode utilisée est sendMessage, les autres sont obsolètes + class NIRCameraController(DeviceController): def __init__(self): @@ -34,3 +38,7 @@ class NIRCameraController(DeviceController): def open_shutter(self): self.do("OPEN SHUTTER") return 0 + + def send_command(self, command): + self.sendMessage(command) + return self.blockAndReadMessage() diff --git a/src/devices/CameraNIRRemoteControlDefault.py b/src/devices/CameraNIRRemoteControlDefault.py new file mode 100644 index 0000000..5c0533a --- /dev/null +++ b/src/devices/CameraNIRRemoteControlDefault.py @@ -0,0 +1,78 @@ +from devices.CameraRemoteControlAbstract import CameraRemoteControlAbstract +from devices import CameraNIR + +''' + Class binding method for real grammar corresponding to the generic command received in the command_matchers +''' + +class CameraNIRRemoteControlDefault(CameraRemoteControlAbstract): + def __init__(self, command, expert_mode): + super().__init__(command, expert_mode) + self._camera = CameraNIR.NIRCameraController() + + self._command_matcher_set = { + "WINDOW": self.set, + "READMODE [MODE 1]": self.set, + "READMODE [MODE 2]": self.set, + "READMODE [MODE 3]": self.set, + "FILENAME": self.set, + "HEADER": self.set, + "READOUT_FREQUENCY": self.set, + "FILTER [FILTER 1]": self.set, + "FILTER [FILTER 2]": self.set, + "FILTER [FILTER 3]": self.set, + "NB_IMAGES": self.set, + } + self._command_matcher_get = { + "STATUS": self.get, + "SETUP": self.get, + "TIMER": self.get + } + self._command_matcher_do = { + "COOLER ON": self.do, + "COOLER OFF": self.do, + "DOME SHUTTER OPEN": self.do, + "DOME SHUTTER CLOSE": self.do, + "DOME SHUTTER SYNCHRO": self.do, + "START": self.do, + "ABORT": self.do, + "STOP": self.do + } + + + + + def exec_command(self): + + #os.system("echo \"status :" + str(self._command) + "\" >> /home/portos/IRAP/pyros/src/commande_recu") + if self._command[0] == "GET": + self._current_matcher = self._command_matcher_get + elif self._command[0] == "SET": + self._current_matcher = self._command_matcher_set + elif self._command[0] == "DO": + self._current_matcher = self._command_matcher_do + else: return "KO: Unknown command" + if self._command[1] in self._current_matcher: + return self._current_matcher[self._command[1]]() + else: + return "KO: Unknown command" + + + ''' + Locals methods for the generic grammar + ''' + + def do(self): + definitive_command = "DO " + self._command[1] + ' ' + ' '.join(self._command[2:]) + response = self._camera.send_command(definitive_command) + return response + + def set(self): + definitive_command = "SET " + self._command[1] + ' ' + ' '.join(self._command[2:]) + response = self._camera.send_command(definitive_command) + return response + + def get(self): + definitive_command = "GET " + self._command[1] #+ ' ' + ' '.join(self._command[2:]) + response = self._camera.send_command(definitive_command) + return response \ No newline at end of file diff --git a/src/devices/CameraVISRemoteControlDefault.py b/src/devices/CameraVISRemoteControlDefault.py index 3e6b8a0..573d10c 100644 --- a/src/devices/CameraVISRemoteControlDefault.py +++ b/src/devices/CameraVISRemoteControlDefault.py @@ -1,6 +1,10 @@ from devices.CameraRemoteControlAbstract import CameraRemoteControlAbstract from devices import CameraVIS +''' + Class binding method for real grammar corresponding to the generic command received in the command_matchers +''' + class CameraVISRemoteControlDefault(CameraRemoteControlAbstract): def __init__(self, command, expert_mode, chosen_camera): super().__init__(command, expert_mode) @@ -19,7 +23,9 @@ class CameraVISRemoteControlDefault(CameraRemoteControlAbstract): "READOUT_FREQUENCY": self.set, "FILTER [FILTER 1]": self.set, "FILTER [FILTER 2]": self.set, - "FILTER [FILTER 3]": self.set + "FILTER [FILTER 3]": self.set, + "BINNING": self.set, + "EXPOSURE": self.set } self._command_matcher_get = { "STATUS": self.get, diff --git a/src/devices/Device.py b/src/devices/Device.py index aecd3f7..3eef159 100644 --- a/src/devices/Device.py +++ b/src/devices/Device.py @@ -104,7 +104,7 @@ class DeviceController(): readable, writable, exceptional = select.select([self.sock], [], [self.sock], 0) if not (readable or exceptional): #ret = self.sock.recv(size).decode() - raise (Exception("KO: socket error")) + raise (Exception("KO: socket error or not implemented command")) ret = self.sock.recv(size).decode() if (not ret): diff --git a/src/misc/static/js/command_control_cameraNIR.js b/src/misc/static/js/command_control_cameraNIR.js new file mode 100644 index 0000000..918c925 --- /dev/null +++ b/src/misc/static/js/command_control_cameraNIR.js @@ -0,0 +1,203 @@ +var LOGS_REFRESH_FREQUENCE = 2000; //in milliseconds +var expert_mode = false; + +jQuery(document).ready(function(){ + + var current_command = data; + var help = null; + + /** + These functions are event watchers on the img_display_help + **/ + + $(document).on("mouseenter", "#img_display_help", function() { + $('#img_display_help').attr('width','40px'); + $('#img_display_help').attr('height','40px'); + }); + + $(document).on("mouseleave", "#img_display_help", function() { + $('#img_display_help').attr('width','30px'); + $('#img_display_help').attr('height','30px'); +}); + + +$(document).on('click','#img_display_help', function() { + alert(help); +}); + + +/** + This function detete the childs of command_form after the child n° start_position +**/ + +function delete_childs_after(start_position) +{ + var form = $("#command_form"); + var length = form.children().length; + + for (var i = start_position; i < length; i++) { + form.children().last().remove(); + } +} + +/** + This function create the select item with the commands in grammar.json +**/ +function init_first_param(corresponding_data) +{ + var sel = $('"); + form.append(input_element); + } + + var img_display_help = document.createElement('img'); + img_display_help.src = "/public/static/media/question-mark.png"; + img_display_help.width = 30; + img_display_help.height = 30; + img_display_help.title = "Click to display help for this command"; + img_display_help.id ="img_display_help"; + img_display_help.alt="html5"; + + form.append(img_display_help); + $("#img_display_help").css("margin-left", "10px"); + form.append("

    Command description: " + label + "

    "); + //$("#command_label").css("text-align", "center"); + $("#command_label").css({ + 'font-size' : '15px', + 'text-align' : 'center', + 'font-weight' : 'bold' + }); + form.append("
    ") + $('', {id:"submit_button", type:"submit", value:"SEND"}).appendTo(form); + + }); + + + /** + This event watcher is triggered when the first select element is changed, it set the json current_command to SET, GET or DO, it + calls init_first_param + **/ + $("#id_first_command").change(function() { + delete_childs_after(3); + var selected_command = $("#id_first_command").find(":selected").val(); + if (selected_command !== "Command") + { + current_command = data["CameraNIR"][selected_command.toLowerCase()]; + init_first_param(data["CameraNIR"][selected_command.toLowerCase()]); + } + }); + + + /** + This event watcher is triggered when the expert mode is activated, it generates the corresponding input field and submit_button + **/ + +$("#expert_mode").click(function(){ + if (!expert_mode) + { + expert_mode = true; + $("#command_form_expert").append(""); + $('', {id:"submit_button_expert", type:"submit", value:"SEND"}).appendTo('#command_form_expert'); + } + else { + $("#input_command_expert").remove(); + $("#submit_button_expert").remove(); + expert_mode = false; + } + //$("#command_form").remove(); +}); + +/** + This event watcher is triggered when the expert form is submitted, it's and AJAX POST request, the response is displayed in the console +**/ + +$("#command_form_expert").submit(function(event){ + + event.preventDefault(); + $.post('observation_status/send_command_to_cameraNIR/submit_expert', $(this).serialize(), function(data){ + var obj = JSON.parse(data); + console.log(obj.message); + console.log(obj.response); + if (obj.response === "KO: Unknown command") + alert("Unknown command"); + else if (obj.response.startsWith("KO")) + alert(obj.response); + }) + .fail(function() { + alert( "An error occured" ); + }) + }); + + +/** + This AJAX refreshed request is a get retrieving the Logs of the telescope +**/ + + interval_camera = setInterval(function() {ajax_request();}, LOGS_REFRESH_FREQUENCE); + + function ajax_request(){ + var camera_url ="/devices/update_cagire_logs"; + $.ajax({ + url: camera_url, + type: 'get', + dataType: 'text', + success: function(data) + { + var obj = JSON.parse(data); + if (obj) { + var listDiv = document.getElementById('cagire_logs'); + listDiv.innerHTML = ''; + for (var tmp in obj) { + var li = document.createElement('li'); + li.innerHTML = obj[tmp]['fields']['created'] + ": " + obj[tmp]['fields']['message']; // Use innerHTML to set the text + listDiv.appendChild(li); + } + } + }, + error: function() + { + console.log('Ajax error: GET request failed\n'); + } + + }); + } +}); -- libgit2 0.21.2