Set Floor Level and Use IPC for Disentanglement

Today, yet another introduction to Forge, a simple issue of setting the level of a floor element, and the much more complex one of gaining freedom and total independence via IPC:

Entanglement

What is Forge?

Would you like to quickly understand what Forge is all about?

Well, on one hand, you can check out the high-level picture of Forge shared Scott Sheppard two weeks ago.

Now he added to that in a new article on what Forge is, in which he summarises a presentation and shares the slide deck of three deeply knowledgeable colleagues:

Here is a quick summary of his summary:

For the details, read the full story in It's Alive in the Lab.

What is Forge?

Changing the Level of a Floor

Returning to the Revit API, one nice little discussion in the Revit API forum addresses changing the Level.Id and offset height of floors:

Question: I have a Revit building project with 5-6 floors. Some of the floors don't have a right level associated with them. For example, the floor element of the 4th floor is linked to the 3rd level with a big offset height. I want to have a well-organized Revit file to export a well-organized IFC file from it. Therefore, I want to change the associated level (level.ID) and height offset of some of the floor elements. As level.Id is not a property providing get and set accessors, I have no idea how to do it. Does anyone here have experience?

Answer by Fair59:

You can set the built-in parameters LEVEL_PARAM and FLOOR_HEIGHTABOVELEVEL_PARAM like this:

  Parameter p = floor.get_Parameter(
    BuiltInParameter.LEVEL_PARAM );

  Parameter p1 = floor.get_Parameter(
    BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM );

  usingTransaction tx = new Transaction( doc ) )
  {
    tx.Start( "Set Floor Level" );
    p.Set( level.Id ); // set new level Id
    p1.Set( 2 ); // set new offset from level
    tx.Commit();
  }

Jeremy added a framework around this code snippet to pick the first (or only) floor in a sample model and the first level that differs from its current level to test it.

Here is a really minimal model before running the test external command:

Before running add-in

Model after running add-in:

After running add-in

Saved for posterity in the method SetFloorLevelAndOffset in the module CmdEditFloor.cs of The Building Coder samples release 2019.0.145.17, cf. the diff from the previous release.

Entanglement can be Hell

Now for the main topic, an important decoupling technique that you should be aware of and have ready for use in your toolkit in case of need.

Proem:

Sometimes, things get messed up due to entanglement.

One dreadful and common example that we've presumably all run into at one time or another is DLL hell.

Disentanglement helps.

Making components as independent of each other as possible.

Way back then, that was actually one of the main motivations for object-oriented programming.

Every Revit add-in is pretty heavily entangled with Revit itself, since it is loaded into the same process, and, worse still, the one and only .NET AppDomain it provides.

This can cause problems for people trying to make use of certain other third-party components, especially if their add-in and Revit disagree on the exact version and other component details.

CefSharp Entanglement

This exact problem occurred with Revit 2019.1 and the CefSharp library.

In that discussion, Autodesk points out that:

Revit 2019.1 and other Autodesk add-ins use the CEFsharp library for several features. Some 3rd party add-ins also use this library. Unfortunately, simultaneous use of different versions of the library leads to instability issues. In order to avoid version conflicts, we make it clear that Revit uses CEFsharp version 57.0.0. In addition, Revit 2019.1 forcibly loads a version of CEFsharp prior to add-in initialization. This means that add-ins that load a different version of the CEFsharp library may not function. We recommend realigning add-ins to use the version provided by and loaded by Revit.

We discussed this issue of Revit add-ins using the CEFsharp library here last year.

Disentanglement and Independence via IPC

Later, Kim Sivonen, partner at ProdLib Oy, very kindly shared a much more radical and effective solution to the problem, showing how to completely disentangle a Revit add-in or some part of the functionality it relies on from the main Revit process using IPC, inter-process communication.

In his own words:

We chose to study the possibility of running CEF in another process. After some headache, it actually turned out to be a decent alternative.

I attached a sample solution source code if someone is interested:

The solution consists of two projects; one Revit plugin and one Forms application. All CEF-specific is isolated in the Forms application, which is launched from the Revit plugin as an own process. The code was written very quickly just to prove that the functionality can be done, so please try to tolerate the sloppy code. There's probably many ways how the functionality can fail in different situations, but I believe everything can be handled.

Some comments:

Please note that this software is BEER-WARE. Description of the license below.

Kim Sivonen, ProdLib Oy

Beer-Ware License

/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
*  wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*/

Many thanks to Kim for researching, implementing and sharing this powerful solution!

It may come in handy in many other entangled situations as well.

For instance, try IPC if you ever run into any kind of relationship problem.