infoscreens/lib/timeseries.js

169 lines
4.5 KiB
JavaScript
Raw Permalink Normal View History

2017-11-30 21:21:40 +01:00
import { parseValue } from './values';
2017-11-28 21:57:05 +01:00
export class Timeseries {
constructor(id, endDate, days = 7, slots = 24) {
this.id = id;
this.endDate = endDate;
this.startDate = this.getStartDate(endDate, days);
this.days = this.getDays();
this.slots = slots;
}
getStartDate(endDate, days) {
const startDate = new Date();
startDate.setDate(startDate.getDate() - days);
startDate.setHours(0, 0, 0);
return startDate;
}
getDays() {
return Math.ceil((this.endDate - this.startDate) / (1000 * 60 * 60 * 24));
}
prepareSlots(defaultValue = 0) {
const timeSlots = [];
for (let i = 0; i < this.days; i++) {
const daySlot = [];
for (let ii = 0; ii < this.slots; ii++) {
daySlot.push(defaultValue);
}
timeSlots.push(daySlot);
}
return timeSlots;
}
getDayLabels() {
const weekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const labels = [];
const day = new Date(this.startDate.getTime());
for (let i = 0; i < this.days; i++) {
labels.push(`${day.getDate()} ${weekDays[day.getDay()]}`);
day.setHours(day.getHours() + 24);
}
return labels;
}
getSlotLabels() {
const labels = [];
const day = new Date(this.startDate.getTime());
const pad = (time) => {
let val = `${time}`;
if (val.length === 1) {
val = `0${time}`;
}
return val;
};
day.setHours(0, 0, 0);
for (let i = 0; i < this.slots; i++) {
const from = day.getHours();
day.setHours(day.getHours() + (24 / this.slots));
let to = day.getHours();
if (to === 0) {
to = 24;
}
if (this.slots === 24) {
labels.push(`${pad(from)}`);
} else {
labels.push(`${pad(from)}-${pad(to)}`);
}
}
return labels;
}
flattenData(data, fromDay = 0, fromHour = 0) {
const flattened = [];
data.forEach((dayData, dayIdx) => {
if (dayIdx < fromDay) {
return;
}
dayData.forEach((point, hourIdx) => {
if (dayIdx === fromDay && hourIdx <= fromHour) {
return;
}
flattened.push(point);
});
});
return flattened;
}
mapData(data, options) {
const slots = this.prepareSlots(null);
data.forEach((point) => {
const pointDate = new Date(point.timestamp);
2017-11-30 21:21:40 +01:00
const pointValue = parseValue(point.value);
2017-11-28 21:57:05 +01:00
const daySlot = Math.floor((pointDate - this.startDate) / (1000 * 60 * 60 * 24));
const timeSlot = Math.floor(pointDate.getHours() / (24 / this.slots));
if (slots[daySlot][timeSlot] === null) {
slots[daySlot][timeSlot] = 0;
}
if (options.accumulate) {
slots[daySlot][timeSlot] += pointValue;
} else {
slots[daySlot][timeSlot] = Math.max(pointValue, slots[daySlot][timeSlot]);
}
});
// Deal with slots without data
let prevVal = 0;
slots.forEach((dayData, dayIdx) => {
let prevValIdx = 0;
const dayVals = dayData;
dayVals.forEach((val, idx) => {
if (val !== null) {
prevVal = val;
prevValIdx = idx;
return;
}
if (options.interpolate) {
const flattened = this.flattenData(slots, dayIdx, idx);
for (let nextSlot = 0; nextSlot < flattened.length; nextSlot++) {
if (flattened[nextSlot] === null) {
continue;
}
const nextVal = flattened[nextSlot];
if (nextVal === prevVal) {
dayVals[idx] = prevVal;
return;
}
let difference = 0;
if (nextVal > prevVal) {
difference = nextVal - prevVal;
} else {
difference = prevVal - nextVal;
}
const distance = nextSlot;
let totalDistance = (nextSlot + idx) - prevValIdx;
if (totalDistance < 1) {
totalDistance = 1;
}
dayVals[idx] = Math.max(((difference / totalDistance) * distance) + nextVal, 0);
return;
}
dayVals[idx] = 0;
return;
}
if (options.usePreviousValue) {
dayVals[idx] = prevVal;
return;
}
dayVals[idx] = 0;
});
});
return slots;
}
getUrl() {
return `http://openmct.cbrp3.c-base.org/telemetry/${this.id}?start=${this.startDate.getTime()}&end=${this.endDate.getTime()}`;
}
getData(options) {
return fetch(this.getUrl())
.then(data => data.json())
.then(data => this.mapData(data, options));
}
}
export default Timeseries;