[WIP] add color and style to liveview, add new connections, refactor components, nearly finished
This commit is contained in:
parent
4b9f3e6a84
commit
9e56dffb27
|
@ -53,10 +53,13 @@ const UsersWithInheritedAccess = () => {
|
|||
<Link
|
||||
id={"inherited-access-user-link-" + user.id}
|
||||
to={
|
||||
routes.groups + routes.manageAccess + user.parentId
|
||||
routes.installations +
|
||||
routes.tree +
|
||||
routes.manageAccess +
|
||||
user.parentId
|
||||
}
|
||||
>
|
||||
{folderName}
|
||||
{` ${folderName}`}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
|
|||
import dayjs from "dayjs";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import ShortcutButton from "./ShortcutButton";
|
||||
import { createTimes } from "../../../util/graph.util";
|
||||
import { TimeRange, UnixTime } from "../../../dataCache/time";
|
||||
|
||||
interface DateRangePickerProps {
|
||||
setRange: (value: Date[]) => void;
|
||||
|
@ -14,8 +16,15 @@ const DateRangePicker = (props: DateRangePickerProps) => {
|
|||
const { setRange, range, getCacheSeries } = props;
|
||||
|
||||
const handleChange = (fromDate: Date, toDate: Date) => {
|
||||
setRange([fromDate, toDate]);
|
||||
getCacheSeries(fromDate.getMilliseconds(), toDate.getMilliseconds());
|
||||
const timeRange = createTimes(
|
||||
TimeRange.fromTimes(
|
||||
UnixTime.fromDate(fromDate),
|
||||
UnixTime.fromDate(toDate)
|
||||
),
|
||||
100
|
||||
);
|
||||
setRange([timeRange[0].toDate(), timeRange[timeRange.length - 1].toDate()]);
|
||||
getCacheSeries(timeRange[0].ticks, timeRange[timeRange.length - 1].ticks);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,7 +6,6 @@ const Log = () => {
|
|||
return (
|
||||
<>
|
||||
<TopologyView />
|
||||
|
||||
<ScalarGraph />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Plot from "react-plotly.js";
|
||||
import { RecordSeries } from "../../../dataCache/data";
|
||||
import { DataRecord, RecordSeries } from "../../../dataCache/data";
|
||||
import {
|
||||
Csv,
|
||||
GraphData,
|
||||
createTimes,
|
||||
flattenToggles,
|
||||
|
@ -481,12 +480,43 @@ export const testData = `/AcDc/SystemControl/Alarms;;
|
|||
/Config/HoldSocZone;1;
|
||||
/Config/ControllerPConstant;0.5;
|
||||
/SystemState/Message;Panic: Unknown State!;
|
||||
/SystemState/Id;100;`;
|
||||
/SystemState/Id;100;
|
||||
/LoadOnDc/Power;100;`;
|
||||
|
||||
const s3Access = new S3Access(
|
||||
"saliomameiringen",
|
||||
"sos-ch-dk-2",
|
||||
"exo.io",
|
||||
"EXO464a9ff62fdfa407aa742855",
|
||||
"f2KtCWN4EHFqtvH2kotdyI0w5SjjdHVPAADdcD3ik8g",
|
||||
""
|
||||
);
|
||||
export const fetchData = (
|
||||
timestamp: UnixTime
|
||||
): Promise<FetchResult<DataRecord>> => {
|
||||
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) => {
|
||||
return Promise.resolve(FetchResult.tryLater);
|
||||
});
|
||||
};
|
||||
|
||||
const ScalarGraph = () => {
|
||||
const timeRange = createTimes(
|
||||
UnixTime.now() /* .fromTicks(1682085650) */
|
||||
.rangeBefore(TimeSpan.fromDays(7)),
|
||||
.rangeBefore(TimeSpan.fromHours(5)),
|
||||
NUMBER_OF_NODES
|
||||
);
|
||||
const [timeSeries, setTimeSeries] = useState<RecordSeries>([]);
|
||||
|
@ -502,15 +532,6 @@ const ScalarGraph = () => {
|
|||
|
||||
const times$ = useMemo(() => new BehaviorSubject(timeRange), []);
|
||||
|
||||
const s3Access = new S3Access(
|
||||
"saliomameiringen",
|
||||
"sos-ch-dk-2",
|
||||
"exo.io",
|
||||
"EXO464a9ff62fdfa407aa742855",
|
||||
"f2KtCWN4EHFqtvH2kotdyI0w5SjjdHVPAADdcD3ik8g",
|
||||
""
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = cache.gotData
|
||||
.pipe(
|
||||
|
@ -532,26 +553,6 @@ const ScalarGraph = () => {
|
|||
return () => subscription.unsubscribe();
|
||||
}, [toggles]);
|
||||
|
||||
const fetchData = (timestamp: UnixTime): Promise<FetchResult<Csv>> => {
|
||||
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) => {
|
||||
return Promise.resolve(FetchResult.tryLater);
|
||||
});
|
||||
};
|
||||
|
||||
const cache = useMemo(
|
||||
() => new DataCache(fetchData, TimeSpan.fromSeconds(2)),
|
||||
[]
|
||||
|
@ -570,9 +571,7 @@ const ScalarGraph = () => {
|
|||
marker: { color: stringToColor(key) },
|
||||
};
|
||||
}
|
||||
transformedObject[key].x.push(
|
||||
new Date(item.time.ticks * 1000).toISOString()
|
||||
);
|
||||
transformedObject[key].x.push(new Date(item.time.ticks * 1000));
|
||||
transformedObject[key].y.push(item.value?.[key].value);
|
||||
});
|
||||
}
|
||||
|
@ -600,12 +599,11 @@ const ScalarGraph = () => {
|
|||
const getCacheSeries = (xaxisRange0: number, xaxisRange1: number) => {
|
||||
const times = createTimes(
|
||||
TimeRange.fromTimes(
|
||||
UnixTime.fromDate(new Date(xaxisRange0)),
|
||||
UnixTime.fromDate(new Date(xaxisRange1))
|
||||
UnixTime.fromTicks(xaxisRange0),
|
||||
UnixTime.fromTicks(xaxisRange1)
|
||||
),
|
||||
NUMBER_OF_NODES
|
||||
);
|
||||
console.log("getcacheseries");
|
||||
cache.getSeries(times);
|
||||
times$.next(times);
|
||||
};
|
||||
|
@ -615,7 +613,6 @@ const ScalarGraph = () => {
|
|||
const visibleGraphs = Object.keys(data).filter((path) => {
|
||||
return checkedToggles ? checkedToggles[path] : false;
|
||||
});
|
||||
|
||||
if (data[visibleGraphs[index]]) {
|
||||
const isScalar = isNumeric(data[visibleGraphs[index]].y[0]);
|
||||
const graphData = isScalar
|
||||
|
@ -685,7 +682,9 @@ const ScalarGraph = () => {
|
|||
checkedToggles &&
|
||||
Object.keys(checkedToggles).find((toggle) => checkedToggles[toggle])
|
||||
) {
|
||||
console.log("timeseries", timeSeries);
|
||||
const coordinateTimeSeries = transformToGraphData(timeSeries);
|
||||
console.log("timeSeries", timeSeries);
|
||||
return (
|
||||
<>
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs}>
|
||||
|
|
|
@ -17,6 +17,7 @@ const ShortcutButton = (props: ShortcutButtonProps) => {
|
|||
UnixTime.now().rangeBefore(TimeSpan.fromDays(props.dayRange)),
|
||||
100
|
||||
);
|
||||
console.log("weekrange", weekRange[0].ticks);
|
||||
props.setRange([
|
||||
weekRange[0].toDate(),
|
||||
weekRange[weekRange.length - 1].toDate(),
|
||||
|
|
|
@ -8,45 +8,61 @@ export type BoxData = {
|
|||
|
||||
export type TopologyBoxProps = {
|
||||
title?: string;
|
||||
data?: BoxData[];
|
||||
data?: BoxData;
|
||||
};
|
||||
|
||||
const isInt = (value: number) => {
|
||||
return value % 1 === 0;
|
||||
};
|
||||
|
||||
export const BOX_SIZE = 150;
|
||||
export const BOX_SIZE = 85;
|
||||
const TopologyBox = (props: TopologyBoxProps) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
border: "1px solid grey",
|
||||
visibility: props.title ? "visible" : "hidden",
|
||||
height: BOX_SIZE + "px",
|
||||
width: BOX_SIZE + "px",
|
||||
borderRadius: "4px",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ padding: "5px" }}>
|
||||
<p style={{ marginBlockStart: "2px" }}>{props.title}</p>
|
||||
{props.data &&
|
||||
props.data.map((el) => (
|
||||
<div>
|
||||
{el.label}
|
||||
{el.values.map((value) => {
|
||||
console.log("value", value);
|
||||
<p
|
||||
style={{
|
||||
marginBlockStart: "0",
|
||||
marginBlockEnd: "0",
|
||||
backgroundColor: "#e74c3c",
|
||||
padding: "5px",
|
||||
borderTopLeftRadius: "4px",
|
||||
borderTopRightRadius: "4px",
|
||||
}}
|
||||
>
|
||||
{props.title}
|
||||
</p>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "#c0392b",
|
||||
borderBottomLeftRadius: "4px",
|
||||
borderBottomRightRadius: "4px",
|
||||
padding: "5px",
|
||||
height: "52px",
|
||||
}}
|
||||
>
|
||||
{props.data && (
|
||||
<>
|
||||
{props.data.values.map((value) => {
|
||||
return (
|
||||
<p
|
||||
style={{ marginBlockStart: "2px", marginBlockEnd: "2px" }}
|
||||
style={{ marginBlockStart: "0", marginBlockEnd: "2px" }}
|
||||
// eslint-disable-next-line formatjs/no-literal-string-in-jsx
|
||||
>{`${
|
||||
!isInt(Number(value)) ? Number(value).toPrecision(8) : value
|
||||
}${el.unit}`}</p>
|
||||
!isInt(Number(value)) ? Number(value).toPrecision(4) : value
|
||||
}${props.data?.unit}`}</p>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
.container {
|
||||
position: relative;
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ import "./TopologyFlow.scss";
|
|||
export type TopologyFlowProps = {
|
||||
orientation?: "vertical" | "horizontal";
|
||||
amount?: number;
|
||||
rightToLeft?: boolean;
|
||||
data?: BoxData[];
|
||||
data?: BoxData;
|
||||
hidden?: boolean;
|
||||
};
|
||||
const TopologyFlow = (props: TopologyFlowProps) => {
|
||||
const length = Math.abs((props.amount ?? 1) * BOX_SIZE);
|
||||
const values = props.data?.values;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
@ -28,38 +28,36 @@ const TopologyFlow = (props: TopologyFlowProps) => {
|
|||
backgroundColor: "#f4b3504d",
|
||||
visibility: props.hidden || !props.data ? "hidden" : "visible",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="container"
|
||||
style={{
|
||||
transform:
|
||||
props.orientation === "vertical"
|
||||
? "rotate(90deg)"
|
||||
: props.rightToLeft
|
||||
? "rotate(180deg)"
|
||||
: "",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
height: BOX_SIZE,
|
||||
width: BOX_SIZE,
|
||||
alignItems: "center",
|
||||
position: "relative",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<p
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 1,
|
||||
transform:
|
||||
props.orientation === "vertical"
|
||||
? "rotate(-90deg)"
|
||||
: props.rightToLeft
|
||||
? "rotate(-180deg)"
|
||||
: "",
|
||||
}}
|
||||
>
|
||||
{props.data?.map((value) => value.values)}
|
||||
{values?.map(
|
||||
(value) => `${Math.round(value as number)} ${props.data?.unit}`
|
||||
)}
|
||||
</p>
|
||||
<div className="data-flow" style={{ overflow: "hidden" }}>
|
||||
<div
|
||||
className="container"
|
||||
style={{
|
||||
transform:
|
||||
props.orientation === "vertical"
|
||||
? "rotate(90deg)"
|
||||
: Number(values?.[0]) < 0
|
||||
? "rotate(180deg)"
|
||||
: "",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<div className="data-flow">
|
||||
<div className="dot"></div>
|
||||
<div className="dot"></div>
|
||||
<div className="dot"></div>
|
||||
|
|
|
@ -1,60 +1,119 @@
|
|||
import { Box } from "@mui/material";
|
||||
import TopologyColumn from "./TopologyColumn";
|
||||
import { UnixTime } from "../../../dataCache/time";
|
||||
import { extractTopologyValues, parseCsv } from "../../../util/graph.util";
|
||||
import { testData } from "./ScalarGraph";
|
||||
import { TimeSpan, UnixTime } from "../../../dataCache/time";
|
||||
import {
|
||||
TopologyValues,
|
||||
extractTopologyValues,
|
||||
getAmount,
|
||||
getHighestConnectionValue,
|
||||
} from "../../../util/graph.util";
|
||||
import { fetchData } from "./ScalarGraph";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FetchResult } from "../../../dataCache/dataCache";
|
||||
|
||||
const TopologyView = () => {
|
||||
const values = extractTopologyValues({
|
||||
time: UnixTime.fromTicks(192384239),
|
||||
value: parseCsv(testData),
|
||||
});
|
||||
console.log("csvValue", parseCsv(testData));
|
||||
const [values, setValues] = useState<TopologyValues | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
const now = UnixTime.now().earlier(TimeSpan.fromSeconds(15));
|
||||
fetchData(now.round(2))
|
||||
.then((res) => {
|
||||
if (
|
||||
res !== FetchResult.notAvailable &&
|
||||
res !== FetchResult.tryLater
|
||||
) {
|
||||
setValues(
|
||||
extractTopologyValues({
|
||||
time: now,
|
||||
value: res,
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
}, 2000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
if (values) {
|
||||
const highestConnectionValue = getHighestConnectionValue(values);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
overflow: "auto",
|
||||
padding: 2,
|
||||
fontFamily: `"Ubuntu", sans-serif`,
|
||||
fontSize: "12px",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<TopologyColumn
|
||||
centerBox={{
|
||||
title: "Grid",
|
||||
data: values.acInBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.6,
|
||||
data: values.gridToAcIn,
|
||||
rightToLeft: true,
|
||||
amount: getAmount(
|
||||
highestConnectionValue,
|
||||
values.gridToAcInConnection.values
|
||||
),
|
||||
data: values.gridToAcInConnection,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TopologyColumn
|
||||
centerBox={{
|
||||
title: "AcInBus",
|
||||
data: values.acInBus,
|
||||
title: "GridBus",
|
||||
data: values.islandBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.3,
|
||||
data: values.gridToAcIn,
|
||||
data: values.gridToAcInConnection,
|
||||
}}
|
||||
topConnection={{
|
||||
amount: getAmount(
|
||||
highestConnectionValue,
|
||||
values.gridBusToPvOnGridbusConnection.values
|
||||
),
|
||||
data: values.gridBusToPvOnGridbusConnection,
|
||||
}}
|
||||
topBox={{
|
||||
title: "PvOnGridBus",
|
||||
}}
|
||||
bottomConnection={{
|
||||
amount: getAmount(
|
||||
highestConnectionValue,
|
||||
values.gridBusToLoadOnGridBusConnection.values
|
||||
),
|
||||
data: values.gridBusToLoadOnGridBusConnection,
|
||||
}}
|
||||
bottomBox={{
|
||||
title: "LoadOnGridBus",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TopologyColumn
|
||||
centerBox={{
|
||||
title: "AcOutBus",
|
||||
data: values.acOutBus,
|
||||
title: "IslandBus",
|
||||
data: values.islandBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.5,
|
||||
data: values.gridToAcIn,
|
||||
data: values.gridToAcInConnection,
|
||||
}}
|
||||
bottomBox={{
|
||||
title: "LoadOnIslandBus",
|
||||
}}
|
||||
bottomConnection={{
|
||||
amount: getAmount(
|
||||
highestConnectionValue,
|
||||
values.islandBusToLoadOnIslandBusConnection.values
|
||||
),
|
||||
data: values.islandBusToLoadOnIslandBusConnection,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -62,32 +121,40 @@ const TopologyView = () => {
|
|||
<TopologyColumn
|
||||
centerBox={{
|
||||
title: "Inverter",
|
||||
data: values.acOutBus,
|
||||
data: values.islandBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.3,
|
||||
data: values.gridToAcIn,
|
||||
data: values.gridToAcInConnection,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TopologyColumn
|
||||
topBox={{
|
||||
title: "MPPT",
|
||||
data: values.acOutBus,
|
||||
title: "PvOnDc",
|
||||
}}
|
||||
topConnection={{
|
||||
amount: 0.6,
|
||||
data: values.gridToAcIn,
|
||||
data: values.gridToAcInConnection,
|
||||
}}
|
||||
centerBox={{
|
||||
title: "DcBus",
|
||||
data: values.acOutBus,
|
||||
data: values.dcBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: getAmount(
|
||||
highestConnectionValue,
|
||||
values.dcBusToDcDcConnection.values
|
||||
),
|
||||
data: values.dcBusToDcDcConnection,
|
||||
}}
|
||||
bottomConnection={{
|
||||
amount: 0.2,
|
||||
data: values.gridToAcIn,
|
||||
rightToLeft: true,
|
||||
data: values.dcBusToLoadOnDcConnection,
|
||||
}}
|
||||
bottomBox={{
|
||||
title: "LoadOnDc",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -95,11 +162,14 @@ const TopologyView = () => {
|
|||
<TopologyColumn
|
||||
centerBox={{
|
||||
title: "DcDc",
|
||||
data: values.acOutBus,
|
||||
data: values.islandBus,
|
||||
}}
|
||||
centerConnection={{
|
||||
amount: 0.8,
|
||||
data: values.gridToAcIn,
|
||||
amount: getAmount(
|
||||
highestConnectionValue,
|
||||
values.dcDCToBatteryConnection.values
|
||||
),
|
||||
data: values.dcDCToBatteryConnection,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -107,7 +177,7 @@ const TopologyView = () => {
|
|||
<TopologyColumn
|
||||
centerBox={{
|
||||
title: "Battery",
|
||||
data: values.acOutBus,
|
||||
data: values.battery,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -35,29 +35,60 @@ export interface GraphData {
|
|||
[path: string]: GraphCoordinates;
|
||||
}
|
||||
|
||||
// connections must have the word Connection in the prop name, so the topology works correctly
|
||||
export type TopologyValues = {
|
||||
gridToAcIn: BoxData[];
|
||||
acInBus: BoxData[];
|
||||
acOutBus: BoxData[];
|
||||
dcBus: BoxData[];
|
||||
mpptToDcBus: BoxData[];
|
||||
dcBusToDcDc: BoxData[];
|
||||
dcDCToBattery: BoxData[];
|
||||
battery: BoxData[];
|
||||
gridToAcInConnection: BoxData;
|
||||
gridBus: BoxData;
|
||||
islandBus: BoxData;
|
||||
dcBus: BoxData;
|
||||
dcBusToDcDcConnection: BoxData;
|
||||
dcDCToBatteryConnection: BoxData;
|
||||
battery: BoxData;
|
||||
dcBusToLoadOnDcConnection: BoxData;
|
||||
islandBusToLoadOnIslandBusConnection: BoxData;
|
||||
gridBusToPvOnGridbusConnection: BoxData;
|
||||
gridBusToLoadOnGridBusConnection: BoxData;
|
||||
};
|
||||
|
||||
type TopologyPaths = { [key in keyof TopologyValues]: string[] };
|
||||
|
||||
export const topologyPaths: TopologyPaths = {
|
||||
gridToAcInConnection: [
|
||||
"/GridMeter/Ac/L1/Power/Apparent",
|
||||
"/GridMeter/Ac/L2/Power/Apparent",
|
||||
"/GridMeter/Ac/L3/Power/Apparent",
|
||||
],
|
||||
gridBus: ["/GridMeter/Ac/L2/Voltage"],
|
||||
islandBus: [
|
||||
"/AcDc/Ac/L1/Voltage",
|
||||
"/AcDc/Ac/L2/Voltage",
|
||||
"/AcDc/Ac/L3/Voltage",
|
||||
],
|
||||
dcBus: ["/AcDc/Dc/Voltage"],
|
||||
dcBusToDcDcConnection: ["/DcDc/Dc/Link/Power"],
|
||||
dcDCToBatteryConnection: ["/DcDc/Dc/Battery/Power"],
|
||||
battery: ["/Battery/Soc", "/Battery/Dc/Voltage"],
|
||||
dcBusToLoadOnDcConnection: ["/LoadOnDc/Power"],
|
||||
islandBusToLoadOnIslandBusConnection: ["/LoadOnAcIsland/Ac/Power/Apparent"],
|
||||
gridBusToPvOnGridbusConnection: ["/PvOnAcGrid/Power/Apparent"],
|
||||
gridBusToLoadOnGridBusConnection: ["/LoadOnAcGrid/Power/Apparent"],
|
||||
};
|
||||
|
||||
const getBoxColor = () => {};
|
||||
|
||||
export const extractTopologyValues = (
|
||||
timeSeriesData: DataPoint
|
||||
): TopologyValues | null => {
|
||||
const timeSeriesValue = timeSeriesData.value;
|
||||
let topologyValues: (string | number)[];
|
||||
if (isDefined(timeSeriesValue)) {
|
||||
return Object.keys(topologyPaths).reduce((acc, topologyKey) => {
|
||||
const values = topologyPaths[topologyKey].map(
|
||||
let topologyValues: (string | number)[];
|
||||
const values = topologyPaths[topologyKey as keyof TopologyValues].map(
|
||||
(topologyPath) => timeSeriesValue[topologyPath]
|
||||
);
|
||||
switch (topologyKey) {
|
||||
case "gridToAcIn":
|
||||
console.log("AAA", topologyKey);
|
||||
switch (topologyKey as keyof TopologyValues) {
|
||||
case "gridToAcInConnection":
|
||||
topologyValues = [
|
||||
values.reduce((acc, curr) => Number(acc) + Number(curr.value), 0),
|
||||
];
|
||||
|
@ -65,50 +96,45 @@ export const extractTopologyValues = (
|
|||
default:
|
||||
topologyValues = values.map(({ value }) => value);
|
||||
}
|
||||
console.log("topologyValues", topologyValues);
|
||||
return {
|
||||
...acc,
|
||||
[topologyKey]: [
|
||||
{
|
||||
[topologyKey]: {
|
||||
values: topologyValues,
|
||||
label: topologyPaths[topologyKey][0].split("/").pop(),
|
||||
label: topologyPaths[topologyKey as keyof TopologyValues][0]
|
||||
.split("/")
|
||||
.pop(),
|
||||
unit: values[0].unit,
|
||||
} as BoxData,
|
||||
],
|
||||
};
|
||||
}, {} as TopologyValues);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const topologyPaths: { [key: string]: string[] } = {
|
||||
gridToAcIn: [
|
||||
"/GridMeter/Ac/L1/Power/Apparent",
|
||||
"/GridMeter/Ac/L2/Power/Apparent",
|
||||
"/GridMeter/Ac/L3/Power/Apparent",
|
||||
],
|
||||
acInBus: ["/GridMeter/Ac/L2/Voltage"],
|
||||
acOutBus: [
|
||||
"/AcDc/Ac/L1/Voltage",
|
||||
"/AcDc/Ac/L2/Voltage",
|
||||
"/AcDc/Ac/L3/Voltage",
|
||||
],
|
||||
dcBus: ["/AcDc/Dc/Voltage"],
|
||||
mpptToDcBus: ["/Mppt/Dc/Power"],
|
||||
dcBusToDcDc: ["/DcDc/Dc/Link/Power"],
|
||||
dcDCToBattery: ["/DcDc/Dc/Battery/Power"],
|
||||
battery: ["/Battery/Soc", "/Battery/Dc/Voltage"],
|
||||
export const getHighestConnectionValue = (values: TopologyValues) =>
|
||||
Object.keys(values)
|
||||
.filter((value) => value.includes("Connection"))
|
||||
.reduce((acc, curr) => {
|
||||
const value = values[curr as keyof TopologyValues].values[0] as number;
|
||||
console.log("reduce", value, acc);
|
||||
return value > acc ? value : acc;
|
||||
}, 0);
|
||||
|
||||
export const getAmount = (
|
||||
highestConnectionValue: number,
|
||||
values: (string | number)[]
|
||||
) => {
|
||||
console.log("amount", values[0] as number, highestConnectionValue);
|
||||
return (values[0] as number) / highestConnectionValue;
|
||||
};
|
||||
|
||||
export interface CsvEntry {
|
||||
value: string | number;
|
||||
unit: string;
|
||||
}
|
||||
export interface Csv {
|
||||
[key: string]: CsvEntry;
|
||||
}
|
||||
|
||||
export const parseCsv = (text: string): Csv => {
|
||||
console.log("split", text.split(/\r?\n/));
|
||||
export const parseCsv = (text: string): DataRecord => {
|
||||
const y = text
|
||||
.split(/\r?\n/)
|
||||
.filter((split) => split.length > 0)
|
||||
|
@ -118,12 +144,12 @@ export const parseCsv = (text: string): Csv => {
|
|||
|
||||
const x = y
|
||||
.map((fields) => {
|
||||
if (typeof fields[1] === "string") {
|
||||
if (isNaN(Number(fields[1]))) {
|
||||
return { [fields[0]]: { value: fields[1], unit: fields[2] } };
|
||||
}
|
||||
return { [fields[0]]: { value: parseFloat(fields[1]), unit: fields[2] } };
|
||||
})
|
||||
.reduce((acc, current) => ({ ...acc, ...current }), {} as Csv);
|
||||
.reduce((acc, current) => ({ ...acc, ...current }), {} as DataRecord);
|
||||
return x;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue