I have an iLogic rule in which I'm trying to set three user parameters for the gage of each leg of a steel angle. I want Inventor to set the gage based on the length of each leg. Here's the table I referenced for gages: ftp://ftp.clarkson.edu/.class/ce442/public_html/workshop_2.pdf.
In my code, I have a sub called DetermineGage which takes the leg length, whether it's the short leg or the long leg, and determines the gages that should belong to that leg. Inside of that sub it calls on a sub called ApplyGage which then applies the determined gage to the rule's gage variables. The main sub then grabs those variables and applies those values to the actual user parameters.... In theory.
The rule executes ok with no errors, but doesn't change the value of the user parameters; they're always "0". As best I can tell, the "oGage" variables I used in the rule don't even get assigned the correct value, which makes me think the DetermineGage and ApplyGage subs can't even access the oLegLength or oGage parameters to read or set them. I don't want to use global parameters because I've heard that can make programs very unstable over time and is just bad practice. How can I make these variables accessible to all subs IN THIS RULE, no more no less? Is this possible? See my code below. Thanks for any support!
My code:
Sub Main() Dim oLegShort As Double = Min(G_W,G_H) Dim oLegLong As Double = Max(G_W,G_H) Dim oLegLength As Double Dim oGage As Double Dim oGage1 As Double Dim oGage2 As Double 'Short Leg: oLegLength = oLegShort 'Call DetermineGages sub to set oGage variables: DetermineGages() On Error Resume Next 'If gage parameter does not equal true gage, set it equal: If Parameter("ShortLegGage") <> oGage Then Parameter("ShortLegGage") = oGage End If 'Assume error means gage parameter does not exits. Create and set equal to true gage: If Err.Number <> 0 Then oMyParameter=ThisApplication.ActiveDocument.ComponentDefinition.Parameters.UserParameters oParameter=oMyParameter.AddByExpression("ShortLegGage", oGage, "in") End If On Error Goto 0 On Error Resume Next If Parameter("ShortLegGage1") <> oGage1 Then Parameter("ShortLegGage1") = oGage1 End If If Err.Number <> 0 Then oMyParameter=ThisApplication.ActiveDocument.ComponentDefinition.Parameters.UserParameters oParameter=oMyParameter.AddByExpression("ShortLegGage1", oGage1, "in") End If On Error Goto 0 On Error Resume Next If Parameter("ShortLegGage2") <> oGage2 Then Parameter("ShortLegGage2") = oGage2 End If If Err.Number <> 0 Then oMyParameter=ThisApplication.ActiveDocument.ComponentDefinition.Parameters.UserParameters oParameter=oMyParameter.AddByExpression("ShortLegGage2", oGage2, "in") End If On Error Goto 0 'Long Leg: '[Will repeat above for long leg] End Sub Public Sub ApplyGages(ooGage As Double,ooGage1 As Double,ooGage2 As Double) oGage = ooGage oGage1 = ooGage1 oGage2 = ooGage2 End Sub Public Sub DetermineGages() Select Case oLegLength Case 8 ApplyGages(4.5,3,3) Case 7 ApplyGages(4,2.5,3) Case 6 ApplyGages(3.5,2.25,2.5) Case 5 ApplyGages(3,2,1.75) Case 4 ApplyGages(2.5,0,0) Case 3.5 ApplyGages(2,0,0) Case 3 ApplyGages(1.75,0,0) Case 2.5 ApplyGages(1.375,0,0) Case 2 ApplyGages(1.125,0,0) Case 1.75 ApplyGages(1,0,0) Case 1.5 Or 1.375 ApplyGages(0.875,0,0) Case 1.25 ApplyGages(0.75,0,0) Case 1 ApplyGages(0.625,0,0) End Select End Sub
Any input on this? I suppose a simpler way to ask the question would be "How can I use functions in iLogic that modify variables used elsewhere in the code?"
There may be some really slick way of doing this, but you could just create some extra user parameters. User parameters should be available in any rule or sub.
Edit: To be more clear, I mean to create user parameters for oLegLength and oGage.
Cameron Whetten
Inventor 2012
The problem is here: (I think)
ApplyGages(4,2.5,3)
I believe you need to use a two step process of assigning a value to the variable and then calling the sub with the variable referenced in the arguments. Also, you need to dim the variables in the sub in which they are used, not sub main (unless they're used there). Example:
Public Sub DetermineGages() Dim ooGage, ooGage1, ooGage2 As Double Select Case oLegLength Case 8 ooGage = 4.5 ooGage1 = 3 ooGage2 = 3 <all your other cases> End Select ApplyGages(ooGage,ooGage1,ooGage2) End Sub
Try this and let me know how it works for you.
Thanks for the suggestions. Cameron, at first I thought that would do the trick, but the goal is to be able to run this rule on any angle's file, even if those user parameters haven't been set up yet. I'd rather not have to create more user parameters with iLogic if I don't have to.
Mike, I think you're close to the issue but not quite. The problem is not that the ApplyGages sub can't access the values sent to it by the DetermineGages function; that part is actually functioning fine. I think the issue is in two places:
1. The oLegLength parameter is not available to the DetermineGages sub. Therefore, it's not selecting one of the cases.
2. The Main sub doesn't have access to the same oGage parameters used in the ApplyGages sub. Therefore, even if I go into the DetermineGages sub and set the value of oLegLength from INSIDE the sub, while the ApplyGages sub WILL then set the values of oGage, oGage1, & oGage2 correctly, the main sub does not then have access to those variables, becasue they're within the scope of the ApplyGages sub and not the Main sub.
So my problem basically is scoping, which I don't have near enough programming experience to know how to deal with. All I know is every experienced programmer I know strongly discourages me from using use global variables.
Variables that are available for all functions and subs--sounds to me like you are trying to use global variables.
If you are successfully passing the values of oLegLength and oGage to your functions, can't you also set up your subs to pass values back to the main? Then the main could set those values to its own local oLegLength and oGage variables.
To expand on my earlier idea, you could have the iLogic check if those user parameters already exist, and if they do not, it could create them for you. Then, it could remove them once the rule finishes so you don't have extra user parameters hanging about. They would only exist for a moment in the background and be invisible to the user.
Cameron Whetten
Inventor 2012
How could I have my subs pass those values to the main?
Yeah, I might need to do that, I'd just like for it all to work with variables within the rule if possible. I'd love to make using functions (I think of subs as functions. I think of them as being bits of code that can be used to do the same operation over and over with different inputs)... anyway, I'd love to make using functions like these a regular part of my iLogic rules, so I can do that I just said: perform the same operation multiple times throughout my code without having to write the operation out every time--just call the function.
So if I can just pass variables around between subs and functions without having to use arguments all the time, that would be great. If not, I might just have to work with creating temporary user parameters. Which would be a lot more work (and more code).
Now that I'm looking at your code again I see a couple of problems.
DetermineGages isn't receiving the value of oLegLength because your not passing the variable to it.
Change "Public Sub DetermineGages()" to "Public Sub DetermineGages(oLegLength as Double)" and add oLegLength to the arguments when you call DetermineGages from within Sub Main. This should solve that issue.
If you need Sub Main to see a value created from a sub that it calls then you need to change your subs to functions. There's a difference between a sub and a function, those terms are not interchangeable. The difference is that a function returns a value back to the sub that calls it where a sub does not return anything.
Also, it may help you to use the line "Option Explicit" at the very beginning of your rule. This forces all variables to be declared properly or it will throw an error. It makes it easier to diagnose bad variables and is usually recommended for newer programmers. (I use it.)
I'm working on rewriting your code for you to use functions and oGage as an array, but I'd really like to combine ApplyGages and DetermineGages into one function. Is there a reason I can't do that? Why are you using two subs now and two sets of variables (oGage and ooGage)?
While I was waiting on your reply about combining functions, I went ahead and rewrote your code maintaining your current architecture. In addition to changing two subs to functions and changing oGage to an array (the functions are also arrays now) I also changed your error catching statements to use Try Catch statements. They're much easier and more robust then that obsolete COM stuff you were using.
Try this out:
Sub Main() Dim oLegShort As Double = Min(G_W,G_H) Dim oLegLong As Double = Max(G_W,G_H) Dim oLegLength As Double Dim oGage() As Double 'Short Leg: oLegLength = oLegShort 'Call DetermineGages sub to set oGage variables: oGage = DetermineGages(oLegLength) Try 'If gage parameter does not equal true gage, set it equal: If Parameter("ShortLegGage") <> oGage(0) Then Parameter("ShortLegGage") = oGage(0) End If 'Assume error means gage parameter does not exits. Create and set equal to true gage: Catch oMyParameter=ThisApplication.ActiveDocument.ComponentDefinition.Parameters.UserParameters oParameter=oMyParameter.AddByExpression("ShortLegGage", oGage(0), "in") End Try Try If Parameter("ShortLegGage1") <> oGage(1) Then Parameter("ShortLegGage1") = oGage(1) End If Catch oMyParameter=ThisApplication.ActiveDocument.ComponentDefinition.Parameters.UserParameters oParameter=oMyParameter.AddByExpression("ShortLegGage1", oGage(1), "in") End Try Try If Parameter("ShortLegGage2") <> oGage(2) Then Parameter("ShortLegGage2") = oGage(2) End If Catch oMyParameter=ThisApplication.ActiveDocument.ComponentDefinition.Parameters.UserParameters oParameter=oMyParameter.AddByExpression("ShortLegGage2", oGage(2), "in") End Try 'Long Leg: '[Will repeat above for long leg] End Sub Function ApplyGages(ooGage As Double,ooGage1 As Double,ooGage2 As Double) As Double() ApplyGages(0) = ooGage ApplyGages(1) = ooGage1 ApplyGages(2) = ooGage2 End Function Function DetermineGages(oLegLength As Double) As Double() Select Case oLegLength Case 8 DetermineGages = ApplyGages(4.5,3,3) Case 7 DetermineGages = ApplyGages(4,2.5,3) Case 6 DetermineGages = ApplyGages(3.5,2.25,2.5) Case 5 DetermineGages = ApplyGages(3,2,1.75) Case 4 DetermineGages = ApplyGages(2.5,0,0) Case 3.5 DetermineGages = ApplyGages(2,0,0) Case 3 DetermineGages = ApplyGages(1.75,0,0) Case 2.5 DetermineGages = ApplyGages(1.375,0,0) Case 2 DetermineGages = ApplyGages(1.125,0,0) Case 1.75 DetermineGages = ApplyGages(1,0,0) Case 1.5 Or 1.375 DetermineGages = ApplyGages(0.875,0,0) Case 1.25 DetermineGages = ApplyGages(0.75,0,0) Case 1 DetermineGages = ApplyGages(0.625,0,0) End Select End Function
Matt, thanks a lot for taking the time to do that, I appreciate it. I'm looking over the changes you made so I can understand them, and I don't understand what's going on in the ApplyGages function. For example, what exactly does the line
ApplyGages(0) = ooGage
do? I would understand if it said
oGage(0) = ooGage
but... it looks like it's almost a circular reference between the function and itself. What am I misunderstanding? When does the correct value actually get applied to the oGage variables?
As far as combining the two functions into one, I suppose that could be done in this case, but like I said I'd love to learn how to pass variables between functions like this for cases when things are more complicated and I won't want to re-write the same bit of code over and over again for repetitive tasks. Does this make sense? Honestly when I started learning this stuff it surprised me how difficult it was to do that. I sort of thought that was the essence of programming, to be able to use functions in this manner to perform repetitive operations by taking in certain variables and modifying other variables accordingly. Apparently that's not the case.
No problem, I actually enjoy this stuff!
This is why I wanted to combine the functions, because the function "ApplyGages" is redundant.
This is where the values finally make their way back to "oGage" in "Sub Main":
oGage = DetermineGages(oLegLength)
This calls the function "DetermineGages" and says that whatever the result of the function, assign it to "oGage".
Here, we are saying the value of "DetermineGages" is whatever "ApplyGages" returns when fed the arguments 4.5, 3, and 3.
Case 8 DetermineGages = ApplyGages(4.5,3,3)
Here, we are assigning the "ooGage" values as the result of "ApplyGages". I call this redundant because the result of "ApplyGages" will always simply be it's inputs.
ApplyGages(0) = ooGage ApplyGages(1) = ooGage1 ApplyGages(2) = ooGage2
On second look, I could have written that as:
ApplyGages = ooGage, ooGage1, ooGage2
What I wanted to do was:
DetermineGages = 4.5,3,3
This would eliminate the function "ApplyGages" and simplify everything for us. However, it's not necessary.
Moving variables around between functions isn't difficult, you just have to realize that unless you explicitly include that variable in the arguments of the sub or function it won't have access to it. You just need a bit of practice, don't get discouraged.
I hope I answered your questions and didn't get you even more confused.
No you're definitely helping a lot, thank you.
I finally had a chance to try your code and came up with this error:
Error on Line 48 : Argument not specified for parameter 'ooGage1' of 'Public Function ApplyGages(ooGage As Double, ooGage1 As Double, ooGage2 As Double) As Double()'.
There are two occurances of that error for each line of
ApplyGages(*) = ooGage*
in the ApplyGages function. Do you know what this means?
------
I've also been experimenting with doing away with the ApplyGages function as you said, by simply stating
DetermineGages = *,*,*
within the Determine Gages fuction for each case, but then I get the error
Error on Line 56 : Value of type 'Double' cannot be converted to '1-dimensional array of Double'.
for each case. I've been trying to search on how to cast and deal with array variables, but can't find anything useful. Can you (continue to) enlighten me? Thanks again!
Dragging up an old topic here, but I have something to add:
To use the same variable across multiple subs you can put the "iLogic" rule inside a class and declare the variables as shared.
See below:
Option Compare Text AddReference "Microsoft.Office.Interop.Excel" 'To use excel Imports Microsoft.Office.Interop.Excel 'To use excel 'need the class to share variables across subs. Public Class xlUsage Shared xlWb As Microsoft.Office.Interop.Excel.Workbook Shared xlWs As Microsoft.Office.Interop.Excel.WorkSheet Shared oExcelApp As Microsoft.Office.Interop.Excel.Application Sub Main() oXLSheet = "Sheet1" OpenXL(True) xlWs = xlWb.Worksheets(oXLSheet) xlWs.Cells.Clear Dim oDoc As Document = ThisApplication.ActiveDocument Dim oSheet As Sheet = oDoc.ActiveSheet Dim oRow As Integer = 1 Dim oColumn As Integer = 1 Dim oSSD As SketchedSymbolDefinition = oDoc.SketchedSymbolDefinitions.Item("REV 1") For Each oTextBox In oSSD.Sketch.TextBoxes xlWs.Cells(oRow, oColumn).Value = oTextBox.Text oColumn = oColumn + 1 Next oRow = 2 oColumn = 1 For Each oSketched In oSheet.SketchedSymbols If oSketched.Definition.Name = "REV 1" For Each oTextBox In oSketched.Definition.Sketch.Textboxes xlWs.Cells(oRow, oColumn).Value = oSketched.GetResultText(oTextBox) oColumn = oColumn + 1 Next oRow = oRow + 1 End If oColumn = 1 Next Dim lastrow As Integer = xlWs.Cells(xlWs.Rows.Count, 1).End(xlDirection.xlUp).Row xlWs.Range("A2:D" & lastrow).Sort(Key1:=xlWs.Range("A2:A" & lastrow), Order1 :=XlSortOrder.xlAscending, Header:=XlYesNoGuess.xlNo, Orientation:=XlSortOrientation.xlSortColumns) CloseXL(True) OpenXL(True) End Sub Sub OpenXL(oMakeVisible As Boolean) Dim oXLWorkBook As String = "C:\Users\Public\Documents\iLogic Buffer File.xlsx" Try oExcelApp = GetObject("", "Excel.Application") Catch ex As Exception MsgBox("Error opening excel!") End Try xlWb = oExcelApp.Workbooks.Open(FileName:=oXLWorkBook) oExcelApp.Visible = oMakeVisible End Sub Sub CloseXL(oSaveBool As Boolean) xlWB.Close(oSaveBool,,) oExcelApp.Quit xlWs = Nothing xlWb = Nothing oExcelApp = Nothing End Sub End Class