[WIP] modify pology, style it a bit
This commit is contained in:
parent
3a4c768074
commit
4b9f3e6a84
|
@ -103,6 +103,7 @@ const CheckboxTree = () => {
|
|||
overflowX: "hidden",
|
||||
position: ["sticky", "-webkit-sticky"],
|
||||
top: 1,
|
||||
maxHeight: "90vh",
|
||||
}}
|
||||
>
|
||||
{renderTree(toggles)}
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
import * as React from "react";
|
||||
import { Button } from "@mui/material";
|
||||
import { UnixTime, TimeSpan } from "../../../dataCache/time";
|
||||
import { createTimes } from "../../../util/graph.util";
|
||||
import {
|
||||
DatePicker,
|
||||
DateTimeValidationError,
|
||||
LocalizationProvider,
|
||||
} from "@mui/x-date-pickers";
|
||||
import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
|
||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||
import dayjs from "dayjs";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useState } from "react";
|
||||
import ShortcutButton from "./ShortcutButton";
|
||||
|
||||
interface DateRangePickerProps {
|
||||
setRange: (value: Date[]) => void;
|
||||
|
@ -19,10 +12,6 @@ interface DateRangePickerProps {
|
|||
}
|
||||
const DateRangePicker = (props: DateRangePickerProps) => {
|
||||
const { setRange, range, getCacheSeries } = props;
|
||||
const [fromDateError, setFromDateError] =
|
||||
useState<DateTimeValidationError | null>(null);
|
||||
const [toDateError, setToDateError] =
|
||||
useState<DateTimeValidationError | null>(null);
|
||||
|
||||
const handleChange = (fromDate: Date, toDate: Date) => {
|
||||
setRange([fromDate, toDate]);
|
||||
|
@ -31,26 +20,6 @@ const DateRangePicker = (props: DateRangePickerProps) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
const weekRange = createTimes(
|
||||
UnixTime.now().rangeBefore(TimeSpan.fromDays(7)),
|
||||
100
|
||||
);
|
||||
setRange([
|
||||
weekRange[0].toDate(),
|
||||
weekRange[weekRange.length - 1].toDate(),
|
||||
]);
|
||||
getCacheSeries(
|
||||
weekRange[0].ticks,
|
||||
weekRange[weekRange.length - 1].ticks
|
||||
);
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="lastWeek" defaultMessage="Last week" />
|
||||
</Button>
|
||||
</div>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
<DatePicker
|
||||
disableFuture
|
||||
|
@ -62,16 +31,6 @@ const DateRangePicker = (props: DateRangePickerProps) => {
|
|||
handleChange(newValue.toDate(), range[1]);
|
||||
}
|
||||
}}
|
||||
onError={(err) => setFromDateError(err)}
|
||||
slotProps={{
|
||||
textField: {
|
||||
variant: "outlined",
|
||||
error: !!fromDateError,
|
||||
helperText: fromDateError
|
||||
? "From date needs to be before to date"
|
||||
: "",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<DatePicker
|
||||
disableFuture
|
||||
|
@ -83,23 +42,36 @@ const DateRangePicker = (props: DateRangePickerProps) => {
|
|||
color: "red",
|
||||
},
|
||||
}}
|
||||
onError={(err) => setToDateError(err)}
|
||||
onChange={(newValue) => {
|
||||
if (newValue) {
|
||||
handleChange(range[0], newValue.toDate());
|
||||
}
|
||||
}}
|
||||
slotProps={{
|
||||
textField: {
|
||||
variant: "outlined",
|
||||
error: !!toDateError,
|
||||
helperText: toDateError
|
||||
? "To date needs to be after from date"
|
||||
: "",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
<div>
|
||||
<ShortcutButton
|
||||
dayRange={1}
|
||||
setRange={setRange}
|
||||
getCacheSeries={getCacheSeries}
|
||||
>
|
||||
<FormattedMessage id="today" defaultMessage="Today" />
|
||||
</ShortcutButton>
|
||||
<ShortcutButton
|
||||
dayRange={7}
|
||||
setRange={setRange}
|
||||
getCacheSeries={getCacheSeries}
|
||||
>
|
||||
<FormattedMessage id="lastWeek" defaultMessage="Last week" />
|
||||
</ShortcutButton>
|
||||
<ShortcutButton
|
||||
dayRange={30}
|
||||
setRange={setRange}
|
||||
getCacheSeries={getCacheSeries}
|
||||
>
|
||||
<FormattedMessage id="lastMonth" defaultMessage="Last month" />
|
||||
</ShortcutButton>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ const Log = () => {
|
|||
return (
|
||||
<>
|
||||
<TopologyView />
|
||||
|
||||
<ScalarGraph />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Plot from "react-plotly.js";
|
||||
import { RecordSeries } from "../../../dataCache/data";
|
||||
import {
|
||||
Csv,
|
||||
GraphData,
|
||||
createTimes,
|
||||
flattenToggles,
|
||||
|
@ -19,6 +20,11 @@ import { LogContext } from "../../Context/LogContextProvider";
|
|||
import { isDefined } from "../../../dataCache/utils/maybe";
|
||||
import { Data, Layout } from "plotly.js";
|
||||
import { VariableSizeList as List, areEqual } from "react-window";
|
||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import DateRangePicker from "./DateRangePicker";
|
||||
import { LocalizationProvider } from "@mui/x-date-pickers";
|
||||
import { Alert } from "@mui/material";
|
||||
|
||||
const NUMBER_OF_NODES = 100;
|
||||
|
||||
|
@ -526,9 +532,7 @@ const ScalarGraph = () => {
|
|||
return () => subscription.unsubscribe();
|
||||
}, [toggles]);
|
||||
|
||||
const fetchData = (
|
||||
timestamp: UnixTime
|
||||
): Promise<FetchResult<Record<string, number>>> => {
|
||||
const fetchData = (timestamp: UnixTime): Promise<FetchResult<Csv>> => {
|
||||
const s3Path = `${timestamp.ticks}.csv`;
|
||||
return s3Access
|
||||
.get(s3Path)
|
||||
|
@ -569,7 +573,7 @@ const ScalarGraph = () => {
|
|||
transformedObject[key].x.push(
|
||||
new Date(item.time.ticks * 1000).toISOString()
|
||||
);
|
||||
transformedObject[key].y.push(item.value?.[key]);
|
||||
transformedObject[key].y.push(item.value?.[key].value);
|
||||
});
|
||||
}
|
||||
if (
|
||||
|
@ -601,6 +605,7 @@ const ScalarGraph = () => {
|
|||
),
|
||||
NUMBER_OF_NODES
|
||||
);
|
||||
console.log("getcacheseries");
|
||||
cache.getSeries(times);
|
||||
times$.next(times);
|
||||
};
|
||||
|
@ -630,9 +635,6 @@ const ScalarGraph = () => {
|
|||
barnorm: "percent",
|
||||
}
|
||||
: {};
|
||||
if (!isScalar) {
|
||||
console.log("graphData", data[visibleGraphs[index]]);
|
||||
}
|
||||
return (
|
||||
<div style={style}>
|
||||
<Plot
|
||||
|
@ -679,13 +681,11 @@ const ScalarGraph = () => {
|
|||
return null;
|
||||
}, areEqual);
|
||||
|
||||
/* const renderGraphs = () => {
|
||||
if (checkedToggles) {
|
||||
if (
|
||||
checkedToggles &&
|
||||
Object.keys(checkedToggles).find((toggle) => checkedToggles[toggle])
|
||||
) {
|
||||
const coordinateTimeSeries = transformToGraphData(timeSeries);
|
||||
const visibleGraphs = Object.keys(coordinateTimeSeries).filter((path) => {
|
||||
return checkedToggles[path];
|
||||
});
|
||||
if (visibleGraphs.length > 0) {
|
||||
return (
|
||||
<>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
|
@ -695,65 +695,19 @@ const ScalarGraph = () => {
|
|||
getCacheSeries={getCacheSeries}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
{visibleGraphs.map((path) => {
|
||||
const isScalar = isNumeric(coordinateTimeSeries[path].y[0]);
|
||||
const data = isScalar
|
||||
? [
|
||||
{
|
||||
...coordinateTimeSeries[path],
|
||||
type: "scatter",
|
||||
mode: "lines+markers",
|
||||
fill: "tozeroy",
|
||||
},
|
||||
]
|
||||
: transformToBarGraphData(coordinateTimeSeries[path]);
|
||||
const barGraphLayout: Partial<Layout> = !isScalar
|
||||
? {
|
||||
bargap: 0,
|
||||
barmode: "stack",
|
||||
barnorm: "percent",
|
||||
<List
|
||||
height={1000}
|
||||
itemCount={
|
||||
Object.keys(checkedToggles).filter(
|
||||
(toggle) => checkedToggles[toggle]
|
||||
).length
|
||||
}
|
||||
: {};
|
||||
return (
|
||||
<Plot
|
||||
key={path}
|
||||
data={data as Data[]}
|
||||
layout={{
|
||||
width: 1000,
|
||||
height: 500,
|
||||
title: path,
|
||||
uirevision: uiRevision,
|
||||
xaxis: {
|
||||
autorange: false,
|
||||
range: range,
|
||||
type: "date",
|
||||
},
|
||||
yaxis: {
|
||||
rangemode: "tozero",
|
||||
},
|
||||
...barGraphLayout,
|
||||
}}
|
||||
config={{
|
||||
modeBarButtonsToRemove: [
|
||||
"lasso2d",
|
||||
"select2d",
|
||||
"pan2d",
|
||||
"autoScale2d",
|
||||
],
|
||||
}}
|
||||
onRelayout={(params) => {
|
||||
const xaxisRange0 = params["xaxis.range[0]"];
|
||||
const xaxisRange1 = params["xaxis.range[1]"];
|
||||
|
||||
if (xaxisRange0 && xaxisRange1) {
|
||||
setRange([new Date(xaxisRange0), new Date(xaxisRange1)]);
|
||||
setUiRevision(Math.random());
|
||||
getCacheSeries(xaxisRange0, xaxisRange1);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
itemSize={() => 500}
|
||||
width="100%"
|
||||
itemData={coordinateTimeSeries}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -765,30 +719,5 @@ const ScalarGraph = () => {
|
|||
/>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
}; */
|
||||
if (checkedToggles) {
|
||||
const coordinateTimeSeries = transformToGraphData(timeSeries);
|
||||
console.log(
|
||||
"length",
|
||||
Object.keys(checkedToggles).filter((toggle) => checkedToggles[toggle])
|
||||
.length
|
||||
);
|
||||
return (
|
||||
<List
|
||||
height={1000}
|
||||
itemCount={
|
||||
Object.keys(checkedToggles).filter((toggle) => checkedToggles[toggle])
|
||||
.length
|
||||
}
|
||||
itemSize={() => 500}
|
||||
width="100%"
|
||||
itemData={coordinateTimeSeries}
|
||||
>
|
||||
{Row}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
export default ScalarGraph;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
import { UnixTime, TimeSpan } from "../../../dataCache/time";
|
||||
import { createTimes } from "../../../util/graph.util";
|
||||
import InnovenergyButton from "../../Layout/InnovenergyButton";
|
||||
|
||||
interface ShortcutButtonProps {
|
||||
setRange: (value: Date[]) => void;
|
||||
getCacheSeries: (xaxisRange0: number, xaxisRange1: number) => void;
|
||||
dayRange: number;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const ShortcutButton = (props: ShortcutButtonProps) => {
|
||||
return (
|
||||
<InnovenergyButton
|
||||
onClick={() => {
|
||||
const weekRange = createTimes(
|
||||
UnixTime.now().rangeBefore(TimeSpan.fromDays(props.dayRange)),
|
||||
100
|
||||
);
|
||||
props.setRange([
|
||||
weekRange[0].toDate(),
|
||||
weekRange[weekRange.length - 1].toDate(),
|
||||
]);
|
||||
props.getCacheSeries(
|
||||
weekRange[0].ticks,
|
||||
weekRange[weekRange.length - 1].ticks
|
||||
);
|
||||
}}
|
||||
sx={{ mt: 2, mb: 2, mr: 2 }}
|
||||
>
|
||||
{props.children}
|
||||
</InnovenergyButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShortcutButton;
|
|
@ -34,6 +34,7 @@ const TopologyBox = (props: TopologyBoxProps) => {
|
|||
<div>
|
||||
{el.label}
|
||||
{el.values.map((value) => {
|
||||
console.log("value", value);
|
||||
return (
|
||||
<p
|
||||
style={{ marginBlockStart: "2px", marginBlockEnd: "2px" }}
|
|
@ -1,5 +1,5 @@
|
|||
import { Box } from "@mui/material";
|
||||
import TopologyBox, { TopologyBoxProps } from "./ToplogyBox";
|
||||
import TopologyBox, { TopologyBoxProps } from "./TopologyBox";
|
||||
import TopologyFlow, { TopologyFlowProps } from "./TopologyFlow";
|
||||
|
||||
type TopologyColumnProps = {
|
||||
|
|
|
@ -1,39 +1,65 @@
|
|||
import { Box } from "@mui/material";
|
||||
import { BOX_SIZE, BoxData } from "./ToplogyBox";
|
||||
import { BOX_SIZE, BoxData } from "./TopologyBox";
|
||||
import "./TopologyFlow.scss";
|
||||
|
||||
export type TopologyFlowProps = {
|
||||
orientation?: "vertical" | "horizontal";
|
||||
amount?: number;
|
||||
direction?: "leftToRight" | "rightToLeft";
|
||||
rightToLeft?: boolean;
|
||||
data?: BoxData[];
|
||||
hidden?: boolean;
|
||||
};
|
||||
const TopologyFlow = (props: TopologyFlowProps) => {
|
||||
const length = Math.abs((props.amount ?? 1) * (BOX_SIZE - 20));
|
||||
const length = Math.abs((props.amount ?? 1) * BOX_SIZE);
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: props.orientation === "horizontal" ? BOX_SIZE - 20 : length,
|
||||
height: props.orientation === "vertical" ? BOX_SIZE - 20 : length,
|
||||
backgroundColor: "#f4b3504d",
|
||||
visibility: props.hidden || !props.data ? "hidden" : "visible",
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
height: BOX_SIZE,
|
||||
width: BOX_SIZE,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: props.orientation === "horizontal" ? BOX_SIZE : length,
|
||||
height: props.orientation === "vertical" ? BOX_SIZE : length,
|
||||
backgroundColor: "#f4b3504d",
|
||||
visibility: props.hidden || !props.data ? "hidden" : "visible",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
{props.data?.map((value) => value.values)}
|
||||
<div
|
||||
className="container"
|
||||
style={{
|
||||
transform:
|
||||
props.orientation === "vertical"
|
||||
? "rotate(90deg)"
|
||||
: props.direction === "rightToLeft"
|
||||
: props.rightToLeft
|
||||
? "rotate(180deg)"
|
||||
: "",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
height: BOX_SIZE,
|
||||
width: BOX_SIZE,
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
transform:
|
||||
props.orientation === "vertical"
|
||||
? "rotate(-90deg)"
|
||||
: props.rightToLeft
|
||||
? "rotate(-180deg)"
|
||||
: "",
|
||||
}}
|
||||
>
|
||||
<div className="data-flow">
|
||||
{props.data?.map((value) => value.values)}
|
||||
</p>
|
||||
<div className="data-flow" style={{ overflow: "hidden" }}>
|
||||
<div className="dot"></div>
|
||||
<div className="dot"></div>
|
||||
<div className="dot"></div>
|
||||
|
@ -47,7 +73,7 @@ const TopologyFlow = (props: TopologyFlowProps) => {
|
|||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ const TopologyView = () => {
|
|||
flexDirection: "row",
|
||||
overflow: "auto",
|
||||
padding: 2,
|
||||
fontFamily: `"Ubuntu", sans-serif`,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
|
@ -27,9 +28,9 @@ const TopologyView = () => {
|
|||
data: values.acInBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.5,
|
||||
amount: 0.6,
|
||||
data: values.gridToAcIn,
|
||||
direction: "rightToLeft",
|
||||
rightToLeft: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -40,9 +41,8 @@ const TopologyView = () => {
|
|||
data: values.acInBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.5,
|
||||
amount: 0.3,
|
||||
data: values.gridToAcIn,
|
||||
direction: "leftToRight",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -55,7 +55,6 @@ const TopologyView = () => {
|
|||
centerConnection={{
|
||||
amount: 0.5,
|
||||
data: values.gridToAcIn,
|
||||
direction: "leftToRight",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -66,9 +65,8 @@ const TopologyView = () => {
|
|||
data: values.acOutBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.5,
|
||||
amount: 0.3,
|
||||
data: values.gridToAcIn,
|
||||
direction: "leftToRight",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -79,7 +77,7 @@ const TopologyView = () => {
|
|||
data: values.acOutBus,
|
||||
}}
|
||||
topConnection={{
|
||||
amount: 0.5,
|
||||
amount: 0.6,
|
||||
data: values.gridToAcIn,
|
||||
}}
|
||||
centerBox={{
|
||||
|
@ -87,9 +85,9 @@ const TopologyView = () => {
|
|||
data: values.acOutBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.5,
|
||||
amount: 0.2,
|
||||
data: values.gridToAcIn,
|
||||
direction: "rightToLeft",
|
||||
rightToLeft: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -100,7 +98,7 @@ const TopologyView = () => {
|
|||
data: values.acOutBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.5,
|
||||
amount: 0.8,
|
||||
data: values.gridToAcIn,
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { Maybe } from "yup";
|
||||
import { Timestamped } from "./types";
|
||||
import { isDefined } from "./utils/maybe";
|
||||
import { CsvEntry } from "../util/graph.util";
|
||||
|
||||
export type DataRecord = Record<string, number | string>;
|
||||
export type DataRecord = Record<string, CsvEntry>;
|
||||
|
||||
export type DataPoint = Timestamped<Maybe<DataRecord>>;
|
||||
export type RecordSeries = Array<DataPoint>;
|
||||
export type PointSeries = Array<Timestamped<Maybe<number | string>>>;
|
||||
export type DataSeries = Array<Maybe<number | string>>;
|
||||
export type PointSeries = Array<Timestamped<Maybe<CsvEntry>>>;
|
||||
export type DataSeries = Array<Maybe<CsvEntry>>;
|
||||
|
||||
export function getPoints(
|
||||
recordSeries: RecordSeries,
|
||||
|
|
|
@ -6,7 +6,8 @@ import { createDispatchQueue } from "./promiseQueue";
|
|||
import { SkipListNode } from "./skipList/skipListNode";
|
||||
import { RecordSeries } from "./data";
|
||||
import { Maybe, isUndefined } from "./utils/maybe";
|
||||
import { isNumber, isString } from "./utils/runtimeTypeChecking";
|
||||
import { isNumber } from "./utils/runtimeTypeChecking";
|
||||
import { CsvEntry } from "../util/graph.util";
|
||||
|
||||
export const FetchResult = {
|
||||
notAvailable: "N/A",
|
||||
|
@ -30,7 +31,7 @@ function reverseBits(x: number): number {
|
|||
return x >>> 0;
|
||||
}
|
||||
|
||||
export default class DataCache<T extends Record<string, number>> {
|
||||
export default class DataCache<T extends Record<string, CsvEntry>> {
|
||||
private readonly cache: SkipList<Maybe<T>> = new SkipList<Maybe<T>>();
|
||||
private readonly resolution: TimeSpan;
|
||||
|
||||
|
@ -103,15 +104,20 @@ export default class DataCache<T extends Record<string, number>> {
|
|||
const n = after.index - t;
|
||||
const pn = p + n;
|
||||
|
||||
let interpolated: Partial<Record<string, number>> = {};
|
||||
let interpolated: Partial<Record<string, CsvEntry>> = {};
|
||||
|
||||
//What about string nodes? like Alarms
|
||||
for (const k of Object.keys(dataBefore)) {
|
||||
interpolated[k] = isNumber(dataBefore[k])
|
||||
? (dataBefore[k] * n + dataAfter[k] * p) / pn
|
||||
const beforeData = dataBefore[k].value;
|
||||
const afterData = Number(dataAfter[k].value);
|
||||
let foo = interpolated[k];
|
||||
if (foo) {
|
||||
foo.value = isNumber(beforeData)
|
||||
? (beforeData * n + afterData * p) / pn
|
||||
: n < p
|
||||
? dataAfter[k]
|
||||
: dataBefore[k];
|
||||
? afterData
|
||||
: beforeData;
|
||||
}
|
||||
}
|
||||
|
||||
return interpolated as T;
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
import { TimeRange, UnixTime } from "../dataCache/time";
|
||||
import { DataPoint, DataRecord } from "../dataCache/data";
|
||||
import { isDefined } from "../dataCache/utils/maybe";
|
||||
import { BoxData } from "../components/Installations/Log/ToplogyBox";
|
||||
import { BoxData } from "../components/Installations/Log/TopologyBox";
|
||||
|
||||
export interface GraphCoordinates {
|
||||
x: Datum[] | Datum[][] | TypedArray;
|
||||
|
@ -50,22 +50,28 @@ export const extractTopologyValues = (
|
|||
timeSeriesData: DataPoint
|
||||
): TopologyValues | null => {
|
||||
const timeSeriesValue = timeSeriesData.value;
|
||||
let topologyValues: (string | number)[];
|
||||
if (isDefined(timeSeriesValue)) {
|
||||
return Object.keys(topologyValues).reduce((acc, topologyKey) => {
|
||||
const values = topologyValues[topologyKey].map(
|
||||
return Object.keys(topologyPaths).reduce((acc, topologyKey) => {
|
||||
const values = topologyPaths[topologyKey].map(
|
||||
(topologyPath) => timeSeriesValue[topologyPath]
|
||||
);
|
||||
console.log("values", topologyValues);
|
||||
switch (topologyKey) {
|
||||
case "gridToAcIn":
|
||||
topologyValues = [
|
||||
values.reduce((acc, curr) => Number(acc) + Number(curr.value), 0),
|
||||
];
|
||||
break;
|
||||
default:
|
||||
topologyValues = values.map(({ value }) => value);
|
||||
}
|
||||
return {
|
||||
...acc,
|
||||
[topologyKey]: [
|
||||
{
|
||||
values:
|
||||
topologyKey === "gridToAcIn"
|
||||
? [values.reduce((acc, curr) => Number(acc) + Number(curr))]
|
||||
: values,
|
||||
label: topologyValues[topologyKey][0].split("/").pop(),
|
||||
unit: "V",
|
||||
values: topologyValues,
|
||||
label: topologyPaths[topologyKey][0].split("/").pop(),
|
||||
unit: values[0].unit,
|
||||
} as BoxData,
|
||||
],
|
||||
};
|
||||
|
@ -74,7 +80,7 @@ export const extractTopologyValues = (
|
|||
return null;
|
||||
};
|
||||
|
||||
export const topologyValues: { [key: string]: string[] } = {
|
||||
export const topologyPaths: { [key: string]: string[] } = {
|
||||
gridToAcIn: [
|
||||
"/GridMeter/Ac/L1/Power/Apparent",
|
||||
"/GridMeter/Ac/L2/Power/Apparent",
|
||||
|
@ -101,7 +107,7 @@ export interface Csv {
|
|||
[key: string]: CsvEntry;
|
||||
}
|
||||
|
||||
export const parseCsv = (text: string) => {
|
||||
export const parseCsv = (text: string): Csv => {
|
||||
console.log("split", text.split(/\r?\n/));
|
||||
const y = text
|
||||
.split(/\r?\n/)
|
||||
|
@ -113,11 +119,11 @@ export const parseCsv = (text: string) => {
|
|||
const x = y
|
||||
.map((fields) => {
|
||||
if (typeof fields[1] === "string") {
|
||||
return { [fields[0]]: fields[1] };
|
||||
return { [fields[0]]: { value: fields[1], unit: fields[2] } };
|
||||
}
|
||||
return { [fields[0]]: parseFloat(fields[1]) };
|
||||
return { [fields[0]]: { value: parseFloat(fields[1]), unit: fields[2] } };
|
||||
})
|
||||
.reduce((acc, current) => ({ ...acc, ...current }), {} as any);
|
||||
.reduce((acc, current) => ({ ...acc, ...current }), {} as Csv);
|
||||
return x;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue