Showing results for 
Search instead for 
Do you mean 
Reply

Handling Non-Geometric parts

Hello I am having some issues with my code, and I could use a second opinion.

 

I am trying to use a journal to get the names of all non-geometric components inside my assembly. I have an If statement to check for non-geo parts, however, when the code gets to these parts, it tells me the parts are not currently loaded.

 

So then I added some code to check for parts that are not loaded, and if they are not loaded, then load the part. After loading the part, check to see if it is non-geo. If it is, print the name in the listing window.

 

However, it isn't working as I intend it too. It gives me an error saying that "An Object reference isn't set to an instance of an object", and the line in question is line 66:

 

   If Not thePart.IsFullyLoaded Then

 

My issue is that I am not sure what to assign to thePart when it finds a part that is not loaded. If you look at my code, it appears that thePart is already assigned an object reference.

 

 thePart = kids(ii).Prototype.OwningPart

 

What do I need to do make this code work the way I want it to

 

Imports System
Imports NXOpen
Imports NXOpen.UF
Imports NXOpen.Assemblies
Imports System.Collections.Generic

Module screen_for_non_geometric_parts
    Dim theSession As Session = Session.GetSession()
    Dim theUFSession As UFSession = UFSession.GetUFSession()
    Dim workPart As Part = theSession.Parts.Work

    Public Sub Main(ByVal args As String())
        Dim displayPart As Part = theSession.Parts.Display

        Dim markId1 As NXOpen.Session.UndoMarkId = theSession.SetUndoMark( _
            NXOpen.Session.MarkVisibility.Visible, "Reorder Components")

        Dim theOrders As Assemblies.ComponentOrder() = Nothing
        displayPart.ComponentAssembly.GetComponentOrders(theOrders)

        Echo(displayPart.Leaf & " has " & theOrders.Length & " ComponentOrder objects")

        For Each anOrder As Assemblies.ComponentOrder In theOrders

            anOrder.Activate()
            theSession.UpdateManager.DoUpdate(markId1)

            Echo(anOrder.Name & ":")
            WalkAssembyTree(displayPart.ComponentAssembly.RootComponent, anOrder, "")

            theUFSession.Ui.DisplayMessage(anOrder.Name, 1)

            theSession.UndoToMark(markId1, "")
        Next

        theSession.DeleteUndoMarksUpToMark(markId1, "", False)

    End Sub

    Sub WalkAssembyTree(ByVal theComponent As NXOpen.Assemblies.Component,
                        ByVal theOrder As NXOpen.Assemblies.ComponentOrder,
                        ByVal indent As String)

	   Dim thePart as part
        Dim kids As Assemblies.Component() = theComponent.GetChildren()
        If kids.Length = 0 Then Return ' Not an assembly or sub-assembly 

        indent = indent + "  "

        ' This does not always work - see PR 7421390 
        ' Testing in the Toycar assembly only Chronological returns any Children 

        kids = theOrder.AskChildrenOrder(theComponent)

        For ii As Integer = 0 To kids.Length - 1
            Echo(indent & kids(ii).DisplayName)


            thePart = kids(ii).Prototype.OwningPart

            If IsNothing(thePart) Then
	       

		 If Not thePart.IsFullyLoaded Then

                'component is partially loaded.
                Dim pls As PartLoadStatus
                Try
                    pls = thePart.LoadThisPartFully

                Catch ex As NXException
                    Echo("NX exception: " & ex.Message)
                    Exit Sub
                Catch ex As Exception
                    Echo("Exception: " & ex.Message)
                    Exit Sub
                End Try

            End If

            ' screen for non-geometric components

            If kids(ii).GetNonGeometricState().Equals(True) Then
                'this is a non-geometric component...skip it.
                Echo(Kids(ii).ToString)

            End If

                'Component is not loaded, probably could not be found
                'with the current search options.

            End If

            

            '========================================================

            ' screen for non-geometric components


            ' Process parts here

            Dim thisPart As Part = kids(ii).Prototype()

            processThisPart(thisPart, indent)


            '========================================================



            WalkAssembyTree(kids(ii), theOrder, indent)
        Next
    End Sub

    Public Sub processThisPart(ByVal p As Part, ByVal ind As String)

        Dim theBodies() As Body = p.Bodies.ToArray()
        Echo(ind & "Bodies in part: " & theBodies.GetLength(0).ToString())

        For Each thisBody As Body In theBodies

            Echo(ind & thisBody.ToString())

            '======================== Other desired processing here


        Next

    End Sub

    Sub Echo(ByVal output As String)
        theSession.ListingWindow.Open()
        theSession.ListingWindow.WriteLine(output)
        theSession.LogFile.WriteLine(output)
    End Sub

    Public Function GetUnloadOption(ByVal arg As String) As Integer
        Return Session.LibraryUnloadOption.Immediately
    End Function

End Module
8 REPLIES

Re: Handling Non-Geometric parts

[ Edited ]

You get an error because you attempt to use an object's properties/methods when you know the object reference = Nothing.

 

If IsNothing(thePart) Then
	'this code will only run if "thePart = Nothing"
	
	'error because you attempt to use an object's properties/methods when you know the object reference = Nothing
    If Not thePart.IsFullyLoaded Then

 

If "thePart = Nothing", you know it is not loaded.

Re: Handling Non-Geometric parts

Wow, well all of a sudden that seems very simple to me. Thanks.

 

I am also having another issue. I reworked the code to measure volume for each body. The volumes that my code is producing is not correct. It is almost twice what it should be.

 

I am trying to figure out why. Previously I thought it was because I was passing a collection of  bodies to be measured, instead of just 1, as shown below:

 

mb = myMeasure.NewMassProperties(massUnits, 1, (theBodies))

 

So I reworked the code to pass in  a single body instead of a collection.

 

Dim singleBodyArray(0) As Body

singleBodyArray(0) = thisBody

 

mb = myMeasure.NewMassProperties(massUnits, 1, (singleBodyArray))

 

However, my numbers are even more skewed now. My reworked code is below:

 

Also, if I wanted to identify what the parents where in the assembly, how would I do so?

 

My goal is to make them bold on my spreadsheet.

 

Thanks for your help!

 

 

  Public Sub ProcessNXData()
        Dim displayPart As Part = theSession.Parts.Display
        Dim workPart As Part = theSession.Parts.Work

        Dim markId1 As NXOpen.Session.UndoMarkId = theSession.SetUndoMark( _
            NXOpen.Session.MarkVisibility.Visible, "Reorder Components")

        Dim theOrders As Assemblies.ComponentOrder() = Nothing
        displayPart.ComponentAssembly.GetComponentOrders(theOrders)

        Echo(displayPart.Leaf & " has " & theOrders.Length & " ComponentOrder objects")

        'following line is used to print LOA name to excel
        Dim c As ComponentAssembly = workPart.ComponentAssembly
        Cells(2, 2).Value = c.RootComponent.GetStringAttribute("DB_PART_NAME")

        'following line is used to print LOA # to excel
        Dim rootDispName As String = (c.RootComponent.DisplayName)
        Cells(3, ) = rootDispName

        For Each anOrder As Assemblies.ComponentOrder In theOrders

            anOrder.Activate()
            theSession.UpdateManager.DoUpdate(markId1)

            Echo(anOrder.Name & ":")
            WalkAssembyTree(displayPart.ComponentAssembly.RootComponent, anOrder, "")

            'ufs.Ui.DisplayMessage(anOrder.Name, 1)

            theSession.UndoToMark(markId1, "")
        Next

        theSession.DeleteUndoMarksUpToMark(markId1, "", False)

    End Sub

    Sub WalkAssembyTree(ByVal theComponent As NXOpen.Assemblies.Component,
                        ByVal theOrder As NXOpen.Assemblies.ComponentOrder,
                        ByVal indent As String)
        Dim thePart As Part
        Dim kids As Assemblies.Component() = theComponent.GetChildren()
        If kids.Length = 0 Then Return ' Not an assembly or sub-assembly 

        indent = indent + "  "

        ' This does not always work - see PR 7421390 
        ' Testing in the Toycar assembly only Chronological returns any Children 
        If theOrder.OrderType = Order.Type.ChronologicalComponent Then

            kids = theOrder.AskChildrenOrder(theComponent)

            For ii As Integer = 0 To kids.Length - 1
                Echo(indent & kids(ii).DisplayName)

                '========================================================

                thePart = kids(ii).Prototype.OwningPart
                If IsNothing(thePart) Then
                    Continue For
                    'Component is not loaded, probably could not be found
                    'with the current search options.
                End If

                If Not thePart.IsFullyLoaded Then
                    'component is partially loaded.
                    Dim pls As PartLoadStatus
                    Try
                        pls = thePart.LoadThisPartFully

                    Catch ex As NXException
                        Echo("NX exception: " & ex.Message)
                        Exit Sub
                    End Try
                End If

                If kids(ii).GetNonGeometricState().Equals(True) Then
                    'this is a non-geometric component...
                    ' don't measure it, just record it
                    Cells(row, 3).Value = (kids(ii).Name)
                Else

                    'do nothing

                End If


                If kids(ii).HasUserAttribute("DB_PART_NAME", NXObject.AttributeType.String, -1) = True Then

                    Cells(row, 2).Value = (indent & kids(ii).GetStringAttribute("DB_PART_NAME"))

                Else

                    'do nothing  

                End If

                    Echo("kids name: <<" & kids(ii).Name & ">>")

                Cells(row, 3).Value = (kids(ii).Name)

                    'Measure Volume of parts

                    Dim theBodies() As Body = thePart.Bodies.ToArray()

                Echo(indent & "Bodies in part: " & theBodies.ToString())

                If kids.Length = 0 Then
                    Cells(row, 2).font.bold = True
                End If


                For Each thisBody As Body In theBodies
                    Dim mb As MeasureBodies
                    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 singleBodyArray(0) As Body

                    singleBodyArray(0) = thisBody


                    mb = myMeasure.NewMassProperties(massUnits, 1, (singleBodyArray))
                    mb.InformationUnit = MeasureBodies.AnalysisUnit.PoundFoot

                    If thisBody.HasUserAttribute("DB_PART_NAME", NXObject.AttributeType.String, -1) = True Then

                        Cells(row, 2).Value = (indent & thisBody.GetStringAttribute("DB_PART_NAME"))

                    Else

                        'do nothing  

                    End If

                    Echo("thisBody Name: <<" & thisBody.ToString & ">>")


                    Cells(row, 3).Value = (thisBody.ToString)

                    Cells(row, 4).Value = mb.Volume.ToString

                Next

                row = row + 1

                ' Process parts here

                Dim thisPart As Part = kids(ii).Prototype().OwningPart

                'processThisPart(thisPart, indent)

                '========================================================


                WalkAssembyTree(kids(ii), theOrder, indent)


            Next



        Else


        End If


    End Sub

    Sub Echo(ByVal output As String)

        theSession.ListingWindow.Open()
        theSession.ListingWindow.WriteLine(output)
        theSession.LogFile.WriteLine(output)

    End Sub

 

 

 

 

 

Re: Handling Non-Geometric parts

In your code you measure every body (solid and sheet body) in each part file. Is every body in the part file used in the component reference set in the assembly?

 

In other words, are you using reference sets to filter out some of the bodies in the part file? If so, what you measure in the part file will NOT match what you see in the assembly file.

Re: Handling Non-Geometric parts

So by looping through with the thisBody in theBodies loop, I am processing volume in context of the .prt file, not the assembly file? I don't comletely understand the difference because I am working in TeamCenter and I never usually have to work with files directly.

 

Is this where the difference between instance bodies and prototype bodies comes into play?

 

I did a quick check on the ref set of all of the parts in the assembly and they are in the same ref set. I haven't filtered any parts.

 

 

Re: Handling Non-Geometric parts

"So by looping through with the thisBody in theBodies loop, I am processing volume in context of the .prt file, not the assembly file?"

 

This is correct. If each of your part files contains only a single solid body, the difference probably won't affect you. If, however, you have multiple solid/sheet bodies in your part file and only one is used to represent the component in the assembly, your code will lead to incorrect results.

 

If each of your part files has only a single solid body and you are measuring only the volume, the result from using the prototype bodies vs. the occurrence bodies will be the same in most cases*. If you were attempting to calculate the center of gravity of the assembly, you would need to measure the bodies in context of the assembly.

 

*There are times when the body in the part file does not match what is seen in the assembly file. Promote body, assembly cut, hole series, and deform part are some of the edge cases to watch out for.

Re: Handling Non-Geometric parts

Okay, I understand. So does that mean I have been using the wrong code to try and process my models? Because I need the Volume and centers in context of the assembly.

 

If so, could I use this function to fix it?

 

 

Function AskAllBodies(ByVal thePart As Part) As List(Of Body)

        Dim theBodies As New List(Of Body)

        Dim aBodyTag As Tag = Tag.Null
        Do
            theUfSession.Obj.CycleObjsInPart(thePart.Tag, _
                UFConstants.UF_solid_type, aBodyTag)
            If aBodyTag = Tag.Null Then
                Exit Do
            End If

            Dim theType As Integer, theSubtype As Integer
            theUfSession.Obj.AskTypeAndSubtype(aBodyTag, theType, theSubtype)
            If theSubtype = UFConstants.UF_solid_body_subtype Then
                theBodies.Add(Utilities.NXObjectManager.Get(aBodyTag))

            End If
        Loop While True

        Return theBodies

    End Function

 

 

Re: Handling Non-Geometric parts

I've not had time to test your code, but I'm pretty sure that if you run that code on the assembly file it will get all the bodies in the assembly context*. However, it may take some work to determine which body corresponds to which component.

 

* You may get more than you bargained for. If you run that code in a drawing file that has section views, you will get "section bodies" as well as the normal bodies.

Re: Handling Non-Geometric parts

I got this code from GTAC as an example of what will get the correct centroids from an assembly. I am wondering if somebody can help me to integrate this code into my code above so I can get the same results when getting centroids.

 

Option Strict Off

Imports System
Imports NXOpen
Imports NXOpen.UF
Imports NXOpen.UI
Imports NXOpen.Assemblies
Imports NXOpen.Utilities

Module report_assembly_body_centroids

    Dim theSession As Session = Session.GetSession()
    Dim ufs As UFSession = UFSession.GetUFSession()

    Sub Main()

        ' This assumes the assembly is loaded.

        Dim dp As Part = theSession.Parts.Display
        theSession.EnableRedo(False)

        Dim nextBody As NXOpen.Tag = NXOpen.Tag.Null

        Do
            Dim t As Integer, st As Integer
            Dim isOcc As Boolean = False

            ufs.Obj.CycleTypedObjsInPart(dp.Tag, UFConstants.UF_solid_type, nextBody)
            If nextBody.Equals(NXOpen.Tag.Null) Then
                Exit Do
            End If
            ufs.Obj.AskTypeAndSubtype(nextBody, t, st)
            If st <> UFConstants.UF_solid_body_subtype Then
                Continue Do
            End If
            isOcc = ufs.Assem.IsOccurrence(nextBody)
            If isOcc.Equals(True) Then
                Echo("Found occurrence body: " & nextBody.ToString())
            End If

            Dim theNXOM As NXObjectManager = theSession.GetObjectManager
            Dim theObj As NXObject = theNXOM.GetTaggedObject(nextBody)
            Dim theBody As Body = CType(theObj, Body)


            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 singleBodyArray() As Body = {theBody}
            Dim mb As MeasureBodies = myMeasure.NewMassProperties(massUnits, 1, singleBodyArray)
            mb.InformationUnit = MeasureBodies.AnalysisUnit.GramMillimeter

            Dim centroidalPoint As Point3d = mb.Centroid()
            Echo("Centroid: " & centroidalPoint.X & "  " & centroidalPoint.Y & "  " & centroidalPoint.Z)

        Loop Until nextBody.Equals(NXOpen.Tag.Null)

    End Sub

    Sub Echo(ByVal output As String)

        theSession.ListingWindow.Open()
        theSession.ListingWindow.WriteLine(output)
        theSession.LogFile.WriteLine(output)

    End Sub

    Public Function GetUnloadOption(ByVal dummy As String) As Integer
        Return Session.LibraryUnloadOption.Immediately
    End Function

End Module