14 Replies Latest reply on Mar 21, 2017 12:31 PM by James Watson

    Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)

    James Watson

      OK so, below you will find my current macro, works nearly flawlessly. alas it is so inefficient it hurts... (as you can see if you watch the debug window).

         

      What it does is it gets all components and configurations of said components in an assembly. It then iterates through them using a duplicate iteration to count the quantities of each configuration of each component. Using this method it overwrites the "MfgQty" custom property again and again every time it encounters a part. Though it still gives the correct result, this is horribly inefficient.

      Dim swApp As SldWorks.SldWorks

      Dim swModel As SldWorks.ModelDoc2

      Dim swAssy As SldWorks.AssemblyDoc

      Dim swCmp As SldWorks.Component2

      Dim tCmp As SldWorks.Component2

      Dim CmpDoc As ModelDoc2

      Dim i As Integer

      Dim j As Integer

      Dim Cfg As String

      Dim vCmps As Variant

      Dim swCustPropMgr As SldWorks.CustomPropertyManager

      Dim nstart As Long

      Dim nStatus As Long

      Dim config As SldWorks.Configuration

      Dim lRetVal As Variant

      Dim ValOut As String

      Dim ResolvedValOut As String

      Dim wasResolved As Boolean

      Dim cCnt As Integer

       

      Sub main()

          Set swApp = Application.SldWorks

          Set swModel = swApp.ActiveDoc

          Set swAssy = swModel

       

      ' start timer

          nstart = Timer

       

      ' load all components into array

          vCmps = swAssy.GetComponents(False)

       

      ' resolve all lightweight components

          nStatus = swAssy.ResolveAllLightWeightComponents(False)

       

      'remove duplicates from array

       

      ' iterate though array

          For i = 0 To UBound(vCmps)

              Set swCmp = vCmps(i)

              If (swCmp.GetSuppression = 3) Or (swCmp.GetSuppression = 2) Then

              cCnt = 0

              Set CmpDoc = swCmp.GetModelDoc

              Cfg = swCmp.ReferencedConfiguration

       

      ' for each component in new array of matching configuration, count instances in original array

                  For j = 0 To UBound(vCmps)

                  Set tCmp = vCmps(j)

                  If tCmp.GetSuppression <> 0 Then

                  If tCmp.GetModelDoc2 Is CmpDoc Then

                  If tCmp.ReferencedConfiguration = Cfg Then

                  cCnt = cCnt + 1

                  End If

                  End If

                  End If

                  Next j

              Debug.Print swCmp.Name, swCmp.ReferencedConfiguration, cCnt

       

       

      ' set "MfgQty" custom property to computed value

          Set swCustPropMgr = CmpDoc.Extension.CustomPropertyManager(swCmp.ReferencedConfiguration)

          lRetVal = swCustPropMgr.Delete2("MfgQty")

          lRetVal = swCustPropMgr.Add3("MfgQty", 30, "999", 0)

          lRetVal = swCustPropMgr.Get5("MfgQty", False, ValOut, ResolvedValOut, False)

          lRetVal = swCustPropMgr.Set2("MfgQty", cCnt)

              End If

          Next i

       

       

      ' show elapsed time

          Debug.Print "Time = " & Timer - nstart & " seconds"

       

      End Sub

      What I would like to do is reduce this. I would like to initially build an array of unique components/configurations, then using that array iterate through it using the base array to do the count. this would benefit me at least 2 fold, 1) MUCH more efficient not duplicating counts and 2) it would allow me to add print and dxf commands in (print to .PDF drawings of all parts, and export .DXF drawings of parts that contain a custom property called "ItemCat" with the value of "Laser-Pur" and/or "Router-Pur"), and only have each unique part print out once rather that 24 times for a part that has 24 instances. Below is a sample code of what i think would work... i am just having issues connecting the two.

      Sub Test()

      Dim aReturn As Variant

      Dim aArray As Variant

      Dim swCmp As Variant

      ' build array

       

      aArray = Array("red", "blue", "green", "blue", "blue", "green", "red", "red", "yellow", "red", "green", "blue")

      aReturn = ArrayUnique(aArray)

          For i = 0 To UBound(aReturn)

              Debug.Print aReturn(i)

          Next i

      End Sub

       

      Function ArrayUnique(ByVal aArrayIn As Variant) As Variant

       

      'remove duplicated values from a single dimension array

      Dim aArrayOut() As Variant

      Dim bFlag As Boolean

      Dim vIn As Variant

      Dim vOut As Variant

      Dim i%, j%, k%

       

      ReDim aArrayOut(LBound(aArrayIn) To UBound(aArrayIn))

      i = LBound(aArrayIn)

      j = i

       

      For Each vIn In aArrayIn

          For k = j To i - 1

              If vIn = aArrayOut(k) Then bFlag = True: Exit For

          Next

          If Not bFlag Then aArrayOut(i) = vIn: i = i + 1

          bFlag = False

      Next

       

      If i <> UBound(aArrayIn) Then ReDim Preserve aArrayOut(LBound(aArrayIn) To i - 1)

      ArrayUnique = aArrayOut

      End Function

      the first code has fairly well broken out steps, and works as is to add a custom property called "MfgQty" to each file with the assembly count of said part. Works with multiple configurations of the same part and will add the property to each configuration correctly.

       

      Any help on generating this Array so i can progress this code further would be helpful. as you can see the " 'remove duplicates from array" section is empty

        • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
          Ivana Kolin

          is this faster?

           

          ' iterate though array

          For i = 0 To UBound(vCmps)

                  Set swCmp = vCmps(i)

                  Set CmpDoc = swCmp.GetModelDoc

                  Set swCustPropMgr = CmpDoc.Extension.CustomPropertyManager(swCmp.ReferencedConfiguration)

                  lRetVal = swCustPropMgr.Delete2("MfgQty")

                  lRetVal = swCustPropMgr.Add3("MfgQty", 30, "0", 0)

          Next i

          For i = 0 To UBound(vCmps)

                  Set swCmp = vCmps(i)

                    If swCmp.GetSuppression <> 0 Then

                         Set CmpDoc = swCmp.GetModelDoc

                      ' set "MfgQty" custom property to computed value

              Set swCustPropMgr = CmpDoc.Extension.CustomPropertyManager(swCmp.ReferencedConfiguration)

             

                    lRetVal = swCustPropMgr.Get5("MfgQty", False, ValOut, ResolvedValOut, False)

                    cCnt = ValOut + 1

                    Debug.Print swCmp.Name, swCmp.ReferencedConfiguration, cCnt

                    lRetVal = swCustPropMgr.Set2("MfgQty", cCnt)

                  End If

              Next i

          • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
            James Watson

            I suppose step 1 would be to write the following values seen in the debug window into an array...

            Dim swApp As SldWorks.SldWorks

            Dim swModel As SldWorks.ModelDoc2

            Dim swAssy As SldWorks.AssemblyDoc

            Dim vComps As Variant

            Dim swComp As SldWorks.Component2

            Dim i As Integer

               

            Sub main()

                Set swApp = Application.SldWorks

                Set swModel = swApp.ActiveDoc

                Set swAssy = swModel

                vComps = swAssy.GetComponents(False)

               

                For i = 0 To UBound(vComps)

                    Set swComp = vComps(i)

                    Debug.Print swComp.GetPathName, swComp.ReferencedConfiguration

                   

                Next i

            End Sub

            • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
              James Watson

              OK so i was able to feed the assembly into an array using the following code.

              Sub ArrayTest()

               

              Dim CompArray() As String

              Dim myCmp As Variant

              Dim myCmps As Variant

              Dim vCmps As Variant

              Dim i As Integer

              Dim j As Integer

              Dim nstart As Long

               

              Set swApp = Application.SldWorks

              Set swModel = swApp.ActiveDoc

              Set swAssy = swModel

               

              ' start timer

                  nstart = Timer

               

              ' load all components

                  vCmps = swAssy.GetComponents(False)

               

              ' resolve all lightweight components

                  nStatus = swAssy.ResolveAllLightWeightComponents(False)

               

              ' build array

              j = UBound(vCmps)

              ReDim Preserve CompArray(j, 1) As String

               

              ' itterate though components

                  For i = 0 To UBound(vCmps)

                      Set swCmp = vCmps(i)

                      Debug.Print swCmp.GetPathName, swCmp.ReferencedConfiguration

                      If (swCmp.GetSuppression = 3) Or (swCmp.GetSuppression = 2) Then

                          cCnt = 0

                          Set CmpDoc = swCmp.GetModelDoc

                          CompArray(i, 0) = swCmp.GetModelDoc.GetPathName

                          CompArray(i, 1) = swCmp.ReferencedConfiguration

                      End If

                 Next i

               

              ' show elapsed time

                  Debug.Print "Time = " & Timer - nstart & " seconds"

               

              End Sub

              now to integrate this portion of code into the duplicate remover. Then I might be able to optimize my code a little, add in the printing functions, and more of the actually functionality I was looking for.

                • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
                  Ivana Kolin
                  
                  Sub ArrayTest()
                      Dim swApp                                     As SldWorks.SldWorks
                      Dim swModel                                   As SldWorks.ModelDoc2
                      Dim swAssy                                    As SldWorks.AssemblyDoc
                      Dim CompArray()                               As Variant
                      Dim InnerArray(0 To 3)                        As Variant
                      Dim vCmps                                     As Variant
                      Dim i                                         As Integer
                      Dim nstart                                    As Long
                      Dim nStatus                                   As Long
                      Dim swCmp                                     As SldWorks.Component2
                      Dim CmpDoc                                    As SldWorks.ModelDoc2
                      Dim Index                                     As Integer
                      Dim vInnerArray                               As Variant
                      Set swApp = Application.SldWorks
                      Set swModel = swApp.ActiveDoc
                      Set swAssy = swModel
                  
                  
                      ' start timer
                      nstart = Timer
                  
                  
                      ' load all components
                      vCmps = swAssy.GetComponents(False)
                  
                  
                      ' resolve all lightweight components
                      nStatus = swAssy.ResolveAllLightWeightComponents(False)
                  
                  
                      ' build array
                  
                  
                      ReDim CompArray(UBound(vCmps))
                  
                  
                      ' itterate though components
                      For i = 0 To UBound(vCmps)
                          Set swCmp = vCmps(i)
                          Debug.Print swCmp.GetPathName, swCmp.ReferencedConfiguration
                          If (swCmp.GetSuppression = 3) Or (swCmp.GetSuppression = 2) Then
                              Set CmpDoc = swCmp.GetModelDoc
                              For Each vInnerArray In CompArray
                                  If IsEmpty(vInnerArray) Then
                                      InnerArray(0) = swCmp.GetModelDoc.GetPathName
                                      InnerArray(1) = swCmp.ReferencedConfiguration
                                      InnerArray(2) = 1
                                      InnerArray(3) = Index
                                      CompArray(Index) = InnerArray
                                      Index = Index + 1
                                      Exit For
                                  ElseIf vInnerArray(0) = swCmp.GetModelDoc.GetPathName And vInnerArray(1) = swCmp.ReferencedConfiguration Then
                                      vInnerArray(2) = Val(vInnerArray(2)) + 1
                                      CompArray(vInnerArray(3)) = vInnerArray
                                      Exit For
                                  End If
                              Next
                          End If
                      Next i
                      ReDim Preserve CompArray(Index - 1)
                      For Each vInnerArray In CompArray
                          Debug.Print "Count: " & vInnerArray(2), "Configuration: " & vInnerArray(1), "Path: " & vInnerArray(0)
                      Next
                      ' show elapsed time
                  Debug.Print "Time = " & Timer - nstart & " seconds"
                  
                  
                  End Sub
                  
                    • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
                      James Watson

                      Thank you so much, I was on the right track but this is MUCH cleaner. as you posted this i had it to the point of doing the 2 column array check but was having issues combining them.

                       

                      now the next step is for me to integrate those values into the files themselves and overwrite the "MfgQty" custom property for each configuration of each file.

                        • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
                          James Watson

                          what i am trying to achieve now is to go through that "CompArray" and open the files modify/add the MfgQty custom property.

                          like what i do in this code

                          Dim swApp As SldWorks.SldWorks
                          Dim swModel As SldWorks.ModelDoc2
                          Dim swAssy As SldWorks.AssemblyDoc
                          Dim swCmp As SldWorks.Component2
                          Dim tCmp As SldWorks.Component2
                          Dim CmpDoc As ModelDoc2
                          Dim i As Integer
                          Dim j As Integer
                          Dim cfg As String
                          Dim vCmps As Variant
                          Dim swCustPropMgr As SldWorks.CustomPropertyManager
                          Dim nstart As Long
                          Dim nStatus As Long
                          Dim config As SldWorks.Configuration
                          Dim lRetVal As Variant
                          Dim ValOut As String
                          Dim ResolvedValOut As String
                          Dim wasResolved As Boolean
                          Dim cCnt As Integer
                              
                          Sub TraverseAssembly()
                              Set swApp = Application.SldWorks
                              Set swModel = swApp.ActiveDoc
                              Set swAssy = swModel
                          ' start timer
                              nstart = Timer
                          ' load all components into array
                              vCmps = swAssy.GetComponents(False)
                          ' resolve all lightweight components
                              nStatus = swAssy.ResolveAllLightWeightComponents(False)
                          ' itterate though array
                              For i = 0 To UBound(vCmps)
                                  Set swCmp = vCmps(i)
                                  If (swCmp.GetSuppression = 3) Or (swCmp.GetSuppression = 2) Then
                                  cCnt = 0
                                  Set CmpDoc = swCmp.GetModelDoc
                                  cfg = swCmp.ReferencedConfiguration
                          ' for each component in new array of matching configuration, count instances in original array
                                      For j = 0 To UBound(vCmps)
                                      Set tCmp = vCmps(j)
                                      If tCmp.GetSuppression <> 0 Then
                                      If tCmp.GetModelDoc2 Is CmpDoc Then
                                      If tCmp.ReferencedConfiguration = cfg Then
                                      cCnt = cCnt + 1
                                      End If
                                      End If
                                      End If
                                      Next j
                                  Debug.Print swCmp.GetPathName, swCmp.ReferencedConfiguration, cCnt
                          ' set "MfgQty" custom property to computed value
                              Set swCustPropMgr = CmpDoc.Extension.CustomPropertyManager(swCmp.ReferencedConfiguration)
                              lRetVal = swCustPropMgr.Delete2("MfgQty")
                              lRetVal = swCustPropMgr.Add3("MfgQty", 30, "999", 0)
                              lRetVal = swCustPropMgr.Get5("MfgQty", False, ValOut, ResolvedValOut, False)
                              lRetVal = swCustPropMgr.Set2("MfgQty", cCnt)
                                  End If
                              Next i
                          ' show elapsed time
                              Debug.Print "Time = " & Timer - nstart & " seconds"
                          End Sub
                          
                        • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
                          James Watson

                          how would I go about adding this chunk of code into that one?

                              ' iterate through condensed list

                              For Each vInnerArray In CmpArray

                           

                              ' Determine type of SOLIDWORKS file based on file extension

                              If InStr(LCase(vInnerArray(0)), "sldprt") > 0 Then

                                  nDocType = swDocPART

                              ElseIf InStr(LCase(vInnerArray(0)), "sldasm") > 0 Then

                                  nDocType = swDocASSEMBLY

                              ElseIf InStr(LCase(vInnerArray(0)), "slddrw") > 0 Then

                                  nDocType = swDocDRAWING

                              Else

                                  nDocType = swDocNONE

                              End If

                              Set swModel = swApp.OpenDoc6(vInnerArray(0), nDocType, swOpenDocOptions_Silent, vInnerArray(1), nErrors, nWarnings)

                              Set CmpDoc =

                              Debug.Print CmpDoc.GetPathName

                             

                              ' set "MfgQty" custom property to computed value

                              Set swCustPropMgr = CmpDoc.Extension.CustomPropertyManager(swModel.ReferencedConfiguration)

                              lRetVal = swCustPropMgr.Delete2("MfgQty")

                              lRetVal = swCustPropMgr.Add3("MfgQty", 30, "999", 0)

                              lRetVal = swCustPropMgr.Get5("MfgQty", False, ValOut, ResolvedValOut, False)

                              lRetVal = swCustPropMgr.Set2("MfgQty", vInnerArray(2))

                          cCnt -> vInnerArray(2)

                            • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
                              James Watson

                              i got it to work (seemingly) with this chunk of code

                                  ' iterate through condensed array
                                  For Each vInnerArray In CmpArray
                                 
                                  ' Determine type of SOLIDWORKS file based on file extension
                                  If InStr(LCase(sDocFileName), "sldprt") > 0 Then
                                      nDocType = swDocPART
                                  ElseIf InStr(LCase(sDocFileName), "sldasm") > 0 Then
                                      nDocType = swDocASSEMBLY
                                  ElseIf InStr(LCase(sDocFileName), "slddrw") > 0 Then
                                      nDocType = swDocDRAWING
                                  Else
                                  ' Probably not a SOLIDWORKS file
                                      nDocType = swDocNONE
                                  ' So cannot open the file
                                  End If
                              
                              
                                  Set swCmp = vInnerArray(4)
                                  Set CmpDoc = swCmp.GetModelDoc
                              '    Debug.Print CmpDoc.GetPathName
                              '    Debug.Print swCmp.ReferencedConfiguration
                                 
                                  ' set "MfgQty" custom property to computed value
                                  Set swCustPropMgr = CmpDoc.Extension.CustomPropertyManager(swCmp.ReferencedConfiguration)
                                  lRetVal = swCustPropMgr.Delete2("MfgQty")
                                  lRetVal = swCustPropMgr.Add3("MfgQty", 30, "999", 0)
                                  lRetVal = swCustPropMgr.Get5("MfgQty", False, ValOut, ResolvedValOut, False)
                                  lRetVal = swCustPropMgr.Set2("MfgQty", vInnerArray(2))
                                 
                              '    Debug.Print "Count: " & vInnerArray(2), "Configuration: " & vInnerArray(1), "Path: " & vInnerArray(0)
                                  Next
                              
                              • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
                                Ivana Kolin
                                Option Explicit
                                Sub ArrayTest()
                                    Dim swApp                                     As SldWorks.SldWorks
                                    Dim swModel                                   As SldWorks.ModelDoc2
                                    Dim swAssy                                    As SldWorks.AssemblyDoc
                                    Dim CompArray()                               As Variant
                                    Dim InnerArray(4)                             As Variant
                                    Dim vCmps                                     As Variant
                                    Dim i                                         As Integer
                                    Dim nstart                                    As Long
                                    Dim nStatus                                   As Long
                                    Dim swCmp                                     As SldWorks.Component2
                                    Dim CmpDoc                                    As SldWorks.ModelDoc2
                                    Dim Index                                     As Integer
                                    Dim vInnerArray                               As Variant
                                    Set swApp = Application.SldWorks
                                    Set swModel = swApp.ActiveDoc
                                    Set swAssy = swModel
                                  
                                  
                                    ' start timer
                                    nstart = Timer
                                  
                                  
                                    ' load all components
                                    vCmps = swAssy.GetComponents(False)
                                  
                                  
                                    ' resolve all lightweight components
                                    nStatus = swAssy.ResolveAllLightWeightComponents(False)
                                  
                                  
                                    ' build array
                                  
                                  
                                    ReDim CompArray(UBound(vCmps))
                                  
                                  
                                    ' itterate though components
                                    For i = 0 To UBound(vCmps)
                                        Set swCmp = vCmps(i)
                                        Debug.Print swCmp.GetPathName, swCmp.ReferencedConfiguration
                                        If (swCmp.GetSuppression = 3) Or (swCmp.GetSuppression = 2) Then
                                            Set CmpDoc = swCmp.GetModelDoc
                                            For Each vInnerArray In CompArray
                                                If IsEmpty(vInnerArray) Then
                                                    InnerArray(0) = swCmp.GetModelDoc.GetPathName
                                                    InnerArray(1) = swCmp.ReferencedConfiguration
                                                    InnerArray(2) = 1
                                                    InnerArray(3) = Index
                                                    Set InnerArray(4) = CmpDoc
                                                    CompArray(Index) = InnerArray
                                                    Index = Index + 1
                                                    Exit For
                                                ElseIf vInnerArray(0) = swCmp.GetModelDoc.GetPathName And vInnerArray(1) = swCmp.ReferencedConfiguration Then
                                                    vInnerArray(2) = Val(vInnerArray(2)) + 1
                                                    CompArray(vInnerArray(3)) = vInnerArray
                                                    Exit For
                                                End If
                                            Next
                                        End If
                                    Next i
                                    ReDim Preserve CompArray(Index - 1)
                                    For Each vInnerArray In CompArray
                                        Debug.Print "Count: " & vInnerArray(2), "Configuration: " & vInnerArray(1), "Path: " & vInnerArray(0)
                                        Set CmpDoc = vInnerArray(4)
                                        Set swCustPropMgr = CmpDoc.Extension.CustomPropertyManager(vInnerArray(1))
                                        lRetVal = swCustPropMgr.Delete2("MfgQty")
                                        lRetVal = swCustPropMgr.Add3("MfgQty", 30, vInnerArray(2), 0)
                                        lRetVal = swCustPropMgr.Set2("MfgQty", vInnerArray(2))
                                    Next
                                    ' show elapsed time
                                    Debug.Print "Time = " & Timer - nstart & " seconds"
                                  
                                End Sub
                                
                                  • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
                                    James Watson

                                    my version was much messier, i ended up making the array value swcmp then having to reload it and set it to cmpdoc before reading out the values in the files... this is much nicer thank you for all the effort. and now that i am iterating through a truncated list i can add per file actions like saving flat patterns to dxf, and and such.

                                    • Re: Count All instances of Assemblies, Parts, & Configurations thereof (Working code but horribly inefficient)
                                      James Watson

                                      ok so i have a basic question.

                                         

                                      how do i add to an array without knowing how long it will be. what i am currently doing is this:

                                      Function MFGprint() As Variant

                                      'check if drawing file exists

                                          If fso.FileExists(PrntDoc) = True Then

                                      '    Set swModel = swApp.OpenDoc6(PrntDoc, 3, 1, vInnerArray(1), Errors, Warnings)

                                      '        PrntDoc2 = PDFpath + "Mfg\" + ValOut + ".pdf"

                                          Debug.Print ValOut

                                      '        longstatus = swModel.SaveAs3(PrntDoc2, 0, 0)

                                      '        swApp.QuitDoc (PrntDoc)

                                              Else

                                              NoDrw

                                              End If

                                      End Function

                                       

                                       

                                      Function NoDrw()

                                              Debug.Print "Missing Drawing # " + ValOut

                                              ErrorArray(0) = ValOut

                                      End Function

                                      every time a drawing exists add it's ValOut to the array. so that once the program is over i can output that array in a message box. currently i just keep overwriting the first line of the array.