Nx Journaling Custom Command Project

Hello,
I am trying to make a custom command button for nx by using nx journal vb scripts. The algorithm is like this;

1)User will choose the areas.
2)The areas will be divided to a constant.
3)If the result is bigger than an other constant, a string message will come to screen. Else an other string message will come to screen.

The problem is the vb code of this algorithm. And the user interface of this command. Can you help me with these problems?

Thank you.

I'll need more information before I can offer much help. First and foremost, what do you mean by "areas" and how will the user "choose" them? Is the area a face that the user selects and you want to divide it; or maybe the area is a closed set of planar curves that bounds an area? Something else?

The area is a face of a solid component. And there are more than one this component. So user will have to choose each faces. And the total area value of the faces will be used for step 2 and 3 of algorithm which I wrote in question.

The code below shows one way to select faces and report the total surface area.

'NXJournaling.com
'April 14, 2016
'
'Report the total surface area of the selected faces.

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

Module Module1

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

Sub Main()

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

Dim workPart As Part = theSession.Parts.Work
lw.Open()

Dim userFaces As New List(Of Face)

Const undoMarkName As String = "face surface area"
Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, undoMarkName)

If SelectFaces("Select faces", userFaces) = Selection.Response.Cancel Then
'user pressed cancel, exit journal
Return
End If

lw.WriteLine(userFaces.Count.ToString & " faces selected")

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 mb As MeasureFaces

mb = myMeasure.NewFaceProperties(massUnits(0), massUnits(3), 0.99, userFaces.ToArray)

totalArea = mb.Area

lw.WriteLine("")
lw.WriteLine("total surface area: " & totalArea.ToString & " " & massUnits(0).Abbreviation)

lw.Close()

End Sub

Function SelectFaces(ByVal prompt As String, ByRef selFaces As List(Of Face)) As Selection.Response

Dim theUI As UI = UI.GetUI
Dim title As String = "Select faces"
Dim includeFeatures As Boolean = False
Dim keepHighlighted As Boolean = False
Dim selAction As Selection.SelectionAction = Selection.SelectionAction.ClearAndEnableSpecific
Dim scope As Selection.SelectionScope = Selection.SelectionScope.AnyInAssembly
Dim selectionMask_array(0) As Selection.MaskTriple
Dim selObj() As TaggedObject = Nothing

With selectionMask_array(0)
.Type = UFConstants.UF_solid_type
.SolidBodySubtype = UFConstants.UF_UI_SEL_FEATURE_ANY_FACE
End With

Dim resp As Selection.Response = theUI.SelectionManager.SelectTaggedObjects(prompt, _
title, scope, selAction, _
includeFeatures, keepHighlighted, selectionMask_array, _
selObj)
If resp = Selection.Response.Ok Then
For Each obj As TaggedObject In selObj
selFaces.Add(CType(obj, NXOpen.Face))
Next
Return Selection.Response.Ok
Else
Return Selection.Response.Cancel
End If

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

Thank you.
How to add user input on this command interface. Because user has to input an integer value and after I'll use these total area and input value to make a calculation.

Regards.

Have a look at the articles on input boxes and custom input forms:
http://www.nxjournaling.com/content/user-input-input-boxes
http://www.nxjournaling.com/content/using-winforms-journals

The input box is a bit simplistic, but it would be a good place to start.

Thank you, I made a user input. But it is not on this selection menu. The command is asking first an input after going to this face selection menu. And it is working properly. I want to embed this input in this selection menu. It could be better for user and would be better design.

Thank you, I made a user input. But it is not on this selection menu. The command is asking first an input after going to this face selection menu. And it is working properly. I want to embed this input in this selection menu. It could be better for user and would be better design.

If you have a "Block UI styler" license, you can create NX style dialog boxes for your code. The block styler is an additional cost license, it doesn't come with the basic modeling license; you will need to check with your system administrator to see if you currently have access to such a license.

I just checked and I don't have this license. Is there any other way to do it?
Thank you!

If you are working on Windows, you can do a lot by using Winforms (see link in previous post); but it may not be as easy as using the block styler. The article on winforms shows just a simple example, but much more is possible.

Hello, This command is working well. I want to update this command as following.
I am taking input value from user currently. I want to assign this value from an attribute of a one of parametric model's attribute.
May you help?
Thank you!

The following code will look for a number type part attribute (on the current work part) named "TEST"; if found, the journal will just report the attribute value. Your code can make use of the value as you see fit.

Many NX objects can have attributes, once you have a reference to the proper object, getting the value is very similar to the code below.

Option Strict Off
Imports System
Imports NXOpen

Module Module2

Sub Main()

Dim theSession As Session = Session.GetSession()
If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If

Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow
lw.Open()

Const attributeName As String = "TEST"
Dim attributeInfo As NXObject.AttributeInformation

Dim testVal As Double

If workPart.HasUserAttribute(attributeName, NXObject.AttributeType.Real, -1) Then
attributeInfo = workPart.GetUserAttribute(attributeName, NXObject.AttributeType.Real, -1)
testVal = attributeInfo.RealValue

lw.WriteLine("attribute value: " & testVal.ToString)
Else
lw.WriteLine("the work part does not have a number attribute named: " & attributeName)
End If

lw.Close()

End Sub

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

This code works well on one attribute which is user input in this case.

Now I want to assign attributes on different components with the same name which is "TestArea" for example. And I want my input as sum of these "TestArea" names values which are the areas which user was picking before.

In this way, user will not have to select the areas one by one. Area attributes will be the input.

May you help?
Thank you!

The following article shows how to iterate through the components in an assembly:
http://www.nxjournaling.com/content/creating-subroutine-process-all-comp...

If you combine that code with the code above showing how to get an attribute value, you can find all the components with a given attribute and sum the values. When getting the attribute, query the component rather than the work part.

Option Strict Off

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

Module NxJournaling

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
Const attributeName As String = "TEST"
Dim attributeInfo As NXObject.AttributeInformation

Dim testVal As Double
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)
If c.HasUserAttribute(attributeName, NXObject.AttributeType.Real, -1) Then
attributeInfo = c.GetUserAttribute(attributeName, NXObject.AttributeType.Real, -1)
testVal = attributeInfo.RealValue

lw.WriteLine("attribute value: " & testVal.ToString)
Else
lw.WriteLine("the work part does not have a number attribute named: " & attributeName)
End If

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

If the attribute that you want to check is on the component, I would suggest modifying the "Sub reportComponentChildren" subroutine; in the "For each child..." loop, check each child object (which represents the component) for the attribute. If you want to keep a grand total of the values, you may need to make the "total" variable a module level variable or pass in the "total" variable "ByRef" to the reportComponentChildren subroutine so that it can modify the value.

Journal executes "the work part does not have a number attribute named:" & attributeName" part. I think it is because I couldn't query. How can I query the component in this case?
The code is below.
Thank You!

Sub reportComponentChildren(ByVal comp As Component,
ByVal indent As Integer)

Dim workPart As Part = theSession.Parts.Work
Const attributeName As String = "Test"
Dim attributeInfo As NXObject.AttributeInformation
Dim testVal As String

For Each child As Component In comp.GetChildren()
'*** insert code to process component or subassembly
If workPart.HasUserAttribute(attributeName, NXObject.AttributeType.Real, -1) Then
attributeInfo = workPart.GetUserAttribute(attributeName, NXObject.AttributeType.Real, -1)
testVal = attributeInfo.RealValue
lw.WriteLine("attribute value: " & testVal.ToString)
Else
lw.WriteLine("the work part does not have a number attribute named: " & attributeName)
End If

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

You will want to query the component (the "child" object reference), not the work part.

Sub reportComponentChildren(ByVal comp As Component,
ByVal indent As Integer)

Const attributeName As String = "Test"
Dim attributeInfo As NXObject.AttributeInformation
Dim testVal As String

For Each child As Component In comp.GetChildren()
'*** insert code to process component or subassembly
If child.HasUserAttribute(attributeName, NXObject.AttributeType.Real, -1) Then
attributeInfo = child.GetUserAttribute(attributeName, NXObject.AttributeType.Real, -1)
testVal = attributeInfo.RealValue
lw.WriteLine("attribute value: " & testVal.ToString)
Else
lw.WriteLine("the work part does not have a number attribute named: " & attributeName)
End If

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

Hello first attribute in code works well. I stored it in TestValue1Total variable in the end.But this code writes TestValue1Total value 3 times in screen. What might be the problem here?

And second problem is that I see that second attributes value is 0 despite it is an other nonzero integer value.And it writes the 0 on screen 3 time also.

(Justforinformation: I have 3 subassemblies and 1 component in main assembly. Three of my attributes which has value TestValue1 were defined in each subassemblies components. TestValue2 was defined in this 1 component)

Sub reportComponentChildren(ByVal comp As Component, ByVal indent As Integer)

'First Attributes variables
Const attributeName As String = "Test1"
Dim attributeInfo As NXObject.AttributeInformation
Dim TestValue1 As String
Dim TestValue1Total As Integer = 0

'Second Attributes variables
Const attributeName1 As String = "Test2"
Dim attributeInfo1 As NXObject.AttributeInformation
Dim TestValue2 As Integer

For Each child As Component In comp.GetChildren()

'***insert code to process component or subassembly
If child.HasUserAttribute(attributeName, NXObject.AttributeType.Real, -1) Then

attributeInfo = child.GetUserAttribute(attributeName, NXObject.AttributeType.Real, -1)
TestValue1 = attributeInfo.RealValue

ElseIf child.HasUserAttribute(attributeName1, NXObject.AttributeType.Real, -1) Then

attributeInfo1 = child.GetUserAttribute(attributeName1, NXObject.AttributeType.Real, -1)
TestValue2 = attributeInfo1.RealValue

End If

reportComponentChildren(child, indent + 1)
TestValue1Total = TestValue1Total + TestValue1

Next

If TestValue1Total = 0 Then

Else

lw.WriteLine(TestValue1Total)

End If

End Sub


Thank You!

The subroutine is recursive; this means that it calls itself. It will call itself each time a component is found to check if the found component has any children components. A copy of the variables defined in the subroutine will be created on each call. This will have some implications if you want to keep a running total. It may be easiest to declare the total variable outside of the subroutine and update it on each call.

This problem was solved about taking value from first attribute. Now is the problem is that when I write an elseif statement for other attribute. It doesn't work. I can't take value from second attribute. How can I take value from the second attribute as well?
Thanks!

To get the values of both attributes, use two separate IF statements.

If child.HasUserAttribute(attribute1...
'get value
End if

If child.HasUserAttribute(attribute2...
'get value
End if

When using the If/ElseIf construct; when the If condition returns True, the statements following the If get executed, then control jumps to the End if. None of the ElseIf statments are tested when the If test returns True.

If test1 then
'first statements
ElseIf test2 then
'second statements
ElseIf test3 then
'third statements
Else
'do this when all of the above return False
End If

So, when test1 returns True, "first statements" gets executed, then control jumps down to "End if". If test1 returns False, test2 is evaluated. When test2 returns True, the "second statements" get executed and control jumps down do the "End if". If none of the tests return True, then the statements in the "Else" section get executed.

Thank You! If elseif worked for me.