diff --git a/platforms/interface/app/layout/app.svelte b/platforms/interface/app/layout/app.svelte index 272c14d..1ad6a91 100644 --- a/platforms/interface/app/layout/app.svelte +++ b/platforms/interface/app/layout/app.svelte @@ -107,7 +107,7 @@ // Connect to local WebSocket server const connectToWSServer = () => { - let ws = new WebSocket("ws://localhost:5391") + let ws = new WebSocket("ws://localhost:5390/ws") ws.onopen = () => { console.log("Local WS Connection established") diff --git a/platforms/windows/service/HTTPServer.cs b/platforms/windows/service/Server.cs similarity index 50% rename from platforms/windows/service/HTTPServer.cs rename to platforms/windows/service/Server.cs index c016df0..c214b9c 100644 --- a/platforms/windows/service/HTTPServer.cs +++ b/platforms/windows/service/Server.cs @@ -1,13 +1,18 @@ using lib; using Serilog; +using System.Collections.Concurrent; using System.Net; +using System.Net.WebSockets; using System.Text; using System.Text.Json; +using System.Text.Json.Nodes; namespace service; -public class HTTPServer { +public class Server { static HttpListener listener; + static ConcurrentDictionary connectedClients = new ConcurrentDictionary(); + // Start the server public void Start(HardwareInfo hardwareInfo) { string url = "http://localhost:5390/"; @@ -21,6 +26,7 @@ public void Start(HardwareInfo hardwareInfo) { Task.Run(async () => await HandleRequests(hardwareInfo)); } + // Wait for a request static async Task HandleRequests(HardwareInfo hardwareInfo) { while (true) { // Wait for a request to be received asynchronously @@ -32,20 +38,30 @@ static async Task HandleRequests(HardwareInfo hardwareInfo) { } static async Task ProcessRequestAsync(HttpListenerContext context, HardwareInfo hardwareInfo) { - string path = context.Request.RawUrl; - string method = context.Request.HttpMethod; - - if (method == "OPTIONS") { + if (context.Request.HttpMethod == "OPTIONS") { await HandleOptionsRequest(context); return; } - switch (path) { + switch (context.Request.RawUrl) { case "/": await HandleRootRequest(context); break; - case "/data": - await HandleDataRequest(context, hardwareInfo); + case "/ws": + if (context.Request.IsWebSocketRequest) { + WebSocketContext wsContext = await context.AcceptWebSocketAsync(null); + WebSocket socket = wsContext.WebSocket; + Task clientTask = HandleWSRequest(socket, hardwareInfo); + connectedClients.TryAdd(socket, clientTask); + } else { + byte[] responseBytes = Encoding.UTF8.GetBytes("Connection header did not include 'upgrade'"); + context.Response.ContentLength64 = responseBytes.Length; + context.Response.OutputStream.Write(responseBytes, 0, responseBytes.Length); + context.Response.OutputStream.Close(); + } + break; + case "/rest": + await HandleRESTRequest(context, hardwareInfo); break; case "/post": await HandlePostRequest(context, hardwareInfo); @@ -71,7 +87,7 @@ static async Task HandleRootRequest(HttpListenerContext context) { await SendResponse(context, buffer, "text/plain"); } - static async Task HandleDataRequest(HttpListenerContext context, HardwareInfo hardwareInfo) { + static async Task HandleRESTRequest(HttpListenerContext context, HardwareInfo hardwareInfo) { string responseJson = JsonSerializer.Serialize(hardwareInfo.API, Program.SerializerOptions); byte[] buffer = Encoding.UTF8.GetBytes(responseJson); await SendResponse(context, buffer, "application/json"); @@ -131,6 +147,67 @@ static async Task SendResponse(HttpListenerContext context, byte[] buffer, strin context.Response.Close(); } + static async Task HandleWSRequest(WebSocket socket, HardwareInfo hardwareInfo) { + // Send the initial data + byte[] initialBuffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new GenericMessage() { Type = "initialData", Data = hardwareInfo.API }, Program.CompressedSerializerOptions)); + await socket.SendAsync(new ArraySegment(initialBuffer, 0, initialBuffer.Length), WebSocketMessageType.Text, true, CancellationToken.None); + + var receiveBuffer = new ArraySegment(new byte[1024 * 4]); + WebSocketReceiveResult result; + + // Send last 60s and last 60 minutes data + await Task.Run(async () => { + var secondsList = Program.HardwareStats.seconds.Where((x, i) => (i + 1) % 3 == 0).ToList(); + + for (int i = 0; i < secondsList.Count; i++) { + byte[] buffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new GenericMessage() { Type = "secondsData", Data = JsonNode.Parse(secondsList[i]) }, Program.CompressedSerializerOptions)); + await socket.SendAsync(new ArraySegment(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None); + } + + var minutesList = Program.HardwareStats.minutes.Where((x, i) => (i + 1) % 3 == 0).ToList(); + + for (int i = 0; i < minutesList.Count; i++) { + byte[] buffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new GenericMessage() { Type = "minutesData", Data = JsonNode.Parse(minutesList[i]) }, Program.CompressedSerializerOptions)); + await socket.SendAsync(new ArraySegment(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None); + } + }); + + // Send updated data every 2s + Task sendTask = Task.Run(async () => { + while (socket.State == WebSocketState.Open) { + byte[] buffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new GenericMessage() { Type = "data", Data = hardwareInfo.API }, Program.CompressedSerializerOptions)); + await socket.SendAsync(new ArraySegment(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None); + await Task.Delay(2000); + } + }); + + try { + while (socket.State == WebSocketState.Open) { + result = await socket.ReceiveAsync(receiveBuffer, CancellationToken.None); + + if (result.MessageType == WebSocketMessageType.Close) { + await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); + connectedClients.TryRemove(socket, out _); + break; + } else if (result.MessageType == WebSocketMessageType.Text) { + string receivedText = Encoding.UTF8.GetString(receiveBuffer.Array, receiveBuffer.Offset, result.Count); + HandleWSMessage(receivedText); + } + } + } + catch (Exception) { + Log.Error("Failed to close WS connection"); + connectedClients.TryRemove(socket, out _); + } + + // Wait for the send task to complete + await sendTask; + } + + static void HandleWSMessage(string message) { + Commands.HandleRemoteMessage(message); + } + public void Stop() { listener.Stop(); } diff --git a/platforms/windows/service/WSServer.cs b/platforms/windows/service/WSServer.cs deleted file mode 100644 index c026674..0000000 --- a/platforms/windows/service/WSServer.cs +++ /dev/null @@ -1,112 +0,0 @@ -using lib; -using Serilog; -using System.Collections.Concurrent; -using System.Net; -using System.Net.WebSockets; -using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; - -namespace service; - -public class WSServer { - static HttpListener listener; - static ConcurrentDictionary connectedClients = new ConcurrentDictionary(); - - public void Start(HardwareInfo hardwareInfo) { - string url = "http://localhost:5391/"; - - // Create an HttpListener - listener = new HttpListener(); - listener.Prefixes.Add(url); - - // Start the listener - listener.Start(); - - Task.Run(async () => await HandleRequests(hardwareInfo)); - } - - static async Task HandleRequests(HardwareInfo hardwareInfo) { - while (true) { - // Wait for a request to be received asynchronously - HttpListenerContext context = await listener.GetContextAsync(); - - if (context.Request.IsWebSocketRequest) { - WebSocketContext wsContext = await context.AcceptWebSocketAsync(null); - WebSocket socket = wsContext.WebSocket; - Task clientTask = ProcessWebSocketRequestAsync(socket, hardwareInfo); - connectedClients.TryAdd(socket, clientTask); - } else { - byte[] responseBytes = Encoding.UTF8.GetBytes("Connection header did not include 'upgrade'"); - context.Response.ContentLength64 = responseBytes.Length; - context.Response.OutputStream.Write(responseBytes, 0, responseBytes.Length); - context.Response.OutputStream.Close(); - } - } - } - - static async Task ProcessWebSocketRequestAsync(WebSocket socket, HardwareInfo hardwareInfo) { - // Send the initial data - byte[] initialBuffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new GenericMessage() { Type = "initialData", Data = hardwareInfo.API }, Program.CompressedSerializerOptions)); - await socket.SendAsync(new ArraySegment(initialBuffer, 0, initialBuffer.Length), WebSocketMessageType.Text, true, CancellationToken.None); - - var receiveBuffer = new ArraySegment(new byte[1024 * 4]); - WebSocketReceiveResult result; - - // Send last 60s and last 60 minutes data - await Task.Run(async () => { - var secondsList = Program.HardwareStats.seconds.Where((x, i) => (i + 1) % 3 == 0).ToList(); - - for (int i = 0; i < secondsList.Count; i++) { - byte[] buffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new GenericMessage() { Type = "secondsData", Data = JsonNode.Parse(secondsList[i]) }, Program.CompressedSerializerOptions)); - await socket.SendAsync(new ArraySegment(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None); - } - - var minutesList = Program.HardwareStats.minutes.Where((x, i) => (i + 1) % 3 == 0).ToList(); - - for (int i = 0; i < minutesList.Count; i++) { - byte[] buffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new GenericMessage() { Type = "minutesData", Data = JsonNode.Parse(minutesList[i]) }, Program.CompressedSerializerOptions)); - await socket.SendAsync(new ArraySegment(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None); - } - }); - - // Send updated data every 2s - Task sendTask = Task.Run(async () => { - while (socket.State == WebSocketState.Open) { - byte[] buffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new GenericMessage() { Type = "data", Data = hardwareInfo.API }, Program.CompressedSerializerOptions)); - await socket.SendAsync(new ArraySegment(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None); - await Task.Delay(2000); - } - }); - - try { - while (socket.State == WebSocketState.Open) { - result = await socket.ReceiveAsync(receiveBuffer, CancellationToken.None); - - if (result.MessageType == WebSocketMessageType.Close) { - await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); - connectedClients.TryRemove(socket, out _); - break; - } else if (result.MessageType == WebSocketMessageType.Text) { - string receivedText = Encoding.UTF8.GetString(receiveBuffer.Array, receiveBuffer.Offset, result.Count); - HandleMessage(receivedText); - } - } - } - catch (Exception) { - Log.Error("Failed to close WS connection"); - connectedClients.TryRemove(socket, out _); - } - - // Wait for the send task to complete - await sendTask; - } - - public void Stop() { - listener.Stop(); - } - - static void HandleMessage(string message) { - Commands.HandleRemoteMessage(message); - } -} diff --git a/platforms/windows/service/WindowsBackgroundService.cs b/platforms/windows/service/WindowsBackgroundService.cs index df58bc9..7049a27 100644 --- a/platforms/windows/service/WindowsBackgroundService.cs +++ b/platforms/windows/service/WindowsBackgroundService.cs @@ -5,16 +5,14 @@ namespace service; public sealed class WindowsBackgroundService : BackgroundService { internal static HardwareInfo HardwareInfo = new(); - internal static HTTPServer HTTPServer = new(); - internal static WSServer WSServer = new(); internal static RTCServer RTCServer = new(); internal static Analytics Analytics = new(); + internal static Server Server = new(); protected override async Task ExecuteAsync(CancellationToken stoppingToken) { Log.Information("Starting Cores service"); HardwareInfo.GetInfo(); - HTTPServer.Start(HardwareInfo); - WSServer.Start(HardwareInfo); + Server.Start(HardwareInfo); // Send analytics _ = Task.Run(async () => { @@ -60,8 +58,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { } catch (OperationCanceledException) { RTCServer.Stop(); - HTTPServer.Stop(); - WSServer.Stop(); + Server.Stop(); HardwareInfo.Stop(); } catch (Exception ex) {