attempting to fix date issue

This commit is contained in:
Daniel Ledda
2020-11-18 18:38:43 +01:00
parent 3d73e23777
commit bd7d76940d
7 changed files with 290 additions and 707 deletions

2
webapp/dist/main.js vendored

File diff suppressed because one or more lines are too long

837
webapp/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"dev": "webpack-dev-server"
"dev": "webpack serve"
},
"author": "",
"license": "ISC",
@@ -14,17 +14,17 @@
"@webpack-cli/init": "^1.0.3",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"css-loader": "^5.0.1",
"prettier": "^2.1.2",
"style-loader": "^2.0.0",
"terser-webpack-plugin": "^5.0.3",
"ts-loader": "^8.0.10",
"typescript": "^4.0.5",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0",
"webpack-dev-server": "^3.11.0",
"prettier": "^2.1.2"
"webpack-cli": "^4.2.0"
},
"dependencies": {
"@types/chart.js": "^2.9.27",
"chart.js": "^2.9.4"
"chart.js": "^2.9.4",
"webpack-dev-server": "^3.11.0"
}
}

View File

@@ -1,5 +1,5 @@
//@ts-ignore
import Chart, {ChartPoint} from "chart.js/dist/Chart.bundle.min";
import Chart from "chart.js/dist/Chart.bundle.min";
import type {ChartPoint} from "chart.js";
import {generateClimateChartConfig} from "./climateChartConfig";
interface Snapshot {
@@ -14,38 +14,31 @@ interface SnapshotRecords {
snapshots: Snapshot[]
}
interface ClimatePoint {
x: string;
y: number;
}
class ClimateChart {
private chart: Chart | null;
private latestSnapshot: Snapshot | null;
private onLoadedCallback: () => void = () => {};
private onErrorCallback: (e: Error) => void = () => {};
constructor(
private readonly rootUrl: string,
private readonly canvasId: string,
private readonly minutesDisplayed: number = 60
) {
if (minutesDisplayed > 0 && Math.floor(minutesDisplayed) == minutesDisplayed) {
this.minutesDisplayed = minutesDisplayed;
} else {
throw new Error(`invalid minutes passed to display in chart: ${minutesDisplayed}`);
private errorLog: string = "";
private readonly rootUrl: string;
private readonly canvasId: string;
private readonly minutesDisplayed: number = 60;
constructor(rootUrl: string, canvasId: string, minutesDisplayed: number) {
this.minutesDisplayed = Math.floor(minutesDisplayed);
if (minutesDisplayed < 0 || Math.floor(minutesDisplayed) !== minutesDisplayed) {
console.warn(`Minutes passed were ${ minutesDisplayed }, which is invalid. ${ this.minutesDisplayed } minutes are being shown instead.`);
}
this.initChart().catch((e) => {this.onErrorCallback(e);});
}
onLoaded(callback: () => void) {
this.onLoadedCallback = callback;
}
onErrored(callback: (e: Error) => void) {
this.onErrorCallback = callback;
}
private static isCanvas(el: HTMLElement): el is HTMLCanvasElement {
return el.tagName === "CANVAS";
this.initChart().catch((e) => {this.logError(e);});
}
private async getInitialDataBlob(): Promise<SnapshotRecords> {
const data = await fetch("data?since=" + new Date((new Date().getTime() - this.minutesDisplayed * 60 * 1000)).toISOString());
const data = await fetch("data?since=" + new Date((new Date().getTime() - this.minutesDisplayed * 60000)).toISOString());
const payload = await data.json();
if (payload.snapshots.length < 0) {
throw new Error("Bad response - no snapshots found!");
@@ -66,64 +59,85 @@ class ClimateChart {
const payload = await this.getInitialDataBlob();
this.latestSnapshot = payload.snapshots[0];
this.insertSnapshots(...payload.snapshots);
setInterval(() => this.updateFromServer(), 30 * 1000);
setInterval(async () => this.updateFromServer().catch(e => this.logError(e)), 30 * 1000);
this.chart.update();
this.onLoadedCallback();
}
catch (e) {
this.onErrorCallback(new Error(`Server error: ${e}`));
new Error(`Server error: ${e}`)
}
}
private jsonToChartPoints(json: SnapshotRecords): {humidity: ChartPoint[], temp: ChartPoint[], co2: ChartPoint[]} {
const humidity = [];
const co2 = [];
const temp = [];
for (let i = 0; i < json.snapshots.length; i++) {
const snapshot = json.snapshots[json.snapshots.length - i - 1];
co2.push({x: snapshot.time, y: snapshot.co2});
temp.push({x: snapshot.time, y: snapshot.temp});
humidity.push({x: snapshot.time, y: snapshot.humidity});
}
return {humidity, co2, temp};
}
private async updateFromServer() {
const currentTime = (new Date(this.latestSnapshot.time)).toISOString();
const url = "data?since=" + currentTime;
const lastTimeInChart = (new Date(this.latestSnapshot.time)).toISOString();
const url = "data?since=" + lastTimeInChart;
try {
const payload: SnapshotRecords = await (await fetch(url)).json();
if (payload.snapshots.length > 0 && new Date(payload.snapshots[0].time).getTime() > Date.now()) {
this.latestSnapshot = payload.snapshots[0];
this.removeExpiredData(currentTime);
this.insertSnapshots(...payload.snapshots);
this.chart.update();
if (payload.snapshots.length > 0) {
const latestSnapshotIsNew = new Date(payload.snapshots[0].time).getTime() > new Date(lastTimeInChart).getTime();
if (latestSnapshotIsNew) {
this.removeExpiredPointsAfter(lastTimeInChart);
this.latestSnapshot = payload.snapshots[0];
this.insertSnapshots(...payload.snapshots);
this.chart.update();
}
}
}
catch (e) {
this.onErrorCallback(new Error(`Server error: ${e}`));
this.logError(`Server error: ${e}`);
}
}
private insertSnapshots(...snapshots: Snapshot[]) {
for (const snapshot of snapshots.reverse()) {
this.chart.data.datasets[0].data.push({x: snapshot.time, y: snapshot.humidity});
this.chart.data.datasets[1].data.push({x: snapshot.time, y: snapshot.temp});
this.chart.data.datasets[2].data.push({x: snapshot.time, y: snapshot.co2});
this.humidityPointList().push({x: snapshot.time, y: snapshot.humidity});
this.tempPointList().push({x: snapshot.time, y: snapshot.temp});
this.co2PointList().push({x: snapshot.time, y: snapshot.co2});
}
}
private removeExpiredData(latestTime: string) {
for (let i = 0; i < this.chart.data.datasets[0].data.length; i++) {
const timeOnPoint = (this.chart.data.datasets[0].data[i] as ChartPoint).x as string;
const timeElapsedSincePoint = Date.parse(latestTime) - Date.parse(timeOnPoint);
if (timeElapsedSincePoint > this.minutesDisplayed * 60000) {
this.chart.data.datasets[0].data.splice(i, 1);
this.chart.data.datasets[1].data.splice(i, 1);
this.chart.data.datasets[2].data.splice(i, 1);
private removeExpiredPointsAfter(referenceTime: string) {
for (let i = 0; i < this.humidityPointList().length; i++) {
const timeOnPoint = this.humidityPointList()[i].x;
const timeElapsedSinceReference = Date.parse(referenceTime) - Date.parse(timeOnPoint);
if (timeElapsedSinceReference > this.minutesDisplayed * 60000) {
this.humidityPointList().splice(i, 1);
this.tempPointList().splice(i, 1);
this.co2PointList().splice(i, 1);
} else {
break;
}
}
}
private humidityPointList(): ClimatePoint[] {
return this.chart.data.datasets[0].data as ClimatePoint[];
}
private tempPointList(): ClimatePoint[] {
return this.chart.data.datasets[1].data as ClimatePoint[];
}
private co2PointList(): ClimatePoint[] {
return this.chart.data.datasets[1].data as ClimatePoint[];
}
onLoaded(callback: () => void) {
this.onLoadedCallback = callback;
}
onErrored(callback: (e: Error) => void) {
this.onErrorCallback = callback;
}
private static isCanvas(el: HTMLElement): el is HTMLCanvasElement {
return el.tagName === "CANVAS";
}
private logError(error: string) {
this.errorLog += `${new Date().toISOString()}: ${ error }\n`;
this.onErrorCallback(new Error(error));
}
}
export default ClimateChart;

4
webapp/src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
declare module "chart.js/dist/Chart.bundle.min" {
import * as Charts from "chart.js";
export default Charts;
}

View File

@@ -73,5 +73,13 @@ module.exports = {
minSize: 30000,
name: false
}
}
},
devServer: {
contentBase: path.join(__dirname, "dist/"),
contentBasePublicPath: "/static/",
port: 3000,
publicPath: "http://localhost:3000/",
hotOnly: true
},
}