Compare commits
9 Commits
master
...
chartjs-mi
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).
|
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**
|
**Summary**
|
||||||
|
|
||||||
- [Documentation](#documentation)
|
- [Documentation](#documentation)
|
||||||
|
@ -41,9 +41,14 @@
|
|||||||
<script src="externalFiles/eventsource.js"></script>
|
<script src="externalFiles/eventsource.js"></script>
|
||||||
<!-- <script src="externalFiles/d3.min.js"></script> -->
|
<!-- <script src="externalFiles/d3.min.js"></script> -->
|
||||||
<script src="externalFiles/swiper-bundle.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/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/SEAWebClientLocalStorage.js"></script>
|
||||||
<script src="jsFiles/SEAWebClientResponsivity.js"></script>
|
<script src="jsFiles/SEAWebClientResponsivity.js"></script>
|
||||||
<script src="jsFiles/SEAWebClientSwiper.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 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 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 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 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 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
|
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)
|
* @returns If the minimun y-value of all the curves of the charts is greater than the maximum y-value (same)
|
||||||
*/
|
*/
|
||||||
function autoScale(chart) {
|
function autoScale(chart) {
|
||||||
axis = chart.options.scales.yAxes[0];
|
ay = chart.options.scales.y;
|
||||||
tax = chart.options.scales.xAxes[0].ticks;
|
ax = chart.options.scales.x;
|
||||||
datasets = chart.data.datasets;
|
datasets = chart.data.datasets;
|
||||||
let max = -1e99;
|
let max = -1e99;
|
||||||
let min = 1e99;
|
let min = 1e99;
|
||||||
@ -613,8 +615,8 @@ let graphs = (function (){
|
|||||||
for (let i = 0; i < datasets.length; i++){
|
for (let i = 0; i < datasets.length; i++){
|
||||||
ds = datasets[i];
|
ds = datasets[i];
|
||||||
if (ds.borderWidth == 1) continue;
|
if (ds.borderWidth == 1) continue;
|
||||||
let lmax = maxAr(ds.data, tax.min, tax.max);
|
let lmax = maxAr(ds.data, ax.min, ax.max);
|
||||||
let lmin = minAr(ds.data, tax.min, tax.max);
|
let lmin = minAr(ds.data, ax.min, ax.max);
|
||||||
if(lmax > max)
|
if(lmax > max)
|
||||||
max = lmax;
|
max = lmax;
|
||||||
if(lmin < min)
|
if(lmin < min)
|
||||||
@ -648,8 +650,8 @@ let graphs = (function (){
|
|||||||
}
|
}
|
||||||
extraMin = Math.min(min - ystep * 0.5, extraMin);
|
extraMin = Math.min(min - ystep * 0.5, extraMin);
|
||||||
extraMax = Math.max(max + ystep * 0.5, extraMax);
|
extraMax = Math.max(max + ystep * 0.5, extraMax);
|
||||||
if (min >= axis.ticks.min && axis.ticks.min >= extraMin &&
|
if (min >= ay.min && ay.min >= extraMin &&
|
||||||
max <= axis.ticks.max && axis.ticks.max <= extraMax) {
|
max <= ay.max && ay.max <= extraMax) {
|
||||||
//console.log('NOCHANGE', max, axis.ticks.max, extraMax)
|
//console.log('NOCHANGE', max, axis.ticks.max, extraMax)
|
||||||
return; // do not yet change
|
return; // do not yet change
|
||||||
}
|
}
|
||||||
@ -658,8 +660,8 @@ let graphs = (function (){
|
|||||||
min = extraMin;
|
min = extraMin;
|
||||||
max = extraMax;
|
max = extraMax;
|
||||||
}
|
}
|
||||||
axis.min = axis.ticks.min = min;
|
ay.min = min;
|
||||||
axis.max = axis.ticks.max = max;
|
ay.max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -701,9 +703,13 @@ let graphs = (function (){
|
|||||||
legendFlag = true;
|
legendFlag = true;
|
||||||
let trect = evt.target.getBoundingClientRect();
|
let trect = evt.target.getBoundingClientRect();
|
||||||
let X = evt.clientX - trect.x, Y = evt.clientY - trect.y;
|
let X = evt.clientX - trect.x, Y = evt.clientY - trect.y;
|
||||||
menuGraphicsPopup.hide();
|
if (X == cursorLinePos) {
|
||||||
showLegends(true, false);
|
cursorLine(null);
|
||||||
cursorLine(X);
|
} else {
|
||||||
|
menuGraphicsPopup.hide();
|
||||||
|
showLegends(true, false);
|
||||||
|
cursorLine(X);
|
||||||
|
}
|
||||||
setLiveMode();
|
setLiveMode();
|
||||||
update();
|
update();
|
||||||
for (let gr of graph_array.slice(0, ngraphs)) {
|
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
|
* 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)
|
* @returns When data is received (no need to autoScale and update as it is done in reloadData)
|
||||||
*/
|
*/
|
||||||
function checkReload(graph){
|
function checkReload(graph){
|
||||||
let tk = graph.chart.options.scales.xAxes[0].ticks;
|
let ax = graph.chart.options.scales.x;
|
||||||
let xmin = tk.min, xmax = tk.max;
|
let xmin = ax.min, xmax = ax.max;
|
||||||
/*
|
/*
|
||||||
if (xmax < now()-100000) { // was 100000 = 100sec
|
if (xmax < now()-100000) { // was 100000 = 100sec
|
||||||
if (liveMode) console.log('UPDATES OFF?')
|
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
|
* @param {*} graph - The graph Object on which the zoom callback has to be called
|
||||||
*/
|
*/
|
||||||
function zoomCallback(graph){
|
function zoomCallback(graph){
|
||||||
let tk, min, max;
|
let a, min, max;
|
||||||
if (currentZoomMode == 'y') {
|
if (currentZoomMode == 'y') {
|
||||||
tk = graph.chart.options.scales.yAxes[0].ticks;
|
a = graph.chart.options.scales.y;
|
||||||
} else {
|
} else {
|
||||||
tk = graph.chart.options.scales.xAxes[0].ticks;
|
a = graph.chart.options.scales.x;
|
||||||
}
|
}
|
||||||
min = tk.min;
|
min = a.min;
|
||||||
max = tk.max;
|
max = a.max;
|
||||||
if (!isTouchDevice) {
|
if (!isTouchDevice) {
|
||||||
/*
|
/*
|
||||||
if (prevGraph != graph) {
|
if (prevGraph != graph) {
|
||||||
@ -859,8 +876,8 @@ let graphs = (function (){
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
if (currentZoomMode == 'y') {
|
if (currentZoomMode == 'y') {
|
||||||
tk.min = min;
|
a.min = min;
|
||||||
tk.max = max;
|
a.max = max;
|
||||||
graph.setAutoScale(false);
|
graph.setAutoScale(false);
|
||||||
} else {
|
} else {
|
||||||
if (liveMode && max < lastTime) setLiveMode(false);
|
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
|
* @param {*} graph - The graph for which the function has to be called
|
||||||
*/
|
*/
|
||||||
function panCallback(graph){
|
function panCallback(graph){
|
||||||
let tk = graph.chart.options.scales.xAxes[0].ticks;
|
let ax = graph.chart.options.scales.x;
|
||||||
let xmin = tk.min, xmax = tk.max;
|
let xmin = ax.min, xmax = ax.max;
|
||||||
|
clickMode = 2; // mouse pan mode
|
||||||
if (liveMode && xmax < lastTime) setLiveMode(false);
|
if (liveMode && xmax < lastTime) setLiveMode(false);
|
||||||
setMinMax(xmin,xmax);
|
setMinMax(xmin,xmax);
|
||||||
update();
|
update();
|
||||||
@ -1363,119 +1381,138 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
|||||||
parent.appendChild(canvas);
|
parent.appendChild(canvas);
|
||||||
let ctx = canvas.getContext("2d");
|
let ctx = canvas.getContext("2d");
|
||||||
let self = this;
|
let self = this;
|
||||||
chart = new Chart(ctx, {
|
|
||||||
type: 'scatter',
|
let chart_options = {
|
||||||
options: {
|
responsive: true,
|
||||||
responsive: true,
|
maintainAspectRatio: false,
|
||||||
maintainAspectRatio: false,
|
animation:{duration:0},
|
||||||
animation:{duration:0},
|
scales: {
|
||||||
scales: {
|
y:{
|
||||||
yAxes: [{ticks: {
|
beginAtZero: false,
|
||||||
beginAtZero: false,
|
ticks:{
|
||||||
mirror: true,
|
mirror: true,
|
||||||
padding: -10,
|
padding: -10,
|
||||||
//workaround for proper number format
|
callback: function(label, index, labels) {
|
||||||
callback: function(label, index, labels) {
|
if(index == 0 || index == labels.length-1)
|
||||||
if(index == 0 || index == labels.length-1)
|
return "";
|
||||||
return "";
|
return strFormat(label);
|
||||||
return strFormat(label);
|
}
|
||||||
}
|
},
|
||||||
},
|
grid:{drawTicks:false},
|
||||||
gridLines:{drawTicks:false},
|
title: false, //Former scaleLabel
|
||||||
scaleLabel: false, // {display: true, labelString: y_label},
|
type: scaleType,
|
||||||
type: scaleType,
|
position: 'right',
|
||||||
position: 'right',
|
afterBuildTicks: function(axis) {
|
||||||
afterBuildTicks: function(axis, ticks) {
|
let ticks = axis.ticks
|
||||||
if (scaleType == "logarithmic" && ticks.length <= 4) {
|
if (scaleType == "logarithmic" && ticks.length <= 4) {
|
||||||
y1 = ticks[0];
|
y1 = ticks[0];
|
||||||
y0 = ticks.slice(-1)[0];
|
y0 = ticks.slice(-1)[0];
|
||||||
span = y1 - y0;
|
span = y1 - y0;
|
||||||
step = Math.abs(span * 0.3).toExponential(0);
|
step = Math.abs(span * 0.3).toExponential(0);
|
||||||
if (step[0] > '5') {
|
if (step[0] > '5') {
|
||||||
step = '5' + step.substr(1);
|
step = '5' + step.substr(1);
|
||||||
} else if (step[0] > '2') {
|
} else if (step[0] > '2') {
|
||||||
step = '2' + step.substr(1);
|
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;
|
|
||||||
}
|
}
|
||||||
},
|
step = Number.parseFloat(step);
|
||||||
afterBuildTicks: function(axis, ticks) {
|
ticks = [y1];
|
||||||
if (!ticks || ticks.length <= 2) return ticks;
|
for (let yt = Math.ceil(y1 / step) * step; yt > y0; yt -= step) {
|
||||||
first = ticks[0].value;
|
ticks.push(yt);
|
||||||
step = ticks[1].value - first;
|
}
|
||||||
offset = (first - axis._adapter.startOf(first, 'day')) % step;
|
ticks.push(y0);
|
||||||
let start = 0;
|
}
|
||||||
if (ticks[0].value - offset < axis.min) start = 1;
|
return ticks
|
||||||
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},
|
|
||||||
}],
|
|
||||||
},
|
},
|
||||||
tooltips: false,
|
x:{
|
||||||
legend: false,
|
title: false, // former scaleLabel
|
||||||
pan: {
|
type: 'time',
|
||||||
enabled: true,
|
time: {
|
||||||
mode: 'xy',
|
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'},
|
||||||
speed: 10,
|
},
|
||||||
threshold: 10,
|
ticks: {
|
||||||
onPan: function({chart}) { graphs.panCallback(chart.graph);},
|
padding: -20,
|
||||||
//onPanComplete: function({chart}){graphs.checkReload(chart.graph);redraw()},
|
// stepSize: 180000,
|
||||||
onPanComplete: function({chart}){graphs.updateAuto();},
|
autoSkip: true,
|
||||||
},
|
maxRotation: 0,
|
||||||
zoom: {
|
// callback not used, this is better done in afterBuildTicks
|
||||||
enabled: true,
|
},
|
||||||
drag: false,
|
afterBuildTicks: function(axis) {
|
||||||
mode: isTouchDevice ? 'xy': 'x',
|
let ticks = axis.ticks
|
||||||
speed: 0.1,
|
|
||||||
sensitivity: 1,
|
if (!ticks || ticks.length <= 2) return ticks;
|
||||||
onZoom: function({chart}) { graphs.zoomCallback(chart.graph);},
|
first = ticks[0].value;
|
||||||
//onZoomComplete: function({chart}){graphs.checkReload(chart.graph);redraw()},
|
step = ticks[1].value - first;
|
||||||
onZoomComplete: function({chart}){graphs.onZoomCompleteCallback()},
|
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')
|
//console.log('create legend')
|
||||||
let legend = document.createElement('div');
|
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
|
* @param {number} max - The maximum timestamp in milliseconds of the viewing window
|
||||||
*/
|
*/
|
||||||
function setMinMax(min, max){
|
function setMinMax(min, max){
|
||||||
let ax = chart.options.scales.xAxes[0];
|
let ax = chart.options.scales.x;
|
||||||
let ay = chart.options.scales.yAxes[0];
|
let ay = chart.options.scales.y;
|
||||||
// clamp X-span
|
// clamp X-span
|
||||||
let span = max - min;
|
let span = max - min;
|
||||||
let half = 0;
|
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;
|
mid = (chart.lastXmin + chart.lastXmax) * 0.5;
|
||||||
min = mid - half;
|
min = mid - half;
|
||||||
max = mid + half;
|
max = mid + half;
|
||||||
ay.ticks.min = chart.lastYmin;
|
ay.min = chart.lastYmin;
|
||||||
ay.ticks.max = chart.lastYmax;
|
ay.max = chart.lastYmax;
|
||||||
} else {
|
} else {
|
||||||
chart.lastXmin = min;
|
chart.lastXmin = min;
|
||||||
chart.lastXmax = max;
|
chart.lastXmax = max;
|
||||||
chart.lastYmin = ay.ticks.min;
|
chart.lastYmin = ay.min;
|
||||||
chart.lastYmax = ay.ticks.max;
|
chart.lastYmax = ay.max;
|
||||||
}
|
}
|
||||||
// custom algorithm for tick step
|
// custom algorithm for tick step
|
||||||
mainstep = 1000;
|
mainstep = 1000;
|
||||||
@ -1844,13 +1881,13 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
|||||||
}
|
}
|
||||||
mainstep *= info[1];
|
mainstep *= info[1];
|
||||||
}
|
}
|
||||||
ax.time.unit = info[0];
|
unit = info[0];
|
||||||
ax.time.stepSize = Math.round(step / mainstep);
|
rstep = Math.round(step / mainstep);
|
||||||
//ax.ticks.unit = ax.time.unit;
|
ax.time.unit = unit;
|
||||||
//ax.ticks.stepSize =ax.time.stepSize;
|
ax.ticks.stepSize = rstep;
|
||||||
//console.log('INFO', step, mainstep, info, ax, ax.time);
|
|
||||||
ax.ticks.max = max;
|
ax.max = max;
|
||||||
ax.ticks.min = min;
|
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
|
* Called when log button in the legend is clicked
|
||||||
*/
|
*/
|
||||||
function toggleAxesType(){
|
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{
|
}else{
|
||||||
linlog.innerHTML = "<strong>☒</strong> log";
|
linlog.innerHTML = "<strong>☒</strong> log";
|
||||||
}
|
}
|
||||||
chart.options.scales.yAxes[0].type = type;
|
chart.options.scales.y.type = type;
|
||||||
chart.options.animation.duration = 800;
|
chart.options.animation.duration = 800;
|
||||||
if (autoScaleFlag) graphs.autoScale(chart);
|
if (autoScaleFlag) graphs.autoScale(chart);
|
||||||
update();
|
update();
|
||||||
@ -1905,8 +1942,8 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
|||||||
let y = null;
|
let y = null;
|
||||||
for(let j = 0; j < chart.getDatasetMeta(i).data.length; j++){
|
for(let j = 0; j < chart.getDatasetMeta(i).data.length; j++){
|
||||||
let dp = chart.getDatasetMeta(i).data[j];
|
let dp = chart.getDatasetMeta(i).data[j];
|
||||||
if (dp._model.x >= x) break;
|
if (dp.x >= x) break; //_model does not exist anymore, properties are defined directly on elements
|
||||||
y = chart.data.datasets[i].data[dp._index].y;
|
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];
|
valueElm = legendvalues[chart.data.datasets[i].key];
|
||||||
if (labelMinWidth == 0) {
|
if (labelMinWidth == 0) {
|
||||||
@ -1996,6 +2033,7 @@ function updateCharts2(graph){
|
|||||||
console.log('graphs.doUpdates skipped');
|
console.log('graphs.doUpdates skipped');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//console.log('G', graph);
|
||||||
for(let key in graph){
|
for(let key in graph){
|
||||||
if (graph[key][0] != null) {
|
if (graph[key][0] != null) {
|
||||||
// there is at least ONE valid datapoint
|
// there is at least ONE valid datapoint
|
||||||
|
@ -76,7 +76,7 @@ new Settings()
|
|||||||
.treat("showConsole", "sc", to_bool, true)
|
.treat("showConsole", "sc", to_bool, true)
|
||||||
.treat("showOverview", "so", to_bool, true)
|
.treat("showOverview", "so", to_bool, true)
|
||||||
.treat("showGraphics", "sg", to_bool, true) // false)
|
.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("wideGraphs", "wg", to_bool, false) //used to toggle the size of the graphs part
|
||||||
.treat("showAsync", "sa", to_bool, false)
|
.treat("showAsync", "sa", to_bool, false)
|
||||||
|
|
||||||
|
@ -201,21 +201,15 @@ class InfluxGraph:
|
|||||||
now, = self.get_abs_time([0])
|
now, = self.get_abs_time([0])
|
||||||
|
|
||||||
result = self.influx_data_getter.poll_last_values(list(self.variables.keys()), self.lastvalues, now)
|
result = self.influx_data_getter.poll_last_values(list(self.variables.keys()), self.lastvalues, now)
|
||||||
for variable in self.lastvalues.keys():
|
for variable, values in list(result.items()):
|
||||||
if variable in result.keys():
|
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)
|
||||||
# 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:
|
||||||
while len(result[variable]) > 0:
|
values.pop(0)
|
||||||
if result[variable][0][0] <= self.lastvalues[variable][0]:
|
if values and values[-1][0] > tlast:
|
||||||
result[variable].pop(0)
|
self.lastvalues[variable] = values[-1]
|
||||||
else:
|
else:
|
||||||
break
|
del result[variable]
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
if int(now / 60) != int(self.end_query / 60):
|
if int(now / 60) != int(self.end_query / 60):
|
||||||
# Update unchanged values every plain minute
|
# Update unchanged values every plain minute
|
||||||
for var, (_, lastx) in self.lastvalues.items():
|
for var, (_, lastx) in self.lastvalues.items():
|
||||||
|
@ -82,7 +82,7 @@ def get_update(path=None):
|
|||||||
yield to_json_sse(msg)
|
yield to_json_sse(msg)
|
||||||
if messages:
|
if messages:
|
||||||
lastmsg = time.time()
|
lastmsg = time.time()
|
||||||
gevent.sleep(0.1)
|
gevent.sleep(1)
|
||||||
else:
|
else:
|
||||||
if time.time() > lastmsg + 30:
|
if time.time() > lastmsg + 30:
|
||||||
if not client.info():
|
if not client.info():
|
||||||
|
Reference in New Issue
Block a user