Created error table, provided error handling

This commit is contained in:
Noe 2023-11-15 17:22:42 +01:00
parent 22dd4222ca
commit 6a18e56cf7
21 changed files with 318 additions and 205 deletions

View File

@ -226,8 +226,8 @@
<ItemGroup>
<Folder Include="DbBackups\" />
</ItemGroup>
</Project> </Project>

View File

@ -54,6 +54,8 @@ public class Controller : ControllerBase
: Unauthorized(); : Unauthorized();
} }
[HttpGet(nameof(CreateWebSocket))] [HttpGet(nameof(CreateWebSocket))]
public async Task CreateWebSocket(Token authToken) public async Task CreateWebSocket(Token authToken)
{ {
@ -81,6 +83,23 @@ public class Controller : ControllerBase
//Handle the WebSocket connection //Handle the WebSocket connection
await WebsocketManager.HandleWebSocketConnection(webSocket); await WebsocketManager.HandleWebSocketConnection(webSocket);
} }
[HttpGet(nameof(GetAllErrorsForInstallation))]
public ActionResult<IEnumerable<Error>> GetAllErrorsForInstallation(Int64 id, Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
var installation = Db.GetInstallationById(id);
if (installation is null || !user.HasAccessTo(installation))
return Unauthorized();
return Db.Errors
.Where(error => error.InstallationId == id)
.ToList();
}
[HttpGet(nameof(GetUserById))] [HttpGet(nameof(GetUserById))]
public ActionResult<User> GetUserById(Int64 id, Token authToken) public ActionResult<User> GetUserById(Int64 id, Token authToken)

View File

@ -0,0 +1,14 @@
using SQLite;
namespace InnovEnergy.App.Backend.DataTypes;
public class Error
{
[PrimaryKey, AutoIncrement]
public Int64 Id { get; set; }
public Int64 InstallationId { get; set; }
public String ErrorDescription { get; set; } = null!;
public DateTime CreatedAt { get; set; }
public String DeviceCreatedTheError { get; set; } = null!;
public Boolean Seen { get; set; }
}

View File

@ -5,7 +5,6 @@ namespace InnovEnergy.App.Backend.DataTypes.Methods;
public static class FolderMethods public static class FolderMethods
{ {
public static IEnumerable<User> UsersWithAccess(this Folder folder) public static IEnumerable<User> UsersWithAccess(this Folder folder)
{ {
var direct = folder.UsersWithDirectAccess(); var direct = folder.UsersWithDirectAccess();

View File

@ -141,7 +141,7 @@ public static class InstallationMethods
foreach (var orderNumber in installation.OrderNumbers.Split(",")) foreach (var orderNumber in installation.OrderNumbers.Split(","))
{ {
var rel = relations.FirstOrDefault(i => i.OrderNumber == orderNumber); var rel = relations.FirstOrDefault(i => i.OrderNumber == orderNumber);
if ( rel != null) relations.Remove(rel); if ( rel != null) {relations.Remove(rel); continue;}
var o2I = new OrderNumber2Installation var o2I = new OrderNumber2Installation
{ {
OrderNumber = orderNumber, OrderNumber = orderNumber,

View File

@ -5,7 +5,7 @@ namespace InnovEnergy.App.Backend.DataTypes;
public abstract partial class TreeNode public abstract partial class TreeNode
{ {
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public virtual Int64 Id { get; set; } public Int64 Id { get; set; }
public virtual String Name { get; set; } = ""; // overridden by User (unique) public virtual String Name { get; set; } = ""; // overridden by User (unique)
public String Information { get; set; } = ""; // unstructured random info public String Information { get; set; } = ""; // unstructured random info

View File

@ -20,6 +20,11 @@ public static partial class Db
return Insert(installation); return Insert(installation);
} }
public static Boolean Create(Error error)
{
return Insert(error);
}
public static Boolean Create(Folder folder) public static Boolean Create(Folder folder)
{ {
return Insert(folder); return Insert(folder);
@ -49,4 +54,30 @@ public static partial class Db
{ {
return Insert(o2i); return Insert(o2i);
} }
public static void HandleError(Error newError,int installationId)
{
//Find the total number of errors for this installation
var totalErrors = Errors.Count(error => error.InstallationId == installationId);
//If there are 100 errors, remove the one with the oldest timestamp
if (totalErrors == 100)
{
var oldestError =
Errors.Where(error => error.InstallationId == installationId)
.OrderBy(error => error.CreatedAt)
.FirstOrDefault();
//Remove the old error
Delete(oldestError);
//Add the new error
Create(newError);
}
else
{
Console.WriteLine("---------------Added the new Error to the database-----------------");
Create(newError);
}
}
} }

View File

@ -15,21 +15,36 @@ namespace InnovEnergy.App.Backend.Database;
public static partial class Db public static partial class Db
{ {
// internal const String DbPath = "./db.sqlite"; private static SQLiteConnection Connection { get; } = InitConnection();
private static SQLiteConnection Connection { get; } = ((Func<SQLiteConnection>)(() => private static SQLiteConnection InitConnection()
{ {
var latestDb = new DirectoryInfo(@"DbBackups").GetFiles() var latestDb = new DirectoryInfo("DbBackups")
.OrderBy(f => f.LastWriteTime) .GetFiles()
.Last().Name; .OrderBy(f => f.LastWriteTime)
.Last().Name;
var fileConnection = new SQLiteConnection("DbBackups/"+latestDb); //This is the file connection from the DbBackups folder
var fileConnection = new SQLiteConnection("DbBackups/" + latestDb);
//Create a table if it does not exist
fileConnection.CreateTable<User>();
fileConnection.CreateTable<Installation>();
fileConnection.CreateTable<Folder>();
fileConnection.CreateTable<FolderAccess>();
fileConnection.CreateTable<InstallationAccess>();
fileConnection.CreateTable<Session>();
fileConnection.CreateTable<OrderNumber2Installation>();
fileConnection.CreateTable<Error>();
return CopyDbToMemory(fileConnection);
}
Console.Out.Write(latestDb); private static SQLiteConnection CopyDbToMemory(SQLiteConnection fileConnection)
{
var memoryConnection = new SQLiteConnection(":memory:"); var memoryConnection = new SQLiteConnection(":memory:");
// fileConnection.Backup(memoryConnection.DatabasePath); //Create a table if it does not exist in main memory
memoryConnection.CreateTable<User>(); memoryConnection.CreateTable<User>();
memoryConnection.CreateTable<Installation>(); memoryConnection.CreateTable<Installation>();
memoryConnection.CreateTable<Folder>(); memoryConnection.CreateTable<Folder>();
@ -37,18 +52,21 @@ public static partial class Db
memoryConnection.CreateTable<InstallationAccess>(); memoryConnection.CreateTable<InstallationAccess>();
memoryConnection.CreateTable<Session>(); memoryConnection.CreateTable<Session>();
memoryConnection.CreateTable<OrderNumber2Installation>(); memoryConnection.CreateTable<OrderNumber2Installation>();
memoryConnection.CreateTable<Error>();
fileConnection.Table<Session> ().ForEach(memoryConnection.Insert);
fileConnection.Table<Folder> ().ForEach(memoryConnection.Insert);
fileConnection.Table<Installation> ().ForEach(memoryConnection.Insert);
fileConnection.Table<User> ().ForEach(memoryConnection.Insert);
fileConnection.Table<FolderAccess> ().ForEach(memoryConnection.Insert);
fileConnection.Table<InstallationAccess> ().ForEach(memoryConnection.Insert);
fileConnection.Table<OrderNumber2Installation>().ForEach(memoryConnection.Insert);
//Copy all the existing tables from the disk to main memory
fileConnection.Table<Session>().ForEach(memoryConnection.Insert);
fileConnection.Table<Folder>().ForEach(memoryConnection.Insert);
fileConnection.Table<Installation>().ForEach(memoryConnection.Insert);
fileConnection.Table<User>().ForEach(memoryConnection.Insert);
fileConnection.Table<FolderAccess>().ForEach(memoryConnection.Insert);
fileConnection.Table<InstallationAccess>().ForEach(memoryConnection.Insert);
fileConnection.Table<OrderNumber2Installation>().ForEach(memoryConnection.Insert);
fileConnection.Table<Error>().ForEach(memoryConnection.Insert);
return memoryConnection; return memoryConnection;
}))(); }
public static void BackupDatabase() public static void BackupDatabase()
{ {
var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite"; var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite";
@ -62,17 +80,16 @@ public static partial class Db
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>(); public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>(); public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
public static TableQuery<OrderNumber2Installation> OrderNumber2Installation => Connection.Table<OrderNumber2Installation>(); public static TableQuery<OrderNumber2Installation> OrderNumber2Installation => Connection.Table<OrderNumber2Installation>();
public static TableQuery<Error> Errors => Connection.Table<Error>();
public static void Init()
{ public static void Init(){
// used to force static constructor // used to force static constructor
//Since this class is static, we call Init method from the Program.cs to initialize all the fields of the class
} }
//This is the constructor of the class
static Db() static Db()
{ {
// on startup create/migrate tables
Connection.RunInTransaction(() => Connection.RunInTransaction(() =>
{ {
Connection.CreateTable<User>(); Connection.CreateTable<User>();
@ -82,6 +99,7 @@ public static partial class Db
Connection.CreateTable<InstallationAccess>(); Connection.CreateTable<InstallationAccess>();
Connection.CreateTable<Session>(); Connection.CreateTable<Session>();
Connection.CreateTable<OrderNumber2Installation>(); Connection.CreateTable<OrderNumber2Installation>();
Connection.CreateTable<Error>();
}); });
Observable.Interval(TimeSpan.FromHours(0.5)) Observable.Interval(TimeSpan.FromHours(0.5))

View File

@ -34,6 +34,20 @@ public static partial class Db
return delete>0; return delete>0;
} }
} }
public static Boolean Delete(Error errorToDelete)
{
var deleteSuccess = RunTransaction(DeleteError);
if (deleteSuccess)
BackupDatabase();
return deleteSuccess;
Boolean DeleteError()
{
return Errors.Delete(error => error.Id == errorToDelete.Id) >0;
}
}
public static Boolean Delete(Installation installation) public static Boolean Delete(Installation installation)
{ {

View File

@ -33,7 +33,4 @@ public static partial class Db
return Update(obj: user); return Update(obj: user);
} }
} }

View File

@ -1,6 +1,9 @@
public class StatusMessage public class StatusMessage
{ {
public required int InstallationId { get; init; } public required int InstallationId { get; init; }
public required int Status { get; init; } public required int Status { get; init; }
public DateTime CreatedAt { get; set; }
public String Error { get; init; } = null!;
public String DeviceCreatedTheError { get; init; } = null!;
} }

View File

@ -5,6 +5,7 @@ using System.Net.WebSockets;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Events; using RabbitMQ.Client.Events;
@ -19,7 +20,6 @@ public static class WebsocketManager
public static void InformInstallationsToSubscribeToRabbitMq() public static void InformInstallationsToSubscribeToRabbitMq()
{ {
//var installationIps = new List<string> { "10.2.3.115" };
var installationIps = Db.Installations.Select(inst => inst.VpnIp).ToList(); var installationIps = Db.Installations.Select(inst => inst.VpnIp).ToList();
Console.WriteLine("Count is "+installationIps.Count); Console.WriteLine("Count is "+installationIps.Count);
var maxRetransmissions = 2; var maxRetransmissions = 2;
@ -83,13 +83,31 @@ public static class WebsocketManager
lock (InstallationConnections) lock (InstallationConnections)
{ {
// Process the received message //Consumer received a message
if (receivedStatusMessage != null) if (receivedStatusMessage != null)
{ {
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status); Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
Console.WriteLine("----------------------------------------------"); Console.WriteLine("----------------------------------------------");
Console.WriteLine("Update installation connection table"); Console.WriteLine("Update installation connection table");
var installationId = receivedStatusMessage.InstallationId; var installationId = receivedStatusMessage.InstallationId;
//This is an error message
if (receivedStatusMessage.Status==2)
{
Console.WriteLine("-----------------------New error-----------------------");
Error newError = new Error
{
InstallationId = receivedStatusMessage.InstallationId,
ErrorDescription = receivedStatusMessage.Error,
CreatedAt = receivedStatusMessage.CreatedAt,
DeviceCreatedTheError = receivedStatusMessage.DeviceCreatedTheError,
Seen = false
};
//Create a new error and add it to the database
Db.HandleError(newError,receivedStatusMessage.InstallationId);
}
if (!InstallationConnections.ContainsKey(installationId)) if (!InstallationConnections.ContainsKey(installationId))
{ {

View File

@ -3,6 +3,10 @@ namespace InnovEnergy.App.SaliMax.MiddlewareClasses;
public class StatusMessage public class StatusMessage
{ {
public required int InstallationId { get; init; }
public required int Status { get; init; } public required int InstallationId { get; init; }
public required int Status { get; init; }
public DateTime CreatedAt { get; set; }
public String Error { get; set; } = null!;
public String DeviceCreatedTheError { get; set; } = null!;
} }

View File

@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Net; using System.Net;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
@ -265,10 +266,14 @@ internal static class Program
{ {
_subscribeToQueueForTheFirstTime = true; _subscribeToQueueForTheFirstTime = true;
SubscribeToQueue(currentSalimaxState, s3Bucket); SubscribeToQueue(currentSalimaxState, s3Bucket);
if (_subscribedToQueue && currentSalimaxState != _prevSalimaxState)
{
_prevSalimaxState = currentSalimaxState;
}
} }
//If already subscribed to the queue and the status has been changed, update the queue //If already subscribed to the queue and the status has been changed, update the queue
if (_subscribedToQueue && currentSalimaxState != _prevSalimaxState) else if (_subscribedToQueue && currentSalimaxState != _prevSalimaxState)
{ {
_prevSalimaxState = currentSalimaxState; _prevSalimaxState = currentSalimaxState;
if (s3Bucket != null) if (s3Bucket != null)
@ -343,12 +348,18 @@ internal static class Program
private static void InformMiddleware(String? bucket, int status) private static void InformMiddleware(String? bucket, int status)
{ {
int.TryParse(bucket[0].ToString(), out var installationId); int.TryParse(bucket[0].ToString(), out var installationId);
var jsonObject = new StatusMessage var jsonObject = new StatusMessage
{ {
InstallationId = installationId, InstallationId = installationId,
Status = status Status = status,
}; };
if (status == 2)
{
jsonObject.CreatedAt = DateTime.Now;
jsonObject.Error = "Battery Temperature High";
jsonObject.DeviceCreatedTheError = "Battery/1";
}
var message = JsonSerializer.Serialize(jsonObject); var message = JsonSerializer.Serialize(jsonObject);
var body = Encoding.UTF8.GetBytes(message); var body = Encoding.UTF8.GetBytes(message);

View File

@ -1,3 +1,4 @@
using System.ComponentModel;
using InnovEnergy.Lib.Devices.Trumpf.SystemControl; using InnovEnergy.Lib.Devices.Trumpf.SystemControl;
using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes; using InnovEnergy.Lib.Devices.Trumpf.TruConvertAc.DataTypes;
using InnovEnergy.Lib.Units.Composite; using InnovEnergy.Lib.Units.Composite;
@ -18,6 +19,9 @@ public class AcDcDevicesRecord
public SystemControlRegisters? SystemControl { get; } public SystemControlRegisters? SystemControl { get; }
public IReadOnlyList<AcDcRecord> Devices { get; init; } public IReadOnlyList<AcDcRecord> Devices { get; init; }
//public IEnumerable<AlarmMessage> Alarms => new []{AlarmMessage.BatteryOvervoltage}; //Devices.SelectMany(d => d.Status.Alarms).Distinct();
//public IEnumerable<WarningMessage> Warnings => new []{WarningMessage.TempDerating}; //Devices.SelectMany(d => d.Status.Warnings).Distinct();
public IEnumerable<AlarmMessage> Alarms => Devices.SelectMany(d => d.Status.Alarms).Distinct(); public IEnumerable<AlarmMessage> Alarms => Devices.SelectMany(d => d.Status.Alarms).Distinct();
public IEnumerable<WarningMessage> Warnings => Devices.SelectMany(d => d.Status.Warnings).Distinct(); public IEnumerable<WarningMessage> Warnings => Devices.SelectMany(d => d.Status.Warnings).Distinct();

View File

@ -10,7 +10,7 @@ public enum WarningMessage : UInt16
{ {
NoWarning = 00000, NoWarning = 00000,
/*
// these warnings are not official (not in the manual), and they seem to collide with the DCDC warnings // these warnings are not official (not in the manual), and they seem to collide with the DCDC warnings
// so I commented them // so I commented them
@ -28,5 +28,5 @@ public enum WarningMessage : UInt16
RuntimeEeprom = 11023, //AC-DC module warning RuntimeEeprom = 11023, //AC-DC module warning
Overcurrent = 11024 //Overcurrent handling is active Overcurrent = 11024 //Overcurrent handling is active
*/
} }

View File

@ -1,13 +1,13 @@
import axios from 'axios'; import axios from 'axios';
export const axiosConfigWithoutToken = axios.create({ export const axiosConfigWithoutToken = axios.create({
baseURL: 'https://monitor.innov.energy/api' //baseURL: 'https://monitor.innov.energy/api'
// baseURL: 'http://127.0.0.1:7087/api' baseURL: 'http://127.0.0.1:7087/api'
}); });
const axiosConfig = axios.create({ const axiosConfig = axios.create({
baseURL: 'https://monitor.innov.energy/api' //baseURL: 'https://monitor.innov.energy/api'
//baseURL: 'http://127.0.0.1:7087/api' baseURL: 'http://127.0.0.1:7087/api'
}); });
axiosConfig.defaults.params = {}; axiosConfig.defaults.params = {};

View File

@ -28,7 +28,6 @@ import {
extractValues, extractValues,
TopologyValues TopologyValues
} from 'src/content/dashboards/Log/graph.util'; } from 'src/content/dashboards/Log/graph.util';
import { Notification } from 'src/interfaces/S3Types';
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider'; import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
import Topology from '../Topology/Topology'; import Topology from '../Topology/Topology';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
@ -62,8 +61,6 @@ function Installation(props: singleInstallationProps) {
deleteInstallation deleteInstallation
} = installationContext; } = installationContext;
const [warnings, setWarnings] = useState<Notification[]>([]);
const [errors, setErrors] = useState<Notification[]>([]);
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false); const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
const webSocketsContext = useContext(WebSocketContext); const webSocketsContext = useContext(WebSocketContext);
const { getStatus } = webSocketsContext; const { getStatus } = webSocketsContext;
@ -153,9 +150,6 @@ function Installation(props: singleInstallationProps) {
return; return;
} }
const newWarnings: Notification[] = [];
const newErrors: Notification[] = [];
if ( if (
res === FetchResult.notAvailable || res === FetchResult.notAvailable ||
res === FetchResult.tryLater res === FetchResult.tryLater
@ -681,9 +675,8 @@ function Installation(props: singleInstallationProps) {
{currentTab === 'live' && <Topology values={values}></Topology>} {currentTab === 'live' && <Topology values={values}></Topology>}
{currentTab === 'log' && ( {currentTab === 'log' && (
<Log <Log
warnings={warnings}
errors={errors}
errorLoadingS3Data={errorLoadingS3Data} errorLoadingS3Data={errorLoadingS3Data}
id={props.current_installation.id}
></Log> ></Log>
)} )}
</Grid> </Grid>

View File

@ -1,4 +1,4 @@
import React from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { import {
Alert, Alert,
Card, Card,
@ -14,26 +14,49 @@ import {
TableRow, TableRow,
useTheme useTheme
} from '@mui/material'; } from '@mui/material';
import WarningIcon from '@mui/icons-material/Warning';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { Notification } from 'src/interfaces/S3Types';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import ErrorIcon from '@mui/icons-material/Error'; import ErrorIcon from '@mui/icons-material/Error';
import axiosConfig from '../../../Resources/axiosConfig';
import { AxiosError, AxiosResponse } from 'axios/index';
import routes from '../../../Resources/routes.json';
import { useNavigate } from 'react-router-dom';
import { TokenContext } from '../../../contexts/tokenContext';
import { ErrorMessage } from '../../../interfaces/S3Types';
interface LogProps { interface LogProps {
warnings: Notification[];
errors: Notification[];
errorLoadingS3Data: boolean; errorLoadingS3Data: boolean;
id: number;
} }
function Log(props: LogProps) { function Log(props: LogProps) {
const theme = useTheme(); const theme = useTheme();
//const [warnings, setWarnings] = useState<Notification[]>([]);
const [errors, setErrors] = useState<ErrorMessage[]>([]);
const navigate = useNavigate();
const tokencontext = useContext(TokenContext);
const { removeToken } = tokencontext;
useEffect(() => {
axiosConfig
.get(`/GetAllErrorsForInstallation?id=${props.id}`)
.then((res: AxiosResponse<ErrorMessage[]>) => {
setErrors(res.data);
})
.catch((err: AxiosError) => {
if (err.response && err.response.status == 401) {
removeToken();
navigate(routes.login);
}
});
}, []);
return ( return (
<Container maxWidth="xl"> <Container maxWidth="xl">
<Grid container> <Grid container>
<Grid item xs={12} md={12}> <Grid item xs={12} md={12}>
{(props.errors.length > 0 || props.warnings.length > 0) && ( {/* IT SHOULD BE {(errors.length > 0 || props.warnings.length > 0) && (*/}
{errors.length > 0 && (
<Card sx={{ marginTop: '10px' }}> <Card sx={{ marginTop: '10px' }}>
<Divider /> <Divider />
<TableContainer> <TableContainer>
@ -43,25 +66,26 @@ function Log(props: LogProps) {
<TableCell> <TableCell>
<FormattedMessage id="type" defaultMessage="Type" /> <FormattedMessage id="type" defaultMessage="Type" />
</TableCell> </TableCell>
<TableCell>
<FormattedMessage id="device" defaultMessage="Device" />
</TableCell>
<TableCell> <TableCell>
<FormattedMessage <FormattedMessage
id="description" id="description"
defaultMessage="Description" defaultMessage="Description"
/> />
</TableCell> </TableCell>
<TableCell>
<FormattedMessage id="device" defaultMessage="Device" />
</TableCell>
<TableCell> <TableCell>
<FormattedMessage id="date" defaultMessage="Date" /> <FormattedMessage id="date" defaultMessage="Date" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<FormattedMessage id="time" defaultMessage="Time" /> <FormattedMessage id="seen" defaultMessage="Seen" />
</TableCell> </TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{props.errors.map((error, index) => { {errors.map((error, index) => {
return ( return (
<TableRow hover key={index}> <TableRow hover key={index}>
<TableCell> <TableCell>
@ -82,9 +106,9 @@ function Log(props: LogProps) {
color="text.primary" color="text.primary"
gutterBottom gutterBottom
noWrap noWrap
sx={{ marginTop: '5px' }} sx={{ marginTop: '5px', marginLeft: '-40px' }}
> >
{error.device} {error.errorDescription}
</Typography> </Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
@ -96,7 +120,7 @@ function Log(props: LogProps) {
noWrap noWrap
sx={{ marginTop: '5px' }} sx={{ marginTop: '5px' }}
> >
{error.description} {error.deviceCreatedTheError}
</Typography> </Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
@ -106,9 +130,9 @@ function Log(props: LogProps) {
color="text.primary" color="text.primary"
gutterBottom gutterBottom
noWrap noWrap
sx={{ marginTop: '5px' }} sx={{ marginTop: '5px', marginLeft: '-40px' }}
> >
{error.date} {error.createdAt}
</Typography> </Typography>
</TableCell> </TableCell>
<TableCell> <TableCell>
@ -118,87 +142,87 @@ function Log(props: LogProps) {
color="text.primary" color="text.primary"
gutterBottom gutterBottom
noWrap noWrap
sx={{ marginTop: '5px' }} sx={{ marginTop: '5px', marginLeft: '10px' }}
> >
{error.time} {error.seen == false ? 'No' : 'Yes'}
</Typography> </Typography>
</TableCell> </TableCell>
</TableRow> </TableRow>
); );
})} })}
{props.warnings.map((warning, index) => { {/*{props.warnings.map((warning, index) => {*/}
return ( {/* return (*/}
<TableRow hover key={index}> {/* <TableRow hover key={index}>*/}
<TableCell> {/* <TableCell>*/}
<WarningIcon {/* <WarningIcon*/}
sx={{ {/* sx={{*/}
color: '#ffc04d', {/* color: '#ffc04d',*/}
width: 25, {/* width: 25,*/}
height: 25, {/* height: 25,*/}
marginLeft: '5px', {/* marginLeft: '5px',*/}
marginTop: '8px' {/* marginTop: '8px'*/}
}} {/* }}*/}
/> {/* />*/}
</TableCell> {/* </TableCell>*/}
<TableCell> {/* <TableCell>*/}
<Typography {/* <Typography*/}
variant="body1" {/* variant="body1"*/}
fontWeight="bold" {/* fontWeight="bold"*/}
color="text.primary" {/* color="text.primary"*/}
gutterBottom {/* gutterBottom*/}
noWrap {/* noWrap*/}
sx={{ marginTop: '10px' }} {/* sx={{ marginTop: '10px' }}*/}
> {/* >*/}
{warning.device} {/* {warning.device}*/}
</Typography> {/* </Typography>*/}
</TableCell> {/* </TableCell>*/}
<TableCell> {/* <TableCell>*/}
<Typography {/* <Typography*/}
variant="body1" {/* variant="body1"*/}
fontWeight="bold" {/* fontWeight="bold"*/}
color="text.primary" {/* color="text.primary"*/}
gutterBottom {/* gutterBottom*/}
noWrap {/* noWrap*/}
sx={{ marginTop: '10px' }} {/* sx={{ marginTop: '10px' }}*/}
> {/* >*/}
{warning.description} {/* {warning.description}*/}
</Typography> {/* </Typography>*/}
</TableCell> {/* </TableCell>*/}
<TableCell> {/* <TableCell>*/}
<Typography {/* <Typography*/}
variant="body1" {/* variant="body1"*/}
fontWeight="bold" {/* fontWeight="bold"*/}
color="text.primary" {/* color="text.primary"*/}
gutterBottom {/* gutterBottom*/}
noWrap {/* noWrap*/}
sx={{ marginTop: '10px' }} {/* sx={{ marginTop: '10px' }}*/}
> {/* >*/}
{warning.date} {/* {warning.date}*/}
</Typography> {/* </Typography>*/}
</TableCell> {/* </TableCell>*/}
<TableCell> {/* <TableCell>*/}
<Typography {/* <Typography*/}
variant="body1" {/* variant="body1"*/}
fontWeight="bold" {/* fontWeight="bold"*/}
color="text.primary" {/* color="text.primary"*/}
gutterBottom {/* gutterBottom*/}
noWrap {/* noWrap*/}
sx={{ marginTop: '10px' }} {/* sx={{ marginTop: '10px' }}*/}
> {/* >*/}
{warning.time} {/* {warning.time}*/}
</Typography> {/* </Typography>*/}
</TableCell> {/* </TableCell>*/}
</TableRow> {/* </TableRow>*/}
); {/* );*/}
})} {/*})}*/}
</TableBody> </TableBody>
</Table> </Table>
</TableContainer> </TableContainer>
</Card> </Card>
)} )}
{!props.errorLoadingS3Data && props.errors.length == 0 && ( {!props.errorLoadingS3Data && errors.length == 0 && (
<Alert <Alert
severity="error" severity="error"
sx={{ sx={{
@ -242,27 +266,27 @@ function Log(props: LogProps) {
</Alert> </Alert>
)} )}
{!props.errorLoadingS3Data && props.warnings.length == 0 && ( {/*{!props.errorLoadingS3Data && props.warnings.length == 0 && (*/}
<Alert {/* <Alert*/}
severity="error" {/* severity="error"*/}
sx={{ {/* sx={{*/}
display: 'flex', {/* display: 'flex',*/}
alignItems: 'center', {/* alignItems: 'center',*/}
//marginBottom: '20px' {/* //marginBottom: '20px'*/}
marginTop: '20px' {/* marginTop: '20px'*/}
}} {/* }}*/}
> {/* >*/}
<FormattedMessage {/* <FormattedMessage*/}
id="nowarnings" {/* id="nowarnings"*/}
defaultMessage="There are no warnings" {/* defaultMessage="There are no warnings"*/}
/> {/* />*/}
<IconButton {/* <IconButton*/}
color="inherit" {/* color="inherit"*/}
size="small" {/* size="small"*/}
sx={{ marginLeft: '4px' }} {/* sx={{ marginLeft: '4px' }}*/}
></IconButton> {/* ></IconButton>*/}
</Alert> {/* </Alert>*/}
)} {/*)}*/}
</Grid> </Grid>
</Grid> </Grid>
</Container> </Container>

View File

@ -1,37 +0,0 @@
import { UnixTime } from '../../../dataCache/time';
import { I_S3Credentials } from '../../../interfaces/S3Types';
import { FetchResult } from '../../../dataCache/dataCache';
import { DataRecord } from '../../../dataCache/data';
import { S3Access } from '../../../dataCache/S3/S3Access';
import { parseCsv } from './graph.util';
export const fetchData = (
timestamp: UnixTime,
s3Credentials?: I_S3Credentials
): Promise<FetchResult<DataRecord>> => {
const s3Path = `${timestamp.ticks}.csv`;
if (s3Credentials && s3Credentials.s3Bucket) {
const s3Access = new S3Access(
s3Credentials.s3Bucket,
s3Credentials.s3Region,
s3Credentials.s3Provider,
s3Credentials.s3Key,
s3Credentials.s3Secret
);
return s3Access
.get(s3Path)
.then(async (r) => {
if (r.status === 404) {
return Promise.resolve(FetchResult.notAvailable);
} else if (r.status === 200) {
const text = await r.text();
return parseCsv(text);
} else {
return Promise.resolve(FetchResult.notAvailable);
}
})
.catch((e) => {
return Promise.resolve(FetchResult.tryLater);
});
}
};

View File

@ -6,9 +6,10 @@ export interface I_S3Credentials {
s3Bucket?: string; s3Bucket?: string;
} }
export interface Notification { export interface ErrorMessage {
device: string; installationId: number;
description: string; createdAt: Date;
date: string; errorDescription: string;
time: string; deviceCreatedTheError: string;
seen: boolean;
} }