Skip to content
Snippets Groups Projects
Commit cafd4e6c authored by Sylvain Renault's avatar Sylvain Renault
Browse files

Preparation of REST and websocket client functions.

Some namepsace changes.
parent 7e30b844
No related branches found
No related tags found
1 merge request!2Finalisation of REST and websocket client functions and subscription methodics.
Showing
with 818 additions and 5 deletions
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: August 2024
//
using UnityEditor;
[CustomEditor(typeof(WorldAnalysisInfo))]
public class WorldAnalysisInfoEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
EditorGUILayout.Space();
WorldAnalysisInfo srv = (WorldAnalysisInfo)target;
string state = "";// srv.GetServerState();
EditorGUILayout.LabelField("Server State", state);
string api = "";// srv.GetAPIVersion();
EditorGUILayout.LabelField("OpenAPI Version", api);
}
}
fileFormatVersion: 2
guid: 4b890e537ebae974b862e97bfefa51ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: July 2024
//
using System.Collections;
using UnityEngine;
using UnityEditor;
using ETSI.ARF.OpenAPI;
using ETSI.ARF.OpenAPI.WorldAnalysis;
using ETSI.ARF.WorldAnalysis;
using ETSI.ARF.WorldAnalysis.REST;
[CustomEditor(typeof(WorldAnalysisServer))]
public class WorldStorageServerEditor : Editor
{
WorldAnalysisServer server;
private string state = "";
private string version = "";
private string test = "";
private Queue handleResponseQueue = new Queue();
private ResponseObject<string> pendingTest = null;
private ResponseObject<string> pendingState = null;
private ResponseObject<string> pendingVersion = null;
public void OnEnable()
{
server = (WorldAnalysisServer)target;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawDefaultInspector();
EditorGUILayout.Space();
if (GUILayout.Button("Test server"))
{
TestPing();
}
EditorGUILayout.LabelField("Test Response", test);
EditorGUILayout.Space();
if (GUILayout.Button("Query server"))
{
QueryServer();
}
EditorGUILayout.LabelField("Server State", state);
EditorGUILayout.LabelField("OpenAPI Version", version);
if (handleResponseQueue.Count > 0)
{
object o = handleResponseQueue.Dequeue();
if (o.Equals(pendingTest))
{
ResponseObject<string> response = o as ResponseObject<string>;
Debug.Log($"Get '{response.result}' from server");
test = response.result;
pendingTest = null;
EditorUtility.SetDirty(target);
}
else if (o.Equals(pendingState))
{
ResponseObject<string> response = o as ResponseObject<string>;
Debug.Log($"Get '{response.result}' from server");
state = response.result;
pendingState = null;
EditorUtility.SetDirty(target);
}
else if (o.Equals(pendingVersion))
{
ResponseObject<string> response = o as ResponseObject<string>;
Debug.Log($"Get '{response.result}' from server");
version = response.result;
pendingVersion = null;
EditorUtility.SetDirty(target);
}
else
{
Debug.Log("Unsupported response!");
}
}
}
public override bool RequiresConstantRepaint()
{
return handleResponseQueue.Count > 0;
}
void OnSceneGUI()
{
Debug.Log("OnSceneGUI");
}
private void TestPing()
{
test = "";
EditorUtility.SetDirty(target);
if (server == null)
{
Debug.LogError("No server defined!");
return;
}
//string response = AdminRequest.PingSync(server);
//EditorUtility.DisplayDialog("Test Server", $"Get '{response}' from server", "OK");
if (pendingTest != null)
{
pendingTest.Cancel();
}
pendingTest = AdminRequest.PingAsync(server, (response) =>
{
handleResponseQueue.Enqueue(response);
Debug.Log($"Request Time: { response.requestTime.ToLongTimeString() } / Total Time: { response.DeltaTime.TotalMilliseconds }ms\n\n<b>Content:</b>\n{ response.result }");
});
Debug.Log("Starting request @ time: " + pendingTest.requestTime.ToLongTimeString() + "...");
}
private void QueryServer()
{
version = "";
state = "";
if (pendingState != null)
{
pendingState.Cancel();
}
pendingState = AdminRequest.AdminAsync(server, (response) =>
{
handleResponseQueue.Enqueue(response);
});
if (pendingVersion != null)
{
pendingVersion.Cancel();
}
pendingVersion = AdminRequest.VersionAsync(server, (response) =>
{
handleResponseQueue.Enqueue(response);
});
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 9899978f4c750de4b98f81a9c6937a94
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2022 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: June 2022
//
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using ETSI.ARF.WorldAnalysis;
[CustomEditor(typeof(WorldAnalysisUser))]
public class WorldStorageUserEditor : Editor
{
WorldAnalysisUser user;
public void OnEnable()
{
user = (WorldAnalysisUser)target;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawDefaultInspector();
EditorGUILayout.Space();
if (GUILayout.Button("Generate New Creator UUID"))
{
user.UUID = System.Guid.NewGuid().ToString();
EditorUtility.SetDirty(target);
}
}
}
fileFormatVersion: 2
guid: b0d1b5ae7e5771b4496dfd778bfa031b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
fileFormatVersion: 2
guid: 24a8b3224329f984c9b1737d44f11163
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: August 2024
//
using UnityEngine;
namespace ETSI.ARF.WorldAnalysis
{
[System.Serializable]
[CreateAssetMenu(fileName = "ARFWorldAnalysisServer", menuName = "ARF World Analysis/Create Server", order = 1)]
public class WorldAnalysisServer : ScriptableObject
{
[SerializeField] public string serverName = "myServerName";
[SerializeField] public string company = "";
[SerializeField] public string basePath = "https://";
[SerializeField] public int port = 8080;
[Space(8)]
[SerializeField] public WorldAnalysisUser currentUser = null;
public string URI => port == 0 ? basePath : basePath + ":" + port.ToString();
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 4571d019d0609224aa4a14ed18de30cd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: August 2024
//
using UnityEngine;
namespace ETSI.ARF.WorldAnalysis
{
[System.Serializable]
[CreateAssetMenu(fileName = "ARFWorldAnalysisUser", menuName = "ARF World Analysis/Create User", order = 1)]
public class WorldAnalysisUser : ScriptableObject
{
[SerializeField] public string userName = "myName";
[SerializeField] public string company = "";
[SerializeField] public string UUID = System.Guid.Empty.ToString();
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 01f9889f0a04026429aa8459e181e973
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ETSI.ARF.OpenAPI.WorldAnalysis
{
/// <summary>
/// Simple class to debug the requests
/// </summary>
public class BaseClient
{
static public bool EnableClientLog = true;
public string lastJsonText;
public long lastPayload;
protected void _prepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url)
{
if (EnableClientLog)
{
Debug.Log("[REST][URL] Send request: " + client.BaseAddress + url);
Debug.Log("[REST][URL] Send request: " + request);
}
}
protected void _processResponse(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpResponseMessage response)
{
lastJsonText = response.Content.ReadAsStringAsync().Result.ToString();
lastPayload = response.Content.Headers.ContentLength.Value;
var status_ = (int)response.StatusCode;
if (EnableClientLog)
{
Debug.Log("[REST][Data] Status: " + status_ + " Response: " + client.BaseAddress + " Len: " + lastPayload + " JSON: " + lastJsonText);
}
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 696362f892252db4ca192c6eccbbfa25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ETSI.ARF.OpenAPI.WorldAnalysis
{
public interface IModel
{
public System.Guid Uuid { get; set; } // Bug: SylR: Why is Uuid not capitalized (UUID)???
public string ToJson();
}
// Class to monitor the server
public class Server : IModel
{
public System.Guid Uuid { get; set; }
public string Name { get; set; }
public Server(string name)
{
Uuid = Guid.Empty;
Name = name;
}
public string ToJson() { return JsonUtility.ToJson(this); }
}
//
// Implement here some constructors
//
public partial class Pose : IModel
{
public Pose()
{
Uuid = Guid.NewGuid();
}
public string ToJson() { return JsonUtility.ToJson(this); }
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 90e60e9d41d419146b3b6207e4ef556e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
......@@ -24,11 +24,11 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
[System.CodeDom.Compiler.GeneratedCode("NSwag", "14.0.7.0 (NJsonSchema v11.0.0.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class WorldAnalysisClient
{
private ETSI.ARF.OpenAPI.IHttpClient _httpClient;
private ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient _httpClient;
private static System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings, true);
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public WorldAnalysisClient(ETSI.ARF.OpenAPI.IHttpClient httpClient)
public WorldAnalysisClient(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient httpClient)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
_httpClient = httpClient;
......@@ -45,9 +45,9 @@ namespace ETSI.ARF.OpenAPI.WorldAnalysis
static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
partial void PrepareRequest(ETSI.ARF.OpenAPI.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url);
partial void PrepareRequest(ETSI.ARF.OpenAPI.IHttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
partial void ProcessResponse(ETSI.ARF.OpenAPI.IHttpClient client, System.Net.Http.HttpResponseMessage response);
partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, string url);
partial void PrepareRequest(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
partial void ProcessResponse(ETSI.ARF.OpenAPI.WorldAnalysis.IHttpClient client, System.Net.Http.HttpResponseMessage response);
/// <summary>
/// Test the server availability.
......
// The Fraunhofer HHI Unity Framework
// ___________ .__ _____ ___ ___ ___ ___ .___
// \_ _____/___________ __ __ ____ | |__ _____/ ____\___________ / | \ / | \| |
// | __) \_ __ \__ \ | | \/ \| | \ / _ \ __\/ __ \_ __ \ / ~ \/ ~ \ |
// | \ | | \// __ \| | / | \ Y ( <_> ) | \ ___/| | \/ \ Y /\ Y / |
// \___ / |__| (____ /____/|___| /___| /\____/|__| \___ >__| \___|_ / \___|_ /|___|
// \/ \/ \/ \/ \/ \/ \/
// (C) Fraunhofer HHI, 2024
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
namespace ETSI.ARF.OpenAPI.WorldAnalysis
{
public class CancelToken
{
protected CancellationTokenSource tokenSource;
protected CancellationToken ct;
public CancellationToken cancellationToken { get => ct; }
public void Cancel()
{
tokenSource.Cancel();
}
}
public class ResponseObject<T> : 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<ResponseObject<T>> callback;
public ResponseObject(string msg, Action<ResponseObject<T>> func = null)
{
requestTime = DateTime.Now;
message = msg;
callback = func;
transactionId = ++ID;
tokenSource = new CancellationTokenSource();
ct = tokenSource.Token;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 82dc58594acebf747bd716fd00fcfb10
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
//
// ARF - Augmented Reality Framework (ETSI ISG ARF)
//
// Copyright 2024 ETSI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Last change: June 2024
//
// Depends on UniTask to support cancellation token and GetAwaiter: https://github.com/Cysharp/UniTask
// Otherwise, the code can be adapted using https://gist.github.com/krzys-h/9062552e33dd7bd7fe4a6c12db109a1a
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
//using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
namespace ETSI.ARF.OpenAPI.WorldAnalysis
{
public interface IHttpClient
{
public Uri BaseAddress { get; set; }
public HttpRequestHeaders DefaultRequestHeaders { get; }
public Task<HttpResponseMessage> 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<HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token)
{
return await _httpClient.SendAsync(message, option, token);
}
public void Dispose()
{
_httpClient.Dispose();
DefaultRequestHeaders.Clear();
BaseAddress = null;
}
}
public class UnityWebRequestHttpClient : IHttpClient
{
public UnityWebRequestHttpClient() { }
public UnityWebRequestHttpClient(string baseUri)
{
BaseAddress = new Uri(baseUri);
}
public UnityWebRequestHttpClient(Uri baseUri)
{
BaseAddress = baseUri;
}
public Uri BaseAddress { get; set; }
public HttpRequestHeaders DefaultRequestHeaders => _httpClient.DefaultRequestHeaders;
private readonly HttpClient _httpClient = new HttpClient();
public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage message, HttpCompletionOption option, CancellationToken token)
{
var content = await (message.Content?.ReadAsStringAsync() ?? Task.FromResult(""));
var webRequest = GetUnityWebRequest(message.Method.Method, message.RequestUri, content);
AppendHeaders(webRequest);
Debug.Log("[HTTP] Request " + webRequest.uri.ToString());
try
{
//SylR
webRequest.SendWebRequest();
while (!webRequest.isDone)
{
if (token.IsCancellationRequested)
{
Debug.Log($"Task '{ message.RequestUri }' cancelled");
token.ThrowIfCancellationRequested();
}
await Task.Yield();
}
//await webRequest
// .SendWebRequest()
// .WithCancellation(cancellationToken: token);
}
catch (Exception)
{
webRequest.Dispose();
throw;
}
Debug.Log("[HTTP] Result: " + webRequest.result.ToString());
var responseMessage = CreateHttpResponseMessage(webRequest);
webRequest.Dispose();
Debug.Log("[HTTP] Response len: " + responseMessage.Content.Headers.ContentLength);
return responseMessage;
}
public void Dispose()
{
_httpClient.Dispose();
DefaultRequestHeaders.Clear();
BaseAddress = null;
}
private UnityWebRequest GetUnityWebRequest(string method, Uri endpoint, string content = "")
{
var requestUri = BaseAddress.AbsoluteUri + endpoint;
var webRequest = UnityWebRequest.Get(requestUri);
webRequest.method = method;
webRequest.disposeUploadHandlerOnDispose = true;
webRequest.disposeDownloadHandlerOnDispose = true;
if (!string.IsNullOrEmpty(content))
{
var data = new System.Text.UTF8Encoding().GetBytes(content);
webRequest.uploadHandler = new UploadHandlerRaw(data);
webRequest.SetRequestHeader("Content-Type", "application/json");
//webRequest.SetRequestHeader("Content-Type", "image/jpeg");
}
return webRequest;
}
private void AppendHeaders(UnityWebRequest webRequest)
{
using var enumerator = DefaultRequestHeaders.GetEnumerator();
while (enumerator.MoveNext())
{
var (key, value) = enumerator.Current;
webRequest.SetRequestHeader(key, value.First());
}
}
private HttpResponseMessage CreateHttpResponseMessage(UnityWebRequest webRequest)
{
var responseContent = webRequest.downloadHandler?.text;
var response = new HttpResponseMessage();
response.Content = new StringContent(responseContent);
response.StatusCode = (HttpStatusCode)webRequest.responseCode;
Dictionary<string, string> headers = webRequest.GetResponseHeaders();
if (headers != null)
{
Debug.Log("[HTTP] Header: " + headers.Count.ToString());
foreach (var h in headers)
{
switch (h.Key.ToLower().Trim())
{
case "content-type":
{
var trimmed = h.Value.ToLower().Split(";").FirstOrDefault();
response.Content.Headers.ContentType = new MediaTypeHeaderValue(trimmed);
break;
}
case "content-length":
response.Content.Headers.ContentLength = long.Parse(h.Value);
break;
default:
if (h.Value == "gzip")
{
// bug???
}
else
response.Headers.Add(h.Key, h.Value);
break;
}
}
}
return response;
}
}
}
\ No newline at end of file
fileFormatVersion: 2
guid: 4aa105d9487dc27408feffefde9e4475
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment