Automatically Run API Code When Your Model Changes (Does this toilet make my wall look thin?)

Over at Revit SWAT there is a great post about how to modify a wall-hosted family so it gives an alert when the host wall is too thin. This topic is a great excuse for me to write about Dynamic Model Update, the Revit API feature that automatically runs API code when your Revit model changes.

The idea in this example is to alert the user when a toilet is placed on a wall that is too thin to hold its plumbing. To do this, we need to have API code that runs automatically when the model changes.

But before coding such a real-time alert, here is a quick sample showing how to check all plumbing fixtures that are already in the model.

public void checkPlumbingWallWidth()
{
    Document doc = this.ActiveUIDocument.Document;
    string tooThin = "";
    SelElementSet selSet = SelElementSet.Create();
    foreach (FamilyInstance inst in (from i in new FilteredElementCollector(doc)
             .OfClass(typeof(FamilyInstance)) // find only FamilyInstances
             .OfCategory(BuiltInCategory.OST_PlumbingFixtures) // find only Plumbing Fixtures
             .Cast<FamilyInstance>() // cast to FamilyInstance so FamilyInstance.Host can be used
             where ((Wall)i.Host).Width < 0.5 // FamilyInstance.Host returns an Element, so cast it to a Wall so Wall.Width can be used
             select i))
    {
        tooThin += inst.Symbol.Family.Name + ", id = " + inst.Id + "\n";
        selSet.Add(inst); // add the instance to the selection set
    }
    UIDocument uidoc = new UIDocument(doc);
    uidoc.Selection.Elements = selSet; 
    uidoc.RefreshActiveView();
    TaskDialog.Show("Plumbing in Walls < 6\"",tooThin);
}

fixtures

For more info on the SelElementSet functionality to select the elements that are found, see https://boostyourbim.wordpress.com/2012/12/15/retrieving-and-selecting-elements-in-a-selectionfilterelement/


Now for the real-time alert!

With the code below, placing the toilet on the vertical (thicker) wall gives no warning. But placing one on the thin horizontal wall gives the warning shown.

wallwarning

Below is the code for 3 macros:

  • FamilyInstanceUpdater – the code that runs when the trigger occurs
  • RegisterUpdater to turn on the updater
  • UnregisterUpdater to turn off the updater
public class FamilyInstanceUpdater : IUpdater
{
    static AddInId m_appId;
    static UpdaterId m_updaterId;
    // constructor takes the AddInId for the add-in associated with this updater
    public FamilyInstanceUpdater(AddInId id)
    {
        m_appId = id;
        // every Updater must have a unique ID
        m_updaterId = new UpdaterId(m_appId, new Guid("FBFBF6B2-4C06-42d4-97C1-D1B4EB593EFF"));
    }
    public void Execute(UpdaterData data)
    {
        Document doc = data.GetDocument();

        // loop through the list of added elements
        foreach (ElementId addedElemId in data.GetAddedElementIds())
        {
            // check if the added element is a family instance of the Plumbing Fixtures category
            FamilyInstance instance = doc.GetElement(addedElemId) as FamilyInstance;
            if (instance != null && instance.Category.Name == "Plumbing Fixtures")
            {
                // Get the instance's host wall
                Wall wall = instance.Host as Wall;
                // Check that there is a host wall and that its width is greater than 6"
                // Revit API uses feet for distance measurements
                if (wall != null && wall.Width < 0.5)
                    TaskDialog.Show("Warning!", "Your wall is too thin!");
            }
        }
    }
    public string GetAdditionalInformation(){return "Family Instance host wall thickness check";}
    public ChangePriority GetChangePriority(){return ChangePriority.FloorsRoofsStructuralWalls;}
    public UpdaterId GetUpdaterId(){return m_updaterId;}
    public string GetUpdaterName(){return "Wall Thickness Check";}
}

// This command must be run to register the updater with Revit
// Often this is done as part of the Revit start-up process so that the updater is always active
public void RegisterUpdater()
{
    FamilyInstanceUpdater updater = new FamilyInstanceUpdater(this.Application.ActiveAddInId);
    UpdaterRegistry.RegisterUpdater(updater);

    // Trigger will occur only for FamilyInstance elements
    ElementClassFilter familyInstanceFilter = new ElementClassFilter(typeof(FamilyInstance));

    // GetChangeTypeElementAddition specifies that the triggger will occur when elements are added
    // Other options are GetChangeTypeAny, GetChangeTypeElementDeletion, GetChangeTypeGeometry, GetChangeTypeParameter
    UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), familyInstanceFilter, Element.GetChangeTypeElementAddition());
}

public void UnregisterUpdater()
{
    FamilyInstanceUpdater updater = new FamilyInstanceUpdater(this.Application.ActiveAddInId);
    UpdaterRegistry.UnregisterUpdater(updater.GetUpdaterId());
}

I’ve tried to put a decent amount of description in the comments, for more info check out http://wikihelp.autodesk.com/Revit/enu/2013/Help/00006-API_Developer’s_Guide/0135-Advanced135/0152-Dynamic_152

Enhancement Idea:

  • Create a trigger that uses GetChangeTypeGeometry instead of GetChangeTypeElementAddition with a filter for Walls. If an existing wall that is hosting toilets becomes thinner (because its structure is changed or the wall type is changed) then the warning should be given.
Advertisements

7 thoughts on “Automatically Run API Code When Your Model Changes (Does this toilet make my wall look thin?)

  1. wow! now that is awesome! the additional comments are super helpful! Just out of curiousity, what does OST stand for (BuiltInCategory.OST_PlumbingFixtures)? Object Style Tree?

    • Harry – of course, you already know this, but using a real Revit posted warning would be better than the popup message – it would interact with other actions taken by the user or by other API code more cleanly. It could be made an error if this change is something that just shouldn’t be allowed to stand.

      David – OST = Object Style types, it is a prefix used in the native code. You are seeing the C++ guts of Revit leaking through in this enumerated type 🙂

  2. This is interesting…and something I’ve been thinking about. I’m curious what impact it has on performance since it is triggered anytime anything changes in the model. Have you tested it’s impact? Is is negligible?

    • This code is only triggered when a family instance is added to the model. The code that executed in this situation is very light-weight, so the performance impact of this example is negligible.

      ElementClassFilter familyInstanceFilter = new ElementClassFilter(typeof(FamilyInstance));
      UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), familyInstanceFilter, Element.GetChangeTypeElementAddition());

  3. I would love to use this technique to create and update a log whenever something is deleted. The log would specify who deleted it and when and what the elements were. What would even be great on top of that, is the ability to restore an object from the log!

    • To restore an object from the log would require a lot of geometric and parametric data to be logged. But to log who, when, and what was deleted would be relatively straightforward.

      On Mon, Nov 18, 2013 at 12:14 PM, Boost Your BIM – making Revit even better

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s