Battery view updated

This commit is contained in:
Noe 2024-03-08 11:19:58 +01:00
parent 39fc4ad331
commit b5f8658300
11 changed files with 1511 additions and 480 deletions

View File

@ -1,24 +1,19 @@
{ {
"installation": "installation/",
"live": "live",
"users": "/users/", "users": "/users/",
"log": "log/",
"installations": "/installations/", "installations": "/installations/",
"groups": "/groups/", "installation": "installation/",
"group": "group/", "login": "/login/",
"forgotPassword": "/forgotPassword/",
"folder": "folder/", "folder": "folder/",
"manageAccess": "manageAccess/",
"user": "user/",
"tree": "tree/", "tree": "tree/",
"list": "list/", "list": "list/",
"overview": "overview", "overview": "overview",
"manage": "manage", "manage": "manage",
"batteryview": "batteryview", "batteryview": "batteryview",
"log": "log", "log": "log",
"live": "live",
"information": "information", "information": "information",
"configuration": "configuration", "configuration": "configuration",
"login": "/login/",
"forgotPassword": "/forgotPassword/",
"mainstats": "mainstats", "mainstats": "mainstats",
"general": "general" "detailed_view": "detailed_view/"
} }

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'; import React from 'react';
import { import {
Container, Container,
Grid, Grid,
@ -23,6 +23,7 @@ import { FormattedMessage } from 'react-intl';
import { I_S3Credentials } from '../../../interfaces/S3Types'; import { I_S3Credentials } from '../../../interfaces/S3Types';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
import MainStats from './MainStats'; import MainStats from './MainStats';
import DetailedBatteryView from './DetailedBatteryView';
interface BatteryViewProps { interface BatteryViewProps {
values: TopologyValues; values: TopologyValues;
@ -33,68 +34,30 @@ function BatteryView(props: BatteryViewProps) {
if (props.values === null) { if (props.values === null) {
return null; return null;
} }
const currentPath = useLocation(); const currentLocation = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const [currentTab, setCurrentTab] = useState<string>(undefined); const numOfBatteries = props.values.batteryView.length;
const numOfBatteries = props.values.batteryView.values[0].value
.toString()
.split(',').length;
const batteryData = [];
let batteryId = 1;
// Use a for loop to generate battery data
for (let index = 1; index <= numOfBatteries * 7; index += 7) {
const battery = {
BatteryId: 'Battery ' + batteryId.toString(),
FwVersion: props.values.batteryView.values[index].value,
Power:
Number(props.values.batteryView.values[index + 1].value).toFixed(2) +
' ' +
props.values.batteryView.values[index + 1].unit,
Voltage:
Number(props.values.batteryView.values[index + 2].value).toFixed(2) +
' ' +
props.values.batteryView.values[index + 2].unit,
Soc:
props.values.batteryView.values[index + 3].value +
' ' +
props.values.batteryView.values[index + 3].unit,
AverageTemperature:
props.values.batteryView.values[index + 4].value +
' ' +
props.values.batteryView.values[index + 4].unit,
Warnings: props.values.batteryView.values[index + 5].value,
Alarms: props.values.batteryView.values[index + 6].value
};
batteryId++;
batteryData.push(battery);
}
const handleMainStatsButton = () => { const handleMainStatsButton = () => {
navigate(routes.mainstats); navigate(routes.mainstats);
}; };
useEffect(() => {
let path = currentPath.pathname.split('/');
setCurrentTab(path[path.length - 1]);
}, [currentPath]);
return ( return (
<> <>
<Container maxWidth="xl"> <Container maxWidth="xl">
<Grid container> <Grid container>
<Routes> <Grid
<Route item
path={routes.mainstats} xs={6}
element={ md={6}
<MainStats s3Credentials={props.s3Credentials}></MainStats> sx={{
} display:
/> !currentLocation.pathname.includes('detailed_view') &&
</Routes> !currentLocation.pathname.includes('mainstats')
{currentTab === 'batteryview' && ( ? 'block'
<Grid item xs={6} md={6}> : 'none'
}}
>
<Button <Button
variant="contained" variant="contained"
sx={{ sx={{
@ -105,7 +68,7 @@ function BatteryView(props: BatteryViewProps) {
}} }}
> >
<FormattedMessage <FormattedMessage
id="main_stats" id="battery_view"
defaultMessage="Battery View" defaultMessage="Battery View"
/> />
</Button> </Button>
@ -116,7 +79,6 @@ function BatteryView(props: BatteryViewProps) {
sx={{ sx={{
marginTop: '20px', marginTop: '20px',
marginLeft: '20px', marginLeft: '20px',
// backgroundColor: mainStatsData ? '#808080' : '#ffc04d',
backgroundColor: '#ffc04d', backgroundColor: '#ffc04d',
color: '#000000', color: '#000000',
'&:hover': { bgcolor: '#f7b34d' } '&:hover': { bgcolor: '#f7b34d' }
@ -125,13 +87,42 @@ function BatteryView(props: BatteryViewProps) {
<FormattedMessage id="main_stats" defaultMessage="Main Stats" /> <FormattedMessage id="main_stats" defaultMessage="Main Stats" />
</Button> </Button>
</Grid> </Grid>
)}
</Grid> </Grid>
{currentTab === 'batteryview' && ( <Grid container>
<Routes>
<Route
path={routes.mainstats + '*'}
element={
<MainStats s3Credentials={props.s3Credentials}></MainStats>
}
/>
{Array.from({ length: numOfBatteries }).map((_, i) => (
<Route
key={routes.detailed_view + i}
path={routes.detailed_view + i}
element={
<DetailedBatteryView
s3Credentials={props.s3Credentials}
batteryData={props.values.batteryView[i]}
></DetailedBatteryView>
}
/>
))}
</Routes>
</Grid>
<TableContainer <TableContainer
component={Paper} component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px' }} sx={{
marginTop: '20px',
marginBottom: '20px',
display:
!currentLocation.pathname.includes('detailed_view') &&
!currentLocation.pathname.includes('mainstats')
? 'block'
: 'none'
}}
> >
<Table sx={{ minWidth: 650 }} aria-label="simple table"> <Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead> <TableHead>
@ -147,7 +138,7 @@ function BatteryView(props: BatteryViewProps) {
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{batteryData.map((battery) => ( {props.values.batteryView.map((battery) => (
<TableRow <TableRow
key={battery.BatteryId} key={battery.BatteryId}
style={{ style={{
@ -160,7 +151,15 @@ function BatteryView(props: BatteryViewProps) {
align="center" align="center"
sx={{ fontWeight: 'bold' }} sx={{ fontWeight: 'bold' }}
> >
{battery.BatteryId} <Link
style={{ color: 'black' }}
to={
routes.detailed_view +
(battery.BatteryId - 1).toString()
}
>
{'Battery ' + battery.BatteryId}
</Link>
</TableCell> </TableCell>
<TableCell <TableCell
sx={{ sx={{
@ -168,7 +167,7 @@ function BatteryView(props: BatteryViewProps) {
textAlign: 'center' textAlign: 'center'
}} }}
> >
{battery.FwVersion} {battery.FwVersion.value}
</TableCell> </TableCell>
<TableCell <TableCell
sx={{ sx={{
@ -176,7 +175,7 @@ function BatteryView(props: BatteryViewProps) {
textAlign: 'center' textAlign: 'center'
}} }}
> >
{battery.Power} {battery.Power.value + ' ' + battery.Power.unit}
</TableCell> </TableCell>
<TableCell <TableCell
sx={{ sx={{
@ -184,40 +183,42 @@ function BatteryView(props: BatteryViewProps) {
textAlign: 'center', textAlign: 'center',
backgroundColor: backgroundColor:
battery.Voltage < 44 || battery.Voltage > 57 battery.Voltage.value < 44 || battery.Voltage.value > 57
? '#FF033E' ? '#FF033E'
: '#32CD32', : '#32CD32',
color: battery.Voltage === '' ? 'white' : 'inherit' color: battery.Voltage.value === '' ? 'white' : 'inherit'
}} }}
> >
{battery.Voltage} {battery.Voltage.value + ' ' + battery.Voltage.unit}
</TableCell> </TableCell>
<TableCell <TableCell
sx={{ sx={{
width: '10%', width: '10%',
textAlign: 'center', textAlign: 'center',
backgroundColor: backgroundColor:
battery.Soc < 20 battery.Soc.value < 20
? '#FF033E' ? '#FF033E'
: battery.Soc < 50 : battery.Soc.value < 50
? '#ffbf00 ' ? '#ffbf00'
: '#32CD32', : '#32CD32',
color: battery.Soc === '' ? 'white' : 'inherit' color: battery.Soc.value === '' ? 'white' : 'inherit'
}} }}
> >
{battery.Soc} {battery.Soc.value + ' ' + battery.Soc.unit}
</TableCell> </TableCell>
<TableCell <TableCell
sx={{ sx={{
width: '10%', width: '10%',
textAlign: 'center', textAlign: 'center',
backgroundColor: backgroundColor:
battery.AverageTemperature > 270 battery.AverageTemperature.value > 270
? '#FF033E' ? '#FF033E'
: '#32CD32 ' : '#32CD32 '
}} }}
> >
{battery.AverageTemperature} {battery.AverageTemperature.value +
' ' +
battery.AverageTemperature.unit}
</TableCell> </TableCell>
<TableCell <TableCell
@ -226,21 +227,22 @@ function BatteryView(props: BatteryViewProps) {
textAlign: 'center', textAlign: 'center',
padding: '8px', padding: '8px',
fontWeight: fontWeight:
battery.Warnings !== '' ? 'bold' : 'inherit', battery.Warnings.value !== '' ? 'bold' : 'inherit',
backgroundColor: backgroundColor:
battery.Warnings === '' ? 'inherit' : '#ff9900', battery.Warnings.value === '' ? 'inherit' : '#ff9900',
color: battery.Warnings != '' ? 'black' : 'inherit' color: battery.Warnings.value != '' ? 'black' : 'inherit'
}} }}
> >
{battery.Warnings === '' ? ( {battery.Warnings.value === '' ? (
'None' 'None'
) : battery.Warnings.split(';').length > 1 ? ( ) : battery.Warnings.value.toString().split(';').length >
1 ? (
<Link <Link
style={{ color: 'black' }} style={{ color: 'black' }}
to={ to={
currentPath.pathname.substring( currentLocation.pathname.substring(
0, 0,
currentPath.pathname.lastIndexOf('/') + 1 currentLocation.pathname.lastIndexOf('/') + 1
) + ) +
routes.log + routes.log +
'?open=warning' '?open=warning'
@ -249,28 +251,30 @@ function BatteryView(props: BatteryViewProps) {
Multiple Warnings Multiple Warnings
</Link> </Link>
) : ( ) : (
battery.Warnings battery.Warnings.value
)} )}
</TableCell> </TableCell>
<TableCell <TableCell
sx={{ sx={{
width: '20%', width: '20%',
textAlign: 'center', textAlign: 'center',
fontWeight: battery.Alarms !== '' ? 'bold' : 'inherit', fontWeight:
battery.Alarms.value !== '' ? 'bold' : 'inherit',
backgroundColor: backgroundColor:
battery.Alarms === '' ? 'inherit' : '#FF033E', battery.Alarms.value === '' ? 'inherit' : '#FF033E',
color: battery.Alarms != '' ? 'black' : 'inherit' color: battery.Alarms.value != '' ? 'black' : 'inherit'
}} }}
> >
{battery.Alarms === '' ? ( {battery.Alarms.value === '' ? (
'None' 'None'
) : battery.Alarms.split(';').length > 1 ? ( ) : battery.Alarms.value.toString().split(';').length >
1 ? (
<Link <Link
style={{ color: 'black' }} style={{ color: 'black' }}
to={ to={
currentPath.pathname.substring( currentLocation.pathname.substring(
0, 0,
currentPath.pathname.lastIndexOf('/') + 1 currentLocation.pathname.lastIndexOf('/') + 1
) + ) +
routes.log + routes.log +
'?open=error' '?open=error'
@ -279,7 +283,7 @@ function BatteryView(props: BatteryViewProps) {
Multiple Alarms Multiple Alarms
</Link> </Link>
) : ( ) : (
battery.Alarms battery.Alarms.value
)} )}
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -287,7 +291,6 @@ function BatteryView(props: BatteryViewProps) {
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
)}
</Container> </Container>
</> </>
); );

View File

@ -0,0 +1,971 @@
import React from 'react';
import { I_S3Credentials } from '../../../interfaces/S3Types';
import {
Card,
Grid,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableRow
} from '@mui/material';
import { Battery } from '../Log/graph.util';
import Button from '@mui/material/Button';
import { FormattedMessage } from 'react-intl';
import { useNavigate } from 'react-router-dom';
import routes from '../../../Resources/routes.json';
interface DetailedBatteryViewProps {
s3Credentials: I_S3Credentials;
batteryData: Battery;
}
function DetailedBatteryView(props: DetailedBatteryViewProps) {
if (props.batteryData === null) {
return null;
}
const navigate = useNavigate();
const handleBatteryViewButton = () => {
navigate(location.pathname.split('/').slice(0, -2).join('/'));
};
const handleMainStatsButton = () => {
navigate(
location.pathname.split('/').slice(0, -2).join('/') +
'/' +
routes.mainstats
);
};
return (
<>
<Grid container>
<Grid item xs={6} md={6}>
<Button
variant="contained"
onClick={handleBatteryViewButton}
sx={{
marginTop: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage id="main_stats" defaultMessage="Battery View" />
</Button>
<Button
variant="contained"
onClick={handleMainStatsButton}
sx={{
marginTop: '20px',
marginLeft: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage id="main_stats" defaultMessage="Main Stats" />
</Button>
</Grid>
</Grid>
<Grid item md={2} xs={2}>
<Card
sx={{
overflow: 'visible',
marginTop: '30px',
marginBottom: '30px',
backgroundColor: 'red'
}}
>
<TableContainer
component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }}
>
<Table size="medium" aria-label="a dense table">
<TableBody>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Voltage
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.Voltage.value +
' ' +
props.batteryData.Voltage.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Current
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.Current.value +
' ' +
props.batteryData.Current.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Power
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.Power.value +
' ' +
props.batteryData.Power.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Bus Current
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.BusCurrent.value +
' ' +
props.batteryData.BusCurrent.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Cells Current
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.CellsCurrent.value +
' ' +
props.batteryData.CellsCurrent.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Heating Current
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.HeatingCurrent.value +
' ' +
props.batteryData.HeatingCurrent.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Heating Power
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.HeatingPower.value +
' ' +
props.batteryData.HeatingPower.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Soc
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.Soc.value +
' ' +
props.batteryData.Soc.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
SOCAh
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.SOCAh.value +
' ' +
props.batteryData.SOCAh.unit}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Card>
</Grid>
{/*----------------------------------------------------------------------------------------------------------------------------------*/}
<Grid item md={2.9} xs={2.9}>
<Card
sx={{
overflow: 'visible',
marginTop: '30px',
marginLeft: '20px',
marginBottom: '30px',
backgroundColor: 'red'
}}
>
<TableContainer
component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }}
>
<Table size="medium" aria-label="a dense table">
<TableBody>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Heating
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.HeatingTemperature.value +
' ' +
props.batteryData.HeatingTemperature.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Board Temperature
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.BoardTemperature.value +
' ' +
props.batteryData.BoardTemperature.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Center Cells Temperature
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.AverageTemperature.value +
' ' +
props.batteryData.AverageTemperature.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Left Cells Temperature
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.LeftCellsTemperature.value +
' ' +
props.batteryData.LeftCellsTemperature.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Right Cells Temperature
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.RightCellsTemperature.value +
' ' +
props.batteryData.RightCellsTemperature.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Average Temperature
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.AverageTemperature.value +
' ' +
props.batteryData.AverageTemperature.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
State
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.StateTemperature.value +
' ' +
props.batteryData.StateTemperature.unit}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Card>
</Grid>
{/*----------------------------------------------------------------------------------------------------------------------------------*/}
<Grid item md={2.7} xs={2.7}>
<Card
sx={{
overflow: 'visible',
marginTop: '30px',
marginLeft: '20px',
marginBottom: '30px',
backgroundColor: 'red'
}}
>
<TableContainer
component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }}
>
<Table size="medium" aria-label="a dense table">
<TableBody>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Connected To Dc Bus
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.ConnectedToDcBus.value +
' ' +
props.batteryData.ConnectedToDcBus.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Alarm Out Active
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.AlarmOutActive.value +
' ' +
props.batteryData.AlarmOutActive.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Internal Fan Active
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.InternalFanActive.value +
' ' +
props.batteryData.InternalFanActive.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Volt Measurement Allowed
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.VoltMeasurementAllowed.value +
' ' +
props.batteryData.VoltMeasurementAllowed.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Aux Relay Bus
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.AuxRelayBus.value +
' ' +
props.batteryData.AuxRelayBus.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Remote State Active
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.RemoteStateActive.value +
' ' +
props.batteryData.RemoteStateActive.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
RiscActive
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.RiscActive.value +
' ' +
props.batteryData.RiscActive.unit}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Card>
</Grid>
{/*----------------------------------------------------------------------------------------------------------------------------------*/}
<Grid item md={2.9} xs={2.9}>
<Card
sx={{
overflow: 'visible',
marginTop: '30px',
marginLeft: '20px',
marginBottom: '30px',
backgroundColor: 'red'
}}
>
<TableContainer
component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }}
>
<Table size="medium" aria-label="a dense table">
<TableBody>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Eoc
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.Eoc.value +
' ' +
props.batteryData.Eoc.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Serial Number
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.SerialNumber.value +
' ' +
props.batteryData.SerialNumber.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Firmware Version
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.FwVersion.value +
' ' +
props.batteryData.FwVersion.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Time Since TOC
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.TimeSinceTOC.value +
' ' +
props.batteryData.TimeSinceTOC.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Calibration Charge Requested
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.CalibrationChargeRequested.value +
' ' +
props.batteryData.CalibrationChargeRequested.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Max Charge Power
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.MaxChargePower.value +
' ' +
props.batteryData.MaxChargePower.unit}
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Max Discharge Power
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
{props.batteryData.MaxDischargePower.value +
' ' +
props.batteryData.MaxDischargePower.unit}
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Card>
</Grid>
{/*----------------------------------------------------------------------------------------------------------------------------------*/}
<Grid item md={1.5} xs={1.5}>
<Card
sx={{
overflow: 'visible',
marginTop: '30px',
marginLeft: '20px',
marginBottom: '30px',
backgroundColor: 'red'
}}
>
<TableContainer
component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }}
>
<Table size="medium" aria-label="a dense table">
<TableBody>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Blue Led
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
<div
style={{
width: '20px',
height: '20px',
marginLeft: '2px',
borderRadius: '50%',
backgroundColor:
props.batteryData.BlueLeds.value === 'On'
? '#00ccff'
: 'transparent'
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Green Led
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
<div
style={{
width: '20px',
height: '20px',
marginLeft: '2px',
borderRadius: '50%',
backgroundColor:
props.batteryData.GreenLeds.value === 'On'
? 'green'
: 'transparent'
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Red Led
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
<div
style={{
width: '20px',
height: '20px',
marginLeft: '2px',
borderRadius: '50%',
backgroundColor:
props.batteryData.RedLeds.value === 'On'
? 'red'
: 'transparent'
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell
component="th"
scope="row"
align="left"
sx={{ fontWeight: 'bold' }}
>
Amber Led
</TableCell>
<TableCell
align="right"
sx={{
width: '6ch',
whiteSpace: 'nowrap',
paddingRight: '12px'
}}
>
<div
style={{
width: '20px',
height: '20px',
marginLeft: '2px',
borderRadius: '50%',
backgroundColor:
props.batteryData.AmberLeds.value === 'On'
? 'orange'
: 'transparent'
}}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
</Card>
</Grid>
</>
);
}
export default DetailedBatteryView;

View File

@ -384,7 +384,6 @@ function MainStats(props: MainStatsProps) {
{!loading && ( {!loading && (
<> <>
{' '}
<Grid item xs={6} md={6}> <Grid item xs={6} md={6}>
<Button <Button
variant="contained" variant="contained"

View File

@ -33,10 +33,10 @@ function Configuration(props: ConfigurationProps) {
const forcedCalibrationChargeOptions = ['No', 'UntilEoc', 'Yes']; const forcedCalibrationChargeOptions = ['No', 'UntilEoc', 'Yes'];
const [formValues, setFormValues] = useState<ConfigurationValues>({ const [formValues, setFormValues] = useState<ConfigurationValues>({
minimumSoC: props.values.minimumSoC.values[0].value, minimumSoC: props.values.minimumSoC[0].value,
gridSetPoint: (props.values.gridSetPoint.values[0].value as number) / 1000, gridSetPoint: (props.values.gridSetPoint[0].value as number) / 1000,
forceCalibrationCharge: forcedCalibrationChargeOptions.indexOf( forceCalibrationCharge: forcedCalibrationChargeOptions.indexOf(
props.values.calibrationChargeForced.values[0].value.toString() props.values.calibrationChargeForced[0].value.toString()
) )
}); });
@ -78,7 +78,7 @@ function Configuration(props: ConfigurationProps) {
selectedForcedCalibrationChargeOption, selectedForcedCalibrationChargeOption,
setSelectedForcedCalibrationChargeOption setSelectedForcedCalibrationChargeOption
] = useState<string>( ] = useState<string>(
props.values.calibrationChargeForced.values[0].value.toString() props.values.calibrationChargeForced[0].value.toString()
); );
//const forcedCalibrationChargeOptions = ['No', 'UntilEoc', 'Yes']; //const forcedCalibrationChargeOptions = ['No', 'UntilEoc', 'Yes'];
@ -242,8 +242,7 @@ function Configuration(props: ConfigurationProps) {
/> />
} }
value={ value={
(props.values.installedDcDcPower.values[0] (props.values.installedDcDcPower[0].value as number) * 10
.value as number) * 10
} }
fullWidth fullWidth
/> />
@ -257,10 +256,9 @@ function Configuration(props: ConfigurationProps) {
/> />
} }
value={ value={
(props.values.maximumDischargePower.values[0] (props.values.maximumDischargePower[0].value as number) *
.value as number) *
48 * 48 *
(props.values.DcDcNum.values[0].value as number) (props.values.DcDcNum[0].value as number)
} }
fullWidth fullWidth
/> />
@ -273,7 +271,7 @@ function Configuration(props: ConfigurationProps) {
defaultMessage="Number of Batteries" defaultMessage="Number of Batteries"
/> />
} }
value={props.values.battery.values.length - 4} value={props.values.battery.length - 4}
fullWidth fullWidth
/> />
</div> </div>

View File

@ -18,10 +18,10 @@ import Overview from '../Overview/overview';
import Configuration from '../Configuration/Configuration'; import Configuration from '../Configuration/Configuration';
import { fetchData } from 'src/content/dashboards/Installations/fetchData'; import { fetchData } from 'src/content/dashboards/Installations/fetchData';
import CancelIcon from '@mui/icons-material/Cancel'; import CancelIcon from '@mui/icons-material/Cancel';
import BatteryView from '../BatteryView/BatteryView';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom'; import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import routes from '../../../Resources/routes.json'; import routes from '../../../Resources/routes.json';
import Information from '../Information/Information'; import Information from '../Information/Information';
import BatteryView from '../BatteryView/BatteryView';
interface singleInstallationProps { interface singleInstallationProps {
current_installation?: I_Installation; current_installation?: I_Installation;
@ -31,7 +31,7 @@ interface singleInstallationProps {
function Installation(props: singleInstallationProps) { function Installation(props: singleInstallationProps) {
const context = useContext(UserContext); const context = useContext(UserContext);
const { currentUser } = context; const { currentUser } = context;
const location = useLocation(); const location = useLocation().pathname;
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false); const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
const webSocketsContext = useContext(WebSocketContext); const webSocketsContext = useContext(WebSocketContext);
const { getStatus } = webSocketsContext; const { getStatus } = webSocketsContext;
@ -90,7 +90,7 @@ function Installation(props: singleInstallationProps) {
}; };
useEffect(() => { useEffect(() => {
let path = location.pathname.split('/'); let path = location.split('/');
setCurrentTab(path[path.length - 1]); setCurrentTab(path[path.length - 1]);
}, [location]); }, [location]);
@ -99,26 +99,32 @@ function Installation(props: singleInstallationProps) {
if ( if (
currentTab == 'live' || currentTab == 'live' ||
currentTab == 'configuration' || currentTab == 'configuration' ||
currentTab == 'batteryview' location.includes('batteryview')
) { ) {
var interval; let interval;
if (currentTab == 'live' || currentTab == 'batteryview') { if (
currentTab == 'live' ||
(location.includes('batteryview') && !location.includes('mainstats'))
) {
fetchDataPeriodically(); fetchDataPeriodically();
interval = setInterval(fetchDataPeriodically, 2000); interval = setInterval(fetchDataPeriodically, 2000);
} }
if (currentTab == 'configuration') { if (currentTab == 'configuration' || location.includes('mainstats')) {
fetchDataOnlyOneTime(); fetchDataOnlyOneTime();
} }
// Cleanup function to cancel interval and update isMounted when unmounted // Cleanup function to cancel interval and update isMounted when unmounted
return () => { return () => {
if (currentTab == 'live' || currentTab == 'batteryview') { if (
currentTab == 'live' ||
(location.includes('batteryview') && !location.includes('mainstats'))
) {
clearInterval(interval); clearInterval(interval);
} }
}; };
} }
}, [currentTab, location.pathname]); }, [currentTab, location]);
return ( return (
<> <>
@ -178,7 +184,7 @@ function Installation(props: singleInstallationProps) {
fontSize: '14px' fontSize: '14px'
}} }}
> >
{values.mode.values[0].value} {values.mode[0].value}
</Typography> </Typography>
</div> </div>
)} )}
@ -274,8 +280,7 @@ function Installation(props: singleInstallationProps) {
s3Credentials={s3Credentials} s3Credentials={s3Credentials}
></BatteryView> ></BatteryView>
} }
/> ></Route>
<Route <Route
path={routes.overview} path={routes.overview}
element={<Overview s3Credentials={s3Credentials}></Overview>} element={<Overview s3Credentials={s3Credentials}></Overview>}

View File

@ -43,11 +43,8 @@ function InstallationTabs() {
} else if (path[path.length - 2] === 'tree') { } else if (path[path.length - 2] === 'tree') {
setCurrentTab('tree'); setCurrentTab('tree');
} else { } else {
setCurrentTab( //Even if we are located at path: /batteryview/mainstats, we want the BatteryView tab to be bold
tabList.includes(path[path.length - 1]) setCurrentTab(path.find((pathElement) => tabList.includes(pathElement)));
? path[path.length - 1]
: undefined
);
} }
}, [location]); }, [location]);
@ -140,10 +137,6 @@ function InstallationTabs() {
label: <FormattedMessage id="overview" defaultMessage="Overview" /> label: <FormattedMessage id="overview" defaultMessage="Overview" />
}, },
{
value: 'log',
label: <FormattedMessage id="log" defaultMessage="Log" />
},
{ {
value: 'information', value: 'information',
label: ( label: (
@ -247,10 +240,7 @@ function InstallationTabs() {
/> />
) )
}, },
{
value: 'log',
label: <FormattedMessage id="log" defaultMessage="Log" />
},
{ {
value: 'information', value: 'information',
label: ( label: (

View File

@ -28,59 +28,192 @@ export interface I_BoxDataValue {
value: string | number; value: string | number;
} }
export type BoxData = {
label: string;
values: I_BoxDataValue[];
};
export type ConfigurationValues = { export type ConfigurationValues = {
minimumSoC: string | number; minimumSoC: string | number;
gridSetPoint: number; gridSetPoint: number;
forceCalibrationCharge: number; forceCalibrationCharge: number;
}; };
export interface Battery {
BatteryId: number;
FwVersion: I_BoxDataValue;
Power: I_BoxDataValue;
Voltage: I_BoxDataValue;
Soc: I_BoxDataValue;
AverageTemperature: I_BoxDataValue;
Warnings: I_BoxDataValue;
Alarms: I_BoxDataValue;
Current: I_BoxDataValue;
BusCurrent: I_BoxDataValue;
CellsCurrent: I_BoxDataValue;
HeatingCurrent: I_BoxDataValue;
HeatingPower: I_BoxDataValue;
SOCAh: I_BoxDataValue;
BlueLeds: I_BoxDataValue;
RedLeds: I_BoxDataValue;
GreenLeds: I_BoxDataValue;
AmberLeds: I_BoxDataValue;
HeatingTemperature: I_BoxDataValue;
BoardTemperature: I_BoxDataValue;
LeftCellsTemperature: I_BoxDataValue;
RightCellsTemperature: I_BoxDataValue;
StateTemperature: I_BoxDataValue;
String1Active: I_BoxDataValue;
String2Active: I_BoxDataValue;
String3Active: I_BoxDataValue;
String4Active: I_BoxDataValue;
String5Active: I_BoxDataValue;
ConnectedToDcBus: I_BoxDataValue;
AlarmOutActive: I_BoxDataValue;
InternalFanActive: I_BoxDataValue;
VoltMeasurementAllowed: I_BoxDataValue;
AuxRelayBus: I_BoxDataValue;
RemoteStateActive: I_BoxDataValue;
RiscActive: I_BoxDataValue;
Eoc: I_BoxDataValue;
SerialNumber: I_BoxDataValue;
TimeSinceTOC: I_BoxDataValue;
CalibrationChargeRequested: I_BoxDataValue;
MaxChargePower: I_BoxDataValue;
MaxDischargePower: I_BoxDataValue;
}
const BatteryKeys = [
'BatteryId',
'FwVersion',
'Power',
'Voltage',
'Soc',
'AverageTemperature',
'Warnings',
'Alarms',
'Current',
'BusCurrent',
'CellsCurrent',
'HeatingCurrent',
'HeatingPower',
'SOCAh',
'BlueLeds',
'RedLeds',
'GreenLeds',
'AmberLeds',
'HeatingTemperature',
'BoardTemperature',
'LeftCellsTemperature',
'RightCellsTemperature',
'StateTemperature',
'String1Active',
'String2Active',
'String3Active',
'String4Active',
'String5Active',
'ConnectedToDcBus',
'AlarmOutActive',
'InternalFanActive',
'VoltMeasurementAllowed',
'AuxRelayBus',
'RemoteStateActive',
'RiscActive',
'Eoc',
'SerialNumber',
'TimeSinceTOC',
'CalibrationChargeRequested',
'MaxChargePower',
'MaxDischargePower'
];
export type TopologyValues = { export type TopologyValues = {
gridBox: BoxData; gridBox: I_BoxDataValue[];
pvOnAcGridBox: BoxData; pvOnAcGridBox: I_BoxDataValue[];
loadOnAcGridBox: BoxData; loadOnAcGridBox: I_BoxDataValue[];
pvOnIslandBusBox: BoxData; pvOnIslandBusBox: I_BoxDataValue[];
loadOnIslandBusBox: BoxData; loadOnIslandBusBox: I_BoxDataValue[];
pvOnDcBox: BoxData; pvOnDcBox: I_BoxDataValue[];
loadOnDcBox: BoxData; loadOnDcBox: I_BoxDataValue[];
batteryBox: BoxData; batteryBox: I_BoxDataValue[];
grid: BoxData; grid: I_BoxDataValue[];
gridToAcInConnection: BoxData; gridToAcInConnection: I_BoxDataValue[];
gridBus: BoxData; gridBus: I_BoxDataValue[];
islandBus: BoxData; islandBus: I_BoxDataValue[];
dcBus: BoxData; dcBus: I_BoxDataValue[];
dcBusToDcDcConnection: BoxData; dcBusToDcDcConnection: I_BoxDataValue[];
dcDCToBatteryConnection: BoxData; dcDCToBatteryConnection: I_BoxDataValue[];
battery: BoxData; battery: I_BoxDataValue[];
dcBusToLoadOnDcConnection: BoxData; dcBusToLoadOnDcConnection: I_BoxDataValue[];
islandBusToLoadOnIslandBusConnection: BoxData; islandBusToLoadOnIslandBusConnection: I_BoxDataValue[];
gridBusToPvOnGridbusConnection: BoxData; gridBusToPvOnGridbusConnection: I_BoxDataValue[];
gridBusToLoadOnGridBusConnection: BoxData; gridBusToLoadOnGridBusConnection: I_BoxDataValue[];
inverter: BoxData; inverter: I_BoxDataValue[];
dcDc: BoxData; dcDc: I_BoxDataValue[];
islandBusToInverter: BoxData; islandBusToInverter: I_BoxDataValue[];
inverterToDcBus: BoxData; inverterToDcBus: I_BoxDataValue[];
gridBusToIslandBusConnection: BoxData; gridBusToIslandBusConnection: I_BoxDataValue[];
pvOnDcBusToDcBusConnection: BoxData; pvOnDcBusToDcBusConnection: I_BoxDataValue[];
pvOnIslandBusToIslandBusConnection: BoxData; pvOnIslandBusToIslandBusConnection: I_BoxDataValue[];
minimumSoC: BoxData; minimumSoC: I_BoxDataValue[];
installedDcDcPower: BoxData; installedDcDcPower: I_BoxDataValue[];
gridSetPoint: BoxData; gridSetPoint: I_BoxDataValue[];
maximumDischargePower: BoxData; maximumDischargePower: I_BoxDataValue[];
DcDcNum: BoxData; DcDcNum: I_BoxDataValue[];
calibrationChargeForced: BoxData; calibrationChargeForced: I_BoxDataValue[];
mode: BoxData; mode: I_BoxDataValue[];
batteryView: BoxData; batteryView: Battery[];
}; };
type TopologyPaths = { [key in keyof TopologyValues]: string[] }; type TopologyPaths = { [key in keyof TopologyValues]: string[] };
const batteryIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const batteryPaths = [
'/Battery/Devices/%id%/FwVersion',
'/Battery/Devices/%id%/Dc/Power',
'/Battery/Devices/%id%/Dc/Voltage',
'/Battery/Devices/%id%/Soc',
'/Battery/Devices/%id%/Temperatures/Cells/Average',
'/Battery/Devices/%id%/Warnings',
'/Battery/Devices/%id%/Alarms',
'/Battery/Devices/%id%/Dc/Current',
'/Battery/Devices/%id%/BusCurrent',
'/Battery/Devices/%id%/CellsCurrent',
'/Battery/Devices/%id%/HeatingCurrent',
'/Battery/Devices/%id%/HeatingPower',
'/Battery/Devices/%id%/SOCAh',
'/Battery/Devices/%id%/Leds/Blue',
'/Battery/Devices/%id%/Leds/Red',
'/Battery/Devices/%id%/Leds/Green',
'/Battery/Devices/%id%/Leds/Amber',
'/Battery/Devices/%id%/Temperatures/Heating',
'/Battery/Devices/%id%/Temperatures/Board',
'/Battery/Devices/%id%/Temperatures/Cells/Left',
'/Battery/Devices/%id%/Temperatures/Cells/Right',
'/Battery/Devices/%id%/Temperatures/State',
'/Battery/Devices/%id%/BatteryStrings/String1Active',
'/Battery/Devices/%id%/BatteryStrings/String2Active',
'/Battery/Devices/%id%/BatteryStrings/String3Active',
'/Battery/Devices/%id%/BatteryStrings/String4Active',
'/Battery/Devices/%id%/BatteryStrings/String5Active',
'/Battery/Devices/%id%/IoStatus/ConnectedToDcBus',
'/Battery/Devices/%id%/IoStatus/AlarmOutActive',
'/Battery/Devices/%id%/IoStatus/InternalFanActive',
'/Battery/Devices/%id%/IoStatus/VoltMeasurementAllowed',
'/Battery/Devices/%id%/IoStatus/AuxRelayBus',
'/Battery/Devices/%id%/IoStatus/RemoteStateActive',
'/Battery/Devices/%id%/IoStatus/RiscActive',
'/Battery/Devices/%id%/Eoc',
'/Battery/Devices/%id%/SerialNumber',
'/Battery/Devices/%id%/TimeSinceTOC',
'/Battery/Devices/%id%/CalibrationChargeRequested',
'/Battery/Devices/%id%/MaxChargePower',
'/Battery/Devices/%id%/MaxDischargePower'
];
export const topologyPaths: TopologyPaths = { export const topologyPaths: TopologyPaths = {
gridBox: ['/Config/Devices/GridMeterIp/DeviceState'], gridBox: ['/Config/Devices/GridMeterIp/DeviceState'],
pvOnAcGridBox: ['/Config/Devices/PvOnAcGrid/DeviceState'], pvOnAcGridBox: ['/Config/Devices/PvOnAcGrid/DeviceState'],
@ -150,88 +283,9 @@ export const topologyPaths: TopologyPaths = {
'/Battery/Devices/10/Dc/Voltage' '/Battery/Devices/10/Dc/Voltage'
], ],
batteryView: [ batteryView: batteryIds.flatMap((id) =>
'/Config/Devices/BatteryNodes', batteryPaths.map((path) => path.replace('%id%', id.toString()))
'/Battery/Devices/1/FwVersion', ),
'/Battery/Devices/1/Dc/Power',
'/Battery/Devices/1/Dc/Voltage',
'/Battery/Devices/1/Soc',
'/Battery/Devices/1/Temperatures/Cells/Average',
'/Battery/Devices/1/Warnings',
'/Battery/Devices/1/Alarms',
'/Battery/Devices/2/FwVersion',
'/Battery/Devices/2/Dc/Power',
'/Battery/Devices/2/Dc/Voltage',
'/Battery/Devices/2/Soc',
'/Battery/Devices/2/Temperatures/Cells/Average',
'/Battery/Devices/2/Warnings',
'/Battery/Devices/2/Alarms',
'/Battery/Devices/3/FwVersion',
'/Battery/Devices/3/Dc/Power',
'/Battery/Devices/3/Dc/Voltage',
'/Battery/Devices/3/Soc',
'/Battery/Devices/3/Temperatures/Cells/Average',
'/Battery/Devices/3/Warnings',
'/Battery/Devices/3/Alarms',
'/Battery/Devices/4/FwVersion',
'/Battery/Devices/4/Dc/Power',
'/Battery/Devices/4/Dc/Voltage',
'/Battery/Devices/4/Soc',
'/Battery/Devices/4/Temperatures/Cells/Average',
'/Battery/Devices/4/Warnings',
'/Battery/Devices/4/Alarms',
'/Battery/Devices/5/FwVersion',
'/Battery/Devices/5/Dc/Power',
'/Battery/Devices/5/Dc/Voltage',
'/Battery/Devices/5/Soc',
'/Battery/Devices/5/Temperatures/Cells/Average',
'/Battery/Devices/5/Warnings',
'/Battery/Devices/5/Alarms',
'/Battery/Devices/6/FwVersion',
'/Battery/Devices/6/Dc/Power',
'/Battery/Devices/6/Dc/Voltage',
'/Battery/Devices/6/Soc',
'/Battery/Devices/6/Temperatures/Cells/Average',
'/Battery/Devices/6/Warnings',
'/Battery/Devices/6/Alarms',
'/Battery/Devices/7/FwVersion',
'/Battery/Devices/7/Dc/Power',
'/Battery/Devices/7/Dc/Voltage',
'/Battery/Devices/7/Soc',
'/Battery/Devices/7/Temperatures/Cells/Average',
'/Battery/Devices/7/Warnings',
'/Battery/Devices/7/Alarms',
'/Battery/Devices/8/FwVersion',
'/Battery/Devices/8/Dc/Power',
'/Battery/Devices/8/Dc/Voltage',
'/Battery/Devices/8/Soc',
'/Battery/Devices/8/Temperatures/Cells/Average',
'/Battery/Devices/8/Warnings',
'/Battery/Devices/8/Alarms',
'/Battery/Devices/9/FwVersion',
'/Battery/Devices/9/Dc/Power',
'/Battery/Devices/9/Dc/Voltage',
'/Battery/Devices/9/Soc',
'/Battery/Devices/9/Temperatures/Cells/Average',
'/Battery/Devices/9/Warnings',
'/Battery/Devices/9/Alarms',
'/Battery/Devices/10/FwVersion',
'/Battery/Devices/10/Dc/Power',
'/Battery/Devices/10/Dc/Voltage',
'/Battery/Devices/10/Soc',
'/Battery/Devices/10/Temperatures/Cells/Average',
'/Battery/Devices/10/Warnings',
'/Battery/Devices/10/Alarms'
],
minimumSoC: ['/Config/MinSoc'], minimumSoC: ['/Config/MinSoc'],
installedDcDcPower: ['/DcDc/SystemControl/NumberOfConnectedSlaves'], installedDcDcPower: ['/DcDc/SystemControl/NumberOfConnectedSlaves'],
@ -251,6 +305,44 @@ export const extractValues = (
const paths = topologyPaths[topologyKey]; const paths = topologyPaths[topologyKey];
let topologyValues: { unit: string; value: string | number }[] = []; let topologyValues: { unit: string; value: string | number }[] = [];
if (topologyKey === 'batteryView') {
extractedValues[topologyKey] = [];
const numOfBatteries = timeSeriesData.value[
'/Config/Devices/BatteryNodes'
].value
.toString()
.split(',').length;
let battery_id = 1;
let pathIndex = 0;
while (pathIndex < paths.length) {
let battery = {};
let existingKeys = 0;
battery[BatteryKeys[0]] = battery_id;
for (let i = 1; i < BatteryKeys.length; i++) {
const path = paths[pathIndex];
if (timeSeriesData.value.hasOwnProperty(path)) {
existingKeys++;
battery[BatteryKeys[i]] = {
unit: timeSeriesData.value[path].unit.includes('~')
? timeSeriesData.value[path].unit.replace('~', '')
: timeSeriesData.value[path].unit,
value:
typeof timeSeriesData.value[path].value === 'string'
? timeSeriesData.value[path].value
: Number(timeSeriesData.value[path].value).toFixed(1)
};
}
pathIndex++;
}
battery_id++;
if (existingKeys > 0) {
extractedValues[topologyKey].push(battery as Battery);
}
}
} else {
// Check if any of the specified paths exist in the dataRecord // Check if any of the specified paths exist in the dataRecord
for (const path of paths) { for (const path of paths) {
if (timeSeriesData.value.hasOwnProperty(path)) { if (timeSeriesData.value.hasOwnProperty(path)) {
@ -263,27 +355,26 @@ export const extractValues = (
} }
} }
if (topologyValues.length > 0) { if (topologyValues.length > 0) {
extractedValues[topologyKey] = { extractedValues[topologyKey] = topologyValues;
label: topologyPaths[topologyKey as keyof TopologyValues][0] }
.split('/')
.pop(),
values: topologyValues
};
} }
} }
return extractedValues; return extractedValues;
}; };
export const getHighestConnectionValue = (values: TopologyValues) => export const getHighestConnectionValue = (values: TopologyValues) => {
Object.keys(values) return Object.keys(values)
.filter((value) => value.includes('Connection')) .filter((value) => value.includes('Connection'))
.reduce((acc, curr) => { .reduce((acc, curr) => {
const value = Math.abs( let topologyValue = values[
values[curr as keyof TopologyValues].values[0].value as number curr as keyof TopologyValues
); ][0] as I_BoxDataValue;
const value = Math.abs(topologyValue.value as number);
return value > acc ? value : acc; return value > acc ? value : acc;
}, 0); }, 0);
};
export const getAmount = ( export const getAmount = (
highestConnectionValue: number, highestConnectionValue: number,

View File

@ -75,8 +75,7 @@ function Topology(props: TopologyProps) {
centerBox={{ centerBox={{
title: 'Grid', title: 'Grid',
data: props.values.grid, data: props.values.grid,
connected: connected: props.values.gridBox[0].value.toString() != 'Disabled'
props.values.gridBox.values[0].value.toString() != 'Disabled'
}} }}
centerConnection={{ centerConnection={{
orientation: 'horizontal', orientation: 'horizontal',
@ -84,7 +83,7 @@ function Topology(props: TopologyProps) {
amount: props.values.gridToAcInConnection amount: props.values.gridToAcInConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.gridToAcInConnection.values props.values.gridToAcInConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -97,8 +96,7 @@ function Topology(props: TopologyProps) {
title: 'Pv Inverter', title: 'Pv Inverter',
data: props.values.gridBusToPvOnGridbusConnection, data: props.values.gridBusToPvOnGridbusConnection,
connected: connected:
props.values.pvOnAcGridBox.values[0].value.toString() != props.values.pvOnAcGridBox[0].value.toString() != 'Disabled'
'Disabled'
}} }}
topConnection={{ topConnection={{
orientation: 'vertical', orientation: 'vertical',
@ -107,7 +105,7 @@ function Topology(props: TopologyProps) {
amount: props.values.gridBusToPvOnGridbusConnection amount: props.values.gridBusToPvOnGridbusConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.gridBusToPvOnGridbusConnection.values props.values.gridBusToPvOnGridbusConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -123,7 +121,7 @@ function Topology(props: TopologyProps) {
amount: props.values.gridBusToIslandBusConnection amount: props.values.gridBusToIslandBusConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.gridBusToIslandBusConnection.values props.values.gridBusToIslandBusConnection
) )
: 0, : 0,
data: props.values.gridBusToIslandBusConnection, data: props.values.gridBusToIslandBusConnection,
@ -133,8 +131,7 @@ function Topology(props: TopologyProps) {
title: 'AC Loads', title: 'AC Loads',
data: props.values.gridBusToLoadOnGridBusConnection, data: props.values.gridBusToLoadOnGridBusConnection,
connected: connected:
props.values.loadOnAcGridBox.values[0].value.toString() != props.values.loadOnAcGridBox[0].value.toString() != 'Disabled'
'Disabled'
}} }}
bottomConnection={{ bottomConnection={{
orientation: 'vertical', orientation: 'vertical',
@ -143,7 +140,7 @@ function Topology(props: TopologyProps) {
amount: props.values.gridBusToLoadOnGridBusConnection amount: props.values.gridBusToLoadOnGridBusConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.gridBusToLoadOnGridBusConnection.values props.values.gridBusToLoadOnGridBusConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -156,8 +153,7 @@ function Topology(props: TopologyProps) {
title: 'Pv Inverter', title: 'Pv Inverter',
data: props.values.pvOnIslandBusToIslandBusConnection, data: props.values.pvOnIslandBusToIslandBusConnection,
connected: connected:
props.values.pvOnIslandBusBox.values[0].value.toString() != props.values.pvOnIslandBusBox[0].value.toString() != 'Disabled'
'Disabled'
}} }}
topConnection={{ topConnection={{
orientation: 'vertical', orientation: 'vertical',
@ -166,7 +162,7 @@ function Topology(props: TopologyProps) {
amount: props.values.pvOnIslandBusToIslandBusConnection amount: props.values.pvOnIslandBusToIslandBusConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.pvOnIslandBusToIslandBusConnection.values props.values.pvOnIslandBusToIslandBusConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -182,7 +178,7 @@ function Topology(props: TopologyProps) {
amount: props.values.islandBusToInverter amount: props.values.islandBusToInverter
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.islandBusToInverter.values props.values.islandBusToInverter
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -191,7 +187,7 @@ function Topology(props: TopologyProps) {
title: 'AC Loads', title: 'AC Loads',
data: props.values.islandBusToLoadOnIslandBusConnection, data: props.values.islandBusToLoadOnIslandBusConnection,
connected: connected:
props.values.loadOnIslandBusBox.values[0].value.toString() != props.values.loadOnIslandBusBox[0].value.toString() !=
'Disabled' 'Disabled'
}} }}
bottomConnection={{ bottomConnection={{
@ -201,7 +197,7 @@ function Topology(props: TopologyProps) {
amount: props.values.islandBusToLoadOnIslandBusConnection amount: props.values.islandBusToLoadOnIslandBusConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.islandBusToLoadOnIslandBusConnection.values props.values.islandBusToLoadOnIslandBusConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -221,7 +217,7 @@ function Topology(props: TopologyProps) {
amount: props.values.inverterToDcBus amount: props.values.inverterToDcBus
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.inverterToDcBus.values props.values.inverterToDcBus
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -234,7 +230,7 @@ function Topology(props: TopologyProps) {
title: 'Pv DC-DC', title: 'Pv DC-DC',
data: props.values.pvOnDcBusToDcBusConnection, data: props.values.pvOnDcBusToDcBusConnection,
connected: connected:
props.values.pvOnDcBox.values[0].value.toString() != 'Disabled' props.values.pvOnDcBox[0].value.toString() != 'Disabled'
}} }}
topConnection={{ topConnection={{
orientation: 'vertical', orientation: 'vertical',
@ -243,7 +239,7 @@ function Topology(props: TopologyProps) {
amount: props.values.pvOnDcBusToDcBusConnection amount: props.values.pvOnDcBusToDcBusConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.pvOnDcBusToDcBusConnection.values props.values.pvOnDcBusToDcBusConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -259,7 +255,7 @@ function Topology(props: TopologyProps) {
amount: props.values.dcBusToDcDcConnection amount: props.values.dcBusToDcDcConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.dcBusToDcDcConnection.values props.values.dcBusToDcDcConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -268,8 +264,7 @@ function Topology(props: TopologyProps) {
title: 'DC Loads', title: 'DC Loads',
data: props.values.dcBusToLoadOnDcConnection, data: props.values.dcBusToLoadOnDcConnection,
connected: connected:
props.values.loadOnDcBox.values[0].value.toString() != props.values.loadOnDcBox[0].value.toString() != 'Disabled'
'Disabled'
}} }}
bottomConnection={{ bottomConnection={{
orientation: 'vertical', orientation: 'vertical',
@ -278,7 +273,7 @@ function Topology(props: TopologyProps) {
amount: props.values.dcBusToLoadOnDcConnection amount: props.values.dcBusToLoadOnDcConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.dcBusToLoadOnDcConnection.values props.values.dcBusToLoadOnDcConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -298,7 +293,7 @@ function Topology(props: TopologyProps) {
amount: props.values.dcDCToBatteryConnection amount: props.values.dcDCToBatteryConnection
? getAmount( ? getAmount(
highestConnectionValue, highestConnectionValue,
props.values.dcDCToBatteryConnection.values props.values.dcDCToBatteryConnection
) )
: 0, : 0,
showValues: showValues showValues: showValues
@ -311,7 +306,7 @@ function Topology(props: TopologyProps) {
title: 'Battery', title: 'Battery',
data: props.values.battery, data: props.values.battery,
connected: connected:
props.values.batteryBox.values[0].value.toString() != 'Disabled' props.values.batteryBox[0].value.toString() != 'Disabled'
}} }}
isLast={true} isLast={true}
isFirst={false} isFirst={false}

View File

@ -1,12 +1,5 @@
import React from 'react'; import React from 'react';
import { import { Box, Card, CardContent, Divider, Typography } from '@mui/material';
Box,
Card,
CardContent,
Divider,
Typography,
useTheme
} from '@mui/material';
import { AvatarWrapper } from 'src/layouts/TabsContainerWrapper'; import { AvatarWrapper } from 'src/layouts/TabsContainerWrapper';
import BatteryCharging60Icon from '@mui/icons-material/BatteryCharging60'; import BatteryCharging60Icon from '@mui/icons-material/BatteryCharging60';
import OutletIcon from '@mui/icons-material/Outlet'; import OutletIcon from '@mui/icons-material/Outlet';
@ -14,21 +7,17 @@ import SolarPowerIcon from '@mui/icons-material/SolarPower';
import PowerInputIcon from '@mui/icons-material/PowerInput'; import PowerInputIcon from '@mui/icons-material/PowerInput';
import SignalWifiConnectedNoInternet4Icon from '@mui/icons-material/SignalWifiConnectedNoInternet4'; import SignalWifiConnectedNoInternet4Icon from '@mui/icons-material/SignalWifiConnectedNoInternet4';
import BoltIcon from '@mui/icons-material/Bolt'; import BoltIcon from '@mui/icons-material/Bolt';
import { BoxData } from '../Log/graph.util'; import { I_BoxDataValue } from '../Log/graph.util';
import inverterImage from 'src/Resources/images/inverter.png'; import inverterImage from 'src/Resources/images/inverter.png';
import acCurrentImage from 'src/Resources/images/ac-current.png'; import acCurrentImage from 'src/Resources/images/ac-current.png';
import converterImage from 'src/Resources/images/converter.png'; import converterImage from 'src/Resources/images/converter.png';
export interface TopologyBoxProps { export interface TopologyBoxProps {
title: string; title: string;
data?: BoxData; data?: I_BoxDataValue[];
connected?: boolean; connected?: boolean;
} }
const isInt = (value: number) => {
return value % 1 === 0;
};
function formatPower(value, unit) { function formatPower(value, unit) {
if (isNaN(value)) { if (isNaN(value)) {
return 'Invalid'; return 'Invalid';
@ -60,7 +49,6 @@ function formatPower(value, unit) {
} }
function TopologyBox(props: TopologyBoxProps) { function TopologyBox(props: TopologyBoxProps) {
const theme = useTheme();
const isMobile = window.innerWidth <= 1490; const isMobile = window.innerWidth <= 1490;
return ( return (
@ -106,7 +94,7 @@ function TopologyBox(props: TopologyBoxProps) {
> >
{props.data && props.title === 'Battery' && ( {props.data && props.title === 'Battery' && (
<Typography variant="h5" noWrap> <Typography variant="h5" noWrap>
{props.title} (x{props.data.values.length - 4}) {props.title} (x{props.data.length - 4})
</Typography> </Typography>
)} )}
{props.title != 'Battery' && ( {props.title != 'Battery' && (
@ -245,7 +233,7 @@ function TopologyBox(props: TopologyBoxProps) {
pt: 1 pt: 1
}} }}
> >
{props.data.values.map((boxData, index) => { {props.data.map((boxData, index) => {
return ( return (
<Typography key={index}> <Typography key={index}>
{formatPower(boxData.value, boxData.unit) === 0 {formatPower(boxData.value, boxData.unit) === 0

View File

@ -1,12 +1,12 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import './dotsAnimation.css'; import './dotsAnimation.css';
import { BoxData } from '../Log/graph.util';
import { Box, Typography } from '@mui/material'; import { Box, Typography } from '@mui/material';
import { I_BoxDataValue } from '../Log/graph.util';
export interface TopologyFlowProps { export interface TopologyFlowProps {
orientation: string; orientation: string;
position?: string; position?: string;
data?: BoxData; data?: I_BoxDataValue[];
amount?: number; amount?: number;
showValues: boolean; showValues: boolean;
} }
@ -23,10 +23,6 @@ function getRandomStyleVertical() {
return { animationDelay, left }; return { animationDelay, left };
} }
const isInt = (value: number) => {
return value % 1 === 0;
};
function formatPower(value) { function formatPower(value) {
if (isNaN(value)) { if (isNaN(value)) {
return 'Invalid'; return 'Invalid';
@ -107,7 +103,7 @@ function TopologyFlow(props: TopologyFlowProps) {
zIndex: 1 zIndex: 1
}} }}
> >
{props.showValues && props.data.values[0].value != 0 && ( {props.showValues && props.data[0].value != 0 && (
<Typography <Typography
sx={{ sx={{
marginTop: '1px', marginTop: '1px',
@ -118,12 +114,12 @@ function TopologyFlow(props: TopologyFlowProps) {
zIndex: 1 zIndex: 1
}} }}
> >
{formatPower(props.data.values[0].value) === 0 {formatPower(props.data[0].value) === 0
? null ? null
: formatPower(props.data.values[0].value)} : formatPower(props.data[0].value)}
{formatPower(props.data.values[0].value) === 0 {formatPower(props.data[0].value) === 0
? null ? null
: props.data.values[0].unit} : props.data[0].unit}
</Typography> </Typography>
)} )}
</Box> </Box>
@ -158,13 +154,13 @@ function TopologyFlow(props: TopologyFlowProps) {
> >
{props.orientation === 'horizontal' && {props.orientation === 'horizontal' &&
props.data && props.data &&
props.data.values[0].value != 0 && ( props.data[0].value != 0 && (
<> <>
<div className="container"> <div className="container">
{dotStyles.map((style, index) => ( {dotStyles.map((style, index) => (
<div <div
className={ className={
(props.data.values[0].value as number) >= 0 (props.data[0].value as number) >= 0
? 'dotRight' ? 'dotRight'
: 'dotLeft' : 'dotLeft'
} }
@ -181,7 +177,7 @@ function TopologyFlow(props: TopologyFlowProps) {
{props.orientation === 'vertical' && {props.orientation === 'vertical' &&
props.data && props.data &&
props.data.values[0].value != 0 && ( props.data[0].value != 0 && (
<div className="dot-container"> <div className="dot-container">
{dotStylesVertical.map((style, index) => ( {dotStylesVertical.map((style, index) => (
<div <div