Iterate through components & suppress

skotzur
Contributor
Contributor

Iterate through components & suppress

skotzur
Contributor
Contributor

We have an inventor Add-in from our ERP software manufacturer which takes the BOM from inventor and maps it over into a SQL database. It's a really useful tool so that we don't have to do it manually anymore. The current iLogic code iterates down through an open document and for each file it manipulates some iproperties and maybe some other file attributes, like BOM structure. That code looks something like this (I've condensed for brevity):

 

 

Dim openDoc As Document
openDoc = ThisDoc.Document
Dim docFile As Document
Dim isWeldment As Boolean
If openDoc.DocumentType = 12291 Then 'If openDoc is an Assembly
		
		'disable Part Number Row Merge in BOM
		Dim oCD As AssemblyComponentDefinition
		oCD = openDoc.ComponentDefinition
		oBOM = oCD.BOM
		oBOM.SetPartNumberMergeSettings(False, )
		iProperties.Value("Project", "Description") = ""
		iProperties.Value("project", "Stock Number") = ""
		oBOM.ImportBOMCustomization("D:\Reelex\Designs\iLogic Programs\BOMColumns.xml")
		oBOM.StructuredViewEnabled = True
		oBOM.StructuredViewFirstLevelOnly = False
		oBOM.PartsOnlyViewEnabled = True
		
	'Iterate through assembly
		For Each docFile In openDoc.AllReferencedDocuments
			'do some things
		Next
end if

 

 

 

One key aspect of how we use this add-in is that we do NOT manage content center stuff in our ERP system. We do not have every nut, bolt, washer & other "floor stock" appear in our bills. So we relied VERY heavily on the old "All Content Center Suppressed" Level of Detail. By supressing all content center it does not get included into the data that is moved into the ERP system. As you may know, that feature has been done away with, and we're attempting to recreate it using ilogic. This is a new concept for me because the existing ilogic code that we've written works on the file, but suppression works on the occurrence, and some of the same concepts aren't really working for me. What I'm attempting to do is the following:

  1. Check to see if the Open Document is an assembly
  2. If it is then create a level of Detail in the assembly and set it active
  3. Iterate down through the assembly suppressing any occurence that is from content center 
    -and-
    If there is a sub assembly create a LOD in there & iterate down through, suppressing CC

Here's what I've got so far:

 

 

Dim openDoc As Document
openDoc = ThisDoc.Document
Dim docFile As Document
Dim oLOD As LevelOfDetailRepresentation
If openDoc.DocumentType = 12291 Then 'If openDoc is an Assembly
	Dim oCD As AssemblyComponentDefinition
	oCD = openDoc.ComponentDefinition
	Try
		oLOD = oCD.RepresentationsManager.LevelOfDetailRepresentations.Add("All CC Suppressed")
	Catch
		oLOD = oCD.RepresentationsManager.LevelOfDetailRepresentations.Item("All CC Suppressed")
		oLOD.Activate(True)
	End Try
	oCompDef = openDoc.ComponentDefinition
	Dim oCompOcc As ComponentOccurrence
	For Each oCompOcc In oCompDef.Occurrences
		If oCompOcc.Definition.isContentMember=True Then
			oCompOcc.Suppress
		End If
	Next
	oLOD = oCD.RepresentationsManager.LevelOfDetailRepresentations.Item("Master")
	oLOD.Activate(True)	
End If

 

 

The code successfully creates the LOD in the top level assembly, but that's about it. My glaring issues right now are:

  1. Unlike when using the "For each docFile" the "For Each oCompOcc" doesn't seem to iterate down through multiple levels. It only touches the occurences which are directly reporting to the open assembly, not occurences which are within sub-assemblies.
  2. If there is a weldment occurrence, then I receive the following error:
    "Public member 'isContentMember' on type 'WeldmentComponentDefinition' not found."
  3. I haven't gotten to the point where I am making LODs in the subassemblies, but I swear that at some point yesterday I had some working code, but it has since been deleted.

I do not do this sort of thing for a living. I do not have an in depth understanding of the Inventor API, or programming in general. I know just enough to get by with the code that we have developed, so please be patient with my limited knowledge, abilities & what is bound to be some nearly unreadable/nonsensical code. 

0 Likes
Reply
Accepted solutions (2)
2,426 Views
3 Replies
Replies (3)

skotzur
Contributor
Contributor

I've made SOME progress, where the code now seems to iterate down through multiple layers in an assembly and looks at all occurrences. I accomplished this by using the AllLeafOccurrences. I am now using the fullFileName of the occurence to help to determine if the file is from the content center, so it properly identifies content center stuff. I've no doubt that using the IsContent attribute is a more slick way of doing it, so I would love to see other ways of getting what I'm looking for.

anyway...If the assembly is a simple 1 layer assembly that's a collection of basic parts & content center parts it executes successfully, but the code ends up failing when run in a more complicated assembly. Here's the updated code:

Dim openDoc As Document
openDoc = ThisDoc.Document
Dim docFile As Document
Dim oLOD As LevelOfDetailRepresentation
If openDoc.DocumentType = 12291 Then 'If openDoc is an Assembly
	Dim oCD As AssemblyComponentDefinition
	oCD = openDoc.ComponentDefinition
	oBOM = oCD.BOM
	oBOM.SetPartNumberMergeSettings(False, )
	oBOM.ImportBOMCustomization("D:\Reelex\Designs\iLogic Programs\BOMColumns.xml")
	oBOM.StructuredViewEnabled = True
	oBOM.StructuredViewFirstLevelOnly = False
	oBOM.PartsOnlyViewEnabled = True
	Try
		oLOD = oCD.RepresentationsManager.LevelOfDetailRepresentations.Add("All CC Suppressed")
	Catch
		oLOD = oCD.RepresentationsManager.LevelOfDetailRepresentations.Item("All CC Suppressed")
		oLOD.Activate(True)
	End Try
	Dim oCompOcc As ComponentOccurrence
	Dim oOccs As ComponentOccurrencesEnumerator
	oOccs = oCD.Occurrences.AllLeafOccurrences
	For Each oCompOcc In oOccs
		Dim docFNmae As String			'Full File Name of occurence
		docFName = oCompOcc.Definition.Document.FullfileName
		Dim oDFNamePos As Long			'It's supposed to be a number...the length from the end that the character of interest occurs. In this case, "\"
		oDFNamePos = InStrRev(docFName, "\", -1)
		Dim oDdocFName As String		'The abridged docFName so that it only shows the characters after the delimiter "\"
		oDdocFName = Mid(docFName, oDFNamePos + 1, Len(docFName) -oDFNamePos)
		
		If UCase(docFName).Contains("CONTENT CENTER FILES") Then
			MessageBox.Show("This is a Content Center doohickey: " + oDdocFName, docFName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
			oCompOcc.Suppress
		End If
	Next
	'oLOD = oCD.RepresentationsManager.LevelOfDetailRepresentations.Item("Master")
	'oLOD.Activate(True)	
End If

 

0 Likes

A.Acheson
Mentor
Mentor
Accepted solution

In our company I have set up all the filtering to occur in excel. Here I use a field call category and VBA advance filter to include or exclude any category I require. This allows for welded parts to go to one assembly order and assembly parts to go to another etc. This does involve creating a custom CC but in our case I have filled in part numbers and description. 

 

I have also seen a BOM export where you export line by line by iterating through the BOM and filter as you go. You could check for CC parts here. This can be a bit slower as it contacts excel for each cell for population. The regular BOM export simply paste the whole BOM into excel. 

 

Relating to your question, you are only looping through the parts in the first level, If you have sub assemblies inside first level  your code will not enter into those. The below  rule enters every assembly and loops through the occurrences. I have used the built in property set content center. However if you have copied a part from content center, saved as custom and use it a a library file I assume it will keep the attribute and therefore might give you misleading results. So you may need to use an alternate filtering technique. 

 

The traverse the tree method can get a bit complicated bringing information in and out of the various sub's if you want to carry out actions on assemblies etc. . 

 

 

https://modthemachine.typepad.com/my_weblog/2009/03/accessing-assembly-components.html

Sub Main
	TraverseAssembly()
End Sub


Public Sub TraverseAssembly()
    ' Get the active assembly.
    Dim oAsmDoc As AssemblyDocument
    oAsmDoc = ThisApplication.ActiveDocument
	Logger.Info(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.
		'Level indenting for each sub assemblies
		Logger.Info("Occurence:" & 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
		'------------------------Do Something Else---------------------------------------
		
			If oOcc.DefinitionDocumentType = kPartDocumentObject Then
				
				Dim oOccDoc As Document
				oOccDoc = oOcc.Definition.Document
						
				'If the "ContentCenter" property set exists, the part came from Content Center
				IsCCPart = oOccDoc.PropertySets.PropertySetExists("ContentCenter")
				
				If IsCCPart = True
					MessageBox.Show(oOcc.Name & ": Is Content Center Part: " & IsCCPart)
					'oOcc.Suppress
				Else
				End If
			End If	
		
    Next
End Sub

 

If this solved a problem, please click (accept) as solution.‌‌‌‌
Or if this helped you, please, click (like)‌‌
Regards
Alan

skotzur
Contributor
Contributor
Accepted solution

Hey @A.Acheson thanks for your help. Between your code, and a few other samples that I found online I managed to put together a working program.

 

An interesting aspect to this "ETM" system is that it apparently does NOT look at the BOM when moving the data over to the ERP database. Instead it looks at the information in the Model Browser (at least this is how it was described to me by the people at Genius ERP). Dealing with the BOM directly wouldn't have been a good solution, and I needed to exclude and/or suppress the various components. The code accomplishes this by first iterating through, creating the ModelStates where it must & can. It then iterates through, suppressing Content Center Files where it can. If it's an iAssembly it cannot create model states or suppress, so instead it opens the iassembly Factory & excludes the Content Center. If you watch the demonstration, you'll see where this breaks down a bit when the table gets involved, but it works in 99% of our applications, so that'll have to do. I know we weren't using this feature for what it was originally intended (reduce processor load), but Autodesk getting rid of it really screwed us!

 

Here's a link to a demo of it in action, make sure you chage the res to 1080p if it's too grainy:

https://drive.google.com/file/d/127fuYy3q6inEDC8BRAKChO1XY7AGUnh2/view?usp=sharing

 

And here's the code:

-Please note there's a sub in there SelectModelState(). I used some source code which displayed a drop down list and allowed the user to create their own model state, or select from existing ones. It then allowed the user to click on individual occurrences to suppress. I only needed this "All CC Suppressed" Model State, so some of that stuff is vestigial and commented out.

Sub Main 
	Dim oName As String
	oName = SelectModelState()

	If oName = "!!Leave The Rule!!12345" Then Return	

	TraverseAssemblyMS(oName)

	TraverseAssemblySuppress(oName)

	InventorVb.DocumentUpdate()	
End Sub

Function SelectModelState()
	Dim oDoc As AssemblyDocument
	oDoc = ThisApplication.ActiveDocument
	
	'Get Active Document Component Definition
	Dim oAsmCompDef As AssemblyComponentDefinition
	oAsmCompDef = oDoc.ComponentDefinition
	
	'Get the current Model State
	oCurrrent = oDoc.ModelStateName
	
	'Create a list and add "create New State" as the first member of the list
	Dim oList As New ArrayList
	oList.Add("*** Create New State ***")
	
	'Get all Model States in the active document and add each of them to the list 
	Dim oModelState As ModelState
	For Each oModelState In oDoc.ComponentDefinition.ModelStates
		oList.Add(oModelState.Name)
	Next
	
	If Not oList.Contains("All CC Suppressed") Then
		oList.Add("All CC Suppressed")
	End If
	
	'Display the list in a box and allow the user to select one. 
	
	'oName = InputListBox("Select one to use", oList, oCurrrent, "States")
	oName = "All CC Suppressed"

	If oName = "*** Create New State ***" Then	
		oName = InputBox("Enter name for new model state: ", "Create Model State", "New Model State")
	End If
	
	'I had to do this because a Function can't return a null, and attempting to do so crashed inventor. 
	If oName = "" Then 'Return 'exit rule
		oName = "!!Leave The Rule!!12345"
	End If
	
	oFound = False
	'look for Modelstate and set active if found
	For Each oModelState In oDoc.ComponentDefinition.ModelStates
		If oModelState.Name = oName Then 
			oDoc.ComponentDefinition.ModelStates.Item(oName).Activate()
			oFound = True
		End If
	Next
	
	'create ModelState in the top level if it wasn't found
	If oFound = False And oName <> "!!Leave The Rule!!12345" Then
		oModelState = oDoc.ComponentDefinition.ModelStates.Add(oName)
	End If
	Return oName
End Function

'This sub Traverses the assembly & applies the model states to any assembly that can have one
Sub TraverseAssemblyMS(oName As String)
 	Dim oDoc As AssemblyDocument
	oDoc = ThisApplication.ActiveDocument
	Dim oOCD As ComponentDefinition
	oOCD = oDoc.ComponentDefinition
	
	Dim oOcc As ComponentOccurrence 
	
	' Get all occurrences from component definition for Assembly document
	For Each oOcc In oOCD.Occurrences
				
		Dim bWasSuppressed As Boolean = False
		If oOcc.Suppressed Then
			bWasSuppressed = True
			oOcc.Unsuppress
		End If
		
		Dim docFNmae As String		
		Dim oOccDef As ComponentDefinition
		oOccDef = oOcc.Definition
		docFName = oOccDef.Document.FullFileName
		oOccObjectType = oOccDef.Type
		

		' 'check for content center at the top level. 
		If oOcc.SubOccurrences.Count <> 0 And Not UCase(docFName).Contains("REELEX\CONTENT CENTER FILES") And Not UCase(docFName).Contains("REELEX\DESIGNS\PARTS") And Not UCase(docFName).Contains("\FRAME\") And Not oOcc.IsiAssemblyMember Then 

			CreateModelState(oOcc, oName)
			processAllSubOccMS(oOcc, oName)
		Else If oOcc.IsiAssemblyMember
		End If
			InventorVb.DocumentUpdate()
	Next 
	
End Sub

' This function is called for processing sub assembly.  It is called recursively
' to iterate through the entire assembly tree.
Private Sub processAllSubOccMS (ByVal oOcc As ComponentOccurrence, oName As String)


    Dim oSubCompOcc As ComponentOccurrence
    For Each oSubCompOcc In oOcc.SubOccurrences

		Dim bWasSuppressed As Boolean = False
		
		'This means that this ModelState already existed. So gotta open the parent file, then unsuppress. 
		If oSubCompOcc.Suppressed Then

			bWasSuppressed = True
			UnsuppressInParent(oSubCompOcc, oName)
			InventorVb.DocumentUpdate()

		End If

		Dim docFNmae As String		
		Dim oOccDef As ComponentDefinition
		oOccDef = oSubCompOcc.Definition
		docFName = oOccDef.Document.FullFileName
        
		'If the occurence is located in content center
		If oSubCompOcc.SubOccurrences.Count <> 0 And Not UCase(docFName).Contains("REELEX\CONTENT CENTER FILES") And Not UCase(docFName).Contains("REELEX\DESIGNS\PARTS") And Not UCase(docFName).Contains("\FRAME\") And Not oSubCompOcc.IsiAssemblyMember Then 
		'If it's got subOccurences and its not Content Center, not a part, and not a frame, there might be CC lower, so keep going

			'Recursively call this Sub
			Call processAllSubOccMS(oSubCompOcc, oName)

			'Create the model state in a subassembly
			CreateModelStateinSub(oSubCompOcc, oName)
		Else If oSubCompOcc.IsiAssemblyMember Then 
		End If 
    Next
End Sub


Sub CreateModelState(oOcc As ComponentOccurrence, oName As String)
		
	'It's safe to define oOcd as an Assembly Component Definition because that's a condition for calling this Sub
	Dim oOcd As AssemblyComponentDefinition
	oOcd = oOcc.Definition


	Dim oOcdModelStates As ModelStates
	oOcdModelStates = oOcd.ModelStates
	

	'Create a list & add all model states to the list. 
	Dim oList As New ArrayList
	For Each oModelState In oOcdModelStates
		oList.Add(oModelState.Name)
	Next


	'If the list does not contain the new Model States, then add it. If it does, then activate the selected model states
	If Not oList.Contains(oName) Then
		oOcdModelStates.Add(oName)
		oOcc.ActiveModelState = oName
	Else 
		oOcc.ActiveModelState = oName
	End If

End Sub

Sub CreateModelStateinSub(oOcc As ComponentOccurrence, oName As String)
	
	

	oParent = oOcc.ParentOccurrence.Definition.Document.fullfilename
	oDoc = ThisApplication.Documents.Open(oOcc.Definition.Document.FullFileName, False) 
	
'	'It's safe to define oOcd as an Assembly Component Definition because that's a condition for calling this Sub

	Dim oOcd As AssemblyComponentDefinition
	oOcd = oDoc.ComponentDefinition
	'oOcd = oOcc.Definition

'	Dim oModelState As ModelState
	Dim oOcdModelStates As ModelStates
	oOcdModelStates = oOcd.ModelStates

'	'Create a list & add all model states to the list. 
	Dim oList As New ArrayList
	For Each oModelState In oOcdModelStates

		oList.Add(oModelState.Name)
	Next


	'If the list does not contain the new Model States, then add it. If it does, then activate the selected model states

	If Not oList.Contains(oName) Then
		oOcdModelStates.Add(oName)
	End If
	
	'Now the Model State should be added. Now the PARENT must be opened, correct model state activated in Parent, then activate the correct model state in the occurence within the parent

	oPDoc = ThisApplication.Documents.Open(oParent, False) 

	oPDoc.ComponentDefinition.ModelStates.Item(oName).Activate()

	For Each iOcc In oPDoc.ComponentDefinition.Occurrences
		If iOcc.Definition.Document Is oOcc.Definition.Document Then
			iOcc.ActiveModelState = oName
		End If
	Next

End Sub

Sub TraverseAssemblySuppress(oName As String)
 	Dim oDoc As AssemblyDocument
	oDoc = ThisApplication.ActiveDocument
	Dim oOCD As ComponentDefinition
	oOCD = oDoc.ComponentDefinition
		
	Dim oOcc As ComponentOccurrence 
	
	' Get all occurrences from component definition for Assembly document
	For Each oOcc In oOCD.Occurrences
		
		Dim docFNmae As String		
		Dim oOccDef As ComponentDefinition
		oOccDef = oOcc.Definition
		docFName = oOccDef.Document.FullFileName
		
		' 'check for content center at the top level. 
		If  UCase(docFName).Contains("REELEX\CONTENT CENTER FILES") Then

			'Suppress the occurence. This works on top level only
			oOcc.Suppress
			
		'Otherwise, check to see that it is an assembly that is not a part or frame. 
		Else If oOcc.SubOccurrences.Count <> 0 And Not UCase(docFName).Contains("REELEX\DESIGNS\PARTS") And Not UCase(docFName).Contains("\FRAME\") Then 

			'We want to keep traversing to either leaf nodes or CC
			ProcessAllSubOccSuppress(oOcc, oName)
		End If 
		
	Next 
	
End Sub

Private Sub ProcessAllSubOccSuppress(ByVal oOcc As ComponentOccurrence, oName As String)

	Dim oSubCompOcc As ComponentOccurrence
		'MessageBox.Show("The program got here 0 " & oOcc.Name)
	'if the subassembly is an iAssembly
	If oOcc.IsiAssemblyMember Then 
		'MessageBox.Show("The program got here 1")
		Dim oPFactory As String
		oPFactory = oOcc.Definition.iAssemblyMember.ParentFactory.Parent.Document.FullFileName
		'MessageBox.Show("The program got here 2" & oPFactory)
		'Open the Factory Document 
		oPDoc = ThisApplication.Documents.Open(oPFactory, True) 
		'MessageBox.Show("The program got here 3")
		MessageBox.Show("The code has encountered an iAssembly. It will now open the iAssembly Factory and exclude all Content Center. Please take note of the following: " & vbCr & vbCr & "- You will need to manually ""dirty"" the iassembly. You can do this by toggling exclusion on any component in the iassembly. " & vbCr & vbCr & " - Any component that is ""included"" in the table will still apear in the upper level browser & will need to be manually unchecked when running ETM. " & vbCr & vbCr & "- All content Center that is excluded by the code will need to be manually un-excluded. ", oOcc.Name)
		'Iterate through the iassembly
		For Each oSubCompOcc In oOcc.SubOccurrences
			
			Dim oOccDef As ComponentDefinition
			Dim docFNmae As String
			oOccDef = oSubCompOcc.Definition
			docFName = oOccDef.Document.FullFileName
			
			'If the occurence is located in content center
			If UCase(docFName).Contains("REELEX\CONTENT CENTER FILES") Then 
				oOcc.Definition.Document.Dirty = True
				For Each iOcc In oPDoc.ComponentDefinition.Occurrences
					If iOcc.Name = oSubCompOcc.Name Then 
						iOcc.Excluded = True
					End If
				Next
			
			' Else if it's an assembly that's not a frame or a Part
			Else If oSubCompOcc.SubOccurrences.Count <> 0 And Not UCase(docFName).Contains("REELEX\DESIGNS\PARTS") And Not UCase(docFName).Contains("\FRAME\") Then 
				ProcessAllSubOccSuppress(oSubCompOcc, oName)
			End If 
		Next
	Else 
		
		'MessageBox.Show("The program got here 5 " & oOcc.Name)
		'Open the parent document 
		'Set the model state in the parent
		Dim oParent As String
		oParent = oOcc.Definition.Document.fullFileName
		'MessageBox.Show("The program got here 6 " & oParent)
		
		oPDoc = ThisApplication.Documents.Open(oParent, False) 
		oPDoc.ComponentDefinition.ModelStates.Item(oName).Activate()
				
		
		For Each oSubCompOcc In oOcc.SubOccurrences
			
			If oSubCompOcc.Suppressed Then
				oSubCompOcc.Unsuppress
			End If
				
			Dim oOccDef As ComponentDefinition
			Dim docFNmae As String
			oOccDef = oSubCompOcc.Definition
			docFName = oOccDef.Document.FullFileName
			
			'If the occurence is located in content center
			If UCase(docFName).Contains("REELEX\CONTENT CENTER FILES") Then 
				
				For Each iOcc In oPDoc.ComponentDefinition.Occurrences
					If iOcc.Name = oSubCompOcc.Name Then 
						iOcc.Suppress
					End If 
				Next
			
			' Else if it's an assembly that's not a frame or a Part
			Else If oSubCompOcc.SubOccurrences.Count <> 0 And Not UCase(docFName).Contains("REELEX\DESIGNS\PARTS") And Not UCase(docFName).Contains("\FRAME\") Then 
				ProcessAllSubOccSuppress(oSubCompOcc, oName)
			End If 	
		Next 
	End If 
	
	
				InventorVb.DocumentUpdate()
				
				
End Sub

Private Sub UnsuppressInParent(oOcc As ComponentOccurrence, oName As String)
	'Open the parent Document
	oParent = oOcc.ParentOccurrence.Definition.Document.fullfilename

	oPDoc = ThisApplication.Documents.Open(oParent, False)
		
	'Set the ModelState Active
	oPDoc.ComponentDefinition.ModelStates.Item(oName).Activate()
		
	'find the occurence in question and unsuppress it
	For Each iOcc In oPDoc.ComponentDefinition.Occurrences
		If iOcc.name = oOcc.Name
			iOcc.unSuppress
		End If
	Next
End Sub

	

 

0 Likes