Sampling data (aggregateWindow) for responsitivity + fixed old values not shown bug + readme
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,3 +5,5 @@
|
||||
__pycache__
|
||||
.idea
|
||||
log
|
||||
client/favicon_package_v0
|
||||
client/favicon.ico
|
32
README.md
32
README.md
@ -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 |
|
||||
|
@ -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.
|
||||
*
|
||||
|
30
influxdb.py
30
influxdb.py
@ -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):
|
||||
|
@ -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]
|
||||
|
Reference in New Issue
Block a user