Sampling data (aggregateWindow) for responsitivity + fixed old values not shown bug + readme

This commit is contained in:
l_samenv
2024-07-26 10:00:28 +02:00
parent 676e7e4f10
commit dd422e89d8
5 changed files with 87 additions and 19 deletions

2
.gitignore vendored
View File

@ -5,3 +5,5 @@
__pycache__
.idea
log
client/favicon_package_v0
client/favicon.ico

View File

@ -37,12 +37,42 @@ The current server exposes some routes for the graphical part and the control pa
The way the main page is served (where you can select the instrument you want to interact with) has not been decided yet, even if its code is in this repository.
### Process
### Initialization
For a selected instrument, there is a single HTML file that holds the application : the `SEAWebClient.html` file, which is served as the main route. It includes all the JS files needed, meaning all the JS files located in the `client/jsFiles` folder excepted `SeaWebClientStart.js`, plus the external librairies (`client/externalFiles/`).
The entry point is the `SEAWebClientMain.js`, which has a `window.onload` function and which initiates all the content of the page, plus the responsitivity.
### About updating graphics
- When the server is pushing data, the newly received data is appened to the current curves (on livemode).
- When zooming in the x direction, when the zoom is complete (for e.g. meaning that there are no longer enough mouse wheel step in a certain range of time), then the resolution is computed, the client asks for the data within the new viewing window with the given resolution, and then sets (overwrites) the data for the curves.
### About livemode
#### Using it
Every plain minute, all the curves are synchronized. For the curve that have not receive any new data, their last known point is retreived at the "now" date.
#### Setting it
**In the context of the client**
An user enters the livemode when :
- arriving on the app,
- clicking on the "Go to now" button
An user leaves the livemode when :
- zooming (more precisely, when the zoom is complete),
- moving the visualisation window, if the maximum time on the x axis is older than the last received (displayed) value of one of the curves in all the graphs
**In the context of the server**
**Known bugs**
Even if the livemode is set to true on the client side, the server can set it to HISTORICAL because of (not fully investigated yet)
### External libraries
| Name | Version | Website |

View File

@ -216,6 +216,8 @@ let graphs = (function (){
let minTime, maxTime; // the queried time range
let lastTime = 0; // time of most recent data point
let resolution = undefined;
let container = document.createElement('div');
container.classList.add("graphs-container");
@ -336,7 +338,11 @@ let graphs = (function (){
//let varlist = top_vars.concat(bottom_vars);
varlist = vars_array[gindex];
let graph_elm = graph_elm_array[gindex];
AJAX("http://" + hostPort + "/graph?time=" + minTime/1000 + "," + maxTime/1000 + "&variables=" + varlist + "&id=" + clientID).getJSON().then(function(data){
timeDeltaAxis = maxTime - minTime
setResolution(timeDeltaAxis)
AJAX("http://" + hostPort + "/graph?time=" + minTime/1000 + "," + maxTime/1000 + "&variables=" + varlist + "&interval=" + resolution + "&id=" + clientID).getJSON().then(function(data){
//console.log('Graph', block, data)
let graph = new Graph(gindex, graph_elm, "Time", block.unit, block.tag, type);
@ -492,7 +498,7 @@ let graphs = (function (){
/**
* Function called when the graph container is clicked
* Shows the legend, disable livemode, put a cursor then applies the changes
* Shows the legend, shows the go to now button, put a cursor then applies the changes
* If the click is located on the legend, brings it to the front
* @param {*} evt - The JS event triggered by the click
*/
@ -556,7 +562,11 @@ let graphs = (function (){
}else{
max = max/1000;
}
AJAX("http://" + hostPort + "/graph?time=" + min + ","+max+"&variables=" + variables() + "&id=" + clientID).getJSON().then(function(data){
timeDelta = currentMaxTime - currentMinTime
setResolution(timeDelta)
AJAX("http://" + hostPort + "/graph?time=" + min + ","+max+"&variables=" + variables() + "&interval=" + resolution + "&id=" + clientID).getJSON().then(function(data){
for(let key in data.graph){
let pdata = [];
for(let e of data.graph[key]){
@ -673,6 +683,14 @@ let graphs = (function (){
update();
}
/**
* Sets the resolution of the viewing window in seconds
* @param {*} timeDelta - The difference between the maximum time and the minimum time of the window
*/
function setResolution(timeDelta){
resolution = Math.ceil((timeDelta / container.getBoundingClientRect().width)/1000)
}
/**
* The function called when the viewing window is moved by the mouse.
*

View File

@ -72,13 +72,14 @@ class InfluxDataGetter:
return res
def get_curves_in_timerange(self, variables, time):
def get_curves_in_timerange(self, variables, time, interval = None):
"""
Gets the curves for the given variables within a timerange.
Parameters :
variables ([(str)]) : an array of variable names (Influx) to get the curves for
time ([int]) : the timerange we want the values in. It consists of two values which are Unix timestamps in seconds, first included, second excluded.
interval (int) : the interval (resolution) of the values to get (in nanoseconds)
Returns :
{(str):[[(int), (float)]]} : a dictionnary of curves. The key is the name of the influx variable, and the value is an array of pairs (also arrays), the first value being the Unix timestamp in second (x), the seconds being the value (y).
@ -91,8 +92,8 @@ class InfluxDataGetter:
if variable_name_for_query.endswith(".target"):
variable_name_for_query = variable_name_for_query[:-len(".target")]
is_target = True
curve = self._get_curve(variable_name_for_query, is_target, time)
curve = self._get_curve(variable_name_for_query, is_target, time, interval)
if len(curve) > 0:
res[variable] = curve
@ -300,7 +301,7 @@ class InfluxDataGetter:
"color":""
})
def _get_curve(self, variable, is_target, time):
def _get_curve(self, variable, is_target, time, interval=None):
"""
Gets the points (curve) within a timerange for the given variable.
@ -308,7 +309,8 @@ class InfluxDataGetter:
variable (str) : the name (Influx) of the variable we want the values of.
is_target (bool) : tells if the given variable is a target, or not (if variable is "nicos/se_t_chip.target", then is_target has to be set to True)
time ([(int)]) : the timerange we want the values in. It consists of two values which are Unix timestamps in seconds, first included, second excluded.
interval (int) : the interval (resolution) of the values to get (in nanoseconds)
Returns :
[[(int), (float)]] : an array of pairs (also arrays), the first value being the Unix timestamp in second (x), the seconds being the value (y)
"""
@ -318,6 +320,7 @@ class InfluxDataGetter:
|> range(start: {time[0]}, stop: {time[1] + 1})
|> filter(fn : (r) => r._measurement == "{variable}")
|> filter(fn : (r) => r._field == "{"target_float" if is_target else "value_float"}")
{"|> aggregateWindow(every: duration(v:"+str(interval)+"), fn: last, createEmpty:false)" if interval else ""}
|> keep(columns: ["_time","_value"])
|> yield(name: "res")
"""
@ -358,13 +361,16 @@ class InfluxDataGetter:
|> keep(columns: ["_value"])
|> yield(name: "res")
"""
record = self._db.query(query)[0].records[0]
value = record.get_value()
try:
value = PrettyFloat(value)
except:
value = None
curve.insert(0, [time[0], value])
tables = self._db.query(query)
for table in tables:
for record in table.records:
value = record.get_value()
try:
value = PrettyFloat(value)
except:
value = None
curve.insert(0, [time[0], value])
return curve
def _get_last_values(self, variable, is_target, time):

View File

@ -34,6 +34,16 @@ class InfluxGraph:
self.lastvalues = {}
self.variables = []
def seconds_to_nano(self, seconds):
"""
Converts seconds to nanoseconds
Parameters:
seconds (int)
Returns :
int
"""
return seconds*1000000000
def get_abs_time(self, times):
"""
Gets the absolute times for the given pontential relative times. If the given timestamps are less than one year, then the value is relative
@ -82,7 +92,7 @@ class InfluxGraph:
c.append((endtime, lastx))
self.lastvalues[var] = (endtime, lastx)
def w_graph(self, variables, time="-1800,0"):
def w_graph(self, variables, time="-1800,0", interval=None):
"""
Gets the curves given by variables in the time range "time"
Called when the route /graph is reached.
@ -90,6 +100,7 @@ class InfluxGraph:
Parameters :
variables (str) : a comma separataed value string of variable names (influx names) to retrieve
time (str) : a commma separated value string (range) of seconds. They are treated as relative from now if they are lesser than one year.
interval (str) : the interval (resolution) of the values to get (string in seconds)
Returns :
{"type":"graph-draw", "reduced":(bool), "graph":{(str):[[(int),(float)]]}} : a dictionnary with its "graph-draw" type (so it can be processed by the client), a "reduced" value
@ -104,8 +115,9 @@ class InfluxGraph:
self.livemode = self.ACTUAL if end >= now else self.HISTORICAL
logging.info('LIVE %g %g %d %d', end, now, end >= now, self.livemode)
result = self.influx_data_getter.get_curves_in_timerange(self.variables, self.time)
if interval : interval = self.seconds_to_nano(int(interval))
result = self.influx_data_getter.get_curves_in_timerange(self.variables, self.time, interval)
self.strip_future(result)
self.complete_to_end(result, end)
self.time[0] = self.time[1]