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))]
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 installationToUpdate = Db.GetInstallationById(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)
{
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();
}

View File

@ -2,6 +2,7 @@ using System.Diagnostics;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.Relations;
using InnovEnergy.Lib.Utils;
using Org.BouncyCastle.Asn1.X509;
namespace InnovEnergy.App.Backend.DataTypes.Methods;
@ -67,8 +68,9 @@ public static class SessionMethods
.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";
await Task.Run(() =>
@ -80,11 +82,11 @@ public static class SessionMethods
process.StartInfo.RedirectStandardOutput = true;
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
Console.WriteLine(output);
});
return true;
Console.WriteLine("222222222222222222222222222222222222222222222222222222222222222");
}

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import {
Container,
Grid,
@ -8,7 +8,8 @@ import {
TableCell,
TableContainer,
TableHead,
TableRow
TableRow,
Typography
} from '@mui/material';
import { TopologyValues } from '../Log/graph.util';
import {
@ -24,10 +25,12 @@ import { I_S3Credentials } from '../../../interfaces/S3Types';
import routes from '../../../Resources/routes.json';
import MainStats from './MainStats';
import DetailedBatteryView from './DetailedBatteryView';
import CircularProgress from '@mui/material/CircularProgress';
interface BatteryViewProps {
values: TopologyValues;
s3Credentials: I_S3Credentials;
installationId: number;
}
function BatteryView(props: BatteryViewProps) {
@ -40,6 +43,10 @@ function BatteryView(props: BatteryViewProps) {
(a, b) => b.BatteryId - a.BatteryId
);
const [loading, setLoading] = useState(
sortedBatteryView.length == 0 ? true : false
);
const handleMainStatsButton = () => {
navigate(routes.mainstats);
};
@ -52,8 +59,42 @@ function BatteryView(props: BatteryViewProps) {
}
};
useEffect(() => {
if (sortedBatteryView.length == 0) {
setLoading(true);
} else {
setLoading(false);
}
}, [sortedBatteryView]);
return (
<>
{loading && (
<Container
maxWidth="xl"
sx={{
display: 'flex',
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
@ -115,6 +156,7 @@ function BatteryView(props: BatteryViewProps) {
<DetailedBatteryView
s3Credentials={props.s3Credentials}
batteryData={findBatteryData(battery.BatteryId)}
installationId={props.installationId}
></DetailedBatteryView>
}
/>
@ -190,10 +232,12 @@ function BatteryView(props: BatteryViewProps) {
textAlign: 'center',
backgroundColor:
battery.Voltage.value < 44 || battery.Voltage.value > 57
battery.Voltage.value < 44 ||
battery.Voltage.value > 57
? '#FF033E'
: '#32CD32',
color: battery.Voltage.value === '' ? 'white' : 'inherit'
color:
battery.Voltage.value === '' ? 'white' : 'inherit'
}}
>
{battery.Voltage.value + ' ' + battery.Voltage.unit}
@ -237,7 +281,8 @@ function BatteryView(props: BatteryViewProps) {
battery.Warnings.value !== '' ? 'bold' : 'inherit',
backgroundColor:
battery.Warnings.value === '' ? 'inherit' : '#ff9900',
color: battery.Warnings.value != '' ? 'black' : 'inherit'
color:
battery.Warnings.value != '' ? 'black' : 'inherit'
}}
>
{battery.Warnings.value === '' ? (
@ -299,6 +344,7 @@ function BatteryView(props: BatteryViewProps) {
</Table>
</TableContainer>
</Container>
)}
</>
);
}

View File

@ -1,8 +1,11 @@
import React, { useEffect, useState } from 'react';
import { I_S3Credentials } from '../../../interfaces/S3Types';
import {
Box,
Card,
Grid,
IconButton,
Modal,
Paper,
Table,
TableBody,
@ -12,14 +15,16 @@ import {
Typography
} from '@mui/material';
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 { FormattedMessage } from 'react-intl';
import { useNavigate } from 'react-router-dom';
import routes from '../../../Resources/routes.json';
import axiosConfig from '../../../Resources/axiosConfig';
interface DetailedBatteryViewProps {
s3Credentials: I_S3Credentials;
batteryData: Battery;
installationId: number;
}
function DetailedBatteryView(props: DetailedBatteryViewProps) {
@ -27,16 +32,20 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
return null;
}
const navigate = useNavigate();
const [openModalFirmwareUpdate, setOpenModalFirmwareUpdate] = useState(false);
const [openModalResultFirmwareUpdate, setOpenModalResultFirmwareUpdate] =
useState(false);
const handleBatteryViewButton = () => {
navigate(location.pathname.split('/').slice(0, -2).join('/'));
};
const handleMainStatsButton = () => {
navigate(
location.pathname.split('/').slice(0, -2).join('/') +
'/' +
routes.mainstats
);
const handleUpdateFirmware = () => {
setOpenModalFirmwareUpdate(true);
};
const firmwareModalResultHandleOk = () => {
navigate(location.pathname.split('/').slice(0, -2).join('/'));
};
const [GreenisBlinking, setGreenisBlinking] = useState(
@ -96,26 +105,177 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
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 (
<>
{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 item xs={6} md={6}>
<Button
variant="contained"
onClick={handleBatteryViewButton}
<IconButton
aria-label="go back"
sx={{
marginTop: '20px',
backgroundColor: '#ffc04d',
backgroundColor: 'grey',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={handleBatteryViewButton}
>
<FormattedMessage id="main_stats" defaultMessage="Battery View" />
</Button>
<ArrowBackIcon />
</IconButton>
<Button
variant="contained"
onClick={handleMainStatsButton}
onClick={handleUpdateFirmware}
sx={{
marginTop: '20px',
marginLeft: '20px',
@ -124,7 +284,10 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage id="main_stats" defaultMessage="Main Stats" />
<FormattedMessage
id="update_firmware"
defaultMessage="Update Firmware"
/>
</Button>
</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 React, { useEffect, useState } from 'react';
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 CircularProgress from '@mui/material/CircularProgress';
import { useLocation, useNavigate } from 'react-router-dom';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
interface MainStatsProps {
s3Credentials: I_S3Credentials;
@ -384,31 +393,18 @@ function MainStats(props: MainStatsProps) {
{!loading && (
<>
<Grid item xs={6} md={6}>
<Button
variant="contained"
<IconButton
aria-label="go back"
sx={{
marginTop: '20px',
backgroundColor: 'grey',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={handleBatteryViewButton}
sx={{
marginTop: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage id="main_stats" defaultMessage="Battery View" />
</Button>
<Button
variant="contained"
sx={{
marginTop: '20px',
marginLeft: '20px',
backgroundColor: '#808080',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage id="main_stats" defaultMessage="Main Stats" />
</Button>
<ArrowBackIcon />
</IconButton>
<Button
variant="contained"

View File

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

View File

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

View File

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