From 196c503789d14b281d0431fff03dec8c4ebaca67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinhagen?= Date: Wed, 11 Dec 2024 10:08:15 +0000 Subject: [PATCH] bjorn/cnx 882 send etabsobjects with their properties (#442) * trying a bold refactoring ETABSShared and CSiShared is strictly correct, but just looks horrible EtabsShared and CsiShared looks better * refactor: improve * property extraction foundation - better architectural approach for property extraction depending on csi product and wrapper type - correctly configures return type depending on the product (e.g. EtabsObject) * refactor(props): streamline property extraction across CSi and ETABS Reorganizes property extraction to better handle base and product-specific properties: - Introduces PropertyExtractionResult for cleaner property management - Separates shared CSi properties from product-specific ones - Implements dedicated extractors for Frame, Joint, and Shell - Standardizes property extraction patterns between CSi and ETABS - Removes property redundancy and improves null safety * frame data extraction 1/2 * remaining data extraction - finished data extraction for frames - added data extraction for joints and shells - re-instated collections * documentation - added some updates to the documentation * SpeckleApplicationIdExtensions - Extension methods for speckle applicationIds * IApplicationPropertiesExtractorConcretization - Renamed GeneralPropertiesExtractor to SharedPropertiesExtractor - Renamed EtabsClassPropertiesExtractor to ApplicationPropertiesExtractor - Removed PropertiesExtractor file - Enforced injection of shared properties extractor to application specific extractor - Output of Extract() of type PropertyExtractorResult - Application property extractor mutates dictionary * applicationId simplification * review comments - directly assigning applicationId to base - rename ApplicationPropertiesExtractor to EtabsPropertiesExtractor --------- Co-authored-by: Claire Kuang --- ...g.cs => CsiSharedBasicConnectorBinding.cs} | 4 +- ...inding.cs => CsiSharedSelectionBinding.cs} | 6 +- ...SendBinding.cs => CsiSharedSendBinding.cs} | 26 +-- ...nFilter.cs => CsiSharedSelectionFilter.cs} | 4 +- ...ionService.cs => CsiApplicationService.cs} | 6 +- ...ModelStore.cs => CsiDocumentModelStore.cs} | 12 +- .../{CSiIdleManager.cs => CsiIdleManager.cs} | 4 +- ...Manager.cs => CsiSendCollectionManager.cs} | 6 +- ...jectBuilder.cs => CsiRootObjectBuilder.cs} | 26 +-- .../{CSiPluginBase.cs => CsiPluginBase.cs} | 0 .../Plugin/SpeckleFormBase.cs | 6 +- .../ServiceRegistration.cs | 24 +-- .../Speckle.Connectors.CSiShared.projitems | 26 ++- .../Utils/ObjectIdentifiers.cs | 2 +- .../Plugin/SpeckleForm.cs | 2 +- .../Plugin/cPlugin.cs | 4 +- .../Speckle.Connectors.ETABS21.csproj | 6 +- .../Plugin/SpeckleForm.cs | 2 +- .../Plugin/cPlugin.cs | 4 +- .../Speckle.Connectors.ETABS22.csproj | 4 +- .../HostApp/ETABSSendCollectionManager.cs | 117 -------------- .../HostApp/EtabsSendCollectionManager.cs | 153 ++++++++++++++++++ ...{ETABSPluginBase.cs => EtabsPluginBase.cs} | 6 +- ...kleFormBase.cs => EtabsSpeckleFormBase.cs} | 4 +- .../ServiceRegistration.cs | 6 +- .../Speckle.Connectors.ETABSShared.projitems | 8 +- .../CSiConversionSettings.cs | 2 +- ...ory.cs => CsiConversionSettingsFactory.cs} | 10 +- ...verter.cs => CsiRootToSpeckleConverter.cs} | 15 +- ...verter.cs => CsiToSpeckleUnitConverter.cs} | 4 +- .../{CSiWrappers.cs => CsiWrappers.cs} | 38 ++--- .../SpeckleApplicationIdExtensions.cs | 34 ++++ .../ServiceRegistration.cs | 21 +-- .../Speckle.Converters.CSiShared.projitems | 31 ++-- .../Geometry/PointToSpeckleConverter.cs | 49 ------ .../Helpers/ClassPropertyExtractor.cs | 32 ---- .../Helpers/CsiFramePropertiesExtractor.cs | 150 +++++++++++++++++ .../Helpers/CsiJointPropertiesExtractor.cs | 64 ++++++++ .../Helpers/CsiShellPropertiesExtractor.cs | 101 ++++++++++++ .../Helpers/DisplayValueExtractor.cs | 26 +-- .../IApplicationPropertiesExtractor.cs | 20 +++ .../Helpers/SharedPropertiesExtractor.cs | 73 +++++++++ .../Raw/CSiFrameToSpeckleConverter.cs | 53 ------ .../Raw/CSiJointToSpeckleConverter.cs | 53 ------ .../Raw/CSiShellToSpeckleConverter.cs | 53 ------ .../LineToSpeckleConverter.cs | 25 ++- .../MeshToSpeckleConverter.cs | 31 ++-- .../ToSpeckle/Raw/PointToSpeckleConverter.cs | 43 +++++ .../TopLevel/CSiObjectToSpeckleConverter.cs | 62 ------- .../CsiObjectToSpeckleConverterBase.cs | 69 ++++++++ .../Utils/DictionaryUtils.cs | 36 +++++ .../ServiceRegistration.cs | 17 +- .../Speckle.Converters.ETABSShared.projitems | 8 +- .../Helpers/EtabsFramePropertiesExtractor.cs | 84 ++++++++++ .../Helpers/EtabsJointPropertiesExtractor.cs | 66 ++++++++ .../Helpers/EtabsPropertiesExtractor.cs | 58 +++++++ .../Helpers/EtabsShellPropertiesExtractor.cs | 97 +++++++++++ .../ToSpeckle/Raw/FrameToSpeckleConverter.cs | 51 ------ .../ToSpeckle/Raw/JointToSpeckleConverter.cs | 52 ------ .../ToSpeckle/Raw/ShellToSpeckleConverter.cs | 49 ------ .../TopLevel/EtabsObjectToSpeckleConverter.cs | 54 +++++++ Speckle.Connectors.sln | 4 + 62 files changed, 1316 insertions(+), 787 deletions(-) rename Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/{CSiSharedBasicConnectorBinding.cs => CsiSharedBasicConnectorBinding.cs} (93%) rename Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/{CSiSharedSelectionBinding.cs => CsiSharedSelectionBinding.cs} (91%) rename Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/{CSiSharedSendBinding.cs => CsiSharedSendBinding.cs} (86%) rename Connectors/CSi/Speckle.Connectors.CSiShared/Filters/{CSiSharedSelectionFilter.cs => CsiSharedSelectionFilter.cs} (68%) rename Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/{CSiApplicationService.cs => CsiApplicationService.cs} (89%) rename Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/{CSiDocumentModelStore.cs => CsiDocumentModelStore.cs} (85%) rename Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/{CSiIdleManager.cs => CsiIdleManager.cs} (77%) rename Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/{CSiSendCollectionManager.cs => CsiSendCollectionManager.cs} (89%) rename Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/{CSiRootObjectBuilder.cs => CsiRootObjectBuilder.cs} (82%) rename Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/{CSiPluginBase.cs => CsiPluginBase.cs} (100%) delete mode 100644 Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/ETABSSendCollectionManager.cs create mode 100644 Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSendCollectionManager.cs rename Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/{ETABSPluginBase.cs => EtabsPluginBase.cs} (65%) rename Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/{ETABSSpeckleFormBase.cs => EtabsSpeckleFormBase.cs} (80%) rename Converters/CSi/Speckle.Converters.CSiShared/{CSiConversionSettingsFactory.cs => CsiConversionSettingsFactory.cs} (50%) rename Converters/CSi/Speckle.Converters.CSiShared/{CSiRootToSpeckleConverter.cs => CsiRootToSpeckleConverter.cs} (65%) rename Converters/CSi/Speckle.Converters.CSiShared/{CSiToSpeckleUnitConverter.cs => CsiToSpeckleUnitConverter.cs} (93%) rename Converters/CSi/Speckle.Converters.CSiShared/{CSiWrappers.cs => CsiWrappers.cs} (63%) create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/Extensions/SpeckleApplicationIdExtensions.cs delete mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs delete mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiFramePropertiesExtractor.cs create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiJointPropertiesExtractor.cs create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiShellPropertiesExtractor.cs create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/IApplicationPropertiesExtractor.cs create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/SharedPropertiesExtractor.cs delete mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiFrameToSpeckleConverter.cs delete mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiJointToSpeckleConverter.cs delete mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiShellToSpeckleConverter.cs rename Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/{Geometry => Raw}/LineToSpeckleConverter.cs (63%) rename Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/{Geometry => Raw}/MeshToSpeckleConverter.cs (63%) create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/PointToSpeckleConverter.cs delete mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CSiObjectToSpeckleConverter.cs create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CsiObjectToSpeckleConverterBase.cs create mode 100644 Converters/CSi/Speckle.Converters.CSiShared/Utils/DictionaryUtils.cs create mode 100644 Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsFramePropertiesExtractor.cs create mode 100644 Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsJointPropertiesExtractor.cs create mode 100644 Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsPropertiesExtractor.cs create mode 100644 Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsShellPropertiesExtractor.cs delete mode 100644 Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs delete mode 100644 Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/JointToSpeckleConverter.cs delete mode 100644 Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/ShellToSpeckleConverter.cs create mode 100644 Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/TopLevel/EtabsObjectToSpeckleConverter.cs diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedBasicConnectorBinding.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs similarity index 93% rename from Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedBasicConnectorBinding.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs index 6526e4ae8..bb0039cc2 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedBasicConnectorBinding.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedBasicConnectorBinding.cs @@ -6,7 +6,7 @@ namespace Speckle.Connectors.CSiShared.Bindings; -public class CSiSharedBasicConnectorBinding : IBasicConnectorBinding +public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding { private readonly ISpeckleApplication _speckleApplication; private readonly DocumentModelStore _store; @@ -15,7 +15,7 @@ public class CSiSharedBasicConnectorBinding : IBasicConnectorBinding public IBrowserBridge Parent { get; } public BasicConnectorBindingCommands Commands { get; } - public CSiSharedBasicConnectorBinding( + public CsiSharedBasicConnectorBinding( IBrowserBridge parent, ISpeckleApplication speckleApplication, DocumentModelStore store diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedSelectionBinding.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSelectionBinding.cs similarity index 91% rename from Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedSelectionBinding.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSelectionBinding.cs index 5ba75ef41..7981a3029 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedSelectionBinding.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSelectionBinding.cs @@ -5,13 +5,13 @@ namespace Speckle.Connectors.CSiShared.Bindings; -public class CSiSharedSelectionBinding : ISelectionBinding +public class CsiSharedSelectionBinding : ISelectionBinding { public string Name => "selectionBinding"; public IBrowserBridge Parent { get; } - private readonly ICSiApplicationService _csiApplicationService; + private readonly ICsiApplicationService _csiApplicationService; - public CSiSharedSelectionBinding(IBrowserBridge parent, ICSiApplicationService csiApplicationService) + public CsiSharedSelectionBinding(IBrowserBridge parent, ICsiApplicationService csiApplicationService) { Parent = parent; _csiApplicationService = csiApplicationService; diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedSendBinding.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSendBinding.cs similarity index 86% rename from Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedSendBinding.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSendBinding.cs index d74bd99d9..785d2c2ee 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CSiSharedSendBinding.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Bindings/CsiSharedSendBinding.cs @@ -20,7 +20,7 @@ namespace Speckle.Connectors.CSiShared.Bindings; -public sealed class CSiSharedSendBinding : ISendBinding +public sealed class CsiSharedSendBinding : ISendBinding { public string Name => "sendBinding"; public SendBindingUICommands Commands { get; } @@ -32,13 +32,13 @@ public sealed class CSiSharedSendBinding : ISendBinding private readonly List _sendFilters; private readonly CancellationManager _cancellationManager; private readonly IOperationProgressManager _operationProgressManager; - private readonly ILogger _logger; - private readonly ICSiApplicationService _csiApplicationService; - private readonly ICSiConversionSettingsFactory _csiConversionSettingsFactory; + private readonly ILogger _logger; + private readonly ICsiApplicationService _csiApplicationService; + private readonly ICsiConversionSettingsFactory _csiConversionSettingsFactory; private readonly ISpeckleApplication _speckleApplication; private readonly ISdkActivityFactory _activityFactory; - public CSiSharedSendBinding( + public CsiSharedSendBinding( DocumentModelStore store, IAppIdleManager idleManager, IBrowserBridge parent, @@ -46,11 +46,11 @@ public CSiSharedSendBinding( IServiceProvider serviceProvider, CancellationManager cancellationManager, IOperationProgressManager operationProgressManager, - ILogger logger, - ICSiConversionSettingsFactory csiConversionSettingsFactory, + ILogger logger, + ICsiConversionSettingsFactory csiConversionSettingsFactory, ISpeckleApplication speckleApplication, ISdkActivityFactory activityFactory, - ICSiApplicationService csiApplicationService + ICsiApplicationService csiApplicationService ) { _store = store; @@ -84,12 +84,12 @@ public async Task Send(string modelCardId) } using var scope = _serviceProvider.CreateScope(); scope - .ServiceProvider.GetRequiredService>() + .ServiceProvider.GetRequiredService>() .Initialize(_csiConversionSettingsFactory.Create(_csiApplicationService.SapModel)); CancellationToken cancellationToken = _cancellationManager.InitCancellationTokenSource(modelCardId); - List wrappers = modelCard + List wrappers = modelCard .SendFilter.NotNull() .RefreshObjectIds() .Select(DecodeObjectIdentifier) @@ -101,7 +101,7 @@ public async Task Send(string modelCardId) } var sendResult = await scope - .ServiceProvider.GetRequiredService>() + .ServiceProvider.GetRequiredService>() .Execute( wrappers, modelCard.GetSendInfo(_speckleApplication.Slug), @@ -125,10 +125,10 @@ await Commands } } - private ICSiWrapper DecodeObjectIdentifier(string encodedId) + private ICsiWrapper DecodeObjectIdentifier(string encodedId) { var (type, name) = ObjectIdentifier.Decode(encodedId); - return CSiWrapperFactory.Create(type, name); + return CsiWrapperFactory.Create(type, name); } public void CancelSend(string modelCardId) diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CSiSharedSelectionFilter.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CsiSharedSelectionFilter.cs similarity index 68% rename from Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CSiSharedSelectionFilter.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CsiSharedSelectionFilter.cs index a302f1202..2d26785ef 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CSiSharedSelectionFilter.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Filters/CsiSharedSelectionFilter.cs @@ -2,9 +2,9 @@ namespace Speckle.Connectors.CSiShared.Filters; -public class CSiSharedSelectionFilter : DirectSelectionSendFilter +public class CsiSharedSelectionFilter : DirectSelectionSendFilter { - public CSiSharedSelectionFilter() + public CsiSharedSelectionFilter() { IsDefault = true; } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiApplicationService.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiApplicationService.cs similarity index 89% rename from Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiApplicationService.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiApplicationService.cs index 27a034195..9614ce5a2 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiApplicationService.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiApplicationService.cs @@ -10,18 +10,18 @@ namespace Speckle.Connectors.CSiShared.HostApp; /// Prevent having to pass the "sapModel" around between classes and this ensures consistent access. /// Name "sapModel" is misleading since it doesn't only apply to SAP2000, but this is the convention in the API, so we keep it. /// -public interface ICSiApplicationService +public interface ICsiApplicationService { cSapModel SapModel { get; } void Initialize(cSapModel sapModel, cPluginCallback pluginCallback); } -public class CSiApplicationService : ICSiApplicationService +public class CsiApplicationService : ICsiApplicationService { public cSapModel SapModel { get; private set; } private cPluginCallback _pluginCallback; - public CSiApplicationService() + public CsiApplicationService() { SapModel = null!; } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiDocumentModelStore.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs similarity index 85% rename from Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiDocumentModelStore.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs index cda75e378..ab7f97353 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiDocumentModelStore.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiDocumentModelStore.cs @@ -8,20 +8,20 @@ namespace Speckle.Connectors.CSiShared.HostApp; -public class CSiDocumentModelStore : DocumentModelStore +public class CsiDocumentModelStore : DocumentModelStore { private readonly ISpeckleApplication _speckleApplication; - private readonly ILogger _logger; - private readonly ICSiApplicationService _csiApplicationService; + private readonly ILogger _logger; + private readonly ICsiApplicationService _csiApplicationService; private string HostAppUserDataPath { get; set; } private string DocumentStateFile { get; set; } private string ModelPathHash { get; set; } - public CSiDocumentModelStore( + public CsiDocumentModelStore( IJsonSerializer jsonSerializerSettings, ISpeckleApplication speckleApplication, - ILogger logger, - ICSiApplicationService csiApplicationService + ILogger logger, + ICsiApplicationService csiApplicationService ) : base(jsonSerializerSettings) { diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiIdleManager.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiIdleManager.cs similarity index 77% rename from Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiIdleManager.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiIdleManager.cs index 081639361..59d50296b 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiIdleManager.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiIdleManager.cs @@ -2,11 +2,11 @@ namespace Speckle.Connectors.CSiShared.HostApp; -public sealed class CSiIdleManager : AppIdleManager +public sealed class CsiIdleManager : AppIdleManager { private readonly IIdleCallManager _idleCallManager; - public CSiIdleManager(IIdleCallManager idleCallManager) + public CsiIdleManager(IIdleCallManager idleCallManager) : base(idleCallManager) { _idleCallManager = idleCallManager; diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiSendCollectionManager.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiSendCollectionManager.cs similarity index 89% rename from Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiSendCollectionManager.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiSendCollectionManager.cs index 1268be47b..cb25ca67c 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CSiSendCollectionManager.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/HostApp/CsiSendCollectionManager.cs @@ -12,12 +12,12 @@ namespace Speckle.Connectors.CSiShared.HostApp; /// This class manages the collections. If the key (from the path) already exists, this collection is returned. /// If it doesn't exist, a new collection is created and added to the rootObject. /// -public class CSiSendCollectionManager +public class CsiSendCollectionManager { - protected IConverterSettingsStore ConverterSettings { get; } + protected IConverterSettingsStore ConverterSettings { get; } protected Dictionary CollectionCache { get; } = new(); - public CSiSendCollectionManager(IConverterSettingsStore converterSettings) + public CsiSendCollectionManager(IConverterSettingsStore converterSettings) { ConverterSettings = converterSettings; } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CSiRootObjectBuilder.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CsiRootObjectBuilder.cs similarity index 82% rename from Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CSiRootObjectBuilder.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CsiRootObjectBuilder.cs index 434589715..5660a1ce6 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CSiRootObjectBuilder.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Operations/Send/CsiRootObjectBuilder.cs @@ -13,24 +13,24 @@ namespace Speckle.Connectors.CSiShared.Builders; -public class CSiRootObjectBuilder : IRootObjectBuilder +public class CsiRootObjectBuilder : IRootObjectBuilder { private readonly IRootToSpeckleConverter _rootToSpeckleConverter; private readonly ISendConversionCache _sendConversionCache; - private readonly IConverterSettingsStore _converterSettings; - private readonly CSiSendCollectionManager _sendCollectionManager; - private readonly ILogger _logger; + private readonly IConverterSettingsStore _converterSettings; + private readonly CsiSendCollectionManager _sendCollectionManager; + private readonly ILogger _logger; private readonly ISdkActivityFactory _activityFactory; - private readonly ICSiApplicationService _csiApplicationService; + private readonly ICsiApplicationService _csiApplicationService; - public CSiRootObjectBuilder( + public CsiRootObjectBuilder( IRootToSpeckleConverter rootToSpeckleConverter, ISendConversionCache sendConversionCache, - IConverterSettingsStore converterSettings, - CSiSendCollectionManager sendCollectionManager, - ILogger logger, + IConverterSettingsStore converterSettings, + CsiSendCollectionManager sendCollectionManager, + ILogger logger, ISdkActivityFactory activityFactory, - ICSiApplicationService csiApplicationService + ICsiApplicationService csiApplicationService ) { _sendConversionCache = sendConversionCache; @@ -43,7 +43,7 @@ ICSiApplicationService csiApplicationService } public async Task Build( - IReadOnlyList csiObjects, + IReadOnlyList csiObjects, SendInfo sendInfo, IProgress onOperationProgressed, CancellationToken cancellationToken = default @@ -60,7 +60,7 @@ public async Task Build( using (var _ = _activityFactory.Start("Convert all")) { - foreach (ICSiWrapper csiObject in csiObjects) + foreach (ICsiWrapper csiObject in csiObjects) { using var _2 = _activityFactory.Start("Convert"); cancellationToken.ThrowIfCancellationRequested(); @@ -82,7 +82,7 @@ public async Task Build( return new RootObjectBuilderResult(rootObjectCollection, results); } - private SendConversionResult ConvertCSiObject(ICSiWrapper csiObject, Collection typeCollection, string projectId) + private SendConversionResult ConvertCSiObject(ICsiWrapper csiObject, Collection typeCollection, string projectId) { string applicationId = $"{csiObject.ObjectType}{csiObject.Name}"; // TODO: NO! Use GUID string sourceType = csiObject.ObjectName; diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CSiPluginBase.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CsiPluginBase.cs similarity index 100% rename from Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CSiPluginBase.cs rename to Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/CsiPluginBase.cs diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs index bf47594cd..a3a81cad1 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Plugin/SpeckleFormBase.cs @@ -35,8 +35,8 @@ protected SpeckleFormBase() protected virtual void ConfigureServices(IServiceCollection services) { services.Initialize(GetHostApplication(), GetVersion()); - services.AddCSi(); - services.AddCSiConverters(); + services.AddCsi(); + services.AddCsiConverters(); } protected abstract HostApplication GetHostApplication(); @@ -48,7 +48,7 @@ public void SetSapModel(ref cSapModel sapModel, ref cPluginCallback pluginCallba _sapModel = sapModel; _pluginCallback = pluginCallback; - var csiService = Container.GetRequiredService(); + var csiService = Container.GetRequiredService(); csiService.Initialize(sapModel, pluginCallback); } diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/ServiceRegistration.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/ServiceRegistration.cs index 994d0c75d..572b81b46 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/ServiceRegistration.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/ServiceRegistration.cs @@ -18,32 +18,32 @@ namespace Speckle.Connectors.CSiShared; public static class ServiceRegistration { - public static IServiceCollection AddCSi(this IServiceCollection services) + public static IServiceCollection AddCsi(this IServiceCollection services) { services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddConnectorUtils(); - services.AddDUI(); + services.AddDUI(); services.AddDUIView(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(sp => sp.GetRequiredService()); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped, CSiRootObjectBuilder>(); - services.AddScoped>(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped, CsiRootObjectBuilder>(); + services.AddScoped>(); services.RegisterTopLevelExceptionHandler(); diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.projitems b/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.projitems index e49c115ee..be32df90c 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.projitems +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Speckle.Connectors.CSiShared.projitems @@ -9,21 +9,19 @@ Speckle.Connectors.CSiShared - - - - - - - - - Form - + + + + + + + + - - - + + + - + \ No newline at end of file diff --git a/Connectors/CSi/Speckle.Connectors.CSiShared/Utils/ObjectIdentifiers.cs b/Connectors/CSi/Speckle.Connectors.CSiShared/Utils/ObjectIdentifiers.cs index 443893e7c..63122f594 100644 --- a/Connectors/CSi/Speckle.Connectors.CSiShared/Utils/ObjectIdentifiers.cs +++ b/Connectors/CSi/Speckle.Connectors.CSiShared/Utils/ObjectIdentifiers.cs @@ -7,7 +7,7 @@ namespace Speckle.Connectors.CSiShared.Utils; /// All API methods are based on the objectType and objectName, not the GUID. /// We will obviously manage the GUIDs but for all method calls we need a concatenated version of the objectType and objectName. /// Since objectType is a single int (1, 2 ... 7) we know first index will always be the objectType. -/// This int gets used by the CSiWrapperFactory to create the CSiWrappers. +/// This int gets used by the CsiWrapperFactory to create the CSiWrappers. /// public static class ObjectIdentifier { diff --git a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs index b97205397..79043b5ee 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/SpeckleForm.cs @@ -6,7 +6,7 @@ #pragma warning disable IDE0130 namespace Speckle.Connectors.ETABS21; -public class SpeckleForm : ETABSSpeckleFormBase +public class SpeckleForm : EtabsSpeckleFormBase { 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 9f40a27fa..ec042fead 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs @@ -6,7 +6,7 @@ namespace Speckle.Connectors.ETABS21; [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] -public class cPlugin : ETABSPluginBase +public class cPlugin : EtabsPluginBase { - protected override ETABSSpeckleFormBase CreateETABSForm() => 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 d51ff3065..5b81e56d5 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj +++ b/Connectors/CSi/Speckle.Connectors.ETABS21/Speckle.Connectors.ETABS21.csproj @@ -20,13 +20,11 @@ - + - - Form - + diff --git a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs index 6ed11e0b7..920ed8d0f 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/SpeckleForm.cs @@ -6,7 +6,7 @@ #pragma warning disable IDE0130 namespace Speckle.Connectors.ETABS22; -public class SpeckleForm : ETABSSpeckleFormBase +public class SpeckleForm : EtabsSpeckleFormBase { 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 7d1eabd5c..acc99c4ad 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs @@ -6,7 +6,7 @@ namespace Speckle.Connectors.ETABS22; [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] -public class cPlugin : ETABSPluginBase +public class cPlugin : EtabsPluginBase { - protected override ETABSSpeckleFormBase CreateETABSForm() => 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 9c27a9766..a067aedd2 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj +++ b/Connectors/CSi/Speckle.Connectors.ETABS22/Speckle.Connectors.ETABS22.csproj @@ -23,9 +23,7 @@ - - Form - + diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/ETABSSendCollectionManager.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/ETABSSendCollectionManager.cs deleted file mode 100644 index d2f24ac02..000000000 --- a/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/ETABSSendCollectionManager.cs +++ /dev/null @@ -1,117 +0,0 @@ -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/HostApp/EtabsSendCollectionManager.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSendCollectionManager.cs new file mode 100644 index 000000000..b721c2d18 --- /dev/null +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/HostApp/EtabsSendCollectionManager.cs @@ -0,0 +1,153 @@ +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 level and type. +/// Creates a hierarchical structure that mirrors ETABS' native organization. +/// +public class EtabsSendCollectionManager : CsiSendCollectionManager +{ + private const string DEFAULT_LEVEL = "Unassigned"; + + private readonly Dictionary _categoryNames = + new() + { + { ElementCategory.COLUMN, "Columns" }, + { ElementCategory.BEAM, "Beams" }, + { ElementCategory.BRACE, "Braces" }, + { ElementCategory.WALL, "Walls" }, + { ElementCategory.FLOOR, "Floors" }, + { ElementCategory.RAMP, "Ramps" }, + { ElementCategory.JOINT, "Joints" }, + { ElementCategory.OTHER, "Other" } + }; + + public EtabsSendCollectionManager(IConverterSettingsStore converterSettings) + : base(converterSettings) { } + + public override Collection AddObjectCollectionToRoot(Base convertedObject, Collection rootObject) + { + var level = GetObjectLevelFromObject(convertedObject); + var category = GetElementCategoryFromObject(convertedObject); + + return GetOrCreateCollectionHierarchy(level, category, rootObject); + } + + private string GetObjectLevelFromObject(Base obj) + { + // Properties from converter are stored in "Object ID" dictionary + // NOTE: Introduce enums for these object keys? I don't like string indexing. + if (obj["properties"] is not Dictionary properties) + { + return DEFAULT_LEVEL; + } + + if (properties.TryGetValue("Object ID", out var objectId) && objectId is Dictionary parameters) + { + return parameters.TryGetValue("level", out var level) ? level?.ToString() ?? DEFAULT_LEVEL : DEFAULT_LEVEL; + } + + return DEFAULT_LEVEL; + } + + private ElementCategory GetElementCategoryFromObject(Base obj) + { + var type = obj["type"]?.ToString(); + + // Handle non-structural elements + if (string.IsNullOrEmpty(type)) + { + return ElementCategory.OTHER; + } + + // For frames and shells, get design orientation from Object ID + if ((type == "Frame" || type == "Shell") && obj["properties"] is Dictionary properties) + { + if (properties.TryGetValue("Object ID", out var objectId) && objectId is Dictionary parameters) + { + if (parameters.TryGetValue("designOrientation", out var orientation)) + { + return GetCategoryFromDesignOrientation(orientation?.ToString(), type); + } + } + } + + // For joints, simply categorize as joints + return type == "Joint" ? ElementCategory.JOINT : ElementCategory.OTHER; + } + + private ElementCategory GetCategoryFromDesignOrientation(string? orientation, string type) + { + if (string.IsNullOrEmpty(orientation)) + { + return ElementCategory.OTHER; + } + + return (orientation, type) switch + { + ("Column", "Frame") => ElementCategory.COLUMN, + ("Beam", "Frame") => ElementCategory.BEAM, + ("Brace", "Frame") => ElementCategory.BRACE, + ("Wall", "Shell") => ElementCategory.WALL, + ("Floor", "Shell") => ElementCategory.FLOOR, + ("Ramp", "Shell") => ElementCategory.RAMP, + _ => ElementCategory.OTHER + }; + } + + private Collection GetOrCreateCollectionHierarchy(string level, ElementCategory category, Collection root) + { + var hierarchyKey = $"{level}_{category}"; + + if (CollectionCache.TryGetValue(hierarchyKey, out var existingCollection)) + { + return existingCollection; + } + + var levelCollection = GetOrCreateLevelCollection(level, root); + var categoryCollection = CreateCategoryCollection(category, levelCollection); + + CollectionCache[hierarchyKey] = categoryCollection; + return categoryCollection; + } + + private Collection GetOrCreateLevelCollection(string level, Collection root) + { + var levelKey = $"Level_{level}"; + + if (CollectionCache.TryGetValue(levelKey, out var existingCollection)) + { + return existingCollection; + } + + var levelCollection = new Collection(level); + root.elements.Add(levelCollection); + CollectionCache[levelKey] = levelCollection; + + return levelCollection; + } + + private Collection CreateCategoryCollection(ElementCategory category, Collection levelCollection) + { + var categoryCollection = new Collection(_categoryNames[category]); + levelCollection.elements.Add(categoryCollection); + return categoryCollection; + } + + private enum ElementCategory + { + COLUMN, + BEAM, + BRACE, + WALL, + FLOOR, + RAMP, + JOINT, + OTHER + } +} diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSPluginBase.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsPluginBase.cs similarity index 65% rename from Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSPluginBase.cs rename to Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsPluginBase.cs index 9620df7b5..f0965c12e 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSPluginBase.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsPluginBase.cs @@ -3,7 +3,7 @@ namespace Speckle.Connectors.ETABSShared; [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "")] -public abstract class ETABSPluginBase : CSiPluginBase +public abstract class EtabsPluginBase : CSiPluginBase { public override int Info(ref string text) { @@ -11,7 +11,7 @@ public override int Info(ref string text) return 0; } - protected override SpeckleFormBase CreateForm() => CreateETABSForm(); + protected override SpeckleFormBase CreateForm() => CreateEtabsForm(); - protected abstract ETABSSpeckleFormBase 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 similarity index 80% rename from Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSSpeckleFormBase.cs rename to Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsSpeckleFormBase.cs index eebe168b4..0556f35ad 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/ETABSSpeckleFormBase.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Plugin/EtabsSpeckleFormBase.cs @@ -4,13 +4,13 @@ namespace Speckle.Connectors.ETABSShared; -public abstract class ETABSSpeckleFormBase : SpeckleFormBase +public abstract class EtabsSpeckleFormBase : SpeckleFormBase { protected override HostApplication GetHostApplication() => HostApplications.ETABS; protected override void ConfigureServices(IServiceCollection services) { base.ConfigureServices(services); - services.AddETABS(); + services.AddEtabs(); } } diff --git a/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs b/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs index 75c2f8df2..b4fcbc991 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/ServiceRegistration.cs @@ -7,10 +7,10 @@ namespace Speckle.Connectors.ETABSShared; public static class ServiceRegistration { - public static IServiceCollection AddETABS(this IServiceCollection services) + public static IServiceCollection AddEtabs(this IServiceCollection services) { - services.AddETABSConverters(); - services.AddScoped(); + 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 index b10ec07bf..badbea3c9 100644 --- a/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.projitems +++ b/Connectors/CSi/Speckle.Connectors.ETABSShared/Speckle.Connectors.ETABSShared.projitems @@ -9,9 +9,9 @@ Speckle.Connectors.ETABSShared - - - + + + - + \ No newline at end of file diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettings.cs b/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettings.cs index 1fb310342..313c12637 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettings.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettings.cs @@ -1,3 +1,3 @@ namespace Speckle.Converters.CSiShared; -public record CSiConversionSettings(cSapModel SapModel, string SpeckleUnits); +public record CsiConversionSettings(cSapModel SapModel, string SpeckleUnits); diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettingsFactory.cs b/Converters/CSi/Speckle.Converters.CSiShared/CsiConversionSettingsFactory.cs similarity index 50% rename from Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettingsFactory.cs rename to Converters/CSi/Speckle.Converters.CSiShared/CsiConversionSettingsFactory.cs index 89aade283..bc6b13e3d 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/CSiConversionSettingsFactory.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/CsiConversionSettingsFactory.cs @@ -4,13 +4,13 @@ namespace Speckle.Converters.CSiShared; [GenerateAutoInterface] -public class CSiConversionSettingsFactory( +public class CsiConversionSettingsFactory( IHostToSpeckleUnitConverter unitsConverter, - IConverterSettingsStore settingsStore -) : ICSiConversionSettingsFactory + IConverterSettingsStore settingsStore +) : ICsiConversionSettingsFactory { - public CSiConversionSettings Current => settingsStore.Current; + public CsiConversionSettings Current => settingsStore.Current; - public CSiConversionSettings Create(cSapModel document) => + public CsiConversionSettings Create(cSapModel document) => new(document, unitsConverter.ConvertOrThrow(document.GetPresentUnits())); } diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CSiRootToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/CsiRootToSpeckleConverter.cs similarity index 65% rename from Converters/CSi/Speckle.Converters.CSiShared/CSiRootToSpeckleConverter.cs rename to Converters/CSi/Speckle.Converters.CSiShared/CsiRootToSpeckleConverter.cs index f99a75692..a44a9d863 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/CSiRootToSpeckleConverter.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/CsiRootToSpeckleConverter.cs @@ -7,16 +7,16 @@ namespace Speckle.Converters.CSiShared; -public class CSiRootToSpeckleConverter : IRootToSpeckleConverter +public class CsiRootToSpeckleConverter : IRootToSpeckleConverter { private readonly IConverterManager _toSpeckle; - private readonly IConverterSettingsStore _settingsStore; - private readonly ILogger _logger; + private readonly IConverterSettingsStore _settingsStore; + private readonly ILogger _logger; - public CSiRootToSpeckleConverter( + public CsiRootToSpeckleConverter( IConverterManager toSpeckle, - IConverterSettingsStore settingsStore, - ILogger logger + IConverterSettingsStore settingsStore, + ILogger logger ) { _toSpeckle = toSpeckle; @@ -26,7 +26,7 @@ ILogger logger public Base Convert(object target) { - if (target is not ICSiWrapper wrapper) + if (target is not ICsiWrapper) { throw new ValidationException($"Target object is not a CSiWrapper. It's a ${target.GetType()}"); } @@ -35,7 +35,6 @@ public Base Convert(object target) var objectConverter = _toSpeckle.ResolveConverter(type, true); Base result = objectConverter.Convert(target); - result.applicationId = $"{wrapper.ObjectType}{wrapper.Name}"; return result; } diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CSiToSpeckleUnitConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/CsiToSpeckleUnitConverter.cs similarity index 93% rename from Converters/CSi/Speckle.Converters.CSiShared/CSiToSpeckleUnitConverter.cs rename to Converters/CSi/Speckle.Converters.CSiShared/CsiToSpeckleUnitConverter.cs index ab8e3df92..4082abd8f 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/CSiToSpeckleUnitConverter.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/CsiToSpeckleUnitConverter.cs @@ -11,11 +11,11 @@ namespace Speckle.Converters.CSiShared; /// CSi GetPresentUnits() valid for both SAP 2000 and ETABS. /// Represents units transmitted through API calls and not necessarily those displayed in GUI. /// -public class CSiToSpeckleUnitConverter : IHostToSpeckleUnitConverter +public class CsiToSpeckleUnitConverter : IHostToSpeckleUnitConverter { private readonly Dictionary _unitMapping = new Dictionary(); - public CSiToSpeckleUnitConverter() + public CsiToSpeckleUnitConverter() { _unitMapping[eUnits.lb_in_F] = Units.Inches; _unitMapping[eUnits.lb_ft_F] = Units.Feet; diff --git a/Converters/CSi/Speckle.Converters.CSiShared/CSiWrappers.cs b/Converters/CSi/Speckle.Converters.CSiShared/CsiWrappers.cs similarity index 63% rename from Converters/CSi/Speckle.Converters.CSiShared/CSiWrappers.cs rename to Converters/CSi/Speckle.Converters.CSiShared/CsiWrappers.cs index cd7c8f062..63d01448a 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/CSiWrappers.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/CsiWrappers.cs @@ -1,10 +1,10 @@ namespace Speckle.Converters.CSiShared; -public interface ICSiWrapper +public interface ICsiWrapper { string Name { get; set; } int ObjectType { get; } - string ObjectName { get; } // TODO: Better approach to objectType number and name. Enum? + string ObjectName { get; } } /// @@ -15,50 +15,50 @@ public interface ICSiWrapper /// Since the API only provides a framework for us to query the model, we don't get instances. /// The types are the same for both SAP 2000 and ETABS. /// -public abstract class CSiWrapperBase : ICSiWrapper +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 class CsiJointWrapper : CsiWrapperBase { public override int ObjectType => 1; public override string ObjectName => "Joint"; } -public class CSiFrameWrapper : CSiWrapperBase +public class CsiFrameWrapper : CsiWrapperBase { public override int ObjectType => 2; public override string ObjectName => "Frame"; } -public class CSiCableWrapper : CSiWrapperBase +public class CsiCableWrapper : CsiWrapperBase { public override int ObjectType => 3; public override string ObjectName => "Cable"; } -public class CSiTendonWrapper : CSiWrapperBase +public class CsiTendonWrapper : CsiWrapperBase { public override int ObjectType => 4; public override string ObjectName => "Tendon"; } -public class CSiShellWrapper : CSiWrapperBase +public class CsiShellWrapper : CsiWrapperBase { public override int ObjectType => 5; public override string ObjectName => "Shell"; } -public class CSiSolidWrapper : CSiWrapperBase +public class CsiSolidWrapper : CsiWrapperBase { public override int ObjectType => 6; public override string ObjectName => "Solid"; } -public class CSiLinkWrapper : CSiWrapperBase +public class CsiLinkWrapper : CsiWrapperBase { public override int ObjectType => 7; public override string ObjectName => "Link"; @@ -71,18 +71,18 @@ public class CSiLinkWrapper : CSiWrapperBase /// Switch statements based off of the objectType int return. /// Used in the connectors and allows converters to be resolved effectively. /// -public static class CSiWrapperFactory +public static class CsiWrapperFactory { - public static ICSiWrapper Create(int objectType, string name) => + public static ICsiWrapper Create(int objectType, string name) => objectType switch { - 1 => new CSiJointWrapper { Name = name }, - 2 => new CSiFrameWrapper { 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 }, // TODO: CSiSolidWrapper - 7 => new CSiLinkWrapper { Name = name }, // TODO: CSiLinkWrapper + 1 => new CsiJointWrapper { Name = name }, + 2 => new CsiFrameWrapper { 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 }, // 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/Extensions/SpeckleApplicationIdExtensions.cs b/Converters/CSi/Speckle.Converters.CSiShared/Extensions/SpeckleApplicationIdExtensions.cs new file mode 100644 index 000000000..f061e06eb --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Extensions/SpeckleApplicationIdExtensions.cs @@ -0,0 +1,34 @@ +namespace Speckle.Converters.CSiShared.Extensions; + +public static class SpeckleApplicationIdExtensions +{ + /// + /// Retrieves the Speckle object application id for a frame object + /// + public static string GetSpeckleApplicationId(this CsiFrameWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.FrameObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } + + /// + /// Retrieves the Speckle object application id for a joint object + /// + public static string GetSpeckleApplicationId(this CsiJointWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.PointObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } + + /// + /// Retrieves the Speckle object application id for a shell object + /// + public static string GetSpeckleApplicationId(this CsiShellWrapper wrapper, cSapModel sapModel) + { + string applicationId = string.Empty; + _ = sapModel.AreaObj.GetGUID(wrapper.Name, ref applicationId); + return applicationId; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs b/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs index 65cade601..5928293d7 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/ServiceRegistration.cs @@ -3,30 +3,31 @@ using Speckle.Converters.Common; using Speckle.Converters.Common.Registration; using Speckle.Converters.CSiShared.ToSpeckle.Helpers; -using Speckle.Converters.CSiShared.ToSpeckle.TopLevel; using Speckle.Sdk; namespace Speckle.Converters.CSiShared; public static class ServiceRegistration { - public static IServiceCollection AddCSiConverters(this IServiceCollection serviceCollection) + public static IServiceCollection AddCsiConverters(this IServiceCollection serviceCollection) { var converterAssembly = Assembly.GetExecutingAssembly(); // Register top-level converters - serviceCollection.AddTransient(); - serviceCollection.AddRootCommon(converterAssembly); + serviceCollection.AddRootCommon(converterAssembly); - // Register extractors + // Register property extractors + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); serviceCollection.AddScoped(); - serviceCollection.AddScoped(); - // Register application-level converters - serviceCollection.AddApplicationConverters(converterAssembly); + // Settings and unit conversions + serviceCollection.AddApplicationConverters(converterAssembly); serviceCollection.AddScoped< - IConverterSettingsStore, - ConverterSettingsStore + IConverterSettingsStore, + ConverterSettingsStore >(); serviceCollection.AddMatchingInterfacesAsTransient(converterAssembly); diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems b/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems index 0b3d91e2e..7d59d119a 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems +++ b/Converters/CSi/Speckle.Converters.CSiShared/Speckle.Converters.CSiShared.projitems @@ -9,21 +9,24 @@ Speckle.Converters.CSiShared - - - - - + + + + + + - - - - + + + + + + + - - - - + + + - + \ No newline at end of file diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs deleted file mode 100644 index 1a0f1aa5c..000000000 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/PointToSpeckleConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Objects.Geometry; - -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 PointToSpeckleConverter(IConverterSettingsStore settingStore) - { - _settingStore = settingStore; - } - - public Point Convert(CSiJointWrapper target) // NOTE: This is just a temporary POC - { - double pointX = 0; - double pointY = 0; - double pointZ = 0; - - int result = _settingStore.Current.SapModel.PointObj.GetCoordCartesian( - target.Name, - ref pointX, - ref pointY, - ref pointZ - ); - - if (result != 0) - { - throw new ArgumentException($"Failed to convert {target.Name} to {typeof(Point)}"); - } - - 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 deleted file mode 100644 index 81d4abf97..000000000 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/ClassPropertyExtractor.cs +++ /dev/null @@ -1,32 +0,0 @@ -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/Helpers/CsiFramePropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiFramePropertiesExtractor.cs new file mode 100644 index 000000000..63be4e657 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiFramePropertiesExtractor.cs @@ -0,0 +1,150 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared.Extensions; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Extracts properties common to frame elements across CSi products (e.g., Etabs, Sap2000) +/// using the FrameObj API calls. +/// +/// +/// Design Decisions: +/// - Individual methods preferred over batched calls due to: +/// * Independent API calls with no performance gain from batching (?) +/// * Easier debugging and error tracing +/// * Simpler maintenance as each method maps to one API concept +/// Integration: +/// - Part of the property extraction hierarchy +/// - Used by for delegating frame property extraction +/// +public sealed class CsiFramePropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + private static readonly string[] s_releaseKeys = + [ + "axial", + "minorShear", + "majorShear", + "torsion", + "minorBending", + "majorBending" + ]; // Note: caching keys for better performance + + public CsiFramePropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiFrameWrapper frame, PropertyExtractionResult frameData) + { + frameData.ApplicationId = frame.GetSpeckleApplicationId(_settingsStore.Current.SapModel); + + var geometry = DictionaryUtils.EnsureNestedDictionary(frameData.Properties, "Geometry"); + (geometry["startJointName"], geometry["endJointName"]) = GetEndPointNames(frame); + + var assignments = DictionaryUtils.EnsureNestedDictionary(frameData.Properties, "Assignments"); + assignments["groups"] = new List(GetGroupAssigns(frame)); + assignments["materialOverwrite"] = GetMaterialOverwrite(frame); + assignments["localAxis"] = GetLocalAxes(frame); + assignments["propertyModifiers"] = GetModifiers(frame); + assignments["endReleases"] = GetReleases(frame); + assignments["sectionProperty"] = GetSectionName(frame); + assignments["path"] = GetPathType(frame); + } + + private string[] GetGroupAssigns(CsiFrameWrapper frame) + { + int numberGroups = 0; + string[] groups = []; + _ = _settingsStore.Current.SapModel.FrameObj.GetGroupAssign(frame.Name, ref numberGroups, ref groups); + return (groups); + } + + private Dictionary GetLocalAxes(CsiFrameWrapper frame) + { + double angle = 0; + bool advanced = false; + _ = _settingsStore.Current.SapModel.FrameObj.GetLocalAxes(frame.Name, ref angle, ref advanced); + return new Dictionary { ["angle"] = angle, ["advanced"] = advanced.ToString() }; + } + + private string GetMaterialOverwrite(CsiFrameWrapper frame) + { + string propName = "None"; + _ = _settingsStore.Current.SapModel.FrameObj.GetMaterialOverwrite(frame.Name, ref propName); + return propName; + } + + private Dictionary GetModifiers(CsiFrameWrapper frame) + { + double[] value = Array.Empty(); + _ = _settingsStore.Current.SapModel.FrameObj.GetModifiers(frame.Name, ref value); + return new Dictionary + { + ["crossSectionalAreaModifier"] = value[0], + ["shearAreaInLocal2DirectionModifier"] = value[1], + ["shearAreaInLocal3DirectionModifier"] = value[2], + ["torsionalConstantModifier"] = value[3], + ["momentOfInertiaAboutLocal2AxisModifier"] = value[4], + ["momentOfInertiaAboutLocal3AxisModifier"] = value[5], + ["massModifier"] = value[6], + ["weightModifier"] = value[7] + }; + } + + private (string point1, string point2) GetEndPointNames(CsiFrameWrapper frame) + { + string point1 = string.Empty, + point2 = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetPoints(frame.Name, ref point1, ref point2); + return (point1, point2); + } + + private Dictionary GetReleases(CsiFrameWrapper frame) + { + bool[] ii = Array.Empty(), + jj = Array.Empty(); + double[] startValue = Array.Empty(), + endValue = Array.Empty(); + + _ = _settingsStore.Current.SapModel.FrameObj.GetReleases(frame.Name, ref ii, ref jj, ref startValue, ref endValue); + + var startNodes = s_releaseKeys + .Select( + (key, index) => + new KeyValuePair( + $"{key}StartNode", + new Dictionary { ["release"] = ii[index], ["stiffness"] = startValue[index] } + ) + ) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + var endNodes = s_releaseKeys + .Select( + (key, index) => + new KeyValuePair( + $"{key}EndNode", + new Dictionary { ["release"] = jj[index], ["stiffness"] = endValue[index] } + ) + ) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + return startNodes.Concat(endNodes).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + } + + private string GetSectionName(CsiFrameWrapper frame) + { + string sectionName = string.Empty, + sAuto = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetSection(frame.Name, ref sectionName, ref sAuto); + return sectionName; + } + + private string GetPathType(CsiFrameWrapper frame) + { + string pathType = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetTypeOAPI(frame.Name, ref pathType); + return pathType; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiJointPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiJointPropertiesExtractor.cs new file mode 100644 index 000000000..5b3baf53c --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiJointPropertiesExtractor.cs @@ -0,0 +1,64 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared.Extensions; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Extracts properties common to joint elements across CSi products (e.g., ETABS, SAP2000) +/// using the PointObj API calls. +/// +/// +/// Design Decisions: +/// - Individual methods preferred over batched calls due to: +/// * Independent API calls with no performance gain from batching (?) +/// * Easier debugging and error tracing +/// * Simpler maintenance as each method maps to one API concept +/// Responsibilities: +/// - Provides a focused interface for extracting properties specific to joint elements. +/// - Ensures consistency in property extraction logic across supported CSi products. +/// Integration: +/// - Part of the property extraction hierarchy +/// - Used by for delegating joint property extraction +/// +public sealed class CsiJointPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public CsiJointPropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiJointWrapper joint, PropertyExtractionResult jointData) + { + jointData.ApplicationId = joint.GetSpeckleApplicationId(_settingsStore.Current.SapModel); + + var assignments = DictionaryUtils.EnsureNestedDictionary(jointData.Properties, "Assignments"); + assignments["groups"] = new List(GetGroupAssigns(joint)); + assignments["restraints"] = GetRestraints(joint); + } + + private string[] GetGroupAssigns(CsiJointWrapper joint) + { + int numberGroups = 0; + string[] groups = []; + _ = _settingsStore.Current.SapModel.PointObj.GetGroupAssign(joint.Name, ref numberGroups, ref groups); + return (groups); + } + + private Dictionary GetRestraints(CsiJointWrapper joint) + { + bool[] restraints = Array.Empty(); + _ = _settingsStore.Current.SapModel.PointObj.GetRestraint(joint.Name, ref restraints); + return new Dictionary + { + ["U1"] = restraints[0], + ["U2"] = restraints[1], + ["U3"] = restraints[2], + ["R1"] = restraints[3], + ["R2"] = restraints[4], + ["R3"] = restraints[5], + }; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiShellPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiShellPropertiesExtractor.cs new file mode 100644 index 000000000..1e5310e97 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/CsiShellPropertiesExtractor.cs @@ -0,0 +1,101 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared.Extensions; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Extracts properties common to shell elements across CSi products (e.g., Etabs, Sap2000) +/// using the AreaObj API calls. +/// +/// +/// Design Decisions: +/// - Individual methods preferred over batched calls due to: +/// * Independent API calls with no performance gain from batching (?) +/// * Easier debugging and error tracing +/// * Simpler maintenance as each method maps to one API concept +/// Integration: +/// - Part of the property extraction hierarchy +/// - Used by for delegating shell property extraction +/// +public sealed class CsiShellPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public CsiShellPropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiShellWrapper shell, PropertyExtractionResult shellData) + { + shellData.ApplicationId = shell.GetSpeckleApplicationId(_settingsStore.Current.SapModel); + + var geometry = DictionaryUtils.EnsureNestedDictionary(shellData.Properties, "Geometry"); + geometry["shellVerticesJointNames"] = GetPointNames(shell); + + var assignments = DictionaryUtils.EnsureNestedDictionary(shellData.Properties, "Assignments"); + assignments["groups"] = new List(GetGroupAssigns(shell)); + assignments["localAxis"] = GetLocalAxes(shell); + assignments["materialOverwrite"] = GetMaterialOverwrite(shell); + assignments["propertyModifiers"] = GetModifiers(shell); + assignments["sectionProperty"] = GetSectionName(shell); + } + + private string[] GetGroupAssigns(CsiShellWrapper shell) + { + int numberGroups = 0; + string[] groups = []; + _ = _settingsStore.Current.SapModel.AreaObj.GetGroupAssign(shell.Name, ref numberGroups, ref groups); + return (groups); + } + + private Dictionary GetLocalAxes(CsiShellWrapper shell) + { + double angle = 0; + bool advanced = false; + _ = _settingsStore.Current.SapModel.AreaObj.GetLocalAxes(shell.Name, ref angle, ref advanced); + return new Dictionary { ["angle"] = angle, ["advanced"] = advanced.ToString() }; + } + + private string GetMaterialOverwrite(CsiShellWrapper shell) + { + string propName = "None"; + _ = _settingsStore.Current.SapModel.AreaObj.GetMaterialOverwrite(shell.Name, ref propName); + return propName; + } + + private Dictionary GetModifiers(CsiShellWrapper shell) + { + double[] value = Array.Empty(); + _ = _settingsStore.Current.SapModel.AreaObj.GetModifiers(shell.Name, ref value); + return new Dictionary + { + ["membraneF11Modifier"] = value[0], + ["membraneF22Modifier"] = value[1], + ["membraneF12Modifier"] = value[2], + ["bendingM11Modifier"] = value[3], + ["bendingM22Modifier"] = value[4], + ["bendingM12Modifier"] = value[5], + ["shearV13Modifier"] = value[6], + ["shearV23Modifier"] = value[7], + ["massModifier"] = value[8], + ["weightModifier"] = value[9] + }; + } + + private string[] GetPointNames(CsiShellWrapper shell) + { + int numberPoints = 0; + string[] pointNames = Array.Empty(); + _ = _settingsStore.Current.SapModel.AreaObj.GetPoints(shell.Name, ref numberPoints, ref pointNames); + return pointNames; + } + + private string GetSectionName(CsiShellWrapper shell) + { + string sectionName = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetProperty(shell.Name, ref sectionName); + return sectionName; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DisplayValueExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DisplayValueExtractor.cs index 248d21465..c5e9b16e3 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DisplayValueExtractor.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/DisplayValueExtractor.cs @@ -6,14 +6,14 @@ namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; public class DisplayValueExtractor { - private readonly ITypedConverter _jointConverter; - private readonly ITypedConverter _frameConverter; - private readonly ITypedConverter _shellConverter; + private readonly ITypedConverter _jointConverter; + private readonly ITypedConverter _frameConverter; + private readonly ITypedConverter _shellConverter; public DisplayValueExtractor( - ITypedConverter jointConverter, - ITypedConverter frameConverter, - ITypedConverter shellConverter + ITypedConverter jointConverter, + ITypedConverter frameConverter, + ITypedConverter shellConverter ) { _jointConverter = jointConverter; @@ -21,28 +21,28 @@ ITypedConverter shellConverter _shellConverter = shellConverter; } - public IEnumerable GetDisplayValue(ICSiWrapper wrapper) + public IEnumerable GetDisplayValue(ICsiWrapper wrapper) { return wrapper switch { - CSiJointWrapper joint => ExtractJoint(joint), - CSiFrameWrapper frame => ExtractFrame(frame), - CSiShellWrapper shell => ExtractShell(shell), + CsiJointWrapper joint => ExtractJoint(joint), + CsiFrameWrapper frame => ExtractFrame(frame), + CsiShellWrapper shell => ExtractShell(shell), _ => Enumerable.Empty() }; } - private IEnumerable ExtractJoint(CSiJointWrapper target) + private IEnumerable ExtractJoint(CsiJointWrapper target) { yield return _jointConverter.Convert(target); } - private IEnumerable ExtractFrame(CSiFrameWrapper target) + private IEnumerable ExtractFrame(CsiFrameWrapper target) { yield return _frameConverter.Convert(target); } - private IEnumerable ExtractShell(CSiShellWrapper target) + private IEnumerable ExtractShell(CsiShellWrapper target) { yield return _shellConverter.Convert(target); } diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/IApplicationPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/IApplicationPropertiesExtractor.cs new file mode 100644 index 000000000..51e7524f3 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/IApplicationPropertiesExtractor.cs @@ -0,0 +1,20 @@ +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +public struct PropertyExtractionResult +{ + public string Name { get; set; } + public string Type { get; set; } + public string ApplicationId { get; set; } + public Dictionary Properties { get; set; } +} + +/// +/// Interface for extracting application-specific properties (e.g., ETABS-specific properties). +/// Implementations must compose with SharedPropertiesExtractor to ensure both shared and +/// application-specific properties are extracted. +/// +public interface IApplicationPropertiesExtractor +{ + SharedPropertiesExtractor SharedPropertiesExtractor { get; } + PropertyExtractionResult ExtractProperties(ICsiWrapper wrapper); +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/SharedPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/SharedPropertiesExtractor.cs new file mode 100644 index 000000000..c69074273 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Helpers/SharedPropertiesExtractor.cs @@ -0,0 +1,73 @@ +namespace Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +/// +/// Extracts properties common to all CSi products (SAP2000, ETABS). +/// +public class SharedPropertiesExtractor +{ + private readonly CsiFramePropertiesExtractor _csiFramePropertiesExtractor; + private readonly CsiJointPropertiesExtractor _csiJointPropertiesExtractor; + private readonly CsiShellPropertiesExtractor _csiShellPropertiesExtractor; + + /// + /// Initializes a new instance of the class. + /// + /// The extractor for frame-specific properties. + /// The extractor for joint-specific properties. + /// The extractor for shell-specific properties. + /// + /// The sub-extractors are resolved by the DI container and injected into this class. + /// + public SharedPropertiesExtractor( + CsiFramePropertiesExtractor csiFramePropertiesExtractor, + CsiJointPropertiesExtractor csiJointPropertiesExtractor, + CsiShellPropertiesExtractor csiShellPropertiesExtractor + ) + { + _csiFramePropertiesExtractor = csiFramePropertiesExtractor; + _csiJointPropertiesExtractor = csiJointPropertiesExtractor; + _csiShellPropertiesExtractor = csiShellPropertiesExtractor; + } + + /// + /// Extracts properties from a CSi element wrapper, delegating to the appropriate sub-extractor based on the wrapper type. + /// + /// + /// A representing a CSi element (Frame, Joint, or Shell). + /// + /// + /// A containing common and specific properties of the CSi element, + /// or null if the wrapper type is unsupported. + /// + /// + /// Supported wrapper types: + /// (FrameObj API), + /// (PointObj API), + /// and (AreaObj API). + /// + public PropertyExtractionResult Extract(ICsiWrapper wrapper) + { + var objectData = new PropertyExtractionResult + { + Name = wrapper.Name, + Type = wrapper.ObjectName, + ApplicationId = string.Empty, // Populated in ExtractProperties + Properties = new Dictionary() + }; + + switch (wrapper) + { + case CsiFrameWrapper frame: + _csiFramePropertiesExtractor.ExtractProperties(frame, objectData); + break; + case CsiJointWrapper joint: + _csiJointPropertiesExtractor.ExtractProperties(joint, objectData); + break; + case CsiShellWrapper shell: + _csiShellPropertiesExtractor.ExtractProperties(shell, objectData); + break; + } + + return objectData; + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiFrameToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiFrameToSpeckleConverter.cs deleted file mode 100644 index dc46d1960..000000000 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiFrameToSpeckleConverter.cs +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 8ea827193..000000000 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiJointToSpeckleConverter.cs +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index 6b9169579..000000000 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/CSiShellToSpeckleConverter.cs +++ /dev/null @@ -1,53 +0,0 @@ -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/Geometry/LineToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/LineToSpeckleConverter.cs similarity index 63% rename from Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs rename to Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/LineToSpeckleConverter.cs index 3b302c297..25a99a91e 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/LineToSpeckleConverter.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/LineToSpeckleConverter.cs @@ -2,34 +2,31 @@ using Speckle.Converters.Common.Objects; using Speckle.Objects.Geometry; -namespace Speckle.Converters.CSiShared.ToSpeckle.Geometry; +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; /// -/// Every frame has as its displayValue a Speckle lines by defined by the endpoint coordinates. +/// Every frame has as its displayValue a Speckle line. This is defined by the start and end points. /// /// -/// 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. +/// Display value extraction is always handled by CsiShared. +/// This is because geometry representation is the same for both Sap2000 and Etabs products. +/// TODO: Point caching /// -public class LineToSpeckleConverter : ITypedConverter +public class LineToSpeckleConverter : ITypedConverter { - private readonly IConverterSettingsStore _settingsStore; - private readonly ITypedConverter _pointConverter; + private readonly IConverterSettingsStore _settingsStore; + private readonly ITypedConverter _pointConverter; public LineToSpeckleConverter( - IConverterSettingsStore settingsStore, - ITypedConverter pointConverter + IConverterSettingsStore settingsStore, + ITypedConverter pointConverter ) { _settingsStore = settingsStore; _pointConverter = pointConverter; } - public Line Convert(CSiFrameWrapper target) + public Line Convert(CsiFrameWrapper target) { // TODO: Better exception handling string startPoint = "", diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/MeshToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs similarity index 63% rename from Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/MeshToSpeckleConverter.cs rename to Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs index 831689689..827dd4185 100644 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Geometry/MeshToSpeckleConverter.cs +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/MeshToSpeckleConverter.cs @@ -2,37 +2,27 @@ using Speckle.Converters.Common.Objects; using Speckle.Objects.Geometry; -namespace Speckle.Converters.CSiShared.ToSpeckle.Geometry; +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; /// -/// Every shell has as its displayValue a planar 2D Speckle mesh defined by the vertices. +/// Every shell has as its displayValue a planar 2D Speckle mesh. This is 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 +/// Display value extraction is always handled by CsiShared. +/// This is because geometry representation is the same for both Sap2000 and Etabs products. +/// TODO: 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 +public class MeshToSpeckleConverter : ITypedConverter { - private readonly IConverterSettingsStore _settingsStore; + private readonly IConverterSettingsStore _settingsStore; - public MeshToSpeckleConverter(IConverterSettingsStore settingsStore) + public MeshToSpeckleConverter(IConverterSettingsStore settingsStore) { _settingsStore = settingsStore; } - public Mesh Convert(CSiShellWrapper target) + public Mesh Convert(CsiShellWrapper target) { int numberPoints = 0; string[] pointNames = Array.Empty(); @@ -43,6 +33,9 @@ public Mesh Convert(CSiShellWrapper target) throw new ArgumentException($"Failed to convert {target.Name} to Speckle Mesh"); } + // NOTE: Face indices format: + // - First value is the number of vertices in the face + // - Followed by indices into the vertex list List vertices = new List(numberPoints * 3); List faces = new List(numberPoints + 1); diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/PointToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/PointToSpeckleConverter.cs new file mode 100644 index 000000000..c0e83705d --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/Raw/PointToSpeckleConverter.cs @@ -0,0 +1,43 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Objects.Geometry; + +namespace Speckle.Converters.CSiShared.ToSpeckle.Raw; + +/// +/// Every joint has as its displayValue a Speckle point. This is defined by extracting their coordinates. +/// +/// +/// Display value extraction is always handled by CsiShared. +/// This is because geometry representation is the same for both Sap2000 and Etabs products. +/// +public class PointToSpeckleConverter : ITypedConverter +{ + private readonly IConverterSettingsStore _settingStore; + + public PointToSpeckleConverter(IConverterSettingsStore settingStore) + { + _settingStore = settingStore; + } + + public Point Convert(CsiJointWrapper target) // NOTE: This is just a temporary POC + { + double pointX = 0; + double pointY = 0; + double pointZ = 0; + + int result = _settingStore.Current.SapModel.PointObj.GetCoordCartesian( + target.Name, + ref pointX, + ref pointY, + ref pointZ + ); + + if (result != 0) + { + throw new ArgumentException($"Failed to convert {target.Name} to {typeof(Point)}"); + } + + return new(pointX, pointY, pointZ, _settingStore.Current.SpeckleUnits); + } +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CSiObjectToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CSiObjectToSpeckleConverter.cs deleted file mode 100644 index 1425c7cb4..000000000 --- a/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CSiObjectToSpeckleConverter.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Speckle.Converters.Common; -using Speckle.Converters.Common.Objects; -using Speckle.Converters.CSiShared.ToSpeckle.Helpers; -using Speckle.Sdk.Models; - -namespace Speckle.Converters.CSiShared.ToSpeckle.TopLevel; - -[NameAndRankValue(nameof(CSiWrapperBase), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] -public class CSiObjectToSpeckleConverter : IToSpeckleTopLevelConverter -{ - private readonly IConverterSettingsStore _settingsStore; - private readonly DisplayValueExtractor _displayValueExtractor; - private readonly ClassPropertyExtractor _classPropertyExtractor; - - /// - /// Converts CSi objects to Speckle format, extracting properties, display geometry and application IDs. - /// - public CSiObjectToSpeckleConverter( - IConverterSettingsStore settingsStore, - DisplayValueExtractor displayValueExtractor, - ClassPropertyExtractor classPropertyExtractor - ) - { - _settingsStore = settingsStore; - _displayValueExtractor = displayValueExtractor; - _classPropertyExtractor = classPropertyExtractor; - } - - public Base Convert(object target) => 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.ObjectName, - ["units"] = _settingsStore.Current.SpeckleUnits, - ["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.CSiShared/ToSpeckle/TopLevel/CsiObjectToSpeckleConverterBase.cs b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CsiObjectToSpeckleConverterBase.cs new file mode 100644 index 000000000..04134251c --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/ToSpeckle/TopLevel/CsiObjectToSpeckleConverterBase.cs @@ -0,0 +1,69 @@ +using Speckle.Converters.Common; +using Speckle.Converters.Common.Objects; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Objects; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.CSiShared.ToSpeckle.TopLevel; + +/// +/// Abstract base converter that serves as the foundation for product-specific CSi converters (ETABS, SAP2000). +/// Implements a Template Method pattern for object conversion while allowing product-specific customization. +/// +/// +/// Core Components: +/// 1. DisplayValueExtractor: Handles geometry conversion common to all CSi products +/// 2. IApplicationPropertiesExtractor: Handles both shared and product-specific properties through composition +/// +/// The Convert method defines the template for conversion: +/// - Extracts display geometry +/// - Gathers properties through the application-specific implementation +/// - Delegates final object creation to product-specific implementations +/// +public abstract class CsiObjectToSpeckleConverterBase : IToSpeckleTopLevelConverter +{ + private readonly IConverterSettingsStore _settingsStore; + private readonly DisplayValueExtractor _displayValueExtractor; + private readonly IApplicationPropertiesExtractor _applicationPropertiesExtractor; + + protected CsiObjectToSpeckleConverterBase( + IConverterSettingsStore settingsStore, + DisplayValueExtractor displayValueExtractor, + IApplicationPropertiesExtractor applicationPropertiesExtractor + ) + { + _settingsStore = settingsStore; + _displayValueExtractor = displayValueExtractor; + _applicationPropertiesExtractor = applicationPropertiesExtractor; + } + + public Base Convert(object target) => Convert((CsiWrapperBase)target); + + public Base Convert(CsiWrapperBase wrapper) + { + var displayValue = _displayValueExtractor.GetDisplayValue(wrapper).ToList(); + var objectData = _applicationPropertiesExtractor.ExtractProperties(wrapper); + + var baseObject = CreateTargetObject( + objectData.Name, + objectData.Type, + new List(), + displayValue, + objectData.Properties, + _settingsStore.Current.SpeckleUnits, + objectData.ApplicationId + ); + + return baseObject; + } + + protected abstract Base CreateTargetObject( + string name, + string type, + List elements, + List displayValue, + Dictionary properties, + string units, + string applicationId + ); +} diff --git a/Converters/CSi/Speckle.Converters.CSiShared/Utils/DictionaryUtils.cs b/Converters/CSi/Speckle.Converters.CSiShared/Utils/DictionaryUtils.cs new file mode 100644 index 000000000..257e7e5dd --- /dev/null +++ b/Converters/CSi/Speckle.Converters.CSiShared/Utils/DictionaryUtils.cs @@ -0,0 +1,36 @@ +namespace Speckle.Converters.CSiShared.Utils; + +/// +/// Provides utility methods for dictionary operations common across the CSI converter. +/// +public static class DictionaryUtils +{ + /// + /// Ensures a nested dictionary exists at the specified key, creating it if necessary. + /// Used for organizing properties into hierarchical categories (e.g., "Geometry", "Assignments", "Design"). + /// + /// The parent dictionary to check or modify + /// The key where the nested dictionary should exist + /// + /// The existing nested dictionary if present, or a new empty dictionary after adding it to the parent + /// + /// + /// Common usage: + /// + /// var geometry = DictionaryUtils.EnsureNestedDictionary(properties, "Geometry"); + /// geometry["startPoint"] = startPoint; + /// geometry["endPoint"] = endPoint; + /// + /// This pattern is used throughout property extractors to maintain consistent property organization. + /// + public static Dictionary EnsureNestedDictionary(Dictionary dictionary, string key) + { + if (!dictionary.TryGetValue(key, out var obj) || obj is not Dictionary nestedDictionary) + { + nestedDictionary = new Dictionary(); + dictionary[key] = nestedDictionary; + } + + return nestedDictionary; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs index 7f6fdf28c..0a79b9353 100644 --- a/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ServiceRegistration.cs @@ -1,20 +1,25 @@ using System.Reflection; using Microsoft.Extensions.DependencyInjection; -using Speckle.Converters.CSiShared.ToSpeckle.Raw; -using Speckle.Converters.ETABSShared.ToSpeckle.Raw; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.ToSpeckle.TopLevel; +using Speckle.Converters.ETABSShared.ToSpeckle.Helpers; +using Speckle.Converters.ETABSShared.ToSpeckle.TopLevel; using Speckle.Sdk; namespace Speckle.Converters.ETABSShared; public static class ServiceRegistration { - public static IServiceCollection AddETABSConverters(this IServiceCollection serviceCollection) + public static IServiceCollection AddEtabsConverters(this IServiceCollection serviceCollection) { var converterAssembly = Assembly.GetExecutingAssembly(); - serviceCollection.AddTransient(); - serviceCollection.AddTransient(); - serviceCollection.AddTransient(); + // Etabs-specific implementations + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); serviceCollection.AddMatchingInterfacesAsTransient(converterAssembly); diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems index 794cf6a91..c5feb494b 100644 --- a/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems +++ b/Converters/CSi/Speckle.Converters.ETABSShared/Speckle.Converters.ETABSShared.projitems @@ -10,8 +10,10 @@ - - - + + + + + diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsFramePropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsFramePropertiesExtractor.cs new file mode 100644 index 000000000..e7aa57f56 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsFramePropertiesExtractor.cs @@ -0,0 +1,84 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers; + +/// +/// Extracts ETABS-specific properties from frame elements using the FrameObj API calls. +/// +/// +/// Responsibilities: +/// - Extracts properties only available in ETABS (e.g., Label, Level) +/// - Complements by adding product-specific data +/// - Follows same pattern of single-purpose methods for clear API mapping +/// +/// Design Decisions: +/// - Maintains separate methods for each property following CSI API structure +/// - Properties are organized by their functional groups (Object ID, Assignments, Design) +/// +/// Integration: +/// - Used by for frame-specific property extraction +/// - Works alongside CsiFramePropertiesExtractor to build complete property set +/// +public sealed class EtabsFramePropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public EtabsFramePropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiFrameWrapper frame, Dictionary properties) + { + var objectId = DictionaryUtils.EnsureNestedDictionary(properties, "Object ID"); + objectId["designOrientation"] = GetDesignOrientation(frame); + (objectId["label"], objectId["level"]) = GetLabelAndLevel(frame); + + var assignments = DictionaryUtils.EnsureNestedDictionary(properties, "Assignments"); + assignments["springAssignment"] = GetSpringAssignmentName(frame); + + var design = DictionaryUtils.EnsureNestedDictionary(properties, "Design"); + design["designProcedure"] = GetDesignProcedure(frame); + } + + private (string label, string level) GetLabelAndLevel(CsiFrameWrapper frame) + { + string label = string.Empty, + level = string.Empty; + _ = _settingsStore.Current.SapModel.FrameObj.GetLabelFromName(frame.Name, ref label, ref level); + return (label, level); + } + + private string GetDesignOrientation(CsiFrameWrapper frame) + { + eFrameDesignOrientation designOrientation = eFrameDesignOrientation.Null; + _ = _settingsStore.Current.SapModel.FrameObj.GetDesignOrientation(frame.Name, ref designOrientation); + return designOrientation.ToString(); + } + + private string GetDesignProcedure(CsiFrameWrapper frame) + { + int myType = 0; + _ = _settingsStore.Current.SapModel.FrameObj.GetDesignProcedure(frame.Name, ref myType); + return myType switch + { + 1 => "Steel Frame Design", + 2 => "Concrete Frame Design", + 3 => "Composite Beam Design", + 4 => "Steel Joist Design", + 7 => "No Design", + 13 => "Composite Column Design", + _ => "Program determined" + }; + } + + private string GetSpringAssignmentName(CsiFrameWrapper frame) + { + string springPropertyName = "None"; // Is there a better way to handle null? + _ = _settingsStore.Current.SapModel.FrameObj.GetSpringAssignment(frame.Name, ref springPropertyName); + return springPropertyName; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsJointPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsJointPropertiesExtractor.cs new file mode 100644 index 000000000..9709ae653 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsJointPropertiesExtractor.cs @@ -0,0 +1,66 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers; + +/// +/// Extracts ETABS-specific properties from joint elements using the PointObj API calls. +/// +/// +/// Responsibilities: +/// - Extracts properties only available in ETABS (e.g., Diaphragm) +/// - Complements by adding product-specific data +/// - Follows same pattern of single-purpose methods for clear API mapping +/// +/// Design Decisions: +/// - Maintains separate methods for each property following CSI API structure +/// - Properties are organized by their functional groups (Object ID, Assignments, Design) +/// +/// Integration: +/// - Used by for joint-specific property extraction +/// - Works alongside CsiJointPropertiesExtractor to build complete property set +/// +public sealed class EtabsJointPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public EtabsJointPropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiJointWrapper joint, Dictionary properties) + { + var objectId = DictionaryUtils.EnsureNestedDictionary(properties, "Object ID"); + (objectId["label"], objectId["level"]) = GetLabelAndLevel(joint); + + var assignments = DictionaryUtils.EnsureNestedDictionary(properties, "Assignments"); + (assignments["diaphragmOption"], assignments["diaphragmName"]) = GetAssignedDiaphragm(joint); + assignments["springAssignment"] = GetSpringAssignmentName(joint); + } + + private (string diaphramOption, string diaphragmName) GetAssignedDiaphragm(CsiJointWrapper joint) + { + eDiaphragmOption diaphragmOption = eDiaphragmOption.Disconnect; + string diaphragmName = "None"; // Is there a better way to handle null? + _ = _settingsStore.Current.SapModel.PointObj.GetDiaphragm(joint.Name, ref diaphragmOption, ref diaphragmName); + return (diaphragmOption.ToString(), diaphragmName); + } + + private (string label, string level) GetLabelAndLevel(CsiJointWrapper joint) + { + string label = string.Empty, + level = string.Empty; + _ = _settingsStore.Current.SapModel.PointObj.GetLabelFromName(joint.Name, ref label, ref level); + return (label, level); + } + + private string GetSpringAssignmentName(CsiJointWrapper joint) + { + string springPropertyName = "None"; // Is there a better way to handle null? + _ = _settingsStore.Current.SapModel.PointObj.GetSpringAssignment(joint.Name, ref springPropertyName); + return springPropertyName; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsPropertiesExtractor.cs new file mode 100644 index 000000000..e39eed0f4 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsPropertiesExtractor.cs @@ -0,0 +1,58 @@ +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers; + +/// +/// ETABS-specific property extractor that composes with SharedPropertiesExtractor to provide +/// both shared and ETABS-specific properties. +/// +/// +/// Follows the composition pattern where SharedPropertiesExtractor handles common CSI properties, +/// while this class adds ETABS-specific properties. The extraction order is important: +/// 1. Extract shared properties first via SharedPropertiesExtractor +/// 2. Augment with ETABS-specific properties +/// This ensures consistent base properties with ETABS-specific enrichment. +/// +public class EtabsPropertiesExtractor : IApplicationPropertiesExtractor +{ + public SharedPropertiesExtractor SharedPropertiesExtractor { get; } + private readonly EtabsFramePropertiesExtractor _etabsFramePropertiesExtractor; + private readonly EtabsJointPropertiesExtractor _etabsJointPropertiesExtractor; + private readonly EtabsShellPropertiesExtractor _etabsShellPropertiesExtractor; + + public EtabsPropertiesExtractor( + SharedPropertiesExtractor sharedPropertiesExtractor, + EtabsFramePropertiesExtractor etabsFramePropertiesExtractor, + EtabsJointPropertiesExtractor etabsJointPropertiesExtractor, + EtabsShellPropertiesExtractor etabsShellPropertiesExtractor + ) + { + SharedPropertiesExtractor = sharedPropertiesExtractor; + _etabsFramePropertiesExtractor = etabsFramePropertiesExtractor; + _etabsJointPropertiesExtractor = etabsJointPropertiesExtractor; + _etabsShellPropertiesExtractor = etabsShellPropertiesExtractor; + } + + public PropertyExtractionResult ExtractProperties(ICsiWrapper wrapper) + { + // Extract shared properties first + var propertiesExtractionResult = SharedPropertiesExtractor.Extract(wrapper); + + // Then we go into Etabs-specific stuff + switch (wrapper) + { + case CsiFrameWrapper frame: + _etabsFramePropertiesExtractor.ExtractProperties(frame, propertiesExtractionResult.Properties); + break; + case CsiJointWrapper joint: + _etabsJointPropertiesExtractor.ExtractProperties(joint, propertiesExtractionResult.Properties); + break; + case CsiShellWrapper shell: + _etabsShellPropertiesExtractor.ExtractProperties(shell, propertiesExtractionResult.Properties); + break; + } + + return propertiesExtractionResult; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsShellPropertiesExtractor.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsShellPropertiesExtractor.cs new file mode 100644 index 000000000..638b3fb7c --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Helpers/EtabsShellPropertiesExtractor.cs @@ -0,0 +1,97 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.Utils; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.Helpers; + +/// +/// Extracts ETABS-specific properties from shell elements using the AreaObj API calls. +/// +/// +/// Responsibilities: +/// - Extracts properties only available in ETABS (e.g., Label, Level) +/// - Complements by adding product-specific data +/// - Follows same pattern of single-purpose methods for clear API mapping +/// +/// Design Decisions: +/// - Maintains separate methods for each property following CSI API structure +/// - Properties are organized by their functional groups (Object ID, Assignments, Design) +/// +/// Integration: +/// - Used by for shell-specific property extraction +/// - Works alongside CsiShellPropertiesExtractor to build complete property set +/// +public sealed class EtabsShellPropertiesExtractor +{ + private readonly IConverterSettingsStore _settingsStore; + + public EtabsShellPropertiesExtractor(IConverterSettingsStore settingsStore) + { + _settingsStore = settingsStore; + } + + public void ExtractProperties(CsiShellWrapper shell, Dictionary properties) + { + var objectId = DictionaryUtils.EnsureNestedDictionary(properties, "Object ID"); + objectId["designOrientation"] = GetDesignOrientation(shell); + (objectId["label"], objectId["level"]) = GetLabelAndLevel(shell); + + var assignments = DictionaryUtils.EnsureNestedDictionary(properties, "Assignments"); + assignments["diaphragmName"] = GetAssignedDiaphragmName(shell); + assignments["isOpening"] = IsOpening(shell); + assignments["pierAssignment"] = GetPierAssignmentName(shell); + assignments["spandrelAssignment"] = GetSpandrelAssignmentName(shell); + assignments["springAssignmentName"] = GetSpringAssignmentName(shell); + } + + private (string label, string level) GetLabelAndLevel(CsiShellWrapper shell) + { + string label = string.Empty, + level = string.Empty; + _ = _settingsStore.Current.SapModel.AreaObj.GetLabelFromName(shell.Name, ref label, ref level); + return (label, level); + } + + private string GetDesignOrientation(CsiShellWrapper shell) + { + eAreaDesignOrientation designOrientation = eAreaDesignOrientation.Null; + _ = _settingsStore.Current.SapModel.AreaObj.GetDesignOrientation(shell.Name, ref designOrientation); + return designOrientation.ToString(); + } + + private string GetAssignedDiaphragmName(CsiShellWrapper shell) + { + string diaphragmName = "None"; // Is there a better way to handle null? + _ = _settingsStore.Current.SapModel.AreaObj.GetDiaphragm(shell.Name, ref diaphragmName); + return diaphragmName; + } + + private string IsOpening(CsiShellWrapper shell) + { + bool isOpening = false; + _ = _settingsStore.Current.SapModel.AreaObj.GetOpening(shell.Name, ref isOpening); + return isOpening.ToString(); + } + + private string GetPierAssignmentName(CsiShellWrapper shell) + { + string pierAssignment = "None"; // Is there a better way to handle null? + _ = _settingsStore.Current.SapModel.AreaObj.GetPier(shell.Name, ref pierAssignment); + return pierAssignment; + } + + private string GetSpandrelAssignmentName(CsiShellWrapper shell) + { + string spandrelAssignment = "None"; // Is there a better way to handle null? + _ = _settingsStore.Current.SapModel.AreaObj.GetSpandrel(shell.Name, ref spandrelAssignment); + return spandrelAssignment; + } + + private string GetSpringAssignmentName(CsiShellWrapper shell) + { + string springAssignmentName = "None"; // Is there a better way to handle null? + _ = _settingsStore.Current.SapModel.AreaObj.GetSpringAssignment(shell.Name, ref springAssignmentName); + return springAssignmentName; + } +} diff --git a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs deleted file mode 100644 index fe8bea78d..000000000 --- a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/FrameToSpeckleConverter.cs +++ /dev/null @@ -1,51 +0,0 @@ -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 deleted file mode 100644 index b853deb16..000000000 --- a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/JointToSpeckleConverter.cs +++ /dev/null @@ -1,52 +0,0 @@ -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 deleted file mode 100644 index 8c841b781..000000000 --- a/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/Raw/ShellToSpeckleConverter.cs +++ /dev/null @@ -1,49 +0,0 @@ -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/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/TopLevel/EtabsObjectToSpeckleConverter.cs b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/TopLevel/EtabsObjectToSpeckleConverter.cs new file mode 100644 index 000000000..2bdda3d11 --- /dev/null +++ b/Converters/CSi/Speckle.Converters.ETABSShared/ToSpeckle/TopLevel/EtabsObjectToSpeckleConverter.cs @@ -0,0 +1,54 @@ +using Speckle.Converters.Common; +using Speckle.Converters.CSiShared; +using Speckle.Converters.CSiShared.ToSpeckle.Helpers; +using Speckle.Converters.CSiShared.ToSpeckle.TopLevel; +using Speckle.Objects; +using Speckle.Objects.Data; +using Speckle.Sdk.Models; + +namespace Speckle.Converters.ETABSShared.ToSpeckle.TopLevel; + +/// +/// Top level converter responsible for converting Etabs objects to Speckle objects. +/// Implements the Template Method pattern through inheritance from (abstract) CsiObjectToSpeckleConverterBase. +/// +/// +/// Conversion Flow: +/// 1. EtabsObjectToSpeckleConverter inherits base conversion logic from CsiObjectToSpeckleConverterBase +/// 2. Base Convert method orchestrates the conversion process: +/// - DisplayValue extraction (handled by CsiShared - shared geometry conversion) +/// - Object data querying (combination of shared and application-specific data) +/// * SharedPropertiesExtractor for common CSi data +/// * IApplicationPropertiesExtractor for ETABS-specific data +/// 3. CreateTargetObject method ensures type-safe conversion to EtabsObject +/// +[NameAndRankValue(nameof(CsiWrapperBase), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)] +public class EtabsObjectToSpeckleConverter : CsiObjectToSpeckleConverterBase +{ + public EtabsObjectToSpeckleConverter( + IConverterSettingsStore settingsStore, + DisplayValueExtractor displayValueExtractor, + IApplicationPropertiesExtractor applicationPropertiesExtractor + ) + : base(settingsStore, displayValueExtractor, applicationPropertiesExtractor) { } + + protected override Base CreateTargetObject( + string name, + string type, + List elements, + List displayValue, + Dictionary properties, + string units, + string applicationId + ) => + new EtabsObject + { + name = name, + type = type, + elements = elements.Cast().ToList(), + displayValue = displayValue, + properties = properties, + units = units, + applicationId = applicationId + }; +} diff --git a/Speckle.Connectors.sln b/Speckle.Connectors.sln index 75f0759b6..79ab2e23f 100644 --- a/Speckle.Connectors.sln +++ b/Speckle.Connectors.sln @@ -639,6 +639,7 @@ Global Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{01f98733-7352-47ad-a594-537d979de3de}*SharedItemsImports = 5 Connectors\Tekla\Speckle.Connector.TeklaShared\Speckle.Connectors.TeklaShared.projitems*{025c85f8-f741-4600-bc46-5fead754b65d}*SharedItemsImports = 5 Connectors\CSi\Speckle.Connectors.CsiShared\Speckle.Connectors.CsiShared.projitems*{115d6106-1801-484a-b4e5-bcc94b6e5c7f}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{115d6106-1801-484a-b4e5-bcc94b6e5c7f}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{19424b55-058c-4e9c-b86f-700aef9eaec3}*SharedItemsImports = 5 Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{1b5c5fb2-3b22-4371-9aa5-3edf3b4d62de}*SharedItemsImports = 13 Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{1e2644a9-6b31-4350-8772-ceaad6ee0b21}*SharedItemsImports = 5 @@ -668,7 +669,9 @@ Global Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{68cf9bdf-94ac-4d2d-a7bd-d1c064f97051}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{6a40cbe4-ecab-4ced-9917-5c64cbf75da6}*SharedItemsImports = 13 Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{791e3288-8001-4d54-8eab-03d1d7f51044}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{791e3288-8001-4d54-8eab-03d1d7f51044}*SharedItemsImports = 5 Connectors\CSi\Speckle.Connectors.CsiShared\Speckle.Connectors.CsiShared.projitems*{7c49337a-6f7b-47ab-b549-42e799e89cf2}*SharedItemsImports = 5 + Connectors\CSi\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems*{7c49337a-6f7b-47ab-b549-42e799e89cf2}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared.Cef\Speckle.Connectors.RevitShared.Cef.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5 Connectors\Revit\Speckle.Connectors.RevitShared\Speckle.Connectors.RevitShared.projitems*{7f1fdcf2-0ce8-4119-b3c1-f2cc6d7e1c36}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{81fcee13-feac-475d-9ef9-71132ef26909}*SharedItemsImports = 5 @@ -698,6 +701,7 @@ Global Connectors\Autocad\Speckle.Connectors.AutocadShared\Speckle.Connectors.AutocadShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5 Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{ca8eae01-ab9f-4ec1-b6f3-73721487e9e1}*SharedItemsImports = 5 Converters\CSi\Speckle.Converters.CSiShared\Speckle.Converters.CSiShared.projitems*{d61ecd90-3d17-4af0-8b1a-0e0ad302dff9}*SharedItemsImports = 5 + Converters\CSi\Speckle.Converters.ETABSShared\Speckle.Converters.ETABSShared.projitems*{d61ecd90-3d17-4af0-8b1a-0e0ad302dff9}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared.Tests\Speckle.Converters.RevitShared.Tests.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5 Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{d8069a23-ad2e-4c9e-8574-7e8c45296a46}*SharedItemsImports = 5 Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{db31e57b-60fc-49be-91e0-1374290bcf03}*SharedItemsImports = 5