From 5d3b3b4cb26feeaeede03e9be68c523156e28dce Mon Sep 17 00:00:00 2001 From: Noe Date: Thu, 27 Jun 2024 12:51:24 +0200 Subject: [PATCH] Updated backend to provide support for the overview tab Updated frontend to parse chunks --- .../Backend/Websockets/WebsockerManager.cs | 6 +- .../App/SaliMax/deploy_all_installations.sh | 2 +- csharp/App/SaliMax/src/Program.cs | 2 +- .../BatteryView/DetailedBatteryView.tsx | 65 +- .../Information/InformationSalidomo.tsx | 2 +- .../dashboards/Installations/Installation.tsx | 7 +- .../dashboards/Overview/salidomoOverview.tsx | 574 ++++++++++++++++++ .../SalidomoInstallations/Installation.tsx | 15 +- .../SalidomoInstallations/index.tsx | 4 + .../frontend-marios2/src/interfaces/Chart.tsx | 1 - 10 files changed, 631 insertions(+), 47 deletions(-) create mode 100644 typescript/frontend-marios2/src/content/dashboards/Overview/salidomoOverview.tsx diff --git a/csharp/App/Backend/Websockets/WebsockerManager.cs b/csharp/App/Backend/Websockets/WebsockerManager.cs index f7d287ef3..257ce3f76 100644 --- a/csharp/App/Backend/Websockets/WebsockerManager.cs +++ b/csharp/App/Backend/Websockets/WebsockerManager.cs @@ -11,8 +11,8 @@ public static class WebsocketManager { public static Dictionary InstallationConnections = new Dictionary(); - //Every 1 minute, check the timestamp of the latest received message for every installation. - //If the difference between the two timestamps is more than one minute, we consider this installation unavailable. + //Every 2 minutes, check the timestamp of the latest received message for every installation. + //If the difference between the two timestamps is more than two minutes, we consider this installation unavailable. public static async Task MonitorInstallationTable() { while (true){ @@ -25,7 +25,7 @@ public static class WebsocketManager } } } - await Task.Delay(TimeSpan.FromMinutes(1)); + await Task.Delay(TimeSpan.FromMinutes(2)); } } diff --git a/csharp/App/SaliMax/deploy_all_installations.sh b/csharp/App/SaliMax/deploy_all_installations.sh index 0fdf25c16..533946856 100755 --- a/csharp/App/SaliMax/deploy_all_installations.sh +++ b/csharp/App/SaliMax/deploy_all_installations.sh @@ -18,7 +18,7 @@ dotnet publish \ echo -e "\n============================ Deploy ============================\n" #ip_addresses=("10.2.3.115" "10.2.3.104" "10.2.4.29" "10.2.4.33" "10.2.4.32" "10.2.4.36" "10.2.4.35" "10.2.4.154" "10.2.4.113" "10.2.4.211") #ip_addresses=("10.2.4.154" "10.2.4.29") -ip_addresses=("10.2.3.115" "10.2.3.104" "10.2.4.29" "10.2.4.33" "10.2.4.32" "10.2.4.36" "10.2.4.35") +ip_addresses=("10.2.3.115" "10.2.3.104" "10.2.4.29" "10.2.4.33" "10.2.4.32" "10.2.4.36" "10.2.4.35" "10.2.4.154" ) diff --git a/csharp/App/SaliMax/src/Program.cs b/csharp/App/SaliMax/src/Program.cs index 6545ac962..928d28478 100644 --- a/csharp/App/SaliMax/src/Program.cs +++ b/csharp/App/SaliMax/src/Program.cs @@ -59,7 +59,7 @@ internal static class Program private static Boolean _subscribeToQueueForTheFirstTime = false; private static SalimaxAlarmState _prevSalimaxState = SalimaxAlarmState.Green; //private static Int32 _heartBitInterval = 0; - private const UInt16 NbrOfFileToConcatenate = 15; + private const UInt16 NbrOfFileToConcatenate = 30; private static UInt16 _counterOfFile = 0; private static SalimaxAlarmState _salimaxAlarmState = SalimaxAlarmState.Green; diff --git a/typescript/frontend-marios2/src/content/dashboards/BatteryView/DetailedBatteryView.tsx b/typescript/frontend-marios2/src/content/dashboards/BatteryView/DetailedBatteryView.tsx index d03f82778..9240d7283 100644 --- a/typescript/frontend-marios2/src/content/dashboards/BatteryView/DetailedBatteryView.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/BatteryView/DetailedBatteryView.tsx @@ -269,7 +269,6 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) { - {props.productNum === 0 && ( - <> - - - - )} + + diff --git a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSalidomo.tsx b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSalidomo.tsx index 2ca122a05..f169e5947 100644 --- a/typescript/frontend-marios2/src/content/dashboards/Information/InformationSalidomo.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/Information/InformationSalidomo.tsx @@ -361,7 +361,7 @@ function InformationSalidomo(props: InformationSalidomoProps) {
{ - var timeperiodToSearch = 80; + var timeperiodToSearch = 70; let res; let timestampToFetch; @@ -107,7 +107,7 @@ function Installation(props: singleInstallationProps) { }; const fetchDataPeriodically = async () => { - var timeperiodToSearch = 80; + var timeperiodToSearch = 70; let res; let timestampToFetch; @@ -133,6 +133,7 @@ function Installation(props: singleInstallationProps) { return false; } setConnected(true); + console.log('NUMBER OF FILES=' + Object.keys(res).length); while (continueFetching.current) { for (const timestamp of Object.keys(res)) { @@ -154,7 +155,7 @@ function Installation(props: singleInstallationProps) { await timeout(2000); } - timestampToFetch = timestampToFetch.later(TimeSpan.fromSeconds(30)); + timestampToFetch = timestampToFetch.later(TimeSpan.fromSeconds(60)); console.log('NEW TIMESTAMP TO FETCH IS ' + timestampToFetch); for (i = 0; i < 10; i++) { diff --git a/typescript/frontend-marios2/src/content/dashboards/Overview/salidomoOverview.tsx b/typescript/frontend-marios2/src/content/dashboards/Overview/salidomoOverview.tsx new file mode 100644 index 000000000..383a9ca5c --- /dev/null +++ b/typescript/frontend-marios2/src/content/dashboards/Overview/salidomoOverview.tsx @@ -0,0 +1,574 @@ +import { Box, Card, Container, Grid, Modal, Typography } from '@mui/material'; +import ReactApexChart from 'react-apexcharts'; +import React, { useContext, useEffect, useState } from 'react'; +import { I_S3Credentials } from 'src/interfaces/S3Types'; +import { getChartOptions } from './chartOptions'; +import { + chartAggregatedDataInterface, + overviewInterface, + transformInputToAggregatedData +} from 'src/interfaces/Chart'; +import Button from '@mui/material/Button'; +import { FormattedMessage } from 'react-intl'; +import CircularProgress from '@mui/material/CircularProgress'; +import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import dayjs from 'dayjs'; +import { UserContext } from '../../../contexts/userContext'; +import { UserType } from '../../../interfaces/UserTypes'; + +interface salidomoOverviewProps { + s3Credentials: I_S3Credentials; + id: number; +} + +const computeLast7Days = (): string[] => { + const currentDate = new Date(); + const daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + let currentDayIndex = currentDate.getDay(); + const last7Days = []; + + for (let i = 0; i < 7; i++) { + last7Days.push(daysOfWeek[currentDayIndex]); + currentDayIndex++; + if (currentDayIndex > 6) { + currentDayIndex = 0; + } + } + return last7Days; +}; + +function SalidomoOverview(props: salidomoOverviewProps) { + const context = useContext(UserContext); + const { currentUser } = context; + const [loading, setLoading] = useState(true); + const [aggregatedChartState, setAggregatedChartState] = useState(0); + const [isDateModalOpen, setIsDateModalOpen] = useState(false); + const [isErrorDateModalOpen, setErrorDateModalOpen] = useState(false); + const [dateSelectionError, setDateSelectionError] = useState(''); + const [dateOpen, setDateOpen] = useState(false); + + const [aggregatedDataArray, setAggregatedDataArray] = useState< + { + chartData: chartAggregatedDataInterface; + chartOverview: overviewInterface; + datelist: any[]; + netbalance: any[]; + }[] + >([]); + + const [startDate, setStartDate] = useState(dayjs().add(-1, 'day')); + const [endDate, setEndDate] = useState(dayjs()); + + useEffect(() => { + handleWeekData(); + }, []); + + const handleWeekData = () => { + setAggregatedChartState(0); + + if ( + aggregatedDataArray[aggregatedChartState] && + aggregatedDataArray[aggregatedChartState].chartData != null + ) { + return; + } + setLoading(true); + + const resultPromise: Promise<{ + chartAggregatedData: chartAggregatedDataInterface; + chartOverview: overviewInterface; + }> = transformInputToAggregatedData( + props.s3Credentials, + dayjs().subtract(1, 'week'), + dayjs() + ); + + resultPromise + .then((result) => { + const powerDifference = []; + for ( + let i = 0; + i < result.chartAggregatedData.gridImportPower.data.length; + i++ + ) { + powerDifference.push( + result.chartAggregatedData.gridImportPower.data[i] - + Math.abs(result.chartAggregatedData.gridExportPower.data[i]) + ); + } + + setAggregatedDataArray((prevData) => + prevData.concat({ + chartData: result.chartAggregatedData, + chartOverview: result.chartOverview, + datelist: computeLast7Days(), + netbalance: powerDifference + }) + ); + + setAggregatedChartState(aggregatedDataArray.length); + setLoading(false); + }) + .catch((error) => { + console.error('Error:', error); + }); + }; + + const handleSetDate = () => { + setDateOpen(true); + setIsDateModalOpen(true); + }; + + const handleOkOnErrorDateModal = () => { + setErrorDateModalOpen(false); + }; + + const handleCancel = () => { + setIsDateModalOpen(false); + setDateOpen(false); + }; + + const handleConfirm = () => { + setIsDateModalOpen(false); + setDateOpen(false); + + if (endDate.isAfter(dayjs())) { + setDateSelectionError('You cannot ask for future data'); + setErrorDateModalOpen(true); + return; + } else if (startDate.isAfter(endDate)) { + setDateSelectionError('Εnd date must precede start date'); + setErrorDateModalOpen(true); + return; + } + setLoading(true); + + const resultPromise: Promise<{ + chartAggregatedData: chartAggregatedDataInterface; + chartOverview: overviewInterface; + dateList: string[]; + }> = transformInputToAggregatedData( + props.s3Credentials, + startDate, + endDate + ); + + resultPromise + .then((result) => { + const powerDifference = []; + + for ( + let i = 0; + i < result.chartAggregatedData.gridImportPower.data.length; + i++ + ) { + powerDifference.push( + result.chartAggregatedData.gridImportPower.data[i] - + Math.abs(result.chartAggregatedData.gridExportPower.data[i]) + ); + } + + setAggregatedDataArray((prevData) => + prevData.concat({ + chartData: result.chartAggregatedData, + chartOverview: result.chartOverview, + datelist: result.dateList, + netbalance: powerDifference + }) + ); + + setAggregatedChartState(aggregatedDataArray.length); + setLoading(false); + }) + .catch((error) => { + console.error('Error:', error); + }); + }; + + const handleGoBack = () => { + if (aggregatedChartState > 0) { + setAggregatedChartState(aggregatedChartState - 1); + } + }; + + const handleGoForward = () => { + if (aggregatedChartState + 1 < aggregatedDataArray.length) { + setAggregatedChartState(aggregatedChartState + 1); + } + }; + + const renderGraphs = () => { + return ( + + {isErrorDateModalOpen && ( + {}}> + + + {dateSelectionError} + + + + + + )} + + {isDateModalOpen && ( + + {}}> + + setStartDate(newDate)} + sx={{ + marginTop: 2 + }} + /> + + setEndDate(newDate)} + sx={{ + marginTop: 2 + }} + /> + +
+ + + +
+
+
+
+ )} + + + + + + + + + + + + {loading && ( + + + + Fetching data... + + + )} + + {!loading && ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {currentUser.userType == UserType.admin && ( + + )} + + {currentUser.userType == UserType.client && ( + + )} + + + + + )} + +
+ ); + }; + + return <>{renderGraphs()}; +} + +export default SalidomoOverview; diff --git a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx index 375b42b69..339bbeee9 100644 --- a/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx +++ b/typescript/frontend-marios2/src/content/dashboards/SalidomoInstallations/Installation.tsx @@ -17,6 +17,7 @@ import InformationSalidomo from '../Information/InformationSalidomo'; import BatteryView from '../BatteryView/BatteryView'; import Log from '../Log/Log'; import CancelIcon from '@mui/icons-material/Cancel'; +import SalidomoOverview from '../Overview/salidomoOverview'; interface singleInstallationProps { current_installation?: I_Installation; @@ -66,7 +67,7 @@ function Installation(props: singleInstallationProps) { const continueFetching = useRef(false); const fetchDataPeriodically = async () => { - var timeperiodToSearch = 80; + var timeperiodToSearch = 70; let res; let timestampToFetch; @@ -113,7 +114,7 @@ function Installation(props: singleInstallationProps) { await timeout(2000); } - timestampToFetch = timestampToFetch.later(TimeSpan.fromSeconds(30)); + timestampToFetch = timestampToFetch.later(TimeSpan.fromSeconds(60)); console.log('NEW TIMESTAMP TO FETCH IS ' + timestampToFetch); for (i = 0; i < 10; i++) { @@ -296,6 +297,16 @@ function Installation(props: singleInstallationProps) { } /> + + } + /> + ) }, + { + value: 'overview', + label: + }, { value: 'log', label: diff --git a/typescript/frontend-marios2/src/interfaces/Chart.tsx b/typescript/frontend-marios2/src/interfaces/Chart.tsx index a4de6ef34..394ab265f 100644 --- a/typescript/frontend-marios2/src/interfaces/Chart.tsx +++ b/typescript/frontend-marios2/src/interfaces/Chart.tsx @@ -403,7 +403,6 @@ export const transformInputToDailyData = async ( }); } - //while (startUnixTime < endTimestamp) { for (var i = 0; i < timestampArray.length; i++) { timestampPromises.push( fetchDataForOneTime(