Hi!
Does someone has an iLogic rule, to run through the whole assembly and connect all filenames of assemblies to corresponding displaynames and export this to excel?
Thanks,
Nick
Solved! Go to Solution.
Solved by WCrihfield. Go to Solution.
Hi @nick5MWHR. Can you show an example of what the data within the resulting Excel file should look like, and how it should be laid out. The Document.DisplayName property is Read/Write, so it can be changed. It usually has a default value, based on document type, when the document is still new, and has not been saved yet, but then it usually defaults to the file name, after the document gets saved. However, I have seen it sometimes include the file extension, and other times not include the file extension, so it can be slightly unpredictable. Why do you need to write out a list of file names and display names? Will this only include files that represent assemblies, or will it also include part files?
Wesley Crihfield
(Not an Autodesk Employee)
Hi Wesley,
Thanks for the reply.
I've included an image on what the output should look like. I know that the displayname can be changed.
Our idea is the following:
- AssemblyX is (for example) a conveyor, but at customerY the conveyor is conveyor1 but at CustomerZ the conveyor is conveyor10. Or maybe within one project we use the assembly multiple times and we want to rename these to Conveyor1, Conveyor2 etc, without changing the (internal) assembly-filename.
the file extension doesn't need to be included, we only want to recieve the assembly filename and the corresponding displayname, after that we can make the magic happen within excel.
Something import to notice, is that it needs to run also through all the subassemblies, and the subassemblies of these etc. All assemblies should be checked, also on deeper levels. Something nice to be added is how deep this assembly is, see also included image.
Nick
There is still something confusing here. You do not seem to be talking about the Document.DisplayName property here, because the same document can not have multiple values for that one property. So, I assume you are talking about the name shown in the model browser tree of the main assembly. Those names usually end with something like ":1", or ":2", and so on, unless renamed on purpose. This is the value we get back from the ComponentOccurrence.Name property, not a property named DisplayName. I also do not understand the level values. Where would 1.2 or 1.4.1 come from. It seems to me like it should just be Level 1, Level 2, Level 3, and so on, no partial levels. All components found at top level of main assembly would be at Level 1. Then all components found at second level down would be at Level 2, and so on.
Wesley Crihfield
(Not an Autodesk Employee)
Hi @nick5MWHR. Here is an iLogic rule example that does a task very similar to what you are asking for, but since I did not fully understand some of it, I can only hope this will get you most of the way to where you wanted to go. Then maybe you can modify it further, as needed, to meet your needs more closely.
Sub Main
Dim oADoc As AssemblyDocument = TryCast(ThisDoc.Document, Inventor.AssemblyDocument)
If oADoc Is Nothing Then Logger.Debug(iLogicVb.RuleName & " exited (no AssemblyDocument obtained)") : Return
Dim oADef As AssemblyComponentDefinition = oADoc.ComponentDefinition
Dim oOccs As ComponentOccurrences = oADef.Occurrences
oData = New List(Of List(Of String)) 'to initialize it
RecurseComponents(oOccs, 1, AddressOf ProcessComponent)
If oData.Count > 0 Then
Dim sExcelFile As String = "C:\Temp\Assembly file & component names with levels.xlsx"
Dim sSheet As String = "Sheet1"
GoExcel.Open(sExcelFile, sSheet)
GoExcel.DisplayAlerts = True
Dim iFirstDataRow As Integer = 2
Dim iRow As Integer = iFirstDataRow
GoExcel.CellValue("A1") = "File Name"
GoExcel.CellValue("B1") = "Component Name"
GoExcel.CellValue("C1") = "Level"
For i As Integer = 0 To oData.Count - 1
Dim oDataEntry As List(Of String) = oData.Item(i)
'Dim sFileName As String = oDataEntry.Item(0)
'Dim sCompName As String = oDataEntry.Item(1)
'Dim sLevel As String = oDataEntry.Item(2)
GoExcel.CellValues("A" & iRow.ToString, "C" & iRow.ToString) = oDataEntry
iRow = iRow + 1
Next i
GoExcel.Save
End If
'MsgBox("This rule's work has finished.",,"Job Is Done!")
End Sub
Dim iLevel As Integer
Dim oData As List(Of List(Of String))
Sub RecurseComponents(oComps As ComponentOccurrences, iLevel As Integer, ComponentProcess As Action(Of ComponentOccurrence, Integer))
If oComps Is Nothing OrElse oComps.Count = 0 Then Return
For Each oComp As ComponentOccurrence In oComps
ComponentProcess(oComp, iLevel)
If oComp.Suppressed = False AndAlso _
oComp.DefinitionDocumentType = DocumentTypeEnum.kAssemblyDocumentObject Then
RecurseComponents(oComp.SubOccurrences, iLevel + 1, ComponentProcess)
End If
Next oComp
End Sub
Sub ProcessComponent(oComp As ComponentOccurrence, iLevel As Integer)
If oComp Is Nothing OrElse oComp.Suppressed Then Return
If TypeOf oComp.Definition Is VirtualComponentDefinition Then Return
If TypeOf oComp.Definition Is WeldsComponentDefinition Then Return
Dim sOccName As String = oComp.Name
Dim sFDN As String = oComp.ReferencedDocumentDescriptor.FullDocumentName
Dim sFFN As String = ThisApplication.FileManager.GetFullFileName(sFDN)
Dim sFileName As String = System.IO.Path.GetFileNameWithoutExtension(sFFN)
Dim oDataEntry As New List(Of String) From {sFileName, sOccName, iLevel.ToString}
oData.Add(oDataEntry)
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)
Hee Wesley
You are right. I mean the name shown in the model browser tree (ComponentOccurrence name)
as for the levels. The reason I indicate this in the way in my example is because it is also displayed this way in a BOM export if you select structured on all levels. This way we also know which sub-assemblies belong to which assembly.
If the order of the tree structure from the model browser does not change when running the rule, then the way you propose is also fine for me.
Nick
Hi Wesley
Thank you very much for creating the iLogic rule.
I quickly checked whether the rule works.
There seems to be an error when opening Excel, but this is something we have done before, so we will figure it out later. I have other things to do first, but I will continue with this soon. I will keep you informed about the progress.
Nick
Hi @nick5MWHR. That earlier code example simply iterated down through all levels of assembly components, capturing component name, referenced document file name, and what primary level the component was on in the overall structure of the assembly. However, since you seem to prefer how the structured BOM with all levels included does its Item Number values, I decided that we could do this process by iterating through the structured view of the BOM directly, then just use the Item Number property of the BOMRow for the 'level'. So, below is a very similar code example that is recursively iterating through the rows in a BOM view, instead of recursively iterating through assembly components.
Just remember, down around Lines 19 & 20 is where the Excel file and sheet name are being specified, so you will most likely need to change those, as needed. This code does not create a new Excel file...just opens one, then writes this data into it. So, if that Excel file does not already exist, this will not work. And if the Excel file does exist, make sure the sheet name is correct. And, if the file and sheet names are OK, but the file already contained other, older data, this code will not clear that old data before it writes the new data into it. If old data exists, it will be overwritten. However if the old data had 400 lines of data, and the new data being written to it only has 200 lines of data, the result will be the first 200 lines being new data, but the other 200 below that still being old data. Just something to keep in mind. Using the iLogic GoExcel tools is a trade-off of (simplicity & ease of use) vs (more dynamic control).
Sub Main
Dim oADoc As AssemblyDocument = TryCast(ThisDoc.Document, Inventor.AssemblyDocument)
If oADoc Is Nothing Then Logger.Debug(iLogicVb.RuleName & " exited (no AssemblyDocument obtained)") : Return
Dim oADef As AssemblyComponentDefinition = oADoc.ComponentDefinition
Dim oBOM As Inventor.BOM = oADef.BOM
oBOM.StructuredViewEnabled = True
oBOM.StructuredViewFirstLevelOnly = False
Dim oStrBOMView As BOMView = Nothing
For Each oBOMView As BOMView In oBOM.BOMViews
If oBOMView.ViewType = BOMViewTypeEnum.kStructuredBOMViewType Then
oStrBOMView = oBOMView
Exit For
End If
Next oBOMView
If oStrBOMView Is Nothing Then Return
oData = New List(Of List(Of String)) 'to initialize it
RecurseBOMRows(oStrBOMView.BOMRows, AddressOf ProcessBOMRow)
If oData.Count > 0 Then
Dim sExcelFile As String = "C:\Temp\Assembly file & component names with levels.xlsx"
Dim sSheet As String = "Sheet1"
GoExcel.Open(sExcelFile, sSheet)
GoExcel.DisplayAlerts = True
Dim iFirstDataRow As Integer = 2
Dim iRow As Integer = iFirstDataRow
GoExcel.CellValue("A1") = "File Name"
GoExcel.CellValue("B1") = "Component Name"
GoExcel.CellValue("C1") = "Level"
For i As Integer = 0 To oData.Count - 1
Dim oDataEntry As List(Of String) = oData.Item(i)
'Dim sFileName As String = oDataEntry.Item(0)
'Dim sCompName As String = oDataEntry.Item(1)
'Dim sLevel As String = oDataEntry.Item(2)
GoExcel.CellValues("A" & iRow.ToString, "C" & iRow.ToString) = oDataEntry
iRow = iRow + 1
Next i
GoExcel.Save
End If
'MsgBox("This rule's work has finished.",,"Job Is Done!")
End Sub
Dim oData As List(Of List(Of String))
Sub RecurseBOMRows(oBOMRows As BOMRowsEnumerator, BOMRowProcess As Action(Of Inventor.BOMRow))
If oBOMRows Is Nothing OrElse oBOMRows.Count = 0 Then Return
For Each oBOMRow As Inventor.BOMRow In oBOMRows
BOMRowProcess(oBOMRow)
If oBOMRow.ChildRows IsNot Nothing AndAlso oBOMRow.ChildRows.Count > 0 Then
RecurseBOMRows(oBOMRow.ChildRows, BOMRowProcess)
End If
Next oBOMRow
End Sub
Sub ProcessBOMRow(oBOMRow As Inventor.BOMRow)
If oBOMRow Is Nothing Then Return
Dim oCD As Inventor.ComponentDefinition = Nothing
Dim oRowDoc As Inventor.Document = Nothing
Dim oOcc As ComponentOccurrence = Nothing
Try : oCD = oBOMRow.ComponentDefinitions.Item(1) : Catch : End Try
Try : oRowDoc = oCD.Document : Catch : End Try
Try : oOcc = oBOMRow.ComponentOccurrences.Item(1) : Catch : End Try
If (oCD Is Nothing) OrElse (oRowDoc Is Nothing) OrElse (oOcc Is Nothing) Then Return
Dim sFileName As String = System.IO.Path.GetFileNameWithoutExtension(oRowDoc.FullFileName)
Dim sOccName As String = oOcc.Name
Dim sLevel As String = oBOMRow.ItemNumber
Dim oDataEntry As New List(Of String) From {sFileName, sOccName, sLevel}
oData.Add(oDataEntry)
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)
Hi,
Sorry for the late response, but we tested the code today and it works, however, we’ve got a few questions.
Thanks again!
Nick
Hi @WCrihfield ,
Again thanks for your reply!
We have modified your code to better fit our application. And now we can get most of the data we need, so that is great! Unfortunately we can’t quite get the level functionality to work as we need it. Currently we can get the level of the component in relation to the main assembly. However, we also need to get the level of the component in relation to the sub-assembly. So for example, if we have a main assembly that has a few assemblies, which also have assemblies we would need to get the level as follows: (assembly – level)
Main assembly – 0
Sub assembly – 0.1
Sub-Sub Assembly – 0.1.1
Sub assembly – 0.2
Sub-Sub Assembly – 0.2.1
Part 1 – 0.2.1.1
Part 2 – 0.2.1.2
Sub Assembly – 0.3
Etc.
(The . seperator is not strictly neccesary, a string of int as follows is also fine (0211). )
The code from your last post does work for this, however it only works once per unique part/assembly. In our case assemblies and parts can be used more then once.
I have attached the results from the excel sheet as they are now, and have added the requested syntax behind it. If you could help us that would be great!
Thanks in advance for your time and expertise!
Hi @nick5MWHR. You said that you modified to the code I posted to better suit your needs, but then it sounds like you are requesting that I modify the code some more to fix the level specification data. Could you maybe post the modified version of the code you have here, either as a text file, or within a code window, so that if I do post an updated code example, it will already have your modifications in it. I am not super sure how that level spec would be helpful, because how would you know which number in the sequence represents which sub assembly, when there are 4 or 5 digits in the sequence. This is just a thought, but maybe a comma separated String which includes either the file names (from main to current) or component names (from top level to current) would be more helpful and informational in letting you know precisely what the assembly structure above a component is, and which sub assemblies are involved in it. We can get this information much easier also, because there is actually a property for it (ComponentOccurrence.OccurrencePath). That property of the component will return a ComponentOccurrencesEnumerator type collection which contains every ComponentOccurrence object in this component's path, starting from the top level component, and including the current component, in order. That collection could be iterated through, in order, and the component names (or referenced file names) could be recorded into either a comma separated String, or an Array of Strings, whichever would work better later. If simply iterating components recursively with a Sub routine, I can not think of a good way (right now) to keep track of the levels numerically in the custom way you are wanting. Maybe if each component had an 'Attribute' or 'instance property' where this numerical value was assigned in a way that each one was unique per level, or something like that, but that would involve a whole extra level of preparations ahead of time, and complication.
Below are 2 example iLogic rules to test this property.
Dim oPickedOcc As ComponentOccurrence = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kAssemblyLeafOccurrenceFilter, "Select a Component.")
If oPickedOcc Is Nothing Then Return
Dim oOccPath As ComponentOccurrencesEnumerator = oPickedOcc.OccurrencePath
If oOccPath IsNot Nothing AndAlso oOccPath.Count > 0 Then
For i As Integer = 1 To oOccPath.Count
Logger.Info(oOccPath.Item(i).Name)
Next i
End If
...or like this, where a custom Function is used to convert the 'path' into an Array of Strings.
Sub Main
Dim oPickedOcc As ComponentOccurrence = ThisApplication.CommandManager.Pick(SelectionFilterEnum.kAssemblyLeafOccurrenceFilter, "Select a Component.")
If oPickedOcc Is Nothing Then Return
Dim oOccPath() As String = GetComponentPath(oPickedOcc)
If oOccPath IsNot Nothing AndAlso oOccPath.Length > 0 Then
For i As Integer = LBound(oOccPath) To UBound(oOccPath)
Logger.Info(oOccPath(i))
Next i
End If
End Sub
Function GetComponentPath(oOcc As ComponentOccurrence) As String()
If oOcc Is Nothing Then Return Nothing
Dim oOccPathOccs As ComponentOccurrencesEnumerator = oOcc.OccurrencePath
Dim oOccPath(oOccPathOccs.Count - 1) As String
For i As Integer = 1 To oOccPathOccs.Count
oOccPath(i-1) = oOccPathOccs.Item(i).Name
Next
Return oOccPath
End Function
Wesley Crihfield
(Not an Autodesk Employee)
Hi @WCrihfield , thank you for your quick reply!
You are correct in assuming that the component names would be more helpful than the numbers as we had previously suggested. I just tested both your examples and the latest of the 2 is exactly what we need. I just copied them to a string with a “ - ” as separator. A comma would also be fine.
I tried to incorporate the function in our current code, but I cannot seem get it to work for all components. Could you perhaps help us integrate this function in the code? It would be much appreciated!! I have attached the code that we currently have below. I had added more filters yesterday, but with the assembly’s I am currently using, they are not of use so I left those out.
Thanks in advance!
Hi @WCrihfield , thank you so much for you helped, I've managed to make it work, thanks to you!
I added the following code to make add a seperate sign, which I'll later use in excel to seperate the names. I've added multiple functions to the code, but I'm getting stuck at a certain part... Maybe you could take a look at my new post?
If oOccPath IsNot Nothing AndAlso oOccPath.Length > 0 Then
For i As Integer = LBound(oOccPath) To UBound(oOccPath)
'Logger.Info(oOccPath(i))
If i >= 1 Then
sOccPath = sOccPath & ";" & (oOccPath(i))
Else
sOccPath = sOccPath & (oOccPath(i))
End If
'MsgBox(oOccPath(i).ToString)
Next i
End If
Hi @nick5MWHR. Looks like I'm a little late responding here. I was away on vacation most of last week, and just got back to work yesterday. I'm only active on here while at work, so sometimes I get too many things going on in my head at the same time, and forget about one (or more) forum topics that I had previously been involved with in the forums. Anyways, here is a slightly edited version of that last block of code. The extra set of () characters around oOccPath(i) was not necessary, and checking the Index number did not seem like the most intuitive way to check for first entry, so this example just checks if the variable's value is an 'empty' String, and if so, just sets its value to the current component's name. I added the variable declaration in there, just for clarity, and to help it not throw errors while I was editing it.
Dim sOccPath As String = ""
If oOccPath IsNot Nothing AndAlso oOccPath.Length > 0 Then
For i As Integer = LBound(oOccPath) To UBound(oOccPath)
If sOccPath = "" Then
sOccPath = oOccPath(i)
Else
sOccPath = sOccPath & ";" & oOccPath(i)
End If
Next i
End If
Wesley Crihfield
(Not an Autodesk Employee)
Can't find what you're looking for? Ask the community or share your knowledge.