diff --git a/Runtime/Scripts/OpenAPI/DataModels.cs b/Runtime/Scripts/OpenAPI/DataModels.cs index 91d04fc170eaa3f828c803928f87feb1fd404229..0d2864d31d7729442315279c757705276ee5caa1 100644 --- a/Runtime/Scripts/OpenAPI/DataModels.cs +++ b/Runtime/Scripts/OpenAPI/DataModels.cs @@ -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 diff --git a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs index 126f5ab2e7d000f038fffb4bd997490724b11be5..713ebd5dec71dd6a7bb553d364b7b8ab6869d787 100644 --- a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs +++ b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs @@ -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> diff --git a/Runtime/Scripts/REST/AdminRequest.cs b/Runtime/Scripts/REST/AdminRequest.cs index 991052665a677fe50c1e5d03997b69a922fab000..048b10cd7e23a1a039c1386bdf6bebb90db61a43 100644 --- a/Runtime/Scripts/REST/AdminRequest.cs +++ b/Runtime/Scripts/REST/AdminRequest.cs @@ -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 // diff --git a/Runtime/Scripts/WorldAnalysisREST.cs b/Runtime/Scripts/WorldAnalysisREST.cs index 073f237184e5f1b08fff06aaec076c86c8672768..3b20a5224448429ad0f06188cadd06afa16faa3a 100644 --- a/Runtime/Scripts/WorldAnalysisREST.cs +++ b/Runtime/Scripts/WorldAnalysisREST.cs @@ -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,16 +196,28 @@ 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; - body.Mode = mode; + body.Mode = mode; body.Validity = validity; body.WebhookUrl = callback != null ? "" : "https:\\..."; // empty -> app will use websockets (client)! @@ -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(); diff --git a/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs b/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs index 118c780f75c235d252469c621d1311d53528b3cf..e2adc631c9e6f75ff552f7d0cb6a4776e5a92ae7 100644 --- a/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs +++ b/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs @@ -1,10 +1,13 @@ +#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,57 +72,51 @@ 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()); - - // to check: p.Confidence - // to check: p.Mode, p.Value - + 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? + 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 +#endregion } \ No newline at end of file diff --git a/Runtime/Scripts/WorldAnalysisUnityHelper.cs b/Runtime/Scripts/WorldAnalysisUnityHelper.cs index b6f5b21805dbfb4d1023890eca2dd69465f7a570..830de284d346ca9fff7553343e2ccb965fabe1e2 100644 --- a/Runtime/Scripts/WorldAnalysisUnityHelper.cs +++ b/Runtime/Scripts/WorldAnalysisUnityHelper.cs @@ -33,7 +33,7 @@ public class WorldAnalysisUnityHelper /// </summary> /// <param name="matrix">the values to convert</param> /// <returns>Converted Unity Matrix</returns> - public static Matrix4x4 ConvertETSIARFTransform3DToUnity(ETSI.ARF.OpenAPI.WorldStorage.Transform3D value) + public static Matrix4x4 ConvertETSIARFTransform3DToUnity(ETSI.ARF.OpenAPI.WorldAnalysis.Transform3D value) { if (value.Count == 16) {