From 0f4197f438ba38144b1cb7881a3fc931a24a4570 Mon Sep 17 00:00:00 2001 From: Sylvain Renault Date: Tue, 24 Sep 2024 21:22:16 +0200 Subject: [PATCH] New version in C# with complete visual studio project. --- README.md | 44 +- openapi | 2 +- .../Access/WorldStorageServer.cs | 34 + .../Access/WorldStorageUser.cs | 29 + .../OpenAPI/WorldAnalysis/BaseClient.cs | 38 ++ .../OpenAPI/WorldAnalysis/DataModels.cs | 56 ++ .../OpenAPI/WorldAnalysis/ResponseObject.cs | 64 ++ .../UnityWebRequestHttpClient.cs | 81 +++ .../WorldAnalysis/WorldAnalysisClient.cs | 26 + .../OpenAPI/WorldStorage/BaseClient.cs | 38 ++ .../OpenAPI/WorldStorage/DataModels.cs | 78 +++ .../OpenAPI/WorldStorage/ResponseObject.cs | 64 ++ .../WorldStorage/UnityWebRequestHttpClient.cs | 81 +++ .../WorldStorage/WorldStorageClient.cs | 26 + wrapper/c#/WorldAnalysisWrapper/Program.cs | 62 ++ .../WorldAnalysisWrapper/REST/AdminRequest.cs | 65 ++ .../REST/RelocalizationInformationRequest.cs | 204 ++++++ .../WorldAnalysisWrapper/REST/RequestBase.cs | 129 ++++ .../REST/TrackableRequest.cs | 89 +++ .../REST/WorldAnchorRequest.cs | 91 +++ .../REST/WorldLinkRequest.cs | 90 +++ .../c#/WorldAnalysisWrapper/WorldAnalysis.cs | 624 ++++++++++++++++++ .../net5.0/WorldAnalysisWrapper.deps.json | 57 ++ ...orldAnalysisWrapper.runtimeconfig.dev.json | 9 + .../WorldAnalysisWrapper.runtimeconfig.json | 9 + ...CoreApp,Version=v5.0.AssemblyAttributes.cs | 4 + .../WorldAnalysisWrapper.AssemblyInfo.cs | 23 + ...ldAnalysisWrapper.AssemblyInfoInputs.cache | 1 + ....GeneratedMSBuildEditorConfig.editorconfig | 8 + .../net5.0/WorldAnalysisWrapper.assets.cache | Bin 0 -> 1116 bytes .../WorldAnalysisWrapper.csproj.CopyComplete} | 0 ...ysisWrapper.csproj.CoreCompileInputs.cache | 1 + ...nalysisWrapper.csproj.FileListAbsolute.txt | 38 ++ ...lysisWrapper.csprojAssemblyReference.cache | Bin 0 -> 103975 bytes ...orldAnalysisWrapper.genruntimeconfig.cache | 1 + ...ldAnalysisWrapper.csproj.nuget.dgspec.json | 76 +++ .../WorldAnalysisWrapper.csproj.nuget.g.props | 19 + ...orldAnalysisWrapper.csproj.nuget.g.targets | 6 + .../obj/project.assets.json | 148 +++++ .../obj/project.nuget.cache | 11 + wrapper/{ => python}/TestWSServer.py | 0 wrapper/{ => python}/WorldAnalysisWrapper.py | 170 +++-- wrapper/python/arf specific.txt | 0 wrapper/{ => python}/localhost.pem | 0 wrapper/python/openapitools.json | 0 45 files changed, 2534 insertions(+), 62 deletions(-) create mode 100644 wrapper/c#/WorldAnalysisWrapper/Access/WorldStorageServer.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/Access/WorldStorageUser.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/BaseClient.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/DataModels.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/ResponseObject.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/UnityWebRequestHttpClient.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/WorldAnalysisClient.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/BaseClient.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/DataModels.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/ResponseObject.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/UnityWebRequestHttpClient.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/WorldStorageClient.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/Program.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/REST/AdminRequest.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/REST/RelocalizationInformationRequest.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/REST/RequestBase.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/REST/TrackableRequest.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/REST/WorldAnchorRequest.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/REST/WorldLinkRequest.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/WorldAnalysis.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.deps.json create mode 100644 wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.runtimeconfig.dev.json create mode 100644 wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.runtimeconfig.json create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/.NETCoreApp,Version=v5.0.AssemblyAttributes.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.AssemblyInfo.cs create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.AssemblyInfoInputs.cache create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.GeneratedMSBuildEditorConfig.editorconfig create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.assets.cache rename wrapper/{arf specific.txt => c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.CopyComplete} (100%) create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.CoreCompileInputs.cache create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.FileListAbsolute.txt create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csprojAssemblyReference.cache create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.genruntimeconfig.cache create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/WorldAnalysisWrapper.csproj.nuget.dgspec.json create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/WorldAnalysisWrapper.csproj.nuget.g.props create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/WorldAnalysisWrapper.csproj.nuget.g.targets create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/project.assets.json create mode 100644 wrapper/c#/WorldAnalysisWrapper/obj/project.nuget.cache rename wrapper/{ => python}/TestWSServer.py (100%) rename wrapper/{ => python}/WorldAnalysisWrapper.py (70%) create mode 100644 wrapper/python/arf specific.txt rename wrapper/{ => python}/localhost.pem (100%) create mode 100644 wrapper/python/openapitools.json diff --git a/README.md b/README.md index c2f6bae..a6c65e2 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,34 @@ What you need: 2. Installed openapi generator with npm: https://openapi-generator.tech/docs/installation/ 3. Optional: Installed docker (recommanded): https://www.docker.com/get-started +## Auto-generate client code for WS + +Use/define following setup for the config file `openapitools.json`: + +``` +{ + "$schema": "node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "7.6.0", + "generators":{ + "python": { + "generatorName": "python", + "output": "./generated_wa_client", + "inputSpec": "./openapi/API/worldanalysis/worldanalysisopenapi.yaml", + "additionalProperties": { + "packageName": "ETSI.ARF.OpenAPI.WorldAnalysis" + } + } + } + } +} +``` + +Open a command shell and execute: +``` + npx openapi-generator-cli generate +``` ## Installing the python module on your local computer @@ -40,7 +68,21 @@ conda create -n openapi conda activate openapi ``` -Install the common apckae for async WebSockets: +Install the World Analysis OpenAPI: + +``` +pip install .\generated_wa_client +``` + +In case of not having the pip installed for your cml you can use following line: + +``` +py -m pip install .\generated_wa_client +``` + +## Installing the python module for WebSockets on your local computer + +Install the common package for async WebSockets: ``` pip install websockets diff --git a/openapi b/openapi index 073fd72..7e50e43 160000 --- a/openapi +++ b/openapi @@ -1 +1 @@ -Subproject commit 073fd7213fd9e6ebc2f8a47d628a650de30c8bc4 +Subproject commit 7e50e43e90a8dcd958944e8e9ceda05c7668db48 diff --git a/wrapper/c#/WorldAnalysisWrapper/Access/WorldStorageServer.cs b/wrapper/c#/WorldAnalysisWrapper/Access/WorldStorageServer.cs new file mode 100644 index 0000000..c39c53f --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/Access/WorldStorageServer.cs @@ -0,0 +1,34 @@ +// +// 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 +// + +namespace ETSI.ARF.WorldStorage +{ + public class WorldStorageServer + { + public string serverName = "myServerName"; + public string company = ""; + public string basePath = "https://"; + public int port = 8080; + + public WorldStorageUser currentUser = null; + + public string URI => port == 0 ? basePath : basePath + ":" + port.ToString(); + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/Access/WorldStorageUser.cs b/wrapper/c#/WorldAnalysisWrapper/Access/WorldStorageUser.cs new file mode 100644 index 0000000..b5dae43 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/Access/WorldStorageUser.cs @@ -0,0 +1,29 @@ +// +// 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 +// + +namespace ETSI.ARF.WorldStorage +{ + public class WorldStorageUser + { + public string userName = "myName"; + public string company = ""; + public string UUID = System.Guid.Empty.ToString(); + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/BaseClient.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/BaseClient.cs new file mode 100644 index 0000000..9ba5593 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/BaseClient.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +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) + { + Console.WriteLine("[REST][URL] Send request: " + client.BaseAddress + url); + Console.WriteLine("[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) + { + Console.WriteLine("[REST][Data] Status: " + status_ + " Response: " + client.BaseAddress + " Len: " + lastPayload + " JSON: " + lastJsonText); + } + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/DataModels.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/DataModels.cs new file mode 100644 index 0000000..9320ae9 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/DataModels.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Numerics; +using Newtonsoft.Json; + +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 JsonConvert.SerializeObject(this); } + } + + // + // Implement here some constructors + // + public partial class Pose : IModel + { + public Pose() + { + Uuid = Guid.NewGuid(); + } + + public string ToJson() { return JsonConvert.SerializeObject(this); } + } + + public partial class Transform3D + { + public Matrix4x4 Matrix() + { + return new Matrix4x4( + this[0], this[1], this[2], this[3], + this[4], this[5], this[6], this[7], + this[8], this[9], this[10], this[11], + this[12], this[13], this[14], this[15] + ); + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/ResponseObject.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/ResponseObject.cs new file mode 100644 index 0000000..1344360 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/ResponseObject.cs @@ -0,0 +1,64 @@ +// The Fraunhofer HHI Unity Framework +// ___________ .__ _____ ___ ___ ___ ___ .___ +// \_ _____/___________ __ __ ____ | |__ _____/ ____\___________ / | \ / | \| | +// | __) \_ __ \__ \ | | \/ \| | \ / _ \ __\/ __ \_ __ \ / ~ \/ ~ \ | +// | \ | | \// __ \| | / | \ Y ( <_> ) | \ ___/| | \/ \ Y /\ Y / | +// \___ / |__| (____ /____/|___| /___| /\____/|__| \___ >__| \___|_ / \___|_ /|___| +// \/ \/ \/ \/ \/ \/ \/ +// (C) Fraunhofer HHI, 2024 + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +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/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/UnityWebRequestHttpClient.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/UnityWebRequestHttpClient.cs new file mode 100644 index 0000000..b3bc2e1 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/UnityWebRequestHttpClient.cs @@ -0,0 +1,81 @@ +// +// 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: September 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; + +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; + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/WorldAnalysisClient.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/WorldAnalysisClient.cs new file mode 100644 index 0000000..126c78d --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldAnalysis/WorldAnalysisClient.cs @@ -0,0 +1,26 @@ +using System.Collections; +using System.Collections.Generic; + +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/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/BaseClient.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/BaseClient.cs new file mode 100644 index 0000000..765563f --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/BaseClient.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace ETSI.ARF.OpenAPI +{ + /// + /// Simple class to debug the requests + /// + public class BaseClient + { + static public bool EnableClientLog = false; + public string lastJsonText; + public long lastPayload; + + protected void _prepareRequest(ETSI.ARF.OpenAPI.WorldStorage.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url) + { + if (EnableClientLog) + { + Console.WriteLine("[REST][URL] Send request: " + client.BaseAddress + url); + Console.WriteLine("[REST][URL] Send request: " + request); + } + } + + protected void _processResponse(ETSI.ARF.OpenAPI.WorldStorage.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) + { + Console.WriteLine("[REST][Data] Status: " + status_ + " Response: " + client.BaseAddress + " Len: " + lastPayload + " JSON: " + lastJsonText); + } + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/DataModels.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/DataModels.cs new file mode 100644 index 0000000..f330510 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/DataModels.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Numerics; +using Newtonsoft.Json; + +namespace ETSI.ARF.OpenAPI.WorldStorage +{ + public partial class Transform3D + { + public Matrix4x4 Matrix() + { + return new Matrix4x4( + this[0], this[1], this[2], this[3], + this[4], this[5], this[6], this[7], + this[8], this[9], this[10], this[11], + this[12], this[13], this[14], this[15] + ); + } + } + + public interface IModel + { + public System.Guid UUID { get; set; } + + 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 JsonConvert.SerializeObject(this); } + } + + // + // Implement here some constructors + // + public partial class Trackable : IModel + { + public Trackable(string name) + { + UUID = Guid.NewGuid(); + Name = name; + } + + public string ToJson() { return JsonConvert.SerializeObject(this); } + } + + public partial class WorldAnchor : IModel + { + public WorldAnchor(string name) + { + UUID = Guid.NewGuid(); + Name = name; + } + + public string ToJson() { return JsonConvert.SerializeObject(this); } + } + + public partial class WorldLink : IModel + { + public WorldLink() + { + UUID = Guid.NewGuid(); + } + + public string ToJson() { return JsonConvert.SerializeObject(this); } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/ResponseObject.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/ResponseObject.cs new file mode 100644 index 0000000..40165ff --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/ResponseObject.cs @@ -0,0 +1,64 @@ +// The Fraunhofer HHI Unity Framework +// ___________ .__ _____ ___ ___ ___ ___ .___ +// \_ _____/___________ __ __ ____ | |__ _____/ ____\___________ / | \ / | \| | +// | __) \_ __ \__ \ | | \/ \| | \ / _ \ __\/ __ \_ __ \ / ~ \/ ~ \ | +// | \ | | \// __ \| | / | \ Y ( <_> ) | \ ___/| | \/ \ Y /\ Y / | +// \___ / |__| (____ /____/|___| /___| /\____/|__| \___ >__| \___|_ / \___|_ /|___| +// \/ \/ \/ \/ \/ \/ \/ +// (C) Fraunhofer HHI, 2024 + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace ETSI.ARF.OpenAPI.WorldStorage +{ + 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/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/UnityWebRequestHttpClient.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/UnityWebRequestHttpClient.cs new file mode 100644 index 0000000..1cc6cf5 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/UnityWebRequestHttpClient.cs @@ -0,0 +1,81 @@ +// +// 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: September 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; + +namespace ETSI.ARF.OpenAPI.WorldStorage +{ + 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; + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/WorldStorageClient.cs b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/WorldStorageClient.cs new file mode 100644 index 0000000..4cd88a7 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/OpenAPI/WorldStorage/WorldStorageClient.cs @@ -0,0 +1,26 @@ +using System.Collections; +using System.Collections.Generic; + +namespace ETSI.ARF.OpenAPI.WorldStorage +{ + /// + /// Catch the pre/pos request methods from the autogenerated classes + /// + public partial class WorldStorageClient : BaseClient + { + partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldStorage.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url) + { + _prepareRequest(client, request, url); + } + + partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldStorage.IHttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder) + { + // do something... + } + + partial void ProcessResponse(ETSI.ARF.OpenAPI.WorldStorage.IHttpClient client, System.Net.Http.HttpResponseMessage response) + { + _processResponse(client, response); + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/Program.cs b/wrapper/c#/WorldAnalysisWrapper/Program.cs new file mode 100644 index 0000000..e3db13b --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/Program.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; + +namespace WorldAnalysisWrapper +{ + class Program + { + static public bool isDev = true; + static bool isDev_Secure = true; + static bool isLocalNetwork = true; + + static public WorldAnalysis module; + + // + // Web server (World Storage) + // + static private string webserver_url; + + // + // WebSocket server (World Analysis) + // + static private string websocket_url; + + static void Main(string[] args) + { + // + // Web server + // + // for production + webserver_url = "https://etsi.hhi.fraunhofer.de"; // public + + // + // WebSockets + // + + if (isDev) + { + // For development + + if (isDev_Secure) websocket_url = "wss://localhost:44301/ws"; // secure + else websocket_url = "ws://localhost:61788/ws"; + } + else + { + // For production + + if (isLocalNetwork) websocket_url = "ws://192.168.20.29:8084/ws"; // local network + else websocket_url = "wss://analysis.etsi.hhi.fraunhofer.de"; // public + } + + + Console.WriteLine("Starting World Analysis module..."); + module = new WorldAnalysis(webserver_url, websocket_url, 2); + module.Start(); + + while (module.isRunning()) + { + // don't exit here, wait! + } + } + } +} diff --git a/wrapper/c#/WorldAnalysisWrapper/REST/AdminRequest.cs b/wrapper/c#/WorldAnalysisWrapper/REST/AdminRequest.cs new file mode 100644 index 0000000..47665bf --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/REST/AdminRequest.cs @@ -0,0 +1,65 @@ +// +// 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 ETSI.ARF.OpenAPI; +using ETSI.ARF.OpenAPI.WorldStorage; + +namespace ETSI.ARF.WorldStorage.REST +{ + public class AdminRequest : RequestBase + { + // + // Wrapper for the endpoints + // + static public string PingSync(WorldStorageServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + string response = apiClient.GetPing(); + return response; + } + + static public string AdminSync(WorldStorageServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + string response = apiClient.GetAdmin(); + return response; + } + + + static public string VersionSync(WorldStorageServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + string response = apiClient.GetVersion(); + return response; + } + } +} diff --git a/wrapper/c#/WorldAnalysisWrapper/REST/RelocalizationInformationRequest.cs b/wrapper/c#/WorldAnalysisWrapper/REST/RelocalizationInformationRequest.cs new file mode 100644 index 0000000..2b96de8 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/REST/RelocalizationInformationRequest.cs @@ -0,0 +1,204 @@ +// +// 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: September 2024 +// +using System; +using System.Collections.Generic; +using ETSI.ARF.OpenAPI.WorldStorage; +using ETSI.ARF.OpenAPI; +using Newtonsoft.Json; + +namespace ETSI.ARF.WorldStorage.REST +{ + public class RelocalizationInformationRequest : RequestBase + { + static public RelocalizationInformations GetRelocalizationInformation(WorldStorageServer ws, List uuids, List modes, List capabilities) + { + return GetRelocalizationInformationSync(ws, uuids, modes, capabilities); + } + + static public RelocalizationInformations GetRelocalizationInformationSync(WorldStorageServer ws, List uuids, List modes, List capabilities) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new MyWorldStorageClient(httpClient); + + + List anonymous = new List(); + for (int i = 0; i < uuids.Count; i++) + { + // Check same size or give anonymous as parameter? + UuidAndMode newOne = new UuidAndMode(); + newOne.Uuid = uuids[i]; + newOne.Mode = modes[i]; + anonymous.Add(newOne); + } + + RelocalizationInformations ro = apiClient.GetRelocalizationInformation(token, anonymous, capabilities); + return ro; + } + } +} + +namespace ETSI.ARF.OpenAPI.WorldStorage +{ + public partial class WorldStorageClient + { + public ETSI.ARF.OpenAPI.WorldStorage.IHttpClient GetHttpClient() + { + return _httpClient; + } + + public string ConvertToString_(object value, System.Globalization.CultureInfo cultureInfo) + { + return ConvertToString(value, cultureInfo); + } + + + public void PrepareRequest_(ETSI.ARF.OpenAPI.WorldStorage.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url) + { + PrepareRequest(client, request, url); + } + public void PrepareRequest_(ETSI.ARF.OpenAPI.WorldStorage.IHttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder) + { + PrepareRequest(client, request, urlBuilder); + } + public void ProcessResponse_(ETSI.ARF.OpenAPI.WorldStorage.IHttpClient client, System.Net.Http.HttpResponseMessage response) + { + ProcessResponse(client, response); + } + } + + // Custom client to be able to properly serialize objects in the query string + public partial class MyWorldStorageClient : WorldStorageClient + { + public MyWorldStorageClient(IHttpClient httpClient) : base(httpClient) + { + } + + /// + /// Override Nswag generated function to access relocalization information : deal with parameters in the query + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public override async System.Threading.Tasks.Task GetRelocalizationInformationAsync(string token, System.Collections.Generic.IEnumerable uuids, System.Collections.Generic.IEnumerable capabilities, System.Threading.CancellationToken cancellationToken) + { + if (uuids == null) + throw new System.ArgumentNullException("uuids"); + + if (capabilities == null) + throw new System.ArgumentNullException("capabilities"); + + var client_ = GetHttpClient(); + 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)); + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + + urlBuilder_.Append("relocalizationInformation"); + urlBuilder_.Append('?'); + foreach (var item_ in uuids) { urlBuilder_.Append(System.Uri.EscapeDataString("uuids")).Append('=').Append(System.Uri.EscapeDataString(JsonConvert.SerializeObject(item_))).Append('&'); } + foreach (var item_ in capabilities) { urlBuilder_.Append(System.Uri.EscapeDataString("capabilities")).Append('=').Append(System.Uri.EscapeDataString(JsonConvert.SerializeObject(item_))).Append('&'); } + urlBuilder_.Length--; + + 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("Invalid UUID supplied.", 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, could not find UUID in database.", 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(); + } + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/REST/RequestBase.cs b/wrapper/c#/WorldAnalysisWrapper/REST/RequestBase.cs new file mode 100644 index 0000000..c1012fe --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/REST/RequestBase.cs @@ -0,0 +1,129 @@ +// +// 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: September 2024 +// + +using System; +using System.Threading.Tasks; +using System.Collections.Generic; + +using ETSI.ARF.OpenAPI; +using ETSI.ARF.OpenAPI.WorldStorage; + +namespace ETSI.ARF.WorldStorage.REST +{ + public class RequestBase // where T : Trackable, WorldAnchor, WorldLink + { + static protected WorldStorageServer wsServer; + static protected WorldStorageClient apiClient; + + static protected string token = "dev"; // only for development + + // 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; + Console.WriteLine($"[REST] Server Response = {o.result.ToString()} (ID={o.transactionId}, Msg={o.message})"); + + o.callback?.Invoke(o); + } + else Console.WriteLine("[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; + Console.WriteLine($"[REST] " + o.result.GetType()); + + // Adapation for new API, SylR + int cnt = 0; + + // Remember the new list + listOfObjects.Clear(); + + // Get the new list + if (o.result.GetType() == typeof(TrackablesResponse)) + { + Console.WriteLine($"[REST] TR"); + TrackablesResponse res = o.result as TrackablesResponse; + foreach (var item in res.Trackables) + { + listOfObjects.Add(item.UUID, item); + cnt++; + } + } + else if (o.GetType() == typeof(WorldAnchorsResponse)) + { + WorldAnchorsResponse res = o as WorldAnchorsResponse; + foreach (var item in res.WorldAnchors) + { + listOfObjects.Add(item.UUID, item); + cnt++; + } + } + else if (o.GetType() == typeof(WorldLinksResponse)) + { + WorldLinksResponse res = o as WorldLinksResponse; + foreach (var item in res.WorldLinks) + { + listOfObjects.Add(item.UUID, item); + cnt++; + } + } + + Console.WriteLine($"[REST] Server Response = Got {cnt} entrie(s) (ID={o.transactionId}, Msg={o.message})"); + o.callback?.Invoke(o); + } + else Console.WriteLine("[REST] OpenAPI Timeout!"); + } + + static protected void old_OnReceiveListOfObjects(Task> t, object id) where TObj : IModel + { + if (t.IsCompleted) + { + ResponseObject> o = (ResponseObject>)id; + o.responseTime = DateTime.Now; + o.result = t.Result; + Console.WriteLine($"[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 Console.WriteLine("[REST] OpenAPI Timeout!"); + } + + } +} diff --git a/wrapper/c#/WorldAnalysisWrapper/REST/TrackableRequest.cs b/wrapper/c#/WorldAnalysisWrapper/REST/TrackableRequest.cs new file mode 100644 index 0000000..3ac53cc --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/REST/TrackableRequest.cs @@ -0,0 +1,89 @@ +// +// 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 +// + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +using ETSI.ARF.OpenAPI; +using ETSI.ARF.OpenAPI.WorldStorage; + +namespace ETSI.ARF.WorldStorage.REST +{ + public class TrackableRequest : RequestBase + { + // + // Wrapper for the endpoints + // + static public Trackable GetTrackableSync(WorldStorageServer ws, Guid UUID) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + //Console.WriteLine($"[REST] Request Trackable {UUID}..."); + return apiClient.GetTrackableById(token, UUID); + } + + static public List GetTrackablesSync(WorldStorageServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine("[REST] Request Trackables..."); + return apiClient.GetTrackables(token).Trackables as List; + } + + static public string CreateTrackableSync(WorldStorageServer ws, Trackable trackable) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + // Add some management stuffs + if (trackable.UUID == Guid.Empty) trackable.UUID = Guid.NewGuid(); + if (trackable.CreatorUUID == Guid.Empty) trackable.CreatorUUID = System.Guid.Parse("8fb169e2-8910-4cd5-a8f9-b7abff38d013"); + + Console.WriteLine($"[REST] Create Trackable {trackable.UUID}..."); + return apiClient.AddTrackable(token, trackable).Message; + } + + static public string UpdateTrackableSync(WorldStorageServer ws, Trackable trackable) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine($"[REST] Update Trackable {trackable.UUID}..."); + return apiClient.ModifyTrackable(token, trackable).Message; + } + + static public string DeleteTrackableSync(WorldStorageServer ws, Guid UUID) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine($"[REST] Delete Trackable {UUID}..."); + return apiClient.DeleteTrackable(token, UUID).Message; + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/REST/WorldAnchorRequest.cs b/wrapper/c#/WorldAnalysisWrapper/REST/WorldAnchorRequest.cs new file mode 100644 index 0000000..733a712 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/REST/WorldAnchorRequest.cs @@ -0,0 +1,91 @@ +// +// 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.Collections.Generic; +using System.Threading.Tasks; + +using ETSI.ARF.OpenAPI; +using ETSI.ARF.OpenAPI.WorldStorage; + +//#if UNITY_EDITOR +namespace ETSI.ARF.WorldStorage.REST +{ + public class WorldAnchorRequest : RequestBase + { + // + // Wrapper for the endpoints + // + static public WorldAnchor GetWorldAnchorSync(WorldStorageServer ws, Guid UUID) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + //Console.WriteLine($"[REST] Request WorldAnchor {UUID}..."); + return apiClient.GetWorldAnchorById(token, UUID); + } + + static public List GetWorldAnchorsSync(WorldStorageServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine("[REST] Request WorldAnchors..."); + return apiClient.GetWorldAnchors(token).WorldAnchors as List; + } + + static public string CreateWorldAnchorSync(WorldStorageServer ws, WorldAnchor worldAnchor) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + // Add some management stuffs + if (worldAnchor.UUID == Guid.Empty) worldAnchor.UUID = Guid.NewGuid(); + if (worldAnchor.CreatorUUID == Guid.Empty) worldAnchor.CreatorUUID = System.Guid.Parse("8fb169e2-8910-4cd5-a8f9-b7abff38d013"); + + //Console.WriteLine($"[REST] Create WorldAnchor {worldAnchor.UUID}..."); + return apiClient.AddWorldAnchor(token, worldAnchor).Message; + } + + static public string UpdateWorldAnchorSync(WorldStorageServer ws, WorldAnchor worldAnchor) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine($"[REST] Update WorldAnchor {worldAnchor.UUID}..."); + return apiClient.ModifyWorldAnchor(token, worldAnchor).Message; + } + + static public string DeleteWorldAnchorSync(WorldStorageServer ws, Guid UUID) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine($"[REST] Delete WorldAnchor {UUID}..."); + return apiClient.DeleteWorldAnchor(token, UUID).Message; + } + } +} +//#endif \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/REST/WorldLinkRequest.cs b/wrapper/c#/WorldAnalysisWrapper/REST/WorldLinkRequest.cs new file mode 100644 index 0000000..ed604e7 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/REST/WorldLinkRequest.cs @@ -0,0 +1,90 @@ +// +// 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 +// + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +using ETSI.ARF.OpenAPI; +using ETSI.ARF.OpenAPI.WorldStorage; + +namespace ETSI.ARF.WorldStorage.REST +{ + public class WorldLinkRequest : RequestBase + { + // + // Wrapper for the endpoints + // + static public WorldLink GetWorldLinkSync(WorldStorageServer ws, Guid UUID) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + //Console.WriteLine($"[REST] Request WorldLink {UUID}..."); + return apiClient.GetWorldLinkById(token, UUID); + } + + static public List GetWorldLinksSync(WorldStorageServer ws) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine("[REST] Request WorldLinks..."); + return apiClient.GetWorldLinks(token).WorldLinks as List; + } + + static public string CreateWorldLinkSync(WorldStorageServer ws, WorldLink worldLink) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + // Add some management stuffs + if (worldLink.UUID == Guid.Empty) worldLink.UUID = Guid.NewGuid(); + if (worldLink.CreatorUUID == Guid.Empty) worldLink.CreatorUUID = System.Guid.Parse("8fb169e2-8910-4cd5-a8f9-b7abff38d013"); + + //Console.WriteLine($"[REST] Create WorldLink {worldLink.UUID}..."); + return apiClient.AddWorldLink(token, worldLink).Message; + } + + static public string UpdateWorldLinkSync(WorldStorageServer ws, WorldLink worldLink) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine($"[REST] Update WorldLink {worldLink.UUID}..."); + return apiClient.ModifyWorldLink(token, worldLink).Message; + } + + static public string DeleteWorldLinkSync(WorldStorageServer ws, Guid UUID) + { + wsServer = ws; + var httpClient = new BasicHTTPClient(ws.URI); + apiClient = new WorldStorageClient(httpClient); + + Console.WriteLine($"[REST] Delete WorldLink {UUID}..."); + return apiClient.DeleteWorldLink(token, UUID).Message; + } + } +} +//#endif \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/WorldAnalysis.cs b/wrapper/c#/WorldAnalysisWrapper/WorldAnalysis.cs new file mode 100644 index 0000000..8bd9acf --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/WorldAnalysis.cs @@ -0,0 +1,624 @@ +#define SIMULATE_ANALYSIS +//#define ALWAYS_CALL_REST_API + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using ETSI.ARF.OpenAPI.WorldStorage; +using ETSI.ARF.OpenAPI.WorldAnalysis; + +using ETSI.ARF.WorldStorage; +using ETSI.ARF.WorldStorage.REST; +using Newtonsoft.Json; +using System.Numerics; + + +namespace WorldAnalysisWrapper +{ + class WorldAnalysis + { + static public List trackables; + static public List anchors; + static public List links; + + float randomScale = 0.01f; // m + + public class SubscriptionInfo + { + public Guid uuidSub; // id of subscription (id is defined by the WA server) + + public SubscriptionSingleRequest subscription; + public Mode_WorldAnalysis mode; + + // Monitor + public int sendCnt; + + // Results + public Pose pose; + public Trackable trackable; + public ETSI.ARF.OpenAPI.WorldStorage.Transform3D transform3d; + + public Trackable extra; + } + + private int simulationDelay = 500; // ms + private string modulename = ""; + private SubscriptionInfo lastSubscription; + private Dictionary subscriptions = new Dictionary(); + + // WS + private ETSI.ARF.OpenAPI.WorldStorage.RelocalizationInformations relocInfos; + + private bool sendPose = false; + + // Credencials + private WorldStorageServer server; + private WorldStorageUser user; + private bool isRegistered = false; + + private WebSocketSharp.WebSocket webSocket; + private bool websocketConnected = false; + private string webserver_url; + private string websocket_url; + + // Capabilities of the module + private ETSI.ARF.OpenAPI.WorldStorage.EncodingInformationStructure enc = null; + private List capabilities = new List(); + + + public WorldAnalysis(string webserver, string websocket, int mode) + { + webserver_url = webserver; + websocket_url = websocket; + ETSI.ARF.OpenAPI.WorldStorage.Capability cap = null; + + // Simulate a World Analysis module + if (mode == 1) + { + modulename = "HHI-ImageTracker"; + + enc = new ETSI.ARF.OpenAPI.WorldStorage.EncodingInformationStructure() + { + Version = "HHI-0.1.0", // version + DataFormat = ETSI.ARF.OpenAPI.WorldStorage.EncodingInformationStructureDataFormat.OTHER // type/company? + }; + + // Capability #1 + cap = new ETSI.ARF.OpenAPI.WorldStorage.Capability(); + cap.TrackableType = ETSI.ARF.OpenAPI.WorldStorage.TrackableType.IMAGE_MARKER; + cap.EncodingInformation = enc; + cap.Framerate = 30; + cap.Latency = 0.05; + cap.Accuracy = 01; + capabilities.Add(cap); + } + else if (mode == 2) + { + modulename = "HHI-MapAndMesh-Detection"; + + enc = new ETSI.ARF.OpenAPI.WorldStorage.EncodingInformationStructure() + { + Version = "HHI-0.1.2", // version + DataFormat = ETSI.ARF.OpenAPI.WorldStorage.EncodingInformationStructureDataFormat.OTHER // type/company? + }; + + // Capability #1 + cap = new ETSI.ARF.OpenAPI.WorldStorage.Capability(); + cap.TrackableType = ETSI.ARF.OpenAPI.WorldStorage.TrackableType.MAP; + cap.EncodingInformation = enc; + cap.Framerate = 6; + cap.Latency = 0.5; + cap.Accuracy = 0.8; + capabilities.Add(cap); + + // Capability #2 + cap = new ETSI.ARF.OpenAPI.WorldStorage.Capability(); + cap.TrackableType = ETSI.ARF.OpenAPI.WorldStorage.TrackableType.MESH; + cap.EncodingInformation = enc; + cap.Framerate = 8; + cap.Latency = 0.5; + cap.Accuracy = 0.9; + capabilities.Add(cap); + } + + // + // REST + // + + // Test the REST World Storage API + server = new WorldStorageServer(); + server.serverName = "HHI"; + server.company = "Fraunhofer HHI"; + server.basePath = webserver_url; + server.port = 0; + + user = new WorldStorageUser(); + user.userName = "HHI"; + user.company = "Fraunhofer HHI"; + user.UUID = "b4380ebb-eb56-41c2-aeaa-146315a39606"; + + // Some admin stuffs + string res = AdminRequest.PingSync(server); + Console.WriteLine("Sending 'ping', got response: " + res); + + res = AdminRequest.AdminSync(server); + Console.WriteLine("Sending 'admin', got response: " + res); + + res = AdminRequest.VersionSync(server); + Console.WriteLine("Sending 'version', got response: " + res); + + // + // Reloc test + // + //RelocTest(); + } + + ~WorldAnalysis() + { + WebSocketClient_Close(); + } + + + public bool isRunning() + { + return websocketConnected && webSocket.ReadyState == WebSocketSharp.WebSocketState.Open; + } + + public void Start() + { + // + // Get all lists from the World Storage REST server (test) + // + if (Program.isDev) + { + // Get trackables + trackables = TrackableRequest.GetTrackablesSync(server); + Console.WriteLine("Querying Trackables: got list with " + trackables.Count + " items: "); + foreach (var t in trackables) + { + Console.WriteLine($"\tUUID: { t.UUID } Name: { t.Name } (Type: { t.TrackableType })"); + } + + // Get anchors + anchors = WorldAnchorRequest.GetWorldAnchorsSync(server); + Console.WriteLine("Querying World Anchors: got list with " + anchors.Count + " items: "); + foreach (var a in anchors) + { + Console.WriteLine($"\tUUID: { a.UUID } Name: { a.Name }"); + } + + // Get links + links = WorldLinkRequest.GetWorldLinksSync(server); + Console.WriteLine("Querying World Links: got list with " + links.Count + " items: "); + foreach (var l in links) + { + //Console.WriteLine($"\tUUID: { l.UUID } From: { l.UUIDFrom } To: { l.UUIDTo }"); + Console.WriteLine($"\tUUID: { l.UUID }"); + } + } + + // + // Start the WebSockets (World Analysis server) + // + webSocket = new WebSocketSharp.WebSocket(websocket_url); + + Console.WriteLine($"[WS] Try connecting the WA server as { modulename}..."); + + // Define standard callbacks + webSocket.OnOpen += (sender, e) => + { + Console.WriteLine("[WS] Connected"); + websocketConnected = true; + + Console.WriteLine($"[WS] Send registration request to server for {modulename}"); + webSocket.Send($"RegisterModule={modulename}"); + }; + webSocket.OnClose += (sender, e) => + { + Console.WriteLine("[WS] Disconnected"); + websocketConnected = false; + }; + webSocket.OnMessage += (sender, e) => WebSocketClient_OnReceive(e.Data); // main event + webSocket.OnError += (sender, e) => Console.WriteLine("[WS] Websocket error!"); + webSocket.Connect(); + } + + public string ToJson(object o) + { + return JsonConvert.SerializeObject(o, Formatting.Indented); + } + + private void WebSocketClient_Close() + { + if (websocketConnected) + { + webSocket.Send("UnregisterModule=" + modulename); + webSocket.Close(); + webSocket = null; + } + } + + public void WebSocketClient_Send(string msg) + { + Console.WriteLine($"[WS] Send to WA server: {msg}"); + webSocket?.Send(msg); + } + + public void _webSocketClient_SendPose(SubscriptionInfo si) + { + //Console.WriteLine($"[WS] #{sendCnt } Send to WA server: {msg}"); + si.sendCnt++; + Console.WriteLine($"[WS] Send pose #{ si.sendCnt } to WA server (ID: { si.uuidSub })"); + webSocket?.Send("NewPose=" + si.pose.ToJson()); + } + + private void WebSocketClient_SendCapabilities() + { + foreach (var cap in capabilities) + { + string cap_json = ToJson(cap); + WebSocketClient_Send("Capabilities=" + cap_json); + } + } + + public void WebSocketClient_SendPose(SubscriptionInfo info) + { + Guid uuidTarget = info.subscription.Target; + ETSI.ARF.OpenAPI.WorldStorage.Transform3D matrix = null; + + Trackable tr = null; + WorldAnchor an = null; + +#if ALWAYS_CALL_REST_API + try + { + tr = TrackableRequest.GetTrackableSync(server, uuidTarget); + matrix = tr.LocalCRS; + } + catch + { + try + { + an = WorldAnchorRequest.GetWorldAnchorSync(server, uuidTarget); + matrix = an.LocalCRS; + } + catch { } + } +#else + foreach (var item in trackables) + { + if (item.UUID == uuidTarget) + { + tr = item; + matrix = tr.LocalCRS; + break; + } + } + + foreach (var item in anchors) + { + if (item.UUID == uuidTarget) + { + an = item; + matrix = an.LocalCRS; + break; + } + } + +#endif + if (matrix == null) + { + WebSocketClient_Send("Error! Pose cannot be calculated (missing trackable or world-anchor)"); + return; + } + + // + // Calculate/Get the current pose of the uuid + // + Pose pose = new Pose(); + pose.Uuid = uuidTarget; + pose.Mode = info.mode; + pose.SubscriptionUrl = websocket_url; + + MatrixPoseValue pv = new MatrixPoseValue(); + pv.Type = PoseValueType.MATRIX; + pv.Unit = ETSI.ARF.OpenAPI.WorldAnalysis.UnitSystem.M; + + // + // Get the reloc infos ? + // + if (info.trackable == null) + { +#if !SIMULATE_ANALYSIS + List uuids = new List { uuidTarget }; + List modes = new List { Mode_WorldStorage.REQUEST_TO_TRACKABLES }; + + RelocalizationInformations relocInfos = RelocalizationInformationRequest.GetRelocalizationInformationSync(server, uuids, modes, capabilities); + foreach (var i in relocInfos.RelocInfo) + { + if (i.RelocObjects.Count > 0) + { + RelocObjects ro = i.RelocObjects.ToList()[0]; + info.trackable = ro.Trackable; + info.transform3d = ro.Transform3D; + break; + } + } +#else + // workaround - validation/simulation (STF669) + //if (info.trackable == null) + { + foreach (var item in links) + { + if (item.UUIDTo == uuidTarget) + { +#if ALWAYS_CALL_REST_API + try + { + Trackable t = TrackableRequest.GetTrackableSync(server, item.UUIDFrom); + info.trackable = t; + info.transform3d = t.LocalCRS; + } + catch { } +#else + foreach (var t in trackables) + { + if (t.UUID == item.UUIDFrom) + { + info.trackable = t; + info.transform3d = t.LocalCRS; + break; + } + } +#endif + } + + + if (item.UUIDFrom == uuidTarget) + { +#if ALWAYS_CALL_REST_API + try + { + Trackable t = TrackableRequest.GetTrackableSync(server, item.UUIDTo); + info.trackable = t; + info.transform3d = t.LocalCRS; + } + catch { } +#else + foreach (var t in trackables) + { + if (t.UUID == item.UUIDTo) + { + info.trackable = t; + info.transform3d = t.LocalCRS; + break; + } + } +#endif + } + } +#endif + } + } + + if (info.trackable != null) + { + pose.InstructionInfo = "Workaround for STF669 (parent; w/no RelocInfo)"; + + // + // Direct parent request (without calling RelocInfo!) + // + ETSI.ARF.OpenAPI.WorldStorage.Transform3D parentMatrix = info.transform3d; + + Matrix4x4 _parentMatrix = parentMatrix.Matrix(); + Matrix4x4 _matrix = matrix.Matrix(); // target + Matrix4x4 m = _matrix * _parentMatrix; // target * parent + + // todo: Invert? + + // Transpose one matrix to the other + pv.Transform = new ETSI.ARF.OpenAPI.WorldAnalysis.Transform3D() + { + m.M11, m.M12, m.M13, m.M14, + m.M21, m.M22, m.M23, m.M24, + m.M31, m.M32, m.M33, m.M34, + m.M41, m.M42, m.M43, m.M44, + }; + } + else + { + pose.InstructionInfo = "Workaround for STF669 (identity pose)"; + + // + // Return identiy + // + + // Transpose one matrix to the other + pv.Transform = new ETSI.ARF.OpenAPI.WorldAnalysis.Transform3D() + { + matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5], matrix[6], matrix[7], + matrix[8], matrix[9], matrix[10], matrix[11], + matrix[12], matrix[13], matrix[14], matrix[15] + }; + } + +#if SIMULATE_ANALYSIS + // Simulate jitter in position + Random r = new Random(); + pv.Transform[3] = pv.Transform[3] + (float)r.NextDouble() * randomScale; + pv.Transform[7] = pv.Transform[7] + (float)r.NextDouble() * randomScale; + pv.Transform[11] = pv.Transform[11] + (float)r.NextDouble() * randomScale; +#endif + + // Set the new pose + pose.Value = pv; + info.pose = pose; + + //Console.WriteLine($"[WS] Found pose for uuid={ uuidTarget }..."); + + // Send the pose + _webSocketClient_SendPose(info); + } + + // Events + private void WebSocketClient_OnReceive(string serverMessage) + { + if (serverMessage.Contains("You are now registered")) + { + isRegistered = true; + Console.WriteLine($"[WS] {serverMessage }"); + //Console.WriteLine($"[WS] Registration of {modulename} was succesfull."); + + WebSocketClient_SendCapabilities(); + + Console.WriteLine($"[WS] Entering the websockets main loop..."); + } + else if (isRegistered) + { + if (serverMessage == "UnregisterModuleOK") + { + // Stop the loop and disconnect the server + sendPose = false; + } + else if (serverMessage.StartsWith("SubscribeSimulatedPose=")) // simulation for STFT669 workaround / SylR + { + Guid uuidSub = Guid.Parse(serverMessage.Split('=')[1]); + string json_subPose = serverMessage.Split('=')[2]; + string _trackableUUID = serverMessage.Split('=')[3]; // extra field + + SubscriptionInfo si = new SubscriptionInfo() + { + uuidSub = uuidSub, + }; + si.subscription = JsonConvert.DeserializeObject(json_subPose); + si.mode = si.subscription.Mode; + Console.WriteLine($"[WS] New pose subscription (simulation) for UUID={ si.subscription.Target }"); + + // receive also the parent object (trackable) from the client? + si.extra = TrackableRequest.GetTrackableSync(server, Guid.Parse(_trackableUUID)); + Console.WriteLine($"Found trackable (UUID={ _trackableUUID }) for simulation: name = { si.extra.Name }"); + + // Update lists + if (!subscriptions.ContainsKey(si.subscription.Target)) subscriptions.Add(si.subscription.Target, si); + if (subscriptions.Count > 0) sendPose = true; + + lastSubscription = si; + + WebSocketClient_Send("PoseIsNowSubscribed"); + } + else if (serverMessage.StartsWith("SubscribePose=")) + { + Guid uuidSub = Guid.Parse(serverMessage.Split('=')[1]); + string json_subPose = serverMessage.Split('=')[2]; + + SubscriptionInfo si = new SubscriptionInfo() + { + uuidSub = uuidSub, + }; + si.subscription = JsonConvert.DeserializeObject(json_subPose); + si.mode = si.subscription.Mode; + Console.WriteLine($"[WS] New pose subscription for UUDI={ si.subscription.Target }"); + + // Update lists + if (!subscriptions.ContainsKey(si.subscription.Target)) subscriptions.Add(si.subscription.Target, si); + if (subscriptions.Count > 0) sendPose = true; + + WebSocketClient_Send("PoseIsNowSubscribed"); + } + else if (serverMessage.StartsWith("UnsubscribePose=")) + { + Guid uuidSub = Guid.Parse(serverMessage.Split('=')[1]); + + // Update lists + foreach (var si in subscriptions.Values) + { + if (si.uuidSub == uuidSub) + { + // remove + subscriptions.Remove(si.subscription.Target); + } + } + if (subscriptions.Count == 0) sendPose = false; + + WebSocketClient_Send("PoseIsNowUnsubscribed"); + } + else if (serverMessage.StartsWith("ConfigureFramerate=")) + { + // todo: check if the framerate is authorized, otherwise send an error back + int fps = int.Parse(serverMessage.Split('=')[1]); + + // Not implemented + WebSocketClient_Send("Error=Server: Setting framerate is not Implemented!"); + } + // + // Special msg for simulating a global user's position + // + else if (serverMessage.StartsWith("CurrentUserPose=")) + { + string _userPose = serverMessage.Split('=')[1]; + Pose userPose = JsonConvert.DeserializeObject(_userPose); + Console.WriteLine($"New user current position is: { userPose.ToJson() }"); + } + // + // Pose requests + // + else if (serverMessage.StartsWith("GetPose=")) // 2 args + { + Guid uuidTarget = Guid.Parse(serverMessage.Split('=')[1]); + Mode_WorldAnalysis mode = Mode_WorldAnalysis.TRACKABLES_TO_DEVICE; // serverMessage.split(':')[2]; + + SubscriptionSingleRequest ss = new SubscriptionSingleRequest(); + ss.Target = uuidTarget; + SubscriptionInfo si = new SubscriptionInfo() + { + uuidSub = Guid.Empty, // without subscription! + mode = mode + }; + WebSocketClient_SendPose(si); + } + else if (serverMessage == "PoseStop") + { + //SetColor(Color.yellow); + } + else if (serverMessage == "RequestNextPose" && sendPose == true) + { + System.Threading.Thread.Sleep(simulationDelay); + foreach (var si in subscriptions.Values) + { + WebSocketClient_SendPose(si); + } + } + } + } + + private void RelocTest() + { + RelocalizationInformation relocInfo = null; + Guid tr_uuid = new Guid("6a555bf1-fceb-4a8c-a648-191087ad1b21"); + + Guid an_uuid = new Guid("da1284ee-e895-4434-906f-5599b92ab51a"); + List uuids = new List + { + an_uuid + }; + + List modes = new List + { + Mode_WorldStorage.TRACKABLES_TO_REQUEST + }; + + /// Collect relocalization information + relocInfos = RelocalizationInformationRequest.GetRelocalizationInformation(server, uuids, modes, capabilities); + if (relocInfos == null) + { + Console.WriteLine("ETSI ARF GetRelocalizationInformation : request response is null"); + } + relocInfo = relocInfos.RelocInfo.First(); //Only one uuid requested + Console.WriteLine($"Found Reloc: num = { relocInfo.RelocObjects.Count }"); + } + } +} diff --git a/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.deps.json b/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.deps.json new file mode 100644 index 0000000..176fead --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.deps.json @@ -0,0 +1,57 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v5.0", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v5.0": { + "WorldAnalysisWrapper/1.0.0": { + "dependencies": { + "Newtonsoft.Json": "13.0.3", + "websocket-sharp-latest": "1.0.2" + }, + "runtime": { + "WorldAnalysisWrapper.dll": {} + } + }, + "Newtonsoft.Json/13.0.3": { + "runtime": { + "lib/netstandard2.0/Newtonsoft.Json.dll": { + "assemblyVersion": "13.0.0.0", + "fileVersion": "13.0.3.27908" + } + } + }, + "websocket-sharp-latest/1.0.2": { + "runtime": { + "lib/netstandard2.0/websocket-sharp-latest.dll": { + "assemblyVersion": "1.0.2.0", + "fileVersion": "1.0.2.0" + } + } + } + } + }, + "libraries": { + "WorldAnalysisWrapper/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Newtonsoft.Json/13.0.3": { + "type": "package", + "serviceable": true, + "sha512": "sha512-HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==", + "path": "newtonsoft.json/13.0.3", + "hashPath": "newtonsoft.json.13.0.3.nupkg.sha512" + }, + "websocket-sharp-latest/1.0.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-1dLVMsURAIp9YCc43hW9x3HgSw5hWZU+w3YRTWv7HOopm2N4+noa+rCp1VKTEmmRkpJja8oNYmbrDYFZRi7kCw==", + "path": "websocket-sharp-latest/1.0.2", + "hashPath": "websocket-sharp-latest.1.0.2.nupkg.sha512" + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.runtimeconfig.dev.json b/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.runtimeconfig.dev.json new file mode 100644 index 0000000..657b9ba --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.runtimeconfig.dev.json @@ -0,0 +1,9 @@ +{ + "runtimeOptions": { + "additionalProbingPaths": [ + "C:\\Users\\renault\\.dotnet\\store\\|arch|\\|tfm|", + "C:\\Users\\renault\\.nuget\\packages", + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ] + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.runtimeconfig.json b/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.runtimeconfig.json new file mode 100644 index 0000000..a8e7e82 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/bin/Debug/net5.0/WorldAnalysisWrapper.runtimeconfig.json @@ -0,0 +1,9 @@ +{ + "runtimeOptions": { + "tfm": "net5.0", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "5.0.0" + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/.NETCoreApp,Version=v5.0.AssemblyAttributes.cs b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/.NETCoreApp,Version=v5.0.AssemblyAttributes.cs new file mode 100644 index 0000000..2f7e5ec --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/.NETCoreApp,Version=v5.0.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v5.0", FrameworkDisplayName = "")] diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.AssemblyInfo.cs b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.AssemblyInfo.cs new file mode 100644 index 0000000..c3bdbfc --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.AssemblyInfo.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; + +[assembly: System.Reflection.AssemblyCompanyAttribute("WorldAnalysisWrapper")] +[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] +[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] +[assembly: System.Reflection.AssemblyProductAttribute("WorldAnalysisWrapper")] +[assembly: System.Reflection.AssemblyTitleAttribute("WorldAnalysisWrapper")] +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] + +// Von der MSBuild WriteCodeFragment-Klasse generiert. + diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.AssemblyInfoInputs.cache b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.AssemblyInfoInputs.cache new file mode 100644 index 0000000..4ee14d3 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.AssemblyInfoInputs.cache @@ -0,0 +1 @@ +5adaa75f34a2faff0b4c6795b47ec0ed127d4d1e diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.GeneratedMSBuildEditorConfig.editorconfig b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.GeneratedMSBuildEditorConfig.editorconfig new file mode 100644 index 0000000..d7e2983 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.GeneratedMSBuildEditorConfig.editorconfig @@ -0,0 +1,8 @@ +is_global = true +build_property.TargetFramework = net5.0 +build_property.TargetPlatformMinVersion = +build_property.UsingMicrosoftNETSdkWeb = +build_property.ProjectTypeGuids = +build_property.PublishSingleFile = +build_property.IncludeAllContentForSelfExtract = +build_property._SupportedPlatformList = Android,iOS,Linux,macOS,Windows diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.assets.cache b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.assets.cache new file mode 100644 index 0000000000000000000000000000000000000000..885baf3f86d322d92d3ee5361c565530318aa6bc GIT binary patch literal 1116 zcmWIWc6a1rU|=}JefNTk+VnrQ|EoUpcZW<>pXt$Z@6-ZAUY40(fA5<3+?tsI4wx7i z7~-6*VnT~ki;824Qu7i^b4p_L@=DWFOJWKVld}`kQ;TErQp-#7^NRD+O7yad^Yda1 zjr9!ljAL>#lVb8xONvVp^HLIvQjGKrV*C&)y^8bm^ipziV74(bFff3u1MyiH7#LU? z7#Ok%T3w!+RGgojom!$>oRL^mpqrCel3H96W2k4KXN1iG*wrJugAM8qb_NCpkUKcw zfq`Iv+{usT7m%zNcH==(AT5dny#rF|?raqkP?Voul$fjFmYI`Ute|RSQDI@GYGfJX zo0(jc4+?07u*~Ap#2kg-lG2pS{Fva3#G=%c7{5~Y)RF*bw1CV4VUW!O;CiN@N53$# zbXaOpab|uV8zdv}1|*hbc;-RlMJ*>YNgp*^=%XbiP;$hPlvu$jTNjHN*pnW}C{D-X z;?$Cm%7Rq(qSCyQ%-mE?=lp_7pZw&+9F~%z(o`Xr)Z&uNyu^~s{Jh}OB#?83u{cQz Gw-o?D_e%5t literal 0 HcmV?d00001 diff --git a/wrapper/arf specific.txt b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.CopyComplete similarity index 100% rename from wrapper/arf specific.txt rename to wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.CopyComplete diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.CoreCompileInputs.cache b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.CoreCompileInputs.cache new file mode 100644 index 0000000..f41a2d4 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +6575fad8fbac49cf14aae79f887d6a378d3d9299 diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.FileListAbsolute.txt b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..de6ab55 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csproj.FileListAbsolute.txt @@ -0,0 +1,38 @@ +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.exe +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.deps.json +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.runtimeconfig.json +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.runtimeconfig.dev.json +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\ref\WorldAnalysisWrapper.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.pdb +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.csprojAssemblyReference.cache +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.GeneratedMSBuildEditorConfig.editorconfig +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.AssemblyInfoInputs.cache +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.AssemblyInfo.cs +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.csproj.CoreCompileInputs.cache +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\ref\WorldAnalysisWrapper.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.pdb +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.genruntimeconfig.cache +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\Newtonsoft.Json.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.csproj.CopyComplete +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Labs\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\websocket-sharp-latest.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.exe +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.deps.json +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.runtimeconfig.json +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.runtimeconfig.dev.json +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\ref\WorldAnalysisWrapper.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\WorldAnalysisWrapper.pdb +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\Newtonsoft.Json.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\bin\Debug\net5.0\websocket-sharp-latest.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.csprojAssemblyReference.cache +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.GeneratedMSBuildEditorConfig.editorconfig +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.AssemblyInfoInputs.cache +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.AssemblyInfo.cs +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.csproj.CoreCompileInputs.cache +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.csproj.CopyComplete +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\ref\WorldAnalysisWrapper.dll +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.pdb +D:\Fraunhofer\Projects\ETSI\STF 669\Git-Forge-Lab\world-analysis-hhi-wrapper\wrapper\c#\WorldAnalysisWrapper\obj\Debug\net5.0\WorldAnalysisWrapper.genruntimeconfig.cache diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csprojAssemblyReference.cache b/wrapper/c#/WorldAnalysisWrapper/obj/Debug/net5.0/WorldAnalysisWrapper.csprojAssemblyReference.cache new file mode 100644 index 0000000000000000000000000000000000000000..b81d931f0e1e84be4ccf47bc97bace909b14b3ff GIT binary patch literal 103975 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 RKvx-~dJ1%}F*Z+e0|4H1K + + + True + NuGet + $(MSBuildThisFileDirectory)project.assets.json + $(UserProfile)\.nuget\packages\ + C:\Users\renault\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages + PackageReference + 5.9.1 + + + + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/WorldAnalysisWrapper.csproj.nuget.g.targets b/wrapper/c#/WorldAnalysisWrapper/obj/WorldAnalysisWrapper.csproj.nuget.g.targets new file mode 100644 index 0000000..53cfaa1 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/WorldAnalysisWrapper.csproj.nuget.g.targets @@ -0,0 +1,6 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/project.assets.json b/wrapper/c#/WorldAnalysisWrapper/obj/project.assets.json new file mode 100644 index 0000000..e4c110f --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/project.assets.json @@ -0,0 +1,148 @@ +{ + "version": 3, + "targets": { + "net5.0": { + "Newtonsoft.Json/13.0.3": { + "type": "package", + "compile": { + "lib/netstandard2.0/Newtonsoft.Json.dll": {} + }, + "runtime": { + "lib/netstandard2.0/Newtonsoft.Json.dll": {} + } + }, + "websocket-sharp-latest/1.0.2": { + "type": "package", + "compile": { + "lib/netstandard2.0/websocket-sharp-latest.dll": {} + }, + "runtime": { + "lib/netstandard2.0/websocket-sharp-latest.dll": {} + } + } + } + }, + "libraries": { + "Newtonsoft.Json/13.0.3": { + "sha512": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==", + "type": "package", + "path": "newtonsoft.json/13.0.3", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "LICENSE.md", + "README.md", + "lib/net20/Newtonsoft.Json.dll", + "lib/net20/Newtonsoft.Json.xml", + "lib/net35/Newtonsoft.Json.dll", + "lib/net35/Newtonsoft.Json.xml", + "lib/net40/Newtonsoft.Json.dll", + "lib/net40/Newtonsoft.Json.xml", + "lib/net45/Newtonsoft.Json.dll", + "lib/net45/Newtonsoft.Json.xml", + "lib/net6.0/Newtonsoft.Json.dll", + "lib/net6.0/Newtonsoft.Json.xml", + "lib/netstandard1.0/Newtonsoft.Json.dll", + "lib/netstandard1.0/Newtonsoft.Json.xml", + "lib/netstandard1.3/Newtonsoft.Json.dll", + "lib/netstandard1.3/Newtonsoft.Json.xml", + "lib/netstandard2.0/Newtonsoft.Json.dll", + "lib/netstandard2.0/Newtonsoft.Json.xml", + "newtonsoft.json.13.0.3.nupkg.sha512", + "newtonsoft.json.nuspec", + "packageIcon.png" + ] + }, + "websocket-sharp-latest/1.0.2": { + "sha512": "1dLVMsURAIp9YCc43hW9x3HgSw5hWZU+w3YRTWv7HOopm2N4+noa+rCp1VKTEmmRkpJja8oNYmbrDYFZRi7kCw==", + "type": "package", + "path": "websocket-sharp-latest/1.0.2", + "files": [ + ".nupkg.metadata", + ".signature.p7s", + "lib/netstandard2.0/websocket-sharp-latest.dll", + "websocket-sharp-latest.1.0.2.nupkg.sha512", + "websocket-sharp-latest.nuspec", + "websocket-sharp_icon.png" + ] + } + }, + "projectFileDependencyGroups": { + "net5.0": [ + "Newtonsoft.Json >= 13.0.3", + "websocket-sharp-latest >= 1.0.2" + ] + }, + "packageFolders": { + "C:\\Users\\renault\\.nuget\\packages\\": {}, + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {} + }, + "project": { + "version": "1.0.0", + "restore": { + "projectUniqueName": "D:\\Fraunhofer\\Projects\\ETSI\\STF 669\\Git-Forge-Lab\\world-analysis-hhi-wrapper\\wrapper\\c#\\WorldAnalysisWrapper\\WorldAnalysisWrapper.csproj", + "projectName": "WorldAnalysisWrapper", + "projectPath": "D:\\Fraunhofer\\Projects\\ETSI\\STF 669\\Git-Forge-Lab\\world-analysis-hhi-wrapper\\wrapper\\c#\\WorldAnalysisWrapper\\WorldAnalysisWrapper.csproj", + "packagesPath": "C:\\Users\\renault\\.nuget\\packages\\", + "outputPath": "D:\\Fraunhofer\\Projects\\ETSI\\STF 669\\Git-Forge-Lab\\world-analysis-hhi-wrapper\\wrapper\\c#\\WorldAnalysisWrapper\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\Users\\renault\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net5.0" + ], + "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "https://api.nuget.org/v3/index.json": {} + }, + "frameworks": { + "net5.0": { + "targetAlias": "net5.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks": { + "net5.0": { + "targetAlias": "net5.0", + "dependencies": { + "Newtonsoft.Json": { + "target": "Package", + "version": "[13.0.3, )" + }, + "websocket-sharp-latest": { + "target": "Package", + "version": "[1.0.2, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\5.0.214\\RuntimeIdentifierGraph.json" + } + } + } +} \ No newline at end of file diff --git a/wrapper/c#/WorldAnalysisWrapper/obj/project.nuget.cache b/wrapper/c#/WorldAnalysisWrapper/obj/project.nuget.cache new file mode 100644 index 0000000..50cd978 --- /dev/null +++ b/wrapper/c#/WorldAnalysisWrapper/obj/project.nuget.cache @@ -0,0 +1,11 @@ +{ + "version": 2, + "dgSpecHash": "pZkNrM9uJ7+MjfMVevTsfvAv1jKRE6V7dS+EbM//vg/L9qdLB3ot6ELSoxopPc51/FLk7I7qD0fAvKEdfZS8+Q==", + "success": true, + "projectFilePath": "D:\\Fraunhofer\\Projects\\ETSI\\STF 669\\Git-Forge-Lab\\world-analysis-hhi-wrapper\\wrapper\\c#\\WorldAnalysisWrapper\\WorldAnalysisWrapper.csproj", + "expectedPackageFiles": [ + "C:\\Users\\renault\\.nuget\\packages\\newtonsoft.json\\13.0.3\\newtonsoft.json.13.0.3.nupkg.sha512", + "C:\\Users\\renault\\.nuget\\packages\\websocket-sharp-latest\\1.0.2\\websocket-sharp-latest.1.0.2.nupkg.sha512" + ], + "logs": [] +} \ No newline at end of file diff --git a/wrapper/TestWSServer.py b/wrapper/python/TestWSServer.py similarity index 100% rename from wrapper/TestWSServer.py rename to wrapper/python/TestWSServer.py diff --git a/wrapper/WorldAnalysisWrapper.py b/wrapper/python/WorldAnalysisWrapper.py similarity index 70% rename from wrapper/WorldAnalysisWrapper.py rename to wrapper/python/WorldAnalysisWrapper.py index 7454fac..e32cc7a 100644 --- a/wrapper/WorldAnalysisWrapper.py +++ b/wrapper/python/WorldAnalysisWrapper.py @@ -17,7 +17,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# Last change: August 2024 +# Author: Fraunhofer HHI, SylR +# Last change: September 2024 # import sys @@ -42,6 +43,7 @@ import ETSI.ARF.OpenAPI.WorldAnalysis from ETSI.ARF.OpenAPI.WorldAnalysis.api import default_api from ETSI.ARF.OpenAPI.WorldAnalysis.api import capabilities_api from ETSI.ARF.OpenAPI.WorldAnalysis.api import pose_api + # Models (classes) from ETSI.ARF.OpenAPI.WorldAnalysis.models.pose import Pose from ETSI.ARF.OpenAPI.WorldAnalysis.models.capability import Capability @@ -56,15 +58,31 @@ from ETSI.ARF.OpenAPI.WorldAnalysis.models.capability import Capability # and then run python script: # python .py +# +# Web server (World Storage) +# + +# for production +webserver_url = "https://etsi.hhi.fraunhofer.de" # public + +# for development +#webserver_url = "http://localhost:61788" +webserver_url = "https://localhost:44301" # secure # See configuration.py for a list of all supported configuration parameters. -configuration = ETSI.ARF.OpenAPI.WorldStorage.Configuration(host="https://etsi.hhi.fraunhofer.de") -#configuration = ETSI.ARF.OpenAPI.WorldStorage.Configuration(host="https://localhost:44301") -#configuration = ETSI.ARF.OpenAPI.WorldStorage.Configuration(host="http://localhost:61788") +configuration = ETSI.ARF.OpenAPI.WorldStorage.Configuration(host=webserver_url) -webs_server = "ws://localhost:61788/ws" -#webs_server = "wss://localhost:44301/ws" -#webs_server = "wss://analysis.etsi.hhi.fraunhofer.de" # final url +# +# WebSocket server (World Analysis) +# + +# for production +websocket_url ="ws://192.168.20.29:8084/ws" # local network +#websocket_url = "wss://analysis.etsi.hhi.fraunhofer.de" # public + +# for development +#websocket_url = "ws://localhost:61788/ws" +websocket_url = "wss://localhost:44301/ws" # secure print() print("ETSI ISG - ARF World Storage") @@ -73,11 +91,9 @@ print("Simple request tests") print("====================") print() print("Using REST World Storage server: " + configuration.host) -print("Using Websockets server: " + webs_server) +print("Using WebSockets server: " + websocket_url) print() -isTime = False -isPose = False running = True success = 0 #websocket = websockets.connect(my_ws_server) @@ -85,16 +101,22 @@ myWebsocket = None serverResponse = "None" modulename = "" msgToSend = "None" +isPose = False +secretToken = "dev" -# certificate -localhost_pem = pathlib.Path(__file__).with_name("localhost.pem") -print(f"Using PEM: {localhost_pem}") -print() - +# +# Handle some certificates +# # See here: https://websockets.readthedocs.io/en/stable/howto/quickstart.html#encrypt-connections -webs_ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) -webs_ssl_context.load_verify_locations(localhost_pem) +#localhost_pem = pathlib.Path(__file__).with_name("localhost.pem") +#webs_ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) +#webs_ssl_context.load_verify_locations(localhost_pem) +#print(f"Using PEM: {localhost_pem}") +#print() +################################################################### +# REST -> World Storage +################################################################### # # Test the REST server availability (World Storage) @@ -114,7 +136,7 @@ def CheckRESTServer(client): print("Sending 'ping', got response: " + api_response) success += 1 except ETSI.ARF.OpenAPI.WorldStorage.ApiException as e: - print("Exception when calling DefaultApi->get_ping: %s\n" % e) + print("[REST] Exception when calling DefaultApi->get_ping: %s\n" % e) try: # Test the server availability. @@ -122,7 +144,7 @@ def CheckRESTServer(client): print("Sending 'version', got response: " + api_response) success += 1 except ETSI.ARF.OpenAPI.WorldStorage.ApiException as e: - print("Exception when calling DefaultApi->get_ping: %s\n" % e) + print("[REST] Exception when calling DefaultApi->get_ping: %s\n" % e) try: # Test the server availability. @@ -130,10 +152,33 @@ def CheckRESTServer(client): print("Sending 'admin', got response: " + api_response) success += 1 except ETSI.ARF.OpenAPI.WorldStorage.ApiException as e: - print("Exception when calling DefaultApi->get_ping: %s\n" % e) + print("[REST] Exception when calling DefaultApi->get_ping: %s\n" % e) return success == 3 +# +# Get the list of trackables +# +def REST_GetTrackables(): + + global firstJob + + # Enter a context with an instance of the API client + with ETSI.ARF.OpenAPI.WorldStorage.ApiClient(configuration) as api_client: + api_instance_t = trackables_api.TrackablesApi(api_client) + + try: + list_response = api_instance_t.get_trackables(token="dev") + print("Querying Trackables: got list with " + str(len(list_response)) + " items:") + for item in list_response: + print(" UUID: " + str(item.uuid) + " Name: " + item.name + " (Type: " + str(item.trackable_type) + ")") + except ETSI.ARF.OpenAPI.WorldStorage.ApiException as e: + print("[REST] Exception when calling TrackablesApi->get_trackables: %s\n" % e) + +################################################################### +# WebSockets -> World Analysis +################################################################### + # # WebSockets helpers # @@ -160,13 +205,14 @@ def _OnReceiveText(msg): async def WS_ConnectAndLoop(): global modulename global msgToSend, serverResponse - global running, isTime + global running, isTime, isPose try: ###ws = websockets.WebSocket(sslopt={"cert_reqs": ssl.CERT_NONE}) - #with connect(uri=webs_server, ssl_context=my_ssl_context) as websocket: + #with connect(uri=websocket + #_server, ssl_context=my_ssl_context) as websocket: - async with websockets.connect(uri=webs_server) as websocket: + async with websockets.connect(uri=websocket_url) as websocket: #myWebsocket = websocket @@ -199,7 +245,7 @@ async def WS_ConnectAndLoop(): cap1.latency = 0.5 cap1.accuracy = 0.8 cap_json = cap1.to_json() - await WS_send(websocket, "Capabilities of:" + modulename + " Data=" + cap_json, isResponse=False) + await WS_send(websocket, "Capabilities=" + cap_json, isResponse=False) elif modulename == "HHI-MeshDetection": @@ -215,7 +261,7 @@ async def WS_ConnectAndLoop(): cap1.latency = 0.5 cap1.accuracy = 0.9 cap_json = cap1.to_json() - await WS_send(websocket, "Capabilities of:" + modulename + " Data=" + cap_json, isResponse=False) + await WS_send(websocket, "Capabilities=" + cap_json, isResponse=False) # Capability #2 cap2 = Capability() @@ -225,10 +271,9 @@ async def WS_ConnectAndLoop(): cap2.latency = 0.05 cap2.accuracy = 1 cap_json = cap2.to_json() - await WS_send(websocket, "Capabilities of:" + modulename + " Data=" + cap_json, isResponse=False) + await WS_send(websocket, "Capabilities=" + cap_json, isResponse=False) - #msgToSend = "Idle" - msgToSend = "TimeStart" # test + msgToSend = "Idle" #msgToSend = "PoseStart" # @@ -244,31 +289,14 @@ async def WS_ConnectAndLoop(): # Wait for a message serverMessage = await WS_receive(websocket) - # Stop the loop and disconnect the server + # + # Management + # if serverMessage == "SessionStop": + # Stop the loop and disconnect the server isPose = False running = False - elif serverMessage.startswith("Time="): - isTime = True - datetime = serverMessage.split('=') - print(f"New server time is: { datetime[1] }") - - elif serverMessage.startswith("TimeStop"): - isTime = False - - elif serverMessage.startswith("ConfigureFramerate:"): - json = serverMessage.split(':')[1] - - elif serverMessage.startswith("GetPose:"): - uuid2 = serverMessage.split(':')[1] - mode = serverMessage.split(':')[2] - - # Send the pose - p = Pose() - msgToSend = "PoseNew:" + p.to_json() - print(f"Send pose for: uuid={ uuid2 }") - elif serverMessage.startswith("SubscribePose:"): uuid = serverMessage.split(':')[1] msgToSend = "PoseIsRegistered" @@ -278,20 +306,29 @@ async def WS_ConnectAndLoop(): uuid = serverMessage.split(':')[1] isPose = False - elif isPose == True and serverMessage == "RequestNextPose": - time.sleep(1) + elif serverMessage.startswith("ConfigureFramerate:"): + fps = serverMessage.split(':')[1] + + # + # Special msg for simulating a global user's position + # + elif serverMessage.startswith("CurrentUserPose="): + userPose = serverMessage.split('=')[1] + print(f"New user current position is: {userPose }") + + # + # Pose requests + # + elif serverMessage.startswith("GetPose:"): # 2 args + uuid2 = serverMessage.split(':')[1] + mode = serverMessage.split(':')[2] - # Send the pose - p = Pose() - msgToSend = "PoseNew:" + p.to_json() - print(f"Send pose for: uuid={ uuid }") + WS_SendPose(uuid2) - elif isTime: - serverResponse = await WS_receive(websocket) - if serverResponse == "TimeStop": - isTime = False - msgToSend = "Idle" + elif isPose == True and serverMessage == "RequestNextPose": + time.sleep(1) + WS_SendPose(uuid) except websockets.exceptions.ConnectionClosed as e: print(f"[WS] Connection closed with error: {e}") @@ -299,6 +336,17 @@ async def WS_ConnectAndLoop(): except Exception as e: print(f"[WS] An error occurred: {e}") + +def WS_SendPose(uuid): + # Send the pose + p = Pose() + + # Get the reloc infos? + + msgToSend = "NewPose:" + p.to_json() + print(f"Send pose for: uuid={ uuid }") + + #async def main(): def main(): global modulename @@ -318,6 +366,8 @@ def main(): if isServerOk == True: print ("[REST] Connection to WS was succesfull.") + REST_GetTrackables() + # # Use this (websockets lib) # diff --git a/wrapper/python/arf specific.txt b/wrapper/python/arf specific.txt new file mode 100644 index 0000000..e69de29 diff --git a/wrapper/localhost.pem b/wrapper/python/localhost.pem similarity index 100% rename from wrapper/localhost.pem rename to wrapper/python/localhost.pem diff --git a/wrapper/python/openapitools.json b/wrapper/python/openapitools.json new file mode 100644 index 0000000..e69de29 -- GitLab