-
Notifications
You must be signed in to change notification settings - Fork 16
The BHoM Toolkit
This page explains how to develop what we call a Toolkit.
Before reading this page, please check out:
- What is a Toolkit?
- Create a new software Toolkit using the BHoM Toolkit Template
- Implement the Adapter
- Additional note: set up a repo without using the template (not recommended)
A Toolkit is a Visual Studio solution that can contain one or more of the following:
- A BHoM_Adapter project, that allows to implement the connection with an external software.
- A BHoM_Engine project, that should contain the Engine methods specific to your Toolkit.
- A BHoM_oM project, that should contain any oM class (types) specific to your Toolkit.
In order to implement a new Toolkit, we prepared a Toolkit Template that does all the scaffolding for you: create an new Toolkit using the BHoM Toolkit Template.
Once you have created the Visual Studio solution using the template, you only need to implement the Adapter, and any Engine method and/or oM class that the Adapter should be using.
Let's get started!
This will install the Template in the system and it is required only once per machine.
Take SoftwareName_Toolkit.zip from the documentation
repo (documentation\templates\Toolkit template
) and copy it over to your Visual Studio ProjectTemplates
folder, generally in: C:\userName\Documents\Visual Studio 20xx\Templates\ProjectTemplates
Reboot Visual Studio.
-
Open Visual Studio. Do File --> New Project and search for "BHoM". Select the template "BHoM Toolkit Template".
-
If Visual Studio displays the checkbox "Place solution and project in the same directory" (VS version 2019 onwards), select it:
(For all other VS versions, just make sure that the checkbox "Create directory for solution" is ticked))
-
Specify your "SoftwareName" as the name of the solution. PascalCase, no spaces.
-
As parent root folder, specify the folder where you keep all other BHoM repos (generally, your GitHub folder
C:\Users\userName\GitHub\
). -
Confirm to create the new Toolkit solution.
This will result in a folder under your GitHub folder, such as ...\GitHub\SoftwareName
, with the template code inside, and the solution will open in Visual Studio.
Once the solution is created:
-
Close Visual Studio.
-
(Applies only if you couldn't do step 1. "Place solution and project in the same directory")
Go in the repo folder, which will be called "SoftwareName". It will contain only the solution file "SoftwareName.sln" and another folder "SoftwareName".
Enter in this "SoftwareName" folder, take all the folders and files and move them one folder up, so they sit together with the .sln file.
Delete the resulting empty folder "SoftwareName".
-
Rename the solution name and the folder name:
7.1. Add "_Toolkit" to the solution name
7.2. Add "_Toolkit" to the toolkit containing folder name.
-
Open the solution again.
If you could do step 1. "Place solution and project in the same directory", everything will be working and you are good to go.
Otherwise, if you couldn't do step 1, you will get "Project loading errors" due to the folder renaming of step 7. To correct:
In Solution Explorer, right click each project and do "Remove". Then right-click on the Solution name and do "Add Existing Project". Browse and select the
SoftwareName_Adapter
,SoftwareName_Engine
andSoftwareName_oM
projects in their new location.
The template creates the correct file names, class names and namespaces to be used for you.
To get started there are four files to have a first look at:
Main file for the adapter class. Here you will have to specify the constructor.
In this file you can also include any private local fields that you might need share between all the adapter files (given that your SoftwareName_Adapter is a partial
class).
The Create method scope should in general be limited to this:
- calling some conversion from BHoM to the object model of the specific software and a
- Use the external software API to export the objects.
If no API calls are necessary to convert the objects, the best practice is to do this conversion in a ToSoftwareName
file that extends the public static class Convert
. See the GSA_Toolkit for an example of this.
If API calls are required for the conversion, it's best to include the conversion process directly in the Create method. See Robot_Toolkit for an example of this.
In the template, you will find some methods to get you started for creating BH.oM.Structure.Element.Bar
objects.
The read method is responsible for reading the external model and returning all objects that respect some rule (or, simply, all of them).
The Read method scope should in general be specular to the Create:
- Use the external software API to import the objects.
- Call some conversion from the object model of the specific software to the BHoM object model.
Like for the Create, if no API calls are necessary to convert the objects, the best practice is to do this conversion in a FromSoftwareName
file that extends the public static class Convert
. See the GSA_Toolkit for an example of this.
Otherwise, if API calls are required for the conversion, it's best to include the conversion process directly in the Read method. See Robot_Toolkit for an example of this.
Contains a method for returning a free index that can be used in the creation process. Important method to implement to get pushing of dependant properties working correctly. Some more info given in the template. For example see: GSA Next Index and Add link to robot file.
A lot of functionality for Pushing as well as Pulling objects from a software has been implemented as helper methods in the BHoMAdapter base class. Some functionality implemented that can be utilised are:
- Merging objects deemed to be the same
- Merging incoming objects with objects already existing in the model
- Pushing of dependant objects
- Applying an software specific 'id' to the objects being pushed
To utilise these methods some additional methods will need to be implemented as well as some settings done. After all settings have been gone through one by one, some general code snippets that could be copied will be presented lower down this page.
An example of pushing of dependant objects would be pushing bars into an analysis package. To be able to create a bar one generally first need to create nodes as well as section properties. To get the BHoMAdapter to do this for you, one setting in the config needs to be set and one method needs to be implemented.
First the one need to set the config file (which is generally done in the constructor):
Config.SeparateProperties = true;
Then the following method needs to be implemented:
protected override List<Type> DependencyTypes<T>()
For the example case of the bar, where we want to first push nodes and section properties this could be implemented as (a slightly more general implementation for several types will be given lower down):
namespace BH.Adapter.GSA
{
public partial class GSAAdapter
{
/***************************************************/
/**** BHoM Adapter Interface ****/
/***************************************************/
protected override List<Type> DependencyTypes<T>()
{
Type type = typeof(T);
if(type = typeof(Bar)
return new List<Type>(){typeof(ISectionProperty), typeof(Node)};
}
}
}
This code should sit in the .cs file called DependencyTypes and be placed under the Types folder. For an example file that will work for most structural adapters the following file from the gsa toolkit can be copied over:
The BHoMAdapter is if set up to do so, making use of IEqualityComparers for merging of both incoming objects deemed the same as well as merging incoming objects with objects already in the model. Example of a couple of EqualityComparers have already been implemented:
To make use of these comparers and get the BHoMAdapter to merge for example nodes that are in the same position one need to make sure two settings in the Config are set:
Config.MergeWithComparer = true;
Config.ProcessInMemory = false;
On top of this You will need to override the method called Comparer<T>()
. For the example of merging nodes based on the position an implementation of this method could look like:
namespace BH.Adapter.GSA
{
public partial class GSAAdapter
{
/***************************************************/
/**** BHoM Adapter Interface ****/
/***************************************************/
protected override IEqualityComparer<T> Comparer<T>()
{
Type type = typeof(T);
if (type == typeof(Node))
return (IEqualityComparer<T>)new BH.Engine.Structure.NodeDistanceComparer(3); //Create a node distance comparer checking down to 3 decimal places
else
return EqualityComparer<T>.Default;
}
}
}
This should sit in the .cs file called Comparer.cs under the folder Types. For an implementation of this that would work as a starting point for most structural adapters please see this.
If merging has been implemented according to the above, the method called UpdateObejcts will be called for any objects deemed to already exist in the model. The standard implementation for this is to delete the existing ones and the create the new ones. If this does not work for the software you are implementing this method can be overridden.
- The name of the visual studio solution and also the repository should be 'SoftwareName'_Toolkit.
- For the general case this solution will contain one or two projects:
- 'SoftwareName'_Adapter - Main project that will contain all methods for communication with the software.
- 'SoftwareName'_Engine - Optional project to host helper methods not requiring any connection with the application. Could be thing as converters and queries to get out specific information required for the software.
The _Adapter project should have the following folder and file structure (Example from the GSA_Adapter):
All files in this project should be part of a partial class called 'SoftwareName'Adapter. The main file .cs in the project called the same thing should be inheriting from the
BHoMAdapter
. The namespace used should beBH.Adapter.'SoftwareName'
(Example from gsa adapter):namespace BH.Adapter.GSA { public partial class GSAAdapter : BHoMAdapter { //Basic code for the adapter constructor, local fields and properties in this file } }The engine project should follow the guidlines outlined here: the Engine
Namespace for this repo should be
BH.Engine.'SoftwareName'
-
Introduction to the BHoM:
What is the BHoM for?
Structure of the BHoM
Technical Philosophy of the BHoM -
Getting Started:
Installing the BHoM
Using the BHoM
Submitting an Issue
Getting started for developers -
Use GitHub & Visual Studio:
Using the SCRUM Board
Resolving an Issue
Avoiding Conflicts
Creating a new Repository
Using Visual Studio
Using Visual Studio Code -
Contribute:
The oM
The Engine
The Adapter
The Toolkit
The UI
The Tests -
Guidelines:
Unit convention
Geometry
BHoM_Engine Classes
The IImmutable Interface
Handling Exceptional Events
BHoM Structural Conventions
BHoM View Quality Conventions
Code Versioning
Wiki Style
Coding Style
Null Handling
Code Attributes
Creating Icons
Changelog
Releases and Versioning
Open Sourcing Procedure
Dataset guidelines -
Foundational Interfaces:
IElement Required Extension Methods -
Continuous Integration:
Introduction
Check-PR-Builds
Check-Core
Check-Installer -
Code Compliance:
Compliance -
Further Reading:
FAQ
Structural Adapters
Mongo_Toolkit
Socket_Toolkit