From 5735fe9f016530cf06d02169fd6981a33e161f6c Mon Sep 17 00:00:00 2001 From: l_samenv Date: Wed, 14 Aug 2024 08:27:59 +0200 Subject: [PATCH] Working jumps (routes) + goToNow now reloads --- client/components/dates_popup/dates_popup.css | 2 +- client/components/dates_popup/dates_popup.js | 16 +- .../states_indicator/dates/dates.js | 1 - client/jsFiles/SEAWebClientCommunication.js | 5 +- client/jsFiles/SEAWebClientGraphics.js | 238 ++++++++++-------- influxdb.py | 77 +++--- influxgraph.py | 5 +- 7 files changed, 193 insertions(+), 151 deletions(-) diff --git a/client/components/dates_popup/dates_popup.css b/client/components/dates_popup/dates_popup.css index cd68d37..6d14e47 100644 --- a/client/components/dates_popup/dates_popup.css +++ b/client/components/dates_popup/dates_popup.css @@ -57,6 +57,6 @@ margin-top: 10px; } -.jump-button-all, .jump-button-end{ +.jump-button-all, .jump-button-time{ width: 200px; } \ No newline at end of file diff --git a/client/components/dates_popup/dates_popup.js b/client/components/dates_popup/dates_popup.js index 6d34c2f..ec6b902 100644 --- a/client/components/dates_popup/dates_popup.js +++ b/client/components/dates_popup/dates_popup.js @@ -1,6 +1,6 @@ class DatesPopup extends HTMLElement{ - static END = 0; + static TIME = 0; static ALL = 1; static DAY = new Date(24*60*60*1000); @@ -24,10 +24,10 @@ class DatesPopup extends HTMLElement{ this.jumpCallback(dateTimeInput[0], dateTimeInput[1], DatesPopup.ALL); } - doJumpEndCallback(){ + doJumpTimeCallback(){ let dateTimeInput = this.getDateTimeInput(); this.hide(); - this.jumpCallback(dateTimeInput[0], dateTimeInput[1], DatesPopup.END); + this.jumpCallback(dateTimeInput[0], dateTimeInput[1], DatesPopup.TIME); } convertTimeStringToTimestamp(formattedTime){ @@ -62,7 +62,7 @@ class DatesPopup extends HTMLElement{ this.hide(); this.getElementsByTagName("img")[0].onclick = () => {this.hide();}; this.getElementsByClassName("go-to-now-button")[0].onclick = () => {this.doGoToNowCallback();}; - this.getElementsByClassName("jump-button-end")[0].onclick = () => {this.doJumpEndCallback();}; + this.getElementsByClassName("jump-button-time")[0].onclick = () => {this.doJumpTimeCallback();}; this.getElementsByClassName("jump-button-all")[0].onclick = () => {this.doJumpAllCallback();}; } @@ -85,17 +85,15 @@ class DatesPopup extends HTMLElement{
- +
- - +
- - +
diff --git a/client/components/states_indicator/dates/dates.js b/client/components/states_indicator/dates/dates.js index 4b08874..0d88d10 100644 --- a/client/components/states_indicator/dates/dates.js +++ b/client/components/states_indicator/dates/dates.js @@ -40,7 +40,6 @@ class DateIndicator extends HTMLElement{ } showPopup(){ - console.log("clicked"); this.datePopup.show(); } diff --git a/client/jsFiles/SEAWebClientCommunication.js b/client/jsFiles/SEAWebClientCommunication.js index f76f1fa..bbae654 100644 --- a/client/jsFiles/SEAWebClientCommunication.js +++ b/client/jsFiles/SEAWebClientCommunication.js @@ -345,7 +345,7 @@ function successHandler(s, message) { begin = timeRange[0] - timeRange[1]; select.value = begin; // Server-request for variable-list.*/ - reqJSON(0, "http://" + hostPort + "/getvars?time=" + timeRange + "&id=" + reqJSON(0, "http://" + hostPort + "/getvars?time=" + timeRange + "&all=False&id=" + clientID, successHandler, errorHandler); break; // Response to a "getvars"-server-request. @@ -365,7 +365,8 @@ function successHandler(s, message) { } else { nextInitCommand(); }*/ - graphs.receivedVars(message.blocks); + // graphs.receivedVars(message.blocks); + graphs.initGraphs(message.blocks); nextInitCommand(); break; // Response to a "graph"-server-request. diff --git a/client/jsFiles/SEAWebClientGraphics.js b/client/jsFiles/SEAWebClientGraphics.js index ae81280..ea633db 100644 --- a/client/jsFiles/SEAWebClientGraphics.js +++ b/client/jsFiles/SEAWebClientGraphics.js @@ -446,7 +446,7 @@ let graphs = (function (){ }*/ } } - graph.setMinMax(minTime,maxTime); + graph.setMinMax(currentMinTime,currentMaxTime); graph.autoScaleIf(); graph.update(); showLegends(legendFlag, false); @@ -804,11 +804,49 @@ let graphs = (function (){ } function jumpToDate(dateTimestampMs, timeValueMs, mode){ - let msLeftTimestamp = dateTimestampMs + timeValueMs; - let msRightTimestamp = msLeftTimestamp + 24*60*60*1000; - if (liveMode && msRightTimestamp < lastTime) setLiveMode(false); - setMinMax(msLeftTimestamp, msRightTimestamp); - updateAuto(); + + cursorLine(null); + + let msLeftTimestampGetVars = 0, msLeftTimestampGetGraph = 0 , msRightTimestampGetVars = 0, msRightTimestampGetGraph = 0; + + msLeftTimestampGetVars = dateTimestampMs; + msLeftTimestampGetGraph = dateTimestampMs; + + if(mode == DatesPopup.TIME){ + msRightTimestampGetVars = dateTimestampMs + timeValueMs; + } + else if(mode == DatesPopup.ALL){ // we ignore time + msRightTimestampGetVars = dateTimestampMs + 24*60*60*1000 - 1000; // we exclude the very beginning of the next day + } + + msRightTimestampGetGraph = dateTimestampMs + 24*60*60*1000 - 1000; + + AJAX("http://" + hostPort + "/getvars?time=" + msLeftTimestampGetVars/1000 + "," + msRightTimestampGetVars/1000 + "&all=" +allQueryParameterRepresentation(mode) + "&id="+ clientID).getJSON().then(function(data){ + blocks = data.blocks; + + maxTime = msRightTimestampGetGraph; + minTime = msLeftTimestampGetGraph; + + currentMaxTime = maxTime + 60000; + currentMinTime = minTime; + + globalIndicators.getIndicatorsMap()[datesKey].update(currentMinTime); + + ngraphs = 0; // forces all the graphs to reset + createGraphs(); + + if (liveMode && msRightTimestampGetGraph < lastTime) setLiveMode(false); + }); + + } + + function allQueryParameterRepresentation(mode){ + if(mode == DatesPopup.ALL){ + return "True"; + } + else { + return "False"; + } } function shiftOlder(){ @@ -881,15 +919,28 @@ let graphs = (function (){ } /** - * Removes the cursor, sets the window visualisation to [now-previousDelta, now], goes for live mode and updates + * Removes the cursor, gets the var + graphs for now (window visualisation to [now-30min, now]) and ask for updates */ function gotoNow() { - cursorLine(null); - if (!liveMode) { - setMinMax(graphs.now() - (currentMaxTime - currentMinTime), graphs.now()); - } - setLiveMode(true); - updateAuto(); + let msRightTimestamp = graphs.now(); + let msLeftTimestamp = msRightTimestamp - 30*60*1000; + cursorLine(null); + + AJAX("http://" + hostPort + "/getvars?time=" + msLeftTimestamp/1000 + "," + msRightTimestamp/1000 + "&all=False&id="+ clientID).getJSON().then(function(data){ + currentMaxTime = msRightTimestamp + 60000; + currentMinTime = msLeftTimestamp; + + maxTime = msRightTimestamp; + minTime = msLeftTimestamp; + + blocks = data.blocks; + + ngraphs = 0; + createGraphs(); + globalIndicators.getIndicatorsMap()[datesKey].update(currentMinTime); + activateUpdates(); + + }); } /* @@ -906,6 +957,62 @@ let graphs = (function (){ } */ + function buildGraphicsUI(){ + + let f = 0; + insertSlide(f, " ", "graphics", container); + + let currentSwiper = swiper[f]; + + function setSlidingMode(mode) { + currentSwiper.params.noSwipingClass = mode ? "allow-swipe" : "swiper-slide-main"; + } + + currentSwiper.enableSwiping(false); + currentSwiper.on('reachBeginning', function () { + currentSwiper.enableSwiping(false); + }) + + let graphicsPanel = container.parentNode.querySelector('.panel') + graphicsPanel.classList.add('graphics'); + graphicsPanel.childNodes[0].style.visibility = "hidden"; // hides the span added by the swippers + // The cross to display "main" panel at the location of the graphs + let gotoMainElm = document.createElement('div'); + gotoMainElm.innerHTML = "×"; + gotoMainElm.addEventListener('click', function () { + currentSwiper.enableSwiping(true); + console.log("MAIN") + currentSwiper.slideNext(); + }); + + globalIndicators.loadIndicators(graphicsPanel); + globalControls.loadControls(graphicsPanel); + loadGraphicsMenu(graphicsPanel); + graphicsPanel.appendChild(gotoMainElm); + gotoMainElm.style.marginTop = "auto"; + gotoMainElm.style.marginBottom = "auto"; + gotoMainElm.style.marginRight = "6px"; + gotoMainElm.style.color = "white"; + gotoMainElm.style.cursor = "pointer"; + created = true; + + if (isTouchDevice) { + doubleTap(removeCursor); + } else { + window.addEventListener('dblclick', removeCursor); + showLegends(true, false); + adjustLegends(); + } + } + + function initGraphs(blocks){ + if(!created){ + buildGraphicsUI(); + created = true; + } + receivedVars(blocks); + } + /** * Holds the received variables from the /getvars call, gets the server time, insert slide (?), * create the graphs, activate SSE graph-update messages, sets the different event listeners @@ -920,104 +1027,18 @@ let graphs = (function (){ } AJAX("http://" + hostPort + "/gettime?time=-1800,0&id="+ clientID).getJSON().then(function(data){ startTime = data.time[1]*1000; - maxTime = startTime + 60000; - console.log('MAXTIME', maxTime - Date.now()); + maxTime = startTime; + currentMaxTime = maxTime + 60000; + // console.log('MAXTIME', currentMaxTime - Date.now()); minTime = data.time[0]*1000; recvTime = performance.now(); + + blocks = blocks_arg; + + createGraphs(); + + activateUpdates(); }); - - - // g_varlist = getVarlist(nblocks) - let f = 0; - insertSlide(f, " ", "graphics", container); - blocks = blocks_arg; - - createGraphs(); - - activateUpdates(); - - let graphicsPanel = container.parentNode.querySelector('.panel') - graphicsPanel.classList.add('graphics'); - graphicsPanel.childNodes[0].style.visibility = "hidden"; // hides the span added by the swippers - // The cross to display "main" panel at the location of the graphs - let gotoMainElm = document.createElement('div'); - gotoMainElm.innerHTML = "×"; - gotoMainElm.addEventListener('click', function () { - currentSwiper.enableSwiping(true); - console.log("MAIN") - currentSwiper.slideNext(); - }); - - if(!created){ - globalIndicators.loadIndicators(graphicsPanel); - globalControls.loadControls(graphicsPanel); - loadGraphicsMenu(graphicsPanel); - graphicsPanel.appendChild(gotoMainElm); - gotoMainElm.style.marginTop = "auto"; - gotoMainElm.style.marginBottom = "auto"; - gotoMainElm.style.marginRight = "6px"; - gotoMainElm.style.color = "white"; - gotoMainElm.style.cursor = "pointer"; - created = true; - } - - // gotoNowElm.addEventListener('click', gotoNow); - //gotoNowElm.innerHTML = "go to now"; - // container.parentNode.querySelector('.panel span').appendChild(gotoNowElm); - - // Removes the cursor then applies the changes - // function removeCursor(evt=null) { - // graphs.cursorLine(null); - // graphs.update(); - // } - if (isTouchDevice) { - doubleTap(removeCursor); - } else { - window.addEventListener('dblclick', removeCursor); - showLegends(true, false); - adjustLegends(); - // let zoomMode = document.createElement('div'); - /** - * Sets the HTML button for the y-zoom depending on the current mode - */ - // function setInnerZoomMode(){ - // if (currentZoomMode == 'y') { - // zoomMode.innerHTML = " y-zoom"; - // } else { - // zoomMode.innerHTML = " y-zoom"; - // } - // prevTime = null; // reset zoom speed time - // } - // setInnerZoomMode(); - /** - * Inverts the zoom mode (x<->y) - */ - // function toggleZoomMode(){ - // if (currentZoomMode == 'y') - // currentZoomMode = 'x'; - // else - // currentZoomMode = 'y'; - // setInnerZoomMode(); - // for (let gr of graph_array) { - // if (gr) gr.setZoomMode(currentZoomMode); - // } - // } - // zoomMode.addEventListener('click', toggleZoomMode); - // container.parentNode.querySelector('.panel span').appendChild(zoomMode); - } - - let currentSwiper = swiper[f]; - - function setSlidingMode(mode) { - currentSwiper.params.noSwipingClass = mode ? "allow-swipe" : "swiper-slide-main"; - } - - currentSwiper.enableSwiping(false); - currentSwiper.on('reachBeginning', function () { - currentSwiper.enableSwiping(false); - }) - - // container.parentNode.querySelector('.panel span').appendChild(gotoMainElm); } @@ -1261,6 +1282,7 @@ let graphs = (function (){ shiftOlder: shiftOlder, shiftNewer: shiftNewer, jumpToDate: jumpToDate, + initGraphs: initGraphs, } })(); diff --git a/influxdb.py b/influxdb.py index 3663438..7bd8145 100644 --- a/influxdb.py +++ b/influxdb.py @@ -52,22 +52,24 @@ class InfluxDataGetter: # ----- PUBLIC METHODS - def get_available_variables_at_time(self, time): + def get_available_variables_at_time(self, times, all=False): """ Gets the available variables (those that we can have a value for since the device has been installed on the instrument) at the given point in time. + We can get the last available variables at the given point in time or all the known variables for the day corresponding to the timestamp. Parameters : - time (int) : the unix timestamp in seconds of the given point in time - + times ([int]) : the unix timestamps in seconds of the range. The first value can be unused. The last can represent the point in time. + all (bool) : indicates if we want all the variables for the given times[1] timestamp (all the day) + Returns : [{"tag":(str), "unit":(str), "curves":[{"name":(str), "label":(str), "color":(str)}]}] : a list of dictionnaries, each one representing a block of curves with their name, their label and their color to display, grouped by their tag (which can be the unit augmented with an index) and their unit. """ - all_setup_info = self._get_all_setup_info_as_dict(time) + all_setup_info = self._get_all_setup_info_as_dict(times, all) available_variables = self._extract_variables(all_setup_info) - available_variables = self._remove_variables_without_value_float(available_variables, time) - available_variables = self._set_variables_with_target(available_variables, time) + available_variables = self._remove_variables_without_value_float(available_variables, times) + available_variables = self._set_variables_with_target(available_variables, times) res = self._group_variables_by_unit(available_variables) return res @@ -126,12 +128,14 @@ class InfluxDataGetter: # ----- PRIVATE METHODS - def _get_all_setup_info_as_dict(self, time): + def _get_all_setup_info_as_dict(self, times, all=False): """ Gets the value of the field setup_info in the measurements nicos/se_main, nicos/se_stick, nicos/se_addons as an array of Python dicts. + Takes the last setup_info dict (for each measurement) known at times[1], or all the dicts for this day + the previous known (also for each) Parameters - time (int) : the Unix timestamp in seconds we want to look the availability of variables for. + times ([int]) : the unix timestamps in seconds of the range. The first value can be unused. The last can represent the point in time. + all (bool) : indicates if we want all the variables for the given times[1] timestamp (all the day) Returns : [{(str):((str), {...})}]: an array of the parsed "setup_info dict" of each measurements. The key is the secop_module prefixed with "se_", and the value is a tuple with its first value @@ -141,21 +145,34 @@ class InfluxDataGetter: measurements = ["nicos/se_main", "nicos/se_stick", "nicos/se_addons"] res = [] for measurement in measurements: - to_add = {} + to_add = [] query = f""" from(bucket: "{self._bucket}") - |> range(start: 0, stop: {time + 1}) + |> range(start: {times[0] if all else 0}, stop: {times[1] + 1}) |> filter(fn: (r) => r._measurement == "{measurement}") |> filter(fn: (r) => r._field == "setup_info") - |> last() + {"" if all else "|> last()"} |> yield(name: "res") """ - tables = self._db.query(query) for table in tables: for record in table.records: - to_add = ast.literal_eval(record.get_value()) - res.append(to_add) + to_add.append(ast.literal_eval(record.get_value())) + + if all: + query = f""" + from(bucket: "{self._bucket}") + |> range(start: 0, stop: {times[0]+1}) + |> filter(fn: (r) => r._measurement == "{measurement}") + |> filter(fn: (r) => r._field == "setup_info") + |> last() + |> yield(name: "res") + """ + tables = self._db.query(query) + for table in tables: + for record in table.records: + to_add.append(ast.literal_eval(record.get_value())) + res.extend(to_add) return res def _extract_variables(self, all_setup_info_dict): @@ -172,17 +189,21 @@ class InfluxDataGetter: """ available_varirables = [] + added_names = [] for setup_info_dict in all_setup_info_dict: for (setup_info_variable_name, content) in setup_info_dict.items(): if content[0] != "nicos.devices.secop.devices.SecopDevice": - available_varirables.append( - { - "name":self._transform_setup_info_variable_name_to_influx(setup_info_variable_name), - "label":content[1]["secop_module"], - "unit":content[1]["unit"], - "has_potential_target": "target_datainfo" in content[1].keys() - } - ) + name = self._transform_setup_info_variable_name_to_influx(setup_info_variable_name) + if name not in added_names: + available_varirables.append( + { + "name":name, + "label":content[1]["secop_module"], + "unit":content[1]["unit"], + "has_potential_target": "target_datainfo" in content[1].keys() + } + ) + added_names.append(name) return available_varirables def _transform_setup_info_variable_name_to_influx(self, setup_info_name): @@ -197,14 +218,14 @@ class InfluxDataGetter: """ return self._influx_instrument_config["measurement_prefix"] + setup_info_name.lower()[len(self._influx_instrument_config["setup_info_prefix"]):] - def _remove_variables_without_value_float(self, available_variables, time): + def _remove_variables_without_value_float(self, available_variables, times): """ Removes some of the previously identified available_variables if they effectively do not have a value_float field in InfluxDB. Parameters : available_variables ([{"name":(str), "label":(str), "unit":(str), "has_potential_target":(bool)}]) : an array of dictionnaries, each containing the Influx name of the corresponding variable out of the setup_info dict, the label to display in the Web GUI, its unit and a boolean value indicating if the variable has a potential target available. - time (int): the unix timestamp in seconds at which we want to get the available variables (for the target). + times ([int]): (only second value used) the unix timestamps in seconds of the range at which we want to get the available variables (for the value). Returns : [{"name":(str), "label":(str), "unit":(str), "has_potential_target":(bool)}] : an array of dictionnaries (updated), each containing the Influx name of the corresponding variable out of the setup_info dict, @@ -214,7 +235,7 @@ class InfluxDataGetter: for variable in available_variables: query = f""" import "influxdata/influxdb/schema" - schema.measurementFieldKeys(bucket: "{self._bucket}", measurement: "{variable["name"]}", start:0, stop: {time + 1}) + schema.measurementFieldKeys(bucket: "{self._bucket}", measurement: "{variable["name"]}", start:0, stop: {times[1] + 1}) |> yield(name: "res") """ records = self._db.query(query)[0].records @@ -223,14 +244,14 @@ class InfluxDataGetter: return res - def _set_variables_with_target(self, available_variables, time): + def _set_variables_with_target(self, available_variables, times): """ Determines if the previously identified available_variables have effectively a target or not (meaning it has a target_float field in Influx). Parameters : available_variables ([{"name":(str), "label":(str), "unit":(str), "has_potential_target":(bool)}]) : an array of dictionnaries, each containing the Influx name of the corresponding variable out of the setup_info dict, the label to display in the Web GUI, its unit and a boolean value indicating if the variable has a potential target available. - time (int): the unix timestamp in seconds at which we want to get the available variables (for the target). + times ([int]): (only second value used) the unix timestamps in seconds of the range at which we want to get the available variables (for the target). Returns : [{"name":(str), "label":(str), "unit":(str), "has_potential_target":(bool)}] : an array of dictionnaries, each containing the Influx name of the corresponding variable out of the setup_info dict, @@ -241,7 +262,7 @@ class InfluxDataGetter: if variable["has_potential_target"]: query = f""" import "influxdata/influxdb/schema" - schema.measurementFieldKeys(bucket: "{self._bucket}", measurement: "{variable["name"]}", start:0, stop: {time + 1}) + schema.measurementFieldKeys(bucket: "{self._bucket}", measurement: "{variable["name"]}", start:0, stop: {times[1] + 1}) |> yield(name: "res") """ records = self._db.query(query)[0].records diff --git a/influxgraph.py b/influxgraph.py index 0d7425f..4f80080 100644 --- a/influxgraph.py +++ b/influxgraph.py @@ -139,7 +139,7 @@ class InfluxGraph: time = [float(t) for t in time.split(',')] return dict(type='time', time= self.get_abs_time(time)) - def w_getvars(self, time): + def w_getvars(self, time, all=False): """ Gets the curve names available at a given point in time. Called when the route /getvars is reached. @@ -154,9 +154,10 @@ class InfluxGraph: """ time = [float(t) for t in time.split(',')] + start_time = int(self.get_abs_time(time)[0]) end_time = int(self.get_abs_time(time)[-1]) - blocks = self.influx_data_getter.get_available_variables_at_time(end_time) + blocks = self.influx_data_getter.get_available_variables_at_time([start_time, end_time], all) # updates the self.variables attribute to keep track of the available variables self.variables = [variable["name"] for block in blocks for variable in block["curves"]]