Loading Runtime/Scripts/OpenAPI/DataModels.cs +8 −2 Original line number Diff line number Diff line Loading @@ -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 // Loading @@ -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 Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs +1 −1 Original line number Diff line number Diff line Loading @@ -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> Loading Runtime/Scripts/REST/AdminRequest.cs +12 −0 Original line number Diff line number Diff line Loading @@ -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 // Loading Runtime/Scripts/WorldAnalysisREST.cs +64 −64 Original line number Diff line number Diff line Loading @@ -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 // Loading @@ -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; } Loading @@ -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); Loading @@ -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) { } } Loading @@ -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 = ""; Loading @@ -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) Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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; Loading @@ -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; Loading @@ -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; } Loading @@ -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(); Loading Runtime/Scripts/WorldAnalysisREST_WebSockets.cs +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 Loading @@ -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 Loading @@ -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; Loading @@ -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; } Loading @@ -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 Loading
Runtime/Scripts/OpenAPI/DataModels.cs +8 −2 Original line number Diff line number Diff line Loading @@ -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 // Loading @@ -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
Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs +1 −1 Original line number Diff line number Diff line Loading @@ -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> Loading
Runtime/Scripts/REST/AdminRequest.cs +12 −0 Original line number Diff line number Diff line Loading @@ -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 // Loading
Runtime/Scripts/WorldAnalysisREST.cs +64 −64 Original line number Diff line number Diff line Loading @@ -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 // Loading @@ -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; } Loading @@ -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); Loading @@ -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) { } } Loading @@ -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 = ""; Loading @@ -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) Loading @@ -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 Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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); Loading @@ -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; Loading @@ -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; Loading @@ -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; } Loading @@ -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(); Loading
Runtime/Scripts/WorldAnalysisREST_WebSockets.cs +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 Loading @@ -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 Loading @@ -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; Loading @@ -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; } Loading @@ -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