Hi guys,
I need some help with iLogic. I have an assembly where 8 of the 10 parts are driven by Multi-Body Model State. The source Multi-Body part has multiple Model States with various sizes. I would like to have a form in the Assembly where the user enters the Model state that they want their assembly to corresponding to.
From my basic understanding of iLogic, we want the iLogic rule to open all the 8 individual components and change the Multi State to what the user entered in the form. With the help from the post below, I was able to run external rules where the code goes to the individual components and changes the model state. The difficulty I'm having is where I want to incorporate the Form function with user input for the Model State. Currently I'm opening the source code and manually changing the Multi-State name, I would like to change this so the users don't mess around the source code and just uses the Form to change between the Model States.
Solved! Go to Solution.
Solved by WCrihfield. Go to Solution.
Solved by frahaman7SKBG. Go to Solution.
This sounds fairly simple when you say it, but I'm thinking it will be more complicated than it sounds. In order to use an iLogic Form in the main assembly that will allow you to change the ModelState of all those components, you would likely have to create a multi-value, text type user parameter within your main assembly for each of those components, that contains the names of each available ModelState within that component. Then you will need to create the Form. Then you will need to create a rule that is triggered by those specific user parameters changing in that main assembly. Then this rule will need to be able to identify which parameter is for which component, so that when you loop through the components, it will know which parameter to get the ModelState name from to assign to the component. This is just a game plan for the project. One additional tool you may or may not need to help in this task, is a rule that will loop through those components first, get their list of available ModelState names, and write them to the multi-value parameters in your main assembly. Then if you continue to develop any of those components and add or subtract from those ModelStates, you would need to run this rule again to update the parameters multi-value lists, or update them manually.
Wesley Crihfield
(Not an Autodesk Employee)
It sounds like you're looking for something like this? 🙂
You can create a multivalue parameter, in my case named ModelStates and have a rule that tries to update all the occurrences modelstates in the assembly to that parameters value.
Sub Main Dim oAsm As AssemblyDocument = ThisDoc.Document Dim oDef As AssemblyComponentDefinition = oAsm.ComponentDefinition For Each oOcc As ComponentOccurrence In oDef.Occurrences Try ChangeState(oOcc, ModelStates) Catch End Try Next End Sub Sub ChangeState(occ As ComponentOccurrence, stateName As String) occ.ActiveModelState = stateName End Sub
Jhoel Forshav
Download my free Inventor Addin - Hole Projector
LinkedIn | Ideas | Contributions | Blog posts | Website
I forgot to add the screencast
Jhoel Forshav
Download my free Inventor Addin - Hole Projector
LinkedIn | Ideas | Contributions | Blog posts | Website
Thanks @JhoelForshav, but for some reason your code does nothing. However, I was able to come up with a solution that seem to be doing the task I wanted.
I first use one rule to take user input to capture the new Model State and place it as a custom iProperty and then use another rule to push this custom iProperty that has a new Model State name to all the sub-assemblies and parts. I have added a rule to all the sub-assemblies and parts to change Model State to what the custom iProperty has. And then I run another rule in my man assembly that runs this rule that is on each individual part. Below are snippets of all the rules. Let me know if there is a faster/better way of doing this.
Let me know if my explanation makes sense, can do a screencast to show how it works.
Step 1:
'Promt the user for the value of a variable modelstate = InputBox("Please Enter the Model Sate Here", "Custom Property Input Box", "Eg. 21017-xxx") 'assign the value of that variable to a custom property iProperties.Value("Custom", "Model_State:") = modelstate
Step 2:
Sub Main 'Gets the Active Document oDoc = ThisDoc.Document 'Saves this document oDoc.Save 'Checks if the active document is an assembly If oDoc.DocumentType = kAssemblyDocumentObject 'Gets the assembly occurrences Dim oOccs As ComponentOccurrences oOccs = oDoc.ComponentDefinition.Occurrences 'Call the subprocedure to traverse the assembly Call TraverseAssembly(oOccs) End If End Sub '*************** Public Sub TraverseAssembly(oOccs As ComponentOccurrences) 'Integer to traverse the assembly occurrences Dim i As Integer 'For i=1 To oOccs.Count Dim oOcc As ComponentOccurrence For Each oOcc In oOccs 'oOcc = oOccs.Item(i) If oOcc.Suppressed = False 'Occurrence Name (Display Name) oOccName = oOcc.Name 'Gets the first three leters of the Occurrence Name (e.g. "STK") oOccPrefix = Left(oOccName, 3) If oOccPrefix <> "STK" Try ' iProperties.Value(oOccName, "Custom", "Model_State:") = iProperties.Value("Custom", "Model_State:") Catch 'It won't be able to change Read-Only parts (e.g. Content Center) 'MessageBox.Show("This is a content center file: " & oOcc.Name, "Title") End Try End If If oOcc.DefinitionDocumentType = kAssemblyDocumentObject Call TraverseAssembly(oOcc.SubOccurrences) End If End If Next End Sub
Step 3:
Sub Main auto = iLogicVb.Automation ' Set Rule name Dim ruleName As String ruleName = "Update_Model_State" ' Get the active assembly. Dim oAsmDoc As AssemblyDocument oAsmDoc = ThisApplication.ActiveDocument ' Get all of the referenced documents. Dim oRefDocs As DocumentsEnumerator oRefDocs = oAsmDoc.AllReferencedDocuments ' Iterate through the list of documents. Dim oRefDoc As Document For Each oRefDoc In oRefDocs Dim rule As Object rule = auto.GetRule(oRefDoc, ruleName) If (rule Is Nothing) Then ' Call MsgBox("No rule named " & ruleName & " was found in the document.") Else ' MessageBox.Show(rule.Name, oRefDoc.DisplayName) Dim i As Integer i = auto.RunRuleDirect(rule) End If Next End Sub
Here is the rule that is in the individual parts:
Dim doc As PartDocument = ThisDoc.Document Dim comp As DerivedPartComponent = doc.ComponentDefinition.ReferenceComponents.DerivedPartComponents.Item(1) Dim derivedDef = comp.Definition Logger.Info("current model state = {0}", derivedDef.ActiveModelState) Dim stateWanted = iProperties.Value("Custom", "Model_State:") If (derivedDef.ActiveModelState <> stateWanted) Then derivedDef.ActiveModelState = stateWanted comp.Definition = derivedDef ' apply the modified definition back to the DerivedPartComponent End If
Hi @frahaman7SKBG. Most of your code looks OK at first glance, but there was one point in 'Step 2' code that I saw that I believe will need to be changed. You are using a Sub routine in that rule that accepts a ComponentOccurrences object as its input variable. You are using this properly the first time, within your main Sub of that rule, but within that 'TraverseAssembly' Sub itself, where it calls itself again, you are supplying it with a ComponentOccurrencesEnumerator object, which I don't think will work. You should get the ComponentOccurrences from that assembly type component's definition, and use that as input for that Sub routine, similarly to how you did in the main Sub part of that rule instead. Does that make sense to you?
Wesley Crihfield
(Not an Autodesk Employee)
Hi @WCrihfield , Thanks for your response.
I am not an expert in iLogic in any capacity. Most of my codes are taken as snippets from various forums and modified slightly to suit my needs. I think I understand what you are saying, but I don't have the in-depth knowledge on how to put it in code format. Would you be able to provide me with a snippet that I can use? Thank you for all your wonderful help and suggestion.
Sure. This is just a slight update to the code you posted in your 'Step 2' above.
Mainly I just changed this line:
Call TraverseAssembly(oOcc.SubOccurrences)
to this:
TraverseAssembly(oOcc.Definition.Occurrences)
Because oOcc.SubOccurrences returns a ComponentOccurrencesEnumerator object, and the oOcc.Definition.Occurrences returns a ComponentOccurrences object, which matches the Type of the input variable specified in the definition line of the TraverseAssembly Sub routine. Also you don't really need to include the term 'Call' in front of those lines in iLogic. That is more for use in a VBA macro.
Here is the whole thing again, just to be clear.
Sub Main
'Gets the 'local' document (the one this rule is saved within)
'or the document that was 'Active' when the rule started
oDoc = ThisDoc.Document
'Saves this document
oDoc.Save
'Checks if the active document is an assembly
If oDoc.DocumentType = kAssemblyDocumentObject
'Gets the assembly occurrences
Dim oOccs As ComponentOccurrences = oDoc.ComponentDefinition.Occurrences
'Call the subprocedure to traverse the assembly
TraverseAssembly(oOccs)
End If
End Sub
'***************
Public Sub TraverseAssembly(oOccs As ComponentOccurrences)
Dim oOcc As ComponentOccurrence
For Each oOcc In oOccs
If oOcc.Suppressed = False Then
'Occurrence Name (Display Name)
oOccName = oOcc.Name
'Gets the first three leters of the Occurrence Name (e.g. "STK")
oOccPrefix = Left(oOccName, 3)
If oOccPrefix <> "STK" Then
Try
iProperties.Value(oOccName, "Custom", "Model_State:") = iProperties.Value("Custom", "Model_State:")
Catch
'It won't be able to change Read-Only parts (e.g. Content Center)
'MessageBox.Show("This is a content center file: " & oOcc.Name, "Title")
End Try
End If
If oOcc.DefinitionDocumentType = kAssemblyDocumentObject
TraverseAssembly(oOcc.Definition.Occurrences)
End If
End If
Next
End Sub
Wesley Crihfield
(Not an Autodesk Employee)
Ok, so I think I need to figure this out for a project.
I have a multibody part that drives a Main Assembly, I need to be able to switch model states on each part via ONE main switch in the Main Assembly. But I'm not sure where to put each part of these codes? IE what code needs to be in the multibody part? What needs to be in the Main Assembly? What needs to be in each part?
Thanks in advance for any insight and assistance.
HI Jhoel Forshav,
that works great but have some question about. I do have 3 MS in part level and 3 in assembly. They named same.
When i switching MS from assembly it is working just as supposed. When using you rule there is no error but it changed on assembly level but not on part level. It is not in sinc. Could you help i would like to change as you present in video which is working awsome.
Thank you
Hi @GosponZ. It sounds like you are wanting to simulate the 'Link ModelStates' tool, but from an iLogic rule, is that correct? If so, then the only thing you would likely need to add to the routine is to loop through each of the main assembly's ModelStates, activate it, then loop the components, trying to set them to a ModelState with the same name as the currently active assembly ModelState. I posted an example of this process last year at the following link:
However, I would probably enclose that one line that is trying to set the ModelState of components within a Try...Catch block, to avoid the potential errors that would occur when the component does not have a ModelState available to it with the same name.
Try : oOcc.ActiveModelState = oMS.Name : Catch : End Try
...and I would likely change the oADoc.Update to oADoc.Update2(True).
Wesley Crihfield
(Not an Autodesk Employee)
Thank you for fast answer. I tried all possible ways i cold think off but no success. So again 3 text parameters. 3 ms part level and 3 ms in assembly. Would like to change thru form like in above example.
IS there any way you can make this iterate through sub assemblies and any opart driven by a muitl solid so the shildren change when i change the model state in the assembly so that the children from the multibody change to the same. CAn't find a way to edit derived part and make all of the model states match so i can use the top level assembly in mutiple states. Basically the children do not change when i change the model state of the assembly.
Hi @t_fransman. Unfortunately I do not have time to tackle this one today, but hopefully I can get back here to work on this tomorrow, if I don't forget, or get swamped with other stuff. This can certainly be a pretty complex task to automate properly. And controlling that many levels of ModelStates in an assembly is not really something that I have ever needed to deal with where I work, especially where derived parts are involved (or skeleton modeling in an assembly).
I have dealt with first level components before, as you can see in the article I had a link to above, but perpetuating a top level ModelState change down to lower levels is more complicated, and would require the use of a 'recursive' Sub routine. One important factor in this type of routine is how you step down into each lower level. We can either use ComponentOccurrence.SubOccurrences property, or we can use ComponentOccurrence.Definition.Occurrences property. Which one you use can make a big difference, because it changes 'context'. Using SubOccurrences tends to keep the context 'as seen by the main assembly', while the other is more strict, only making changes within the definitions of those other referenced documents. One thing that may need to be included in a process like this is saving these ModelState status changes at each level of the assembly, within that parent assembly's active ModelState. But similar to dealing with view representations, this type of thing is best set up manually, starting with the lowest level parts, then lowest level sub assemblies, then stepping up the assembly structure until you get to the top level. Trying to do processes like this starting from the top level, then down through multiple levels is very challenging to get to work out correctly.
In the example codes that @frahaman7SKBG posted in Message 5, within the fourth code window, is a similar process that would have to be dealt with to change the ModelState of a derived part. But getting that change to show at the top level of an assembly that this component may be in a lower level of is another challenge. And I do not believe that whatever ModelState was active in the host part will record which ModelState you set the 'derived in' part to, so controlling that at the assembly level is going to be very challenging also, if possible.
Wesley Crihfield
(Not an Autodesk Employee)
THANKS BECAUSE THE OTHER CODE SEE MY HARDWARE AND THE FACT I HAVE A COUPLE BASIC WELDMENTS PROBABLY AND CRASHES MY SESSION EVERY TIME.
i just can't believe that this is not a fundamental default behavior. Lol
Another issue i am having (that may be easier to fix) is if i add the skeleton to the spreadsheet and make all the matching lines so it changes when a model state is selected at the top level it works but my rules to change number of of holes in the skeleton does not update until i open the skeleton. I've tried many event triggers at the skeleton level to no effect.
Hi @t_fransman. I finally found my way back here. I just typed/assembled some code that you can put into a single external iLogic rule for hopefully handling a large part of this task, but I did not leave much in the way of comments or feedback messages in there, because I was in a hurry. The main two things this code will attempt to do is the following:
I am not experienced with 'skeleton' modeling in an assembly, so if that is what you are doing, I may not be able to help much with that aspect of the project.
The code I have is in the attached text file.
Wesley Crihfield
(Not an Autodesk Employee)
Can't find what you're looking for? Ask the community or share your knowledge.