Just the other day someone on the "Inventor iLogic and VB.net Forum" asked if it's possible to stop a long-running iLogic rule. I guess, in some cases, you want to allow your users to stop an iLogic rule. Out of the box, this is not possible, but there are some functions in the Inventor API that make it possible.

Have a look at the following rule. You probably will notice that it does nothing. There are just a lot of delays but imagine that each delay is some long-running process.

' some process that cost 3 sec (3000 miliseconds)
System.Threading.Tasks.Task.Delay(3000).Wait()
For i = 2 To 10
	' some process that cost 1 sec (1000 miliseconds)
    System.Threading.Tasks.Task.Delay(1000).Wait()
Next

From a user's point of view, this is a terrible rule. If he/she runs the rule Inventor will just freeze for 12 seconds. The user does not know what is happening and how long it is going to run. We can improve this rule a lot by adding a progress bar. We can transform the rule like this to accomplish that.

Class ThisRule
    Private _progressBar As Inventor.ProgressBar
	
    Public Sub Main()

        _progressBar = ThisApplication.CreateProgressBar(False, 10, "This is your main title", True)

        UpDateProgress("Executing first process")
        System.Threading.Tasks.Task.Delay(3000).Wait()

        For i = 2 To 10
            UpDateProgress("Executing some process: " & i)
            System.Threading.Tasks.Task.Delay(1000).Wait()
        Next

        _progressBar.Close()
    End Sub

    Private Sub UpDateProgress(message As String)
        _progressBar.Message = message
        _progressBar.UpdateProgress()
    End Sub
End Class

When the user runs this rule he/she will get a nice progress bar with information like this.

Also, notice the cancel button. This option is documented in the help files but there is no example of it being used. And because it's an optional parameter it is by default turned off. On line 6 the progress bar is initialized with the cancel button but it doesn't do a thing. We need to write all the code to stop the rule ourselves.

To stop the program from running we need 2 mechanisms:

  • Detect that the user wants to stop/cancel the rule and store that information
  • check if the user wants to stop/cancel and act on it.

You might wonder why can't we stop the rule directly when we detect that the user wants to stop. The problem is in fact that code/functions are always read from top to bottom and only proceed to the next line if the current line is finished. Therefore if we create a line that tells the computer to wait for user input it will not finish. There is a solution to this problem. Creating multiple threads in our rule. One thread that runs the rule like we are used to. And one thread that listens for user input. The nice thing is that we already have created a second thread. Maybe it's not obvious but the progress bar runs on a new thread. But one thread can't interact with another thread directly. however, they can read from the same memory place/variable. Therefore we need to save user input on the tread with the progress bar and read it (and act on it) on the thread of the rule.

The complete script will look like this. Notice that I have added the event handler "OnCancel". (line 7) This is the function where we save the user input in the property "UserWantsToCancel". This does not stop the script therefore we read this property in a couple of places in the rule (lines 11 and 16) to check if we need to stop the script. Before I can stop the script I need to close the progress bar and maybe you want to do some more actions (like roling back some changes done to the model). Therefore I pushed all that code into 1 function "UserWantsToCancel".

Class ThisRule
    Private _progressBar As Inventor.ProgressBar
	
    Public Sub Main()

        _progressBar = ThisApplication.CreateProgressBar(False, 10, "This is your main title", True)
        AddHandler _progressBar.OnCancel, AddressOf OnCancel

        UpDateProgress("Executing first process")
        System.Threading.Tasks.Task.Delay(3000).Wait()
		If UserWantsToCancel Then Return
	
        For i = 2 To 10
            UpDateProgress("Executing some process: " & i)
            System.Threading.Tasks.Task.Delay(1000).Wait()
            If UserWantsToCancel Then Return
        Next

        _progressBar.Close()
    End Sub
	
	Private Sub UpDateProgress(message As String)
        _progressBar.Message = message
        _progressBar.UpdateProgress()
    End Sub
	
	' global property that can set and read by both threads.
    Private Property UserClickedOnCancel() As Boolean = False
		
	' This function is called from the main thread to check if 
	' the user clicked on the "Cancel" button
	Private Function UserWantsToCancel()
		If (UserClickedOnCancel) Then
            MsgBox("User clicked cancel and it has been detected now.")

            ' Add code here for clean exit of the rule.
			' If you work with transactions then this could be 
			' the place To role back any changes

            _progressBar.Close()
            Return True
        End If
		Return False
	End Function	

	' This sub is only called from the progress bar thread
    Private Sub OnCancel()
        MsgBox("OnCancel fired. Setting property UserClickedOnCancel.")
        UserClickedOnCancel = True
    End Sub
End Class

 

Skills:

Autodesk Inventor, Vault, Git, C#, vb, .net, php HTML, css, js

Education:

University computer science.
HBO Mechanical engineer.
MBO Fine mechanics.

Experience:

Programmer and Mechanical engineer at Kelvion
(2016 - 20..)

Mechanical engineer at Strukton
(2009 - 2016)

Mechanical engineer at RDG-engineering
(2007 - 2009)

CNC Programmer at VMC
(2005 - 2007)

volunteer at Taizé
(2007)

Certifications:

Objectgeoriënteerd analyseren en ontwerpen, Objectgeoriënteerd programmeren in Java, Webapplicaties: de clientkant, Databases, Security Aware Programmer, Web Security Specialist