Auto-Run an Add-In for Design Automation

Still at the Forge Accelerator in Rome and looking further into the Forge Design Automation API for Revit.

As mentioned yesterday, it is not yet available or documented, except to a closely restricted private beta. For more information on its current status, please refer to Mikako Harada's discussion of Design Automation for Revit.

However, you can stiil start preparing your add-in for the day when it comes:

Aspects to Consider

Here are some aspects to consider:

Yesterday, as a first example step, we modified the StairsAutomations sample to avoid displaying any warnings, so the second item listed above is handled.

Today, we'll address most of the remaining ones:

We'll move the execution away from an external command and trigger it from the ApplicationInitialized event instead.

In fact, we'll entirely remove all references to RevitAPIUI.dll.

We'll also open the model file ourselves.

In Forge, a different system will be used, so you cannot later use the ApplicationInitialized event there. Design Automation for Revit continues doing setup past the point at which ApplicationInitialized is raised. For the time being, though, we can use it to just mimic the 'run automatically' behaviour.

Implementing DB Application and Accessing the Revit Application Object

The trickiest step for me was finding out how to access the Revit Application object using only the IExternalDBApplication interface, because that is apparently not documented anywhere at all.

I finally found the solution in a previous blog post on automatically opening a project on start-up – the sender argument passed in to the ApplicationInitialized can be cast to Application.

That enables me to implement the entirely UI-independent DB application to drive the stairs creation utility class like this:

using System;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.ApplicationServices;

namespace Revit.SDK.Samples.StairsAutomation.CS
{
  /// <summary>
  /// Implement the Revit add-in IExternalDBApplication interface
  /// </summary>
  public class DbApp : IExternalDBApplication
  {
    string _model_path = "C:/a/vs/StairsAutomation/CS/Stairs_automation_2019_1.rvt";

    /// <summary>
    /// The implementation of the automatic stairs creation.
    /// </summary>
    public void Execute( Document document )
    {
      // Create an automation utility with a hardcoded 
      // stairs configuration number

      StairsAutomationUtility utility
        = StairsAutomationUtility.Create(
          document, stairsConfigs[stairsIndex] );

      // Generate the stairs

      utility.GenerateStairs();

      ++stairsIndex;
      if( stairsIndex > 4 )
        stairsIndex = 0;
    }

    void OnApplicationInitialized(
      object sender,
      ApplicationInitializedEventArgs e )
    {
      // Sender is an Application instance:

      Application app = sender as Application;

      Document doc = app.OpenDocumentFile( _model_path );

      if( doc == null )
      {
        throw new InvalidOperationException(
          "Could not open document." );
      }
      Execute( doc );
    }

    public ExternalDBApplicationResult OnStartup(
      ControlledApplication a )
    {
      // ApplicationInitialized cannot be used in Forge!
      a.ApplicationInitialized += OnApplicationInitialized;
      return ExternalDBApplicationResult.Succeeded;
    }

    public ExternalDBApplicationResult OnShutdown(
      ControlledApplication a )
    {
      return ExternalDBApplicationResult.Succeeded;
    }

    private static int stairsIndex = 0;
    private static int[] stairsConfigs = { 0, 3, 4, 1, 2 };
  }
}

Note the absence of all references to the Autodesk.Revit.UI namespace.

DB Application Add-In Manifest

Now that we implement no external command, Revit complains that no external command is found:

External command not found

That makes perfect sense, of course.

We need to adapt the add-in manifest and inform Revit that we are loading a DB application instead.

As an external application, it requires a Name node:

External application requires a Name node

We end up with the following add-in manifest file:

<?xml version="1.0" encoding="utf-8"?>
<RevitAddIns>
  <AddIn Type="DBApplication">
    <Assembly>StairsAutomation.dll</Assembly>
    <ClientId>4ce08562-a2e1-4cbf-816d-4923e1363a21</ClientId>
    <FullClassName>Revit.SDK.Samples.StairsAutomation.CS.DbApp</FullClassName>
    <Name>StairsAutomation</Name>
    <Description>A utility sample that creates a series of stairs, stairs runs and stairs landings configurations 
      based upon predefined rules and parameters.</Description>
    <VendorId>ADSK</VendorId>
  </AddIn>
</RevitAddIns>

Next Steps

There is not much more left to do now, really.

These are all that come to mind off-hand:

The first three we can address right away...

Download

Oops, I almost forgot: You can download the modified SDK sample and examine every step I took in modifying it so far from and in the StairsAutomation GitHub repository.