Update backend and frontend with middleware functionality
This commit is contained in:
parent
71b4a1d2bd
commit
1155e1bc4d
|
@ -61,6 +61,7 @@ public class Controller : ControllerBase
|
|||
|
||||
if (session is null)
|
||||
{
|
||||
Console.WriteLine("------------------------------------Unauthorized user----------------------------------------------");
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
HttpContext.Abort();
|
||||
return;
|
||||
|
@ -68,6 +69,7 @@ public class Controller : ControllerBase
|
|||
|
||||
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
Console.WriteLine("------------------------------------Not a websocket request ----------------------------------------------");
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||
HttpContext.Abort();
|
||||
return;
|
||||
|
|
|
@ -8,7 +8,8 @@ public class Installation : TreeNode
|
|||
public String Region { get; set; } = "";
|
||||
public String Country { get; set; } = "";
|
||||
public String InstallationName { get; set; } = "";
|
||||
|
||||
public String VpnIp { get; set; } = "";
|
||||
|
||||
// TODO: make relation
|
||||
//public IReadOnlyList<String> OrderNumbers { get; set; } = Array.Empty<String>();
|
||||
// public String? OrderNumbers { get; set; } = "";
|
||||
|
@ -25,6 +26,7 @@ public class Installation : TreeNode
|
|||
public String ReadRoleId { get; set; } = "";
|
||||
public String WriteRoleId { get; set; } = "";
|
||||
|
||||
|
||||
[Ignore]
|
||||
public String OrderNumbers { get; set; }
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@ using System.Net;
|
|||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using Amazon.S3.Model;
|
||||
using InnovEnergy.App.Backend.Database;
|
||||
using InnovEnergy.Lib.Utils;
|
||||
|
||||
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||
|
||||
|
@ -244,17 +242,11 @@ public static class ExoCmd
|
|||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
public static async Task<Boolean> CreateBucket(this Installation installation)
|
||||
{
|
||||
var cors = new CORSConfiguration();
|
||||
cors.Rules.Add(new CORSRule());
|
||||
cors.Rules[0].AllowedHeaders = new List<string> { "*" };
|
||||
cors.Rules[0].AllowedOrigins = new List<string> { "*" };
|
||||
cors.Rules[0].AllowedMethods = new List<string> { "Get", "Head" };
|
||||
var s3Region = new S3Region($"https://{installation.S3Region}.{installation.S3Provider}", S3Creds!);
|
||||
var a = await s3Region.PutBucket(installation.BucketName());
|
||||
return a != null && await a.PutCors(cors);
|
||||
return await s3Region.PutBucket(installation.BucketName()) != null;
|
||||
}
|
||||
|
||||
public static async Task<Boolean> SendConfig(this Installation installation, String config)
|
||||
|
|
|
@ -99,11 +99,10 @@ public static class SessionMethods
|
|||
&& user.HasWriteAccess
|
||||
&& user.HasAccessToParentOf(installation)
|
||||
&& Db.Create(installation) // TODO: these two in a transaction
|
||||
&& installation.SetOrderNumbers()
|
||||
&& installation.SetOrderNumbers()
|
||||
&& Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id })
|
||||
&& await installation.CreateBucket()
|
||||
&& await installation.RenewS3Credentials();
|
||||
// generation of access _after_ generation of
|
||||
&& await installation.CreateBucket()
|
||||
&& await installation.RenewS3Credentials(); // generation of access _after_ generation of
|
||||
// bucket to prevent "zombie" access-rights.
|
||||
// This might ** us over if the creation of access rights fails,
|
||||
// as bucket-names are unique and bound to the installation id... -K
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Channels;
|
||||
using Hellang.Middleware.ProblemDetails;
|
||||
using InnovEnergy.App.Backend.Database;
|
||||
using InnovEnergy.App.Backend.Websockets;
|
||||
|
@ -5,6 +9,7 @@ using Microsoft.AspNetCore.HttpOverrides;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using InnovEnergy.Lib.Utils;
|
||||
using RabbitMQ.Client;
|
||||
|
||||
namespace InnovEnergy.App.Backend;
|
||||
|
||||
|
@ -16,6 +21,16 @@ public static class Program
|
|||
Db.Init();
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
string vpnServerIp = "194.182.190.208";
|
||||
//string vpnServerIp = "127.0.0.1";
|
||||
WebsocketManager.Factory = new ConnectionFactory { HostName = vpnServerIp};
|
||||
WebsocketManager.Connection = WebsocketManager.Factory.CreateConnection();
|
||||
WebsocketManager.Channel = WebsocketManager.Connection.CreateModel();
|
||||
Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages");
|
||||
WebsocketManager.Channel.QueueDeclare(queue: "statusQueue", durable: true, exclusive: false, autoDelete: false, arguments: null);
|
||||
|
||||
WebsocketManager.StartRabbitMqConsumer();
|
||||
Console.WriteLine("Queue declared");
|
||||
WebsocketManager.InformInstallationsToSubscribeToRabbitMq();
|
||||
|
||||
builder.Services.AddControllers();
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Net.Sockets;
|
|||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using InnovEnergy.App.Backend.Database;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
|
||||
|
@ -11,27 +12,30 @@ namespace InnovEnergy.App.Backend.Websockets;
|
|||
|
||||
public static class WebsocketManager
|
||||
{
|
||||
public static readonly Dictionary<int, InstallationInfo> InstallationConnections = new Dictionary<int, InstallationInfo>();
|
||||
private static ConnectionFactory _factory = null!;
|
||||
private static IConnection _connection = null!;
|
||||
private static IModel _channel = null!;
|
||||
public static Dictionary<int, InstallationInfo> InstallationConnections = new Dictionary<int, InstallationInfo>();
|
||||
public static ConnectionFactory Factory = null!;
|
||||
public static IConnection Connection = null!;
|
||||
public static IModel Channel = null!;
|
||||
|
||||
public static void InformInstallationsToSubscribeToRabbitMq()
|
||||
{
|
||||
var installationsIds = new List<int> { 1 };
|
||||
var installationIps = new List<string> { "10.2.3.115" };
|
||||
//var installationIps = new List<string> { "10.2.3.115" };
|
||||
var installationIps = Db.Installations.Select(inst => inst.VpnIp).ToList();
|
||||
Console.WriteLine("Count is "+installationIps.Count);
|
||||
var maxRetransmissions = 2;
|
||||
|
||||
StartRabbitMqConsumer();
|
||||
|
||||
|
||||
UdpClient udpClient = new UdpClient();
|
||||
udpClient.Client.ReceiveTimeout = 2000;
|
||||
int port = 9000;
|
||||
//Send a message to each installation and tell it to subscribe to the queue
|
||||
for (int i = 0; i < installationsIds.Count; i++)
|
||||
using (udpClient)
|
||||
{
|
||||
using (udpClient)
|
||||
for (int i = 0; i < installationIps.Count; i++)
|
||||
{
|
||||
if(installationIps[i]==""){continue;}
|
||||
Console.WriteLine("-----------------------------------------------------------");
|
||||
Console.WriteLine("Trying to reach installation with IP: " + installationIps[i]);
|
||||
//Try at most MAX_RETRANSMISSIONS times to reach an installation.
|
||||
for (int j = 0; j < maxRetransmissions; j++)
|
||||
{
|
||||
|
@ -46,7 +50,7 @@ public static class WebsocketManager
|
|||
{
|
||||
byte[] replyData = udpClient.Receive(ref remoteEndPoint);
|
||||
string replyMessage = Encoding.UTF8.GetString(replyData);
|
||||
Console.WriteLine("Received "+replyMessage +" from installation " + installationsIds[i]);
|
||||
Console.WriteLine("Received " + replyMessage + " from installation " + installationIps[i]);
|
||||
break;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
|
@ -63,83 +67,79 @@ public static class WebsocketManager
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("Start RabbitMQ Consumer");
|
||||
|
||||
}
|
||||
|
||||
public static void StartRabbitMqConsumer()
|
||||
public static async Task StartRabbitMqConsumer()
|
||||
{
|
||||
string vpnServerIp = "194.182.190.208";
|
||||
//string vpnServerIp = "127.0.0.1";
|
||||
_factory = new ConnectionFactory { HostName = vpnServerIp};
|
||||
_connection = _factory.CreateConnection();
|
||||
_channel = _connection.CreateModel();
|
||||
Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages");
|
||||
_channel.QueueDeclare(queue: "statusQueue", durable: false, exclusive: false, autoDelete: false, arguments: null);
|
||||
|
||||
var consumer = new EventingBasicConsumer(_channel);
|
||||
consumer.Received += (_, ea) => CallbackReceiveMessageFromQueue(ea);
|
||||
_channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
|
||||
}
|
||||
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
private static void CallbackReceiveMessageFromQueue(BasicDeliverEventArgs ea)
|
||||
{
|
||||
var body = ea.Body.ToArray();
|
||||
var message = Encoding.UTF8.GetString(body);
|
||||
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
|
||||
|
||||
lock (InstallationConnections)
|
||||
var consumer = new EventingBasicConsumer(Channel);
|
||||
consumer.Received += (_, ea) =>
|
||||
{
|
||||
// Process the received message
|
||||
if (receivedStatusMessage != null)
|
||||
var body = ea.Body.ToArray();
|
||||
var message = Encoding.UTF8.GetString(body);
|
||||
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
|
||||
|
||||
lock (InstallationConnections)
|
||||
{
|
||||
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
|
||||
Console.WriteLine("----------------------------------------------");
|
||||
Console.WriteLine("Update installation connection table");
|
||||
var installationId = receivedStatusMessage.InstallationId;
|
||||
|
||||
if (!InstallationConnections.ContainsKey(installationId))
|
||||
// Process the received message
|
||||
if (receivedStatusMessage != null)
|
||||
{
|
||||
Console.WriteLine("Create new empty list for installation: " + installationId);
|
||||
InstallationConnections[installationId] = new InstallationInfo
|
||||
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
|
||||
Console.WriteLine("----------------------------------------------");
|
||||
Console.WriteLine("Update installation connection table");
|
||||
var installationId = receivedStatusMessage.InstallationId;
|
||||
|
||||
if (!InstallationConnections.ContainsKey(installationId))
|
||||
{
|
||||
Status = receivedStatusMessage.Status
|
||||
};
|
||||
}
|
||||
|
||||
Console.WriteLine("----------------------------------------------");
|
||||
|
||||
foreach (var installationConnection in InstallationConnections)
|
||||
{
|
||||
if (installationConnection.Key == installationId && installationConnection.Value.Connections.Count > 0)
|
||||
{
|
||||
Console.WriteLine("Update all the connected websockets for installation " + installationId);
|
||||
installationConnection.Value.Status = receivedStatusMessage.Status;
|
||||
|
||||
var jsonObject = new
|
||||
Console.WriteLine("Create new empty list for installation: " + installationId);
|
||||
InstallationConnections[installationId] = new InstallationInfo
|
||||
{
|
||||
id = installationId,
|
||||
status = receivedStatusMessage.Status
|
||||
Status = receivedStatusMessage.Status
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
InstallationConnections[installationId].Status = receivedStatusMessage.Status;
|
||||
}
|
||||
|
||||
string jsonString = JsonSerializer.Serialize(jsonObject);
|
||||
byte[] dataToSend = Encoding.UTF8.GetBytes(jsonString);
|
||||
Console.WriteLine("----------------------------------------------");
|
||||
|
||||
foreach (var connection in installationConnection.Value.Connections)
|
||||
foreach (var installationConnection in InstallationConnections)
|
||||
{
|
||||
if (installationConnection.Key == installationId && installationConnection.Value.Connections.Count > 0)
|
||||
{
|
||||
connection.SendAsync(
|
||||
new ArraySegment<byte>(dataToSend, 0, dataToSend.Length),
|
||||
WebSocketMessageType.Text,
|
||||
true, // Indicates that this is the end of the message
|
||||
CancellationToken.None
|
||||
);
|
||||
Console.WriteLine("Update all the connected websockets for installation " + installationId);
|
||||
installationConnection.Value.Status = receivedStatusMessage.Status;
|
||||
|
||||
var jsonObject = new
|
||||
{
|
||||
id = installationId,
|
||||
status = receivedStatusMessage.Status
|
||||
};
|
||||
|
||||
string jsonString = JsonSerializer.Serialize(jsonObject);
|
||||
byte[] dataToSend = Encoding.UTF8.GetBytes(jsonString);
|
||||
|
||||
foreach (var connection in installationConnection.Value.Connections)
|
||||
{
|
||||
connection.SendAsync(
|
||||
new ArraySegment<byte>(dataToSend, 0, dataToSend.Length),
|
||||
WebSocketMessageType.Text,
|
||||
true, // Indicates that this is the end of the message
|
||||
CancellationToken.None
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
|
||||
}
|
||||
|
||||
|
||||
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||
public static async Task HandleWebSocketConnection(WebSocket currentWebSocket)
|
||||
{
|
||||
var buffer = new byte[4096];
|
||||
|
@ -149,13 +149,34 @@ public static class WebsocketManager
|
|||
{
|
||||
//Listen for incoming messages on this WebSocket
|
||||
var result = await currentWebSocket.ReceiveAsync(buffer, CancellationToken.None);
|
||||
Console.WriteLine("Received a new message from websocket");
|
||||
|
||||
if (result.MessageType != WebSocketMessageType.Text)
|
||||
continue;
|
||||
|
||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
var installationIds = JsonSerializer.Deserialize<int[]>(message);
|
||||
|
||||
//This is a ping message to keep the connection alive, reply with a pong
|
||||
if (installationIds[0] == -1)
|
||||
{
|
||||
var jsonObject = new
|
||||
{
|
||||
id = -1,
|
||||
status = -1
|
||||
};
|
||||
|
||||
var jsonString = JsonSerializer.Serialize(jsonObject);
|
||||
var dataToSend = Encoding.UTF8.GetBytes(jsonString);
|
||||
currentWebSocket.SendAsync(dataToSend,
|
||||
WebSocketMessageType.Text,
|
||||
true,
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Console.WriteLine("Received a new message from websocket");
|
||||
lock (InstallationConnections)
|
||||
{
|
||||
//Each front-end will send the list of the installations it wants to access
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
Prototype ie-entwicklung@10.2.3.115
|
||||
Salimax0001 ie-entwicklung@10.2.3.104
|
||||
Salimax0002 ie-entwicklung@10.2.4.29
|
||||
Salimax0003 ie-entwicklung@10.2.4.33
|
||||
Salimax0004 ie-entwicklung@10.2.4.32
|
||||
Prototype ie-entwicklung@10.2.3.115 Prototype
|
||||
Salimax0001 ie-entwicklung@10.2.3.104 Marti Technik (Bern)
|
||||
Salimax0002 ie-entwicklung@10.2.4.29 Weidmann Oberwil (ZG)
|
||||
Salimax0003 ie-entwicklung@10.2.4.33 Elektrotechnik Stefan GmbH
|
||||
Salimax0004 ie-entwicklung@10.2.4.32 Biohof Gubelmann (Walde)
|
||||
Salimax0004A ie-entwicklung@10.2.4.153
|
||||
Salimax0005 ie-entwicklung@10.2.4.36
|
||||
Salimax0006 ie-entwicklung@10.2.4.35
|
||||
Salimax0007 ie-entwicklung@10.2.4.154
|
||||
Salimax0008 ie-entwicklung@10.2.4.113
|
||||
Salimax0005 ie-entwicklung@10.2.4.36 Schreinerei Schönthal (Thun)
|
||||
Salimax0006 ie-entwicklung@10.2.4.35 Steakhouse Mettmenstetten
|
||||
Salimax0007 ie-entwicklung@10.2.4.154 LerchenhofHerr Twannberg
|
||||
Salimax0008 ie-entwicklung@10.2.4.113 Wittmann Kottingbrunn
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
"MinSoc": Number, 0 - 100 this is the minimum State of Charge that the batteries must not go below,
|
||||
"ForceCalibrationCharge": Boolean (true or false), A flag to force a calibration charge,
|
||||
"DisplayIndividualBatteries": Boolean (true or false), To display the indvidual batteries
|
||||
"PConstant": Number 0 - 1, P value of our controller.
|
||||
"GridSetPoint": Number in Watts, The set point of our controller.
|
||||
"BatterySelfDischargePower": Number, 200, this a physical measurement of the self discharging power.
|
||||
"HoldSocZone": Number, 1, This is magic number for the soft landing factor.
|
||||
"IslandMode": { // Dc Link Voltage in Island mode
|
||||
"AcDc": {
|
||||
"MaxDcLinkVoltage": Number, 810, Max Dc Link Voltage,
|
||||
"MinDcLinkVoltage": Number, 690, Min Dc Link Voltage,
|
||||
"ReferenceDcLinkVoltage": Number, 750, Reference Dc Link
|
||||
},
|
||||
"DcDc": {
|
||||
"LowerDcLinkVoltage": Number, 50, Lower Dc Link Window ,
|
||||
"ReferenceDcLinkVoltage": 750, reference Dc Link
|
||||
"UpperDcLinkVoltage": Number, 50, Upper Dc Link Window ,
|
||||
}
|
||||
},
|
||||
"GridTie": {// Dc Link Voltage in GrieTie mode
|
||||
"AcDc": {
|
||||
"MaxDcLinkVoltage":Number, 780, Max Dc Link Voltage,
|
||||
"MinDcLinkVoltage": Number, 690, Min Dc Link Voltage,
|
||||
"ReferenceDcLinkVoltage": Number, 750, Reference Dc Link
|
||||
},
|
||||
"DcDc": {
|
||||
"LowerDcLinkVoltage": Number, 20, Lower Dc Link Window ,
|
||||
"ReferenceDcLinkVoltage": 750, reference Dc Link
|
||||
"UpperDcLinkVoltage": Number, 20, Upper Dc Link Window ,
|
||||
}
|
||||
},
|
||||
"MaxBatteryChargingCurrent":Number, 0 - 210, Max Charging current by DcDc
|
||||
"MaxBatteryDischargingCurrent":Number, 0 - 210, Max Discharging current by DcDc
|
||||
"MaxDcPower": Number, 0 - 10000, Max Power exported/imported by DcDc (10000 is the maximum)
|
||||
"MaxChargeBatteryVoltage": Number, 57, Max Charging battery Voltage
|
||||
"MinDischargeBatteryVoltage": Number, 0, Min Charging Battery Voltage
|
||||
"Devices": { This is All Salimax devices (including offline ones)
|
||||
"RelaysIp": {
|
||||
"DeviceState": 1, // 0: is not present, 1: Present and Can be mesured, 2: Present but must be computed/calculted
|
||||
"Host": "10.0.1.1", // Ip @ of the device in the local network
|
||||
"Port": 502 // port
|
||||
},
|
||||
"GridMeterIp": {
|
||||
"DeviceState": 1,
|
||||
"Host": "10.0.4.1",
|
||||
"Port": 502
|
||||
},
|
||||
"PvOnAcGrid": {
|
||||
"DeviceState": 0, // If a device is not present
|
||||
"Host": "false", // this is not important
|
||||
"Port": 0 // this is not important
|
||||
},
|
||||
"LoadOnAcGrid": {
|
||||
"DeviceState": 2, // this is a computed device
|
||||
"Host": "true",
|
||||
"Port": 0
|
||||
},
|
||||
"PvOnAcIsland": {
|
||||
"DeviceState": 0,
|
||||
"Host": "false",
|
||||
"Port": 0
|
||||
},
|
||||
"IslandBusLoadMeterIp": {
|
||||
"DeviceState": 1,
|
||||
"Host": "10.0.4.2",
|
||||
"Port": 502
|
||||
},
|
||||
"TruConvertAcIp": {
|
||||
"DeviceState": 1,
|
||||
"Host": "10.0.2.1",
|
||||
"Port": 502
|
||||
},
|
||||
"PvOnDc": {
|
||||
"DeviceState": 1,
|
||||
"Host": "10.0.5.1",
|
||||
"Port": 502
|
||||
},
|
||||
"LoadOnDc": {
|
||||
"DeviceState": 0,
|
||||
"Host": "false",
|
||||
"Port": 0
|
||||
},
|
||||
"TruConvertDcIp": {
|
||||
"DeviceState": 1,
|
||||
"Host": "10.0.3.1",
|
||||
"Port": 502
|
||||
},
|
||||
"BatteryIp": {
|
||||
"DeviceState": 1,
|
||||
"Host": "localhost",
|
||||
"Port": 6855
|
||||
},
|
||||
"BatteryNodes": [ // this is a list of battery nodes
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6
|
||||
]
|
||||
},
|
||||
"S3": { // this is parameters of S3 Buckets and co
|
||||
"Bucket": "8-3e5b3069-214a-43ee-8d85-57d72000c19d",
|
||||
"Region": "sos-ch-dk-2",
|
||||
"Provider": "exo.io",
|
||||
"Key": "EXO502627299197f83e8b090f63",
|
||||
"Secret": "jUNYJL6B23WjndJnJlgJj4rc1i7uh981u5Aba5xdA5s",
|
||||
"ContentType": "text/plain; charset=utf-8",
|
||||
"Host": "8-3e5b3069-214a-43ee-8d85-57d72000c19d.sos-ch-dk-2.exo.io",
|
||||
"Url": "https://8-3e5b3069-214a-43ee-8d85-57d72000c19d.sos-ch-dk-2.exo.io"
|
||||
}
|
|
@ -26,7 +26,8 @@ public record InstallationToHtmlInterface(
|
|||
[Controller]
|
||||
public class Controller : ControllerBase
|
||||
{
|
||||
//Todo change me for updates
|
||||
|
||||
//Todo automatically grab newest version?
|
||||
private const String FirmwareVersion = "AF09";
|
||||
|
||||
|
||||
|
|
|
@ -17,8 +17,9 @@ import routes from 'src/Resources/routes.json';
|
|||
import './App.css';
|
||||
import ForgotPassword from './components/ForgotPassword';
|
||||
import { axiosConfigWithoutToken } from './Resources/axiosConfig';
|
||||
import UsersContextProvider from './contexts/UsersContextProvider';
|
||||
import InstallationsContextProvider from './contexts/InstallationsContextProvider';
|
||||
import AccessContextProvider from './contexts/AccessContextProvider';
|
||||
import WebSocketContextProvider from './contexts/WebSocketContextProvider';
|
||||
|
||||
function App() {
|
||||
const context = useContext(UserContext);
|
||||
|
@ -148,20 +149,22 @@ function App() {
|
|||
<Route
|
||||
path="/"
|
||||
element={
|
||||
<SidebarLayout
|
||||
language={language}
|
||||
onSelectLanguage={setLanguage}
|
||||
/>
|
||||
<WebSocketContextProvider>
|
||||
<SidebarLayout
|
||||
language={language}
|
||||
onSelectLanguage={setLanguage}
|
||||
/>
|
||||
</WebSocketContextProvider>
|
||||
}
|
||||
>
|
||||
<Route
|
||||
path={routes.installations + '*'}
|
||||
element={
|
||||
<UsersContextProvider>
|
||||
<AccessContextProvider>
|
||||
<InstallationsContextProvider>
|
||||
<InstallationTabs />
|
||||
</InstallationsContextProvider>
|
||||
</UsersContextProvider>
|
||||
</AccessContextProvider>
|
||||
}
|
||||
/>
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import axios from 'axios';
|
||||
|
||||
export const axiosConfigWithoutToken = axios.create({
|
||||
//baseURL: 'https://monitor.innov.energy/api'
|
||||
baseURL: 'https://stage.innov.energy/api'
|
||||
baseURL: 'https://monitor.innov.energy/api'
|
||||
// baseURL: 'http://127.0.0.1:7087/api'
|
||||
});
|
||||
|
||||
const axiosConfig = axios.create({
|
||||
//baseURL: 'https://monitor.innov.energy/api'
|
||||
baseURL: 'https://stage.innov.energy/api'
|
||||
baseURL: 'https://monitor.innov.energy/api'
|
||||
//baseURL: 'http://127.0.0.1:7087/api'
|
||||
});
|
||||
|
||||
axiosConfig.defaults.params = {};
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||
import Installation from './Installation';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import { LogContext } from 'src/contexts/LogContextProvider';
|
||||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
|
@ -25,8 +25,8 @@ interface FlatInstallationViewProps {
|
|||
|
||||
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||
const [isRowHovered, setHoveredRow] = useState(-1);
|
||||
const logContext = useContext(LogContext);
|
||||
const { getStatus } = logContext;
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
const { getStatus } = webSocketContext;
|
||||
const navigate = useNavigate();
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const installationId = parseInt(searchParams.get('installation'));
|
||||
|
@ -95,7 +95,6 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
installation.id === selectedInstallation;
|
||||
|
||||
const status = getStatus(installation.id);
|
||||
|
||||
const rowStyles =
|
||||
isRowHovered === installation.id
|
||||
? {
|
||||
|
|
|
@ -29,12 +29,13 @@ import {
|
|||
TopologyValues
|
||||
} from 'src/content/dashboards/Log/graph.util';
|
||||
import { Notification } from 'src/interfaces/S3Types';
|
||||
import { LogContext } from 'src/contexts/LogContextProvider';
|
||||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import Topology from '../Topology/Topology';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Overview from '../Overview/overview';
|
||||
import Configuration from '../Configuration/Configuration';
|
||||
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
|
||||
interface singleInstallationProps {
|
||||
current_installation?: I_Installation;
|
||||
|
@ -64,16 +65,17 @@ function Installation(props: singleInstallationProps) {
|
|||
const [warnings, setWarnings] = useState<Notification[]>([]);
|
||||
const [errors, setErrors] = useState<Notification[]>([]);
|
||||
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
|
||||
const logContext = useContext(LogContext);
|
||||
const { installationStatus, handleLogWarningOrError, getStatus } = logContext;
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { getStatus } = webSocketsContext;
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const installationId = parseInt(searchParams.get('installation'));
|
||||
const currentTab = searchParams.get('tab');
|
||||
|
||||
const [values, setValues] = useState<TopologyValues | null>(null);
|
||||
const [openModalDeleteInstallation, setOpenModalDeleteInstallation] =
|
||||
useState(false);
|
||||
|
||||
const status = getStatus(props.current_installation.id);
|
||||
|
||||
if (formValues == undefined) {
|
||||
return null;
|
||||
}
|
||||
|
@ -131,126 +133,67 @@ function Installation(props: singleInstallationProps) {
|
|||
const s3Credentials = { s3Bucket, ...S3data };
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
setFormValues(props.current_installation);
|
||||
setErrorLoadingS3Data(false);
|
||||
let disconnectedStatusResult = [];
|
||||
if (installationId == props.current_installation.id) {
|
||||
let isMounted = true;
|
||||
setFormValues(props.current_installation);
|
||||
setErrorLoadingS3Data(false);
|
||||
let disconnectedStatusResult = [];
|
||||
|
||||
const fetchDataPeriodically = async () => {
|
||||
const now = UnixTime.now().earlier(TimeSpan.fromSeconds(20));
|
||||
const date = now.toDate();
|
||||
const fetchDataPeriodically = async () => {
|
||||
const now = UnixTime.now().earlier(TimeSpan.fromSeconds(20));
|
||||
const date = now.toDate();
|
||||
|
||||
try {
|
||||
const res = await fetchData(now, s3Credentials);
|
||||
try {
|
||||
const res = await fetchData(now, s3Credentials);
|
||||
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newWarnings: Notification[] = [];
|
||||
const newErrors: Notification[] = [];
|
||||
|
||||
if (res === FetchResult.notAvailable || res === FetchResult.tryLater) {
|
||||
handleLogWarningOrError(props.current_installation.id, -1);
|
||||
|
||||
disconnectedStatusResult.unshift(-1);
|
||||
disconnectedStatusResult = disconnectedStatusResult.slice(0, 5);
|
||||
|
||||
let i = 0;
|
||||
//If at least one status value shows an error, then show error
|
||||
for (i; i < disconnectedStatusResult.length; i++) {
|
||||
if (disconnectedStatusResult[i] != -1) {
|
||||
break;
|
||||
}
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (i === disconnectedStatusResult.length) {
|
||||
setErrorLoadingS3Data(true);
|
||||
}
|
||||
} else {
|
||||
setErrorLoadingS3Data(false);
|
||||
setValues(
|
||||
extractValues({
|
||||
time: now,
|
||||
value: res
|
||||
})
|
||||
);
|
||||
const newWarnings: Notification[] = [];
|
||||
const newErrors: Notification[] = [];
|
||||
|
||||
for (const key in res) {
|
||||
if (
|
||||
(res.hasOwnProperty(key) &&
|
||||
key.includes('/Alarms') &&
|
||||
res[key].value !== '') ||
|
||||
(key.includes('/Warnings') && res[key].value !== '')
|
||||
) {
|
||||
if (key.includes('/Warnings')) {
|
||||
newWarnings.push({
|
||||
device: key.substring(1, key.lastIndexOf('/')),
|
||||
description: res[key].value.toString(),
|
||||
date:
|
||||
date.getFullYear() +
|
||||
'-' +
|
||||
date.getMonth() +
|
||||
'-' +
|
||||
date.getDay(),
|
||||
time:
|
||||
date.getHours() +
|
||||
':' +
|
||||
date.getMinutes() +
|
||||
':' +
|
||||
date.getSeconds()
|
||||
});
|
||||
} else if (key.includes('/Alarms')) {
|
||||
newErrors.push({
|
||||
device: key.substring(1, key.lastIndexOf('/')),
|
||||
description: res[key].value.toString(),
|
||||
date:
|
||||
date.getFullYear() +
|
||||
'-' +
|
||||
date.getMonth() +
|
||||
'-' +
|
||||
date.getDay(),
|
||||
time:
|
||||
date.getHours() +
|
||||
':' +
|
||||
date.getMinutes() +
|
||||
':' +
|
||||
date.getSeconds()
|
||||
});
|
||||
if (
|
||||
res === FetchResult.notAvailable ||
|
||||
res === FetchResult.tryLater
|
||||
) {
|
||||
disconnectedStatusResult.unshift(-1);
|
||||
disconnectedStatusResult = disconnectedStatusResult.slice(0, 5);
|
||||
|
||||
let i = 0;
|
||||
//If at least one status value shows an error, then show error
|
||||
for (i; i < disconnectedStatusResult.length; i++) {
|
||||
if (disconnectedStatusResult[i] != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setWarnings(newWarnings);
|
||||
setErrors(newErrors);
|
||||
|
||||
if (newErrors.length > 0) {
|
||||
handleLogWarningOrError(props.current_installation.id, 2);
|
||||
disconnectedStatusResult.unshift(2);
|
||||
disconnectedStatusResult = disconnectedStatusResult.slice(0, 5);
|
||||
} else if (newWarnings.length > 0) {
|
||||
disconnectedStatusResult.unshift(1);
|
||||
disconnectedStatusResult = disconnectedStatusResult.slice(0, 5);
|
||||
handleLogWarningOrError(props.current_installation.id, 1);
|
||||
if (i === disconnectedStatusResult.length) {
|
||||
setErrorLoadingS3Data(true);
|
||||
}
|
||||
} else {
|
||||
disconnectedStatusResult.unshift(0);
|
||||
disconnectedStatusResult = disconnectedStatusResult.slice(0, 5);
|
||||
handleLogWarningOrError(props.current_installation.id, 0);
|
||||
setErrorLoadingS3Data(false);
|
||||
setValues(
|
||||
extractValues({
|
||||
time: now,
|
||||
value: res
|
||||
})
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
setErrorLoadingS3Data(true);
|
||||
}
|
||||
} catch (err) {
|
||||
setErrorLoadingS3Data(true);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const interval = setInterval(fetchDataPeriodically, 2000);
|
||||
const interval = setInterval(fetchDataPeriodically, 2000);
|
||||
|
||||
// Cleanup function to cancel interval and update isMounted when unmounted
|
||||
return () => {
|
||||
isMounted = false;
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
// Cleanup function to cancel interval and update isMounted when unmounted
|
||||
return () => {
|
||||
isMounted = false;
|
||||
clearInterval(interval);
|
||||
};
|
||||
}
|
||||
}, [installationId]);
|
||||
|
||||
if (installationId == props.current_installation.id) {
|
||||
return (
|
||||
|
@ -354,6 +297,69 @@ function Installation(props: singleInstallationProps) {
|
|||
{props.current_installation.name}
|
||||
</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: '80px',
|
||||
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
|
||||
|
@ -482,6 +488,16 @@ function Installation(props: singleInstallationProps) {
|
|||
|
||||
{currentUser.hasWriteAccess && (
|
||||
<>
|
||||
<div>
|
||||
<TextField
|
||||
label="Vpn IP"
|
||||
name="VpnIp"
|
||||
value={formValues.vpnIp}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
label="S3 Write Key"
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
} from '@mui/material';
|
||||
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
||||
import FlatInstallationView from 'src/content/dashboards/Installations/FlatInstallationView';
|
||||
import LogContextProvider from '../../../contexts/LogContextProvider';
|
||||
import { I_Installation } from '../../../interfaces/InstallationTypes';
|
||||
|
||||
interface installationSearchProps {
|
||||
|
@ -59,9 +58,8 @@ function InstallationSearch(props: installationSearchProps) {
|
|||
</FormControl>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<LogContextProvider>
|
||||
<FlatInstallationView installations={filteredData} />
|
||||
</LogContextProvider>
|
||||
|
||||
<FlatInstallationView installations={filteredData} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@ import InstallationSearch from './InstallationSearch';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
import { UserContext } from '../../../contexts/userContext';
|
||||
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
||||
import LogContextProvider from '../../../contexts/LogContextProvider';
|
||||
import Installation from './Installation';
|
||||
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||
|
||||
function InstallationTabs() {
|
||||
const theme = useTheme();
|
||||
|
@ -34,6 +34,15 @@ function InstallationTabs() {
|
|||
const { installations, fetchAllInstallations } =
|
||||
useContext(InstallationsContext);
|
||||
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { socket, openSocket } = webSocketsContext;
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket && installations.length > 0) {
|
||||
openSocket(installations);
|
||||
}
|
||||
}, [installations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (installations.length === 0) {
|
||||
fetchAllInstallations();
|
||||
|
@ -315,12 +324,10 @@ function InstallationTabs() {
|
|||
element={
|
||||
<Grid item xs={12}>
|
||||
<Box p={4}>
|
||||
<LogContextProvider>
|
||||
<Installation
|
||||
current_installation={installations[0]}
|
||||
type="installation"
|
||||
></Installation>
|
||||
</LogContextProvider>
|
||||
<Installation
|
||||
current_installation={installations[0]}
|
||||
type="installation"
|
||||
></Installation>
|
||||
</Box>
|
||||
</Grid>
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import PersonRemoveIcon from '@mui/icons-material/PersonRemove';
|
|||
import PersonIcon from '@mui/icons-material/Person';
|
||||
import Button from '@mui/material/Button';
|
||||
import { Close as CloseIcon } from '@mui/icons-material';
|
||||
import { UsersContext } from 'src/contexts/UsersContextProvider';
|
||||
import { AccessContext } from 'src/contexts/AccessContextProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
|
@ -43,7 +42,6 @@ function Access(props: AccessProps) {
|
|||
const [isRowHovered, setHoveredRow] = useState(-1);
|
||||
const [directButtonPressed, setDirectButtonPressed] = useState(false);
|
||||
const [inheritedButtonPressed, setInheritedButtonPressed] = useState(false);
|
||||
const { availableUsers, fetchAvailableUsers } = useContext(UsersContext);
|
||||
const [selectedUsers, setSelectedUsers] = useState<string[]>([]);
|
||||
const [openFolder, setOpenFolder] = useState(false);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
|
@ -55,6 +53,8 @@ function Access(props: AccessProps) {
|
|||
|
||||
const accessContext = useContext(AccessContext);
|
||||
const {
|
||||
availableUsers,
|
||||
fetchAvailableUsers,
|
||||
usersWithDirectAccess,
|
||||
fetchUsersWithDirectAccessForResource,
|
||||
usersWithInheritedAccess,
|
||||
|
|
|
@ -125,7 +125,7 @@ function Overview(props: OverviewProps) {
|
|||
const timestamp = item.time.ticks * 1000;
|
||||
|
||||
const adjustedTimestamp = new Date(timestamp);
|
||||
adjustedTimestamp.setHours(adjustedTimestamp.getHours() + 2);
|
||||
adjustedTimestamp.setHours(adjustedTimestamp.getHours() + 1);
|
||||
|
||||
if (csvContent[path]) {
|
||||
const value = csvContent[path];
|
||||
|
|
|
@ -7,7 +7,7 @@ import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
|||
import Typography from '@mui/material/Typography';
|
||||
import { makeStyles } from '@mui/styles';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import { LogContext } from 'src/contexts/LogContextProvider';
|
||||
import { WebSocketContext } from 'src/contexts/WebSocketContextProvider';
|
||||
import routes from 'src/Resources/routes.json';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
|
@ -38,8 +38,8 @@ const useTreeItemStyles = makeStyles((theme) => ({
|
|||
function CustomTreeItem(props: CustomTreeItemProps) {
|
||||
const theme = useTheme();
|
||||
const classes = useTreeItemStyles();
|
||||
const logContext = useContext(LogContext);
|
||||
const { getStatus } = logContext;
|
||||
const webSocketContext = useContext(WebSocketContext);
|
||||
const { getStatus } = webSocketContext;
|
||||
const status = getStatus(props.node.id);
|
||||
const navigate = useNavigate();
|
||||
const [selected, setSelected] = useState(false);
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from '@mui/material';
|
||||
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
||||
import FlatUsersView from './FlatUsersView';
|
||||
import { UsersContext } from '../../../contexts/UsersContextProvider';
|
||||
import { AccessContext } from '../../../contexts/AccessContextProvider';
|
||||
import Button from '@mui/material/Button';
|
||||
import UserForm from './userForm';
|
||||
import { UserContext } from '../../../contexts/userContext';
|
||||
|
@ -17,7 +17,7 @@ import { FormattedMessage } from 'react-intl';
|
|||
function UsersSearch() {
|
||||
const theme = useTheme();
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const { availableUsers, fetchAvailableUsers } = useContext(UsersContext);
|
||||
const { availableUsers, fetchAvailableUsers } = useContext(AccessContext);
|
||||
const [filteredData, setFilteredData] = useState(availableUsers);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const context = useContext(UserContext);
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import Footer from 'src/components/Footer';
|
||||
import { Box, Container, Grid, useTheme } from '@mui/material';
|
||||
import UsersSearch from './UsersSearch';
|
||||
import UsersContextProvider from 'src/contexts/UsersContextProvider';
|
||||
import React from 'react';
|
||||
import AccessContextProvider from '../../../contexts/AccessContextProvider';
|
||||
|
||||
function Users() {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<>
|
||||
<UsersContextProvider>
|
||||
<AccessContextProvider>
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }}>
|
||||
<Grid item xs={12}>
|
||||
<Box p={4}>
|
||||
|
@ -18,7 +18,7 @@ function Users() {
|
|||
</Grid>
|
||||
</Container>
|
||||
<Footer />
|
||||
</UsersContextProvider>
|
||||
</AccessContextProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import {
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
interface AccessContextProviderProps {
|
||||
availableUsers: InnovEnergyUser[];
|
||||
fetchAvailableUsers: () => Promise<void>;
|
||||
usersWithDirectAccess: InnovEnergyUser[];
|
||||
fetchUsersWithDirectAccessForResource: (
|
||||
tempresourceType: string,
|
||||
|
@ -42,6 +44,10 @@ interface AccessContextProviderProps {
|
|||
}
|
||||
|
||||
export const AccessContext = createContext<AccessContextProviderProps>({
|
||||
availableUsers: [],
|
||||
fetchAvailableUsers: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
usersWithDirectAccess: [],
|
||||
fetchUsersWithDirectAccessForResource: () => Promise.resolve(),
|
||||
usersWithInheritedAccess: [],
|
||||
|
@ -67,6 +73,7 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
|||
const [usersWithDirectAccess, setUsersWithDirectAccess] = useState<
|
||||
InnovEnergyUser[]
|
||||
>([]);
|
||||
const [availableUsers, setAvailableUsers] = useState<InnovEnergyUser[]>([]);
|
||||
|
||||
const [usersWithInheritedAccess, setUsersWithInheritedAccess] = useState<
|
||||
I_UserWithInheritedAccess[]
|
||||
|
@ -120,6 +127,12 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
|||
[]
|
||||
);
|
||||
|
||||
const fetchAvailableUsers = async (): Promise<void> => {
|
||||
return axiosConfig.get('/GetAllChildUsers').then((res) => {
|
||||
setAvailableUsers(res.data);
|
||||
});
|
||||
};
|
||||
|
||||
const RevokeAccessFromResource = useCallback(
|
||||
async (
|
||||
resourceType: string,
|
||||
|
@ -179,6 +192,8 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
|||
return (
|
||||
<AccessContext.Provider
|
||||
value={{
|
||||
availableUsers,
|
||||
fetchAvailableUsers,
|
||||
usersWithDirectAccess,
|
||||
fetchUsersWithDirectAccessForResource,
|
||||
usersWithInheritedAccess,
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
import { createContext, ReactNode, useState } from 'react';
|
||||
|
||||
interface LogContextProviderProps {
|
||||
installationStatus: Record<number, number[]>;
|
||||
handleLogWarningOrError: (installation_id: number, value: number) => void;
|
||||
getStatus: (installationId: number) => number;
|
||||
}
|
||||
|
||||
export const LogContext = createContext<LogContextProviderProps | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
export const LogContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [installationStatus, setInstallationStatus] = useState<
|
||||
Record<number, number[]>
|
||||
>({});
|
||||
|
||||
const BUFFER_LENGTH = 5;
|
||||
const handleLogWarningOrError = (installation_id: number, value: number) => {
|
||||
setInstallationStatus((prevStatus) => {
|
||||
// Create a new object by spreading the previous state
|
||||
const newStatus = { ...prevStatus };
|
||||
|
||||
if (!newStatus.hasOwnProperty(installation_id)) {
|
||||
newStatus[installation_id] = [];
|
||||
}
|
||||
newStatus[installation_id].unshift(value);
|
||||
newStatus[installation_id] = newStatus[installation_id].slice(
|
||||
0,
|
||||
BUFFER_LENGTH
|
||||
);
|
||||
|
||||
return newStatus;
|
||||
});
|
||||
};
|
||||
|
||||
const getStatus = (installationId: number) => {
|
||||
let status;
|
||||
|
||||
if (!installationStatus.hasOwnProperty(installationId)) {
|
||||
status = -2;
|
||||
} else {
|
||||
let i = 0;
|
||||
//If at least one status value shows an error, then show error
|
||||
for (i; i < installationStatus[installationId].length; i++) {
|
||||
if (installationStatus[installationId][i] === 2) {
|
||||
status = 2;
|
||||
return status;
|
||||
}
|
||||
if (installationStatus[installationId][i] === 1) {
|
||||
status = 1;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
if (installationStatus[installationId][0] == -1) {
|
||||
let i = 0;
|
||||
for (i; i < installationStatus[installationId].length; i++) {
|
||||
if (installationStatus[installationId][i] != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i === installationStatus[installationId].length) {
|
||||
status = -1;
|
||||
}
|
||||
} else {
|
||||
status = installationStatus[installationId][0];
|
||||
}
|
||||
}
|
||||
return status;
|
||||
};
|
||||
|
||||
return (
|
||||
<LogContext.Provider
|
||||
value={{
|
||||
installationStatus,
|
||||
handleLogWarningOrError,
|
||||
getStatus
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</LogContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogContextProvider;
|
|
@ -1,114 +0,0 @@
|
|||
import { createContext, ReactNode, useCallback, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import axiosConfig from 'src/Resources/axiosConfig';
|
||||
import {
|
||||
I_UserWithInheritedAccess,
|
||||
InnovEnergyUser
|
||||
} from 'src/interfaces/UserTypes';
|
||||
|
||||
interface I_UsersContextProviderProps {
|
||||
directAccessUsers: InnovEnergyUser[];
|
||||
setDirectAccessUsers: (value: InnovEnergyUser[]) => void;
|
||||
inheritedAccessUsers: I_UserWithInheritedAccess[];
|
||||
setInheritedAccessUsers: (value: I_UserWithInheritedAccess[]) => void;
|
||||
availableUsers: InnovEnergyUser[];
|
||||
setAvailableUsers: (value: InnovEnergyUser[]) => void;
|
||||
getAvailableUsersForResource: () => InnovEnergyUser[];
|
||||
fetchUsersWithInheritedAccessForResource: () => Promise<void>;
|
||||
fetchUsersWithDirectAccessForResource: () => Promise<void>;
|
||||
fetchAvailableUsers: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const UsersContext = createContext<I_UsersContextProviderProps>({
|
||||
directAccessUsers: [],
|
||||
setDirectAccessUsers: () => {},
|
||||
inheritedAccessUsers: [],
|
||||
setInheritedAccessUsers: () => {},
|
||||
availableUsers: [],
|
||||
setAvailableUsers: () => {},
|
||||
getAvailableUsersForResource: () => {
|
||||
return [];
|
||||
},
|
||||
fetchUsersWithInheritedAccessForResource: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
fetchUsersWithDirectAccessForResource: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
fetchAvailableUsers: () => {
|
||||
return Promise.resolve();
|
||||
}
|
||||
});
|
||||
|
||||
const UsersContextProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [directAccessUsers, setDirectAccessUsers] = useState<InnovEnergyUser[]>(
|
||||
[]
|
||||
);
|
||||
const [inheritedAccessUsers, setInheritedAccessUsers] = useState<
|
||||
I_UserWithInheritedAccess[]
|
||||
>([]);
|
||||
const [availableUsers, setAvailableUsers] = useState<InnovEnergyUser[]>([]);
|
||||
|
||||
const currentType = 13;
|
||||
const { id } = useParams();
|
||||
|
||||
const fetchUsersWithAccess = useCallback(
|
||||
async (route: string, setState: (value: any) => void) => {
|
||||
axiosConfig.get(route + currentType, { params: { id } }).then((res) => {
|
||||
setState(res.data);
|
||||
});
|
||||
},
|
||||
[currentType, id]
|
||||
);
|
||||
|
||||
const fetchUsersWithInheritedAccessForResource = useCallback(async () => {
|
||||
fetchUsersWithAccess(
|
||||
'/GetUsersWithInheritedAccessTo',
|
||||
setInheritedAccessUsers
|
||||
);
|
||||
}, [fetchUsersWithAccess]);
|
||||
|
||||
const fetchUsersWithDirectAccessForResource = useCallback(async () => {
|
||||
fetchUsersWithAccess('/GetUsersWithDirectAccessTo', setDirectAccessUsers);
|
||||
}, [fetchUsersWithAccess]);
|
||||
|
||||
const getAvailableUsersForResource = () => {
|
||||
const inheritedUsers = inheritedAccessUsers.map(
|
||||
(inheritedAccessUser) => inheritedAccessUser.user
|
||||
);
|
||||
const allUsersWithAccess = [...inheritedUsers, ...directAccessUsers];
|
||||
return availableUsers.filter(
|
||||
(availableUser) =>
|
||||
!allUsersWithAccess.find(
|
||||
(userWithAccess) => availableUser.id === userWithAccess.id
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const fetchAvailableUsers = async (): Promise<void> => {
|
||||
return axiosConfig.get('/GetAllChildUsers').then((res) => {
|
||||
setAvailableUsers(res.data);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<UsersContext.Provider
|
||||
value={{
|
||||
directAccessUsers,
|
||||
setDirectAccessUsers,
|
||||
inheritedAccessUsers,
|
||||
setInheritedAccessUsers,
|
||||
availableUsers,
|
||||
setAvailableUsers,
|
||||
getAvailableUsersForResource,
|
||||
fetchUsersWithInheritedAccessForResource,
|
||||
fetchUsersWithDirectAccessForResource,
|
||||
fetchAvailableUsers
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</UsersContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default UsersContextProvider;
|
|
@ -10,6 +10,7 @@ export interface I_Installation extends I_S3Credentials {
|
|||
region: string;
|
||||
country: string;
|
||||
installationName: string;
|
||||
vpnIp: string;
|
||||
orderNumbers: string[] | string;
|
||||
lat: number;
|
||||
long: number;
|
||||
|
|
|
@ -18,6 +18,7 @@ import { FormattedMessage } from 'react-intl';
|
|||
import { TokenContext } from 'src/contexts/tokenContext';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import routes from 'src/Resources/routes.json';
|
||||
import { WebSocketContext } from '../../../../contexts/WebSocketContextProvider';
|
||||
|
||||
const UserBoxButton = styled(Button)(
|
||||
({ theme }) => `
|
||||
|
@ -63,8 +64,11 @@ function HeaderUserbox() {
|
|||
const tokencontext = useContext(TokenContext);
|
||||
const { token, setNewToken, removeToken } = tokencontext;
|
||||
const navigate = useNavigate();
|
||||
const webSocketsContext = useContext(WebSocketContext);
|
||||
const { closeSocket } = webSocketsContext;
|
||||
|
||||
const handleSubmit = () => {
|
||||
closeSocket();
|
||||
axiosConfig.post('/Logout').then(() => {
|
||||
removeToken();
|
||||
removeUser();
|
||||
|
|
Loading…
Reference in New Issue