Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
3cf82b3153
|
@ -22,6 +22,7 @@
|
|||
"clsx": "1.1.1",
|
||||
"cytoscape": "^3.26.0",
|
||||
"date-fns": "^2.28.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"history": "5.3.0",
|
||||
"linq-to-typescript": "^11.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
|
@ -7103,6 +7104,11 @@
|
|||
"url": "https://opencollective.com/date-fns"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.10",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
|
||||
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
|
@ -23246,6 +23252,11 @@
|
|||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz",
|
||||
"integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw=="
|
||||
},
|
||||
"dayjs": {
|
||||
"version": "1.11.10",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
|
||||
"integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
"clsx": "1.1.1",
|
||||
"cytoscape": "^3.26.0",
|
||||
"date-fns": "^2.28.0",
|
||||
"dayjs": "^1.11.10",
|
||||
"history": "5.3.0",
|
||||
"linq-to-typescript": "^11.0.0",
|
||||
"nprogress": "0.2.0",
|
||||
|
|
|
@ -5,6 +5,38 @@ import { DataRecord } from 'src/dataCache/data';
|
|||
import { S3Access } from 'src/dataCache/S3/S3Access';
|
||||
import { parseCsv } from '../Log/graph.util';
|
||||
|
||||
export const fetchDailyData = (
|
||||
date: string,
|
||||
s3Credentials?: I_S3Credentials
|
||||
): Promise<FetchResult<DataRecord>> => {
|
||||
const s3Path = `${date}.csv`;
|
||||
if (s3Credentials && s3Credentials.s3Bucket) {
|
||||
const s3Access = new S3Access(
|
||||
s3Credentials.s3Bucket,
|
||||
s3Credentials.s3Region,
|
||||
s3Credentials.s3Provider,
|
||||
s3Credentials.s3Key,
|
||||
s3Credentials.s3Secret
|
||||
);
|
||||
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();
|
||||
//console.log(parseCsv(text));
|
||||
return parseCsv(text);
|
||||
} else {
|
||||
return Promise.resolve(FetchResult.notAvailable);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
return Promise.resolve(FetchResult.tryLater);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchData = (
|
||||
timestamp: UnixTime,
|
||||
s3Credentials?: I_S3Credentials
|
||||
|
|
|
@ -159,20 +159,56 @@ export const getChartOptions = (
|
|||
enabled: true
|
||||
}
|
||||
},
|
||||
|
||||
yaxis: {
|
||||
axisBorder: {
|
||||
show: false
|
||||
min:
|
||||
chartInfo.min >= 0
|
||||
? 0
|
||||
: chartInfo.max <= 0
|
||||
? Math.ceil(chartInfo.min / findPower(chartInfo.min).value) *
|
||||
findPower(chartInfo.min).value
|
||||
: undefined,
|
||||
max:
|
||||
chartInfo.min >= 0
|
||||
? Math.ceil(chartInfo.max / findPower(chartInfo.max).value) *
|
||||
findPower(chartInfo.max).value
|
||||
: chartInfo.max <= 0
|
||||
? 0
|
||||
: undefined,
|
||||
title: {
|
||||
text: chartInfo.unit,
|
||||
style: {
|
||||
fontSize: '12px'
|
||||
},
|
||||
axisTicks: {
|
||||
show: false
|
||||
offsetY: -160,
|
||||
offsetX: 25,
|
||||
rotate: 0
|
||||
},
|
||||
labels: {
|
||||
show: false,
|
||||
formatter: function (val) {
|
||||
return val + '%';
|
||||
formatter: function (value: number) {
|
||||
return formatPowerForGraph(
|
||||
value,
|
||||
Math.max(Math.abs(chartInfo.max), Math.abs(chartInfo.min))
|
||||
).value.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// yaxis: {
|
||||
// axisBorder: {
|
||||
// show: false
|
||||
// },
|
||||
// axisTicks: {
|
||||
// show: false
|
||||
// },
|
||||
// labels: {
|
||||
// show: false,
|
||||
// formatter: function (val) {
|
||||
// return val + '%';
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
: {
|
||||
chart: {
|
||||
|
@ -189,8 +225,15 @@ export const getChartOptions = (
|
|||
},
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
formatter: function (val) {
|
||||
return val + '%';
|
||||
formatter: function (val, opts) {
|
||||
return (
|
||||
formatPowerForGraph(
|
||||
val,
|
||||
Math.max(Math.abs(chartInfo.max), Math.abs(chartInfo.min))
|
||||
).value.toFixed(2) +
|
||||
' ' +
|
||||
chartInfo.unit
|
||||
);
|
||||
},
|
||||
offsetY: -20,
|
||||
style: {
|
||||
|
@ -213,19 +256,66 @@ export const getChartOptions = (
|
|||
}
|
||||
},
|
||||
yaxis: {
|
||||
axisBorder: {
|
||||
show: false
|
||||
min:
|
||||
chartInfo.min >= 0
|
||||
? 0
|
||||
: chartInfo.max <= 0
|
||||
? Math.ceil(chartInfo.min / findPower(chartInfo.min).value) *
|
||||
findPower(chartInfo.min).value
|
||||
: undefined,
|
||||
max:
|
||||
chartInfo.min >= 0
|
||||
? Math.ceil(chartInfo.max / findPower(chartInfo.max).value) *
|
||||
findPower(chartInfo.max).value
|
||||
: chartInfo.max <= 0
|
||||
? 0
|
||||
: undefined,
|
||||
title: {
|
||||
text: chartInfo.unit,
|
||||
style: {
|
||||
fontSize: '12px'
|
||||
},
|
||||
axisTicks: {
|
||||
show: false
|
||||
offsetY: -160,
|
||||
offsetX: 25,
|
||||
rotate: 0
|
||||
},
|
||||
labels: {
|
||||
show: false,
|
||||
formatter: function (val) {
|
||||
return val + '%';
|
||||
formatter: function (value: number) {
|
||||
return formatPowerForGraph(
|
||||
value,
|
||||
Math.max(Math.abs(chartInfo.max), Math.abs(chartInfo.min))
|
||||
).value.toString();
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
y: {
|
||||
formatter: function (val, opts) {
|
||||
return (
|
||||
formatPowerForGraph(
|
||||
val,
|
||||
Math.max(Math.abs(chartInfo.max), Math.abs(chartInfo.min))
|
||||
).value.toFixed(2) +
|
||||
' ' +
|
||||
chartInfo.unit
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// yaxis: {
|
||||
// axisBorder: {
|
||||
// show: false
|
||||
// },
|
||||
// axisTicks: {
|
||||
// show: false
|
||||
// },
|
||||
// labels: {
|
||||
// show: false,
|
||||
// formatter: function (val) {
|
||||
// return val + '%';
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
return chartOptions;
|
||||
|
|
|
@ -1,302 +1,246 @@
|
|||
import { Box, Card, Container, Grid, Typography } from '@mui/material';
|
||||
import ReactApexChart from 'react-apexcharts';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import DataCache from 'src/dataCache/dataCache';
|
||||
import { TimeRange, TimeSpan, UnixTime } from 'src/dataCache/time';
|
||||
import {
|
||||
BehaviorSubject,
|
||||
combineLatest,
|
||||
startWith,
|
||||
tap,
|
||||
throttleTime
|
||||
} from 'rxjs';
|
||||
import { RecordSeries } from 'src/dataCache/data';
|
||||
import { createTimes } from '../Log/graph.util';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { I_S3Credentials } from 'src/interfaces/S3Types';
|
||||
import { getChartOptions } from './chartOptions';
|
||||
import { chartDataInterface, overviewInterface } from 'src/interfaces/Chart';
|
||||
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
|
||||
import {
|
||||
chartAggregatedDataInterface,
|
||||
chartDataInterface,
|
||||
overviewInterface,
|
||||
transformInputToAggregatedData,
|
||||
transformInputToDailyData
|
||||
} from 'src/interfaces/Chart';
|
||||
import Button from '@mui/material/Button';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
const prefixes = ['', 'k', 'M', 'G', 'T'];
|
||||
const MAX_NUMBER = 9999999;
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import { TimeSpan, UnixTime } from '../../../dataCache/time';
|
||||
|
||||
interface OverviewProps {
|
||||
s3Credentials: I_S3Credentials;
|
||||
}
|
||||
|
||||
function Overview(props: OverviewProps) {
|
||||
const numOfPointsToFetch = 100;
|
||||
const [timeRange, setTimeRange] = useState(
|
||||
createTimes(
|
||||
UnixTime.now().rangeBefore(TimeSpan.fromDays(1)),
|
||||
numOfPointsToFetch
|
||||
)
|
||||
);
|
||||
// const numOfPointsToFetch = 100;
|
||||
// const [timeRange, setTimeRange] = useState(
|
||||
// createTimes(
|
||||
// UnixTime.now().rangeBefore(TimeSpan.fromDays(1)),
|
||||
// numOfPointsToFetch
|
||||
// )
|
||||
// );
|
||||
//
|
||||
// const cache = useMemo(() => {
|
||||
// return new DataCache(
|
||||
// fetchData,
|
||||
// TimeSpan.fromSeconds(2),
|
||||
// props.s3Credentials
|
||||
// );
|
||||
// }, []);
|
||||
|
||||
const [chartData, setChartData] = useState<chartDataInterface>({
|
||||
soc: [],
|
||||
temperature: [],
|
||||
dcPower: [],
|
||||
gridPower: [],
|
||||
pvProduction: [],
|
||||
dcBusVoltage: []
|
||||
});
|
||||
|
||||
const [chartOverview, setChartOverview] = useState<overviewInterface>({
|
||||
soc: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
temperature: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
dcPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 }
|
||||
});
|
||||
|
||||
const pathsToSearch = [
|
||||
'/Battery/Soc',
|
||||
'/Battery/Temperature',
|
||||
'/Battery/Dc/Power',
|
||||
'/GridMeter/Ac/Power/Active',
|
||||
'/PvOnDc/Dc/Power',
|
||||
'/DcDc/Dc/Link/Voltage'
|
||||
];
|
||||
|
||||
const cache = useMemo(() => {
|
||||
return new DataCache(
|
||||
fetchData,
|
||||
TimeSpan.fromSeconds(2),
|
||||
props.s3Credentials
|
||||
);
|
||||
}, []);
|
||||
|
||||
const times$ = useMemo(() => new BehaviorSubject(timeRange), []);
|
||||
//const times$ = useMemo(() => new BehaviorSubject(timeRange), []);
|
||||
const [dailyData, setDailyData] = useState(true);
|
||||
const [weeklyData, setWeeklyData] = useState(false);
|
||||
const [monthlyData, setMonthlyData] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [chartState, setChartState] = useState(0);
|
||||
|
||||
const transformToGraphData = (
|
||||
input: RecordSeries
|
||||
): {
|
||||
// const [chartData, setChartData] = useState<chartDataInterface>({
|
||||
// soc: [],
|
||||
// temperature: [],
|
||||
// dcPower: [],
|
||||
// gridPower: [],
|
||||
// pvProduction: [],
|
||||
// dcBusVoltage: []
|
||||
// });
|
||||
//
|
||||
// const [chartOverview, setChartOverview] = useState<overviewInterface>({
|
||||
// soc: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
// temperature: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
// dcPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
// gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
// pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
// dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 }
|
||||
// });
|
||||
|
||||
const [dailyDataArray, setDailyDataArray] = useState<
|
||||
{
|
||||
chartData: chartDataInterface;
|
||||
chartOverview: overviewInterface;
|
||||
} => {
|
||||
const data = {};
|
||||
const overviewData = {};
|
||||
}[]
|
||||
>([]);
|
||||
|
||||
const chartData: chartDataInterface = {
|
||||
soc: [],
|
||||
temperature: [],
|
||||
dcPower: [],
|
||||
gridPower: [],
|
||||
pvProduction: [],
|
||||
dcBusVoltage: []
|
||||
};
|
||||
const [chartAggregatedData, setChartAggregatedData] =
|
||||
useState<chartAggregatedDataInterface>({
|
||||
soc: [{ data: [] }],
|
||||
pvProduction: [{ data: [] }],
|
||||
dcPower: [{ data: [] }]
|
||||
});
|
||||
|
||||
const chartOverview: overviewInterface = {
|
||||
const [chartAggregatedOverview, setChartAggregatedOverview] =
|
||||
useState<overviewInterface>({
|
||||
soc: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
temperature: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
dcPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 }
|
||||
};
|
||||
|
||||
pathsToSearch.forEach((path) => {
|
||||
data[path] = [];
|
||||
overviewData[path] = {
|
||||
magnitude: 0,
|
||||
unit: '',
|
||||
min: MAX_NUMBER,
|
||||
max: -MAX_NUMBER
|
||||
};
|
||||
});
|
||||
|
||||
//console.log(input);
|
||||
input.forEach((item) => {
|
||||
const csvContent = item.value;
|
||||
pathsToSearch.forEach((path) => {
|
||||
if (csvContent) {
|
||||
const timestamp = item.time.ticks * 1000;
|
||||
|
||||
const adjustedTimestamp = new Date(timestamp);
|
||||
adjustedTimestamp.setHours(adjustedTimestamp.getHours() + 1);
|
||||
|
||||
if (csvContent[path]) {
|
||||
const value = csvContent[path];
|
||||
|
||||
if (value.value < overviewData[path].min) {
|
||||
overviewData[path].min = value.value;
|
||||
}
|
||||
|
||||
if (value.value > overviewData[path].max) {
|
||||
overviewData[path].max = value.value;
|
||||
}
|
||||
|
||||
data[path].push([adjustedTimestamp, value.value]);
|
||||
} else {
|
||||
//data[path].push([adjustedTimestamp, null]);
|
||||
}
|
||||
} else {
|
||||
// data[path].push([
|
||||
// addHours(new Date(item.time.ticks * 1000), 2),
|
||||
// null
|
||||
// ]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
pathsToSearch.forEach((path) => {
|
||||
let value = Math.max(
|
||||
Math.abs(overviewData[path].max),
|
||||
Math.abs(overviewData[path].min)
|
||||
);
|
||||
let magnitude = 0;
|
||||
|
||||
if (value < 0) {
|
||||
value = -value;
|
||||
}
|
||||
while (value >= 1000) {
|
||||
value /= 1000;
|
||||
magnitude++;
|
||||
}
|
||||
overviewData[path].magnitude = prefixes[magnitude];
|
||||
});
|
||||
|
||||
let path = '/Battery/Soc';
|
||||
chartData.soc = [{ name: 'State of Charge', data: data[path] }];
|
||||
|
||||
chartOverview.soc = {
|
||||
unit: '(%)',
|
||||
magnitude: overviewData[path].magnitude,
|
||||
min: 0,
|
||||
max: 100
|
||||
};
|
||||
|
||||
path = '/Battery/Temperature';
|
||||
chartData.temperature = [
|
||||
{ name: 'Battery Temperature:', data: data[path] }
|
||||
];
|
||||
|
||||
chartOverview.temperature = {
|
||||
unit: '(°C)',
|
||||
magnitude: overviewData[path].magnitude,
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/Battery/Dc/Power';
|
||||
chartData.dcPower = [{ name: 'Battery Power', data: data[path] }];
|
||||
|
||||
chartOverview.dcPower = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'W' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/GridMeter/Ac/Power/Active';
|
||||
chartData.gridPower = [{ name: 'Grid Power', data: data[path] }];
|
||||
|
||||
chartOverview.gridPower = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'W' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/PvOnDc/Dc/Power';
|
||||
chartData.pvProduction = [{ name: 'Pv Production', data: data[path] }];
|
||||
|
||||
chartOverview.pvProduction = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'W' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/DcDc/Dc/Link/Voltage';
|
||||
chartData.dcBusVoltage = [{ name: 'DC Bus Voltage', data: data[path] }];
|
||||
|
||||
chartOverview.dcBusVoltage = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'V' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
return {
|
||||
chartData: chartData,
|
||||
chartOverview: chartOverview
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const left = cache.gotData.pipe(
|
||||
startWith(UnixTime.fromTicks(0)),
|
||||
throttleTime(200, undefined, { leading: true, trailing: true })
|
||||
);
|
||||
|
||||
const right = times$.pipe(
|
||||
tap((times) => {
|
||||
//console.log(times);
|
||||
})
|
||||
);
|
||||
|
||||
const combined = combineLatest([left, right]);
|
||||
|
||||
const subscription = combined.subscribe(([_, times]) => {
|
||||
const timeSeries = cache.getSeries(times);
|
||||
const result: {
|
||||
const resultPromise: Promise<{
|
||||
chartData: chartDataInterface;
|
||||
chartOverview: overviewInterface;
|
||||
} = transformToGraphData(timeSeries);
|
||||
}> = transformInputToDailyData(
|
||||
props.s3Credentials,
|
||||
UnixTime.now().rangeBefore(TimeSpan.fromDays(1)).start,
|
||||
UnixTime.now()
|
||||
);
|
||||
|
||||
setChartData(result.chartData);
|
||||
setChartOverview(result.chartOverview);
|
||||
resultPromise
|
||||
.then((result) => {
|
||||
// setChartData(result.chartData);
|
||||
// setChartOverview(result.chartOverview);
|
||||
|
||||
setDailyDataArray((prevData) =>
|
||||
prevData.concat({
|
||||
chartData: result.chartData,
|
||||
chartOverview: result.chartOverview
|
||||
})
|
||||
);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
return () => subscription.unsubscribe();
|
||||
|
||||
// const left = cache.gotData.pipe(
|
||||
// startWith(UnixTime.fromTicks(0)),
|
||||
// throttleTime(200, undefined, { leading: true, trailing: true })
|
||||
// );
|
||||
//
|
||||
// const right = times$.pipe(
|
||||
// tap((times) => {
|
||||
// //console.log(times);
|
||||
// })
|
||||
// );
|
||||
//
|
||||
// const combined = combineLatest([left, right]);
|
||||
//
|
||||
// const subscription = combined.subscribe(([_, times]) => {
|
||||
// const timeSeries = cache.getSeries(times);
|
||||
// const result: {
|
||||
// chartData: chartDataInterface;
|
||||
// chartOverview: overviewInterface;
|
||||
// } = transformInputToDailyData(timeSeries);
|
||||
//
|
||||
// setChartData(result.chartData);
|
||||
// setChartOverview(result.chartOverview);
|
||||
// });
|
||||
// return () => subscription.unsubscribe();
|
||||
}, []);
|
||||
|
||||
const handleBeforeZoom = (chartContext, { xaxis }) => {
|
||||
const startX = parseInt(xaxis.min) / 1000;
|
||||
const endX = parseInt(xaxis.max) / 1000;
|
||||
|
||||
const times = createTimes(
|
||||
TimeRange.fromTimes(UnixTime.fromTicks(startX), UnixTime.fromTicks(endX)),
|
||||
numOfPointsToFetch
|
||||
//console.log(UnixTime.fromTicks(startX));
|
||||
//console.log(endX);
|
||||
|
||||
setLoading(true);
|
||||
const resultPromise: Promise<{
|
||||
chartData: chartDataInterface;
|
||||
chartOverview: overviewInterface;
|
||||
}> = transformInputToDailyData(
|
||||
props.s3Credentials,
|
||||
UnixTime.fromTicks(startX),
|
||||
UnixTime.fromTicks(endX)
|
||||
);
|
||||
|
||||
cache.getSeries(times);
|
||||
times$.next(times);
|
||||
};
|
||||
resultPromise
|
||||
.then((result) => {
|
||||
//setChartData(result.chartData);
|
||||
//setChartOverview(result.chartOverview);
|
||||
|
||||
const handleDoubleClick = () => {
|
||||
const times = createTimes(
|
||||
UnixTime.now().rangeBefore(TimeSpan.fromDays(1)),
|
||||
numOfPointsToFetch
|
||||
setDailyDataArray((prevData) =>
|
||||
prevData.concat({
|
||||
chartData: result.chartData,
|
||||
chartOverview: result.chartOverview
|
||||
})
|
||||
);
|
||||
cache.getSeries(times);
|
||||
times$.next(times);
|
||||
|
||||
setLoading(false);
|
||||
setChartState(dailyDataArray.length);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
|
||||
// const times = createTimes(
|
||||
// TimeRange.fromTimes(UnixTime.fromTicks(startX), UnixTime.fromTicks(endX)),
|
||||
// numOfPointsToFetch
|
||||
// );
|
||||
//
|
||||
// cache.getSeries(times);
|
||||
// times$.next(times);
|
||||
};
|
||||
|
||||
const handle24HourData = () => {
|
||||
setDailyData(true);
|
||||
setWeeklyData(false);
|
||||
setMonthlyData(false);
|
||||
const times = createTimes(
|
||||
UnixTime.now().rangeBefore(TimeSpan.fromDays(1)),
|
||||
numOfPointsToFetch
|
||||
);
|
||||
cache.getSeries(times);
|
||||
times$.next(times);
|
||||
// const times = createTimes(
|
||||
// UnixTime.now().rangeBefore(TimeSpan.fromDays(1)),
|
||||
// numOfPointsToFetch
|
||||
// );
|
||||
// cache.getSeries(times);
|
||||
// times$.next(times);
|
||||
// setLoading(true);
|
||||
// const resultPromise: Promise<{
|
||||
// chartData: chartDataInterface;
|
||||
// chartOverview: overviewInterface;
|
||||
// }> = transformInputToDailyData(
|
||||
// props.s3Credentials,
|
||||
// UnixTime.now().rangeBefore(TimeSpan.fromDays(1)).start,
|
||||
// UnixTime.now()
|
||||
// );
|
||||
//
|
||||
// resultPromise
|
||||
// .then((result) => {
|
||||
// //setChartData(result.chartData);
|
||||
// //setChartOverview(result.chartOverview);
|
||||
// setDailyDataArray((prevData) =>
|
||||
// prevData.concat({
|
||||
// chartData: result.chartData,
|
||||
// chartOverview: result.chartOverview
|
||||
// })
|
||||
// );
|
||||
// setLoading(false);
|
||||
// setChartState(dailyDataArray.length);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error('Error:', error);
|
||||
// });
|
||||
setChartState(0);
|
||||
};
|
||||
|
||||
const handleWeekData = () => {
|
||||
setDailyData(false);
|
||||
setWeeklyData(true);
|
||||
setMonthlyData(false);
|
||||
//fetchData(12312,props.s3Credentials);
|
||||
|
||||
const resultPromise: Promise<{
|
||||
chartAggregatedData: chartAggregatedDataInterface;
|
||||
chartOverview: overviewInterface;
|
||||
}> = transformInputToAggregatedData(props.s3Credentials);
|
||||
|
||||
resultPromise
|
||||
.then((result) => {
|
||||
setChartAggregatedData(result.chartAggregatedData);
|
||||
setChartAggregatedOverview(result.chartOverview);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
|
||||
// const times = createTimes(
|
||||
// UnixTime.now().rangeBefore(TimeSpan.fromWeeks(1)),
|
||||
// numOfPointsToFetch
|
||||
|
@ -305,6 +249,18 @@ function Overview(props: OverviewProps) {
|
|||
// times$.next(times);
|
||||
};
|
||||
|
||||
const handleGoBack = () => {
|
||||
if (chartState > 0) {
|
||||
setChartState(chartState - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoForward = () => {
|
||||
if (chartState + 1 < dailyDataArray.length) {
|
||||
setChartState(chartState + 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMonthData = () => {
|
||||
setDailyData(false);
|
||||
setWeeklyData(false);
|
||||
|
@ -317,18 +273,32 @@ function Overview(props: OverviewProps) {
|
|||
// times$.next(times);
|
||||
};
|
||||
|
||||
const series = [
|
||||
{
|
||||
name: 'Inflation',
|
||||
data: [2.3, 3.1, 4.0, 10.1, 4.0, 3.6, 3.2, 2.3, 1.4, 0.8, 0.5, 0.2]
|
||||
}
|
||||
];
|
||||
|
||||
const renderGraphs = () => {
|
||||
if (loading) {
|
||||
// Display a loading indicator while waiting for the data
|
||||
return (
|
||||
<Container
|
||||
maxWidth="xl"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100vh'
|
||||
}}
|
||||
>
|
||||
<CircularProgress size={60} style={{ color: '#ffc04d' }} />
|
||||
<Typography variant="body2" style={{ color: 'black' }} mt={2}>
|
||||
Fetching data...
|
||||
</Typography>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container maxWidth="xl">
|
||||
<Grid container>
|
||||
<Grid item xs={12} md={12}>
|
||||
<Grid item xs={6} md={6}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handle24HourData}
|
||||
|
@ -368,6 +338,44 @@ function Overview(props: OverviewProps) {
|
|||
<FormattedMessage id="lastmonth" defaultMessage="Last Month" />
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
justifyContent="flex-end"
|
||||
alignItems="center"
|
||||
item
|
||||
xs={6}
|
||||
md={6}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!(chartState > 0)}
|
||||
onClick={handleGoBack}
|
||||
sx={{
|
||||
marginTop: '20px',
|
||||
marginLeft: '10px',
|
||||
backgroundColor: '#ffc04d',
|
||||
color: '#000000',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="goback" defaultMessage="Go Back" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!(chartState < dailyDataArray.length - 1)}
|
||||
onClick={handleGoForward}
|
||||
sx={{
|
||||
marginTop: '20px',
|
||||
marginLeft: '10px',
|
||||
backgroundColor: '#ffc04d',
|
||||
color: '#000000',
|
||||
'&:hover': { bgcolor: '#f7b34d' }
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="goback" defaultMessage="Go Forward" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={12}>
|
||||
<Grid
|
||||
|
@ -413,14 +421,17 @@ function Overview(props: OverviewProps) {
|
|||
{dailyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(chartOverview.soc, 'daily'),
|
||||
...getChartOptions(
|
||||
dailyDataArray[chartState].chartOverview.soc,
|
||||
'daily'
|
||||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={chartData.soc}
|
||||
series={dailyDataArray[chartState].chartData.soc}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
|
@ -429,9 +440,12 @@ function Overview(props: OverviewProps) {
|
|||
{weeklyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(chartOverview.soc, 'weekly')
|
||||
...getChartOptions(
|
||||
chartAggregatedOverview.soc,
|
||||
'weekly'
|
||||
)
|
||||
}}
|
||||
series={series}
|
||||
series={chartAggregatedData.soc}
|
||||
type="bar"
|
||||
height={350}
|
||||
/>
|
||||
|
@ -440,9 +454,12 @@ function Overview(props: OverviewProps) {
|
|||
{monthlyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(chartOverview.soc, 'monthly')
|
||||
...getChartOptions(
|
||||
chartAggregatedOverview.soc,
|
||||
'monthly'
|
||||
)
|
||||
}}
|
||||
series={series}
|
||||
series={chartAggregatedData.soc}
|
||||
type="bar"
|
||||
height={350}
|
||||
/>
|
||||
|
@ -481,21 +498,22 @@ function Overview(props: OverviewProps) {
|
|||
}}
|
||||
></Box>
|
||||
</Box>
|
||||
<div onDoubleClick={handleDoubleClick}>
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(chartOverview.temperature, 'daily'),
|
||||
...getChartOptions(
|
||||
dailyDataArray[chartState].chartOverview.temperature,
|
||||
'daily'
|
||||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={chartData.temperature}
|
||||
series={dailyDataArray[chartState].chartData.temperature}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -539,21 +557,53 @@ function Overview(props: OverviewProps) {
|
|||
}}
|
||||
></Box>
|
||||
</Box>
|
||||
<div onDoubleClick={handleDoubleClick}>
|
||||
|
||||
{dailyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(chartOverview.pvProduction, 'daily'),
|
||||
...getChartOptions(
|
||||
dailyDataArray[chartState].chartOverview.pvProduction,
|
||||
'daily'
|
||||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={chartData.pvProduction}
|
||||
series={dailyDataArray[chartState].chartData.pvProduction}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{weeklyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(
|
||||
chartAggregatedOverview.pvProduction,
|
||||
'weekly'
|
||||
)
|
||||
}}
|
||||
series={chartAggregatedData.pvProduction}
|
||||
type="bar"
|
||||
height={350}
|
||||
/>
|
||||
)}
|
||||
|
||||
{monthlyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(
|
||||
chartAggregatedOverview.pvProduction,
|
||||
'monthly'
|
||||
)
|
||||
}}
|
||||
series={chartAggregatedData.pvProduction}
|
||||
type="bar"
|
||||
height={350}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item md={6} xs={12}>
|
||||
|
@ -588,21 +638,22 @@ function Overview(props: OverviewProps) {
|
|||
}}
|
||||
></Box>
|
||||
</Box>
|
||||
<div onDoubleClick={handleDoubleClick}>
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(chartOverview.gridPower, 'daily'),
|
||||
...getChartOptions(
|
||||
dailyDataArray[chartState].chartOverview.gridPower,
|
||||
'daily'
|
||||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={chartData.gridPower}
|
||||
series={dailyDataArray[chartState].chartData.gridPower}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -645,21 +696,53 @@ function Overview(props: OverviewProps) {
|
|||
}}
|
||||
></Box>
|
||||
</Box>
|
||||
<div onDoubleClick={handleDoubleClick}>
|
||||
|
||||
{dailyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(chartOverview.dcPower, 'daily'),
|
||||
...getChartOptions(
|
||||
dailyDataArray[chartState].chartOverview.dcPower,
|
||||
'daily'
|
||||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={chartData.dcPower}
|
||||
series={dailyDataArray[chartState].chartData.dcPower}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{weeklyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(
|
||||
chartAggregatedOverview.dcPower,
|
||||
'weekly'
|
||||
)
|
||||
}}
|
||||
series={chartAggregatedData.dcPower}
|
||||
type="bar"
|
||||
height={350}
|
||||
/>
|
||||
)}
|
||||
|
||||
{monthlyData && (
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(
|
||||
chartAggregatedOverview.dcPower,
|
||||
'monthly'
|
||||
)
|
||||
}}
|
||||
series={chartAggregatedData.dcPower}
|
||||
type="bar"
|
||||
height={350}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</Grid>
|
||||
<Grid item md={6} xs={12}>
|
||||
|
@ -694,21 +777,22 @@ function Overview(props: OverviewProps) {
|
|||
}}
|
||||
></Box>
|
||||
</Box>
|
||||
<div onDoubleClick={handleDoubleClick}>
|
||||
<ReactApexChart
|
||||
options={{
|
||||
...getChartOptions(chartOverview.dcBusVoltage, 'daily'),
|
||||
...getChartOptions(
|
||||
dailyDataArray[chartState].chartOverview.dcBusVoltage,
|
||||
'daily'
|
||||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
}
|
||||
}
|
||||
}}
|
||||
series={chartData.dcBusVoltage}
|
||||
series={dailyDataArray[chartState].chartData.dcBusVoltage}
|
||||
type="area"
|
||||
height={350}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
|
|
@ -4,9 +4,9 @@ import { isDefined } from './utils/maybe';
|
|||
import { I_CsvEntry } from 'src/content/dashboards/Log/graph.util';
|
||||
|
||||
export type DataRecord = Record<string, I_CsvEntry>;
|
||||
|
||||
export type DataPoint = Timestamped<Maybe<DataRecord>>;
|
||||
export type RecordSeries = Array<DataPoint>;
|
||||
export type DataRecordSeries = Array<DataRecord>;
|
||||
export type PointSeries = Array<Timestamped<Maybe<I_CsvEntry>>>;
|
||||
export type DataSeries = Array<Maybe<I_CsvEntry>>;
|
||||
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import { ApexOptions } from 'apexcharts';
|
||||
import dayjs from 'dayjs';
|
||||
import {
|
||||
fetchDailyData,
|
||||
fetchData
|
||||
} from '../content/dashboards/Installations/fetchData';
|
||||
import { FetchResult } from '../dataCache/dataCache';
|
||||
import { I_S3Credentials } from './S3Types';
|
||||
import { UnixTime } from '../dataCache/time';
|
||||
|
||||
export interface overviewInterface {
|
||||
soc: chartInfoInterface;
|
||||
|
@ -16,6 +24,12 @@ export interface chartInfoInterface {
|
|||
max: number;
|
||||
}
|
||||
|
||||
export interface chartAggregatedDataInterface {
|
||||
soc: [{ data: number[] }];
|
||||
pvProduction: [{ data: number[] }];
|
||||
dcPower: [{ data: number[] }];
|
||||
}
|
||||
|
||||
export interface chartDataInterface {
|
||||
soc: ApexOptions['series'];
|
||||
temperature: ApexOptions['series'];
|
||||
|
@ -24,3 +38,360 @@ export interface chartDataInterface {
|
|||
pvProduction: ApexOptions['series'];
|
||||
dcBusVoltage: ApexOptions['series'];
|
||||
}
|
||||
|
||||
export const transformInputToDailyData = async (
|
||||
s3Credentials: I_S3Credentials,
|
||||
startTimestamp: UnixTime,
|
||||
endTimestamp: UnixTime
|
||||
): Promise<{
|
||||
chartData: chartDataInterface;
|
||||
chartOverview: overviewInterface;
|
||||
}> => {
|
||||
const data = {};
|
||||
const overviewData = {};
|
||||
const prefixes = ['', 'k', 'M', 'G', 'T'];
|
||||
const MAX_NUMBER = 9999999;
|
||||
const pathsToSearch = [
|
||||
'/Battery/Soc',
|
||||
'/Battery/Temperature',
|
||||
'/Battery/Dc/Power',
|
||||
'/GridMeter/Ac/Power/Active',
|
||||
'/PvOnDc/Dc/Power',
|
||||
'/DcDc/Dc/Link/Voltage'
|
||||
];
|
||||
|
||||
const chartData: chartDataInterface = {
|
||||
soc: [],
|
||||
temperature: [],
|
||||
dcPower: [],
|
||||
gridPower: [],
|
||||
pvProduction: [],
|
||||
dcBusVoltage: []
|
||||
};
|
||||
|
||||
const chartOverview: overviewInterface = {
|
||||
soc: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
temperature: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
dcPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 }
|
||||
};
|
||||
|
||||
pathsToSearch.forEach((path) => {
|
||||
data[path] = [];
|
||||
overviewData[path] = {
|
||||
magnitude: 0,
|
||||
unit: '',
|
||||
min: MAX_NUMBER,
|
||||
max: -MAX_NUMBER
|
||||
};
|
||||
});
|
||||
|
||||
// const currentTimestamp = UnixTime.now();
|
||||
// const twentyFourHoursAgoRange = currentTimestamp.rangeBefore(
|
||||
// TimeSpan.fromDays(1)
|
||||
// );
|
||||
|
||||
let startTimestampToNum = Number(startTimestamp);
|
||||
if (startTimestampToNum % 2 != 0) {
|
||||
startTimestampToNum += 1;
|
||||
}
|
||||
let startUnixTime = UnixTime.fromTicks(startTimestampToNum);
|
||||
|
||||
let diff = endTimestamp.ticks - startUnixTime.ticks;
|
||||
|
||||
//console.log('Current Unix Timestamp:', currentTimestamp);
|
||||
//console.log('Unix Timestamp 24 hours ago (even):', startTimestamp);
|
||||
|
||||
while (startUnixTime < endTimestamp) {
|
||||
// console.log('Current day:', currentDay.format('YYYY-MM-DD'));
|
||||
|
||||
let result = await Promise.resolve(fetchData(startUnixTime, s3Credentials));
|
||||
if (
|
||||
result === FetchResult.notAvailable ||
|
||||
result === FetchResult.tryLater
|
||||
) {
|
||||
// Handle not available or try later case
|
||||
} else {
|
||||
//console.log('Received data:', result);
|
||||
// eslint-disable-next-line @typescript-eslint/no-loop-func
|
||||
pathsToSearch.forEach((path) => {
|
||||
const timestamp = startUnixTime.ticks * 1000;
|
||||
|
||||
const adjustedTimestamp = new Date(timestamp);
|
||||
adjustedTimestamp.setHours(adjustedTimestamp.getHours() + 1);
|
||||
|
||||
if (result[path]) {
|
||||
const value = result[path];
|
||||
|
||||
if (value.value < overviewData[path].min) {
|
||||
overviewData[path].min = value.value;
|
||||
}
|
||||
|
||||
if (value.value > overviewData[path].max) {
|
||||
overviewData[path].max = value.value;
|
||||
}
|
||||
|
||||
data[path].push([adjustedTimestamp, value.value]);
|
||||
} else {
|
||||
//data[path].push([adjustedTimestamp, null]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
startUnixTime = UnixTime.fromTicks(startUnixTime.ticks + diff / 100);
|
||||
if (startUnixTime.ticks % 2 != 0) {
|
||||
startUnixTime = UnixTime.fromTicks(startUnixTime.ticks + 1);
|
||||
}
|
||||
|
||||
console.log('Try next timestamp: ', startUnixTime);
|
||||
}
|
||||
|
||||
//console.log(input);
|
||||
// input.forEach((item) => {
|
||||
// const csvContent = item.value;
|
||||
// pathsToSearch.forEach((path) => {
|
||||
// if (csvContent) {
|
||||
// const timestamp = item.time.ticks * 1000;
|
||||
//
|
||||
// const adjustedTimestamp = new Date(timestamp);
|
||||
// adjustedTimestamp.setHours(adjustedTimestamp.getHours() + 1);
|
||||
//
|
||||
// if (csvContent[path]) {
|
||||
// const value = csvContent[path];
|
||||
//
|
||||
// if (value.value < overviewData[path].min) {
|
||||
// overviewData[path].min = value.value;
|
||||
// }
|
||||
//
|
||||
// if (value.value > overviewData[path].max) {
|
||||
// overviewData[path].max = value.value;
|
||||
// }
|
||||
//
|
||||
// data[path].push([adjustedTimestamp, value.value]);
|
||||
// } else {
|
||||
// //data[path].push([adjustedTimestamp, null]);
|
||||
// }
|
||||
// } else {
|
||||
// // data[path].push([
|
||||
// // addHours(new Date(item.time.ticks * 1000), 2),
|
||||
// // null
|
||||
// // ]);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
|
||||
pathsToSearch.forEach((path) => {
|
||||
let value = Math.max(
|
||||
Math.abs(overviewData[path].max),
|
||||
Math.abs(overviewData[path].min)
|
||||
);
|
||||
let magnitude = 0;
|
||||
|
||||
if (value < 0) {
|
||||
value = -value;
|
||||
}
|
||||
while (value >= 1000) {
|
||||
value /= 1000;
|
||||
magnitude++;
|
||||
}
|
||||
overviewData[path].magnitude = prefixes[magnitude];
|
||||
});
|
||||
|
||||
let path = '/Battery/Soc';
|
||||
chartData.soc = [{ name: 'State of Charge', data: data[path] }];
|
||||
|
||||
chartOverview.soc = {
|
||||
unit: '(%)',
|
||||
magnitude: overviewData[path].magnitude,
|
||||
min: 0,
|
||||
max: 100
|
||||
};
|
||||
|
||||
path = '/Battery/Temperature';
|
||||
chartData.temperature = [{ name: 'Battery Temperature:', data: data[path] }];
|
||||
|
||||
chartOverview.temperature = {
|
||||
unit: '(°C)',
|
||||
magnitude: overviewData[path].magnitude,
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/Battery/Dc/Power';
|
||||
chartData.dcPower = [{ name: 'Battery Power', data: data[path] }];
|
||||
|
||||
chartOverview.dcPower = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'W' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/GridMeter/Ac/Power/Active';
|
||||
chartData.gridPower = [{ name: 'Grid Power', data: data[path] }];
|
||||
|
||||
chartOverview.gridPower = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'W' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/PvOnDc/Dc/Power';
|
||||
chartData.pvProduction = [{ name: 'Pv Production', data: data[path] }];
|
||||
|
||||
chartOverview.pvProduction = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'W' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/DcDc/Dc/Link/Voltage';
|
||||
chartData.dcBusVoltage = [{ name: 'DC Bus Voltage', data: data[path] }];
|
||||
|
||||
chartOverview.dcBusVoltage = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'V' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
return {
|
||||
chartData: chartData,
|
||||
chartOverview: chartOverview
|
||||
};
|
||||
};
|
||||
|
||||
export const transformInputToAggregatedData = async (
|
||||
s3Credentials: I_S3Credentials
|
||||
): Promise<{
|
||||
chartAggregatedData: chartAggregatedDataInterface;
|
||||
chartOverview: overviewInterface;
|
||||
}> => {
|
||||
const data = {};
|
||||
const overviewData = {};
|
||||
const prefixes = ['', 'k', 'M', 'G', 'T'];
|
||||
const MAX_NUMBER = 9999999;
|
||||
|
||||
let currentDate = dayjs().add(1, 'day');
|
||||
let currentDay = currentDate.subtract(1, 'week');
|
||||
|
||||
const pathsToSearch = [
|
||||
'/AvgSoc',
|
||||
'/AvgPvPower',
|
||||
'/BatteryPowerAverage',
|
||||
'/GridMeter/Ac/Power/Active',
|
||||
'/PvOnDc/Dc/Power',
|
||||
'/DcDc/Dc/Link/Voltage'
|
||||
];
|
||||
|
||||
const chartAggregatedData: chartAggregatedDataInterface = {
|
||||
soc: [{ data: [] }],
|
||||
pvProduction: [{ data: [] }],
|
||||
dcPower: [{ data: [] }]
|
||||
};
|
||||
|
||||
const chartOverview: overviewInterface = {
|
||||
soc: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
temperature: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
dcPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
gridPower: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
pvProduction: { magnitude: 0, unit: '', min: 0, max: 0 },
|
||||
dcBusVoltage: { magnitude: 0, unit: '', min: 0, max: 0 }
|
||||
};
|
||||
|
||||
pathsToSearch.forEach((path) => {
|
||||
data[path] = [];
|
||||
overviewData[path] = {
|
||||
magnitude: 0,
|
||||
unit: '',
|
||||
min: MAX_NUMBER,
|
||||
max: -MAX_NUMBER
|
||||
};
|
||||
});
|
||||
|
||||
while (currentDay.isBefore(currentDate)) {
|
||||
// console.log('Current day:', currentDay.format('YYYY-MM-DD'));
|
||||
|
||||
let result = await Promise.resolve(
|
||||
fetchDailyData(currentDay.format('YYYY-MM-DD'), s3Credentials)
|
||||
);
|
||||
if (
|
||||
result === FetchResult.notAvailable ||
|
||||
result === FetchResult.tryLater
|
||||
) {
|
||||
// Handle not available or try later case
|
||||
} else {
|
||||
console.log('Received data:', result);
|
||||
pathsToSearch.forEach((path) => {
|
||||
if (result[path]) {
|
||||
if (result[path].value < overviewData[path].min) {
|
||||
overviewData[path].min = result[path].value;
|
||||
}
|
||||
|
||||
if (result[path].value > overviewData[path].max) {
|
||||
overviewData[path].max = result[path].value;
|
||||
}
|
||||
data[path].push(result[path].value as number);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Add one day to move to the next day
|
||||
currentDay = currentDay.add(1, 'day');
|
||||
}
|
||||
|
||||
pathsToSearch.forEach((path) => {
|
||||
let value = Math.max(
|
||||
Math.abs(overviewData[path].max),
|
||||
Math.abs(overviewData[path].min)
|
||||
);
|
||||
let magnitude = 0;
|
||||
|
||||
if (value < 0) {
|
||||
value = -value;
|
||||
}
|
||||
while (value >= 1000) {
|
||||
value /= 1000;
|
||||
magnitude++;
|
||||
}
|
||||
overviewData[path].magnitude = prefixes[magnitude];
|
||||
});
|
||||
|
||||
let path = '/AvgSoc';
|
||||
chartAggregatedData.soc[0].data = data[path];
|
||||
|
||||
chartOverview.soc = {
|
||||
unit: '(%)',
|
||||
magnitude: overviewData[path].magnitude,
|
||||
min: 0,
|
||||
max: 100
|
||||
};
|
||||
|
||||
path = '/AvgPvPower';
|
||||
chartAggregatedData.pvProduction[0].data = data[path];
|
||||
|
||||
chartOverview.pvProduction = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'W' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
path = '/BatteryPowerAverage';
|
||||
chartAggregatedData.dcPower[0].data = data[path];
|
||||
|
||||
chartOverview.dcPower = {
|
||||
magnitude: overviewData[path].magnitude,
|
||||
unit: '(' + overviewData[path].magnitude + 'W' + ')',
|
||||
min: overviewData[path].min,
|
||||
max: overviewData[path].max
|
||||
};
|
||||
|
||||
return {
|
||||
chartAggregatedData: chartAggregatedData,
|
||||
chartOverview: chartOverview
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue