Justin -
I would assume that with all the work that you outlined in item (3), the step still will not return properly. Keep in mind that if the step is executing code, i.e. waiting, looping or just blocking, their is no way for the step to know to return to its caller, TestStand, unless the code is written specifically to do this.
Typically termination occurs only when the Execution.Terminate method is called. Using execution termination to instruct a step to stop and just fail is not the way to go, because once an execution initiates a termination, you typically cannot easily stop it. What you are trying to do is just tell a step to stop and fail.
To illustrate existing code that steps can use to monitor some state while executing, I will show you an example for when a step monitors for termination of the execution. For example in the file \Components\NI\StepTypes\SyncSteps\Wait.cpp has code, the DoWaitForTimeInterval function monitors whether the execution has been terminated while it is waiting for the sunchronization operation to occur. If the execution is terminated while it is waiting, it leaves. In addition, the code also processes messages so that it can allow TestStand to do other messaging based things while it waits. This makes the TestStand application GUI thread more responsive. Another thing the code does it tells the execution that it can be externally suspended. This is nice because you can break in TestStand while the step is still running.
Here is a simplified version of the C++ code:
static void DoWork(struct IDispatch *seqContextDisp)
{
MSG msg;
SequenceContextPtr seqContext;
ExecutionPtr execution;
PropertyObjectPtr termMonitorData;
IEnginePtr engine;
ExecutionRunStates runState;
ExecutionTerminationStates termState_NotUsed;
// initialize execution state variables
seqContext.Attach((SequenceContext *)seqContextDisp, true);
execution = seqContext->GetExecution();
engine = seqContext->GetEngineAsDispatch();
termMonitorData = execution->InitTerminationMonitor();
// place your code here to start task
...
termMonitorData = execution->InitTerminationMonitor();
while (true)
{
execution->GetStates(&runState, &termState_NotUsed);
// place your code here to monitor or continue task
...
If (workDone)
break;
// process messages if any are pending
// must peek because we don't want to block
while (::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
{
::GetMessage(&msg, NULL, 0, 0);
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
if(execution->GetTerminationMonitorStatus(termMonitorData, _variant_t(seqContext.GetInterfacePtr())))
break; // break out of loop for term or abort.
}
}
Steps can/should do this whenever they will be busy doing something for a long time and the task is iterative or waits without blocking. Clearly if you are making calls into a driver that do not return, then the above code is of no use. But if you make multiple long blocking calls, the above code could be used between each long blocking call.
Now, using execution termination to instruct a step to stop and just fail is not the way to go. Instead you could use the above idea but instead have the step just monitor its own status. While a step is running the Step.Result.Status is set to "Running". If the step "sees" that its status is set to something different than what it was when it started, it could just exit or even set its status to "Failed" before exiting.
Scott Richardson (NI)
Scott Richardson
https://testeract.com