Fixed bug when updating offline installation in frontend

Update offline table every 1 minute in backend
This commit is contained in:
Noe 2024-08-30 14:33:47 +02:00
parent b60d279157
commit d6172e6fa8
10 changed files with 224 additions and 268 deletions

View File

@ -36,9 +36,9 @@ public static class WebsocketManager
foreach (var installationConnection in InstallationConnections){
if (installationConnection.Value.Product==1 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(30))
{
Console.WriteLine("Installation ID is "+installationConnection.Key);
Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
// Console.WriteLine("Installation ID is "+installationConnection.Key);
// Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
// Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
installationConnection.Value.Status = -1;
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}

View File

@ -13,9 +13,9 @@ DEVICE_INSTANCE = 1
SERVICE_NAME_PREFIX = 'com.victronenergy.battery.'
#s3 configuration
S3BUCKET = "195-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXO4657447df7b842d19b40af13"
S3SECRET = "1ZAZ-ftzKsfYEL7SxldkWKg-3Ik_yQ6vXffLMIz5ACU"
S3BUCKET = "637-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXOe9a2f9b47c34cf9f1b615b09"
S3SECRET = "S8MuM7k3KGAVw2iPiociaCfYVrJ5RXvozL1wY_f_i90"
# driver configuration

View File

@ -54,6 +54,6 @@ INNOVENERGY_PROTOCOL_VERSION = '48TL200V3'
# S3 Credentials
S3BUCKET = "199-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXOe09da11251853e22b8b84bbb"
S3SECRET = "bFH9uwoiZjMx-ADsoAOaEADx4I65DRnXF1YtQ7rfzls"
S3BUCKET = "425-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
S3KEY = "EXO3fb358076b23f6daebe779ac"
S3SECRET = "HnspAdjwfRjtB_6vm0aH2BUYPsPOvZW6Hya_OU0gSLU"

View File

@ -15,135 +15,64 @@ echo -e "\n============================ Deploy ============================\n"
# Steiger, Rheinau 10.2.0.188 failed with ssh
#ip_addresses=("10.2.0.144")
ip_addresses=(
"10.2.1.35"
"10.2.1.159"
"10.2.0.227"
"10.2.0.211"
"10.2.1.134"
"10.2.0.130"
"10.2.1.169"
"10.2.0.105"
"10.2.0.220"
"10.2.1.124"
"10.2.1.2"
"10.2.1.158"
"10.2.0.195"
"10.2.1.171"
"10.2.0.225"
"10.2.1.53"
"10.2.0.107"
"10.2.0.153"
"10.2.1.106"
"10.2.1.117"
"10.2.0.145"
"10.2.0.110"
"10.2.1.177"
"10.2.1.247"
"10.2.0.101"
"10.2.0.108"
"10.2.1.120"
"10.2.1.160"
"10.2.1.173"
"10.2.0.113"
"10.2.0.150"
"10.2.0.233"
"10.2.1.162"
"10.2.1.138"
"10.2.1.100"
"10.2.1.165"
"10.2.1.163"
"10.2.1.32"
"10.2.1.110"
"10.2.0.103"
"10.2.1.113"
"10.2.0.216"
"10.2.0.184"
"10.2.0.191"
"10.2.1.60"
"10.2.1.219"
"10.2.0.214"
"10.2.1.83"
"10.2.0.217"
"10.2.1.39"
"10.2.0.194"
"10.2.1.81"
"10.2.1.125"
"10.2.0.154"
"10.2.0.196"
"10.2.1.84"
"10.2.1.130"
"10.2.1.38"
"10.2.1.33"
"10.2.0.249"
"10.2.0.133"
"10.2.1.118"
"10.2.0.138"
"10.2.0.144"
"10.2.0.188"
"10.2.1.74"
"10.2.1.141"
"10.2.1.142"
"10.2.0.193"
"10.2.1.145"
"10.2.1.15"
"10.2.1.70"
"10.2.0.135"
"10.2.0.247"
"10.2.0.134"
"10.2.1.21"
"10.2.1.73"
"10.2.0.243"
"10.2.1.19"
"10.2.1.166"
"10.2.0.192"
"10.2.1.12"
"10.2.2.188"
"10.2.0.158"
"10.2.1.146"
"10.2.1.27"
"10.2.0.202"
"10.2.0.157"
"10.2.1.55"
"10.2.1.16"
"10.2.1.28"
"10.2.0.254"
"10.2.1.128"
"10.2.1.58"
"10.2.0.187"
"10.2.1.156"
"10.2.1.137"
"10.2.1.135"
"10.2.1.24"
"10.2.1.109"
"10.2.1.90"
"10.2.1.153"
"10.2.1.111"
"10.2.1.71"
"10.2.1.37"
"10.2.0.127"
"10.2.0.126"
"10.2.1.22"
"10.2.1.91"
"10.2.1.123"
"10.2.1.220"
"10.2.1.114"
"10.2.1.41"
"10.2.0.125"
"10.2.1.62"
"10.2.0.161"
"10.2.1.121"
"10.2.1.10"
"10.2.0.112"
"10.2.0.111"
"10.2.0.218"
"10.2.1.49"
"10.2.0.230"
"10.2.1.170"
"10.2.0.114"
)
ip_addresses=("10.2.1.115" "10.2.0.238" "10.2.0.115" "10.2.0.160" "10.2.0.149")
#
#ip_addresses=(
#"10.2.1.70"
#"10.2.0.135"
#"10.2.0.247"
#"10.2.0.134"
#"10.2.1.21"
#"10.2.1.73"
#"10.2.0.243"
#"10.2.1.19"
#"10.2.1.166"
#"10.2.0.192"
#"10.2.1.12"
#"10.2.2.188"
#"10.2.0.158"
#"10.2.1.146"
#"10.2.1.27"
#"10.2.0.202"
#"10.2.0.157"
#"10.2.1.55"
#"10.2.1.16"
#"10.2.1.28"
#"10.2.0.254"
#"10.2.1.128"
#"10.2.1.58"
#"10.2.0.187"
#"10.2.1.156"
#"10.2.1.137"
#"10.2.1.135"
#"10.2.1.24"
#"10.2.1.109"
#"10.2.1.90"
#"10.2.1.153"
#"10.2.1.111"
#"10.2.1.71"
#"10.2.1.37"
#"10.2.0.127"
#"10.2.0.126"
#"10.2.1.22"
#"10.2.1.91"
#"10.2.1.123"
#"10.2.1.220"
#"10.2.1.114"
#"10.2.1.41"
#"10.2.0.125"
#"10.2.1.62"
#"10.2.0.161"
#"10.2.1.121"
#"10.2.1.10"
#"10.2.0.112"
#"10.2.0.111"
#"10.2.0.218"
#"10.2.1.49"
#"10.2.0.230"
#"10.2.1.170"
#"10.2.0.114"
#)
## scp template

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useContext, useEffect, useState } from 'react';
import { I_S3Credentials } from '../../../interfaces/S3Types';
import {
Box,
@ -21,6 +21,8 @@ import { useNavigate } from 'react-router-dom';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import Button from '@mui/material/Button';
import axiosConfig from '../../../Resources/axiosConfig';
import { UserType } from '../../../interfaces/UserTypes';
import { UserContext } from '../../../contexts/userContext';
interface DetailedBatteryViewProps {
s3Credentials: I_S3Credentials;
@ -119,6 +121,9 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
backgroundColor: '#bfbfbf'
};
const context = useContext(UserContext);
const { currentUser } = context;
const FirmwareModalHandleProceed = async (e) => {
setOpenModalFirmwareUpdate(false);
@ -559,6 +564,8 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
<IconButton
aria-label="go back"
sx={{
marginTop:
currentUser.userType != UserType.admin ? '30px' : '0px',
backgroundColor: 'grey',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
@ -568,49 +575,53 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
<ArrowBackIcon />
</IconButton>
<Select
value={selectedVersion}
onChange={(event) => setSelectedVersion(event.target.value)}
displayEmpty
variant="outlined"
sx={{
marginTop: '30px',
marginLeft: '20px',
height: '40px'
}}
>
<MenuItem disabled value="">
Select Firmware Version
</MenuItem>
<MenuItem value="AF09">AF09</MenuItem>
<MenuItem value="AF11">AF11</MenuItem>
<MenuItem value="AF0A">AF0A</MenuItem>
</Select>
<Button
variant="contained"
onClick={handleUpdateFirmware}
disabled={!selectedVersion} // Disable button if no version selected
sx={{
marginLeft: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
Update Firmware
</Button>
<Button
variant="contained"
onClick={handleDownloadBmsLog}
sx={{
marginLeft: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
Download Battery Log
</Button>
{currentUser.userType == UserType.admin && (
<>
<Select
value={selectedVersion}
onChange={(event) => setSelectedVersion(event.target.value)}
displayEmpty
variant="outlined"
sx={{
marginTop: '30px',
marginLeft: '20px',
height: '40px'
}}
>
<MenuItem disabled value="">
Select Firmware Version
</MenuItem>
<MenuItem value="AF09">AF09</MenuItem>
<MenuItem value="AF11">AF11</MenuItem>
<MenuItem value="AF0A">AF0A</MenuItem>
</Select>
<Button
variant="contained"
onClick={handleUpdateFirmware}
disabled={!selectedVersion} // Disable button if no version selected
sx={{
marginLeft: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
Update Firmware
</Button>
<Button
variant="contained"
onClick={handleDownloadBmsLog}
sx={{
marginLeft: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
Download Battery Log
</Button>
</>
)}
</Grid>
</Grid>
<Grid container>

View File

@ -180,12 +180,7 @@ function Information(props: InformationProps) {
>
<div>
<TextField
label={
<FormattedMessage
id="customerName"
defaultMessage="Customer Name"
/>
}
label={<FormattedMessage id="name" defaultMessage="name" />}
name="name"
value={formValues.name}
onChange={handleChange}

View File

@ -25,6 +25,7 @@ import { InstallationsContext } from '../../../contexts/InstallationsContextProv
import { UserContext } from '../../../contexts/userContext';
import routes from '../../../Resources/routes.json';
import { useNavigate } from 'react-router-dom';
import { UserType } from '../../../interfaces/UserTypes';
interface InformationSalidomoProps {
values: I_Installation;
@ -41,7 +42,7 @@ function InformationSalidomo(props: InformationSalidomoProps) {
const { currentUser } = context;
const theme = useTheme();
const [formValues, setFormValues] = useState(props.values);
const requiredFields = ['installationName', 'region', 'location', 'country'];
const requiredFields = ['name', 'region', 'location', 'country'];
const [openModalDeleteInstallation, setOpenModalDeleteInstallation] =
useState(false);
const navigate = useNavigate();
@ -336,38 +337,42 @@ function InformationSalidomo(props: InformationSalidomoProps) {
/>
</div>
<div>
<TextField
label="S3 Bucket Name"
name="s3writesecretkey"
value={
formValues.s3BucketId +
'-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e'
}
variant="outlined"
fullWidth
/>
</div>
{currentUser.userType == UserType.admin && (
<>
<div>
<TextField
label="S3 Bucket Name"
name="s3writesecretkey"
value={
formValues.s3BucketId +
'-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e'
}
variant="outlined"
fullWidth
/>
</div>
<div>
<TextField
label="S3 Write Key"
name="s3writesecretkey"
value={formValues.s3WriteKey}
variant="outlined"
fullWidth
/>
</div>
<div>
<TextField
label="S3 Write Key"
name="s3writesecretkey"
value={formValues.s3WriteKey}
variant="outlined"
fullWidth
/>
</div>
<div>
<TextField
label="S3 Write Secret Key"
name="s3writesecretkey"
value={formValues.s3WriteSecret}
variant="outlined"
fullWidth
/>
</div>
<div>
<TextField
label="S3 Write Secret Key"
name="s3writesecretkey"
value={formValues.s3WriteSecret}
variant="outlined"
fullWidth
/>
</div>
</>
)}
<div
style={{
@ -390,18 +395,20 @@ function InformationSalidomo(props: InformationSalidomoProps) {
/>
</Button>
<Button
variant="contained"
onClick={handleDelete}
sx={{
marginLeft: '10px'
}}
>
<FormattedMessage
id="deleteInstallation"
defaultMessage="Delete Installation"
/>
</Button>
{currentUser.userType == UserType.admin && (
<Button
variant="contained"
onClick={handleDelete}
sx={{
marginLeft: '10px'
}}
>
<FormattedMessage
id="deleteInstallation"
defaultMessage="Delete Installation"
/>
</Button>
)}
{loading && (
<CircularProgress

View File

@ -3,6 +3,7 @@ import {
Card,
CircularProgress,
Grid,
styled,
Table,
TableBody,
TableCell,
@ -25,7 +26,6 @@ interface FlatInstallationViewProps {
}
const FlatInstallationView = (props: FlatInstallationViewProps) => {
const [isRowHovered, setHoveredRow] = useState(-1);
const webSocketContext = useContext(WebSocketContext);
const { getStatus, getTestingMode } = webSocketContext;
const navigate = useNavigate();
@ -70,13 +70,12 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
const theme = useTheme();
const handleRowMouseEnter = (id: number) => {
setHoveredRow(id);
};
const handleRowMouseLeave = () => {
setHoveredRow(-1);
};
const HoverableTableRow = styled(TableRow)(({ theme }) => ({
cursor: 'pointer',
'&:hover': {
backgroundColor: theme.palette.action.hover // Or any other color
}
}));
return (
<Grid container spacing={1} sx={{ marginTop: 0.1 }}>
@ -126,27 +125,13 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
installation.s3BucketId === selectedInstallation;
const status = getStatus(installation.id);
const rowStyles =
isRowHovered === installation.s3BucketId
? {
cursor: 'pointer',
backgroundColor: theme.colors.primary.lighter
}
: {};
return (
<TableRow
hover
<HoverableTableRow
key={installation.s3BucketId}
selected={isInstallationSelected}
style={rowStyles}
onClick={() =>
handleSelectOneInstallation(installation.s3BucketId)
}
onMouseEnter={() =>
handleRowMouseEnter(installation.s3BucketId)
}
onMouseLeave={() => handleRowMouseLeave()}
>
<TableCell>
<Typography
@ -336,7 +321,7 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
)}
</div>
</TableCell>
</TableRow>
</HoverableTableRow>
);
})}
</TableBody>

View File

@ -96,7 +96,11 @@ function CustomTreeItem(props: CustomTreeItemProps) {
nodeId={props.node.id.toString() + props.node.type}
label={
<div className={itemClasses.join(' ')}>
<ListItemIcon color="inherit" className={classes.labelIcon}>
<ListItemIcon
color="inherit"
className={classes.labelIcon}
sx={{ marginLeft: '10px' }}
>
{renderIcon()}
</ListItemIcon>
<Typography

View File

@ -46,10 +46,14 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
}
}, 10000); // Send a ping every 10 seconds
// Listen for messages
let messageBuffer = [];
let isProcessing = false;
socket.addEventListener('message', (event) => {
const message = JSON.parse(event.data); // Parse the JSON data
if (Array.isArray(message)) {
// Existing code for handling arrays, if necessary
setInstallationMode((prevMode) => {
const newMode = new Map(prevMode);
message.forEach((item) => {
@ -66,20 +70,41 @@ const WebSocketContextProvider = ({ children }: { children: ReactNode }) => {
return newStatus;
});
} else if (message.id != -1) {
const installation_id = message.id;
// Accumulate messages in the buffer
messageBuffer.push(message);
setInstallationMode((prevMode) => {
const newMode = new Map(prevMode);
newMode.set(message.id, message.testingMode);
return newMode;
});
setInstallationStatus((prevStatus) => {
const newStatus = new Map(prevStatus);
newStatus.set(message.id, message.status);
return newStatus;
});
// Process the buffer if not already processing
if (!isProcessing) {
isProcessing = true;
// Use setTimeout to process the buffer periodically
setTimeout(() => {
const newInstallationMode = new Map();
const newInstallationStatus = new Map();
// Process all accumulated messages
messageBuffer.forEach((msg) => {
newInstallationMode.set(msg.id, msg.testingMode);
newInstallationStatus.set(msg.id, msg.status);
});
// Update the state with the accumulated messages
setInstallationMode(
(prevMode) => new Map([...prevMode, ...newInstallationMode])
);
setInstallationStatus(
(prevStatus) =>
new Map([...prevStatus, ...newInstallationStatus])
);
// Clear the buffer after processing
messageBuffer = [];
isProcessing = false; // Reset processing flag
}, 100); // Adjust the delay as needed to control processing frequency
}
}
});
setSocket(socket);
}
}, [installations]);