Does anyone have a solution that will allow me to always ensure that when linking parameters from a part that all parts using the linked parameters see all newly added parameters?

Does anyone have a solution that will allow me to always ensure that when linking parameters from a part that all parts using the linked parameters see all newly added parameters?

chris
Advisor Advisor
254 Views
6 Replies
Message 1 of 7

Does anyone have a solution that will allow me to always ensure that when linking parameters from a part that all parts using the linked parameters see all newly added parameters?

chris
Advisor
Advisor

Does anyone have a solution that will allow me to always ensure that when linking parameters from a part that all parts using the linked parameters see all newly added parameters?

 

Example:

 

I have a base part with project parameters, and all parts within the project are linked with that base part. However, if I go back to add more parameters to the base part I then have to go back into all project parts to manually add in the rest of the user defined base parameters.

 

Is there a way that when linking to tell the project parts to "ALWAYS" include "ALL" user defined parameters in the link? as it's unlikely that I can foresee from the start of the project every possible dimension needing to be linked.

 

or am I missing a step?

 

Is there an iLogic rule I can run that will look at all user defined base part parameters and if those have not been linked it will add them in to it's list of linked parts?

0 Likes
255 Views
6 Replies
Replies (6)
Message 2 of 7

WCrihfield
Mentor
Mentor

That sounds like a pretty complex scenario to 'manage' entirely by code, but does not sound impossible to achieve, with enough time & effort invested in that overall project.  Most likely, the 'base part' will not know about any of the other files referencing some of its user parameters.  That angle will likely only be valid while all those other files are currently open in Inventor, and not really valid otherwise.  On the other hand, the other files will know about that connection, and be able to access the 'base part' to check things out, when the code process starts from their end, and you know where to look, but they will not really get any sort of 'notification' that more user parameters are present in that 'base part' than there were before.  That part will need to be some custom code that checks for them, and compares the source collection with the destination collection.  This may be far more achievable in a 'manually ran/initiated' type of solution at first, then if that is working really well, without errors, then maybe develop it further into something like an add-in.  The add-in can help by monitoring multiple types of events, such as when that 'base part' is initialized / opened, when it is closed/terminated, when new user parameters get created, changed, or deleted in that base part, and such, and when those events get triggered, they can react to those events with some additional code that uses the previously established code processes for checking things out, and copying the user parameters out to all the files they are 'derived' into.  It may be seem like a long and complex process at first, with a lot of variables involved, but may be possible, taking it one step at a time.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 3 of 7

chris
Advisor
Advisor

@WCrihfield  I don't mind using Excel to handle the parameters, but as you know, I can't add "Text" or True/False" parameters or multi value parameters at the excel level, so while Excel is great for "Numeric" only parameters, it falls short when other parameters are also needed.

 

I don't understand why Inventor/Autodesk doesn't give us the ability to create "User Parameter Groups", so we can separate the user parameters, or also the ability to set the linked user parameters of a base part to include "ALL" user defined parameters, even those created after the initial link... you know, like how it already works with Excel

Message 4 of 7

WCrihfield
Mentor
Mentor

I did not really have Excel in mind at all when attempting to imagine all the automation code that may be required in that sort of scenario.  One of the primary methods involved at the heart of this process has two versions (DerivedParameterTables.Add & DerivedParameterTables.Add2).  The regular 'add' method just asks for the FullFileName of the 'source' file, and will only 'derive in' the parameters that have been set to 'exported/exposed'.  If there are none in that file marked that way, the method will fail.  Then the 'Add2' method asks for an optional second input, an ObjectCollection populated with the Parameter objects from that source file that you want to 'derive in'.  If none are specified, then it will try to derive in all that are marked for export/exposed, and if none are found, it will fail.  So, that one property/setting of each Parameter object (Parameter.ExposedAsProperty) is what Inventor uses in that process, unless specified otherwise in the second version method, in the ObjectCollection.

Inventor just does not seem to 'manage' most types of 'references' in both directions.  A 'reference' is known about by the 'dependent' document/object, because it 'needs' it, but the 'source' document/object usually does not know about any other documents/objects that are dependent on it, unless they are all loaded/open in Inventor at the same time.  And even when all are loaded/open at the same time, it seems like Inventor has to listen for events, then checks to find out if something changed, then attempt updates in reaction to those changes.  Since the source does not know that anything is dependent on it, it does not post a notice to those dependents about the change, to force the update.  I guess Vault may help with some of that, because it keeps a more advanced database of 'references' in both directions, making it ideal for its 'where used' type functionality, but I don't have any experience with Vault yet, so I am not sure.  If source files in Inventor recorded something every time some other file created a reference to it, or some sort of dependency on it, that would cause the source file to be opened and edited every single time that happened, and increase its file size each time, which it seems could cost a lot of disk space and performance in the long run.  Just some speculation/guessing on the 'why' question.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 5 of 7

Curtis_Waguespack
Consultant
Consultant

@chris , see this example.

 

I'm short on time and did not read all the discussion, but if this doesn't do what you are after, post back.

 

Dim oDoc As Document = ThisDoc.Document
Dim oReffeDocFullName As String = oDoc.ReferencedDocuments.Item(1).FullFileName
Dim oReffeDoc As PartDocument = ThisApplication.Documents.Open(oReffeDocFullName, False) 'false opens invisible
Dim oLinkedParametersCollection As ObjectCollection = ThisApplication.TransientObjects.CreateObjectCollection

'Inlcude all user parameters
For Each Param As Inventor.Parameter In oReffeDoc.ComponentDefinition.Parameters.UserParameters	
	oLinkedParametersCollection.Add(Param)
Next

Dim oDerivedParamTable As DerivedParameterTable

Try
oDerivedParamTable = oDoc.ComponentDefinition.Parameters.DerivedParameterTables.Item(1)
Catch
oDerivedParamTable = oDoc.ComponentDefinition.Parameters.DerivedParameterTables.Add(oReffeDocFullName)
End Try
'MsgBox(oDerivedParamTable.ReferencedDocumentDescriptor.FullDocumentName)

'set collection of Parameters to the linked/derived table
oDerivedParamTable.LinkedParameters = oLinkedParametersCollection

oReffeDoc.Close(False)

 

EESignature

0 Likes
Message 6 of 7

chris
Advisor
Advisor

@Curtis_Waguespack Thank you, the initial code did not work, HOWEVER, I ran it through "Claude.ai" and with a few extra prompts and copy/pasting the erorrs I was getting, Claude.ai was able to write something that updates for both assemblies and parts, as long as the linked file is a "part" file. It also looks into sub-assemblies and their parts, and I sent the rule to run after a save for the event trigger... and it works great!

 

Below is the claude.ai code:

 

Sub Main()
    ' Create list to store parameter information
    Dim addedParameters As New System.Collections.Generic.List(Of String)
    
    ' Get the current document - could be either part or assembly
    Dim oCurrentDoc As Document
    oCurrentDoc = ThisDoc.Document

    ' Handle different document types
    If TypeOf oCurrentDoc Is AssemblyDocument Then
        ' It's an assembly document
        ProcessAssembly(oCurrentDoc, addedParameters)
    ElseIf TypeOf oCurrentDoc Is PartDocument Then
        ' It's a part document with a reference
        ProcessPartWithReference(oCurrentDoc, addedParameters)
    Else
        MessageBox.Show("This code works only with Assembly or Part documents.")
        Return
    End If
    
    ' Show results to the user
    If addedParameters.Count > 0 Then
        Dim resultMessage As String = "New parameters added:" & vbCrLf & vbCrLf
        For Each param As String In addedParameters
            resultMessage &= param & vbCrLf
        Next
        MessageBox.Show(resultMessage, "Parameter Update Results")
    Else
        MessageBox.Show("No new parameters were added.", "Parameter Update Results")
    End If
End Sub

' Main recursive function to process an assembly and all its sub-assemblies
Sub ProcessAssembly(ByVal assemblyDoc As AssemblyDocument, ByRef addedParameters As System.Collections.Generic.List(Of String))
    ' Process each occurrence in the assembly
    For Each oOcc As ComponentOccurrence In assemblyDoc.ComponentDefinition.Occurrences
        ' Get the component's document
        Dim oCompDoc As Document = oOcc.Definition.Document
        
        ' Check if it's a sub-assembly
        If TypeOf oCompDoc Is AssemblyDocument Then
            ' Process this sub-assembly
            Dim subAssemblyParams As New System.Collections.Generic.List(Of String)
            ProcessAssembly(oCompDoc, subAssemblyParams)
            
            ' Add sub-assembly params to main list with context
            If subAssemblyParams.Count > 0 Then
                addedParameters.Add("In sub-assembly: " + oCompDoc.DisplayName)
                For Each param As String In subAssemblyParams
                    addedParameters.Add("  " + param)
                Next
                addedParameters.Add("---")
            End If
        ElseIf TypeOf oCompDoc Is PartDocument Then
            ' Process this part that belongs to the assembly
            Dim partParams As New System.Collections.Generic.List(Of String)
            ProcessPartWithReference(oCompDoc, partParams)
            
            ' Add part params to main list with context
            If partParams.Count > 0 Then
                addedParameters.Add("In part: " + oCompDoc.DisplayName)
                For Each param As String In partParams
                    addedParameters.Add("  " + param)
                Next
                addedParameters.Add("---")
            End If
        End If
    Next
    
    ' Also process the assembly itself if it has references
    If assemblyDoc.ReferencedDocuments.Count > 0 Then
        Dim assemblyParams As New System.Collections.Generic.List(Of String)
        ProcessDocumentReferences(assemblyDoc, assemblyParams)
        
        ' Add assembly params to main list with context
        If assemblyParams.Count > 0 Then
            addedParameters.Add("In current assembly: " + assemblyDoc.DisplayName)
            For Each param As String In assemblyParams
                addedParameters.Add("  " + param)
            Next
            addedParameters.Add("---")
        End If
    End If
End Sub

' Process a part document that might have references
Sub ProcessPartWithReference(ByVal partDoc As Document, ByRef addedParameters As System.Collections.Generic.List(Of String))
    ' Check if this part has any references
    If partDoc.ReferencedDocuments.Count > 0 Then
        ProcessDocumentReferences(partDoc, addedParameters)
    End If
End Sub

' Process references for a document (either assembly or part)
Sub ProcessDocumentReferences(ByVal doc As Document, ByRef addedParameters As System.Collections.Generic.List(Of String))
    ' Loop through all referenced documents
    For i As Integer = 1 To doc.ReferencedDocuments.Count
        Dim oReffeDocFullName As String = doc.ReferencedDocuments.Item(i).FullFileName
        Dim refDocName As String = System.IO.Path.GetFileName(oReffeDocFullName)
        
        ' Open the referenced document
        Dim oGenericDoc As Document
        oGenericDoc = ThisApplication.Documents.Open(oReffeDocFullName, False)
        
        ' Check if the referenced document is a part file
        If TypeOf oGenericDoc Is PartDocument Then
            Dim oReffeDoc As PartDocument = oGenericDoc
            
            ' Create collections for parameters
            Dim oLinkedParametersCollection As ObjectCollection
            oLinkedParametersCollection = ThisApplication.TransientObjects.CreateObjectCollection
            
            ' Get existing parameters if the table already exists
            Dim existingParams As New System.Collections.Generic.HashSet(Of String)
            Dim tableExists As Boolean = False
            
            ' Check for existing table
            For Each table As DerivedParameterTable In doc.ComponentDefinition.Parameters.DerivedParameterTables
                If table.ReferencedDocumentDescriptor.FullDocumentName = oReffeDocFullName Then
                    ' Table exists, gather existing parameter names
                    tableExists = True
                    
                    ' Get all existing user parameters in the derived table
                    Dim paramNamePattern As String = refDocName & ":"
                    For Each userParam As Parameter In doc.ComponentDefinition.Parameters.UserParameters
                        If userParam.Name.StartsWith(paramNamePattern) Then
                            existingParams.Add(userParam.Name.Substring(paramNamePattern.Length))
                        End If
                    Next
                    
                    Exit For
                End If
            Next
            
            ' Track new parameters
            Dim newParamsForThisDoc As New System.Collections.Generic.List(Of String)
            
            ' Add all user parameters to the collection
            For Each Param As Inventor.Parameter In oReffeDoc.ComponentDefinition.Parameters.UserParameters
                oLinkedParametersCollection.Add(Param)
                
                ' Check if this parameter is new
                If Not existingParams.Contains(Param.Name) Then
                    newParamsForThisDoc.Add("From " & refDocName & ": " & Param.Name)
                End If
            Next
            
            ' Find or create parameter table
            Dim oDerivedParamTable As DerivedParameterTable = Nothing
            
            If tableExists Then
                ' Get the existing table
                For Each table As DerivedParameterTable In doc.ComponentDefinition.Parameters.DerivedParameterTables
                    If table.ReferencedDocumentDescriptor.FullDocumentName = oReffeDocFullName Then
                        oDerivedParamTable = table
                        Exit For
                    End If
                Next
            Else
                ' Create a new table
                oDerivedParamTable = doc.ComponentDefinition.Parameters.DerivedParameterTables.Add(oReffeDocFullName)
                
                ' All parameters are new if table is new
                newParamsForThisDoc.Clear()
                For Each Param As Inventor.Parameter In oReffeDoc.ComponentDefinition.Parameters.UserParameters
                    newParamsForThisDoc.Add("From " & refDocName & ": " & Param.Name & " (new table)")
                Next
            End If
            
            ' Set collection of Parameters to the linked/derived table
            oDerivedParamTable.LinkedParameters = oLinkedParametersCollection
            
            ' Add new parameters to the output list
            For Each paramName As String In newParamsForThisDoc
                addedParameters.Add(paramName)
            Next
        End If
        
        ' Close the referenced document
        oGenericDoc.Close(False)
    Next
End Sub
Message 7 of 7

chris
Advisor
Advisor

Note: This code was written with Claude.ai (and a lot of prompts to get it right)

 

Here is an updated version of the above "attempt". The way this code works is that it allows a specifically listed set of "part files" that will be the controlling parameters for a specific list of assembly(s) or Part(s) files that will be affected.

 

In other words, specific assemblies and/or parts that are linked to specific part files will automatically update and include any new parameters added to the specific list of part files that the specific list of assemblies/parts are linked to. (hopefully you got that, lol)

 

All you have to do is change your list of parts files that the assemblies/parts will link to, and add a list of assemblies/parts that will be linked and this code should handle the rest. You can set the trigger for each assembly to be "before save" to trigger the update if you like.

 

See screen show for what parts of the code to change. 

chris_1-1742308257613.png

 

 

Sub Main()
    ' Create list to store parameter information
    Dim addedParameters As New System.Collections.Generic.List(Of String)
    
    ' The specific parameter files we want to use as sources
    Dim parameterFiles As New System.Collections.Generic.List(Of String)
    parameterFiles.Add("Linked Part-01.ipt")
    parameterFiles.Add("Linked Part-02.ipt")
    parameterFiles.Add("Linked Part-03.ipt")
    
    ' Get the current document
    Dim currentDoc As Document = ThisDoc.Document
    
    ' List of documents (assemblies and parts) that should have parameters updated
    Dim targetDocuments As New System.Collections.Generic.List(Of String)
    ' Assemblies
    targetDocuments.Add("Assembly Using Links-01.iam")
    targetDocuments.Add("Assembly Using Links-02.iam")
    targetDocuments.Add("Assembly Using Links-03.iam")
    ' Parts
    targetDocuments.Add("Part Using Links-01.ipt")
    targetDocuments.Add("Part Using Links-02.ipt")
    targetDocuments.Add("Part Using Links-03.ipt")
    
    ' Check if current document is one of our target documents
    Dim currentFileName As String = System.IO.Path.GetFileName(currentDoc.FullFileName)
    Dim isTargetDocument As Boolean = False
    
    For Each docName As String In targetDocuments
        If String.Compare(currentFileName, docName, True) = 0 Then
            isTargetDocument = True
            Exit For
        End If
    Next
    
    If Not isTargetDocument Then
        Dim message As String = "This document (" & currentFileName & ") is not in the list of documents to update." & vbCrLf & _
                               "Please open one of the following documents:" & vbCrLf
        
        For Each docName As String In targetDocuments
            message &= "- " & docName & vbCrLf
        Next
        
        MessageBox.Show(message, "Not a Target Document")
        Return
    End If
    
    ' Verify the current document is a part or assembly (has parameters)
    If Not (TypeOf currentDoc Is PartDocument OrElse TypeOf currentDoc Is AssemblyDocument) Then
        MessageBox.Show("The current document type is not supported." & vbCrLf & _
                        "Only parts (.ipt) and assemblies (.iam) can have parameters updated.", _
                        "Unsupported Document Type")
        Return
    End If
    
    ' Find and update parameters from each parameter file
    Dim totalSourcesProcessed As Integer = 0
    Dim totalSourcesFound As Integer = 0
    
    For Each parameterFileName As String In parameterFiles
        ' Find the parameter file in the project
        Dim paramFilePath As String = FindFileInProject(parameterFileName)
        
        totalSourcesProcessed += 1
        
        If String.IsNullOrEmpty(paramFilePath) Then
            addedParameters.Add("WARNING: Could not find '" & parameterFileName & "' in the active project.")
        Else
            totalSourcesFound += 1
            addedParameters.Add("===== Processing parameters from " & parameterFileName & " =====")
            
            ' Update parameters from this parameter file
            UpdateParametersFromFile(currentDoc, paramFilePath, addedParameters)
            
            addedParameters.Add("") ' Add blank line for separation
        End If
    Next
    
    ' Show results to the user
    If addedParameters.Count > 0 Then
        Dim resultMessage As String = String.Format(
            "Parameter Update Results ({0} of {1} parameter files found):" & vbCrLf & vbCrLf, 
            totalSourcesFound, 
            totalSourcesProcessed)
            
        For Each param As String In addedParameters
            resultMessage &= param & vbCrLf
        Next
        
        MessageBox.Show(resultMessage, "Parameter Update Results")
    Else
        MessageBox.Show(String.Format(
            "No parameters were updated. ({0} of {1} parameter files found)", 
            totalSourcesFound, 
            totalSourcesProcessed), 
            "Parameter Update Results")
    End If
End Sub

' Find a file in the active project by filename
Function FindFileInProject(ByVal fileName As String) As String
    Try
        ' Get the active project
        Dim project As DesignProject = ThisApplication.DesignProjectManager.ActiveDesignProject
        
        ' Store the result
        Dim foundFilePath As String = ""
        
        ' First try to find in workspace folder directly
        Dim workspacePath As String = project.WorkspacePath
        Dim potentialPath As String = System.IO.Path.Combine(workspacePath, fileName)
        
        If System.IO.File.Exists(potentialPath) Then
            Return potentialPath
        End If
        
        ' Next try the registered project paths
        For Each folder As ProjectPath In project.WorkspaceFolders
            potentialPath = System.IO.Path.Combine(folder.Path, fileName)
            
            If System.IO.File.Exists(potentialPath) Then
                Return potentialPath
            End If
            
            ' Also try subfolders (one level deep)
            Try
                For Each subfolder As String In System.IO.Directory.GetDirectories(folder.Path)
                    potentialPath = System.IO.Path.Combine(subfolder, fileName)
                    
                    If System.IO.File.Exists(potentialPath) Then
                        Return potentialPath
                    End If
                Next
            Catch
                ' Skip if we can't access subfolders
            End Try
        Next
        
        Return ""  ' File not found
    Catch ex As Exception
        Return ""  ' Return empty on error
    End Try
End Function

' Update parameters from the specified file to the current document
Sub UpdateParametersFromFile(ByVal currentDoc As Document, ByVal parameterFilePath As String, ByRef addedParameters As System.Collections.Generic.List(Of String))
    Try
        ' Remember parameters before update
        Dim beforeParams As New System.Collections.Generic.Dictionary(Of String, String)
        For Each param As UserParameter In currentDoc.ComponentDefinition.Parameters.UserParameters
            beforeParams.Add(param.Name, param.Expression)
        Next
        
        ' Open the parameter document
        Dim paramDoc As Document = ThisApplication.Documents.Open(parameterFilePath, False)
        
        If Not (TypeOf paramDoc Is PartDocument) Then
            addedParameters.Add("ERROR: Parameter file is not a part document.")
            paramDoc.Close(False)
            Return
        End If
        
        Dim paramPartDoc As PartDocument = paramDoc
        
        ' Make sure we have a modifiable document
        If Not currentDoc.IsModifiable Then
            addedParameters.Add("ERROR: Current document is read-only or locked.")
            paramDoc.Close(False)
            Return
        End If
        
        ' Create a collection for the parameters
        Dim paramCollection As ObjectCollection = ThisApplication.TransientObjects.CreateObjectCollection
        
        ' Get simple filename for reporting
        Dim paramFileName As String = System.IO.Path.GetFileName(parameterFilePath)
        
        ' Check if there are user parameters to link
        If paramPartDoc.ComponentDefinition.Parameters.UserParameters.Count = 0 Then
            addedParameters.Add("Parameter file contains no user parameters.")
            paramDoc.Close(False)
            Return
        End If
        
        ' Add all user parameters from parameter file to collection
        For Each param As UserParameter In paramPartDoc.ComponentDefinition.Parameters.UserParameters
            paramCollection.Add(param)
        Next
        
        ' Find or create the derived parameter table
        Dim paramTable As DerivedParameterTable = Nothing
        Dim isNewTable As Boolean = True
        
        ' Check for existing table
        For Each table As DerivedParameterTable In currentDoc.ComponentDefinition.Parameters.DerivedParameterTables
            If table.ReferencedDocumentDescriptor.FullDocumentName = parameterFilePath Then
                paramTable = table
                isNewTable = False
                Exit For
            End If
        Next
        
        ' Create new table if needed
        If paramTable Is Nothing Then
            paramTable = currentDoc.ComponentDefinition.Parameters.DerivedParameterTables.Add(parameterFilePath)
            addedParameters.Add("Created new parameter table for " & paramFileName)
        End If
        
        ' Set parameters to table and update
        paramTable.LinkedParameters = paramCollection
        
        ' Compare parameters to see what was added or changed
        Dim afterParams As New System.Collections.Generic.Dictionary(Of String, String)
        For Each param As UserParameter In currentDoc.ComponentDefinition.Parameters.UserParameters
            afterParams.Add(param.Name, param.Expression)
        Next
        
        ' Find new and changed parameters
        Dim changes As Integer = 0
        
        For Each kvp As KeyValuePair(Of String, String) In afterParams
            If Not beforeParams.ContainsKey(kvp.Key) Then
                ' This is a new parameter
                addedParameters.Add("ADDED: " & kvp.Key & " = " & kvp.Value)
                changes += 1
            ElseIf beforeParams(kvp.Key) <> kvp.Value Then
                ' This parameter changed value
                addedParameters.Add("UPDATED: " & kvp.Key & " from " & beforeParams(kvp.Key) & " to " & kvp.Value)
                changes += 1
            End If
        Next
        
        ' Report if no changes
        If changes = 0 Then
            addedParameters.Add("All parameters were already up to date")
        End If
        
        ' Close the parameter file
        paramDoc.Close(False)
    Catch ex As Exception
        addedParameters.Add("ERROR: " & ex.Message)
    End Try
End Sub

 

 

0 Likes