Assembly model tree name updates

Assembly model tree name updates

tfrohe_LSI
Advocate Advocate
986 Views
11 Replies
Message 1 of 12

Assembly model tree name updates

tfrohe_LSI
Advocate
Advocate

I have the following code to update the names in my assembly browser tree in Inventor 2021. It works fine but the model tree does not show the updates until the rule is done so the message box is shown before the model tree is visually updated.

 

in the code debug.print() just wraps messagebox.show() in a custom library I have for debugging my code. This is a remnant of days before Logger.

 

For Each oCompOcc In oCompDef.Occurrences
    occNum = GetDigitsAfterColon(oCompOcc.Name)
    If occNum Is Nothing Then badOcc = True
    debug.Print(2, "occNum " & occNum, ruleName)
    sPartNum = iProperties.Value(oCompOcc.Name, "Project", "Part Number").Trim()
    sPartDesc = iProperties.Value(oCompOcc.Name, "Project", "Description").Trim()
    If Len(sPartDesc) = 0 Then badOcc = True
        newBrowserName = sPartNum & " ~ " & If(len(sPartDesc) > 0, sPartDesc, "▲▲▲ Missing Description ▲▲▲") & ":" & If(occNum IsNot Nothing, occNum,"▲▲▲ Missing Instance ID ▲▲▲")
    oCompOcc.Name = newBrowserName
Next

If badOcc Then
    debug.Print(0,"You have 1 or more parts with a missing Description or a missing instance ID." & vbCrLf & vbCrLf & "Please edit the part and update the Description iProperty using the Model Properties Form and save the part." & vbCrLf & vbCrLf & "For a missing Instance ID, please edit the description in the assembly model tree and add one. "":1""", ruleName)
End If

 

This is how it behaves.

 

tfrohe_LSI_0-1702992415102.png

 

This is how it should look 

 

tfrohe_LSI_1-1702992460723.png

 

I want that view before the messagebox pops up so the user can identify the nonconforming parts while the error is on screen.

 

This is problematic. How can I ensure that the browser tree is visually updated prior to showing the messagebox using my debug.print()?

 

0 Likes
Accepted solutions (1)
987 Views
11 Replies
Replies (11)
Message 2 of 12

WCrihfield
Mentor
Mentor

If when you call debug.print, that shows a MessageBox, and you have that code within the loop for every component, then there is not much way to avoid it showing before the task is complete.  You could try a line of code to update the assembly just before calling that debug.print line, but I do not see a variable with a reference to the main assembly shown in your example code.  Something like AssemblyDocument.Update, AssemblyDocument.Update2, or ThisApplication.ActiveView.Update.  It looks like your code renames all components starting with their Part Number iProperty value, followed by their Description iProperty value, if available.  Have you thought about creating a collection before the loop starts, then within the loop, if a problem component is found, you could add that component to the collection, that way you still have a reference to the problem components when the process is done, and can do something with them at that point?  We could also help you re-write the code in some way, if needed.  Do you just maintain the portion after the colon (:) then?

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 3 of 12

tfrohe_LSI
Advocate
Advocate

@WCrihfield  Thank you for your time. Here is the entire rule. We have a template for rules that this is built using. Should be easy enough to follow. I added all three update lines in and still have the same behavior. If I am approaching this wrong, I am open to any suggestion on how to restructure this. We are attempting to maintain the instance number that was provided by Inventor. Occasionally someone will rename a browser node and remove the :# so our existing script (not written by me) crashes. This is my attempted rewrite into our template with some added functionality to help identify the problem components and avoid the generic exception message. 

 

'Define Imports below
Imports System.Text.RegularExpressions

AddVbFile "LSIiLogicLibrary.VB"
AddVbFile "LSIiLogicDebugger.VB"
Class BrowserNames

	' Instantiate the LSI iLogic Function Library
	Shared Dim lsiLib As LSIiLogicLibrary = New LSIiLogicLibrary
	
	' Instantiate the LSI Debugging Library and set the debugging level for this rule
	'	- 0 = None; 1 = Minimal; 2 = Verbose
	Shared Dim debug As LSIiLogicDebugger = New LSIiLogicDebugger(0)
	
	' Set the rule name for use later in the rule
	Shared Dim ruleName As String = "Browser Names"

	Sub Main()
		'Rule: BrowserNames
		'Purpose: This rule updates the model browser to show PN ~ DESCRIPTION : OCCURRENCE
		'         If the description is blank, an error will be shown with instructions on how to correct it
		
		'Set initial script references to the Inventor Application and the Document
		Dim oApp As Inventor.Application = ThisApplication
		Dim doc, oDoc As Document
		doc = ThisDoc.Document
		oDoc = oApp.ActiveDocument
		
		' Set the type of document this rule is targeted to
		'	- valid entries are "Drawing", "Part", "Assembly"
		Dim ruleTargetType() As String = {"Assembly"}
		
		' Test If we are actually in the document before running rule
		'    - prevents running rule if triggered from another doc
		If Not doc Is oDoc Then
			Exit Sub
		End If
		
		' Test If we are in the correct document type for the rule
		'	- prevents running the rule on an incorrect document type
		If Not lsiLib.checkDocType(ruleTargetType, oDoc.DocumentType) Then
			Dim msgStr = "This is not the correct document type for rule """ & ruleName & """." + vbCrLf + "The correct document types are: "
			For i As Integer = 0 To UBound(ruleTargetType)
				msgStr = msgStr + vbCrLf + vbTab + ruleTargetType(i)
			Next
			debug.Print(0, msgStr, ruleName, MessageBoxButtons.OK, MessageBoxicon.Information)
			Exit Sub
		End If
		
		'Setup TransactionManager so we can undo our changes later
		Dim oTransMgr As TransactionManager = oApp.TransactionManager
		Dim oTrans As Transaction
			
		Try
			debug.Print(1,"Running the rule", ruleName)
			
			'Begin the transaction
			oTrans = oTransMgr.StartTransaction(oDoc, ruleName)
			
			' Rule code should be placed in Sub Rule() below
			Rule(oApp, oDoc)
			
			'End and commit the transaction for later Undo
			If Not oTrans Is Nothing Then
				oTrans.End()
			End If
			
		Catch ex As Exception
			' Abort the transaction so we dont leave it orphaned (avoid possible memory leak)
			If Not oTrans Is Nothing Then
			oTrans.Abort()
			End If
			debug.Print(0,"There is an error in the script. Please see the notes below or notIfy the script author." & vbCrLf & "Please provide the drawing/part number, rev and the following error text." & vbCrLf & vbCrLf & ruleName & ": " & ex.Message, ruleName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
		End Try

		'Update the document
		InventorVb.DocumentUpdate()
	End Sub

	'
	' Rule(oApp As Inventor.Application, oDoc as Document)
	'	- This is where the actual rule code lives.
	'   - We Get a reference To the Application And the Active Document by default
	'
	Sub Rule(oApp As Inventor.Application, oDoc As Document)
	'	Comment the following line if using Try...Catch in the rule
	'	On Error Resume Next
	
	' Cast the current document to the correct type based on what we expect it to be
	' as different document types have similar methods that do different things, we
	' need to specify the document type and use the correct types in our script
	Dim oPartDoc As PartDocument
	Dim oAsmDoc As AssemblyDocument
	Dim oDrawDoc As DrawingDocument
	
	Select Case oDoc.DocumentType
		Case Inventor.DocumentTypeEnum.kDrawingDocumentObject
			'Document is a Drawing. Use oDrawDoc as your docuemnt reference in your code
			oDrawDoc = DirectCast(oDoc, DrawingDocument)
		Case Inventor.DocumentTypeEnum.kAssemblyDocumentObject
			'Document is an Assembly. Use oAsmDoc as your docuemnt reference in your code
			oAsmDoc = DirectCast(oDoc, AssemblyDocument)
		Case Inventor.DocumentTypeEnum.kPartDocumentObject
			'Document is a Part. Use oPartDoc as your docuemnt reference in your code
			oPartDoc = DirectCast(oDoc, PartDocument)
	End Select
	debug.Print(2,"Document cast successful!", ruleName) 'This was just a placeholder that was never removed from the template
	
	'Begin code here
		Dim badOcc As Boolean = False
		Dim oCompDef As Inventor.ComponentDefinition = oAsmDoc.ComponentDefinition
		Dim oCompOcc As Inventor.ComponentOccurrence
		Dim oPartCompDef As Inventor.PartComponentDefinition
		Dim oPane As BrowserPane = oAsmDoc.BrowserPanes.Item("Model")

		
		Dim occNum, sPartNum, sPartDesc As String
		
		For Each oCompOcc In oCompDef.Occurrences
			occNum = GetDigitsAfterColon(oCompOcc.Name)
			If occNum Is Nothing Then badOcc = True
			debug.Print(2, "occNum " & occNum, ruleName)
			sPartNum = iProperties.Value(oCompOcc.Name, "Project", "Part Number").Trim()
			sPartDesc = iProperties.Value(oCompOcc.Name, "Project", "Description").Trim()
			If Len(sPartDesc) = 0 Then badOcc = True
			oCompOcc.Name = sPartNum & " ~ " & If(len(sPartDesc) > 0, sPartDesc, "▲▲▲ Missing Description ▲▲▲") & ":" & If(occNum IsNot Nothing, occNum,"▲▲▲ Missing Instance ID ▲▲▲")
		Next

		oAsmDoc.Update2()
		InventorVb.DocumentUpdate()
		ThisApplication.ActiveView.Update()

		If badOcc Then
			debug.Print(0,"You have 1 or more parts with a missing Description or a missing instance ID." & vbCrLf & vbCrLf & "Please edit the part and update the Description iProperty using the Model Properties Form and save the part." & vbCrLf & vbCrLf & "For a missing Instance ID, please edit the description in the assembly model tree and add one. "":1""", ruleName)
		End If
	End Sub

	Function GetDigitsAfterColon(input As String) As String
        Dim regexPattern As String = ":(\d+)$" ' colon character followed by one or more digits
        Dim match As Match = Regex.Match(input, regexPattern)

        If match.Success Then
            ' Extract and return the digits
            Return match.Groups(1).Value
        Else
            ' No match found
            Return Nothing
        End If
    End Function
End Class

 

 

Again, I am super grateful for your eyes on this. Please feel free to tear this down. I am just a hobbyist programmer and some of this might be inefficient or just wrong. Thank you.

0 Likes
Message 4 of 12

tfrohe_LSI
Advocate
Advocate

As a reference, here is the original rule that was given to us by a vendor before my time here.

 

Dim oDoc As Document = ThisDoc.Document
Dim oCompDef As Inventor.ComponentDefinition = oDoc.ComponentDefinition
Dim oCompOcc As Inventor.ComponentOccurrence
For Each oCompOcc in oCompDef.Occurrences
    searchfor = ":"
    firstchr = oCompOcc.Name.IndexOf(searchfor)
    firstchr = firstchr+1
    totalchr = Len(oCompOcc.Name) - firstchr 
    post1 = Right(oCompOcc.Name, totalchr)
    newBrowserName1 = iProperties.Value(oCompOcc.Name, "Project", "Part Number") & post1
    oCompOcc.Name = newBrowserName1
    newBrowserName2 = iProperties.Value(oCompOcc.Name, "Project", "Part Number") & " ~ " & iProperties.Value(oCompOcc.Name, "Project", "Description") & ":" & post1
    oCompOcc.Name = newBrowserName2
    firstchr= ""
    totalchr= ""
    post1 = ""
    newBrowserName1 = ""
    newBrowserName2= ""
Next
0 Likes
Message 5 of 12

WCrihfield
Mentor
Mentor

Hi @tfrohe_LSI.  That is certainly a long and involved rule template you have there, and it even seems to be referencing two other external iLogic rules for extra functionality.  It does seem like a bit of overkill to me, but if that system is working OK for you guys, then who am I to judge.  I also occasionally use references to external rules with extra resources in them from time to time.  There are several ways that this situation could be handled differently, but I guess it all comes down to personal preference and what you ideally want to see happen (how you want it to work).  Just attempting to maintain the existing integers at the end of the component name may not be enough in a situation like this, but there is only one way to find out...testing.  The code you are using to extract the digits after the colon is a fairly advanced technique that I do not see that often, so I have not used it for that exact purpose before.  I generally just first make sure that the component name 'Contains' the ":" (colon) character, then use the 'Split' function to split the name into the portion before that character, and the portion after that character.  Of course, if for some reason three is more than one colon character in its name, that would normally cause problems.

I created an alternate iLogic rule of my own, just as something to try out or compare with the functionality of your current solution.  It does utilize the Logger functionality to log some useful information to the iLogic Log window, but you can comment those lines out, if you do not want to pay attention to that as a resource.  This code also keeps track of all components that encountered issues with a 'Dictionary' type collection.  Each entry would potentially contain the component object as its 'Key', and a note/message about what was wrong with that component as the value of the dictionary entry.  Then, if any problem components were found, it loops back through those problem components, informing you about the current name of each one, and what the problem was.  As I said, there are many directions and different actions that could be done here, so this is just one possibility among many.  I could have also added the problem components to a HighlightSet, as an additional way to point them out, but that is often just a fleeting moment that they remain highlighted, so often not super helpful.

Sub Main
	If ThisApplication.ActiveDocumentType <> DocumentTypeEnum.kAssemblyDocumentObject Then
		MsgBox("An Assembly Document must be active for this rule to work. Exiting.", vbCritical, "")
		Return
	End If
	Dim oADoc As AssemblyDocument = ThisApplication.ActiveDocument
	Dim oOccs As ComponentOccurrences = oADoc.ComponentDefinition.Occurrences
	If oOccs.Count = 0 Then Return
	Dim oOccsWithProblems As New Dictionary(Of Inventor.ComponentOccurrence, String)
	For Each oOcc As ComponentOccurrence In oOccs
		If oOcc.Name.Contains(":") Then
			'assumes that there is just one instance of the ":" string present
			'if more than that, that would mess this process up
			Dim oSections() As String = oOcc.Name.Split(":") 'split the name into multiple Strings devided by the ":" character
			If oSections.Length <> 2 Then
				'the split resulted in more or less than 2 Strings
				'code here for what to do about that situation
				oOccsWithProblems.Add(oOcc, "More Or Less Than 2 Sections In Name When Split By Colon")
				Continue For 'skip to next component
			End If
			Dim sName As String = oSections.First 'the portion of the name before the ":" character
			Dim sInstance As String = oSections.Last 'the portion of the name after the ":" character
			Dim sPN As String = iProperties.Value(oOcc.Name, "Project", "Part Number")
			If sPN = "" Then
				'what to do when the Part Number iProperty value is empty
				Logger.Debug("Component named:  " & oOcc.Name & vbCrLf & _
				"...had empty Part Number value.")
				sPN = "▲▲▲ Missing Part Number ▲▲▲"
				oOccsWithProblems.Add(oOcc, "Empty Part Number")
				'Continue For 'skip to next component
			End If
			Dim sDesc As String = iProperties.Value(oOcc.Name, "Project", "Description")
			If sDesc = "" Then
				'what to do when the Description iProperty value is empty
				Logger.Debug("Component named:  " & oOcc.Name & vbCrLf & _
				"...had empty Description value.")
				sDesc = "▲▲▲ Missing Description ▲▲▲"
				oOccsWithProblems.Add(oOcc, "Empty Description")
				'Continue For 'skip to next component
			End If
			Dim sNewOccName As String = sPN & " ~ " & sDesc & ":" & sInstance
			Try
				oOcc.Name = sNewOccName
			Catch oEx As Exception
				Logger.Error("Error renaming component..." & vbCrLf & _
				"From:  " & oOcc.Name & vbCrLf & _
				"To:  " & sNewOccName)
			End Try
		Else 'component's name did not contain ":"
			Logger.Debug("Component named:  " & oOcc.Name & vbCrLf & _
				"...does not have ':' character in its name.")
			oOccsWithProblems.Add(oOcc, "No Colon")
		End If
	Next
	If oADoc.RequiresUpdate Then oADoc.Update2(True)
	'If oADoc.Dirty Then oADoc.Save2(True)
	If oOccsWithProblems.Count > 0 Then
		For Each oEntry As KeyValuePair(Of Inventor.ComponentOccurrence, String) In oOccsWithProblems
			Dim oComp As ComponentOccurrence = oEntry.Key
			Dim sNote As String = oEntry.Value
			'some code here for what to do with each problem component, such as a report or message to the user
			MessageBox.Show("Component named:  " & oComp.Name & vbCrLf & sNote, "Component With Problem", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
		Next
	End If
End Sub

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 6 of 12

tfrohe_LSI
Advocate
Advocate

I also have experienced some finicky behavior of SelectionSet in the past. Thank you for your interpretation. You have given me some ideas on how to change things up a little. It still does not seem to update the browser tree visually until the message boxes have been displayed. At the very least it is inconsistent. On one run, it visually updates 2 items then displays the message boxes (12) before visually updating the rest of the browser. On another run, it does not visually update any of them before the message boxes start appearing.  If i have some time tomorrow i will try to record a video of the behavior.

0 Likes
Message 7 of 12

J-Camper
Advisor
Advisor
Accepted solution

@tfrohe_LSI,

 

You have the entire "Rule" sub inside a transaction.  I think you need the transaction to close before Inventor will update to show changes.  Try it without the Transaction

Message 8 of 12

tfrohe_LSI
Advocate
Advocate

@J-Camper That was exactly it. Thank you. I didn't take that away from the Transactions article in the API Help. 

0 Likes
Message 9 of 12

J-Camper
Advisor
Advisor

@tfrohe_LSI,

 

Glad it is working.  I will say a transaction is a good thing to have when doing something like this as you will easily hit the undo limit in any large assembly.  I would move the transaction somewhere where you can end it before calling the message box for the user.

0 Likes
Message 10 of 12

tfrohe_LSI
Advocate
Advocate

@J-Camper I added a flag in my template now for undoAllowed and am passing the transaction to the rule sub ByRef so I can end it and set it to nothing before displaying the message boxes. 

0 Likes
Message 11 of 12

WCrihfield
Mentor
Mentor

Another thing you could explore where transactions are concerned is the TransactionManager.SetCheckPoint method.  I do not know if using that would force the internal updates you are looking for, but it sounds interesting enough to check it out, just in case.  It appears like a way to sub divide a long transaction into sections.  Transactions can also apparently have parent or child transactions, but I have never attempted to create child transactions within another transaction before, so I do not know how that would work.  Perhaps if one main transaction was created before doing anything, then another transaction is started while that other one is still in effect, the newer one will be a child of the older one...not sure.  Either way, that would be a lot of transactions to manage in a code for what should be a fairly simple task. 😅

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 12 of 12

tfrohe_LSI
Advocate
Advocate

@WCrihfieldThanks for pointing that out.  I ready the full article on transactions yesterday, after @J-Camper mentioned transactions being a potential issue, and made the same discovery you did. I'm not sure I need that much functionality here so will likely save that for some future task.

0 Likes