diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/GlobalUsing.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/GlobalUsing.cs new file mode 100644 index 00000000..9cd59254 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/GlobalUsing.cs @@ -0,0 +1 @@ +global using TSM = Tekla.Structures.Model; diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/HostApp/ComponentUnpacker.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/HostApp/ComponentUnpacker.cs new file mode 100644 index 00000000..e7803c30 --- /dev/null +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/HostApp/ComponentUnpacker.cs @@ -0,0 +1,55 @@ +using Speckle.Converter.Tekla2024.Extensions; +using Speckle.Sdk.Models.Proxies; + +namespace Speckle.Connector.Tekla2024.HostApp; + +public class ComponentUnpacker +{ + // POC: should add ILogger here in the case that component unpacker fails to unpack a component + + /// + /// Stores processed Base Components as group proxies. These include Components and Connections. + /// Expects to be scoped per send operation. Should be added to the root collection. + /// + public Dictionary ComponentProxiesCache { get; } = new(); + + public ComponentUnpacker() { } + + public IEnumerable UnpackComponents(IReadOnlyList modelObjects) + { + foreach (TSM.ModelObject modelObject in modelObjects) + { + if (modelObject is TSM.BaseComponent component) + { + // create a group proxy for this component + string appId = component.GetSpeckleApplicationId(); + List childIds = new(); + + foreach (TSM.ModelObject child in component.GetChildren()) + { + childIds.Add(child.GetSpeckleApplicationId()); + yield return child; + } + + GroupProxy componentProxy = + new() + { + name = component.Name, + objects = childIds, + applicationId = appId + }; + + componentProxy["number"] = component.Number; + + if (!ComponentProxiesCache.ContainsKey(appId)) + { + ComponentProxiesCache.Add(appId, componentProxy); + } + } + else + { + yield return modelObject; + } + } + } +} diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/Operations/Send/TeklaRootObjectBuilder.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/Operations/Send/TeklaRootObjectBuilder.cs index 40e7b247..f07d1421 100644 --- a/Connectors/Tekla/Speckle.Connector.Tekla2024/Operations/Send/TeklaRootObjectBuilder.cs +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/Operations/Send/TeklaRootObjectBuilder.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Speckle.Connector.Tekla2024.HostApp; using Speckle.Connectors.Common.Builders; using Speckle.Connectors.Common.Caching; using Speckle.Connectors.Common.Conversion; @@ -21,13 +22,15 @@ public class TeklaRootObjectBuilder : IRootObjectBuilder private readonly IConverterSettingsStore _converterSettings; private readonly ILogger _logger; private readonly ISdkActivityFactory _activityFactory; + private readonly ComponentUnpacker _componentUnpacker; public TeklaRootObjectBuilder( IRootToSpeckleConverter rootToSpeckleConverter, ISendConversionCache sendConversionCache, IConverterSettingsStore converterSettings, ILogger logger, - ISdkActivityFactory activityFactory + ISdkActivityFactory activityFactory, + ComponentUnpacker componentUnpacker ) { _sendConversionCache = sendConversionCache; @@ -35,6 +38,7 @@ ISdkActivityFactory activityFactory _rootToSpeckleConverter = rootToSpeckleConverter; _logger = logger; _activityFactory = activityFactory; + _componentUnpacker = componentUnpacker; } public async Task Build( @@ -52,12 +56,16 @@ public async Task Build( Collection rootObjectCollection = new() { name = modelName }; rootObjectCollection["units"] = _converterSettings.Current.SpeckleUnits; + // Step 0: unpack all component model objects + List unpackedTeklaObjects = _componentUnpacker.UnpackComponents(teklaObjects).ToList(); + rootObjectCollection["componentProxies"] = _componentUnpacker.ComponentProxiesCache.Values; + List results = new(teklaObjects.Count); int count = 0; using (var _ = _activityFactory.Start("Convert all")) { - foreach (ModelObject teklaObject in teklaObjects) + foreach (ModelObject teklaObject in unpackedTeklaObjects) { using var _2 = _activityFactory.Start("Convert"); cancellationToken.ThrowIfCancellationRequested(); diff --git a/Connectors/Tekla/Speckle.Connector.Tekla2024/ServiceRegistration.cs b/Connectors/Tekla/Speckle.Connector.Tekla2024/ServiceRegistration.cs index 78653cef..5cac45ec 100644 --- a/Connectors/Tekla/Speckle.Connector.Tekla2024/ServiceRegistration.cs +++ b/Connectors/Tekla/Speckle.Connector.Tekla2024/ServiceRegistration.cs @@ -66,6 +66,7 @@ public static IServiceCollection AddTekla(this IServiceCollection services) IConverterSettingsStore, ConverterSettingsStore >(); + services.AddScoped(); services.AddMatchingInterfacesAsTransient(converterAssembly); diff --git a/Converters/Tekla/Speckle.Converter.Tekla2024/Extensions/SpeckleApplicationIdExtensions.cs b/Converters/Tekla/Speckle.Converter.Tekla2024/Extensions/SpeckleApplicationIdExtensions.cs new file mode 100644 index 00000000..58a8b91a --- /dev/null +++ b/Converters/Tekla/Speckle.Converter.Tekla2024/Extensions/SpeckleApplicationIdExtensions.cs @@ -0,0 +1,7 @@ +namespace Speckle.Converter.Tekla2024.Extensions; + +public static class SpeckleApplicationIdExtensions +{ + public static string GetSpeckleApplicationId(this TSM.ModelObject modelObject) => + modelObject.Identifier.GUID.ToString(); +} diff --git a/Converters/Tekla/Speckle.Converter.Tekla2024/TeklaRootToSpeckleConverter.cs b/Converters/Tekla/Speckle.Converter.Tekla2024/TeklaRootToSpeckleConverter.cs index fabb8adc..134b36b2 100644 --- a/Converters/Tekla/Speckle.Converter.Tekla2024/TeklaRootToSpeckleConverter.cs +++ b/Converters/Tekla/Speckle.Converter.Tekla2024/TeklaRootToSpeckleConverter.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Logging; +using Speckle.Converter.Tekla2024.Extensions; using Speckle.Converters.Common; using Speckle.Converters.Common.Objects; using Speckle.Converters.Common.Registration; @@ -38,7 +39,7 @@ public Base Convert(object target) Base result = objectConverter.Convert(target); // add tekla specific identifiers - result.applicationId = modelObject.Identifier.GUID.ToString(); + result.applicationId = modelObject.GetSpeckleApplicationId(); //TODO: attach properties return result; diff --git a/Converters/Tekla/Speckle.Converter.Tekla2024/ToSpeckle/Helpers/DisplayValueExtractor.cs b/Converters/Tekla/Speckle.Converter.Tekla2024/ToSpeckle/Helpers/DisplayValueExtractor.cs index bf67d8a9..3e4ae8b2 100644 --- a/Converters/Tekla/Speckle.Converter.Tekla2024/ToSpeckle/Helpers/DisplayValueExtractor.cs +++ b/Converters/Tekla/Speckle.Converter.Tekla2024/ToSpeckle/Helpers/DisplayValueExtractor.cs @@ -26,11 +26,16 @@ public IEnumerable GetDisplayValue(TSM.ModelObject modelObject) // both beam and contour plate are child classes of part // its simpler to use part for common methods case TSM.Part part: - var solid = part.GetSolid(); - if (solid != null) + if (part.GetSolid() is TSM.Solid partSolid) { - var mesh = _meshConverter.Convert(solid); - yield return mesh; + yield return _meshConverter.Convert(partSolid); + } + break; + + case TSM.BoltGroup boltGroup: + if (boltGroup.GetSolid() is TSM.Solid boltSolid) + { + yield return _meshConverter.Convert(boltSolid); } break;