Skip to content

Conversation

@adecler
Copy link
Member

@adecler adecler commented Oct 19, 2025

NOTE: Used by

BHoM/Excel_UI#423

NOTE: Depends on

BHoM/BHoM#1698
BHoM/BHoM_Engine#3561
BHoM/BHoMAnalytics_Toolkit#95
https://github.com/BuroHappoldEngineering/BuroHappold_BHoMAnalytics_Toolkit/pull/82
https://github.com/BuroHappoldEngineering/BuroHappold_Installer/pull/462
BHoM/Versioning_Toolkit#317

Issues addressed by this PR

Closes #521

This PR eliminates the need to load all the assemblies when starting the BHoM UI. Instead, here's how it now works:

  • Menus are populated using a pre-compiled file containing all the items found in the menus.
  • Those menu items are first created with just the json representation of the method/object it links to.
  • When a menu item is selected for the first time, the json will be deserialised to link the method/object to it.
  • The serialiser itself in in charge of dynamically loading the dlls needed to deserialise an object. Those that haven't been loaded already at least.

Solving the problem at the level of the serialiser means we are taking care of the following problems at the same time:

  • UI component creation
  • UI component copy-pasting
  • File saving
  • Toolkit setting
  • Deserialisation of BHoM datasets

The file that contains all the information needed to populate the menus is located here: C:\ProgramData\BHoM\Resources\AssemblyContent.tsv

Test files

In both Excel, Rhino 7, and Rhino 8:

  • Try to create each type of component from the global menu. Make sure each component links to a method/object from a different. assembly.
  • Try to create each type of component from the components found in the ribbon. Make sure each component links to a method/object from a different. assembly.
  • Save the file, close BHoM_UI and re-open the file to make sure everything was saved correctly
  • Deserialise BHoM data from existing datasets. Make sure to test versioning by using older datasets
  • Check that the analytics is still working

@adecler
Copy link
Member Author

adecler commented Oct 22, 2025

  • The UI is now loading any dll than is newer or not found in the AssemblyContent.tsv file. And then update the file itself. The system will work even if the file doesn't exist when loading the UI and will create it after loading all the dlls.
  • I was also not a big fan of the engine having to access that file so I added a dependency injection for an IAssemblyResolver that the UI can set on the engine. That resolver is now the only place where the tsv file is accessed and used. I hope that makes sense.
  • I have tested switching only the BHoM_UI repo to the develop branch and both UIs are still working fine for me. So it seems we should indeed be able to merge everything excel BHoM_UI if needed. Let me know if that is the same for you.
  • Finally, the slowest part of the UI loading is now in collecting the menu items for the CreateData component. No idea why it was done this way but asking for the list of datasets, also load the content of all the datasets files in memory. I'll have a look how to change that.

@adecler
Copy link
Member Author

adecler commented Oct 23, 2025

@BHoMBot check installer

@bhombot-ci
Copy link

bhombot-ci bot commented Oct 23, 2025

@adecler to confirm, the following actions are now queued:

  • check installer

@adecler
Copy link
Member Author

adecler commented Oct 26, 2025

@BHoMBot check versioning

@bhombot-ci
Copy link

bhombot-ci bot commented Oct 26, 2025

@adecler to confirm, the following actions are now queued:

  • check versioning

@adecler
Copy link
Member Author

adecler commented Oct 26, 2025

@BHoMBot check copyright-compliance
@BHoMBot check project-compliance
@BHoMBot check core
@BHoMBot check installer

@bhombot-ci
Copy link

bhombot-ci bot commented Oct 26, 2025

@adecler to confirm, the following actions are now queued:

  • check copyright-compliance
  • check project-compliance
  • check core
  • check installer

@bhombot-ci
Copy link

bhombot-ci bot commented Oct 26, 2025

@adecler just to let you know, I have provided a check-versioning result to this Pull Request as it was detected to be linked to other Pull Requests in a series. The comment which triggered this check came from @adecler on BHoM_Engine

Copy link
Member

@pawelbaran pawelbaran left a comment

Choose a reason for hiding this comment

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

I read through the code changes, happy with the overall setup, just a few comments concerning particular decisions

public UnitTestCaller() : base()
{
SetPossibleItems(Engine.UI.Query.EngineItems());
IEnumerable<SearchItem> items = Initialisation.SearchItems.Where(x => x.Text.Contains('('));
Copy link
Member

Choose a reason for hiding this comment

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

I do not know the exact details of the UI, but should we not have a UnitTestCaller type filter here rather than checking whether text contains a parenthesis?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, all methods are candidate for unit testing as far as I understand. I could filter by looking at all callers that use a method but this is much simple and efficient.

/**** Private Fields ****/
/*************************************/

Dictionary<string, List<string>> m_AssemblyNamePerType = new Dictionary<string, List<string>>();
Copy link
Member

Choose a reason for hiding this comment

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

I would consider changing the type here to an array for performance purposes, assuming these are closed sets that do not change dynamically (and knowing there might be thousands of them)

Suggested change
Dictionary<string, List<string>> m_AssemblyNamePerType = new Dictionary<string, List<string>>();
Dictionary<string, string[]> m_AssemblyNamePerType = new Dictionary<string, string[]>();

Copy link
Member Author

Choose a reason for hiding this comment

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

We've never used arrays in the BHoM, always lists

using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
Copy link
Member

Choose a reason for hiding this comment

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

😉
image

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, that must have been added when I was exploring different options. I've removed the unnecessary using in this commit: 15aac9c


public static List<CodeElementRecord> CodeElements { get; set; } = new List<CodeElementRecord>();

public static AssemblyResolver AssemblyResolver { get; set; } = new AssemblyResolver();
Copy link
Member

Choose a reason for hiding this comment

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

What's the purpose of this property? It feels a bit weird that we have a duplicate property in Engine and UI, which (theoretically) could mismatch. I would rather remove it and default all calls to the engine one, assuming it does not serve any other purpose.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is the same object. The UI is just passing it to the Engine so it can be access from there.


// Create the assembly resolver and link it the the BHoM engine
AssemblyResolver = new AssemblyResolver(assemblyNamesPerType);
BH.Engine.Base.Compute.SetAssemblyResolver(AssemblyResolver);
Copy link
Member

Choose a reason for hiding this comment

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

I am wondering if it would not be worth making this call a bit more explicit, now it is a bit hidden in a method of lower order (LoadCodeElements being called as one of the 4 methods in a sequence). It is a bit like 'now we populate the UI with items to select, and by the way we are changing the way in which assemblies are being loaded - and only if you read the code carefully.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. I have create a comit with that change: 0379593

{
List<string> loadedAssemblies = new List<string>();

Regex regex = new Regex(@"oM$|_Engine$|_Adapter$");
Copy link
Member

Choose a reason for hiding this comment

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

Not a big fan of this one, it introduces misalignment with BHoM_Engine

Copy link
Member Author

Choose a reason for hiding this comment

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

Not sure how it is misaligned with the BHoM_Engine. The is the exact same filter as used by BH.Engine.Base.Compute.LoadAllAssemblies(): https://github.com/BHoM/BHoM_Engine/blob/develop/BHoM_Engine/Compute/LoadAllAssemblies.cs

foreach (string file in Directory.GetFiles(BH.Engine.Base.Query.BHoMFolder(), "*.dll", SearchOption.TopDirectoryOnly))
{
string name = Path.GetFileNameWithoutExtension(file);
if (regex.IsMatch(name))
Copy link
Member

Choose a reason for hiding this comment

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

Following my previous comment, I would suggest using the existing methods to centralise the core classification of assemblies

Suggested change
if (regex.IsMatch(name))
if (name.IsOmAssembly() || name.IsEngineAssembly() || name.IsAdapterAssembly())

Copy link
Member Author

Choose a reason for hiding this comment

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

I could be convinced of that. As mentioned above, my goal at the moment though is to stay aligned with the previous code from LoadAllAssemblies()

{
return Engine.Base.Query.BHoMTypeList()
.Concat(Engine.Base.Query.BHoMInterfaceTypeList())
return Engine.Base.Query.AllTypeList()
Copy link
Member

Choose a reason for hiding this comment

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

Why did you make this change here? Trying to follow but can't come up with a reason off the top of my head

Copy link
Member Author

Choose a reason for hiding this comment

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

Limiting the types registered exclusively to the BHoM objects causes multiple failure on serialisation.
We have separate items for specifically creation BHoM objects via their set of properties or Create methods. So the Types registered here are purely for deserialisation and creating Type objects in the UI.

@adecler adecler requested a review from pawelbaran October 28, 2025 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:feature New capability or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Speed up the loading time of the BHoM UI

3 participants