Occasionally you will want to process all the parts in a given assembly. The journal in this tutorial will recursively step through your assembly structure allowing you to process each part. If the code is run as provided, it will open a listing window and print the name of each component and the current active arrangement if it is a subassembly. This journal was developed on NX 7.5 native; therefore if you are running Teamcenter (or other PLM) some changes will need to be made.
'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) For Each child As Component In comp.GetChildren() '*** insert code to process component or subassembly 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
So how does it work?
First, we grab the ComponentAssembly of the displayed part. The ComponentAssembly object gives us access to the components and allows us to do things such as add components, move components, query components, set attributes, etc. If the RootComponent property is Nothing, then we are dealing with a piece part (no components). If the RootComponent property indicates this part is an assembly, we call the ReportComponentChildren subroutine; this is where the heavy lifting takes place. Near the end of the ReportComponentChildren subroutine a call is made to the subroutine: ReportComponentChildren.
To understand recursion, you must first understand recursion
When a subroutine or function calls itself, it is called recursion. Recursion is very useful when dealing with tree type structures such as a directory listing or NX assembly. If you wanted a hand written list of all the folders on your C drive, you could do the following:
- Open the C drive, write down the name of all the first folder
- Open folder: if subfolders exist, write down the name of the first folder
- No subfolders? move up a level and open the next folder
- Repeat until you run out of folders
The beauty of this approach is it works if you only have a handful of folders or if you have hundreds of folders nested dozens of levels deep - you don't need to know anything about the structure, other than the basic layout, before you start. It may look strange for a subroutine to call itself, but as far as the computer is concerned it is a copy. So subroutine A calls itself, now subroutine A' is running which kicks off A''. A'' finishes returning control to A' which eventually finishes returning control to A. Each copy has its own copy of local variables to use so they won't overwrite each other's information. If my explanation left you scratching your head, you may want to try Wikipedia's definition.
Conclusion
A journal was presented that recursively steps through the assembly structure. Feel free to copy this code and modify it for your purposes. Some examples would be:
- assigning an attribute to every component in the assembly
- writing out the assembly structure to a text file or spreadsheet
- report the quantity of each component and/or the number of unique components
I hope you find this journal useful. If you experience any problems or could do some testing for a Teamcenter version, please let me know!

Comments
recursion
Hi,
I want to walk /process recursive from buttom up to the top level of a deeper fragmented assembly structure . -> I want to fix all compoments of a imported assembly.
thanks ind ad
recursion
If you want to process all the files in the assembly, recursively walking through an assembly works best from the top down. If you are starting somewhere down the tree, loop through the component's .RootComponent property until it is equal "nothing" - when that happens, you are at the top node.
If you are starting somewhere down the assembly tree and only want to process the files in its particular branch, you can get the .RootComponent each time to cycle up a level in the assembly. You will miss other "branches" of the assembly tree with this strategy.
Teamcenter - Same process
I'd like to know if is possible create this same subroutine to run at Teamcenter?
I need to perform a task over files inside an assembly.
My journal works individually, but i need a way to run this determined task (just some clicks) over whole assembly components.
Christopher Prazeres
Brazil - Designer
551297174168
christopherdomingues@hotmail.com
RE: Teamcenter - Same process
Hello Christopher,
Did you find a solution to running this in Teamcenter?
Carlo Tony Daristotile
Teamcenter Assembly
I currently do not have a Teamcenter install to test with. When you run the code, what errors do you get?
write to a text file
How would you go about writing this to a text file instead of sending it to the listing window ?
re: writing to a text file
Both .NET and NXOpen provide ways of writing to text files, here are 2 links for using .NET methods:
http://www.dotnetperls.com/streamwriter-vbnet
http://www.homeandlearn.co.uk/net/nets8p4.html
In the NXOpen API, the listing window itself provides for a method of writing to a text file. Have a look at the SelectDevice method of the ListingWindow object in the NXOpen API help file.
It is a question that I've seen come up multiple times, I'll add it to my queue of "future article ideas".
components obtained from a function ???
Hello I have a component returned from a function.
But then I cannot apply any component functions to the component.
Can anyone help with this.
Thanks
dim device_component(ConnectorDevice_s1.length - 1) as Component '''works
dim ConnectorDevice_s1() as nxopen.routing.electrical.ConnectorDevice '''works
for i as integer = 0 to ConnectorDevice_s1.length - 1 '''works
device_component(i) = ConnectorDevice_s1(i).OwningComponent '''works component returned from function
device_component(i).DisplayName() '''fails!!!!!!!!!!!!!!!!!!!!
next
Carlo Tony Daristotile
re: component errors
There are two problems with the code snippet above:
Is there an error message shown when you run your code? If so, what exactly does it say?
"OBJECT REFERENCE NOT SET TO AN INSTANCE OF AN OBJECT"
HELP
I updated the code.
I still get an error "OBJECT REFERENCE NOT SET TO AN INSTANCE OF AN OBJECT"
dim ConnectorDevice_s1() as nxopen.routing.electrical.ConnectorDevice = ConnectorDevice_Collection1.toarray()
dim device_component(ConnectorDevice_s1.length - 1) as Component
dim lineouts(ConnectorDevice_s1.length - 1) as string
dim lineOut as string = ""
for i as integer = 0 to ( ConnectorDevice_s1.length - 1 )
device_component(i) = ConnectorDevice_s1(i).OwningComponent '''works component returned from function
lineOut = device_component(i).DisplayName '''fails!!!!!!!!!!!!!!!!!!!!
lineouts(i)= lineOut
next
Carlo Tony Daristotile
UNABLE TO CAST OBJECT OF TYPE
Please HELP
'''fails!! UNABLE TO CAST OBJECT OF TYPE 'NXOpen.Routing.PartDefinitionShadow' to type 'NXOpen.Routing.Electrical.ElectricalPartDefinitionShadow'
dim ConnectorDevice_Collection1 as nxopen.routing.electrical.ConnectorDeviceCollection
ConnectorDevice_Collection1 = workpart.routemanager.ConnectorDevices
dim ConnectorDevice_s1() as nxopen.routing.electrical.ConnectorDevice = ConnectorDevice_Collection1.toarray()
dim ElectricalPartDefinitionShadow1(ConnectorDevice_s1.length - 1) as NXOpen.Routing.Electrical.ElectricalPartDefinitionShadow
dim lineouts(ConnectorDevice_s1.length - 1) as string
dim lineOut as string = ""
for i as integer = 0 to ( ConnectorDevice_s1.length - 1 )
ElectricalPartDefinitionShadow1(i) = ConnectorDevice_s1(i).GetPartDefinition() '''fails!! UNABLE TO CAST OBJECT OF TYPE
lineouts(i)= lineOut
next
Carlo Tony Daristotile
re: cast object error
I don't see an obvious reason why it shouldn't work. Perhaps this is a good one to take up with the GTAC help desk...
re: cast object error: "GTAC reply"
Hello,
This is the analysis I got from GTAC. I still dont see the error in my Journal.
The error states ‘UNABLE TO CAST OBJECT OF TYPE’ PartDefinitionShadow to type ElectricalPartDefinitionShadow.
In the documentation we can see the hierarchy.
PartDefinitionShadow Inheritance Hierarchy
Object
MarshalByRefObject
NXRemotableObject
TaggedObject
NXObject
RootObject
RouteObject
ItemDefinition
PartDefinitionShadow
ElectricalPartDefinitionShadow Inheritance Hierarchy
Object
MarshalByRefObject
NXRemotableObject
TaggedObject
NXObject
RootObject
RouteObject
ItemDefinition
PartDefinitionShadow
ElectricalPartDefinitionShadow
Note that ElectricalPartDefinitionShadow inherits from PartDefinitionShadow. What the journal is trying to do is cast an object down the inherintance tree. I.E. make an onject inherit from itself. One can usually cast to the same level or up. But not down.
Art Schumacher
Sr. Application Engineer
art.schumacher@siemens.com
---------------------------------------------------------------
'Brief Summary of My Journal
dim ConnectorDevice_s1() as nxopen.routing.electrical.ConnectorDevice = ConnectorDevice_Collection1.toarray()
dim EleclPartDefShad1(ConnectorDevice_s1.length - 1) as NXOpen.Routing.Electrical.ElectricalPartDefinitionShadow
for i as integer = 0 to ( ConnectorDevice_s1.length - 1 )
'''fails!! UNABLE TO CAST OBJECT OF TYPE 'NXOpen.Routing.PartDefinitionShadow' to type 'NXOpen.Routing.Electrical.ElectricalPartDefinitionShadow'
EleclPartDefShad1(i) = ConnectorDevice_s1(i).GetPartDefinition()
Next
Carlo Tony Daristotile
re: GTAC answer
I understand what the GTAC representative is saying, however the question becomes "where is this NXOpen.Routing.PartDefinitionShadow object coming from?".
If your actual code matches what you have posted here, I don't see where this PartDefinitionShadow is coming from. According to the .net API reference docs (7.5, 8, & 8.5), a "ConnectorDevice" object is part of the Routing.Electrical namespace and the ".GetPartDefinition" method of this object should return an "ElectricalPartDefinitionShadow" object.
Cycle through each component as Make Displayed/Work part ?
Is it possible to
Cycle through each component as Make Displayed part ?
or
Cycle through each component as Work Displayed part ?
Carlo Tony Daristotile
re: cycling components
The short answer is: yes that is possible.
I'd suggest opening an assembly, start the journal recorder and make one of the components the displayed/work part as desired. Stop the recording and examine the code, incorporate the code as needed into the journal above.
Cycle through 1st level components and Make Displayed part
It works
'Journal to walk through the assembly structure
' will run on assemblies or piece parts
' will step through all "components of 1st level" of the displayed part and make them displayed part
Option Strict Off
Imports System
Imports NXOpen
Imports NXOpen.UF
Imports NXOpen.Assemblies
Imports System.IO
Imports NXOpenUI
Imports System.Windows.Forms
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
'lw.WriteLine("")
'lw.WriteLine("comps")
'lw.WriteLine("")
dim comps() as component = c.RootComponent.getchildren()
Dim part2 As Part = CType(theSession.Parts.FindObject(c.RootComponent.DisplayName), Part)
for i as integer = 0 to ( comps.length - 1 )
'lw.writeline( comps(i).name )
'-----------------------------------------------------------
Dim component1 As Assemblies.Component = comps(i)
Dim components1(0) As Assemblies.Component
components1(0) = component1
Dim errorList1 As ErrorList
errorList1 = component1.DisplayComponentsExact(components1)
errorList1.Clear()
Dim part1 As Part = CType(theSession.Parts.FindObject(comps(i).displayname), Part)
part1.Preferences.Modeling.CutViewUpdateDelayed = True
Dim partLoadStatus1 As PartLoadStatus
Dim status1 As PartCollection.SdpsStatus
status1 = theSession.Parts.SetDisplay(part1, True, False, partLoadStatus1)
workPart = theSession.Parts.Work
dispPart = theSession.Parts.Display
partLoadStatus1.Dispose()
MessageBox.Show(comps(i).displayname)
'------------------------------------------------------------
'Dim part2 As Part = CType(theSession.Parts.FindObject(c.RootComponent.DisplayName), Part)
workPart.Preferences.Modeling.CutViewUpdateDelayed = True
Dim partLoadStatus2 As PartLoadStatus
Dim status2 As PartCollection.SdpsStatus
status2 = theSession.Parts.SetDisplay(part2, True, False, partLoadStatus2)
workPart = theSession.Parts.Work
dispPart = theSession.Parts.Display
partLoadStatus2.Dispose()
'------------------------------------------------------------
next
workPart.Preferences.Modeling.CutViewUpdateDelayed = True
Dim partLoadStatus6 As PartLoadStatus
Dim status6 As PartCollection.SdpsStatus
status6 = theSession.Parts.SetDisplay(part2, True, False, partLoadStatus6)
workPart = theSession.Parts.Work
dispPart = theSession.Parts.Display
Dim nullAssemblies_Component As Assemblies.Component = Nothing
dispPart.Preferences.Modeling.CutViewUpdateDelayed = True
Dim partLoadStatus7 As PartLoadStatus
theSession.Parts.SetWorkComponent(nullAssemblies_Component, PartCollection.RefsetOption.Current, PartCollection.WorkComponentOption.Visible, partLoadStatus7)
workPart = theSession.Parts.Work
partLoadStatus7.Dispose()
MessageBox.Show(c.RootComponent.DisplayName)
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
'**********************************************************
Public Function GetUnloadOption(ByVal dummy As String) As Integer
Return Session.LibraryUnloadOption.Immediately
End Function
'**********************************************************
End Module
Carlo Tony Daristotile
Verify each component is loaded?
How do you verify each component is loaded/opened?
Thanks
Carlo Tony Daristotile
re: verify load status
I've been doing some work on a class that gathers information about the open assembly. It isn't finished yet, but I think it is far enough along to help answer your question. The LoadComponent function in the code listing below (last function in the listing), checks to see if the given component is fully or partially loaded; if it is not, it will attempt to load the component and check the "load status" variable to see if it was successful.
Internal Error
I received an internal error when attempting to un-hide everything in a Nx 7.5 assembly after running this code in Nx 7.5, I later used the same assembly in Nx 8.5 and attempted the same process with success. Any ideas?
DHuskic
Nx 9 VB
re: internal error
I was using NX 8.5 when I wrote the code, but I can't see anything obvious that would cause errors in 7.5. The code writes updates to the log file, check the log after you run the code, it may give you clues as to what is causing the error.
Replace component
Hi
Do you know what the command to replace a child component after you identify it as not loaded?
Thanks
re: replace component
There are a number of reasons why a component may not be loaded. Perhaps it would be better to see if the component in question could be loaded, rather than replacing it?
If you need a code sample of replacing a component (or just about anything else), I suggest recording a journal while performing the operation. The returned code will show you what commands are necessary; with a little tweaking, the code can be made more general (usually it only works on objects involved at the time of recording).
Output to Excel
At the end of this tutorial, you left us with the 'homework' to output the BoM to excel. I have done that and posted the example here. It also generates a screenshot and reports the quantity of each part.
PDF Batch all in folder
Paul Fillion
Measure Bodies
So what would I put in to make this script measure bodies for each piece part in the assembly, while saving a text file to the hard drive for each part? I tired different things but I am not getting the desired result.
moving to layers
I would like to use this journal as a base for a customer requirement that I have.
The requirement is that all purchase parts be on a specific range of layers and all manufactured parts be on another set of layers. The parts can be identified by the first character of the part number.
Any recommendations on how to approach this? I was thinking about adding a conditional statement to determine the part file name and then move to the appropriate layer, and then index the layer number by one.
re: moving to layers
Sounds like a good plan. If you are working in native NX, you can get the component part file name with something like:
So if you were working on the file: "C:\CAD_data\rest\of\the\path\12345.prt", the above command would return "12345". Then you can use the .net string functions to read the first character.
Use code in a Public Class
Hello! First off thank you for all the information about journaling with NX! I'm currently working on a program that analyses assemblies and parts to predict the cost of a product. The program is able to process parts already. Now I need to cycle trough all parts of my assembly. I use a Public Class that contains Public functions and subs. But I get errors if I change the program from a module to a Class. The Error Message: System.Reflection.TargetException: The non-static method requires target. Or is there a way to integrate a Module in a Class and call Functions from the Class?
Greetings
Tim Schröder
re: module to class
Why do you want to convert your code from a module to a class? What benefit do you hope to gain by doing so?
Hi,
Hi,
I just want to integrate the code in my program that uses a Public Class. I figured out, that it isn't possible to call a Function from a Module that is contained in a Public Class. Is there a way to change this code, to run in a Class instead of a Module? This would save me a lot of work! I'm currently working on my thesis and I’ve never been working with Visual Basic before. I don’t really know exactly the difference between a Module and a Class. The program I’m working on is based on a Class that contains Functions and Subs. Or is there a way to integrate this Module in my program?
re: module vs. class
"I figured out, that it isn't possible to call a Function from a Module that is contained in a Public Class."
You will need to create an instance of the class in your module, then you can call its public methods and properties. If you are going to be using VB, I suggest reading up on modules and classes. Each have their use and trying to force one to work like the other may be more trouble than it is worth.
Below is a quick and dirty (i.e. probably not the best) way to get your journal to run as a class.
Hi! It's working now! Thank
Hi! It's working now! Thank you so much for your help. It's not easy to get useful information about programing with NX exapt on this site!