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.
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.
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:
- 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.
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
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).
- I've created the following new tag on the Tag server of FTV
- I've created the following new event on FTV:
I've added a string display containing the new tag Tick in ma1_header and in ma2_header
- 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.