diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiSendCollectionManager.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiSendCollectionManager.cs index 472e3fc2d..1268be47b 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiSendCollectionManager.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiSendCollectionManager.cs @@ -1,5 +1,6 @@ using Speckle.Converters.Common; using Speckle.Converters.CSiShared; +using Speckle.Sdk.Models; using Speckle.Sdk.Models.Collections; namespace Speckle.Connectors.CSiShared.HostApp; @@ -13,27 +14,30 @@ namespace Speckle.Connectors.CSiShared.HostApp; /// public class CSiSendCollectionManager { - private readonly IConverterSettingsStore _converterSettings; - private readonly Dictionary _collectionCache = new(); + protected IConverterSettingsStore ConverterSettings { get; } + protected Dictionary CollectionCache { get; } = new(); public CSiSendCollectionManager(IConverterSettingsStore converterSettings) { - _converterSettings = converterSettings; + ConverterSettings = converterSettings; } - // TODO: Frames could be further classified under Columns, Braces and Beams. Same for Shells which could be classified into walls, floors - public Collection AddObjectCollectionToRoot(ICSiWrapper csiObject, Collection rootObject) + public virtual Collection AddObjectCollectionToRoot(Base convertedObject, Collection rootObject) { - var path = csiObject.GetType().Name.Replace("Wrapper", ""); // CSiJointWrapper → CSiJoint, CSiFrameWrapper → CSiFrame etc. + var path = GetCollectionPath(convertedObject); - if (_collectionCache.TryGetValue(path, out Collection? collection)) + if (CollectionCache.TryGetValue(path, out Collection? collection)) { return collection; } - Collection childCollection = new(path); + Collection childCollection = CreateCollection(convertedObject); rootObject.elements.Add(childCollection); - _collectionCache[path] = childCollection; + CollectionCache[path] = childCollection; return childCollection; } + + protected virtual string GetCollectionPath(Base convertedObject) => convertedObject["type"]?.ToString() ?? "Unknown"; + + protected virtual Collection CreateCollection(Base convertedObject) => new(GetCollectionPath(convertedObject)); } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CSiRootObjectBuilder.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CSiRootObjectBuilder.cs index a5cb5c58c..434589715 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CSiRootObjectBuilder.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CSiRootObjectBuilder.cs @@ -85,7 +85,7 @@ public async Task Build( private SendConversionResult ConvertCSiObject(ICSiWrapper csiObject, Collection typeCollection, string projectId) { string applicationId = $"{csiObject.ObjectType}{csiObject.Name}"; // TODO: NO! Use GUID - string sourceType = csiObject.GetType().Name; + string sourceType = csiObject.ObjectName; try { @@ -99,7 +99,7 @@ private SendConversionResult ConvertCSiObject(ICSiWrapper csiObject, Collection converted = _rootToSpeckleConverter.Convert(csiObject); } - var collection = _sendCollectionManager.AddObjectCollectionToRoot(csiObject, typeCollection); + var collection = _sendCollectionManager.AddObjectCollectionToRoot(converted, typeCollection); collection.elements ??= new List(); collection.elements.Add(converted); diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CSiPluginBase.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CSiPluginBase.cs index 28be43ebf..35be6a110 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CSiPluginBase.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CSiPluginBase.cs @@ -27,7 +27,7 @@ public void Main(ref cSapModel sapModel, ref cPluginCallback pluginCallback) public virtual int Info(ref string text) { - text = "Hey Speckler! This is our next-gen ETABS Connector."; + text = "Hey Speckler! This is our next-gen CSi Connector."; return 0; } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs index 12ad13f2e..bf47594cd 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs @@ -1,4 +1,5 @@ -using System.Windows.Forms.Integration; +using System.ComponentModel; +using System.Windows.Forms.Integration; using Microsoft.Extensions.DependencyInjection; using Speckle.Connectors.Common; using Speckle.Connectors.CSiShared.HostApp; @@ -8,6 +9,7 @@ namespace Speckle.Connectors.CSiShared; +[DesignerCategory("")] public abstract class SpeckleFormBase : Form { protected ElementHost Host { get; set; } @@ -20,9 +22,7 @@ protected SpeckleFormBase() Text = "Speckle (Beta)"; var services = new ServiceCollection(); - services.Initialize(HostApplications.ETABS, GetVersion()); - services.AddCSi(); - services.AddCSiConverters(); + ConfigureServices(services); Container = services.BuildServiceProvider(); @@ -32,6 +32,17 @@ protected SpeckleFormBase() FormClosing += Form1Closing; } + protected virtual void ConfigureServices(IServiceCollection services) + { + services.Initialize(GetHostApplication(), GetVersion()); + services.AddCSi(); + services.AddCSiConverters(); + } + + protected abstract HostApplication GetHostApplication(); + + protected abstract HostAppVersion GetVersion(); + public void SetSapModel(ref cSapModel sapModel, ref cPluginCallback pluginCallback) { _sapModel = sapModel; @@ -47,8 +58,6 @@ protected void Form1Closing(object? sender, FormClosingEventArgs e) _pluginCallback.Finish(0); } - protected abstract HostAppVersion GetVersion(); - public new void ShowDialog() { base.ShowDialog(); diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs index b61dfdc2d..b97205397 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs @@ -1,4 +1,4 @@ -using Speckle.Connectors.CSiShared; +using Speckle.Connectors.ETABSShared; using Speckle.Sdk.Host; // NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin @@ -6,7 +6,7 @@ #pragma warning disable IDE0130 namespace Speckle.Connectors.ETABS21; -public class SpeckleForm : SpeckleFormBase +public class SpeckleForm : ETABSSpeckleFormBase { - protected override HostAppVersion GetVersion() => HostAppVersion.v2021; + protected override HostAppVersion GetVersion() => HostAppVersion.v2021; // TODO: We need a v21 } diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs index eb7f18b63..9f40a27fa 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs @@ -1,4 +1,4 @@ -using Speckle.Connectors.CSiShared; +using Speckle.Connectors.ETABSShared; // NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin // Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed @@ -6,7 +6,7 @@ namespace Speckle.Connectors.ETABS21; [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] -public class cPlugin : CSiPluginBase +public class cPlugin : ETABSPluginBase { - protected override SpeckleFormBase CreateForm() => new SpeckleForm(); + protected override ETABSSpeckleFormBase CreateETABSForm() => new SpeckleForm(); } diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj b/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj index 6d295e8b5..d51ff3065 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj @@ -31,4 +31,6 @@ + + diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs index aebd87a5c..6ed11e0b7 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs @@ -1,4 +1,4 @@ -using Speckle.Connectors.CSiShared; +using Speckle.Connectors.ETABSShared; using Speckle.Sdk.Host; // NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin @@ -6,7 +6,7 @@ #pragma warning disable IDE0130 namespace Speckle.Connectors.ETABS22; -public class SpeckleForm : SpeckleFormBase +public class SpeckleForm : ETABSSpeckleFormBase { - protected override HostAppVersion GetVersion() => HostAppVersion.v2021; + protected override HostAppVersion GetVersion() => HostAppVersion.v2021; // TODO: v22 } diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs index d6ab41c8d..7d1eabd5c 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs @@ -1,4 +1,4 @@ -using Speckle.Connectors.CSiShared; +using Speckle.Connectors.ETABSShared; // NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin // Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed @@ -6,7 +6,7 @@ namespace Speckle.Connectors.ETABS22; [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] -public class cPlugin : CSiPluginBase +public class cPlugin : ETABSPluginBase { - protected override SpeckleFormBase CreateForm() => new SpeckleForm(); + protected override ETABSSpeckleFormBase CreateETABSForm() => new SpeckleForm(); } diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj b/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj index 9705a4de9..9c27a9766 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj @@ -30,4 +30,6 @@ + + diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/ETABSSendCollectionManager.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/ETABSSendCollectionManager.cs new file mode 100644 index 000000000..d2f24ac02 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/ETABSSendCollectionManager.cs @@ -0,0 +1,117 @@ +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Sdk.Models; +using Speckle.Sdk.Models.Collections; + +namespace Speckle.Connectors.ETABSShared.HostApp; + +/// +/// ETABS-specific collection manager that organizes structural elements by story and type. +/// +/// +/// Creates a two-level hierarchy: +/// 1. Story level (e.g., "Story 1", "Story 2") +/// 2. Element type level with ETABS-specific categorization: +/// - Frames: Columns, Beams, Braces +/// - Shells: Walls, Floors, Ramps +/// +/// Elements without story assignment are placed in "Unassigned" collection. +/// Uses caching to maintain collection references and prevent duplicates. +/// +public class ETABSSendCollectionManager : CSiSendCollectionManager +{ + public ETABSSendCollectionManager(IConverterSettingsStore converterSettings) + : base(converterSettings) { } + + // TODO: This is gross. Too many strings. Improve as part of next milestone. Out of scope of "First Send". + public override Collection AddObjectCollectionToRoot(Base convertedObject, Collection rootObject) + { + var properties = convertedObject["properties"] as Dictionary; + string story = GetStoryName(properties); + string objectType = GetObjectType(convertedObject, properties); + + var storyCollection = GetOrCreateStoryCollection(story, rootObject); + + var typeCollection = GetOrCreateTypeCollection(objectType, storyCollection); + + return typeCollection; + } + + // TODO: This is gross. Too many strings. Improve as part of next milestone. Out of scope of "First Send". + private Collection GetOrCreateStoryCollection(string story, Collection rootObject) + { + string storyPath = $"Story_{story}"; + if (CollectionCache.TryGetValue(storyPath, out Collection? existingCollection)) + { + return existingCollection; + } + + var storyCollection = new Collection(story); + rootObject.elements.Add(storyCollection); + CollectionCache[storyPath] = storyCollection; + return storyCollection; + } + + // TODO: This is gross. Too many strings. Improve as part of next milestone. Out of scope of "First Send". + private Collection GetOrCreateTypeCollection(string objectType, Collection storyCollection) + { + string typePath = $"{storyCollection["name"]}_{objectType}"; + if (CollectionCache.TryGetValue(typePath, out Collection? existingCollection)) + { + return existingCollection; + } + + var typeCollection = new Collection(objectType); + storyCollection.elements.Add(typeCollection); + CollectionCache[typePath] = typeCollection; + return typeCollection; + } + + // TODO: This is gross. Too many strings. Improve as part of next milestone. Out of scope of "First Send". + private string GetObjectType(Base convertedObject, Dictionary? properties) + { + string baseType = convertedObject["type"]?.ToString() ?? "Unknown"; + + if (baseType == "Frame" && properties != null) + { + if (properties.TryGetValue("designOrientation", out var orientation)) + { + return orientation?.ToString() switch + { + "Column" => "Columns", + "Beam" => "Beams", + "Brace" => "Braces", + "Null" => "Null", + _ => "Frames (Other)" + }; + } + } + else if (baseType == "Shell" && properties != null) + { + if (properties.TryGetValue("designOrientation", out var orientation)) + { + return orientation?.ToString() switch + { + "Wall" => "Walls", + "Floor" => "Floors", + "Ramp_DO_NOT_USE" => "Ramps", + "Null" => "Null", + _ => "Shells (Other)" + }; + } + } + + return baseType; + } + + // TODO: This is gross. Too many strings. Improve as part of next milestone. Out of scope of "First Send". + private string GetStoryName(Dictionary? properties) + { + if (properties != null && properties.TryGetValue("story", out var story)) + { + return story?.ToString() ?? "Unassigned"; + } + return "Unassigned"; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSPluginBase.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSPluginBase.cs new file mode 100644 index 000000000..9620df7b5 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSPluginBase.cs @@ -0,0 +1,17 @@ +using Speckle.Connectors.CSiShared; + +namespace Speckle.Connectors.ETABSShared; + +[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] +public abstract class ETABSPluginBase : CSiPluginBase +{ + public override int Info(ref string text) + { + text = "Hey Speckler! This is our next-gen ETABS Connector."; + return 0; + } + + protected override SpeckleFormBase CreateForm() => CreateETABSForm(); + + protected abstract ETABSSpeckleFormBase CreateETABSForm(); +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSSpeckleFormBase.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSSpeckleFormBase.cs new file mode 100644 index 000000000..eebe168b4 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSSpeckleFormBase.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.CSiShared; +using Speckle.Sdk.Host; + +namespace Speckle.Connectors.ETABSShared; + +public abstract class ETABSSpeckleFormBase : SpeckleFormBase +{ + protected override HostApplication GetHostApplication() => HostApplications.ETABS; + + protected override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + services.AddETABS(); + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs new file mode 100644 index 000000000..75c2f8df2 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using Speckle.Connectors.CSiShared.HostApp; +using Speckle.Connectors.ETABSShared.HostApp; +using Speckle.Converters.ETABSShared; + +namespace Speckle.Connectors.ETABSShared; + +public static class ServiceRegistration +{ + public static IServiceCollection AddETABS(this IServiceCollection services) + { + services.AddETABSConverters(); + services.AddScoped(); + + return services; + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.projitems b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.projitems new file mode 100644 index 000000000..b10ec07bf --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.projitems @@ -0,0 +1,17 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 5d1e0b0d-56a7-4e13-b9a9-8633e02b8f17 + + + Speckle.Connectors.ETABSShared + + + + + + + + diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.shproj b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.shproj new file mode 100644 index 000000000..64ebd540a --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.shproj @@ -0,0 +1,13 @@ + + + + 5d1e0b0d-56a7-4e13-b9a9-8633e02b8f17 + 14.0 + + + + + + + + diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CSiWrappers.cs b/Converters/CSi/Speckle.Converters.CSiShared/CSiWrappers.cs index d8aa5b7a1..cd7c8f062 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/CSiWrappers.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/CSiWrappers.cs @@ -4,10 +4,11 @@ public interface ICSiWrapper { string Name { get; set; } int ObjectType { get; } + string ObjectName { get; } // TODO: Better approach to objectType number and name. Enum? } /// -/// Based on GetSelected() returns of objectType and objectName, we need to create a CSiWrapper object. +/// Based on the GetSelected() returns of objectType and objectName, we need to create a CSiWrapper object. /// /// /// Creating a class that can be used to pass a type to the converter. @@ -18,41 +19,49 @@ public abstract class CSiWrapperBase : ICSiWrapper { public required string Name { get; set; } public abstract int ObjectType { get; } + public abstract string ObjectName { get; } } public class CSiJointWrapper : CSiWrapperBase { public override int ObjectType => 1; + public override string ObjectName => "Joint"; } public class CSiFrameWrapper : CSiWrapperBase { public override int ObjectType => 2; + public override string ObjectName => "Frame"; } public class CSiCableWrapper : CSiWrapperBase { public override int ObjectType => 3; + public override string ObjectName => "Cable"; } public class CSiTendonWrapper : CSiWrapperBase { public override int ObjectType => 4; + public override string ObjectName => "Tendon"; } public class CSiShellWrapper : CSiWrapperBase { public override int ObjectType => 5; + public override string ObjectName => "Shell"; } public class CSiSolidWrapper : CSiWrapperBase { public override int ObjectType => 6; + public override string ObjectName => "Solid"; } public class CSiLinkWrapper : CSiWrapperBase { public override int ObjectType => 7; + public override string ObjectName => "Link"; } /// @@ -69,11 +78,11 @@ public static ICSiWrapper Create(int objectType, string name) => { 1 => new CSiJointWrapper { Name = name }, 2 => new CSiFrameWrapper { Name = name }, - 3 => new CSiCableWrapper { Name = name }, - 4 => new CSiTendonWrapper { Name = name }, + 3 => new CSiCableWrapper { Name = name }, // TODO: CSiCableWrapper + 4 => new CSiTendonWrapper { Name = name }, // TODO: CSiTendonWrapper 5 => new CSiShellWrapper { Name = name }, - 6 => new CSiSolidWrapper { Name = name }, - 7 => new CSiLinkWrapper { Name = name }, + 6 => new CSiSolidWrapper { Name = name }, // TODO: CSiSolidWrapper + 7 => new CSiLinkWrapper { Name = name }, // TODO: CSiLinkWrapper _ => throw new ArgumentOutOfRangeException(nameof(objectType), $"Unsupported object type: {objectType}") }; } diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs b/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs index aa6819e91..65cade601 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs @@ -14,12 +14,15 @@ public static IServiceCollection AddCSiConverters(this IServiceCollection servic { var converterAssembly = Assembly.GetExecutingAssembly(); + // Register top-level converters serviceCollection.AddTransient(); - serviceCollection.AddScoped(); + serviceCollection.AddRootCommon(converterAssembly); - // TODO: Property extractor + // Register extractors + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); - serviceCollection.AddRootCommon(converterAssembly); + // Register application-level converters serviceCollection.AddApplicationConverters(converterAssembly); serviceCollection.AddScoped< IConverterSettingsStore, diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems b/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems index 0a27119d4..0b3d91e2e 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems +++ b/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems @@ -16,10 +16,14 @@ + + + + - - - + + + diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs similarity index 63% rename from Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs rename to Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs index 9655c60cb..3b302c297 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs @@ -2,15 +2,25 @@ using Speckle.Converters.Common.Objects; using Speckle.Objects.Geometry; -namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; +namespace Speckle.Converters.CSiShared.ToSpeckle.Geometry; -// NOTE: This is HORRIBLE but serves just as a poc! We need point caching and weak referencing to joint objects -public class FrameToSpeckleConverter : ITypedConverter +/// +/// Every frame has as its displayValue a Speckle lines by defined by the endpoint coordinates. +/// +/// +/// Creates a line from frame endpoints using the CSi API: +/// 1. Gets frame endpoint names +/// 2. Extracts coordinate values for each endpoint +/// 3. Creates a Speckle line with appropriate units +/// Throws ArgumentException if coordinate extraction fails. +/// The TODOs noted below will be completed as part of the "Data Extraction (Send)" milestone. +/// +public class LineToSpeckleConverter : ITypedConverter { private readonly IConverterSettingsStore _settingsStore; private readonly ITypedConverter _pointConverter; - public FrameToSpeckleConverter( + public LineToSpeckleConverter( IConverterSettingsStore settingsStore, ITypedConverter pointConverter ) @@ -19,9 +29,9 @@ ITypedConverter pointConverter _pointConverter = pointConverter; } - public Line Convert(CSiFrameWrapper target) // NOTE: THIS IS TEMPORARY POC + public Line Convert(CSiFrameWrapper target) { - // frame points + // TODO: Better exception handling string startPoint = "", endPoint = ""; if (_settingsStore.Current.SapModel.FrameObj.GetPoints(target.Name, ref startPoint, ref endPoint) != 0) @@ -29,7 +39,7 @@ public Line Convert(CSiFrameWrapper target) // NOTE: THIS IS TEMPORARY POC throw new ArgumentException($"Failed to convert frame {target.Name}"); } - // start point coordinates + // TODO: Point caching. This is gross! double startX = 0, startY = 0, startZ = 0; @@ -38,7 +48,7 @@ public Line Convert(CSiFrameWrapper target) // NOTE: THIS IS TEMPORARY POC throw new ArgumentException($"Failed to convert point {startPoint}"); } - // end point coordinates + // TODO: Point caching. This is gross! double endX = 0, endY = 0, endZ = 0; @@ -47,7 +57,6 @@ public Line Convert(CSiFrameWrapper target) // NOTE: THIS IS TEMPORARY POC throw new ArgumentException($"Failed to convert point {endPoint}"); } - // Create and return the line return new() { start = new Point(startX, startY, startZ, _settingsStore.Current.SpeckleUnits), diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/MeshToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/MeshToSpeckleConverter.cs new file mode 100644 index 000000000..831689689 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/MeshToSpeckleConverter.cs @@ -0,0 +1,85 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Geometry; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Geometry; + +/// +/// Every shell has as its displayValue a planar 2D Speckle mesh defined by the vertices. +/// +/// +/// Creates a mesh from shell vertices using the CSi API: +/// 1. Gets shell vertex point names +/// 2. Extracts coordinates for each vertex +/// 3. Constructs mesh using flat vertex list (x,y,z triplets) and face indices +/// +/// TODO: Implement point caching and weak referencing to joint objects for better performance +/// TODO: Investigate if SAP2000 has other freeform non-planar surface definitions? +/// The TODOs noted will be completed as part of the "Data Extraction (Send)" milestone. +/// +/// Face indices format: +/// - First value is the number of vertices in the face +/// - Followed by indices into the vertex list +/// +/// Throws ArgumentException if vertex extraction fails. +/// +public class MeshToSpeckleConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingsStore; + + public MeshToSpeckleConverter(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public Mesh Convert(CSiShellWrapper target) + { + int numberPoints = 0; + string[] pointNames = Array.Empty(); + int result = _settingsStore.Current.SapModel.AreaObj.GetPoints(target.Name, ref numberPoints, ref pointNames); + + if (result != 0) + { + throw new ArgumentException($"Failed to convert {target.Name} to Speckle Mesh"); + } + + List vertices = new List(numberPoints * 3); + List faces = new List(numberPoints + 1); + + for (int i = 0; i < numberPoints; i++) + { + double pointX = 0; + double pointY = 0; + double pointZ = 0; + + result = _settingsStore.Current.SapModel.PointObj.GetCoordCartesian( + pointNames[i], + ref pointX, + ref pointY, + ref pointZ + ); + + if (result != 0) + { + throw new ArgumentException($"Failed to retrieve coordinate of vertex point name {pointNames[i]}."); + } + + vertices.Add(pointX); + vertices.Add(pointY); + vertices.Add(pointZ); + } + + faces.Add(numberPoints); + for (int i = 0; i < numberPoints; i++) + { + faces.Add(i); + } + + return new Mesh() + { + vertices = vertices, + faces = faces, + units = _settingsStore.Current.SpeckleUnits + }; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/JointToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs similarity index 51% rename from Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/JointToSpeckleConverter.cs rename to Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs index b82da95e9..1a0f1aa5c 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/JointToSpeckleConverter.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs @@ -2,24 +2,32 @@ using Speckle.Converters.Common.Objects; using Speckle.Objects.Geometry; -namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; - -// NOTE: This is HORRIBLE but serves just as a poc! -public class JointToSpeckleConverter : ITypedConverter +namespace Speckle.Converters.CSiShared.ToSpeckle.Geometry; + +/// +/// Every joint has as its displayValue a Speckle point defined by extracting their coordinates. +/// +/// +/// Creates a point from joint coordinates using the CSi API: +/// 1. Extracts cartesian coordinates +/// 2. Creates a Speckle point with appropriate units +/// +/// TODO: Current implementation is a proof of concept, needs refinement +/// The TODOs noted will be completed as part of the "Data Extraction (Send)" milestone. +/// +/// Throws ArgumentException if coordinate extraction fails. +/// +public class PointToSpeckleConverter : ITypedConverter { private readonly IConverterSettingsStore _settingStore; - public JointToSpeckleConverter(IConverterSettingsStore settingStore) + public PointToSpeckleConverter(IConverterSettingsStore settingStore) { _settingStore = settingStore; } public Point Convert(CSiJointWrapper target) // NOTE: This is just a temporary POC { - string applicationId = ""; - - _ = _settingStore.Current.SapModel.PointObj.GetGUID(target.Name, ref applicationId); - double pointX = 0; double pointY = 0; double pointZ = 0; @@ -36,6 +44,6 @@ ref pointZ throw new ArgumentException($"Failed to convert {target.Name} to {typeof(Point)}"); } - return new(pointX, pointY, pointZ, _settingStore.Current.SpeckleUnits, applicationId); + return new(pointX, pointY, pointZ, _settingStore.Current.SpeckleUnits); } } diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs new file mode 100644 index 000000000..81d4abf97 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs @@ -0,0 +1,32 @@ +using Speckle.Converters.CSiShared.ToSpeckle.Raw; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +public class ClassPropertyExtractor +{ + private readonly CSiFrameToSpeckleConverter _frameConverter; + private readonly CSiJointToSpeckleConverter _jointConverter; + private readonly CSiShellToSpeckleConverter _shellConverter; + + public ClassPropertyExtractor( + CSiFrameToSpeckleConverter frameConverter, + CSiJointToSpeckleConverter jointConverter, + CSiShellToSpeckleConverter shellConverter + ) + { + _frameConverter = frameConverter; + _jointConverter = jointConverter; + _shellConverter = shellConverter; + } + + public Dictionary GetProperties(ICSiWrapper wrapper) + { + return wrapper switch + { + CSiJointWrapper joint => _jointConverter.GetClassProperties(joint), + CSiFrameWrapper frame => _frameConverter.GetClassProperties(frame), + CSiShellWrapper shell => _shellConverter.GetClassProperties(shell), + _ => new Dictionary() + }; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiFrameToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiFrameToSpeckleConverter.cs new file mode 100644 index 000000000..dc46d1960 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiFrameToSpeckleConverter.cs @@ -0,0 +1,53 @@ +using Speckle.Converters.Common; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; + +/// +/// Base converter for extracting frame properties common to all CSi applications. +/// Product-specific implementations (ETABS, SAP2000) can extend this to add their own properties. +/// +public abstract class CSiFrameToSpeckleConverter +{ + protected CSiFrameToSpeckleConverter(IConverterSettingsStore settingsStore) + { + SettingsStore = settingsStore; + } + + protected IConverterSettingsStore SettingsStore { get; } + + /// + /// Extracts both common and product-specific properties from a frame element. + /// Product-specific properties are defined by derived classes. + /// + /// + /// This will be refined! Just a POC for now. Data Extraction (Send) milestone will incorporate improvements here. + /// + public Dictionary GetClassProperties(CSiFrameWrapper frame) + { + var properties = new Dictionary(); + + AddCommonClassProperties(frame, properties); + AddProductSpecificClassProperties(frame, properties); + + return properties; + } + + private void AddCommonClassProperties(CSiFrameWrapper frame, Dictionary properties) + { + // TODO: As part of data extraction. But a placeholder example below: + int numberGroups = 0; + string[] groups = Array.Empty(); + + int result = SettingsStore.Current.SapModel.FrameObj.GetGroupAssign(frame.Name, ref numberGroups, ref groups); + + if (result == 0 && groups.Length > 0) + { + properties["groupAssigns"] = new List(groups); + } + } + + protected virtual void AddProductSpecificClassProperties( + CSiFrameWrapper frame, + Dictionary properties + ) { } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiJointToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiJointToSpeckleConverter.cs new file mode 100644 index 000000000..8ea827193 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiJointToSpeckleConverter.cs @@ -0,0 +1,53 @@ +using Speckle.Converters.Common; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; + +/// +/// Base converter for extracting joint properties common to all CSi applications. +/// Product-specific implementations (ETABS, SAP2000) can extend this to add their own properties. +/// +public abstract class CSiJointToSpeckleConverter +{ + protected CSiJointToSpeckleConverter(IConverterSettingsStore settingsStore) + { + SettingsStore = settingsStore; + } + + protected IConverterSettingsStore SettingsStore { get; } + + /// + /// Extracts both common and product-specific properties from a frame element. + /// Product-specific properties are defined by derived classes. + /// + /// + /// This will be refined! Just a POC for now. Data Extraction (Send) milestone will incorporate improvements here. + /// + public Dictionary GetClassProperties(CSiJointWrapper joint) + { + var properties = new Dictionary(); + + AddCommonClassProperties(joint, properties); + AddProductSpecificClassProperties(joint, properties); + + return properties; + } + + private void AddCommonClassProperties(CSiJointWrapper joint, Dictionary properties) + { + // TODO: As part of data extraction. But a placeholder example below: + int numberGroups = 0; + string[] groups = Array.Empty(); + + int result = SettingsStore.Current.SapModel.FrameObj.GetGroupAssign(joint.Name, ref numberGroups, ref groups); + + if (result == 0 && groups.Length > 0) + { + properties["groupAssigns"] = new List(groups); + } + } + + protected virtual void AddProductSpecificClassProperties( + CSiJointWrapper joint, + Dictionary properties + ) { } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiShellToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiShellToSpeckleConverter.cs new file mode 100644 index 000000000..6b9169579 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiShellToSpeckleConverter.cs @@ -0,0 +1,53 @@ +using Speckle.Converters.Common; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; + +/// +/// Base converter for extracting shell properties common to all CSi applications. +/// Product-specific implementations (ETABS, SAP2000) can extend this to add their own properties. +/// +public abstract class CSiShellToSpeckleConverter +{ + protected CSiShellToSpeckleConverter(IConverterSettingsStore settingsStore) + { + SettingsStore = settingsStore; + } + + protected IConverterSettingsStore SettingsStore { get; } + + /// + /// Extracts both common and product-specific properties from a shell element. + /// Product-specific properties are defined by derived classes. + /// + /// + /// This will be refined! Just a POC for now. Data Extraction (Send) milestone will incorporate improvements here. + /// + public Dictionary GetClassProperties(CSiShellWrapper shell) + { + var properties = new Dictionary(); + + AddCommonClassProperties(shell, properties); + AddProductSpecificClassProperties(shell, properties); + + return properties; + } + + private void AddCommonClassProperties(CSiShellWrapper shell, Dictionary properties) + { + // TODO: As part of data extraction. But a placeholder example below: + int numberGroups = 0; + string[] groups = Array.Empty(); + + int result = SettingsStore.Current.SapModel.AreaObj.GetGroupAssign(shell.Name, ref numberGroups, ref groups); + + if (result == 0 && groups.Length > 0) + { + properties["groupAssigns"] = new List(groups); + } + } + + protected virtual void AddProductSpecificClassProperties( + CSiShellWrapper shell, + Dictionary properties + ) { } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/ShellToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/ShellToSpeckleConverter.cs deleted file mode 100644 index 461bf0b47..000000000 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/ShellToSpeckleConverter.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects.Geometry; - -namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; - -// NOTE: This is HORRIBLE but serves just as a poc! We need point caching and weak referencing to joint objects -public class ShellToSpeckleConverter : ITypedConverter -{ - private readonly IConverterSettingsStore _settingsStore; - - public ShellToSpeckleConverter(IConverterSettingsStore settingsStore) - { - _settingsStore = settingsStore; - } - - public Mesh Convert(CSiShellWrapper target) - { - int numberPoints = 0; - string[] pointNames = Array.Empty(); - int result = _settingsStore.Current.SapModel.AreaObj.GetPoints(target.Name, ref numberPoints, ref pointNames); - - if (result != 0) - { - throw new ArgumentException($"Failed to convert {target.Name} to Speckle Mesh"); - } - - // List to store vertices defining a face - List vertices = new List(); - List faces = new List(); - - // How many vertices to define a face? - faces.Add(numberPoints); - - // Lopp through points to get coordinates - // TODO: This is gross! - foreach (string pointName in pointNames) - { - double pointX = 0; - double pointY = 0; - double pointZ = 0; - - result = _settingsStore.Current.SapModel.PointObj.GetCoordCartesian( - pointName, - ref pointX, - ref pointY, - ref pointZ - ); - - if (result != 0) - { - throw new ArgumentException($"Failed to retrieve coordinate of vertex point name {pointName}."); - } - - // Add vertex info - vertices.Add(pointX); - vertices.Add(pointY); - vertices.Add(pointZ); - - // TODO: Check normals direction? - } - - return new Mesh() - { - vertices = vertices, - faces = faces, - units = _settingsStore.Current.SpeckleUnits - }; - } -} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CSiObjectToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CSiObjectToSpeckleConverter.cs index d387b4cc5..1425c7cb4 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CSiObjectToSpeckleConverter.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CSiObjectToSpeckleConverter.cs @@ -10,33 +10,53 @@ public class CSiObjectToSpeckleConverter : IToSpeckleTopLevelConverter { private readonly IConverterSettingsStore _settingsStore; private readonly DisplayValueExtractor _displayValueExtractor; + private readonly ClassPropertyExtractor _classPropertyExtractor; - // TODO: _propertyExtractor - + /// + /// Converts CSi objects to Speckle format, extracting properties, display geometry and application IDs. + /// public CSiObjectToSpeckleConverter( IConverterSettingsStore settingsStore, - DisplayValueExtractor displayValueExtractor - // TODO: _propertyExtractor + DisplayValueExtractor displayValueExtractor, + ClassPropertyExtractor classPropertyExtractor ) { _settingsStore = settingsStore; _displayValueExtractor = displayValueExtractor; - // TODO: _property_extractor + _classPropertyExtractor = classPropertyExtractor; } public Base Convert(object target) => Convert((CSiWrapperBase)target); - private Base Convert(CSiWrapperBase target) + /// + /// This will be refined! Just a POC for now. Data Extraction (Send) milestone will incorporate improvements here. + /// + private Base Convert(CSiWrapperBase target) // TODO: CSiObject and not Base pending SDK updates. { var result = new Base { ["name"] = target.Name, - ["type"] = target.GetType().ToString().Split('.').Last().Replace("Wrapper", ""), // CSiJointWrapper → CSiJoint, CSiFrameWrapper → CSiFrame etc. + ["type"] = target.ObjectName, ["units"] = _settingsStore.Current.SpeckleUnits, - // TODO: properties + ["properties"] = _classPropertyExtractor.GetProperties(target), ["displayValue"] = _displayValueExtractor.GetDisplayValue(target).ToList() }; + string applicationId = ""; // TODO: Investigate the GUIDs coming through + if (target is CSiJointWrapper) // TODO: Surely there is a better way of doing this? Gross. + { + _ = _settingsStore.Current.SapModel.PointObj.GetGUID(target.Name, ref applicationId); + } + else if (target is CSiFrameWrapper) + { + _ = _settingsStore.Current.SapModel.FrameObj.GetGUID(target.Name, ref applicationId); + } + else if (target is CSiShellWrapper) + { + _ = _settingsStore.Current.SapModel.AreaObj.GetGUID(target.Name, ref applicationId); + } + result["applicationId"] = applicationId; + return result; } } diff --git a/Converters/CSi/Speckle.Converters.ETABS21/Speckle.Converters.ETABS21.csproj b/Converters/CSi/Speckle.Converters.ETABS21/Speckle.Converters.ETABS21.csproj index 7803e3924..87f7e7e34 100644 --- a/Converters/CSi/Speckle.Converters.ETABS21/Speckle.Converters.ETABS21.csproj +++ b/Converters/CSi/Speckle.Converters.ETABS21/Speckle.Converters.ETABS21.csproj @@ -16,4 +16,6 @@ + + diff --git a/Converters/CSi/Speckle.Converters.ETABS22/Speckle.Converters.ETABS22.csproj b/Converters/CSi/Speckle.Converters.ETABS22/Speckle.Converters.ETABS22.csproj index 20610d5ca..1cce1da6e 100644 --- a/Converters/CSi/Speckle.Converters.ETABS22/Speckle.Converters.ETABS22.csproj +++ b/Converters/CSi/Speckle.Converters.ETABS22/Speckle.Converters.ETABS22.csproj @@ -16,4 +16,6 @@ + + diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs new file mode 100644 index 000000000..7f6fdf28c --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Speckle.Converters.CSiShared.ToSpeckle.Raw; +using Speckle.Converters.ETABSShared.ToSpeckle.Raw; +using Speckle.Sdk; + +namespace Speckle.Converters.ETABSShared; + +public static class ServiceRegistration +{ + public static IServiceCollection AddETABSConverters(this IServiceCollection serviceCollection) + { + var converterAssembly = Assembly.GetExecutingAssembly(); + + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); + + serviceCollection.AddMatchingInterfacesAsTransient(converterAssembly); + + return serviceCollection; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems new file mode 100644 index 000000000..794cf6a91 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems @@ -0,0 +1,17 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 36377858-d696-4567-ab05-637f4ec841f5 + + + Speckle.Converters.ETABSShared + + + + + + + + diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.shproj b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.shproj new file mode 100644 index 000000000..ec47ff880 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.shproj @@ -0,0 +1,13 @@ + + + + 36377858-d696-4567-ab05-637f4ec841f5 + 14.0 + + + + + + + + diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs new file mode 100644 index 000000000..fe8bea78d --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs @@ -0,0 +1,51 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Raw; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Raw; + +/// +/// ETABS-specific frame property converter that extends the base CSi implementation. +/// Adds ETABS-specific properties such as story assignment and design orientation. +/// +/// +/// Additional properties extracted: +/// - label: User-defined label for the frame +/// - story: Story/level assignment of the frame +/// - designOrientation: Frame classification (Column, Beam, Brace, etc.) +/// These properties are used to organize the model structure in collections and provide ETABS-specific information. +/// This information is not available in SAP 2000. +/// +public class FrameToSpeckleConverter : CSiFrameToSpeckleConverter +{ + public FrameToSpeckleConverter(IConverterSettingsStore settingsStore) + : base(settingsStore) { } + + /// + /// Adds ETABS-specific properties to the frame object. + /// + /// The frame wrapper containing the ETABS object reference + /// Dictionary to store the extracted properties + /// + /// This structure for AddProductSpecificClassProperties() is exactly same for Frame, Joint and Shell. Write better? + /// + protected override void AddProductSpecificClassProperties( + CSiFrameWrapper frame, + Dictionary properties + ) + { + // Get label and story + string label = "", + story = ""; + _ = SettingsStore.Current.SapModel.FrameObj.GetLabelFromName(frame.Name, ref label, ref story); + + properties["label"] = label; + properties["story"] = story; + + // Get design orientation + eFrameDesignOrientation designOrientation = eFrameDesignOrientation.Null; + _ = SettingsStore.Current.SapModel.FrameObj.GetDesignOrientation(frame.Name, ref designOrientation); + + properties["designOrientation"] = designOrientation.ToString(); + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/JointToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/JointToSpeckleConverter.cs new file mode 100644 index 000000000..b853deb16 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/JointToSpeckleConverter.cs @@ -0,0 +1,52 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Raw; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Raw; + +/// +/// ETABS-specific joint property converter that extends the base CSi implementation. +/// Adds ETABS-specific properties such as story assignment and design orientation. +/// +/// +/// Additional properties extracted: +/// - label: User-defined label for the joint +/// - story: Story/level assignment of the joint +/// - diaphragm: Joint diaphragm assignment +/// These properties are used to organize the model structure in collections and provide ETABS-specific information. +/// This information is not available in SAP 2000. +/// +public class JointToSpeckleConverter : CSiJointToSpeckleConverter +{ + public JointToSpeckleConverter(IConverterSettingsStore settingsStore) + : base(settingsStore) { } + + /// + /// Adds ETABS-specific properties to the joint object. + /// + /// The joint wrapper containing the ETABS object reference + /// Dictionary to store the extracted properties + /// + /// This structure for AddProductSpecificClassProperties() is exactly same for Frame, Joint and Shell. Write better? + /// + protected override void AddProductSpecificClassProperties( + CSiJointWrapper joint, + Dictionary properties + ) + { + // Get label and story + string label = "", + story = ""; + _ = SettingsStore.Current.SapModel.PointObj.GetLabelFromName(joint.Name, ref label, ref story); + + properties["label"] = label; + properties["story"] = story; + + // Diaphragm assignments + eDiaphragmOption diaphragmOption = eDiaphragmOption.Disconnect; + string diaphragmName = ""; + _ = SettingsStore.Current.SapModel.PointObj.GetDiaphragm(joint.Name, ref diaphragmOption, ref diaphragmName); + + properties["diaphragm"] = diaphragmName.ToString(); + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/ShellToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/ShellToSpeckleConverter.cs new file mode 100644 index 000000000..8c841b781 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/ShellToSpeckleConverter.cs @@ -0,0 +1,49 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Raw; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Raw; + +/// +/// ETABS-specific shell property converter that extends the base CSi implementation. +/// Adds ETABS-specific properties such as story assignment and design orientation. +/// +/// +/// Additional properties extracted: +/// - label: User-defined label for the shell +/// - story: Story/level assignment of the shell +/// - designOrientation: Shell classification (Wall, Floor, Null, etc.) +/// These properties are used to organize the model structure in collections and provide ETABS-specific information. +/// This information is not available in SAP 2000. +/// +public class ShellToSpeckleConverter : CSiShellToSpeckleConverter +{ + public ShellToSpeckleConverter(IConverterSettingsStore settingsStore) + : base(settingsStore) { } + + /// + /// Adds ETABS-specific properties to the shell object. + /// + /// The shell wrapper containing the ETABS object reference + /// Dictionary to store the extracted properties + /// + /// This structure for AddProductSpecificClassProperties() is exactly same for Frame, Joint and Shell. Write better? + /// + protected override void AddProductSpecificClassProperties( + CSiShellWrapper shell, + Dictionary properties + ) + { + string label = "", + story = ""; + _ = SettingsStore.Current.SapModel.AreaObj.GetLabelFromName(shell.Name, ref label, ref story); + + properties["label"] = label; + properties["story"] = story; + + eAreaDesignOrientation designOrientation = eAreaDesignOrientation.Null; + _ = SettingsStore.Current.SapModel.AreaObj.GetDesignOrientation(shell.Name, ref designOrientation); + + properties["designOrientation"] = designOrientation.ToString(); + } +} diff --git a/Local.sln b/Local.sln index 4fcb17378..8e6072242 100644 --- a/Local.sln +++ b/Local.sln @@ -214,6 +214,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.ETABS22" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.CSiShared", "Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.shproj", "{1B5C5FB2-3B22-4371-9AA5-3EDF3B4D62DE}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.ETABSShared", "Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.shproj", "{5D1E0B0D-56A7-4E13-B9A9-8633E02B8F17}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.ETABSShared", "Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.shproj", "{36377858-D696-4567-AB05-637F4EC841F5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -647,6 +651,8 @@ Global {3B81B220-92E3-43FC-86C6-1E6DBEEB1917} = {FCCE9A47-05D8-41CB-A8EE-586FC5B69545} {5E924B13-B3E8-4724-9BA7-CE82E39866EB} = {F2A1E5FC-CFEF-4590-BF0D-BE7B9F74E567} {1B5C5FB2-3B22-4371-9AA5-3EDF3B4D62DE} = {D2638AC8-28B2-4667-A47B-3FAB9F900E6F} + {5D1E0B0D-56A7-4E13-B9A9-8633E02B8F17} = {D2638AC8-28B2-4667-A47B-3FAB9F900E6F} + {36377858-D696-4567-AB05-637F4EC841F5} = {D2638AC8-28B2-4667-A47B-3FAB9F900E6F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8} diff --git a/Speckle.Connectors.sln b/Speckle.Connectors.sln index 2a7b7f877..75f0759b6 100644 --- a/Speckle.Connectors.sln +++ b/Speckle.Connectors.sln @@ -209,6 +209,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Speckle.Converters.ETABS22" EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.CSiShared", "Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.shproj", "{1B5C5FB2-3B22-4371-9AA5-3EDF3B4D62DE}" EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Connectors.ETABSShared", "Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.shproj", "{5D1E0B0D-56A7-4E13-B9A9-8633E02B8F17}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Speckle.Converters.ETABSShared", "Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.shproj", "{36377858-D696-4567-AB05-637F4EC841F5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -624,6 +628,8 @@ Global {791E3288-8001-4D54-8EAB-03D1D7F51044} = {DA6A607B-C267-4B2E-9C8A-F50B2F1BBFE0} {D61ECD90-3D17-4AF0-8B1A-0E0AD302DFF9} = {C6CD9332-874A-49DA-BEB6-3FAA5A700793} {1B5C5FB2-3B22-4371-9AA5-3EDF3B4D62DE} = {181F0468-B7A7-4CD7-ABD1-7F32B3ABB991} + {5D1E0B0D-56A7-4E13-B9A9-8633E02B8F17} = {181F0468-B7A7-4CD7-ABD1-7F32B3ABB991} + {36377858-D696-4567-AB05-637F4EC841F5} = {181F0468-B7A7-4CD7-ABD1-7F32B3ABB991} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8} @@ -641,6 +647,7 @@ Global Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{25172c49-7aa4-4739-bb07-69785094c379}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{26391930-f86f-47e0-a5f6-b89919e38ce1}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{35175682-da83-4c0a-a49d-b191f5885d8e}*SharedItemsImports = 13 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{36377858-d696-4567-ab05-637f4ec841f5}*SharedItemsImports = 13 Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.projitems*{3ab9028b-b2d2-464b-9ba3-39c192441e50}*SharedItemsImports = 13 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{41bc679f-887f-44cf-971d-a5502ee87db0}*SharedItemsImports = 13 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{4459f2b1-a340-488e-a856-eb2ae9c72ad4}*SharedItemsImports = 5 @@ -651,6 +658,7 @@ Global Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{56a909ae-6e99-4d4d-a22e-38bdc5528b8e}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{5cdec958-708e-4d19-a79e-0c1db23a6039}*SharedItemsImports = 5 Converters\Civil3d\Speckle.Converters.Civil3dShared\Speckle.Converters.Civil3dShared.projitems*{5cdec958-708e-4d19-a79e-0c1db23a6039}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{5d1e0b0d-56a7-4e13-b9a9-8633e02b8f17}*SharedItemsImports = 13 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{617bd3c7-87d9-4d28-8ac9-4910945bb9fc}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{631c295a-7ccf-4b42-8686-7034e31469e7}*SharedItemsImports = 5