infoscreens/elements/linechart.js

186 lines
4.7 KiB
JavaScript
Raw Permalink Normal View History

2017-11-30 20:18:49 +01:00
import { withComponent, props } from 'skatejs';
import { colors } from '../lib/colors';
import { parseValue } from '../lib/values';
2017-12-04 18:43:12 +01:00
import { subscribe } from '../lib/liveupdate';
import injectCss from '../lib/plotly-shadowdom';
2017-11-30 20:18:49 +01:00
const Component = withComponent();
2017-11-30 21:22:07 +01:00
const legendLabel = (datasets, idx) => {
const parts = datasets.map(set => set.split('.'));
2017-11-30 21:25:56 +01:00
const uniques = parts[idx].filter((part, index) => {
2017-11-30 21:22:07 +01:00
for (let i = 0; i < parts.length; i++) {
2017-11-30 21:25:56 +01:00
if (parts[i][index] !== part) {
2017-11-30 21:22:07 +01:00
return true;
}
}
return false;
});
return uniques.join('.');
};
2017-11-30 20:18:49 +01:00
class LineChart extends Component {
static props = {
timeseries: props.string,
days: props.number,
data: props.array,
shape: props.string,
2017-11-30 21:22:07 +01:00
width: props.number,
height: props.number,
references: props.string,
2018-12-24 16:04:02 +01:00
source: props.string,
2017-11-30 20:18:49 +01:00
};
connected() {
if (!this.timeseries || !this.timeseries.length) {
return;
}
2018-12-24 16:04:02 +01:00
if (!this.source) {
this.source = 'http://openmct.cbrp3.c-base.org';
}
2017-11-30 21:22:07 +01:00
const days = this.days || 1;
2017-11-30 20:18:49 +01:00
const endDate = new Date();
2017-11-30 21:22:07 +01:00
const startDate = new Date();
2017-12-04 18:43:12 +01:00
const series = this.timeseries.split(' ');
2017-11-30 21:22:07 +01:00
startDate.setDate(startDate.getDate() - days);
2017-12-04 18:43:12 +01:00
const promises = series.map(dataset => this.fetch(dataset, startDate, endDate));
2017-11-30 20:18:49 +01:00
Promise.all(promises)
.then((res) => {
this.data = res;
2017-12-04 18:43:12 +01:00
this.subscribe(series);
2017-11-30 20:18:49 +01:00
});
}
2017-11-30 21:22:07 +01:00
fetch(timeseries, start, end) {
2018-12-24 16:04:02 +01:00
const url = `${this.source}/telemetry/${timeseries}?start=${start.getTime()}&end=${end.getTime()}`;
2017-11-30 21:22:07 +01:00
return fetch(url)
2017-11-30 21:25:56 +01:00
.then(data => data.json());
2017-11-30 21:22:07 +01:00
}
2017-12-04 18:43:12 +01:00
subscribe(timeseries) {
subscribe(timeseries, (data) => {
if (!this.el) {
return;
}
const dataIdx = timeseries.indexOf(data.id);
if (dataIdx === -1) {
return;
}
Plotly.extendTraces(this.el, {
y: [[parseValue(data.value)]],
x: [[new Date(data.timestamp)]],
}, [dataIdx]);
});
}
2017-11-30 20:18:49 +01:00
renderer(renderRoot, render) {
const root = renderRoot;
2017-12-04 18:43:12 +01:00
this.el = null;
2017-11-30 20:18:49 +01:00
while (root.firstChild) {
root.removeChild(root.firstChild);
}
root.appendChild(render());
injectCss(root);
2017-11-30 20:18:49 +01:00
}
render({ data, shape, width, height, references }) {
2017-11-30 20:18:49 +01:00
const el = document.createElement('div');
2017-11-30 21:22:07 +01:00
if (!data || !data.length) {
2017-11-30 20:18:49 +01:00
// No data yet
return el;
}
const lineShape = shape || 'spline';
2017-11-30 21:25:56 +01:00
const graphWidth = Math.floor((window.innerWidth / 100) * (width || 40));
const graphHeight = Math.floor((window.innerHeight / 100) * (height || 50));
2017-11-30 21:22:07 +01:00
const datasets = this.timeseries.split(' ');
2017-11-30 21:25:56 +01:00
const showLegend = (datasets.length > 1);
2017-11-30 20:18:49 +01:00
const graphData = data.map((values, idx) => {
const res = {
2017-11-30 21:22:07 +01:00
y: values.map(point => parseValue(point.value)),
x: values.map(point => new Date(point.timestamp)),
2017-11-30 20:18:49 +01:00
type: 'scatter',
mode: 'lines',
2017-11-30 21:22:07 +01:00
name: legendLabel(datasets, idx),
2017-11-30 20:18:49 +01:00
line: {
shape: lineShape,
},
};
if (colors[idx]) {
res.line.color = colors[idx];
}
return res;
});
2018-03-07 16:09:51 +01:00
const shapes = references.split(' ').filter(spec => !Number.isNaN(parseFloat(spec))).map((spec, idx) => {
let colorIdx = data.length + idx;
if (colorIdx > colors.length) {
// Start cycle again
colorIdx = idx;
}
const res = {
type: 'line',
xref: 'paper', // x relative to [0,1] "paper" axis
yref: 'y',
x0: 0,
x1: 1,
y0: parseFloat(spec),
y1: parseFloat(spec),
line: {
color: colors[colorIdx],
width: 1,
},
};
return res;
});
2017-11-30 20:18:49 +01:00
const layout = {
2017-11-30 21:22:07 +01:00
autosize: false,
width: graphWidth,
height: graphHeight,
2017-11-30 20:18:49 +01:00
yaxis: {
tickfont: {
family: 'Source Code Pro',
},
tickcolor: '#204a87',
gridcolor: '#204a87',
},
xaxis: {
tickfont: {
family: 'Source Code Pro',
},
tickcolor: '#204a87',
gridcolor: '#204a87',
},
font: {
family: ['Source Code Pro', 'sans-serif'],
size: 16,
color: '#fff',
outlineColor: 'transparent',
},
legend: {
orientation: 'h',
font: {
family: 'Source Code Pro',
},
2017-11-30 21:22:07 +01:00
x: 0,
y: 1,
bgcolor: 'rgba(0, 0, 0, 0.6)',
},
margin: {
t: 0,
r: 0,
2017-11-30 20:18:49 +01:00
},
2017-11-30 21:22:07 +01:00
showlegend: showLegend,
shapes,
2017-11-30 20:18:49 +01:00
paper_bgcolor: 'transparent',
plot_bgcolor: 'transparent',
};
Plotly.newPlot(el, graphData, layout, {
staticPlot: true,
});
2017-12-04 18:43:12 +01:00
this.el = el;
2017-11-30 20:18:49 +01:00
return el;
}
}
customElements.define('cbase-linechart', LineChart);