Push iProperties with Exception

HogueOne
Enthusiast
Enthusiast

Push iProperties with Exception

HogueOne
Enthusiast
Enthusiast

Hey there.

 

I have this code that pushes custom iproperties from the top level assembly down to all parts at all levels. I want to modify it so that it skips parts in the first layer of the assembly. For example:

 

  • Top level assembly
    • Subassembly (process)
      • Part (process)
      • Part (process)
    • Part (skip)
    • Subassembly (process)
    • Subassembly (process)
      • Part (process)
    • Part (skip)

I tried inserting an "If" statement that checks whether the "oOcc" Occurrence is an assembly (kAssemblyDocumentObject) before executing the TraverseAssembly Public Subroutine,  But I can't find a solution that works. I'm still learning iLogic so I tried feeding the problem to ChatGPT and it was unsuccessful. Could somebody spare a few minutes to look at this? Thank you so much.

 

Sub Main
'Gets the Active Document
oDoc = ThisDoc.Document

'Checks if the active document is an assembly
If oDoc.DocumentType = kAssemblyDocumentObject
	'Gets the assembly occurrences
	Dim oOccs As ComponentOccurrences
	oOccs = oDoc.ComponentDefinition.Occurrences
	'Call the subprocedure to traverse the assembly
		Call TraverseAssembly(oOccs)
End If

End Sub

'***************
Public Sub TraverseAssembly(oOccs As ComponentOccurrences)
 'Integer to traverse the assembly occurrences
 Dim i As Integer
 
 'For i=1 To oOccs.Count
  Dim oOcc As ComponentOccurrence
For Each oOcc In oOccs
  'oOcc = oOccs.Item(i)  
 	If oOcc.Suppressed = False
   
		'Occurrence Name (Display Name)
		oOccName = oOcc.Name
					Try
				iProperties.Value(oOccName, "Custom", "extrusionColourCode") = iProperties.Value("Custom", "extrusionColourCode")
				iProperties.Value(oOccName, "Custom", "extrusionColourName Date") = iProperties.Value("Custom", "extrusionColourName")
				iProperties.Value(oOccName, "Custom", "panelClass") = iProperties.Value("Custom", "panelClass")
				iProperties.Value(oOccName, "Custom", "panelType") = iProperties.Value("Custom", "panelType")
				iProperties.Value(oOccName, "Custom", "rivetColourCode") = iProperties.Value("Custom", "rivetColourCode")
				iProperties.Value(oOccName, "Custom", "rivetColourName") = iProperties.Value("Custom", "rivetColourName")
				iProperties.Value(oOccName, "Custom", "sheetColourCode") = iProperties.Value("Custom", "sheetColourCode")
				iProperties.Value(oOccName, "Custom", "sheetColourName") = iProperties.Value("Custom", "sheetColourName")
				iProperties.Value(oOccName, "Custom", "sheetStockSize") = iProperties.Value("Custom", "sheetStockSize")
				iProperties.Value(oOccName, "Custom", "sheetSupplier") = iProperties.Value("Custom", "sheetSupplier")
				iProperties.Value(oOccName, "Custom", "zoneID") = iProperties.Value("Custom", "zoneID")
				iProperties.Value(oOccName, "Custom", "elevation") = iProperties.Value("Custom", "elevation")
			
				
			Catch
				'It won't be able to change Read-Only parts (e.g. Content Center)
				'MessageBox.Show("This is a content center file: " & oOcc.Name, "Title")
			End Try
		End If

		If oOcc.DefinitionDocumentType = kAssemblyDocumentObject
			Call TraverseAssembly(oOcc.SubOccurrences)
		End If
Next
End Sub
0 Likes
Reply
Accepted solutions (1)
508 Views
8 Replies
Replies (8)

WCrihfield
Mentor
Mentor

Hi @HogueOne.  One similar thing I have seen in the past in several examples is that they would include an extra 'input' variable in the definition line of the TraverseAssembly Sub routine for an Integer labeled something like iLevel.  This would be set to 1 when the method first gets called to run from within the Sub Main area.  Then every time it 'recurses' (calls itself to run again), it adds 1 to that value within the line of code that calls it to run again.  Then, within the early part of that Sub routine, you can check its value to see what 'level' of the assembly it is currently at.  Another thing you can check is ComponentOccurrence.ParentOccurrence property.  If the component is a top level one, this property will have Nothing as its value, instead of another ComponentOccurrence object (which would normally be a component representing a higher level sub assembly).

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

WCrihfield
Mentor
Mentor

Try this slightly edited version of your code, and see if it works the way you want.  It could be done in more efficient ways, but these small edits should at least work.  You will notice that I have implemented both of the things I mentioned in my earlier post.

Sub Main
	'Gets the Active Document
	oDoc = ThisDoc.Document
	'Checks if the active document is an assembly
	If oDoc.DocumentType = kAssemblyDocumentObject
		'Gets the assembly occurrences
		Dim oOccs As ComponentOccurrences
		oOccs = oDoc.ComponentDefinition.Occurrences
		'Call the subprocedure to traverse the assembly
		Call TraverseAssembly(oOccs, 1)
	End If
End Sub

'***************
Public Sub TraverseAssembly(oOccs As ComponentOccurrences, iLevel As Integer)
	Dim oOcc As ComponentOccurrence
	For Each oOcc In oOccs
		If oOcc.Suppressed = False
			If oOcc.DefinitionDocumentType = DocumentTypeEnum.kPartDocumentObject AndAlso _
				oOcc.ParentOccurrence Is Nothing Then
				Continue For
			End If
			'Occurrence Name (Display Name)
			Dim oOccName As String = oOcc.Name
			Try
				iProperties.Value(oOccName, "Custom", "extrusionColourCode") = iProperties.Value("Custom", "extrusionColourCode")
				iProperties.Value(oOccName, "Custom", "extrusionColourName Date") = iProperties.Value("Custom", "extrusionColourName")
				iProperties.Value(oOccName, "Custom", "panelClass") = iProperties.Value("Custom", "panelClass")
				iProperties.Value(oOccName, "Custom", "panelType") = iProperties.Value("Custom", "panelType")
				iProperties.Value(oOccName, "Custom", "rivetColourCode") = iProperties.Value("Custom", "rivetColourCode")
				iProperties.Value(oOccName, "Custom", "rivetColourName") = iProperties.Value("Custom", "rivetColourName")
				iProperties.Value(oOccName, "Custom", "sheetColourCode") = iProperties.Value("Custom", "sheetColourCode")
				iProperties.Value(oOccName, "Custom", "sheetColourName") = iProperties.Value("Custom", "sheetColourName")
				iProperties.Value(oOccName, "Custom", "sheetStockSize") = iProperties.Value("Custom", "sheetStockSize")
				iProperties.Value(oOccName, "Custom", "sheetSupplier") = iProperties.Value("Custom", "sheetSupplier")
				iProperties.Value(oOccName, "Custom", "zoneID") = iProperties.Value("Custom", "zoneID")
				iProperties.Value(oOccName, "Custom", "elevation") = iProperties.Value("Custom", "elevation")
			Catch
				'It won't be able to change Read-Only parts (e.g. Content Center)
				'MessageBox.Show("This is a content center file: " & oOcc.Name, "Title")
			End Try
		End If
		If oOcc.DefinitionDocumentType = kAssemblyDocumentObject
			TraverseAssembly(oOcc.SubOccurrences, iLevel + 1)
		End If
	Next
End Sub

If this solved your problem, or answered your question, please click ACCEPT SOLUTION .
Or, if this helped you, please click (LIKE or KUDOS) 👍.

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

HogueOne
Enthusiast
Enthusiast

I tried it out and didn't get any errors. However, after testing it the functionality didn't seem to skip over the parts at the top level of the assembly. in the image below, the part formerly had an elevation value of "WEST". but it was changed with the rule.

 

HogueOne_0-1720792016230.png

 

0 Likes

WCrihfield
Mentor
Mentor
Accepted solution

Hi @HogueOne.  Is it possible that there are some top level assembly components that represent parts, where those same component names are also seen within lower level sub assemblies?  If so, then the iLogic shortcut snippet like:

iProperties.Value(oOccName, "Custom", "zoneID")

may not know the difference between a top level or lower level component with the same name, since it is only using its name, and not the component object itself.  If this is the case, then I would recommend switching from those iLogic API codes, to the normal Inventor API way of getting/setting those custom iProperty values.  The Inventor API way would include getting a reference to the actual Document object that the component is referencing, then using that in the following way:

ComponentRefDoc.PropertySets.Item(4).Item("customPropName").Value

The main differences between using the iLogic API route vs the Inventor API route are the following:

  • The iLogic API way allows you to specify PropertySet 'nicknames' (such as "Custom", "Project", "Summary"), as well as their proper names ; while the Inventor API way requires the proper names (such as the values you get from the actual PropertySet.Name property, or PropertySet.InternalName), and does not recognize the shorter nicknames.
  • When 'setting' (not getting) the value of a 'custom' (not standard) iProperty using the iLogic API way, it will automatically create a new custom iProperty for you, if it did not already exist.  But the Inventor API way will not, and instead will throw an error if you try to set the value of a custom iProperty that does not exist.

Here is another variation of this type of code that you can try out, which mainly uses the Inventor API way.  Again, this is not as efficient as it could be.  It would be more efficient to capture the values of the custom iProperties of the main assembly just once, before the main loop ever starts, into some sort of collection, like a Dictionary or NameValueMap first.  That way you are not digging into each of those same exact properties again for every single assembly component instance.  This example does include the use of a List(Of String), just to hold the common custom iProperty names, so that later we can just use another loop to cover all of them.  In this example, within the 'inner' Try...Catch...End Try block of code...on the Catch side, we could have included some code that would have added/created the custom iProperty, if it was not found (Try side failed), instead of just a Logger line, but I was not sure if you wanted that or not.  If you want to be able to see the 'feedback' from those Logger lines, you must first make sure your iLogic Log tab is showing next to your regular iLogic tab.  Then, after you run the rule, you can switch over to that tab, to review its contents.

Sub Main
	'Gets the Active Document
	Dim oDoc As Inventor.Document = ThisDoc.Document
	
	oMainCProps = oDoc.PropertySets.Item(4)
	
	oCPropNames = New List(Of String)
	oCPropNames.Add("extrusionColourCode")
	oCPropNames.Add("extrusionColourName Date")
	oCPropNames.Add("panelClass")
	oCPropNames.Add("panelType")
	oCPropNames.Add("rivetColourCode")
	oCPropNames.Add("rivetColourName")
	oCPropNames.Add("sheetColourCode")
	oCPropNames.Add("sheetColourName")
	oCPropNames.Add("sheetStockSize")
	oCPropNames.Add("sheetSupplier")
	oCPropNames.Add("zoneID")
	oCPropNames.Add("elevation")
	
	'Checks if the active document is an assembly
	If oDoc.DocumentType = kAssemblyDocumentObject
		'Gets the assembly occurrences
		Dim oOccs As ComponentOccurrences = oDoc.ComponentDefinition.Occurrences
		'run the Sub routine to recursively iterate through the assembly's components
		TraverseAssembly(oOccs, 1)
	End If
End Sub

'when declared between routines, all routines can access it
Dim oMainCProps As Inventor.PropertySet
Dim oCPropNames As List(Of String)

'***************
Public Sub TraverseAssembly(oOccs As ComponentOccurrences, iLevel As Integer)
	Dim oOcc As ComponentOccurrence
	For Each oOcc In oOccs
		If oOcc.Suppressed = False
			If oOcc.DefinitionDocumentType = DocumentTypeEnum.kPartDocumentObject AndAlso _
				oOcc.ParentOccurrence Is Nothing Then
				Continue For
			End If
			Dim oOccRefDoc As Inventor.Document = Nothing
			Try
				oOccRefDoc = oOcc.Definition.Document
			Catch
				Logger.Error("Could not access / obtain the Document for a component!")
			End Try
			If oOccRefDoc Is Nothing Then Continue For
			Dim oOccCProps As Inventor.PropertySet = oOccRefDoc.PropertySets.Item(4)
			For Each sCPropName In oCPropNames
				Try
					oOccCProps.Item(sCPropName).Value = oMainCProps.Item(sCPropName).Value
				Catch
					Logger.Error("Error Copying Custom iProperty")
				End Try
			Next sCPropName
		End If
		If oOcc.DefinitionDocumentType = kAssemblyDocumentObject
			TraverseAssembly(oOcc.SubOccurrences, iLevel + 1)
		End If
	Next
End Sub

If this solved your problem, or answered your question, please click ACCEPT SOLUTION .
Or, if this helped you, please click (LIKE or KUDOS) 👍.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

WCrihfield
Mentor
Mentor

I noticed that I am not checking the 'iLevel' property value, but only checking the ComponentOccurrence.ParentOccurrence Property value in my 'filter', so that check could still be added in there, to be more thorough.

 

Another thing that could be changed to achieve better performance would be to switch from recursively iterating assembly component objects, to iterating referenced documents.  However, that may eliminate our ability to determine if it is 'top level' or not.  You may simply be able to compare whether or not a specific document is only available within the between the Document.ReferencedDocuments collection vs the Document.AllReferencedDocuments collection though.  The one starting will 'All' will be a fully inclusive collection, including references at all levels of the assembly, while the other is only immediately referenced documents.  Or...if we stick with recursively iterating the components, we could simply make use of something like an additional List(Of String) to record each unique FullFileName encountered during the process, and check that list each time, to avoid processing the same file multiple times, when multiple components are referencing the same file.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

HogueOne
Enthusiast
Enthusiast

This is driving me crazy. The message box for this code behaves exactly how I want it. skipping top level parts, and showing a message box for every other occurrence. I added a boolean value argument to the subroutine that changes to false when iterating through subassemblies. The only thing that isn't working correctly is it's still assigning iproperties to every occurrence. I'm starting to think there's a bug or something. I don't know what else to do. Try this out and let me know what you think:

Sub Main
    ' Gets the Active Document
    Dim oDoc As Document
    oDoc = ThisDoc.Document

    ' Checks if the active document is an assembly
    If oDoc.DocumentType = kAssemblyDocumentObject Then
        ' Gets the assembly occurrences
        Dim oOccs As ComponentOccurrences
        oOccs = oDoc.ComponentDefinition.Occurrences
        ' Call the subprocedure to traverse the assembly
        Call TraverseAssembly(oOccs, True) ' Pass True for TopLevel
    End If
End Sub

' Public subroutine to traverse the assembly and propagate properties
Public Sub TraverseAssembly(oOccs As ComponentOccurrences, TopLevel As Boolean)
    Dim oOcc As ComponentOccurrence
    
    For Each oOcc In oOccs
        If oOcc.Suppressed = False Then
            ' Occurrence Name (Display Name)
            Dim oOccName As String
            oOccName = oOcc.Name
            
            ' Check if it's the top level and an assembly
            If TopLevel = False Or oOcc.DefinitionDocumentType = kAssemblyDocumentObject Then
                ' Try to update properties
                Try
                    iProperties.Value(oOccName, "Project", "Part Number") = iProperties.Value("Project", "Part Number")
                    
                    ' Show message box with occurrence name
                    MsgBox( "Properties updated for: " & oOccName)
                Catch
                    ' Handle exception if necessary
                End Try
            End If
            
            ' Recursive call for subassemblies
            If oOcc.DefinitionDocumentType = kAssemblyDocumentObject Then
                Call TraverseAssembly(oOcc.SubOccurrences, False) ' Pass False for subsequent levels
            End If
        End If
    Next
End Sub

 

 

0 Likes

WCrihfield
Mentor
Mentor

Hi @HogueOne.  I do not have a good test subject assembly to test on right now, because I do not want to change the part numbers in any of my parts or assemblies to match any other higher level assembly.  We do things very differently where I work.  We build most of our assemblies in a more traditional way, from the lowest level parts first, to the lower level sub assemblies, then on up to the main assemblies.  And we do not copy/push iProperties, Parameters, representations, ModelStates, or anything else down into sub components from our main assemblies.  Everything that is needed in our lower level stuff is already in them, the way we want them, before they get put into the assemblies.

 

One additional thing that comes to mind is how you are checking the Enum variations.  When writing code in the old VBA editor user interface, it directs us to only include the 'name' of an Enum variation.  However, in iLogic (and vb.net, which is used in iLogic rules), we should always precede any Enum variation name with the name of the Enum itself.  For example:

 

If oDoc.DocumentType = kAssemblyDocumentObject Then

 

...should be written like this instead:

 

If oDoc.DocumentType = DocumentTypeEnum.kAssemblyDocumentObject Then

 

When writing code within an iLogic rule, we can usually just hover our mouse over the property to see what 'Type' its value is supposed to be.

WCrihfield_0-1721062446711.png

In the case of Document.DocumentType property, its Value should be DocumentTypeEnum.  This tells us the name of the Enum, so we can just type that in, then when we type the dot after it, it shows us all the variations of it, so that we can choose one to drop in there.

WCrihfield_1-1721062678901.png

I am not 100% sure if that is the problem in your case, but correcting that may help.  There are at least 3 different places in your last code example where these changes could be made.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

HogueOne
Enthusiast
Enthusiast

I figured out that I was testing it wrong. I made a foolish mistake actually. Got the code working now. Thanks for your advice

0 Likes