So I just finally learned how to pass arguments when running an iLogic rule, which is awesome. This opens up a lot of doors. I can basically use an external rule as a "Sub" -- pass arguments to it, and make it do stuff based on them.
However, now that I can do this, it would be really neat if I could use an External rule as a "function". I'm wondering if it's possible for the called rule (the one that received arguments from the calling rule) to return a value BACK to the calling rule?
I've created a simple example below of how this might work. There's a main rule that wants to get the value of a custom iProperty, and it does so by calling on an External rule called "CustomPropCheck". Telling the External rule which iProperty to check for is easy enough, by passing it as an argument.
However, where I'm stumped is how to pass the iProperty value that "CustomPropCheck" gets BACK to the calling Main Rule.
The only thing I can think of is to maybe use Shared Variables, but I'd like to do this in a more direct way, like how Arguments work. Is there some way to pass a value back UP the call stack, like we can pass them DOWN the call stack via arguments?
Thanks for any suggestions!
Main Rule:
'---Set up arguments'---
Dim oRuleArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap() oRuleArguments.Add("oPropName","Total QTY")
'---Call external "function" rule--- iLogicVb.RunExternalRule("CustomPropCheck",oRuleArguments)
'---Retrieve resultant value from "function" rule--- 'How??
'---Display retrieved value--- MessageBox.Show("Main Rule - iProp Value: " & oPropValue)
"CustomPropCheck" external rule:
'---Get argument values--- Dim oPropName As String = RuleArguments("oPropName")
'---Determine iProperty value--- Dim oPropValue As String Try oPropValue = ThisDoc.Document.PropertySets.Item("Inventor User Defined Properties").Item(oPropName).Value Catch oPropValue = "**DNE**" End Try
'---Display iProperty value--- MessageBox.Show("CustomPropCheck - iProp Value: " & oPropValue)
'---Pass oPropValue value back to main rule--- 'How??
Solved! Go to Solution.
Solved by R.Mabery. Go to Solution.
Hi DRoam,
Hope this helps
Main Rule:
'---Set up arguments'--- Dim oRuleArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap() oRuleArguments.Add("oPropName","Total QTY") '---Call external "function" rule--- iLogicVb.RunExternalRule("CustomPropCheck",oRuleArguments) '---Retrieve resultant value from "function" rule--- oPropValue = oRuleArguments.Item("Return") '---Display retrieved value--- MessageBox.Show("Main Rule - iProp Value: " & oPropValue)
CustomPropCheck rule:
'---Get argument values--- Dim oPropName As String = RuleArguments("oPropName") '---Determine iProperty value--- Dim oPropValue As String Try oPropValue = ThisDoc.Document.PropertySets.Item("Inventor User Defined Properties").Item(oPropName).Value Catch oPropValue = "**DNE**" End Try '---Display iProperty value--- MessageBox.Show("CustomPropCheck - iProp Value: " & oPropValue) '---Pass oPropValue value back to main rule--- RuleArguments.Arguments.Value("Return") = oPropValue
Let me know if you have any questions.
Hi Randy, that's a really good suggestion. I thought it might work, but I tested it and it doesn't (unless I'm doing something wrong). I think it's because the RuleArguments collection for the "CustomPropCheck" rule is local to that rule, and likewise for the RuleArguments collection in the Main Rule. So we add the "Return" argument to CustomPropCheck's arguments with:
RuleArguments.Arguments.Value("Return") = oPropValue
But then when Main Rule takes back over, "Return" is not in its RuleArguments collection. So this line:
oPropValue = oRuleArguments.Item("Return")
...just returns an empty string.
Good suggestion, though.
After doing a bit more research, I'm fairly certain what I'm trying to do isn't possible (except by using Shared Variables). There doesn't seem to be any mechanism for passing a return value from a called iLogic rule to the calling rule (or, even for passing a return value from a called VBA function to the calling iLogic rule -- see here under the "Run Macro" section -- it says "You can call a VBA Function, but you cannot get a return value.").
Any other suggestions are welcome, though.
PS, one of the reasons I'm looking for something other than Shared Variables, is those can only store basic string, integer, Boolean, etc. variables, and I'd like for these Eternal iLogic rule "functions" to be able to return other Inventor objects as well.
PPS: well whaddya know, I was totally wrong about that. I had *assumed* that Shared Variables can only store those types of variables, but as it turns out they can store other object types, too, including Inventor objects like documents. So the code snippets below do exactly what I want:
Main Rule:
'---Set up arguments'--- Dim oRuleArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap() oRuleArguments.Add("oPropName","Total QTY") '---Call external "function" rule--- iLogicVb.RunExternalRule("CustomPropCheck",oRuleArguments) '---Retrieve resultant value from "function" rule--- oPropValue = SharedVariable("oPropValue") SharedVariable.Remove("oPropValue") '---Display retrieved value--- MessageBox.Show("Main Rule - iProp Value: " & oPropValue)
"CustomPropCheck" external rule:
'---Get argument values--- Dim oPropName As String = RuleArguments("oPropName") '---Determine iProperty value--- Dim oPropValue As String Try oPropValue = ThisDoc.Document.PropertySets.Item("Inventor User Defined Properties").Item(oPropName).Value Catch oPropValue = "**DNE**" End Try '---Display iProperty value--- MessageBox.Show("CustomPropCheck Rule - iProp Value: " & oPropValue) '---Pass oPropValue value back to main rule--- SharedVariable("oPropValue") = oPropValue
This apparently will work for "returning" any object type back to the calling rule, including Inventor object types. Actually, I could even return multiple values back, as multiple Shared Variables, if required.
Unless someone replies back with a way to directly return a value from a called rule back to the calling rule, this will work for my purposes.
Shared Variables is my recommended route, basically they're variables that stay in the Inventor session memory until the session is closed or deleted by code. The beauty is it also makes it easier to have multiple rules throughout your workflow to reference the same variables. Whenever a relevant file is open I run a rule that automatically generates an appropriate set of Shared Variables so they can be referenced by whatever rule at any given time.
DRoam,
I'm curious to find out why it isn't working on your end....if you would indulge me with some questions.
What does the msgbox that's coming from the CustomPropCheck rule show?
What does the msgbox that's coming from the Main Rule show?
Thanks,
If I can add my $0.02 to this thread:
I too recently figured out how to use RuleArguments to pass values from one rule to another in order to allow creation of "cascading multi-value lists" i.e. lists whose participants change based on the values in other "parent" lists.
Returning values from iLogic rules ought to be possible, but without testing it, the only way I can think of achieving this would be to leverage the IiLogicAutomation Interface described here: http://help.autodesk.com/view/INVNTOR/2019/ENU/?guid=__iLogic_API_html_8db501bb_0b0d_acaf_e151_dca66...
This would (I think) require the calling rule to actually create an iLogicRule object with which you should in theory be able to pass properties to/from.
After further reading, I don't think this is possible since the RunExternalRule method returns 0 if the resultant rule ran successfully. Attached is a file I created a week or so ago to demonstrate the cascading list principal to a colleague and here are the rules it uses:
(this first external rule populates a series of multivalue list objects in the parent assembly)
Option Explicit On Imports System.Linq ''' This rule is designed to populate a series of supplied column parameters, creating them as required. Sub Main() 'Dim trans As Transaction = ThisApplication.TransactionManager.StartTransaction(ThisApplication.ActiveDocument, "Populate Multivalue Lists") Dim columns As String = "E,G,I,L,N,O,R" 'this could be automated by adding an "isKey" at the top of each column!? 'Try Call SetMatchingParameterNames(columns, cDefaultMultivaluePrefix, cDefaultMultivalueSuffix) Call SetMatchingParameterNames(columns, cDefaultMultivaluePrefix) Call PopulateColumnDefaults(columns, cDefaultMultivaluePrefix, cDefaultMultivalueSuffix) Call PopulateColumnDefaults(columns, cDefaultMultivaluePrefix) 'trans.End() ' Catch ex As Exception ' Logger.Error("The error was: " & ex.Message) ' trans.Abort() ' End Try End Sub Public Const cStartRow As Integer = 7 Public Const cEndRow As Integer = 50 Public Const cDefaultMultivaluePrefix As String = "column" Public Const cDefaultMultivalueSuffix As String = "defaults" Public ColumnAList As New List(Of Object) Public ColumnBList As New List(Of Object) Public ColumnCList As New List(Of Object) Public ColumnDList As New List(Of Object) Public ColumnEList As New List(Of Object) Public ColumnGList As New List(Of Object) Public ColumnIList As New List(Of Object) Public ColumnLList As New List(Of Object) Public ColumnNList As New List(Of Object) Public ColumnOList As New List(Of Object) Public ColumnRList As New List(Of Object) Sub SetMatchingParameterNames(ByVal columnlist As String, ByVal columnPrefix As String, Optional columnSuffix As String = "") Dim Doc As AssemblyDocument = ThisApplication.ActiveDocument Dim columns As List(Of String) = columnlist.Split(",").ToList() For Each col As String In columns Dim thisParam As Inventor.Parameter = (From param As Inventor.Parameter In Doc.ComponentDefinition.Parameters Where param.Name = columnPrefix & col & columnSuffix Select param).FirstOrDefault() If thisParam Is Nothing Then Logger.Debug(columnPrefix & col & columnSuffix & " does not exist in " & Doc.FullFileName) Doc.ComponentDefinition.Parameters.UserParameters.AddByValue(columnPrefix & col & columnSuffix, "", UnitsTypeEnum.kTextUnits) Else Logger.Debug(thisParam.Name & " already exists") End If Next End Sub Sub PopulateColumnDefaults(ByVal columnlist As String, ByVal columnPrefix As String, Optional columnSuffix As String = "") Dim newFilesArray As New ArrayList newFilesArray = GoExcel.CellValues("path\to\spreadsheet.xlsx", "sheet1", "a1", "a2") Dim columns As List(Of String) = columnlist.Split(",").ToList() Logger.Debug(columns.ToString()) Dim TempList As New List(Of Object) For Each col As String In columns Logger.Debug("columnname: "& col) For MyRow As Integer = cStartRow To cEndRow If Not CStr(GoExcel.CellValue(col & MyRow)) = "" Then If isParamText(columnPrefix & col & columnSuffix) And Not TypeOf GoExcel.CellValue(col & MyRow) Is String Then TempList.Add(GoExcel.CellValue(col & MyRow).ToString()) Else TempList.Add(GoExcel.CellValue(col & MyRow)) End If End If Next TempList.Sort() TempList = TempList.Distinct().ToList() Logger.Debug("Column" & col & "list.Count: " & TempList.Count) Dim tmpArrayList As ArrayList = New ArrayList(TempList) MultiValue.List(columnPrefix & col & columnSuffix) = tmpArrayList TempList.Clear() Next ' break End Sub Function isParamText(ByVal paramName As String) As Boolean Logger.Debug("Parameter is: " & paramName) Dim p As Parameter = Parameter.Param(paramName) If p.Units = "Text" Or p.units = "Boolean" Then Return True Else Return False End If End Function Sub SetArrayListDefaults(col As String, tmplist As List(Of Object)) Select col Case "E" ColumnEList.addrange(tmplist) Case "G" ColumnGList.addrange(tmplist) Case "I" ColumnIList.addrange(tmplist) Case "L" ColumnLList.addrange(tmplist) Case "N" ColumnNList.addrange(tmplist) Case "O" ColumnOList.addrange(tmplist) Case "R" ColumnRList.addrange(tmplist) Case Else 'do nothing End Select End Sub
Inside the assembly file itself we need a series of rules which reference the newly created multivalue parameters by name so taking column L as an example we have:
'columnL Rule
s = columnL 'Logger.Debug("columnL updated to: " & Parameter("columnL")) Dim RuleArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap() RuleArguments.Value("ParameterName") = "columnL" iLogicVb.RunExternalRule("UpdateMultivalueLists", RuleArguments)
This rule will fire every time the columnL parameter is updated. Which in turn activates this rule:
' UpdateMultiValueLists Rule
Option Explicit On Sub Main() If Not RuleArguments.Exists("ParameterName") Then 'not fired from relevant rule MessageBox.Show("This rule only works from the context-sensitive files we need!") Exit Sub Else Dim paramName As String = RuleArguments.Value("ParameterName") Dim p As Parameter = Parameter.Param(paramName) Logger.Debug(paramName & " updated value = " & p.Value) Dim FilterArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap() FilterArguments.Value("ParameterValue") = p.Value iLogicVb.RunExternalRule("Filter" & paramName, FilterArguments) End If End Sub
Which in turn fires the next rule in the sequence In this case "FilterColumnL":
'FilterColumnL Rule Option Explicit On Sub main() If Not RuleArguments.Exists("ParameterValue") Then MessageBox.Show("How did we get here?") Else 'Column L Dim paramValue As String = RuleArguments.Value("ParameterValue") Dim FilterArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap() 'this needs to be a list of values to remove/add from the next column along. Dim filterList As List(Of String) = New List(Of String) Select paramValue Case "L" filterList.Add("C") filterList.Add("K") filterList.Add("S") Case "R" filterList.Add("F") filterList.Add("K") filterList.Add("M") filterList.Add("S") filterList.Add("T") Case Else End Select FilterArguments.Value("UniqueValues") = filterList FilterArguments.Value("ColumnToFilter") = cNextColumn iLogicVb.RunExternalRule("FilterColumnRule", FilterArguments) End If End Sub Public Const cNextColumn As String = "N"
The final rule in the sequence takes us back to editing the multivalue list:
Option Explicit On 'FilterColumn Rule Sub Main() If Not RuleArguments.Exists("UniqueValues") And Not RuleArguments.Exists("ColumnToFilter")Then MessageBox.Show("How did we get here?") Else Dim Doc As AssemblyDocument = ThisApplication.ActiveDocument Dim columnToEdit As Inventor.Parameter = (From param As Inventor.Parameter In Doc.ComponentDefinition.Parameters Where param.Name = "column" & RuleArguments.Value("ColumnToFilter") Select param).FirstOrDefault() ' Dim columnToEdit As Inventor.Parameter = Parameter.Param("column" & RuleArguments.Value("ColumnToFilter")) Dim listofUniqueValues As List(Of String) = RuleArguments.Value("UniqueValues") Logger.Debug(columnToEdit.Name) Logger.Debug(debugListValues(listofUniqueValues)) If Not listofUniqueValues Is Nothing And Not columnToEdit Is Nothing Then listofUniqueValues.Sort() Dim tmpArraylist As ArrayList = New ArrayList(listofUniqueValues) MultiValue.List(columnToEdit.Name) = tmpArraylist End If End If End Sub Function debugListValues(ByVal listofValues As List(Of String)) As String For Each Val As String In listofValues debugListValues = Val & "," & debugListValues Next End Function
If you store the last two rules externally, and open the attached assembly file you'll see there's an iLogic form included in the assembly that will update the values of column 'N' when you change the selection in column L.
I'm not sure how much of this helps what you were trying to accomplish, but hopefully it will present some ideas not seen or considered before.
Cheers,
Alex.
Apparently the Forum is so broken (on Google Chrome 70.0.3538.77) it no longer allows attachments. So you can find the assembly file here:
https://1drv.ms/u/s!AiGmKryqFliPmKx3tz9elwa4krd4tw
Hi @DRoam
I think R.Mabery was on the right track, but maybe the iproperty did not exist in your test, so it returned blank?
These example rules are what I used to test and it worked.
Note my external rule name was "Test Rule", since I keep an external rule named as such on disk for testing, I just reused it... change as needed.
I hope this helps.
Best of luck to you in all of your Inventor pursuits,
Curtis
http://inventortrenches.blogspot.com
'---Set up arguments'--- Dim oRuleArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap() oRuleArguments.Add("oPropName","Total QTY") '---Call external "function" rule--- iLogicVb.RunExternalRule("Test Rule",oRuleArguments) '---Retrieve resultant value from "function" rule--- oPropValue = oRuleArguments.Item("T_QTY") '---Display retrieved value--- MessageBox.Show("Total QTY: " & oPropValue)
Test Rule:
'---Get argument value--- Dim oPropName As String = RuleArguments("oPropName") Try 'try to get current value to use in input box default oPropValue = iProperties.Value(ThisDoc.Document ,"Custom", oPropName) Catch 'catch error when iprop doesn't exit End Try 'get user input oInput = InputBox("Enter value for " & oPropName, "iLogic",oPropValue) 'sets or creates/sets iproperty iProperties.Value(ThisDoc.Document,"Custom", oPropName) = oInput 'create return value RuleArguments.Arguments.Value("T_QTY") = oInput
Ah, indeed you're right, Curits! Thanks for double-checking me on that. I'm really not sure why it didn't work before -- I was getting a value of "1" in the first messagebox but blank in the second, so I assumed the argument wasn't getting passed back properly. But I just tested again and it is indeed working.
So @R.Mabery your answer is exactly what I was looking for, thanks!
Maybe the two of you can enlighten me.... why does this work? Lol. I would have thought that "RuleArguments" are local object collections in each rule, and only get shared between rules if passed explicitly as a NameValueMap in a RunRule method...
So how does this work?
@DRoam wrote:
I would have thought that "RuleArguments" are local object collections in each rule, and only get shared between rules if passed explicitly as a NameValueMap in a RunRule method...
So how does this work?
Hi @DRoam
It works via the use of the APIs Transient Objects, which are objects that are considered temporary and only exist in memory.
In other words, it works by first imagining a spoon , then once imagined you can bend that spoon... or in this case use the spoon to ladle soup from another rule.
I hope this helps.
Best of luck to you in all of your Inventor pursuits,
Curtis
http://inventortrenches.blogspot.com
Hahaha ahh Curits, always on point with the spoon analogies... so basically what you're saying is instead of typing...
ThisApplication.TransientObjects.CreateNameValueMap()
...I could instead write:
ThisApplication.Spoons.CreateNameValueMap()
Correct?
I think I fully understand now, thanks 😉
Dim Spoons As TransientObjects Spoons = ThisApplication.TransientObjects Dim oRuleArguments As Inventor.NameValueMap = Spoons.CreateNameValueMap() oRuleArguments.Add("oPropName","Total QTY")
I try to check how your solution works on a standard sample 3Dmodel (C:\Autodesk\autodesk_inventor_2019_samples_dlm\Models\Assemblies\Engine MKII\Components\Flywheel.ipt)
The rule i've run is 100% equal to Main Rule from your post marked as a solution.
The CustomPropCheck rule has only 1 line difference to yours (I've replaced a Custom iProp with Standard iProp*):
Dim oPropName As String = RuleArguments("oPropName") Dim oPropValue As String Try oPropValue = iProperties.Value("Project", "Part Number") Catch oPropValue = "**DNE**" End Try MessageBox.Show("CustomPropCheck - iProp Value: " & oPropValue)
I them get two consequent messages:
1st message with iProp value (as expected):
But 2nd message is empty:
The whole process seems so simple, thus I'm a bit puzzled regarding what was my mistake.
Any ideas?
* Neither it works with custom iProp (same result - 1st msg is OK; 2nd is empty).
PS: I've used Inventor 2019.2
Issue is also reproducible after installing Update 2019.3
@Maxim-CADman77, it looks like you're missing this line at the end of your CustomPropCheck rule:
'---Pass oPropValue value back to main rule--- RuleArguments.Arguments.Value("Return") = oPropValue
Wow!
Seems like the workflow discussed here doesn't support using CustomPropCheck rule from the shared net folder.
Can you please re-check this statement on your side?
...I apologize for the simplicity of my question, but I probably miss a point in this construct (I am definitely NOT a software developer, so there may be something obvious that I ignore).
I am trying to solve a very simply problem: I want to pass an argument to my external rule "CleanUP" from my rule "DoSomething". Somehow I fail to figure out how to populate the NameValueMap. My rule CleanUP is designed to do some stuff, and give a feedback once it is done. When I call this rule from another rule, I prefer NOT to have that feedback, as this will be already coming from calling rule.
Here my rule "DoSomething":
Sub Main
Dim RuleTitle As String = "Do Something"
Dim oRuleArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap()
Dim hideFeedback As Boolean = True
oRuleArguments.Add(hideFeedback)
iLogicVb.RunExternalRule("CleanUP",oRuleArguments)
'--here I actually do stuff and give feedback once I am done...
End Sub
and here my rule "CleanUP":
Sub Main
Dim RuleTitle As String = "Clean UP"
Dim showFeedback As Boolean = True
If RuleArguments.Exists("hideFeedback") Then
ShowFeedback = False
End If
'--here I actually do stuff...
If ShowFeedback = True Then
MsgBox("CleanUP is done"& vbCr &"You're good to go!!", vbOKOnly + vbInformation, RuleTitle)
End If
End Sub
Whatever I try to do my code always fails at the line oRuleArguments.add
This makes me think that probably I am missing something so simple that it is obvious for all of you but not for me.
Hi @freesbee,
On the oRulearguments.add line, you need to add both the namevaluemap object and the name it is associated with:
Dim oRuleArguments As Inventor.NameValueMap = ThisApplication.TransientObjects.CreateNameValueMap()
Dim hideFeedback As Boolean = True
oRuleArguments.Add("hideFeedback", hideFeedback)
🙂
...thank you so much!
I would have never figured out on my own.
Now I have also understood entirely how it works...
Can't find what you're looking for? Ask the community or share your knowledge.