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:
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
Solved! Go to Solution.
Solved by WCrihfield. Go to Solution.
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
(Not an Autodesk Employee)
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
(Not an Autodesk Employee)
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.
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:
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
(Not an Autodesk Employee)
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
(Not an Autodesk Employee)
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
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.
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.
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
(Not an Autodesk Employee)
I figured out that I was testing it wrong. I made a foolish mistake actually. Got the code working now. Thanks for your advice
Can't find what you're looking for? Ask the community or share your knowledge.