From 7e30b8448e183bbd8d0f3b38f7276fff6e788c20 Mon Sep 17 00:00:00 2001 From: Sylvain Buche Date: Fri, 17 May 2024 16:45:22 +0200 Subject: [PATCH 1/6] Change string uuid to guid type --- Runtime/Scripts/WorldAnalysisInterface.cs | 19 ++++++++++--------- Runtime/Scripts/WorldAnalysisREST.cs | 21 +++++++++++---------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Runtime/Scripts/WorldAnalysisInterface.cs b/Runtime/Scripts/WorldAnalysisInterface.cs index 727ebc5..1efeacd 100644 --- a/Runtime/Scripts/WorldAnalysisInterface.cs +++ b/Runtime/Scripts/WorldAnalysisInterface.cs @@ -1,4 +1,5 @@ -using ETSI.ARF.OpenAPI.WorldAnalysis; +using ETSI.ARF.OpenAPI.WorldAnalysis; +using System; public interface WorldAnalysisInterface { @@ -83,7 +84,7 @@ public interface WorldAnalysisInterface /// trackable/anchor to ar device or opposite RQ-AR8-001 The WA function shall provide the SM function with a pose of either an AR device in relation to a WorldAnchor or a Trackable, or conversely a Trackable or World Anchor in relation to the AR device. /// Last estimated pose /// The last state of the estimation of this pose - public PoseEstimationResult GetLastPose(string token, string uuid, Mode_WorldAnalysis mode, out Pose pose); + public PoseEstimationResult GetLastPose(string token, Guid uuid, Mode_WorldAnalysis mode, out Pose pose); /// /// Pull the most recent pose of a multiple trackables and anchors @@ -94,7 +95,7 @@ public interface WorldAnalysisInterface /// list of trackable/anchor to ar device or opposite RQ-AR8-001 The WA function shall provide the SM function with a pose of either an AR device in relation to a WorldAnchor or a Trackable, or conversely a Trackable or World Anchor in relation to the AR device. /// Last estimated pose /// The last state of the estimation of this pose - public PoseEstimationResult[] GetLastPoses(string token, string[] uuids, Mode_WorldAnalysis [] modes, out Pose [] poses); + public PoseEstimationResult[] GetLastPoses(string token, Guid[] uuids, Mode_WorldAnalysis [] modes, out Pose [] poses); /// /// Subscribe to receive the pose data of a given trackable or anchor @@ -107,7 +108,7 @@ public interface WorldAnalysisInterface /// Subscription validity delay in millisecond /// id of the callback /// Result of the subscription - public InformationSubscriptionResult SubscribeToPose(string token, string uuid, Mode_WorldAnalysis mode, PoseCallback callback, ref int validity, out string subscriptionUUID); + public InformationSubscriptionResult SubscribeToPose(string token, Guid uuid, Mode_WorldAnalysis mode, PoseCallback callback, ref int validity, out Guid subscriptionUUID); /// /// Subscribe to receive the pose data of a multiple trackables and anchors @@ -120,7 +121,7 @@ public interface WorldAnalysisInterface /// Subscription validity delay in millisecond /// id of the callback /// List of subscription result - public InformationSubscriptionResult[] SubscribeToPoses(string token, string[] uuids, Mode_WorldAnalysis [] modes, PoseCallback callback, ref int validity, out string[] subscriptionUUIDs); + public InformationSubscriptionResult[] SubscribeToPoses(string token, Guid[] uuids, Mode_WorldAnalysis [] modes, PoseCallback callback, ref int validity, out Guid[] subscriptionUUIDs); /// /// Get the subscription and the associated callback for collecting a given pose @@ -132,7 +133,7 @@ public interface WorldAnalysisInterface /// Mode representing the context of the relocalization information (AR device to WorldAnchor/Trackable or WorldAnchor/Trackable to AR device) /// Remaining Subscription validity delay in millisecond /// The subscription exists or not - public InformationSubscriptionResult GetSubsription(string token, string subscriptionUUID, out PoseCallback callback, out string target, out Mode_WorldAnalysis mode, out int validity); + public InformationSubscriptionResult GetSubsription(string token, Guid subscriptionUUID, out PoseCallback callback, out Guid target, out Mode_WorldAnalysis mode, out int validity); /// /// Update an existing subscription @@ -143,14 +144,14 @@ public interface WorldAnalysisInterface /// new validity of the subscription /// new callback to use /// The subscription has been updated or not - public InformationSubscriptionResult UpdateSubscription(string token, string subscriptionUUID, Mode_WorldAnalysis mode, int validity, PoseCallback callback); + public InformationSubscriptionResult UpdateSubscription(string token, Guid subscriptionUUID, Mode_WorldAnalysis mode, int validity, PoseCallback callback); /// /// Unsubscribe to a pose /// /// id of the subscription /// /// The unsubscription has been performed or not - public InformationSubscriptionResult UnSubscribeToPose(string subscriptionUUID); + public InformationSubscriptionResult UnSubscribeToPose(Guid subscriptionUUID); #endregion @@ -175,7 +176,7 @@ public interface WorldAnalysisInterface /// the specified id corresponds to an anchor or trackable /// list metadata associated with the pose estimation of this element /// Capabilities have correctly identified or not - public CapabilityResult GetCapability(string token, string uuid, out bool isSupported, out TypeWorldStorage type, out Capability [] capability); + public CapabilityResult GetCapability(string token, Guid uuid, out bool isSupported, out TypeWorldStorage type, out Capability [] capability); #endregion } \ No newline at end of file diff --git a/Runtime/Scripts/WorldAnalysisREST.cs b/Runtime/Scripts/WorldAnalysisREST.cs index 9d1de9d..5c18ceb 100644 --- a/Runtime/Scripts/WorldAnalysisREST.cs +++ b/Runtime/Scripts/WorldAnalysisREST.cs @@ -1,6 +1,7 @@ using UnityEngine; using ETSI.ARF.OpenAPI.WorldAnalysis; using static WorldAnalysisInterface; +using System; //Implementation of the WorldAnalysis interface public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface @@ -39,46 +40,46 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface return AskFrameRateResult.NOT_SUPPORTED; ///We cannot set any framerate for tracking on ARKit and ARCore } - public PoseEstimationResult GetLastPose(string token, string uuid, Mode_WorldAnalysis mode, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose) + public PoseEstimationResult GetLastPose(string token, Guid uuid, Mode_WorldAnalysis mode, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose) { pose = null; return PoseEstimationResult.OK; } - public PoseEstimationResult[] GetLastPoses(string token, string[] uuids, Mode_WorldAnalysis [] modes, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose[] poses) + public PoseEstimationResult[] GetLastPoses(string token, Guid[] uuids, Mode_WorldAnalysis [] modes, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose[] poses) { poses = null; return null; } - public InformationSubscriptionResult SubscribeToPose(string token, string uuid, Mode_WorldAnalysis mode, PoseCallback callback, ref int validity, out string subscriptionUUID) + public InformationSubscriptionResult SubscribeToPose(string token, Guid uuid, Mode_WorldAnalysis mode, PoseCallback callback, ref int validity, out Guid subscriptionUUID) { - subscriptionUUID = ""; + subscriptionUUID = Guid.Empty; return InformationSubscriptionResult.OK; } - public InformationSubscriptionResult[] SubscribeToPoses(string token, string[] uuids, Mode_WorldAnalysis [] modes, PoseCallback callback, ref int validity, out string[] subscriptionUUIDs) + public InformationSubscriptionResult[] SubscribeToPoses(string token, Guid[] uuids, Mode_WorldAnalysis [] modes, PoseCallback callback, ref int validity, out Guid[] subscriptionUUIDs) { subscriptionUUIDs = null; return null; } - public InformationSubscriptionResult GetSubsription(string token, string subscriptionUUID, out PoseCallback callback, out string target, out Mode_WorldAnalysis mode, out int validity) + public InformationSubscriptionResult GetSubsription(string token, Guid subscriptionUUID, out PoseCallback callback, out Guid target, out Mode_WorldAnalysis mode, out int validity) { callback = null; - target = ""; + target = Guid.Empty; mode = Mode_WorldAnalysis.TRACKABLES_TO_DEVICE; validity = 0; return InformationSubscriptionResult.OK; } - public InformationSubscriptionResult UpdateSubscription(string token, string subscriptionUUID, Mode_WorldAnalysis mode, int validity, PoseCallback callback) + public InformationSubscriptionResult UpdateSubscription(string token, Guid subscriptionUUID, Mode_WorldAnalysis mode, int validity, PoseCallback callback) { return InformationSubscriptionResult.OK; } - public InformationSubscriptionResult UnSubscribeToPose(string subscriptionUUID) + public InformationSubscriptionResult UnSubscribeToPose(Guid subscriptionUUID) { return InformationSubscriptionResult.OK; } @@ -89,7 +90,7 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface return CapabilityResult.OK; } - public CapabilityResult GetCapability(string token, string uuid, out bool isSupported, out TypeWorldStorage type, out Capability [] capability) + public CapabilityResult GetCapability(string token, Guid uuid, out bool isSupported, out TypeWorldStorage type, out Capability [] capability) { isSupported = false; type = TypeWorldStorage.UNKNOWN; -- GitLab From cafd4e6c5d163bd637415b09531f072fb38f1644 Mon Sep 17 00:00:00 2001 From: Sylvain Renault Date: Thu, 8 Aug 2024 18:01:33 +0200 Subject: [PATCH 2/6] Preparation of REST and websocket client functions. Some namepsace changes. --- Editor/Scripts/WorldAnalysisInfoEditor.cs | 39 +++ .../Scripts/WorldAnalysisInfoEditor.cs.meta | 11 + Editor/Scripts/WorldAnalysisServerEditor.cs | 174 ++++++++++++++ .../Scripts/WorldAnalysisServerEditor.cs.meta | 11 + Editor/Scripts/WorldAnalysisUserEditor.cs | 51 ++++ .../Scripts/WorldAnalysisUserEditor.cs.meta | 11 + Runtime/Scriptables.meta | 8 + Runtime/Scriptables/WorldAnalysisServer.cs | 39 +++ .../Scriptables/WorldAnalysisServer.cs.meta | 11 + Runtime/Scriptables/WorldAnalysisUser.cs | 33 +++ Runtime/Scriptables/WorldAnalysisUser.cs.meta | 11 + Runtime/Scripts/OpenAPI/BaseClient.cs | 38 +++ Runtime/Scripts/OpenAPI/BaseClient.cs.meta | 11 + Runtime/Scripts/OpenAPI/DataModels.cs | 42 ++++ Runtime/Scripts/OpenAPI/DataModels.cs.meta | 11 + .../OpenAPI/Generated/WorldAnalysisOpenAPI.cs | 10 +- Runtime/Scripts/OpenAPI/ResponseObject.cs | 65 +++++ .../Scripts/OpenAPI/ResponseObject.cs.meta | 11 + .../OpenAPI/UnityWebRequestHttpClient.cs | 225 ++++++++++++++++++ .../OpenAPI/UnityWebRequestHttpClient.cs.meta | 11 + .../Scripts/OpenAPI/WorldAnalysisClient.cs | 28 +++ .../OpenAPI/WorldAnalysisClient.cs.meta | 11 + Runtime/Scripts/REST.meta | 8 + Runtime/Scripts/REST/AdminRequest.cs | 101 ++++++++ Runtime/Scripts/REST/AdminRequest.cs.meta | 11 + Runtime/Scripts/REST/RequestBase.cs | 79 ++++++ Runtime/Scripts/REST/RequestBase.cs.meta | 11 + Runtime/Scripts/WorldAnalysisInfo.cs | 45 ++++ Runtime/Scripts/WorldAnalysisInfo.cs.meta | 11 + Runtime/Scripts/WorldAnalysisREST.cs | 69 +++++- 30 files changed, 1186 insertions(+), 11 deletions(-) create mode 100644 Editor/Scripts/WorldAnalysisInfoEditor.cs create mode 100644 Editor/Scripts/WorldAnalysisInfoEditor.cs.meta create mode 100644 Editor/Scripts/WorldAnalysisServerEditor.cs create mode 100644 Editor/Scripts/WorldAnalysisServerEditor.cs.meta create mode 100644 Editor/Scripts/WorldAnalysisUserEditor.cs create mode 100644 Editor/Scripts/WorldAnalysisUserEditor.cs.meta create mode 100644 Runtime/Scriptables.meta create mode 100644 Runtime/Scriptables/WorldAnalysisServer.cs create mode 100644 Runtime/Scriptables/WorldAnalysisServer.cs.meta create mode 100644 Runtime/Scriptables/WorldAnalysisUser.cs create mode 100644 Runtime/Scriptables/WorldAnalysisUser.cs.meta create mode 100644 Runtime/Scripts/OpenAPI/BaseClient.cs create mode 100644 Runtime/Scripts/OpenAPI/BaseClient.cs.meta create mode 100644 Runtime/Scripts/OpenAPI/DataModels.cs create mode 100644 Runtime/Scripts/OpenAPI/DataModels.cs.meta create mode 100644 Runtime/Scripts/OpenAPI/ResponseObject.cs create mode 100644 Runtime/Scripts/OpenAPI/ResponseObject.cs.meta create mode 100644 Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs create mode 100644 Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs.meta create mode 100644 Runtime/Scripts/OpenAPI/WorldAnalysisClient.cs create mode 100644 Runtime/Scripts/OpenAPI/WorldAnalysisClient.cs.meta create mode 100644 Runtime/Scripts/REST.meta create mode 100644 Runtime/Scripts/REST/AdminRequest.cs create mode 100644 Runtime/Scripts/REST/AdminRequest.cs.meta create mode 100644 Runtime/Scripts/REST/RequestBase.cs create mode 100644 Runtime/Scripts/REST/RequestBase.cs.meta create mode 100644 Runtime/Scripts/WorldAnalysisInfo.cs create mode 100644 Runtime/Scripts/WorldAnalysisInfo.cs.meta diff --git a/Editor/Scripts/WorldAnalysisInfoEditor.cs b/Editor/Scripts/WorldAnalysisInfoEditor.cs new file mode 100644 index 0000000..70a489e --- /dev/null +++ b/Editor/Scripts/WorldAnalysisInfoEditor.cs @@ -0,0 +1,39 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: August 2024 +// + +using UnityEditor; + +[CustomEditor(typeof(WorldAnalysisInfo))] +public class WorldAnalysisInfoEditor : Editor +{ + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + + EditorGUILayout.Space(); + WorldAnalysisInfo srv = (WorldAnalysisInfo)target; + + string state = "";// srv.GetServerState(); + EditorGUILayout.LabelField("Server State", state); + + string api = "";// srv.GetAPIVersion(); + EditorGUILayout.LabelField("OpenAPI Version", api); + } +} diff --git a/Editor/Scripts/WorldAnalysisInfoEditor.cs.meta b/Editor/Scripts/WorldAnalysisInfoEditor.cs.meta new file mode 100644 index 0000000..0642c98 --- /dev/null +++ b/Editor/Scripts/WorldAnalysisInfoEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b890e537ebae974b862e97bfefa51ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Scripts/WorldAnalysisServerEditor.cs b/Editor/Scripts/WorldAnalysisServerEditor.cs new file mode 100644 index 0000000..4fe96c9 --- /dev/null +++ b/Editor/Scripts/WorldAnalysisServerEditor.cs @@ -0,0 +1,174 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: July 2024 +// + +using System.Collections; +using UnityEngine; +using UnityEditor; + +using ETSI.ARF.OpenAPI; +using ETSI.ARF.OpenAPI.WorldAnalysis; +using ETSI.ARF.WorldAnalysis; +using ETSI.ARF.WorldAnalysis.REST; + +[CustomEditor(typeof(WorldAnalysisServer))] +public class WorldStorageServerEditor : Editor +{ + WorldAnalysisServer server; + + private string state = ""; + private string version = ""; + + private string test = ""; + + private Queue handleResponseQueue = new Queue(); + + private ResponseObject pendingTest = null; + private ResponseObject pendingState = null; + private ResponseObject pendingVersion = null; + + public void OnEnable() + { + server = (WorldAnalysisServer)target; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + DrawDefaultInspector(); + EditorGUILayout.Space(); + + if (GUILayout.Button("Test server")) + { + TestPing(); + } + EditorGUILayout.LabelField("Test Response", test); + + EditorGUILayout.Space(); + + if (GUILayout.Button("Query server")) + { + QueryServer(); + } + + EditorGUILayout.LabelField("Server State", state); + EditorGUILayout.LabelField("OpenAPI Version", version); + + if (handleResponseQueue.Count > 0) + { + object o = handleResponseQueue.Dequeue(); + if (o.Equals(pendingTest)) + { + ResponseObject response = o as ResponseObject; + Debug.Log($"Get '{response.result}' from server"); + + test = response.result; + pendingTest = null; + EditorUtility.SetDirty(target); + } + else if (o.Equals(pendingState)) + { + ResponseObject response = o as ResponseObject; + Debug.Log($"Get '{response.result}' from server"); + + state = response.result; + pendingState = null; + EditorUtility.SetDirty(target); + } + else if (o.Equals(pendingVersion)) + { + ResponseObject response = o as ResponseObject; + Debug.Log($"Get '{response.result}' from server"); + + version = response.result; + pendingVersion = null; + EditorUtility.SetDirty(target); + } + else + { + Debug.Log("Unsupported response!"); + } + } + } + + public override bool RequiresConstantRepaint() + { + return handleResponseQueue.Count > 0; + } + + void OnSceneGUI() + { + Debug.Log("OnSceneGUI"); + } + + private void TestPing() + { + test = ""; + EditorUtility.SetDirty(target); + + if (server == null) + { + Debug.LogError("No server defined!"); + return; + } + + //string response = AdminRequest.PingSync(server); + //EditorUtility.DisplayDialog("Test Server", $"Get '{response}' from server", "OK"); + + if (pendingTest != null) + { + pendingTest.Cancel(); + } + + pendingTest = AdminRequest.PingAsync(server, (response) => + { + handleResponseQueue.Enqueue(response); + Debug.Log($"Request Time: { response.requestTime.ToLongTimeString() } / Total Time: { response.DeltaTime.TotalMilliseconds }ms\n\nContent:\n{ response.result }"); + }); + + Debug.Log("Starting request @ time: " + pendingTest.requestTime.ToLongTimeString() + "..."); + } + + private void QueryServer() + { + version = ""; + state = ""; + + if (pendingState != null) + { + pendingState.Cancel(); + } + + pendingState = AdminRequest.AdminAsync(server, (response) => + { + handleResponseQueue.Enqueue(response); + }); + + if (pendingVersion != null) + { + pendingVersion.Cancel(); + } + + pendingVersion = AdminRequest.VersionAsync(server, (response) => + { + handleResponseQueue.Enqueue(response); + }); + } +} \ No newline at end of file diff --git a/Editor/Scripts/WorldAnalysisServerEditor.cs.meta b/Editor/Scripts/WorldAnalysisServerEditor.cs.meta new file mode 100644 index 0000000..b6c4ebc --- /dev/null +++ b/Editor/Scripts/WorldAnalysisServerEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9899978f4c750de4b98f81a9c6937a94 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Scripts/WorldAnalysisUserEditor.cs b/Editor/Scripts/WorldAnalysisUserEditor.cs new file mode 100644 index 0000000..043379e --- /dev/null +++ b/Editor/Scripts/WorldAnalysisUserEditor.cs @@ -0,0 +1,51 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2022 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: June 2022 +// + +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; + +using ETSI.ARF.WorldAnalysis; + +[CustomEditor(typeof(WorldAnalysisUser))] +public class WorldStorageUserEditor : Editor +{ + WorldAnalysisUser user; + + public void OnEnable() + { + user = (WorldAnalysisUser)target; + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + DrawDefaultInspector(); + EditorGUILayout.Space(); + + if (GUILayout.Button("Generate New Creator UUID")) + { + user.UUID = System.Guid.NewGuid().ToString(); + EditorUtility.SetDirty(target); + } + } +} diff --git a/Editor/Scripts/WorldAnalysisUserEditor.cs.meta b/Editor/Scripts/WorldAnalysisUserEditor.cs.meta new file mode 100644 index 0000000..74303eb --- /dev/null +++ b/Editor/Scripts/WorldAnalysisUserEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0d1b5ae7e5771b4496dfd778bfa031b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scriptables.meta b/Runtime/Scriptables.meta new file mode 100644 index 0000000..058c09b --- /dev/null +++ b/Runtime/Scriptables.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 24a8b3224329f984c9b1737d44f11163 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scriptables/WorldAnalysisServer.cs b/Runtime/Scriptables/WorldAnalysisServer.cs new file mode 100644 index 0000000..fb21b57 --- /dev/null +++ b/Runtime/Scriptables/WorldAnalysisServer.cs @@ -0,0 +1,39 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: August 2024 +// + +using UnityEngine; + +namespace ETSI.ARF.WorldAnalysis +{ + [System.Serializable] + [CreateAssetMenu(fileName = "ARFWorldAnalysisServer", menuName = "ARF World Analysis/Create Server", order = 1)] + public class WorldAnalysisServer : ScriptableObject + { + [SerializeField] public string serverName = "myServerName"; + [SerializeField] public string company = ""; + [SerializeField] public string basePath = "https://"; + [SerializeField] public int port = 8080; + + [Space(8)] + [SerializeField] public WorldAnalysisUser currentUser = null; + + public string URI => port == 0 ? basePath : basePath + ":" + port.ToString(); + } +} \ No newline at end of file diff --git a/Runtime/Scriptables/WorldAnalysisServer.cs.meta b/Runtime/Scriptables/WorldAnalysisServer.cs.meta new file mode 100644 index 0000000..7abd0ff --- /dev/null +++ b/Runtime/Scriptables/WorldAnalysisServer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4571d019d0609224aa4a14ed18de30cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scriptables/WorldAnalysisUser.cs b/Runtime/Scriptables/WorldAnalysisUser.cs new file mode 100644 index 0000000..85faade --- /dev/null +++ b/Runtime/Scriptables/WorldAnalysisUser.cs @@ -0,0 +1,33 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: August 2024 +// + +using UnityEngine; + +namespace ETSI.ARF.WorldAnalysis +{ + [System.Serializable] + [CreateAssetMenu(fileName = "ARFWorldAnalysisUser", menuName = "ARF World Analysis/Create User", order = 1)] + public class WorldAnalysisUser : ScriptableObject + { + [SerializeField] public string userName = "myName"; + [SerializeField] public string company = ""; + [SerializeField] public string UUID = System.Guid.Empty.ToString(); + } +} \ No newline at end of file diff --git a/Runtime/Scriptables/WorldAnalysisUser.cs.meta b/Runtime/Scriptables/WorldAnalysisUser.cs.meta new file mode 100644 index 0000000..93b285f --- /dev/null +++ b/Runtime/Scriptables/WorldAnalysisUser.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01f9889f0a04026429aa8459e181e973 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/OpenAPI/BaseClient.cs b/Runtime/Scripts/OpenAPI/BaseClient.cs new file mode 100644 index 0000000..c93808e --- /dev/null +++ b/Runtime/Scripts/OpenAPI/BaseClient.cs @@ -0,0 +1,38 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace ETSI.ARF.OpenAPI.WorldAnalysis +{ + /// + /// Simple class to debug the requests + /// + public class BaseClient + { + static public bool EnableClientLog = true; + public string lastJsonText; + public long lastPayload; + + protected void _prepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url) + { + if (EnableClientLog) + { + Debug.Log("[REST][URL] Send request: " + client.BaseAddress + url); + Debug.Log("[REST][URL] Send request: " + request); + } + } + + protected void _processResponse(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpResponseMessage response) + { + lastJsonText = response.Content.ReadAsStringAsync().Result.ToString(); + lastPayload = response.Content.Headers.ContentLength.Value; + + var status_ = (int)response.StatusCode; + + if (EnableClientLog) + { + Debug.Log("[REST][Data] Status: " + status_ + " Response: " + client.BaseAddress + " Len: " + lastPayload + " JSON: " + lastJsonText); + } + } + } +} \ No newline at end of file diff --git a/Runtime/Scripts/OpenAPI/BaseClient.cs.meta b/Runtime/Scripts/OpenAPI/BaseClient.cs.meta new file mode 100644 index 0000000..0af7d2c --- /dev/null +++ b/Runtime/Scripts/OpenAPI/BaseClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 696362f892252db4ca192c6eccbbfa25 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/OpenAPI/DataModels.cs b/Runtime/Scripts/OpenAPI/DataModels.cs new file mode 100644 index 0000000..91d04fc --- /dev/null +++ b/Runtime/Scripts/OpenAPI/DataModels.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace ETSI.ARF.OpenAPI.WorldAnalysis +{ + public interface IModel + { + public System.Guid Uuid { get; set; } // Bug: SylR: Why is Uuid not capitalized (UUID)??? + + public string ToJson(); + } + + // Class to monitor the server + public class Server : IModel + { + public System.Guid Uuid { get; set; } + public string Name { get; set; } + + public Server(string name) + { + Uuid = Guid.Empty; + Name = name; + } + + public string ToJson() { return JsonUtility.ToJson(this); } + } + + // + // Implement here some constructors + // + public partial class Pose : IModel + { + public Pose() + { + Uuid = Guid.NewGuid(); + } + + public string ToJson() { return JsonUtility.ToJson(this); } + } +} \ No newline at end of file diff --git a/Runtime/Scripts/OpenAPI/DataModels.cs.meta b/Runtime/Scripts/OpenAPI/DataModels.cs.meta new file mode 100644 index 0000000..ba6ef36 --- /dev/null +++ b/Runtime/Scripts/OpenAPI/DataModels.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90e60e9d41d419146b3b6207e4ef556e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs index d9ac029..a59f807 100644 --- a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs +++ b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs @@ -24,11 +24,11 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class WorldAnalysisClient { - private ETSI.ARF.OpenAPI.IHttpClient _httpClient; + private ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient _httpClient; private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public WorldAnalysisClient(ETSI.ARF.OpenAPI.IHttpClient httpClient) + public WorldAnalysisClient(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient httpClient) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { _httpClient = httpClient; @@ -45,9 +45,9 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - partial void PrepareRequest(ETSI.ARF.OpenAPI.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(ETSI.ARF.OpenAPI.IHttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(ETSI.ARF.OpenAPI.IHttpClient client, System.Net.Http.HttpResponseMessage response); + partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpResponseMessage response); /// /// Test the server availability. diff --git a/Runtime/Scripts/OpenAPI/ResponseObject.cs b/Runtime/Scripts/OpenAPI/ResponseObject.cs new file mode 100644 index 0000000..fa21f3d --- /dev/null +++ b/Runtime/Scripts/OpenAPI/ResponseObject.cs @@ -0,0 +1,65 @@ +// The Fraunhofer HHI Unity Framework +// ___________ .__ _____ ___ ___ ___ ___ .___ +// \_ _____/___________ __ __ ____ | |__ _____/ ____\___________ / | \ / | \| | +// | __) \_ __ \__ \ | | \/ \| | \ / _ \ __\/ __ \_ __ \ / ~ \/ ~ \ | +// | \ | | \// __ \| | / | \ Y ( <_> ) | \ ___/| | \/ \ Y /\ Y / | +// \___ / |__| (____ /____/|___| /___| /\____/|__| \___ >__| \___|_ / \___|_ /|___| +// \/ \/ \/ \/ \/ \/ \/ +// (C) Fraunhofer HHI, 2024 + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using UnityEngine; + +namespace ETSI.ARF.OpenAPI.WorldAnalysis +{ + public class CancelToken + { + protected CancellationTokenSource tokenSource; + protected CancellationToken ct; + + public CancellationToken cancellationToken { get => ct; } + + public void Cancel() + { + tokenSource.Cancel(); + } + } + + public class ResponseObject : CancelToken + { + // Management stuffs + static int ID = 0; + public int transactionId = 0; + public string message = ""; // custom message, type of data... + + // Time monitoring + public TimeSpan DeltaTime { get => responseTime - requestTime; } + public DateTime requestTime; + public DateTime responseTime; + + // Incoming data + public T result; + public int payload; // size of data + + //public string result = ""; // text result + //public object data = null; // custom result + + // Callback + public Action> callback; + + + public ResponseObject(string msg, Action> func = null) + { + requestTime = DateTime.Now; + message = msg; + callback = func; + transactionId = ++ID; + + tokenSource = new CancellationTokenSource(); + ct = tokenSource.Token; + } + } +} \ No newline at end of file diff --git a/Runtime/Scripts/OpenAPI/ResponseObject.cs.meta b/Runtime/Scripts/OpenAPI/ResponseObject.cs.meta new file mode 100644 index 0000000..0f1afb5 --- /dev/null +++ b/Runtime/Scripts/OpenAPI/ResponseObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 82dc58594acebf747bd716fd00fcfb10 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs b/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs new file mode 100644 index 0000000..8d696aa --- /dev/null +++ b/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs @@ -0,0 +1,225 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: June 2024 +// + +// Depends on UniTask to support cancellation token and GetAwaiter: https://github.com/Cysharp/UniTask +// Otherwise, the code can be adapted using https://gist.github.com/krzys-h/9062552e33dd7bd7fe4a6c12db109a1a + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +//using Cysharp.Threading.Tasks; + +using UnityEngine; +using UnityEngine.Networking; + +namespace ETSI.ARF.OpenAPI.WorldAnalysis +{ + public interface IHttpClient + { + public Uri BaseAddress { get; set; } + public HttpRequestHeaders DefaultRequestHeaders { get; } + + public Task SendAsync(HttpRequestMessage message, HttpCompletionOption option, + CancellationToken token); + + public void Dispose(); + } + + public class BasicHTTPClient : IHttpClient + { + + public BasicHTTPClient() { } + + public BasicHTTPClient(string baseUri) + { + BaseAddress = new Uri(baseUri); + _httpClient.BaseAddress = BaseAddress; + } + + public BasicHTTPClient(Uri baseUri) + { + BaseAddress = baseUri; + _httpClient.BaseAddress = BaseAddress; + } + + public Uri BaseAddress { get; set; } + public HttpRequestHeaders DefaultRequestHeaders => _httpClient.DefaultRequestHeaders; + + private readonly HttpClient _httpClient = new HttpClient(); + + public async Task SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token) + { + return await _httpClient.SendAsync(message, option, token); + } + + public void Dispose() + { + _httpClient.Dispose(); + DefaultRequestHeaders.Clear(); + BaseAddress = null; + } + } + + public class UnityWebRequestHttpClient : IHttpClient + { + public UnityWebRequestHttpClient() { } + + public UnityWebRequestHttpClient(string baseUri) + { + BaseAddress = new Uri(baseUri); + } + + public UnityWebRequestHttpClient(Uri baseUri) + { + BaseAddress = baseUri; + } + + public Uri BaseAddress { get; set; } + public HttpRequestHeaders DefaultRequestHeaders => _httpClient.DefaultRequestHeaders; + + private readonly HttpClient _httpClient = new HttpClient(); + + public async Task SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token) + { + var content = await (message.Content?.ReadAsStringAsync() ?? Task.FromResult("")); + var webRequest = GetUnityWebRequest(message.Method.Method, message.RequestUri, content); + + AppendHeaders(webRequest); + + Debug.Log("[HTTP] Request " + webRequest.uri.ToString()); + try + { + //SylR + webRequest.SendWebRequest(); + while (!webRequest.isDone) + { + if (token.IsCancellationRequested) + { + Debug.Log($"Task '{ message.RequestUri }' cancelled"); + token.ThrowIfCancellationRequested(); + } + await Task.Yield(); + } + + //await webRequest + // .SendWebRequest() + // .WithCancellation(cancellationToken: token); + } + catch (Exception) + { + webRequest.Dispose(); + throw; + } + + Debug.Log("[HTTP] Result: " + webRequest.result.ToString()); + + var responseMessage = CreateHttpResponseMessage(webRequest); + webRequest.Dispose(); + + Debug.Log("[HTTP] Response len: " + responseMessage.Content.Headers.ContentLength); + + return responseMessage; + } + + public void Dispose() + { + _httpClient.Dispose(); + DefaultRequestHeaders.Clear(); + BaseAddress = null; + } + + private UnityWebRequest GetUnityWebRequest(string method, Uri endpoint, string content = "") + { + var requestUri = BaseAddress.AbsoluteUri + endpoint; + var webRequest = UnityWebRequest.Get(requestUri); + webRequest.method = method; + + webRequest.disposeUploadHandlerOnDispose = true; + webRequest.disposeDownloadHandlerOnDispose = true; + + if (!string.IsNullOrEmpty(content)) + { + var data = new System.Text.UTF8Encoding().GetBytes(content); + webRequest.uploadHandler = new UploadHandlerRaw(data); + webRequest.SetRequestHeader("Content-Type", "application/json"); + //webRequest.SetRequestHeader("Content-Type", "image/jpeg"); + } + return webRequest; + } + + private void AppendHeaders(UnityWebRequest webRequest) + { + using var enumerator = DefaultRequestHeaders.GetEnumerator(); + + while (enumerator.MoveNext()) + { + var (key, value) = enumerator.Current; + webRequest.SetRequestHeader(key, value.First()); + } + } + + private HttpResponseMessage CreateHttpResponseMessage(UnityWebRequest webRequest) + { + var responseContent = webRequest.downloadHandler?.text; + + var response = new HttpResponseMessage(); + response.Content = new StringContent(responseContent); + response.StatusCode = (HttpStatusCode)webRequest.responseCode; + + Dictionary headers = webRequest.GetResponseHeaders(); + + if (headers != null) + { + Debug.Log("[HTTP] Header: " + headers.Count.ToString()); + foreach (var h in headers) + { + switch (h.Key.ToLower().Trim()) + { + case "content-type": + { + var trimmed = h.Value.ToLower().Split(";").FirstOrDefault(); + response.Content.Headers.ContentType = new MediaTypeHeaderValue(trimmed); + break; + } + case "content-length": + response.Content.Headers.ContentLength = long.Parse(h.Value); + break; + + default: + if (h.Value == "gzip") + { + // bug??? + } + else + response.Headers.Add(h.Key, h.Value); + break; + } + } + } + return response; + } + } + +} \ No newline at end of file diff --git a/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs.meta b/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs.meta new file mode 100644 index 0000000..7524d3a --- /dev/null +++ b/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4aa105d9487dc27408feffefde9e4475 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/OpenAPI/WorldAnalysisClient.cs b/Runtime/Scripts/OpenAPI/WorldAnalysisClient.cs new file mode 100644 index 0000000..852ee38 --- /dev/null +++ b/Runtime/Scripts/OpenAPI/WorldAnalysisClient.cs @@ -0,0 +1,28 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Networking; + +namespace ETSI.ARF.OpenAPI.WorldAnalysis +{ + /// + /// Catch the pre/pos request methods from the autogenerated classes + /// + public partial class WorldAnalysisClient : BaseClient + { + partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url) + { + _prepareRequest(client, request, url); + } + + partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder) + { + // do something... + } + + partial void ProcessResponse(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpResponseMessage response) + { + _processResponse(client, response); + } + } +} \ No newline at end of file diff --git a/Runtime/Scripts/OpenAPI/WorldAnalysisClient.cs.meta b/Runtime/Scripts/OpenAPI/WorldAnalysisClient.cs.meta new file mode 100644 index 0000000..077447f --- /dev/null +++ b/Runtime/Scripts/OpenAPI/WorldAnalysisClient.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 748d04737c53fc04697ac6888226e399 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/REST.meta b/Runtime/Scripts/REST.meta new file mode 100644 index 0000000..29c8716 --- /dev/null +++ b/Runtime/Scripts/REST.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6f0fd52fd30564a4c838a545fe0d00ec +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/REST/AdminRequest.cs b/Runtime/Scripts/REST/AdminRequest.cs new file mode 100644 index 0000000..9910526 --- /dev/null +++ b/Runtime/Scripts/REST/AdminRequest.cs @@ -0,0 +1,101 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: March 2024 +// + +using System; +using System.Threading.Tasks; +using UnityEngine; + +using ETSI.ARF.OpenAPI; +using ETSI.ARF.OpenAPI.WorldAnalysis; + +namespace ETSI.ARF.WorldAnalysis.REST +{ + public class AdminRequest : RequestBase + { + // + // Wrapper for the endpoints + // + static public string PingSync(WorldAnalysisServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldAnalysisClient(httpClient); + + string response = apiClient.GetPing(); + return response; + } + + static public ResponseObject PingAsync(WorldAnalysisServer ws, Action> func) + { + wsServer = ws; + var httpClient = new UnityWebRequestHttpClient(ws.URI); + apiClient = new WorldAnalysisClient(httpClient); + + Debug.Log("[REST] Request Ping..."); + ResponseObject ro = new ResponseObject("Request Ping", func); + apiClient.GetPingAsync(ro.cancellationToken).ContinueWith(OnReceiveObject, ro); + return ro; + } + + static public string AdminSync(WorldAnalysisServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldAnalysisClient(httpClient); + + string response = apiClient.GetAdmin(); + return response; + } + + static public ResponseObject AdminAsync(WorldAnalysisServer ws, Action> func) + { + wsServer = ws; + var httpClient = new UnityWebRequestHttpClient(ws.URI); + apiClient = new WorldAnalysisClient(httpClient); + + Debug.Log("[REST] Request Admin..."); + ResponseObject ro = new ResponseObject("Request Admin", func); + apiClient.GetAdminAsync(ro.cancellationToken).ContinueWith(OnReceiveObject, ro); + return ro; + } + + static public string VersionSync(WorldAnalysisServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldAnalysisClient(httpClient); + + string response = apiClient.GetVersion(); + return response; + } + + static public ResponseObject VersionAsync(WorldAnalysisServer ws, Action> func) + { + wsServer = ws; + var httpClient = new UnityWebRequestHttpClient(ws.URI); + apiClient = new WorldAnalysisClient(httpClient); + + Debug.Log("[REST] Request Version..."); + ResponseObject ro = new ResponseObject("Request Version", func); + apiClient.GetVersionAsync(ro.cancellationToken).ContinueWith(OnReceiveObject, ro); + return ro; + } + } +} diff --git a/Runtime/Scripts/REST/AdminRequest.cs.meta b/Runtime/Scripts/REST/AdminRequest.cs.meta new file mode 100644 index 0000000..29a5fd8 --- /dev/null +++ b/Runtime/Scripts/REST/AdminRequest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f287a535887d14c46859e33386b90b0e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/REST/RequestBase.cs b/Runtime/Scripts/REST/RequestBase.cs new file mode 100644 index 0000000..6650e8f --- /dev/null +++ b/Runtime/Scripts/REST/RequestBase.cs @@ -0,0 +1,79 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: March 2024 +// + +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using UnityEngine; + +using ETSI.ARF.OpenAPI; +using ETSI.ARF.OpenAPI.WorldAnalysis; + +namespace ETSI.ARF.WorldAnalysis.REST +{ + public class RequestBase // where T : Trackable, WorldAnchor, WorldLink + { + static protected WorldAnalysisServer wsServer; + static protected WorldAnalysisClient apiClient; + + static protected string token = "ARF_Permission"; + + // Cache the current list + static public Dictionary listOfObjects = new Dictionary(); + + + // + // Helpers + // + static protected void OnReceiveObject(Task t, object id) + { + if (t.IsCompleted) + { + ResponseObject o = (ResponseObject)id; + o.responseTime = DateTime.Now; + o.result = t.Result; + Debug.Log($"[REST] Server Response = {o.result.ToString()} (ID={o.transactionId}, Msg={o.message})"); + + o.callback?.Invoke(o); + } + else Debug.Log("[REST] OpenAPI Timeout!"); + } + + static protected void OnReceiveListOfObjects(Task> t, object id) where TObj : IModel + { + if (t.IsCompleted) + { + ResponseObject> o = (ResponseObject>)id; + o.responseTime = DateTime.Now; + o.result = t.Result; + Debug.Log($"[REST] Server Response = Got {o.result.Count} entrie(s) (ID={o.transactionId}, Msg={o.message})"); + + listOfObjects.Clear(); + foreach (var i in o.result) + { + listOfObjects.Add(i.Uuid, i); + } + o.callback?.Invoke(o); + } + else Debug.Log("[REST] OpenAPI Timeout!"); + } + + } +} diff --git a/Runtime/Scripts/REST/RequestBase.cs.meta b/Runtime/Scripts/REST/RequestBase.cs.meta new file mode 100644 index 0000000..2bb32b0 --- /dev/null +++ b/Runtime/Scripts/REST/RequestBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c6453e5289606a848ae29ddffa0f7288 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/WorldAnalysisInfo.cs b/Runtime/Scripts/WorldAnalysisInfo.cs new file mode 100644 index 0000000..b461d5e --- /dev/null +++ b/Runtime/Scripts/WorldAnalysisInfo.cs @@ -0,0 +1,45 @@ +// +// ARF - Augmented Reality Framework (ETSI ISG ARF) +// +// Copyright 2024 ETSI +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Last change: August 2024 +// + +using ETSI.ARF.WorldAnalysis; +using UnityEngine; + +public class WorldAnalysisInfo : MonoBehaviour +{ + public WorldAnalysisServer worldAnalysisServer; + + //public bool isServerAlive() + //{ + // if (worldStorageServer == null) return false; + // return !string.IsNullOrEmpty(ETSI.ARF.WorldStorage.REST.AdminRequest.Ping(worldStorageServer)); + //} + + //public string GetServerState() + //{ + // if (worldStorageServer == null) return "No Server Defined!"; + // return ETSI.ARF.WorldStorage.REST.AdminRequest.GetAdminInfo(worldStorageServer); + //} + + //public string GetAPIVersion() + //{ + // if (worldStorageServer == null) return "Unknown Version!"; + // return ETSI.ARF.WorldStorage.REST.AdminRequest.GetVersion(worldStorageServer); + //} +} diff --git a/Runtime/Scripts/WorldAnalysisInfo.cs.meta b/Runtime/Scripts/WorldAnalysisInfo.cs.meta new file mode 100644 index 0000000..3190dcd --- /dev/null +++ b/Runtime/Scripts/WorldAnalysisInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 269d5f98c3c478c4983c24ec09d10a70 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/WorldAnalysisREST.cs b/Runtime/Scripts/WorldAnalysisREST.cs index 5c18ceb..ca881cc 100644 --- a/Runtime/Scripts/WorldAnalysisREST.cs +++ b/Runtime/Scripts/WorldAnalysisREST.cs @@ -1,11 +1,23 @@ +using System; using UnityEngine; using ETSI.ARF.OpenAPI.WorldAnalysis; +using ETSI.ARF.WorldAnalysis; using static WorldAnalysisInterface; -using System; +using ETSI.ARF.WorldAnalysis.REST; //Implementation of the WorldAnalysis interface public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface { + static protected string token = "ARF_Permission"; + + public WorldAnalysisServer waServer; + + // For sync calls + private WorldAnalysisClient apiClient; + + // For async calls + private WorldAnalysisClient apiClientAsync; + #region Unity_Methods @@ -14,6 +26,13 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface /// protected void Awake() { + // sync + var httpClient = new BasicHTTPClient(waServer.URI); + apiClient = new WorldAnalysisClient(httpClient); + + // async + //var httpClientAsync = new UnityWebRequestHttpClient(waServer.URI); + //apiClientAsync = new WorldAnalysisClient(httpClientAsync); } /// @@ -32,10 +51,48 @@ public 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); + } - #region ARF_API + public string GetWebSocketEndpoint() + { + string res = "empty"; + + SubscriptionSingleRequest param = new SubscriptionSingleRequest(); + param.Mode = Mode_WorldAnalysis.DEVICE_TO_TRACKABLES; + param.Target = Guid.Parse("fa8bbe40-8052-11ec-a8a3-0242ac120002"); // test + + SubscriptionSingle response = apiClient.SubscribeToPose(token, "1", param); + res = response.WebsocketUrl; + return res; + } + + public void PrintCapabilities() + { + string res = "Capabilities:"; + + Response2 cap = apiClient.GetCapabilities(token, "1"); + foreach (var item in cap.Capabilities) + { + res += "\n" + item.TrackableType; + } + Debug.Log("[REST] Capabilities: " + res); + } + #endregion - public AskFrameRateResult SetPoseEstimationFramerate(string token, PoseConfigurationTrackableType type, EncodingInformationStructure encodingInformation, int minimumFramerate) + #region ARF_API + // + // Implementation of the endpoints + // + public AskFrameRateResult SetPoseEstimationFramerate(string token, PoseConfigurationTrackableType type, EncodingInformationStructure encodingInformation, int minimumFramerate) { return AskFrameRateResult.NOT_SUPPORTED; ///We cannot set any framerate for tracking on ARKit and ARCore } @@ -47,7 +104,7 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface } - public PoseEstimationResult[] GetLastPoses(string token, Guid[] uuids, Mode_WorldAnalysis [] modes, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose[] poses) + public PoseEstimationResult[] GetLastPoses(string token, Guid[] uuids, Mode_WorldAnalysis[] modes, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose[] poses) { poses = null; return null; @@ -59,7 +116,7 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface return InformationSubscriptionResult.OK; } - public InformationSubscriptionResult[] SubscribeToPoses(string token, Guid[] uuids, Mode_WorldAnalysis [] modes, PoseCallback callback, ref int validity, out Guid[] subscriptionUUIDs) + public InformationSubscriptionResult[] SubscribeToPoses(string token, Guid[] uuids, Mode_WorldAnalysis[] modes, PoseCallback callback, ref int validity, out Guid[] subscriptionUUIDs) { subscriptionUUIDs = null; return null; @@ -90,7 +147,7 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface return CapabilityResult.OK; } - public CapabilityResult GetCapability(string token, Guid uuid, out bool isSupported, out TypeWorldStorage type, out Capability [] capability) + public CapabilityResult GetCapability(string token, Guid uuid, out bool isSupported, out TypeWorldStorage type, out Capability[] capability) { isSupported = false; type = TypeWorldStorage.UNKNOWN; -- GitLab From 24e858d29cc68dd6b8a1e3536ddee165737d166b Mon Sep 17 00:00:00 2001 From: Sylvain Renault Date: Thu, 22 Aug 2024 18:41:15 +0200 Subject: [PATCH 3/6] API draft implementation and integration of a websockets client. --- Runtime/Scripts/WorldAnalysisREST.cs | 417 ++++++++++++++++++++++++--- 1 file changed, 381 insertions(+), 36 deletions(-) diff --git a/Runtime/Scripts/WorldAnalysisREST.cs b/Runtime/Scripts/WorldAnalysisREST.cs index ca881cc..4398712 100644 --- a/Runtime/Scripts/WorldAnalysisREST.cs +++ b/Runtime/Scripts/WorldAnalysisREST.cs @@ -1,23 +1,54 @@ using System; +using System.Collections; +using System.Collections.Generic; using UnityEngine; using ETSI.ARF.OpenAPI.WorldAnalysis; using ETSI.ARF.WorldAnalysis; using static WorldAnalysisInterface; using ETSI.ARF.WorldAnalysis.REST; +using WebSocketSharp; + //Implementation of the WorldAnalysis interface public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface { - static protected string token = "ARF_Permission"; - + // + // Inspector variables + // + /// + /// WorldSAnalysisServer + /// public WorldAnalysisServer waServer; + public string token = "ETSI-ARF-STF"; + public string sessionID = "RESTful-API"; - // For sync calls - private WorldAnalysisClient apiClient; + [Space(8)] + public bool isDebug = false; - // For async calls - private WorldAnalysisClient apiClientAsync; + // + // Private members + // + private WorldAnalysisClient apiClient; // For sync calls + private WorldAnalysisClient apiClientAsync; // For async calls + private WebSocketSharp.WebSocket webSocket; // For WebSockets + private bool websocketConnected = false; + // + // Management of subscriptions + // + /// + /// Dictionnary of susbscription informations for poses, for each item, stored using the UUID of the item (anchor/trackable) + /// + private Dictionary m_subscriptionsPoses; + + public struct SubscriptionInfo + { + 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; + } #region Unity_Methods @@ -26,6 +57,11 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface /// protected void Awake() { + Instance = this; + //m_relocalizationInformations = new Dictionary(); + //m_computedPoses = new Dictionary(); + m_subscriptionsPoses = new Dictionary(); + // sync var httpClient = new BasicHTTPClient(waServer.URI); apiClient = new WorldAnalysisClient(httpClient); @@ -47,6 +83,12 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface /// protected void Update() { + ManageSubscriptionValidity(); + + // todo: Call subscription callback(s) here or in the websocket?!? + foreach (KeyValuePair subPose in m_subscriptionsPoses) + { + } } #endregion @@ -62,29 +104,130 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface Debug.Log("[REST] WA Version: " + ver); } - public string GetWebSocketEndpoint() + public void PrintCapabilities(Capability[] capabilities) + { + string res = ""; + Debug.Log("[REST] Got " + capabilities.Length + " capabilities."); + foreach (var item in capabilities) + { + res += "\nCapability: " + item.TrackableType + " Version: " + item.EncodingInformation.Version; + } + res += "\nEnd of capabilities."; + Debug.Log("[REST] Capabilities: " + res); + } + #endregion + + #region Communication system + private void CreateWebHookServer() + { + throw new Exception("[REST] CreateWebHookServer(): Not implemented!"); + } + + private void DestroyWebHookServer() + { + return; + } + + public WebSocket OpenWebSocketClient(string url) { - string res = "empty"; + webSocket = new WebSocketSharp.WebSocket(url); + + // + // Define standard callbacks + // + webSocket.OnOpen += (sender, e) => + { + Debug.Log("[WS] Connected"); + websocketConnected = true; + webSocket.Send("RegisterClient:UnitySceneManagement"); + }; + webSocket.OnClose += (sender, e) => + { + Debug.Log("[WS] Disconnected"); + websocketConnected = false; + }; + webSocket.OnError += (sender, e) => Debug.Log("[WS] Error!"); + webSocket.OnMessage += (sender, e) => HandleWebSocketClient(e.Data); + webSocket.Connect(); - SubscriptionSingleRequest param = new SubscriptionSingleRequest(); - param.Mode = Mode_WorldAnalysis.DEVICE_TO_TRACKABLES; - param.Target = Guid.Parse("fa8bbe40-8052-11ec-a8a3-0242ac120002"); // test + return webSocket; + } - SubscriptionSingle response = apiClient.SubscribeToPose(token, "1", param); - res = response.WebsocketUrl; - return res; - } - - public void PrintCapabilities() + private void OnDestroy() { - string res = "Capabilities:"; + // State: red + CloseWebSocketClient(); + } - Response2 cap = apiClient.GetCapabilities(token, "1"); - foreach (var item in cap.Capabilities) + private void CloseWebSocketClient() + { + if (websocketConnected) { - res += "\n" + item.TrackableType; + webSocket.Send("UnregisterClient"); + webSocket.Close(); + } + } + + bool ok = false; + public void HandleWebSocketClient(string data) + { + Debug.Log("[WS] Receiving: " + data); + + if (data.Contains("You are now registered")) + { + ok = true; + if (isDebug) webSocket.Send("PoseStart:10"); // test + } + else if (data == "PoseStop") + { + //SetColor(Color.yellow); + } + else if (ok) + { + if (data.Contains("estimationState")) + { + // Handle pose + ETSI.ARF.OpenAPI.WorldAnalysis.Pose p = JsonUtility.FromJson(data); + Debug.Log("[WS][Pose] State: " + p.EstimationState.ToString()); + + PoseEstimationResult res = p.EstimationState == PoseEstimationState.OK ? PoseEstimationResult.OK : PoseEstimationResult.FAILURE; + + // Search the corresponding callbacks + foreach (var item in m_subscriptionsPoses.Values) + { + if (p.Uuid == item.uuidTarget) + { + item.callback(res, p); + } + } + } + } + } + + #endregion + + #region Lifecycle + /// + /// Check the validity of all subscriptions and delete one if needed + /// + protected void ManageSubscriptionValidity() + { + if (m_subscriptionsPoses.Count == 0) return; + + List subscriptionToDelete = new List(); + foreach (KeyValuePair sub in m_subscriptionsPoses) + { + float validity = sub.Value.timeValidity; + if (Time.time > validity) + { + subscriptionToDelete.Add(sub.Key); + } + } + foreach (Guid s in subscriptionToDelete) + { + Debug.Log("ETSI ARF : Subscription deleted " + s); + m_subscriptionsPoses.Remove(s); } - Debug.Log("[REST] Capabilities: " + res); } #endregion @@ -94,57 +237,245 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface // public AskFrameRateResult SetPoseEstimationFramerate(string token, PoseConfigurationTrackableType type, EncodingInformationStructure encodingInformation, int minimumFramerate) { - return AskFrameRateResult.NOT_SUPPORTED; ///We cannot set any framerate for tracking on ARKit and ARCore + PoseConfiguration poseConfig = new PoseConfiguration(); + poseConfig.TrackableType = type; + poseConfig.EncodingInformation = encodingInformation; + poseConfig.Framerate = minimumFramerate; + apiClient.ConfigureFramerate(token, sessionID, poseConfig); + return AskFrameRateResult.OK; } public PoseEstimationResult GetLastPose(string token, Guid uuid, Mode_WorldAnalysis mode, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose) { - pose = null; - return PoseEstimationResult.OK; + pose = apiClient.GetPose(token, sessionID, uuid, mode); + return pose != null ? PoseEstimationResult.OK : PoseEstimationResult.NOT_SUPPORTED; } public PoseEstimationResult[] GetLastPoses(string token, Guid[] uuids, Mode_WorldAnalysis[] modes, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose[] poses) { - poses = null; - return null; + if (uuids.Length != modes.Length) + { + Debug.LogError("[REST] ETSI ARF: Get poses: uuids and modes array do no have the same length"); + poses = null; + return null; + } + PoseEstimationResult[] resul = new PoseEstimationResult[uuids.Length]; + poses = new ETSI.ARF.OpenAPI.WorldAnalysis.Pose[uuids.Length]; + List uuidList = new List(); + Response poses_ = apiClient.GetPoses(token, sessionID, uuidList.ToArray()); + List posesList = poses_.Poses as List; + + if (poses_ != null && posesList != null && posesList.Count > 0) + { + for (int i = 0; i < uuids.Length; i++) + { + PoseEstimationResult poseResul = new PoseEstimationResult(); + resul[i] = poseResul; + poses[i] = posesList[i]; + } + return resul; + } + else + { + poses = null; + return null; + } } public InformationSubscriptionResult SubscribeToPose(string token, Guid uuid, Mode_WorldAnalysis mode, PoseCallback callback, ref int validity, out Guid subscriptionUUID) { - subscriptionUUID = Guid.Empty; + // Todo: Maintain the callback to the subscription id + // Get capabilities? + // Get reloc info? + + SubscriptionSingleRequest body = new SubscriptionSingleRequest(); + body.Target = uuid; + body.Mode = mode; + body.Validity = validity; + body.WebhookUrl = callback == null ? "" : "https:\\..."; // empty -> app will use websockets (client)! + + // Get subscription info from the REST server + SubscriptionSingle response = apiClient.SubscribeToPose(token, sessionID, body); + subscriptionUUID = response.Uuid; + + // We add the subscription + SubscriptionInfo sub = new SubscriptionInfo(); + sub.uuid = response.Uuid; + sub.timeValidity = Time.time + (validity / 1000.0f); + sub.pose = new ETSI.ARF.OpenAPI.WorldAnalysis.Pose(); + sub.pose.Mode = mode; + sub.uuidTarget = uuid; + sub.callback = callback; + m_subscriptionsPoses.Add(sub.uuid, sub); + + if (!string.IsNullOrEmpty(response.WebhookUrl)) + { + CloseWebSocketClient(); + + // todo: 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? + CreateWebHookServer(); + } + else + { + DestroyWebHookServer(); + + // todo: Open the websocket? + string websocketUrl = response.WebsocketUrl; + 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) OpenWebSocketClient(websocketUrl); + } + else throw new Exception("[REST] No valid WebSockets URL in server reponse."); + } return InformationSubscriptionResult.OK; } public InformationSubscriptionResult[] SubscribeToPoses(string token, Guid[] uuids, Mode_WorldAnalysis[] modes, PoseCallback callback, ref int validity, out Guid[] subscriptionUUIDs) { - subscriptionUUIDs = null; - return null; + if (uuids.Length != 0 && uuids.Length == modes.Length) + { + InformationSubscriptionResult[] resul = new InformationSubscriptionResult[uuids.Length]; + subscriptionUUIDs = new Guid[uuids.Length]; + for (int i = 0; i < uuids.Length; i++) + { + resul[i] = SubscribeToPose(token, uuids[i], modes[i], callback, ref validity, out subscriptionUUIDs[i]); + } + return resul; + } + else + { + Debug.LogError("[REST] ETSI ARF: Subscribe poses: uuids and modes array do no have the same length"); + subscriptionUUIDs = null; + return null; + } } public InformationSubscriptionResult GetSubsription(string token, Guid subscriptionUUID, out PoseCallback callback, out Guid target, out Mode_WorldAnalysis mode, out int validity) { + // default callback = null; target = Guid.Empty; mode = Mode_WorldAnalysis.TRACKABLES_TO_DEVICE; validity = 0; - return InformationSubscriptionResult.OK; + + if (m_subscriptionsPoses.ContainsKey(subscriptionUUID)) + { + // Check the server subscription + SubscriptionSingle sub = apiClient.GetSubscription(token, sessionID, subscriptionUUID); + + // Check local one + if (sub.Uuid == subscriptionUUID) + { + SubscriptionInfo subInfo = m_subscriptionsPoses[subscriptionUUID]; + callback = subInfo.callback; + target = subInfo.uuidTarget; + mode = subInfo.pose.Mode; + float validitySeconds = subInfo.timeValidity - Time.time; + validity = (int)validitySeconds * 1000;// conversion in ms + + // Compare both + if (target == sub.Target && mode == sub.Mode && validity == sub.Validity) + return InformationSubscriptionResult.OK; + else + return InformationSubscriptionResult.NOT_ALLOWED; + } + } + return InformationSubscriptionResult.UNKNOWN_ID; } public InformationSubscriptionResult UpdateSubscription(string token, Guid subscriptionUUID, Mode_WorldAnalysis mode, int validity, PoseCallback callback) { - return InformationSubscriptionResult.OK; + // default + callback = null; + mode = Mode_WorldAnalysis.TRACKABLES_TO_DEVICE; + validity = 0; + + if (m_subscriptionsPoses.ContainsKey(subscriptionUUID)) + { + SubscriptionInfo sub = m_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)! + + // Update subscription info in the REST server + SubscriptionSingle response = apiClient.UpdateSubscription(token, sessionID, subscriptionUUID, body); + + // Update local data + sub.pose.Mode = response.Mode; + sub.callback = callback; + sub.timeValidity = Time.time + (validity / 1000.0f); + + // + // Recreate server/connection to ws only if someone changed! + // + if (oldCB != null && callback == null && !string.IsNullOrEmpty(response.WebhookUrl)) + { + CloseWebSocketClient(); + + // todo: 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? + CreateWebHookServer(); + } + else if (oldCB == null && callback != null && string.IsNullOrEmpty(response.WebhookUrl)) + { + DestroyWebHookServer(); + + // todo: Open the websocket? + string websocketUrl = response.WebsocketUrl; + 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) OpenWebSocketClient(websocketUrl); + } + else throw new Exception("[REST] No valid WebSockets URL in server reponse."); + } + + return InformationSubscriptionResult.OK; + } + return InformationSubscriptionResult.UNKNOWN_ID; } public InformationSubscriptionResult UnSubscribeToPose(Guid subscriptionUUID) { - return InformationSubscriptionResult.OK; + if (m_subscriptionsPoses.ContainsKey(subscriptionUUID)) + { + apiClient.UnsubscribeFromPose(token, sessionID, subscriptionUUID); + m_subscriptionsPoses.Remove(subscriptionUUID); + + if (m_subscriptionsPoses.Count == 0) + { + // Close the connection via websockets + CloseWebSocketClient(); + } + return InformationSubscriptionResult.OK; + } + return InformationSubscriptionResult.UNKNOWN_ID; } public CapabilityResult GetCapabilities(string token, out Capability[] capabilities) { - capabilities = null; - return CapabilityResult.OK; + Response2 cap = apiClient.GetCapabilities(token, sessionID); + if (cap == null || cap.Capabilities == null || cap.Capabilities.Count == 0) + { + capabilities = null; + return CapabilityResult.FAIL; + } + else + { + capabilities = new Capability[cap.Capabilities.Count]; + cap.Capabilities.CopyTo(capabilities, 0); + return CapabilityResult.OK; + } } public CapabilityResult GetCapability(string token, Guid uuid, out bool isSupported, out TypeWorldStorage type, out Capability[] capability) @@ -152,7 +483,21 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface isSupported = false; type = TypeWorldStorage.UNKNOWN; capability = null; - return CapabilityResult.OK; + + Response3 cap = apiClient.GetSupport(token, sessionID, uuid); + if (cap == null || cap.Capabilities == null || cap.Capabilities.Count == 0) + { + isSupported = false; + capability = null; + return CapabilityResult.FAIL; + } + else + { + isSupported = true; + capability = new Capability[cap.Capabilities.Count]; + cap.Capabilities.CopyTo(capability, 0); + return CapabilityResult.OK; + } } #endregion -- GitLab From b1996331849fecef1b126b723a1e974d00d3af7f Mon Sep 17 00:00:00 2001 From: Sylvain Renault Date: Thu, 5 Sep 2024 14:52:00 +0200 Subject: [PATCH 4/6] Integration of websocket subscriptions --- .../OpenAPI/Generated/WorldAnalysisOpenAPI.cs | 130 +++++++++++++- Runtime/Scripts/WorldAnalysisInterface.cs | 2 +- Runtime/Scripts/WorldAnalysisREST.cs | 164 ++++++------------ 3 files changed, 178 insertions(+), 118 deletions(-) diff --git a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs index a59f807..fe98f3a 100644 --- a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs +++ b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs @@ -763,6 +763,12 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis return SubscribeToPoseAsync(token, sessionID, body, System.Threading.CancellationToken.None); } + // SylR + public virtual System.Threading.Tasks.Task SubscribeToPoseAsync(string token, string sessionID, SubscriptionMultipleRequest body) + { + return SubscribeToPoseAsync(token, sessionID, body, System.Threading.CancellationToken.None); + } + /// /// Subscribe to collect the pose of an AR device, an Anchor or a Trackable /// @@ -775,6 +781,12 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis { return System.Threading.Tasks.Task.Run(async () => await SubscribeToPoseAsync(token, sessionID, body, System.Threading.CancellationToken.None)).GetAwaiter().GetResult(); } + + // SylR + public virtual SubscriptionMultiple SubscribeToPose(string token, string sessionID, SubscriptionMultipleRequest body) + { + return System.Threading.Tasks.Task.Run(async () => await SubscribeToPoseAsync(token, sessionID, body, System.Threading.CancellationToken.None)).GetAwaiter().GetResult(); + } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// @@ -898,6 +910,120 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } } + // SylR + public virtual async System.Threading.Tasks.Task SubscribeToPoseAsync(string token, string sessionID, SubscriptionMultipleRequest body, System.Threading.CancellationToken cancellationToken) + { + if (body == null) + throw new System.ArgumentNullException("body"); + + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + + if (token != null) + request_.Headers.TryAddWithoutValidation("token", ConvertToString(token, System.Globalization.CultureInfo.InvariantCulture)); + + if (sessionID != null) + request_.Headers.TryAddWithoutValidation("sessionID", ConvertToString(sessionID, System.Globalization.CultureInfo.InvariantCulture)); + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value); + var content_ = new System.Net.Http.StringContent(json_); + content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); + request_.Content = content_; + request_.Method = new System.Net.Http.HttpMethod("POST"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + + // Operation Path: "pose/subscriptions" + urlBuilder_.Append("pose/subscriptions"); + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); + throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); + } + else + if (status_ == 403) + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); + throw new ApiException("Not allowed.", status_, responseData_, headers_, result_, null); + } + else + if (status_ == 404) + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); + throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); + } + else + if (status_ == 405) + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); + throw new ApiException("Not supported.", status_, responseData_, headers_, result_, null); + } + else + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Unexpected error.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + /// /// Get information about a subscription /// @@ -1927,8 +2053,8 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// List of modes representing the context of the Relocalization information (AR device to WorldAnchor/Trackable or WorldAnchor/Trackable to AR device) /// - [Newtonsoft.Json.JsonProperty("mode", Required = Newtonsoft.Json.Required.Always, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] - public System.Collections.Generic.ICollection Mode { get; set; } = new System.Collections.ObjectModel.Collection(); + [Newtonsoft.Json.JsonProperty("modes", Required = Newtonsoft.Json.Required.Always, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public System.Collections.Generic.ICollection Modes { get; set; } = new System.Collections.ObjectModel.Collection(); /// /// Subscription validity delay in millisecond diff --git a/Runtime/Scripts/WorldAnalysisInterface.cs b/Runtime/Scripts/WorldAnalysisInterface.cs index 1efeacd..416f917 100644 --- a/Runtime/Scripts/WorldAnalysisInterface.cs +++ b/Runtime/Scripts/WorldAnalysisInterface.cs @@ -151,7 +151,7 @@ public interface WorldAnalysisInterface /// /// id of the subscription /// /// The unsubscription has been performed or not - public InformationSubscriptionResult UnSubscribeToPose(Guid subscriptionUUID); + public InformationSubscriptionResult UnsubscribeFromPose(Guid subscriptionUUID); #endregion diff --git a/Runtime/Scripts/WorldAnalysisREST.cs b/Runtime/Scripts/WorldAnalysisREST.cs index 4398712..c4e0d6c 100644 --- a/Runtime/Scripts/WorldAnalysisREST.cs +++ b/Runtime/Scripts/WorldAnalysisREST.cs @@ -7,11 +7,14 @@ using ETSI.ARF.WorldAnalysis; using static WorldAnalysisInterface; using ETSI.ARF.WorldAnalysis.REST; -using WebSocketSharp; +using UnityEngine.Events; //Implementation of the WorldAnalysis interface -public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface +public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface { + [Serializable] + public class StringEvent : UnityEvent { } + // // Inspector variables // @@ -25,13 +28,15 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface [Space(8)] public bool isDebug = false; + //[Serializable] + //public class StringEvent : UnityEvent { } + //public StringEvent webSocketMessage; + // // Private members // private WorldAnalysisClient apiClient; // For sync calls private WorldAnalysisClient apiClientAsync; // For async calls - private WebSocketSharp.WebSocket webSocket; // For WebSockets - private bool websocketConnected = false; // // Management of subscriptions @@ -91,6 +96,11 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface } } + private void OnDestroy() + { + WebSocketClient_Close(); + } + #endregion #region Test methods @@ -117,95 +127,6 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface } #endregion - #region Communication system - private void CreateWebHookServer() - { - throw new Exception("[REST] CreateWebHookServer(): Not implemented!"); - } - - private void DestroyWebHookServer() - { - return; - } - - public WebSocket OpenWebSocketClient(string url) - { - webSocket = new WebSocketSharp.WebSocket(url); - - // - // Define standard callbacks - // - webSocket.OnOpen += (sender, e) => - { - Debug.Log("[WS] Connected"); - websocketConnected = true; - webSocket.Send("RegisterClient:UnitySceneManagement"); - }; - webSocket.OnClose += (sender, e) => - { - Debug.Log("[WS] Disconnected"); - websocketConnected = false; - }; - webSocket.OnError += (sender, e) => Debug.Log("[WS] Error!"); - webSocket.OnMessage += (sender, e) => HandleWebSocketClient(e.Data); - webSocket.Connect(); - - return webSocket; - } - - private void OnDestroy() - { - // State: red - CloseWebSocketClient(); - } - - private void CloseWebSocketClient() - { - if (websocketConnected) - { - webSocket.Send("UnregisterClient"); - webSocket.Close(); - } - } - - bool ok = false; - public void HandleWebSocketClient(string data) - { - Debug.Log("[WS] Receiving: " + data); - - if (data.Contains("You are now registered")) - { - ok = true; - if (isDebug) webSocket.Send("PoseStart:10"); // test - } - else if (data == "PoseStop") - { - //SetColor(Color.yellow); - } - else if (ok) - { - if (data.Contains("estimationState")) - { - // Handle pose - ETSI.ARF.OpenAPI.WorldAnalysis.Pose p = JsonUtility.FromJson(data); - Debug.Log("[WS][Pose] State: " + p.EstimationState.ToString()); - - PoseEstimationResult res = p.EstimationState == PoseEstimationState.OK ? PoseEstimationResult.OK : PoseEstimationResult.FAILURE; - - // Search the corresponding callbacks - foreach (var item in m_subscriptionsPoses.Values) - { - if (p.Uuid == item.uuidTarget) - { - item.callback(res, p); - } - } - } - } - } - - #endregion - #region Lifecycle /// /// Check the validity of all subscriptions and delete one if needed @@ -270,8 +191,8 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface { for (int i = 0; i < uuids.Length; i++) { - PoseEstimationResult poseResul = new PoseEstimationResult(); - resul[i] = poseResul; + PoseEstimationResult poseResult = new PoseEstimationResult(); + resul[i] = poseResult; poses[i] = posesList[i]; } return resul; @@ -288,20 +209,29 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface // 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)! + body.WebhookUrl = callback != null ? "" : "https:\\..."; // empty -> app will use websockets (client)! // Get subscription info from the REST server SubscriptionSingle response = apiClient.SubscribeToPose(token, sessionID, body); subscriptionUUID = response.Uuid; + validity = response.Validity; + + //SubscriptionMultipleRequest body_m = new SubscriptionMultipleRequest(); + //body_m.Targets = new List(); + //body_m.Mode = new List(); + //body_m.Validity = validity; + //body_m.WebhookUrl = callback == null ? "" : "https:\\..."; // empty -> app will use websockets (client)! + //SubscriptionMultiple response = apiClient.SubscribeToPose(token, sessionID, body_m); // We add the subscription SubscriptionInfo sub = new SubscriptionInfo(); - sub.uuid = response.Uuid; + sub.uuid = subscriptionUUID; sub.timeValidity = Time.time + (validity / 1000.0f); sub.pose = new ETSI.ARF.OpenAPI.WorldAnalysis.Pose(); sub.pose.Mode = mode; @@ -311,24 +241,27 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface if (!string.IsNullOrEmpty(response.WebhookUrl)) { - CloseWebSocketClient(); + // Close other communication channel + WebSocketClient_Close(); - // todo: 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? - CreateWebHookServer(); + // 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 { - DestroyWebHookServer(); + // Close other communication channel + WebHookServer_Close(); - // todo: Open the websocket? - string websocketUrl = response.WebsocketUrl; - if (isDebug) websocketUrl = "ws://localhost:61788/ws"; // for tests + // Open the websocket + string websocketUrl = "ws://" + response.WebsocketUrl + "/ws"; + //if (isDebug) websocketUrl = "ws://localhost:61788/ws"; // for tests - if (string.IsNullOrEmpty(websocketUrl)) + if (!string.IsNullOrEmpty(websocketUrl)) { // Create the WebSockets client here (NOT in the scene scripts) - if (!websocketConnected) OpenWebSocketClient(websocketUrl); + if (!websocketConnected) WebSocketClient_Create(websocketUrl); } else throw new Exception("[REST] No valid WebSockets URL in server reponse."); } @@ -418,15 +351,16 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface // if (oldCB != null && callback == null && !string.IsNullOrEmpty(response.WebhookUrl)) { - CloseWebSocketClient(); + WebSocketClient_Close(); - // todo: create a REST server so that the WA server can send pose update to it + // 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? - CreateWebHookServer(); + string webhookUrl = response.WebhookUrl; + WebHookServer_Create(webhookUrl); } else if (oldCB == null && callback != null && string.IsNullOrEmpty(response.WebhookUrl)) { - DestroyWebHookServer(); + WebHookServer_Close(); // todo: Open the websocket? string websocketUrl = response.WebsocketUrl; @@ -435,7 +369,7 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface if (string.IsNullOrEmpty(websocketUrl)) { // Create the WebSockets client here (NOT in the scene scripts) - if (!websocketConnected) OpenWebSocketClient(websocketUrl); + if (!websocketConnected) WebSocketClient_Create(websocketUrl); } else throw new Exception("[REST] No valid WebSockets URL in server reponse."); } @@ -445,7 +379,7 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface return InformationSubscriptionResult.UNKNOWN_ID; } - public InformationSubscriptionResult UnSubscribeToPose(Guid subscriptionUUID) + public InformationSubscriptionResult UnsubscribeFromPose(Guid subscriptionUUID) { if (m_subscriptionsPoses.ContainsKey(subscriptionUUID)) { @@ -455,7 +389,7 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface if (m_subscriptionsPoses.Count == 0) { // Close the connection via websockets - CloseWebSocketClient(); + WebSocketClient_Close(); } return InformationSubscriptionResult.OK; } -- GitLab From 9d13fce036d2d43fca1d5e7bec0db8cad4b3387a Mon Sep 17 00:00:00 2001 From: Sylvain Renault Date: Mon, 16 Sep 2024 13:55:12 +0200 Subject: [PATCH 5/6] Implementation of the subscription for websockets. Bug fix for the issue with the HTTP header (token). --- Runtime/Packages.meta | 8 + .../websocket-sharp-latest.1.0.2.meta | 8 + .../.signature.p7s | Bin 0 -> 11515 bytes .../websocket-sharp-latest.1.0.2/lib.meta | 8 + .../lib/netstandard2.0.meta | 8 + .../websocket-sharp-latest.dll.meta | 23 + .../websocket-sharp-latest.nuspec | 17 + .../websocket-sharp-latest.nuspec.meta | 7 + .../websocket-sharp_icon.png | Bin 0 -> 911 bytes .../websocket-sharp_icon.png.meta | 153 +++++ .../OpenAPI/Generated/WorldAnalysisOpenAPI.cs | 567 ++++++++++-------- .../OpenAPI/UnityWebRequestHttpClient.cs | 20 +- Runtime/Scripts/WorldAnalysisREST.cs | 16 +- .../Scripts/WorldAnalysisREST_WebSockets.cs | 119 ++++ .../WorldAnalysisREST_WebSockets.cs.meta | 11 + Runtime/Scripts/WorldAnalysisREST_Webhook.cs | 44 ++ .../Scripts/WorldAnalysisREST_Webhook.cs.meta | 11 + 17 files changed, 755 insertions(+), 265 deletions(-) create mode 100644 Runtime/Packages.meta create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2.meta create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2/.signature.p7s create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2/lib.meta create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2/lib/netstandard2.0.meta create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2/lib/netstandard2.0/websocket-sharp-latest.dll.meta create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp-latest.nuspec create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp-latest.nuspec.meta create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp_icon.png create mode 100644 Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp_icon.png.meta create mode 100644 Runtime/Scripts/WorldAnalysisREST_WebSockets.cs create mode 100644 Runtime/Scripts/WorldAnalysisREST_WebSockets.cs.meta create mode 100644 Runtime/Scripts/WorldAnalysisREST_Webhook.cs create mode 100644 Runtime/Scripts/WorldAnalysisREST_Webhook.cs.meta diff --git a/Runtime/Packages.meta b/Runtime/Packages.meta new file mode 100644 index 0000000..b6c7aec --- /dev/null +++ b/Runtime/Packages.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 953b8657509a139449794a24f2147730 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Packages/websocket-sharp-latest.1.0.2.meta b/Runtime/Packages/websocket-sharp-latest.1.0.2.meta new file mode 100644 index 0000000..15642e0 --- /dev/null +++ b/Runtime/Packages/websocket-sharp-latest.1.0.2.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4af05175caa96bb43844e080f1d8701b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Packages/websocket-sharp-latest.1.0.2/.signature.p7s b/Runtime/Packages/websocket-sharp-latest.1.0.2/.signature.p7s new file mode 100644 index 0000000000000000000000000000000000000000..29b82e384cd4bfbf60c118bfe30e6e30bd1d7699 GIT binary patch literal 11515 zcmeI2XH*nfw}$DaCFdj(o1CG#$p{h!2}(vla?YWf3=*0o7zmOC$w&}HKmi4mD56Ld zL`(#U0)l`bLAli`4m#eMx$CYq-=B{^&FQLB>vYxG&%5`#3&03&;t}%4oQT;Zh2TT; zFoJUcMsNxW!9|b*q5DsAX$BB@<(=bCLdlcH)(?bS9M1&MZNC-=b z0m2Aj1b`406&4c)zY;oOjdqd=&~=v5chnR2@t5)q(sk36bP;j!@wU-INE>KLc?OAl z=-DCyk4pr3OBu=$5#?dXlmHBFJ%EAc=HWsiP$&h-?D+KI=c->4rmc1vR{&}3y1uU% zatK_PJ%Sh@#KSd&;ZZ{k^%1lHHFiKmO`_uL=&Wpy@`0;+*dmSqhp}H1Q&af+`wQEF zhit(^!nU672u^?GMf_}_z8NBle}g;&DdG3z&1>1x zf@PN-wpBgrv0V5F>tyxl7kaxvsxy{b#`NM%YCZW9TdrY4RZ_=V8QU+2jy@Nm|H4at zt0eMSyN61PwRjrc7n7^!{oF@B6ljtP>=C10K3N+N+z7bY1}cE&yE1C^>(R zSXl)yhX4QrqyHYdFhY7p%i%@`Uw!3T&r}_PgFBb_UkzP^C;~tt5lC$Cf}x9)IxqxK z|6hV201ORY4ZW zD-$|D;P|w2hmYss(_oI2sU(iHM1RayeiVlTm4T>^Pc<)hBd2p)o8BsoV|?84A))-j z`E!Es)Ji6eSb{N5tMDo)?+u#|=}yj{K8j%Kd$&q7b-Gcb zcHU=3;EP*#sE@P+Y2xx!?Wv-&cP>QbQEL^Br)oPkFATuBI2H)(j_IYfu98!&Ke$BR zS}-T5yNPbP7hP==iB`nmPf4A3LBspW?a;D0xy7PopO1`jT^{%r#~4Ttc&8o#RM=Sj z8oLAl9vHh&2+p748;gBrE*J?wghwEN2TB7zE-nzb|33r*a|c`j&iyd}iJ^D$@$r%o z5%IJ|d;O#cfbx&CpfQr)&$6|~3I_V86GiqP_xA^_u7XtyWkq3Iln)RQfn5+QEC>hy zWb&l)Bx1z&FNJaw`={4&`}17Ue?C_my97Aw1fU8?;SpigJm#|-4ipCP1HAjc5elJW z!Y&LA-ilwfQAAlsAA3K)&%`~zOw*3L!Tdyp*WP*KwpeWmZP@#g8ULS{2?F3NGmkBj znsVM+V;`XH5)9$u^DAXGXA+WO=vW4N|EQxlf;byIF+eOYw z&d!HC{P}#m3^LxlP+8%m!WJ&d$*VMK`9V%jH?vC2;AW%V)g%dxrJo*9Ipy{#bStP_ zqh6V@BjF-YLe@ycb3(s}phB5O)Q_jlnJ~|9``EeGEI25?xK(vwc%Jn21_a)F1z{^k zz(9X1B1^mTG6mi)e#G|q7h4$hO&gDV{cWlg`e@6LPCOSnh<;>1e^`FE&KzjbEAhpMVvZ`yfGJA|!s~K1eY@93hSr6_*y5HU%Os z{7g`4fD+tzFam0#(_jh+63@re13~dMVqy5y_VPa~pcJLRip?<4Gq za)j&IdwHUreLPWtaGOB*NoQM>C)(4&Xa8K>e{k;Kvh{sfLrney{zIwt%ML2<@QP_# zVS#QTZmt|;YukZhgo%z3-p!ehG~x4(L5J5bN4ZFkPrDKNCj~tZJL@f*I?3=rqG@^} zE4J^$3sUR5JTIa03^k`L)NhPU?xY5bI_3;&lsJ4I95&UG@`;ID61?=7Yc1`O-Gt@M zyA^{ytyy%2zLj#H>RX7Wbkq(x%Fnqsizpu@(0)2Cp2^a%Cw)BxO^xbCQX1mwxxMwh zUUWrBuli_&A9aeZ zCVNHtWNgS{GP=+zKCC?TKXVvA{A>FCCl336W1$z|0f^^`<{@H4{$SxRJVQHKBZaKL zCE!A~Xdy=gh*P&ePx}R-|I0MZZv<2UlmLlu^$bWrTnNAi@Eiajif=#8sK`$)XXgMXJkM=wbo=XgaM%_r|#(ZW=T6#a< z)px@>a3<%MDYIeSJd=$L4vh>?R$iAOt0rjhNiVro(%cNQq@Xv>kOuf`6AP7vE1ibv z8|Pl1X(zukC10ZkO+%n4w`+P{yo_Tg-F;klCyK~Bf!p(LD$V3I*UkqNTxSRjrPDZH z*_FcSD%RiEm*1}zu-_0PJgT0EE%9=eaW#DBN4 zeYYQhVSxh}7ACNc4X63-rv16L&BH+M0`x!1NJ!Tqex@!drW*5|2O)d3^PLY0mFTf3m|8zwNf{Tu0 zU%;)soP|An9lya;#7|Vj*~890K-dNC=>bFxA6%&jBH{rM(FQ~`0})LC_Dhh0r1 z2tZi6G%~n4gHzw~`7BzfSQt!407gkf0VTU`)X;lj2Js_weT}=A6F4<>_Ty&iDKl#w ziiys=33br-SCQ3Q?rT~O0nzE3oHiXe#WS(J^WCu`;@u+Gtdk-=Y}gjIBJA13^@sSw zauAPwa2q;94?Cs4J?fQuxOtos19?rUGEq4Ky(F22e>CMa^+esR=;M7a#68!Vo+=ph zH*(u-?r6Xm*E6X@+Ea>i80<^^nlEv}Hk&_Ol?`!OO8fjoH#L7XQDI&vLQ@IZU4s-> zc$bT=y}sP3SXS|l5(vCOq>EShKvsAB%=xDT@`paK`aQf~!SFI|bG((LBv_X*h`2K! z!wBwwMy7+$n+$5UTfZkF`@nPvEHC0B41DYnvEU~D0a7qH z5Lf{H*gN3;hqo1&X^yp6oWDVq`9(m?mxFtdE0OgvL#AlCA+KZyojjBhr=)9O$Y72a z{h?U3U&t^OMo*3e4n!5w6bi?G{L=3zMO^|3jw4wB-`yxb_LnP+H;%in=I?cm9dX^w z!_c+;*D2b!xDHgB`-auuRha*EivH^q{YRami(u8``@QNR%c8%u)h{%={8DFY{o8T*vGcNbb z?5PZ9j3Vs-Sl1Yfr05omI@8^m%WDExA5#uaSw1@?t4FTK+L=RpE%bhF*=afxL)1;D z%U*3Y4Zc@g(8!bl3Ka_BG0Yl%)Qrzjckf9d62|xN;YV<0TRx%QMT=8imp`#tXNfHw z>aD`$q5lapm~8knM=dP$z^nDXlVR=#hQPWdJ zN~$x{6gmrt2ai#7?1W<>TE6nOi)@x1a9vl=>hhTL+HKE{TV}&|^I0CWBotJK5^-Po zIOQyo!7TJ~w5*1CnsMJ5lL4zYiNA8S7f0S=2EEPgw8n%TJu?)@z>`O)G{N3*{(5zEy1c?aMOD(VOh2Q;@zqyKf z!`Y;V+jXj+|IN0LQQy}3W(5$GdA`PCsBb`7B}p2ZV zo~u=S=5^q=ICBulSnK}ZAR*RvLIR>-V)%o`lHd@zPh;DkX?z%<1)nr%Cz0*5RS?_y z6Gj{bq_GEZsge5!en3}E46diIC$J*U%->Cl^9CyZuNC7kfT-Lo>%oGa?5msGGB#O$+GC&n$z~#wX1Q&VcD!X zf&PmNjoQ&h~zkrgB^+yJqV)Rs4XtM}pLBc!sgG)y_wu zqy_b$;S_6it?|jgFCz))%N2sslh;l>Td6`&m!0NI;}QNCTbyvpkJgaeD}y^Mk3kKt z{8rgEJlNNRhbu8dMvC&*#zxM}(KCw?Z`f%@G5imcoAchhJyw9;o@F(4OIg)Rr4%ss zQQ$2{EBB6RXk=d_4`VddMjI|a2R*Id8I)1vC=F06Ja!BeS=!%%UNEj0b8bT2=;Kyvglkz%~gFTtqP3^A-p(bQ3mgO=gqq;0IGYcIkivy?hcZs2k7IKX z$0omRRQ%zeHPDT^pNtkk^Z8|p_+D^(;O1K{5f z4L0e4eYXv|?@54u9av@_XtsWB9Ds??S(8gGm+L$;%2e1Pp$^ZUvnfqf(F;?Yr0xUs zdz$PvsLkFS{i ze4)vXJJv1vUUo>o7g5M^X_K#ZgXv}&MUDCV9%%5c25pZ$v`E2u(_?G_%4O63(E z#&S*;?$-J@{0kd7)qz(c3XNjQiH<)S>$sRVo3XP~oQ=BK@vybk#e1}qI-IbhF1IH( zma|d2Q2nmIL(&J-)p<>*UWsJrW&Mhy%mnJ>DLv9bulKlYukZ-U$-9<(aA;TT(XLa- zVq;V2uczkhdBq$cGnI2=TFg6ngxhsW3SC|d(Y_~tG~kBTjh&9v;3XnUQR!I4fFP;) zs}rv|J~Q(^eYiFEA;WS<;Cbk(YPPQ+3y&c8OHbNhmLumS?pz+4Sv`Ek^*Yo|w>VB45C z$T3*l3QmuC+g3kE7!L82AC!A^#okY;m@k%4O<*RBp(vVsHzRGs&_IASe7j|pOkn!i zPML@bt;Q@h8S_g?DTaK3jiqNHyP#sVfQnW6U9m_`I-@jGwq^M>^uIOhnWMPqTZ%Q^Kv0l$6^p~SeBM@=5EBcHV zNjH*|EX9O?rtP4r(`5{AiY{8m&lmH)Otdw-7pFfiBXl8Fsgk>BsxctoZV=ssQD;ko zoqi)f-K@@u#&gaj=Q=;!9~U`$wED6BMTJO~*>03NFN;<8Kx4+|ly)@Rnn;^4d?cdc=?DCwiF>2wnjVN0zVkDRMfNEz#?#Zne2K;N z)^^*Ya5s8gtA^qTazZZs56wsC%kjxv@TF~zJbD%#Mm>5lu+Qef;zaP>CyvzknXLg! zo#(o$JdlA8iA^FecE0~87!rJ`+b-@yb;q4qeG5y&!LvvBOmI(ao4-4bW{L_O>75*s z@!waa^Pnol{;5Uta|3}63tukmi!ld#=LG)%$_HPO`K`A7A$>OSZ~Cow6A&~@>BK#9 z{3$eJdRGC{Z`z>`=mOe#C-XF8)c>h=sEK_~+rDLO2a~q1O%gXCB5_~aKt+s*1R}!! zp(Yyd&jU44Ykxg|)vft1XMeXOpWkVi;dOq7HPy9%(~`ih?_2F>(HtLkw^F6ExMns{ z@fq#@Lw38gI}&Z`x`F}cYz+xjrEzwhhaP9~?G(zlJi^JMX0FPJdlj(S6B6B{pMBG2 z(%PYrhW~i1Muw7r&MsqZ4go;8JtZ_k7#FxjICHM|EaM%Qf~;13E5f2DzmPMTGxd}; z24Z&=r)}QU$Cp%YBJf-@6D0l75svT?2~*27x@udJPF01Yz2@RWCdZUB=_}hGb+_+i zQCqsyJ{(^=)NV4yVCzILa_ikmrc?hCk#20)y)Zy4WbtR0sV36^R00sQisk9x(9pA_f44 zA58+V7e@M1AK#0Dy1M!D!ZE*-yrZVv@znQkw21q|JX}3PJy= z^R+XDcrd70kZoR)T;=)fE#K)3%-YH=_6L7VDL<2}3yt)X27vKiKw>M$%hwBsf zk-Ty6@=0!)`wShs+A{%6LU#RH;l8KyIBM&*4~E{?9uX8r@Ooi;0BmV?@B$Y@E0jQz zF5F!s3AitX@SSuyj&Xhe-yCpFyOLr{zGVOacAz%p|T6wdS_4ud6@{U`M z$$&<#(L>VViXL-cn7$K}2~Va%a8uHoiXHOmo`LG^W49XW)FYb&35Yjc5G{S(dkPTD z$T53`x0NdSc%_x5RWw&q{PJ2q!o#iawg?H$OnTWH8yF0HHsOvIkSrqh9Ns!P*TLp# zH7c<#;bbD5t%MWHI14tc;u_^M6xXeSmR=^mrbiHo6sRV4sf@XdGmf8QJgK-otloFW z)MA0?_1ij?4_(1EIUQGWQSww2{qUehH8oz{JxxAh`r=EDfd#{T^v{aZADeDF zycUj*r=WA^ob6`DZ<90(?=#!g0_LFN2BXXATCXO8Lf?@k6~Cn_=QPU50cP?nG~XK& z^EEoDQ?x|(>GK)xZRf1?JSx4j*hyyTsCpsaFCyQ>Ep+L*hsf?FoV9*L&6CJ8x>;#s zv}AUB + + + websocket-sharp-latest + 1.0.2 + websocket-sharp-latest + MIT + https://licenses.nuget.org/MIT + websocket-sharp_icon.png + Package Description + https://github.com/garbles-labs/websocket-sharp/releases + + + + + + \ No newline at end of file diff --git a/Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp-latest.nuspec.meta b/Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp-latest.nuspec.meta new file mode 100644 index 0000000..667b2c7 --- /dev/null +++ b/Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp-latest.nuspec.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: a9d61c123a66f5b4a8d236c2972a3609 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp_icon.png b/Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a07c30eb38d729691b987ff3ae8597fe3256b999 GIT binary patch literal 911 zcmeAS@N?(olHy`uVBq!ia0y~yU~~Xs4mP03*$GQm0V&P`kH}&M25w;xW@MN(M}mQY z+0oO*F{I+w+iMp&4;u)$IBNIizKx#wNtR=V1W%VuaHHjXxpEnxX2QVVq^E&~#RP^4KZ*}?Of!GWy91Zu6kGcvP2yntOZmCkyYI}IbR#vUfq{ub0Zt$> z5rSl}fLi*EdlD)y- literal 0 HcmV?d00001 diff --git a/Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp_icon.png.meta b/Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp_icon.png.meta new file mode 100644 index 0000000..76b216b --- /dev/null +++ b/Runtime/Packages/websocket-sharp-latest.1.0.2/websocket-sharp_icon.png.meta @@ -0,0 +1,153 @@ +fileFormatVersion: 2 +guid: b3235afb951bf0a4f9e21db48a0937f6 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: iPhone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: WebGL + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs index fe98f3a..126f5ab 100644 --- a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs +++ b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs @@ -1,6 +1,6 @@ //---------------------- // -// Generated using the NSwag toolchain v14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) +// Generated using the NSwag toolchain v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) // //---------------------- @@ -8,6 +8,7 @@ #pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword." #pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?' #pragma warning disable 612 // Disable "CS0612 '...' is obsolete" +#pragma warning disable 649 // Disable "CS0649 Field is never assigned to, and will always have its default value null" #pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ... #pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..." #pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'" @@ -15,23 +16,25 @@ #pragma warning disable 8603 // Disable "CS8603 Possible null reference return" #pragma warning disable 8604 // Disable "CS8604 Possible null reference argument for parameter" #pragma warning disable 8625 // Disable "CS8625 Cannot convert null literal to non-nullable reference type" -#pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes). +#pragma warning disable 8765 // Disable "CS8765 Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes)." namespace ETSI.ARF.OpenAPI.WorldAnalysis { using System = global::System; - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class WorldAnalysisClient { private ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient _httpClient; private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); + private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. public WorldAnalysisClient(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient httpClient) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { _httpClient = httpClient; + Initialize(); } private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() @@ -41,10 +44,12 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis return settings; } - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } } + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + partial void Initialize(); + partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url); partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); partial void ProcessResponse(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpResponseMessage response); @@ -374,7 +379,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis if (sessionID != null) request_.Headers.TryAddWithoutValidation("sessionID", ConvertToString(sessionID, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value); + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, JsonSerializerSettings); var content_ = new System.Net.Http.StringContent(json_); content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); request_.Content = content_; @@ -415,9 +420,22 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis else if (status_ == 405) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not supported.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not supported.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 511) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else { @@ -551,30 +569,52 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis else if (status_ == 400) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad request.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 403) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not allowed.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not allowed.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not found.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 405) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not supported.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not supported.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 511) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else { @@ -609,7 +649,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// List of UUIDs of the Trackables and Anchors to request with the mode representing the context of the Relocalization information (AR device to WorldAnchor/Trackable or WorldAnchor/Trackable to AR device) /// Successful operation. /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetPosesAsync(string token, string sessionID, System.Collections.Generic.IEnumerable uuid) + public virtual System.Threading.Tasks.Task GetPosesAsync(string token, string sessionID, System.Collections.Generic.IEnumerable uuid) { return GetPosesAsync(token, sessionID, uuid, System.Threading.CancellationToken.None); } @@ -623,7 +663,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// List of UUIDs of the Trackables and Anchors to request with the mode representing the context of the Relocalization information (AR device to WorldAnchor/Trackable or WorldAnchor/Trackable to AR device) /// Successful operation. /// A server side error occurred. - public virtual Response GetPoses(string token, string sessionID, System.Collections.Generic.IEnumerable uuid) + public virtual Poses GetPoses(string token, string sessionID, System.Collections.Generic.IEnumerable uuid) { return System.Threading.Tasks.Task.Run(async () => await GetPosesAsync(token, sessionID, uuid, System.Threading.CancellationToken.None)).GetAwaiter().GetResult(); } @@ -638,7 +678,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// List of UUIDs of the Trackables and Anchors to request with the mode representing the context of the Relocalization information (AR device to WorldAnchor/Trackable or WorldAnchor/Trackable to AR device) /// Successful operation. /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetPosesAsync(string token, string sessionID, System.Collections.Generic.IEnumerable uuid, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetPosesAsync(string token, string sessionID, System.Collections.Generic.IEnumerable uuid, System.Threading.CancellationToken cancellationToken) { if (uuid == null) throw new System.ArgumentNullException("uuid"); @@ -691,7 +731,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -701,30 +741,52 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis else if (status_ == 400) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad request.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 403) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not allowed.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not allowed.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not found.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 405) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not supported.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not supported.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 511) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else { @@ -763,12 +825,6 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis return SubscribeToPoseAsync(token, sessionID, body, System.Threading.CancellationToken.None); } - // SylR - public virtual System.Threading.Tasks.Task SubscribeToPoseAsync(string token, string sessionID, SubscriptionMultipleRequest body) - { - return SubscribeToPoseAsync(token, sessionID, body, System.Threading.CancellationToken.None); - } - /// /// Subscribe to collect the pose of an AR device, an Anchor or a Trackable /// @@ -781,12 +837,6 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis { return System.Threading.Tasks.Task.Run(async () => await SubscribeToPoseAsync(token, sessionID, body, System.Threading.CancellationToken.None)).GetAwaiter().GetResult(); } - - // SylR - public virtual SubscriptionMultiple SubscribeToPose(string token, string sessionID, SubscriptionMultipleRequest body) - { - return System.Threading.Tasks.Task.Run(async () => await SubscribeToPoseAsync(token, sessionID, body, System.Threading.CancellationToken.None)).GetAwaiter().GetResult(); - } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// @@ -814,7 +864,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis if (sessionID != null) request_.Headers.TryAddWithoutValidation("sessionID", ConvertToString(sessionID, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value); + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, JsonSerializerSettings); var content_ = new System.Net.Http.StringContent(json_); content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); request_.Content = content_; @@ -861,144 +911,52 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis else if (status_ == 400) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad request.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 403) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not allowed.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not allowed.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not found.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 405) - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not supported.", status_, responseData_, headers_, result_, null); - } - else { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - throw new ApiException("Unexpected error.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + throw new ApiException("Not supported.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally - { - if (disposeClient_) - client_.Dispose(); - } - } - - // SylR - public virtual async System.Threading.Tasks.Task SubscribeToPoseAsync(string token, string sessionID, SubscriptionMultipleRequest body, System.Threading.CancellationToken cancellationToken) - { - if (body == null) - throw new System.ArgumentNullException("body"); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - - if (token != null) - request_.Headers.TryAddWithoutValidation("token", ConvertToString(token, System.Globalization.CultureInfo.InvariantCulture)); - - if (sessionID != null) - request_.Headers.TryAddWithoutValidation("sessionID", ConvertToString(sessionID, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value); - var content_ = new System.Net.Http.StringContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; - request_.Method = new System.Net.Http.HttpMethod("POST"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - var urlBuilder_ = new System.Text.StringBuilder(); - - // Operation Path: "pose/subscriptions" - urlBuilder_.Append("pose/subscriptions"); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = new System.Collections.Generic.Dictionary>(); - foreach (var item_ in response_.Headers) - headers_[item_.Key] = item_.Value; - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) + else + if (status_ == 511) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } - return objectResponse_.Object; - } - else - if (status_ == 400) - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); - } - else - if (status_ == 403) - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not allowed.", status_, responseData_, headers_, result_, null); - } - else - if (status_ == 404) - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); - } - else - if (status_ == 405) - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not supported.", status_, responseData_, headers_, result_, null); + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else { @@ -1123,16 +1081,32 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis else if (status_ == 400) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad request.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not found.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 511) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else { @@ -1212,7 +1186,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis if (sessionID != null) request_.Headers.TryAddWithoutValidation("sessionID", ConvertToString(sessionID, System.Globalization.CultureInfo.InvariantCulture)); - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, _settings.Value); + var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(body, JsonSerializerSettings); var content_ = new System.Net.Http.StringContent(json_); content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); request_.Content = content_; @@ -1260,16 +1234,32 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis else if (status_ == 400) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad request.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not found.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 511) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else { @@ -1298,9 +1288,9 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// End the subscription and the associated callback for collecting a given pose /// /// Subscription UUID to delete. - /// OK, unsubcription successful. + /// Success request. /// A server side error occurred. - public virtual System.Threading.Tasks.Task UnsubscribeFromPoseAsync(string token, string sessionID, System.Guid subscriptionUUID) + public virtual System.Threading.Tasks.Task UnsubscribeFromPoseAsync(string token, string sessionID, System.Guid subscriptionUUID) { return UnsubscribeFromPoseAsync(token, sessionID, subscriptionUUID, System.Threading.CancellationToken.None); } @@ -1312,9 +1302,9 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// End the subscription and the associated callback for collecting a given pose /// /// Subscription UUID to delete. - /// OK, unsubcription successful. + /// Success request. /// A server side error occurred. - public virtual string UnsubscribeFromPose(string token, string sessionID, System.Guid subscriptionUUID) + public virtual Success UnsubscribeFromPose(string token, string sessionID, System.Guid subscriptionUUID) { return System.Threading.Tasks.Task.Run(async () => await UnsubscribeFromPoseAsync(token, sessionID, subscriptionUUID, System.Threading.CancellationToken.None)).GetAwaiter().GetResult(); } @@ -1327,9 +1317,9 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// End the subscription and the associated callback for collecting a given pose /// /// Subscription UUID to delete. - /// OK, unsubcription successful. + /// Success request. /// A server side error occurred. - public virtual async System.Threading.Tasks.Task UnsubscribeFromPoseAsync(string token, string sessionID, System.Guid subscriptionUUID, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task UnsubscribeFromPoseAsync(string token, string sessionID, System.Guid subscriptionUUID, System.Threading.CancellationToken cancellationToken) { if (subscriptionUUID == null) throw new System.ArgumentNullException("subscriptionUUID"); @@ -1347,7 +1337,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis if (sessionID != null) request_.Headers.TryAddWithoutValidation("sessionID", ConvertToString(sessionID, System.Globalization.CultureInfo.InvariantCulture)); request_.Method = new System.Net.Http.HttpMethod("DELETE"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("text/plain")); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); var urlBuilder_ = new System.Text.StringBuilder(); @@ -1380,23 +1370,42 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis var status_ = (int)response_.StatusCode; if (status_ == 200) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - return result_; + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; } else if (status_ == 400) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad request.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not found.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 511) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else { @@ -1426,7 +1435,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Successful operation. /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetCapabilitiesAsync(string token, string sessionID) + public virtual System.Threading.Tasks.Task GetCapabilitiesAsync(string token, string sessionID) { return GetCapabilitiesAsync(token, sessionID, System.Threading.CancellationToken.None); } @@ -1439,7 +1448,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Successful operation. /// A server side error occurred. - public virtual Response2 GetCapabilities(string token, string sessionID) + public virtual Capabilities GetCapabilities(string token, string sessionID) { return System.Threading.Tasks.Task.Run(async () => await GetCapabilitiesAsync(token, sessionID, System.Threading.CancellationToken.None)).GetAwaiter().GetResult(); } @@ -1453,7 +1462,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Successful operation. /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetCapabilitiesAsync(string token, string sessionID, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetCapabilitiesAsync(string token, string sessionID, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -1500,7 +1509,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1508,6 +1517,16 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis return objectResponse_.Object; } else + if (status_ == 511) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else { var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) @@ -1540,7 +1559,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// UUID of the Trackable or Anchor to check support /// Successful operation. /// A server side error occurred. - public virtual System.Threading.Tasks.Task GetSupportAsync(string token, string sessionID, System.Guid trackableOrAnchorUUID) + public virtual System.Threading.Tasks.Task GetSupportAsync(string token, string sessionID, System.Guid trackableOrAnchorUUID) { return GetSupportAsync(token, sessionID, trackableOrAnchorUUID, System.Threading.CancellationToken.None); } @@ -1554,7 +1573,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// UUID of the Trackable or Anchor to check support /// Successful operation. /// A server side error occurred. - public virtual Response3 GetSupport(string token, string sessionID, System.Guid trackableOrAnchorUUID) + public virtual Supports GetSupport(string token, string sessionID, System.Guid trackableOrAnchorUUID) { return System.Threading.Tasks.Task.Run(async () => await GetSupportAsync(token, sessionID, trackableOrAnchorUUID, System.Threading.CancellationToken.None)).GetAwaiter().GetResult(); } @@ -1569,7 +1588,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// UUID of the Trackable or Anchor to check support /// Successful operation. /// A server side error occurred. - public virtual async System.Threading.Tasks.Task GetSupportAsync(string token, string sessionID, System.Guid trackableOrAnchorUUID, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task GetSupportAsync(string token, string sessionID, System.Guid trackableOrAnchorUUID, System.Threading.CancellationToken cancellationToken) { if (trackableOrAnchorUUID == null) throw new System.ArgumentNullException("trackableOrAnchorUUID"); @@ -1620,7 +1639,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis var status_ = (int)response_.StatusCode; if (status_ == 200) { - var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); @@ -1630,30 +1649,52 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis else if (status_ == 400) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Bad request.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Bad request.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 403) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not allowed.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not allowed.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 404) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not found.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not found.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else if (status_ == 405) { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - var result_ = (string)System.Convert.ChangeType(responseData_, typeof(string)); - throw new ApiException("Not supported.", status_, responseData_, headers_, result_, null); + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("Not supported.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); + } + else + if (status_ == 511) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + throw new ApiException("The secret token is not valid. Please ask an ISG ARF team member for a valid token.", status_, objectResponse_.Text, headers_, objectResponse_.Object, null); } else { @@ -1794,7 +1835,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// An element representing the result of the pose estimation of an AR device, a Trackable or a WorldAnchor by the World Analysis /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Pose { /// @@ -1861,7 +1902,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Base type to define the pose value /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class PoseValue { /// @@ -1885,7 +1926,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// A pose value that is described with a 4*4 matrix /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class MatrixPoseValue : PoseValue { /// @@ -1906,7 +1947,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// A pose value that is described with a position and a rotation /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class VectorQuaternionPoseValue : PoseValue { /// @@ -1933,7 +1974,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// A pose value in a geodetic coordinate system /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class GeodeticPoseValue : PoseValue { [Newtonsoft.Json.JsonProperty("altitude", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -1969,7 +2010,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// An object representing the framerate that the World Analysis needs to reach for a given Trackable Type /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class PoseConfiguration { [Newtonsoft.Json.JsonProperty("trackableType", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -1999,7 +2040,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Request when subscribing for pose update of a Trackable or a WorldAnchor /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class SubscriptionSingleRequest { /// @@ -2041,14 +2082,14 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Request when subscribing for pose update of multiple Trackable or a WorldAnchor /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class SubscriptionMultipleRequest { /// /// List of UUID of the Trackable or Anchor to subscribe /// [Newtonsoft.Json.JsonProperty("targets", Required = Newtonsoft.Json.Required.Always)] - public System.Collections.Generic.ICollection Targets { get; set; } = new System.Collections.ObjectModel.Collection(); + public System.Collections.Generic.ICollection Targets { get; set; } = new System.Collections.ObjectModel.Collection(); /// /// List of modes representing the context of the Relocalization information (AR device to WorldAnchor/Trackable or WorldAnchor/Trackable to AR device) @@ -2082,7 +2123,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Response when subscribing for pose update of a Trackable or a WorldAnchor /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class SubscriptionSingle { /// @@ -2133,7 +2174,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Response when subscribing for pose update of multiple Trackable or a WorldAnchor /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class SubscriptionMultiple { [Newtonsoft.Json.JsonProperty("subscriptions", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -2153,7 +2194,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// An object representing a supported capability of the World Analysis and its associated metadata /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Capability { [Newtonsoft.Json.JsonProperty("trackableType", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -2195,7 +2236,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// An object holding the info of a Trackable`'`s encoding information `:` the data format and the version. /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class EncodingInformationStructure { /// @@ -2222,13 +2263,10 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Error { - [Newtonsoft.Json.JsonProperty("code", Required = Newtonsoft.Json.Required.Always)] - public int Code { get; set; } - - [Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Always)] + [Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public string Message { get; set; } private System.Collections.Generic.IDictionary _additionalProperties; @@ -2245,7 +2283,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Mode representing the context of the relocalization information (AR device to WorldAnchor/Trackable or WorldAnchor/Trackable to AR device) /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum Mode_WorldAnalysis { @@ -2260,16 +2298,33 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// A quaternion /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Quaternion : System.Collections.ObjectModel.Collection { } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Success + { + [Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Message { get; set; } + + private System.Collections.Generic.IDictionary _additionalProperties; + + [Newtonsoft.Json.JsonExtensionData] + public System.Collections.Generic.IDictionary AdditionalProperties + { + get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary()); } + set { _additionalProperties = value; } + } + + } + /// /// Type for trackable /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum TrackableType { @@ -2296,7 +2351,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Coordinate reference system of the world anchor, a 4*4 matrix (rowmajor) represented by a float vector. /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Transform3D : System.Collections.ObjectModel.Collection { @@ -2305,7 +2360,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Trackable or Anchor /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum TypeWorldStorage { @@ -2323,7 +2378,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// Unit of length. /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum UnitSystem { @@ -2365,14 +2420,14 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// A 3 coordinates vector /// - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Vector3 : System.Collections.ObjectModel.Collection { } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class Anonymous + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class UuidAndMode { [Newtonsoft.Json.JsonProperty("uuid", Required = Newtonsoft.Json.Required.Always)] public System.Guid Uuid { get; set; } @@ -2395,7 +2450,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Body { /// @@ -2428,11 +2483,11 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class Response + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Poses { [Newtonsoft.Json.JsonProperty("poses", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.ICollection Poses { get; set; } + public System.Collections.Generic.ICollection Poses1 { get; set; } private System.Collections.Generic.IDictionary _additionalProperties; @@ -2445,11 +2500,11 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class Response2 + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Capabilities { [Newtonsoft.Json.JsonProperty("capabilities", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public System.Collections.Generic.ICollection Capabilities { get; set; } + public System.Collections.Generic.ICollection Capabilities1 { get; set; } private System.Collections.Generic.IDictionary _additionalProperties; @@ -2462,8 +2517,8 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class Response3 + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class Supports { [Newtonsoft.Json.JsonProperty("type", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] @@ -2483,7 +2538,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum PoseEstimationState { @@ -2495,7 +2550,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum PoseValueType { @@ -2510,7 +2565,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum GeodeticPoseValueRotationTarget { @@ -2522,7 +2577,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum PoseConfigurationTrackableType { @@ -2546,7 +2601,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public enum EncodingInformationStructureDataFormat { @@ -2572,7 +2627,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ApiException : System.Exception { public int StatusCode { get; private set; } @@ -2595,7 +2650,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis } } - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ApiException : ApiException { public TResult Result { get; private set; } diff --git a/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs b/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs index 8d696aa..c841a48 100644 --- a/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs +++ b/Runtime/Scripts/OpenAPI/UnityWebRequestHttpClient.cs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -// Last change: June 2024 +// Last change: September 2024 // // Depends on UniTask to support cancellation token and GetAwaiter: https://github.com/Cysharp/UniTask @@ -101,6 +101,21 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis private readonly HttpClient _httpClient = new HttpClient(); + private void AppendARFHeaders(HttpRequestMessage message, UnityWebRequest webRequest) + { + // Add some ARF headers + foreach (var item in message.Headers) + { + try + { + List li = item.Value as List; + if (item.Key == "token") webRequest.SetRequestHeader(item.Key, li[0].ToString()); // add it + if (item.Key == "sessionID") webRequest.SetRequestHeader(item.Key, li[0].ToString()); // add it + } + catch { } // ignore it + } + } + public async Task SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token) { var content = await (message.Content?.ReadAsStringAsync() ?? Task.FromResult("")); @@ -108,6 +123,9 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis AppendHeaders(webRequest); + // Add the ARF API headers + AppendARFHeaders(message, webRequest); + Debug.Log("[HTTP] Request " + webRequest.uri.ToString()); try { diff --git a/Runtime/Scripts/WorldAnalysisREST.cs b/Runtime/Scripts/WorldAnalysisREST.cs index c4e0d6c..073f237 100644 --- a/Runtime/Scripts/WorldAnalysisREST.cs +++ b/Runtime/Scripts/WorldAnalysisREST.cs @@ -183,9 +183,9 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface } PoseEstimationResult[] resul = new PoseEstimationResult[uuids.Length]; poses = new ETSI.ARF.OpenAPI.WorldAnalysis.Pose[uuids.Length]; - List uuidList = new List(); - Response poses_ = apiClient.GetPoses(token, sessionID, uuidList.ToArray()); - List posesList = poses_.Poses as List; + List uuidList = new List(); + Poses poses_ = apiClient.GetPoses(token, sessionID, uuidList.ToArray()); + List posesList = poses_.Poses1 as List; if (poses_ != null && posesList != null && posesList.Count > 0) { @@ -398,16 +398,16 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface public CapabilityResult GetCapabilities(string token, out Capability[] capabilities) { - Response2 cap = apiClient.GetCapabilities(token, sessionID); - if (cap == null || cap.Capabilities == null || cap.Capabilities.Count == 0) + Capabilities cap = apiClient.GetCapabilities(token, sessionID); + if (cap == null || cap.Capabilities1 == null || cap.Capabilities1.Count == 0) { capabilities = null; return CapabilityResult.FAIL; } else { - capabilities = new Capability[cap.Capabilities.Count]; - cap.Capabilities.CopyTo(capabilities, 0); + capabilities = new Capability[cap.Capabilities1.Count]; + cap.Capabilities1.CopyTo(capabilities, 0); return CapabilityResult.OK; } } @@ -418,7 +418,7 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface type = TypeWorldStorage.UNKNOWN; capability = null; - Response3 cap = apiClient.GetSupport(token, sessionID, uuid); + Supports cap = apiClient.GetSupport(token, sessionID, uuid); if (cap == null || cap.Capabilities == null || cap.Capabilities.Count == 0) { isSupported = false; diff --git a/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs b/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs new file mode 100644 index 0000000..118c780 --- /dev/null +++ b/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using ETSI.ARF.OpenAPI.WorldAnalysis; + +using WebSocketSharp; +using static WorldAnalysisInterface; + +//Implementation of the WorldAnalysis interface +public partial class WorldAnalysisREST +{ + // + // Inspector variables + // + public StringEvent webSocketMessage; + + // + // Private members + // + private WebSocketSharp.WebSocket webSocket; // For WebSockets + private bool websocketConnected = false; + + #region Communication system for WebSockets + public WebSocket WebSocketClient_Create(string url) + { + webSocket = new WebSocketSharp.WebSocket(url); + + // + // Define standard callbacks + // + webSocket.OnOpen += (sender, e) => + { + Debug.Log("[WS] Connected"); + websocketConnected = true; + webSocket.Send("RegisterClient:UnitySceneManagement"); + }; + 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.Connect(); + + return webSocket; + } + + private void WebSocketClient_Close() + { + if (websocketConnected) + { + webSocket.Send("UnregisterClient"); + webSocket.Close(); + webSocket = null; + } + } + + public void WebSocketClient_Send(string msg) + { + webSocket?.Send(msg); + } + + bool isRegistered = false; + public void WebSocketClient_OnReceive(string data) + { + //Debug.Log("[WS] Receiving: " + data); + + if (data.Contains("You are now registered")) + { + isRegistered = true; + if (isDebug) + { + webSocket.Send("TimeStart:3"); // test + } + } + else if (isRegistered) + { + if (data.StartsWith("Time=")) + { + // test the communication + Debug.Log("[WS] Server time is: " + data.Split('=')[1]); + webSocketMessage?.Invoke(data); + } + else if (data == "TimeStop") + { + // Get some dummy poses? + //webSocket.Send("PoseStart:5"); // test + } + else if (data == "PoseStop") + { + //SetColor(Color.yellow); + } + else if (data.StartsWith("Pose=") && data.Contains("estimationState")) + { + // Handle the new pose + string json = data.Substring("Pose=".Length); + ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose = JsonUtility.FromJson(json); + Debug.Log("[WS][Pose] State: " + pose.EstimationState.ToString()); + + // to check: p.Confidence + // to check: p.Mode, p.Value + + PoseEstimationResult res = pose.EstimationState == PoseEstimationState.OK ? PoseEstimationResult.OK : PoseEstimationResult.FAILURE; + + // Look for the corresponding callbacks + foreach (var item in m_subscriptionsPoses.Values) + { + if (pose.Uuid == item.uuidTarget) + { + item.callback(res, pose); + } + } + } + } + } + #endregion +} \ No newline at end of file diff --git a/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs.meta b/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs.meta new file mode 100644 index 0000000..31b4333 --- /dev/null +++ b/Runtime/Scripts/WorldAnalysisREST_WebSockets.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b7bcc535a949e24283db7e23c51dc46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Scripts/WorldAnalysisREST_Webhook.cs b/Runtime/Scripts/WorldAnalysisREST_Webhook.cs new file mode 100644 index 0000000..1b4a981 --- /dev/null +++ b/Runtime/Scripts/WorldAnalysisREST_Webhook.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using ETSI.ARF.OpenAPI.WorldAnalysis; +using ETSI.ARF.WorldAnalysis; +using ETSI.ARF.WorldAnalysis.REST; + +//Implementation of the WorldAnalysis interface +public partial class WorldAnalysisREST +{ + // + // Inspector variables + // + public StringEvent webhookMessage; + private bool webhookRunning = false; + + #region Communication system for WebHooks + private void WebHookServer_Create(string url) + { + webhookRunning = true; + throw new Exception("[API] WebHookServer_Create(): Not implemented!"); + } + + private void WebHookServer_Close() + { + if (webhookRunning) + { + webhookRunning = false; + throw new Exception("[API] WebHookServer_Close(): Not implemented!"); + } + } + + private object WebHookServer_OnReceive() + { + throw new Exception("[API] WebHookServer_OnReceive(): Not implemented!"); + } + + private void WebHookServer_Send(object message) + { + throw new Exception("[API] WebHookServer_Send(): Not implemented!"); + } + #endregion +} \ No newline at end of file diff --git a/Runtime/Scripts/WorldAnalysisREST_Webhook.cs.meta b/Runtime/Scripts/WorldAnalysisREST_Webhook.cs.meta new file mode 100644 index 0000000..b7ce0cd --- /dev/null +++ b/Runtime/Scripts/WorldAnalysisREST_Webhook.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67d4b99a5f7593e4a857b6c88249d564 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: -- GitLab From 9e97f577a5dd43eaa596a7be720ab19f7f9fa35b Mon Sep 17 00:00:00 2001 From: Sylvain Renault Date: Wed, 25 Sep 2024 18:43:18 +0200 Subject: [PATCH 6/6] Implementation of all ws functions. --- Runtime/Scripts/OpenAPI/DataModels.cs | 10 +- .../OpenAPI/Generated/WorldAnalysisOpenAPI.cs | 2 +- Runtime/Scripts/REST/AdminRequest.cs | 12 ++ Runtime/Scripts/WorldAnalysisREST.cs | 128 +++++++++--------- .../Scripts/WorldAnalysisREST_WebSockets.cs | 79 +++++------ Runtime/Scripts/WorldAnalysisUnityHelper.cs | 2 +- 6 files changed, 127 insertions(+), 106 deletions(-) diff --git a/Runtime/Scripts/OpenAPI/DataModels.cs b/Runtime/Scripts/OpenAPI/DataModels.cs index 91d04fc..0d2864d 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 126f5ab..713ebd5 100644 --- a/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs +++ b/Runtime/Scripts/OpenAPI/Generated/WorldAnalysisOpenAPI.cs @@ -1879,7 +1879,7 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis /// /// The pose value /// - [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; } /// diff --git a/Runtime/Scripts/REST/AdminRequest.cs b/Runtime/Scripts/REST/AdminRequest.cs index 9910526..048b10c 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 { + #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 073f237..3b20a52 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"; + + /// /// WorldSAnalysisServer /// 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 { } - //public StringEvent webSocketMessage; - // // Private members // @@ -44,14 +45,15 @@ public partial class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface /// /// Dictionnary of susbscription informations for poses, for each item, stored using the UUID of the item (anchor/trackable) /// - private Dictionary m_subscriptionsPoses; + private Dictionary 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(); //m_computedPoses = new Dictionary(); - m_subscriptionsPoses = new Dictionary(); + subscriptionsPoses = new Dictionary(); // 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 subPose in m_subscriptionsPoses) + foreach (KeyValuePair 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 /// protected void ManageSubscriptionValidity() { - if (m_subscriptionsPoses.Count == 0) return; + if (subscriptionsPoses.Count == 0) return; List subscriptionToDelete = new List(); - foreach (KeyValuePair sub in m_subscriptionsPoses) + foreach (KeyValuePair 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 118c780..e2adc63 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(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(_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 b6f5b21..830de28 100644 --- a/Runtime/Scripts/WorldAnalysisUnityHelper.cs +++ b/Runtime/Scripts/WorldAnalysisUnityHelper.cs @@ -33,7 +33,7 @@ public class WorldAnalysisUnityHelper /// /// the values to convert /// Converted Unity Matrix - public static Matrix4x4 ConvertETSIARFTransform3DToUnity(ETSI.ARF.OpenAPI.WorldStorage.Transform3D value) + public static Matrix4x4 ConvertETSIARFTransform3DToUnity(ETSI.ARF.OpenAPI.WorldAnalysis.Transform3D value) { if (value.Count == 16) { -- GitLab