FTViewSE - Need help at VBA - Display command

Revnus

Member
Join Date
Aug 2019
Location
Brazil
Posts
71
I'm begginer at VBA. What should be the correct way to check if a display is currently showing and then show another display?

I tried to put this code on two main displays "display1" and "display2":

Private Sub Display_AnimationStart()
ShowDisplay ("bannerA")
End Sub

Private Sub Display_AfterAnimationStop()
ShowDisplay ("bannerB")
End Sub

This works when start, display1 or display2, or when I change from them to another display (to display99 for example).

But when I change from display 1 to display 2 , it shows "bannerB" replacing "bannerA", when it should keep "bannerA" in this case.

It seems that sub Display_AfterAnimationStop of the closing display executes after Display_AnimationStart of the opening display(which is strange).

I tried BeforeAnimationStop and same thing happens.

I don't want to use "pause", because banner B would show quickly before displaying banner A. What I want is:

- When I change between display1 and display2, it keeps banner A.
- When I change from display1 or display2 to any other main display, it changes from bannerA to bannerB.
- When I change from any other main display to display1 or display2, it changes from bannerB to bannerA.
- Use the working VBA command only on display1 and display2.

Thank you!
 
If the theory is correct that AfterAnimationStop on display 1 or 2 is called after AnimationStart on display 2 or 1, then I wonder if the following approach might work (new code in blue).

Code:
[B][COLOR=blue]Boolean showB = True[/COLOR][/B]

Private Sub Display_AnimationStart()
  ShowDisplay ("bannerA")[COLOR=blue][B]
  showB = False     ''' Display is 1 or 2[/B][/COLOR]
End Sub

Private Sub Display_AfterAnimationStop()
  [COLOR=Blue][B]if ([/B][/COLOR][COLOR=Blue][B][COLOR=blue][B]showB[/B][/COLOR]) then   ''' Display is neither 1 nor 2[/B][/COLOR]
    ShowDisplay ("bannerB"[COLOR=blue][B])[/B][/COLOR][COLOR=blue][B]
  else[/B][/COLOR][COLOR=blue][B][COLOR=blue][B]
    showB [/B][/COLOR]= True    ''' Assume next display will be neither 1 nor 2[/B][/COLOR][COLOR=blue][B]
  endif[/B][/COLOR]
End Sub


It depends on the variable [showB] being Global, or at least Static.
 
If the theory is correct that AfterAnimationStop on display 1 or 2 is called after AnimationStart on display 2 or 1, then I wonder if the following approach might work (new code in blue).

Code:
[B][COLOR=blue]Boolean showB = True[/COLOR][/B]

Private Sub Display_AnimationStart()
  ShowDisplay ("bannerA")[COLOR=blue][B]
  showB = False     ''' Display is 1 or 2[/B][/COLOR]
End Sub

Private Sub Display_AfterAnimationStop()
  [COLOR=Blue][B]if ([/B][/COLOR][COLOR=Blue][B][COLOR=blue][B]showB[/B][/COLOR]) then   ''' Display is neither 1 nor 2[/B][/COLOR]
    ShowDisplay ("bannerB"[COLOR=blue][B])[/B][/COLOR][COLOR=blue][B]
  else[/B][/COLOR][COLOR=blue][B][COLOR=blue][B]
    showB [/B][/COLOR]= True    ''' Assume next display will be neither 1 nor 2[/B][/COLOR][COLOR=blue][B]
  endif[/B][/COLOR]
End Sub


It depends on the variable [showB] being Global, or at least Static.

Thank you. I will try it in the next minutes and post if it worked or not.
Looking at your code I have a question tho:

Let's assume operator opens display 1 (showB =False), and then change from display1 to display 99. When Sub Display_AfternimationStop executes, wouldn't showB still be False? If this is the case, it would not show "bannerB" (keeping "bannerA") and only then, change showB to true.
 
Thank you. I will try it in the next minutes and post if it worked or not.
Looking at your code I have a question tho:

Let's assume operator opens display 1 (showB =False), and then change from display1 to display 99. When Sub Display_AfternimationStop executes, wouldn't showB still be False? If this is the case, it would not show "bannerB" (keeping "bannerA") and only then, change showB to true.




yeah, I might not have gotten that right yet. there are more than two cases, so it needs more than one bit to hold state ...


My other idea was similar but with a timestamp, instead of the Boolean, written by the ...Start, and if the ...Stop is called within a few ms of the ...Start, then don't display B.



How about this?


Code:
Dim count12 as Integer

Private Sub Display_AnimationStart()
   count12 = count12 + 1
   ShowDisplay("bannerA")
End Sub

Private Sub Display_AfterAnimationStop)
   count12 = count12 - 1
   if (count12 < 1) then
     ShowDisplay("bannerB")
   endif
 End Sub
Assume count12 starts as 0.


At the first selection of display1 or display2 from a display other than display1 or display2, AnimationStart will bump count12 to 1 and bannerA will be displayed; AfterAnimationStop will not be called so bannerA will remain displayed and count12 will still be 1.



After that, at any subsequent selections of display1 or display2, count12 will be bumped to 2 at the exit of AnimationStart, and then be dropped back to 1 after the first statement in AfterAnimationStop, so bannerB will not be displayed.


When a display other than display1 or display2 is selected, from display1 or display2, AnimationStart will not be called, so count12 will not change until AfterAnimationStop decrements it back to to 0, at which point bannerB will be displayed.
 
Last edited:
yeah, I might not have gotten that right yet. there are more than two cases, so it needs more than one bit to hold state ...


My other idea was similar but with a timestamp, instead of the Boolean, written by the ...Start, and if the ...Stop is called within a few ms of the ...Start, then don't display B.



How about this?


Code:
Dim count12 as Integer

Private Sub Display_AnimationStart()
   count12 = count12 + 1
   ShowDisplay("bannerA")
End Sub

Private Sub Display_AfterAnimationStop)
   count12 = count12 - 1
   if (count12 < 1) then
     ShowDisplay("bannerB")
   endif
 End Sub
Assume count12 starts as 0.


At the first selection of display1 or display2 from a display other than display1 or display2, AnimationStart will bump count12 to 1 and bannerA will be displayed; AfterAnimationStop will not be called so bannerA will remain displayed and count12 will still be 1.



After that, at any subsequent selections of display1 or display2, count12 will be bumped to 2 at the exit of AnimationStart, and then be dropped back to 1 after the first statement in AfterAnimationStop, so bannerB will not be displayed.


When a display other than display1 or display2 is selected, from display1 or display2, AnimationStart will not be called, so count12 will not change until AfterAnimationStop decrements it back to to 0, at which point bannerB will be displayed.

This one seems to be perfect!

I was able to accomplish it, but using other method which is not ideal since it has a delay to open banner. Here is the code:


Dim WithEvents Group As tagGroup
Dim WithEvents ChangeBannerAlarmVBA As Tag

Private Sub Display_AnimationStart()
On Error GoTo ErrHandler

Set Group = Application.CreateTagGroup(Me.AreaName) 'Group Creation
Group.Add ("ChangeBannerAlarmHMI")
Group.Active = True
Set ChangeBannerAlarmVBA = Group.Item("ChangeBannerAlarmHMI")

Exit Sub

ErrHandler:
Application.LogDiagnosticsMessage "Error occurred in " & ThisDisplay.FullName & _
" Display_AnimationStart(). Error # " & Err.Number & ": " & Err.Description, ftDiagSeverityError
End Sub
Private Sub ChangeBannerAlarmVBA_Change(ByVal Value, ByVal TimeStamp As Date, ByVal Quality As tagQualityConstants, ByVal SubStatus As tagSubStatusConstants, ByVal Limit As tagLimitConstants)
On Error GoTo ErrHandler


Dim PauseTime, Start, Finish, TotalTime
PauseTime = 1 ' Set duration.
Start = Timer ' Set start time.
Do While Timer < Start + PauseTime
DoEvents ' Yield to other processes.
Loop
Finish = Timer ' Set end time.
If ChangeBannerAlarmVBA.Value = "False" Then
Application.ShowDisplay ("bannerB")
Else
Application.ShowDisplay ("bannerA")
End If

Exit Sub

ErrHandler:
Application.LogDiagnosticsMessage "Error occurred in " & ThisDisplay.FullName & _
" ChangeBannerAlarmVBA_Change(). Error # " & Err.Number & ": " & Err.Description, ftDiagSeverityError
End Sub


To set ChangeBannerAlarmHMI, I used the following startup and shutdown commands at display1 and display2 properties:

Startup: Pause 1.2;&Set ChangeBannerAlarmHMI 1
Shutdown: &Set ChangeBannerAlarmHMI 0

This worked, but as I said, it has some delay (between 1 and 2 seconds)

I just tried your code: I hoped it would work.

When I change from display99 to display1 or display2 it shows bannerA, but when I change between display1 and display2, it tries to open bannerA again, and after, it opens banner B.

The complete code with your idea:

Dim count12 As Integer

Private Sub Display_AnimationStart()
On Error GoTo ErrHandler

count12 = count12 + 1
If (count12 >= 1) Then
ShowDisplay ("bannerA")
Else
End If

Exit Sub

ErrHandler:
Application.LogDiagnosticsMessage "Error occurred in " & ThisDisplay.FullName & _
" Display_AnimationStart(). Error # " & Err.Number & ": " & Err.Description, ftDiagSeverityError
End Sub

Private Sub Display_AfterAnimationStop()
On Error GoTo ErrHandler

count12 = count12 - 1
If (count12 < 1) Then
ShowDisplay ("bannerB")
Else
End If

Exit Sub

ErrHandler:
Application.LogDiagnosticsMessage "Error occurred in " & ThisDisplay.FullName & _
" Display_AfterAnimationStop(). Error # " & Err.Number & ": " & Err.Description, ftDiagSeverityError
End Sub


It seems count12 is loosing his value and going back to 0 at some point, I guess.
 
Last edited:
Dim count12 As Integer

Private Sub Display_AnimationStart()
On Error GoTo ErrHandler

count12 = count12 + 1
If (count12 >= 1) Then
ShowDisplay ("bannerA")
Else
End If


Exit Sub

ErrHandler:
Application.LogDiagnosticsMessage "Error occurred in " & ThisDisplay.FullName & _
" Display_AnimationStart(). Error # " & Err.Number & ": " & Err.Description, ftDiagSeverityError
End Sub

Private Sub Display_AfterAnimationStop()
On Error GoTo ErrHandler

count12 = count12 - 1
If (count12 < 1) Then
ShowDisplay ("bannerB")
Else
End If

Exit Sub

ErrHandler:
Application.LogDiagnosticsMessage "Error occurred in " & ThisDisplay.FullName & _
" Display_AfterAnimationStop(). Error # " & Err.Number & ": " & Err.Description, ftDiagSeverityError
End Sub


It seems count12 is loosing his value and going back to 0 at some point, I guess.


Hmm, maybe just [Dim count12 as Integer] creates an automatic (temporary) variable that gets re-initialized when the code is called. Perhaps there is another keyword (Private? Global? Static?) that will make it permanent.


The app should not need the code in red above, but it should not cause a problem either (also, [count12 > 0] is cleaner than [count12 >= 1], don't you think?).


Is it possible for the VBA to write code to a console that you could watch? Or maybe write to a variable in the PLC? Perhaps we could store count12 on the PLC to make it more permanent.


Is it possible that AfterAnimationStop is not executing when switching away from display1 or from display2, but it is called as part of the actual switch TO display1 or display2?
 
Hmm, maybe just [Dim count12 as Integer] creates an automatic (temporary) variable that gets re-initialized when the code is called. Perhaps there is another keyword (Private? Global? Static?) that will make it permanent.


The app should not need the code in red above, but it should not cause a problem either (also, [count12 > 0] is cleaner than [count12 >= 1], don't you think?).


Is it possible for the VBA to write code to a console that you could watch? Or maybe write to a variable in the PLC? Perhaps we could store count12 on the PLC to make it more permanent.


Is it possible that AfterAnimationStop is not executing when switching away from display1 or from display2, but it is called as part of the actual switch TO display1 or display2?

I used count12 as a HMI tag and it worked perfectly!!!

Thank you very much. I've been strugling with this almost a week. (y)

Also, I followed your recomendations to make the code cleaner.

Here is the complete code (count12 new name is ChangeBannerVBA_int as VBA Tag which points to ChangeBannerHMI_int integer HMI tag):


Dim ChangeBannerVBA_int As Tag
Dim MyGroup As TagGroup

Private Sub Display_AnimationStart()
On Error GoTo ErrHandler

Set MyGroup = Application.CreateTagGroup(Me.AreaName) 'Criação do Grupo
MyGroup.Add ("ChangeBannerHMI_int")
Set ChangeBannerVBA_int = MyGroup.Item("ChangeBannerHMI_int")

ChangeBannerVBA_int.Value = ChangeBannerVBA_int.Value + 1
ShowDisplay ("bannerA")

Exit Sub

ErrHandler:
Application.LogDiagnosticsMessage "Error occurred in " & ThisDisplay.FullName & _
" Display_AnimationStart(). Error # " & Err.Number & ": " & Err.Description, ftDiagSeverityError
End Sub

Private Sub Display_AfterAnimationStop()
On Error GoTo ErrHandler

ChangeBannerVBA_int.Value = ChangeBannerVBA_int.Value - 1
If (ChangeBannerVBA_int.Value < 1) Then
ShowDisplay ("bannerB")
Else
End If


Exit Sub

ErrHandler:
Application.LogDiagnosticsMessage "Error occurred in " & ThisDisplay.FullName & _
" Display_AfterAnimationStop(). Error # " & Err.Number & ": " & Err.Description, ftDiagSeverityError
End Sub

 
Last edited:
I used count12 as a HMI tag and it worked perfectly!!!



Brilliant on your part to use the HMI to make the value persistent!!! (y)


So apparently the VBA script/code starts from scratch each time it is triggered. I wonder why you only need to do the Set MyGroup/MyGroup.Add/Set ChangeBannerVBA_int in the AnimationStart Sub but not again in the AfterAnimationStop Sub; I suppose the Stop Sub is called as part of the same event that triggers the Start Sub, so ChangeBannerVBA_int is still valid.
 
Brilliant on your part to use the HMI to make the value persistent!!! (y)


So apparently the VBA script/code starts from scratch each time it is triggered. I wonder why you only need to do the Set MyGroup/MyGroup.Add/Set ChangeBannerVBA_int in the AnimationStart Sub but not again in the AfterAnimationStop Sub; I suppose the Stop Sub is called as part of the same event that triggers the Start Sub, so ChangeBannerVBA_int is still valid.

It was your idea to create an external tag. I just found easier to create HMI instead of PLC tag. Thanks to you!

I think that what you supposed is true. I saw that there are subs, private subs and public subs. I don't know the difference yet, but I will study it later. Still, at first I thought that if we declared the "count12" before any sub, it would become a global variable and then it could be used by other scripts. That might not be true, but maybe there is way to do it..
 
Another problem has appeared..

It was working until two operatos each one with its own runtime application opened the same display. The HMI tag would increment from the two PCs.

It has to be something local to the runtime application.

I tried to use "Public" to declare the ChangeBannerVBA_int as a variable. But it will reset its value when the runtime triggers the next VBA code.

Public ChangeBannerVBA_int as Integer

Public Sub Display_AnimationStart(...)
 
But if there are two runtimes being executed, there is something different about them, assuming that each is on a separate PC/HMI, each has a different IP address, use that to differentiate between each runtime.
 
The runtime applications share the same HMI displays with same VBA code and same HMI tags.


Create a tag based off of the computer name?


For simplicity, use a function to return the computer name, ...




Code:
'*******************************************************************
'  Function: ComputerName()
'   Returns the network name of this computer as a string.
'*******************************************************************
Public Function ComputerName() As String
  Dim sBuffer As String
  Dim CName As String
  Dim UpperCase As String
  
  Dim lAns As Long
 
  sBuffer = Space$(255)
  lAns = GetComputerName(sBuffer, 255)
  If lAns <> 0 Then
        'read from beginning of string to null-terminator
        CName = Left$(sBuffer, InStr(sBuffer, Chr(0)) - 1)
        UpperCase = UCase(CName)
        ComputerName = UpperCase
   Else
        Err.Raise Err.LastDllError, , _
          "A system call returned an error code of " _
           & Err.LastDllError
   End If

End Function
 
Create a tag based off of the computer name?


For simplicity, use a function to return the computer name, ...




Code:
'*******************************************************************
'  Function: ComputerName()
'   Returns the network name of this computer as a string.
'*******************************************************************
Public Function ComputerName() As String
  Dim sBuffer As String
  Dim CName As String
  Dim UpperCase As String
  
  Dim lAns As Long
 
  sBuffer = Space$(255)
  lAns = GetComputerName(sBuffer, 255)
  If lAns <> 0 Then
        'read from beginning of string to null-terminator
        CName = Left$(sBuffer, InStr(sBuffer, Chr(0)) - 1)
        UpperCase = UCase(CName)
        ComputerName = UpperCase
   Else
        Err.Raise Err.LastDllError, , _
          "A system call returned an error code of " _
           & Err.LastDllError
   End If

End Function

How do I create a tag based off of computer name?

I tried your code and GetComputerName shows "Sub or function not defined".
 

Similar Topics

I have a alarm banner A display that shows on the botton of the screen and that must shown along with any of two specific main centered displays...
Replies
2
Views
2,026
I am attempting to access HMI tag properties (Description, Minimum, Maximum values) to populate a numeric input. Is there a clean way to do this...
Replies
0
Views
360
Hello everyone. I'm asking for help with the following problem: in FTView SE 13.00 for the AlarmEventSummary1 object, you need to add the filter...
Replies
0
Views
586
Hi all, I've got a bit of a need to convert an integer (and a real in another situation) to a string with a particular format. This seems possible...
Replies
11
Views
2,414
I want to have two displays that uses the same VBA variable "vbaint". But when I change between them, the variable seems to reset. How can I do...
Replies
4
Views
2,801
Back
Top Bottom