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;