Today I was working on a question someone asked me, this person had created a wind turbine animation in PI ProcessBook but he could not figure out how to "Run" his animation.

 

So the question is how could we do that in the most effective way?

So certainly, you can call your animation in the Display_DataUpdate method, it is executed every five seconds.  But this is not suitable if you need more control over the refresh rate.

 

We need an event mechanism, and this is not so easy because VBA does not contain any delegates or fancy timer objects.  I also want this solution to stay as simple as possible.

 

So first I started looking at existing posts on the subject, thanks to David Hollebeek that made the suggestion about the Application.OnIdle event that resides within the ProcessBook VBA Model.

That gave me an idea to create my own timer class in ProcessBook.  I firstly implemented the Application.OnIdle Event, but after few tests, it turned out that performance was very bad and was consuming a lot of CPU.

 

So I have finally written my own Timer class. This class contains a waiting Loop that is combined with a DoEvents and a Sleep.  DoEvents lets ProcessBook update the User Interface (UI), so be sure that you do not remove it! Otherwise ProcessBook will seems to hang, the only way to get it back it to hit Ctrl+Brk.  The "Sleep 25" limits the CPU usage by giving time to the CPU so everything runs smoothly without high CPU usage.

 

To use the class, copy and paste the following code into a new Class in PI ProcessBook Visual Basic Editor:

 

'---------------------------------------------------------------------------------------
' Class    : clsTimer1
' Date      : 2014-12-16
' Purpose  : Class for PI Processbook, to create a timer
'---------------------------------------------------------------------------------------


Private lastTrigger As Single
Private timerPeriod As Single
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private isRunning As Boolean


'---------------------------------------------------------------------------------------
' Procedure : Start
' Purpose  : Start the timer and defines at which interval it ticks
'---------------------------------------------------------------------------------------
'
Public Sub Start(TimeInMilliseconds As Single)
    timerPeriod = TimeInMilliseconds
    isRunning = True
    RunTimer
End Sub


'---------------------------------------------------------------------------------------
' Procedure : Stop
' Purpose  : Stops the timer
'---------------------------------------------------------------------------------------
'
Public Sub StopTimer()
    isRunning = False
End Sub


'---------------------------------------------------------------------------------------
' Procedure : OnTimer
' Purpose  : This is the function called when the timer reaches the configured duration
'            You can manually change what is called here
'
'            To create a timer that calls another method,
'            you need to create a new clsTimer. (unfortunately there is no easy way for delegation in VBA... )
'---------------------------------------------------------------------------------------
'
Private Sub OnTimer()
  ModAnimation.ExecuteAnimation1
End Sub


'---------------------------------------------------------------------------------------
' Procedure : RunTimer
' Purpose  : Loop, wait and fires OnTimer when duration has elapsed...
'---------------------------------------------------------------------------------------
'
Private Sub RunTimer()


    ' infinite loop
    Do While isRunning


        If ((Timer - lastTrigger) * 1000 >= timerPeriod) Then
            lastTrigger = Timer
            Call OnTimer
        End If
     
        ' refreshes the UI
        DoEvents
   
        ' add a small pause in the loop so CPU does not goes up...
        Sleep 25
   
    Loop


End Sub








 

So we have our Timer class, now to use it I chose to create a Module, my Module would contain all my animations. I also have added two methods: StartAnimation and StopAnimation, that I can call with a button to start and stop the animation.

 

Create a new Module called ModAnimation  and paste this code into it.

You will need to change, at least the line 37, depending on which object(s) you need to animate, and add more objects depending of the complexity of your animation.

 

Option Explicit


Private animation1Timer As clsTimer1


Const ANIMATION_SPEED = 500 ' speed in milli seconds


Public Sub StartAnimation()


    If animation1Timer Is Nothing Then
        Set animation1Timer = New clsTimer1
        animation1Timer.Start ANIMATION_SPEED
    End If


End Sub


Public Sub StopAnimation()
    animation1Timer.StopTimer
    Set animation1Timer = Nothing
End Sub




Public Sub ExecuteAnimation1()
  
    ' static value remembers the value between sub calls...
    Static i As Integer
      
    ' do the animation(s)
    ' this is a very simple example...
    ThisDisplay.Symbols.Item("OSILogo").Rotation = i
  
    ' rotation
    i = i + 1
    If i > 359 Then i = 0
                    
End Sub


 

 

In my example, the module makes the following image rotate, this is a very simple example.

But you can easily change the content of ExecuteAnimation1 to create your own animation.

2014-12-16_16-31-50_PI ProcessBook - [ProcessBookAnimationTimer.pdi_].png

 

 

I hope this may help you with PI ProcessBook, when you need to create your own events and control when they occurs.  Also let me know what you think and if you have other ideas to improve this solution.

 

A working example is also attached to this post.