Accessing Assembly Components

A common need when working with an assembly is accessing the components that make up the assembly.  This reason to do this can be for a lot of reasons from creating your own custom BOM to editing parameter values within certain parts.  Here’s a quick overview of how to access its components.  (For this discussion I’m going to focus only on the parts and subassemblies within an assembly and not anything else that can be in an assembly; constraints, assembly features, level of detail, etc.)

What is an Assembly?
For the topic at hand, an assembly can be thought of as a container that contains instances of parts and other assembles.  Each instance (or occurrence) defines a position within the assembly and other properties that define the appearance and behavior (color, visibility, adaptive, etc.) of the instance.  An instance doesn’t contain any geometry but displays the geometry that it gets from the referenced part.  A part can be referenced once and then instanced many times by displaying that single part in different locations in the assembly.  Below is the example assembly I’ll use to illustrate.

AssemblyStructureAssembly

Below is the browser for this assembly.  You can see that the assembly consists of 14 occurrences where two of these are subassemblies and 12 of them are parts.  There is one unique subassembly and 5 unique parts, which is illustrated in the view of Windows Explorer shown below.  The geometry for the parts only exists within the five .ipt files.  The assembly references these parts and displays their graphics at positions defined by the location of the occurrence that represents the part.

AssemblyStructureBrowser

Here are some API terms you should be familiar with for the rest of the discussion:

ComponentOccurrence – This is the API object that represents an occurrence within an assembly.  A ComponentOccurrence can represent a subassembly or part and can be at any level of the assembly.  The example assembly above is represented by 14 ComponentOccurrence API objects.

File ReferenceThis is the reference between the assembly to the subassemblies and parts it contains.  In the example above there are six file references within the assembly to the various subassemblies and parts that are referenced.  There is only ever one file reference for a particular file.  For example, even though there are four Pillar parts, there is only one file reference between the assembly and the Pillar.ipt file.  For more information about file references see the previous post Understanding File References.

Leaf Occurrences – These are occurrences that define the end of each branch as you traverse an assembly.  In Inventor all parts are considered leaf occurrences.

There are a few ways to access the occurrences of an assembly.  Which one to use depends on what you’ll be doing with the results.  Below are descriptions and samples illustrating each one.

Assembly Structure
The first method is to access the full assembly structure.  This provides all of the occurrences and the full assembly tree, just as you see it in the browser.  This provides the complete view of the assembly and should be a sufficient amount of information for any task that needs the complete structure. 

Writing a program to access the full assembly tree is the most difficult of the approaches discussed here.  This is because an assembly tree is not a pre-defined size.  An assembly can have any number of levels with each level having any number of occurrences.  It’s a tree with any number of branches.  To get the full assembly structure you need to walk down every branch until you reach every leaf.  To handle a problem like this you need to use a recursive function, which is demonstrated in the code below.

Public Sub TraverseAssemblySample()
    ' Get the active assembly.
    Dim oAsmDoc As AssemblyDocument
    Set oAsmDoc = ThisApplication.ActiveDocument
    Debug.Print oAsmDoc.DisplayName

    ' Call the function that does the recursion.
    Call TraverseAssembly(oAsmDoc.ComponentDefinition.Occurrences, 1)
End Sub

Private Sub TraverseAssembly(Occurrences As ComponentOccurrences, _
                             Level As Integer)
    ' Iterate through all of the occurrence in this collection.  This
    ' represents the occurrences at the top level of an assembly.
    Dim oOcc As ComponentOccurrence
    For Each oOcc In Occurrences
        ' Print the name of the current occurrence.
        Debug.Print Space(Level * 3) & oOcc.Name

        ' Check to see if this occurrence represents a subassembly
        ' and recursively call this function to traverse through it.
        If oOcc.DefinitionDocumentType = kAssemblyDocumentObject Then
            Call TraverseAssembly(oOcc.SubOccurrences, Level + 1)
        End If
    Next
End Sub

The TraverseAssemblySample sub gets the active assembly document and calls the TraverseAssembly sub, passing in the collection of occurrences from the top-level assembly.  The Occurrences property of the AssemblyComponentDefinition object and the SubOccurrences property of the ComponentOccurrence object only return the occurrences directly within that assembly, not in any of its subassemblies.  The TraverseAssembly Sub then does all the work to walk the tree.  It iterates through each occurrences in the collection and for this sample,  prints out the name of each occurrence, but anything could be done with the occurrence at that point.  The next thing it does is what’s the most interesting; it checks to see if the occurrence is a subassembly or a part and if it is a subassembly it calls the TraverseAssembly sub again, passing the collection of occurrences in that subassembly.  The fact that the sub is calling itself is recursion.  It continues this process until it’s completely walked the assembly tree.

When running the program using the previous sample assembly, the following is written into the VBA Immediate window.  You can compare this with the browser view of the assembly and see that they match.

Sample.iam
   Floor:1
   Arch:1
      CurvedSupport:1
      CurvedSupport:2
      ArchTop:1
   Arch:2
      CurvedSupport:1
      CurvedSupport:2
      ArchTop:1
   Pillar:1
   Pillar:2
   Pillar:3
   Pillar:4
   Inventor:1

There are a couple of things to point out in the program.  One is the “Level” argument of the TraverseAssembly sub.  This is used to keep track of what level of the assembly the program is currently at so it can correctly indent the output of the tree and is not required to walk an assembly tree.  The area of the code that you would replace to do the work you want to perform with each occurrence is the line where it prints out the occurrence name.  This line is executed for every occurrence in the assembly.  You can replace this with code that performs whatever functionality you need to accomplish.  For example, if you need to create a custom BOM your code might access the document that’s referenced by this occurrence and extract some iProperty values and then print this out as part of a structured BOM.

Occurrence Lists 
For several releases of Inventor the only way to access the assembly through the API was to traverse the entire tree, as shown above.  Often you don’t need the tree structure but just want a simple list of the contents of the assembly.  There are a couple of properties that provide this simpler access to the assembly contents.

All Leaf Occurrences
This first example uses the AllLeafOccurrences property of the ComponentOccurrences object which returns a collection that contains all of the leaf occurrences of an entire assembly.  the leaf occurrences at every level of the assembly are returned in this single collection.  Remember that the leaf occurrences are the parts, so this won’t contain any subassembly occurrences.  The AllLeafOccurrences property also has one optional argument, (that isn’t used in the sample below), that provides some additional flexibility by allowing you to get only the leaf occurrences for a specified subassembly.

Public Sub GetPartOccurrences()
    ' Get the active assembly.
    Dim oAsmDoc As AssemblyDocument
    Set oAsmDoc = ThisApplication.ActiveDocument

    ' Get the assembly component definition.
    Dim oAsmDef As AssemblyComponentDefinition
    Set oAsmDef = oAsmDoc.ComponentDefinition

    ' Get all of the leaf occurrences of the assembly.
    Dim oLeafOccs As ComponentOccurrencesEnumerator
    Set oLeafOccs = oAsmDef.Occurrences.AllLeafOccurrences

    ' Iterate through the occurrences and print the name.
    Dim oOcc As ComponentOccurrence
    For Each oOcc In oLeafOccs
        Debug.Print oOcc.Name
    Next
End Sub

When the above program is run on the sample assembly the following is written to the VBA Immediate window.  Making the single call is much simpler than traversing the entire assembly looking for part occurrences.

Floor:1
CurvedSupport:1
CurvedSupport:2
ArchTop:1
CurvedSupport:1
CurvedSupport:2
ArchTop:1
Pillar:1
Pillar:2
Pillar:3
Pillar:4
Inventor:1

All Occurrences of a Specified Component
You can also get all of the occurrences that reference a specific document.  For example, you might need to access every instance of a specific fastener in an assembly to replace it with another.  To find every occurrence that references a specified document you can use the AllReferencedOccurrences property of the ComponentOccurrences object.  This property has a single required argument that specifies which part or assembly you want the referencing occurrences for.  It will return all of the occurrences at all levels of the assembly that reference the specified document.  The sample below illustrates this.

Public Sub FindOccurrences()
    ' Get the active assembly.
    Dim oAsmDoc As AssemblyDocument
    Set oAsmDoc = ThisApplication.ActiveDocument

    ' Get the definition of the assembly.
    Dim oAsmDef As AssemblyComponentDefinition
    Set oAsmDef = oAsmDoc.ComponentDefinition

    ' Get the document to find occurrences for. Since it’s assumed
    ' there is at least one occurrence in the assembly that 
    ' references this document, it will already be open since it
    ' was opened when the assembly was opened.
    Dim oDoc As Document
    Set oDoc = ThisApplication.Documents.ItemByName( _
                                "C:\AssemblySample\Pillar.ipt")

    ' Get the occurrences that represent this document.
    Dim oOccs As ComponentOccurrencesEnumerator
    Set oOccs = oAsmDef.Occurrences.AllReferencedOccurrences(oDoc)

    ' Print the occurrences to the Immediate window.
    Dim oOcc As ComponentOccurrence
    For Each oOcc In oOccs
        Debug.Print oOcc.Name
    Next
End Sub

The output for this program is shown below.

Pillar:1
Pillar:2
Pillar:3
Pillar:4

Getting a single list of specific occurrences is easier than traversing through the assembly but you also lose the assembly context that you get when traversing.  However, there are a few properties supported by the occurrence that provide this information in various ways.  If you ever need this kind of information look at the ContainingOccurrence, OccurrencePath, and ParentOccurrence properties of the ComponentOccurrence object.

All Referenced Documents
Another common need when working with an assembly is to access the unique documents referenced by the assembly, because in many cases you don’t need individual access to each occurrence.  For example, let’s say you want to change the material of all parts to a certain material.  You can do that with either one of the two previous programs by adding code that takes a given occurrence, gets the associated document, and changes its material.  The problem is that you’ll end up doing a lot more work than is necessary.  For example, in the sample assembly you’ll end up setting the material for the pillar part four times since it’s used that many times in the assembly, where you really only need to set it once for Pillar.ipt.

You can do this using the list of references instead of the occurrences since there is only one reference to each document regardless of how many times it’s used in the assembly.  The AllReferencedDocuments property of the AssemblyDocument object provides this.  It returns the complete set of referenced documents for the entire assembly regardless of the level in the assembly the reference actually occurs.

Here’s a simple example that illustrates this.

Public Sub ShowReferences()
    ' Get the active assembly.
    Dim oAsmDoc As AssemblyDocument
    Set oAsmDoc = ThisApplication.ActiveDocument

    ' Get all of the referenced documents.
    Dim oRefDocs As DocumentsEnumerator
    Set oRefDocs = oAsmDoc.AllReferencedDocuments

    ' Iterate through the list of documents.
    Dim oRefDoc As Document
    For Each oRefDoc In oRefDocs
        Debug.Print oRefDoc.DisplayName
    Next
End Sub

And here are the results when this sample is run.

Pillar.ipt
Inventor.ipt
ArchTop.ipt
CurvedSupport.ipt
Arch.iam
Floor.ipt

Here’s a more complex sample that uses this to demonstrate setting the material of every referenced part to Gold.

Public Sub ChangeAllPartMaterial()
    ' Get the active assembly document.
    Dim oAsmDoc As AssemblyDocument 
    Set oAsmDoc = ThisApplication.ActiveDocument

    ' Get the material to assign to all of the parts.
    Dim strMaterialName As String
    strMaterialName = "Gold"

    Dim oMaterial As Material
    On Error Resume Next
    ' Try to get the material of the specified name.
    Set oMaterial = oAsmDoc.Materials.Item(strMaterialName)
    If Err Then
        MsgBox "The material """ & strMaterialName & _
                     """ was not found in the library."
        Exit Sub
    End If
    On Error GoTo 0

    ' Iterate through all of the documents refrenced by the assembly.
    Dim oDoc As Document
    For Each oDoc In oAsmDoc.AllReferencedDocuments
        ' Check to see if this is a part.
        If oDoc.DocumentType = kPartDocumentObject Then
            Dim oPartDoc As PartDocument
            Set oPartDoc = oDoc

            ' Set the material.
            oPartDoc.ComponentDefinition.Material = oMaterial
        End If
    Next
End Sub


Comments

5 responses to “Accessing Assembly Components”

  1. Marian S. Avatar
    Marian S.

    Hello, when using script “ChangeAllPartMaterial” an error message “Set oAsmDoc = ThisApplication.ActiveDocument” appears, in form of “Tipe mismatch”; if I remove “Set oAsm….”, application shows a message, that particular kind of material is not recognized in the library. I´ll be very grateful for your advice on this problem.

  2. Marian S. Avatar
    Marian S.

    Hello, when using script “ChangeAllPartMaterial” an error message “Set oAsmDoc = ThisApplication.ActiveDocument” appears, in form of “Tipe mismatch”; if I remove “Set oAsm….”, application shows a message, that particular kind of material is not recognized in the library. I´ll be very grateful for your advice on this problem.

  3. I suspect the first error is because you’re running this when a document besides an assembly is active. This macro expects an assembly to be active. The sample also expects that there is a material named “Gold” in your materials library.

  4. luis mestre Avatar
    luis mestre

    Hi Brian
    I’m trying to access subassembly parameters but without sucess,can you please see my post because i have pictures that can explain better what I’m looking for.
    http://discussion.autodesk.com/forums/thread.jspa?threadID=759541&tstart=0
    Kind regards
    Luis Mestre

  5. Hey Brian!! I was impressed by the “Assembly Structure” you described in the article, but when I tried writing the script and saving, I got an error saying that “Tipe Mismatch” and a dialog box appears with a dialog “Particular kind of material is not available in the library”.
    Can you please help me out to resolve it…..

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading