Exporting BOMs of Top level and All Subassemblies to Folder

Exporting BOMs of Top level and All Subassemblies to Folder

e_frissell
Advocate Advocate
592 Views
6 Replies
Message 1 of 7

Exporting BOMs of Top level and All Subassemblies to Folder

e_frissell
Advocate
Advocate

Been working on writing an iLogic script for a number of hours now and tried googling but couldn't find anyone who has exported their assembly and sub-assembly BOMs via the same iLogic script and at this point I'm stumped

 

Tried to write the simplest script I could to accomplish this however I get an error on the ActiveDocument and pretty much can't go any further.  I've tried writing the loop about 10 different ways using ThisDoc.Document to keep the top level document while re-defining the active document, or continually trying to redefine what is the current active document, or opening the refdoc...just can't get anything to work and seeing if someone might know what the solution to this is

 

Sub Main()
	
Dim oDoc As Document = ThisApplication.ActiveDocument
Dim refDocs As Document = ThisDoc.Document
Dim refDoc As Document
Dim projectNumber As String = Left(oDoc.DisplayName, 4)

Dim oFolder As String = System.Environment.GetFolderPath(Desktop) & "\Export BOMs\" & projectNumber & "\"

Call ExportBom(oDoc, oFolder)

For Each refDoc In refDocs.AllReferencedDocuments
	If refDoc.DocumentType = kAssemblyDocumentObject Then 
		refDoc.Activate
		Call ExportBom(oDoc, oFolder)
	End If
Next

End Sub

Function ExportBom(oDoc As Document, oFolder As String)
	
	Dim oPath As String = oFolder & oDoc.DisplayName & ".xls"
	ThisBOM.Export("Structured", oPath, kMicrosoftExcelFormat)
	
End Function

 

0 Likes
Accepted solutions (2)
593 Views
6 Replies
Replies (6)
Message 2 of 7

WCrihfield
Mentor
Mentor
Accepted solution

Hi @e_frissell.  I see several problems in this code, but perhaps one of the main ones is within your 'ExportBom' Function.  First of all, a Function is meant to 'return' something to the routine that called it to run, so it should have an 'As Type' at the end of the Function definition line, just like if you declared a variable, and set its Type.  If you do not need to 'return' anything from a routine, then use a Sub routine, instead of a Function.  Next, you are using the iLogic 'Rule Object' named 'ThisBOM', which as you can see, does not allow you to specify an assembly document for it to focus its attention on, so it will always be focused on the main assembly that was 'active' when you started the rule, instead of being focused on the 'oDoc' variable you passed into that Function.

 

When you start an iLogic rule while an assembly is 'active', it will remain the 'active' document the entire time that rule is running.  If you want some of your code to focus on a different document, then you must capture the 'other' document to a variable, then use that variable to do read/write to that Document.  For instance, within your loop of referenced documents, you should be using the 'refDoc' variable for the stuff you are doing within that variable, because that is the variable you created for that iteration.  However, you should not have set the value of that variable outside of that iteration.  Each iteration should be setting a new value to that variable...namely the next referenced document in the referenced documents collection.  You should not need to activate each of those referenced documents in order to do stuff with them, unless you are doing something 'visual' with them, such as capturing screenshots or manipulating their views in some way.

 

So, instead of using the iLogic 'ThisBOM' rule object, which is OK for working on the active document only, you should be using the Inventor API method for exporting BOM's.

BOMView.Export 

...Where the BOMView object is obtained from AssemblyDocument.ComponentDefinition.BOM.BOMViews collection of the current document you are working with in that moment of the code.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 3 of 7

Michael.Navara
Advisor
Advisor
Accepted solution

Hi @e_frissell 

I agree with @WCrihfield and I just want to add some additional information.

You can't export BOM using ThisBOM object from another document, because ThisBOM returns reference to the BOM of the document in which context the rule is running. You can use BOMView.Export mentioned above, or you need to split the code to two external rules.

If you need it, you can pass arguments from MainRule to ExportBom rule using args variable.

 

It is recommended to ensure the output folder exists, before you try to export anything to it. Line 5.

 

 

MainRule.iLogicVb

Dim topAsm As AssemblyDocument = ThisDoc.Document
Dim projectNumber As String = Left(topAsm.DisplayName, 4)

Dim outputFolder As String = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) & "\Export BOMs\" & projectNumber & "\"
System.IO.Directory.CreateDirectory(outputFolder)


Dim args As NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap()
args.Value("Folder") = outputFolder

For Each referencedAsm As AssemblyDocument In topAsm.AllReferencedDocuments.OfType(Of AssemblyDocument)
	iLogicVb.Automation.RunExternalRuleWithArguments(referencedAsm, "ExportBom", args)
Next

 

ExportBom.iLogicVb

Dim oFolder As String = RuleArguments.Value("Folder")
Dim asm As AssemblyDocument = ThisDoc.Document
Dim oPath As String = oFolder & asm.DisplayName & ".xls"
Logger.Debug(oPath)
ThisBOM.Export("Structured", oPath, FileFormatEnum.kMicrosoftExcelFormat)

 

 

Message 4 of 7

WCrihfield
Mentor
Mentor

Using your own external rule just for exporting BOM's is a good idea, and is relatively efficient, because it avoids the same (or similar) block of code in multiple places (within multiple other rules).  However, if you only ever use that one rule for exporting BOM's, then a separate, but internal Sub routine or Function may be easier for you to manage for now.

 

Below is an example combined iLogic rule code I just typed up which you could review or test with.  It may look a bit complicated, but there are lots of options when exporting BOM's, so it includes many of those as inputs into the separate routine, and more could be added, if needed.  Plus, I wanted it to be relatively dynamic and error free, so that you can either just use the default options/values, or you can specify the details, as needed.  You can change or develop it further, as needed.  When using the simplistic iLogic tool, you do not get the chance to set all those options.

Sub Main
	Dim oDoc As Inventor.Document = ThisDoc.Document
	ExportBOM(oDoc)
	For Each oRefDoc As Inventor.Document In oDoc.AllReferencedDocuments
		ExportBOM(oRefDoc)
	Next
End Sub

Sub ExportBOM(oDoc As Inventor.Document, _
	Optional eBOMViewType As Inventor.BOMViewTypeEnum = BOMViewTypeEnum.kStructuredBOMViewType, _
	Optional sFullFileName As String = Nothing, _
	Optional eFileFormat As Inventor.FileFormatEnum = FileFormatEnum.kMicrosoftExcelFormat, _
	Optional bAllLevels As Boolean = False)
	
	If (oDoc Is Nothing) OrElse (Not TypeOf oDoc Is AssemblyDocument) Then Return
	If eBOMViewType = BOMViewTypeEnum.kModelDataBOMViewType Then Return
	Dim oADoc As AssemblyDocument = oDoc
	Dim oBOM As Inventor.BOM = oADoc.ComponentDefinition.BOM
	'Dim oOptions As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap
	If eBOMViewType = BOMViewTypeEnum.kPartsOnlyBOMViewType Then
		Try : oBOM.PartsOnlyViewEnabled = True : Catch : End Try
	ElseIf eBOMViewType = BOMViewTypeEnum.kStructuredBOMViewType Then
		Try : oBOM.StructuredViewEnabled = True : Catch : End Try
		If bAllLevels = True Then
			Try : oBOM.StructuredViewFirstLevelOnly = False : Catch : End Try
		Else
			Try : oBOM.StructuredViewFirstLevelOnly = True : Catch : End Try
		End If
		'oOptions.Add("Table Name", System.IO.Path.GetFileNameWithoutExtension(oADoc.FullFileName))
	End If
	Dim oBOMView, oBOMViewToExport As BOMView
	For Each oBOMView In oBOM.BOMViews
		If oBOMView.ViewType = eBOMViewType Then oBOMViewToExport = oBOMView
	Next
	If sFullFileName Is Nothing OrElse sFullFileName = String.Empty Then
		If oADoc.FileSaveCounter = 0 Then Return
		'could check 'eFileFormat' here, then set as appropriate
		sFullFileName = System.IO.Path.ChangeExtension(oADoc, ".xlsx")
	End If
	Try
		oBOMViewToExport.Export(sFullFileName, eFileFormat)
		'oBOMViewToExport.Export(sFullFileName, eFileFormat, oOptions)
	Catch
		Logger.Error("Error exporting BOM of: " & oADoc.DisplayName)
	End Try
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)

0 Likes
Message 5 of 7

e_frissell
Advocate
Advocate

Oh duh, both you guys are right, I should have caught that ThisBom would only run within the document which the rule was run as opposed to the active document...

 

I rewrote the function as a sub then, instead of passing oDoc to the sub, changed that to refDoc.  The hope was that I could set refDoc to become Inventor's active document but in the end it looks like that was unnecessary.  Thanks for the help.  Ended up being pretty simple after your recommendation @WCrihfield to look at the API export - I thought it was going to be a little more complicated to go from an AssemblyDocument to a ComponentOccurrence as well but turns out it was pretty simple

 

 

Sub Main()
	
	Dim oDoc As Document = ThisApplication.ActiveDocument
	Dim refDocs As Document = ThisDoc.Document
	Dim refDoc As Document
	Dim projectNumber As String = Left(oDoc.DisplayName, 4)
	
	Dim oFolder As String = System.Environment.GetFolderPath(Desktop) & "\Export BOMs\" & projectNumber & "\"
	
	refDoc = oDoc
	
	Call ExportBom(refDoc, oFolder)
	
	For Each refDoc In refDocs.AllReferencedDocuments
		If refDoc.DocumentType = kAssemblyDocumentObject Then 
			Call ExportBom(refDoc, oFolder)
		End If
	Next

End Sub

Sub ExportBom(refDoc As AssemblyDocument, oFolder As String)
	
	Dim oPath As String = oFolder & refDoc.DisplayName & ".xls"
	Dim oBom As BOM = refDoc.ComponentDefinition.BOM
	Dim bomType As Integer = refDoc.ComponentDefinition.BOMStructure
	
	If bomType = 51971 Or bomType = 51973 Or bomType = 51972 Or bomType = 51975 Then GoTo Skip :
		
	
	oBom.StructuredViewEnabled = True
	oBom.StructuredViewFirstLevelOnly = True
	Dim oStView As BOMView = oBom.BOMViews.Item("Structured")
	oStView.Export(oPath, kMicrosoftExcelFormat)
	
Skip :

	
End Sub

'kDefaultBOMStructure	51969	The Default Structure type.
'kInseparableBOMStructure	51974	The inseparable Structure type.
'kNormalBOMStructure	51970	The normal Structure type.
'kPhantomBOMStructure	51971	The phantom Structure type.
'kPurchasedBOMStructure	51973	The purchased Structure type.
'kReferenceBOMStructure	51972	The reference Structure type.
'kVariesBOMStructure	51975	The Structure type varies amongst references.

 

 

0 Likes
Message 6 of 7

e_frissell
Advocate
Advocate

Thanks for that, there's a lot in there to figure out!  I swear if you ever wrote a book going over all the help you've given out here I'd buy it in a second

 

I've  never taken a course in programming however I've got about 5 or 6 years into some very disorganized learning of VBA and VB.net, and really like VBA for what it is, but learning how to run iLogic has been a bit of a different animal, especially in regards to organization where I'd love to have more rules that pass variables between them rather than contain redundant subs...but I suppose the alternative is that making one change can break a bunch of different rules which would cause it's own headaches.

 

Anyways, looked through this code and saw the Optional parameter?  I'm curious, what does an optional parameter do?

0 Likes
Message 7 of 7

WCrihfield
Mentor
Mentor

Optional input parameters are just that, optional.  It is a good way to set 'defaults', but still have the control to change the values of those defaults, if you really want to, without re-writing the code within the routine.  If you usually always use certain settings and options the same way, or with the same values, but do occasionally need to change them up a bit, you can make those specifications optional, with default values.  Then, when you call that routine to run, if you are not doing anything special, you do not need to specify those values, and the default values will be used.  However, when you call that routine to run, and need to change one of those specifications, you can, by including just the optional inputs needed to make the changes needed, without really needing to specify all of the optional ones.  You just need to make sure you keep the positions of those optional inputs.  For example, if there is one required input, then 3 optional inputs, and you want only want to specify the first required one, then only need to specify the last of the 3 optional ones, you would include 2 commas after the first input, with nothing between them, then specify the last one.  Sometimes one of the earlier optional ones is required before a later optional input will be used though.  Such as in this case, if you specified parts only view, then the last one asking for all levels would not be used, because it only applies to the structured BOM view.

I may have overdone the optional stuff in that example, though.  I get carried away sometimes when typing new stuff up quickly for a forum response. 😅

 

In this case, I made the file name optional, without really setting an appropriate default, simply because I usually export my BOM's to the same location, and same file name, as the assembly file.  Later in that routine, I check if that variable is still Nothing, or is empty, and if so, I set its value that way.  But if the assembly itself has not been saved yet, it will not be able to obtain the file path, or file name, so that will fail, without an error.  Not really a good way to do it, but just a possibility.  It's not very often I try to export a BOM from an assembly that has not been saved yet, because they usually take a long time to built-up, and I do not want to risk loosing progress.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes