From 9fa0fd1f22b49938ce18a2d7b31333490305e464 Mon Sep 17 00:00:00 2001 From: Sina Blattmann Date: Wed, 26 Apr 2023 13:54:57 +0200 Subject: [PATCH] add working (not yet perfect) graphs, all have same range and toggle for graphs not there yet --- .../src/components/Installations/Log/Log.tsx | 59 +-------- .../Installations/Log/ScalarGraph.tsx | 122 ++++++++++++++++-- typescript/Frontend/src/util/graph.util.tsx | 3 +- 3 files changed, 116 insertions(+), 68 deletions(-) diff --git a/typescript/Frontend/src/components/Installations/Log/Log.tsx b/typescript/Frontend/src/components/Installations/Log/Log.tsx index 550ef8f83..d1156f14e 100644 --- a/typescript/Frontend/src/components/Installations/Log/Log.tsx +++ b/typescript/Frontend/src/components/Installations/Log/Log.tsx @@ -1,65 +1,10 @@ -import React, { useState } from "react"; +import React from "react"; import ScalarGraph from "./ScalarGraph"; -import DataCache, { FetchResult } from "../../../dataCache/dataCache"; -import { TimeSpan, UnixTime } from "../../../dataCache/time"; -import { S3Access } from "../../../dataCache/S3/S3Access"; -import { map, debounceTime } from "rxjs/operators"; -import { RecordSeries } from "../../../dataCache/data"; -import { parseCsv } from "../../../util/graph.util"; const Log = () => { - const [timeSeries, setTimeSeries] = useState([]); - const s3Access = new S3Access( - "saliomameiringen", - "sos-ch-dk-2", - "exo.io", - "EXO18e7ae9e53fae71ee55cf35b", - "3Cyonq8gMQ0a3elTH2vP7Yv-czcCj8iE2lBcPB9XhSc", - "" - ); - - const fetchData = ( - timestamp: UnixTime - ): Promise>> => { - const s3Path = `${timestamp.ticks}.csv`; - return s3Access - .get(s3Path) - .then(async (r) => { - if (r.status === 404) { - return Promise.resolve(FetchResult.notAvailable); - } else if (r.status === 200) { - const text = await r.text(); - return parseCsv(text); - } else { - console.error("unexpected status code"); - return Promise.resolve(FetchResult.notAvailable); - } - }) - .catch((e) => { - console.log(e); - return Promise.resolve(FetchResult.tryLater); - }); - }; - - const cache = new DataCache(fetchData, TimeSpan.fromSeconds(2)); - - const sampleTimes = UnixTime.fromTicks(1682085650) - .earlier(TimeSpan.fromMinutes(1)) - .rangeBefore(TimeSpan.fromMinutes(1)) - .sample(TimeSpan.fromSeconds(2)); - - cache.getSeries(sampleTimes); - - const update = cache.gotData.pipe( - debounceTime(2000), - map((_) => setTimeSeries(cache.getSeries(sampleTimes))) - ); - - update.subscribe(); - return ( <> - + ); }; diff --git a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx index 69e6e5218..655eb220f 100644 --- a/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx +++ b/typescript/Frontend/src/components/Installations/Log/ScalarGraph.tsx @@ -1,26 +1,112 @@ import Plot from "react-plotly.js"; import { RecordSeries } from "../../../dataCache/data"; -import { transformToGraphData } from "../../../util/graph.util"; +import { parseCsv, transformToGraphData } from "../../../util/graph.util"; +import { TimeSpan, UnixTime } from "../../../dataCache/time"; +import { useEffect, useMemo, useState } from "react"; +import { BehaviorSubject, startWith, throttleTime, withLatestFrom } from "rxjs"; +import { S3Access } from "../../../dataCache/S3/S3Access"; +import DataCache, { FetchResult } from "../../../dataCache/dataCache"; -interface I_ScalarGraphProps { - data: RecordSeries; -} +const ScalarGraph = () => { + const timeRange = UnixTime.fromTicks(1682085650) + .rangeBefore(TimeSpan.fromDays(1)) + .sample(TimeSpan.fromMinutes(30)); + const [timeSeries, setTimeSeries] = useState([]); + const [uiRevision, setUiRevision] = useState(Math.random()); + const [range, setRange] = useState([ + timeRange[0].toDate(), + timeRange[timeRange.length - 1].toDate(), + ]); + const [toggles, setToggles] = useState(null); + + const times$ = useMemo(() => new BehaviorSubject(timeRange), []); + + const s3Access = new S3Access( + "saliomameiringen", + "sos-ch-dk-2", + "exo.io", + "EXO18e7ae9e53fae71ee55cf35b", + "3Cyonq8gMQ0a3elTH2vP7Yv-czcCj8iE2lBcPB9XhSc", + "" + ); + + useEffect(() => { + const subscription = cache.gotData + .pipe( + startWith(0), + throttleTime(200, undefined, { leading: true, trailing: true }), + withLatestFrom(times$) + ) + .subscribe(([_, times]) => { + const timeSeries = cache.getSeries(times); + console.log("GOT DATA", _, times, timeSeries); + setTimeSeries(timeSeries); + if (toggles === null) setToggles(timeSeries); + }); + + return () => subscription.unsubscribe(); + }, []); + + const roundToNearest30 = (date: Date) => { + const minutes = 30; + const ms = 1000 * 60 * minutes; + + return new Date(Math.round(date.getTime() / ms) * ms); + }; + + const fetchData = ( + timestamp: UnixTime + ): Promise>> => { + const s3Path = `${timestamp.ticks}.csv`; + return s3Access + .get(s3Path) + .then(async (r) => { + if (r.status === 404) { + return Promise.resolve(FetchResult.notAvailable); + } else if (r.status === 200) { + const text = await r.text(); + return parseCsv(text); + } else { + console.error("unexpected status code"); + return Promise.resolve(FetchResult.notAvailable); + } + }) + .catch((e) => { + console.log(e); + return Promise.resolve(FetchResult.tryLater); + }); + }; + + const cache = useMemo( + () => new DataCache(fetchData, TimeSpan.fromSeconds(2)), + [] + ); -const ScalarGraph = (props: I_ScalarGraphProps) => { const renderGraphs = () => { - const coordinateTimeSeries = transformToGraphData(props.data); + const coordinateTimeSeries = transformToGraphData(timeSeries); return Object.keys(coordinateTimeSeries).map((path) => { + const data = coordinateTimeSeries[path] ?? { x: [], y: [] }; return ( { "autoScale2d", ], }} - onUpdate={(figure) => { - //console.log(figure); + onRelayout={(params) => { + console.log("PARAMS", params); + const xaxisRange0 = params["xaxis.range[0]"]; + const xaxisRange1 = params["xaxis.range[1]"]; + + if (xaxisRange0 && xaxisRange1) { + const epoch0 = + roundToNearest30(new Date(xaxisRange0)).getTime() / 1000; + const epoch1 = + roundToNearest30(new Date(xaxisRange1)).getTime() / 1000; + setRange([new Date(xaxisRange0), new Date(xaxisRange1)]); + setUiRevision(Math.random()); + const times = UnixTime.fromTicks(epoch1) + .rangeBefore(TimeSpan.fromSeconds(epoch1 - epoch0)) + .sample(TimeSpan.fromMinutes(30)); + cache.getSeries(times); + times$.next(times); + } }} /> ); diff --git a/typescript/Frontend/src/util/graph.util.tsx b/typescript/Frontend/src/util/graph.util.tsx index 8bbf435c8..b1a56f793 100644 --- a/typescript/Frontend/src/util/graph.util.tsx +++ b/typescript/Frontend/src/util/graph.util.tsx @@ -31,7 +31,7 @@ export const transformToGraphData = (timeStampData: RecordSeries) => { return { ...pathAcc, [currPath]: { - x: [curr.time.ticks], + x: [new Date(curr.time.ticks * 1000)], y: [curr.value ? curr.value[currPath] : 0], }, }; @@ -40,6 +40,7 @@ export const transformToGraphData = (timeStampData: RecordSeries) => { }, {} as GraphData ); + console.log("acc", acc, timeStampObj); return mergeDeep(acc, timeStampObj); } return acc;