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

View File

@@ -16,7 +16,7 @@ try:
else: else:
raise RuntimeError() raise RuntimeError()
print( print(
'Time:', str(datetime.now()), 'Time:', str(datetime.isoformat(datetime.now())),
'\nTemp:', temp, '\nTemp:', temp,
'\nHumidity:', humidity, '\nHumidity:', humidity,
'\nCO2:', co2, '\nCO2:', co2,

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": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack", "build": "webpack",
"dev": "webpack-dev-server" "dev": "webpack serve"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
@@ -14,17 +14,17 @@
"@webpack-cli/init": "^1.0.3", "@webpack-cli/init": "^1.0.3",
"babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-syntax-dynamic-import": "^6.18.0",
"css-loader": "^5.0.1", "css-loader": "^5.0.1",
"prettier": "^2.1.2",
"style-loader": "^2.0.0", "style-loader": "^2.0.0",
"terser-webpack-plugin": "^5.0.3", "terser-webpack-plugin": "^5.0.3",
"ts-loader": "^8.0.10", "ts-loader": "^8.0.10",
"typescript": "^4.0.5", "typescript": "^4.0.5",
"webpack": "^5.4.0", "webpack": "^5.4.0",
"webpack-cli": "^4.2.0", "webpack-cli": "^4.2.0"
"webpack-dev-server": "^3.11.0",
"prettier": "^2.1.2"
}, },
"dependencies": { "dependencies": {
"@types/chart.js": "^2.9.27", "@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 from "chart.js/dist/Chart.bundle.min";
import Chart, {ChartPoint} from "chart.js/dist/Chart.bundle.min"; import type {ChartPoint} from "chart.js";
import {generateClimateChartConfig} from "./climateChartConfig"; import {generateClimateChartConfig} from "./climateChartConfig";
interface Snapshot { interface Snapshot {
@@ -14,38 +14,31 @@ interface SnapshotRecords {
snapshots: Snapshot[] snapshots: Snapshot[]
} }
interface ClimatePoint {
x: string;
y: number;
}
class ClimateChart { class ClimateChart {
private chart: Chart | null; private chart: Chart | null;
private latestSnapshot: Snapshot | null; private latestSnapshot: Snapshot | null;
private onLoadedCallback: () => void = () => {}; private onLoadedCallback: () => void = () => {};
private onErrorCallback: (e: Error) => void = () => {}; private onErrorCallback: (e: Error) => void = () => {};
constructor( private errorLog: string = "";
private readonly rootUrl: string, private readonly rootUrl: string;
private readonly canvasId: string, private readonly canvasId: string;
private readonly minutesDisplayed: number = 60 private readonly minutesDisplayed: number = 60;
) {
if (minutesDisplayed > 0 && Math.floor(minutesDisplayed) == minutesDisplayed) { constructor(rootUrl: string, canvasId: string, minutesDisplayed: number) {
this.minutesDisplayed = minutesDisplayed; this.minutesDisplayed = Math.floor(minutesDisplayed);
} else { if (minutesDisplayed < 0 || Math.floor(minutesDisplayed) !== minutesDisplayed) {
throw new Error(`invalid minutes passed to display in chart: ${minutesDisplayed}`); console.warn(`Minutes passed were ${ minutesDisplayed }, which is invalid. ${ this.minutesDisplayed } minutes are being shown instead.`);
} }
this.initChart().catch((e) => {this.onErrorCallback(e);}); this.initChart().catch((e) => {this.logError(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";
} }
private async getInitialDataBlob(): Promise<SnapshotRecords> { 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(); const payload = await data.json();
if (payload.snapshots.length < 0) { if (payload.snapshots.length < 0) {
throw new Error("Bad response - no snapshots found!"); throw new Error("Bad response - no snapshots found!");
@@ -66,64 +59,85 @@ class ClimateChart {
const payload = await this.getInitialDataBlob(); const payload = await this.getInitialDataBlob();
this.latestSnapshot = payload.snapshots[0]; this.latestSnapshot = payload.snapshots[0];
this.insertSnapshots(...payload.snapshots); this.insertSnapshots(...payload.snapshots);
setInterval(() => this.updateFromServer(), 30 * 1000); setInterval(async () => this.updateFromServer().catch(e => this.logError(e)), 30 * 1000);
this.chart.update(); this.chart.update();
this.onLoadedCallback(); this.onLoadedCallback();
} }
catch (e) { 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() { private async updateFromServer() {
const currentTime = (new Date(this.latestSnapshot.time)).toISOString(); const lastTimeInChart = (new Date(this.latestSnapshot.time)).toISOString();
const url = "data?since=" + currentTime; const url = "data?since=" + lastTimeInChart;
try { try {
const payload: SnapshotRecords = await (await fetch(url)).json(); const payload: SnapshotRecords = await (await fetch(url)).json();
if (payload.snapshots.length > 0 && new Date(payload.snapshots[0].time).getTime() > Date.now()) { if (payload.snapshots.length > 0) {
this.latestSnapshot = payload.snapshots[0]; const latestSnapshotIsNew = new Date(payload.snapshots[0].time).getTime() > new Date(lastTimeInChart).getTime();
this.removeExpiredData(currentTime); if (latestSnapshotIsNew) {
this.insertSnapshots(...payload.snapshots); this.removeExpiredPointsAfter(lastTimeInChart);
this.chart.update(); this.latestSnapshot = payload.snapshots[0];
this.insertSnapshots(...payload.snapshots);
this.chart.update();
}
} }
} }
catch (e) { catch (e) {
this.onErrorCallback(new Error(`Server error: ${e}`)); this.logError(`Server error: ${e}`);
} }
} }
private insertSnapshots(...snapshots: Snapshot[]) { private insertSnapshots(...snapshots: Snapshot[]) {
for (const snapshot of snapshots.reverse()) { for (const snapshot of snapshots.reverse()) {
this.chart.data.datasets[0].data.push({x: snapshot.time, y: snapshot.humidity}); this.humidityPointList().push({x: snapshot.time, y: snapshot.humidity});
this.chart.data.datasets[1].data.push({x: snapshot.time, y: snapshot.temp}); this.tempPointList().push({x: snapshot.time, y: snapshot.temp});
this.chart.data.datasets[2].data.push({x: snapshot.time, y: snapshot.co2}); this.co2PointList().push({x: snapshot.time, y: snapshot.co2});
} }
} }
private removeExpiredData(latestTime: string) { private removeExpiredPointsAfter(referenceTime: string) {
for (let i = 0; i < this.chart.data.datasets[0].data.length; i++) { for (let i = 0; i < this.humidityPointList().length; i++) {
const timeOnPoint = (this.chart.data.datasets[0].data[i] as ChartPoint).x as string; const timeOnPoint = this.humidityPointList()[i].x;
const timeElapsedSincePoint = Date.parse(latestTime) - Date.parse(timeOnPoint); const timeElapsedSinceReference = Date.parse(referenceTime) - Date.parse(timeOnPoint);
if (timeElapsedSincePoint > this.minutesDisplayed * 60000) { if (timeElapsedSinceReference > this.minutesDisplayed * 60000) {
this.chart.data.datasets[0].data.splice(i, 1); this.humidityPointList().splice(i, 1);
this.chart.data.datasets[1].data.splice(i, 1); this.tempPointList().splice(i, 1);
this.chart.data.datasets[2].data.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; 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, minSize: 30000,
name: false name: false
} }
} },
devServer: {
contentBase: path.join(__dirname, "dist/"),
contentBasePublicPath: "/static/",
port: 3000,
publicPath: "http://localhost:3000/",
hotOnly: true
},
} }