made it all easier ot use and sexier
This commit is contained in:
@@ -1,7 +1,16 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@200;400;500&family=Roboto:wght@200;300;400&display=swap');
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--accent-dark: #4f6fa0;
|
||||||
|
--accent-light: #fbfbff;
|
||||||
|
}
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: #fff1de;
|
background-color: var(--accent-light);
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-weight: lighter;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
@@ -44,9 +53,9 @@ html, body {
|
|||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
display: block;
|
display: block;
|
||||||
font-family: "Georgia", serif;
|
font-family: 'Roboto Slab', serif;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
color: #7b5b2f;
|
color: var(--accent-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.widget {
|
.widget {
|
||||||
@@ -55,14 +64,15 @@ h1 {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
border: 0.1em #c7ab82 solid;
|
border: 0.1em var(--accent-dark) solid;
|
||||||
|
border-radius: 0.4em;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.widget h2 {
|
.widget h2 {
|
||||||
font-family: "Georgia", serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
color: #7b5b2f;
|
color: var(--accent-dark);
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@@ -204,7 +214,7 @@ h1 {
|
|||||||
.help-box {
|
.help-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: solid 1px #7b5b2f;
|
border: solid 1px var(--accent-dark);
|
||||||
width: 40em;
|
width: 40em;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
id="layer1">
|
id="layer1">
|
||||||
<ellipse
|
<ellipse
|
||||||
style="fill:#fff1de;fill-opacity:1;fill-rule:evenodd;stroke:#c7ab82;stroke-width:0.573164;stroke-opacity:1;stop-color:#000000"
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#4f6fa0;stroke-width:0.573164;stroke-opacity:1;stop-color:#000000"
|
||||||
id="path2811"
|
id="path2811"
|
||||||
cx="4.2333336"
|
cx="4.2333336"
|
||||||
cy="4.2333336"
|
cy="4.2333336"
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
ry="3.9467514" />
|
ry="3.9467514" />
|
||||||
<text
|
<text
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
style="font-style:normal;font-weight:normal;font-size:8.0888px;line-height:1.25;font-family:sans-serif;fill:#c7ab82;fill-opacity:1;stroke:none;stroke-width:0.20222;"
|
style="font-style:normal;font-weight:normal;font-size:8.0888px;line-height:1.25;font-family:sans-serif;fill:#4f6fa0;fill-opacity:1;stroke:none;stroke-width:0.20222;"
|
||||||
x="2.5377054"
|
x="2.5377054"
|
||||||
y="6.9956398"
|
y="6.9956398"
|
||||||
id="text2809"><tspan
|
id="text2809"><tspan
|
||||||
@@ -73,6 +73,6 @@
|
|||||||
id="tspan2807"
|
id="tspan2807"
|
||||||
x="2.5377054"
|
x="2.5377054"
|
||||||
y="6.9956398"
|
y="6.9956398"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Bitstream Charter';-inkscape-font-specification:'Bitstream Charter';stroke-width:0.20222;fill:#c7ab82;fill-opacity:1;">?</tspan></text>
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Bitstream Charter';-inkscape-font-specification:'Bitstream Charter';stroke-width:0.20222;fill:#4f6fa0;fill-opacity:1;">?</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -131,37 +131,37 @@ class AppStateStore {
|
|||||||
start = this.state.lastUpdateTime - this.state.minutesDisplayed * 60;
|
start = this.state.lastUpdateTime - this.state.minutesDisplayed * 60;
|
||||||
stop = this.state.lastUpdateTime;
|
stop = this.state.lastUpdateTime;
|
||||||
}
|
}
|
||||||
this.addLoad();
|
const allTimeseries = this.state.leftTimeseries.concat(this.state.rightTimeseries);
|
||||||
try {
|
const allHistoriesComplete = !allTimeseries.some(timeseries => !timeseries.historyIsComplete());
|
||||||
for (const timeseries of this.state.leftTimeseries) {
|
if (start < this.getExtrema().minIndex && allHistoriesComplete) {
|
||||||
await timeseries.updateFromWindow(start, stop);
|
return;
|
||||||
}
|
|
||||||
for (const timeseries of this.state.rightTimeseries) {
|
|
||||||
await timeseries.updateFromWindow(start, stop);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
AppStore().fatalError(e);
|
|
||||||
}
|
}
|
||||||
this.finishLoad();
|
this.addLoad();
|
||||||
this.notifyAllTimeseriesUpdated();
|
const timeseriesFetches: Promise<unknown>[] = [];
|
||||||
|
for (const timeseries of allTimeseries) {
|
||||||
|
timeseriesFetches.push(timeseries.updateFromWindow(start, stop));
|
||||||
|
}
|
||||||
|
await Promise.allSettled(timeseriesFetches).then(() => {
|
||||||
|
this.finishLoad();
|
||||||
|
this.notifyAllTimeseriesUpdated();
|
||||||
|
}).catch((e) => this.fatalError(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getNewTimeseriesData() {
|
private async getNewTimeseriesData() {
|
||||||
const updateTime = new Date().getTime() / 1000;
|
const updateTime = new Date().getTime() / 1000;
|
||||||
this.addLoad();
|
this.addLoad();
|
||||||
try {
|
const timeseriesFetches: Promise<unknown>[] = [];
|
||||||
for (const timeseries of this.state.leftTimeseries) {
|
for (const timeseries of this.state.leftTimeseries) {
|
||||||
await timeseries.getLatest();
|
timeseriesFetches.push(timeseries.getLatest());
|
||||||
}
|
|
||||||
for (const timeseries of this.state.rightTimeseries) {
|
|
||||||
await timeseries.getLatest();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
AppStore().fatalError(e);
|
|
||||||
}
|
}
|
||||||
this.finishLoad();
|
for (const timeseries of this.state.rightTimeseries) {
|
||||||
this.setLastUpdateTime(updateTime);
|
timeseriesFetches.push(timeseries.getLatest());
|
||||||
this.notifyAllTimeseriesUpdated();
|
}
|
||||||
|
await Promise.allSettled(timeseriesFetches).then(() => {
|
||||||
|
this.finishLoad();
|
||||||
|
this.setLastUpdateTime(updateTime);
|
||||||
|
this.notifyAllTimeseriesUpdated();
|
||||||
|
}).catch((e) => this.fatalError(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
private notifyAllTimeseriesUpdated() {
|
private notifyAllTimeseriesUpdated() {
|
||||||
@@ -195,16 +195,53 @@ class AppStateStore {
|
|||||||
|
|
||||||
setDisplayWindow(newWin: TimeWindow) {
|
setDisplayWindow(newWin: TimeWindow) {
|
||||||
if (newWin.start < newWin.stop) {
|
if (newWin.start < newWin.stop) {
|
||||||
if (newWin.stop <= this.state.lastUpdateTime) {
|
const extrema = this.getExtrema();
|
||||||
this.state.displayWindow = {...newWin};
|
if (newWin.stop >= extrema.maxIndex) {
|
||||||
this.notifyStoreVal("displayWindow");
|
newWin.stop = extrema.maxIndex;
|
||||||
this.updateTimeseriesFromSettings();
|
|
||||||
}
|
}
|
||||||
|
this.state.displayWindow = {...newWin};
|
||||||
|
this.updateTimeseriesFromSettings().then(() => {
|
||||||
|
const newExtrema = this.getExtrema();
|
||||||
|
this.state.displayWindow = {
|
||||||
|
start: Math.max(newExtrema.minIndex, this.state.displayWindow.start),
|
||||||
|
stop: Math.min(newExtrema.maxIndex, this.state.displayWindow.stop),
|
||||||
|
};
|
||||||
|
this.notifyStoreVal("displayWindow");
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Invalid display window from ${newWin.start} to ${newWin.stop}`);
|
console.warn(`Invalid display window from ${newWin.start} to ${newWin.stop}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
shiftDisplayWindow(deltaX: number) {
|
||||||
|
const oldWin = {...this.state.displayWindow};
|
||||||
|
this.state.displayWindow.start += deltaX;
|
||||||
|
this.state.displayWindow.stop += deltaX;
|
||||||
|
this.updateTimeseriesFromSettings().then(() => {
|
||||||
|
const newExtrema = this.getExtrema();
|
||||||
|
const blockedBack = newExtrema.minIndex > this.state.displayWindow.start && deltaX < 0;
|
||||||
|
const blockedForward = newExtrema.maxIndex < this.state.displayWindow.stop && deltaX >= 0;
|
||||||
|
if (blockedBack || blockedForward) {
|
||||||
|
this.state.displayWindow = oldWin;
|
||||||
|
}
|
||||||
|
this.notifyStoreVal("displayWindow");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getExtrema() {
|
||||||
|
let minIndex = Infinity;
|
||||||
|
let maxIndex = -Infinity;
|
||||||
|
let minVal = Infinity;
|
||||||
|
let maxVal = -Infinity;
|
||||||
|
for (const timeseries of this.state.leftTimeseries.concat(this.state.rightTimeseries)) {
|
||||||
|
minIndex = Math.min(minIndex, timeseries.getExtrema().minIndex);
|
||||||
|
maxIndex = Math.max(maxIndex, timeseries.getExtrema().maxIndex);
|
||||||
|
minVal = Math.min(minIndex, timeseries.getExtrema().minVal);
|
||||||
|
maxVal = Math.max(maxIndex, timeseries.getExtrema().maxVal);
|
||||||
|
}
|
||||||
|
return {minIndex, maxIndex, minVal, maxVal};
|
||||||
|
}
|
||||||
|
|
||||||
setMinutesDisplayed(mins: number) {
|
setMinutesDisplayed(mins: number) {
|
||||||
if (mins > 0) {
|
if (mins > 0) {
|
||||||
this.state.minutesDisplayed = Math.ceil(mins);
|
this.state.minutesDisplayed = Math.ceil(mins);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class Timeseries {
|
|||||||
private valExtremaOverride?: { high: number, low: number };
|
private valExtremaOverride?: { high: number, low: number };
|
||||||
private colour: string;
|
private colour: string;
|
||||||
private tolerance: number;
|
private tolerance: number;
|
||||||
|
private historyComplete = false;
|
||||||
|
|
||||||
constructor(options: TimeseriesOptions) {
|
constructor(options: TimeseriesOptions) {
|
||||||
this.cache = new Int32Array();
|
this.cache = new Int32Array();
|
||||||
@@ -58,7 +59,7 @@ class Timeseries {
|
|||||||
getExtremaInRange(start: number, stop: number): Extrema {
|
getExtremaInRange(start: number, stop: number): Extrema {
|
||||||
let maxVal = -Infinity;
|
let maxVal = -Infinity;
|
||||||
let minVal = Infinity;
|
let minVal = Infinity;
|
||||||
for (let i = this.findIndexInCache(start) - 1; i < this.findIndexInCache(stop) - 1; i += 2) {
|
for (let i = this.findIndexInCache(start) - 1; i < this.findIndexInCache(stop) + 1; i += 2) {
|
||||||
if (this.cache[i] < minVal) {
|
if (this.cache[i] < minVal) {
|
||||||
minVal = this.cache[i];
|
minVal = this.cache[i];
|
||||||
}
|
}
|
||||||
@@ -69,8 +70,8 @@ class Timeseries {
|
|||||||
return {
|
return {
|
||||||
minIndex: this.extrema.minIndex,
|
minIndex: this.extrema.minIndex,
|
||||||
maxIndex: this.extrema.maxIndex,
|
maxIndex: this.extrema.maxIndex,
|
||||||
maxVal: this.valExtremaOverride.high > maxVal ? this.valExtremaOverride.high : maxVal,
|
maxVal: this.valExtremaOverride ? Math.max(this.valExtremaOverride.high, maxVal) : maxVal,
|
||||||
minVal: this.valExtremaOverride.low < minVal ? this.valExtremaOverride.low : minVal,
|
minVal: this.valExtremaOverride ? Math.min(this.valExtremaOverride.low, minVal) : minVal,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ class Timeseries {
|
|||||||
const cacheStopIndex = this.findIndexInCache(stop);
|
const cacheStopIndex = this.findIndexInCache(stop);
|
||||||
return this.cache.slice(
|
return this.cache.slice(
|
||||||
(cacheStartIndex - (cacheStartIndex) % blockSize),
|
(cacheStartIndex - (cacheStartIndex) % blockSize),
|
||||||
(cacheStopIndex + blockSize - (cacheStopIndex) % blockSize) + blockSize * 2,
|
(cacheStopIndex + blockSize - (cacheStopIndex) % blockSize) + blockSize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,13 +176,17 @@ class Timeseries {
|
|||||||
if (atLeastUntil < priorTo - backDist) {
|
if (atLeastUntil < priorTo - backDist) {
|
||||||
backDist = priorTo - atLeastUntil;
|
backDist = priorTo - atLeastUntil;
|
||||||
}
|
}
|
||||||
const result = await this.loader(priorTo - backDist, priorTo);
|
const requestIndexStart = priorTo - backDist;
|
||||||
|
const result = await this.loader(requestIndexStart, priorTo);
|
||||||
const newCache = new Int32Array(this.cache.length + result.length);
|
const newCache = new Int32Array(this.cache.length + result.length);
|
||||||
newCache.set(result, 0);
|
newCache.set(result, 0);
|
||||||
newCache.set(this.cache, result.length);
|
newCache.set(this.cache, result.length);
|
||||||
this.cache = newCache;
|
this.cache = newCache;
|
||||||
this.currentEndPointer += result.length;
|
this.currentEndPointer += result.length;
|
||||||
this.updateExtremaFrom(result);
|
this.updateExtremaFrom(result);
|
||||||
|
if (this.extrema.minIndex === priorTo) {
|
||||||
|
this.historyComplete = true;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(`Error fetching prior data: ${e}`);
|
throw new Error(`Error fetching prior data: ${e}`);
|
||||||
}
|
}
|
||||||
@@ -234,6 +239,10 @@ class Timeseries {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
historyIsComplete(): boolean {
|
||||||
|
return this.historyComplete;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Timeseries;
|
export default Timeseries;
|
||||||
@@ -46,7 +46,6 @@ export default class Chart {
|
|||||||
this.ctx.fillStyle = "rgb(255,255,255)";
|
this.ctx.fillStyle = "rgb(255,255,255)";
|
||||||
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||||
this.ctx.fill();
|
this.ctx.fill();
|
||||||
this.ctx.translate(0.5, 0.5);
|
|
||||||
this.ctx.canvas.onmousemove = (e) => this.handleMouseMove(e);
|
this.ctx.canvas.onmousemove = (e) => this.handleMouseMove(e);
|
||||||
this.ctx.canvas.onmousedown = (e) => this.dragging = true;
|
this.ctx.canvas.onmousedown = (e) => this.dragging = true;
|
||||||
this.ctx.canvas.onmouseup = (e) => this.dragging = false;
|
this.ctx.canvas.onmouseup = (e) => this.dragging = false;
|
||||||
@@ -78,7 +77,6 @@ export default class Chart {
|
|||||||
height: this.ctx.canvas.height - verticalMargins,
|
height: this.ctx.canvas.height - verticalMargins,
|
||||||
width: rightScaleInitialWidth,
|
width: rightScaleInitialWidth,
|
||||||
});
|
});
|
||||||
this.render();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addTimeseries(timeseries: Timeseries, scale?: ScaleId) {
|
addTimeseries(timeseries: Timeseries, scale?: ScaleId) {
|
||||||
@@ -138,16 +136,16 @@ export default class Chart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.clearCanvas();
|
this.resetCanvas();
|
||||||
this.updateResolution();
|
this.updateResolution();
|
||||||
this.renderGuides();
|
|
||||||
this.leftScale.updateIndexRange(this.indexRange);
|
this.leftScale.updateIndexRange(this.indexRange);
|
||||||
this.rightScale.updateIndexRange(this.indexRange);
|
this.rightScale.updateIndexRange(this.indexRange);
|
||||||
|
this.renderGuides();
|
||||||
this.leftScale.listTimeseries().forEach(timeseries => this.renderTimeseries(timeseries, ScaleId.Left));
|
this.leftScale.listTimeseries().forEach(timeseries => this.renderTimeseries(timeseries, ScaleId.Left));
|
||||||
this.rightScale.listTimeseries().forEach(timeseries => this.renderTimeseries(timeseries, ScaleId.Right));
|
this.rightScale.listTimeseries().forEach(timeseries => this.renderTimeseries(timeseries, ScaleId.Right));
|
||||||
this.renderMargins();
|
|
||||||
this.leftScale.render(this.ctx);
|
this.leftScale.render(this.ctx);
|
||||||
this.rightScale.render(this.ctx);
|
this.rightScale.render(this.ctx);
|
||||||
|
this.renderMargins();
|
||||||
this.renderTooltips();
|
this.renderTooltips();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,9 +177,11 @@ export default class Chart {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearCanvas() {
|
private resetCanvas() {
|
||||||
this.ctx.fillStyle = "rgb(255,255,255)";
|
this.ctx.fillStyle = "rgb(255,255,255)";
|
||||||
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
this.ctx.fillRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
||||||
|
this.ctx.resetTransform();
|
||||||
|
this.ctx.translate(0.5, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateResolution() {
|
private updateResolution() {
|
||||||
@@ -200,7 +200,7 @@ export default class Chart {
|
|||||||
this.ctx.lineWidth = 1;
|
this.ctx.lineWidth = 1;
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
for (const tick of this.rightScale.getTicks()) {
|
for (const tick of this.rightScale.getTicks()) {
|
||||||
const pos = Math.floor(this.rightScale.getY(tick));
|
const pos = this.rightScale.getY(tick) | 0;
|
||||||
this.ctx.moveTo(this.chartBounds.left, pos);
|
this.ctx.moveTo(this.chartBounds.left, pos);
|
||||||
this.ctx.lineTo(this.chartBounds.left + this.chartBounds.width, pos);
|
this.ctx.lineTo(this.chartBounds.left + this.chartBounds.width, pos);
|
||||||
}
|
}
|
||||||
@@ -294,6 +294,7 @@ export default class Chart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderTooltip(text: string, x: number, y: number, markerColour: string) {
|
private renderTooltip(text: string, x: number, y: number, markerColour: string) {
|
||||||
|
this.ctx.lineWidth = 1;
|
||||||
this.ctx.strokeStyle = "rgb(255,0,0)";
|
this.ctx.strokeStyle = "rgb(255,0,0)";
|
||||||
this.ctx.beginPath();
|
this.ctx.beginPath();
|
||||||
this.ctx.ellipse(x, y, 5, 5, 0, 0, 2 * Math.PI);
|
this.ctx.ellipse(x, y, 5, 5, 0, 0, 2 * Math.PI);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export default class Scale {
|
|||||||
private tickCache: number[] = [];
|
private tickCache: number[] = [];
|
||||||
private tickCacheDirty = true;
|
private tickCacheDirty = true;
|
||||||
private bounds: Bounds;
|
private bounds: Bounds;
|
||||||
|
private margins: {top: number, bottom: number} = {top: 10, bottom: 10};
|
||||||
|
|
||||||
constructor(bounds?: Bounds) {
|
constructor(bounds?: Bounds) {
|
||||||
this.bounds = bounds ?? {height: 0, width: 0, top: 0, left: 0};
|
this.bounds = bounds ?? {height: 0, width: 0, top: 0, left: 0};
|
||||||
@@ -72,7 +73,8 @@ export default class Scale {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getY(value: number) {
|
getY(value: number) {
|
||||||
return this.bounds.top + this.bounds.height - (value - this.valRange.low) / (this.valRange.high - this.valRange.low) * this.bounds.height;
|
const internalHeight = this.bounds.height - this.margins.top - this.margins.bottom;
|
||||||
|
return this.bounds.top + this.bounds.height - this.margins.bottom - (value - this.valRange.low) / (this.valRange.high - this.valRange.low) * internalHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
getValue(y: number) {
|
getValue(y: number) {
|
||||||
|
|||||||
@@ -6,21 +6,18 @@ export const newCo2Timeseries = (tolerance: number) => new Timeseries({
|
|||||||
name: "CO₂ (ppm)",
|
name: "CO₂ (ppm)",
|
||||||
loader: (start, stop) => loadClimateTimeseriesData("co2", start, stop),
|
loader: (start, stop) => loadClimateTimeseriesData("co2", start, stop),
|
||||||
tolerance,
|
tolerance,
|
||||||
valueRangeOverride: { high: 800, low: 400 },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const newTempTimeseries = (tolerance: number) => new Timeseries({
|
export const newTempTimeseries = (tolerance: number) => new Timeseries({
|
||||||
name: "Temperature (°C)",
|
name: "Temperature (°C)",
|
||||||
loader: (start, stop) => loadClimateTimeseriesData("temp", start, stop),
|
loader: (start, stop) => loadClimateTimeseriesData("temp", start, stop),
|
||||||
tolerance,
|
tolerance,
|
||||||
valueRangeOverride: { high: 30, low: 10 },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const newHumidityTimeseries = (tolerance: number) => new Timeseries({
|
export const newHumidityTimeseries = (tolerance: number) => new Timeseries({
|
||||||
name: "Humidity (%)",
|
name: "Humidity (%)",
|
||||||
loader: (start, stop) => loadClimateTimeseriesData("humidity", start, stop),
|
loader: (start, stop) => loadClimateTimeseriesData("humidity", start, stop),
|
||||||
tolerance,
|
tolerance,
|
||||||
valueRangeOverride: { high: 75, low: 40 },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function loadClimateTimeseriesData(dataType: "temp" | "humidity" | "co2", start?: number, stop?: number) {
|
async function loadClimateTimeseriesData(dataType: "temp" | "humidity" | "co2", start?: number, stop?: number) {
|
||||||
@@ -50,7 +47,7 @@ async function loadClimateTimeseriesData(dataType: "temp" | "humidity" | "co2",
|
|||||||
}
|
}
|
||||||
return new Int32Array(data.buffer);
|
return new Int32Array(data.buffer);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const message = "timerseries data couldn't be loaded from the server";
|
const message = "timeseries data couldn't be loaded from the server";
|
||||||
throw new ClayPIDashboardError(`${message}: ${e}`, message);
|
throw new ClayPIDashboardError(`${message}: ${e}`, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"development": false,
|
"development": true,
|
||||||
"defaultMinuteSpan": 60,
|
"defaultMinuteSpan": 60,
|
||||||
"reloadIntervalSec": 30,
|
"reloadIntervalSec": 30,
|
||||||
"dataEndpoint": "/climate/api"
|
"dataEndpoint": "http://192.168.178.21:4040/climate/api"
|
||||||
}
|
}
|
||||||
|
|||||||
4
dashboard/src/types.d.ts
vendored
4
dashboard/src/types.d.ts
vendored
@@ -5,4 +5,8 @@ declare module "*.gif" {
|
|||||||
declare module "*.png" {
|
declare module "*.png" {
|
||||||
const value: string;
|
const value: string;
|
||||||
export = value;
|
export = value;
|
||||||
|
}
|
||||||
|
declare module "*.svg" {
|
||||||
|
const value: string;
|
||||||
|
export = value;
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@ import LegendWidget from "./LegendWidget";
|
|||||||
import HelpModal from "./HelpModal";
|
import HelpModal from "./HelpModal";
|
||||||
import * as JSX from "../JSXFactory";
|
import * as JSX from "../JSXFactory";
|
||||||
import {AppStore} from "../StateStore";
|
import {AppStore} from "../StateStore";
|
||||||
import HelpButtonImg from "../../assets/help-button.png";
|
import HelpButtonImg from "../../assets/help-button.svg";
|
||||||
|
|
||||||
class AppUI extends UIComponent {
|
class AppUI extends UIComponent {
|
||||||
private timezoneWidget: TimezoneWidget;
|
private timezoneWidget: TimezoneWidget;
|
||||||
|
|||||||
@@ -75,11 +75,7 @@ class ClimateChartWidget extends UIComponent {
|
|||||||
if (getAppState().displayMode === "pastMins") {
|
if (getAppState().displayMode === "pastMins") {
|
||||||
AppStore().emulateLastMinsWithWindow();
|
AppStore().emulateLastMinsWithWindow();
|
||||||
}
|
}
|
||||||
const displayedWindow = getAppState().displayWindow;
|
AppStore().shiftDisplayWindow(deltaIndex);
|
||||||
AppStore().setDisplayWindow({
|
|
||||||
start: displayedWindow.start + deltaIndex,
|
|
||||||
stop: displayedWindow.stop + deltaIndex,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateTimezone() {
|
private updateTimezone() {
|
||||||
@@ -96,8 +92,8 @@ class ClimateChartWidget extends UIComponent {
|
|||||||
getAppState().rightTimeseries.forEach(timeseries => this.chart.addTimeseries(timeseries, ScaleId.Right));
|
getAppState().rightTimeseries.forEach(timeseries => this.chart.addTimeseries(timeseries, ScaleId.Right));
|
||||||
this.chart.on("scroll", (...args) => this.handleScroll(...args));
|
this.chart.on("scroll", (...args) => this.handleScroll(...args));
|
||||||
this.chart.on("drag", (...args) => this.handleDrag(...args));
|
this.chart.on("drag", (...args) => this.handleDrag(...args));
|
||||||
await this.rerender();
|
|
||||||
this.initialised = true;
|
this.initialised = true;
|
||||||
|
await this.rerender();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
AppStore().fatalError(e);
|
AppStore().fatalError(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user