Automating Entire Copy/Monitor Process in Revit API

Automating Entire Copy/Monitor Process in Revit API

than.naing.oo
Participant Participant
1,041 Views
5 Replies
Message 1 of 6

Automating Entire Copy/Monitor Process in Revit API

than.naing.oo
Participant
Participant

Hello,

I’m trying to fully automate the Copy/Monitor process in Revit using the API. The built-in 

PostableCommand.CopyMonitorSelectLink

only initiates the workflow but doesn’t allow me to programmatically:

  1. Select elements (e.g., grids, levels) for copying and monitoring.
  2. Copy elements from the linked model to the host project.
  3. Finalize the operation without user interaction.

Question: Is there a way to:

  • Automate the entire Copy/Monitor workflow, including element selection, copying, and monitoring?
  • Bypass the built-in UI-based process and implement a custom solution using the Revit API?

If anyone has experience with this or can suggest alternative approaches, I’d appreciate your guidance or sample code.

Thank you!

0 Likes
Accepted solutions (1)
1,042 Views
5 Replies
Replies (5)
Message 2 of 6

jeremy_tammik
Alumni
Alumni
Accepted solution

Thank you for your pertinent question. I asked an AI for you, simply by prefixing your original text with a persona prompt. The answer looks pretty useful to me. Please review it and let us know (a) whether it is accurate and (b) how you end up solving your problem. I hope that you do! 

   

Cheers, 

   

Jeremy

   

Jeremy asks: you are a wise and helpful BIM expert, experienced Revit application ewngineer and proficient .net, Revit API add-in and BIM programmer. Can you please help with this Revit API question, maybe discussed in the Revit API discussion forum, by The Building Coder blog, or other Revit API exchanges: <your original question text>

   

Gemini 2.0 Flash Experimental answersYou're hitting a common roadblock with the Revit API and Copy/Monitor. The PostableCommand.CopyMonitorSelectLink command is indeed just a starting point for the interactive UI. Directly automating the entire process, including element selection and finalization, through the public Revit API is not possible.

  

Here's a breakdown of why and what alternatives you can consider, drawing on common Revit API knowledge and best practices:

Why Direct Automation Isn't Possible:

  • UI-Driven Core: The Copy/Monitor functionality is deeply intertwined with Revit's UI. The API exposes the entry point but doesn't provide fine-grained control over the internal workings of the Copy/Monitor engine. Selecting elements, establishing the monitoring relationships, and finalizing the process are all handled within this UI context.
  • No Public API for Element Mapping: There's no publicly exposed API to directly map elements between the linked model and the host model for Copy/Monitor purposes. This mapping is crucial for establishing the monitoring relationship.
  • Transaction Management: Copy/Monitor involves complex transaction management within Revit. Directly manipulating this without going through the intended UI flow could lead to instability or data corruption.

Alternative Approaches and Workarounds:

  1. Element Copying and Comparison:

    • Copy Elements Directly: You can use the Element.CopyElements() method to copy elements from the linked model into the host model. This bypasses Copy/Monitor entirely.
    • Compare Element Parameters: After copying, you can compare relevant parameters (e.g., location, geometry, identity data) between the original elements in the linked model and the copied elements in the host model. This allows you to create a custom "monitoring" system.
    • Store Relationships: You'll need to store the relationship between the original and copied elements in a custom way (e.g., using shared parameters, extensible storage, or an external database). This allows you to track changes.
    • Pros: More control, avoids UI limitations.
    • Cons: Requires significant custom coding, doesn't provide the built-in Copy/Monitor change notification system.
  2. Workarounds (Limited):

    • UI Automation (Not Recommended): You could theoretically use UI automation tools (like AutoIt or UI Automation framework) to simulate user interaction with the Copy/Monitor dialogs. However, this is extremely fragile, dependent on Revit's UI remaining consistent, and generally not a robust solution. It is also not supported by Autodesk.
    • Journal Files (For Research Only): You can examine journal files generated during manual Copy/Monitor operations to understand the underlying commands and data exchange. However, these journals are not intended for programmatic use and their format can change between Revit versions.

Example (Conceptual - Element Copying and Comparison):

C#
 
// Get linked document
Document linkedDoc = ...;
// Get host document
Document hostDoc = ...;

// Get elements to copy from linked document (e.g., grids)
FilteredElementCollector collector = new FilteredElementCollector(linkedDoc);
collector.OfCategory(BuiltInCategory.OST_Grids);

List<ElementId> idsToCopy = collector.ToElementIds().ToList();

// Copy elements
using (Transaction trans = new Transaction(hostDoc, "Copy Grids"))
{
    trans.Start();
    ICollection<ElementId> copiedIds = ElementTransformUtils.CopyElements(linkedDoc, idsToCopy, hostDoc, Transform.Identity, new CopyPasteOptions());
    trans.Commit();

    // Now compare parameters between original and copied elements
    foreach (ElementId originalId in idsToCopy)
    {
        Element originalElement = linkedDoc.GetElement(originalId);
        // Find corresponding copied element (you'll need a robust way to do this, e.g., by comparing unique IDs or geometry)
        // ...
        Element copiedElement = hostDoc.GetElement(copiedId);

        // Compare parameters (e.g., Location)
        LocationCurve originalLocation = originalElement.Location as LocationCurve;
        LocationCurve copiedLocation = copiedElement.Location as LocationCurve;

        // ... Compare the curves ...
    }
}

Key Takeaways:

  • Full automation of the Copy/Monitor UI workflow through the public Revit API is currently impossible.
  • Direct element copying and custom comparison is the most viable approach for programmatic "monitoring."
  • Careful consideration of element identification and relationship storage is crucial for a robust custom solution.

This approach is more complex than a simple API call, but it's the only reliable way to achieve a similar outcome programmatically. Remember to handle transactions and error conditions appropriately in your code.

    

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 3 of 6

richard.laroche
Contributor
Contributor

Funny how I get here just a few days after this subject was raised. Conincidences.

Long story is that I have en engineering team that made several large MEP models for a large building(one model for heating, one model for electrical distribution, and so on).
Then, the construction manager for legal/managing reasons, divided the construction of the building into 3 phases, hiring several different contractors for different tender/proposals.
Long story short, I started with a design model for a large building (with complete MEP systems) and now I have 3-4 separated models representing the same building, each having incomplete systems.

So I want to benefit from the contractor's models, having a more up-to-date and precise information. I want to merge all these models together to recreate something similar to the original designer's model.

First. Element.CopyElements() seems to be a quick and easy way. But I notices that it creates Family and FamilySymbol duplicates, even though the different contractor models use the very same families and types from the designer's model. This would make my central model very heavy and clunky.

Then, I tried to copy families and duplicate FamilySymbols and copy all parameters of all contractor's models into a single model. I simply manage Family names and FamilySymbol names to avoid duplicates. Then, from this new reference model, I use this new bank of Families and FamilySymbols to re-create all FamilyInstances using NewFamilyInstance.

Oh. My. God.

Welcome to the hell of finding the right overload for the right family. Not to mention weird behavior like an electrical outlet with FamilyPlacementType.OneLevelBased that, no matter what Level I push into it's overload arguments, always end up with "Elevation from Level" calculated from z=0.

Now I was looking into the possibility of using PostableCommand.CopyMonitorSelectLink. Here's what Copilot gave me to go further:

You can use the Revit journal files to find the command IDs for buttons you want to programmatically press.

Here's how you can do it:

  1. Enable Revit Journal Files:

    • Revit automatically creates journal files that log user actions. These files are typically located in the C:\Users\<YourUsername>\AppData\Local\Autodesk\Revit\Autodesk Revit 2023\Journals directory.
  2. Perform the Action Manually:

    • Open Revit and manually press the button you are interested in. This action will be recorded in the journal file.
  3. Inspect the Journal File:

    • Open the latest journal file and look for entries related to the button press. The entries will include the command name and ID.
  4. Use the Command ID in Your Script:

    • Once you have the command ID from the journal file, you can use it in your script with the RevitCommandId.LookupCommandId method.



Am I on some plausible good track?
Thank you gentlemen!

0 Likes
Message 4 of 6

jeremy_tammik
Alumni
Alumni

Sounds good for starters.

   

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 5 of 6

richard.laroche
Contributor
Contributor

Thank you Jeremy, as always, for the programming gold mine at The Building Coder.

Just to understand the context. The designer made a large model and transferred this model to 3 different contractors who each had to build part of the building. In return, the contractors deleted all familyinstances not related to their respectual contracts, slightly moved and changed parameters of familyinstances that had changes during the construction and finally delivered 3 "as built" models that were essentially based on the designer's model.

Now, repeat this situation 20+ times (I have a large multi-billion$ project), and you get 80+ contractor models to re-stitch up into 20ish designer models. You see why I'm trying to automate the process and avoid doing this manually with copy/monitor button.


Unfortunately, after tinkering with PostableCommand.CopyMonitorSelectLink, I see that the process automatically creates Family and FamilySymbol duplicates.  Even though the contractor models use the very same Families and FamilySymbols as the designer's model, it seems like Revit consider all these Families and FamilySymbols in these models to be "different". So, when I try Copy/Monitor-Select Link-Copy, is still creates a bunch of "duplicate" Families and FamilySymbols, adding "1" or "2" at the end of duplicate "Family.Name" and "FamilySymbol.get_Parameter(BuiltInParameter.SYMBOL_NAME_PARAM).AsString()".

 

richardlaroche_0-1736783060456.png

 



So in conclusion, my recommendation for anyone who wish to "merge" models in a similar context, would be to simply use the ElementTransformUtils.CopyElements(), which will spare you the complexity of managing NewFamilyInstance creation and various overloads, but will unfortunately create duplicate Family And FamilySymbols.

Next step is to clean up the stitched model by replacing every FamilyInstance that has a Family or FamilySymbol ending with "1" or "2" by the Family/FamilySymbol of same string without "1" or "2", I guess by using ChangeTypeId method on the Element.

At least, that's whete I'm a now and I'll come back here to explain how it goes!

Message 6 of 6

richard.laroche
Contributor
Contributor

I DID IT.
It was not easy, an epic adventure.

 

Ok first, I'm coding CPython3 scripts in the Dynamo environment. So just a hint here, if you want to include a FailuresAccessor interface to manage warnings and error in such an operation, you'll need to manage the programming namespace by including something like this (I don't know why):
import uuid
class MyFailuresPreprocessor(IFailuresPreprocessor):
    __namespace__ = str(uuid.uuid4())

 

As explained in my prevoius post, you'll have to gather all the elements from the source_document and use ElementTransformUtils.CopyElements() to your target document. (this part is on you).

This will create a bunch of duplicate families and familysymbols.
duplicate_families always end with " 1" or " 2" and so on, a space and an increment added to the end of Family.Name. 
duplicate_familytypes always end with "1" or "2" and so on, just an increment without space, added to the end of FamilySymbol .get_Parameter(BuiltInParameter.SYMBOL_NAME_PARAM).AsString()

Now, if you want to do a clean job, you'll have to find, for every duplicate_family and duplicate_familysymbol, the corresponding original family and familysymbol, those that don't have the " 1" and "1" at the end. To find them all, I used a loop with something like if duplicate_familysymbol_name.replace(original_familysymbol_name,"")==" 1".

So after you build a correspondence table between the duplicate_families - original_families and duplicate_familysymbol - original_familysymbol, you may find that your original_families don't contain all the corresponding familysymbols.
Example:
Original family and familysymbols = 
Family: D50_Clock,  FamilySymbol: wall

Duplicate family and familysymbols = 
Family: D50_Clock 1,  FamilySymbol: wall
Family: D50_Clock 1,  FamilySymbol: ceiling

So if I want a clean model, I want to remove the "D50_Clock 1" family and only keep "D50_Clock". To do so, I'll have to apply FamilySymbol.Duplicate() on  Family: D50_Clock,  FamilySymbol: wall to create the new (Family: D50_Clock,  FamilySymbol: ceiling). That loop gymnastics is could be complicated, depending on how many duplicates you have ("1","2","3"...) and if you have original families and familisymbols that have names that already end by numbers (you'll have to find another way to manage them).

Once your original family contains all necessary familysymbols, you will have to make another loop to reassign the correct familysymbol to every element in the duplicate_families:
UnwrapElement(element_to_reassign).ChangeTypeId(ElementId(typeid))

For this last part, weird bug, you'll have to make a separate script to be executed in IronPython2. 
(If you try this in CPython3, you'll get a bug as reported here https://forums.autodesk.com/t5/revit-api-forum/changetypeid-not-working-for-stairspaths-in-revit-202...)

Last step: PURGE (somehow satisfying for W40k fans.)

Next mission: auto-reconstruct MEP systems.

0 Likes