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)
|
if (session is null)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("------------------------------------Unauthorized user----------------------------------------------");
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||||
HttpContext.Abort();
|
HttpContext.Abort();
|
||||||
return;
|
return;
|
||||||
|
@ -68,6 +69,7 @@ public class Controller : ControllerBase
|
||||||
|
|
||||||
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("------------------------------------Not a websocket request ----------------------------------------------");
|
||||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||||
HttpContext.Abort();
|
HttpContext.Abort();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -8,7 +8,8 @@ public class Installation : TreeNode
|
||||||
public String Region { get; set; } = "";
|
public String Region { get; set; } = "";
|
||||||
public String Country { get; set; } = "";
|
public String Country { get; set; } = "";
|
||||||
public String InstallationName { get; set; } = "";
|
public String InstallationName { get; set; } = "";
|
||||||
|
public String VpnIp { get; set; } = "";
|
||||||
|
|
||||||
// TODO: make relation
|
// TODO: make relation
|
||||||
//public IReadOnlyList<String> OrderNumbers { get; set; } = Array.Empty<String>();
|
//public IReadOnlyList<String> OrderNumbers { get; set; } = Array.Empty<String>();
|
||||||
// public String? OrderNumbers { get; set; } = "";
|
// public String? OrderNumbers { get; set; } = "";
|
||||||
|
@ -25,6 +26,7 @@ public class Installation : TreeNode
|
||||||
public String ReadRoleId { get; set; } = "";
|
public String ReadRoleId { get; set; } = "";
|
||||||
public String WriteRoleId { get; set; } = "";
|
public String WriteRoleId { get; set; } = "";
|
||||||
|
|
||||||
|
|
||||||
[Ignore]
|
[Ignore]
|
||||||
public String OrderNumbers { get; set; }
|
public String OrderNumbers { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,7 @@ using System.Net;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
using Amazon.S3.Model;
|
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
@ -244,17 +242,11 @@ public static class ExoCmd
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Boolean> CreateBucket(this Installation installation)
|
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 s3Region = new S3Region($"https://{installation.S3Region}.{installation.S3Provider}", S3Creds!);
|
||||||
var a = await s3Region.PutBucket(installation.BucketName());
|
return await s3Region.PutBucket(installation.BucketName()) != null;
|
||||||
return a != null && await a.PutCors(cors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Boolean> SendConfig(this Installation installation, String config)
|
public static async Task<Boolean> SendConfig(this Installation installation, String config)
|
||||||
|
|
|
@ -99,11 +99,10 @@ public static class SessionMethods
|
||||||
&& user.HasWriteAccess
|
&& user.HasWriteAccess
|
||||||
&& user.HasAccessToParentOf(installation)
|
&& user.HasAccessToParentOf(installation)
|
||||||
&& Db.Create(installation) // TODO: these two in a transaction
|
&& Db.Create(installation) // TODO: these two in a transaction
|
||||||
&& installation.SetOrderNumbers()
|
&& installation.SetOrderNumbers()
|
||||||
&& Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id })
|
&& Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id })
|
||||||
&& await installation.CreateBucket()
|
&& await installation.CreateBucket()
|
||||||
&& await installation.RenewS3Credentials();
|
&& await installation.RenewS3Credentials(); // generation of access _after_ generation of
|
||||||
// generation of access _after_ generation of
|
|
||||||
// bucket to prevent "zombie" access-rights.
|
// bucket to prevent "zombie" access-rights.
|
||||||
// This might ** us over if the creation of access rights fails,
|
// This might ** us over if the creation of access rights fails,
|
||||||
// as bucket-names are unique and bound to the installation id... -K
|
// 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 Hellang.Middleware.ProblemDetails;
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using InnovEnergy.App.Backend.Websockets;
|
using InnovEnergy.App.Backend.Websockets;
|
||||||
|
@ -5,6 +9,7 @@ using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend;
|
namespace InnovEnergy.App.Backend;
|
||||||
|
|
||||||
|
@ -16,6 +21,16 @@ public static class Program
|
||||||
Db.Init();
|
Db.Init();
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
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();
|
WebsocketManager.InformInstallationsToSubscribeToRabbitMq();
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.Net.Sockets;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
using RabbitMQ.Client;
|
using RabbitMQ.Client;
|
||||||
using RabbitMQ.Client.Events;
|
using RabbitMQ.Client.Events;
|
||||||
|
|
||||||
|
@ -11,27 +12,30 @@ namespace InnovEnergy.App.Backend.Websockets;
|
||||||
|
|
||||||
public static class WebsocketManager
|
public static class WebsocketManager
|
||||||
{
|
{
|
||||||
public static readonly Dictionary<int, InstallationInfo> InstallationConnections = new Dictionary<int, InstallationInfo>();
|
public static Dictionary<int, InstallationInfo> InstallationConnections = new Dictionary<int, InstallationInfo>();
|
||||||
private static ConnectionFactory _factory = null!;
|
public static ConnectionFactory Factory = null!;
|
||||||
private static IConnection _connection = null!;
|
public static IConnection Connection = null!;
|
||||||
private static IModel _channel = null!;
|
public static IModel Channel = null!;
|
||||||
|
|
||||||
public static void InformInstallationsToSubscribeToRabbitMq()
|
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;
|
var maxRetransmissions = 2;
|
||||||
|
|
||||||
StartRabbitMqConsumer();
|
|
||||||
|
|
||||||
UdpClient udpClient = new UdpClient();
|
UdpClient udpClient = new UdpClient();
|
||||||
udpClient.Client.ReceiveTimeout = 2000;
|
udpClient.Client.ReceiveTimeout = 2000;
|
||||||
int port = 9000;
|
int port = 9000;
|
||||||
//Send a message to each installation and tell it to subscribe to the queue
|
//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.
|
//Try at most MAX_RETRANSMISSIONS times to reach an installation.
|
||||||
for (int j = 0; j < maxRetransmissions; j++)
|
for (int j = 0; j < maxRetransmissions; j++)
|
||||||
{
|
{
|
||||||
|
@ -46,7 +50,7 @@ public static class WebsocketManager
|
||||||
{
|
{
|
||||||
byte[] replyData = udpClient.Receive(ref remoteEndPoint);
|
byte[] replyData = udpClient.Receive(ref remoteEndPoint);
|
||||||
string replyMessage = Encoding.UTF8.GetString(replyData);
|
string replyMessage = Encoding.UTF8.GetString(replyData);
|
||||||
Console.WriteLine("Received "+replyMessage +" from installation " + installationsIds[i]);
|
Console.WriteLine("Received " + replyMessage + " from installation " + installationIps[i]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
catch (SocketException ex)
|
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";
|
var consumer = new EventingBasicConsumer(Channel);
|
||||||
//string vpnServerIp = "127.0.0.1";
|
consumer.Received += (_, ea) =>
|
||||||
_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)
|
|
||||||
{
|
{
|
||||||
// Process the received message
|
var body = ea.Body.ToArray();
|
||||||
if (receivedStatusMessage != null)
|
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);
|
// Process the received message
|
||||||
Console.WriteLine("----------------------------------------------");
|
if (receivedStatusMessage != null)
|
||||||
Console.WriteLine("Update installation connection table");
|
|
||||||
var installationId = receivedStatusMessage.InstallationId;
|
|
||||||
|
|
||||||
if (!InstallationConnections.ContainsKey(installationId))
|
|
||||||
{
|
{
|
||||||
Console.WriteLine("Create new empty list for installation: " + installationId);
|
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
|
||||||
InstallationConnections[installationId] = new InstallationInfo
|
Console.WriteLine("----------------------------------------------");
|
||||||
|
Console.WriteLine("Update installation connection table");
|
||||||
|
var installationId = receivedStatusMessage.InstallationId;
|
||||||
|
|
||||||
|
if (!InstallationConnections.ContainsKey(installationId))
|
||||||
{
|
{
|
||||||
Status = receivedStatusMessage.Status
|
Console.WriteLine("Create new empty list for installation: " + installationId);
|
||||||
};
|
InstallationConnections[installationId] = new InstallationInfo
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
id = installationId,
|
Status = receivedStatusMessage.Status
|
||||||
status = receivedStatusMessage.Status
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InstallationConnections[installationId].Status = receivedStatusMessage.Status;
|
||||||
|
}
|
||||||
|
|
||||||
string jsonString = JsonSerializer.Serialize(jsonObject);
|
Console.WriteLine("----------------------------------------------");
|
||||||
byte[] dataToSend = Encoding.UTF8.GetBytes(jsonString);
|
|
||||||
|
|
||||||
foreach (var connection in installationConnection.Value.Connections)
|
foreach (var installationConnection in InstallationConnections)
|
||||||
|
{
|
||||||
|
if (installationConnection.Key == installationId && installationConnection.Value.Connections.Count > 0)
|
||||||
{
|
{
|
||||||
connection.SendAsync(
|
Console.WriteLine("Update all the connected websockets for installation " + installationId);
|
||||||
new ArraySegment<byte>(dataToSend, 0, dataToSend.Length),
|
installationConnection.Value.Status = receivedStatusMessage.Status;
|
||||||
WebSocketMessageType.Text,
|
|
||||||
true, // Indicates that this is the end of the message
|
var jsonObject = new
|
||||||
CancellationToken.None
|
{
|
||||||
);
|
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)
|
public static async Task HandleWebSocketConnection(WebSocket currentWebSocket)
|
||||||
{
|
{
|
||||||
var buffer = new byte[4096];
|
var buffer = new byte[4096];
|
||||||
|
@ -149,13 +149,34 @@ public static class WebsocketManager
|
||||||
{
|
{
|
||||||
//Listen for incoming messages on this WebSocket
|
//Listen for incoming messages on this WebSocket
|
||||||
var result = await currentWebSocket.ReceiveAsync(buffer, CancellationToken.None);
|
var result = await currentWebSocket.ReceiveAsync(buffer, CancellationToken.None);
|
||||||
Console.WriteLine("Received a new message from websocket");
|
|
||||||
if (result.MessageType != WebSocketMessageType.Text)
|
if (result.MessageType != WebSocketMessageType.Text)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||||
var installationIds = JsonSerializer.Deserialize<int[]>(message);
|
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)
|
lock (InstallationConnections)
|
||||||
{
|
{
|
||||||
//Each front-end will send the list of the installations it wants to access
|
//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
|
Prototype ie-entwicklung@10.2.3.115 Prototype
|
||||||
Salimax0001 ie-entwicklung@10.2.3.104
|
Salimax0001 ie-entwicklung@10.2.3.104 Marti Technik (Bern)
|
||||||
Salimax0002 ie-entwicklung@10.2.4.29
|
Salimax0002 ie-entwicklung@10.2.4.29 Weidmann Oberwil (ZG)
|
||||||
Salimax0003 ie-entwicklung@10.2.4.33
|
Salimax0003 ie-entwicklung@10.2.4.33 Elektrotechnik Stefan GmbH
|
||||||
Salimax0004 ie-entwicklung@10.2.4.32
|
Salimax0004 ie-entwicklung@10.2.4.32 Biohof Gubelmann (Walde)
|
||||||
Salimax0004A ie-entwicklung@10.2.4.153
|
Salimax0004A ie-entwicklung@10.2.4.153
|
||||||
Salimax0005 ie-entwicklung@10.2.4.36
|
Salimax0005 ie-entwicklung@10.2.4.36 Schreinerei Schönthal (Thun)
|
||||||
Salimax0006 ie-entwicklung@10.2.4.35
|
Salimax0006 ie-entwicklung@10.2.4.35 Steakhouse Mettmenstetten
|
||||||
Salimax0007 ie-entwicklung@10.2.4.154
|
Salimax0007 ie-entwicklung@10.2.4.154 LerchenhofHerr Twannberg
|
||||||
Salimax0008 ie-entwicklung@10.2.4.113
|
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]
|
[Controller]
|
||||||
public class Controller : ControllerBase
|
public class Controller : ControllerBase
|
||||||
{
|
{
|
||||||
//Todo change me for updates
|
|
||||||
|
//Todo automatically grab newest version?
|
||||||
private const String FirmwareVersion = "AF09";
|
private const String FirmwareVersion = "AF09";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,9 @@ import routes from 'src/Resources/routes.json';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import ForgotPassword from './components/ForgotPassword';
|
import ForgotPassword from './components/ForgotPassword';
|
||||||
import { axiosConfigWithoutToken } from './Resources/axiosConfig';
|
import { axiosConfigWithoutToken } from './Resources/axiosConfig';
|
||||||
import UsersContextProvider from './contexts/UsersContextProvider';
|
|
||||||
import InstallationsContextProvider from './contexts/InstallationsContextProvider';
|
import InstallationsContextProvider from './contexts/InstallationsContextProvider';
|
||||||
|
import AccessContextProvider from './contexts/AccessContextProvider';
|
||||||
|
import WebSocketContextProvider from './contexts/WebSocketContextProvider';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const context = useContext(UserContext);
|
const context = useContext(UserContext);
|
||||||
|
@ -148,20 +149,22 @@ function App() {
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path="/"
|
||||||
element={
|
element={
|
||||||
<SidebarLayout
|
<WebSocketContextProvider>
|
||||||
language={language}
|
<SidebarLayout
|
||||||
onSelectLanguage={setLanguage}
|
language={language}
|
||||||
/>
|
onSelectLanguage={setLanguage}
|
||||||
|
/>
|
||||||
|
</WebSocketContextProvider>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Route
|
<Route
|
||||||
path={routes.installations + '*'}
|
path={routes.installations + '*'}
|
||||||
element={
|
element={
|
||||||
<UsersContextProvider>
|
<AccessContextProvider>
|
||||||
<InstallationsContextProvider>
|
<InstallationsContextProvider>
|
||||||
<InstallationTabs />
|
<InstallationTabs />
|
||||||
</InstallationsContextProvider>
|
</InstallationsContextProvider>
|
||||||
</UsersContextProvider>
|
</AccessContextProvider>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
@ -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: 'https://stage.innov.energy/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: 'https://stage.innov.energy/api'
|
//baseURL: 'http://127.0.0.1:7087/api'
|
||||||
});
|
});
|
||||||
|
|
||||||
axiosConfig.defaults.params = {};
|
axiosConfig.defaults.params = {};
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||||
import Installation from './Installation';
|
import Installation from './Installation';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
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 { FormattedMessage } from 'react-intl';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ interface FlatInstallationViewProps {
|
||||||
|
|
||||||
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
const [isRowHovered, setHoveredRow] = useState(-1);
|
const [isRowHovered, setHoveredRow] = useState(-1);
|
||||||
const logContext = useContext(LogContext);
|
const webSocketContext = useContext(WebSocketContext);
|
||||||
const { getStatus } = logContext;
|
const { getStatus } = webSocketContext;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const searchParams = new URLSearchParams(location.search);
|
const searchParams = new URLSearchParams(location.search);
|
||||||
const installationId = parseInt(searchParams.get('installation'));
|
const installationId = parseInt(searchParams.get('installation'));
|
||||||
|
@ -95,7 +95,6 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
||||||
installation.id === selectedInstallation;
|
installation.id === selectedInstallation;
|
||||||
|
|
||||||
const status = getStatus(installation.id);
|
const status = getStatus(installation.id);
|
||||||
|
|
||||||
const rowStyles =
|
const rowStyles =
|
||||||
isRowHovered === installation.id
|
isRowHovered === installation.id
|
||||||
? {
|
? {
|
||||||
|
|
|
@ -29,12 +29,13 @@ import {
|
||||||
TopologyValues
|
TopologyValues
|
||||||
} from 'src/content/dashboards/Log/graph.util';
|
} from 'src/content/dashboards/Log/graph.util';
|
||||||
import { Notification } from 'src/interfaces/S3Types';
|
import { Notification } from 'src/interfaces/S3Types';
|
||||||
import { LogContext } from 'src/contexts/LogContextProvider';
|
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';
|
||||||
import Overview from '../Overview/overview';
|
import Overview from '../Overview/overview';
|
||||||
import Configuration from '../Configuration/Configuration';
|
import Configuration from '../Configuration/Configuration';
|
||||||
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
|
import { fetchData } from 'src/content/dashboards/Installations/fetchData';
|
||||||
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
|
|
||||||
interface singleInstallationProps {
|
interface singleInstallationProps {
|
||||||
current_installation?: I_Installation;
|
current_installation?: I_Installation;
|
||||||
|
@ -64,16 +65,17 @@ function Installation(props: singleInstallationProps) {
|
||||||
const [warnings, setWarnings] = useState<Notification[]>([]);
|
const [warnings, setWarnings] = useState<Notification[]>([]);
|
||||||
const [errors, setErrors] = useState<Notification[]>([]);
|
const [errors, setErrors] = useState<Notification[]>([]);
|
||||||
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
|
const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
|
||||||
const logContext = useContext(LogContext);
|
const webSocketsContext = useContext(WebSocketContext);
|
||||||
const { installationStatus, handleLogWarningOrError, getStatus } = logContext;
|
const { getStatus } = webSocketsContext;
|
||||||
const searchParams = new URLSearchParams(location.search);
|
const searchParams = new URLSearchParams(location.search);
|
||||||
const installationId = parseInt(searchParams.get('installation'));
|
const installationId = parseInt(searchParams.get('installation'));
|
||||||
const currentTab = searchParams.get('tab');
|
const currentTab = searchParams.get('tab');
|
||||||
|
|
||||||
const [values, setValues] = useState<TopologyValues | null>(null);
|
const [values, setValues] = useState<TopologyValues | null>(null);
|
||||||
const [openModalDeleteInstallation, setOpenModalDeleteInstallation] =
|
const [openModalDeleteInstallation, setOpenModalDeleteInstallation] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
|
||||||
|
const status = getStatus(props.current_installation.id);
|
||||||
|
|
||||||
if (formValues == undefined) {
|
if (formValues == undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -131,126 +133,67 @@ function Installation(props: singleInstallationProps) {
|
||||||
const s3Credentials = { s3Bucket, ...S3data };
|
const s3Credentials = { s3Bucket, ...S3data };
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
if (installationId == props.current_installation.id) {
|
||||||
setFormValues(props.current_installation);
|
let isMounted = true;
|
||||||
setErrorLoadingS3Data(false);
|
setFormValues(props.current_installation);
|
||||||
let disconnectedStatusResult = [];
|
setErrorLoadingS3Data(false);
|
||||||
|
let disconnectedStatusResult = [];
|
||||||
|
|
||||||
const fetchDataPeriodically = async () => {
|
const fetchDataPeriodically = async () => {
|
||||||
const now = UnixTime.now().earlier(TimeSpan.fromSeconds(20));
|
const now = UnixTime.now().earlier(TimeSpan.fromSeconds(20));
|
||||||
const date = now.toDate();
|
const date = now.toDate();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetchData(now, s3Credentials);
|
const res = await fetchData(now, s3Credentials);
|
||||||
|
|
||||||
if (!isMounted) {
|
if (!isMounted) {
|
||||||
return;
|
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 (i === disconnectedStatusResult.length) {
|
const newWarnings: Notification[] = [];
|
||||||
setErrorLoadingS3Data(true);
|
const newErrors: Notification[] = [];
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setErrorLoadingS3Data(false);
|
|
||||||
setValues(
|
|
||||||
extractValues({
|
|
||||||
time: now,
|
|
||||||
value: res
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const key in res) {
|
if (
|
||||||
if (
|
res === FetchResult.notAvailable ||
|
||||||
(res.hasOwnProperty(key) &&
|
res === FetchResult.tryLater
|
||||||
key.includes('/Alarms') &&
|
) {
|
||||||
res[key].value !== '') ||
|
disconnectedStatusResult.unshift(-1);
|
||||||
(key.includes('/Warnings') && res[key].value !== '')
|
disconnectedStatusResult = disconnectedStatusResult.slice(0, 5);
|
||||||
) {
|
|
||||||
if (key.includes('/Warnings')) {
|
let i = 0;
|
||||||
newWarnings.push({
|
//If at least one status value shows an error, then show error
|
||||||
device: key.substring(1, key.lastIndexOf('/')),
|
for (i; i < disconnectedStatusResult.length; i++) {
|
||||||
description: res[key].value.toString(),
|
if (disconnectedStatusResult[i] != -1) {
|
||||||
date:
|
break;
|
||||||
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()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
setWarnings(newWarnings);
|
if (i === disconnectedStatusResult.length) {
|
||||||
setErrors(newErrors);
|
setErrorLoadingS3Data(true);
|
||||||
|
}
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
disconnectedStatusResult.unshift(0);
|
setErrorLoadingS3Data(false);
|
||||||
disconnectedStatusResult = disconnectedStatusResult.slice(0, 5);
|
setValues(
|
||||||
handleLogWarningOrError(props.current_installation.id, 0);
|
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
|
// Cleanup function to cancel interval and update isMounted when unmounted
|
||||||
return () => {
|
return () => {
|
||||||
isMounted = false;
|
isMounted = false;
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
};
|
};
|
||||||
}, []);
|
}
|
||||||
|
}, [installationId]);
|
||||||
|
|
||||||
if (installationId == props.current_installation.id) {
|
if (installationId == props.current_installation.id) {
|
||||||
return (
|
return (
|
||||||
|
@ -354,6 +297,69 @@ function Installation(props: singleInstallationProps) {
|
||||||
{props.current_installation.name}
|
{props.current_installation.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</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">
|
<Card variant="outlined">
|
||||||
<Grid
|
<Grid
|
||||||
|
@ -482,6 +488,16 @@ function Installation(props: singleInstallationProps) {
|
||||||
|
|
||||||
{currentUser.hasWriteAccess && (
|
{currentUser.hasWriteAccess && (
|
||||||
<>
|
<>
|
||||||
|
<div>
|
||||||
|
<TextField
|
||||||
|
label="Vpn IP"
|
||||||
|
name="VpnIp"
|
||||||
|
value={formValues.vpnIp}
|
||||||
|
variant="outlined"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<TextField
|
<TextField
|
||||||
label="S3 Write Key"
|
label="S3 Write Key"
|
||||||
|
|
|
@ -8,7 +8,6 @@ import {
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
||||||
import FlatInstallationView from 'src/content/dashboards/Installations/FlatInstallationView';
|
import FlatInstallationView from 'src/content/dashboards/Installations/FlatInstallationView';
|
||||||
import LogContextProvider from '../../../contexts/LogContextProvider';
|
|
||||||
import { I_Installation } from '../../../interfaces/InstallationTypes';
|
import { I_Installation } from '../../../interfaces/InstallationTypes';
|
||||||
|
|
||||||
interface installationSearchProps {
|
interface installationSearchProps {
|
||||||
|
@ -59,9 +58,8 @@ function InstallationSearch(props: installationSearchProps) {
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<LogContextProvider>
|
|
||||||
<FlatInstallationView installations={filteredData} />
|
<FlatInstallationView installations={filteredData} />
|
||||||
</LogContextProvider>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ import InstallationSearch from './InstallationSearch';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { UserContext } from '../../../contexts/userContext';
|
import { UserContext } from '../../../contexts/userContext';
|
||||||
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
||||||
import LogContextProvider from '../../../contexts/LogContextProvider';
|
|
||||||
import Installation from './Installation';
|
import Installation from './Installation';
|
||||||
|
import { WebSocketContext } from '../../../contexts/WebSocketContextProvider';
|
||||||
|
|
||||||
function InstallationTabs() {
|
function InstallationTabs() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -34,6 +34,15 @@ function InstallationTabs() {
|
||||||
const { installations, fetchAllInstallations } =
|
const { installations, fetchAllInstallations } =
|
||||||
useContext(InstallationsContext);
|
useContext(InstallationsContext);
|
||||||
|
|
||||||
|
const webSocketsContext = useContext(WebSocketContext);
|
||||||
|
const { socket, openSocket } = webSocketsContext;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!socket && installations.length > 0) {
|
||||||
|
openSocket(installations);
|
||||||
|
}
|
||||||
|
}, [installations]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (installations.length === 0) {
|
if (installations.length === 0) {
|
||||||
fetchAllInstallations();
|
fetchAllInstallations();
|
||||||
|
@ -315,12 +324,10 @@ function InstallationTabs() {
|
||||||
element={
|
element={
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
<LogContextProvider>
|
<Installation
|
||||||
<Installation
|
current_installation={installations[0]}
|
||||||
current_installation={installations[0]}
|
type="installation"
|
||||||
type="installation"
|
></Installation>
|
||||||
></Installation>
|
|
||||||
</LogContextProvider>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import PersonRemoveIcon from '@mui/icons-material/PersonRemove';
|
||||||
import PersonIcon from '@mui/icons-material/Person';
|
import PersonIcon from '@mui/icons-material/Person';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import { Close as CloseIcon } from '@mui/icons-material';
|
import { Close as CloseIcon } from '@mui/icons-material';
|
||||||
import { UsersContext } from 'src/contexts/UsersContextProvider';
|
|
||||||
import { AccessContext } from 'src/contexts/AccessContextProvider';
|
import { AccessContext } from 'src/contexts/AccessContextProvider';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
@ -43,7 +42,6 @@ function Access(props: AccessProps) {
|
||||||
const [isRowHovered, setHoveredRow] = useState(-1);
|
const [isRowHovered, setHoveredRow] = useState(-1);
|
||||||
const [directButtonPressed, setDirectButtonPressed] = useState(false);
|
const [directButtonPressed, setDirectButtonPressed] = useState(false);
|
||||||
const [inheritedButtonPressed, setInheritedButtonPressed] = useState(false);
|
const [inheritedButtonPressed, setInheritedButtonPressed] = useState(false);
|
||||||
const { availableUsers, fetchAvailableUsers } = useContext(UsersContext);
|
|
||||||
const [selectedUsers, setSelectedUsers] = useState<string[]>([]);
|
const [selectedUsers, setSelectedUsers] = useState<string[]>([]);
|
||||||
const [openFolder, setOpenFolder] = useState(false);
|
const [openFolder, setOpenFolder] = useState(false);
|
||||||
const [openModal, setOpenModal] = useState(false);
|
const [openModal, setOpenModal] = useState(false);
|
||||||
|
@ -55,6 +53,8 @@ function Access(props: AccessProps) {
|
||||||
|
|
||||||
const accessContext = useContext(AccessContext);
|
const accessContext = useContext(AccessContext);
|
||||||
const {
|
const {
|
||||||
|
availableUsers,
|
||||||
|
fetchAvailableUsers,
|
||||||
usersWithDirectAccess,
|
usersWithDirectAccess,
|
||||||
fetchUsersWithDirectAccessForResource,
|
fetchUsersWithDirectAccessForResource,
|
||||||
usersWithInheritedAccess,
|
usersWithInheritedAccess,
|
||||||
|
|
|
@ -125,7 +125,7 @@ function Overview(props: OverviewProps) {
|
||||||
const timestamp = item.time.ticks * 1000;
|
const timestamp = item.time.ticks * 1000;
|
||||||
|
|
||||||
const adjustedTimestamp = new Date(timestamp);
|
const adjustedTimestamp = new Date(timestamp);
|
||||||
adjustedTimestamp.setHours(adjustedTimestamp.getHours() + 2);
|
adjustedTimestamp.setHours(adjustedTimestamp.getHours() + 1);
|
||||||
|
|
||||||
if (csvContent[path]) {
|
if (csvContent[path]) {
|
||||||
const value = csvContent[path];
|
const value = csvContent[path];
|
||||||
|
|
|
@ -7,7 +7,7 @@ import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { makeStyles } from '@mui/styles';
|
import { makeStyles } from '@mui/styles';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
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 routes from 'src/Resources/routes.json';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -38,8 +38,8 @@ const useTreeItemStyles = makeStyles((theme) => ({
|
||||||
function CustomTreeItem(props: CustomTreeItemProps) {
|
function CustomTreeItem(props: CustomTreeItemProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const classes = useTreeItemStyles();
|
const classes = useTreeItemStyles();
|
||||||
const logContext = useContext(LogContext);
|
const webSocketContext = useContext(WebSocketContext);
|
||||||
const { getStatus } = logContext;
|
const { getStatus } = webSocketContext;
|
||||||
const status = getStatus(props.node.id);
|
const status = getStatus(props.node.id);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [selected, setSelected] = useState(false);
|
const [selected, setSelected] = useState(false);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
||||||
import FlatUsersView from './FlatUsersView';
|
import FlatUsersView from './FlatUsersView';
|
||||||
import { UsersContext } from '../../../contexts/UsersContextProvider';
|
import { AccessContext } from '../../../contexts/AccessContextProvider';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import UserForm from './userForm';
|
import UserForm from './userForm';
|
||||||
import { UserContext } from '../../../contexts/userContext';
|
import { UserContext } from '../../../contexts/userContext';
|
||||||
|
@ -17,7 +17,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
function UsersSearch() {
|
function UsersSearch() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
const { availableUsers, fetchAvailableUsers } = useContext(UsersContext);
|
const { availableUsers, fetchAvailableUsers } = useContext(AccessContext);
|
||||||
const [filteredData, setFilteredData] = useState(availableUsers);
|
const [filteredData, setFilteredData] = useState(availableUsers);
|
||||||
const [openModal, setOpenModal] = useState(false);
|
const [openModal, setOpenModal] = useState(false);
|
||||||
const context = useContext(UserContext);
|
const context = useContext(UserContext);
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import Footer from 'src/components/Footer';
|
import Footer from 'src/components/Footer';
|
||||||
import { Box, Container, Grid, useTheme } from '@mui/material';
|
import { Box, Container, Grid, useTheme } from '@mui/material';
|
||||||
import UsersSearch from './UsersSearch';
|
import UsersSearch from './UsersSearch';
|
||||||
import UsersContextProvider from 'src/contexts/UsersContextProvider';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import AccessContextProvider from '../../../contexts/AccessContextProvider';
|
||||||
|
|
||||||
function Users() {
|
function Users() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<UsersContextProvider>
|
<AccessContextProvider>
|
||||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }}>
|
<Container maxWidth="xl" sx={{ marginTop: '20px' }}>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
|
@ -18,7 +18,7 @@ function Users() {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
<Footer />
|
<Footer />
|
||||||
</UsersContextProvider>
|
</AccessContextProvider>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ import {
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
interface AccessContextProviderProps {
|
interface AccessContextProviderProps {
|
||||||
|
availableUsers: InnovEnergyUser[];
|
||||||
|
fetchAvailableUsers: () => Promise<void>;
|
||||||
usersWithDirectAccess: InnovEnergyUser[];
|
usersWithDirectAccess: InnovEnergyUser[];
|
||||||
fetchUsersWithDirectAccessForResource: (
|
fetchUsersWithDirectAccessForResource: (
|
||||||
tempresourceType: string,
|
tempresourceType: string,
|
||||||
|
@ -42,6 +44,10 @@ interface AccessContextProviderProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AccessContext = createContext<AccessContextProviderProps>({
|
export const AccessContext = createContext<AccessContextProviderProps>({
|
||||||
|
availableUsers: [],
|
||||||
|
fetchAvailableUsers: () => {
|
||||||
|
return Promise.resolve();
|
||||||
|
},
|
||||||
usersWithDirectAccess: [],
|
usersWithDirectAccess: [],
|
||||||
fetchUsersWithDirectAccessForResource: () => Promise.resolve(),
|
fetchUsersWithDirectAccessForResource: () => Promise.resolve(),
|
||||||
usersWithInheritedAccess: [],
|
usersWithInheritedAccess: [],
|
||||||
|
@ -67,6 +73,7 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const [usersWithDirectAccess, setUsersWithDirectAccess] = useState<
|
const [usersWithDirectAccess, setUsersWithDirectAccess] = useState<
|
||||||
InnovEnergyUser[]
|
InnovEnergyUser[]
|
||||||
>([]);
|
>([]);
|
||||||
|
const [availableUsers, setAvailableUsers] = useState<InnovEnergyUser[]>([]);
|
||||||
|
|
||||||
const [usersWithInheritedAccess, setUsersWithInheritedAccess] = useState<
|
const [usersWithInheritedAccess, setUsersWithInheritedAccess] = useState<
|
||||||
I_UserWithInheritedAccess[]
|
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(
|
const RevokeAccessFromResource = useCallback(
|
||||||
async (
|
async (
|
||||||
resourceType: string,
|
resourceType: string,
|
||||||
|
@ -179,6 +192,8 @@ const AccessContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
<AccessContext.Provider
|
<AccessContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
availableUsers,
|
||||||
|
fetchAvailableUsers,
|
||||||
usersWithDirectAccess,
|
usersWithDirectAccess,
|
||||||
fetchUsersWithDirectAccessForResource,
|
fetchUsersWithDirectAccessForResource,
|
||||||
usersWithInheritedAccess,
|
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;
|
region: string;
|
||||||
country: string;
|
country: string;
|
||||||
installationName: string;
|
installationName: string;
|
||||||
|
vpnIp: string;
|
||||||
orderNumbers: string[] | string;
|
orderNumbers: string[] | string;
|
||||||
lat: number;
|
lat: number;
|
||||||
long: number;
|
long: number;
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import { TokenContext } from 'src/contexts/tokenContext';
|
import { TokenContext } from 'src/contexts/tokenContext';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import routes from 'src/Resources/routes.json';
|
import routes from 'src/Resources/routes.json';
|
||||||
|
import { WebSocketContext } from '../../../../contexts/WebSocketContextProvider';
|
||||||
|
|
||||||
const UserBoxButton = styled(Button)(
|
const UserBoxButton = styled(Button)(
|
||||||
({ theme }) => `
|
({ theme }) => `
|
||||||
|
@ -63,8 +64,11 @@ function HeaderUserbox() {
|
||||||
const tokencontext = useContext(TokenContext);
|
const tokencontext = useContext(TokenContext);
|
||||||
const { token, setNewToken, removeToken } = tokencontext;
|
const { token, setNewToken, removeToken } = tokencontext;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const webSocketsContext = useContext(WebSocketContext);
|
||||||
|
const { closeSocket } = webSocketsContext;
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
|
closeSocket();
|
||||||
axiosConfig.post('/Logout').then(() => {
|
axiosConfig.post('/Logout').then(() => {
|
||||||
removeToken();
|
removeToken();
|
||||||
removeUser();
|
removeUser();
|
||||||
|
|
Loading…
Reference in New Issue