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 InstallationConnections = new Dictionary(); //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. public static async Task MonitorSalimaxInstallationTable() { while (true){ lock (InstallationConnections){ Console.WriteLine("MONITOR SALIMAX INSTALLATIONS\n"); foreach (var installationConnection in InstallationConnections){ if (installationConnection.Value.Product==0 && (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 = -1; Installation installation = Db.Installations.FirstOrDefault(f => f.Product == 0 && f.Id == installationConnection.Key); installation.Status = -1; installation.Apply(Db.Update); if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);} } } Console.WriteLine("FINISHED MONITORING SALIMAX INSTALLATIONS\n"); } await Task.Delay(TimeSpan.FromMinutes(1)); } } public static async Task MonitorSalidomoInstallationTable() { while (true){ Console.WriteLine("TRY TO LOCK FOR MONITOR SALIDOMO INSTALLATIONS\n"); lock (InstallationConnections){ 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)) { // 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.Apply(Db.Update); installationConnection.Value.Status = -1; if (installationConnection.Value.Connections.Count > 0){InformWebsocketsForInstallation(installationConnection.Key);} } } Console.WriteLine("FINISHED WITH UPDATING\n"); } 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 " + installationId); 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(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(message); //This is a ping message to keep the connection alive, reply with a pong if (installationIds[0] == -1) { var jsonObject = new { id = -1, status = -1 }; var jsonString = JsonSerializer.Serialize(jsonObject); var dataToSend = Encoding.UTF8.GetBytes(jsonString); currentWebSocket.SendAsync(dataToSend, WebSocketMessageType.Text, true, CancellationToken.None ); continue; } //Console.WriteLine("Received a new message from websocket"); lock (InstallationConnections) { List dataToSend = new List(); //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) { //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); InstallationConnections[installationId] = new InstallationInfo { Status = installation.Status, 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); } } }