Commit 42f62762 authored by Sylvain Renault's avatar Sylvain Renault
Browse files

Functions for the API 2.0.0.

Complete handling of websocket requests, for modules and clients side.
parent afbfe86d
Loading
Loading
Loading
Loading

openapi @ 073fd721

Original line number Original line Diff line number Diff line
Subproject commit b639a02180c2b5e301c77483b3a2fa645ba94169
Subproject commit 073fd7213fd9e6ebc2f8a47d628a650de30c8bc4
+9 −4
Original line number Original line Diff line number Diff line
@@ -53,13 +53,16 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult GetCapabilities([FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
        public override IActionResult GetCapabilities([FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
        {
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID


            // Get all capabilities from all anchors/trackables
            // Get all capabilities from all anchors/trackables
            // Info: Capabilities are collected after a module is connected via websockets


            // Create list
            // Create list
            List<Capability> capabilitiesList = new List<Capability>();
            List<Capability> capabilitiesList = new List<Capability>();
            capabilitiesList.AddRange(WorldAnalysisModules.Singleton.GetCapabilities());
            capabilitiesList.AddRange(WorldAnalysisConnections.Singleton.GetCapabilities());


            // Create response object
            // Create response object
            GetCapabilities200Response response = new GetCapabilities200Response();
            GetCapabilities200Response response = new GetCapabilities200Response();
@@ -73,11 +76,13 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult GetSupport([FromRoute (Name = "trackableOrAnchorUUID")][Required]Guid trackableOrAnchorUUID, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
        public override IActionResult GetSupport([FromRoute (Name = "trackableOrAnchorUUID")][Required]Guid trackableOrAnchorUUID, [FromHeader (Name = "token")]string token, [FromHeader (Name = "sessionID")]string sessionID)
        {
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID


            // Create list
            // Create list
            List<Capability> capabilitiesList = new List<Capability>();
            List<Capability> capabilitiesList = new List<Capability>();
            capabilitiesList.AddRange(WorldAnalysisModules.Singleton.GetCapabilitiesFromUuid(trackableOrAnchorUUID));
            capabilitiesList.AddRange(WorldAnalysisConnections.Singleton.GetCapabilitiesFromUuid(trackableOrAnchorUUID));


            // Create response object
            // Create response object
            GetSupport200Response response = new GetSupport200Response();
            GetSupport200Response response = new GetSupport200Response();
+98 −45
Original line number Original line Diff line number Diff line
@@ -53,11 +53,13 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult ConfigureFramerate([FromBody] PoseConfiguration poseConfiguration, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        public override IActionResult ConfigureFramerate([FromBody] PoseConfiguration poseConfiguration, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        {
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID


            // Notify the modules that the client need a new framerate
            // Notify the modules that the client need a new framerate
            WorldAnalysisModules.Singleton.ConfigureFramerate(poseConfiguration);
            bool result = WorldAnalysisConnections.Singleton.ConfigureFramerate(poseConfiguration);
            return StatusCode(200, "Ok.");
            return result ? StatusCode(200, "Ok.") : StatusCode(405, "Not supported.");
        }
        }


        /// <summary>
        /// <summary>
@@ -65,11 +67,13 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult GetPose([FromRoute(Name = "trackableOrAnchorUUID")][Required] Guid trackableOrAnchorUUID, [FromQuery(Name = "mode")][Required()] ModeWorldAnalysis mode, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        public override IActionResult GetPose([FromRoute(Name = "trackableOrAnchorUUID")][Required] Guid trackableOrAnchorUUID, [FromQuery(Name = "mode")][Required()] ModeWorldAnalysis mode, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        {
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID


            // Request from the modules a new pose of a single UUID
            // Request from the modules a new pose of a single UUID
            Pose result;
            Pose result;
            result = WorldAnalysisModules.Singleton.GetPose(trackableOrAnchorUUID, mode);
            result = WorldAnalysisConnections.Singleton.GetPose(trackableOrAnchorUUID, mode);
            return new ObjectResult(result);
            return new ObjectResult(result);
        }
        }


@@ -78,13 +82,15 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult GetPoses([FromQuery(Name = "uuid")][Required()] List<UuidAndMode> uuid, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        public override IActionResult GetPoses([FromQuery(Name = "uuid")][Required()] List<UuidAndMode> uuid, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        {
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID


            // Request from the modules new poses from all UUIDs
            // Request from the modules new poses from all UUIDs
            GetPoses200Response result = new GetPoses200Response();
            GetPoses200Response result = new GetPoses200Response();
            foreach (var item in uuid)
            foreach (var item in uuid)
            {
            {
                result.Poses.Add(WorldAnalysisModules.Singleton.GetPose(item.Uuid, item.Mode));
                result.Poses.Add(WorldAnalysisConnections.Singleton.GetPose(item.Uuid, item.Mode));
            }
            }
            return new ObjectResult(result);
            return new ObjectResult(result);
        }
        }
@@ -101,10 +107,13 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers


        public struct SubscriptionInfo
        public struct SubscriptionInfo
        {
        {
            public Guid uuid;           // id of subscription (id is defined by the WA server)
            public Guid uuidSub;           // id of subscription (id is defined by the WA server)
            public Guid uuidTarget;     // id trackable or anchor

            public DateTime timeValidity;  //The duration of the validity of the subscription
            public SubscribeToPoseRequest subscription;
            public string webSocket;
            public Pose pose;
            public Pose pose;
            public DateTime timeValidity;

            //public PoseCallback callback;
            //public PoseCallback callback;
        }
        }
#pragma warning restore CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element
#pragma warning restore CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element
@@ -114,11 +123,26 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult GetSubscription([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        public override IActionResult GetSubscription([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        {
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });


            // todo: search for a subscription and send it back
            // todo: compare sessionID


            return StatusCode(405, "Not supported yet!");
            // todo: search for a subscription and send it back
            foreach (var item in m_subscriptionsPoses)
            {
                if (item.Key == subscriptionUUID)
                {
                    SubscriptionSingle response = new SubscriptionSingle();
                    response.Uuid = subscriptionUUID;
                    response.Target = item.Value.subscription.Target;
                    response.Mode = item.Value.pose.Mode;
                    response.Validity = item.Value.timeValidity.Millisecond;
                    response.WebhookUrl = item.Value.subscription.WebhookUrl;
                    response.WebsocketUrl = item.Value.webSocket;
                    return new ObjectResult(response);
                }
            }
            return StatusCode(404, "Not found.");
        }
        }


        /// <summary>
        /// <summary>
@@ -126,46 +150,67 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult SubscribeToPose([FromBody] SubscribeToPoseRequest subscribeToPoseRequest, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        public override IActionResult SubscribeToPose([FromBody] SubscribeToPoseRequest subscribeToPoseRequest, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        {
        {
            // todo: compare token and sessionID
            // Simulation ON for STF669 pose calcualtion
            string _token = token;
            Guid parentUUID = Guid.Empty;
            if (token.Contains(","))
            {
                _token = token.Split(',')[0];
                parentUUID = new Guid(token.Split(',')[1]);
            }

            if (!Startup.IsAccessGranted(_token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID

            if (subscribeToPoseRequest.Targets != null && subscribeToPoseRequest.Targets.Count > 0)
            {
                return StatusCode(404, "Multiple subscriptions are not implemented (targets).");
            }
            else if (subscribeToPoseRequest.Modes != null && subscribeToPoseRequest.Modes.Count > 0)
            {
                return StatusCode(404, "Multiple subscriptions are not implemented (modes).");
            }


            int validity = subscribeToPoseRequest.Validity; // todo: is to handle here or by the client?
            int validity = subscribeToPoseRequest.Validity; // todo: is to handle here or by the client?


            SubscribeToPose200Response response = new SubscribeToPose200Response();
            response.Validity = validity;
            response.Uuid = Guid.NewGuid();
            response.Target = subscribeToPoseRequest.Target;
            response.Mode = subscribeToPoseRequest.Mode;
            response.WebhookUrl = subscribeToPoseRequest.WebhookUrl;
            response.WebsocketUrl = "";

            // Send the websocket connection URL
            if (string.IsNullOrEmpty(response.WebhookUrl))
            {
                // Notice: starting websocket server is done autom. by the client, when calling "URL:xxx/ws"
                // Registering the client is done in the analysis module, so the websocket can send to it pose updates
                response.WebsocketUrl = "wss://" + Request.Host.ToString() + "/ws";
            }

            // We add the subscription
            // We add the subscription
            SubscriptionInfo info = new SubscriptionInfo();
            SubscriptionInfo info = new SubscriptionInfo();
            info.uuid = new Guid();
            info.uuidSub = response.Uuid;
            info.uuidTarget = subscribeToPoseRequest.Target;
            info.webSocket = response.WebsocketUrl;
            info.timeValidity = DateTime.Now.AddMilliseconds(validity / 1000.0f);
            info.timeValidity = DateTime.Now.AddMilliseconds(validity / 1000.0f);

            info.pose = new Pose();
            info.pose = new Pose();
            info.pose.Mode = subscribeToPoseRequest.Mode;
            info.pose.Mode = response.Mode;
            m_subscriptionsPoses.Add(info.uuid, info);

            info.subscription = new SubscribeToPoseRequest();
            info.subscription.Target = response.Target;
            info.subscription.Mode = response.Mode;
            info.subscription.Validity = response.Validity;
            info.subscription.WebhookUrl = response.WebhookUrl;


            // todo: handle multiple subscriptions ?
            m_subscriptionsPoses.Add(info.uuidSub, info);
            // subscribeToPoseRequest.Targets[]
            // subscribeToPoseRequest.Modes[]
            
            
            // todo: inform the module(s) that the client will track an anchor/trackable and need the pose
            // todo: inform the module(s) that the client will track an anchor/trackable and need the pose
            // todo: has the module to call GetRelocalizationInformation() then?!?
            // todo: has the module to call GetRelocalizationInformation() then?!?
            WorldAnalysisModules.Singleton.SubscribeToPose(info);
            WorldAnalysisConnections.Singleton.SubscribeToPose(info, parentUUID);

            SubscribeToPose200Response response = new SubscribeToPose200Response();
            response.Validity = validity;
            response.Uuid = info.uuid;
            response.Target = info.uuidTarget;
            response.Mode = info.pose.Mode;


            if (string.IsNullOrEmpty(subscribeToPoseRequest.WebhookUrl))
            {
                response.WebhookUrl = "";
                response.WebsocketUrl = Request.Host.ToString();
                // Notice: starting websocket server is done autom. by the client, when calling "URL:/ws"

                // todo: register the client, so the websocket will send to it pose updates
            }
            else
            {
                response.WebhookUrl = subscribeToPoseRequest.WebhookUrl;
                response.WebsocketUrl = null;
            }
            return new ObjectResult(response);
            return new ObjectResult(response);
        }
        }


@@ -174,15 +219,21 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult UnsubscribeFromPose([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        public override IActionResult UnsubscribeFromPose([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        {
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID


            // Remove the subscription from the list?
            // Remove the subscription from the list?
            if (m_subscriptionsPoses.ContainsKey(subscriptionUUID)) m_subscriptionsPoses.Remove(subscriptionUUID);
            if (m_subscriptionsPoses.ContainsKey(subscriptionUUID)) m_subscriptionsPoses.Remove(subscriptionUUID);
            else
            {
                //return StatusCode(404, new Error() { Message = "Unsubscribe UUID not found!" });
            }


            // Inform the module(s) that the subscription ended
            // Inform the module(s) that the subscription ended
            WorldAnalysisModules.Singleton.UnsubscribeFromPose(subscriptionUUID);
            WorldAnalysisConnections.Singleton.UnsubscribeFromPose(subscriptionUUID);


            return StatusCode(200, "Ok!");
            return StatusCode(200, new Success() { Message = "Unsubscription successfull." });
        }
        }


        /// <summary>
        /// <summary>
@@ -190,7 +241,9 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        /// </summary>
        public override IActionResult UpdateSubscription([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromBody] UpdateSubscriptionRequest updateSubscriptionRequest, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        public override IActionResult UpdateSubscription([FromRoute(Name = "subscriptionUUID")][Required] Guid subscriptionUUID, [FromBody] UpdateSubscriptionRequest updateSubscriptionRequest, [FromHeader(Name = "token")] string token, [FromHeader(Name = "sessionID")] string sessionID)
        {
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID


            // todo: inform the module(s) that the subscription changed
            // todo: inform the module(s) that the subscription changed


+128 −96

File changed.

Preview size limit exceeded, changes collapsed.

+38 −17
Original line number Original line Diff line number Diff line
@@ -17,20 +17,21 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
        public WebSocketController websockets;
        public WebSocketController websockets;
    }
    }


    // For management of WA clinet (e.g. Unity client)
    // For management of WA client (e.g. Unity client)
    public class Client
    public class Client
    {
    {
        public string name;
        public string name;
        public string session;
        public string session;
        public VectorQuaternionPoseValue currentUserPose;
        public WebSocketController websockets;
        public WebSocketController websockets;
    }
    }


    public class WorldAnalysisModules
    public class WorldAnalysisConnections
    {
    {
        static public WorldAnalysisModules Singleton = new WorldAnalysisModules();
        static public WorldAnalysisConnections Singleton = new WorldAnalysisConnections();


        public List<Module> modules = new List<Module>();
        public List<Module> modules = new List<Module>();
        public List<Client> clients = new List<Client>();
        public List<Client> clients = new List<Client>();   // e.g. Unity clients


        // todo
        // todo
        // Manage the sessions and modules
        // Manage the sessions and modules
@@ -58,13 +59,24 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
            return list;
            return list;
        }
        }


        public void ConfigureFramerate(PoseConfiguration poseConfiguration)
        public bool ConfigureFramerate(PoseConfiguration poseConfiguration)
        {
        {
            foreach (var item in modules)
            bool ok = false;
            foreach (var m in modules)
            {
                bool moduleHasCap = false;
                foreach (var c in m.capabilities)
                {
                {
                // todo: configure the module via websocket
                    if ((int)poseConfiguration.TrackableType == (int)c.TrackableType)
                item.websockets.SendText("ConfigureFramerate:" + poseConfiguration.ToJson());
                    {
                        moduleHasCap = true;
                        break;
                    }
                }
                }
                // Configure the module via websocket
                if (moduleHasCap) m.websockets.SendText("ConfigureFramerate=" + poseConfiguration.ToJson());
            }
            return ok;
        }
        }


        public Pose GetPose(Guid trackableOrAnchorUUID, ModeWorldAnalysis mode)
        public Pose GetPose(Guid trackableOrAnchorUUID, ModeWorldAnalysis mode)
@@ -73,7 +85,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
            foreach (var item in modules)
            foreach (var item in modules)
            {
            {
                // todo: get the pose via websocket
                // todo: get the pose via websocket
                item.websockets.SendText("GetPose:" + trackableOrAnchorUUID + ":" + mode.ToString());
                item.websockets.SendText("GetPose=" + trackableOrAnchorUUID + "=" + mode.ToString());


                // todo: find the pose with the best confidence?
                // todo: find the pose with the best confidence?
                // How to get the results !?!?! (List with results, per Modules)
                // How to get the results !?!?! (List with results, per Modules)
@@ -81,31 +93,40 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
            return result;
            return result;
        }
        }
        
        

        public void SubscribeToPose(SubscriptionInfo info, Guid parentTrackableUUID)
        public void SubscribeToPose(SubscriptionInfo info)
        {
        {
            // Send to all modules a request of subscription
            // Send to all modules a request of subscription
            foreach (var item in modules)
            foreach (var item in modules)
            {
            {
                item.websockets.SendText("SubscribePose:" + info.uuidTarget.ToString());
                if (parentTrackableUUID == Guid.Empty) item.websockets.SendText("SubscribePose=" + info.uuidSub + "=" + info.subscription.ToJson());
                else item.websockets.SendText("SubscribeSimulatedPose=" + info.uuidSub + "=" + info.subscription.ToJson() + "=" + parentTrackableUUID.ToString());
            }
            }
        }
        }


        public void UnsubscribeFromPose(Guid uuid)
        public void UnsubscribeFromPose(Guid subscriptionUUID)
        {
        {
            // Send to all modules a request of subscription
            // Send to all modules a request of subscription
            foreach (var item in modules)
            foreach (var item in modules)
            {
            {
                item.websockets.SendText("UnsubscribePose:" + uuid.ToString());
                item.websockets.SendText("UnsubscribePose=" + subscriptionUUID.ToString());
            }
        }

        public void SendPoseToClients(Pose pose)
        {
            // Send to all clients with valid subscription the new pose
            foreach (var item in clients)
            {
                item.websockets.SendText("NewPose=" + pose.ToJson());
            }
            }
        }
        }


        public void SendPoseToClients(Pose p)
        public void SendPoseToClients(string str_pose)
        {
        {
            // Send to all clients with valid subscription the new pose
            // Send to all clients with valid subscription the new pose
            foreach (var item in clients)
            foreach (var item in clients)
            {
            {
                item.websockets.SendText("Pose=" + p.ToJson());
                item.websockets.SendText("NewPose=" + str_pose);
            }
            }
        }
        }
    }
    }
Loading