Compare commits

...

9 Commits

Author SHA1 Message Date
Noe 4c7bc55526 update print in salimax controller for charge date 2024-03-11 16:12:13 +01:00
Noe 490d721795 update struct in battery view front end 2024-03-11 16:11:18 +01:00
Noe d88a18c982 update titles in battery view 2024-03-11 16:11:00 +01:00
Noe ca3c9d2903 update config types in controller 2024-03-11 16:10:47 +01:00
Noe 0428770b48 Update config in front end 2024-03-11 16:10:21 +01:00
Noe c0bc1d601a update config struct 2024-03-11 16:10:12 +01:00
Noe 950308e4cd update config file 2024-03-11 16:09:38 +01:00
Noe 55fc1708da update calibration charge type 2024-03-11 16:09:06 +01:00
Noe d9ae1d7b94 update battery view 2024-03-11 16:08:20 +01:00
10 changed files with 587 additions and 269 deletions

View File

@ -5,11 +5,12 @@ public class Configuration
public Double MinimumSoC { get; set; } public Double MinimumSoC { get; set; }
public Double GridSetPoint { get; set; } public Double GridSetPoint { get; set; }
public CalibrationChargeType ForceCalibrationCharge { get; set; } public CalibrationChargeType ForceCalibrationCharge { get; set; }
public DateTime CalibrationChargeDate { get; set; }
} }
public enum CalibrationChargeType public enum CalibrationChargeType
{ {
No, RepetitivelyEvery,
UntilEoc, AdditionallyOnce,
Yes ChargePermanently
} }

View File

@ -7,5 +7,6 @@ public class Configuration
public Double MinimumSoC { get; set; } public Double MinimumSoC { get; set; }
public Double GridSetPoint { get; set; } public Double GridSetPoint { get; set; }
public CalibrationChargeType ForceCalibrationCharge { get; set; } public CalibrationChargeType ForceCalibrationCharge { get; set; }
public DateTime CalibrationChargeDate { get; set; }
} }

View File

@ -190,13 +190,13 @@ public static class Controller
var calibrationChargeForced = statusRecord.Config.ForceCalibrationCharge; var calibrationChargeForced = statusRecord.Config.ForceCalibrationCharge;
var batteryCalibrationChargeRequested = statusRecord.Battery is { CalibrationChargeRequested: true };//BatteryCalibrationChargeRequested(statusRecord.Battery?.CalibrationChargeRequested?? false) ; var batteryCalibrationChargeRequested = statusRecord.Battery is { CalibrationChargeRequested: true };//BatteryCalibrationChargeRequested(statusRecord.Battery?.CalibrationChargeRequested?? false) ;
var mustDoCalibrationCharge = batteryCalibrationChargeRequested || calibrationChargeForced == CalibrationChargeType.Yes || calibrationChargeForced == CalibrationChargeType.UntilEoc ; var mustDoCalibrationCharge = batteryCalibrationChargeRequested || calibrationChargeForced == CalibrationChargeType.ChargePermanently || calibrationChargeForced == CalibrationChargeType.AdditionallyOnce ;
if (statusRecord.Battery is not null) if (statusRecord.Battery is not null)
{ {
if (calibrationChargeForced == CalibrationChargeType.UntilEoc && statusRecord.Battery.Eoc ) if (calibrationChargeForced == CalibrationChargeType.AdditionallyOnce && statusRecord.Battery.Eoc )
{ {
statusRecord.Config.ForceCalibrationCharge = CalibrationChargeType.No; statusRecord.Config.ForceCalibrationCharge = CalibrationChargeType.RepetitivelyEvery;
} }
} }
return mustDoCalibrationCharge; return mustDoCalibrationCharge;

View File

@ -64,7 +64,7 @@ public static class MiddlewareAgent
Configuration? config = JsonSerializer.Deserialize<Configuration>(message); Configuration? config = JsonSerializer.Deserialize<Configuration>(message);
Console.WriteLine($"Received a configuration message: GridSetPoint is " + config.GridSetPoint + ", MinimumSoC is " + config.MinimumSoC + " and ForceCalibrationCharge is " + config.ForceCalibrationCharge); Console.WriteLine($"Received a configuration message: GridSetPoint is " + config.GridSetPoint + ", MinimumSoC is " + config.MinimumSoC + " and ForceCalibrationCharge is " + config.ForceCalibrationCharge+ " and CalibrationChargeDate is " + config.CalibrationChargeDate);
// Send the reply to the sender's endpoint // Send the reply to the sender's endpoint
UdpListener.Send(replyData, replyData.Length, serverEndpoint); UdpListener.Send(replyData, replyData.Length, serverEndpoint);

View File

@ -2,7 +2,7 @@ namespace InnovEnergy.App.SaliMax.SystemConfig;
public enum CalibrationChargeType public enum CalibrationChargeType
{ {
No, RepetitivelyEvery,
UntilEoc, AdditionallyOnce,
Yes ChargePermanently
} }

View File

@ -40,7 +40,7 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
public static Config Default => new() public static Config Default => new()
{ {
MinSoc = 20, MinSoc = 20,
ForceCalibrationCharge = CalibrationChargeType.No, ForceCalibrationCharge = CalibrationChargeType.RepetitivelyEvery,
DisplayIndividualBatteries = false, DisplayIndividualBatteries = false,
PConstant = .5, PConstant = .5,
GridSetPoint = 0, GridSetPoint = 0,
@ -117,7 +117,7 @@ public class Config //TODO: let IE choose from config files (Json) and connect t
public static Config Default => new() public static Config Default => new()
{ {
MinSoc = 20, MinSoc = 20,
ForceCalibrationCharge = CalibrationChargeType.No, ForceCalibrationCharge = CalibrationChargeType.RepetitivelyEvery,
DisplayIndividualBatteries = false, DisplayIndividualBatteries = false,
PConstant = .5, PConstant = .5,
GridSetPoint = 0, GridSetPoint = 0,

View File

@ -36,12 +36,22 @@ function BatteryView(props: BatteryViewProps) {
} }
const currentLocation = useLocation(); const currentLocation = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const numOfBatteries = props.values.batteryView.length; const sortedBatteryView = [...props.values.batteryView].sort(
(a, b) => b.BatteryId - a.BatteryId
);
const handleMainStatsButton = () => { const handleMainStatsButton = () => {
navigate(routes.mainstats); navigate(routes.mainstats);
}; };
const findBatteryData = (batteryId: number) => {
for (let i = 0; i < props.values.batteryView.length; i++) {
if (props.values.batteryView[i].BatteryId == batteryId) {
return props.values.batteryView[i];
}
}
};
return ( return (
<> <>
<Container maxWidth="xl"> <Container maxWidth="xl">
@ -97,14 +107,14 @@ function BatteryView(props: BatteryViewProps) {
<MainStats s3Credentials={props.s3Credentials}></MainStats> <MainStats s3Credentials={props.s3Credentials}></MainStats>
} }
/> />
{Array.from({ length: numOfBatteries }).map((_, i) => ( {props.values.batteryView.map((battery) => (
<Route <Route
key={routes.detailed_view + i} key={routes.detailed_view + battery.BatteryId}
path={routes.detailed_view + i} path={routes.detailed_view + battery.BatteryId}
element={ element={
<DetailedBatteryView <DetailedBatteryView
s3Credentials={props.s3Credentials} s3Credentials={props.s3Credentials}
batteryData={props.values.batteryView[i]} batteryData={findBatteryData(battery.BatteryId)}
></DetailedBatteryView> ></DetailedBatteryView>
} }
/> />
@ -138,7 +148,7 @@ function BatteryView(props: BatteryViewProps) {
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{props.values.batteryView.map((battery) => ( {sortedBatteryView.map((battery) => (
<TableRow <TableRow
key={battery.BatteryId} key={battery.BatteryId}
style={{ style={{
@ -153,12 +163,9 @@ function BatteryView(props: BatteryViewProps) {
> >
<Link <Link
style={{ color: 'black' }} style={{ color: 'black' }}
to={ to={routes.detailed_view + battery.BatteryId.toString()}
routes.detailed_view +
(battery.BatteryId - 1).toString()
}
> >
{'Battery ' + battery.BatteryId} {'Node ' + battery.BatteryId}
</Link> </Link>
</TableCell> </TableCell>
<TableCell <TableCell

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useState } from 'react';
import { I_S3Credentials } from '../../../interfaces/S3Types'; import { I_S3Credentials } from '../../../interfaces/S3Types';
import { import {
Card, Card,
@ -8,7 +8,8 @@ import {
TableBody, TableBody,
TableCell, TableCell,
TableContainer, TableContainer,
TableRow TableRow,
Typography
} from '@mui/material'; } from '@mui/material';
import { Battery } from '../Log/graph.util'; import { Battery } from '../Log/graph.util';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
@ -38,6 +39,64 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
); );
}; };
const [GreenisBlinking, setGreenisBlinking] = useState(
props.batteryData.GreenLeds.value === 'Blinking'
);
const [AmberisBlinking, setAmberisBlinking] = useState(
props.batteryData.AmberLeds.value === 'Blinking'
);
const [RedisBlinking, setRedisBlinking] = useState(
props.batteryData.RedLeds.value === 'Blinking'
);
const [BlueisBlinking, setBlueisBlinking] = useState(
props.batteryData.BlueLeds.value === 'Blinking'
);
useEffect(() => {
const intervalId = setInterval(() => {
if (props.batteryData.AmberLeds.value === 'Blinking') {
setAmberisBlinking((prevIsBlinking) => !prevIsBlinking);
}
if (props.batteryData.RedLeds.value === 'Blinking') {
setRedisBlinking((prevIsBlinking) => !prevIsBlinking);
}
if (props.batteryData.BlueLeds.value === 'Blinking') {
setBlueisBlinking((prevIsBlinking) => !prevIsBlinking);
}
if (props.batteryData.GreenLeds.value === 'Blinking') {
setGreenisBlinking((prevIsBlinking) => !prevIsBlinking);
}
}, 500); // Blink every 500 milliseconds
// Cleanup the interval on component unmount
return () => clearInterval(intervalId);
}, []);
const batteryStyle = {
borderRadius: '15px',
padding: '10px',
backgroundColor: 'lightgray',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '150px',
marginTop: '30px'
};
const batteryStringStyle = {
flex: 1,
border: '1px solid #000',
height: '97%',
width: '30px',
borderRadius: '7px',
backgroundColor: '#bfbfbf'
};
return ( return (
<> <>
<Grid container> <Grid container>
@ -70,18 +129,149 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
</Button> </Button>
</Grid> </Grid>
</Grid> </Grid>
<Grid item md={2} xs={2}> <Grid container>
<Grid item md={4.9} xs={4.9}></Grid>
<Grid item md={2.2} xs={2.2}>
<div style={batteryStyle}>
<div
style={{
...batteryStringStyle,
backgroundColor:
props.batteryData.String1Active.value == 'True'
? '#32CD32'
: '#FF033E'
}}
></div>
<div
style={{
...batteryStringStyle,
backgroundColor:
props.batteryData.String2Active.value == 'True'
? '#32CD32'
: '#FF033E'
}}
></div>
<div
style={{
...batteryStringStyle,
backgroundColor:
props.batteryData.String3Active.value == 'True'
? '#32CD32'
: '#FF033E'
}}
></div>
<div
style={{
...batteryStringStyle,
backgroundColor:
props.batteryData.String4Active.value == 'True'
? '#32CD32'
: '#FF033E'
}}
></div>
<div
style={{
...batteryStringStyle,
backgroundColor:
props.batteryData.String5Active.value == 'True'
? '#32CD32'
: '#FF033E'
}}
></div>
<div>
<div
style={{
width: '20px',
height: '20px',
marginLeft: '10px',
marginTop: '-10px',
borderRadius: '50%',
backgroundColor:
props.batteryData.GreenLeds.value === 'On' ||
GreenisBlinking
? 'green'
: 'transparent'
}}
/>
<div
style={{
width: '20px',
height: '20px',
marginLeft: '10px',
marginTop: '10px',
borderRadius: '50%',
backgroundColor:
props.batteryData.AmberLeds.value === 'On' ||
AmberisBlinking
? 'orange'
: 'transparent'
}}
/>
<div
style={{
width: '20px',
height: '20px',
marginLeft: '10px',
marginTop: '10px',
borderRadius: '50%',
backgroundColor:
props.batteryData.BlueLeds.value === 'On' || BlueisBlinking
? '#00ccff'
: 'transparent'
}}
/>
<div
style={{
width: '20px',
height: '20px',
marginLeft: '10px',
marginTop: '10px',
borderRadius: '50%',
backgroundColor:
props.batteryData.RedLeds.value === 'On' || RedisBlinking
? 'red'
: 'transparent'
}}
/>
</div>
</div>
</Grid>
<Grid item md={4.9} xs={4.9}></Grid>
</Grid>
<Grid item md={3} xs={3}>
<Card <Card
sx={{ sx={{
overflow: 'visible', overflow: 'visible',
marginTop: '30px', marginTop: '30px',
marginBottom: '30px', marginBottom: '30px',
backgroundColor: 'red' display: 'flex',
flexDirection: 'column',
alignItems: 'center',
border: '2px solid #ccc',
borderRadius: '12px'
}} }}
> >
<Typography
variant="h6"
component="div"
sx={{
marginTop: '10px',
borderBottom: '1px solid #ccc',
fontWeight: 'bold'
}}
>
Battery Information
</Typography>
<TableContainer <TableContainer
component={Paper} component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }} sx={{ marginTop: '20px', width: '100%' }}
> >
<Table size="medium" aria-label="a dense table"> <Table size="medium" aria-label="a dense table">
<TableBody> <TableBody>
@ -217,28 +407,7 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
props.batteryData.HeatingCurrent.unit} props.batteryData.HeatingCurrent.unit}
</TableCell> </TableCell>
</TableRow> </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> <TableRow>
<TableCell <TableCell
component="th" component="th"
@ -261,47 +430,40 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
props.batteryData.Soc.unit} props.batteryData.Soc.unit}
</TableCell> </TableCell>
</TableRow> </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> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
</Card> </Card>
</Grid> </Grid>
{/*----------------------------------------------------------------------------------------------------------------------------------*/} {/*----------------------------------------------------------------------------------------------------------------------------------*/}
<Grid item md={2.9} xs={2.9}> <Grid item md={3} xs={3}>
<Card <Card
sx={{ sx={{
overflow: 'visible', overflow: 'visible',
marginTop: '30px', marginTop: '30px',
marginLeft: '20px', marginLeft: '20px',
marginBottom: '30px', display: 'flex',
backgroundColor: 'red' flexDirection: 'column',
alignItems: 'center',
border: '2px solid #ccc',
borderRadius: '12px'
}} }}
> >
<Typography
variant="h6"
component="div"
sx={{
marginTop: '10px',
borderBottom: '1px solid #ccc',
fontWeight: 'bold'
}}
>
Temperature
</Typography>
<TableContainer <TableContainer
component={Paper} component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }} sx={{ marginTop: '20px', width: '100%' }}
> >
<Table size="medium" aria-label="a dense table"> <Table size="medium" aria-label="a dense table">
<TableBody> <TableBody>
@ -466,19 +628,33 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
</Grid> </Grid>
{/*----------------------------------------------------------------------------------------------------------------------------------*/} {/*----------------------------------------------------------------------------------------------------------------------------------*/}
<Grid item md={2.7} xs={2.7}> <Grid item md={3} xs={3}>
<Card <Card
sx={{ sx={{
overflow: 'visible', overflow: 'visible',
marginTop: '30px', marginTop: '30px',
marginLeft: '20px', marginLeft: '20px',
marginBottom: '30px', display: 'flex',
backgroundColor: 'red' flexDirection: 'column',
alignItems: 'center',
border: '2px solid #ccc',
borderRadius: '12px'
}} }}
> >
<Typography
variant="h6"
component="div"
sx={{
marginTop: '10px',
borderBottom: '1px solid #ccc',
fontWeight: 'bold'
}}
>
Io Status
</Typography>
<TableContainer <TableContainer
component={Paper} component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }} sx={{ marginTop: '20px', width: '100%' }}
> >
<Table size="medium" aria-label="a dense table"> <Table size="medium" aria-label="a dense table">
<TableBody> <TableBody>
@ -643,19 +819,33 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
</Grid> </Grid>
{/*----------------------------------------------------------------------------------------------------------------------------------*/} {/*----------------------------------------------------------------------------------------------------------------------------------*/}
<Grid item md={2.9} xs={2.9}> <Grid item md={3} xs={3}>
<Card <Card
sx={{ sx={{
overflow: 'visible', overflow: 'visible',
marginTop: '30px', marginTop: '30px',
marginLeft: '20px', marginLeft: '20px',
marginBottom: '30px', display: 'flex',
backgroundColor: 'red' flexDirection: 'column',
alignItems: 'center',
border: '2px solid #ccc',
borderRadius: '12px'
}} }}
> >
<Typography
variant="h6"
component="div"
sx={{
marginTop: '10px',
borderBottom: '1px solid #ccc',
fontWeight: 'bold'
}}
>
Specification
</Typography>
<TableContainer <TableContainer
component={Paper} component={Paper}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }} sx={{ marginTop: '20px', width: '100%' }}
> >
<Table size="medium" aria-label="a dense table"> <Table size="medium" aria-label="a dense table">
<TableBody> <TableBody>
@ -819,151 +1009,109 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
</Card> </Card>
</Grid> </Grid>
{/*----------------------------------------------------------------------------------------------------------------------------------*/} {/*----------------------------------------------------------------------------------------------------------------------------------*/}
<Grid item md={1.5} xs={1.5}>
<Card {/*<Grid item md={1.5} xs={1.5}>*/}
sx={{ {/* <Card*/}
overflow: 'visible', {/* sx={{*/}
marginTop: '30px', {/* overflow: 'visible',*/}
marginLeft: '20px', {/* marginTop: '30px',*/}
marginBottom: '30px', {/* marginLeft: '20px',*/}
backgroundColor: 'red' {/* marginBottom: '30px',*/}
}} {/* backgroundColor: 'red'*/}
> {/* }}*/}
<TableContainer {/* >*/}
component={Paper} {/* <TableContainer*/}
sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }} {/* component={Paper}*/}
> {/* sx={{ marginTop: '20px', marginBottom: '20px', width: '100%' }}*/}
<Table size="medium" aria-label="a dense table"> {/* >*/}
<TableBody> {/* <Table size="medium" aria-label="a dense table">*/}
<TableRow> {/* <TableBody>*/}
<TableCell {/* <TableRow>*/}
component="th" {/* <TableCell*/}
scope="row" {/* component="th"*/}
align="left" {/* scope="row"*/}
sx={{ fontWeight: 'bold' }} {/* align="left"*/}
> {/* sx={{ fontWeight: 'bold' }}*/}
Blue Led {/* >*/}
</TableCell> {/* Green Led*/}
<TableCell {/* </TableCell>*/}
align="right" {/* <TableCell*/}
sx={{ {/* align="right"*/}
width: '6ch', {/* sx={{*/}
whiteSpace: 'nowrap', {/* width: '6ch',*/}
paddingRight: '12px' {/* whiteSpace: 'nowrap',*/}
}} {/* paddingRight: '12px'*/}
> {/* }}*/}
<div {/* >*/}
style={{ {/* {props.batteryData.GreenLeds.value}*/}
width: '20px', {/* </TableCell>*/}
height: '20px', {/* </TableRow>*/}
marginLeft: '2px', {/* <TableRow>*/}
borderRadius: '50%', {/* <TableCell*/}
backgroundColor: {/* component="th"*/}
props.batteryData.BlueLeds.value === 'On' {/* scope="row"*/}
? '#00ccff' {/* align="left"*/}
: 'transparent' {/* sx={{ fontWeight: 'bold' }}*/}
}} {/* >*/}
/> {/* Amber Led*/}
</TableCell> {/* </TableCell>*/}
</TableRow> {/* <TableCell*/}
<TableRow> {/* align="right"*/}
<TableCell {/* sx={{*/}
component="th" {/* width: '6ch',*/}
scope="row" {/* whiteSpace: 'nowrap',*/}
align="left" {/* paddingRight: '12px'*/}
sx={{ fontWeight: 'bold' }} {/* }}*/}
> {/* >*/}
Green Led {/* {props.batteryData.AmberLeds.value}*/}
</TableCell> {/* </TableCell>*/}
<TableCell {/* </TableRow>*/}
align="right" {/* <TableRow>*/}
sx={{ {/* <TableCell*/}
width: '6ch', {/* component="th"*/}
whiteSpace: 'nowrap', {/* scope="row"*/}
paddingRight: '12px' {/* align="left"*/}
}} {/* sx={{ fontWeight: 'bold' }}*/}
> {/* >*/}
<div {/* Blue Led*/}
style={{ {/* </TableCell>*/}
width: '20px', {/* <TableCell*/}
height: '20px', {/* align="right"*/}
marginLeft: '2px', {/* sx={{*/}
borderRadius: '50%', {/* width: '6ch',*/}
backgroundColor: {/* whiteSpace: 'nowrap',*/}
props.batteryData.GreenLeds.value === 'On' {/* paddingRight: '12px'*/}
? 'green' {/* }}*/}
: 'transparent' {/* >*/}
}} {/* {props.batteryData.BlueLeds.value}*/}
/> {/* </TableCell>*/}
</TableCell> {/* </TableRow>*/}
</TableRow>
<TableRow> {/* <TableRow>*/}
<TableCell {/* <TableCell*/}
component="th" {/* component="th"*/}
scope="row" {/* scope="row"*/}
align="left" {/* align="left"*/}
sx={{ fontWeight: 'bold' }} {/* sx={{ fontWeight: 'bold' }}*/}
> {/* >*/}
Red Led {/* Red Led*/}
</TableCell> {/* </TableCell>*/}
<TableCell {/* <TableCell*/}
align="right" {/* align="right"*/}
sx={{ {/* sx={{*/}
width: '6ch', {/* width: '6ch',*/}
whiteSpace: 'nowrap', {/* whiteSpace: 'nowrap',*/}
paddingRight: '12px' {/* paddingRight: '12px'*/}
}} {/* }}*/}
> {/* >*/}
<div {/* {props.batteryData.RedLeds.value}*/}
style={{ {/* </TableCell>*/}
width: '20px', {/* </TableRow>*/}
height: '20px', {/* </TableBody>*/}
marginLeft: '2px', {/* </Table>*/}
borderRadius: '50%', {/* </TableContainer>*/}
backgroundColor: {/* </Card>*/}
props.batteryData.RedLeds.value === 'On' {/*</Grid>*/}
? '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>
</> </>
); );
} }

View File

@ -9,16 +9,22 @@ import {
Grid, Grid,
IconButton, IconButton,
InputLabel, InputLabel,
Modal,
Select, Select,
TextField, TextField,
Typography,
useTheme useTheme
} from '@mui/material'; } from '@mui/material';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import { DateField } from '@mui/x-date-pickers/DateField';
import axiosConfig from '../../../Resources/axiosConfig'; import axiosConfig from '../../../Resources/axiosConfig';
import { Close as CloseIcon } from '@mui/icons-material'; import { Close as CloseIcon } from '@mui/icons-material';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@mui/material/MenuItem';
import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import dayjs from 'dayjs';
interface ConfigurationProps { interface ConfigurationProps {
values: TopologyValues; values: TopologyValues;
@ -30,32 +36,11 @@ function Configuration(props: ConfigurationProps) {
return null; return null;
} }
const forcedCalibrationChargeOptions = ['No', 'UntilEoc', 'Yes']; const forcedCalibrationChargeOptions = [
'RepetitivelyEvery',
const [formValues, setFormValues] = useState<ConfigurationValues>({ 'AdditionallyOnce',
minimumSoC: props.values.minimumSoC[0].value, 'ChargePermanently'
gridSetPoint: (props.values.gridSetPoint[0].value as number) / 1000, ];
forceCalibrationCharge: forcedCalibrationChargeOptions.indexOf(
props.values.calibrationChargeForced[0].value.toString()
)
});
const handleSubmit = async (e) => {
setLoading(true);
const res = await axiosConfig
.post(`/EditInstallationConfig?installationId=${props.id}`, formValues)
.catch((err) => {
if (err.response) {
setError(true);
setLoading(false);
}
});
if (res) {
setUpdated(true);
setLoading(false);
}
};
const [errors, setErrors] = useState({ const [errors, setErrors] = useState({
minimumSoC: false, minimumSoC: false,
@ -80,9 +65,62 @@ function Configuration(props: ConfigurationProps) {
] = useState<string>( ] = useState<string>(
props.values.calibrationChargeForced[0].value.toString() props.values.calibrationChargeForced[0].value.toString()
); );
//const forcedCalibrationChargeOptions = ['No', 'UntilEoc', 'Yes']; const [isDateModalOpen, setIsDateModalOpen] = useState(false);
const [calibrationChargeDate, setCalibrationChargeDate] = useState();
const [isErrorDateModalOpen, setErrorDateModalOpen] = useState(false);
const [dateSelectionError, setDateSelectionError] = useState('');
const [dateFieldOpen, setDateFieldOpen] = useState(false);
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()
),
calibrationChargeDate: Date(props.values.calibrationChargeDate[0].value)
});
const handleSubmit = async (e) => {
setLoading(true);
const res = await axiosConfig
.post(`/EditInstallationConfig?installationId=${props.id}`, formValues)
.catch((err) => {
if (err.response) {
setError(true);
setLoading(false);
}
});
if (res) {
setUpdated(true);
setLoading(false);
}
};
const handleCancel = () => {
setIsDateModalOpen(false);
};
const handleOkOnErrorDateModal = () => {
setErrorDateModalOpen(false);
setIsDateModalOpen(true);
};
const handleConfirm = () => {
setIsDateModalOpen(false);
if (calibrationChargeDate.isBefore(dayjs())) {
setDateSelectionError('You must use a future date');
setErrorDateModalOpen(true);
return;
}
setDateFieldOpen(true);
};
const handleSelectedCalibrationChargeChange = (event) => { const handleSelectedCalibrationChargeChange = (event) => {
if (event.target.value != 'ChargePermanently') {
setIsDateModalOpen(true);
}
setSelectedForcedCalibrationChargeOption(event.target.value); setSelectedForcedCalibrationChargeOption(event.target.value);
setFormValues({ setFormValues({
@ -199,7 +237,6 @@ function Configuration(props: ConfigurationProps) {
open={openForcedCalibrationCharge} open={openForcedCalibrationCharge}
onClose={handleCloseForcedCalibrationCharge} onClose={handleCloseForcedCalibrationCharge}
onOpen={handleOpenForcedCalibrationCharge} onOpen={handleOpenForcedCalibrationCharge}
//renderValue={selectedForcedCalibrationChargeOption}
> >
{forcedCalibrationChargeOptions.map((option) => ( {forcedCalibrationChargeOptions.map((option) => (
<MenuItem key={option} value={option}> <MenuItem key={option} value={option}>
@ -209,6 +246,127 @@ function Configuration(props: ConfigurationProps) {
</Select> </Select>
</FormControl> </FormControl>
</div> </div>
{(dateFieldOpen || formValues.forceCalibrationCharge != 2) && (
<div>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<DateField
label="Calibration Charge Date"
value={calibrationChargeDate}
/>
</LocalizationProvider>
</div>
)}
{isErrorDateModalOpen && (
<Modal open={isErrorDateModalOpen} onClose={() => {}}>
<Box
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 450,
bgcolor: 'background.paper',
borderRadius: 4,
boxShadow: 24,
p: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}
>
<Typography
variant="body1"
gutterBottom
sx={{ fontWeight: 'bold' }}
>
{dateSelectionError}
</Typography>
<Button
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={handleOkOnErrorDateModal}
>
Ok
</Button>
</Box>
</Modal>
)}
{isDateModalOpen && (
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Modal open={isDateModalOpen} onClose={() => {}}>
<Box
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 450,
bgcolor: 'background.paper',
borderRadius: 4,
boxShadow: 24,
p: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}
>
<DateTimePicker
label="Select Calibration Charge Date"
value={calibrationChargeDate}
onChange={(newDate) =>
setCalibrationChargeDate(newDate)
}
sx={{
marginTop: 2
}}
/>
<div
style={{
display: 'flex',
alignItems: 'center',
marginTop: 10
}}
>
<Button
sx={{
marginTop: 2,
textTransform: 'none',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={handleConfirm}
>
Confirm
</Button>
<Button
sx={{
marginTop: 2,
marginLeft: 2,
textTransform: 'none',
bgcolor: '#ffc04d',
color: '#111111',
'&:hover': { bgcolor: '#f7b34d' }
}}
onClick={handleCancel}
>
Cancel
</Button>
</div>
</Box>
</Modal>
</LocalizationProvider>
)}
<div style={{ marginBottom: '5px' }}> <div style={{ marginBottom: '5px' }}>
<TextField <TextField

View File

@ -32,6 +32,7 @@ export type ConfigurationValues = {
minimumSoC: string | number; minimumSoC: string | number;
gridSetPoint: number; gridSetPoint: number;
forceCalibrationCharge: number; forceCalibrationCharge: number;
calibrationChargeDate: Date | null;
}; };
export interface Battery { export interface Battery {
@ -158,6 +159,7 @@ export type TopologyValues = {
DcDcNum: I_BoxDataValue[]; DcDcNum: I_BoxDataValue[];
calibrationChargeForced: I_BoxDataValue[]; calibrationChargeForced: I_BoxDataValue[];
mode: I_BoxDataValue[]; mode: I_BoxDataValue[];
calibrationChargeDate: I_BoxDataValue[];
batteryView: Battery[]; batteryView: Battery[];
}; };
@ -293,7 +295,8 @@ export const topologyPaths: TopologyPaths = {
maximumDischargePower: ['/Config/MaxBatteryDischargingCurrent'], maximumDischargePower: ['/Config/MaxBatteryDischargingCurrent'],
DcDcNum: ['/DcDc/SystemControl/NumberOfConnectedSlaves'], DcDcNum: ['/DcDc/SystemControl/NumberOfConnectedSlaves'],
calibrationChargeForced: ['/Config/ForceCalibrationCharge'], calibrationChargeForced: ['/Config/ForceCalibrationCharge'],
mode: ['/EssControl/Mode'] mode: ['/EssControl/Mode'],
calibrationChargeDate: ['/EssControl/Date']
}; };
export const extractValues = ( export const extractValues = (
@ -307,19 +310,19 @@ export const extractValues = (
if (topologyKey === 'batteryView') { if (topologyKey === 'batteryView') {
extractedValues[topologyKey] = []; extractedValues[topologyKey] = [];
const numOfBatteries = timeSeriesData.value[ const battery_ids_from_csv = timeSeriesData.value[
'/Config/Devices/BatteryNodes' '/Config/Devices/BatteryNodes'
].value ].value
.toString() .toString()
.split(',').length; .split(',');
let battery_id = 1; let battery_index = 0;
let pathIndex = 0; let pathIndex = 0;
while (pathIndex < paths.length) { while (pathIndex < paths.length) {
let battery = {}; let battery = {};
let existingKeys = 0; let existingKeys = 0;
battery[BatteryKeys[0]] = battery_id; battery[BatteryKeys[0]] = battery_ids_from_csv[battery_index];
for (let i = 1; i < BatteryKeys.length; i++) { for (let i = 1; i < BatteryKeys.length; i++) {
const path = paths[pathIndex]; const path = paths[pathIndex];
if (timeSeriesData.value.hasOwnProperty(path)) { if (timeSeriesData.value.hasOwnProperty(path)) {
@ -337,7 +340,7 @@ export const extractValues = (
} }
pathIndex++; pathIndex++;
} }
battery_id++; battery_index++;
if (existingKeys > 0) { if (existingKeys > 0) {
extractedValues[topologyKey].push(battery as Battery); extractedValues[topologyKey].push(battery as Battery);
} }