Hey all,
We have a client that has requested that our shop drawings follow a standard that is slightly different from the one we're familiar with.
Each part file for the project will get it's own drawing and special qty field.
The QTY field will list the number of instances per assembly, and the total number per project. I think this will look something like this.
A1001(6), A1007(2), A1015(3)
TOTAL QTY: 11
My intention it to run an iLogic code from the top level assembly that dives down and adds the QTY's for each part as a custom iProperty that can then be linked in the drawing.
I found this bit of code here that is close to what I would need, but doesn't differentiate sub assembly quantities.
http://forums.autodesk.com/t5/inventor-forum/multiple-sub-assemblies-in-a-parts-list/td-p/4342872
Sub Main () If iProperties.Value("Project", "Project")="" oProj = InputBox("Change Project Name", "Project Name", iProperties.Value("Project", "Project")) iProperties.Value("Project", "Project")=oProj End If 'get the project number of this assembly and use it to build the name of a custom property, i.e. "4100_QTY" customPropertyName = CStr(iProperties.Value("Project", "Project")) & "_QTY" oQ=MessageBox.Show(customPropertyName, "Project Number",MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question) If oQ=vbYes oDone() ElseIf oQ=vbNo Call oProject ElseIf oQ=vbCancel MessageBox.Show("Cancelled", "Completed", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1) Return End If End Sub Sub oDone oCompDef = ThisDoc.Document.ComponentDefinition openDoc = ThisDoc.Document customPropertyName = CStr(iProperties.Value("Project", "Project")) & "_QTY" 'Let's open the document that inventor has in focus right now 'Let's check to make sure that it's an assembly document. 'We don't want to run this on just a part document as it - 'will have none of the required information! If openDoc.DocumentType = kAssemblyDocumentObject Then 'If it is an assembly document, then we need to get a collection 'of all the documents that are associated to it. 'the following line actually does that, while at the time time 'starts the loop to look at each one of those documents. 'imagine that it's written out in this way : 'For each Document file in the collection of ALL the document 'files that are in this open document, do the stuff below... For Each docFile In openDoc.AllReferencedDocuments 'First thing first, we need to make a string that can represent the 'document that we're working on. This way it will be easier to 'call on that document when we want to make changes to the iproperties. 'this is no different than if you just typed out : ' 'iProperties.Value("Part_Name_Here.ipt", "Project", "Authority") ' 'But because we are doing this dynamically, we don't have to type that 'about line out for every single part that is in the document. ' 'FNamePos is getting the number of spaces that it takes to get to the 'very last back slash in the full file name of our document. FNamePos = InStrRev(docFile.FullFileName, "\", -1) 'We can then take that number (position) and use it to cut off all of the 'file path that we don't need. In this case, it's the front of the path 'that we're getting rid of, leaving us with just the file name. docFName = Mid(docFile.FullFileName, FNamePos + 1, Len(docFile.FullFileName) - FNamePos) 'Let's make sure that we can even change this part. If we can, then we'll continue. If docFile.IsModifiable = True Then 'Because you can only grab the occurrences from an assembly document 'we need to fill up that empty AssemblyDocument container with an object 'that we know is definitely an assembly document. Because that was one 'of the first checks we made up there on this open document, we can safely 'declare that assemblyDoc (Which is an AssemblyDocument) is equal to ' openDoc (Which is just a regular old Document) assemblyDoc = openDoc 'While we're at it, let's go on and define the empty ComponentDefinition container '(we named ours assemblyDef) with some sort of content. assemblyDef = assemblyDoc.ComponentDefinition 'Now we need to collect every instance of the document against 'our open assembly and put them all inside of partQty. partQty = assemblyDef.Occurrences.AllReferencedOccurrences(docFile) 'Now that we have our collection of instances, we need to populate 'our target iproperty with the total amount. 'Instead of just throwing that amount in there, let's make sure that 'the value inside of the target iProperty isn't already equal to our count. 'If it is equal, then we don't have to change anything (which saves us time!), 'but if it isn't equal then we will need to change it. 'The Try statement is here because of the next if statement --- ' 'If partQty.Count <> iProperties.Value(docFName, "Project", "Authority") Then' ' 'If we just compare the two values, there is a small chance that our target 'iProperty is already set to something that is NOT a number, which would create 'an error (as you can't compare numbers against things that aren't numbers). 'So, we TRY to complete that if statement, and if there is an error (The CATCH) 'we just force that target to equal our part qty total. Try If partQty.Count <> iProperties.Value(docFName, "Custom", customPropertyName) Then iProperties.Value(docFName, "Custom", customPropertyName) = partQty.Count End If Catch iProperties.Value(docFName, "Custom", customPropertyName) = partQty.Count End Try End If Next Else 'This is here to warn the user that they are attempting to run this in a document that is invalid! 'we don't want them running things that aren't correct! Force them to obey! MessageBox.Show("You must have a valid Assembly document open before using this code!", "File Type Mismatch!",MessageBoxButtons.OK,MessageBoxIcon.Exclamation) End If iLogicVb.UpdateWhenDone = True MessageBox.Show("Completed", "Completed", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1) End Sub Sub oProject oProj = InputBox("Change Project Name", "Project Name", iProperties.Value("Project", "Project")) iProperties.Value("Project", "Project")=oProj Main () End Sub
Any tips on how to concatenate the per assembly quantities into one field?
Thanks in advance!
Solved! Go to Solution.
Solved by MechMachineMan. Go to Solution.
Method 1:
1) Iterate through ALL ref'd docs of top level.
2) Iterate through ALL Referencing Docs of this sub-doc.
3) In each assy it occurs in, count the amounts of times it occurs. - May need additional filtering to check if any of the occurrences are Reference BOMStruct.
4) Keep a running tally, and also use a separate string to summarize results of the assembly usage
5) Output this info into the iProp stored in that part.
*This will likely really only work if all of the assemblies that use these parts are compiled into a general assembly, and will not work for sub-assemblies.
Method 2:
1) Iterate through BOM,
@MechMachineMan wrote:
Method 1:
*This will likely really only work if all of the assemblies that use these parts are compiled into a general assembly, and will not work for sub-assemblies.
Yeah, all the assemblies are typically put in one 'Parts List' top level assembly for a complete BOM for the project, so this would work fine.
Do you know what the iLogic code for the summarized results would look like?
Yeah, fairly well. Just would take time that I do not have until later this evening. But here is the start of the idea behind that method.
Sub Main() Dim oDoc As Document = ThisApplication.ActiveDocument For Each oSubDoc in oDoc.AllReferencedDocuments Call PopulateAncestryProp(oSubDoc) Next MsgBox("Rule Complete!") End Sub Sub PopulateAncestryProp(oSubDoc As Document) For Each oParentDoc in oSubDoc.ReferencingDocuments oThisCount = GetParentNonRefCount() oThisPN = ...Property("Part Number").Value oTotalCount = oTotalCount + oThisCount oAncestryString = oAncestryString & oThisP/N & oThisCount Next
oSubDoc.PropertySets("Inventor User Defined Properties")("AncestryStr").Value = oAncestryString End Sub
Function GetparentNonRefCount(oSubDoc As Document, oOccDoc As Document)
For Each oOcc in oSubDoc.Occurrences.AllReferencedOccurrences(oOccDoc)
If oOcc.BOMStruct <> Ref
oCount = oCount + 1
End if
Next
Return oCount
End Function
Hi
It does subs for me?
Perhaps I miss read your question?
Edited:
Okay, perhaps I did miss read, The code only does total quantity per project.
Give this a whirl. Let me know if anything isn't working for you.
VBA & vb.net versions included.
'AncestryiPropRule in VBA 'by MechMachineMan via AutoDesk InventorCustomization Forums 'Feb 06, 2017 ' 'Purpose: Iterates through all children documents of a main assembly and adds their "Where Used" and ' "Qty" information to the children for use on a drawing. ' 'Notes: End user will need to use the Custom iProps "_AncestryStr" and "_TotalCount" ' 'Rev 0: As released to forums Sub Main() Dim oDoc As Document Set oDoc = ThisApplication.ActiveDocument Dim oSubDoc As Document For Each oSubDoc In oDoc.AllReferencedDocuments Call PopulateAncestryProp(oSubDoc) Next MsgBox ("Rule Complete!") End Sub Sub PopulateAncestryProp(oSubDoc As Document) Dim oTotalCount As Integer oTotalCount = 0 Dim oThisPN As String Dim oAncestryString As String oAncestryString = "" Dim oParentDoc As Document For Each oParentDoc In oSubDoc.ReferencingDocuments oThisCount = GetParentNonRefCount(oParentDoc, oSubDoc) oThisPN = oParentDoc.PropertySets("Design Tracking Properties")("Part Number").Value oTotalCount = oTotalCount + oThisCount If oAncestryString = "" Then oAncestryString = oThisPN & "(" & oThisCount & ")" Else oAncestryString = oAncestryString & ", " & oThisPN & "(" & oThisCount & ")" End If Next On Error Resume Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_AncestryStr").Value = oAncestryString If Err.Number <> 0 Then Call oSubDoc.PropertySets("Inventor User Defined Properties").Add(oAncestryString, "_AncestryStr") End If On Error Resume Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_TotalCount").Value = oTotalCount If Err.Number <> 0 Then Call oSubDoc.PropertySets("Inventor User Defined Properties").Add(oTotalCount, "_TotalCount") End If End Sub Function GetParentNonRefCount(oParentDoc As Document, oOccDoc As Document) As Integer For Each oOcc In oParentDoc.ComponentDefinition.Occurrences.AllReferencedOccurrences(oOccDoc) If oOcc.BOMStructure <> BOMStructureEnum.kReferenceBOMStructure Then oCount = oCount + 1 End If Next GetParentNonRefCount = oCount End Function
-----------------------------------------------------------------------------------------------------------
In vb.net:
(note, IN THIS CASE, all I had to do to convert from VBA to vb.net was remove "Set" where it appeared, and change "
GetParentNonRefCount = oCount <- (vba) TO (vb.net) -> Return oCount
Code:
'AncestryiPropRule in vb.net 'by MechMachineMan via AutoDesk InventorCustomization Forums 'Feb 06, 2017 ' 'Purpose: Iterates through all children documents of a main assembly and adds their "Where Used" and ' "Qty" information to the children for use on a drawing. ' 'Notes: End user will need to use the Custom iProps "_AncestryStr" and "_TotalCount" ' 'Rev 0: As released to forums Sub Main() Dim oDoc As Document oDoc = ThisApplication.ActiveDocument Dim oSubDoc As Document For Each oSubDoc In oDoc.AllReferencedDocuments Call PopulateAncestryProp(oSubDoc) Next MsgBox ("Rule Complete!") End Sub Sub PopulateAncestryProp(oSubDoc As Document) Dim oTotalCount As Integer oTotalCount = 0 Dim oThisPN As String Dim oAncestryString As String oAncestryString = "" Dim oParentDoc As Document For Each oParentDoc In oSubDoc.ReferencingDocuments oThisCount = GetParentNonRefCount(oParentDoc, oSubDoc) oThisPN = oParentDoc.PropertySets("Design Tracking Properties")("Part Number").Value oTotalCount = oTotalCount + oThisCount If oAncestryString = "" Then oAncestryString = oThisPN & "(" & oThisCount & ")" Else oAncestryString = oAncestryString & ", " & oThisPN & "(" & oThisCount & ")" End If Next On Error Resume Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_AncestryStr").Value = oAncestryString If Err.Number <> 0 Then Call oSubDoc.PropertySets("Inventor User Defined Properties").Add(oAncestryString, "_AncestryStr") End If On Error Resume Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_TotalCount").Value = oTotalCount If Err.Number <> 0 Then Call oSubDoc.PropertySets("Inventor User Defined Properties").Add(oTotalCount, "_TotalCount") End If End Sub Function GetParentNonRefCount(oParentDoc As Document, oOccDoc As Document) As Integer For Each oOcc In oParentDoc.ComponentDefinition.Occurrences.AllReferencedOccurrences(oOccDoc) If oOcc.BOMStructure <> BOMStructureEnum.kReferenceBOMStructure Then oCount = oCount + 1 End If Next Return oCount End Function
It sounds like he wants ancestry tags (or whatever you want to call them), especially if it's based off of a client standard, which unfortunately has no built-in functions in Inventor 😞
To add, to use the macro/rule I wrote, you will need to call off the custom iProperties "_AncestryStr" and "_TotalCount" from the part model in your drawing.
@MechMachineMan wrote:
It sounds like he wants ancestry tags (or whatever you want to call them), especially if it's based off of a client standard, which unfortunately has no built-in functions in Inventor 😞
Exactly. We have been using parts only to wrap up totals, but sub-assembly totals is new.
Thank you so much Justin!
I just tried your code and ran into the following error. Any ideas? Does oOcc need to be defined?
Thanks Again!
I had overlooked adding handling for if it ran into virtual components and derived parts, which I'm guessing you are using, which is what is causing the error?
'AncestryiPropRule in VBA 'by MechMachineMan via AutoDesk InventorCustomization Forums 'Feb 06, 2017 ' 'Purpose: Iterates through all children documents of a main assembly and adds their "Where Used" and ' "Qty" information to the children for use on a drawing. ' 'Notes: End user will need to use the Custom iProps "_AncestryStr" and "_TotalCount" ' 'Rev 0: As released to forums Sub Main() Dim oDoc As Document Set oDoc = ThisApplication.ActiveDocument Dim oSubDoc As Document For Each oSubDoc In oDoc.AllReferencedDocuments Call PopulateAncestryProp(oSubDoc) Next MsgBox ("Rule Complete!") End Sub Sub PopulateAncestryProp(oSubDoc As Document) Dim oTotalCount As Integer oTotalCount = 0 Dim oThisPN As String Dim oAncestryString As String oAncestryString = "" Dim oParentDoc As Document For Each oParentDoc In oSubDoc.ReferencingDocuments
If oParentDoc.DocumentType = DocumentTypeEnum.kAssemblyDocumentObject Then oThisCount = GetParentNonRefCount(oParentDoc, oSubDoc) oThisPN = oParentDoc.PropertySets("Design Tracking Properties")("Part Number").Value oTotalCount = oTotalCount + oThisCount If oAncestryString = "" Then oAncestryString = oThisPN & "(" & oThisCount & ")" Else oAncestryString = oAncestryString & ", " & oThisPN & "(" & oThisCount & ")" End If
End if Next On Error Resume Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_AncestryStr").Value = oAncestryString If Err.Number <> 0 Then Call oSubDoc.PropertySets("Inventor User Defined Properties").Add(oAncestryString, "_AncestryStr") End If On Error Resume Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_TotalCount").Value = oTotalCount If Err.Number <> 0 Then Call oSubDoc.PropertySets("Inventor User Defined Properties").Add(oTotalCount, "_TotalCount") End If End Sub Function GetParentNonRefCount(oParentDoc As Document, oOccDoc As Document) As Integer For Each oOcc In oParentDoc.ComponentDefinition.Occurrences.AllReferencedOccurrences(oOccDoc) If oOcc.BOMStructure <> BOMStructureEnum.kReferenceBOMStructure Then oCount = oCount + 1 End If Next GetParentNonRefCount = oCount End Function
Yeah, it's mostly derived parts.
I'll give this a try and get back to you.
Thanks again!
Yes, most of the parts are derived from a master part.
The code now works with only a couple issues.
One, is it seems to gather the total count from the ancestry count, but that doesn't account for sub-assemblies used more than one. But we can just use the original code I posted for that value.
The other issue is I've utilized a bunch of phantom assemblies for parts then get reused often. Is there anyway to ignore the phantom assemblies, and use their parent assembly part numbers instead? I realize this may be tricky.
Thank you so much for your time.
Shouldn't be too, too terrible to fix it.
I'm assuming this total includes plates used in weldments as well.
Do you use weldments as INSEPARABLE Bom Structure, or have do you switch them to Normal?
Well it's starting to seem like there is a lot of exception cases and that it doesn't really work easily from top down;
This algorithm would work find, except, working from the Bottom up, if it iterates up and encounters a file that's phantom, it just put in a bunch of information that is incorrect.
Because the control of this all works from TopDown, I actually need to approach the problem that way. Which makes me think it might just be easier to have the Inventor BOMS do all the legwork there for me.
I had spent my lunch working on writing a macro that exports the BOM to an array so that I can process it really fast.. However, it appears that without a sort algorithm, it doesn't ever pull the information sorted by Item number.
Which means when I have time, I'm just going to have to re-write the rule to iterate through the BOM and Push the Parent Row Part Number down to it's sub-components.
Which means I actually need a rule to clear the iProps for a fresh run every time the rule is run, then rewrite the rule to to store the ancestry in the iProps, and then check the iProp before just appending it, so I can modify it with the Parent Occ QTY info.
I'm guessing adding multiple tags for multiple instances of the parent is unacceptable and that you want them all rolled together, eh?
ie;
Assy1
- Sub Assy1
-- Part
-Sub Assy1
--Part
In part, tags should show as: Sub Assy(1), Sub Assy(1) OR Sub Assy(2) OR Sub Assy (2 X 1)?
Yeah, weldments are normal assemblies at the moment.
I suppose 'Sub Assy (2 X 1)' conveys the information the most effectively.
If it's going to be a real hassle don't feel obligated to make it work. We might be able to fumble our way there.
Meh. It's a fun challenge!
Ok here is another go.
Hopefully I didn't miss anything else haha
Private oLogStr As String Sub Main() Dim oDoc As Document Set oDoc = ThisApplication.ActiveDocument Dim oACD As AssemblyComponentDefinition Set oACD = oDoc.ComponentDefinition Dim oBOM As BOM Set oBOM = oACD.BOM Dim oPartsBOM As BOMView Dim oStructBOM As BOMView With oBOM .StructuredViewEnabled = True .StructuredViewFirstLevelOnly = False .StructuredViewDelimiter = "." Set oStructBOM = .BOMViews("Structured") End With Call PurgeNewiProps(oDoc) Call AddAncestryViaStruct(oDoc, oStructBOM) Call AddTotalQTY(oDoc) MsgBox ("Rule complete!" & vbLf & vbLf & "Custom Properties Used: _AncestryStr & _TotalQTY" & vbLf & vbLf & "Files with issues: " & vbLf & oLogStr) End Sub Sub PurgeNewiProps(oDoc) For Each oSubDoc In oDoc.AllReferencedDocuments If oSubDoc.IsModifiable = True Then On Error Resume Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_AncestryStr").Value = "" oSubDoc.PropertySets("Inventor User Defined Properties").Item("_TotalQTY").Value = "" End If Next End Sub Sub AddAncestryViaStruct(oGADoc As Document, oStructBOM As BOMView) Dim oRow As BOMRow For Each oRow In oStructBOM.BOMRows Call AddAncestryToRow(oRow, oGADoc.PropertySets("Design Tracking Properties")("Part Number").Value, "1") Next End Sub Sub AddAncestryToChildren(oRow As BOMRow, oParentQTY As String) If oRow.ComponentDefinitions.Item(1).Type = ObjectTypeEnum.kVirtualComponentDefinitionObject Then Exit Sub End If Dim oPN As String oPN = oRow.ComponentDefinitions.Item(1).Document.PropertySets("Design Tracking Properties")("Part Number").Value Dim oPQTY As String oPQTY = oRow.TotalQuantity * CInt(oParentQTY) Dim oCRow As BOMRow For Each oCRow In oRow.ChildRows If oCRow.ComponentDefinitions.Item(1).Type <> ObjectTypeEnum.kVirtualComponentDefinitionObject Then Call AddAncestryToRow(oCRow, oPN, oPQTY) End If Next End Sub Sub AddAncestryToRow(oCRow As BOMRow, oPN As String, oParentQTY As String) Dim oAncestryString As String Dim oSubDoc As Document Dim oQTY As String
Dim oiQTY As Integer Set oSubDoc = oCRow.ComponentDefinitions(1).Document If oSubDoc.IsModifiable = True Then On Error Resume Next oAncestryString = oSubDoc.PropertySets("Inventor User Defined Properties").Item("_AncestryStr").Value If Err.Number <> 0 Then Call oSubDoc.PropertySets("Inventor User Defined Properties").Add("", "_AncestryStr") oAncestryString = "" End If oQTY = oCRow.TotalQuantity
On Error Resume Next
oiQTY = CINT(oQTY)
If Err.Number <> 0 Then
MsgBox("Error: A BOM row uses length or a parameter rather than an integer quantity. This rule is not cut out for that." & vblf & vblf & _
"Please revise Base QTY of that part then re-run this rule.") End if
On Error GoTo 0
For j = 1 To oParentQTY oAncestryString = AppendAncestryString(oAncestryString, oPN, oQTY) Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_AncestryStr").Value = oAncestryString Else oLogStr = oLogStr & vbLf & "NO ANCESTRY ADDED FOR: " & oSubDoc.PropertySets("Design Tracking Properties")("Part Number").Value End If If Not oCRow.ChildRows Is Nothing Then Call AddAncestryToChildren(oCRow, oParentQTY) End If End Sub Function AppendAncestryString(oAncestryString As String, oPN As String, oQTY As String) As String pos = InStr(oAncestryString, oPN) If pos = 0 Then If Len(oAncestryString) = 0 Then oAncestryString = oPN & "(" & oQTY & ")" Else oAncestryString = oAncestryString & ", " & oPN & "(" & oQTY & ")" End If Else openpos = InStr(pos, oAncestryString, "(") closepos = InStr(openpos, oAncestryString, ")") xpos = InStr(openpos, oAncestryString, "x", vbTextCompare) If xpos = 0 Or xpos > closepos Or xpos < openpos Then oAncestryString = Left(oAncestryString, openpos) & "2 X " & Right(oAncestryString, Len(oAncestryString) - openpos) Else oOldQTYx = Trim(Mid(oAncestryString, openpos + 1, xpos - 1 - openpos)) oNewQTYX = CInt(oOldQTYx) + 1 oAncestryString = Left(oAncestryString, openpos) & oNewQTYX & Right(oAncestryString, Len(oAncestryString) - xpos + 2) End If End If AppendAncestryString = oAncestryString End Function Sub AddTotalQTY(oDoc) Dim oSubDoc As Document For Each oSubDoc In oDoc.AllReferencedDocuments If oSubDoc.IsModifiable = True Then oTotalQTY = GetTotalFromAncestry(oSubDoc.PropertySets("Inventor User Defined Properties")("_AncestryStr").Value) On Error Resume Next oSubDoc.PropertySets("Inventor User Defined Properties").Item("_TotalQTY").Value = oTotalQTY If Err.Number <> 0 Then Call oSubDoc.PropertySets("Inventor User Defined Properties").Add(oTotalQTY, "_TotalQTY") End If Else oLogStr = oLogStr & vbLf & "NO QTY ADDED: " & oSubDoc.PropertySets("Design Tracking Properties")("Part Number").Value End If Next End Sub Function GetTotalFromAncestry(oStr As String) As Integer Dim openpos As Integer openpos = 1 Dim multiplier As Integer Dim qty As Integer Dim runningtotal As Integer Do Until oStr = "" openpos = InStr(1, oStr, "(") closepos = InStr(openpos, oStr, ")") xpos = InStr(openpos, oStr, "x", vbTextCompare) If xpos = 0 Or xpos > closepos Or xpos < openpos Then thistotal = Trim(Mid(oStr, openpos + 1, closepos - openpos - 1)) Else multiplier = Trim(Mid(oStr, openpos + 1, xpos - 1 - openpos)) qty = Trim(Mid(oStr, xpos + 1, closepos - 1 - xpos)) thistotal = multiplier * qty End If runningtotal = runningtotal + thistotal oStr = Right(oStr, Len(oStr) - closepos) Loop GetTotalFromAncestry = runningtotal End Function
Can't find what you're looking for? Ask the community or share your knowledge.