Commit 9e97f577 authored by Sylvain Renault's avatar Sylvain Renault
Browse files

Implementation of all ws functions.

parent 9d13fce0
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -24,9 +24,15 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
            Name = name;
        }

        public string ToJson() { return JsonUtility.ToJson(this); }
        public string ToJson() { return Newtonsoft.Json.JsonConvert.SerializeObject(this); }
    }

    //public class MatrixPose : Pose
    //{
    //    [Newtonsoft.Json.JsonProperty("matrixValue", Required = Newtonsoft.Json.Required.AllowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    //    public MatrixPoseValue MatrixValue { get; set; }
    //}

    //
    // Implement here some constructors
    //
@@ -37,6 +43,6 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
            Uuid = Guid.NewGuid();
        }

        public string ToJson() { return JsonUtility.ToJson(this); }
        public string ToJson() { return Newtonsoft.Json.JsonConvert.SerializeObject(this); }
    }
}
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -1879,7 +1879,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
        /// <summary>
        /// The pose value
        /// </summary>
        [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.AllowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
        public PoseValue Value { get; set; }

        /// <summary>
+12 −0
Original line number Diff line number Diff line
@@ -29,6 +29,18 @@ namespace ETSI.ARF.WorldAnalysis.REST
{
    public class AdminRequest : RequestBase<string>
    {
        #region Test methods
        static public void CheckServer(WorldAnalysisServer wa)
        {
            string ping = AdminRequest.PingSync(wa);
            string state = AdminRequest.AdminSync(wa);
            string ver = AdminRequest.VersionSync(wa);
            Debug.Log("[REST] WA Ping: " + ping);
            Debug.Log("[REST] WA State: " + state);
            Debug.Log("[REST] WA Version: " + ver);
        }
        #endregion

        //
        // Wrapper for the endpoints
        //
+64 −64
Original line number Diff line number Diff line
@@ -18,20 +18,21 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
    //
    // Inspector variables
    //

    // Name to register the client to the World Analysis
    public string modulename = "UnityValidationApp";


    /// <summary>
    /// WorldSAnalysisServer
    /// </summary>
    public WorldAnalysisServer waServer;
    public string token = "ETSI-ARF-STF";
    public string sessionID = "RESTful-API";
    public string sessionID = "ARF-STF669";

    [Space(8)]
    public bool isDebug = false;

    //[Serializable]
    //public class StringEvent : UnityEvent<string> { }
    //public StringEvent webSocketMessage;

    //
    // Private members
    //
@@ -44,14 +45,15 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
    /// <summary>
    /// Dictionnary of susbscription informations for poses, for each item, stored using the UUID of the item (anchor/trackable)
    /// </summary>
    private Dictionary<Guid, SubscriptionInfo> m_subscriptionsPoses;
    private Dictionary<Guid, SubscriptionInfo> subscriptionsPoses;

    public struct SubscriptionInfo
    {
        public ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose;

        public Guid uuid;           // id of subscription (id is defined by the WA server)
        public Guid uuidTarget;     // id trackable or anchor
        public float timeValidity;  //The duration of the validity of the subscription
        public ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose;
        public PoseCallback callback;
    }

@@ -65,7 +67,7 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
        Instance = this;
        //m_relocalizationInformations = new Dictionary<Guid, ETSI.ARF.OpenAPI.WorldStorage.RelocalizationInformation>();
        //m_computedPoses = new Dictionary<Guid, ETSI.ARF.OpenAPI.WorldAnalysis.Pose>(); 
        m_subscriptionsPoses = new Dictionary<Guid, SubscriptionInfo>();
        subscriptionsPoses = new Dictionary<Guid, SubscriptionInfo>();

        // sync
        var httpClient = new BasicHTTPClient(waServer.URI);
@@ -91,7 +93,7 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
        ManageSubscriptionValidity();

        // todo: Call subscription callback(s) here or in the websocket?!?
        foreach (KeyValuePair<Guid, SubscriptionInfo> subPose in m_subscriptionsPoses)
        foreach (KeyValuePair<Guid, SubscriptionInfo> subPose in subscriptionsPoses)
        {
        }
    }
@@ -104,16 +106,6 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
    #endregion

    #region Test methods
    public void CheckServer()
    {
        string ping = AdminRequest.PingSync(waServer);
        string state = AdminRequest.AdminSync(waServer);
        string ver = AdminRequest.VersionSync(waServer);
        Debug.Log("[REST] WA Ping: " + ping);
        Debug.Log("[REST] WA State: " + state);
        Debug.Log("[REST] WA Version: " + ver);
    }

    public void PrintCapabilities(Capability[] capabilities)
    {
        string res = "";
@@ -133,10 +125,10 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
    /// </summary>
    protected void ManageSubscriptionValidity()
    {
        if (m_subscriptionsPoses.Count == 0) return;
        if (subscriptionsPoses.Count == 0) return;

        List<Guid> subscriptionToDelete = new List<Guid>();
        foreach (KeyValuePair<Guid, SubscriptionInfo> sub in m_subscriptionsPoses)
        foreach (KeyValuePair<Guid, SubscriptionInfo> sub in subscriptionsPoses)
        {
            float validity = sub.Value.timeValidity;
            if (Time.time > validity)
@@ -147,7 +139,7 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
        foreach (Guid s in subscriptionToDelete)
        {
            Debug.Log("ETSI ARF : Subscription deleted " + s);
            m_subscriptionsPoses.Remove(s);
            subscriptionsPoses.Remove(s);
        }
    }
    #endregion
@@ -204,12 +196,24 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
        }
    }

    private string CreateWebsocket_URL(string originalURL)
    {
        string uri = originalURL;
        
        //
        // Overwrite server setting for testing local/extra servers
        //
        //if (isDebug) uri = "ws://localhost:61788/ws"; // for tests
        //if (isDebug) uri = "wss://localhost:44301/ws"; // for tests

        return uri;
    }

    public InformationSubscriptionResult SubscribeToPose(string token, Guid uuid, Mode_WorldAnalysis mode, PoseCallback callback, ref int validity, out Guid subscriptionUUID)
    {
        // Todo: Maintain the callback to the subscription id
        // Get capabilities?
        // Get reloc info?
        subscriptionUUID = Guid.Empty; // default

        SubscriptionSingleRequest body = new SubscriptionSingleRequest();
        body.Target = uuid;
@@ -232,39 +236,37 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
        // We add the subscription
        SubscriptionInfo sub = new SubscriptionInfo();
        sub.uuid = subscriptionUUID;
        sub.uuidTarget = uuid;
        sub.timeValidity = Time.time + (validity / 1000.0f);
        sub.callback = callback;

        sub.pose = new ETSI.ARF.OpenAPI.WorldAnalysis.Pose();
        sub.pose.Mode = mode;
        sub.uuidTarget = uuid;
        sub.callback = callback;
        m_subscriptionsPoses.Add(sub.uuid, sub);

        subscriptionsPoses.Add(sub.uuid, sub);

        if (!string.IsNullOrEmpty(response.WebhookUrl))
        {
            // Create a REST server so that the WA server can send pose update to it
            string webhookUrl = response.WebhookUrl;

            // Close other communication channel
            WebSocketClient_Close();

            // Create a REST server so that the WA server can send pose update to it
            // How to auto-generate the C# REST webhook server for pose for Unity?
            string webhookUrl = response.WebhookUrl;
            WebHookServer_Create(webhookUrl);
        }
        else
        else if (!string.IsNullOrEmpty(response.WebsocketUrl))
        {
            // Create the WebSockets client here (NOT in the scene scripts)

            // Close other communication channel
            WebHookServer_Close();

            // Open the websocket
            string websocketUrl = "ws://" + response.WebsocketUrl + "/ws";
            //if (isDebug) websocketUrl = "ws://localhost:61788/ws"; // for tests

            if (!string.IsNullOrEmpty(websocketUrl))
            {
                // Create the WebSockets client here (NOT in the scene scripts)
                if (!websocketConnected) WebSocketClient_Create(websocketUrl);
            }
            else throw new Exception("[REST] No valid WebSockets URL in server reponse.");
            if (webSocket == null) WebSocketClient_Create(CreateWebsocket_URL(response.WebsocketUrl));  // only one instance
        }
        else throw new Exception("[REST] No valid URL in server reponse.");
        return InformationSubscriptionResult.OK;
    }

@@ -296,7 +298,7 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
        mode = Mode_WorldAnalysis.TRACKABLES_TO_DEVICE;
        validity = 0;

        if (m_subscriptionsPoses.ContainsKey(subscriptionUUID))
        if (subscriptionsPoses.ContainsKey(subscriptionUUID))
        {
            // Check the server subscription
            SubscriptionSingle sub = apiClient.GetSubscription(token, sessionID, subscriptionUUID);
@@ -304,7 +306,7 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
            // Check local one
            if (sub.Uuid == subscriptionUUID)
            {
                SubscriptionInfo subInfo = m_subscriptionsPoses[subscriptionUUID];
                SubscriptionInfo subInfo = subscriptionsPoses[subscriptionUUID];
                callback = subInfo.callback;
                target = subInfo.uuidTarget;
                mode = subInfo.pose.Mode;
@@ -328,18 +330,18 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
        mode = Mode_WorldAnalysis.TRACKABLES_TO_DEVICE;
        validity = 0;

        if (m_subscriptionsPoses.ContainsKey(subscriptionUUID))
        if (subscriptionsPoses.ContainsKey(subscriptionUUID))
        {
            SubscriptionInfo sub = m_subscriptionsPoses[subscriptionUUID];
            SubscriptionInfo sub = subscriptionsPoses[subscriptionUUID];
            PoseCallback oldCB = sub.callback;

            Body body = new Body();
            body.Mode = mode;
            body.Validity = validity;
            body.WebhookUrl = callback == null ? "" : "https:\\..."; // empty -> app will use websockets (client)!
            Body newSub = new Body();
            newSub.Mode = mode;
            newSub.Validity = validity;
            newSub.WebhookUrl = callback == null ? "" : "https:\\..."; // empty -> app will use websockets (client)!

            // Update subscription info in the REST server
            SubscriptionSingle response = apiClient.UpdateSubscription(token, sessionID, subscriptionUUID, body);
            SubscriptionSingle response = apiClient.UpdateSubscription(token, sessionID, subscriptionUUID, newSub);

            // Update local data
            sub.pose.Mode = response.Mode;
@@ -351,28 +353,26 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
            //
            if (oldCB != null && callback == null && !string.IsNullOrEmpty(response.WebhookUrl))
            {
                // Create a REST server so that the WA server can send pose update to it
                string webhookUrl = response.WebhookUrl;

                // Close other communication channel
                WebSocketClient_Close();

                // Create a REST server so that the WA server can send pose update to it
                // How to auto-generate the C# REST server for pose for Unity?
                string webhookUrl = response.WebhookUrl;
                WebHookServer_Create(webhookUrl);
            }
            else if (oldCB == null && callback != null && string.IsNullOrEmpty(response.WebhookUrl))
            else if (oldCB == null && callback != null && !string.IsNullOrEmpty(response.WebsocketUrl))
            {
                WebHookServer_Close();
                // Create the WebSockets client here (NOT in the scene scripts)

                // todo: Open the websocket?
                string websocketUrl = response.WebsocketUrl;
                if (isDebug) websocketUrl = "ws://localhost:61788/ws"; // for tests
                // Close other communication channel
                WebHookServer_Close();

                if (string.IsNullOrEmpty(websocketUrl))
                {
                    // Create the WebSockets client here (NOT in the scene scripts)
                    if (!websocketConnected) WebSocketClient_Create(websocketUrl);
                }
                else throw new Exception("[REST] No valid WebSockets URL in server reponse.");
                // Open the websocket?
                if (webSocket == null) WebSocketClient_Create(CreateWebsocket_URL(response.WebsocketUrl));
            }
            else throw new Exception("[REST] No valid URL in server reponse.");

            return InformationSubscriptionResult.OK;
        }
@@ -381,12 +381,12 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface

    public InformationSubscriptionResult UnsubscribeFromPose(Guid subscriptionUUID)
    {
        if (m_subscriptionsPoses.ContainsKey(subscriptionUUID))
        if (subscriptionsPoses.ContainsKey(subscriptionUUID))
        {
            apiClient.UnsubscribeFromPose(token, sessionID, subscriptionUUID);
            m_subscriptionsPoses.Remove(subscriptionUUID);
            subscriptionsPoses.Remove(subscriptionUUID);

            if (m_subscriptionsPoses.Count == 0)
            if (subscriptionsPoses.Count == 0)
            {
                // Close the connection via websockets
                WebSocketClient_Close();
+41 −38
Original line number Diff line number Diff line
#define IS_HHI_LAN

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using ETSI.ARF.OpenAPI.WorldAnalysis;

using Newtonsoft.Json;
using WebSocketSharp;

using ETSI.ARF.OpenAPI.WorldAnalysis;
using static WorldAnalysisInterface;

//Implementation of the WorldAnalysis interface
@@ -18,13 +21,21 @@ public partial class WorldAnalysisREST
    //
    // Private members
    //
    private WebSocketSharp.WebSocket webSocket;     // For WebSockets
    private bool websocketConnected = false;
    private WebSocketSharp.WebSocket webSocket = null;     // For WebSockets

    #region Communication system for WebSockets
    public WebSocket WebSocketClient_Create(string url)
    {
        webSocket = new WebSocketSharp.WebSocket(url);
        if (webSocket != null) return webSocket;

        string _url = url;

#if IS_HHI_LAN
        _url = "ws://192.168.20.29:8084/ws";
        Debug.LogWarning("[WS] Changing the websocket URL to: " + _url + " (local network VM, HHI)");
#endif

        webSocket = new WebSocketSharp.WebSocket(_url);

        //
        // Define standard callbacks
@@ -32,16 +43,14 @@ public partial class WorldAnalysisREST
        webSocket.OnOpen += (sender, e) =>
        {
            Debug.Log("[WS] Connected");
            websocketConnected = true;
            webSocket.Send("RegisterClient:UnitySceneManagement");
            webSocket.Send("RegisterClient=" + modulename);
        };
        webSocket.OnClose += (sender, e) =>
        {
            Debug.Log("[WS] Disconnected");
            websocketConnected = false;
        };
        webSocket.OnError += (sender, e) => Debug.Log("[WS] Error!");
        webSocket.OnMessage += (sender, e) => WebSocketClient_OnReceive(e.Data);
        webSocket.OnError += (sender, e) => Debug.Log("[WS] Websocket error!");
        webSocket.Connect();

        return webSocket;
@@ -49,9 +58,9 @@ public partial class WorldAnalysisREST

    private void WebSocketClient_Close()
    {
        if (websocketConnected)
        if (webSocket != null)
        {
            webSocket.Send("UnregisterClient");
            webSocket.Send("UnregisterClient=" + modulename);
            webSocket.Close();
            webSocket = null;
        }
@@ -63,56 +72,50 @@ public partial class WorldAnalysisREST
    }

    bool isRegistered = false;
    public void WebSocketClient_OnReceive(string data)
    public void WebSocketClient_OnReceive(string serverMessage)
    {
        //Debug.Log("[WS] Receiving: " + data);

        if (data.Contains("You are now registered"))
        if (serverMessage.Contains("You are now registered"))
        {
            isRegistered = true;
            if (isDebug)
            {
                webSocket.Send("TimeStart:3"); // test                
            }
            Debug.Log($"[WS] {serverMessage }");
            //Debug.Log($"[WS] Registration of { modulename } was succesfull.");
        }
        else if (isRegistered)
        {
            if (data.StartsWith("Time="))
            if (serverMessage == "PoseStop")
            {
                // test the communication
                Debug.Log("[WS] Server time is: " + data.Split('=')[1]);
                webSocketMessage?.Invoke(data);
                //SetColor(Color.yellow);
            }
            else if (data == "TimeStop")
            else if (serverMessage == "PoseIsNowSubscribed")
            {
                // Get some dummy poses?
                //webSocket.Send("PoseStart:5"); // test
            }
            else if (data == "PoseStop")
            else if (serverMessage == "PoseIsNowUnubscribed")
            {
                //SetColor(Color.yellow);
            }
            else if (data.StartsWith("Pose=") && data.Contains("estimationState"))
            else if (serverMessage.StartsWith("NewPose=") && serverMessage.Contains("estimationState"))
            {
                // Handle the new pose
                string json = data.Substring("Pose=".Length);
                ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose = JsonUtility.FromJson<ETSI.ARF.OpenAPI.WorldAnalysis.Pose>(json);
                Debug.Log("[WS][Pose] State: " + pose.EstimationState.ToString());
                string _str = serverMessage.Substring("NewPose=".Length);
                ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose = JsonConvert.DeserializeObject<ETSI.ARF.OpenAPI.WorldAnalysis.Pose>(_str);
                //Debug.Log("[WS] JSON - my new pose : " + pose.ToJson());

                // to check: p.Confidence
                // to check: p.Mode, p.Value 
                // to check: p.Confidence?

                PoseEstimationResult res = pose.EstimationState == PoseEstimationState.OK ? PoseEstimationResult.OK : PoseEstimationResult.FAILURE;

                // Look for the corresponding callbacks
                foreach (var item in m_subscriptionsPoses.Values)
                foreach (var item in subscriptionsPoses.Values)
                {
                    if (pose.Uuid == item.uuidTarget)
                    if (item.uuidTarget == pose.Uuid)
                    {
                        item.pose.Value = pose.Value;
                        item.callback(res, pose);
                    }
                }
            }
            else webSocketMessage?.Invoke(serverMessage);
        }
    }
#endregion
Loading