Merge remote-tracking branch 'origin/main'

This commit is contained in:
atef 2023-12-04 10:31:37 +01:00
commit 3b02b46b78
18 changed files with 399 additions and 448 deletions

View File

@ -14,7 +14,7 @@ I'll reroute my emails to one of you for software updates.
MARIOS: Please make sure to patch out the vulnerable npm packages in the frontend. MARIOS: Please make sure to patch out the vulnerable npm packages in the frontend.
And in my opinion, get started on React or Testcafe Integration tests ;) Get started on React or Testcafe Integration tests ;)
You can add them into the Gitea Actions Pipeline, read the documentation on Github-actions and integration tests. You can add them into the Gitea Actions Pipeline, read the documentation on Github-actions and integration tests.
Runner: Runner:

View File

@ -326,11 +326,20 @@ public class Controller : ControllerBase
[HttpPost(nameof(CreateUser))] [HttpPost(nameof(CreateUser))]
public async Task<ActionResult<User>> CreateUser([FromBody] User newUser, Token authToken) public async Task<ActionResult<User>> CreateUser([FromBody] User newUser, Token authToken)
{ {
var create = Db.GetSession(authToken).Create(newUser); var create = Db.GetSession(authToken).Create(newUser);
if (create)
{
var mail_success= await Db.SendNewUserEmail(newUser);
if (!mail_success)
{
Db.GetSession(authToken).Delete(newUser);
}
return mail_success ? newUser.HidePassword():Unauthorized();
}
return create && await Db.SendNewUserEmail(newUser) return Unauthorized() ;
? newUser.HidePassword()
: Unauthorized() ;
} }
[HttpPost(nameof(CreateInstallation))] [HttpPost(nameof(CreateInstallation))]

View File

@ -11,6 +11,7 @@ using Microsoft.OpenApi.Models;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using RabbitMQ.Client; using RabbitMQ.Client;
namespace InnovEnergy.App.Backend; namespace InnovEnergy.App.Backend;
public static class Program public static class Program
@ -22,18 +23,10 @@ public static class Program
Db.Init(); Db.Init();
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
RabbitMqManager.InitializeEnvironment();
string vpnServerIp = "194.182.190.208"; RabbitMqManager.StartRabbitMqConsumer();
//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"); Console.WriteLine("Queue declared");
WebsocketManager.InformInstallationsToSubscribeToRabbitMq(); //WebsocketManager.InformInstallationsToSubscribeToRabbitMq();
WebsocketManager.MonitorInstallationTable(); WebsocketManager.MonitorInstallationTable();
builder.Services.AddControllers(); builder.Services.AddControllers();

View File

@ -0,0 +1,304 @@
using System.Drawing.Printing;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Json;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
namespace InnovEnergy.App.Backend.Websockets;
public static class RabbitMqManager
{
public static ConnectionFactory Factory = null!;
public static IConnection Connection = null!;
public static IModel Channel = null!;
public static void InitializeEnvironment()
{
//string vpnServerIp = "194.182.190.208";
string vpnServerIp = "10.2.0.11";
// ConnectionFactory factory = new ConnectionFactory();
// factory.HostName = vpnServerIp;
// factory.AutomaticRecoveryEnabled = true;
// //factory.UserName = "";
// //factory.Password = "";
// factory.VirtualHost = "/";
// factory.Port = 5672;
//
// //factory.AuthMechanisms = new IAuthMechanismFactory[] { new ExternalMechanismFactory() };
//
// System.Diagnostics.Debug.WriteLine("2 ");
//
// X509Certificate2Collection certCollection = new X509Certificate2Collection();
// X509Certificate2 certificate = new X509Certificate2("/etc/rabbitmq/testca/ca_certificate.pem");
// certCollection.Add(certificate);
//
// factory.Ssl.Certs = certCollection;
// factory.Ssl.Enabled = true;
// factory.Ssl.ServerName = "Webserver-FrontAndBack";
// factory.Ssl.Version = SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
// factory.Ssl.AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors;
// factory.Ssl.CertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
// {
// if (sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)
// {
// // Log or debug information about the chain
// foreach (var chainElement in chain.ChainElements)
// {
// Console.WriteLine($"Element Subject: {chainElement.Certificate.Subject}");
// Console.WriteLine($"Element Issuer: {chainElement.Certificate.Issuer}");
// // Add more details as needed
// }
// }
//
// // Your custom validation logic
// return sslPolicyErrors == SslPolicyErrors.None;
// };
Factory = new ConnectionFactory
{
HostName = vpnServerIp,
Port = 5672,
VirtualHost = "/",
UserName = "consumer",
Password = "faceaddb5005815199f8366d3d15ff8a",
//AuthMechanisms = new IAuthMechanismFactory[] { new ExternalMechanismFactory() },
// Ssl = new SslOption
// {
// Enabled = true,
// ServerName = "Webserver-FrontAndBack",
// //Roots = new X509Certificate2Collection { caCertificate },
// //CertPath = "/etc/rabbitmq/testca/ca_certificate.pem",
// CertPath = "/etc/rabbitmq/client/client_certificate.pem",
//
//
// // CertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
// // {
// // //X509Certificate2 clientCertificate = new X509Certificate2("/etc/rabbitmq/client/client_certificate.pem");
// //
// // //X509Certificate2 caCertificate = new X509Certificate2("/etc/openvpn/client/ca-certificate");
// // X509Certificate2 caCertificate = new X509Certificate2("/etc/rabbitmq/testca/ca_certificate.pem");
// //
// //
// //
// // Console.WriteLine(certificate.Subject);
// // Console.WriteLine("---------------------------------");
// // //Console.WriteLine(certificate.GetPublicKey());
// // // Your custom validation logic using the CA certificate
// // // Return true if the certificate is valid, false otherwise
// // return certificate.Issuer == caCertificate.Subject;
// // }
// CertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
// {
// X509Certificate2 caCertificate = new X509Certificate2("/etc/rabbitmq/testca/ca_certificate.pem");
//
// // Add the CA certificate to the chain policy's extra store
// chain.ChainPolicy.ExtraStore.Add(caCertificate);
//
// // Check if the chain builds successfully
// bool chainIsValid = chain.Build((X509Certificate2)certificate);
//
// if (!chainIsValid)
// {
// Console.WriteLine("Certificate chain validation failed:");
//
// // Print details of each chain status
// foreach (var chainStatus in chain.ChainStatus)
// {
// Console.WriteLine($"Chain Status: {chainStatus.Status}");
// Console.WriteLine($"Chain Status Information: {chainStatus.StatusInformation}");
// // Add more details as needed
// // Check if the failure is due to UntrustedRoot
// if (chainStatus.Status == X509ChainStatusFlags.UntrustedRoot)
// {
// // Manually check if the root certificate is the expected one
// if (certificate.Issuer == caCertificate.Subject)
// {
// Console.WriteLine("Manually trusting the root certificate.");
// chainIsValid = true;
// }
// }
// }
//
//
// }
//
// // Additional validation logic if needed
// Console.WriteLine($"Certificate Subject: {certificate.Subject}"+chainIsValid);
//
// // Return true if the certificate is valid
// return chainIsValid;
// }
//}
};
Connection = Factory.CreateConnection();
Channel = Connection.CreateModel();
Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages");
Channel.QueueDeclare(queue: "statusQueue", durable: true, exclusive: false, autoDelete: false, arguments: null);
}
public static async Task StartRabbitMqConsumer()
{
var consumer = new EventingBasicConsumer(Channel);
consumer.Received += (_, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
lock (WebsocketManager.InstallationConnections)
{
//Consumer received a message
if (receivedStatusMessage != null)
{
Console.WriteLine("----------------------------------------------");
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
var installationId = receivedStatusMessage.InstallationId;
//This is a heartbit message, just update the timestamp for this installation.
//There is no need to notify the corresponding front-ends.
if (receivedStatusMessage.Type == MessageType.Heartbit)
{
Console.WriteLine("This is a heartbit message from installation: " + installationId);
}
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
{
InstallationId = receivedStatusMessage.InstallationId,
Description = warning.Description,
Date = warning.Date,
Time = warning.Time,
DeviceCreatedTheMessage = warning.CreatedBy,
Seen = false
};
//Create a new warning and add it to the database
Db.HandleWarning(newWarning, receivedStatusMessage.InstallationId);
}
}
//Traverse the Alarm list, and store each of them to the database
if (receivedStatusMessage.Alarms != null)
{
Console.WriteLine("Add an alarm for installation "+receivedStatusMessage.InstallationId);
foreach (var alarm in receivedStatusMessage.Alarms)
{
Error newError = new Error
{
InstallationId = receivedStatusMessage.InstallationId,
Description = alarm.Description,
Date = alarm.Date,
Time = alarm.Time,
DeviceCreatedTheMessage = alarm.CreatedBy,
Seen = false
};
//Create a new error and add it to the database
Db.HandleError(newError, receivedStatusMessage.InstallationId);
}
}
}
var prevStatus = 0;
//This installation id does not exist in our data structure, add it.
if (!WebsocketManager.InstallationConnections.ContainsKey(installationId))
{
prevStatus = -2;
Console.WriteLine("Create new empty list for installation: " + installationId);
WebsocketManager.InstallationConnections[installationId] = new InstallationInfo
{
Status = receivedStatusMessage.Status,
Timestamp = DateTime.Now
};
}
else
{
prevStatus = WebsocketManager.InstallationConnections[installationId].Status;
WebsocketManager.InstallationConnections[installationId].Status = receivedStatusMessage.Status;
WebsocketManager.InstallationConnections[installationId].Timestamp = DateTime.Now;
}
//Console.WriteLine("----------------------------------------------");
//Update all the connected front-ends regarding this installation
if(prevStatus != receivedStatusMessage.Status && WebsocketManager.InstallationConnections[installationId].Connections.Count > 0)
{
WebsocketManager.InformWebsocketsForInstallation(installationId);
}
}
}
};
Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
}
public static void InformInstallationsToSubscribeToRabbitMq()
{
var installationIps = Db.Installations.Select(inst => inst.VpnIp).ToList();
Console.WriteLine("Count is "+installationIps.Count);
var maxRetransmissions = 2;
UdpClient udpClient = new UdpClient();
udpClient.Client.ReceiveTimeout = 2000;
int port = 9000;
//Send a message to each installation and tell it to subscribe to the queue
using (udpClient)
{
for (int i = 0; i < installationIps.Count; i++)
{
if(installationIps[i]==""){continue;}
Console.WriteLine("-----------------------------------------------------------");
Console.WriteLine("Trying to reach installation with IP: " + installationIps[i]);
//Try at most MAX_RETRANSMISSIONS times to reach an installation.
for (int j = 0; j < maxRetransmissions; j++)
{
string message = "This is a message from RabbitMQ server, you can subscribe to the RabbitMQ queue";
byte[] data = Encoding.UTF8.GetBytes(message);
udpClient.Send(data, data.Length, installationIps[i], port);
Console.WriteLine($"Sent UDP message to {installationIps[i]}:{port}: {message}");
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(installationIps[i]), port);
try
{
byte[] replyData = udpClient.Receive(ref remoteEndPoint);
string replyMessage = Encoding.UTF8.GetString(replyData);
Console.WriteLine("Received " + replyMessage + " from installation " + installationIps[i]);
break;
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.TimedOut){Console.WriteLine("Timed out waiting for a response. Retry...");}
else{Console.WriteLine("Error: " + ex.Message);}
}
}
}
}
Console.WriteLine("Start RabbitMQ Consumer");
}
}

View File

@ -13,10 +13,7 @@ namespace InnovEnergy.App.Backend.Websockets;
public static class WebsocketManager public static class WebsocketManager
{ {
public static Dictionary<int, InstallationInfo> InstallationConnections = new Dictionary<int, InstallationInfo>(); public static Dictionary<int, InstallationInfo> InstallationConnections = new Dictionary<int, InstallationInfo>();
public static ConnectionFactory Factory = null!;
public static IConnection Connection = null!;
public static IModel Channel = null!;
public static void InformInstallationsToSubscribeToRabbitMq() public static void InformInstallationsToSubscribeToRabbitMq()
{ {
var installationIps = Db.Installations.Select(inst => inst.VpnIp).ToList(); var installationIps = Db.Installations.Select(inst => inst.VpnIp).ToList();
@ -82,99 +79,7 @@ public static class WebsocketManager
} }
} }
public static async Task StartRabbitMqConsumer()
{
var consumer = new EventingBasicConsumer(Channel);
consumer.Received += (_, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
lock (InstallationConnections)
{
//Consumer received a message
if (receivedStatusMessage != null)
{
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
Console.WriteLine("----------------------------------------------");
Console.WriteLine("Update installation connection table");
var installationId = receivedStatusMessage.InstallationId;
//This is a heartbit message, just update the timestamp for this installation.
//There is no need to notify the corresponding front-ends.
if (receivedStatusMessage.Type == MessageType.Heartbit)
{
InstallationConnections[installationId].Timestamp = DateTime.Now;
}
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
{
InstallationId = receivedStatusMessage.InstallationId,
Description = warning.Description,
Date = warning.Date,
Time = warning.Time,
DeviceCreatedTheMessage = warning.CreatedBy,
Seen = false
};
//Create a new warning and add it to the database
Db.HandleWarning(newWarning, receivedStatusMessage.InstallationId);
}
}
//Traverse the Alarm list, and store each of them to the database
if (receivedStatusMessage.Alarms != null)
{
foreach (var alarm in receivedStatusMessage.Alarms)
{
Error newError = new Error
{
InstallationId = receivedStatusMessage.InstallationId,
Description = alarm.Description,
Date = alarm.Date,
Time = alarm.Time,
DeviceCreatedTheMessage = alarm.CreatedBy,
Seen = false
};
//Create a new error and add it to the database
Db.HandleError(newError, receivedStatusMessage.InstallationId);
}
}
//This installation id does not exist in our data structure, add it.
if (!InstallationConnections.ContainsKey(installationId))
{
Console.WriteLine("Create new empty list for installation: " + installationId);
InstallationConnections[installationId] = new InstallationInfo
{
Status = receivedStatusMessage.Status,
Timestamp = DateTime.Now
};
}
else
{
InstallationConnections[installationId].Status = receivedStatusMessage.Status;
InstallationConnections[installationId].Timestamp = DateTime.Now;
}
Console.WriteLine("----------------------------------------------");
//Update all the connected front-ends regarding this installation
if(InstallationConnections[installationId].Connections.Count > 0)
{
InformWebsocketsForInstallation(installationId);
}
}
}
}
};
Channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
}
//Inform all the connected websockets regarding installation "installationId" //Inform all the connected websockets regarding installation "installationId"
public static void InformWebsocketsForInstallation(int installationId) public static void InformWebsocketsForInstallation(int installationId)

View File

@ -1,9 +0,0 @@
using System.Net.WebSockets;
namespace InnovEnergy.App.Middleware;
public class InstallationInfo
{
public int Status { get; set; }
public List<WebSocket> Connections { get; } = new List<WebSocket>();
}

View File

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="../InnovEnergy.App.props" />
<PropertyGroup>
<RootNamespace>InnovEnergy.App.Middleware</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RabbitMQ.Client" Version="6.6.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Lib\Utils\Utils.csproj" />
</ItemGroup>
</Project>

View File

@ -1,66 +0,0 @@
using InnovEnergy.App.Middleware;
using System;
using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using InnovEnergy.Lib.Utils;
internal class Program
{
public static readonly object SharedDataLock = new object();
public static async Task Main(string[] args)
{
//For each installation id, we maintain a list of the connected clients
var installationConnections = new Dictionary<int, InstallationInfo>();
var installationsIds = new List<int> {1};
var installationIps = new List<string> {"10.2.3.115"};
var MAX_RETRANSMISSIONS = 2;
RabbitMqConsumer.StartRabbitMqConsumer(installationConnections,SharedDataLock);
UdpClient udpClient = new UdpClient();
udpClient.Client.ReceiveTimeout = 2000;
int port = 9000;
//Send a message to each installation and tell it to subscribe to the queue
for (int i = 0; i < installationsIds.Count; i++)
{
using (udpClient)
{
//Try at most MAX_RETRANSMISSIONS times to reach an installation.
for (int j = 0; j < MAX_RETRANSMISSIONS; j++)
{
string message = "This is a message from RabbitMQ server, you can subscribe to the RabbitMQ queue";
byte[] data = Encoding.UTF8.GetBytes(message);
udpClient.Send(data, data.Length, installationIps[i], port);
Console.WriteLine($"Sent UDP message to {installationIps[i]}:{port}: {message}");
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(installationIps[i]), port);
try
{
byte[] replyData = udpClient.Receive(ref remoteEndPoint);
string replyMessage = Encoding.UTF8.GetString(replyData);
Console.WriteLine("Received message from installation " + installationsIds[i]);
break;
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.TimedOut)
{
Console.WriteLine("Timed out waiting for a response. Retry...");
}
else
{
Console.WriteLine("Error: " + ex.Message);
}
}
}
}
}
Console.WriteLine("WebSocket server is running. Press Enter to exit.");
await WebSocketListener.StartServerAsync(installationConnections,SharedDataLock);
}
}

View File

@ -1,88 +0,0 @@
using System.Net.WebSockets;
using System.Text.Json;
namespace InnovEnergy.App.Middleware;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
public static class RabbitMqConsumer
{
private static ConnectionFactory _factory = null!;
private static IConnection _connection = null!;
private static IModel _channel= null!;
public static void StartRabbitMqConsumer(Dictionary<Int32, InstallationInfo> installationConnections, Object sharedDataLock)
{
string vpnServerIp = "194.182.190.208";
_factory = new ConnectionFactory { HostName = "localhost" };
_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) => Callback(installationConnections, sharedDataLock, ea);
_channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
}
private static void Callback(Dictionary<Int32, InstallationInfo> installationConnections, Object sharedDataLock, BasicDeliverEventArgs ea)
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
lock (sharedDataLock)
{
// Process the received message
if (receivedStatusMessage != null)
{
Console.WriteLine("Received a message from installation: " + receivedStatusMessage.InstallationId + " and status is: " + receivedStatusMessage.Status);
Console.WriteLine("----------------------------------------------");
Console.WriteLine("Update installation connection table");
var installationId = receivedStatusMessage.InstallationId;
if (!installationConnections.ContainsKey(installationId))
{
Console.WriteLine("Create new empty list for installation: " + installationId);
installationConnections[installationId] = new InstallationInfo
{
Status = receivedStatusMessage.Status
};
}
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in installationConnections)
{
if (installationConnection.Key == installationId && installationConnection.Value.Connections.Count > 0)
{
Console.WriteLine("Update all the connected websockets for installation " + installationId);
installationConnection.Value.Status = receivedStatusMessage.Status;
var jsonObject = new
{
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
);
}
}
}
}
}
}
}

View File

@ -1,8 +0,0 @@
namespace InnovEnergy.App.Middleware;
public class StatusMessage
{
public required int InstallationId { get; init; }
public required int Status { get; init; }
}

View File

@ -1,129 +0,0 @@
using System.Net;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
namespace InnovEnergy.App.Middleware;
public static class WebSocketListener
{
public static async Task StartServerAsync(Dictionary<Int32, InstallationInfo> installationConnections, Object sharedDataLock)
{
var listener = new HttpListener();
listener.Prefixes.Add("http://127.0.0.1:8080/");
listener.Start();
//Http listener listens for connections. When it accepts a new connection, it creates a new Task to handle this connection
while (true)
{
var context = await listener.GetContextAsync();
if (context.Request.IsWebSocketRequest)
{
var webSocketContext = await context.AcceptWebSocketAsync(null);
var webSocket = webSocketContext.WebSocket;
// Add the connected WebSocket to the collection
Console.WriteLine("Accepted a new websocket connection");
HandleWebSocketConnection(webSocket, installationConnections);
}
else
{
context.Response.StatusCode = 400;
context.Response.Close();
}
}
//We have a task per websocket connection
async Task HandleWebSocketConnection(WebSocket currentWebSocket, Dictionary<Int32, InstallationInfo> installationConnections)
{
var buffer = new byte[4096];
try
{
while (currentWebSocket.State == WebSocketState.Open)
{
//Listen for incoming messages on this WebSocket
var result = await currentWebSocket.ReceiveAsync(buffer, CancellationToken.None);
Console.WriteLine("Received a new message from websocket");
if (result.MessageType != WebSocketMessageType.Text)
continue;
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
var installationIds = JsonSerializer.Deserialize<int[]>(message);
lock (sharedDataLock)
{
//Each front-end will send the list of the installations it wants to access
//If this is a new key (installation id), initialize the list for this key and then add the websocket object for this client
//Then, report the status of each requested installation to the front-end that created the websocket connection
foreach (var installationId in installationIds)
{
if (!installationConnections.ContainsKey(installationId))
{
Console.WriteLine("Create new empty list for installation id "+installationId);
installationConnections[installationId] = new InstallationInfo
{
Status = -2
};
}
installationConnections[installationId].Connections.Add(currentWebSocket);
var jsonObject = new
{
id = installationId,
status = installationConnections[installationId].Status
};
var jsonString = JsonSerializer.Serialize(jsonObject);
var dataToSend = Encoding.UTF8.GetBytes(jsonString);
currentWebSocket.SendAsync(dataToSend,
WebSocketMessageType.Text,
true, // Indicates that this is the end of the message
CancellationToken.None
);
}
Console.WriteLine("Printing installation connection list");
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in installationConnections)
{
Console.WriteLine("Installation ID: " + installationConnection.Key + " Number of Connections: " + installationConnection.Value.Connections.Count);
}
Console.WriteLine("----------------------------------------------");
}
}
//When the front-end terminates the connection, the following code will be executed
Console.WriteLine("The connection has been terminated");
foreach (var installationConnection in installationConnections)
{
if (installationConnection.Value.Connections.Contains(currentWebSocket))
{
installationConnection.Value.Connections.Remove(currentWebSocket);
}
}
await currentWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Connection closed by server", CancellationToken.None);
//Print the installationConnections dictionary after deleting a websocket
Console.WriteLine("Print the installation connections list after deleting a websocket");
Console.WriteLine("----------------------------------------------");
foreach (var installationConnection in installationConnections)
{
Console.WriteLine("Installation ID: "+ installationConnection.Key+" Number of Connections: "+installationConnection.Value.Connections.Count);
}
Console.WriteLine("----------------------------------------------");
}
catch (Exception ex)
{
Console.WriteLine("WebSocket error: " + ex.Message);
}
}
}
}

View File

@ -1 +0,0 @@
dotnet publish Middleware.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:~/middleware

View File

@ -0,0 +1,34 @@
#!/bin/bash
dotnet_version='net6.0'
salimax_ip="$1"
username='ie-entwicklung'
root_password='Salimax4x25'
set -e
echo -e "\n============================ Build ============================\n"
dotnet publish \
./SaliMax.csproj \
-p:PublishTrimmed=false \
-c Release \
-r linux-x64
echo -e "\n============================ Deploy ============================\n"
ip_addresses=("10.2.3.115" "10.2.3.104" "10.2.4.33" "10.2.4.32" "10.2.4.36" "10.2.4.35" "10.2.4.154" "10.2.4.113" "10.2.4.29")
for ip_address in "${ip_addresses[@]}"; do
rsync -v \
--exclude '*.pdb' \
./bin/Release/$dotnet_version/linux-x64/publish/* \
$username@"$ip_address":~/salimax
ssh "$username"@"$ip_address" "cd salimax && echo '$root_password' | sudo -S ./restart"
echo "Deployed and ran commands on $ip_address"
done

View File

@ -4,6 +4,7 @@ using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Threading.Tasks; using System.Reactive.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using Flurl.Http; using Flurl.Http;
using InnovEnergy.App.SaliMax.Devices; using InnovEnergy.App.SaliMax.Devices;
@ -50,8 +51,9 @@ internal static class Program
private static readonly Channel RelaysChannel ; private static readonly Channel RelaysChannel ;
private static readonly Channel BatteriesChannel ; private static readonly Channel BatteriesChannel ;
private const String VpnServerIp = "194.182.190.208"; //private const String VpnServerIp = "194.182.190.208";
private const String VpnServerIp = "10.2.0.11";
private static IPAddress? _controllerIpAddress; private static IPAddress? _controllerIpAddress;
private static UdpClient _udpListener = null!; private static UdpClient _udpListener = null!;
private static ConnectionFactory? _factory ; private static ConnectionFactory? _factory ;
@ -262,6 +264,7 @@ internal static class Program
{ {
var s3Bucket = Config.Load().S3?.Bucket; var s3Bucket = Config.Load().S3?.Bucket;
//Every 15 iterations(30 seconds), the installation sends a heartbit message to the queue
_heartBitInterval++; _heartBitInterval++;
//When the controller boots, it tries to subscribe to the queue //When the controller boots, it tries to subscribe to the queue
@ -269,14 +272,9 @@ internal static class Program
{ {
_subscribeToQueueForTheFirstTime = true; _subscribeToQueueForTheFirstTime = true;
SubscribeToQueue(currentSalimaxState, s3Bucket); SubscribeToQueue(currentSalimaxState, s3Bucket);
if (_subscribedToQueue && currentSalimaxState.Status != _prevSalimaxState)
{
_prevSalimaxState = currentSalimaxState.Status;
}
} }
//If already subscribed to the queue and the status has been changed, update the queue //If already subscribed to the queue and the status has been changed, update the queue
else if (_subscribedToQueue && currentSalimaxState.Status != _prevSalimaxState) if (_subscribedToQueue && currentSalimaxState.Status != _prevSalimaxState)
{ {
_prevSalimaxState = currentSalimaxState.Status; _prevSalimaxState = currentSalimaxState.Status;
if (s3Bucket != null) if (s3Bucket != null)
@ -284,6 +282,7 @@ internal static class Program
} }
else if (_subscribedToQueue && _heartBitInterval>=15) else if (_subscribedToQueue && _heartBitInterval>=15)
{ {
//Send a heartbit to the backend
Console.WriteLine("----------------------------------------Sending Heartbit----------------------------------------"); Console.WriteLine("----------------------------------------Sending Heartbit----------------------------------------");
_heartBitInterval = 0; _heartBitInterval = 0;
currentSalimaxState.Type = MessageType.Heartbit; currentSalimaxState.Type = MessageType.Heartbit;
@ -317,14 +316,39 @@ internal static class Program
{ {
try try
{ {
_factory = new ConnectionFactory { HostName = VpnServerIp }; //_factory = new ConnectionFactory { HostName = VpnServerIp };
_factory = new ConnectionFactory
{
HostName = VpnServerIp,
Port = 5672,
VirtualHost = "/",
UserName = "producer",
Password = "b187ceaddb54d5485063ddc1d41af66f",
// Ssl = new SslOption
// {
// Enabled = true,
// ServerName = VpnServerIp, // Disable hostname validation
// CertPath = "/etc/openvpn/client/client-certificate",
//
// CertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
// {
// X509Certificate2 caCertificate = new X509Certificate2("/etc/openvpn/client/ca-certificate");
// //Console.WriteLine(caCertificate);
// //Console.WriteLine("---------------------------------");
// //Console.WriteLine(certificate.GetPublicKey());
// // Your custom validation logic using the CA certificate
// // Return true if the certificate is valid, false otherwise
// return certificate.Issuer == caCertificate.Subject;
// }
// }
};
_connection = _factory.CreateConnection(); _connection = _factory.CreateConnection();
_channel = _connection.CreateModel(); _channel = _connection.CreateModel();
_channel.QueueDeclare(queue: "statusQueue", durable: true, exclusive: false, autoDelete: false, arguments: null); _channel.QueueDeclare(queue: "statusQueue", durable: true, exclusive: false, autoDelete: false, arguments: null);
Console.WriteLine("The controller sends its status to the middleware for the first time"); Console.WriteLine("The controller sends its status to the middleware for the first time");
if (s3Bucket != null) InformMiddleware(currentSalimaxState); if (s3Bucket != null) InformMiddleware(currentSalimaxState);
_subscribedToQueue = true; _subscribedToQueue = true;

View File

@ -48,8 +48,8 @@ function Configuration(props: ConfigurationProps) {
<TextField <TextField
label={ label={
<FormattedMessage <FormattedMessage
id="calibration_charge_forced" id="forced_calibration_charge"
defaultMessage="Calibration Charge forced" defaultMessage="Forced Calibration Charge"
/> />
} }
value={props.values.calibrationChargeForced.values[0].value} value={props.values.calibrationChargeForced.values[0].value}

View File

@ -80,7 +80,7 @@ function installationForm(props: installationFormProps) {
<Box <Box
sx={{ sx={{
position: 'absolute', position: 'absolute',
top: isMobile ? '50%' : '30%', top: isMobile ? '50%' : '40%',
left: '50%', left: '50%',
transform: 'translate(-50%, -50%)', transform: 'translate(-50%, -50%)',
width: 500, width: 500,
@ -152,10 +152,11 @@ function installationForm(props: installationFormProps) {
error={formValues.country === ''} error={formValues.country === ''}
/> />
</div> </div>
<div> <div>
<TextField <TextField
label={<FormattedMessage id="VpnIp" defaultMessage="VpnIp" />} label={<FormattedMessage id="VpnIp" defaultMessage="VpnIp" />}
name="VpnIp" name="vpnIp"
value={formValues.vpnIp} value={formValues.vpnIp}
onChange={handleChange} onChange={handleChange}
fullWidth fullWidth

View File

@ -95,7 +95,7 @@ function UsersSearch() {
> >
<FormattedMessage <FormattedMessage
id="successfullyCreatedUser" id="successfullyCreatedUser"
defaultMessage="Successfully Updated User" defaultMessage="Successfully Created User"
/> />
<IconButton <IconButton

View File

@ -215,7 +215,7 @@ function userForm(props: userFormProps) {
<Box <Box
sx={{ sx={{
position: 'absolute', position: 'absolute',
top: isMobile ? '50%' : '30%', top: isMobile ? '50%' : '40%',
left: '50%', left: '50%',
transform: 'translate(-50%, -50%)', transform: 'translate(-50%, -50%)',
width: 500, width: 500,