NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

Elapsed timer in operator interface?

Hi:

I'm trying to modify the Teststand 4 CVI full featured user interface. I want to create an elapsed timer that shows the number of seconds elapsed since the main sequence callback began. I want the timer to update based on the execution that is selected, so that the timer will be accurate even for asynchronous sockets run in the parallel model.

Below is my current solution - if there is an easier way of implementing this, please let me know.

Current solution:
* created a timer on the interface that executes a callback every second
* callback determines sequence context from currently selected execution
* if RunState.Root.RunState.Sequence.Main[\"MainSequence Callback\"].Result.Status exists and is "Running", then
--> get RunState.Root.Parameters.TestSocket.StartDate/Time values
--> get starting seconds (A) since 1900 using MakeDateTime on the StartDate/Time values
--> get current seconds (B) since 1970 using TS_EngineGetSecondsSince1970UniversalCoordinatedTime
--> determine and display elapsed number of seconds by subtracting A from B and adding seventy years worth of seconds

There are two problems I have not gotten around and need help with:
1. Since the daylight savings time change here, the timer always starts at one hour. Does one of the functions not account for DST?
2. In the middle of the test run, the RunState.Root.RunState path sometimes comes back as 'not existing', based on a TS_PropertyExists
call. How is this possible? I've confirmed that the sequence context is the same before, during and after the second it doesn't exist. If I put a watch statement on the MainSequence Callback path in the editor, with break on change, it does not change from the time it says Running until a final result is set (Passed).

Any ideas are appreciated!
Cheers,
Dave


0 Kudos
Message 1 of 10
(5,097 Views)
I was able to resolve the DST problem by replacing TS_EngineGetSecondsSince1970UniversalCoordinatedTime with another MakeDateTime call based on GetSystemTime and GetSystemDate.

So that leaves the RunState.Root.Runstate problem. Ideas?
I don't see how I can get around reading values within this path.

Thanks,
Dave
0 Kudos
Message 2 of 10
(5,091 Views)
Hi Dave,

RunState.Root.RunState references the current RunState of the Process Model sequence that is executing your client sequence.  If you access this near the end of your sequence, it could very well no longer exist since you are executing asynchronously.  However, you stated that this occurs in the middle of your sequence execution.  One thing you might want to try is to execute your sequence in the TestStand sequence editor to see if it exhibits the same behavior.  Also, how are you calling TS_PropertyExists?  Is this being called as a TestStand expression such as PropertyExists("RunState.Root.RunState") or are you using the API calls in CVI to test that it exists?  I have been trying to reproduce this issue with no luck so far.  Do you have a simple example that would demonstrate this behavior that you could attach to this post?
0 Kudos
Message 3 of 10
(5,057 Views)
Hi:

Here is how to reproduce what I'm seeing:

* create a timer control on the EXECTAB of the user interface panel
* set timer control to be disabled by default
* set timer to 1 second interval
* set callback function to elapsed
* add global variable int g_canBreak = 0
* enable timer on start of execution (see attached c file)
* use elapsed callback function (see attached c file)
* Run the runstatetest sequence in the interface (see attached sequence)

It doesn't happen every run, but you should see the error within a few runs.
Either a teststand error against the getvalstring call, or the runstate property
won't exist when it should.

Thanks for looking into this. Let me know how it goes. If you can't reproduce
the error, I can give you my entire user interface code if necessary.

Thanks,
Dave

Download All
0 Kudos
Message 4 of 10
(5,039 Views)
Hi Dave,

I followed your steps and was able to recreate your Operator Interface.  I then ran the sequence file that you provided.  The sequence file execution completed entirely without ever reaching the CVI code that indicates that the variable does not exist.  I tested this on Windows XP SP2 with TestStand 4.0 with the full CVI operator interface, modified as per your description.  I am not sure why this behavior is occuring on your system while I am unable to reproduce it on my end.  Do you have any other TestStand processes running at the time or is the computer under a heavy load while running this?
0 Kudos
Message 5 of 10
(5,004 Views)
Hi:

I don't believe the computer is under heavy load at the time. The error occurs on multiple stations, including ones only running the operator interface at the time. I am using Windows XP as well, though I've seen the error on Windows 2000 as well.

I followed my own instructions and created the user interface from scratch with only the callback modifications. I was able to reproduce the error within 2 runs. So please try the attached project and executable and let me know if you can reproduce the error.

If you are still unable to reproduce the error within a half dozen runs, then I'm not sure what to try next.
One thing to note is that the time it takes to produce the error seems to be sequence dependent. I created a sequence with just a wait step and couldn't create the problem. But a sequence with lots of subsequences and lots of steps seems to produce the error faster.

Thanks,
Dave
0 Kudos
Message 6 of 10
(4,967 Views)
Hi Dave,

I have been able to reproduce your error.  I am currently looking into what is causing this behavior.  As soon as I have any additional information I will post it here.
0 Kudos
Message 7 of 10
(4,876 Views)
Hi Dave,

I believe we have traced down the problem.  It appears that while executing, RunState.Root.RunState.Sequence.Main[\"MainSequence Callback\"].Result.Status is referencing a variable in the Batch Model sequence that is running each of the individual sockets.  The error seems to be occuring at the "Get Next Result" step in the "ProcessTestSocketRequests" sub-sequence in the Batch Model sequence.

By verifying that we are not checking the RunState.Root.RunState.Sequence.Main[\"MainSequence Callback\"].Result.Status variable while in the ProcessTestSocketRequests, we can avoid the error.  You can do this by checking the sequence name and seeing if it is the "ProcessTestSocketRequests" sub-sequence.  I have included a code snipet below that shows the modified portion of your CVICALLBACK elapsed function that demonstrates how to do this.  I have bolded the parts that were added.

            if (seqContext>0) // execution selected
            {   
                char *seqname;
                CAObjHandle CurrentSequence;                             
               
                TS_PropertyGetPropertyObject (seqContext, &errorInfo,"RunState.Sequence", 0, &CurrentSequence);
                TS_PropertyGetProperty (CurrentSequence, &errorInfo, TS_PropertyName, CAVT_CSTRING, &seqname);
                                                           
                if (!strcmp (seqname, "ProcessTestSocketRequests"))
                {
               
                    // look and see if within MainSequence callback
                    strcpy(buffer,"RunState.Root.RunState.Sequence.Main[\"MainSequence Callback\"].Result.Status");
                    TS_PropertyExists (seqContext, &errorInfo, buffer, 0, &exists);
                    if (exists!=VFALSE)
                    {
                         tsErrChk(TS_PropertyGetValString (seqContext, &errorInfo, buffer, 0, &mainSeqStatus));
                    } else if (g_canBreak==1)
                    {
                        strcpy(buffer,"RunState.Root");
                        TS_PropertyExists (seqContext, &errorInfo, buffer, 0, &exists2);
                   
                        SetCtrlAttribute (gMainWindow.executionTab, EXECTAB_TIMER, ATTR_ENABLED, 0);
                        TS_EngineBreakAll (gMainWindow.engine, NULL);
   
                        MessagePopup("Break","Runstate didn't exist when it should");
                    }
                    if (mainSeqStatus!=NULL)
                    {
                       
                        if (!strncmp(mainSeqStatus,"Running",7))  // yes, running main sequence
                        {
                            g_canBreak = 1;
                        }
                    }
                }
            }

0 Kudos
Message 8 of 10
(4,864 Views)
Hi:

I'm glad you were able to reproduce the problem - at least I'm not losing my mind!

I incorporated your code into my callback, but I'm still seeing the error. I tweaked your code a few different ways, all without success.
1. Changed if (!strcmp (seqname, "ProcessTestSocketRequests")) to if (strcmp (seqname, "ProcessTestSocketRequests"))
I assume this was the intended code (callback runs on any sequence except ProcessTestSocketRequests). Still got an error.
2. I found several other instances of the Get Next Result (dequeue) step in both the batch model and parallel model. So I added WaitForTestSocket and ProcessDialogRequests to the excluded sequence list. Still got an error.
3. Tried excluding all Teststand model sequence files by only running the callback if RunState.SequenceFile.Path started with c:\Helios\ (directory for my test sequence file). Still got an error.

Let me know what else I can try ... I feel we're on the right track. If there is an alternative way of implementing the timer that avoids looking at RunState.Root.RunState.Sequence.Main[\"MainSequence Callback\"].Result.Status, let me know!

Thanks,
Dave
0 Kudos
Message 9 of 10
(4,840 Views)
Hi:

We're still qualifying the changes, but I believe I finally have the elapsed timer working. I was able to run overnight with the changes without any crashes, which is something I could not do before.

I tried several things to get it working. In the end, I discovered I could not get any variables within the Runstate context from the callback. With the callback being called every second, and with numerous executions being in the foreground at any time, it seems that the Runstate context does not exist 100% of the time. Even with PropertyExist checks before getting values, the operator interface eventually crashes when trying to get a value that does not appear to exist (though it did the line before...)!

So here is a summary of what I ended up doing:
1. From the batch and parallel model, send a UI message to set progress text to include socket number (we were doing this already).
2. From the batch and parallel model, immediately before the Mainsequence call, send a custom UI message to the operator interface to start the timer for the given socket index
--> operator interface receives the UI message, determines start seconds, and assigns this value to a global array value indexed by socket number
3. Elapsed timer callback every second
--> determine socket number from progress text (if socket number exists)
--> determine start seconds from global array (if start seconds has been assigned yet)
--> determine current seconds, calculate/update elapsed timer
4.  From the batch and parallel model, immediately after the Mainsequence call, send a custom UI message to the operator interface to stop the timer for the given socket index
--> operator interface receives the UI message, assigns 0 to a global array value indexed by socket number

Appears to work - and no Runstate calls required in the callback.
If anyone can tell me why the RunState calls appear to not be reliable from the callback, please let me know!
Thanks for your help.

Cheers,
Dave

0 Kudos
Message 10 of 10
(4,766 Views)