Compare commits

...

3 Commits

Author SHA1 Message Date
Noe 2851e34e47 Update firmware on backend 2024-03-19 17:43:00 +01:00
Noe ce12d6a59a Update firmware on frontend 2024-03-19 17:42:52 +01:00
Noe 2f24f97304 Update firmware on frontend 2024-03-19 17:42:33 +01:00
8 changed files with 557 additions and 329 deletions

View File

@ -514,18 +514,16 @@ public class Controller : ControllerBase
} }
[HttpPost(nameof(UpdateFirmware))] [HttpPost(nameof(UpdateFirmware))]
public ActionResult UpdateFirmware(Int64 batteryNode, Int64 installationId,Token authToken) public async Task<ActionResult> UpdateFirmware(Int64 batteryNode, Int64 installationId,Token authToken)
{ {
var session = Db.GetSession(authToken); var session = Db.GetSession(authToken);
var installationToUpdate = Db.GetInstallationById(installationId); var installationToUpdate = Db.GetInstallationById(installationId);
Console.WriteLine("Inside firmware function controller,batteryNode="+batteryNode+ "and installation is "+installationId); Console.WriteLine("Inside firmware function controller,batteryNode="+batteryNode+ "and installation is "+installationId);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
if (installationToUpdate != null) if (installationToUpdate != null)
{ {
session.RunScriptInBackground(installationToUpdate.VpnIp, batteryNode); _ = session.RunScriptInBackground(installationToUpdate.VpnIp, batteryNode);
} }
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
return Ok(); return Ok();
} }

View File

@ -2,6 +2,7 @@ using System.Diagnostics;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.Relations; using InnovEnergy.App.Backend.Relations;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using Org.BouncyCastle.Asn1.X509;
namespace InnovEnergy.App.Backend.DataTypes.Methods; namespace InnovEnergy.App.Backend.DataTypes.Methods;
@ -67,8 +68,9 @@ public static class SessionMethods
.Apply(Db.Update); .Apply(Db.Update);
} }
public static async Task<Boolean> RunScriptInBackground(this Session? session, String vpnIp, Int64 batteryNode) public static async Task RunScriptInBackground(this Session? session, String vpnIp, Int64 batteryNode)
{ {
Console.WriteLine("11111111111111111111111111111111111111111111111111111111111111");
string scriptPath = "/home/ubuntu/backend/uploadBatteryFw/update_firmware.sh"; string scriptPath = "/home/ubuntu/backend/uploadBatteryFw/update_firmware.sh";
await Task.Run(() => await Task.Run(() =>
@ -80,11 +82,11 @@ public static class SessionMethods
process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardOutput = true;
process.Start(); process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit(); // This can be removed if you don't need to wait for the process to finish process.WaitForExit(); // This can be removed if you don't need to wait for the process to finish
Console.WriteLine(output);
}); });
Console.WriteLine("222222222222222222222222222222222222222222222222222222222222222");
return true;
} }

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { import {
Container, Container,
Grid, Grid,
@ -8,7 +8,8 @@ import {
TableCell, TableCell,
TableContainer, TableContainer,
TableHead, TableHead,
TableRow TableRow,
Typography
} from '@mui/material'; } from '@mui/material';
import { TopologyValues } from '../Log/graph.util'; import { TopologyValues } from '../Log/graph.util';
import { import {
@ -24,10 +25,12 @@ 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'; import DetailedBatteryView from './DetailedBatteryView';
import CircularProgress from '@mui/material/CircularProgress';
interface BatteryViewProps { interface BatteryViewProps {
values: TopologyValues; values: TopologyValues;
s3Credentials: I_S3Credentials; s3Credentials: I_S3Credentials;
installationId: number;
} }
function BatteryView(props: BatteryViewProps) { function BatteryView(props: BatteryViewProps) {
@ -40,6 +43,10 @@ function BatteryView(props: BatteryViewProps) {
(a, b) => b.BatteryId - a.BatteryId (a, b) => b.BatteryId - a.BatteryId
); );
const [loading, setLoading] = useState(
sortedBatteryView.length == 0 ? true : false
);
const handleMainStatsButton = () => { const handleMainStatsButton = () => {
navigate(routes.mainstats); navigate(routes.mainstats);
}; };
@ -52,15 +59,116 @@ function BatteryView(props: BatteryViewProps) {
} }
}; };
useEffect(() => {
if (sortedBatteryView.length == 0) {
setLoading(true);
} else {
setLoading(false);
}
}, [sortedBatteryView]);
return ( return (
<> <>
<Container maxWidth="xl"> {loading && (
<Grid container> <Container
<Grid maxWidth="xl"
item sx={{
xs={6} display: 'flex',
md={6} flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '70vh'
}}
>
<CircularProgress size={60} style={{ color: '#ffc04d' }} />
<Typography
variant="body2"
style={{ color: 'black', fontWeight: 'bold' }}
mt={2}
>
Battery service is not available at the moment
</Typography>
<Typography variant="body2" style={{ color: 'black' }}>
Please wait or refresh the page
</Typography>
</Container>
)}
{!loading && (
<Container maxWidth="xl">
<Grid container>
<Grid
item
xs={6}
md={6}
sx={{
display:
!currentLocation.pathname.includes('detailed_view') &&
!currentLocation.pathname.includes('mainstats')
? 'block'
: 'none'
}}
>
<Button
variant="contained"
sx={{
marginTop: '20px',
backgroundColor: '#808080',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage
id="battery_view"
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 container>
<Routes>
<Route
path={routes.mainstats + '*'}
element={
<MainStats s3Credentials={props.s3Credentials}></MainStats>
}
/>
{props.values.batteryView.map((battery) => (
<Route
key={routes.detailed_view + battery.BatteryId}
path={routes.detailed_view + battery.BatteryId}
element={
<DetailedBatteryView
s3Credentials={props.s3Credentials}
batteryData={findBatteryData(battery.BatteryId)}
installationId={props.installationId}
></DetailedBatteryView>
}
/>
))}
</Routes>
</Grid>
<TableContainer
component={Paper}
sx={{ sx={{
marginTop: '20px',
marginBottom: '20px',
display: display:
!currentLocation.pathname.includes('detailed_view') && !currentLocation.pathname.includes('detailed_view') &&
!currentLocation.pathname.includes('mainstats') !currentLocation.pathname.includes('mainstats')
@ -68,237 +176,175 @@ function BatteryView(props: BatteryViewProps) {
: 'none' : 'none'
}} }}
> >
<Button <Table sx={{ minWidth: 650 }} aria-label="simple table">
variant="contained" <TableHead>
sx={{ <TableRow>
marginTop: '20px', <TableCell align="center">Battery</TableCell>
backgroundColor: '#808080', <TableCell align="center">Firmware</TableCell>
color: '#000000', <TableCell align="center">Power</TableCell>
'&:hover': { bgcolor: '#f7b34d' } <TableCell align="center">Voltage</TableCell>
}} <TableCell align="center">SoC</TableCell>
> <TableCell align="center">Temperature</TableCell>
<FormattedMessage <TableCell align="center">Warnings</TableCell>
id="battery_view" <TableCell align="center">Alarms</TableCell>
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 container>
<Routes>
<Route
path={routes.mainstats + '*'}
element={
<MainStats s3Credentials={props.s3Credentials}></MainStats>
}
/>
{props.values.batteryView.map((battery) => (
<Route
key={routes.detailed_view + battery.BatteryId}
path={routes.detailed_view + battery.BatteryId}
element={
<DetailedBatteryView
s3Credentials={props.s3Credentials}
batteryData={findBatteryData(battery.BatteryId)}
></DetailedBatteryView>
}
/>
))}
</Routes>
</Grid>
<TableContainer
component={Paper}
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">
<TableHead>
<TableRow>
<TableCell align="center">Battery</TableCell>
<TableCell align="center">Firmware</TableCell>
<TableCell align="center">Power</TableCell>
<TableCell align="center">Voltage</TableCell>
<TableCell align="center">SoC</TableCell>
<TableCell align="center">Temperature</TableCell>
<TableCell align="center">Warnings</TableCell>
<TableCell align="center">Alarms</TableCell>
</TableRow>
</TableHead>
<TableBody>
{sortedBatteryView.map((battery) => (
<TableRow
key={battery.BatteryId}
style={{
height: '10px'
}}
>
<TableCell
component="th"
scope="row"
align="center"
sx={{ fontWeight: 'bold' }}
>
<Link
style={{ color: 'black' }}
to={routes.detailed_view + battery.BatteryId.toString()}
>
{'Node ' + battery.BatteryId}
</Link>
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center'
}}
>
{battery.FwVersion.value}
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center'
}}
>
{battery.Power.value + ' ' + battery.Power.unit}
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center',
backgroundColor:
battery.Voltage.value < 44 || battery.Voltage.value > 57
? '#FF033E'
: '#32CD32',
color: battery.Voltage.value === '' ? 'white' : 'inherit'
}}
>
{battery.Voltage.value + ' ' + battery.Voltage.unit}
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center',
backgroundColor:
battery.Soc.value < 20
? '#FF033E'
: battery.Soc.value < 50
? '#ffbf00'
: '#32CD32',
color: battery.Soc.value === '' ? 'white' : 'inherit'
}}
>
{battery.Soc.value + ' ' + battery.Soc.unit}
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center',
backgroundColor:
battery.AverageTemperature.value > 270
? '#FF033E'
: '#32CD32 '
}}
>
{battery.AverageTemperature.value +
' ' +
battery.AverageTemperature.unit}
</TableCell>
<TableCell
style={{
width: '20%',
textAlign: 'center',
padding: '8px',
fontWeight:
battery.Warnings.value !== '' ? 'bold' : 'inherit',
backgroundColor:
battery.Warnings.value === '' ? 'inherit' : '#ff9900',
color: battery.Warnings.value != '' ? 'black' : 'inherit'
}}
>
{battery.Warnings.value === '' ? (
'None'
) : battery.Warnings.value.toString().split('-').length >
1 ? (
<Link
style={{ color: 'black' }}
to={
currentLocation.pathname.substring(
0,
currentLocation.pathname.lastIndexOf('/') + 1
) +
routes.log +
'?open=warning'
}
>
Multiple Warnings
</Link>
) : (
battery.Warnings.value
)}
</TableCell>
<TableCell
sx={{
width: '20%',
textAlign: 'center',
fontWeight:
battery.Alarms.value !== '' ? 'bold' : 'inherit',
backgroundColor:
battery.Alarms.value === '' ? 'inherit' : '#FF033E',
color: battery.Alarms.value != '' ? 'black' : 'inherit'
}}
>
{battery.Alarms.value === '' ? (
'None'
) : battery.Alarms.value.toString().split('-').length >
1 ? (
<Link
style={{ color: 'black' }}
to={
currentLocation.pathname.substring(
0,
currentLocation.pathname.lastIndexOf('/') + 1
) +
routes.log +
'?open=error'
}
>
Multiple Alarms
</Link>
) : (
battery.Alarms.value
)}
</TableCell>
</TableRow> </TableRow>
))} </TableHead>
</TableBody> <TableBody>
</Table> {sortedBatteryView.map((battery) => (
</TableContainer> <TableRow
</Container> key={battery.BatteryId}
style={{
height: '10px'
}}
>
<TableCell
component="th"
scope="row"
align="center"
sx={{ fontWeight: 'bold' }}
>
<Link
style={{ color: 'black' }}
to={routes.detailed_view + battery.BatteryId.toString()}
>
{'Node ' + battery.BatteryId}
</Link>
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center'
}}
>
{battery.FwVersion.value}
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center'
}}
>
{battery.Power.value + ' ' + battery.Power.unit}
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center',
backgroundColor:
battery.Voltage.value < 44 ||
battery.Voltage.value > 57
? '#FF033E'
: '#32CD32',
color:
battery.Voltage.value === '' ? 'white' : 'inherit'
}}
>
{battery.Voltage.value + ' ' + battery.Voltage.unit}
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center',
backgroundColor:
battery.Soc.value < 20
? '#FF033E'
: battery.Soc.value < 50
? '#ffbf00'
: '#32CD32',
color: battery.Soc.value === '' ? 'white' : 'inherit'
}}
>
{battery.Soc.value + ' ' + battery.Soc.unit}
</TableCell>
<TableCell
sx={{
width: '10%',
textAlign: 'center',
backgroundColor:
battery.AverageTemperature.value > 270
? '#FF033E'
: '#32CD32 '
}}
>
{battery.AverageTemperature.value +
' ' +
battery.AverageTemperature.unit}
</TableCell>
<TableCell
style={{
width: '20%',
textAlign: 'center',
padding: '8px',
fontWeight:
battery.Warnings.value !== '' ? 'bold' : 'inherit',
backgroundColor:
battery.Warnings.value === '' ? 'inherit' : '#ff9900',
color:
battery.Warnings.value != '' ? 'black' : 'inherit'
}}
>
{battery.Warnings.value === '' ? (
'None'
) : battery.Warnings.value.toString().split('-').length >
1 ? (
<Link
style={{ color: 'black' }}
to={
currentLocation.pathname.substring(
0,
currentLocation.pathname.lastIndexOf('/') + 1
) +
routes.log +
'?open=warning'
}
>
Multiple Warnings
</Link>
) : (
battery.Warnings.value
)}
</TableCell>
<TableCell
sx={{
width: '20%',
textAlign: 'center',
fontWeight:
battery.Alarms.value !== '' ? 'bold' : 'inherit',
backgroundColor:
battery.Alarms.value === '' ? 'inherit' : '#FF033E',
color: battery.Alarms.value != '' ? 'black' : 'inherit'
}}
>
{battery.Alarms.value === '' ? (
'None'
) : battery.Alarms.value.toString().split('-').length >
1 ? (
<Link
style={{ color: 'black' }}
to={
currentLocation.pathname.substring(
0,
currentLocation.pathname.lastIndexOf('/') + 1
) +
routes.log +
'?open=error'
}
>
Multiple Alarms
</Link>
) : (
battery.Alarms.value
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Container>
)}
</> </>
); );
} }

View File

@ -1,8 +1,11 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { I_S3Credentials } from '../../../interfaces/S3Types'; import { I_S3Credentials } from '../../../interfaces/S3Types';
import { import {
Box,
Card, Card,
Grid, Grid,
IconButton,
Modal,
Paper, Paper,
Table, Table,
TableBody, TableBody,
@ -12,14 +15,16 @@ import {
Typography Typography
} from '@mui/material'; } from '@mui/material';
import { Battery } from '../Log/graph.util'; import { Battery } from '../Log/graph.util';
import { useNavigate } from 'react-router-dom';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { useNavigate } from 'react-router-dom'; import axiosConfig from '../../../Resources/axiosConfig';
import routes from '../../../Resources/routes.json';
interface DetailedBatteryViewProps { interface DetailedBatteryViewProps {
s3Credentials: I_S3Credentials; s3Credentials: I_S3Credentials;
batteryData: Battery; batteryData: Battery;
installationId: number;
} }
function DetailedBatteryView(props: DetailedBatteryViewProps) { function DetailedBatteryView(props: DetailedBatteryViewProps) {
@ -27,16 +32,20 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
return null; return null;
} }
const navigate = useNavigate(); const navigate = useNavigate();
const [openModalFirmwareUpdate, setOpenModalFirmwareUpdate] = useState(false);
const [openModalResultFirmwareUpdate, setOpenModalResultFirmwareUpdate] =
useState(false);
const handleBatteryViewButton = () => { const handleBatteryViewButton = () => {
navigate(location.pathname.split('/').slice(0, -2).join('/')); navigate(location.pathname.split('/').slice(0, -2).join('/'));
}; };
const handleMainStatsButton = () => {
navigate( const handleUpdateFirmware = () => {
location.pathname.split('/').slice(0, -2).join('/') + setOpenModalFirmwareUpdate(true);
'/' + };
routes.mainstats
); const firmwareModalResultHandleOk = () => {
navigate(location.pathname.split('/').slice(0, -2).join('/'));
}; };
const [GreenisBlinking, setGreenisBlinking] = useState( const [GreenisBlinking, setGreenisBlinking] = useState(
@ -96,26 +105,177 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
backgroundColor: '#bfbfbf' backgroundColor: '#bfbfbf'
}; };
const FirmwareModalHandleProceed = async (e) => {
setOpenModalFirmwareUpdate(false);
const res = await axiosConfig
.post(
`/UpdateFirmware?batteryNode=${props.batteryData.BatteryId.toString()}&installationId=${
props.installationId
}`
)
.catch((err) => {
if (err.response) {
// setError(true);
// setLoading(false);
}
});
//if (res) {
setOpenModalResultFirmwareUpdate(true);
//}
};
const FirmwareModalHandleCancel = () => {
setOpenModalFirmwareUpdate(false);
};
return ( return (
<> <>
{openModalResultFirmwareUpdate && (
<Modal
open={openModalResultFirmwareUpdate}
aria-labelledby="error-modal"
aria-describedby="error-modal-description"
>
<Box
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 420,
bgcolor: 'background.paper',
borderRadius: 4,
boxShadow: 24,
p: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}
>
<Typography
variant="body1"
gutterBottom
sx={{ fontWeight: 'bold' }}
>
The firmware is getting updated. Please wait...
</Typography>
<div
style={{
display: 'flex',
alignItems: 'center',
marginTop: 10
}}
>
<Button
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={firmwareModalResultHandleOk}
>
Ok
</Button>
</div>
</Box>
</Modal>
)}
{openModalFirmwareUpdate && (
<Modal
open={openModalFirmwareUpdate}
onClose={FirmwareModalHandleCancel}
aria-labelledby="error-modal"
aria-describedby="error-modal-description"
>
<Box
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 420,
bgcolor: 'background.paper',
borderRadius: 4,
boxShadow: 24,
p: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}
>
<Typography
variant="body1"
gutterBottom
sx={{ fontWeight: 'bold' }}
>
Do you really want to update the firmware?
</Typography>
<Typography variant="body1" gutterBottom>
This action requires the battery service to be stopped.
</Typography>
<div
style={{
display: 'flex',
alignItems: 'center',
marginTop: 10
}}
>
<Button
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={FirmwareModalHandleProceed}
>
Proceed
</Button>
<Button
sx={{
marginTop: 2,
marginLeft: 2,
textTransform: 'none',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={FirmwareModalHandleCancel}
>
Cancel
</Button>
</div>
</Box>
</Modal>
)}
<Grid container> <Grid container>
<Grid item xs={6} md={6}> <Grid item xs={6} md={6}>
<Button <IconButton
variant="contained" aria-label="go back"
onClick={handleBatteryViewButton}
sx={{ sx={{
marginTop: '20px', marginTop: '20px',
backgroundColor: '#ffc04d', backgroundColor: 'grey',
color: '#000000', color: '#000000',
'&:hover': { bgcolor: '#f7b34d' } '&:hover': { bgcolor: '#f7b34d' }
}} }}
onClick={handleBatteryViewButton}
> >
<FormattedMessage id="main_stats" defaultMessage="Battery View" /> <ArrowBackIcon />
</Button> </IconButton>
<Button <Button
variant="contained" variant="contained"
onClick={handleMainStatsButton} onClick={handleUpdateFirmware}
sx={{ sx={{
marginTop: '20px', marginTop: '20px',
marginLeft: '20px', marginLeft: '20px',
@ -124,7 +284,10 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
'&:hover': { bgcolor: '#f7b34d' } '&:hover': { bgcolor: '#f7b34d' }
}} }}
> >
<FormattedMessage id="main_stats" defaultMessage="Main Stats" /> <FormattedMessage
id="update_firmware"
defaultMessage="Update Firmware"
/>
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>

View File

@ -1,4 +1,12 @@
import { Box, Card, Container, Grid, Modal, Typography } from '@mui/material'; import {
Box,
Card,
Container,
Grid,
IconButton,
Modal,
Typography
} from '@mui/material';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { I_S3Credentials } from '../../../interfaces/S3Types'; import { I_S3Credentials } from '../../../interfaces/S3Types';
@ -16,6 +24,7 @@ import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import { useLocation, useNavigate } from 'react-router-dom'; import { useLocation, useNavigate } from 'react-router-dom';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
interface MainStatsProps { interface MainStatsProps {
s3Credentials: I_S3Credentials; s3Credentials: I_S3Credentials;
@ -384,31 +393,18 @@ function MainStats(props: MainStatsProps) {
{!loading && ( {!loading && (
<> <>
<Grid item xs={6} md={6}> <Grid item xs={6} md={6}>
<Button <IconButton
variant="contained" aria-label="go back"
sx={{
marginTop: '20px',
backgroundColor: 'grey',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={handleBatteryViewButton} onClick={handleBatteryViewButton}
sx={{
marginTop: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
> >
<FormattedMessage id="main_stats" defaultMessage="Battery View" /> <ArrowBackIcon />
</Button> </IconButton>
<Button
variant="contained"
sx={{
marginTop: '20px',
marginLeft: '20px',
backgroundColor: '#808080',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage id="main_stats" defaultMessage="Main Stats" />
</Button>
<Button <Button
variant="contained" variant="contained"

View File

@ -35,7 +35,13 @@ function Configuration(props: ConfigurationProps) {
return null; return null;
} }
const forcedCalibrationChargeOptions = [ const CalibrationChargeOptions = [
'Repetitive Calibration',
'Additional Calibration',
'Force Calibration Now'
];
const CalibrationChargeOptionsController = [
'RepetitivelyEvery', 'RepetitivelyEvery',
'AdditionallyOnce', 'AdditionallyOnce',
'ChargePermanently' 'ChargePermanently'
@ -64,31 +70,55 @@ function Configuration(props: ConfigurationProps) {
selectedForcedCalibrationChargeOption, selectedForcedCalibrationChargeOption,
setSelectedForcedCalibrationChargeOption setSelectedForcedCalibrationChargeOption
] = useState<string>( ] = useState<string>(
props.values.calibrationChargeForced[0].value.toString() CalibrationChargeOptions[
CalibrationChargeOptionsController.indexOf(
props.values.calibrationChargeState[0].value.toString()
)
]
); );
const [formValues, setFormValues] = useState<ConfigurationValues>({ const [formValues, setFormValues] = useState<ConfigurationValues>({
minimumSoC: props.values.minimumSoC[0].value, minimumSoC: props.values.minimumSoC[0].value,
gridSetPoint: (props.values.gridSetPoint[0].value as number) / 1000, gridSetPoint: (props.values.gridSetPoint[0].value as number) / 1000,
forceCalibrationCharge: forcedCalibrationChargeOptions.indexOf( CalibrationChargeState: CalibrationChargeOptionsController.indexOf(
props.values.calibrationChargeForced[0].value.toString() props.values.calibrationChargeState[0].value.toString()
), ),
calibrationChargeDate: null calibrationChargeDate:
CalibrationChargeOptionsController.indexOf(
props.values.calibrationChargeState[0].value.toString()
) == 0
? dayjs(props.values.repetitiveCalibrationChargeDate[0].value).toDate()
: dayjs(props.values.additionalCalibrationChargeDate[0].value).toDate()
}); });
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
setLoading(true); if (
const res = await axiosConfig props.values.mode[0].value === 'CalibrationCharge' &&
.post(`/EditInstallationConfig?installationId=${props.id}`, formValues) formValues.CalibrationChargeState == 0
.catch((err) => { ) {
if (err.response) { setDateSelectionError(
setError(true); 'You cannot change the date while the installation is in Calibration Charge Mode'
setLoading(false); );
} setErrorDateModalOpen(true);
}); return;
} else if (dayjs(formValues.calibrationChargeDate).isBefore(dayjs())) {
setDateSelectionError('You must specify a future date');
setErrorDateModalOpen(true);
return;
} else {
setLoading(true);
const res = await axiosConfig
.post(`/EditInstallationConfig?installationId=${props.id}`, formValues)
.catch((err) => {
if (err.response) {
setError(true);
setLoading(false);
}
});
if (res) { if (res) {
setUpdated(true); setUpdated(true);
setLoading(false); setLoading(false);
}
} }
}; };
@ -97,12 +127,6 @@ function Configuration(props: ConfigurationProps) {
}; };
const handleConfirm = (newDate) => { const handleConfirm = (newDate) => {
if (newDate.isBefore(dayjs())) {
setDateSelectionError('You must specify a future date');
setErrorDateModalOpen(true);
return;
}
setFormValues({ setFormValues({
...formValues, ...formValues,
['calibrationChargeDate']: newDate.toDate() ['calibrationChargeDate']: newDate.toDate()
@ -114,9 +138,17 @@ function Configuration(props: ConfigurationProps) {
setFormValues({ setFormValues({
...formValues, ...formValues,
['forceCalibrationCharge']: forcedCalibrationChargeOptions.indexOf( ['CalibrationChargeState']: CalibrationChargeOptions.indexOf(
event.target.value event.target.value
) ),
['calibrationChargeDate']:
CalibrationChargeOptions.indexOf(event.target.value) == 0
? dayjs(
props.values.repetitiveCalibrationChargeDate[0].value
).toDate()
: dayjs(
props.values.additionalCalibrationChargeDate[0].value
).toDate()
}); });
}; };
@ -258,7 +290,7 @@ function Configuration(props: ConfigurationProps) {
> >
<FormattedMessage <FormattedMessage
id="forced_calibration_charge" id="forced_calibration_charge"
defaultMessage="Forced Calibration Charge" defaultMessage="Calibration Charge State"
/> />
</InputLabel> </InputLabel>
<Select <Select
@ -268,7 +300,7 @@ function Configuration(props: ConfigurationProps) {
onClose={handleCloseForcedCalibrationCharge} onClose={handleCloseForcedCalibrationCharge}
onOpen={handleOpenForcedCalibrationCharge} onOpen={handleOpenForcedCalibrationCharge}
> >
{forcedCalibrationChargeOptions.map((option) => ( {CalibrationChargeOptions.map((option) => (
<MenuItem key={option} value={option}> <MenuItem key={option} value={option}>
{option} {option}
</MenuItem> </MenuItem>
@ -276,22 +308,12 @@ function Configuration(props: ConfigurationProps) {
</Select> </Select>
</FormControl> </FormControl>
</div> </div>
{formValues.forceCalibrationCharge != 2 && ( {formValues.CalibrationChargeState != 2 && (
<div> <div>
<LocalizationProvider dateAdapter={AdapterDayjs}> <LocalizationProvider dateAdapter={AdapterDayjs}>
<DateTimePicker <DateTimePicker
label="Select Next Calibration Charge Date" label="Select Next Calibration Charge Date"
value={ value={dayjs(formValues.calibrationChargeDate)}
formValues.forceCalibrationCharge == 0
? dayjs(
props.values.repetitiveCalibrationChargeDate[0]
.value
)
: dayjs(
props.values.additionalCalibrationChargeDate[0]
.value
)
}
onChange={handleConfirm} onChange={handleConfirm}
sx={{ sx={{
marginTop: 2 marginTop: 2

View File

@ -278,6 +278,7 @@ function Installation(props: singleInstallationProps) {
<BatteryView <BatteryView
values={values} values={values}
s3Credentials={s3Credentials} s3Credentials={s3Credentials}
installationId={props.current_installation.id}
></BatteryView> ></BatteryView>
} }
></Route> ></Route>

View File

@ -31,7 +31,7 @@ export interface I_BoxDataValue {
export type ConfigurationValues = { export type ConfigurationValues = {
minimumSoC: string | number; minimumSoC: string | number;
gridSetPoint: number; gridSetPoint: number;
forceCalibrationCharge: number; CalibrationChargeState: number;
calibrationChargeDate: Date | null; calibrationChargeDate: Date | null;
}; };
@ -157,7 +157,7 @@ export type TopologyValues = {
gridSetPoint: I_BoxDataValue[]; gridSetPoint: I_BoxDataValue[];
maximumDischargePower: I_BoxDataValue[]; maximumDischargePower: I_BoxDataValue[];
DcDcNum: I_BoxDataValue[]; DcDcNum: I_BoxDataValue[];
calibrationChargeForced: I_BoxDataValue[]; calibrationChargeState: I_BoxDataValue[];
mode: I_BoxDataValue[]; mode: I_BoxDataValue[];
repetitiveCalibrationChargeDate: I_BoxDataValue[]; repetitiveCalibrationChargeDate: I_BoxDataValue[];
additionalCalibrationChargeDate: I_BoxDataValue[]; additionalCalibrationChargeDate: I_BoxDataValue[];
@ -296,7 +296,7 @@ export const topologyPaths: TopologyPaths = {
gridSetPoint: ['/Config/GridSetPoint'], gridSetPoint: ['/Config/GridSetPoint'],
maximumDischargePower: ['/Config/MaxBatteryDischargingCurrent'], maximumDischargePower: ['/Config/MaxBatteryDischargingCurrent'],
DcDcNum: ['/DcDc/SystemControl/NumberOfConnectedSlaves'], DcDcNum: ['/DcDc/SystemControl/NumberOfConnectedSlaves'],
calibrationChargeForced: ['/Config/ForceCalibrationChargeState'], calibrationChargeState: ['/Config/ForceCalibrationChargeState'],
mode: ['/EssControl/Mode'], mode: ['/EssControl/Mode'],
repetitiveCalibrationChargeDate: [ repetitiveCalibrationChargeDate: [
'/Config/DayAndTimeForRepetitiveCalibration' '/Config/DayAndTimeForRepetitiveCalibration'