Working jumps (routes) + goToNow now reloads

This commit is contained in:
l_samenv
2024-08-14 08:27:59 +02:00
parent 58d44dd4d7
commit 5735fe9f01
7 changed files with 193 additions and 151 deletions

View File

@ -57,6 +57,6 @@
margin-top: 10px;
}
.jump-button-all, .jump-button-end{
.jump-button-all, .jump-button-time{
width: 200px;
}

View File

@ -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{
<div class="jump-container">
<div class="datetime-container">
<input type="date" class="input-date">
<input type="time" class="input-time" value="00:00">
<input type="time" class="input-time" value="23:59">
</div>
<div>
<button class="jump-button-end">Curves at the end of the day</button>
<!-- <span>Curves at the end of the day</span> -->
<button class="jump-button-time">Curves at the given time</button>
</div>
<div>
<button class="jump-button-all">All curves</button>
<!-- <span>All curves</span> -->
<button class="jump-button-all">All curves of the given day</button>
</div>
</div>
</div>

View File

@ -40,7 +40,6 @@ class DateIndicator extends HTMLElement{
}
showPopup(){
console.log("clicked");
this.datePopup.show();
}

View File

@ -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.

View File

@ -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() {
let msRightTimestamp = graphs.now();
let msLeftTimestamp = msRightTimestamp - 30*60*1000;
cursorLine(null);
if (!liveMode) {
setMinMax(graphs.now() - (currentMaxTime - currentMinTime), graphs.now());
}
setLiveMode(true);
updateAuto();
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 = "&#215;";
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();
});
// 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 = "&#215;";
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 = "<strong>&#9746;</strong> y-zoom";
// } else {
// zoomMode.innerHTML = "<strong>&#9744;</strong> 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,
}
})();

View File

@ -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")
{"" if all else "|> 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()))
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 = ast.literal_eval(record.get_value())
res.append(to_add)
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":
name = self._transform_setup_info_variable_name_to_influx(setup_info_variable_name)
if name not in added_names:
available_varirables.append(
{
"name":self._transform_setup_info_variable_name_to_influx(setup_info_variable_name),
"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

View File

@ -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"]]