add working (not yet perfect) graphs, all have same range and toggle for graphs not there yet
This commit is contained in:
parent
6fc414a2c0
commit
9fa0fd1f22
|
@ -1,65 +1,10 @@
|
||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import ScalarGraph from "./ScalarGraph";
|
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 Log = () => {
|
||||||
const [timeSeries, setTimeSeries] = useState<RecordSeries>([]);
|
|
||||||
const s3Access = new S3Access(
|
|
||||||
"saliomameiringen",
|
|
||||||
"sos-ch-dk-2",
|
|
||||||
"exo.io",
|
|
||||||
"EXO18e7ae9e53fae71ee55cf35b",
|
|
||||||
"3Cyonq8gMQ0a3elTH2vP7Yv-czcCj8iE2lBcPB9XhSc",
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
const fetchData = (
|
|
||||||
timestamp: UnixTime
|
|
||||||
): Promise<FetchResult<Record<string, number>>> => {
|
|
||||||
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<ScalarGraph data={timeSeries} />
|
<ScalarGraph />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,26 +1,112 @@
|
||||||
import Plot from "react-plotly.js";
|
import Plot from "react-plotly.js";
|
||||||
import { RecordSeries } from "../../../dataCache/data";
|
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 {
|
const ScalarGraph = () => {
|
||||||
data: RecordSeries;
|
const timeRange = UnixTime.fromTicks(1682085650)
|
||||||
|
.rangeBefore(TimeSpan.fromDays(1))
|
||||||
|
.sample(TimeSpan.fromMinutes(30));
|
||||||
|
const [timeSeries, setTimeSeries] = useState<RecordSeries>([]);
|
||||||
|
const [uiRevision, setUiRevision] = useState(Math.random());
|
||||||
|
const [range, setRange] = useState([
|
||||||
|
timeRange[0].toDate(),
|
||||||
|
timeRange[timeRange.length - 1].toDate(),
|
||||||
|
]);
|
||||||
|
const [toggles, setToggles] = useState<Object | null>(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<FetchResult<Record<string, number>>> => {
|
||||||
|
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 renderGraphs = () => {
|
||||||
const coordinateTimeSeries = transformToGraphData(props.data);
|
const coordinateTimeSeries = transformToGraphData(timeSeries);
|
||||||
return Object.keys(coordinateTimeSeries).map((path) => {
|
return Object.keys(coordinateTimeSeries).map((path) => {
|
||||||
|
const data = coordinateTimeSeries[path] ?? { x: [], y: [] };
|
||||||
return (
|
return (
|
||||||
<Plot
|
<Plot
|
||||||
data={[
|
data={[
|
||||||
{
|
{
|
||||||
...coordinateTimeSeries[path],
|
...data,
|
||||||
type: "scatter",
|
type: "scatter",
|
||||||
mode: "lines+markers",
|
mode: "lines+markers",
|
||||||
marker: { color: "red" },
|
marker: { color: "red" },
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
layout={{ width: 1000, height: 500, title: path }}
|
layout={{
|
||||||
|
width: 1000,
|
||||||
|
height: 500,
|
||||||
|
title: path,
|
||||||
|
uirevision: uiRevision,
|
||||||
|
xaxis: {
|
||||||
|
autorange: false,
|
||||||
|
range: range,
|
||||||
|
type: "date",
|
||||||
|
},
|
||||||
|
}}
|
||||||
config={{
|
config={{
|
||||||
modeBarButtonsToRemove: [
|
modeBarButtonsToRemove: [
|
||||||
"lasso2d",
|
"lasso2d",
|
||||||
|
@ -29,8 +115,24 @@ const ScalarGraph = (props: I_ScalarGraphProps) => {
|
||||||
"autoScale2d",
|
"autoScale2d",
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
onUpdate={(figure) => {
|
onRelayout={(params) => {
|
||||||
//console.log(figure);
|
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);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,7 +31,7 @@ export const transformToGraphData = (timeStampData: RecordSeries) => {
|
||||||
return {
|
return {
|
||||||
...pathAcc,
|
...pathAcc,
|
||||||
[currPath]: {
|
[currPath]: {
|
||||||
x: [curr.time.ticks],
|
x: [new Date(curr.time.ticks * 1000)],
|
||||||
y: [curr.value ? curr.value[currPath] : 0],
|
y: [curr.value ? curr.value[currPath] : 0],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,7 @@ export const transformToGraphData = (timeStampData: RecordSeries) => {
|
||||||
},
|
},
|
||||||
{} as GraphData
|
{} as GraphData
|
||||||
);
|
);
|
||||||
|
console.log("acc", acc, timeStampObj);
|
||||||
return mergeDeep(acc, timeStampObj);
|
return mergeDeep(acc, timeStampObj);
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
|
|
Loading…
Reference in New Issue