Update parameter, using iLogic, in all modelstates

Update parameter, using iLogic, in all modelstates

JelteDeJong
Mentor Mentor
4,971 Views
29 Replies
Message 1 of 30

Update parameter, using iLogic, in all modelstates

JelteDeJong
Mentor
Mentor

This seems like a silly question. But I'm moving from Inventor 2021 to Inventor 2023. And now I have to deal with model states. For my generator, I have many documents with even more iLogic rules. And all of them update parameters like this:

 

Parameter("test") = 123

 

Many assemblies have 2 "Levels of details", which get converted to model states. (The parameters in those model states need to be the same. I need to keep the same functionality as the "Levels of details".)

And that works if the "edit scope" is set to "All model states". But users can change that and of course, there are legit reasons for that. (That is the point of "Levels of details" and model states.)

I know that I can solve this with 1 line of extra code like this:

ThisDoc.FactoryDocument.ComponentDefinition.ModelStates.MemberEditScope = MemberEditScopeEnum.kEditAllMembers

So here is the real problem.

  • I don't like this very long line of code.
    • I would like something simple as:  ThisDoc.EditAllModelStates()
    • Or better no code at all just a setting in the iLogic form...
  • I have to do a lot of rework on my generator models.

Is there some setting that I did not find or has someone else found a solution for a similar problem?

 

 

Jelte de Jong
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


Blog: hjalte.nl - github.com

4,972 Views
29 Replies
Replies (29)
Message 2 of 30

Anonymous
Not applicable
I'm not sure there's a better way, or to be honest what's so wrong about it.
Anyway I do this all the time, for what that's worth.
But maybe someone else has a better idea
0 Likes
Message 3 of 30

JelteDeJong
Mentor
Mentor

I did say it was a silly question. 😉 I have this obsession with lines of code that get too long. They are hard to read if they run off the screen. like in this example you can't see what is happening without scrolling.

JelteDeJong_0-1677939636000.png

And long lines are hard to debug. For example, if you would get a NULL reference exception. You will get the line of the exception but you don't know what property is NULL/Nothing. Therefore I consider lines with more than 100 characters as a code smell. Anyway, in the end, this is not wrong but I hope to do better. Probably I will end up doing something like this. (Unless someone has a better idea)

 

Dim def = ThisDoc.FactoryDocument.ComponentDefinition
def.ModelStates.MemberEditScope = MemberEditScopeEnum.kEditAllMembers

 

Wich breaks the line into 2 parts and often I need the ComponentDefinition somewhere anyway.

But maybe my biggest issue is that I'm lazy 😁 and I have to update many rules. I hope for a solution where I don't have to do that.

 

 

 

Jelte de Jong
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


Blog: hjalte.nl - github.com

Message 4 of 30

Anonymous
Not applicable
Yeah I can sympathize, but still I don't think it's too bad 😛
Makes me think it would be nice if VB supported optional chaining
Anyway, splitting the line like in your second snippet is what I tend to do
Message 5 of 30

WCrihfield
Mentor
Mentor

Hi @JelteDeJong.  I do know of another line of code that may be shorter to use for setting the 'edit all members' specification, but I don't know if you would be able to use it outside of the iLogic environment.  There are several seemingly built-in utilities that we automatically have access to while working in the iLogic rule editor environment, because a lot of references are already included for us in the background.  One of the areas where you can find several ModelStates related utilities is within the Autodesk.iLogic.Runtime Namespace (which is already included for us).  Only a few items within that spectrum are mentioned in the official Help documentation, but there are tons more that you can access by code fairly easily.  One is the "ModelStateUtils" Class, and you do not need to instantiate it to use it, so you can just type that directly into a rule, and it will be recognized and ready to be used.  Under that object are 8 really handy methods.  The first two are for setting a cell value within the ModelStatesTable, and they both seemed to work OK for me, even when the 'input' Document was not necessarily the 'factory' version.  So, when using those, you may not even need to use the member edit scope.  However, there is also a "ModelStatesGlobalScope" Class within the Runtime area, which you have to instantiate with the 'New' keyword, but then you put your Document in as input.  Then I believe you can just work with that document like normal (I'd probably stick to API), then it has a Dispose method, which will return the edit scope to its original state.  I tested it an it works!  I would have included links, but as far as I know, they are undocumented publicly.

 

There is also a ModelStateOperations Class (under Runtime), which has 2 methods for getting and setting the 'active' ModelState name.

There are also 3 ModelStates related methods available under the ThisApplication.FileManager object.  One for getting the list of ModelState names for the supplied FullFileName.  One for getting the ModelState name from the FullDocumentName.  And one for getting the last active ModelState name from the supplied FullFileName.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 6 of 30

WCrihfield
Mentor
Mentor

I used 4 lines of code to change the value of a parameter the same in every ModelState, without needing to use a loop, when using this technique.  Here is an example of the code I used while testing on a part I had open, which had 3 total ModelStates.  The edit scope was set to member (not all members) before running, and was still there when the code finished, yet it worked on every ModelState.

Dim oDoc As Document = ThisApplication.ActiveDocument
Dim MSGS As New ModelStatesGlobalScope(oDoc)
	oDoc.ComponentDefinition.Parameters.Item("Length").Expression = "1.5 in"
MSGS.Dispose

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 7 of 30

JelteDeJong
Mentor
Mentor

@WCrihfield I was unaware that those functions are in the iLogic dll's. That is good to know. I guess that I did have the same thoughts as the Autodesk developers. For my addins, I created a class that can set the edit scope to "all members" and implemented the IDisposible interface to return everything to its original state. (Very similar to the ModelStatesGlobalScope class.) in this same class, it's also possible to set the active model state which also gets reset if the class is disposed of. And last, It can give you the factory document. I will share the  class here (and later in my blog when it's finished.)

But first, you can optimise your snippet a bit. (I hope that you don't mind the silly comment.)

If a class implements the interface IDisposible then you can use the "using" statement. And the "ModelStatesGlobalScope" class does. That would look like this:

Dim oDoc As Document = ThisApplication.ActiveDocument
Using MSGS As New ModelStatesGlobalScope(oDoc)
	' some code here...
	Throw New Exception()
end using 

Normally you would not add an exception but that can happen. And if an exception is thrown the "using" statement has an advantage. It will make sure that the method "Dispose()" always gets called. In this example that would mean that the edit scope is returned to its original state. Where your code snippet would stop at the exception and not return to the original edit scope. To get the same result as the using statement (without the using statement) you code should look like this:

Dim oDoc As Document = ThisApplication.ActiveDocument
Dim MSGS As New ModelStatesGlobalScope(oDoc)
Try
	'  some code here...
	Throw New Exception()
Finally	
	MSGS.Dispose
End Try

 

 

Jelte de Jong
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


Blog: hjalte.nl - github.com

Message 8 of 30

JelteDeJong
Mentor
Mentor

The previous post became so long that I decided to create 2 posts.

My "ModelStateManager" class can be used like this if you want to:

  • set the edit scope to "All members"
  • Activate the model state "MyModelState"
  • want to edit the factory document.
  • At the end want to return to the old model state status.

 

 

Dim doc As PartDocument = ThisDoc.Document
Using manager As New ModelStateManager(doc, MemberEditScopeEnum.kEditAllMembers)
    manager.ModelStates.Item("MyModleState").Activate()

    Dim factoryDoc As PartDocument = manager.FactoryDocument
    ' some code here....

End Using

 

 

or if you like:

 

 

Dim doc As PartDocument = ThisDoc.Document
Using manager As New ModelStateManager(doc)
    manager.ModelStates.Item("MyModleState").Activate()
    manager.EditScope = MemberEditScopeEnum.kEditAllMembers

    Dim factoryDoc As PartDocument = manager.FactoryDocument
    ' some code here....

End Using

 

 

Of course, you are not obligated to set the active model state and edit scope, For example, if you only want t get the factory document. (In that case, you also would not need the using statement.)

Anyway here is the implementation:

 

 

Public Class ModelStateManager
    Implements IDisposable

    Private _doc As Document
    Private _documentType As DocumentTypeEnum
    Private _startActiveModelState As ModelState
    Private _startMemberEditScope As MemberEditScopeEnum

    Public Sub New(ByVal doc As Document)
        If doc.DocumentType <> DocumentTypeEnum.kPartDocumentObject AndAlso doc.DocumentType <> DocumentTypeEnum.kAssemblyDocumentObject Then
            Throw New ArgumentException("This is not a part or assembly document")
        End If

        If doc Is Nothing Then
            Throw New ArgumentException("Document may not be NULL")
        End If

        _doc = doc
        _documentType = _doc.DocumentType
        _doc = FactoryDocument


        If ModelStates.Count <> 0 Then
            _startActiveModelState = ModelStates.ActiveModelState
        End If

        _startMemberEditScope = ModelStates.MemberEditScope
    End Sub

    Public Sub New(ByVal doc As Document, ByVal editScope As MemberEditScopeEnum)
        Me.New(doc)
        Me.EditScope = editScope
    End Sub

    Public ReadOnly Property FactoryDocument As Document
        Get
            If (_doc.ComponentDefinition.IsModelStateMember) Then
                Return _doc.ComponentDefinition.FactoryDocument
            End If

            Return _doc
        End Get
    End Property

    Public ReadOnly Property ModelStates As ModelStates
        Get
            Return _doc.ComponentDefinition.ModelStates
        End Get
    End Property

    Public Property EditScope As MemberEditScopeEnum
        Get
            Return ModelStates.MemberEditScope
        End Get
        Set(ByVal value As MemberEditScopeEnum)
            ModelStates.MemberEditScope = value
        End Set
    End Property

    Public Sub Dispose() Implements IDisposable.Dispose
        If (_startActiveModelState IsNot Nothing) Then
            _startActiveModelState.Activate()
        End If
        ModelStates.MemberEditScope = _startMemberEditScope
    End Sub
End Class

 

 

 

 

 

Jelte de Jong
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


Blog: hjalte.nl - github.com

0 Likes
Message 9 of 30

Anonymous
Not applicable
I never knew about the IDisposable interface or even the `Using` statement. I'd implemented the same thing without it, and originally tried to use the destructor to revert everything, but that was way too unreliable. But `Using` is nice because I can be sure I haven't forgotten anything. Anyway thanks
0 Likes
Message 10 of 30

JelteDeJong
Mentor
Mentor

I was notified that there was a bug in my ModelStateManger class. The FactoryDocument property did not always return the factory document. I have fixed the issue in the post above. It was also pointed out to me that people were experiencing problems while changing the ModelStateScope and active model state if the assembly was opened in a hidden state. I was not sure if I did test this so I created a test code. The Main code looks like this. (The complete code is 125 lines long and that is too much for 1 post I think. But I will attach the complete vb.net/iLogic code also so you can test it yourself on your own assembly.)

Sub Main()
    Dim fileName = "P:\ath\to\your\assembly.iam"
    Dim doc As Document = ThisApplication.Documents.Open(fileName, False)

    Using mainMSManager = New ModelStateManager(doc, GetAnotherModelStateScope(doc))
        SetAnotherModelStateActive(mainMSManager.ModelStates)

        doc = mainMSManager.FactoryDocument
        SetGuidParameter(doc)

        For Each refDoc As Document In doc.AllReferencedDocuments

            Using refMSManager = New ModelStateManager(refDoc, GetAnotherModelStateScope(refDoc))
                SetAnotherModelStateActive(refMSManager.ModelStates)
                SetGuidParameter(refMSManager.FactoryDocument)
            End Using

        Next
    End Using

    doc.Save2()
    doc.Close(True)
End Sub
Private Function GetAnotherModelStateScope(doc As Document)
    Dim currentScope As MemberEditScopeEnum = doc.ComponentDefinition.ModelStates.MemberEditScope

    If (currentScope = MemberEditScopeEnum.kEditActiveMember) Then
        Return MemberEditScopeEnum.kEditAllMembers
    Else
        Return MemberEditScopeEnum.kEditActiveMember
    End If
End Function

Private Sub SetAnotherModelStateActive(modelStates As ModelStates)
    Dim activeModelState As ModelState = modelStates.ActiveModelState
    Dim primaryModelState As ModelState = modelStates.Item(1)

    If (activeModelState Is primaryModelState) Then
        modelStates.Item(modelStates.Count).Activate()
    Else
        primaryModelState.Activate()
    End If
End Sub

 As you can see I open the assembly here in a hidden status and checked and change the ModelStateScope and active model state. I did not manage to get exceptions. Of course, that is how it's supposed and I'm happy about that. But if you see a bug or some potentially risky situation then plz let me know. Don't hesitate to tell me what I did not test.

Jelte de Jong
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


Blog: hjalte.nl - github.com

0 Likes
Message 11 of 30

JelteDeJong
Mentor
Mentor

I thought I had found all the issues I needed to look out for. And I wrote a blog post about it. ("Modelstates: iLogic and unexpected results") After I posted on Linkedin about the blog post, someone pointed out some other problem. I didn't fully understand what issue he was referring to. But it made me wonder if there are other issues that I need to look out for. If you know about any issues that I might encounter I would appreciate it if you could share them.

Jelte de Jong
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


Blog: hjalte.nl - github.com

0 Likes
Message 12 of 30

FxRod
Enthusiast
Enthusiast

Someone here ^^:

There's nothing wrong with your manager that I can see, but I encountered some issues in rules using references between documents created for earlier Inventor versions.

For working with iProperties in a bunch of components placed in an assembly for example, you have to get the primary model state to avoid errors. If a rule runs through all ReferencedDocuments to update iProperties, we could just use the iLogic-iProperties.Value(oRefDoc.DisplayName,...) for example. Not too clean, I know, but it worked fine. If oRefDoc is placed with a model state different from primary in the assembly, that doesn't work anymore, because the model state name is appended to the DisplayName. Anyway we can't predict which model state we get from ReferencedDocuments while writing a rule. I did the following to handle that by opening the respective file in primary state and continue working with this document instead of the original RefDoc:

 

		If oRefDoc.ModelStateName <> "" Then		
			oRefDoc = ThisApplication.Documents.Open(oRefDoc.FullFileName, False)
			If oRefList.Contains(oRefDoc) Then
				Continue For
			Else				
				oRefList.Add(oRefDoc)
			End If
		End If

 (I use the RefList to collect and process components with multiple occurrences only once)

 

Something similar happens when getting the document used for the title block of a drawing. The ThisDoc.ModelDocument for DrawingDocument does not work anymore, we have to get the View that is referred to by TextPropertySource first and then use that to get the ReferencedDocument.

A bit off-topic, but the changes of IV2023 have created more possibilities and with them the need for more distinction of cases in our rules.

0 Likes
Message 13 of 30

JelteDeJong
Mentor
Mentor

If I get it correct then you do something like this:

 

Dim doc As AssemblyDocument = ThisDoc.Document

For Each refDoc As Document In doc.AllReferencedDocuments

    iProperties.Value(refDoc.DisplayName, "Design Tracking Properties", "Stock Number") = "My new value"
    '                            ^------- This now includes the model state name.
Next

 

It never crossed my mind to update iProperies like this. Looks very effective and I would have considered using it.  But as you mentioned the display name contains the model state. And I did a test with a similar situation in Inventor 2021 but now with a level of detail (LoD). (And selected another LoD than the Master in the assembly.) In that case, I also get the LoD name from the DisplayName. Therefore I don't think of this as a changed functionality.

 

Offtopic:

On LinkedIn, you mentioned that you have 2 versions of this code. And that you need to switch depending on the Inventor version. Did you know that you can do everything from 1 rule and I expect that it does everything you want. something like this.

 

Dim doc As AssemblyDocument = ThisDoc.Document

If (doc.ComponentDefinition.IsiAssemblyMember) Then
    doc = doc.ComponentDefinition.FactoryDocument
End If

For Each refDoc As Document In doc.AllReferencedDocuments

    'Make sure you have the factory document. But only if you are running 2022 or higer
    If (ThisApplication.SoftwareVersion.Major > 25) Then ' > Inventor 2022
        If (refDoc.ComponentDefinition.isModelStateMember) Then
            refDoc = refDoc.ComponentDefinition.FactoryDocument
        End If
    End If

    Try
        ' check this post for set names of the iProperties
        ' https://modthemachine.typepad.com/my_weblog/2010/02/accessing-iproperties.html
        Dim propSet = refDoc.PropertySets.Item("Design Tracking Properties")
        Dim stockNumberProperty = propSet.Item("Stock Number")
        stockNumberProperty.Value = "My new value"
    Catch ex As Exception
        MsgBox("Unable to update iProperty in doc: " & refDoc.DisplayName)
    End Try

Next

(Edit: fixed bug changed to "isModelStateMember")

 

Jelte de Jong
Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.

EESignature


Blog: hjalte.nl - github.com

0 Likes
Message 14 of 30

FxRod
Enthusiast
Enthusiast
@JelteDeJong wrote:

It never crossed my mind to update iProperies like this. Looks very effective and I would have considered using it.  But as you mentioned the display name contains the model state. And I did a test with a similar situation in Inventor 2021 but now with a level of detail (LoD). (And selected another LoD than the Master in the assembly.) In that case, I also get the LoD name from the DisplayName. Therefore I don't think of this as a changed functionality.

Yes exactly, that's what I'm doing. Goes back to my first steps in iLogic, so, yes, it's effective in a way that I need few lines of code and few handles, but as we see now it's not very safe to do it this way. Today I would go down the classes tree and get the PropertySets.Item().[...]. This would still probably change the iProperties for the active model state, not for the base document, which can be intended but for me usually isn't.

I just didn't see the effect of the LoDs before because I didn't use LoDs this way. For subassemblies my usual policy is to always make the assembly state that's relevant for the BOM the standard model state (or LoD), so my rule never ran over subassemblies placed with active LoDs.

Now we can do it with parts, too, and sometimes I use different versions of a part in an assembly's draft state to compare them. So my rule sometimes encounters parts with active model states and this made my faulty code visible.


@JelteDeJong wrote:

If I get it correct then you do something like this:

Offtopic:

On LinkedIn, you mentioned that you have 2 versions of this code. And that you need to switch depending on the Inventor version. Did you know that you can do everything from 1 rule and I expect that it does everything you want. something like this.

[] 

The if-statement about the version is similar to my solution. That's what I mentioned as "different code", as the FactoryDocument is used in higher versions only. But now I see I don't need to get the refDoc's FullFileName and Documents.open it to get the base state, I can just get its ComponentDefinition.FactoryDocument, so saves some lines again. I didn't expect the FactoryDocument to be in the ComponentDefinition and didn't search for it thoroughly. Thank you!

0 Likes
Message 15 of 30

FxRod
Enthusiast
Enthusiast

A quick note about the model state check rules:

I think instead of "isIAssemblyMember" we should rather check the "isModelStateMember"-property, which is present in PartComponentDefinition as well as in AssemblyComponentDefinition, right? It seems to work for me and allows me to avoid case-switching for different DocumentTypes.

0 Likes
Message 16 of 30

WCrihfield
Mentor
Mentor

I realize this topic is not fresh anymore, but I was searching the forum for a similar topic and came across this one again, and saw that it did not have an accepted answer yet.  And after reading the original post again, then quickly scrolling through all the responses, I did not see this simple little line of iLogic code yet, so I thought I would post it here, just for reference.

iLogicVb.MemberEditScope = MemberEditScopeEnum.kEditAllMembers

And here is the link to the online help documentation about it:

https://help.autodesk.com/view/INVNTOR/2024/ENU/?guid=5c2263d4-6a0b-5aad-433d-d7d00787a8d5 

This (and the following) were apparently made available in the 2024 version of Inventor.

 

It appears as though, if you are using simple iLogic snippets like Parameter(), iProperties.Value(), Component.IsActive(), etc, this will effect how they behave (will try to apply those changes to all ModelStates that may be present).  And similarly, each of those individual iLogic snippet types appears to have its own granular control for this, which is nice.

iProperties.MemberEditScope = MemberEditScopeEnum.kEditAllMembers
Parameter.MemberEditScope = MemberEditScopeEnum.kEditAllMembers
Component.MemberEditScope = MemberEditScopeEnum.kEditAllMembers
Feature.MemberEditScope = MemberEditScopeEnum.kEditAllMembers
Constraint.MemberEditScope = MemberEditScopeEnum.kEditAllMembers
Joint.MemberEditScope = MemberEditScopeEnum.kEditAllMembers

If this solved your problem, or answered your question, please click ACCEPT SOLUTION .
Or, if this helped you, please click (LIKE or KUDOS) 👍.

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 17 of 30

chrisw01a
Collaborator
Collaborator

This looks like exactly what I need to simplify some things.

I am not able to get this to work. Do you know why? My code is attached.

 

chrisw01a_0-1697049934600.png

 

0 Likes
Message 18 of 30

WCrihfield
Mentor
Mentor

Hi @chrisw01a.  I do not download or open ZIP or other similar types of files from forums, due to company security policies.  If you are using Windows 10, try opening your system settings, then go to the System topic, then to the About sub topic, then click on the 'Advanced system settings' link on the right of the screen.  Then go to the Advanced tab of that dialog, and down to the 'Environment Variables...' button.  If you do not have administrators rights on your computer (like me, also due to company security policies), you might not be able to do that though.  It does seem nuts that we would have to deal with a system environment variable like that though.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 19 of 30

chrisw01a
Collaborator
Collaborator
Are you suggesting that I go and add the variable manually? I'll have to do that on our other workstations as well if it works...
0 Likes
Message 20 of 30

WCrihfield
Mentor
Mentor

I should clarify one important detail first.  I have not actually messed with that system environment variable on my end yet, and I do not utilize that method in my own production codes yet.  I generally use Inventor API code for as much of my production related code as possible, so I do not heavily utilize, or rely on a lot of code that is unique to the iLogic add-in or iLogic API.  I do definitely use some of it in some situations quite regularly, just not that specific tool yet.  I do plan on getting that looked into by our IT department when I have them update my Autodesk software (and other stuff) the next time though.

I just happened to notice those references being available while typing code in iLogic rules, and looked into their online help documentation at the time, because it was pretty interesting.  Then I came across this post again after some time, and that brought it to mind again, so I posted it, because I thought it would be useful to others.  You could contact someone at Autodesk directly if you are hesitant about it, like their technical support folks.  I worked with them just a month or so ago about an issue I was having with iLogic rule buttons in my ribbons disappearing.  They were fairly responsive.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes