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 zcmXqL()rHDsnzDu_MMlJooPXn&I^MkohM9;%!d31ylk8aZ61uN%q&cdtPBPz2!)Ib z!dXJYQj3Z+^Yg3>xwwq<49)Z`Obql4^$ZOR^^EmQ^o;Zjbv+V`Gps5CGcAJCgG@`x zElR5bbG*&7^s`C|lY9&uw=lO%D;y2v>^*>GcBmKd^667wZiy2v284OYlxeYkkm_u3EgqcEv4Mh!vK^!h& z9+%AYOy|_15(Uq^WJ5^#ut0fvxn4?UdS-HJQHfr1ey*X4ff7iHSy&pb!?C!y zv?w)2!P7+{C_leM!P(J3PMp`k%+S!#00N@Kd5sN`xb$*H6QdGx05GyLFgG#sGZ-{6 zaxpbAGBT{?6Fv9Dxglm_`{(TmKMRGHuDhc)rDN5Xb6*uKH6jvtel*-#DLU0C(PryL zjh3#iJM+1Cn#HU7C#>muUKt!?*rN4~e}<7+*_nHHZB{ILpY>buQ&8gl`3l$NA8ow( zPa@@#P;~sy2no|Y?q?U8{mibvvcbacp!DG$2`+ zeC|xEqJ__XExCDB+^}?AYpv2cdq%gmZyJ>+Hr{#?E`Fp=hHt}`Nz1}E@7*;0?W*M; zwjW(#KUM6)|Ao_DbHq!O`!g{!GB7SqHb^w!V`C1L4_wwb6b3f;v;5U#5Nh`BR7>G4sSHKI;5TJC( z3<~{4e0%Ocdi&vVQE^qul>Og}(ry?%aVkw(*Z%s`)tmRiHqSqEPDEoT@AQNiA)kmR zmu;IpT_dKOW=gGae-(DDa<$Nn^v0I^^_(_Sp6yW6QP^v{S)=oN{C}lK(_7?UroL}~ zaQ|Ss=jPbgn>e*ztG%t*S8K2RdApivZhR->HqYq!jI6U)+o~Qsv#tKn$NaWqzfv+@ zH|kD#?X>+@kKEm)kNX1Bx99Br{82ue$BbR9-n?dIbZ*s0A-9C)>*_4~yw6SFcQ{=u zL-2mXa~|Wg{ldH=d7ta$lLh2{^-8Pe=JYS!p2)cI(qgrR0)=zeK05sK@XWyN+Uy+X z^qvK5TKI zae1>$J)hwE^m6vg+=iZ#=P&$row?EKw#(Q0rz_^(Ho9K;s%$Q+-uC`qlbjDM{n>fN zT=M$1RUhWGExn<-hObh?s_x~*^vlP7YpNf=T&+Ci$wcL86Uv)@E^JXw6ACd7DA}l? zwp%6h(4nADBI#Y7u2*yyE`HgeqcC-yj8{9yeV(t6d=n)0tva%4U4dbmaiQIsf<>#( z)xWGcJ!#=nYeSjyuRd`-3E%Ct^<~L3?SDC^>q;!mcqhDnvej?N%2!!jt6M&=N%z}& zXu$l)#3&{Ma#*pIzCL>0sP7yQ3<^)EPG+Ps zGwR6BYAvqCOJ5Z-A4vGA`z^JUFM^~plN(%hF*4Y_;f+$+{8{Ou=n0(~Rn4*$vavF{ zR#Hda8zf$@<4*W{*p~mu`tHB)weBl~ns0dc?c$_T{=hAHOZ%c8#i=y<2`qVMU)Yz- zC+xlUdb~^JXEPDDuJ3c){9^u3*eK1;H&OKWuJv035A~|7Q#p2AU2CDHhLlyI29u56 z#Ki|x1s}~3abH-Kk+FXE@)(7fn}r*B`ggDRaa1T{{&|Pf4lYZDKRip}QRQ$l+GN(H z!t_tSs7~g(r|YyAk+Ls;m84{EdY$HcYx9Plt1o%)eq&TPH`6fLmP1NnbK`9PW0M8g z{;)Ux>b~`}$@6{G_jaN%S^`2 ztE+82!opj1_Ww)bD*lFEW)4JQB`3~nWN2VyXa*`i4Gcg9sEL88p{bFvsimo9ltE(; zY6&WAAP6cCIfS{w6N@u4^U_Q5^9%*RIhKW8m^~;pB{x4W#SoMTnL}B)gcbct-BU~S z@{7_Hf>I0ei!)2|iz*e8DiwS)lZ*0;^V3RTYMF^pYe1-MeOJG$ZQ)TZ)~Vayr@6dh zTPAXlr81``CtI2C^RG%1&I##_h5Kin^j3J8UM2oza!Z!wgQq#1Q@w7-|GM)_Ejv^Brg={u$R zdD_X8hw8d3&b%QoqH{6KS9mNK> zP4TbU+gEL{Q(d?F;p;Q4J)hn`SGEl{Z8o?VSneJCkXidm+u^cjYqyyj2f)kLBL+>3 z2T<}X)=Z6PC;bRGGUd@Z{qt5yd~GvM6uXqjt8WL#D!iS=-T-cXW0b??pn^!ArNAK1 zz;uD}0>d_aP?2M$ub*6$gI1muXCxZwCPEtEy2-`5>4tjAMLC$-(27tT+Ppy)E3mj2 zI2o8h`ZH~RbC?+oG!4{|3P5S7Yp|Ao9BgdqjXaDjj0TM@Fg=WnEENW2L{=w$FkJ>r zZB9^|@Kz_F#sj=c0ks^U4F_n=!UOUXxHe=mU_dJ$z|GJb%#Tl1oj)br{WsS=T4m8?v(LENaHcAY#g>P{Z05?p zC#_L*S(Fx3KPl|}l>YOg-6CvRZBky_uCM#{a@K)q(=TYwd37~FK_>I|l;`!Q)gSHN zm9|^z%!j)xjW#?KVdw43Stqi-K3+f~Vuq!G*47D&o%PmbT$R}U;%@X|{vA*3H@P!S zGb|GPwdw4w+nrJ?{+!yjqlK%mUoC&tpYwVbAMCuN0#*qSfzwy)l| zQ9JdU38%H^JgNDr$Ff#-oqNC&v0{3dPSb|fC-mQ6XQZ5K2#?lh2qH|IH-4Y}bQc18mkIERzfkYAkF$k@Qp$js2d)X>B* z%0P~d8`>S^6krkn_nQ#{Y)l67Y&_a*j4X^z<{?asj7%T}JlyVyMM*iS#kwx3Wd`~P z<5>)qSQO$OddE5bE-HWZEAQE^O{ceNFM9sk{a{pl5SJhC*LT`#2Bs(`G8(FiDT7iK zr~*$c$kfX#O@|bw`enxYnRzLx6?$34`FRG7SCL(+Z`gR$pz)AF<9>t2JqFy6yaDd( zvnWJqGG1G|_+nGS&SUo<3END)?mh2))`O>ajD1={F1pu#F=*^EC|go85DjN00elrC zBP#<-6C;B*qudFv8LG#sr#{v!c@yUn7xJ{xAZ#L6#qvKn!c2SXL%Obuo!|YZ$%A3j zw$ukPo>QKEKH@kbQ=@+y)4MrF8-M10_DQQ~o&H@V=?KHJXYJ=+o^IDSJ*~eqabj~` zlH%*1jj4*J!B@2E=NXN?lR5zfXnb`~Ht}Y-+OJPWyW% zaO%QO6C7UZHhMc5o!(@m=kRKN@zz!Ek2$Vf^GeX5axGUN+v1}(fe)g)FLT(lev~ge zzHg1x?P=d19OPME9VlJJeQaTqw1PpCv}6@}GH7gPVq}CQmbSk+?2HD6paKZgJ_M&7Rt6S@LyKa(?;nWkUduP@ zpSDS8cuDbKguRZA>_tf;c;ptnG znJH{9AbafZW$%EJ}Mw{1MKHA}*RS)ljE74s!}thF6qo9qN87OW0!ax}Vro26JQ zZz5xjqR*8*>!g`WV=K~bS{b^`GIrR{aXrd&$)RT}7o^`^qP}OLzvWiJ2=VVKF>d`S z-w$SXl+7r!jf{0FYww(%S>^HJ!6G5)v{@ym?s>~S=SyDq?8^H|ldR_1-eGv+s@l12 zmW!lz-GM1vE+yu^HokfEzBdcQnOd2thx@-PZrUWceRut*t33rejtQ$aCQG#e-_Fe zJ`sDvV9>xv!UQ|9sbs zbu#?^;N&NZZ&Vw7*n9v-&|E?N9C;Qy0~?A4!9ZgXvI;C*1{?-#Y%HO&3MhRh9_TPs z!GitHF`>^b;ukC~k+$6?cK!SQaQ3~k)+}t-dD9xVQfjx-k9Mid5T&{GO+BwCbiEW)h6U#Jcfy{1ZzFj4x=7#&4zZz*;anZ;HGHB?Y9wi}Y*vZJi z*vJ?@>||_iU~FUrD~*#;OJi{ZQP8LpmoOi!Y}Elx{plH68(4xQn1zjC67WLT-9#ZM z*wMtm(o6x=={7Y}2+7P%4K7K{Er1Qw6DW?4wVAScZa!#RxZRd0+z3)|^?T2e&SDtI=zP`M1Yw2@& zw$$D!nwM|B$-IAaLc1G7^=%i&&O0?(;Z+~ZE9b|qIsKv~WlPYfGWYL^&60QHw}fyj zX5^VG?sixczv-S=?YXa(Lh0}MmL}&sS$0(RtQl|2)o0Qx4yKeiEI&|mbeEs}#;p;) zmH)2y7f)WJWBF*Q$F&a|424&QYfe+w``W&&->*zGRIOl!TKxhkcLnFi&dCkcrFrVA z6J}Uh2yXuNZQe8M=r@h`ltgYeX&s-mf5E-Sc8iLCJ(rKlner)Us-Sj6iG#-KV&`-B zy%x%3TO8H=d}`ISVyBGx+uSzKwOV|Y`N94Vi)>Cg-I4I97cn`1$!GQZ*|z`qcBE+i zD%DVNeCxEIbxxXRP{5bK&t{&!-3pgKXy~+P95B%I@u+-=J z%Nbq~c9)qu{%!8R9Oqpo6gc^0507sC*ll*_PWj6dbw=pCBE#LJeQ2OoZSd3j$;j) z7$dPxR3OIBc8T50^=;KJ)-0Q)U)qzU&_C})K1zv zi5!`7gy}M1YBL3mA%I5Y@J)0;8!Mn`4tEpqG>0>!khU?fGO$=+w!oy#2xbn($Q~^B zp&OE8pa3#Nfko0l+yFM(#snLafOIpFCR@Q12TY6%wUK=XCU481vC>6}u`cb}4MnGi z8zl4ue1*>&1fSb4@%wl{>?3c@-vwQ~t28tJWF9;(%l^_X;lvH@r!mpjbK8HlD5#nk zrk~v=G%f!~fKQSv4@c7qvArw9&WjfJGAgN_o0@6wUjDnNMCAJa)&5;NRjXUiI>*0F z*tW`lV%j{Nc@MhQJpJ3VCq=D2XVTueH5UrFYTmEdQ|iAf>ibH8O|dWkYlL)uxY|`% zmU`>VhoZ&GzYeH3xvZP1F1=bsPVM0Kds?r*&D&TxvvF})+iEV?llPDGE_goU_wQwM zi`E}Ge(+#c;mu>h4V*`|%|F}TuCm*IvFFb6w22>!=DhS~3R-SnH#vBXwJe7x|CF%9WHDaRylh|w!-SkytPkF3MXAx%YI@}ym}d< z|6Y6RinTs#e;=7z{gx}<*s|TRqRQgsoQHRn|H^7yKK}E?#~JazwQtmYa+_PKBd4yT zb~|6RT28XtY{%p)&pwIIj67BF!F>M=hWiR){_4LI(#000?YbL!z)G`i!;A&mZ>KtL zx4J4i!*ELyCqLKT#+$?E9jvsBIM*ve1YyL%7v&bqzu zg-C0Ov6R}H3o@Z{a%Q=ECWZ1OewoWzZYw&CLq9uZ$zQf+vFj5jS;!bNP5WK6ak^FL zdgC{VT`!ku+@6pev$r$&ft7AgyVE+gB~Nx&RP3x0dl+`?z^;_w-CAPL13Y$jWb$+z z`?&9cey#P!Q^CCs&2rCA7kO&P`83G2hCOe6`zdkHDl7KdZ>#g3rrmuM^Hu+C?-k#v zEg6p+PG?nKyd*HuP4l&#+NUi&XMJ|2o!Gr9z=HQ_&y-lnsQq879rE(UR?M0+)xE8? z(vBxCtw)MWW#61tm*#By?w?n2M8kfu41WNd`7Vx|rpva<-KvO?wS4izXQIHgI^TzP z53OBV_d{1&{*AVq#A3;Bmfyb~^e@!wWENcHy?e&~U;a<3&EB)QJPG}8_UdVk;q%Xj zf{r{BtB$H$s#<*eK-Nq}-Q=f{Uw!o_$g6L^v|8k;MOu3xNA~YvXUhvIzrxNx_`>x} zBsZmE_l&<&4i_ta)<2}DaJ_NO<&W%F9`3yv>z!vfMYH>vB{YpwOdH+w73CmntBuzKg2bYb>c2P@tl z>o~C?Y^cVu(#v3Fl}YO4EAr*wYYcx1=(;JEnEOSO`kk<5O-Vqduy%e2&8KlkX0 zRXMCy>NaR%Y{!_QL7hMl0~NlipoKBWbLSjH6w1g;WU$t4h^jBC>)wULKmCRxZ>Mvg zwbhy;azAK}K@`Yld6r;p>YPRO@#r6kIGRzj1-7ur` zPQ|CQHLYiZ=dMqBl$f?yM9a0^Ylf3{z#r+Y^EeDRe?8H?&e>V{lk-`}xmxKRS&L>L z3{K#Dv3SSa>ucp@cD$M4_~9F0!|x~m@6Q!j{+eM|yjQ=JRnV4K`#C*DP0b=_@rPX6 zBwb$@$GNsFq^l(N)Fsw*?XuVZXKYfNY9ZO-BJz|ga#8kkB|8_zOZGAcX1}fR*mPqO z&qRNlv?=RjnSx)>do=IKW%)M7lsOECUPhIa^4dPE?AR~EIQ^uga>I4AsQ77O8IF8y+a%`@y{Ao2&cNb32%w++R4F6_&64yXeLA5bJ@nz2(hC3CdvD0h3Ou4e>s$PC_368&>9vL*NO)cmR-g6$(9~Jpf=nlx z=A4vbt=Uz$)vs!1&X*VSu0+i^F@JyWZiyJ@uNSmlJW6_3!E$@=#oy6fD+=m9OmC@p z%)uP%u9Lh%)nYp1C3~w|PoI8!Hv3MU&YAz>KE4c#Zd_yJ+tK~**@L3cP91g0#Bb${ zJ%&xpWd=>mMNEv02IU6ysG^tzuvL^u1NaLXFB(X}CkZ&24SCrR^I426_-4b5q71Z= z6f^M{${0u)NEnE*h29Y^a$`%~_s?dR+kS@QmxQXfwz$emqgVY2J~U^NZ|-1TH94?RIz92>kD@$t|(0FTHZn)$R$0pT=rOKwMy8Gc?QoG z#CgAq;MUxo;VE#S`FyZu=>K2yKAb(dV#k|feDUdSJqybk7e?mPy}gmA|EG`P^993A zXPTn}XHUB?%9ry0LD9+y487bb%kFLo=l9n))D~*=-I&>>y>Fwj(i&r%_e_C@+3MUF zxWeKLC9`%aIOHA?rAf%4<%Fnp0Mh-?7GeV*f) + + + 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~~Xs4mJh`hO-lvu3}(d;4JWnEM{Qf76xHPhFNnY z7#NrxJzX3_DsH{Kc9HY2fq;vnc5m+6=$W5nIc7-kblC(qTF#d%mti0V_?z@Ja4(BZ zW7k(^U=eU&V5FG9FyTk>VUB6$Z+Um%GMr*-f22tqY=0>~H+%P;Ig@Ur#xyW6aVWqE zBqlyXv{e20EEeP$>iB~xg9T(5 z-2XW#5{%Xv*5SXGsoa~+O-fW?L^K}F4bi;H55jYwXWgH2MDH;ZN?4E*h@L2`-hTaXS -// 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