NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

C# UImessage problem with Teststand API(non UI Control)

Solved!
Go to solution

Hi, Everyone:

        

I want to implement a custom call through C# and the teststand API(not UI Control) and get information about each test step through a messaging mechanism (e.g. Test step id, name, test type, test result, etc.), but I have some questions on the processing of UI Message. I hope to get your answers.

 

I instantiated the Teststand engine, loaded the execution of a specified Seq file, and then received and processed the UI Message through a while loop during the execution. Attached is my demo code.

 

Through debugging, I confirm that messages such as UIMsg_StartExecution / UIMsg_EndExecution can be triggered and received. If I add an ActiveX UIMessagePost to the Seq file, a custom message ID such as 10001 can also trigger receiving.

 

My current question:

      1. I would like to know how each test step in the Seq file is delivered to the interface after the test is completed. How to customize (What do I need to do? Or write code), and then trigger and receive normally at test time?

      2. How to interpret this information from the message (e.g. test step id, name, test type, test result, etc.)?

      3. If UIMessagePost is not added to the Seq file, how does the message trigger?

 

Can anyone help me with these questions?

 

Thank you very much!

0 Kudos
Message 1 of 9
(1,669 Views)

I don't know why my attachment has not been submitted. Then, shall I directly reply the code here:

------------------------------------------------------------------------------------------------------------------------------------------------------

 

static int Main(string[] args)
{
returnCode = LaunchTestStandApplicationInNewDomain.LaunchProtectedReturnCode(
new LaunchTestStandApplicationInNewDomain.
MainEntryPointDelegateWithArgsReturnCode(MainEntryPoint),
new string[] { String.Format($"/env \"{arguments.EnvironmentFilePath}\"") },
"TestStand Demo",
new LaunchTestStandApplicationInNewDomain.
DisplayErrorMessageDelegate(DisplayErrorMessage),
true);
return (int)ReturnCode.CommandLineError;
}

public static int MainEntryPoint(string[] args)
{
SequenceFile sequenceFile = null;
try
{
engine = new Engine();
engine.LoadTypePaletteFilesEx(
TypeConflictHandlerTypes.ConflictHandler_Error);

sequenceFile = engine.GetSequenceFileEx(
@"Test_test.seq",
GetSeqFileOptions.GetSeqFile_DoNotRunLoadCallback,
TypeConflictHandlerTypes.ConflictHandler_Error);

string sequenceName = GetSequenceNameIfEmpty(sequenceFile, "");

Execution execution = engine.NewExecution(
sequenceFile,
sequenceName,
null,
false,
ExecutionTypeMask.ExecTypeMask_CloseWindowWhenDone |
ExecutionTypeMask.ExecTypeMask_DiscardArgumentsWhenDone |
ExecutionTypeMask.ExecTypeMask_InitiallyHidden |
ExecutionTypeMask.ExecTypeMask_InitiallySuspended |
ExecutionTypeMask.ExecTypeMask_NotRestartable |
ExecutionTypeMask.ExecTypeMask_TracingInitiallyOff);

execution.Resume();

engine.UIMessagePollingEnabled = true;
engine.OutputMessagesEnabled = true;

bool stop = false;
while (!stop)
{
if (!engine.IsUIMessageQueueEmpty)
{
UIMessage msg = engine.GetUIMessage();
switch (msg.Event)
{
case UIMessageCodes.UIMsg_StartFileExecution:
//do something...
break;
case UIMessageCodes.UIMsg_StartExecution:
//do something...
break;
case UIMessageCodes.UIMsg_EndFileExecution:
//do something...
break;
case UIMessageCodes.UIMsg_EndExecution:
//do something...
break;
case UIMessageCodes.UIMsg_AbortingExecution:
//do something...
break;
case UIMessageCodes.UIMsg_KillingExecutionThreads:
//do something...
break;
case UIMessageCodes.UIMsg_UserMessageBase + 1:
//do something...
break;
case UIMessageCodes.UIMsg_RuntimeError:
//do something...
break;
case UIMessageCodes.UIMsg_OutputMessages:
OutputMessages outputMessages = msg.ActiveXData as OutputMessages;
OutputMessages tmpOutputMessages = engine.NewOutputMessages();
outputMessages.CopyMessagesToCollection(tmpOutputMessages);
for (int i = 0; i < tmpOutputMessages.Count; i++)
{
Console.Write("Output Message:\t" + tmpOutputMessages[i].Message + "\t");
Console.Write(tmpOutputMessages[i].TimeStamp.ToString() + "\t");
}
break;
default:
Console.WriteLine(" Event: " + msg.Event);
break;
}
if (msg.IsSynchronous) msg.Acknowledge();
}
}
engine.UIMessagePollingEnabled = false;

if (execution.ErrorObject.GetValBoolean("Occurred", PropertyOptions.PropOption_NoOptions))
Console.WriteLine("Runtime Error:\n Code: " + execution.ErrorObject.GetValNumber("Code", PropertyOptions.PropOption_NoOptions) +
"\n Message: " + execution.ErrorObject.GetValString("Msg", PropertyOptions.PropOption_NoOptions));
Console.WriteLine("Execution Ended");
}
catch (COMException exception)
{
Console.Error.WriteLine("Error: " + exception.ErrorCode + "\n" + exception.Message);
string tmpMessage = "";
bool found = engine.GetErrorString((TSError)exception.ErrorCode, out tmpMessage);
Console.Error.WriteLine("Exception: " + tmpMessage);
returnCode = exception.ErrorCode;
}
finally
{
engine.UIMessagePollingEnabled = false;
if (sequenceFile != null)
{
engine.ReleaseSequenceFileEx(
sequenceFile,
ReleaseSeqFileOptions.ReleaseSeqFile_DoNotRunUnloadCallback | ReleaseSeqFileOptions.ReleaseSeqFile_UnloadFile);
}
engine.UnloadTypePaletteFiles();
engine = null;
}
return returnCode;
}

 

------------------------------------------------------------------------------------------------------------------------------------------------------

0 Kudos
Message 2 of 9
(1,665 Views)
Solution
Accepted by angus2022

Hello Angus,

 

I have not programmed a Teststand UI in C# yet, but I'd hope the way of implementation is not dependent on the language.

 

One has to separate between UI Messages and User Messages.

User messages use the message ID 10000+ and are for custom purposes. They are only send it the PostUIMessage is implemented in the sequence manually.

UI messages use the message ID below 10000 and are specific pre-designed purposes. They are sent by multiple sources, most importantly the TestStand Engine when specific actions take place but also by the Process Model.

I do not know how this translates to C# exactly, but in LabVIEW the best way of handling UI Messages is to register for the UI Message event, which is sent by the Application Manager. In the callback function, you'll have the UI Message data (ActiveXData, String Data, Numeric Data, etc.) available for further actions.

 

However to my knowledge there is no UI Message being sent after each completed step automatically. This would probably slow down the execution speed to much.
So on a pure UI implementation, I believe you'd have to dig into the execution -> thread -> sequence context -> current step etc.

Now for multiple test sockets and even additional executions spawned this gets infinitely more complicated.

 

An idea for a cleaner solution would be to program something into the SequenceFilePostStep callback in TestStand directly (assuming you have access to that). Maybe a custom UI message containing the needed data.

But as mentioned before, this may slow down your execution speed significantly.

Message 3 of 9
(1,565 Views)
Solution
Accepted by angus2022

Hi,

I can't answer your question directly, but perhaps you could use this code as a reference: https://github.com/425J/TestStandCLI. I implemented messages handling some time ago but I forgot most of it 🙄 (that is why it is hard for me to give solid answer). I should refactor this code at some point so please don't judge 😁

Michał Bieńkowski
CLA, CTA

Someone devote his time to help solve your problem? Appreciate it and give kudos. Problem solved? Accept as a solution so that others can find it faster in the future.
Make a contribution to the development of TestStand - vote on TestStand Idea Exchange.
Message 4 of 9
(1,548 Views)
Solution
Accepted by angus2022

I'm not sure, but it sounds like you might be looking for UIMsg_Trace.

 

This message is sent between steps. You use UIMessage.Thread.GetSequenceContext(0, ..) to get a SequenceContext. Then you use properties like SequenceContext.PreviousStep or SequenceContext.PreviousStep.ResultStatus to get information.  You must get all the information from the context before returning from or acknowledging the trace event, because once you do, it is all free to change.

 

This event is not sent if tracing is disabled, so you probably don't want to pass ExecutionTypeMask.ExecTypeMask_TracingInitiallyOff to NewExecution and you should double check that tracing is enabled for your station as a whole (Engine.StationOptions.TracingEnabled).

Message 5 of 9
(1,536 Views)

Hi,  bienieck:
     Thank you for your kind answer!

     In fact, part of my code refers to the example you mentioned, which helps me understand how to receive the engine's native UImessages and user-defined messages through events.

     My problem is that I misunderstood. I thought that the message was automatically posted and I just needed to receive it, but this is not the case.

     Now I know how to modify my code. Thanks again and wish you have a nice day!

     

0 Kudos
Message 6 of 9
(1,519 Views)

Hi, Kghzvi:

 

       Your explanation is very detailed. My problem is that I don't fully understand the whole process of user defined UImessages from generation, post, reception and processing. I mistakenly think they are automatically sent, and I just need to receive and process them

       They are only send it the PostUIMessage is implemented in the sequence manually

       An idea for a cleaner solution would be to program something into the SequenceFilePostStep callback in TestStand directly

       These two paragraphs inspired me, In fact, I'm trying the latter way now *-*

       Thank you very much for your help!

0 Kudos
Message 7 of 9
(1,516 Views)

Hi,James_ Grey:

 

    I'm very glad to see such a detailed explanation given by you. It's already telling me how to operate. You even reminded me that I should not ignore ExecutionTypeMask. ExecutionTypeMask_ TracingInitialyOff property and Engine.StationOptions.TracingEnabled. This is awesome!

    Yes, because of the wrong understanding of UIMessage, I missed the two process of UImessage's get and post, so I have been unable to receive the UIMessage as I want.

    Now I'm trying to create a UIMessage Post callback in the seq file, which can post a user-defined UIMessage after each step of the test. In this way, I just need to parse the message to get what I want.

    I even came up with a simpler idea, which is also in the process of being tried. It may be faster than the current method.

 

    My gratitude to you is beyond words.Thank you so much!

0 Kudos
Message 8 of 9
(1,504 Views)

89f6ae08ab98b517e577e8baea2a4dd.png

0 Kudos
Message 9 of 9
(1,113 Views)