Assigning active assembly to an Array

I am trying to assign an the active assembly to an Array of type body. I found some code on the forums and I have been trying to tweak it to fit my needs.

I took the journal to process an assembly, so for each part in the assembly it will measure bodies:

If you run this code it will generate a List Window with each parts volume, centroid and name. However the values are 0, and it does not actually measure any parts in the assembly.

My guess is that I need to somehow assign the active assembly to the array theBodies in the code below, because as of right now the array is empty, which is why I think it isn't measuring anything in the assembly. What do you guys think?

'Journal to recursively walk through the assembly structure
' will run on assemblies or piece parts
' will step through all components of the displayed part
'NX 7.5, native
'NXJournaling.com February 24, 2012

Option Strict Off

Imports System
Imports NXOpen
Imports NXOpen.UF
Imports NXOpen.Assemblies

Module NXJournal

Public theSession As Session = Session.GetSession()
Public ufs As UFSession = UFSession.GetUFSession()
Public lw As ListingWindow = theSession.ListingWindow

Sub Main()
Dim workPart As Part = theSession.Parts.Work
Dim dispPart As Part = theSession.Parts.Display

lw.Open
Try
'Dim c As ComponentAssembly = dispPart.ComponentAssembly
'to process the work part rather than the display part,
' comment the previous line and uncomment the following line
Dim c As ComponentAssembly = workPart.ComponentAssembly
if not IsNothing(c.RootComponent) then
'*** insert code to process 'root component' (assembly file)
lw.WriteLine("Assembly: " & c.RootComponent.DisplayName)
lw.WriteLine(" + Active Arrangement: " & c.ActiveArrangement.Name)
'*** end of code to process root component
ReportComponentChildren(c.RootComponent, 0)
else
'*** insert code to process piece part
lw.WriteLine("Part has no components")
end if
Catch e As Exception
theSession.ListingWindow.WriteLine("Failed: " & e.ToString)
End Try
lw.Close

End Sub

'**********************************************************
Sub reportComponentChildren( ByVal comp As Component, _
ByVal indent As Integer)

Dim theBody As Body
Dim theBodies(0) As Body
theBodies(0) = theBody

For Each child As Component In comp.GetChildren()

theBodies = ActiveArrangement

Dim myMeasure As MeasureManager = theSession.Parts.Display.MeasureManager()
Dim massUnits(4) As Unit
massUnits(0) = theSession.Parts.Display.UnitCollection.GetBase("Area")
massUnits(1) = theSession.Parts.Display.UnitCollection.GetBase("Volume")
massUnits(2) = theSession.Parts.Display.UnitCollection.GetBase("Mass")
massUnits(3) = theSession.Parts.Display.UnitCollection.GetBase("Length")

Dim mb As MeasureBodies = Nothing
'.NewMassProperties(array of units, accuracy parameter, array of bodies to measure)
mb = myMeasure.NewMassProperties(massUnits, 0.99, theBodies)
mb.InformationUnit = MeasureBodies.AnalysisUnit.PoundFoot

lw.WriteLine("volume: " & mb.Volume.ToString & " ft^3")
lw.WriteLine("surface area: " & mb.Area.ToString & " ft^2")
lw.WriteLine("centroid: " & mb.Centroid.ToString & " (coordinates in feet)")

lw.WriteLine(New String(" ", indent * 2) & child.DisplayName())
'*** end of code to process component or subassembly
if child.GetChildren.Length <> 0 then
'*** this is a subassembly, add code specific to subassemblies
lw.WriteLine(New String(" ", indent * 2) & _
"* subassembly with " & _
child.GetChildren.Length & " components")
lw.WriteLine(New String(" ", indent * 2) & _
" + Active Arrangement: " & _
child.OwningPart.ComponentAssembly.ActiveArrangement.Name)
'*** end of code to process subassembly
else
'this component has no children (it is a leaf node)
'add any code specific to bottom level components
end if
reportComponentChildren(child, indent + 1)
Next
End Sub
'**********************************************************
Public Function GetUnloadOption(ByVal dummy As String) As Integer
Return Session.LibraryUnloadOption.Immediately
End Function
'**********************************************************

End Module

The following journal is close to what you want; it measures the surface area of each part in an assembly. There is a lot of code, but you should only need to make a few minor changes in Sub Main to get most of what you want.

This journal writes the info to the listing window. If you want it saved automatically, you can write the information window contents to a file or use a stream object as described here.

'NXJournaling.com
'March 25, 2014
'
'When run on an assembly, this journal will cycle through the components
'and measure the surface area of the body(ies) in the used reference set.
'The journal writes information for each component to the listing window along
'with the total surface area calculated. The base units of the assembly part
'are used for the calculation.
'The contents of the listing window can be saved as a text file and opened
'in Excel using the "delimited" (semi-colon delimiter) option.
'
'March 26, 2014
' updated to correctly handle "empty" and "entire part" reference sets
' updated to filter out sheet bodies - the measure body command output zero if
' any of the bodies in the array were sheet bodies

Option Strict Off
Imports System
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UF
Imports NXOpen.Assemblies

Module assembly_surface_area

Dim theSession As Session = Session.GetSession()
Dim ufs As UFSession = UFSession.GetUFSession()
Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow

Sub Main()

If IsNothing(theSession.Parts.Display) Then
'active part required
Return
End If

lw.Open()
Dim myMeasure As MeasureManager = theSession.Parts.Display.MeasureManager()
Dim massUnits(4) As Unit
massUnits(0) = theSession.Parts.Display.UnitCollection.GetBase("Area")
massUnits(1) = theSession.Parts.Display.UnitCollection.GetBase("Volume")
massUnits(2) = theSession.Parts.Display.UnitCollection.GetBase("Mass")
massUnits(3) = theSession.Parts.Display.UnitCollection.GetBase("Length")

Dim totalArea As Double = 0

Dim theBodies As New List(Of Body)

Dim myInfo As New NXJ_Assembly_info
myInfo.Part = theSession.Parts.Display

lw.WriteLine("component name; reference set; body count; surface area" & " (" & massUnits(0).Abbreviation() & ")")
Dim mb As MeasureBodies
For Each theComp As Assemblies.Component In myInfo.AllComponents

Dim refName As String = theComp.ReferenceSet
GetCompBodies(theComp, theBodies, refName)
mb = myMeasure.NewMassProperties(massUnits, 0.99, theBodies.ToArray)

lw.WriteLine(theComp.DisplayName & "; " & refName & "; " & theBodies.Count.ToString & "; " & mb.Area.ToString())
totalArea += mb.Area

Next

MakeWorkPart(Nothing)

lw.WriteLine("")
lw.WriteLine(";;total surface area;" & totalArea.ToString)

lw.WriteLine("")
lw.WriteLine("")
lw.WriteLine("components that could not be loaded")
For Each tComp As String In myInfo.NotLoaded
lw.WriteLine(tComp)
Next

lw.Close()

End Sub

Function SelComp(ByVal prompt As String, ByRef selObj As NXObject) As Selection.Response

Dim theUI As UI = UI.GetUI
Dim title As String = "Select a component"
Dim includeFeatures As Boolean = False
Dim keepHighlighted As Boolean = False
Dim selAction As Selection.SelectionAction = Selection.SelectionAction.ClearAndEnableSpecific
Dim cursor As Point3d
Dim scope As Selection.SelectionScope = Selection.SelectionScope.AnyInAssembly
Dim selectionMask_array(0) As Selection.MaskTriple

With selectionMask_array(0)
.Type = UFConstants.UF_component_type
.Subtype = UFConstants.UF_all_subtype
End With

Dim resp As Selection.Response = theUI.SelectionManager.SelectObject(prompt, title, scope, selAction, includeFeatures, keepHighlighted, selectionMask_array, selObj, cursor)
If resp = Selection.Response.ObjectSelected OrElse resp = Selection.Response.ObjectSelectedByName Then
Return Selection.Response.Ok
Else
Return Selection.Response.Cancel
End If

End Function

Sub MakeWorkPart(ByVal theComponent As Assemblies.Component)

Try
Dim partLoadStatus1 As PartLoadStatus
theSession.Parts.SetWorkComponent(theComponent, PartCollection.RefsetOption.Current, PartCollection.WorkComponentOption.Visible, partLoadStatus1)

workPart = theSession.Parts.Work
partLoadStatus1.Dispose()

Catch ex As NXException

End Try

End Sub

Sub GetCompBodies(ByVal tempComp As Assemblies.Component, ByRef refSetBodies As List(Of Body), ByVal refSetName As String)

MakeWorkPart(tempComp)

refSetBodies.Clear()

If refSetName.ToLower = "empty" Then
Return
End If

If refSetName.ToLower = "entire part" Then
For Each tempBody As Body In workPart.Bodies
If tempBody.IsSolidBody Then
refSetBodies.Add(tempBody)
End If
Next
Return
End If

For Each theRefSet As ReferenceSet In workPart.GetAllReferenceSets

'lw.WriteLine("wp ref set: " & theRefSet.Name)
If theRefSet.Name = refSetName Then
Dim refObjs() As NXObject = theRefSet.AskAllDirectMembers
'lw.WriteLine(".AskAllDirectMembers count: " & refObjs.Length.ToString)
For Each temp As NXObject In refObjs
'lw.WriteLine(temp.GetType.ToString)
If TypeOf (temp) Is Body Then
Dim theBody As Body = temp
If theBody.IsSolidBody Then
refSetBodies.Add(theBody)
End If
End If
Next
End If
Next

End Sub

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image when the NX session terminates
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.AtTermination

End Function

End Module

Public Class NXJ_Assembly_info

#Region "Private Variables"

Private Const Version As String = "0.2.0"

Private _theSession As Session = Session.GetSession()
Private _theUfSession As UFSession = UFSession.GetUFSession

Private _components As New List(Of Assemblies.Component)
Private _uniqueParts As New List(Of Part)
Private _allComponents As New List(Of Assemblies.Component)
Private _allUniqueParts As New List(Of Part)
Private _notLoaded As New List(Of String)

Private lg As LogFile = _theSession.LogFile

#End Region

#Region "Properties"

Private _isTCRunning As Boolean
Public ReadOnly Property IsTCRunning() As Boolean
Get
Return _isTCRunning
End Get
End Property

Private _thePart As Part = Nothing
Public Property Part() As Part
Get
Return _thePart
End Get
Set(ByVal value As Part)
_thePart = value
'Me.GetInfo()
Me.GetAllInfo()
End Set
End Property

Public ReadOnly Property AllComponents() As List(Of Component)
Get
Return _allComponents
End Get
End Property

Public ReadOnly Property AllUniqueParts() As List(Of Part)
Get
Return _allUniqueParts
End Get
End Property

Public ReadOnly Property Components As List(Of Component)
Get
Return _components
End Get
End Property

Public ReadOnly Property UniqueParts As List(Of Part)
Get
Return _uniqueParts
End Get
End Property

Public ReadOnly Property NotLoaded As List(Of String)
Get
Return _notLoaded
End Get
End Property

'Private _loadParts As Boolean = True
'Public Property LoadParts() As Boolean
' Get
' Return _loadParts
' End Get
' Set(ByVal value As Boolean)
' _loadParts = value
' End Set
'End Property

#End Region

Public Sub New()

lg.WriteLine("")
lg.WriteLine("~ NXJournaling.com: NXJ_Assembly_info object created ~")
lg.WriteLine(" ~~ Version: " & Version & " ~~")
lg.WriteLine(" ~~ Timestamp of run: " & DateTime.Now.ToString & " ~~")
lg.WriteLine("NXJ_Assembly_info Sub New()")

'determine if we are running under TC or native
_theUfSession.UF.IsUgmanagerActive(_isTCRunning)
lg.WriteLine("IsTcRunning: " & _isTCRunning.ToString)

lg.WriteLine("exiting Sub New")
lg.WriteLine("")

End Sub

Private Sub GetAllInfo()

'get all component info from assembly (all levels)
lg.WriteLine("Sub GetAllInfo()")

Try
Dim c As ComponentAssembly = Part.ComponentAssembly
If Not IsNothing(c.RootComponent) Then
'*** insert code to process 'root component' (assembly file)
lg.WriteLine(" part has components")
'*** end of code to process root component
lg.WriteLine(" calling GetAllComponentChildren")
GetAllComponentChildren(c.RootComponent)
Else
'*** insert code to process piece part, part has no components
lg.WriteLine(" part has no components")
End If
Catch ex As NXException
lg.WriteLine("Sub GetAllInfo error: " & ex.ErrorCode)
lg.WriteLine(" " & ex.Message)
End Try

lg.WriteLine("exiting Sub GetAllInfo()")

End Sub

Private Sub GetAllComponentChildren(ByVal comp As Component)

For Each child As Component In comp.GetChildren()
lg.WriteLine(child.DisplayName)
'*** insert code to process component or subassembly

If Me.LoadComponent(child) Then

_allComponents.Add(child)

Dim tempPart As Part = child.Prototype.OwningPart
If Not _allUniqueParts.Contains(tempPart) Then
_allUniqueParts.Add(tempPart)
End If

Else
'component could not be loaded
End If
'*** end of code to process component or subassembly
If child.GetChildren.Length <> 0 Then
'*** this is a subassembly, add code specific to subassemblies

'*** end of code to process subassembly
Else
'this component has no children (it is a leaf node)
'add any code specific to bottom level components

End If
Me.GetAllComponentChildren(child)
Next
End Sub

Private Sub GetInfo()

'get top level component info from assembly (no recursion)
lg.WriteLine("Sub GetInfo()")

Try
Dim c As ComponentAssembly = Part.ComponentAssembly
If Not IsNothing(c.RootComponent) Then
'*** insert code to process 'root component' (assembly file)
lg.WriteLine(" part has components")
'*** end of code to process root component
lg.WriteLine(" calling GetComponentChildren")
Me.GetComponentChildren(c.RootComponent)
Else
'*** insert code to process piece part, part has no components
lg.WriteLine(" part has no components")
End If
Catch ex As NXException
lg.WriteLine("Sub GetInfo error: " & ex.ErrorCode)
lg.WriteLine(" " & ex.Message)
End Try

lg.WriteLine("exiting GetInfo()")

End Sub

Private Sub GetComponentChildren(ByVal comp As Component)

For Each child As Component In comp.GetChildren()
'*** insert code to process component or subassembly
_components.Add(child)
Dim tempPart As Part = child.Prototype.OwningPart
If Not _uniqueParts.Contains(tempPart) Then
_uniqueParts.Add(tempPart)
End If
'*** end of code to process component or subassembly
If child.GetChildren.Length <> 0 Then
'*** this is a subassembly, add code specific to subassemblies

'*** end of code to process subassembly
Else
'this component has no children (it is a leaf node)
'add any code specific to bottom level components

End If
Next
End Sub

Private Function LoadComponent(ByVal theComponent As Component) As Boolean

lg.WriteLine("Sub LoadComponent()")

Dim thePart As Part = theComponent.Prototype.OwningPart

Dim partName As String = ""
Dim refsetName As String = ""
Dim instanceName As String = ""
Dim origin(2) As Double
Dim csysMatrix(8) As Double
Dim transform(3, 3) As Double

'save the current assembly load options
Dim curLoadOptions As UFAssem.Options
_theUfSession.Assem.AskAssemOptions(curLoadOptions)

'When loading a component, the current load options are used.
'If the current load option is set to "from folder" or "search folders" and the component
'is not in one of those folders, it will not be found.
'Change the load options to "as-saved" to look for the component in the last known location.

'To Do:
'If the component is not found using the "as-saved" option, try changing to the user's
'"from folder" or "search folders" option to see if it can be found.

'change the load options to "as-saved"
Dim newLoadOptions As UFAssem.Options
_theUfSession.Assem.AskAssemOptions(newLoadOptions)
newLoadOptions.load_options = UFConstants.UF_ASSEM_load_as_saved
_theUfSession.Assem.SetAssemOptions(newLoadOptions)

Try
If thePart.IsFullyLoaded Then
'component is fully loaded
Else
'component is partially loaded
End If
lg.WriteLine(" component: " & theComponent.DisplayName & " is already partially or fully loaded")
lg.WriteLine(" return: True")
lg.WriteLine("exiting Sub LoadComponent()")
lg.WriteLine("")
Return True
Catch ex As NullReferenceException
'component is not loaded
Try
lg.WriteLine(" component not loaded, retrieving part information")
_theUfSession.Assem.AskComponentData(theComponent.Tag, partName, refsetName, instanceName, origin, csysMatrix, transform)
lg.WriteLine(" component part file: " & partName)

Dim theLoadStatus As PartLoadStatus
_theSession.Parts.Open(partName, theLoadStatus)

'_theUfSession.Assem.SetAssemOptions(curLoadOptions)

If theLoadStatus.NumberUnloadedParts > 0 Then
If theLoadStatus.NumberUnloadedParts > 1 Then
lg.WriteLine(" problem loading " & theLoadStatus.NumberUnloadedParts.ToString & " components")
Else
lg.WriteLine(" problem loading 1 component")
End If

Dim allReadOnly As Boolean = True
For i As Integer = 0 To theLoadStatus.NumberUnloadedParts - 1
lg.WriteLine("part name: " & theLoadStatus.GetPartName(i))
lg.WriteLine("part status: " & theLoadStatus.GetStatus(i))
If theLoadStatus.GetStatus(i) = 641058 Then
'read-only warning, file loaded ok
Else
'641044: file not found
allReadOnly = False
If Not _notLoaded.Contains(partName) Then
_notLoaded.Add(partName)
End If
End If
lg.WriteLine("status description: " & theLoadStatus.GetStatusDescription(i))
lg.WriteLine("")
Next
If allReadOnly Then
lg.WriteLine(" 'read-only' warnings only")
lg.WriteLine(" return: True")
Return True
Else
'warnings other than read-only...
lg.WriteLine(" return: False")
lg.WriteLine("exiting Sub LoadComponent()")
lg.WriteLine("")
Return False
End If
Else
lg.WriteLine(" component(s) loaded successfully")
lg.WriteLine(" return: True")
lg.WriteLine("exiting Sub LoadComponent()")
lg.WriteLine("")
Return True
End If

Catch ex2 As NXException
lg.WriteLine(" Load error: " & ex2.Message)
lg.WriteLine(" error code: " & ex2.ErrorCode)
lg.WriteLine(" return: False")
lg.WriteLine("exiting Sub LoadComponent()")
lg.WriteLine("")
If ex2.Message.ToLower = "file not found" Then
If Not _notLoaded.Contains(partName) Then
_notLoaded.Add(partName)
End If
End If
Return False
End Try
Catch ex As NXException
'unexpected error
lg.WriteLine(" Error in Sub LoadComponent: " & ex.Message)
lg.WriteLine(" return: False")
lg.WriteLine("exiting Sub LoadComponent()")
lg.WriteLine("")
Return False
Finally
'Reset assembly load option to the user's preference.
_theUfSession.Assem.SetAssemOptions(curLoadOptions)

End Try

End Function

End Class

It Works! Thank you. I was wondering however, how can I change how many parts it measures at once? lets say instead of measuring each part like the way it is now, it measures each sub assembly. So if the assembly had 20 model parts, but 4 subassemblies and 5 model parts under each sub-assembly, then the listing window would only display 4 volumes.