From 88513ff4167c18a3ed2bcd501a3ddb89dc3abc20 Mon Sep 17 00:00:00 2001 From: Sylvain Renault <sylvain.renault@hhi.fraunhofer.de> Date: Thu, 8 Aug 2024 18:03:45 +0200 Subject: [PATCH] Some trst implementation for REST. First websocket functions (register, send poses...) --- .../ControllersImpl/CapabilitiesImpl.cs | 24 ++- .../ETSI-ARF/ControllersImpl/PoseImpl.cs | 9 +- .../ControllersImpl/WebSocketController.cs | 169 ++++++++++++++++-- .../ETSI-ARF/ModelsExt/NotNeeded.txt | 0 .../ETSI-ARF/NoMongoDBneeded.txt | 0 .../ETSI-ARF/Services/NotNeeded.txt | 0 .../ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs | 11 +- .../wwwroot/ws.html | 1 + 8 files changed, 187 insertions(+), 27 deletions(-) create mode 100644 server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ModelsExt/NotNeeded.txt create mode 100644 server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/NoMongoDBneeded.txt create mode 100644 server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/Services/NotNeeded.txt diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/CapabilitiesImpl.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/CapabilitiesImpl.cs index 3529f66..648b36f 100644 --- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/CapabilitiesImpl.cs +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/CapabilitiesImpl.cs @@ -53,7 +53,22 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers /// </summary> public override IActionResult GetCapabilities([FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID) { - return StatusCode(405, "Not supported yet!"); + // Get all capabilities from all anchors/trackables + // test + Capability capability = new Capability(); + capability.Framerate = 24; + capability.Latency = 0; + capability.TrackableType = TrackableType.FIDUCIALMARKEREnum; + + // Create list + List<Capability> capabilities = new List<Capability>(); + capabilities.Add(capability); + + // Create repsonse object + GetCapabilities200Response response = new GetCapabilities200Response(); + response.Capabilities = capabilities; + return new ObjectResult(response); + //return StatusCode(405, "Not supported yet!"); } /// <summary> @@ -61,7 +76,12 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers /// </summary> public override IActionResult GetSupport([FromRoute (Name = "trackableOrAnchorUUID")][Required]Guid trackableOrAnchorUUID, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID) { - return StatusCode(405, "Not supported yet!"); + Capability capability = new Capability(); + capability.Framerate = 24; + capability.Latency = 0; + capability.TrackableType = TrackableType.FIDUCIALMARKEREnum; + return (null != capability) ? new ObjectResult(capability) : StatusCode(404, "Not found, could not find capability for UUID: " + trackableOrAnchorUUID); + //return StatusCode(405, "Not supported yet!"); } } } diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/PoseImpl.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/PoseImpl.cs index 93f84bf..1fbaa52 100644 --- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/PoseImpl.cs +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/PoseImpl.cs @@ -85,7 +85,14 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers /// </summary> public override IActionResult SubscribeToPose([FromBody]SubscribeToPoseRequest subscribeToPoseRequest, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID) { - return StatusCode(405, "Not supported yet!"); + SubscribeToPose200Response response = new SubscribeToPose200Response(); + response.Target = subscribeToPoseRequest.Target; + response.Mode = subscribeToPoseRequest.Mode[0]; + response.WebhookUrl = subscribeToPoseRequest.WebhookUrl; + response.WebsocketUrl = Request.Host.ToString(); ; + + return new ObjectResult(response); + //return StatusCode(405, "Not supported yet!"); } /// <summary> diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs index 6e80b54..f0c81d3 100644 --- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ControllersImpl/WebSocketController.cs @@ -31,6 +31,8 @@ using Microsoft.AspNetCore.Http; using Swashbuckle.AspNetCore.Annotations; using Swashbuckle.AspNetCore.SwaggerGen; +using ETSI.ARF.OpenAPI.WorldAnalysis.Models; + #pragma warning disable CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element namespace ETSI.ARF.OpenAPI.WorldStorage.Services { @@ -40,6 +42,11 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Services // public class WebSocketController : ControllerBase { + private string currentName = ""; + private bool registered = false; + private bool firstTime = true; + private int timeCnt = 3; + [HttpGet("/ws")] public async Task Get() { @@ -47,12 +54,14 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Services { using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); - if (webSocket.State == WebSocketState.Open) + if (webSocket.State == WebSocketState.Connecting) { // Response an OK message - await SendText(webSocket, "Hello, here is the ARF World Analysis services!"); } - await Echo(HttpContext, webSocket); + else if (webSocket.State == WebSocketState.Open) + { + await HandleClientData(HttpContext, webSocket); + } } else { @@ -76,17 +85,22 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Services // // Send the time all seconds // + //[HttpGet("/ws/time")] private async Task SendTime(WebSocket webSocket) { while (true) { - var message = "Hello, my time is: " + DateTime.Now.ToLocalTime(); - var bytes = Encoding.UTF8.GetBytes(message); - var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length); + var message = "ARF World Analysis Server, Time = " + DateTime.Now.ToLocalTime(); if (webSocket.State == WebSocketState.Open) { - await webSocket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None); + timeCnt--; + if (timeCnt < 0) + { + await SendText(webSocket, "Stop"); + break; + } + else await SendText(webSocket, message); } else if (webSocket.State == WebSocketState.Closed || webSocket.State == WebSocketState.Aborted) { @@ -96,28 +110,145 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Services } } - // - // Echo incoming messages - // - private async Task Echo(HttpContext context, WebSocket webSocket) + float rotInc = 0; + private async Task SendPose(WebSocket webSocket) + { + while (true) + { + if (webSocket.State == WebSocketState.Open) + { + timeCnt--; + if (timeCnt < 0) + { + await SendText(webSocket, "Stop"); + break; + } + else + { + PoseValue val = new PoseValue(); + val.Unit = UnitSystem.MEnum; + val.Position = new List<float>() { 1, 2, 3 }; + val.Rotation = new List<float>() { rotInc, 0, 0, 0 }; + rotInc += 0.01f; + + Pose pose = new Pose(); + pose.Uuid = new Guid(); + pose.EstimationState = Pose.EstimationStateEnum.OKEnum; + //pose.SubscriptionUrl = ""; + pose.Value = val; + pose.Timestamp = (int)DateTime.Now.ToFileTime(); + pose.Confidence = 1; + pose.Mode = ModeWorldAnalysis.DEVICETOTRACKABLESEnum; + + string json = pose.ToJson(); + + await SendText(webSocket, json); + } + } + else if (webSocket.State == WebSocketState.Closed || webSocket.State == WebSocketState.Aborted) + { + break; + } + Thread.Sleep(250); + } + } + + private async void OnReceiveText(WebSocket webSocket, string msg) + { + if (firstTime) + { + // Register the client/module + if (msg.ToLower().StartsWith("module:")) + { + registered = true; + firstTime = false; + currentName = msg.Split(':')[1]; + await SendText(webSocket, "ARF World Analysis Server: You are now registered as a module: " + currentName); + } + else if (msg.ToLower().StartsWith("client:")) + { + registered = true; + firstTime = false; + currentName = msg.Split(':')[1]; + await SendText(webSocket, "ARF World Analysis Server: You are now registered as a client: " + currentName); + } + else + { + registered = false; + await SendText(webSocket, "ARF World Analysis Server: Cannot register " + msg); + } + } + else if (registered) + { + if (msg.ToLower().StartsWith("startsendingpose")) + { + if (msg.Contains(':')) timeCnt = int.Parse(msg.Split(':')[1]); + else timeCnt = 3; + await SendPose(webSocket); + } + else if (msg.ToLower().StartsWith("time")) + { + if (msg.Contains(':')) timeCnt = int.Parse(msg.Split(':')[1]); + else timeCnt = 3; + await SendTime(webSocket); + } + else if (msg.ToLower() == "unregister") + { + // Unregister client/user + currentName = ""; + firstTime = true; + registered = false; + await SendText(webSocket, "Stop"); + } + else + { + // Send a response + await SendText(webSocket, "ARF World Analysis Server: I got this unknown message: " + msg); + } + } + } + + private async Task HandleClientData(HttpContext context, WebSocket webSocket) { var buffer = new byte[1024 * 4]; - // get the first data block + // Read/get the first data block WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); - while (!result.CloseStatus.HasValue) + if (result.MessageType == WebSocketMessageType.Text) { - // test - await SendText(webSocket, "Thanks, I got this message:"); - - // echo the message back to the client - await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + string getMsg = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count); + OnReceiveText(webSocket, getMsg); + } - // get the next block + // Entering the loop + while (!result.CloseStatus.HasValue) + { + // Read/get the next block result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); + if (result.MessageType == WebSocketMessageType.Text) + { + string getMsg = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count); + OnReceiveText(webSocket, getMsg); + } } await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); } + + + // + // Echo incoming messages + // + private async Task Echo(WebSocketReceiveResult context, WebSocket webSocket) + { + var buffer = new byte[1024 * 4]; + + // Read/get the first data block + WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None); + + // Send/echo the message back to the client + await SendText(webSocket, "ARF World Analysis Server: I got this (raw) message: "); + await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + } } } #pragma warning restore CS1591 // Fehlendes XML-Kommentar für öffentlich sichtbaren Typ oder Element diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ModelsExt/NotNeeded.txt b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/ModelsExt/NotNeeded.txt new file mode 100644 index 0000000..e69de29 diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/NoMongoDBneeded.txt b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/NoMongoDBneeded.txt new file mode 100644 index 0000000..e69de29 diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/Services/NotNeeded.txt b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/ETSI-ARF/Services/NotNeeded.txt new file mode 100644 index 0000000..e69de29 diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs index 48be552..f56fad2 100644 --- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/Startup.cs @@ -161,14 +161,15 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis app.UseHsts(); } - // ETSI-ARF Websocket implementation + // ETSI-ARF Websockets implementation var webSocketOptions = new WebSocketOptions() { - KeepAliveInterval = TimeSpan.FromSeconds(120), + //KeepAliveInterval = TimeSpan.FromSeconds(120), + //AllowedOrigins.Add("https://etsi.hhi.fraunhofer.de"), + //AllowedOrigins.Add("https://www.client.com") }; - //webSocketOptions.AllowedOrigins.Add("https://etsi.hhi.fraunhofer.de"); - //webSocketOptions.AllowedOrigins.Add("https://www.client.com"); - app.UseWebSockets(); + app.UseWebSockets(webSocketOptions); + //app.UseWebSockets(); app.UseHttpsRedirection(); app.UseDefaultFiles(); diff --git a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/wwwroot/ws.html b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/wwwroot/ws.html index bf3da4b..cbc7959 100644 --- a/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/wwwroot/ws.html +++ b/server/worldanalysis/src/ETSI.ARF.OpenAPI.WorldAnalysis/wwwroot/ws.html @@ -71,6 +71,7 @@ var port = document.location.port ? (":" + document.location.port) : ""; connectionUrl.value = scheme + "://" + document.location.hostname + port + "/ws" ; + //connectionUrl.value = scheme + "://" + "etsi.hhi.fraunhofer.de" + "/ws" ; function updateState() { function disable() { -- GitLab