diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Binders/FromJsonBinderProviderT.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Binders/FromJsonBinderProviderT.cs new file mode 100644 index 0000000000000000000000000000000000000000..b77d044efbf5d91ef60cf55677ddecd903bc406a --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Binders/FromJsonBinderProviderT.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; +using ETSI.ARF.OpenAPI.WorldStorage.Models; +using System; + +namespace ETSI.ARF.OpenAPI.WorldStorage.Binders +{ + public class FromJsonBinderProviderT : IModelBinderProvider + { + public IModelBinder GetBinder(ModelBinderProviderContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (context.Metadata.ModelType == typeof(T)) + { + return new BinderTypeModelBinder(typeof(FromJsonBinderT)); + } + + return null; + } + } +} \ No newline at end of file diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Binders/FromJsonBinderT.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Binders/FromJsonBinderT.cs new file mode 100644 index 0000000000000000000000000000000000000000..8fd6002f759cd4f9d758d8252b98a502d4a14808 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/Binders/FromJsonBinderT.cs @@ -0,0 +1,48 @@ +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; +using System; +using System.Threading.Tasks; +using Newtonsoft.Json; + +using ETSI.ARF.OpenAPI.WorldStorage.Models; + +namespace ETSI.ARF.OpenAPI.WorldStorage.Binders +{ + public class FromJsonBinderT : IModelBinder + { + public Task BindModelAsync(ModelBindingContext bindingContext) + { + if (bindingContext == null) + { + throw new ArgumentNullException(nameof(bindingContext)); + } + + var modelName = bindingContext.ModelName; + var jsonString = bindingContext.ActionContext.HttpContext.Request.Query[modelName]; + var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName); + + if (valueProviderResult == ValueProviderResult.None) + { + return Task.CompletedTask; + } + + bindingContext.ModelState.SetModelValue(modelName, valueProviderResult); + + var value = valueProviderResult.FirstValue; + + if (string.IsNullOrEmpty(value)) + { + return Task.CompletedTask; + } + + Console.WriteLine($"json string: {value}"); + + T result = JsonConvert.DeserializeObject(value); + + Console.WriteLine($"instance: {result}"); + + bindingContext.Result = ModelBindingResult.Success(result); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ControllersImpl/RelocalizationInformationImpl.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ControllersImpl/RelocalizationInformationImpl.cs new file mode 100644 index 0000000000000000000000000000000000000000..e8bcc07405814d12a2794af8a1d8e819ad1c0d08 --- /dev/null +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ControllersImpl/RelocalizationInformationImpl.cs @@ -0,0 +1,370 @@ +// +// 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. +// + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http; +using Swashbuckle.AspNetCore.Annotations; +using Swashbuckle.AspNetCore.SwaggerGen; +using Newtonsoft.Json; +using ETSI.ARF.OpenAPI.WorldStorage.Attributes; +using ETSI.ARF.OpenAPI.WorldStorage.Models; +using ETSI.ARF.OpenAPI.WorldStorage.Services; +using MongoDB.Driver; + +using System.Numerics; + +namespace ETSI.ARF.OpenAPI.WorldStorage.Controllers +{ + /// + /// + /// + [ApiController] + public class RelocalizationInformationApiControllerImpl : RelocalizationInformationApiController + { + /// + /// + /// + public RelocalizationInformationApiControllerImpl() + { + + } + + /// + /// Operation to retrieve all the relocalization information of one or severals WorldAnchors or Trackables. + /// + /// + public override IActionResult GetRelocalizationInformation([FromQuery(Name = "uuids")][Required()] List uuids, [FromQuery(Name = "capabilities")][Required()] List capabilities, [FromHeader(Name = "token")] string token) + { + WorldAnchorService _worldAnchorService = WorldAnchorService.Singleton as WorldAnchorService; + + var history = new List(); + var result = new GetRelocalizationInformation200Response(); + result.RelocInfo = new List(); + + foreach (var request in uuids) + { + var anchor = _worldAnchorService.Get(request.Uuid); + + if (anchor != null) + { + history.Add(anchor.UUID); + + Matrix4x4 matrix = Matrix4x4.Identity; + + var relocalizationInformation = new RelocalizationInformation(); + relocalizationInformation.RequestUUID = anchor.UUID; + relocalizationInformation.RelocObjects = FindRelocInfo(anchor.UUID, request.Mode, matrix, capabilities, ref history); + + result.RelocInfo.Add(relocalizationInformation); + } + else + { + Console.WriteLine($"WorldAnchor with UUID {request.Uuid} does not exist in database"); + } + } + + return result.RelocInfo.Count > 0 ? new ObjectResult(result) : StatusCode(404, "Not found, could not find UUID in database."); + } + + private List FindRelocInfo(Guid targetUUID, ModeWorldStorage mode, Matrix4x4 matrix, List capabilities, ref List history) + { + var results = new List(); + + results.AddRange(FindRelocInfoAsTo(targetUUID, mode, matrix, capabilities, ref history)); + results.AddRange(FindRelocInfoAsFrom(targetUUID, mode, matrix, capabilities, ref history)); + + return results; + } + + // Check links where the target UUID appeared as "To" + private List FindRelocInfoAsTo(Guid targetUUID, ModeWorldStorage mode, Matrix4x4 matrix, List capabilities, ref List history) + { + TrackableService _trackableService = TrackableService.Singleton as TrackableService; + WorldAnchorService _worldAnchorService = WorldAnchorService.Singleton as WorldAnchorService; + WorldLinkService _worldLinkService = WorldLinkService.Singleton as WorldLinkService; + + var results = new List(); + + // Check links where the target UUID appeared as "To" + List linksTo; + + linksTo = _worldLinkService.GetWorldLinkUUIDTo(targetUUID); + foreach (var link in linksTo) + { + var found = history.Find(uuid => uuid == link.UUIDFrom); + if (found != Guid.Empty) + { + // Skip link, already processed! + Console.WriteLine($"Skip link ({link.UUIDFrom} -> {link.UUIDTo}) as it was already processed"); + continue; + } + + history.Add(link.UUIDFrom); + + if (link.TypeFrom == TypeWorldStorage.TRACKABLEEnum) + { + // handle "From" as a trackable + Trackable trackable = _trackableService.Get(link.UUIDFrom); + + if (trackable != null) + { + // Check if trackable is compatible with the request + if (!trackable.Match(capabilities)) + { + continue; + } + + Matrix4x4 m; + + if (mode == ModeWorldStorage.REQUESTTOTRACKABLESEnum) + { + Matrix4x4 invert; + if (Matrix4x4.Invert(link.Matrix(), out invert)) + { + m = matrix * invert; + } + else + { + Console.WriteLine("Fail to invert 3D transform!"); + continue; + } + } + else if (mode == ModeWorldStorage.TRACKABLESTOREQUESTEnum) + { + m = link.Matrix() * matrix; + } + else + { + // Invalid mode + continue; + } + + // We found a valid trackable, add it to the results + var result = new RelocalizationInformationRelocObjectsInner(); + result.Trackable = trackable; + result.Mode = mode; + result.Transform3D = new List() + { + 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, + }; + + results.Add(result); + + // Recursive search starting with the attached trackable + results.AddRange(FindRelocInfo(trackable.UUID, mode, m, capabilities, ref history)); + } + else + { + Console.WriteLine($"Trackable with UUID {link.UUIDFrom} does not exist in database"); + } + } + else if (link.TypeFrom == TypeWorldStorage.ANCHOREnum) + { + WorldAnchor anchor = _worldAnchorService.Get(link.UUIDFrom); + + if (anchor != null) + { + + Matrix4x4 m; + m = link.Matrix()*matrix; + + if (mode == ModeWorldStorage.REQUESTTOTRACKABLESEnum) + { + Matrix4x4 invert; + if (Matrix4x4.Invert(link.Matrix(), out invert)) + { + m = matrix * invert; + } + else + { + Console.WriteLine("Fail to invert 3D transform!"); + continue; + } + } + else if (mode == ModeWorldStorage.TRACKABLESTOREQUESTEnum) + { + m = link.Matrix() * matrix; + } + else + { + // Invalid mode + continue; + } + + // Recursive search starting with the attached anchor + results.AddRange(FindRelocInfo(anchor.UUID, mode, m, capabilities, ref history)); + } + else + { + Console.WriteLine($"WorldAnchor with UUID {link.UUIDFrom} does not exist in database"); + } + } + else + { + // Skip link, invalid type + continue; + } + } + + return results; + } + + // Check links where the target UUID appeared as "From" + private List FindRelocInfoAsFrom(Guid targetUUID, ModeWorldStorage mode, Matrix4x4 matrix, List capabilities, ref List history) + { + TrackableService _trackableService = TrackableService.Singleton as TrackableService; + WorldAnchorService _worldAnchorService = WorldAnchorService.Singleton as WorldAnchorService; + WorldLinkService _worldLinkService = WorldLinkService.Singleton as WorldLinkService; + + var results = new List(); + + // Check links where the anchor appeared as "From" + List linksTo; + + linksTo = _worldLinkService.GetWorldLinkUUIDFrom(targetUUID); + foreach (var link in linksTo) + { + var found = history.Find(uuid => uuid == link.UUIDTo); + if (found != Guid.Empty) + { + // Skip link, already processed! + Console.WriteLine($"Skip link ({link.UUIDFrom} -> {link.UUIDTo}) as it was already processed"); + continue; + } + + history.Add(link.UUIDTo); + + if (link.TypeTo == TypeWorldStorage.TRACKABLEEnum) + { + // handle "To" as a trackable + Trackable trackable = _trackableService.Get(link.UUIDTo); + + if (trackable != null) + { + // Check if trackable is compatible with the request + if (!trackable.Match(capabilities)) + { + continue; + } + + Matrix4x4 m; + + if (mode == ModeWorldStorage.REQUESTTOTRACKABLESEnum) + { + m = link.Matrix() * matrix; + } + else if (mode == ModeWorldStorage.TRACKABLESTOREQUESTEnum) + { + Matrix4x4 invert; + if (Matrix4x4.Invert(link.Matrix(), out invert)) + { + m = matrix * invert; + } + else + { + Console.WriteLine("Fail to invert 3D transform!"); + continue; + } + } + else + { + // Invalid mode + continue; + } + + // We found a valid trackable, add it to the results + var result = new RelocalizationInformationRelocObjectsInner(); + result.Trackable = trackable; + result.Mode = mode; + result.Transform3D = new List() + { + 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, + }; + + results.Add(result); + + // Recursive search starting with the attached trackable + results.AddRange(FindRelocInfo(trackable.UUID, mode, m, capabilities, ref history)); + } + else + { + Console.WriteLine($"Trackable with UUID {link.UUIDTo} does not exist in database"); + } + } + else if (link.TypeTo == TypeWorldStorage.ANCHOREnum) + { + WorldAnchor anchor = _worldAnchorService.Get(link.UUIDTo); + + if (anchor != null) + { + + Matrix4x4 m; + m = link.Matrix() * matrix; + + if (mode == ModeWorldStorage.REQUESTTOTRACKABLESEnum) + { + m = link.Matrix() * matrix; + } + else if (mode == ModeWorldStorage.TRACKABLESTOREQUESTEnum) + { + Matrix4x4 invert; + if (Matrix4x4.Invert(link.Matrix(), out invert)) + { + m = matrix * invert; + } + else + { + Console.WriteLine("Fail to invert 3D transform!"); + continue; + } + } + else + { + // Invalid mode + continue; + } + + // Recursive search starting with the attached anchor + results.AddRange(FindRelocInfo(anchor.UUID, mode, m, capabilities, ref history)); + } + else + { + Console.WriteLine($"WorldAnchor with UUID {link.UUIDTo} does not exist in database"); + } + } + else + { + // Skip link, invalid type + continue; + } + } + + return results; + } + } +} diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ModelsExt/TrackableExt.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ModelsExt/TrackableExt.cs index a6561a19e9b49db031ce02134a178dc1d0361ddc..db4d674300258f0510450525b767d85cafba99a4 100644 --- a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ModelsExt/TrackableExt.cs +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ModelsExt/TrackableExt.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Numerics; using System.Text; using System.Collections.Generic; using System.ComponentModel; @@ -38,5 +39,35 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Models /// //[BsonIgnore] - don't ignore, so mongo replace can use it! public ObjectId _mongoID { get => _id; set => _id = value; } + + public bool Match(List capabilities) + { + if (capabilities.Count == 0) + { + return true; + } + + foreach(var capability in capabilities) + { + if (capability.TrackableType == TrackableType && + capability.EncodingInformation.DataFormat == TrackableEncodingInformation.DataFormat && + capability.EncodingInformation.VarVersion == TrackableEncodingInformation.VarVersion) + { + return true; + } + } + + return false; + } + + public Matrix4x4 Matrix() + { + return new Matrix4x4( + LocalCRS[0], LocalCRS[1], LocalCRS[2], LocalCRS[3], + LocalCRS[4], LocalCRS[5], LocalCRS[6], LocalCRS[7], + LocalCRS[8], LocalCRS[9], LocalCRS[10], LocalCRS[11], + LocalCRS[12], LocalCRS[13], LocalCRS[14], LocalCRS[15] + ); + } } } \ No newline at end of file diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ModelsExt/WorldLinksExt.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ModelsExt/WorldLinksExt.cs index 6ae00227942c16651e547456da25d616e124c288..3191d4a2f6130d08b76832ecaadc94e20ca559e3 100644 --- a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ModelsExt/WorldLinksExt.cs +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/ETSI-ARF/ModelsExt/WorldLinksExt.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Numerics; using System.Text; using System.Collections.Generic; using System.ComponentModel; @@ -44,5 +45,15 @@ namespace ETSI.ARF.OpenAPI.WorldStorage.Models /// //[BsonIgnore] - don't ignore, so mongo replace can use it! public ObjectId _mongoID { get => _id; set => _id = value; } + + public Matrix4x4 Matrix() + { + return new Matrix4x4( + Transform[0], Transform[1], Transform[2], Transform[3], + Transform[4], Transform[5], Transform[6], Transform[7], + Transform[8], Transform[9], Transform[10], Transform[11], + Transform[12], Transform[13], Transform[14], Transform[15] + ); + } } } \ No newline at end of file diff --git a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Startup.cs b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Startup.cs index 2978f0ad5a5b4b25c7cbc8693f1673badfdf250c..701c68495ee2ff253082dfcf4313efafd35e126f 100644 --- a/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Startup.cs +++ b/server/worldstorage/src/ETSI.ARF.OpenAPI.WorldStorage/Startup.cs @@ -25,6 +25,8 @@ using ETSI.ARF.OpenAPI.WorldStorage.Filters; using ETSI.ARF.OpenAPI.WorldStorage.OpenApi; using ETSI.ARF.OpenAPI.WorldStorage.Formatters; using ETSI.ARF.OpenAPI.WorldStorage.Services; +using ETSI.ARF.OpenAPI.WorldStorage.Models; +using ETSI.ARF.OpenAPI.WorldStorage.Binders; using MongoDB.Bson.Serialization.Serializers; using MongoDB.Bson.Serialization; using MongoDB.Bson; @@ -149,8 +151,15 @@ namespace ETSI.ARF.OpenAPI.WorldStorage // Use [ValidateModelState] on Actions to actually validate it in C# as well! c.OperationFilter(); }); - services - .AddSwaggerGenNewtonsoftSupport(); + services + .AddSwaggerGenNewtonsoftSupport(); + + services + .AddControllers(options => + { + options.ModelBinderProviders.Insert(0, new FromJsonBinderProviderT()); + options.ModelBinderProviders.Insert(0, new FromJsonBinderProviderT()); + }); } ///