Changing the reference set

Hello, I'm in need of some assistance in attempting to change the reference set of some components. However I keep on getting errors on the "replaceReferenceSet" calls when attempting to switch the reference set to a true body.

For example:


Sub Main()

Lw.Open()

Dim cvInsertComponent As Component = searchAssembly(displayComponent, "component name string")
Dim cvInsertPart As Part = cvInsertComponent.Prototype.OwningPart
workPart.ComponentAssembly.ReplaceReferenceSet(cvInsertComponent, "TRUE")

Dim ejInsertComponent As Component = searchAssembly(displayComponent, "component name string")
Dim ejInsertPart As Part = ejInsertComponent.Prototype.OwningPart
workPart.ComponentAssembly.ReplaceReferenceSet(ejInsertComponent, "TRUE")

processBodies(cvInsertPart)
processBodies(ejInsertPart)

Lw.Close()
End Sub

Produces the error: NXOpen.NXException: First parameter is invalid...method stub

Sub Main()

Lw.Open()

Dim cvInsertComponent As Component = searchAssembly(displayComponent, "component name string")
Dim ejInsertComponent As Component = searchAssembly(displayComponent, "component name string")

Dim refSetToTrue(2) As Component
refSetToTrue(0) = cvInsertComponent
refSetToTrue(1) = ejInsertComponent

workPart.ComponentAssembly.ReplaceReferenceSetInOwners("TRUE", refSetToTrue)

Dim cvInsertPart As Part = cvInsertComponent.Prototype.OwningPart
Dim ejInsertPart As Part = ejInsertComponent.Prototype.OwningPart

processBodies(cvInsertPart)
processBodies(ejInsertPart)

Lw.Close()
End Sub

Produces the error: NXOpen.NXException: Incorrect object for this operation... method stub




Other possibly useful code:

Module NXJournal

Public NxSession As Session = Session.GetSession()
Public workPart As Part = NxSession.Parts.Work
Public ReadOnly displayComponent As Component = NxSession.Parts.Display.ComponentAssembly.RootComponent
Public Lw As ListingWindow = NxSession.ListingWindow

Sub Main()

Double check your "searchAssembly" sub/function to make sure that it is:

  1. returning a component
  2. returning the correct component

Here is the searchAssembly code:


Function searchAssembly(ByVal parentComponent As Component, ByVal searchString As String) As Component

If parentComponent.Name.Equals(searchString) Then
Lw.WriteFullline("Found: " & parentComponent.Name)
Return parentComponent
End If

For Each childComponent As Component In parentComponent.GetChildren
Dim returnComponent As Component = searchAssembly(childComponent, searchString)
If returnComponent IsNot Nothing Then
Return returnComponent
End If
Next
Return Nothing
End Function



The Found: parentComponent.name is returning the correct component name. I additionally added another check in sub main:



Sub Main()

Lw.Open()

Dim cvInsertComponent As Component = searchAssembly(displayComponent, "component name")
Dim cvInsertPart As Part = cvInsertComponent.Prototype.OwningPart
Lw.WriteFullline(cvInsertComponent.Name)
workPart.ComponentAssembly.ReplaceReferenceSet(cvInsertComponent, "TRUE")



This also returns the correct component name. This component name (AFAIK) is and should be unique to the assembly. The code returns the same error as before right after printing out the correct name.

NX 8.5
NX 9.0

According to the help file, the .ReplaceReferenceSet method only works on top level components. In your case, that means components directly owned by the work part. Your "searchAssembly" routine searches the entire assembly, not just the top level. If you want to change the reference set regardless of the direct owner, you would be better off using the .ReplaceReferenceSetInOwners method. You have indicated that that method is also throwing errors, you can examine the error list to get a better idea of what is going wrong and what actions to take.



Something like:


Dim myErrList As ErrorList
myErrList = workPart.ComponentAssembly.ReplaceReferenceSetInOwners("TRUE", refSetToTrue)

For i As Integer = 0 To myErrList.Length - 1
lw.WriteLine("Error number: " & i.ToString)
lw.WriteLine(" " & myErrList.GetErrorInfo(i).ErrorCode & ": " & myErrList.GetErrorInfo(i).Description)
lw.WriteLine("")
Next

So here's the problem, my code keeps crashing before I can look at the error list.


And when I do a try-catch to prevent it from crashing, the error list never gets filled up from the replaceReferenceSetInOwners call. This results in a null reference exception when trying to access the error list.

Code
Dim refSetToTrue(2) As Component
refSetToTrue(0) = cvInsertComponent
refSetToTrue(1) = ejInsertComponent

Dim errorList As ErrorList = Nothing

Try
'Code will crash here, never allowing the errorList to be created
errorList = workPart.ComponentAssembly.ReplaceReferenceSetInOwners("TRUE", refSetToTrue)

For i As Integer = 0 To errorList.Length - 1
Lw.WriteLine("Error number: " & i.ToString)
Lw.WriteLine(" " & errorList.GetErrorInfo(i).ErrorCode & ": " & errorList.GetErrorInfo(i).Description)
Lw.WriteLine("")
Next
Catch nxe As NXException
Lw.WriteFullline(nxe.ToString)

End Try

NX 8.5
NX 9.0

In the catch block, list the error code and description.




Catch nxe As NXException
Lw.WriteFullline(nxe.ErrorCode & ": " & nxe.Message)

End Try

NXOpen.NXException: Incorrect object for this operation.
at NXOpen.Assemblies.ComponentAssembly.ReplaceReferenceSetInOwners(String newReferenceSet, Component[] components)
at NXJournal.Main() in C:\line 34

NX 8.5
NX 9.0

650004: Incorrect object for this operation.

NX 8.5
NX 9.0

Rather than:
workPart.ComponentAssembly.ReplaceReferenceSet(cvInsertComponent, "TRUE")

Try:
cvInsertComponent.DirectOwner.ReplaceReferenceSet(cvInsertComponent, "TRUE")

Worked like a charm. Any ideas on why?

NX 8.5
NX 9.0

As I mentioned in an earlier post, .ReplaceReferenceSet only works on top level components, this means it must be called from the component's direct parent assembly. My best guess is the component you are interested in is 2 or more levels deep in the assembly structure. When you execute the line:

workPart.ComponentAssembly.ReplaceReferenceSet(cvInsertComponent, "TRUE")

you get an error because the component is not directly owned by the work part (i.e. it is not a "top level" component in the work part assembly structure). The line of code I gave you will call the .ReplaceReferenceSet method from the direct owner of the component.

Ah yes. Thank you.



Do you have any ideas on why the .ReplaceReferenceSetInOwners was returning errors? I'm wondering because I've used this method call successfully in the past.

NX 8.5
NX 9.0

Hello,

I am trying to realise just the same sort of thing: Change the RefSets for all components that are the children of the current work part component.
Unfortunately I can't find the clue how to do that in journalling. So I hoped to find some working code that I could adapt to my needs. Now I see that I am not able to make things work using these sniplets only. There seems to exist some context that I don't know.


Would you mind to post your solution code here to help me understand how the whole job can be done? That would be a great help.

/UdoMM

The code below will loop through the displayed part and attempt to change each top level's component reference set to the one named "SOLID". If the component does not have a reference set with that name it will be skipped. Change the refSetName constant to your desired value before running.




Option Strict Off

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

Module Module19

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

Const refSetName As String = "SOLID"

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

lw.Open()

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 IsNothing(c.RootComponent) Then
'no components to process, exit journal
Return
End If

For Each child As Component In c.RootComponent.GetChildren()
Try
child.DirectOwner.ReplaceReferenceSet(child, refSetName)
Catch ex As NXException
'add code to handle error condition
End Try
Next

lw.Close()

End Sub

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

End Module

That's much more straightforward than my result - Thank you!

/UdoMM

Can we make this work recursively for all levels instead of the first level?

Fortunately for us, there is a method in the NXOpen API called ReplaceReferenceSetInOwners that will do all the work for us. The code below will create a reference set with the specified name in each component part (edit the value of "myRefSetName" to the name of the reference set you want to create before running the code). The journal does not save any of the parts, they will need to be saved if you wish to keep the changes.

Also of note, once the reference set is created in the parts, you can make use the the NX assembly load options (reference sets option) to set each component in the assembly to use a specified reference set when the assembly is opened.

'NXJournaling.com
'June 1, 2022

'Process an assembly:
'create a reference set in each part, add all solid bodies from the part to the new ref set
'change the value of Const myRefSetName to your desired reference set name

'update June 7, 2022
'update components to use the newly created reference set.

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

Module Module2

Dim theSession As Session = Session.GetSession()

Sub Main()

If IsNothing(theSession.Parts.Display) Then
MessageBox.Show("Active Part Required", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End If

Dim lw As ListingWindow = theSession.ListingWindow
lw.Open()

'change the following line to the reference set name you want to update/create
Const myRefSetName As String = "TEST"

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

For Each tempPart As Part In myAsmInfo.AllUniqueParts
lw.WriteLine(tempPart.Leaf)

'get MODEL reference set
Dim myRefSet As ReferenceSet = Nothing
myRefSet = GetRefSet(tempPart, myRefSetName)

'get all solid bodies in part
Dim partSolidBodies As New List(Of Body)
For Each tempBody As Body In tempPart.Bodies
If tempBody.IsSolidBody Then
partSolidBodies.Add(tempBody)
End If
Next

'add solid bodies to MODEL reference set
myRefSet.AddObjectsToReferenceSet(partSolidBodies.ToArray)

lw.WriteLine(partSolidBodies.Count.ToString & " bodies added to refset: " & myRefSetName)
lw.WriteLine("")
Next

'lw.WriteLine("")
'lw.WriteLine("Parts that could not be loaded")
'lw.WriteLine("------------------------------")
'For Each temp As String In myAsmInfo.NotLoaded
' lw.WriteLine(temp)
'Next

Dim errorList As ErrorList = Nothing

Try
errorList = myAsmInfo.Part.ComponentAssembly.ReplaceReferenceSetInOwners(myRefSetName, myAsmInfo.AllComponents.ToArray)

For i As Integer = 0 To errorList.Length - 1
lw.WriteLine("Error number: " & i.ToString)
lw.WriteLine(" " & errorList.GetErrorInfo(i).ErrorCode & ": " & errorList.GetErrorInfo(i).Description)
lw.WriteLine("")
Next
Catch nxe As NXException
lw.WriteFullline("error: " & nxe.Message)

End Try

End Sub

Function GetRefSet(ByVal thePart As Part, ByVal refSetName As String) As ReferenceSet

Dim theReferenceSet As ReferenceSet = Nothing
For Each myRefSet As ReferenceSet In thePart.GetAllReferenceSets()
If myRefSet.Name.ToUpper() = refSetName.ToUpper Then
theReferenceSet = myRefSet
End If
Next

If IsNothing(theReferenceSet) Then
'the specified reference set does not exist, create it
Dim markId1 As NXOpen.Session.UndoMarkId = Nothing
markId1 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Reference Sets")

theReferenceSet = thePart.CreateReferenceSet()
theReferenceSet.SetName(refSetName)

Dim nErrs1 As Integer = Nothing
nErrs1 = theSession.UpdateManager.DoUpdate(markId1)

End If

Return theReferenceSet

End Function

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately

End Function

End Module

'To process "non-geometric" components, set the environment variable: UGII_ALLOW_NGC_IN_UGOPEN {YES|NO}

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()
_allUniqueParts.Add(_thePart)
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

Thanks, NXJournaling. This is of huge help, I am trying to make another journal that will only change the reference set for all loaded components to "MODEL" if there is no such reference set skip to the next component. The reason is, that it might be useful on assemblies that do not need a "MODEL" referenceset created but only set it for all components.

HI can i get macro which will consider the STD component,solids and surface while setting the "Reference Set" so that I can avoid the default UG NX steps for the same.

You want to create a new reference set and add all the solid and sheet bodies to it?

I'm not sure what "consider the STD component" means.

Can you list the steps you currently take in NX to do what it is that you need to accomplish?

STD component means the item I take from library and those file will be read only cannot be changed.
Ex: Sankyo Elements,Misumi etc

Do you have write access to the part in which you want to create the reference set?

Can you list the steps you currently take in NX to do what it is that you need to accomplish?

Like "xxxxxxx-main" in this the sub files will come
xxxxxxx-main
--xxxxxxxx-lower assy
- xxxxxxx-lower shoe
-- xxxxxxx-upper assy
- xxxxxxx-upper shoe

in all sub file respective standard item will come.so without opening a sub file in a new window i want make a reference set for all main and sub files.

The code in the following link shows how to process each component in an assembly:
http://nxjournaling.com/content/creating-subroutine-process-all-componen...

Combine that with the function to create reference sets found here:
http://nxjournaling.com/comment/1359#comment-1359

And you can quickly create a reference set in every component.