Skip to content

Commit

Permalink
bjorn/cnx 829 add root object builder and create collection structure (
Browse files Browse the repository at this point in the history
…#435)

* ETABSShared

- Implemented ETABSShared projects for Connectors and Converters to account for ETABS specific things that SAP 2000 doesn't account for  such as levels or frames being further classified as Column, Beam or Brace or shells as wall or floor

* collection poc

- POC for ETABS collection structure
- Logic for getting further classificiations of Frames as either Column, Beam or Brace
- Same for Shells: either Wall or Floor etc.
- Placeholders for object properties

* documentation

- Putting thoughts to "paper"
- A lot of the TODOs will be part of next milestone

* adding new projects to local.sln

* csharpier

got deactivated and wasn't formatting on save anymore :(

* unnecessary registration

* using directives

---------

Co-authored-by: Claire Kuang <[email protected]>
  • Loading branch information
bjoernsteinhagen and clairekuang authored Dec 5, 2024
1 parent f0e54ca commit 0341ebc
Show file tree
Hide file tree
Showing 38 changed files with 834 additions and 138 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;

namespace Speckle.Connectors.CSiShared.HostApp;
Expand All @@ -13,27 +14,30 @@ namespace Speckle.Connectors.CSiShared.HostApp;
/// </remarks>
public class CSiSendCollectionManager
{
private readonly IConverterSettingsStore<CSiConversionSettings> _converterSettings;
private readonly Dictionary<string, Collection> _collectionCache = new();
protected IConverterSettingsStore<CSiConversionSettings> ConverterSettings { get; }
protected Dictionary<string, Collection> CollectionCache { get; } = new();

public CSiSendCollectionManager(IConverterSettingsStore<CSiConversionSettings> converterSettings)
{
_converterSettings = converterSettings;
ConverterSettings = converterSettings;
}

// TODO: Frames could be further classified under Columns, Braces and Beams. Same for Shells which could be classified into walls, floors
public Collection AddObjectCollectionToRoot(ICSiWrapper csiObject, Collection rootObject)
public virtual Collection AddObjectCollectionToRoot(Base convertedObject, Collection rootObject)
{
var path = csiObject.GetType().Name.Replace("Wrapper", ""); // CSiJointWrapper → CSiJoint, CSiFrameWrapper → CSiFrame etc.
var path = GetCollectionPath(convertedObject);

if (_collectionCache.TryGetValue(path, out Collection? collection))
if (CollectionCache.TryGetValue(path, out Collection? collection))
{
return collection;
}

Collection childCollection = new(path);
Collection childCollection = CreateCollection(convertedObject);
rootObject.elements.Add(childCollection);
_collectionCache[path] = childCollection;
CollectionCache[path] = childCollection;
return childCollection;
}

protected virtual string GetCollectionPath(Base convertedObject) => convertedObject["type"]?.ToString() ?? "Unknown";

protected virtual Collection CreateCollection(Base convertedObject) => new(GetCollectionPath(convertedObject));
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public async Task<RootObjectBuilderResult> Build(
private SendConversionResult ConvertCSiObject(ICSiWrapper csiObject, Collection typeCollection, string projectId)
{
string applicationId = $"{csiObject.ObjectType}{csiObject.Name}"; // TODO: NO! Use GUID
string sourceType = csiObject.GetType().Name;
string sourceType = csiObject.ObjectName;

try
{
Expand All @@ -99,7 +99,7 @@ private SendConversionResult ConvertCSiObject(ICSiWrapper csiObject, Collection
converted = _rootToSpeckleConverter.Convert(csiObject);
}

var collection = _sendCollectionManager.AddObjectCollectionToRoot(csiObject, typeCollection);
var collection = _sendCollectionManager.AddObjectCollectionToRoot(converted, typeCollection);
collection.elements ??= new List<Base>();
collection.elements.Add(converted);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void Main(ref cSapModel sapModel, ref cPluginCallback pluginCallback)

public virtual int Info(ref string text)
{
text = "Hey Speckler! This is our next-gen ETABS Connector.";
text = "Hey Speckler! This is our next-gen CSi Connector.";
return 0;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Windows.Forms.Integration;
using System.ComponentModel;
using System.Windows.Forms.Integration;
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.Common;
using Speckle.Connectors.CSiShared.HostApp;
Expand All @@ -8,6 +9,7 @@

namespace Speckle.Connectors.CSiShared;

[DesignerCategory("")]
public abstract class SpeckleFormBase : Form
{
protected ElementHost Host { get; set; }
Expand All @@ -20,9 +22,7 @@ protected SpeckleFormBase()
Text = "Speckle (Beta)";

var services = new ServiceCollection();
services.Initialize(HostApplications.ETABS, GetVersion());
services.AddCSi();
services.AddCSiConverters();
ConfigureServices(services);

Container = services.BuildServiceProvider();

Expand All @@ -32,6 +32,17 @@ protected SpeckleFormBase()
FormClosing += Form1Closing;
}

protected virtual void ConfigureServices(IServiceCollection services)
{
services.Initialize(GetHostApplication(), GetVersion());
services.AddCSi();
services.AddCSiConverters();
}

protected abstract HostApplication GetHostApplication();

protected abstract HostAppVersion GetVersion();

public void SetSapModel(ref cSapModel sapModel, ref cPluginCallback pluginCallback)
{
_sapModel = sapModel;
Expand All @@ -47,8 +58,6 @@ protected void Form1Closing(object? sender, FormClosingEventArgs e)
_pluginCallback.Finish(0);
}

protected abstract HostAppVersion GetVersion();

public new void ShowDialog()
{
base.ShowDialog();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using Speckle.Connectors.CSiShared;
using Speckle.Connectors.ETABSShared;
using Speckle.Sdk.Host;

// NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin
// Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed
#pragma warning disable IDE0130
namespace Speckle.Connectors.ETABS21;

public class SpeckleForm : SpeckleFormBase
public class SpeckleForm : ETABSSpeckleFormBase
{
protected override HostAppVersion GetVersion() => HostAppVersion.v2021;
protected override HostAppVersion GetVersion() => HostAppVersion.v2021; // TODO: We need a v21
}
6 changes: 3 additions & 3 deletions Connectors/CSi/Speckle.Connectors.ETABS21/Plugin/cPlugin.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using Speckle.Connectors.CSiShared;
using Speckle.Connectors.ETABSShared;

// NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin
// Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed
#pragma warning disable IDE0130
namespace Speckle.Connectors.ETABS21;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
public class cPlugin : CSiPluginBase
public class cPlugin : ETABSPluginBase
{
protected override SpeckleFormBase CreateForm() => new SpeckleForm();
protected override ETABSSpeckleFormBase CreateETABSForm() => new SpeckleForm();
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@

<Import Project="..\Speckle.Connectors.CSiShared\Speckle.Connectors.CSiShared.projitems" Label="Shared" />

<Import Project="..\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems" Label="Shared" />

</Project>
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using Speckle.Connectors.CSiShared;
using Speckle.Connectors.ETABSShared;
using Speckle.Sdk.Host;

// NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin
// Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed
#pragma warning disable IDE0130
namespace Speckle.Connectors.ETABS22;

public class SpeckleForm : SpeckleFormBase
public class SpeckleForm : ETABSSpeckleFormBase
{
protected override HostAppVersion GetVersion() => HostAppVersion.v2021;
protected override HostAppVersion GetVersion() => HostAppVersion.v2021; // TODO: v22
}
6 changes: 3 additions & 3 deletions Connectors/CSi/Speckle.Connectors.ETABS22/Plugin/cPlugin.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using Speckle.Connectors.CSiShared;
using Speckle.Connectors.ETABSShared;

// NOTE: Plugin entry point must match the assembly name, otherwise ETABS hits you with a "Not found" error when loading plugin
// Disabling error below to prioritize DUI3 project structure. Name of cPlugin class cannot be changed
#pragma warning disable IDE0130
namespace Speckle.Connectors.ETABS22;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
public class cPlugin : CSiPluginBase
public class cPlugin : ETABSPluginBase
{
protected override SpeckleFormBase CreateForm() => new SpeckleForm();
protected override ETABSSpeckleFormBase CreateETABSForm() => new SpeckleForm();
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@

<Import Project="..\Speckle.Connectors.CSiShared\Speckle.Connectors.CSiShared.projitems" Label="Shared" />

<Import Project="..\Speckle.Connectors.ETABSShared\Speckle.Connectors.ETABSShared.projitems" Label="Shared" />

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Converters.Common;
using Speckle.Converters.CSiShared;
using Speckle.Sdk.Models;
using Speckle.Sdk.Models.Collections;

namespace Speckle.Connectors.ETABSShared.HostApp;

/// <summary>
/// ETABS-specific collection manager that organizes structural elements by story and type.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public class ETABSSendCollectionManager : CSiSendCollectionManager
{
public ETABSSendCollectionManager(IConverterSettingsStore<CSiConversionSettings> 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, object>;
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<string, object>? 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<string, object>? properties)
{
if (properties != null && properties.TryGetValue("story", out var story))
{
return story?.ToString() ?? "Unassigned";
}
return "Unassigned";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Speckle.Connectors.CSiShared;

namespace Speckle.Connectors.ETABSShared;

[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "<Pending>")]
public abstract class ETABSPluginBase : CSiPluginBase
{
public override int Info(ref string text)
{
text = "Hey Speckler! This is our next-gen ETABS Connector.";
return 0;
}

protected override SpeckleFormBase CreateForm() => CreateETABSForm();

protected abstract ETABSSpeckleFormBase CreateETABSForm();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.CSiShared;
using Speckle.Sdk.Host;

namespace Speckle.Connectors.ETABSShared;

public abstract class ETABSSpeckleFormBase : SpeckleFormBase
{
protected override HostApplication GetHostApplication() => HostApplications.ETABS;

protected override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
services.AddETABS();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
using Speckle.Connectors.CSiShared.HostApp;
using Speckle.Connectors.ETABSShared.HostApp;
using Speckle.Converters.ETABSShared;

namespace Speckle.Connectors.ETABSShared;

public static class ServiceRegistration
{
public static IServiceCollection AddETABS(this IServiceCollection services)
{
services.AddETABSConverters();
services.AddScoped<CSiSendCollectionManager, ETABSSendCollectionManager>();

return services;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>5d1e0b0d-56a7-4e13-b9a9-8633e02b8f17</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Speckle.Connectors.ETABSShared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ETABSSendCollectionManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\ETABSPluginBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Plugin\ETABSSpeckleFormBase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ServiceRegistration.cs" />
</ItemGroup>
</Project>
Loading

0 comments on commit 0341ebc

Please sign in to comment.