Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
8a9859575e
|
@ -23,6 +23,7 @@
|
||||||
<PackageReference Include="Microsoft.Owin.Security" Version="4.2.2" />
|
<PackageReference Include="Microsoft.Owin.Security" Version="4.2.2" />
|
||||||
<PackageReference Include="Microsoft.Owin.Security.Cookies" Version="4.2.2" />
|
<PackageReference Include="Microsoft.Owin.Security.Cookies" Version="4.2.2" />
|
||||||
<PackageReference Include="Microsoft.Owin.Security.OAuth" Version="4.2.2" />
|
<PackageReference Include="Microsoft.Owin.Security.OAuth" Version="4.2.2" />
|
||||||
|
<PackageReference Include="RabbitMQ.Client" Version="6.6.0" />
|
||||||
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
|
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
|
||||||
<PackageReference Include="sqlite-net-sqlcipher" Version="1.9.141-beta" />
|
<PackageReference Include="sqlite-net-sqlcipher" Version="1.9.141-beta" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||||
|
@ -51,4 +52,11 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="RabbitMQ.Client">
|
||||||
|
<HintPath>..\..\..\..\..\..\.nuget\packages\rabbitmq.client\6.6.0\lib\netstandard2.0\RabbitMQ.Client.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using InnovEnergy.App.Backend.DataTypes;
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
using InnovEnergy.App.Backend.DataTypes.Methods;
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
using InnovEnergy.App.Backend.Relations;
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
using InnovEnergy.App.Backend.Websockets;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
@ -9,10 +14,12 @@ namespace InnovEnergy.App.Backend;
|
||||||
|
|
||||||
using Token = String;
|
using Token = String;
|
||||||
|
|
||||||
|
|
||||||
[Controller]
|
[Controller]
|
||||||
[Route("api/")]
|
[Route("api/")]
|
||||||
public class Controller : ControllerBase
|
public class Controller : ControllerBase
|
||||||
{
|
{
|
||||||
|
|
||||||
[HttpPost(nameof(Login))]
|
[HttpPost(nameof(Login))]
|
||||||
public ActionResult<Session> Login(String username, String? password)
|
public ActionResult<Session> Login(String username, String? password)
|
||||||
{
|
{
|
||||||
|
@ -47,6 +54,31 @@ public class Controller : ControllerBase
|
||||||
: Unauthorized();
|
: Unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet(nameof(CreateWebSocket))]
|
||||||
|
public async Task CreateWebSocket(Token authToken)
|
||||||
|
{
|
||||||
|
var session = Db.GetSession(authToken)?.User;
|
||||||
|
|
||||||
|
if (session is null)
|
||||||
|
{
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||||
|
HttpContext.Abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||||
|
{
|
||||||
|
HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||||
|
HttpContext.Abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var webSocketContext = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||||
|
var webSocket = webSocketContext;
|
||||||
|
|
||||||
|
//Handle the WebSocket connection
|
||||||
|
await WebsocketManager.HandleWebSocketConnection(webSocket);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet(nameof(GetUserById))]
|
[HttpGet(nameof(GetUserById))]
|
||||||
public ActionResult<User> GetUserById(Int64 id, Token authToken)
|
public ActionResult<User> GetUserById(Int64 id, Token authToken)
|
||||||
|
|
|
@ -44,7 +44,7 @@ public static class ExoCmd
|
||||||
|
|
||||||
messageToSign += time;
|
messageToSign += time;
|
||||||
|
|
||||||
Console.WriteLine("Message to sign:\n" + messageToSign);
|
//Console.WriteLine("Message to sign:\n" + messageToSign);
|
||||||
|
|
||||||
|
|
||||||
var hmac = HmacSha256Digest(messageToSign, Secret);
|
var hmac = HmacSha256Digest(messageToSign, Secret);
|
||||||
|
@ -108,7 +108,7 @@ public static class ExoCmd
|
||||||
if (response.StatusCode != HttpStatusCode.OK){
|
if (response.StatusCode != HttpStatusCode.OK){
|
||||||
Console.WriteLine("Fuck");
|
Console.WriteLine("Fuck");
|
||||||
}
|
}
|
||||||
Console.WriteLine($"Created Key for {installation.Name}");
|
//Console.WriteLine($"Created Key for {installation.Name}");
|
||||||
var responseString = await response.Content.ReadAsStringAsync();
|
var responseString = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
var responseJson = JsonNode.Parse(responseString) ;
|
var responseJson = JsonNode.Parse(responseString) ;
|
||||||
|
@ -155,7 +155,7 @@ public static class ExoCmd
|
||||||
var response = await client.PostAsync(url, content);
|
var response = await client.PostAsync(url, content);
|
||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync();
|
var responseString = await response.Content.ReadAsStringAsync();
|
||||||
Console.WriteLine(responseString);
|
//Console.WriteLine(responseString);
|
||||||
|
|
||||||
//Put Role ID into database
|
//Put Role ID into database
|
||||||
var id = JsonNode.Parse(responseString)!["reference"]!["id"]!.GetValue<String>();
|
var id = JsonNode.Parse(responseString)!["reference"]!["id"]!.GetValue<String>();
|
||||||
|
@ -232,7 +232,7 @@ public static class ExoCmd
|
||||||
var response = await client.PostAsync(url, content);
|
var response = await client.PostAsync(url, content);
|
||||||
|
|
||||||
var responseString = await response.Content.ReadAsStringAsync();
|
var responseString = await response.Content.ReadAsStringAsync();
|
||||||
Console.WriteLine(responseString);
|
//Console.WriteLine(responseString);
|
||||||
|
|
||||||
//Put Role ID into database
|
//Put Role ID into database
|
||||||
var id = JsonNode.Parse(responseString)!["reference"]!["id"]!.GetValue<String>();
|
var id = JsonNode.Parse(responseString)!["reference"]!["id"]!.GetValue<String>();
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Hellang.Middleware.ProblemDetails;
|
using Hellang.Middleware.ProblemDetails;
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.App.Backend.Websockets;
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
@ -15,6 +16,8 @@ public static class Program
|
||||||
Db.Init();
|
Db.Init();
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
WebsocketManager.InformInstallationsToSubscribeToRabbitMq();
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddProblemDetails(setup =>
|
builder.Services.AddProblemDetails(setup =>
|
||||||
{
|
{
|
||||||
|
@ -48,6 +51,7 @@ public static class Program
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.UseWebSockets();
|
||||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||||
{
|
{
|
||||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
|
||||||
|
public class InstallationInfo
|
||||||
|
{
|
||||||
|
public int Status { get; set; }
|
||||||
|
public List<WebSocket> Connections { get; } = new List<WebSocket>();
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
public class StatusMessage
|
||||||
|
{
|
||||||
|
public required int InstallationId { get; init; }
|
||||||
|
public required int Status { get; init; }
|
||||||
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
using RabbitMQ.Client.Events;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Websockets;
|
||||||
|
|
||||||
|
public static class WebsocketManager
|
||||||
|
{
|
||||||
|
public static readonly Dictionary<int, InstallationInfo> InstallationConnections = new Dictionary<int, InstallationInfo>();
|
||||||
|
private static ConnectionFactory _factory = null!;
|
||||||
|
private static IConnection _connection = null!;
|
||||||
|
private static IModel _channel = null!;
|
||||||
|
|
||||||
|
public static void InformInstallationsToSubscribeToRabbitMq()
|
||||||
|
{
|
||||||
|
var installationsIds = new List<int> { 1 };
|
||||||
|
var installationIps = new List<string> { "10.2.3.115" };
|
||||||
|
var maxRetransmissions = 2;
|
||||||
|
|
||||||
|
StartRabbitMqConsumer();
|
||||||
|
|
||||||
|
UdpClient udpClient = new UdpClient();
|
||||||
|
udpClient.Client.ReceiveTimeout = 2000;
|
||||||
|
int port = 9000;
|
||||||
|
//Send a message to each installation and tell it to subscribe to the queue
|
||||||
|
for (int i = 0; i < installationsIds.Count; i++)
|
||||||
|
{
|
||||||
|
using (udpClient)
|
||||||
|
{
|
||||||
|
//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 " + 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void StartRabbitMqConsumer()
|
||||||
|
{
|
||||||
|
string vpnServerIp = "194.182.190.208";
|
||||||
|
//string vpnServerIp = "127.0.0.1";
|
||||||
|
_factory = new ConnectionFactory { HostName = vpnServerIp};
|
||||||
|
_connection = _factory.CreateConnection();
|
||||||
|
_channel = _connection.CreateModel();
|
||||||
|
Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages");
|
||||||
|
_channel.QueueDeclare(queue: "statusQueue", durable: false, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
|
||||||
|
var consumer = new EventingBasicConsumer(_channel);
|
||||||
|
consumer.Received += (_, ea) => CallbackReceiveMessageFromQueue(ea);
|
||||||
|
_channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||||
|
private static void CallbackReceiveMessageFromQueue(BasicDeliverEventArgs ea)
|
||||||
|
{
|
||||||
|
var body = ea.Body.ToArray();
|
||||||
|
var message = Encoding.UTF8.GetString(body);
|
||||||
|
StatusMessage? receivedStatusMessage = JsonSerializer.Deserialize<StatusMessage>(message);
|
||||||
|
|
||||||
|
lock (InstallationConnections)
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
|
||||||
|
public static async Task HandleWebSocketConnection(WebSocket currentWebSocket)
|
||||||
|
{
|
||||||
|
var buffer = new byte[4096];
|
||||||
|
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 (InstallationConnections)
|
||||||
|
{
|
||||||
|
//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
|
||||||
|
if (installationIds != null)
|
||||||
|
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("----------------------------------------------");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (InstallationConnections)
|
||||||
|
{
|
||||||
|
//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);
|
||||||
|
lock (InstallationConnections)
|
||||||
|
{
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,11 +25,11 @@ public static class Program
|
||||||
ExplainNode();
|
ExplainNode();
|
||||||
ExplainExit();
|
ExplainExit();
|
||||||
|
|
||||||
Console.WriteLine("");
|
//Console.WriteLine("");
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Console.WriteLine("");
|
//Console.WriteLine("");
|
||||||
Console.Write($"node{tunnel.Node}> ");
|
Console.Write($"node{tunnel.Node}> ");
|
||||||
|
|
||||||
var cmd = Console.ReadLine()?.ToUpper().Trim();
|
var cmd = Console.ReadLine()?.ToUpper().Trim();
|
||||||
|
|
|
@ -4,6 +4,7 @@ using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
|
@ -16,6 +17,7 @@ internal class Program
|
||||||
var installationsIds = new List<int> {1};
|
var installationsIds = new List<int> {1};
|
||||||
var installationIps = new List<string> {"10.2.3.115"};
|
var installationIps = new List<string> {"10.2.3.115"};
|
||||||
var MAX_RETRANSMISSIONS = 2;
|
var MAX_RETRANSMISSIONS = 2;
|
||||||
|
|
||||||
RabbitMqConsumer.StartRabbitMqConsumer(installationConnections,SharedDataLock);
|
RabbitMqConsumer.StartRabbitMqConsumer(installationConnections,SharedDataLock);
|
||||||
|
|
||||||
UdpClient udpClient = new UdpClient();
|
UdpClient udpClient = new UdpClient();
|
||||||
|
@ -58,8 +60,6 @@ internal class Program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("WebSocket server is running. Press Enter to exit.");
|
|
||||||
|
|
||||||
Console.WriteLine("WebSocket server is running. Press Enter to exit.");
|
Console.WriteLine("WebSocket server is running. Press Enter to exit.");
|
||||||
await WebSocketListener.StartServerAsync(installationConnections,SharedDataLock);
|
await WebSocketListener.StartServerAsync(installationConnections,SharedDataLock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,72 +15,74 @@ public static class RabbitMqConsumer
|
||||||
public static void StartRabbitMqConsumer(Dictionary<Int32, InstallationInfo> installationConnections, Object sharedDataLock)
|
public static void StartRabbitMqConsumer(Dictionary<Int32, InstallationInfo> installationConnections, Object sharedDataLock)
|
||||||
{
|
{
|
||||||
string vpnServerIp = "194.182.190.208";
|
string vpnServerIp = "194.182.190.208";
|
||||||
_factory = new ConnectionFactory { HostName = vpnServerIp };
|
_factory = new ConnectionFactory { HostName = "localhost" };
|
||||||
_connection = _factory.CreateConnection();
|
_connection = _factory.CreateConnection();
|
||||||
_channel = _connection.CreateModel();
|
_channel = _connection.CreateModel();
|
||||||
Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages");
|
Console.WriteLine("Middleware subscribed to RabbitMQ queue, ready for receiving messages");
|
||||||
_channel.QueueDeclare(queue: "statusQueue", durable: false, exclusive: false, autoDelete: false, arguments: null);
|
_channel.QueueDeclare(queue: "statusQueue", durable: false, exclusive: false, autoDelete: false, arguments: null);
|
||||||
|
|
||||||
var consumer = new EventingBasicConsumer(_channel);
|
var consumer = new EventingBasicConsumer(_channel);
|
||||||
consumer.Received += (model, ea) =>
|
consumer.Received += (_, ea) => Callback(installationConnections, sharedDataLock, 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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
_channel.BasicConsume(queue: "statusQueue", autoAck: true, consumer: consumer);
|
_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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
"MinSoc": Number, 0 - 100 this is the minimum State of Charge that the batteries must not go below,
|
||||||
|
"ForceCalibrationCharge": Boolean (true or false), A flag to force a calibration charge,
|
||||||
|
"DisplayIndividualBatteries": Boolean (true or false), To display the indvidual batteries
|
||||||
|
"PConstant": Number 0 - 1, P value of our controller.
|
||||||
|
"GridSetPoint": Number in Watts, The set point of our controller.
|
||||||
|
"BatterySelfDischargePower": Number, 200, this a physical measurement of the self discharging power.
|
||||||
|
"HoldSocZone": Number, 1, This is magic number for the soft landing factor.
|
||||||
|
"IslandMode": { // Dc Link Voltage in Island mode
|
||||||
|
"AcDc": {
|
||||||
|
"MaxDcLinkVoltage": Number, 810, Max Dc Link Voltage,
|
||||||
|
"MinDcLinkVoltage": Number, 690, Min Dc Link Voltage,
|
||||||
|
"ReferenceDcLinkVoltage": Number, 750, Reference Dc Link
|
||||||
|
},
|
||||||
|
"DcDc": {
|
||||||
|
"LowerDcLinkVoltage": Number, 50, Lower Dc Link Window ,
|
||||||
|
"ReferenceDcLinkVoltage": 750, reference Dc Link
|
||||||
|
"UpperDcLinkVoltage": Number, 50, Upper Dc Link Window ,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GridTie": {// Dc Link Voltage in GrieTie mode
|
||||||
|
"AcDc": {
|
||||||
|
"MaxDcLinkVoltage":Number, 780, Max Dc Link Voltage,
|
||||||
|
"MinDcLinkVoltage": Number, 690, Min Dc Link Voltage,
|
||||||
|
"ReferenceDcLinkVoltage": Number, 750, Reference Dc Link
|
||||||
|
},
|
||||||
|
"DcDc": {
|
||||||
|
"LowerDcLinkVoltage": Number, 20, Lower Dc Link Window ,
|
||||||
|
"ReferenceDcLinkVoltage": 750, reference Dc Link
|
||||||
|
"UpperDcLinkVoltage": Number, 20, Upper Dc Link Window ,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MaxBatteryChargingCurrent":Number, 0 - 210, Max Charging current by DcDc
|
||||||
|
"MaxBatteryDischargingCurrent":Number, 0 - 210, Max Discharging current by DcDc
|
||||||
|
"MaxDcPower": Number, 0 - 10000, Max Power exported/imported by DcDc (10000 is the maximum)
|
||||||
|
"MaxChargeBatteryVoltage": Number, 57, Max Charging battery Voltage
|
||||||
|
"MinDischargeBatteryVoltage": Number, 0, Min Charging Battery Voltage
|
||||||
|
"Devices": { This is All Salimax devices (including offline ones)
|
||||||
|
"RelaysIp": {
|
||||||
|
"DeviceState": 1, // 0: is not present, 1: Present and Can be mesured, 2: Present but must be computed/calculted
|
||||||
|
"Host": "10.0.1.1", // Ip @ of the device in the local network
|
||||||
|
"Port": 502 // port
|
||||||
|
},
|
||||||
|
"GridMeterIp": {
|
||||||
|
"DeviceState": 1,
|
||||||
|
"Host": "10.0.4.1",
|
||||||
|
"Port": 502
|
||||||
|
},
|
||||||
|
"PvOnAcGrid": {
|
||||||
|
"DeviceState": 0, // If a device is not present
|
||||||
|
"Host": "false", // this is not important
|
||||||
|
"Port": 0 // this is not important
|
||||||
|
},
|
||||||
|
"LoadOnAcGrid": {
|
||||||
|
"DeviceState": 2, // this is a computed device
|
||||||
|
"Host": "true",
|
||||||
|
"Port": 0
|
||||||
|
},
|
||||||
|
"PvOnAcIsland": {
|
||||||
|
"DeviceState": 0,
|
||||||
|
"Host": "false",
|
||||||
|
"Port": 0
|
||||||
|
},
|
||||||
|
"IslandBusLoadMeterIp": {
|
||||||
|
"DeviceState": 1,
|
||||||
|
"Host": "10.0.4.2",
|
||||||
|
"Port": 502
|
||||||
|
},
|
||||||
|
"TruConvertAcIp": {
|
||||||
|
"DeviceState": 1,
|
||||||
|
"Host": "10.0.2.1",
|
||||||
|
"Port": 502
|
||||||
|
},
|
||||||
|
"PvOnDc": {
|
||||||
|
"DeviceState": 1,
|
||||||
|
"Host": "10.0.5.1",
|
||||||
|
"Port": 502
|
||||||
|
},
|
||||||
|
"LoadOnDc": {
|
||||||
|
"DeviceState": 0,
|
||||||
|
"Host": "false",
|
||||||
|
"Port": 0
|
||||||
|
},
|
||||||
|
"TruConvertDcIp": {
|
||||||
|
"DeviceState": 1,
|
||||||
|
"Host": "10.0.3.1",
|
||||||
|
"Port": 502
|
||||||
|
},
|
||||||
|
"BatteryIp": {
|
||||||
|
"DeviceState": 1,
|
||||||
|
"Host": "localhost",
|
||||||
|
"Port": 6855
|
||||||
|
},
|
||||||
|
"BatteryNodes": [ // this is a list of battery nodes
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"S3": { // this is parameters of S3 Buckets and co
|
||||||
|
"Bucket": "8-3e5b3069-214a-43ee-8d85-57d72000c19d",
|
||||||
|
"Region": "sos-ch-dk-2",
|
||||||
|
"Provider": "exo.io",
|
||||||
|
"Key": "EXO502627299197f83e8b090f63",
|
||||||
|
"Secret": "jUNYJL6B23WjndJnJlgJj4rc1i7uh981u5Aba5xdA5s",
|
||||||
|
"ContentType": "text/plain; charset=utf-8",
|
||||||
|
"Host": "8-3e5b3069-214a-43ee-8d85-57d72000c19d.sos-ch-dk-2.exo.io",
|
||||||
|
"Url": "https://8-3e5b3069-214a-43ee-8d85-57d72000c19d.sos-ch-dk-2.exo.io"
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
export const axiosConfigWithoutToken = axios.create({
|
export const axiosConfigWithoutToken = axios.create({
|
||||||
baseURL: 'https://monitor.innov.energy/api'
|
//baseURL: 'https://monitor.innov.energy/api'
|
||||||
|
baseURL: 'http://127.0.0.1:7087/api'
|
||||||
});
|
});
|
||||||
|
|
||||||
const axiosConfig = axios.create({
|
const axiosConfig = axios.create({
|
||||||
baseURL: 'https://monitor.innov.energy/api'
|
//baseURL: 'https://monitor.innov.energy/api'
|
||||||
|
baseURL: 'http://127.0.0.1:7087/api'
|
||||||
});
|
});
|
||||||
|
|
||||||
axiosConfig.defaults.params = {};
|
axiosConfig.defaults.params = {};
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Box, Grid, useTheme } from '@mui/material';
|
import { Box, Grid, useTheme } from '@mui/material';
|
||||||
import InstallationTree from './InstallationTree';
|
import InstallationTree from './InstallationTree';
|
||||||
import InstallationsContextProvider from 'src/contexts/InstallationsContextProvider';
|
import InstallationsContextProvider from 'src/contexts/InstallationsContextProvider';
|
||||||
import LogContextProvider from '../../../contexts/LogContextProvider';
|
|
||||||
|
|
||||||
function TreeView() {
|
function TreeView() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
@ -10,9 +9,7 @@ function TreeView() {
|
||||||
<InstallationsContextProvider>
|
<InstallationsContextProvider>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Box p={4}>
|
<Box p={4}>
|
||||||
<LogContextProvider>
|
<InstallationTree />
|
||||||
<InstallationTree />
|
|
||||||
</LogContextProvider>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
</InstallationsContextProvider>
|
</InstallationsContextProvider>
|
||||||
|
|
Loading…
Reference in New Issue