Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 127 additions & 1 deletion Etabs_Adapter/CRUD/Read/_Read.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using BH.oM.Base;
Expand All @@ -39,6 +40,8 @@
using BH.oM.Adapter;
using System.ComponentModel;
using BH.oM.Data.Requests;
using BH.oM.Spatial.SettingOut;
using BH.Adapter.ETABS.Types;

namespace BH.Adapter.ETABS
{
Expand Down Expand Up @@ -99,6 +102,120 @@ protected override IEnumerable<IBHoMObject> IRead(Type type, IList ids, ActionCo
return new List<IBHoMObject>();//<--- returning null will throw error in replace method of BHOM_Adapter line 34: can't do typeof(null) - returning null does seem the most sensible to return though
}

/***************************************************/

public IEnumerable<IBHoMObject> Read <T>(T request, ActionConfig actionConfig = null) where T : ILogicalRequest //- **GENERIC TYPES**
{

// Use of GENERIC TYPES, HASH TABLES, STREAMS and RECURSION

/* Use a HashSet data structure to make sure no collected elements are duplicate and to make access/search as fast as possible - **HASH TABLES ** */
HashSet < IBHoMObject > bhomObjects= new HashSet<IBHoMObject >();

/* 1. Handle the LogicalANDRequest */
if (request is LogicalAndRequest)
{
// 1.1 Initialize List of Requests to be extracted from LogicalRequest
List<IRequest> requests = (request as LogicalAndRequest).Requests;
// 1.2 Initialize DynamicComparer class instance allowing to check equality between IBHoMObject class instances
DynamicComparer iBHoMETABSComparer = new DynamicComparer();

// 1.3 Add to bhomObjects List all objects abiding by the FIRST request in the list... - **STREAMS**
IRequest req = requests[0];
// ...when it's a FilterRequest...
if (req.GetType() == typeof(FilterRequest)) Read((FilterRequest)req, actionConfig).ToList().ForEach(bhomObj => bhomObjects.Add(bhomObj));
// ...when it's a SelectionRequest...
if (req.GetType() == typeof(SelectionRequest)) Read((SelectionRequest)req, actionConfig).ToList().ForEach(bhomObj => bhomObjects.Add(bhomObj));
// ...when it's a LogicalRequest...call the method recursively! - **RECURSION**
if (req is ILogicalRequest) bhomObjects=Read<ILogicalRequest>((ILogicalRequest)req, actionConfig).ToHashSet();


// 1.4 Add to bhomObjects List all objects abiding by ALL THE OTHER requests in the list... - **STREAMS**
for (int i = 1; i < requests.Count; i++)
{
// ...when they are FilterRequests...
if (requests[i].GetType() == typeof(FilterRequest)) bhomObjects = (bhomObjects.ToList().Intersect(Read((FilterRequest)requests[i], actionConfig).ToList(), iBHoMETABSComparer)).ToHashSet();
// ...when they are SelectionRequests...
if (requests[i].GetType() == typeof(SelectionRequest)) bhomObjects = (bhomObjects.ToList().Intersect(Read((SelectionRequest)requests[i], actionConfig).ToList(), iBHoMETABSComparer)).ToHashSet();
// ...when they are LogicalRequests...call the method recursively! - **RECURSION**
if (requests[i] is ILogicalRequest) bhomObjects = (bhomObjects.ToList().Intersect(Read<ILogicalRequest>((ILogicalRequest)requests[i], actionConfig))).ToHashSet();
}

// 1.5 Return list of bhomObjects
return bhomObjects;
}

/* 2. Handle the LogicalORRequest */
else if (request is LogicalOrRequest)
{
// 2.1 Initialize List of Requests to be extracted from LogicalRequest
List<IRequest> requests = (request as LogicalOrRequest).Requests;

// 2.2 Add to bhomObjects List all objects abiding by ALL requests in the list... - **STREAMS**
// ...when they are FilterRequests...
requests.ForEach(req => { if (req.GetType() == typeof(FilterRequest)) Read((FilterRequest)req, actionConfig).ToList().ForEach(bhomObj => bhomObjects.Add(bhomObj));
// ...when they are SelectionRequests...
if (req.GetType() == typeof(SelectionRequest)) Read((SelectionRequest)req, actionConfig).ToList().ForEach(bhomObj => bhomObjects.Add(bhomObj));
// ...when they are LogicalRequests...call the method recursively! - **RECURSION**
if (req is ILogicalRequest) bhomObjects=Read<ILogicalRequest>((ILogicalRequest)req, actionConfig).ToHashSet(); });

// 2.3 Return list of bhomObjects
return bhomObjects;
}

/* 3. Handle the LogicalNOTRequest */
else if (request is LogicalNotRequest)
{
// 3.1 Initialize Lists and Hashsets for collecting all bhomObjects - **HASH TABLES **
IRequest iRequest = (request as LogicalNotRequest).Request;
HashSet<IBHoMObject> notBhomObjects = new HashSet<IBHoMObject>();
List<IBHoMObject> allBhomObjects = new List<IBHoMObject>();

// 3.2 Add to NOTbhomObjects HashSet all unique objects abiding by the Request input in the LogicalNOTRequest... - **STREAMS**
// ...when it's a FilterRequest...
if (iRequest.GetType() == typeof(FilterRequest)) Read((FilterRequest)iRequest, actionConfig).ToList().ForEach(bhomObj => notBhomObjects.Add(bhomObj));
// ...when it's a SelectionRequest...
if (iRequest.GetType() == typeof(SelectionRequest)) Read((SelectionRequest)iRequest, actionConfig).ToList().ForEach(bhomObj => notBhomObjects.Add(bhomObj));
// ...when it's a LogicalRequest...call the method recursively! - **RECURSION**
if (iRequest is ILogicalRequest) Read<ILogicalRequest>((ILogicalRequest)iRequest, actionConfig).ToList().ForEach(bhomObj => notBhomObjects.Add(bhomObj));


// 3.3 Get all bhomObjects of ANY kind from ETABS Model... - **STREAMS**
Type[] bhomTypes = {typeof(Node),typeof(Bar),typeof(ISectionProperty), typeof(IMaterialFragment), typeof(Panel),
typeof(ISurfaceProperty), typeof(LoadCombination), typeof(Loadcase), typeof(ILoad), typeof(RigidLink),
typeof(LinkConstraint),typeof(oM.Spatial.SettingOut.Level),typeof(oM.Spatial.SettingOut.Grid),typeof(FEMesh)};

allBhomObjects = bhomTypes.ToList()
.Select(bhomType =>{ FilterRequest fr = new FilterRequest();
fr.Type = bhomType;
return fr;})
.Select(filtReq => Read(filtReq))
.SelectMany(x=>x) // Streams function allowing to flatten multidimensional lists!
.ToList();

// 3.4 Return the difference between ALL Objects and the ones NOT to be taken - **STREAMS**
// - .Except() Streams function returns the difference between two lists/data structures based on a specified EqualityComparer class)
return allBhomObjects.Except(notBhomObjects,new DynamicComparer());
}

BH.Engine.Base.Compute.RecordError($"Read for {request.GetType().Name} is not implemented in {(this as dynamic).GetType().Name}.");
return new List<IBHoMObject>();
}


public IEnumerable<IBHoMObject> Read(FilterRequest filterRequest, ActionConfig actionConfig = null)
{
// Extract the Ids from the FilterRequest
IList objectIds = null;
object idObject;
if (filterRequest.Equalities.TryGetValue("ObjectIds", out idObject) && idObject is IList)
objectIds = idObject as IList;

return IRead(filterRequest.Type, objectIds, actionConfig);
}



/***************************************************/

public IEnumerable<IBHoMObject> Read(SelectionRequest request, ActionConfig actionConfig = null)
Expand All @@ -120,7 +237,7 @@ public Dictionary<Type, List<string>> SelectedElements()
int numItems = 0;
int[] objectTypes = new int[0];
string[] objectIds = new string[0];

m_model.SelectObj.GetSelected(ref numItems, ref objectTypes, ref objectIds);

// Replace Panels' type numbers with Openings' type numbers
Expand Down Expand Up @@ -186,8 +303,17 @@ private static List<string> FilterIds(IEnumerable<string> ids, IEnumerable<strin
}
}


/***************************************************/

public static List<Type> GetClassesInNamespace(Assembly assembly, string nameSpace)
{
return assembly.GetTypes()
.Where(t => t.IsClass && t.Namespace == nameSpace)
.ToList();
}


}
}

Expand Down
1 change: 1 addition & 0 deletions Etabs_Adapter/Etabs_Adapter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@
<Compile Include="CRUD\Update\Node.cs" />
<Compile Include="CRUD\Update\Material.cs" />
<Compile Include="CRUD\Update\Panel.cs" />
<Compile Include="Types\DynamicComparer.cs" />
<Compile Include="Types\NextIndex.cs" />
<Compile Include="CRUD\Read\Link.cs" />
<Compile Include="CRUD\Read\Material.cs" />
Expand Down
103 changes: 103 additions & 0 deletions Etabs_Adapter/Types/DynamicComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2021, the respective contributors. All rights reserved.
*

Check failure on line 4 in Etabs_Adapter/Types/DynamicComparer.cs

View check run for this annotation

BHoMBot-CI / copyright-compliance

Etabs_Adapter/Types/DynamicComparer.cs#L3-L4

Copyright message is invalid - For more information see https://bhom.xyz/documentation/DevOps/Code%20Compliance%20and%20CI/Compliance%20Checks/HasValidCopyright
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using BH.oM.Adapters.ETABS;
using BH.oM.Base;
using BH.oM.Spatial.SettingOut;
using BH.oM.Structure.Constraints;
using BH.oM.Structure.Elements;
using BH.oM.Structure.Loads;
using BH.oM.Structure.MaterialFragments;
using BH.oM.Structure.SectionProperties;
using BH.oM.Structure.SurfaceProperties;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace BH.Adapter.ETABS.Types
{
public class DynamicComparer : IEqualityComparer<IBHoMObject>
{

// Use of STREAMS and REFLECTIONS

// 1. Equality based on ETABS obj Id or Name
public bool Equals(IBHoMObject obj1, IBHoMObject obj2)
{
if (obj1 == null || obj2 == null) return false;
if (obj1.GetType() != obj2.GetType()) return false;


Type objType = obj1.GetType();

// For physical objects, use Id as it is never null
if (objType == typeof(Node) || objType == typeof(Bar) || objType == typeof(Panel) || objType == typeof(RigidLink) || objType == typeof(FEMesh))
return GetEtabsId(obj1).Id.ToString() == GetEtabsId(obj2).Id.ToString();
// For all other items, use Name as it is never null
if (objType == typeof(ISectionProperty) || objType == typeof(IMaterialFragment) || objType == typeof(ISurfaceProperty) ||
objType == typeof(Loadcase) || objType == typeof(LoadCombination) || objType == typeof(ILoad) || objType == typeof(LinkConstraint) ||
objType == typeof(Level) || objType == typeof(Grid))
return obj1.Name.ToString() == obj2.Name.ToString();
Comment on lines +55 to +62
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have all these comparers defined in the Adapter - can we not make sure of that rather than rewriting it here:

{typeof(Node), new BH.Engine.Structure.NodeDistanceComparer(3) },
{typeof(Bar), new BH.Engine.Structure.BarEndNodesDistanceComparer(3) },
{typeof(ISectionProperty), new NameOrDescriptionComparer() },
{typeof(IMaterialFragment), new NameOrDescriptionComparer() },
{typeof(ISurfaceProperty), new NameOrDescriptionComparer() },
{typeof(BH.oM.Adapters.ETABS.Elements.Diaphragm), new BHoMObjectNameComparer() },

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peterjamesnugent, thanks for the comment.
It would be great to reuse it yeah.
Only thing is that there are some objects that are not dealt with by the Adapter Comparer (e.g. Level, FEMesh,LoadCase...) - see screenshot below.
Can we add them in it?

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I don't think that's an issue adding them to the Comparer.cs - most of them will be NameOrDescriptionComparer() anyway.

Can you add to the test script, trying to push two objects with the same name for each type you add (just to test that we are not breaking anything).


return false;

}

// 2. HashCode based on Hash function of ETABS obj Id+Label or Type+Name
public int GetHashCode(IBHoMObject obj)
{
Type objType = obj.GetType();

// For physical objects, use Id and Label as they are never null
if (objType == typeof(Node) || objType == typeof(Bar) || objType == typeof(Panel) || objType == typeof(RigidLink) || objType == typeof(FEMesh))
return (GetEtabsId(obj).Id.ToString() + GetEtabsId(obj).Label.ToString()).GetHashCode();
// For all other items, use Name as Id/Label can be null
else if (objType == typeof(ISectionProperty) || objType == typeof(IMaterialFragment) || objType == typeof(ISurfaceProperty) ||
objType == typeof(Loadcase) || objType == typeof(LoadCombination) || objType == typeof(ILoad) || objType == typeof(LinkConstraint) ||
objType == typeof(Level) || objType == typeof(Grid))
return (obj.GetType().ToString() + obj.Name.ToString()).GetHashCode();

return 0;

}

// 3. Get ETABS Id using REFLECTION - **REFLECTION**
private ETABSId GetEtabsId(IBHoMObject obj)
{
// 1. Get the object Type
Type objsType = obj.GetType();
// 2. Get the Property Fragments - via REFLECTION
PropertyInfo fragmentsProperty = objsType.GetProperty("Fragments");
// 3. Downcast the Fragments Property to FragmentSet Class - via REFLECTION
FragmentSet fragments = (FragmentSet)fragmentsProperty.GetValue(obj);
// 4. Get the ETABSId object contained in the FragmentSet - via STREAMS
ETABSId etabsId = (ETABSId)(fragments.ToList().Find(frag => frag.GetType() == typeof(ETABSId)));
// 5. Return the Etabs Id of the object
return etabsId;
}


}
}