Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
19fcc4d7de | |||
ce5d489b2d | |||
5f5e336370 | |||
ac542b1694 | |||
4e27d66d36 | |||
638f77a047 | |||
45e1957fd7 | |||
d5a5c6553e | |||
ab9f7b8ab0 |
21
README.md
21
README.md
@ -4,6 +4,27 @@ The WEB GUI client of SEA.
|
||||
|
||||
This repository contains the code of the server for the control and graphical parts, plus the client code (HTML, CSS, JS).
|
||||
|
||||
**IMPORTANT**
|
||||
|
||||
This branch is an attempt to migrate from ChartJS 2.9.4 to 4.4.4.
|
||||
TESTED ON SAFARI : with this new version, the application takes much less RAM, and does not crash at some point. The user can still experience some latencies, but it might be due to the presence of too many time axis labels + the fact that each graphs has its own (for the moment).
|
||||
|
||||
Here is a list of what has been done :
|
||||
- Uprgaded the ChartJS zoom plugin library, and changed the corresponding options in the chart configuration. The previous version was not working with the version 4.4.4 of ChartJS
|
||||
- Installing the date library Luxon and its adpater for ChartJS. This is needed because since version 3.x, time axes need these libraries.
|
||||
- Renamed or moved all needed parameters in the ChartJS configuration.
|
||||
- Changed all `xAxes` and `yAxes` references to `x` and `y`.
|
||||
- Adapted `afterBuildTicks` callbacks with the new signature (only `axis` is given)
|
||||
- Changed all references to `ticks.max|min` : these two properties are one step higher, at the level of the axis, not the ticks
|
||||
|
||||
Here is a list of what needs to be done :
|
||||
- Change the implementation of the callback located in `options.scales.x.ticks` at chart creation in the Chart class, so it considers that the label is a timestamp. Reference : https://www.chartjs.org/docs/latest/axes/labelling.html#creating-custom-tick-formats
|
||||
- Labels of the x axis are not displayed in the desired format, and do not rescale properly on zooming/dezooming. There can be too much labels, that make them rotate and invisible.
|
||||
- The cursor now also displays when the click ends, which is not the same behavior as before.
|
||||
- Make the zoom type toggle work again.
|
||||
- Make the zoom via touchpad less sensitive. The recent tests have shown that the zoom via gesture is very sensitive. Two things can be looked for : 1. see if there is the possibility to adapt the sensitivity of the zoom for the touchpad only or 2. update the library Hammer.js which is used by ChartJS to handle this type of gesture (even if the current version is 0.0.1 version later than the last one, this might be an explanation).
|
||||
- Display only one time axis.
|
||||
|
||||
**Summary**
|
||||
|
||||
- [Documentation](#documentation)
|
||||
|
@ -41,9 +41,14 @@
|
||||
<script src="externalFiles/eventsource.js"></script>
|
||||
<!-- <script src="externalFiles/d3.min.js"></script> -->
|
||||
<script src="externalFiles/swiper-bundle.min.js"></script>
|
||||
<script src="externalFiles/Chart.bundle.min.js"></script>
|
||||
<!-- <script src="externalFiles/Chart.bundle.min.js"></script> -->
|
||||
<script src="externalFiles/chart.umd.min.js"></script>
|
||||
<script src="externalFiles/luxon.min.js"></script>
|
||||
<script src="externalFiles/chartjs-adapter-luxon.umd.min.js"></script>
|
||||
|
||||
<script src="externalFiles/hammer.js"></script>
|
||||
<script src="externalFiles/chartjs-zoom.js"></script>
|
||||
<script src="externalFiles/chartjs-plugin-zoom.min.js"></script>
|
||||
<!-- <script src="externalFiles/chartjs-zoom.js"></script> -->
|
||||
<script src="jsFiles/SEAWebClientLocalStorage.js"></script>
|
||||
<script src="jsFiles/SEAWebClientResponsivity.js"></script>
|
||||
<script src="jsFiles/SEAWebClientSwiper.js"></script>
|
||||
|
1
client/externalFiles/chart.umd.js.map
Normal file
1
client/externalFiles/chart.umd.js.map
Normal file
File diff suppressed because one or more lines are too long
20
client/externalFiles/chart.umd.min.js
vendored
Normal file
20
client/externalFiles/chart.umd.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
client/externalFiles/chartjs-adapter-luxon.umd.min.js
vendored
Normal file
7
client/externalFiles/chartjs-adapter-luxon.umd.min.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* chartjs-adapter-luxon v1.3.1
|
||||
* https://www.chartjs.org
|
||||
* (c) 2023 chartjs-adapter-luxon Contributors
|
||||
* Released under the MIT license
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("chart.js"),require("luxon")):"function"==typeof define&&define.amd?define(["chart.js","luxon"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Chart,e.luxon)}(this,(function(e,t){"use strict";const n={datetime:t.DateTime.DATETIME_MED_WITH_SECONDS,millisecond:"h:mm:ss.SSS a",second:t.DateTime.TIME_WITH_SECONDS,minute:t.DateTime.TIME_SIMPLE,hour:{hour:"numeric"},day:{day:"numeric",month:"short"},week:"DD",month:{month:"short",year:"numeric"},quarter:"'Q'q - yyyy",year:{year:"numeric"}};e._adapters._date.override({_id:"luxon",_create:function(e){return t.DateTime.fromMillis(e,this.options)},init(e){this.options.locale||(this.options.locale=e.locale)},formats:function(){return n},parse:function(e,n){const i=this.options,r=typeof e;return null===e||"undefined"===r?null:("number"===r?e=this._create(e):"string"===r?e="string"==typeof n?t.DateTime.fromFormat(e,n,i):t.DateTime.fromISO(e,i):e instanceof Date?e=t.DateTime.fromJSDate(e,i):"object"!==r||e instanceof t.DateTime||(e=t.DateTime.fromObject(e,i)),e.isValid?e.valueOf():null)},format:function(e,t){const n=this._create(e);return"string"==typeof t?n.toFormat(t):n.toLocaleString(t)},add:function(e,t,n){const i={};return i[n]=t,this._create(e).plus(i).valueOf()},diff:function(e,t,n){return this._create(e).diff(this._create(t)).as(n).valueOf()},startOf:function(e,t,n){if("isoWeek"===t){n=Math.trunc(Math.min(Math.max(0,n),6));const t=this._create(e);return t.minus({days:(t.weekday-n+7)%7}).startOf("day").valueOf()}return t?this._create(e).startOf(t).valueOf():e},endOf:function(e,t){return this._create(e).endOf(t).valueOf()}})}));
|
7
client/externalFiles/chartjs-plugin-zoom.min.js
vendored
Normal file
7
client/externalFiles/chartjs-plugin-zoom.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
client/externalFiles/luxon.min.js
vendored
Normal file
1
client/externalFiles/luxon.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -395,9 +395,11 @@ function loadCurvesSettingsPopup(){
|
||||
let graphs = (function (){
|
||||
let dataset_to_graph_map = {}; // a dictionnary mapping a variable name to a two values array, containing its graph index and its position inside the graph
|
||||
let blocks, liveMode=true, top_vars=[], bottom_vars=[];
|
||||
let legendFlag = false, currentZoomMode = isTouchDevice ? 'xy' : 'x';
|
||||
let legendFlag = false;
|
||||
let currentZoomMode = isTouchDevice ? 'xy' : 'x';
|
||||
let prevTime = null, prevMin = null, prevMax = null, prevGraph = null; // zoom speed limitation
|
||||
let cursorLinePos = null; // the position of the cursor line (given by its x value)
|
||||
let clickMode = 0; // 1: mouse is down, 2: pan is active, 0: after mouse down
|
||||
|
||||
let type = 'linear'; // type of graphs axis to display
|
||||
|
||||
@ -601,8 +603,8 @@ let graphs = (function (){
|
||||
* @returns If the minimun y-value of all the curves of the charts is greater than the maximum y-value (same)
|
||||
*/
|
||||
function autoScale(chart) {
|
||||
axis = chart.options.scales.yAxes[0];
|
||||
tax = chart.options.scales.xAxes[0].ticks;
|
||||
ay = chart.options.scales.y;
|
||||
ax = chart.options.scales.x;
|
||||
datasets = chart.data.datasets;
|
||||
let max = -1e99;
|
||||
let min = 1e99;
|
||||
@ -613,8 +615,8 @@ let graphs = (function (){
|
||||
for (let i = 0; i < datasets.length; i++){
|
||||
ds = datasets[i];
|
||||
if (ds.borderWidth == 1) continue;
|
||||
let lmax = maxAr(ds.data, tax.min, tax.max);
|
||||
let lmin = minAr(ds.data, tax.min, tax.max);
|
||||
let lmax = maxAr(ds.data, ax.min, ax.max);
|
||||
let lmin = minAr(ds.data, ax.min, ax.max);
|
||||
if(lmax > max)
|
||||
max = lmax;
|
||||
if(lmin < min)
|
||||
@ -648,8 +650,8 @@ let graphs = (function (){
|
||||
}
|
||||
extraMin = Math.min(min - ystep * 0.5, extraMin);
|
||||
extraMax = Math.max(max + ystep * 0.5, extraMax);
|
||||
if (min >= axis.ticks.min && axis.ticks.min >= extraMin &&
|
||||
max <= axis.ticks.max && axis.ticks.max <= extraMax) {
|
||||
if (min >= ay.min && ay.min >= extraMin &&
|
||||
max <= ay.max && ay.max <= extraMax) {
|
||||
//console.log('NOCHANGE', max, axis.ticks.max, extraMax)
|
||||
return; // do not yet change
|
||||
}
|
||||
@ -658,8 +660,8 @@ let graphs = (function (){
|
||||
min = extraMin;
|
||||
max = extraMax;
|
||||
}
|
||||
axis.min = axis.ticks.min = min;
|
||||
axis.max = axis.ticks.max = max;
|
||||
ay.min = min;
|
||||
ay.max = max;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -701,9 +703,13 @@ let graphs = (function (){
|
||||
legendFlag = true;
|
||||
let trect = evt.target.getBoundingClientRect();
|
||||
let X = evt.clientX - trect.x, Y = evt.clientY - trect.y;
|
||||
menuGraphicsPopup.hide();
|
||||
showLegends(true, false);
|
||||
cursorLine(X);
|
||||
if (X == cursorLinePos) {
|
||||
cursorLine(null);
|
||||
} else {
|
||||
menuGraphicsPopup.hide();
|
||||
showLegends(true, false);
|
||||
cursorLine(X);
|
||||
}
|
||||
setLiveMode();
|
||||
update();
|
||||
for (let gr of graph_array.slice(0, ngraphs)) {
|
||||
@ -714,7 +720,18 @@ let graphs = (function (){
|
||||
}
|
||||
}
|
||||
}
|
||||
container.addEventListener('click', clickHandler)
|
||||
function mouseDown(evt) {
|
||||
clickMode = 1;
|
||||
}
|
||||
function mouseUp(evt) {
|
||||
if (clickMode == 1) { // mouse was down, but no pan happend
|
||||
clickHandler(evt);
|
||||
}
|
||||
clickMode = 0;
|
||||
}
|
||||
// container.addEventListener('click', clickHandler)
|
||||
container.addEventListener('mousedown', mouseDown);
|
||||
container.addEventListener('mouseup', mouseUp);
|
||||
|
||||
/**
|
||||
* Sets (overwrite) the data (curve) of the given variable
|
||||
@ -788,8 +805,8 @@ let graphs = (function (){
|
||||
* @returns When data is received (no need to autoScale and update as it is done in reloadData)
|
||||
*/
|
||||
function checkReload(graph){
|
||||
let tk = graph.chart.options.scales.xAxes[0].ticks;
|
||||
let xmin = tk.min, xmax = tk.max;
|
||||
let ax = graph.chart.options.scales.x;
|
||||
let xmin = ax.min, xmax = ax.max;
|
||||
/*
|
||||
if (xmax < now()-100000) { // was 100000 = 100sec
|
||||
if (liveMode) console.log('UPDATES OFF?')
|
||||
@ -828,14 +845,14 @@ let graphs = (function (){
|
||||
* @param {*} graph - The graph Object on which the zoom callback has to be called
|
||||
*/
|
||||
function zoomCallback(graph){
|
||||
let tk, min, max;
|
||||
let a, min, max;
|
||||
if (currentZoomMode == 'y') {
|
||||
tk = graph.chart.options.scales.yAxes[0].ticks;
|
||||
a = graph.chart.options.scales.y;
|
||||
} else {
|
||||
tk = graph.chart.options.scales.xAxes[0].ticks;
|
||||
a = graph.chart.options.scales.x;
|
||||
}
|
||||
min = tk.min;
|
||||
max = tk.max;
|
||||
min = a.min;
|
||||
max = a.max;
|
||||
if (!isTouchDevice) {
|
||||
/*
|
||||
if (prevGraph != graph) {
|
||||
@ -859,8 +876,8 @@ let graphs = (function (){
|
||||
*/
|
||||
}
|
||||
if (currentZoomMode == 'y') {
|
||||
tk.min = min;
|
||||
tk.max = max;
|
||||
a.min = min;
|
||||
a.max = max;
|
||||
graph.setAutoScale(false);
|
||||
} else {
|
||||
if (liveMode && max < lastTime) setLiveMode(false);
|
||||
@ -924,8 +941,9 @@ let graphs = (function (){
|
||||
* @param {*} graph - The graph for which the function has to be called
|
||||
*/
|
||||
function panCallback(graph){
|
||||
let tk = graph.chart.options.scales.xAxes[0].ticks;
|
||||
let xmin = tk.min, xmax = tk.max;
|
||||
let ax = graph.chart.options.scales.x;
|
||||
let xmin = ax.min, xmax = ax.max;
|
||||
clickMode = 2; // mouse pan mode
|
||||
if (liveMode && xmax < lastTime) setLiveMode(false);
|
||||
setMinMax(xmin,xmax);
|
||||
update();
|
||||
@ -1363,119 +1381,138 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
parent.appendChild(canvas);
|
||||
let ctx = canvas.getContext("2d");
|
||||
let self = this;
|
||||
chart = new Chart(ctx, {
|
||||
type: 'scatter',
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation:{duration:0},
|
||||
scales: {
|
||||
yAxes: [{ticks: {
|
||||
beginAtZero: false,
|
||||
mirror: true,
|
||||
padding: -10,
|
||||
//workaround for proper number format
|
||||
callback: function(label, index, labels) {
|
||||
if(index == 0 || index == labels.length-1)
|
||||
return "";
|
||||
return strFormat(label);
|
||||
}
|
||||
},
|
||||
gridLines:{drawTicks:false},
|
||||
scaleLabel: false, // {display: true, labelString: y_label},
|
||||
type: scaleType,
|
||||
position: 'right',
|
||||
afterBuildTicks: function(axis, ticks) {
|
||||
if (scaleType == "logarithmic" && ticks.length <= 4) {
|
||||
y1 = ticks[0];
|
||||
y0 = ticks.slice(-1)[0];
|
||||
span = y1 - y0;
|
||||
step = Math.abs(span * 0.3).toExponential(0);
|
||||
if (step[0] > '5') {
|
||||
step = '5' + step.substr(1);
|
||||
} else if (step[0] > '2') {
|
||||
step = '2' + step.substr(1);
|
||||
}
|
||||
step = Number.parseFloat(step);
|
||||
ticks = [y1];
|
||||
for (let yt = Math.ceil(y1 / step) * step; yt > y0; yt -= step) {
|
||||
ticks.push(yt);
|
||||
}
|
||||
ticks.push(y0);
|
||||
}
|
||||
return ticks
|
||||
},
|
||||
}],
|
||||
xAxes: [{
|
||||
scaleLabel: false,//{display: true, labelString: x_label},
|
||||
type: 'time',
|
||||
time: {
|
||||
displayFormats: {'millisecond': 'HH:mm:ss.SSS', 'second': 'HH:mm:ss', 'minute': 'HH:mm','hour': 'dd HH:mm', 'day': 'dd MMM DD', 'week': 'MMM DD', 'month': 'MMM DD'},
|
||||
},
|
||||
ticks: { padding: -20,
|
||||
callback: function(label, index, labels) {
|
||||
let l = labels.length - 1;
|
||||
if (index == 0 || index == l) return "";
|
||||
if (index == 1 || index == l - 1) {
|
||||
// skip first and / or last label, if too close to the end
|
||||
let minstep = 0.05 * (labels[l].value - labels[0].value);
|
||||
if (index == 1) {
|
||||
if (labels[1].value - labels[0].value < minstep) return "";
|
||||
} else {
|
||||
if (labels[l].value - labels[l-1].value < minstep) return "";
|
||||
}
|
||||
}
|
||||
hourofday = /\S+ (\d+:00)/.exec(label);
|
||||
if (hourofday && hourofday[1] != '00:00') {
|
||||
return hourofday[1];
|
||||
}
|
||||
return label;
|
||||
|
||||
let chart_options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation:{duration:0},
|
||||
scales: {
|
||||
y:{
|
||||
beginAtZero: false,
|
||||
ticks:{
|
||||
mirror: true,
|
||||
padding: -10,
|
||||
callback: function(label, index, labels) {
|
||||
if(index == 0 || index == labels.length-1)
|
||||
return "";
|
||||
return strFormat(label);
|
||||
}
|
||||
},
|
||||
grid:{drawTicks:false},
|
||||
title: false, //Former scaleLabel
|
||||
type: scaleType,
|
||||
position: 'right',
|
||||
afterBuildTicks: function(axis) {
|
||||
let ticks = axis.ticks
|
||||
if (scaleType == "logarithmic" && ticks.length <= 4) {
|
||||
y1 = ticks[0];
|
||||
y0 = ticks.slice(-1)[0];
|
||||
span = y1 - y0;
|
||||
step = Math.abs(span * 0.3).toExponential(0);
|
||||
if (step[0] > '5') {
|
||||
step = '5' + step.substr(1);
|
||||
} else if (step[0] > '2') {
|
||||
step = '2' + step.substr(1);
|
||||
}
|
||||
},
|
||||
afterBuildTicks: function(axis, ticks) {
|
||||
if (!ticks || ticks.length <= 2) return ticks;
|
||||
first = ticks[0].value;
|
||||
step = ticks[1].value - first;
|
||||
offset = (first - axis._adapter.startOf(first, 'day')) % step;
|
||||
let start = 0;
|
||||
if (ticks[0].value - offset < axis.min) start = 1;
|
||||
let v = axis.min;
|
||||
result = [{value: v, major: false}];
|
||||
for (tick of ticks.slice(start)) {
|
||||
v = tick.value - offset;
|
||||
result.push({value: v, major: false});
|
||||
}
|
||||
v += step;
|
||||
if (v < axis.max) result.push({value:v, major: false});
|
||||
result.push({value: axis.max, major: false});
|
||||
return result;
|
||||
},
|
||||
gridLines:{drawTicks:false},
|
||||
}],
|
||||
step = Number.parseFloat(step);
|
||||
ticks = [y1];
|
||||
for (let yt = Math.ceil(y1 / step) * step; yt > y0; yt -= step) {
|
||||
ticks.push(yt);
|
||||
}
|
||||
ticks.push(y0);
|
||||
}
|
||||
return ticks
|
||||
},
|
||||
},
|
||||
tooltips: false,
|
||||
legend: false,
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'xy',
|
||||
speed: 10,
|
||||
threshold: 10,
|
||||
onPan: function({chart}) { graphs.panCallback(chart.graph);},
|
||||
//onPanComplete: function({chart}){graphs.checkReload(chart.graph);redraw()},
|
||||
onPanComplete: function({chart}){graphs.updateAuto();},
|
||||
},
|
||||
zoom: {
|
||||
enabled: true,
|
||||
drag: false,
|
||||
mode: isTouchDevice ? 'xy': 'x',
|
||||
speed: 0.1,
|
||||
sensitivity: 1,
|
||||
onZoom: function({chart}) { graphs.zoomCallback(chart.graph);},
|
||||
//onZoomComplete: function({chart}){graphs.checkReload(chart.graph);redraw()},
|
||||
onZoomComplete: function({chart}){graphs.onZoomCompleteCallback()},
|
||||
x:{
|
||||
title: false, // former scaleLabel
|
||||
type: 'time',
|
||||
time: {
|
||||
displayFormats: {'millisecond': 'HH:mm:ss.SSS', 'second': 'HH:mm:ss', 'minute': 'HH:mm','hour': 'EEE d. HH:mm', 'day': 'EE d.', 'week': 'd. MMM yy', 'month': 'MMM yy'},
|
||||
},
|
||||
ticks: {
|
||||
padding: -20,
|
||||
// stepSize: 180000,
|
||||
autoSkip: true,
|
||||
maxRotation: 0,
|
||||
// callback not used, this is better done in afterBuildTicks
|
||||
},
|
||||
afterBuildTicks: function(axis) {
|
||||
let ticks = axis.ticks
|
||||
|
||||
if (!ticks || ticks.length <= 2) return ticks;
|
||||
first = ticks[0].value;
|
||||
step = ticks[1].value - first;
|
||||
offset = (first - axis._adapter.startOf(first, 'day')) % step;
|
||||
let result = [];
|
||||
let v = axis.min;
|
||||
for (tick of ticks) {
|
||||
v = tick.value - offset;
|
||||
if (v > axis.min + step / 2) {
|
||||
result.push({value: v, major: false});
|
||||
}
|
||||
}
|
||||
v += step;
|
||||
if (v < axis.max) result.push({value:v, major: false});
|
||||
axis.ticks = result;
|
||||
// return result;
|
||||
},
|
||||
beforeFit: function(axis) { // called after ticks are autoskipped
|
||||
prevday = '';
|
||||
for (tick of axis.ticks) {
|
||||
s = tick.label.split(' ');
|
||||
if (s.length == 3) { // format with day
|
||||
// show date only on first label of a day
|
||||
day = s.slice(0, 2).join(' ');
|
||||
if (day != prevday) {
|
||||
prevday = day;
|
||||
} else {
|
||||
tick.label = s[2]; // time
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
grid:{drawTicks:false},
|
||||
}
|
||||
},
|
||||
plugins:{
|
||||
tooltip: false,
|
||||
legend: false,
|
||||
zoom:{
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'xy',
|
||||
speed: 10,
|
||||
threshold: 10,
|
||||
onPan: function({chart}) { graphs.panCallback(chart.graph);},
|
||||
onPanComplete: function({chart}){graphs.updateAuto();},
|
||||
},
|
||||
zoom: {
|
||||
wheel:{
|
||||
enabled: true
|
||||
},
|
||||
pinch:{
|
||||
enabled: true
|
||||
},
|
||||
mode: isTouchDevice ? 'xy': 'x',
|
||||
speed: 0.1,
|
||||
sensitivity: 1,
|
||||
onZoom: function({chart}) { graphs.zoomCallback(chart.graph);},
|
||||
onZoomComplete: function({chart}){graphs.onZoomCompleteCallback()},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (gindex != 0) {
|
||||
// show time labels only on first chart
|
||||
chart_options.scales.x.ticks.callback = function () { return ' '; }
|
||||
}
|
||||
|
||||
chart = new Chart(ctx, {type: 'scatter', options: chart_options})
|
||||
|
||||
//console.log('create legend')
|
||||
let legend = document.createElement('div');
|
||||
@ -1802,8 +1839,8 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
* @param {number} max - The maximum timestamp in milliseconds of the viewing window
|
||||
*/
|
||||
function setMinMax(min, max){
|
||||
let ax = chart.options.scales.xAxes[0];
|
||||
let ay = chart.options.scales.yAxes[0];
|
||||
let ax = chart.options.scales.x;
|
||||
let ay = chart.options.scales.y;
|
||||
// clamp X-span
|
||||
let span = max - min;
|
||||
let half = 0;
|
||||
@ -1818,13 +1855,13 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
mid = (chart.lastXmin + chart.lastXmax) * 0.5;
|
||||
min = mid - half;
|
||||
max = mid + half;
|
||||
ay.ticks.min = chart.lastYmin;
|
||||
ay.ticks.max = chart.lastYmax;
|
||||
ay.min = chart.lastYmin;
|
||||
ay.max = chart.lastYmax;
|
||||
} else {
|
||||
chart.lastXmin = min;
|
||||
chart.lastXmax = max;
|
||||
chart.lastYmin = ay.ticks.min;
|
||||
chart.lastYmax = ay.ticks.max;
|
||||
chart.lastYmin = ay.min;
|
||||
chart.lastYmax = ay.max;
|
||||
}
|
||||
// custom algorithm for tick step
|
||||
mainstep = 1000;
|
||||
@ -1844,13 +1881,13 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
}
|
||||
mainstep *= info[1];
|
||||
}
|
||||
ax.time.unit = info[0];
|
||||
ax.time.stepSize = Math.round(step / mainstep);
|
||||
//ax.ticks.unit = ax.time.unit;
|
||||
//ax.ticks.stepSize =ax.time.stepSize;
|
||||
//console.log('INFO', step, mainstep, info, ax, ax.time);
|
||||
ax.ticks.max = max;
|
||||
ax.ticks.min = min;
|
||||
unit = info[0];
|
||||
rstep = Math.round(step / mainstep);
|
||||
ax.time.unit = unit;
|
||||
ax.ticks.stepSize = rstep;
|
||||
|
||||
ax.max = max;
|
||||
ax.min = min;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1873,7 +1910,7 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
* Called when log button in the legend is clicked
|
||||
*/
|
||||
function toggleAxesType(){
|
||||
setAxesType((chart.options.scales.yAxes[0].type=== 'linear') ? 'logarithmic' : 'linear');
|
||||
setAxesType((chart.options.scales.y.type=== 'linear') ? 'logarithmic' : 'linear');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1887,7 +1924,7 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
}else{
|
||||
linlog.innerHTML = "<strong>☒</strong> log";
|
||||
}
|
||||
chart.options.scales.yAxes[0].type = type;
|
||||
chart.options.scales.y.type = type;
|
||||
chart.options.animation.duration = 800;
|
||||
if (autoScaleFlag) graphs.autoScale(chart);
|
||||
update();
|
||||
@ -1905,8 +1942,8 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
let y = null;
|
||||
for(let j = 0; j < chart.getDatasetMeta(i).data.length; j++){
|
||||
let dp = chart.getDatasetMeta(i).data[j];
|
||||
if (dp._model.x >= x) break;
|
||||
y = chart.data.datasets[i].data[dp._index].y;
|
||||
if (dp.x >= x) break; //_model does not exist anymore, properties are defined directly on elements
|
||||
y = chart.data.datasets[i].data[dp.$context.index].y; // $context not mentionned in ChartJS doc, seen with console.log
|
||||
}
|
||||
valueElm = legendvalues[chart.data.datasets[i].key];
|
||||
if (labelMinWidth == 0) {
|
||||
@ -1996,6 +2033,7 @@ function updateCharts2(graph){
|
||||
console.log('graphs.doUpdates skipped');
|
||||
return;
|
||||
}
|
||||
//console.log('G', graph);
|
||||
for(let key in graph){
|
||||
if (graph[key][0] != null) {
|
||||
// there is at least ONE valid datapoint
|
||||
|
@ -76,7 +76,7 @@ new Settings()
|
||||
.treat("showConsole", "sc", to_bool, true)
|
||||
.treat("showOverview", "so", to_bool, true)
|
||||
.treat("showGraphics", "sg", to_bool, true) // false)
|
||||
.treat("hideRightPart", "hr", to_bool, false) //used to completely disable the right part
|
||||
.treat("hideRightPart", "hr", to_bool, true) //used to completely disable the right part
|
||||
.treat("wideGraphs", "wg", to_bool, false) //used to toggle the size of the graphs part
|
||||
.treat("showAsync", "sa", to_bool, false)
|
||||
|
||||
|
@ -190,7 +190,7 @@ class InfluxGraph:
|
||||
"""
|
||||
Polls the last known values for all the available variables, and returns only those whose polled values are more recent than the most recent displayed one.
|
||||
Every plain minute, all the variables are returned with a point having their last known value at the current timestamp to synchronize all the curves on the GUI.
|
||||
|
||||
|
||||
Returns :
|
||||
{"type":"graph-update", "time":(int), "graph":{(str):[[(int),(float)]]}} | None : a dictionnary with its "graph-update" type
|
||||
(so it can be processed by the client), and a "graph" dictionnary with the variable names as key, and an array of points, which are an array containing the timestamp
|
||||
@ -201,21 +201,15 @@ class InfluxGraph:
|
||||
now, = self.get_abs_time([0])
|
||||
|
||||
result = self.influx_data_getter.poll_last_values(list(self.variables.keys()), self.lastvalues, now)
|
||||
for variable in self.lastvalues.keys():
|
||||
if variable in result.keys():
|
||||
|
||||
# removes points older than the last known point (queries are in seconds and might return points already displayed)
|
||||
while len(result[variable]) > 0:
|
||||
if result[variable][0][0] <= self.lastvalues[variable][0]:
|
||||
result[variable].pop(0)
|
||||
else:
|
||||
break
|
||||
|
||||
if len(result[variable]) > 0 and result[variable][-1][0] > self.lastvalues[variable][0]:
|
||||
self.lastvalues[variable] = (result[variable][-1][0], result[variable][-1][1])
|
||||
else:
|
||||
del result[variable]
|
||||
|
||||
for variable, values in list(result.items()):
|
||||
tlast = self.lastvalues.get(variable, (0,))[0]
|
||||
# removes points older than the last known point (queries are in seconds and might return points already displayed)
|
||||
while values and values[0][0] <= tlast:
|
||||
values.pop(0)
|
||||
if values and values[-1][0] > tlast:
|
||||
self.lastvalues[variable] = values[-1]
|
||||
else:
|
||||
del result[variable]
|
||||
if int(now / 60) != int(self.end_query / 60):
|
||||
# Update unchanged values every plain minute
|
||||
for var, (_, lastx) in self.lastvalues.items():
|
||||
@ -224,4 +218,4 @@ class InfluxGraph:
|
||||
self.end_query = now
|
||||
if len(result) > 0:
|
||||
return dict(type='graph-update', time=now, graph=result)
|
||||
return None
|
||||
return None
|
||||
|
Reference in New Issue
Block a user