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