3

Context of the problem

I'm developing a new feature for an HMI using Factory Talk View Studio 7.00.00 (CPR 9 SR 6) and VBA 6.5.
I have two displays: ma1_header and ma2_header and, as many of you know, in Factory Talk View Studio (I'm going to call it as FTV for brevity from now on) each display has a dedicated DisplayCode. A Display code can be seen as the VBA code behind an Excel file that stays open as long as its excel file. From this point of view, the VBA code bounded to a display in FTV has the same behaviour of a VBA project in excel, so it's closed when the graphic display it's closed by the user or from code.

enter image description here

Another important point in order to understand the problem is that when a generic display in FTV called A is opened in Replace mode with at least one pixel that overlaps another display B, the display B is closed with its VBA code. Take in mind that ma1_header and ma2_header are always opened in Replace mode.

Problem description

That said, now I'm going to describe the problem that I found.
The VBA code bounded to ma1_header and ma2_header is mostly the same (the differences are pointed out in the following schema) and it performs some init actions on display start and after those it runs a procedure called ScheduleCheck. This procedure updates some UI components and it evaluates some conditions in order to determine if it's time to show ma2_header (ma1_header if the code it's executed behind ma2_header), then it recalls itself.

enter image description here

The command that opens a display is not executed directly from VBA, its executed asynchronously outside VBA. In fact VBA for some actions like: "Show a display", "Set tags values", etc. can uses a library that enables it to tell to a FTV service (that also can be used with a command line tool) to perform a list of these actions (1 command or more transmitted at time).
When a user starts a FTV client the ma1_header is shown first with some others displays which compose the user interface.
In ma1_header, when the opening conditions for ma2_header are satisfied and it's opened, the problem comes out. Let's proceed now with a step by step description of the VBA state, in order to be as clear as possible on the problem:

  1. In ma1_header the opening conditions are satisfied and the asynchronous command that shows ma2_header is excuted. Note that for the moment ma1_header vba is still open.
  2. When ma2_header start opening the code it's executed without any problem till the wait procedure. The wait procedure is written as follow:

    Public Declare Function GetTickCount Lib "kernel32" () As Long
    
    Public Sub wait(lMillSec As Long)
        Dim lT2 As Long
        Dim lT1 As Long
    
    On Error GoTo errHandle
        Const strMethod = "wait"
        MsgBox "wait - 1"
        lT1 = GetTickCount
        lT2 = lT1
        While lT2 - lT1 < lMillSec
            lT2 = GetTickCount
            DoEvents
        Wend
    errHandle:
        If Err.Number Then
            LogDiagnosticsMessage "VBA: Display " & Me.Name & " in Method " & strMethod
            Err.Clear
        End If
    End Sub
    
  3. The execution of the wait procedure proceed without any problems until the DoEvents command on the 18th rows. When DoEvents it's executed the ma1_header has the time to close itself definitively (even its vba code) then the vba flow in ma2_header seems to stops here without any error. Due to this the ScheduleCheck can't recall itself.

Ugly solution 1

Currently I found what I call an ugly solution that let code works.

When ma1_header is open, the ma2_header opening will force the close of ma1_header that , as I explained before, is completed only when DoEvents is executed. I tried to transmit this new list of commands to the FTW tool when the ma2_header need to be shown:

##New##
Abort MA2_HEADER;
Pause 1;
Display MA1_HEADER /TRRU;

##Old##
Display MA1_HEADER /TRRU;

With the new approach I close the ma2_header, then I wait (in the FTV tool, not in VBA) for 1 second with the command "Pause 1;" then I open ma1_header when I'm reasonable sure that ma1_heder is closed thanks to the pause command. In this way the ma2_heder periodically executes the procedure ScheduleCheck without strange execution interruption.

I don't know why this solves my problem, so I'd like to understand why it works and which is the cause of this problem in order to find a better solution.

Ugly solution 2

I found another ugly solution, but as before I'm not satisfied because I would like to know way my current code has this problem (for me solving a problem without knowing what is the cause it's a defeat as a programmer).

  1. I've created the following new tag on the Tag server of FTV

enter image description here

  1. I've created the following new event on FTV:

enter image description here

  1. I've added a string display containing the new tag Tick in ma1_header and in ma2_header

    1. Now, in VBA, I can use the Change event of this string display in order to execute the same code contained in ScheduleCheck (obviously without the wait with DoEvents loop) each time this string display changes (each second).

Any clarification on the problem or a better solution would be really appreciated.

  • Please include code between code tags and not as a picture. – QHarr Jul 02 '18 at 16:12
  • I've not got FTV on this computer, but have you tried declaring the `Sleep` function instead of checking the ticks directly - this would avoid the direct use of `VBA.DoEvents` and may solve your issue. the declaration is `Public Declare Sub Sleep Lib "kernel32" (ByVal millisecs as Long) ` – Taylor Alex Raine Jul 02 '18 at 16:52
  • 1
    FWIW DoEvents is often more trouble than it's worth. At best it can cause screen flicker, at worst it can cause a program to hang or crash. If you have to use DoEvents, do NOT do it on every iteration in a loop, especially one that really does very little. Place a counter in there, or find some way to perhaps only do DoEvents every 10, 100, 500, or 1000 iterations (whatever works best) using the MOD keyword (If (num Mod 100) = 0 Then ... for example) – Bill Hileman Jul 02 '18 at 17:43
  • 1
    @TaylorScott the Sleep function has the following drawbacks: 1 - It locks VBA on the Sleep call until the Sleep period has finished 2 - Other Events in the application are suspended, so the application won't respond to user input during the pause I need to call DoEvents because unfortunately VBA is a single thread environment. So the wait implemented with a DoEvents loop is the only way I found to wait in my code letting other vba events to be executed during the pause – Andrea Del Corto Jul 03 '18 at 07:10

0 Answers0