Inserted select button to select firmware version.

This commit is contained in:
Noe 2024-04-30 14:07:50 +02:00
parent cee72bebe6
commit ccec20432b
15 changed files with 468 additions and 372 deletions

View File

@ -533,14 +533,14 @@ public class Controller : ControllerBase
}
[HttpPost(nameof(UpdateFirmware))]
public async Task<ActionResult> UpdateFirmware(Int64 batteryNode, Int64 installationId,Token authToken)
public async Task<ActionResult> UpdateFirmware(Int64 batteryNode, Int64 installationId,String version,Token authToken)
{
var session = Db.GetSession(authToken);
var installationToUpdate = Db.GetInstallationById(installationId);
if (installationToUpdate != null)
{
_ = session.RunScriptInBackground(installationToUpdate.VpnIp, batteryNode);
_ = session.RunScriptInBackground(installationToUpdate.VpnIp, batteryNode,version);
}
return Ok();

View File

@ -8,6 +8,7 @@ public class Installation : TreeNode
public String Region { get; set; } = "";
public String Country { get; set; } = "";
public String VpnIp { get; set; } = "";
public String InstallationName { get; set; } = "";
public String S3Region { get; set; } = "sos-ch-dk-2";
public String S3Provider { get; set; } = "exo.io";
@ -15,6 +16,7 @@ public class Installation : TreeNode
public String S3Key { get; set; } = "";
public String S3WriteSecret { get; set; } = "";
public String S3Secret { get; set; } = "";
public int S3BucketId { get; set; } = 0;
public String ReadRoleId { get; set; } = "";
public String WriteRoleId { get; set; } = "";

View File

@ -131,7 +131,7 @@ public static class ExoCmd
if (response.StatusCode != HttpStatusCode.OK){
Console.WriteLine("Fuck");
}
//Console.WriteLine($"Created Key for {installation.Name}");
//Console.WriteLine($"Created Key for {installation.InstallationName}");
var responseString = await response.Content.ReadAsStringAsync();
var responseJson = JsonNode.Parse(responseString) ;
@ -143,10 +143,12 @@ public static class ExoCmd
{
const String url = "https://api-ch-dk-2.exoscale.com/v2/iam-role";
const String method = "iam-role";
String rolename = installation.Product==0?Db.Installations.Count(f => f.Product == 0) + installation.InstallationName:Db.Installations.Count(f => f.Product == 1) + installation.InstallationName;
var contentString = $$"""
{
"name" : "{{installation.Id + installation.Name}}",
"name" : "{{rolename}}",
"policy" : {
"default-service-strategy": "deny",
"services": {
@ -243,10 +245,11 @@ public static class ExoCmd
{
const String url = "https://api-ch-dk-2.exoscale.com/v2/iam-role";
const String method = "iam-role";
String rolename = installation.Product==0?Db.Installations.Count(f => f.Product == 0) + installation.InstallationName:Db.Installations.Count(f => f.Product == 1) + installation.InstallationName;
var contentString = $$"""
{
"name" : "WRITE{{installation.Id + installation.Name}}",
"name" : "WRITE{{rolename}}",
"policy" : {
"default-service-strategy": "deny",
"services": {

View File

@ -18,10 +18,10 @@ public static class InstallationMethods
{
if (installation.Product == 0)
{
return $"{installation.Id}-{BucketNameSalt}";
return $"{installation.S3BucketId}-{BucketNameSalt}";
}
return $"{installation.Id}-{SalidomoBucketNameSalt}";
return $"{installation.S3BucketId}-{SalidomoBucketNameSalt}";
}

View File

@ -68,7 +68,7 @@ public static class SessionMethods
.Apply(Db.Update);
}
public static async Task RunScriptInBackground(this Session? session, String vpnIp, Int64 batteryNode)
public static async Task RunScriptInBackground(this Session? session, String vpnIp, Int64 batteryNode,String version)
{
Console.WriteLine("-----------------------------------Start updating firmware-----------------------------------");
string scriptPath = "/home/ubuntu/backend/uploadBatteryFw/update_firmware.sh";
@ -77,7 +77,7 @@ public static class SessionMethods
{
Process process = new Process();
process.StartInfo.FileName = "/bin/bash";
process.StartInfo.Arguments = $"{scriptPath} {vpnIp} {batteryNode}";
process.StartInfo.Arguments = $"{scriptPath} {vpnIp} {batteryNode} {version}";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
@ -118,6 +118,8 @@ public static class SessionMethods
{
var user = session?.User;
//Salimax installation
if (installation.Product==0)
{

View File

@ -16,6 +16,8 @@ public static partial class Db
public static Boolean Create(Installation installation)
{
installation.S3BucketId = Installations.Count(f => f.Product == installation.Product)+1;
// SQLite wrapper is smart and *modifies* t's Id to the one generated (autoincrement) by the insertion
return Insert(installation);
}

View File

@ -108,7 +108,7 @@ public static partial class Db
Connection.CreateTable<Warning>();
});
UpdateKeys();
//UpdateKeys();
CleanupSessions().SupressAwaitWarning();
}
@ -148,6 +148,18 @@ public static partial class Db
.Distinct()
.ToList();
Console.WriteLine("VALID READ KEYS");
for (int i = 0; i < validReadKeys.Count; i++)
{
Console.WriteLine(validReadKeys[i]);
}
Console.WriteLine("VALID WRITE KEYS");
for (int i = 0; i < validReadKeys.Count; i++)
{
Console.WriteLine(validWriteKeys[i]);
}
const String provider = "exo.io";
@ -157,7 +169,7 @@ public static partial class Db
{
if (keyMetadata["key"].ToString()!="EXOa0b53cf10517307cec1bf00e" && !validReadKeys.Contains(keyMetadata["key"].ToString()) && !validWriteKeys.Contains(keyMetadata["key"].ToString()))
{
await ExoCmd.RevokeReadKey(keyMetadata["key"].ToString());
//await ExoCmd.RevokeReadKey(keyMetadata["key"].ToString());
Console.WriteLine("Deleted key "+keyMetadata["key"]);
}

View File

@ -1,3 +1,4 @@
using Flurl.Http;
using Hellang.Middleware.ProblemDetails;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.Websockets;
@ -5,11 +6,13 @@ using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
using InnovEnergy.Lib.Utils;
using Org.BouncyCastle.Math.EC;
namespace InnovEnergy.App.Backend;
public static class Program
{
public static void Main(String[] args)
public static async Task Main(String[] args)
{
Watchdog.NotifyReady();
Db.Init();

View File

@ -58,7 +58,8 @@ public static class RabbitMqManager
{
Console.WriteLine("----------------------------------------------");
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
var installationId = receivedStatusMessage.InstallationId;
int installationId = (int)Db.Installations.Where(f => f.Product == 0 && f.S3BucketId == receivedStatusMessage.InstallationId).Select(f => f.Id).FirstOrDefault();
//This is a heartbit message, just update the timestamp for this installation.
//There is no need to notify the corresponding front-ends.

View File

@ -32,21 +32,21 @@ interface BatteryViewProps {
s3Credentials: I_S3Credentials;
installationId: number;
productNum: number;
connected: boolean;
}
function BatteryView(props: BatteryViewProps) {
if (props.values === null) {
if (props.values === null && props.connected == true) {
return null;
}
const currentLocation = useLocation();
const navigate = useNavigate();
const sortedBatteryView = [...props.values.batteryView].sort(
(a, b) => b.BatteryId - a.BatteryId
);
const sortedBatteryView =
props.values != null
? [...props.values.batteryView].sort((a, b) => b.BatteryId - a.BatteryId)
: [];
const [loading, setLoading] = useState(
sortedBatteryView.length == 0 ? true : false
);
const [loading, setLoading] = useState(sortedBatteryView.length == 0);
const handleMainStatsButton = () => {
navigate(routes.mainstats);
@ -70,7 +70,31 @@ function BatteryView(props: BatteryViewProps) {
return (
<>
{loading && (
{!props.connected && (
<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}
>
Unable to communicate with the installation
</Typography>
<Typography variant="body2" style={{ color: 'black' }}>
Please wait or refresh the page
</Typography>
</Container>
)}
{loading && props.connected && (
<Container
maxWidth="xl"
sx={{
@ -95,7 +119,7 @@ function BatteryView(props: BatteryViewProps) {
</Container>
)}
{!loading && (
{!loading && props.connected && (
<Container maxWidth="xl">
<Grid container>
<Grid

View File

@ -5,8 +5,10 @@ import {
Card,
Grid,
IconButton,
MenuItem,
Modal,
Paper,
Select,
Table,
TableBody,
TableCell,
@ -18,7 +20,6 @@ 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 axiosConfig from '../../../Resources/axiosConfig';
interface DetailedBatteryViewProps {
@ -37,6 +38,10 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
const [openModalResultFirmwareUpdate, setOpenModalResultFirmwareUpdate] =
useState(false);
const [selectedVersion, setSelectedVersion] = useState(
props.batteryData.FwVersion.value
);
const handleBatteryViewButton = () => {
navigate(location.pathname.split('/').slice(0, -2).join('/'));
};
@ -113,7 +118,7 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
.post(
`/UpdateFirmware?batteryNode=${props.batteryData.BatteryId.toString()}&installationId=${
props.installationId
}`
}&version=${selectedVersion}`
)
.catch((err) => {
if (err.response) {
@ -264,7 +269,7 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
<IconButton
aria-label="go back"
sx={{
marginTop: '20px',
marginTop: props.productNum != 0 ? '20px' : '0px',
backgroundColor: 'grey',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
@ -275,22 +280,38 @@ function DetailedBatteryView(props: DetailedBatteryViewProps) {
</IconButton>
{props.productNum === 0 && (
<>
<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="AF11">AF11</MenuItem>
<MenuItem value="AF0A">AF0A</MenuItem>
</Select>
<Button
variant="contained"
onClick={handleUpdateFirmware}
disabled={!selectedVersion} // Disable button if no version selected
sx={{
marginTop: '20px',
marginLeft: '20px',
backgroundColor: '#ffc04d',
color: '#000000',
'&:hover': { bgcolor: '#f7b34d' }
}}
>
<FormattedMessage
id="update_firmware"
defaultMessage="Update Firmware"
/>
Update Firmware
</Button>
</>
)}
</Grid>
</Grid>

View File

@ -1,11 +1,5 @@
import React, { useContext, useEffect, useState } from 'react';
import {
Card,
CircularProgress,
Container,
Grid,
Typography
} from '@mui/material';
import { Card, CircularProgress, Grid, Typography } from '@mui/material';
import { I_Installation } from 'src/interfaces/InstallationTypes';
import { UserContext } from 'src/contexts/userContext';
import AccessContextProvider from 'src/contexts/AccessContextProvider';
@ -49,10 +43,7 @@ function Installation(props: singleInstallationProps) {
failedToCommunicateWithInstallation,
setFailedToCommunicateWithInstallation
] = useState(0);
const [
openModalUnableToCommunicateWIthInstallation,
setOpenModalUnableToCommunicateWIthInstallation
] = useState(false);
const [connected, setConnected] = useState(true);
if (props.current_installation == undefined) {
return null;
@ -83,7 +74,7 @@ function Installation(props: singleInstallationProps) {
if (res != FetchResult.notAvailable && res != FetchResult.tryLater) {
setFailedToCommunicateWithInstallation(0);
setOpenModalUnableToCommunicateWIthInstallation(false);
setConnected(true);
setValues(
extractValues({
time: now,
@ -94,7 +85,7 @@ function Installation(props: singleInstallationProps) {
} else {
setFailedToCommunicateWithInstallation((prevCount) => {
if (prevCount + 1 >= 3) {
setOpenModalUnableToCommunicateWIthInstallation(true);
setConnected(false);
}
return prevCount + 1;
});
@ -125,7 +116,7 @@ function Installation(props: singleInstallationProps) {
useEffect(() => {
if (status === -1) {
setOpenModalUnableToCommunicateWIthInstallation(true);
setConnected(false);
}
}, [status]);
@ -160,10 +151,6 @@ function Installation(props: singleInstallationProps) {
}
}, [currentTab, location]);
const UnableToCommunicateModalHandleOk = () => {
setOpenModalUnableToCommunicateWIthInstallation(false);
};
return (
<>
<Grid item xs={12} md={12}>
@ -298,31 +285,6 @@ function Installation(props: singleInstallationProps) {
alignItems="stretch"
spacing={0}
>
{openModalUnableToCommunicateWIthInstallation && (
<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}
>
Unable to communicate with the installation
</Typography>
<Typography variant="body2" style={{ color: 'black' }}>
Please wait or refresh the page
</Typography>
</Container>
)}
<Routes>
<Route
path={routes.information}
@ -343,6 +305,7 @@ function Installation(props: singleInstallationProps) {
s3Credentials={s3Credentials}
installationId={props.current_installation.id}
productNum={props.current_installation.product}
connected={connected}
></BatteryView>
}
></Route>
@ -353,7 +316,9 @@ function Installation(props: singleInstallationProps) {
<Route
path={routes.live}
element={<Topology values={values}></Topology>}
element={
<Topology values={values} connected={connected}></Topology>
}
/>
<Route

View File

@ -13,8 +13,8 @@ import { FormattedMessage } from 'react-intl';
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
import routes from '../../../Resources/routes.json';
import BatteryView from '../BatteryView/BatteryView';
import InformationSalidomo from '../Information/InformationSalidomo';
import BatteryView from "../BatteryView/BatteryView";
interface singleInstallationProps {
current_installation?: I_Installation;
@ -31,6 +31,11 @@ function Installation(props: singleInstallationProps) {
const [currentTab, setCurrentTab] = useState<string>(undefined);
const [values, setValues] = useState<TopologyValues | null>(null);
const status = getStatus(props.current_installation.id);
const [
failedToCommunicateWithInstallation,
setFailedToCommunicateWithInstallation
] = useState(0);
const [connected, setConnected] = useState(true);
if (props.current_installation == undefined) {
return null;
@ -58,6 +63,8 @@ function Installation(props: singleInstallationProps) {
const res = await fetchData(now, s3Credentials);
if (res != FetchResult.notAvailable && res != FetchResult.tryLater) {
setConnected(true);
setFailedToCommunicateWithInstallation(0);
setValues(
extractValues({
time: now,
@ -66,6 +73,14 @@ function Installation(props: singleInstallationProps) {
);
return true;
}
else {
setFailedToCommunicateWithInstallation((prevCount) => {
if (prevCount + 1 >= 3) {
setConnected(false);
}
return prevCount + 1;
});
}
} catch (err) {
return false;
}
@ -121,6 +136,12 @@ function Installation(props: singleInstallationProps) {
}
}, [currentTab, location]);
useEffect(() => {
if (status === -1) {
setConnected(false);
}
}, [status]);
return (
<>
<Grid item xs={12} md={12}>
@ -183,6 +204,7 @@ function Installation(props: singleInstallationProps) {
s3Credentials={s3Credentials}
installationId={props.current_installation.id}
productNum={props.current_installation.product}
connected={connected}
></BatteryView>
}
></Route>

View File

@ -1,5 +1,11 @@
import React, { useState } from 'react';
import { Container, Grid, Switch, Typography } from '@mui/material';
import {
CircularProgress,
Container,
Grid,
Switch,
Typography
} from '@mui/material';
import TopologyColumn from './topologyColumn';
import {
getAmount,
@ -9,14 +15,15 @@ import {
interface TopologyProps {
values: TopologyValues;
connected: boolean;
}
function Topology(props: TopologyProps) {
if (props.values === null) {
if (props.values === null && props.connected == true) {
return null;
}
const highestConnectionValue = getHighestConnectionValue(props.values);
const highestConnectionValue =
props.values != null ? getHighestConnectionValue(props.values) : 0;
const [showValues, setShowValues] = useState(false);
const handleSwitch = () => () => {
@ -28,6 +35,33 @@ function Topology(props: TopologyProps) {
return (
<Container maxWidth="xl" style={{ backgroundColor: 'white' }}>
<Grid container>
{!props.connected && (
<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}
>
Unable to communicate with the installation
</Typography>
<Typography variant="body2" style={{ color: 'black' }}>
Please wait or refresh the page
</Typography>
</Container>
)}
{props.connected && (
<>
<Grid
item
xs={12}
@ -75,7 +109,8 @@ function Topology(props: TopologyProps) {
centerBox={{
title: 'Grid',
data: props.values.grid,
connected: props.values.gridBox[0].value.toString() != 'Disabled'
connected:
props.values.gridBox[0].value.toString() != 'Disabled'
}}
centerConnection={{
orientation: 'horizontal',
@ -131,7 +166,8 @@ function Topology(props: TopologyProps) {
title: 'AC Loads',
data: props.values.gridBusToLoadOnGridBusConnection,
connected:
props.values.loadOnAcGridBox[0].value.toString() != 'Disabled'
props.values.loadOnAcGridBox[0].value.toString() !=
'Disabled'
}}
bottomConnection={{
orientation: 'vertical',
@ -153,7 +189,8 @@ function Topology(props: TopologyProps) {
title: 'Pv Inverter',
data: props.values.pvOnIslandBusToIslandBusConnection,
connected:
props.values.pvOnIslandBusBox[0].value.toString() != 'Disabled'
props.values.pvOnIslandBusBox[0].value.toString() !=
'Disabled'
}}
topConnection={{
orientation: 'vertical',
@ -312,6 +349,8 @@ function Topology(props: TopologyProps) {
isFirst={false}
/>
</Grid>
</>
)}
</Grid>
</Container>
);

View File

@ -86,7 +86,7 @@ export const transformInputToBatteryViewData = async (
const categories = ['Soc', 'Temperature', 'Power', 'Voltage', 'Current'];
const pathCategories = [
'Soc',
'Temperatures/Cells/Center',
'Temperatures/Cells/Average',
'Dc/Power',
'Dc/Voltage',
'Dc/Current'