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 Diff line number Diff line
Subproject commit b639a02180c2b5e301c77483b3a2fa645ba94169
Subproject commit 073fd7213fd9e6ebc2f8a47d628a650de30c8bc4
+9 −4
Original line number Diff line number Diff line
@@ -53,13 +53,16 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        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
            // Info: Capabilities are collected after a module is connected via websockets

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

            // Create response object
            GetCapabilities200Response response = new GetCapabilities200Response();
@@ -73,11 +76,13 @@ 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)
        {
            // todo: compare token and sessionID
            if (!Startup.IsAccessGranted(token)) return StatusCode(511, new Error() { Message = "Invalid token!" });

            // todo: compare sessionID

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

            // Create response object
            GetSupport200Response response = new GetSupport200Response();
+98 −45
Original line number Diff line number Diff line
@@ -53,11 +53,13 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        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
            WorldAnalysisModules.Singleton.ConfigureFramerate(poseConfiguration);
            return StatusCode(200, "Ok.");
            bool result = WorldAnalysisConnections.Singleton.ConfigureFramerate(poseConfiguration);
            return result ? StatusCode(200, "Ok.") : StatusCode(405, "Not supported.");
        }

        /// <summary>
@@ -65,11 +67,13 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </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)
        {
            // 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
            Pose result;
            result = WorldAnalysisModules.Singleton.GetPose(trackableOrAnchorUUID, mode);
            result = WorldAnalysisConnections.Singleton.GetPose(trackableOrAnchorUUID, mode);
            return new ObjectResult(result);
        }

@@ -78,13 +82,15 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        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
            GetPoses200Response result = new GetPoses200Response();
            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);
        }
@@ -101,10 +107,13 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers

        public struct SubscriptionInfo
        {
            public Guid uuid;           // 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 Guid uuidSub;           // id of subscription (id is defined by the WA server)

            public SubscribeToPoseRequest subscription;
            public string webSocket;
            public Pose pose;
            public DateTime timeValidity;

            //public PoseCallback callback;
        }
#pragma warning restore CS1591 // Fehlendes XML-Kommentar fr ffentlich sichtbaren Typ oder Element
@@ -114,11 +123,26 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        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>
@@ -126,46 +150,67 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        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?

            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
            SubscriptionInfo info = new SubscriptionInfo();
            info.uuid = new Guid();
            info.uuidTarget = subscribeToPoseRequest.Target;
            info.uuidSub = response.Uuid;
            info.webSocket = response.WebsocketUrl;
            info.timeValidity = DateTime.Now.AddMilliseconds(validity / 1000.0f);

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

            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 ?
            // subscribeToPoseRequest.Targets[]
            // subscribeToPoseRequest.Modes[]
            m_subscriptionsPoses.Add(info.uuidSub, info);
            
            // 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?!?
            WorldAnalysisModules.Singleton.SubscribeToPose(info);

            SubscribeToPose200Response response = new SubscribeToPose200Response();
            response.Validity = validity;
            response.Uuid = info.uuid;
            response.Target = info.uuidTarget;
            response.Mode = info.pose.Mode;
            WorldAnalysisConnections.Singleton.SubscribeToPose(info, parentUUID);

            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);
        }

@@ -174,15 +219,21 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        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?
            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
            WorldAnalysisModules.Singleton.UnsubscribeFromPose(subscriptionUUID);
            WorldAnalysisConnections.Singleton.UnsubscribeFromPose(subscriptionUUID);

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

        /// <summary>
@@ -190,7 +241,9 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis.Controllers
        /// </summary>
        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

+128 −96

File changed.

Preview size limit exceeded, changes collapsed.

+38 −17
Original line number Diff line number Diff line
@@ -17,20 +17,21 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
        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 string name;
        public string session;
        public VectorQuaternionPoseValue currentUserPose;
        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<Client> clients = new List<Client>();
        public List<Client> clients = new List<Client>();   // e.g. Unity clients

        // todo
        // Manage the sessions and modules
@@ -58,13 +59,24 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
            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
                item.websockets.SendText("ConfigureFramerate:" + poseConfiguration.ToJson());
                    if ((int)poseConfiguration.TrackableType == (int)c.TrackableType)
                    {
                        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)
@@ -73,7 +85,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
            foreach (var item in modules)
            {
                // 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?
                // How to get the results !?!?! (List with results, per Modules)
@@ -81,31 +93,40 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
            return result;
        }
        

        public void SubscribeToPose(SubscriptionInfo info)
        public void SubscribeToPose(SubscriptionInfo info, Guid parentTrackableUUID)
        {
            // Send to all modules a request of subscription
            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
            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
            foreach (var item in clients)
            {
                item.websockets.SendText("Pose=" + p.ToJson());
                item.websockets.SendText("NewPose=" + str_pose);
            }
        }
    }
Loading