Updated front-end for salidomo installations
Fixed zoom-in problem Added log tab for salidomo installations
This commit is contained in:
parent
bde392e35d
commit
aa912e5fe8
|
@ -109,7 +109,7 @@ public static partial class Db
|
|||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("---------------Added the new Error to the database-----------------");
|
||||
Console.WriteLine("---------------Added the new Warning to the database-----------------");
|
||||
Create(newWarning);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,4 @@ public class AlarmOrWarning
|
|||
public required String Time { get; set; }
|
||||
public required String Description { get; set; }
|
||||
public required String CreatedBy { get; set; }
|
||||
}
|
||||
}
|
|
@ -50,6 +50,8 @@ public static class RabbitMqManager
|
|||
var message = Encoding.UTF8.GetString(body);
|
||||
//A message can be an alarm, a warning or a heartbit
|
||||
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
|
||||
|
||||
|
||||
|
||||
lock (WebsocketManager.InstallationConnections)
|
||||
{
|
||||
|
@ -73,6 +75,7 @@ public static class RabbitMqManager
|
|||
//Traverse the Warnings list, and store each of them to the database
|
||||
if (receivedStatusMessage.Warnings != null)
|
||||
{
|
||||
|
||||
foreach (var warning in receivedStatusMessage.Warnings)
|
||||
{
|
||||
Warning newWarning = new Warning
|
||||
|
@ -85,6 +88,7 @@ public static class RabbitMqManager
|
|||
Seen = false
|
||||
};
|
||||
//Create a new warning and add it to the database
|
||||
Console.WriteLine("Add a warning for installation "+installationId);
|
||||
Db.HandleWarning(newWarning, installationId);
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +97,7 @@ public static class RabbitMqManager
|
|||
//Traverse the Alarm list, and store each of them to the database
|
||||
if (receivedStatusMessage.Alarms != null)
|
||||
{
|
||||
Console.WriteLine("Add an alarm for installation "+installationId);
|
||||
|
||||
foreach (var alarm in receivedStatusMessage.Alarms)
|
||||
{
|
||||
Error newError = new Error
|
||||
|
@ -104,7 +108,7 @@ public static class RabbitMqManager
|
|||
Time = alarm.Time,
|
||||
DeviceCreatedTheMessage = alarm.CreatedBy,
|
||||
Seen = false
|
||||
};
|
||||
}; Console.WriteLine("Add an alarm for installation "+installationId);
|
||||
//Create a new error and add it to the database
|
||||
Db.HandleError(newError, installationId);
|
||||
}
|
||||
|
@ -139,6 +143,7 @@ public static class RabbitMqManager
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace InnovEnergy.App.Backend.Websockets;
|
|||
public class StatusMessage
|
||||
{
|
||||
public required Int32 InstallationId { get; set; }
|
||||
public required int Status { get; set; }
|
||||
public required Int32 Product { get; set; }
|
||||
public required Int32 Status { get; set; }
|
||||
public required MessageType Type { get; set; }
|
||||
public List<AlarmOrWarning>? Warnings { get; set; }
|
||||
public List<AlarmOrWarning>? Alarms { get; set; }
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace InnovEnergy.App.SaliMax.DataTypes;
|
|||
public class StatusMessage
|
||||
{
|
||||
public required Int32 InstallationId { get; set; }
|
||||
public required Int32 Product { get; set; }
|
||||
public required SalimaxAlarmState Status { get; set; }
|
||||
public required MessageType Type { get; set; }
|
||||
public List<AlarmOrWarning>? Warnings { get; set; }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#undef Amax
|
||||
#define Amax
|
||||
#undef GridLimit
|
||||
|
||||
using System.IO.Compression;
|
||||
|
|
Binary file not shown.
|
@ -114,6 +114,16 @@ function MainStats(props: MainStatsProps) {
|
|||
});
|
||||
}, []);
|
||||
|
||||
const [isZooming, setIsZooming] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isZooming) {
|
||||
setLoading(true);
|
||||
} else if (!isZooming && batteryViewDataArray.length > 0) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [isZooming, batteryViewDataArray]);
|
||||
|
||||
function generateSeries(chartData, category, color) {
|
||||
const series = [];
|
||||
const pathsToSearch = [
|
||||
|
@ -223,11 +233,14 @@ function MainStats(props: MainStatsProps) {
|
|||
setErrorDateModalOpen(false);
|
||||
};
|
||||
|
||||
const startZoom = () => {
|
||||
setIsZooming(true);
|
||||
};
|
||||
|
||||
const handleBeforeZoom = (chartContext, { xaxis }) => {
|
||||
const startX = parseInt(xaxis.min) / 1000;
|
||||
const endX = parseInt(xaxis.max) / 1000;
|
||||
|
||||
setLoading(true);
|
||||
const resultPromise: Promise<{
|
||||
chartData: BatteryDataInterface;
|
||||
chartOverview: BatteryOverviewInterface;
|
||||
|
@ -246,7 +259,7 @@ function MainStats(props: MainStatsProps) {
|
|||
})
|
||||
);
|
||||
|
||||
setLoading(false);
|
||||
setIsZooming(false);
|
||||
setChartState(batteryViewDataArray.length);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
@ -509,7 +522,10 @@ function MainStats(props: MainStatsProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -568,7 +584,10 @@ function MainStats(props: MainStatsProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -626,7 +645,10 @@ function MainStats(props: MainStatsProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -684,7 +706,10 @@ function MainStats(props: MainStatsProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -742,7 +767,10 @@ function MainStats(props: MainStatsProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -288,6 +288,26 @@ function InformationSalidomo(props: InformationSalidomoProps) {
|
|||
/>
|
||||
</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
|
||||
style={{
|
||||
display: 'flex',
|
||||
|
|
|
@ -34,7 +34,7 @@ function InstallationTabs() {
|
|||
useContext(InstallationsContext);
|
||||
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { socket, openSocket } = webSocketsContext;
|
||||
const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||
|
||||
useEffect(() => {
|
||||
let path = location.pathname.split('/');
|
||||
|
@ -50,7 +50,11 @@ function InstallationTabs() {
|
|||
}, [location]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket && salimaxInstallations.length > 0) {
|
||||
if (salimaxInstallations && salimaxInstallations.length > 0) {
|
||||
if (socket) {
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
openSocket(salimaxInstallations);
|
||||
}
|
||||
}, [salimaxInstallations]);
|
||||
|
|
|
@ -71,6 +71,15 @@ function Overview(props: OverviewProps) {
|
|||
|
||||
const [startDate, setStartDate] = useState(dayjs().add(-1, 'day'));
|
||||
const [endDate, setEndDate] = useState(dayjs());
|
||||
const [isZooming, setIsZooming] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (isZooming) {
|
||||
setLoading(true);
|
||||
} else if (!isZooming && dailyDataArray.length > 0) {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [isZooming, dailyDataArray]);
|
||||
|
||||
useEffect(() => {
|
||||
const resultPromise: Promise<{
|
||||
|
@ -97,11 +106,14 @@ function Overview(props: OverviewProps) {
|
|||
});
|
||||
}, []);
|
||||
|
||||
const startZoom = () => {
|
||||
setIsZooming(true);
|
||||
};
|
||||
|
||||
const handleBeforeZoom = (chartContext, { xaxis }) => {
|
||||
const startX = parseInt(xaxis.min) / 1000;
|
||||
const endX = parseInt(xaxis.max) / 1000;
|
||||
|
||||
setLoading(true);
|
||||
const resultPromise: Promise<{
|
||||
chartData: chartDataInterface;
|
||||
chartOverview: overviewInterface;
|
||||
|
@ -111,21 +123,31 @@ function Overview(props: OverviewProps) {
|
|||
UnixTime.fromTicks(endX)
|
||||
);
|
||||
|
||||
let isComponentMounted = true;
|
||||
|
||||
resultPromise
|
||||
.then((result) => {
|
||||
setDailyDataArray((prevData) =>
|
||||
prevData.concat({
|
||||
chartData: result.chartData,
|
||||
chartOverview: result.chartOverview
|
||||
})
|
||||
);
|
||||
|
||||
setLoading(false);
|
||||
setChartState(dailyDataArray.length);
|
||||
if (isComponentMounted) {
|
||||
setDailyDataArray((prevData) =>
|
||||
prevData.concat({
|
||||
chartData: result.chartData,
|
||||
chartOverview: result.chartOverview
|
||||
})
|
||||
);
|
||||
setIsZooming(false);
|
||||
setChartState(dailyDataArray.length);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
if (isComponentMounted) {
|
||||
console.error('Error:', error);
|
||||
setLoading(false); // Ensure loading is turned off even if there is an error
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
isComponentMounted = false;
|
||||
};
|
||||
};
|
||||
|
||||
const handle24HourData = () => {
|
||||
|
@ -595,7 +617,10 @@ function Overview(props: OverviewProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -761,7 +786,10 @@ function Overview(props: OverviewProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -851,7 +879,10 @@ function Overview(props: OverviewProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -982,7 +1013,10 @@ function Overview(props: OverviewProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -1040,7 +1074,10 @@ function Overview(props: OverviewProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -1111,7 +1148,10 @@ function Overview(props: OverviewProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
@ -1196,7 +1236,10 @@ function Overview(props: OverviewProps) {
|
|||
),
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: handleBeforeZoom
|
||||
beforeZoom: (chartContext, options) => {
|
||||
startZoom();
|
||||
handleBeforeZoom(chartContext, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import {
|
||||
Card,
|
||||
CircularProgress,
|
||||
Grid,
|
||||
Table,
|
||||
TableBody,
|
||||
|
@ -16,6 +17,7 @@ import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import routes from '../../../Resources/routes.json';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
|
||||
interface FlatInstallationViewProps {
|
||||
installations: I_Installation[];
|
||||
|
@ -29,6 +31,21 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
const [selectedInstallation, setSelectedInstallation] = useState<number>(-1);
|
||||
const currentLocation = useLocation();
|
||||
|
||||
const sortedInstallations = [...props.installations].sort((a, b) => {
|
||||
// Compare the status field of each installation and sort them based on the status.
|
||||
//Installations with alarms go first
|
||||
let a_status = getStatus(a.id);
|
||||
let b_status = getStatus(b.id);
|
||||
|
||||
if (a_status > b_status) {
|
||||
return -1;
|
||||
}
|
||||
if (a_status < b_status) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
const handleSelectOneInstallation = (installationID: number): void => {
|
||||
if (selectedInstallation != installationID) {
|
||||
setSelectedInstallation(installationID);
|
||||
|
@ -95,10 +112,13 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
<TableCell>
|
||||
<FormattedMessage id="VRM Link" defaultMessage="VRM Link" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormattedMessage id="status" defaultMessage="Status" />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{props.installations.map((installation) => {
|
||||
{sortedInstallations.map((installation) => {
|
||||
const isInstallationSelected =
|
||||
installation.s3BucketId === selectedInstallation;
|
||||
|
||||
|
@ -200,6 +220,57 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
</a>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginLeft: '15px'
|
||||
}}
|
||||
>
|
||||
{status === -1 ? (
|
||||
<CancelIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
height: '23px',
|
||||
color: 'red',
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{status === -2 ? (
|
||||
<CircularProgress
|
||||
size={20}
|
||||
sx={{
|
||||
color: '#f7b34d'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
marginLeft: '2px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor:
|
||||
status === 2
|
||||
? 'red'
|
||||
: status === 1
|
||||
? 'orange'
|
||||
: status === -1 || status === -2
|
||||
? 'transparent'
|
||||
: 'green'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Card, 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 { TimeSpan, UnixTime } from 'src/dataCache/time';
|
||||
|
@ -15,6 +15,8 @@ import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
|||
import routes from '../../../Resources/routes.json';
|
||||
import InformationSalidomo from '../Information/InformationSalidomo';
|
||||
import BatteryView from '../BatteryView/BatteryView';
|
||||
import Log from '../Log/Log';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
|
||||
interface singleInstallationProps {
|
||||
current_installation?: I_Installation;
|
||||
|
@ -174,6 +176,69 @@ function Installation(props: singleInstallationProps) {
|
|||
{props.current_installation.installationName}
|
||||
</Typography>
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Typography
|
||||
fontWeight="bold"
|
||||
color="text.primary"
|
||||
noWrap
|
||||
sx={{
|
||||
marginTop: '0px',
|
||||
marginBottom: '10px',
|
||||
fontSize: '14px'
|
||||
}}
|
||||
>
|
||||
Status:
|
||||
</Typography>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginLeft: '75px',
|
||||
marginTop: '-10px'
|
||||
}}
|
||||
>
|
||||
{status === -1 ? (
|
||||
<CancelIcon
|
||||
style={{
|
||||
width: '23px',
|
||||
height: '23px',
|
||||
color: 'red',
|
||||
borderRadius: '50%'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
{status === -2 ? (
|
||||
<CircularProgress
|
||||
size={20}
|
||||
sx={{
|
||||
color: '#f7b34d'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
marginLeft: '2px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor:
|
||||
status === 2
|
||||
? 'red'
|
||||
: status === 1
|
||||
? 'orange'
|
||||
: status === -1 || status === -2
|
||||
? 'transparent'
|
||||
: 'green'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card variant="outlined">
|
||||
<Grid
|
||||
|
@ -195,6 +260,16 @@ function Installation(props: singleInstallationProps) {
|
|||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={routes.log}
|
||||
element={
|
||||
<Log
|
||||
errorLoadingS3Data={errorLoadingS3Data}
|
||||
id={props.current_installation.id}
|
||||
></Log>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={routes.batteryview + '*'}
|
||||
element={
|
||||
|
|
|
@ -23,7 +23,7 @@ function SalidomoInstallationTabs() {
|
|||
useContext(InstallationsContext);
|
||||
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { socket, openSocket } = webSocketsContext;
|
||||
const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||
|
||||
useEffect(() => {
|
||||
let path = location.pathname.split('/');
|
||||
|
@ -43,6 +43,15 @@ function SalidomoInstallationTabs() {
|
|||
}
|
||||
}, [salidomoInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (salidomoInstallations && salidomoInstallations.length > 0) {
|
||||
if (socket) {
|
||||
closeSocket();
|
||||
}
|
||||
openSocket(salidomoInstallations);
|
||||
}
|
||||
}, [salidomoInstallations]);
|
||||
|
||||
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
||||
setCurrentTab(value);
|
||||
};
|
||||
|
@ -83,6 +92,10 @@ function SalidomoInstallationTabs() {
|
|||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
value: 'log',
|
||||
label: <FormattedMessage id="log" defaultMessage="Log" />
|
||||
},
|
||||
|
||||
{
|
||||
value: 'information',
|
||||
|
|
Loading…
Reference in New Issue