Create empty Parts List on drawing and fill rows by itterate the assembly browser pane

Create empty Parts List on drawing and fill rows by itterate the assembly browser pane

checkcheck_master
Advocate Advocate
1,842 Views
23 Replies
Message 1 of 24

Create empty Parts List on drawing and fill rows by itterate the assembly browser pane

checkcheck_master
Advocate
Advocate

Is it possible to create an empty parts list on a drawing and to create the rows by traverse the browser pane?

The goal is to have the parts list sorted as the browser pane.

 

0 Likes
1,843 Views
23 Replies
Replies (23)
Message 2 of 24

WCrihfield
Mentor
Mentor

Hi @checkcheck_master.  It might be doable, but you would have to use a 'CustomTable' instead of a regular PartsList.  But then, the thought of traversing the model browser by code to find each component, then extract data, then record all its data into the individual cells that table, makes my head hurt, so I would avoid that process if possible, but that's just my current personal opinion.  What about creating a regular PartsList, with all data present, then use something like the PartsListRow.Reposition method to sort the rows into the needed order.  With that route, you would still need to get the proper order ahead of time, but then you could not only use a real PartsList, but also skip the whole data entry process.  Just a thought.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 3 of 24

checkcheck_master
Advocate
Advocate
Thanks for your thoughts WCrihfield, very helpfull.
I did not know there is Reposition available in code.
Could it be possible to read the order from the browser pane and reorder the parts list accordingly?
Would you have an idea for this?
0 Likes
Message 4 of 24

WCrihfield
Mentor
Mentor

I haven't tried it before, so I don't have any code sample for you to try out right now, but it certainly sounds doable.  I have definitely navigated through the model browser to certain degrees by code before, but never completely, for a purpose like this before.  Might get complicated, so I'll have to maybe look into it further tomorrow.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 5 of 24

jjstr8
Collaborator
Collaborator

My thought is to assign item numbers in the assembly's BOM to match the browser pane order.  Once that's done, you can sort it by item number and then the parts list on the drawing is in the correct order right when it's placed.  Drilling through the browser pane in code can be a little tedious.  You have to deal with ignoring multiple occurrences, drilling down in component folders while ignoring special folders, as well as patterned components and maybe bolted connections.

0 Likes
Message 6 of 24

checkcheck_master
Advocate
Advocate

Thank you all for the input.
@jjstr8: Going through the browser pane is not that bad, check message 4 of this post:
https://forums.autodesk.com/t5/inventor-ilogic-api-vba-forum/get-all-occurrences-from-assembly-with-...
Getting that output in a list should also work.
Now I'm actually looking for a piece of code to use this list to reposition the rows of the Part List on the drawing.
Anyone have experience with this?

0 Likes
Message 7 of 24

jjstr8
Collaborator
Collaborator

No, going through the browser pane is not bad, just a little tedious depending what you're trying to get out of it.  The code in the post you linked should be a good start.  I would think you'd need to modify it to ignore duplicates and not drill into subassemblies.  As for the other piece of it, as I mentioned in my last post, I think re-ordering the BOM, rather than the parts list directly, is a good approach.  It's just matter of iterating through the BOM, searching the list generated from the browser pane and assigning item numbers based on location in the list.  After the all of the item numbers are assigned, sort the BOM.

0 Likes
Message 8 of 24

WCrihfield
Mentor
Mentor

Been a bit busy today, but squeezed in some time for this.  I assumed your drawing's parts list is only focused on stuff found in the first level of the Structured BOM view, because I think trying to go deeper with this specific process would likely be more trouble than its worth.   I tried collecting a few different object types (BrowserNode, ComponentDefinition, etc.) while looping the browser pane, each with its drawbacks, until going back to the basics and just using a String.  What I came up with is working in my test files so far (assembly document & drawing document for that assembly).  The code is started while the drawing is active, then gets the model reference from the first view on the active sheet.  Then either finds or creates a parts list.  Then I call my custom function to search through the model browser of the model, and return a List of FullFileName's.  In the function, each component it comes across, it attempts to extract the full file name of the document it is representing.  And if it encounters an OccurrencePattern, it just works with the 'source/input' components, because all the rest are just quantity/copies, not originals.  Then back at the main Sub area, we use the 'Index' of each name returned to reposition each PartsListRow.  As you can see, it is a pain in the butt digging down into a PartsListRow to get a full file name to compare to, but it works.  In retrospect, the assembly BOM angle does look like less trouble.

Here's the code I came up with:

Sub Main
	If ThisApplication.ActiveDocumentType <> DocumentTypeEnum.kDrawingDocumentObject Then
		MsgBox("A Drawing Document must be active for this rule to work. Exiting.", vbCritical, "iLogic")
		Exit Sub
	End If
	Dim oDDoc As DrawingDocument = ThisApplication.ActiveDocument
	oTG = ThisApplication.TransientGeometry
	oSheet = oDDoc.ActiveSheet
	oView = oSheet.DrawingViews.Item(1)
	Dim oADoc As AssemblyDocument = oView.ReferencedDocumentDescriptor.ReferencedDocument
	Dim oPList As PartsList
	If oSheet.PartsLists.Count = 0 Then
		oPos1 = oTG.CreatePoint2d(0, 0) '<<< WE CAN CHANGE THIS LATER >>>
		oLevel = PartsListLevelEnum.kStructured 'fist level only Structured view
		oSheet.PartsLists.Add(oADoc, oPos1, oLevel)
	Else
		oPList = oSheet.PartsLists.Item(1) 'or Pick, or find by Title
	End If
	
	'now get order needed
	Dim oFileNames As List(Of String) = GetFileNamesInOrder(oADoc)
'	MsgBox("oDocs.Count = " & oFileNames.Count, , "")
'	For Each oFileName In oFileNames
'		MsgBox("oFileName = " & oFileName,,"")
'	Next
	
	'now reposition rows to match needed order
	For Each oRow As PartsListRow In oPList.PartsListRows
		Try
			Dim oDBOMRow As DrawingBOMRow = oRow.ReferencedRows.Item(1)
			oCD = oDBOMRow.BOMRow.ComponentDefinitions.Item(1)
			Dim oRowDoc As Document = oCD.Document
			'check what 'Index' this one is in our list
			oIndex = oFileNames.IndexOf(oRowDoc.FullFileName)
			oRow.Reposition(oIndex + 1, True) 'specify next index, then use insert before, to position at that index
		Catch oEx As Exception
			MsgBox("Reposition method failed." & vbCrLf & _
			oEx.Message & vbCrLf & oEx.StackTrace,,"")
		End Try
	Next
End Sub

Function GetFileNamesInOrder(oAsmDoc As AssemblyDocument) As List(Of String)
	'oAsmDoc.Activate
	Dim oList As New List(Of String)
	oTopNode = oAsmDoc.BrowserPanes.Item("Model").TopNode
	For Each oNode As BrowserNode In oTopNode.BrowserNodes
		Try
			If TypeOf oNode.NativeObject Is ComponentOccurrence Then
				Dim oOcc As ComponentOccurrence = oNode.NativeObject
				Dim oDoc As Document = oOcc.ReferencedDocumentDescriptor.ReferencedDocument
				If oList.Contains(oDoc.FullFileName) Then
					Continue For 'don't dig into any possible sub-components
				Else
					oList.Add(oDoc.FullFileName) 'add to the end
				End If
			ElseIf TypeOf oNode.NativeObject Is OccurrencePattern Then
				Dim oOccPatt As OccurrencePattern = oNode.NativeObject
				'process 'input' items only - others are just extra quantity items, not originals
				For Each oObj In oOccPatt.ParentComponents 'ObjectCollection
					If Not TypeOf oObj Is ComponentOccurrence Then Continue For
					Dim oPOcc As ComponentOccurrence = oObj
					Dim oODoc As Document = oPOcc.ReferencedDocumentDescriptor.ReferencedDocument
					If oList.Contains(oODoc.FullFileName) Then
						Continue For 'don't dig into any possible sub-components
					Else
						oList.Add(oODoc.FullFileName) 'add to the end
					End If
				Next
			End If
		Catch
		End Try
	Next
	Return oList
End Function

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

If you want and have time, I would appreciate your Vote(s) for My IDEAS 💡 or you can Explore My CONTRIBUTIONS

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 9 of 24

checkcheck_master
Advocate
Advocate

I am dumbfounded.
Totally awesome.
I kind of understand what's happening but I can't do it myself in such a short time, thanks a lot!
It gives me more insight into what is possible and in a much shorter term than I expected.
It also took me a while to see what jjstr8 came up with.
I never quite realized that in the BOM you can manually change the item numbering.
I'm guessing this can also be done with code?
I think in that case we kill two birds with one stone, both the Parts List and the Excel BOM would then be in the same order as the Browser Pane, it shouldn't get any crazier.
More or less complete control over it from the 3D environment, my wet CAD dream so to speak.
Is it true that with the code that is there now that is not such a big step anymore?
I haven't googled it yet, so what I'm going to do now.
Later.

0 Likes
Message 10 of 24

WCrihfield
Mentor
Mentor

Hi @checkcheck_master.  Here is something very similar, for doing about the same thing with the BOM, when ran from the 'active' assembly document.  The BOMRow object doesn't have a Reposition method, so what we are doing here is changing the row's ItemNumber value, according to the Index of the List.  Then whet that process is done, we simply use the BOMView.Sort method to sort the BOM Rows by the ItemNumber values, to put them in proper order.  If your ItemNumber column's Title is not "Item" (capitalization is important), you might need to edit that text in the rule before you run it.  When I first ran this, it gave me an 'parameter is not correct' type error, which was simply because I first had the column title as "ITEM".  After I changed that to "Item", that fixed it, and everything worked as expected.

Here is the BOM version iLogic rule code:

Sub Main
	If ThisApplication.ActiveDocumentType <> DocumentTypeEnum.kAssemblyDocumentObject Then
		MsgBox("An Assembly Document must be active for this rule to work. Exiting.", vbCritical, "iLogic")
		Exit Sub
	End If
	Dim oADoc As AssemblyDocument = ThisApplication.ActiveDocument
	oBOM = oADoc.ComponentDefinition.BOM
	oBOM.StructuredViewEnabled = True
	oBOM.StructuredViewFirstLevelOnly = True
	oView = oBOM.BOMViews.Item("Structured")
	
	'now get order needed
	Dim oFileNames As List(Of String) = GetFileNamesInOrder(oADoc)
'	MsgBox("oDocs.Count = " & oFileNames.Count, , "")
'	For Each oFileName In oFileNames
'		MsgBox("oFileName = " & oFileName,,"")
'	Next
	
	'now reposition rows to match needed order
	For Each oRow As BOMRow In oView.BOMRows
		Try
			oCD = oRow.ComponentDefinitions.Item(1)
			Dim oRowDoc As Document = oCD.Document
			'check what 'Index' this one is in our list
			'this retuns a 'zero' based Integer, so first item is at position zero
			oIndex = oFileNames.IndexOf(oRowDoc.FullFileName)
			'we don't want to use zero as an ItemNumber, so add 1 to each Index
			oRow.ItemNumber = (oIndex + 1)
		Catch oEx As Exception
			MsgBox("Encoutered Error trying to set new ItemNumber value to BOMRow." & vbCrLf & _
			oEx.Message & vbCrLf & oEx.StackTrace,,"")
		End Try
	Next
	'sort the BOM rows by their ItemNumber column values now
	'so that they will all be in proper order afterwards
	'<<< If your ItemNumber column has a different Title, then change this >>>
	oView.Sort("Item", True)
	'oView.Renumber(1, 1)
End Sub

Function GetFileNamesInOrder(oAsmDoc As AssemblyDocument) As List(Of String)
	'oAsmDoc.Activate
	Dim oList As New List(Of String)
	oTopNode = oAsmDoc.BrowserPanes.Item("Model").TopNode
	For Each oNode As BrowserNode In oTopNode.BrowserNodes
		Try
			If TypeOf oNode.NativeObject Is ComponentOccurrence Then
				Dim oOcc As ComponentOccurrence = oNode.NativeObject
				Dim oDoc As Document = oOcc.ReferencedDocumentDescriptor.ReferencedDocument
				If oList.Contains(oDoc.FullFileName) Then
					Continue For 'don't dig into any possible sub-components
				Else
					oList.Add(oDoc.FullFileName) 'add to the end
				End If
			ElseIf TypeOf oNode.NativeObject Is OccurrencePattern Then
				Dim oOccPatt As OccurrencePattern = oNode.NativeObject
				'process 'input' items only - others are just extra quantity items, not originals
				For Each oObj In oOccPatt.ParentComponents 'ObjectCollection
					If Not TypeOf oObj Is ComponentOccurrence Then Continue For
					Dim oPOcc As ComponentOccurrence = oObj
					Dim oODoc As Document = oPOcc.ReferencedDocumentDescriptor.ReferencedDocument
					If oList.Contains(oODoc.FullFileName) Then
						Continue For 'don't dig into any possible sub-components
					Else
						oList.Add(oODoc.FullFileName) 'add to the end
					End If
				Next
			End If
		Catch
		End Try
	Next
	Return oList
End Function

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

If you want and have time, I would appreciate your Vote(s) for My IDEAS 💡 or you can Explore My CONTRIBUTIONS

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 11 of 24

jjstr8
Collaborator
Collaborator

@checkcheck_master: Something else you may need to add to your code is a check for the LOD being set to master.  At least in Inventor 2019, if the LOD is not set to Master, neither the "structured" nor "parts only" are available and cause an exception if you try to access them.  I suspect that this is not the case for the newer Model States, but someone else would have to confirm.  Also in 2019, I can't find a way to change the name of BOM columns, so "Item" would always be the column name to sort on. 

Message 12 of 24

WCrihfield
Mentor
Mentor

Hi @jjstr8.  You are right about that.  Since I did not use LOD's that much, and still have not used the new ModelStates that heavily, I often forget to account for others possibly using them more heavily.  So, right near the top of the code, right after I set the initial value of the 'oADoc' variable, I injected a little bit of code I've used before to get around that possible issue.

Dim oADoc As AssemblyDocument = ThisApplication.ActiveDocument
If oADoc.ModelStateName <> "Master" Or _
	oADoc.ComponentDefinition.IsModelStateMember Or _
	oADoc.ComponentDefinition.IsModelStateFactory = False Then
	oADoc = oADoc.ComponentDefinition.FactoryDocument
End If

As you can see, I have obviously including more lines of code in there than are needed, but I wanted to show them here anyways, just to show the various things that can be checked (under the new 2022 ModelStates system).  Any one of these 3 checks would probably be good enough.  In earlier version of Inventor, using the LODs (LevelOfDetailRepresentations), there were some similar things we could check.  Instead of checking "ModelStateName", we could check AssemblyDocument.LevelOfDetailName.  The other two checks shown above for ModelStates did not exist for LOD's, but were more for the iAssembly system, which ModelStates partially took on the functionality of.  The two similar (but not the same) checks were AssemblyComponentDefinition.IsiAssemblyMember, and AssemblyComponentDefinition.IsiAssemblyFactory, then the two corresponding properties to get either the factory or member were AssemblyComponentDefinition.iAssemblyFactory, and AssemblyComponentDefinition.iAssemblyMember.

 

Also note, you probably want to define your variable for the AssemblyComponentDefinition (if you will be using it) after this point in the code, so that it is pointing to the right AssemblyDocument.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 13 of 24

checkcheck_master
Advocate
Advocate

Thank you all very much for the contribution.
The best thing would be if I could completely renumber the BOM items in the order of the browser pane.
In VBA I managed to get a neat enumeration of the assembly according to the browser pane.
I'm going to try and see if I can do that in iLogic and then link a numbering to it.
In any case, I have gained insight into how I can approach the items in the BOM.
If anyone has a method or tips, I'd really appreciate it.

0 Likes
Message 14 of 24

WCrihfield
Mentor
Mentor

I'm not sure what 'type' of enumeration you created, or what you mean by 'link a numbering to it', but I'm guessing you are thinking about a list, in which each element/position in the list can contain multiple pieces of data.  Something like a Dictionary, where each entry has a 'Key' and a 'Value', and the whole dictionary can be sorted.  Unfortunately most of those similar types of objects are just meant to hold 2 pieces of data in each entry, which can often be a little too limiting for more complex needs.  The next two most obvious alternative objects to use would be either a List of Lists, or an Array of arrays, but then you can't directly sort the main list or array by something that is within the secondary list or array, without a lot more code.  If you really wanted/needed a Dictionary type object that had a custom number of data blocks in each main item, you could create your own type of object using a Class block of code.  But then you would have to define all the code behind its Properties and Methods yourself, to suit your needs.  If your not familiar with a Class (link1, link2, link3), it is like the template or order form used to create new objects of that specific type, and defines what properties, methods, and events it has.  Just some food for thought.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 15 of 24

checkcheck_master
Advocate
Advocate

 

Thanks for your reaction WCrifield and the clear explanation.
I actually meant to say that I would like to see the complete assembly BOM renumbered instead of just the First Level like now.
The VBA rule I'm referencing creates the hierarchy but doesn't do numbering.
The challenge is to process the numbering in the list.
At the same time, I realize that this can certainly be a thing.

 

Does that answer your question?
And does that perhaps shed a different light on the situation?

0 Likes
Message 16 of 24

checkcheck_master
Advocate
Advocate

See attached the VBA code I'm talking about in the hopes that it will give you a better understanding of what I'm talking about.
I've tried to remove the unnecessary from it, it works for me, I hope for you too.
Btw, this code is using 'Dim oOcc As ComponentOccurrence - For Each oOcc In Occurrences' etc., instead of traverse the Browser Pane, but off course the principle remains the same.

 

0 Likes
Message 17 of 24

WCrihfield
Mentor
Mentor

WCrihfield_5-1642448895960.png

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 18 of 24

WCrihfield
Mentor
Mentor

Sorry...I was looking for a simple emoji for mind blown, but came across that image and I just couldn't resist.

Wow.  I just opening, then scrolled down through that really long text file full of code, and it just really blew my mind at first.  You have obviously got a ton more work into this project that I had imagined.  Good for you if you can keep up with all of that in one place.  I think I would have separated each of those good size sub routines out into other modules, in an attempt to keep my sanity. 😂😅

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 19 of 24

checkcheck_master
Advocate
Advocate

Nice picture 🙂
It is easier to explain in text, you may have seen the folders that are created in the 'Production Output' folder.
The goal is to split the main assembly and divide it into these folders.
I saw no other option to fill a batch of lists to gain an overview.
I'm not a programmer and since March last year with IV so it took me a while to get the structure through without being bothered by too much knowledge so to speak.
As said before, I would like to bring it to iLogic, among other things after reading the articles written by Jelte de Jong.
If the filtering could be shorter or the code could be better anyway, I would be happy to receive tips about it.

0 Likes
Message 20 of 24

jjstr8
Collaborator
Collaborator

I'll agree with @WCrihfield for the mind blown, but in the sense that I can't figure out how we got from renumbering a parts list to generating reports.  All of that aside, here's some sample code that generates an ordered and numbered list of an assembly's components and sub-components.  Each item in the list is a string with the full filename and an item number as a prefix separated by the pipe character.  Since the pipe character isn't legal in filenames, it's easy to split the string to get each part.  Run it and check the iLogic Log to see if the results are of use to you.  With the added requirement of "All levels" in your BOM renumbering, it gets a little messy working through BOM and trying to find item in the list and determine its new item number.  Since you're new to programming, I avoided using more complicated techniques like custom class collections and LINQ searches.

 

Public Sub Main()
    Dim asmDoc As AssemblyDocument = ThisDoc.Document
    Dim topNode = asmDoc.BrowserPanes.Item("Model").TopNode
	Dim docs As New List(Of String)
    GetBrowserNodes(topNode, docs, 0, "", 0)
End Sub

Public Function GetBrowserNodes(node As BrowserNode, docs As List(Of String), parentLevel As Integer, parentItemNumber As String, lastNumber As Integer) As Integer
	Dim newOccurrence As ComponentOccurrence
	Dim itemNumber As Integer = 1
	Dim itemString As String
	Dim splitChar As Char() = {"|"c}
	Dim splitString As String()
	Dim duplicate As Boolean
	Dim recurseResult As Integer
	
	'Check if we're in a pattern or folder so the item number continues sequence
	If (lastNumber > 0) Then
		itemNumber = lastNumber
	End If

    For Each subNode As BrowserNode In node.BrowserNodes
        Try
            If (TypeOf subNode.NativeObject Is ComponentOccurrence) Then
				newOccurrence = subNode.NativeObject
				If (parentLevel = 0) Then
					itemString = itemNumber.ToString()
				Else
					itemString = parentItemNumber & "." & itemNumber.ToString()
				End If
				
				'Check the list for duplicates
				duplicate = False
				If (docs.Count > 0) Then
					For i = 0 To docs.Count - 1
						splitString = docs(i).Split(splitChar)
						If ((splitString(1) = newOccurrence.Definition.Document.FullFileName) And docs(i).StartsWith(parentItemNumber)) Then
							'Logger.Info("Duplicate: " & newOccurrence.Definition.Document.FullFileName)
							duplicate = True
							Exit For
						End If					
				Next
				End If
				
				'Add the document name to the list with item numbering as a prefix
				If Not (duplicate) Then
					docs.Add(itemString & "|" & newOccurrence.Definition.Document.FullFileName)
					Logger.Info(itemString & "|" & newOccurrence.Definition.Document.FullFileName)
					
					'Drill into sub-assemblies
					If (subNode.NativeObject.DefinitionDocumentType = DocumentTypeEnum.kAssemblyDocumentObject) Then
						'Logger.Info("Recursing " & newOccurrence.Definition.Document.DisplayName)
						recurseResult = GetBrowserNodes(subNode, docs, parentLevel + 1, itemString, 0)
					End If
					itemNumber = itemNumber + 1
				End If
				Continue For
            End If
			
			'Look for sub-nodes that still belong to the parent level
            If ((TypeOf subNode.NativeObject Is OccurrencePattern) Or (TypeOf subNode.NativeObject Is OccurrencePatternElement) Or _
				(TypeOf subNode.NativeObject Is BrowserFolder)) Then
				'Logger.Info("Recursing pattern or folder")
                recurseResult = GetBrowserNodes(subNode, docs, parentLevel, parentItemNumber, itemNumber)
				If (recurseResult > 0) Then
					itemNumber = recurseResult
				End If
            End If
        Catch ex As Exception
			MessageBox.Show(ex.Message)
        End Try
    Next
	
	'If we were in a pattern or folder, return the last item number used
	If (lastNumber > 0) Then
		Return itemNumber
	Else
		Return 0
	End If
	
End Function

 

 

0 Likes