Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
68d126f691
|
@ -67,6 +67,7 @@ public class Controller : ControllerBase
|
|||
[HttpGet(nameof(CreateWebSocket))]
|
||||
public async Task CreateWebSocket(Token authToken)
|
||||
{
|
||||
//Everytime a user logs in, this function is called
|
||||
var session = Db.GetSession(authToken)?.User;
|
||||
|
||||
if (session is null)
|
||||
|
@ -85,6 +86,8 @@ public class Controller : ControllerBase
|
|||
return;
|
||||
}
|
||||
|
||||
//Create a websocket and pass its descriptor to the HandleWebSocketConnection method.
|
||||
//This descriptor is returned to the frontend on the background
|
||||
var webSocketContext = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
var webSocket = webSocketContext;
|
||||
|
||||
|
@ -179,7 +182,7 @@ public class Controller : ControllerBase
|
|||
Int64 startTimestamp = Int64.Parse(start.ToString().Substring(0,5));
|
||||
Int64 endTimestamp = Int64.Parse(end.ToString().Substring(0,5));
|
||||
|
||||
if (installation.Product == 1)
|
||||
if (installation.Product == (int)ProductType.Salidomo)
|
||||
{
|
||||
|
||||
start = Int32.Parse(start.ToString().Substring(0, start.ToString().Length - 2));
|
||||
|
@ -190,7 +193,7 @@ public class Controller : ControllerBase
|
|||
|
||||
while (startTimestamp <= endTimestamp)
|
||||
{
|
||||
string bucketPath = installation.Product==0? "s3://"+installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/"+startTimestamp :
|
||||
string bucketPath = installation.Product==(int)ProductType.Salimax? "s3://"+installation.S3BucketId + "-3e5b3069-214a-43ee-8d85-57d72000c19d/"+startTimestamp :
|
||||
"s3://"+installation.S3BucketId + "-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e/"+startTimestamp;
|
||||
Console.WriteLine("Fetching data for "+startTimestamp);
|
||||
|
||||
|
@ -447,7 +450,7 @@ public class Controller : ControllerBase
|
|||
return Unauthorized();
|
||||
|
||||
return user
|
||||
.AccessibleInstallations(product:0)
|
||||
.AccessibleInstallations(product:(int)ProductType.Salimax)
|
||||
.Select(i => i.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(user).HideWriteKeyIfUserIsNotAdmin(user.UserType))
|
||||
.ToList();
|
||||
}
|
||||
|
@ -461,7 +464,20 @@ public class Controller : ControllerBase
|
|||
return Unauthorized();
|
||||
|
||||
return user
|
||||
.AccessibleInstallations(product:1)
|
||||
.AccessibleInstallations(product:(int)ProductType.Salidomo)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
[HttpGet(nameof(GetAllSodioHomeInstallations))]
|
||||
public ActionResult<IEnumerable<Installation>> GetAllSodioHomeInstallations(Token authToken)
|
||||
{
|
||||
var user = Db.GetSession(authToken)?.User;
|
||||
|
||||
if (user is null)
|
||||
return Unauthorized();
|
||||
|
||||
return user
|
||||
.AccessibleInstallations(product:(int)ProductType.SodioHome)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
@ -634,7 +650,7 @@ public class Controller : ControllerBase
|
|||
if (!session.Update(installation))
|
||||
return Unauthorized();
|
||||
|
||||
if (installation.Product == 0)
|
||||
if (installation.Product == (int)ProductType.Salimax)
|
||||
{
|
||||
return installation.FillOrderNumbers().HideParentIfUserHasNoAccessToParent(session!.User).HideWriteKeyIfUserIsNotAdmin(session.User.UserType);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,21 @@ using SQLite;
|
|||
|
||||
namespace InnovEnergy.App.Backend.DataTypes;
|
||||
|
||||
public enum ProductType
|
||||
{
|
||||
Salimax = 0,
|
||||
Salidomo = 1,
|
||||
SodioHome =2
|
||||
}
|
||||
|
||||
public enum StatusType
|
||||
{
|
||||
Offline = -1,
|
||||
Green = 0,
|
||||
Warning = 1,
|
||||
Alarm = 2
|
||||
}
|
||||
|
||||
public class Installation : TreeNode
|
||||
{
|
||||
//Each installation has 2 roles, a read role and a write role.
|
||||
|
@ -24,8 +39,9 @@ public class Installation : TreeNode
|
|||
public String WriteRoleId { get; set; } = "";
|
||||
public Boolean TestingMode { get; set; } = false;
|
||||
public int Status { get; set; } = -1;
|
||||
public int Product { get; set; } = 0;
|
||||
public int Product { get; set; } = (int)ProductType.Salimax;
|
||||
public int Device { get; set; } = 0;
|
||||
public string SerialNumber { get; set; } = "";
|
||||
|
||||
[Ignore]
|
||||
public String OrderNumbers { get; set; }
|
||||
|
|
|
@ -143,7 +143,7 @@ public static class ExoCmd
|
|||
{
|
||||
const String url = "https://api-ch-dk-2.exoscale.com/v2/iam-role";
|
||||
const String method = "iam-role";
|
||||
String rolename = installation.Product==0?Db.Installations.Count(f => f.Product == 0) + installation.Name:Db.Installations.Count(f => f.Product == 1) + installation.Name;
|
||||
String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name:Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name;
|
||||
|
||||
|
||||
var contentString = $$"""
|
||||
|
@ -316,7 +316,7 @@ public static class ExoCmd
|
|||
{
|
||||
const String url = "https://api-ch-dk-2.exoscale.com/v2/iam-role";
|
||||
const String method = "iam-role";
|
||||
String rolename = installation.Product==0?Db.Installations.Count(f => f.Product == 0) + installation.Name:Db.Installations.Count(f => f.Product == 1) + installation.Name;
|
||||
String rolename = installation.Product==(int)ProductType.Salimax?Db.Installations.Count(f => f.Product == (int)ProductType.Salimax) + installation.Name:Db.Installations.Count(f => f.Product == (int)ProductType.Salidomo) + installation.Name;
|
||||
|
||||
var contentString = $$"""
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@ public static class InstallationMethods
|
|||
|
||||
public static String BucketName(this Installation installation)
|
||||
{
|
||||
if (installation.Product == 0)
|
||||
if (installation.Product == (int)ProductType.Salimax)
|
||||
{
|
||||
return $"{installation.S3BucketId}-{BucketNameSalt}";
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public static class SessionMethods
|
|||
public static async Task RunScriptInBackground(this Session? session, String vpnIp, Int64 batteryNode,String version,Int64 product)
|
||||
{
|
||||
Console.WriteLine("-----------------------------------Start updating firmware-----------------------------------");
|
||||
string scriptPath = (product == 0)
|
||||
string scriptPath = (product == (int)ProductType.Salimax)
|
||||
? "/home/ubuntu/backend/uploadBatteryFw/update_firmware_Salimax.sh"
|
||||
: "/home/ubuntu/backend/uploadBatteryFw/update_firmware_Salidomo.sh";
|
||||
|
||||
|
@ -95,7 +95,7 @@ public static class SessionMethods
|
|||
public static async Task RunDownloadLogScript(this Session? session, String vpnIp, Int64 batteryNode,Int64 product)
|
||||
{
|
||||
Console.WriteLine("-----------------------------------Start downloading battery log-----------------------------------");
|
||||
string scriptPath = (product == 0)
|
||||
string scriptPath = (product == (int)ProductType.Salimax)
|
||||
? "/home/ubuntu/backend/downloadBatteryLog/download_bms_log_Salimax.sh"
|
||||
: "/home/ubuntu/backend/downloadBatteryLog/download_bms_log_Salidomo.sh";
|
||||
|
||||
|
@ -210,7 +210,7 @@ public static class SessionMethods
|
|||
|
||||
|
||||
//Salimax installation
|
||||
if (installation.Product == 0)
|
||||
if (installation.Product == (int)ProductType.Salimax)
|
||||
{
|
||||
return user is not null
|
||||
&& user.UserType != 0
|
||||
|
@ -223,7 +223,7 @@ public static class SessionMethods
|
|||
|
||||
}
|
||||
|
||||
if (installation.Product == 1)
|
||||
if (installation.Product == (int)ProductType.Salidomo)
|
||||
{
|
||||
return user is not null
|
||||
&& user.UserType != 0
|
||||
|
@ -232,6 +232,17 @@ public static class SessionMethods
|
|||
&& await installation.CreateBucket()
|
||||
&& await installation.RenewS3Credentials();
|
||||
}
|
||||
|
||||
if (installation.Product == (int)ProductType.SodioHome)
|
||||
{
|
||||
return user is not null
|
||||
&& user.UserType != 0
|
||||
&& user.HasAccessToParentOf(installation)
|
||||
&& Db.Create(installation);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -242,7 +253,7 @@ public static class SessionMethods
|
|||
|
||||
var original = Db.GetInstallationById(installation?.Id);
|
||||
//Salimax installation
|
||||
if (installation.Product == 0)
|
||||
if (installation.Product == (int)ProductType.Salimax)
|
||||
{
|
||||
|
||||
return user is not null
|
||||
|
@ -256,7 +267,7 @@ public static class SessionMethods
|
|||
.Apply(Db.Update);
|
||||
}
|
||||
|
||||
if (installation.Product==1)
|
||||
if (installation.Product==(int)ProductType.Salidomo)
|
||||
{
|
||||
return user is not null
|
||||
&& installation is not null
|
||||
|
|
|
@ -100,7 +100,7 @@ public static partial class Db
|
|||
Boolean DeleteInstallationAndItsDependencies()
|
||||
{
|
||||
InstallationAccess.Delete(i => i.InstallationId == installation.Id);
|
||||
if (installation.Product == 0)
|
||||
if (installation.Product == (int)ProductType.Salimax)
|
||||
{
|
||||
//For Salimax, delete the OrderNumber2Installation entries associated with this installation id.
|
||||
OrderNumber2Installation.Delete(i => i.InstallationId == installation.Id);
|
||||
|
|
|
@ -25,12 +25,11 @@ public static class Program
|
|||
Db.Init();
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
RabbitMqManager.InitializeEnvironment();
|
||||
RabbitMqManager.StartRabbitMqConsumer().SupressAwaitWarning();
|
||||
//RabbitMqManager.InitializeEnvironment();
|
||||
//RabbitMqManager.StartRabbitMqConsumer().SupressAwaitWarning();
|
||||
WebsocketManager.MonitorSalimaxInstallationTable().SupressAwaitWarning();
|
||||
WebsocketManager.MonitorSalidomoInstallationTable().SupressAwaitWarning();
|
||||
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddProblemDetails(setup =>
|
||||
{
|
||||
|
|
|
@ -14,6 +14,7 @@ public class Session : Relation<String, Int64>
|
|||
[Indexed] public DateTime LastSeen { get; set; }
|
||||
public Boolean AccessToSalimax { get; set; } = false;
|
||||
public Boolean AccessToSalidomo { get; set; } = false;
|
||||
public Boolean AccessToSodioHome { get; set; } = false;
|
||||
[Ignore] public Boolean Valid => DateTime.Now - LastSeen <=MaxAge ;
|
||||
|
||||
// Private backing field
|
||||
|
@ -43,8 +44,9 @@ public class Session : Relation<String, Int64>
|
|||
Token = CreateToken();
|
||||
UserId = user.Id;
|
||||
LastSeen = DateTime.Now;
|
||||
AccessToSalimax = user.AccessibleInstallations(product: 0).ToList().Count > 0;
|
||||
AccessToSalidomo = user.AccessibleInstallations(product: 1).ToList().Count > 0;
|
||||
AccessToSalimax = user.AccessibleInstallations(product: (int)ProductType.Salimax).ToList().Count > 0;
|
||||
AccessToSalidomo = user.AccessibleInstallations(product: (int)ProductType.Salidomo).ToList().Count > 0;
|
||||
AccessToSodioHome = user.AccessibleInstallations(product: (int)ProductType.SodioHome).ToList().Count > 0;
|
||||
}
|
||||
|
||||
private static String CreateToken()
|
||||
|
|
|
@ -10,16 +10,13 @@ namespace InnovEnergy.App.Backend.Websockets;
|
|||
|
||||
public static class RabbitMqManager
|
||||
{
|
||||
|
||||
public static ConnectionFactory Factory = null!;
|
||||
public static IConnection Connection = null!;
|
||||
public static IModel Channel = null!;
|
||||
|
||||
|
||||
//This function will be called from the Backend/Program.cs
|
||||
public static void InitializeEnvironment()
|
||||
{
|
||||
|
||||
//string vpnServerIp = "194.182.190.208";
|
||||
string vpnServerIp = "10.2.0.11";
|
||||
|
||||
//Subscribe to RabbitMq queue as a consumer
|
||||
|
@ -57,24 +54,19 @@ public static class RabbitMqManager
|
|||
{
|
||||
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == receivedStatusMessage.Product && f.S3BucketId == receivedStatusMessage.InstallationId);
|
||||
int installationId = (int)installation.Id;
|
||||
//Console.WriteLine("received a message from rabbitmq\n");
|
||||
|
||||
//This is a heartbit message, just update the timestamp for this installation.
|
||||
//There is no need to notify the corresponding front-ends.
|
||||
//Every 15 iterations(30 seconds), the installation sends a heartbit message to the queue
|
||||
//Every 15 iterations(30 seconds), the installation sends a heartbit message to the queue.
|
||||
if (receivedStatusMessage.Type == MessageType.Heartbit)
|
||||
{
|
||||
// if (installation.Product == 1 && installation.Device == 2)
|
||||
// {
|
||||
// Console.WriteLine("This is a heartbit message from installation: " + installationId + " Name of the file is " + receivedStatusMessage.Timestamp);
|
||||
// }
|
||||
//Do not do anything here, just for debugging purposes.
|
||||
}
|
||||
else
|
||||
{
|
||||
//Traverse the Warnings list, and store each of them to the database
|
||||
if (receivedStatusMessage.Warnings != null)
|
||||
{
|
||||
|
||||
foreach (var warning in receivedStatusMessage.Warnings)
|
||||
{
|
||||
Warning newWarning = new Warning
|
||||
|
@ -98,7 +90,7 @@ public static class RabbitMqManager
|
|||
{
|
||||
|
||||
string monitorLink;
|
||||
if (installation.Product == 0)
|
||||
if (installation.Product == (int)ProductType.Salimax)
|
||||
{
|
||||
monitorLink =
|
||||
$"https://monitor.innov.energy/installations/list/installation/{installation.S3BucketId}/batteryview";
|
||||
|
@ -140,7 +132,7 @@ public static class RabbitMqManager
|
|||
$"Error created date and time: {alarm.Date} {alarm.Time}\n"+
|
||||
$"\n"+
|
||||
$"Thank you for your great support:)";
|
||||
// Disable this function now
|
||||
//Disable this function now
|
||||
//Mailer.Send("InnovEnergy Support Team", recipient, subject, text);
|
||||
}
|
||||
//Create a new error and add it to the database
|
||||
|
@ -149,9 +141,9 @@ public static class RabbitMqManager
|
|||
}
|
||||
}
|
||||
|
||||
var prevStatus = 0;
|
||||
Int32 prevStatus;
|
||||
|
||||
//This installation id does not exist in our data structure, add it.
|
||||
//This installation id does not exist in our in-memory data structure, add it.
|
||||
if (!WebsocketManager.InstallationConnections.ContainsKey(installationId))
|
||||
{
|
||||
prevStatus = -2;
|
||||
|
@ -168,11 +160,6 @@ public static class RabbitMqManager
|
|||
prevStatus = WebsocketManager.InstallationConnections[installationId].Status;
|
||||
WebsocketManager.InstallationConnections[installationId].Status = receivedStatusMessage.Status;
|
||||
WebsocketManager.InstallationConnections[installationId].Timestamp = DateTime.Now;
|
||||
// if (installationId == 130)
|
||||
// {
|
||||
// Console.WriteLine("prevStatus " + prevStatus + " , new status is: " + receivedStatusMessage.Status + " and status is: " + receivedStatusMessage.Status);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
installation.Status = receivedStatusMessage.Status;
|
||||
|
@ -189,5 +176,4 @@ public static class RabbitMqManager
|
|||
};
|
||||
Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
|
||||
}
|
||||
|
||||
}
|
|
@ -13,8 +13,8 @@ public static class WebsocketManager
|
|||
{
|
||||
public static Dictionary<Int64, InstallationInfo> InstallationConnections = new Dictionary<Int64, InstallationInfo>();
|
||||
|
||||
//Every 2 minutes, check the timestamp of the latest received message for every installation.
|
||||
//If the difference between the two timestamps is more than two minutes, we consider this installation unavailable.
|
||||
//Every 1 minute, check the timestamp of the latest received message for every installation.
|
||||
//If the difference between the two timestamps is more than two minutes, we consider this Salimax installation unavailable.
|
||||
public static async Task MonitorSalimaxInstallationTable()
|
||||
{
|
||||
while (true){
|
||||
|
@ -22,15 +22,15 @@ public static class WebsocketManager
|
|||
Console.WriteLine("MONITOR SALIMAX INSTALLATIONS\n");
|
||||
foreach (var installationConnection in InstallationConnections){
|
||||
|
||||
if (installationConnection.Value.Product==0 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)){
|
||||
if (installationConnection.Value.Product==(int)ProductType.Salimax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)){
|
||||
|
||||
Console.WriteLine("Installation ID is "+installationConnection.Key);
|
||||
Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
|
||||
Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
|
||||
// Console.WriteLine("Installation ID is "+installationConnection.Key);
|
||||
// Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
|
||||
// Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
|
||||
|
||||
installationConnection.Value.Status = -1;
|
||||
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 0 && f.Id == installationConnection.Key);
|
||||
installation.Status = -1;
|
||||
installationConnection.Value.Status = (int)StatusType.Offline;
|
||||
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == (int)ProductType.Salimax && f.Id == installationConnection.Key);
|
||||
installation.Status = (int)StatusType.Offline;
|
||||
installation.Apply(Db.Update);
|
||||
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ public static class WebsocketManager
|
|||
}
|
||||
}
|
||||
|
||||
//Every 1 minute, check the timestamp of the latest received message for every installation.
|
||||
//If the difference between the two timestamps is more than 1 hour, we consider this Salidomo installation unavailable.
|
||||
public static async Task MonitorSalidomoInstallationTable()
|
||||
{
|
||||
while (true){
|
||||
|
@ -50,26 +52,17 @@ public static class WebsocketManager
|
|||
Console.WriteLine("MONITOR SALIDOMO INSTALLATIONS\n");
|
||||
foreach (var installationConnection in InstallationConnections){
|
||||
Console.WriteLine("Installation ID is "+installationConnection.Key);
|
||||
|
||||
|
||||
// if (installationConnection.Key == 104)
|
||||
// {
|
||||
// Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
|
||||
// Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
|
||||
//
|
||||
//
|
||||
// }
|
||||
if (installationConnection.Value.Product==1 && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(30))
|
||||
if (installationConnection.Value.Product==(int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(60))
|
||||
{
|
||||
// Console.WriteLine("Installation ID is "+installationConnection.Key);
|
||||
// Console.WriteLine("installationConnection.Value.Timestamp is "+installationConnection.Value.Timestamp);
|
||||
// Console.WriteLine("diff is "+(DateTime.Now-installationConnection.Value.Timestamp));
|
||||
|
||||
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 1 && f.Id == installationConnection.Key);
|
||||
installation.Status = -1;
|
||||
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == (int)ProductType.Salidomo && f.Id == installationConnection.Key);
|
||||
installation.Status = (int)StatusType.Offline;
|
||||
installation.Apply(Db.Update);
|
||||
|
||||
installationConnection.Value.Status = -1;
|
||||
installationConnection.Value.Status = (int)StatusType.Offline;
|
||||
if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);}
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +136,8 @@ public static class WebsocketManager
|
|||
continue;
|
||||
}
|
||||
|
||||
//Console.WriteLine("Received a new message from websocket");
|
||||
//Received a new message from this websocket.
|
||||
//We have a HandleWebSocketConnection per connected frontend
|
||||
lock (InstallationConnections)
|
||||
{
|
||||
List<WebsocketMessage> dataToSend = new List<WebsocketMessage>();
|
||||
|
@ -153,14 +147,17 @@ public static class WebsocketManager
|
|||
//Then, report the status of each requested installation to the front-end that created the websocket connection
|
||||
foreach (var installationId in installationIds)
|
||||
{
|
||||
//Console.WriteLine("New id is "+installationId);
|
||||
var installation = Db.GetInstallationById(installationId);
|
||||
if (!InstallationConnections.ContainsKey(installationId))
|
||||
{
|
||||
//Console.WriteLine("Create new empty list for installation id " + installationId);
|
||||
//Since we keep all the changes to the database, in case that the backend reboots, we need to update the in-memory data structure.
|
||||
//Thus, if the status is -1, we put an old timestamp, otherwise, we put the most recent timestamp.
|
||||
//We store everything to the database, because when the backend reboots, we do not want to wait until all the installations send the heartbit messages.
|
||||
//We want the in memory data structure to be up to date immediately.
|
||||
InstallationConnections[installationId] = new InstallationInfo
|
||||
{
|
||||
Status = installation.Status,
|
||||
Timestamp = installation.Status==(int)StatusType.Offline ? DateTime.Now.AddDays(-1) : DateTime.Now,
|
||||
Product = installation.Product
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1 +1,5 @@
|
|||
dotnet publish Backend.csproj -c Release -r linux-x64 --self-contained true -p:PublishTrimmed=false && rsync -av bin/Release/net6.0/linux-x64/publish/ ubuntu@194.182.190.208:~/backend && ssh ubuntu@194.182.190.208 'sudo systemctl restart backend'
|
||||
#To deploy to the monitor server, uncomment the following line
|
||||
#dotnet publish Backend.csproj -c Release -r linux-x64 --self-contained true -p:PublishTrimmed=false && rsync -av bin/Release/net6.0/linux-x64/publish/ ubuntu@194.182.190.208:~/backend && ssh ubuntu@194.182.190.208 'sudo systemctl restart backend'
|
||||
|
||||
#To deploy to the stage server, uncomment the following line
|
||||
dotnet publish Backend.csproj -c Release -r linux-x64 --self-contained true -p:PublishTrimmed=false && rsync -av bin/Release/net6.0/linux-x64/publish/ ubuntu@91.92.154.141:~/backend && ssh ubuntu@91.92.154.141 'sudo systemctl restart backend'
|
||||
|
|
|
@ -66,5 +66,4 @@ ln -s "$vpn_service_path" "$vpn_symlink_path"
|
|||
# EmuMeter_symlink_path="/service/EmuMeter"
|
||||
# ln -s "$EmuMeter_service_path" "$EmuMeter_symlink_path"
|
||||
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -54,6 +54,6 @@ INNOVENERGY_PROTOCOL_VERSION = '48TL200V3'
|
|||
|
||||
|
||||
# S3 Credentials
|
||||
S3BUCKET = "627-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
|
||||
S3KEY = "EXOb7bcf7d1e53f2d46923144de"
|
||||
S3SECRET = "-uUmMuAfx40LpTKTZgdbXswTw09o_qmE4gzkmQS8PTk"
|
||||
S3BUCKET = "158-c0436b6a-d276-4cd8-9c44-1eae86cf5d0e"
|
||||
S3KEY = "EXOf4d6d68a9ce062f25541fe4a"
|
||||
S3SECRET = "4zTQBvwIWnFYajRhoZW0F7k_6rdhnPiSqdvw9cMAZw8"
|
||||
|
|
|
@ -19,6 +19,7 @@ import { axiosConfigWithoutToken } from './Resources/axiosConfig';
|
|||
import InstallationsContextProvider from './contexts/InstallationsContextProvider';
|
||||
import AccessContextProvider from './contexts/AccessContextProvider';
|
||||
import SalidomoInstallationTabs from './content/dashboards/SalidomoInstallations';
|
||||
// import SodiohomeInstallationTabs from './content/dashboards/SodiohomeInstallations';
|
||||
import { ProductIdContext } from './contexts/ProductIdContextProvider';
|
||||
|
||||
function App() {
|
||||
|
@ -29,7 +30,7 @@ function App() {
|
|||
const navigate = useNavigate();
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const username = searchParams.get('username');
|
||||
const { setAccessToSalimax, setAccessToSalidomo } =
|
||||
const { setAccessToSalimax, setAccessToSalidomo,setAccessToSodiohome } =
|
||||
useContext(ProductIdContext);
|
||||
|
||||
const [language, setLanguage] = useState('en');
|
||||
|
@ -71,10 +72,13 @@ function App() {
|
|||
setUser(response.data.user);
|
||||
setAccessToSalimax(response.data.accessToSalimax);
|
||||
setAccessToSalidomo(response.data.accessToSalidomo);
|
||||
setAccessToSodiohome(response.data.accessToSodiohome);
|
||||
if (response.data.accessToSalimax) {
|
||||
navigate(routes.installations);
|
||||
} else {
|
||||
} else if(response.data.accessToSalidomo){
|
||||
navigate(routes.salidomo_installations);
|
||||
} else{
|
||||
navigate(routes.sodiohome_installations);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -162,6 +166,15 @@ function App() {
|
|||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={routes.sodiohome_installations + '*'}
|
||||
element={
|
||||
<AccessContextProvider>
|
||||
{/*<SodiohomeInstallationTabs />*/}
|
||||
</AccessContextProvider>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path={routes.users + '*'} element={<Users />} />
|
||||
<Route
|
||||
path={'*'}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import axios from 'axios';
|
||||
|
||||
export const axiosConfigWithoutToken = axios.create({
|
||||
baseURL: 'https://monitor.innov.energy/api'
|
||||
baseURL: 'https://stage.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: 'http://127.0.0.1:7087/api'
|
||||
});
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"users": "/users/",
|
||||
"installations": "/installations/",
|
||||
"salidomo_installations": "/salidomo_installations/",
|
||||
"sodiohome_installations": "/sodiohome_installations/",
|
||||
"installation": "installation/",
|
||||
"login": "/login/",
|
||||
"forgotPassword": "/forgotPassword/",
|
||||
|
|
|
@ -36,7 +36,7 @@ function Login() {
|
|||
|
||||
const theme = useTheme();
|
||||
const context = useContext(UserContext);
|
||||
const { setAccessToSalimax, setAccessToSalidomo } =
|
||||
const { setAccessToSalimax, setAccessToSalidomo,setAccessToSodiohome } =
|
||||
useContext(ProductIdContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
@ -78,6 +78,7 @@ function Login() {
|
|||
|
||||
setAccessToSalimax(response.data.accessToSalimax);
|
||||
setAccessToSalidomo(response.data.accessToSalidomo);
|
||||
setAccessToSodiohome(response.data.accessToSodioHome);
|
||||
|
||||
if (rememberMe) {
|
||||
cookies.set('rememberedUsername', username, { path: '/' });
|
||||
|
@ -85,8 +86,10 @@ function Login() {
|
|||
}
|
||||
if (response.data.accessToSalimax) {
|
||||
navigate(routes.installations);
|
||||
} else {
|
||||
} else if(response.data.accessToSalidomo){
|
||||
navigate(routes.salidomo_installations);
|
||||
} else{
|
||||
navigate(routes.sodiohome_installations);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -533,7 +533,7 @@ function HistoryOfActions(props: HistoryProps) {
|
|||
<IconButton
|
||||
style={iconStyle}
|
||||
onClick={() => HandleDelete(action)}
|
||||
disabled={action.userName != currentUser.name}
|
||||
// disabled={action.userName != currentUser.name}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
|
|
|
@ -130,10 +130,11 @@ const FlatInstallationView = (props: FlatInstallationViewProps) => {
|
|||
</TableHead>
|
||||
<TableBody>
|
||||
{sortedInstallations
|
||||
.filter(
|
||||
(installation) =>
|
||||
installation.status === -1 && installation.device === 1
|
||||
)
|
||||
// .filter(
|
||||
// (installation) =>
|
||||
// installation.status === -1 &&
|
||||
// installation.testingMode == false
|
||||
// )
|
||||
.map((installation) => {
|
||||
const isInstallationSelected =
|
||||
installation.s3BucketId === selectedInstallation;
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||
import React from 'react';
|
||||
import { Grid } from '@mui/material';
|
||||
|
||||
interface singleInstallationProps {
|
||||
current_installation?: I_Installation;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
function SodioHomeInstallation(props: singleInstallationProps) {
|
||||
// const context = useContext(UserContext);
|
||||
// const { currentUser } = context;
|
||||
// const location = useLocation().pathname;
|
||||
// const [errorLoadingS3Data, setErrorLoadingS3Data] = useState(false);
|
||||
// const [currentTab, setCurrentTab] = useState<string>(undefined);
|
||||
// const [values, setValues] = useState<TopologyValues | null>(null);
|
||||
// const status = props.current_installation.status;
|
||||
// const [
|
||||
// failedToCommunicateWithInstallation,
|
||||
// setFailedToCommunicateWithInstallation
|
||||
// ] = useState(0);
|
||||
// const [connected, setConnected] = useState(true);
|
||||
// const [loading, setLoading] = useState(true);
|
||||
//
|
||||
// if (props.current_installation == undefined) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// const S3data = {
|
||||
// s3Region: props.current_installation.s3Region,
|
||||
// s3Provider: props.current_installation.s3Provider,
|
||||
// s3Key: props.current_installation.s3Key,
|
||||
// s3Secret: props.current_installation.s3Secret,
|
||||
// s3BucketId: props.current_installation.s3BucketId
|
||||
// };
|
||||
//
|
||||
// const s3Bucket =
|
||||
// props.current_installation.s3BucketId.toString() +
|
||||
// '-' +
|
||||
// 'c0436b6a-d276-4cd8-9c44-1eae86cf5d0e';
|
||||
//
|
||||
// const [fetchFunctionCalled, setFetchFunctionCalled] = useState(false);
|
||||
// const s3Credentials = { s3Bucket, ...S3data };
|
||||
//
|
||||
// function timeout(delay: number) {
|
||||
// return new Promise((res) => setTimeout(res, delay));
|
||||
// }
|
||||
//
|
||||
// const continueFetching = useRef(false);
|
||||
//
|
||||
// const fetchDataPeriodically = async () => {
|
||||
// var timeperiodToSearch = 30;
|
||||
// let res;
|
||||
// let timestampToFetch;
|
||||
//
|
||||
// for (var i = 0; i < timeperiodToSearch; i += 1) {
|
||||
// if (!continueFetching.current) {
|
||||
// return false;
|
||||
// }
|
||||
// timestampToFetch = UnixTime.now().earlier(TimeSpan.fromMinutes(i));
|
||||
// console.log('timestamp to fetch is ' + timestampToFetch);
|
||||
//
|
||||
// try {
|
||||
// res = await fetchData(timestampToFetch, s3Credentials, true);
|
||||
// if (res !== FetchResult.notAvailable && res !== FetchResult.tryLater) {
|
||||
// break;
|
||||
// }
|
||||
// } catch (err) {
|
||||
// console.error('Error fetching data:', err);
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (i >= timeperiodToSearch) {
|
||||
// setConnected(false);
|
||||
// setLoading(false);
|
||||
// return false;
|
||||
// }
|
||||
// setConnected(true);
|
||||
// setLoading(false);
|
||||
// console.log('NUMBER OF FILES=' + Object.keys(res).length);
|
||||
//
|
||||
// while (continueFetching.current) {
|
||||
// for (const timestamp of Object.keys(res)) {
|
||||
// if (!continueFetching.current) {
|
||||
// setFetchFunctionCalled(false);
|
||||
// return false;
|
||||
// }
|
||||
// console.log(`Timestamp: ${timestamp}`);
|
||||
// console.log(res[timestamp]);
|
||||
//
|
||||
// // Set values asynchronously with delay
|
||||
// setValues(
|
||||
// extractValues({
|
||||
// time: UnixTime.fromTicks(parseInt(timestamp, 10)),
|
||||
// value: res[timestamp]
|
||||
// })
|
||||
// );
|
||||
// // Wait for 2 seconds before processing next timestamp
|
||||
// await timeout(2000);
|
||||
// }
|
||||
//
|
||||
// timestampToFetch = timestampToFetch.later(TimeSpan.fromMinutes(20));
|
||||
// console.log('NEW TIMESTAMP TO FETCH IS ' + timestampToFetch);
|
||||
//
|
||||
// for (i = 0; i < 10; i++) {
|
||||
// if (!continueFetching.current) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// console.log('Trying to fetch timestamp ' + timestampToFetch);
|
||||
// res = await fetchData(timestampToFetch, s3Credentials, true);
|
||||
// if (
|
||||
// res !== FetchResult.notAvailable &&
|
||||
// res !== FetchResult.tryLater
|
||||
// ) {
|
||||
// break;
|
||||
// }
|
||||
// } catch (err) {
|
||||
// console.error('Error fetching data:', err);
|
||||
// return false;
|
||||
// }
|
||||
// timestampToFetch = timestampToFetch.later(TimeSpan.fromMinutes(1));
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// useEffect(() => {
|
||||
// let path = location.split('/');
|
||||
// setCurrentTab(path[path.length - 1]);
|
||||
// }, [location]);
|
||||
//
|
||||
// useEffect(() => {
|
||||
// if (location.includes('batteryview')) {
|
||||
// if (location.includes('batteryview') && !location.includes('mainstats')) {
|
||||
// if (!continueFetching.current) {
|
||||
// continueFetching.current = true;
|
||||
// if (!fetchFunctionCalled) {
|
||||
// setFetchFunctionCalled(true);
|
||||
// fetchDataPeriodically();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return () => {
|
||||
// continueFetching.current = false;
|
||||
// };
|
||||
// } else {
|
||||
// continueFetching.current = false;
|
||||
// }
|
||||
// }, [currentTab, location]);
|
||||
//
|
||||
// useEffect(() => {
|
||||
// if (status === null) {
|
||||
// setConnected(false);
|
||||
// }
|
||||
// }, [status]);
|
||||
//
|
||||
|
||||
return <Grid item xs={12} md={12}></Grid>;
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// <Grid item xs={12} md={12}>
|
||||
// <div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
// <Typography
|
||||
// fontWeight="bold"
|
||||
// color="text.primary"
|
||||
// noWrap
|
||||
// sx={{
|
||||
// marginTop: '-20px',
|
||||
// marginBottom: '10px',
|
||||
// fontSize: '14px'
|
||||
// }}
|
||||
// >
|
||||
// <FormattedMessage
|
||||
// id="installation_name_simple"
|
||||
// defaultMessage="Installation Name:"
|
||||
// />
|
||||
// </Typography>
|
||||
// <Typography
|
||||
// fontWeight="bold"
|
||||
// color="orange"
|
||||
// noWrap
|
||||
// sx={{
|
||||
// marginTop: '-20px',
|
||||
// marginBottom: '10px',
|
||||
// marginLeft: '5px',
|
||||
// fontSize: '14px'
|
||||
// }}
|
||||
// >
|
||||
// {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: '75px',
|
||||
// marginTop: '-10px'
|
||||
// }}
|
||||
// >
|
||||
// {status === -1 ? (
|
||||
// <CancelIcon
|
||||
// style={{
|
||||
// width: '23px',
|
||||
// height: '23px',
|
||||
// color: 'red',
|
||||
// borderRadius: '50%'
|
||||
// }}
|
||||
// />
|
||||
// ) : (
|
||||
// ''
|
||||
// )}
|
||||
//
|
||||
// {status === -2 ? (
|
||||
// <CircularProgress
|
||||
// size={20}
|
||||
// sx={{
|
||||
// color: '#f7b34d'
|
||||
// }}
|
||||
// />
|
||||
// ) : (
|
||||
// ''
|
||||
// )}
|
||||
//
|
||||
// <div
|
||||
// style={{
|
||||
// width: '20px',
|
||||
// height: '20px',
|
||||
// marginLeft: '2px',
|
||||
// borderRadius: '50%',
|
||||
// backgroundColor:
|
||||
// status === 2
|
||||
// ? 'red'
|
||||
// : status === 1
|
||||
// ? 'orange'
|
||||
// : status === -1 || status === -2
|
||||
// ? 'transparent'
|
||||
// : 'green'
|
||||
// }}
|
||||
// />
|
||||
//
|
||||
// {props.current_installation.testingMode && (
|
||||
// <BuildIcon
|
||||
// style={{
|
||||
// width: '23px',
|
||||
// height: '23px',
|
||||
// color: 'purple',
|
||||
// borderRadius: '50%',
|
||||
// position: 'relative',
|
||||
// zIndex: 1,
|
||||
// marginLeft: '15px'
|
||||
// }}
|
||||
// />
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
// {loading &&
|
||||
// currentTab != 'information' &&
|
||||
// currentTab != 'overview' &&
|
||||
// currentTab != 'manage' &&
|
||||
// currentTab != 'history' &&
|
||||
// currentTab != 'log' && (
|
||||
// <Container
|
||||
// maxWidth="xl"
|
||||
// sx={{
|
||||
// display: 'flex',
|
||||
// flexDirection: 'column',
|
||||
// justifyContent: 'center',
|
||||
// alignItems: 'center',
|
||||
// height: '70vh'
|
||||
// }}
|
||||
// >
|
||||
// <CircularProgress size={60} style={{ color: '#ffc04d' }} />
|
||||
// <Typography
|
||||
// variant="body2"
|
||||
// style={{ color: 'black', fontWeight: 'bold' }}
|
||||
// mt={2}
|
||||
// >
|
||||
// Connecting to the device...
|
||||
// </Typography>
|
||||
// </Container>
|
||||
// )}
|
||||
//
|
||||
// <Card variant="outlined">
|
||||
// <Grid
|
||||
// container
|
||||
// direction="row"
|
||||
// justifyContent="center"
|
||||
// alignItems="stretch"
|
||||
// spacing={0}
|
||||
// >
|
||||
// <Routes>
|
||||
// <Route
|
||||
// path={routes.information}
|
||||
// element={
|
||||
// <InformationSalidomo
|
||||
// values={props.current_installation}
|
||||
// s3Credentials={s3Credentials}
|
||||
// type={props.type}
|
||||
// ></InformationSalidomo>
|
||||
// }
|
||||
// />
|
||||
//
|
||||
// <Route
|
||||
// path={routes.log}
|
||||
// element={
|
||||
// <Log
|
||||
// errorLoadingS3Data={errorLoadingS3Data}
|
||||
// id={props.current_installation.id}
|
||||
// ></Log>
|
||||
// }
|
||||
// />
|
||||
//
|
||||
// <Route
|
||||
// path={routes.overview}
|
||||
// element={
|
||||
// <SalidomoOverview
|
||||
// s3Credentials={s3Credentials}
|
||||
// id={props.current_installation.id}
|
||||
// ></SalidomoOverview>
|
||||
// }
|
||||
// />
|
||||
//
|
||||
// <Route
|
||||
// path={routes.batteryview + '*'}
|
||||
// element={
|
||||
// <BatteryView
|
||||
// values={values}
|
||||
// s3Credentials={s3Credentials}
|
||||
// installationId={props.current_installation.id}
|
||||
// productNum={props.current_installation.product}
|
||||
// connected={connected}
|
||||
// ></BatteryView>
|
||||
// }
|
||||
// ></Route>
|
||||
//
|
||||
// {currentUser.userType == UserType.admin && (
|
||||
// <Route
|
||||
// path={routes.history}
|
||||
// element={
|
||||
// <HistoryOfActions
|
||||
// errorLoadingS3Data={errorLoadingS3Data}
|
||||
// id={props.current_installation.id}
|
||||
// ></HistoryOfActions>
|
||||
// }
|
||||
// />
|
||||
// )}
|
||||
//
|
||||
// {currentUser.userType == UserType.admin && (
|
||||
// <Route
|
||||
// path={routes.manage}
|
||||
// element={
|
||||
// <AccessContextProvider>
|
||||
// <Access
|
||||
// currentResource={props.current_installation}
|
||||
// resourceType={props.type}
|
||||
// ></Access>
|
||||
// </AccessContextProvider>
|
||||
// }
|
||||
// />
|
||||
// )}
|
||||
//
|
||||
// <Route
|
||||
// path={'*'}
|
||||
// element={<Navigate to={routes.batteryview}></Navigate>}
|
||||
// />
|
||||
// </Routes>
|
||||
// </Grid>
|
||||
// </Card>
|
||||
// </Grid>
|
||||
// </>
|
||||
// );
|
||||
}
|
||||
|
||||
export default SodioHomeInstallation;
|
|
@ -0,0 +1,100 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import { FormControl, Grid, InputAdornment, TextField } from '@mui/material';
|
||||
import SearchTwoToneIcon from '@mui/icons-material/SearchTwoTone';
|
||||
import { I_Installation } from '../../../interfaces/InstallationTypes';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import routes from '../../../Resources/routes.json';
|
||||
|
||||
interface installationSearchProps {
|
||||
installations: I_Installation[];
|
||||
}
|
||||
|
||||
function InstallationSearch(props: installationSearchProps) {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const currentLocation = useLocation();
|
||||
// const [filteredData, setFilteredData] = useState(props.installations);
|
||||
|
||||
const indexedData = useMemo(() => {
|
||||
return props.installations.map((item) => ({
|
||||
...item,
|
||||
nameLower: item.name.toLowerCase(),
|
||||
locationLower: item.location.toLowerCase(),
|
||||
regionLower: item.region.toLowerCase()
|
||||
}));
|
||||
}, [props.installations]);
|
||||
|
||||
const filteredData = useMemo(() => {
|
||||
return indexedData.filter(
|
||||
(item) =>
|
||||
item.nameLower.includes(searchTerm.toLowerCase()) ||
|
||||
item.locationLower.includes(searchTerm.toLowerCase()) ||
|
||||
item.regionLower.includes(searchTerm.toLowerCase())
|
||||
);
|
||||
}, [searchTerm, indexedData]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
md={6}
|
||||
sx={{
|
||||
display:
|
||||
currentLocation.pathname ===
|
||||
routes.salidomo_installations + 'list' ||
|
||||
currentLocation.pathname ===
|
||||
routes.salidomo_installations + routes.list
|
||||
? 'block'
|
||||
: 'none'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start'
|
||||
}}
|
||||
>
|
||||
<FormControl variant="outlined">
|
||||
<TextField
|
||||
placeholder="Search"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
fullWidth
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchTwoToneIcon />
|
||||
</InputAdornment>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/*<FlatInstallationView installations={filteredData} />*/}
|
||||
{/*<Routes>*/}
|
||||
{/* {filteredData.map((installation) => {*/}
|
||||
{/* return (*/}
|
||||
{/* <Route*/}
|
||||
{/* key={installation.s3BucketId}*/}
|
||||
{/* path={routes.installation + installation.s3BucketId + '*'}*/}
|
||||
{/* element={*/}
|
||||
{/* <SalidomoInstallation*/}
|
||||
{/* key={installation.s3BucketId}*/}
|
||||
{/* current_installation={installation}*/}
|
||||
{/* type="installation"*/}
|
||||
{/* ></SalidomoInstallation>*/}
|
||||
{/* }*/}
|
||||
{/* />*/}
|
||||
{/* );*/}
|
||||
{/* })}*/}
|
||||
{/*</Routes>*/}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default InstallationSearch;
|
|
@ -0,0 +1,253 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
CircularProgress,
|
||||
FormControl,
|
||||
IconButton,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Modal,
|
||||
Select,
|
||||
TextField,
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
import Button from '@mui/material/Button';
|
||||
import { Close as CloseIcon } from '@mui/icons-material';
|
||||
import { I_Installation } from 'src/interfaces/InstallationTypes';
|
||||
import { InstallationsContext } from 'src/contexts/InstallationsContextProvider';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
interface SodiohomeInstallationFormProps {
|
||||
cancel: () => void;
|
||||
submit: () => void;
|
||||
parentid: number;
|
||||
}
|
||||
|
||||
function SodiohomeInstallationForm(props: SodiohomeInstallationFormProps) {
|
||||
const theme = useTheme();
|
||||
const [open, setOpen] = useState(true);
|
||||
const [formValues, setFormValues] = useState<Partial<I_Installation>>({
|
||||
name: '',
|
||||
region: '',
|
||||
location: '',
|
||||
country: '',
|
||||
serialNumber: ''
|
||||
});
|
||||
const requiredFields = ['name', 'location', 'country', 'serialNumber'];
|
||||
|
||||
const installationContext = useContext(InstallationsContext);
|
||||
const { createInstallation, loading, setLoading, error, setError } =
|
||||
installationContext;
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
|
||||
setFormValues({
|
||||
...formValues,
|
||||
[name]: value
|
||||
});
|
||||
};
|
||||
const handleSubmit = async (e) => {
|
||||
setLoading(true);
|
||||
formValues.parentId = props.parentid;
|
||||
formValues.product = 2;
|
||||
const responseData = await createInstallation(formValues);
|
||||
props.submit();
|
||||
};
|
||||
const handleCancelSubmit = (e) => {
|
||||
props.cancel();
|
||||
};
|
||||
|
||||
const areRequiredFieldsFilled = () => {
|
||||
for (const field of requiredFields) {
|
||||
if (!formValues[field]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const isMobile = window.innerWidth <= 1490;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => {}}
|
||||
aria-labelledby="error-modal"
|
||||
aria-describedby="error-modal-description"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: isMobile ? '50%' : '40%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 500,
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: 4,
|
||||
boxShadow: 24,
|
||||
p: 4
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center', // Center items horizontally
|
||||
'& .MuiTextField-root': {
|
||||
m: 1,
|
||||
width: 390
|
||||
}
|
||||
}}
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
>
|
||||
<div>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="installationName"
|
||||
defaultMessage="Installation Name"
|
||||
/>
|
||||
}
|
||||
name="name"
|
||||
value={formValues.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.name === ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
label={<FormattedMessage id="region" defaultMessage="Region" />}
|
||||
name="region"
|
||||
value={formValues.region}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.region === ''}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage id="location" defaultMessage="Location" />
|
||||
}
|
||||
name="location"
|
||||
value={formValues.location}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.location === ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage id="country" defaultMessage="Country" />
|
||||
}
|
||||
name="country"
|
||||
value={formValues.country}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.country === ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage id="serialNumber" defaultMessage="Serial Number" />
|
||||
}
|
||||
name="serialNumber"
|
||||
value={formValues.serialNumber}
|
||||
onChange={handleChange}
|
||||
required
|
||||
error={formValues.serialNumber === ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TextField
|
||||
label={
|
||||
<FormattedMessage
|
||||
id="Information"
|
||||
defaultMessage="Information"
|
||||
/>
|
||||
}
|
||||
name="information"
|
||||
value={formValues.information}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginTop: 10
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleSubmit}
|
||||
sx={{
|
||||
marginLeft: '20px'
|
||||
}}
|
||||
disabled={!areRequiredFieldsFilled()}
|
||||
>
|
||||
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleCancelSubmit}
|
||||
sx={{
|
||||
marginLeft: '10px'
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="cancel" defaultMessage="Cancel" />
|
||||
</Button>
|
||||
|
||||
{loading && (
|
||||
<CircularProgress
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
marginLeft: '20px'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<Alert
|
||||
severity="error"
|
||||
sx={{
|
||||
ml: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="errorOccured"
|
||||
defaultMessage="An error has occured"
|
||||
/>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
size="small"
|
||||
onClick={() => setError(false)}
|
||||
sx={{ marginLeft: '4px' }}
|
||||
>
|
||||
<CloseIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</Box>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default SodiohomeInstallationForm;
|
|
@ -0,0 +1,396 @@
|
|||
import React, { ChangeEvent, useContext, useEffect, useState } from 'react';
|
||||
import { Box, Card, Container, Grid, Tab, Tabs } from '@mui/material';
|
||||
import { TabsContainerWrapper } from 'src/layouts/TabsContainerWrapper';
|
||||
import { Link, Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
||||
import routes from 'src/Resources/routes.json';
|
||||
import InstallationSearch from './InstallationSearch';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { UserContext } from '../../../contexts/userContext';
|
||||
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
||||
import ListIcon from '@mui/icons-material/List';
|
||||
import AccountTreeIcon from '@mui/icons-material/AccountTree';
|
||||
import TreeView from '../Tree/treeView';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
import { UserType } from '../../../interfaces/UserTypes';
|
||||
import SodioHomeInstallation from './Installation';
|
||||
|
||||
function SodioHomeInstallationTabs() {
|
||||
const location = useLocation();
|
||||
const context = useContext(UserContext);
|
||||
const { currentUser } = context;
|
||||
const tabList = [
|
||||
'batteryview',
|
||||
'information',
|
||||
'manage',
|
||||
'overview',
|
||||
'log',
|
||||
'history'
|
||||
];
|
||||
|
||||
const [currentTab, setCurrentTab] = useState<string>(undefined);
|
||||
const [fetchedInstallations, setFetchedInstallations] =
|
||||
useState<boolean>(false);
|
||||
const {
|
||||
sodiohomeInstallations,
|
||||
fetchAllSodiohomeInstallations,
|
||||
currentProduct,
|
||||
socket,
|
||||
openSocket,
|
||||
closeSocket
|
||||
} = useContext(InstallationsContext);
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
|
||||
// const webSocketsContext = useContext(WebSocketContext);
|
||||
// const { socket, openSocket, closeSocket } = webSocketsContext;
|
||||
|
||||
useEffect(() => {
|
||||
let path = location.pathname.split('/');
|
||||
|
||||
if (path[path.length - 2] === 'list') {
|
||||
setCurrentTab('list');
|
||||
} else if (path[path.length - 2] === 'tree') {
|
||||
setCurrentTab('tree');
|
||||
} else {
|
||||
//Even if we are located at path: /batteryview/mainstats, we want the BatteryView tab to be bold
|
||||
setCurrentTab(path.find((pathElement) => tabList.includes(pathElement)));
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sodiohomeInstallations.length === 0 && fetchedInstallations === false) {
|
||||
fetchAllSodiohomeInstallations();
|
||||
setFetchedInstallations(true);
|
||||
}
|
||||
}, [sodiohomeInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sodiohomeInstallations && sodiohomeInstallations.length > 0) {
|
||||
if (!socket) {
|
||||
openSocket(1);
|
||||
} else if (currentProduct == 0) {
|
||||
closeSocket();
|
||||
openSocket(1);
|
||||
}
|
||||
}
|
||||
}, [sodiohomeInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
setProduct(1);
|
||||
}, []);
|
||||
|
||||
const handleTabsChange = (_event: ChangeEvent<{}>, value: string): void => {
|
||||
setCurrentTab(value);
|
||||
};
|
||||
|
||||
const navigateToTabPath = (pathname: string, tab_value: string): string => {
|
||||
let pathlist = pathname.split('/');
|
||||
let ret_path = '';
|
||||
for (let i = 1; i < pathlist.length; i++) {
|
||||
if (Number.isNaN(Number(pathlist[i]))) {
|
||||
ret_path += '/';
|
||||
ret_path += pathlist[i];
|
||||
} else {
|
||||
ret_path += '/';
|
||||
ret_path += pathlist[i];
|
||||
ret_path += '/';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret_path += tab_value;
|
||||
return ret_path;
|
||||
};
|
||||
|
||||
const singleInstallationTabs =
|
||||
currentUser.userType == UserType.admin
|
||||
? [
|
||||
{
|
||||
value: 'batteryview',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="batteryview"
|
||||
defaultMessage="Battery View"
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
value: 'overview',
|
||||
label: <FormattedMessage id="overview" defaultMessage="Overview" />
|
||||
},
|
||||
{
|
||||
value: 'log',
|
||||
label: <FormattedMessage id="log" defaultMessage="Log" />
|
||||
},
|
||||
|
||||
{
|
||||
value: 'manage',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="manage"
|
||||
defaultMessage="Access Management"
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
||||
{
|
||||
value: 'information',
|
||||
label: (
|
||||
<FormattedMessage id="information" defaultMessage="Information" />
|
||||
)
|
||||
},
|
||||
{
|
||||
value: 'history',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="history"
|
||||
defaultMessage="History Of Actions"
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
value: 'batteryview',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="batteryview"
|
||||
defaultMessage="Battery View"
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
value: 'overview',
|
||||
label: <FormattedMessage id="overview" defaultMessage="Overview" />
|
||||
},
|
||||
|
||||
{
|
||||
value: 'information',
|
||||
label: (
|
||||
<FormattedMessage id="information" defaultMessage="Information" />
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
const tabs =
|
||||
currentTab != 'list' &&
|
||||
currentTab != 'tree' &&
|
||||
!location.pathname.includes('folder') &&
|
||||
currentUser.userType == UserType.admin
|
||||
? [
|
||||
{
|
||||
value: 'list',
|
||||
icon: <ListIcon id="mode-toggle-button-list-icon" />
|
||||
},
|
||||
{
|
||||
value: 'tree',
|
||||
icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
|
||||
},
|
||||
{
|
||||
value: 'batteryview',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="batteryview"
|
||||
defaultMessage="Battery View"
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
value: 'overview',
|
||||
label: <FormattedMessage id="overview" defaultMessage="Overview" />
|
||||
},
|
||||
{
|
||||
value: 'log',
|
||||
label: <FormattedMessage id="log" defaultMessage="Log" />
|
||||
},
|
||||
|
||||
{
|
||||
value: 'manage',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="manage"
|
||||
defaultMessage="Access Management"
|
||||
/>
|
||||
)
|
||||
},
|
||||
|
||||
{
|
||||
value: 'information',
|
||||
label: (
|
||||
<FormattedMessage id="information" defaultMessage="Information" />
|
||||
)
|
||||
},
|
||||
{
|
||||
value: 'history',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="history"
|
||||
defaultMessage="History Of Actions"
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
: currentTab != 'list' &&
|
||||
currentTab != 'tree' &&
|
||||
!location.pathname.includes('folder') &&
|
||||
currentUser.userType == UserType.client
|
||||
? [
|
||||
{
|
||||
value: 'list',
|
||||
icon: <ListIcon id="mode-toggle-button-list-icon" />
|
||||
},
|
||||
{
|
||||
value: 'tree',
|
||||
icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
|
||||
},
|
||||
{
|
||||
value: 'batteryview',
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id="batteryview"
|
||||
defaultMessage="Battery View"
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
value: 'overview',
|
||||
label: <FormattedMessage id="overview" defaultMessage="Overview" />
|
||||
},
|
||||
|
||||
{
|
||||
value: 'information',
|
||||
label: (
|
||||
<FormattedMessage id="information" defaultMessage="Information" />
|
||||
)
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
value: 'list',
|
||||
icon: <ListIcon id="mode-toggle-button-list-icon" />
|
||||
},
|
||||
{
|
||||
value: 'tree',
|
||||
icon: <AccountTreeIcon id="mode-toggle-button-tree-icon" />
|
||||
}
|
||||
];
|
||||
|
||||
return sodiohomeInstallations.length > 1 ? (
|
||||
<>
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
|
||||
<TabsContainerWrapper>
|
||||
<Tabs
|
||||
onChange={handleTabsChange}
|
||||
value={currentTab}
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
textColor="primary"
|
||||
indicatorColor="primary"
|
||||
>
|
||||
{tabs.map((tab) => (
|
||||
<Tab
|
||||
key={tab.value}
|
||||
value={tab.value}
|
||||
icon={tab.icon}
|
||||
component={Link}
|
||||
label={tab.label}
|
||||
to={
|
||||
tab.value === 'list' || tab.value === 'tree'
|
||||
? routes[tab.value]
|
||||
: navigateToTabPath(location.pathname, routes[tab.value])
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
</TabsContainerWrapper>
|
||||
<Card variant="outlined">
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justifyContent="center"
|
||||
alignItems="stretch"
|
||||
spacing={0}
|
||||
>
|
||||
<Routes>
|
||||
<Route
|
||||
path={routes.list + '*'}
|
||||
element={
|
||||
<Grid item xs={12}>
|
||||
<Box p={4}>
|
||||
<InstallationSearch
|
||||
installations={sodiohomeInstallations}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path={routes.tree + '*'} element={<TreeView />} />
|
||||
|
||||
<Route
|
||||
path={'*'}
|
||||
element={
|
||||
<Navigate
|
||||
to={routes.salidomo_installations + routes.list}
|
||||
></Navigate>
|
||||
}
|
||||
></Route>
|
||||
</Routes>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Container>
|
||||
</>
|
||||
) : sodiohomeInstallations.length === 1 ? (
|
||||
<>
|
||||
{' '}
|
||||
<Container maxWidth="xl" sx={{ marginTop: '20px' }} className="mainframe">
|
||||
<TabsContainerWrapper>
|
||||
<Tabs
|
||||
onChange={handleTabsChange}
|
||||
value={currentTab}
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
textColor="primary"
|
||||
indicatorColor="primary"
|
||||
>
|
||||
{singleInstallationTabs.map((tab) => (
|
||||
<Tab
|
||||
key={tab.value}
|
||||
value={tab.value}
|
||||
component={Link}
|
||||
label={tab.label}
|
||||
to={routes[tab.value]}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
</TabsContainerWrapper>
|
||||
<Card variant="outlined">
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justifyContent="center"
|
||||
alignItems="stretch"
|
||||
spacing={0}
|
||||
>
|
||||
<Routes>
|
||||
<Route
|
||||
path={'*'}
|
||||
element={
|
||||
<Grid item xs={12}>
|
||||
<Box p={4}>
|
||||
<SodioHomeInstallation
|
||||
current_installation={sodiohomeInstallations[0]}
|
||||
type="installation"
|
||||
></SodioHomeInstallation>
|
||||
</Box>
|
||||
</Grid>
|
||||
}
|
||||
/>
|
||||
</Routes>
|
||||
</Grid>
|
||||
</Card>
|
||||
</Container>
|
||||
</>
|
||||
) : null;
|
||||
}
|
||||
|
||||
export default SodioHomeInstallationTabs;
|
|
@ -4,9 +4,13 @@ import {
|
|||
CardContent,
|
||||
CircularProgress,
|
||||
Container,
|
||||
FormControl,
|
||||
Grid,
|
||||
IconButton,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Modal,
|
||||
Select,
|
||||
TextField,
|
||||
Typography,
|
||||
useTheme
|
||||
|
@ -18,11 +22,11 @@ import React, { useContext, useState } from 'react';
|
|||
import { I_Folder } from '../../../interfaces/InstallationTypes';
|
||||
import { UserContext } from '../../../contexts/userContext';
|
||||
import FolderForm from './folderForm';
|
||||
import InstallationForm from '../Installations/installationForm';
|
||||
import { InstallationsContext } from '../../../contexts/InstallationsContextProvider';
|
||||
import { UserType } from '../../../interfaces/UserTypes';
|
||||
import InstallationForm from '../Installations/installationForm';
|
||||
import SalidomoInstallationForm from '../SalidomoInstallations/SalidomoInstallationForm';
|
||||
import { ProductIdContext } from '../../../contexts/ProductIdContextProvider';
|
||||
import SodiohomeInstallationForm from '../SodiohomeInstallations/SodiohomeInstallationForm';
|
||||
|
||||
interface TreeInformationProps {
|
||||
folder: I_Folder;
|
||||
|
@ -37,6 +41,8 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
const { currentUser } = context;
|
||||
const [formValues, setFormValues] = useState(props.folder);
|
||||
const [openModalFolder, setOpenModalFolder] = useState(false);
|
||||
const [openModalInstallationChoice, setOpenModalInstallationChoice] =
|
||||
useState(false);
|
||||
const [openModalInstallation, setOpenModalInstallation] = useState(false);
|
||||
const requiredFields = ['name'];
|
||||
const [openModalDeleteFolder, setOpenModalDeleteFolder] = useState(false);
|
||||
|
@ -52,7 +58,17 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
deleteFolder
|
||||
} = installationContext;
|
||||
|
||||
const { product, setProduct } = useContext(ProductIdContext);
|
||||
//const { product, setProduct } = useContext(ProductIdContext);
|
||||
const [product, setProduct] = useState('Salimax');
|
||||
|
||||
const handleChangeInstallationChoice = (e) => {
|
||||
setProduct(e.target.value); // Directly update the product state
|
||||
// console.log('Selected Product:', e.target.value);
|
||||
};
|
||||
|
||||
const ProductTypes = ['Salimax', 'Salidomo', 'Sodiohome'];
|
||||
|
||||
const isMobile = window.innerWidth <= 1490;
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
|
@ -69,6 +85,16 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
};
|
||||
|
||||
const handleNewInstallationInsertion = () => {
|
||||
setOpenModalInstallationChoice(true);
|
||||
//setOpenModalInstallation(true);
|
||||
};
|
||||
|
||||
const handleCancelSubmitInstallationChoice = () => {
|
||||
setOpenModalInstallationChoice(false);
|
||||
};
|
||||
|
||||
const handleSubmitInstallationChoice = () => {
|
||||
setOpenModalInstallationChoice(false);
|
||||
setOpenModalInstallation(true);
|
||||
};
|
||||
|
||||
|
@ -96,11 +122,13 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
const handleFolderFormSubmit = () => {
|
||||
setOpenModalFolder(false);
|
||||
setOpenModalInstallation(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleInstallationFormSubmit = () => {
|
||||
setOpenModalFolder(false);
|
||||
setOpenModalInstallation(false);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const handleFormCancel = () => {
|
||||
|
@ -199,20 +227,124 @@ function TreeInformation(props: TreeInformationProps) {
|
|||
parentid={props.folder.id}
|
||||
/>
|
||||
)}
|
||||
{openModalInstallation && product == 0 && (
|
||||
{openModalInstallationChoice && (
|
||||
<Modal
|
||||
open={openModalInstallationChoice}
|
||||
onClose={() => {}}
|
||||
aria-labelledby="error-modal"
|
||||
aria-describedby="error-modal-description"
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: isMobile ? '50%' : '40%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 500,
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: 4,
|
||||
boxShadow: 24,
|
||||
p: 4
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center', // Center items horizontally
|
||||
'& .MuiTextField-root': {
|
||||
m: 1,
|
||||
width: 390
|
||||
}
|
||||
}}
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
>
|
||||
<div>
|
||||
<FormControl
|
||||
fullWidth
|
||||
sx={{ marginTop: 1, marginBottom: 1, width: 390 }}
|
||||
>
|
||||
<InputLabel
|
||||
sx={{
|
||||
fontSize: 14,
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="producType"
|
||||
defaultMessage="Select a Product"
|
||||
/>
|
||||
</InputLabel>
|
||||
<Select
|
||||
name="product"
|
||||
value={product}
|
||||
onChange={handleChangeInstallationChoice}
|
||||
>
|
||||
{ProductTypes.map((type) => (
|
||||
<MenuItem key={type} value={type}>
|
||||
{type}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</div>
|
||||
</Box>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center', // Center horizontally
|
||||
alignItems: 'center', // Center vertically
|
||||
marginTop: 20,
|
||||
gap: '10px' // Space between buttons
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleSubmitInstallationChoice}
|
||||
sx={{
|
||||
marginLeft: '20px'
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="submit" defaultMessage="Submit" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleCancelSubmitInstallationChoice}
|
||||
sx={{
|
||||
marginLeft: '10px'
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="cancel" defaultMessage="Cancel" />
|
||||
</Button>
|
||||
</div>
|
||||
</Box>
|
||||
</Modal>
|
||||
)}
|
||||
{openModalInstallation && product == 'Salimax' && (
|
||||
<InstallationForm
|
||||
cancel={handleFormCancel}
|
||||
submit={handleInstallationFormSubmit}
|
||||
parentid={props.folder.id}
|
||||
/>
|
||||
)}
|
||||
{openModalInstallation && product == 1 && (
|
||||
{openModalInstallation && product == 'Salidomo' && (
|
||||
<SalidomoInstallationForm
|
||||
cancel={handleFormCancel}
|
||||
submit={handleInstallationFormSubmit}
|
||||
parentid={props.folder.id}
|
||||
/>
|
||||
)}
|
||||
{openModalInstallation && product == 'Sodiohome' && (
|
||||
<SodiohomeInstallationForm
|
||||
cancel={handleFormCancel}
|
||||
submit={handleInstallationFormSubmit}
|
||||
parentid={props.folder.id}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Container maxWidth="xl">
|
||||
<Grid
|
||||
container
|
||||
|
|
|
@ -28,6 +28,9 @@ const InstallationsContextProvider = ({
|
|||
const [salidomoInstallations, setSalidomoInstallations] = useState<
|
||||
I_Installation[]
|
||||
>([]);
|
||||
const [sodiohomeInstallations, setSodiohomeInstallations] = useState<
|
||||
I_Installation[]
|
||||
>([]);
|
||||
const [foldersAndInstallations, setFoldersAndInstallations] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
@ -75,12 +78,24 @@ const InstallationsContextProvider = ({
|
|||
: installation;
|
||||
});
|
||||
|
||||
const updatedSodiohome = sodiohomeInstallations.map((installation) => {
|
||||
const update = pendingUpdates.current[installation.id];
|
||||
return update
|
||||
? {
|
||||
...installation,
|
||||
status: update.status,
|
||||
testingMode: update.testingMode
|
||||
}
|
||||
: installation;
|
||||
});
|
||||
|
||||
setSalidomoInstallations(updatedSalidomo);
|
||||
setSalimaxInstallations(updatedSalimax);
|
||||
setSodiohomeInstallations(updatedSodiohome);
|
||||
|
||||
// Clear the pending updates after applying
|
||||
pendingUpdates.current = {};
|
||||
}, [salidomoInstallations, salimaxInstallations]);
|
||||
}, [salidomoInstallations, salimaxInstallations, sodiohomeInstallations]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
|
@ -94,21 +109,39 @@ const InstallationsContextProvider = ({
|
|||
setcurrentProduct(product);
|
||||
const tokenString = localStorage.getItem('token');
|
||||
const token = tokenString !== null ? tokenString : '';
|
||||
const urlWithToken = `wss://monitor.innov.energy/api/CreateWebSocket?authToken=${token}`;
|
||||
const urlWithToken = `wss://stage.innov.energy/api/CreateWebSocket?authToken=${token}`;
|
||||
|
||||
const socket = new WebSocket(urlWithToken);
|
||||
|
||||
// Connection opened
|
||||
socket.addEventListener('open', () => {
|
||||
let installationsToSend = [];
|
||||
|
||||
if (product === 0) {
|
||||
installationsToSend = salimaxInstallations;
|
||||
} else if (product === 1) {
|
||||
installationsToSend = salidomoInstallations;
|
||||
} else if (product === 2) {
|
||||
installationsToSend = sodiohomeInstallations;
|
||||
}
|
||||
|
||||
// Send the corresponding installation IDs
|
||||
socket.send(
|
||||
JSON.stringify(
|
||||
product === 1
|
||||
? salidomoInstallations.map((installation) => installation.id)
|
||||
: salimaxInstallations.map((installation) => installation.id)
|
||||
installationsToSend.map((installation) => installation.id)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// socket.addEventListener('open', () => {
|
||||
// socket.send(
|
||||
// JSON.stringify(
|
||||
// product === 1
|
||||
// ? salidomoInstallations.map((installation) => installation.id)
|
||||
// : salimaxInstallations.map((installation) => installation.id)
|
||||
// )
|
||||
// );
|
||||
// });
|
||||
|
||||
// Periodically send ping messages to keep the connection alive
|
||||
const pingInterval = setInterval(() => {
|
||||
if (socket.readyState === WebSocket.OPEN) {
|
||||
|
@ -166,6 +199,20 @@ const InstallationsContextProvider = ({
|
|||
});
|
||||
}, [navigate, removeToken]);
|
||||
|
||||
const fetchAllSodiohomeInstallations = useCallback(async () => {
|
||||
axiosConfig
|
||||
.get('/GetAllSodioHomeInstallations')
|
||||
.then((res: AxiosResponse<I_Installation[]>) =>
|
||||
setSodiohomeInstallations(res.data)
|
||||
)
|
||||
.catch((err: AxiosError) => {
|
||||
if (err.response?.status === 401) {
|
||||
removeToken();
|
||||
navigate(routes.login);
|
||||
}
|
||||
});
|
||||
}, [navigate, removeToken]);
|
||||
|
||||
const fetchAllFoldersAndInstallations = useCallback(
|
||||
async (product: number) => {
|
||||
axiosConfig
|
||||
|
@ -185,7 +232,10 @@ const InstallationsContextProvider = ({
|
|||
async (formValues: Partial<I_Installation>) => {
|
||||
axiosConfig
|
||||
.post('/CreateInstallation', formValues)
|
||||
.then(() => fetchAllFoldersAndInstallations(formValues.product))
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
fetchAllFoldersAndInstallations(formValues.product);
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(true);
|
||||
if (error.response?.status === 401) {
|
||||
|
@ -207,6 +257,8 @@ const InstallationsContextProvider = ({
|
|||
fetchAllInstallations();
|
||||
else if (formValues.product === 1 && view === 'installation')
|
||||
fetchAllSalidomoInstallations();
|
||||
else if (formValues.product === 2 && view === 'installation')
|
||||
fetchAllSodiohomeInstallations();
|
||||
else fetchAllFoldersAndInstallations(formValues.product);
|
||||
setTimeout(() => setUpdated(false), 3000);
|
||||
})
|
||||
|
@ -222,6 +274,7 @@ const InstallationsContextProvider = ({
|
|||
fetchAllFoldersAndInstallations,
|
||||
fetchAllInstallations,
|
||||
fetchAllSalidomoInstallations,
|
||||
fetchAllSodiohomeInstallations,
|
||||
navigate,
|
||||
removeToken
|
||||
]
|
||||
|
@ -237,6 +290,8 @@ const InstallationsContextProvider = ({
|
|||
fetchAllInstallations();
|
||||
else if (formValues.product === 1 && view === 'installation')
|
||||
fetchAllSalidomoInstallations();
|
||||
else if (formValues.product === 2 && view === 'installation')
|
||||
fetchAllSodiohomeInstallations();
|
||||
else fetchAllFoldersAndInstallations(formValues.product);
|
||||
setTimeout(() => setUpdated(false), 3000);
|
||||
})
|
||||
|
@ -252,6 +307,7 @@ const InstallationsContextProvider = ({
|
|||
fetchAllFoldersAndInstallations,
|
||||
fetchAllInstallations,
|
||||
fetchAllSalidomoInstallations,
|
||||
fetchAllSodiohomeInstallations,
|
||||
navigate,
|
||||
removeToken
|
||||
]
|
||||
|
@ -279,8 +335,10 @@ const InstallationsContextProvider = ({
|
|||
.put('/UpdateFolder', formValues)
|
||||
.then(() => {
|
||||
setUpdated(true);
|
||||
|
||||
fetchAllFoldersAndInstallations(product);
|
||||
setTimeout(() => setUpdated(false), 3000);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
setError(true);
|
||||
|
@ -317,9 +375,11 @@ const InstallationsContextProvider = ({
|
|||
() => ({
|
||||
salimaxInstallations,
|
||||
salidomoInstallations,
|
||||
sodiohomeInstallations,
|
||||
foldersAndInstallations,
|
||||
fetchAllInstallations,
|
||||
fetchAllSalidomoInstallations,
|
||||
fetchAllSodiohomeInstallations,
|
||||
fetchAllFoldersAndInstallations,
|
||||
createInstallation,
|
||||
updateInstallation,
|
||||
|
@ -341,6 +401,7 @@ const InstallationsContextProvider = ({
|
|||
[
|
||||
salimaxInstallations,
|
||||
salidomoInstallations,
|
||||
sodiohomeInstallations,
|
||||
foldersAndInstallations,
|
||||
loading,
|
||||
error,
|
||||
|
|
|
@ -7,8 +7,10 @@ interface ProductIdContextType {
|
|||
setProduct: (new_product: number) => void;
|
||||
accessToSalimax: boolean;
|
||||
accessToSalidomo: boolean;
|
||||
accessToSodiohome: boolean;
|
||||
setAccessToSalimax: (access: boolean) => void;
|
||||
setAccessToSalidomo: (access: boolean) => void;
|
||||
setAccessToSodiohome: (access: boolean) => void;
|
||||
}
|
||||
|
||||
// Create the context.
|
||||
|
@ -16,6 +18,14 @@ export const ProductIdContext = createContext<ProductIdContextType | undefined>(
|
|||
undefined
|
||||
);
|
||||
|
||||
// Define the product mapping for dynamic assignment
|
||||
// const productMapping: { [key: string]: number } = {
|
||||
// salimax: 0,
|
||||
// salidomo: 1,
|
||||
// sodiohome: 2,
|
||||
// // Additional mappings can be added here
|
||||
// };
|
||||
|
||||
// Create a UserContextProvider component
|
||||
export const ProductIdContextProvider = ({
|
||||
children
|
||||
|
@ -31,7 +41,23 @@ export const ProductIdContextProvider = ({
|
|||
const storedValue = localStorage.getItem('accessToSalidomo');
|
||||
return storedValue === 'true';
|
||||
});
|
||||
const [product, setProduct] = useState(location.includes('salidomo') ? 1 : 0);
|
||||
const [accessToSodiohome, setAccessToSodiohome] = useState(() => {
|
||||
const storedValue = localStorage.getItem('accessToSodiohome');
|
||||
return storedValue === 'true';
|
||||
});
|
||||
// const [product, setProduct] = useState(location.includes('salidomo') ? 1 : 0);
|
||||
// const [product, setProduct] = useState<number>(
|
||||
// productMapping[Object.keys(productMapping).find((key) => location.includes(key)) || ''] || -1
|
||||
// );
|
||||
const [product, setProduct] = useState<number>(() => {
|
||||
if (location.includes('salidomo')) {
|
||||
return 1;
|
||||
} else if (location.includes('sodiohome')) {
|
||||
return 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
const changeProductId = (new_product: number) => {
|
||||
setProduct(new_product);
|
||||
|
@ -47,6 +73,11 @@ export const ProductIdContextProvider = ({
|
|||
localStorage.setItem('accessToSalidomo', JSON.stringify(access));
|
||||
};
|
||||
|
||||
const changeAccessSodiohome = (access: boolean) => {
|
||||
setAccessToSodiohome(access);
|
||||
localStorage.setItem('accessToSodiohome', JSON.stringify(access));
|
||||
};
|
||||
|
||||
return (
|
||||
<ProductIdContext.Provider
|
||||
value={{
|
||||
|
@ -54,8 +85,10 @@ export const ProductIdContextProvider = ({
|
|||
setProduct: changeProductId,
|
||||
accessToSalimax,
|
||||
accessToSalidomo,
|
||||
accessToSodiohome,
|
||||
setAccessToSalimax: changeAccessSalimax,
|
||||
setAccessToSalidomo: changeAccessSalidomo
|
||||
setAccessToSalidomo: changeAccessSalidomo,
|
||||
setAccessToSodiohome: changeAccessSodiohome
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -20,6 +20,7 @@ export interface I_Installation extends I_S3Credentials {
|
|||
device: number;
|
||||
testingMode?: boolean;
|
||||
status?: number;
|
||||
serialNumber?: string;
|
||||
}
|
||||
|
||||
export interface I_Folder {
|
||||
|
|
|
@ -164,7 +164,7 @@ function SidebarMenu() {
|
|||
const { closeSidebar } = useContext(SidebarContext);
|
||||
const context = useContext(UserContext);
|
||||
const { currentUser, setUser } = context;
|
||||
const { accessToSalimax, accessToSalidomo } = useContext(ProductIdContext);
|
||||
const { accessToSalimax, accessToSalidomo,accessToSodiohome } = useContext(ProductIdContext);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -216,6 +216,24 @@ function SidebarMenu() {
|
|||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
|
||||
{accessToSodiohome && (
|
||||
<List component="div">
|
||||
<ListItem component="div">
|
||||
<Button
|
||||
disableRipple
|
||||
component={RouterLink}
|
||||
onClick={closeSidebar}
|
||||
to="/sodiohome_installations"
|
||||
startIcon={<BrightnessLowTwoToneIcon />}
|
||||
>
|
||||
<Box sx={{ marginTop: '3px' }}>
|
||||
<FormattedMessage id="sodiohome" defaultMessage="Sodiohome" />
|
||||
</Box>
|
||||
</Button>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
</SubMenuWrapper>
|
||||
</List>
|
||||
|
||||
|
|
Loading…
Reference in New Issue