Innovenergy_trunk/csharp/App/Backend/Websockets/WebsockerManager.cs

213 lines
10 KiB
C#

using System.Net;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.Backend.Websockets;
public static class WebsocketManager
{
public static Dictionary<Int64, InstallationInfo> InstallationConnections = new Dictionary<Int64, InstallationInfo>();
public static async Task MonitorInstallationTable()
{
while (true)
{
lock (InstallationConnections)
{
Console.WriteLine("Monitoring installation table...");
foreach (var installationConnection in InstallationConnections)
{
Console.WriteLine("installationConnection ID is " + installationConnection.Key + ", latest timestamp is" +installationConnection.Value.Timestamp + ", product is "+ installationConnection.Value.Product
+ ", and time diff is "+ (DateTime.Now - installationConnection.Value.Timestamp));
if ((installationConnection.Value.Product == (int)ProductType.Salimax && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(2)) ||
(installationConnection.Value.Product == (int)ProductType.Salidomo && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(60)) ||
(installationConnection.Value.Product == (int)ProductType.SodioHome && (DateTime.Now - installationConnection.Value.Timestamp) > TimeSpan.FromMinutes(4)) ||
(installationConnection.Value.Product == (int)ProductType.SodiStoreMax && (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));
installationConnection.Value.Status = (int)StatusType.Offline;
Installation installation = Db.Installations.FirstOrDefault(f => f.Product == installationConnection.Value.Product && f.Id == installationConnection.Key);
installation.Status = (int)StatusType.Offline;
installation.Apply(Db.Update);
if (installationConnection.Value.Connections.Count > 0)
{
InformWebsocketsForInstallation(installationConnection.Key);
}
}
}
}
await Task.Delay(TimeSpan.FromMinutes(1));
}
}
//Inform all the connected websockets regarding installation "installationId"
public static void InformWebsocketsForInstallation(Int64 installationId)
{
var installation = Db.GetInstallationById(installationId);
var installationConnection = InstallationConnections[installationId];
Console.WriteLine("Update all the connected websockets for installation " + installation.Name);
var jsonObject = new
{
id = installationId,
status = installationConnection.Status,
testingMode = installation.TestingMode
};
string jsonString = JsonSerializer.Serialize(jsonObject);
byte[] dataToSend = Encoding.UTF8.GetBytes(jsonString);
foreach (var connection in installationConnection.Connections)
{
connection.SendAsync(
new ArraySegment<byte>(dataToSend, 0, dataToSend.Length),
WebSocketMessageType.Text,
true, // Indicates that this is the end of the message
CancellationToken.None
);
}
}
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);
if (result.MessageType != WebSocketMessageType.Text)
continue;
var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
var installationIds = JsonSerializer.Deserialize<int[]>(message);
Console.WriteLine("Received Websocket message: " + message);
//This is a ping message to keep the connection alive, reply with a pong
if (installationIds[0] == -1)
{
var jsonObject = new
{
id = -1,
status = -1
};
var jsonString = JsonSerializer.Serialize(jsonObject);
var dataToSend = Encoding.UTF8.GetBytes(jsonString);
currentWebSocket.SendAsync(dataToSend,
WebSocketMessageType.Text,
true,
CancellationToken.None
);
continue;
}
//Received a new message from this websocket.
//We have a HandleWebSocketConnection per connected frontend
lock (InstallationConnections)
{
List<WebsocketMessage> dataToSend = new List<WebsocketMessage>();
//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)
{
var installation = Db.GetInstallationById(installationId);
if (!InstallationConnections.ContainsKey(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
};
}
InstallationConnections[installationId].Connections.Add(currentWebSocket);
var jsonObject = new WebsocketMessage
{
id = installationId,
status = InstallationConnections[installationId].Status,
testingMode = installation.TestingMode
};
dataToSend.Add(jsonObject);
}
var jsonString = JsonSerializer.Serialize(dataToSend);
var encodedDataToSend = Encoding.UTF8.GetBytes(jsonString);
currentWebSocket.SendAsync(encodedDataToSend,
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);
}
}
}