Report Missing Parts List Balloons

When working with a parts list on an assembly drawing, it can be difficult to determine if each item in the list has an associated callout balloon on the drawing. NX offers no command to report those items that have missing callouts; the usual course of action is to print a copy of the drawing and manually working through the parts list marking off those items for which you find a balloon. Frank Berger (of GTAC) has recognized this shortcoming and has written a journal that when run will report which items in the list have no corresponding balloon callout. You can find this journal fairly easily by searching the GTAC solution center with the phrase "report missing balloons" (it will be document: nxapi_5333). This journal takes advantage of some new functionality added to the API in NX 9; consequently, it will only run in NX 9 or higher. For those of us working with NX 8.5 (or lower), hope is not lost; the journal does provide a good "blueprint", we'll just have to find a way to duplicate the functionality while avoiding the NX 9 specific function calls.

The code below is my attempt to duplicate the functionality of Frank's journal for users of NX 8.5. My testing has been fairly limited, but the results are promising. If you use the journal and experience any issues, please let me know at info@nxjournaling.com or in the comments below.

Known Issues

The code below will probably not work if the callout symbol type used with the parts list is set to "none" or "underline". This is due to a bug/limitation of the API.


'NXJournaling.com
'January 20, 2015
'
'NX 8.5
'
'Journal to report which items in a parts list do not have corresponding balloon callouts.
'Inspired by and based on the GTAC sample "report_missing_balloons_of_parts_lists" (nxapi_5333)
'by Frank Berger.
'The GTAC sample will only work with NX 9 or higher; this journal will work with NX 8.5,
'and possibly with older versions back to NX 5.

'Similar to _v2, but this version uses a simple list to hold the autoballoon callout numbers
'instead of a dictionary. I thought there might be a performance improvement by using a list
'rather than a dictionary. Testing on a small assembly (~120 unique parts) shows very little,
'if any difference.

'February 22, 2016
' Update to ignore potential "phantom" parts list returned by Plist.AskTags

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

Module report_missing_balloons_v3

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

Dim lw As ListingWindow = theSession.ListingWindow

Sub Main()

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

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

Dim partListTags() As Tag = Nothing
Dim numPartLists As Integer = 0
theUfSession.Plist.AskTags(partListTags, numPartLists)

'test for a dummy parts list
'.AskTags sometimes returns a tag even on a blank part that contains no parts list
'PR 7643749
'lw.WriteLine("number of parts lists: " & numPartLists.ToString)
If numPartLists > 0 Then
For i As Integer = 0 To partListTags.Length - 1
Dim prtList As DisplayableObject = Utilities.NXObjectManager.Get(partListTags(i))
'lw.WriteLine("parts list layer: " & prtList.Layer.ToString)
If prtList.Layer = 0 Then
numPartLists -= 1
End If
Next
End If

If numPartLists <= 0 Then
lw.WriteLine("No parts list found in the work part")
Return
End If

If numPartLists > 1 Then
lw.WriteLine("ERROR: Cannot evaluate AutoBalloons with multiple parts lists.")
lw.WriteLine("Check environment variable UGII_UPDATE_ALL_ID_SYMBOLS_WITH_PLIST")
Return
End If

theUfSession.Plist.UpdateAllPlists()

Dim numRows As Integer
theUfSession.Tabnot.AskNmRows(partListTags(0), numRows)
'lw.WriteLine("number of rows: " & numRows.ToString)

Dim plistPrefs As UFPlist.Prefs = Nothing
theUfSession.Plist.AskPrefs(partListTags(0), plistPrefs)
'lw.WriteLine("symbol type: " & plistPrefs.symbol_type.ToString)
'lw.WriteLine("main symbol text: " & plistPrefs.main_symbol_text)

Dim colCallout As Tag = PartListCalloutColumn(partListTags(0))
'lw.WriteLine("callout column tag: " & colCallout.ToString)
If colCallout = Tag.Null Then
lw.WriteLine("parts list callout column not found")
Return
End If

Dim plBalloonList As New List(Of String)
CollectPartsListBalloons(plBalloonList, plistPrefs.symbol_type)

Dim missingBalloons As New List(Of String)

Dim startTime As DateTime = Now

'loop through rows of the parts list, look for corresponding autoballoon callout
For i As Integer = 0 To numRows - 1

Dim rowTag As Tag
theUfSession.Tabnot.AskNthRow(partListTags(0), i, rowTag)

Dim cellTag As Tag
theUfSession.Tabnot.AskCellAtRowCol(rowTag, colCallout, cellTag)

Dim calloutEvText As String = ""
theUfSession.Tabnot.AskEvaluatedCellText(cellTag, calloutEvText)

If plBalloonList.Contains(calloutEvText) Then
'balloon found, remove it from the list so future searches are potentially faster
plBalloonList.Remove(calloutEvText)
Else
'balloon not found, add callout to the "missing" list
missingBalloons.Add(calloutEvText)
End If

Next

Dim endTime As DateTime = Now

If missingBalloons.Count = 0 Then
lw.WriteLine("Great news! All the parts in the list have a corresponding balloon callout.")
Else
lw.WriteLine("Hmmm... It seems that " & missingBalloons.Count.ToString & " parts in the list do NOT have corresponding balloon callouts.")
For Each missing As String In missingBalloons
lw.WriteLine(missing)
Next
End If

Dim elapsedTime As TimeSpan = endTime.Subtract(startTime)
lw.WriteFullline("elapsed time: " & elapsedTime.ToString)

lw.Close()

End Sub

Function PartListCalloutColumn(ByVal partListTag As Tag) As Tag

Dim numColumns As Integer
theUfSession.Tabnot.AskNmColumns(partListTag, numColumns)

'Dim numRows As Integer
'theUfSession.Tabnot.AskNmRows(partListTag, numRows)

'Dim plistPrefs As UFPlist.Prefs = Nothing
'theUfSession.Plist.AskPrefs(partListTag, plistPrefs)

Dim rowTag As Tag
theUfSession.Tabnot.AskNthRow(partListTag, 0, rowTag)

For j As Integer = 0 To numColumns - 1
Dim colTag As Tag
theUfSession.Tabnot.AskNthColumn(partListTag, j, colTag)

Dim cellTag As Tag
theUfSession.Tabnot.AskCellAtRowCol(rowTag, colTag, cellTag)

'get the current cell text
Dim cellText As String = ""
theUfSession.Tabnot.AskCellText(cellTag, cellText)
If cellText = "$~C" Then
'callout column
Return colTag

End If

Next

Return Nothing

End Function

Sub CollectPartsListBalloons(ByRef theBalloonList As List(Of String), ByVal plSymbolType As Integer)

For Each tempId As Annotations.IdSymbol In theSession.Parts.Work.Annotations.IdSymbols

Dim theIdSymbolBuilder As Annotations.IdSymbolBuilder = theSession.Parts.Work.Annotations.IdSymbols.CreateIdSymbolBuilder(tempId)

If plSymbolType = theIdSymbolBuilder.Type + 1 Then
'symbol matches type used in parts list
Else
'symbol not the type used in parts list, skip it
Continue For
End If

'add unique callouts to the list

'assumes the callout is in the ".UpperText" property
'change this to .LowerText if needed

If Not theBalloonList.Contains(theIdSymbolBuilder.UpperText) Then
theBalloonList.Add(theIdSymbolBuilder.UpperText)
End If

theIdSymbolBuilder.Destroy()

Next

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

Comments

This is cool but it does not seem to check an items quantity of balloons against the quantity on the parts list; say item number 2 has a quantity of 5, as long the as there is one balloon callout it will not report item 2 as missing any balloons.

The code above was only intended to report if any callouts were completely missing from the drawing. To borrow a phrase from GTAC, it is "working as designed".

However, you bring up a good point. Each user of NX has different needs and that is where customization pays off. I believe with a little work that you could extend this code to check that the balloon quantity matches the parts list quantity. If you undertake this project, please post your results as I'm sure we could all learn something from it!

I am brand new to Journaling... I didn't know it existed until I found this post in a google search. I am interested to know if the code above can be modified to work with a user defined attribute named "CALLOUT" in NX8.5? I ran this code as is and got an error message "parts list callout column not found" which appears to be due to us using an attribute rather than the NX callout.

If the title of your callout attribute is "CALLOUT", try changing the following line:

If cellText = "$~C" Then

to:

If cellText = "CALLOUT" Then

I'm fairly sure that this edit will do the job; if it does not, please post back with any errors you encounter.

Hello,

This script does not work if it runs for the first time without a parts list on the drawing. It seems that Plist.AskTags does not give an appropriate response (1 instead of zero). There is no parts list, why does it says 1?

The funny thing: If I start the journal a second time, everything works fine and it behaves like it should do.

Any ideas why this happens? I can´t find anything wrong

This occurs in NX8.0.3 and NX9.0.3.4

There seems to be some unexpected behavior from the .AskTags function. Sometimes it reports a parts list on layer zero; I'm not sure if this is a placeholder entity in the file or what. I've edited the code above to handle this "phantom" parts list. Post back if you are still having issues with it.