round graph times correctly

This commit is contained in:
Sina Blattmann 2023-05-02 11:08:08 +02:00
parent 9fa0fd1f22
commit b7c443fc93
2 changed files with 134 additions and 20 deletions

View File

@ -1,23 +1,42 @@
import Plot from "react-plotly.js"; import Plot from "react-plotly.js";
import { RecordSeries } from "../../../dataCache/data"; import { RecordSeries } from "../../../dataCache/data";
import { parseCsv, transformToGraphData } from "../../../util/graph.util"; import { parseCsv, transformToGraphData } from "../../../util/graph.util";
import { TimeSpan, UnixTime } from "../../../dataCache/time"; import { TimeRange, TimeSpan, UnixTime } from "../../../dataCache/time";
import { useEffect, useMemo, useState } from "react"; import { ReactNode, useEffect, useMemo, useState } from "react";
import { BehaviorSubject, startWith, throttleTime, withLatestFrom } from "rxjs"; import { BehaviorSubject, startWith, throttleTime, withLatestFrom } from "rxjs";
import { S3Access } from "../../../dataCache/S3/S3Access"; import { S3Access } from "../../../dataCache/S3/S3Access";
import DataCache, { FetchResult } from "../../../dataCache/dataCache"; import DataCache, { FetchResult } from "../../../dataCache/dataCache";
import { TreeItem, TreeView } from "@mui/lab";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import { Checkbox } from "@mui/material";
export const createTimes = (
range: TimeRange,
numberOfNodes: number
): UnixTime[] => {
const oneSpan = range.duration.divide(numberOfNodes);
const roundedRange = TimeRange.fromTimes(
range.start.round(oneSpan),
range.end.round(oneSpan)
);
return roundedRange.sample(oneSpan);
};
const NUMBER_OF_NODES = 100;
const ScalarGraph = () => { const ScalarGraph = () => {
const timeRange = UnixTime.fromTicks(1682085650) const timeRange = createTimes(
.rangeBefore(TimeSpan.fromDays(1)) UnixTime.fromTicks(1682085650).rangeBefore(TimeSpan.fromDays(1)),
.sample(TimeSpan.fromMinutes(30)); NUMBER_OF_NODES
);
const [timeSeries, setTimeSeries] = useState<RecordSeries>([]); const [timeSeries, setTimeSeries] = useState<RecordSeries>([]);
const [uiRevision, setUiRevision] = useState(Math.random()); const [uiRevision, setUiRevision] = useState(Math.random());
const [range, setRange] = useState([ const [range, setRange] = useState([
timeRange[0].toDate(), timeRange[0].toDate(),
timeRange[timeRange.length - 1].toDate(), timeRange[timeRange.length - 1].toDate(),
]); ]);
const [toggles, setToggles] = useState<Object | null>(null); const [toggles, setToggles] = useState<TreeElement[] | null>(null);
const times$ = useMemo(() => new BehaviorSubject(timeRange), []); const times$ = useMemo(() => new BehaviorSubject(timeRange), []);
@ -30,6 +49,24 @@ const ScalarGraph = () => {
"" ""
); );
function insert(
children: TreeElement[] = [],
[head, ...tail]: string[]
): TreeElement[] {
let child = children.find((child) => child.name === head);
if (!child)
children.push(
(child = {
id: head + tail.join("_"),
name: head,
checked: false,
children: [],
})
);
if (tail.length > 0) insert(child.children, tail);
return children;
}
useEffect(() => { useEffect(() => {
const subscription = cache.gotData const subscription = cache.gotData
.pipe( .pipe(
@ -39,15 +76,76 @@ const ScalarGraph = () => {
) )
.subscribe(([_, times]) => { .subscribe(([_, times]) => {
const timeSeries = cache.getSeries(times); const timeSeries = cache.getSeries(times);
console.log("GOT DATA", _, times, timeSeries);
setTimeSeries(timeSeries); setTimeSeries(timeSeries);
if (toggles === null) setToggles(timeSeries); const toggleValues = timeSeries.find((timeStamp) => timeStamp.value);
if (toggles === null && toggleValues && toggleValues.value) {
setToggles(
Object.keys(toggleValues.value)
.map((path) => path.split("/").slice(1))
.reduce(
(children, path) => insert(children, path),
[] as TreeElement[]
)
);
}
}); });
return () => subscription.unsubscribe(); return () => subscription.unsubscribe();
}, []); }, []);
const roundToNearest30 = (date: Date) => { interface TreeElement {
id: string;
name: string;
children: TreeElement[];
checked: boolean;
}
const getNodes = (element: TreeElement): null | ReactNode => {
return element.children.length > 0 ? renderTree(element.children) : null;
};
const renderTree = (data: TreeElement[]): ReactNode => {
return data.map((element) => {
return (
<TreeItem
id={"checkbox-tree-" + element.name}
key={element.name}
nodeId={element.name}
label={
<>
<Checkbox
checked={element.checked}
onClick={() => {
if (toggles) {
const togglesValue = toggles;
const index = toggles?.findIndex((toggle) => {
return element.id === toggle.id;
});
if (index > 0) {
togglesValue[index] = {
...element,
checked: !element.checked,
};
setToggles(togglesValue);
}
}
}}
/>
{element.name}
</>
}
sx={{
".MuiTreeItem-content": { paddingY: "12px" },
}}
>
{getNodes(element)}
</TreeItem>
);
});
};
const round = (date: Date) => {
const minutes = 30; const minutes = 30;
const ms = 1000 * 60 * minutes; const ms = 1000 * 60 * minutes;
@ -72,7 +170,6 @@ const ScalarGraph = () => {
} }
}) })
.catch((e) => { .catch((e) => {
console.log(e);
return Promise.resolve(FetchResult.tryLater); return Promise.resolve(FetchResult.tryLater);
}); });
}; };
@ -116,20 +213,19 @@ const ScalarGraph = () => {
], ],
}} }}
onRelayout={(params) => { onRelayout={(params) => {
console.log("PARAMS", params);
const xaxisRange0 = params["xaxis.range[0]"]; const xaxisRange0 = params["xaxis.range[0]"];
const xaxisRange1 = params["xaxis.range[1]"]; const xaxisRange1 = params["xaxis.range[1]"];
if (xaxisRange0 && xaxisRange1) { 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)]); setRange([new Date(xaxisRange0), new Date(xaxisRange1)]);
setUiRevision(Math.random()); setUiRevision(Math.random());
const times = UnixTime.fromTicks(epoch1) const times = createTimes(
.rangeBefore(TimeSpan.fromSeconds(epoch1 - epoch0)) TimeRange.fromTimes(
.sample(TimeSpan.fromMinutes(30)); UnixTime.fromDate(new Date(xaxisRange0)),
UnixTime.fromDate(new Date(xaxisRange1))
),
NUMBER_OF_NODES
);
cache.getSeries(times); cache.getSeries(times);
times$.next(times); times$.next(times);
} }
@ -139,6 +235,25 @@ const ScalarGraph = () => {
}); });
}; };
return <>{renderGraphs()}</>; return (
<>
{renderGraphs()}
{toggles !== null && (
<TreeView
aria-label="rich object"
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
sx={{
height: 480,
flexGrow: 1,
overflow: "auto",
overflowX: "hidden",
}}
>
{renderTree(toggles)}
</TreeView>
)}
</>
);
}; };
export default ScalarGraph; export default ScalarGraph;

View File

@ -40,7 +40,6 @@ 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;