From 5b5c607822139ba46fb08da84755ec0218d08151 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 12 Dec 2018 10:04:03 +0100 Subject: Move licence header in html templates --- python/gui/templates/home.html | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'python/gui/templates/home.html') diff --git a/python/gui/templates/home.html b/python/gui/templates/home.html index 5cb29f8..cc8fbf0 100644 --- a/python/gui/templates/home.html +++ b/python/gui/templates/home.html @@ -1,3 +1,19 @@ + + + +{% include 'head.html' %} + + + {% include 'body-nav.html' %} + +
+
+

Opendigitalradio

ODR-DabMod Interface

+
+
+ + + - - - - -{% include 'head.html' %} - - - {% include 'body-nav.html' %} - -
-
-

Opendigitalradio

ODR-DabMod Interface

-
-
- - -- cgit v1.2.3 From b717160a90279c21c068d39673c6aafad66dfcae Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Wed, 9 Jan 2019 15:44:06 +0100 Subject: GUI: Add status to home page --- python/dpd/ExtractStatistic.py | 4 +- python/dpdce.py | 10 +-- python/gui.py | 4 +- python/gui/api.py | 20 ++++-- python/gui/static/css/odr.css | 14 +++++ python/gui/static/js/odr-home.js | 132 +++++++++++++++++++++++++++++++++++++++ python/gui/templates/home.html | 38 ++++++++--- python/lib/yamlrpc.py | 2 +- 8 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 python/gui/static/css/odr.css create mode 100644 python/gui/static/js/odr-home.js (limited to 'python/gui/templates/home.html') diff --git a/python/dpd/ExtractStatistic.py b/python/dpd/ExtractStatistic.py index 25647a6..86580d8 100644 --- a/python/dpd/ExtractStatistic.py +++ b/python/dpd/ExtractStatistic.py @@ -76,7 +76,7 @@ class ExtractStatistic: i_sub += 1 ax = plt.subplot(sub_rows, sub_cols, i_sub) ax.plot(tx_values, rx_values, - label="Estimated Values", + label="Averaged measurements", color="red") for i, tx_value in enumerate(tx_values): rx_values_list = self.rx_values_lists[i] @@ -94,7 +94,7 @@ class ExtractStatistic: i_sub += 1 ax = plt.subplot(sub_rows, sub_cols, i_sub) ax.plot(tx_values, np.rad2deg(phase_diffs_values), - label="Estimated Values", + label="Averaged measurements", color="red") for i, tx_value in enumerate(tx_values): phase_diff = phase_diffs_values_lists[i] diff --git a/python/dpdce.py b/python/dpdce.py index 27c5253..5b308c4 100755 --- a/python/dpdce.py +++ b/python/dpdce.py @@ -409,7 +409,7 @@ try: try: addr, msg_id, method, params = cmd_socket.receive_request() except ValueError as e: - logging.warning('YAML-RPC request error: {}'.format(e)) + logging.warning('RPC request error: {}'.format(e)) continue except TimeoutError: continue @@ -417,22 +417,22 @@ try: logging.info('Caught KeyboardInterrupt') break except: - logging.error('YAML-RPC unknown error') + logging.error('RPC unknown error') break if any(method == m for m in ['trigger_run', 'reset', 'adapt']): - logging.info('YAML-RPC request : {}'.format(method)) + logging.info('Received RPC request : {}'.format(method)) command_queue.put(method) cmd_socket.send_success_response(addr, msg_id, None) elif method == 'restore_dump': - logging.info('YAML-RPC restore dump {}'.format(params['dump_id'])) + logging.info('Received RPC request : restore_dump({})'.format(params['dump_id'])) command_queue.put(f"restore_dump-{params['dump_id']}") cmd_socket.send_success_response(addr, msg_id, None) elif method == 'get_results': with lock: cmd_socket.send_success_response(addr, msg_id, results) elif method == 'calibrate': - logging.info('YAML-RPC request : {}'.format(method)) + logging.info('Received RPC request : {}'.format(method)) command_queue.put('calibrate') cmd_socket.send_success_response(addr, msg_id, None) else: diff --git a/python/gui.py b/python/gui.py index 0090a3a..a9328ee 100755 --- a/python/gui.py +++ b/python/gui.py @@ -33,6 +33,7 @@ from lib import zmqrc env = Environment(loader=FileSystemLoader('gui/templates')) base_js = ["js/odr.js"] +base_css = ["css/odr.css"] class Root: def __init__(self, dpd_port): @@ -51,7 +52,8 @@ class Root: @cherrypy.expose def home(self): tmpl = env.get_template("home.html") - return tmpl.render(tab='home', js=base_js, is_login=False) + js = base_js + ["js/odr-home.js"] + return tmpl.render(tab='home', js=js, css=base_css, is_login=False) @cherrypy.expose def rcvalues(self): diff --git a/python/gui/api.py b/python/gui/api.py index c0effde..f9e0ad0 100755 --- a/python/gui/api.py +++ b/python/gui/api.py @@ -80,8 +80,18 @@ class API: return send_error(str(e)) return send_ok() else: - cherrypy.response.status = 400 - return send_error("POST only") + if all(p in kwargs for p in ('controllable', 'param')): + try: + return send_ok(self.mod_rc.get_param_value(kwargs['controllable'], kwargs['param'])) + except IOError as e: + cherrypy.response.status = 503 + return send_error(str(e)) + except ValueError as e: + cherrypy.response.status = 503 + return send_error(str(e)) + else: + cherrypy.response.status = 400 + return send_error("missing 'controllable' or 'param' GET parameters") def _wrap_dpd(self, method, data=None): try: @@ -89,12 +99,12 @@ class API: return send_ok(reply) except ValueError as e: cherrypy.response.status = 503 - return send_error("YAML-RPC call error: {}".format(e)) + return send_error("DPDCE remote procedure call error: {}".format(e)) except TimeoutError as e: cherrypy.response.status = 503 - return send_error("YAML-RPC timeout: {}".format(e)) + return send_error("DPDCE remote procedure call timed out") cherrypy.response.status = 500 - return send_error("YAML-RPC unknown error") + return send_error("Unknown DPDCE remote procedure error error") @cherrypy.expose @cherrypy.tools.json_out() diff --git a/python/gui/static/css/odr.css b/python/gui/static/css/odr.css new file mode 100644 index 0000000..1710464 --- /dev/null +++ b/python/gui/static/css/odr.css @@ -0,0 +1,14 @@ +.glyphicon-refresh-animate { + -animation: spin 1.8s infinite linear; + -webkit-animation: spin2 1.8s infinite linear; +} + +@-webkit-keyframes spin2 { + from { -webkit-transform: rotate(0deg);} + to { -webkit-transform: rotate(360deg);} +} + +@keyframes spin { + from { transform: scale(1) rotate(0deg);} + to { transform: scale(1) rotate(360deg);} +} diff --git a/python/gui/static/js/odr-home.js b/python/gui/static/js/odr-home.js new file mode 100644 index 0000000..11aed8e --- /dev/null +++ b/python/gui/static/js/odr-home.js @@ -0,0 +1,132 @@ +// Copyright (C) 2019 +// Matthias P. Braendli, matthias.braendli@mpb.li +// +// http://www.opendigitalradio.org +// +// This file is part of ODR-DabMod. +// +// ODR-DabMod is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// ODR-DabMod is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with ODR-DabMod. If not, see . +// + +function apiRequestChain(uri, get_data, success_callback, fail_callback) { + $.ajax({ + type: "GET", + url: uri, + data: get_data, + contentType: 'application/json', + dataType: 'json', + + error: function(data) { + console.log("GET " + JSON.stringify(get_data) + " error: " + data.responseText); + fail_callback(data.responseText); + }, + success: function(data) { + console.log("GET " + JSON.stringify(get_data) + " success: " + JSON.stringify(data)); + if (data.status == 'ok') { + success_callback(data.data); + } + else { + fail_callback(data.data); + } + }, + }); +} + +function mark_pending(id) { + document.getElementById(id).className = "glyphicon glyphicon-refresh glyphicon-refresh-animate"; +} + +function mark_ok(id, comment) { + document.getElementById(id).className = "glyphicon glyphicon-ok"; + + if (comment) { + document.getElementById(id + "_comment").innerHTML = comment; + } +} + +function mark_fail(id, reason) { + var el = document.getElementById(id); + el.className = "glyphicon glyphicon-remove"; + el.style.color = "#FF3333"; + + document.getElementById(id + "_comment").innerHTML = reason; + + var overall = document.getElementById("overall_state"); + overall.style.color = "#FF8833"; + overall.className = "glyphicon glyphicon-alert"; +} + +function check_rc() { + mark_pending('is_rc_ok'); + apiRequestChain("/api/parameter", + {controllable: 'sdr', param: 'freq'}, + function(data) { + mark_ok('is_rc_ok'); + check_modulating(0); + }, + function(data) { + mark_fail('is_rc_ok', JSON.parse(data)['reason']); + }); +} + +function check_modulating(last_num_frames) { + mark_pending('is_modulating'); + apiRequestChain("/api/parameter", + {controllable: 'sdr', param: 'frames'}, + function(data) { + if (data > 0) { + if (last_num_frames == 0) { + setTimeout(function() { check_modulating(data); }, 200); + } + else { + if (data == last_num_frames) { + mark_fail('is_modulating', "Frame counter not incrementing: " + data); + } + else { + mark_ok('is_modulating', "Number of frames modulated: " + data); + check_dpdce_running(); + } + } + } + else { + mark_fail('is_modulating', 'number of frames is 0'); + } + }, + function(data) { + mark_fail('is_modulating', data); + }); +} + +function check_dpdce_running() { + mark_pending('is_dpdce_running'); + apiRequestChain("/api/dpd_results", + {controllable: 'sdr', param: 'frames'}, + function(data) { + mark_ok('is_dpdce_running', "State: " + data['state']); + mark_ok('overall_state'); + }, + function(data) { + mark_fail('is_dpdce_running', JSON.parse(data)['reason']); + }); +} + +$(function(){ + setTimeout(check_rc, 20); +}); + + +// ToolTip init +$(function(){ + $('[data-toggle="tooltip"]').tooltip(); +}); diff --git a/python/gui/templates/home.html b/python/gui/templates/home.html index cc8fbf0..bf802d6 100644 --- a/python/gui/templates/home.html +++ b/python/gui/templates/home.html @@ -4,13 +4,37 @@ {% include 'head.html' %} - {% include 'body-nav.html' %} - -
-
-

Opendigitalradio

ODR-DabMod Interface

-
-
+ {% include 'body-nav.html' %} + +
+
+

Opendigitalradio

ODR-DabMod Status Check + +

+
+

ODR-DabMod +

+
    +
  • Answering to RC: + + +
  • +
  • Frame generation: + + +
  • +
+ +

Checking predistortion +

    +
  • DPDCE running: + + +
  • +
+
+
+
diff --git a/python/lib/yamlrpc.py b/python/lib/yamlrpc.py index d4e744a..67c65ff 100644 --- a/python/lib/yamlrpc.py +++ b/python/lib/yamlrpc.py @@ -85,7 +85,7 @@ class Socket: try: data, addr = self.socket.recvfrom(UDP_PACKETSIZE) except socket.timeout as to: - raise TimeoutError("Timeout: " + str(to)) + raise TimeoutError() y = yaml.load(data.decode()) -- cgit v1.2.3 From 77cfae162dea6195e8869c95db8876bbaf98fad1 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 22 Jan 2019 10:08:21 +0100 Subject: GUI: Check modulator rate on home screen --- python/gui/static/js/odr-home.js | 22 ++++++++++++++++++++-- python/gui/templates/home.html | 4 ++++ 2 files changed, 24 insertions(+), 2 deletions(-) (limited to 'python/gui/templates/home.html') diff --git a/python/gui/static/js/odr-home.js b/python/gui/static/js/odr-home.js index 11aed8e..9498ae7 100644 --- a/python/gui/static/js/odr-home.js +++ b/python/gui/static/js/odr-home.js @@ -95,7 +95,7 @@ function check_modulating(last_num_frames) { } else { mark_ok('is_modulating', "Number of frames modulated: " + data); - check_dpdce_running(); + check_rate_4x(); } } } @@ -108,10 +108,28 @@ function check_modulating(last_num_frames) { }); } +function check_rate_4x() { + mark_pending('is_rate_4x'); + apiRequestChain("/api/parameter", + {controllable: 'modulator', param: 'rate'}, + function(data) { + if (data == 8192000) { + mark_ok('is_rate_4x', "Samplerate: " + data); + check_dpdce_running(); + } + else { + mark_fail('is_rate_4x', "Samplerate is not 8192ksps: " + data); + } + }, + function(data) { + mark_fail('is_rate_4x', JSON.parse(data)['reason']); + }); +} + function check_dpdce_running() { mark_pending('is_dpdce_running'); apiRequestChain("/api/dpd_results", - {controllable: 'sdr', param: 'frames'}, + {}, function(data) { mark_ok('is_dpdce_running', "State: " + data['state']); mark_ok('overall_state'); diff --git a/python/gui/templates/home.html b/python/gui/templates/home.html index bf802d6..0a0713a 100644 --- a/python/gui/templates/home.html +++ b/python/gui/templates/home.html @@ -27,6 +27,10 @@

Checking predistortion

    +
  • Sample rate at 4x native rate: + + +
  • DPDCE running: -- cgit v1.2.3 From d7a0913ead6724bfd508a5480c7014b2516975ce Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 22 Jan 2019 11:15:00 +0100 Subject: GUI: Check for underruns in home screen --- python/gui/static/js/odr-home.js | 51 ++++++++++++++++++++++++++++++++++++---- python/gui/templates/home.html | 4 ++++ 2 files changed, 51 insertions(+), 4 deletions(-) (limited to 'python/gui/templates/home.html') diff --git a/python/gui/static/js/odr-home.js b/python/gui/static/js/odr-home.js index 9498ae7..2fa873a 100644 --- a/python/gui/static/js/odr-home.js +++ b/python/gui/static/js/odr-home.js @@ -43,8 +43,20 @@ function apiRequestChain(uri, get_data, success_callback, fail_callback) { }); } -function mark_pending(id) { +function mark_pending(id, comment) { document.getElementById(id).className = "glyphicon glyphicon-refresh glyphicon-refresh-animate"; + + if (comment) { + document.getElementById(id + "_comment").innerHTML = comment; + } +} + +var failure_encountered = false; + +function mark_overall_ok() { + if (!failure_encountered) { + document.getElementById("overall_state").className = "glyphicon glyphicon-ok"; + } } function mark_ok(id, comment) { @@ -56,6 +68,8 @@ function mark_ok(id, comment) { } function mark_fail(id, reason) { + failure_encountered = true; + var el = document.getElementById(id); el.className = "glyphicon glyphicon-remove"; el.style.color = "#FF3333"; @@ -95,7 +109,7 @@ function check_modulating(last_num_frames) { } else { mark_ok('is_modulating', "Number of frames modulated: " + data); - check_rate_4x(); + check_underrunning(0, 0); } } } @@ -108,6 +122,35 @@ function check_modulating(last_num_frames) { }); } +function check_underrunning(iteration, first_underruns) { + var n_checks = 3; + + apiRequestChain("/api/parameter", + {controllable: 'sdr', param: 'underruns'}, + function(data) { + if (iteration == 0) { + mark_pending('is_underrunning', "Checking for underruns"); + setTimeout(function() { check_underrunning(iteration+1, data); }, 2000); + } + else if (iteration < n_checks) { + mark_pending('is_underrunning', "Check " + iteration + "/" + n_checks + "..."); + setTimeout(function() { check_underrunning(iteration+1, first_underruns); }, 2000); + } + else { + if (data == first_underruns) { + mark_ok('is_underrunning', "Number of underruns is not increasing: " + data); + } + else { + mark_fail('is_underrunning', "Underruns observed in last " + n_checks + " seconds: " + data); + } + check_rate_4x(); + } + }, + function(data) { + mark_fail('is_underrunning', data); + }); +} + function check_rate_4x() { mark_pending('is_rate_4x'); apiRequestChain("/api/parameter", @@ -115,11 +158,11 @@ function check_rate_4x() { function(data) { if (data == 8192000) { mark_ok('is_rate_4x', "Samplerate: " + data); - check_dpdce_running(); } else { mark_fail('is_rate_4x', "Samplerate is not 8192ksps: " + data); } + check_dpdce_running(); }, function(data) { mark_fail('is_rate_4x', JSON.parse(data)['reason']); @@ -132,7 +175,7 @@ function check_dpdce_running() { {}, function(data) { mark_ok('is_dpdce_running', "State: " + data['state']); - mark_ok('overall_state'); + mark_overall_ok(); }, function(data) { mark_fail('is_dpdce_running', JSON.parse(data)['reason']); diff --git a/python/gui/templates/home.html b/python/gui/templates/home.html index 0a0713a..d1c8d02 100644 --- a/python/gui/templates/home.html +++ b/python/gui/templates/home.html @@ -23,6 +23,10 @@
  • +
  • Underruns: + + +

Checking predistortion -- cgit v1.2.3 From 2b3cf8e158901ae7d20dd330c34a14947191606e Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 22 Jan 2019 11:40:26 +0100 Subject: GUI: Check for late packets --- python/gui/static/js/odr-home.js | 29 +++++++++++++++++++++++++++++ python/gui/templates/home.html | 4 ++++ 2 files changed, 33 insertions(+) (limited to 'python/gui/templates/home.html') diff --git a/python/gui/static/js/odr-home.js b/python/gui/static/js/odr-home.js index 2fa873a..56c8eb4 100644 --- a/python/gui/static/js/odr-home.js +++ b/python/gui/static/js/odr-home.js @@ -110,6 +110,7 @@ function check_modulating(last_num_frames) { else { mark_ok('is_modulating', "Number of frames modulated: " + data); check_underrunning(0, 0); + check_late(0, 0); } } } @@ -151,6 +152,34 @@ function check_underrunning(iteration, first_underruns) { }); } +function check_late(iteration, first_late) { + var n_checks = 3; + + apiRequestChain("/api/parameter", + {controllable: 'sdr', param: 'latepackets'}, + function(data) { + if (iteration == 0) { + mark_pending('is_late', "Checking for late packets"); + setTimeout(function() { check_late(iteration+1, data); }, 2000); + } + else if (iteration < n_checks) { + mark_pending('is_late', "Check " + iteration + "/" + n_checks + "..."); + setTimeout(function() { check_late(iteration+1, first_late); }, 2000); + } + else { + if (data == first_late) { + mark_ok('is_late', "Number of late packets is not increasing: " + data); + } + else { + mark_fail('is_late', "Late packets observed in last " + n_checks + " seconds: " + data); + } + } + }, + function(data) { + mark_fail('is_late', data); + }); +} + function check_rate_4x() { mark_pending('is_rate_4x'); apiRequestChain("/api/parameter", diff --git a/python/gui/templates/home.html b/python/gui/templates/home.html index d1c8d02..f89e18f 100644 --- a/python/gui/templates/home.html +++ b/python/gui/templates/home.html @@ -27,6 +27,10 @@ +

  • Late packets: + + +
  • Checking predistortion -- cgit v1.2.3 From e352ec38c20d626a4359c684abea23ef92a41470 Mon Sep 17 00:00:00 2001 From: "Matthias P. Braendli" Date: Tue, 22 Jan 2019 11:50:01 +0100 Subject: GUI: Check GPSDO status --- python/gui/static/js/odr-home.js | 24 +++++++++++++++++++++--- python/gui/templates/home.html | 4 ++++ 2 files changed, 25 insertions(+), 3 deletions(-) (limited to 'python/gui/templates/home.html') diff --git a/python/gui/static/js/odr-home.js b/python/gui/static/js/odr-home.js index 56c8eb4..b74c4c8 100644 --- a/python/gui/static/js/odr-home.js +++ b/python/gui/static/js/odr-home.js @@ -32,7 +32,6 @@ function apiRequestChain(uri, get_data, success_callback, fail_callback) { fail_callback(data.responseText); }, success: function(data) { - console.log("GET " + JSON.stringify(get_data) + " success: " + JSON.stringify(data)); if (data.status == 'ok') { success_callback(data.data); } @@ -109,9 +108,8 @@ function check_modulating(last_num_frames) { } else { mark_ok('is_modulating', "Number of frames modulated: " + data); - check_underrunning(0, 0); - check_late(0, 0); } + check_gpsdo_ok(); } } else { @@ -123,6 +121,26 @@ function check_modulating(last_num_frames) { }); } +function check_gpsdo_ok() { + mark_pending('is_gpsdo_ok'); + apiRequestChain("/api/parameter", + {controllable: 'sdr', param: 'gpsdo_num_sv'}, + function(data) { + if (data > 3) { + mark_ok('is_gpsdo_ok', "Number of SVs used: " + data); + } + else { + mark_fail('is_gpsdo_ok', "Number of SVs (" + data + ") is too low"); + } + check_underrunning(0, 0); + check_late(0, 0); + }, + function(data) { + mark_fail('is_gpsdo_ok', json.parse(data)['reason']); + }); +} + + function check_underrunning(iteration, first_underruns) { var n_checks = 3; diff --git a/python/gui/templates/home.html b/python/gui/templates/home.html index f89e18f..398df37 100644 --- a/python/gui/templates/home.html +++ b/python/gui/templates/home.html @@ -23,6 +23,10 @@ +

  • GPSDO status: + + +
  • Underruns: -- cgit v1.2.3