NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

Pass TestStand information to and from a C# winforms application

I have a small C# application to collect information from a user/operator and to post a status periodically.  I have a TestStand application that is running multithreaded applications.  How do I pass the information back and forth between TestStand and the C# winforms application?  I would be happy to read TestStand variables/parameters with C# and write data to TestStand variables/parameters, all being unidirectional.  Also, how do I invoke the C# application with TestStand?  So far I have tried launching it as an STA Sequence thread but I can't seem to get the TestStand to acknowledge the code is there.  I think it is looking for a sequence.  I have also tried to use "Call Executable" which launches the application just fine but there is no place to specify which variables/parameters C# should have access to.  Running in circles on this.  I need to get started properly.  Anyone ever done this?

0 Kudos
Message 1 of 9
(210 Views)

Your code (dll) needs to be built with a reference to the NI API.  After you add the reference, set the Embed Interop Types to No.

Put a using statement in.

using NationalInstruments.TestStand.Interop.API;

 

Once you do that, call your public method from the sequence that has a parameter where you pass the ThisContext.  This gives you access to the Sequence Context, where you can use the NI API to access Locals, FileGlobals, etc.

Message 2 of 9
(186 Views)

Allow me to clarify a little... the C# is an application.  I think I can get it to accept a parameter but what is the parameter type?  Is it a string?

I have already added the reference, that was a no brainer.  Now, how do I call this application?  I have tried the call sequence step type (found literature that said this was the way to go) but had no luck.  Then I tried the Call Executable step type and the application ran but left me short on the parameters part.

Then, which API method do I use to access parameters/variables and what would be an example syntax?

My goal is to collect a pile of information with C# then populate variables or parameters with the information.  I really need some examples to get me going...

Thanks for the support and response!

0 Kudos
Message 3 of 9
(157 Views)
  1. Pass the sequence context of the sequence that is running to your public method, using a .Net Module Step
  2. eejallen_0-1757517901154.png

     

  3. access the properties of the context using the PropertyObject API.  PropertyObject - NI
  4. process or update the data.  See SetValNumber(), SetValString(), etc.
public void DoSomething(SequenceContext seqContext)
{   
    double data =  seqContext.Locals.GetValNumber("myLocalNumber", 0);
    string stringData =  seqContext.Parameters.GetValString("myStringPara", 0);
    bool boolData =  seqContext.FileGlobals.GetValBoolean("myBoolean", 0);
    double stationData =  seqContext.StationGlobals.GetValNumber("myStationData", 0);
    double runTimeData =  seqContext.RunState.GetValNumber("myRunTimeData", 0);

    // process and/or update data

}

 

Message 4 of 9
(149 Views)

What I described depends on your application (exe) having a dll that TestStand can call into.  This is the easiest way to do what you want.  If you absolutely can't create a supporting DLL for your app, let me know and I can share another way where your exe uses the TestStand synchronization manager.

Message 5 of 9
(115 Views)

Thanks for all the help thus far.  I have been able to read and write TestStand variables from C#.  Until I get 2025Q3 TestStand I am stuck using the .NET Framework 4.8 and it doesn't seem to create a .dll.  What I have had to do is to create a "forms launcher" to be able to pass the "ThisContext" to the form:

 

public void LaunchForm(SequenceContext mySeqContext)
{
Form UserInterface = new Form1();
UserInterface.Show();
myFormSeqContext = mySeqContext;
}

 

(I have defined static SequenceContext myFormSeqContext; elsewhere)

 

This could work as long as all of my methods I/O is done through the API.  What I am saying is, any other method calls using the Action .NET adapter are returning the values from Form1() and not from UserInterface().  TestStand only recognizes Form1() for method calls when I really want UserInterface(), the launched Form1().

 

If you have a slick way around this I could use the tip, lol.  I think I can live with this if I limit my application to being a front end to the TestStand and communicated through variables.  My only real issue might be when exiting the application from TestStand.  I can't invoke the Exit() method of the launched Form1().

 

Any thoughts?  I really appreciate all the help.  TestStand is new to me so I am stumbling through it a bit.

0 Kudos
Message 6 of 9
(104 Views)

@NIFrustrated wrote:

Thanks for all the help thus far.  I have been able to read and write TestStand variables from C#.  Until I get 2025Q3 TestStand I am stuck using the .NET Framework 4.8 and it doesn't seem to create a .dll.  What I have had to do is to create a "forms launcher" to be able to pass the "ThisContext" to the form:

 

public void LaunchForm(SequenceContext mySeqContext)
{
Form UserInterface = new Form1();
UserInterface.Show();
myFormSeqContext = mySeqContext;
}

 

 


How are you passing SequenecContext to the form?  2025 Q3 doesn't support .Net Framework 4.8

Message 7 of 9
(53 Views)

That is correct.  Under Framework 4.8 (which mu current (old) version of TestStand supports) I create a sequence variable in the main code.  Then I call a launcher method and pass the context to it to populate that information for the rest of the methods.  Then, as I use the methods (button pushes) the code updates TestStand variables directly.  But, you can't just call methods directly from TestStand because there is no way to point to the launched version of the form.  I think I can live with that limitation.

 

I am going to get TestStand 2025 so that I can create a .dll with the form in it.  I have created that already with Visual Studio.  All I think I need is 2025Q3 TestStand and giver it a try.  I will get back to you when I see how I make out but I think I am on the right track.  Don't hold your breath, IT gears turn slowly....

0 Kudos
Message 8 of 9
(43 Views)

With multi-threading application and TestStand object access, you can take a look at Sync Manager

NI TestStand Synchronization Server API Reference Help - NI

 

Add references to your code for NationalInstruments.TestStand.Interop.API and TSSyncLib.  Then use Engine.GetSyncManager( syncObjectName) to create a SyncManager.  This will allow you to share Sync Objects (for example a Queue) across processes.  A Queue can then be used to enqueue and dequeue teststand variables/objects that you want to share.

 

 

 

// Creating a queue with name 64*MySeqQueue or 32*MySeqQueue. Sync Objects named with a leading * can be shared across processes.
// When the Synchronization object name begins with an asterisk or computer name, you can use a 32 or 64 prefix to specify 
// using 32- or 64-bit TestStand to host the out-of-process Synchronization object. For example, the name 64*syncobj specifies a 
// Synchronization object called *syncobj in the 64-bit TestStand host process, even when used from 32-bit TestStand. 
// Use the prefix to share Synchronization objects between 32-bit TestStand and 64-bit TestStand in the same host process. 
// The Create operation must be called from each thread or execution using the queue. ex. This application and TestStand sequence  
// If the specified queue exists, this operation obtains a reference to the existing queue

Engine engine = new Engine();
string synchronizationName32;
synchronizationName32 = "32*MySeqQueue";
int noSizeLimit = -1;

 // create sync manager to connect to 32 bit Sequence Context Queue
 SyncManager mySyncManager32 = engine.GetSyncManager(synchronizationName32) as SyncManager;
 bool alreadyexists = false;
 queueRef32 = mySyncManager32.CreateQueue(synchronizationName32, noSizeLimit, out alreadyexists);
PropertyObject destinationPropObj = engine.NewPropertyObject(PropertyValueTypes.PropValType_Reference, false, "", PropertyOptions.PropOption_NoOptions);

WaitResult waitRes = new WaitResult();

// in this example the the property object was Enqueued previously in a teststand sequence step
queueRef32.Dequeue(true, false, destinationPropObj, 2, null, false, out waitRes);

if (waitRes == WaitResult.WaitResult_Success)
{
  // the property that was stored earlier was a sequence context so use GetValInterface and cast it back to a SequenceContext
   SequenceContext sequenceContext = destinationPropObj.GetValInterface("",  PropertyOptions.PropOption_NoOptions) as SequenceContext;
 
    
}

// when done with engine, shutdown and cleanup
engine.ShutDown(true);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(engine);
GC.Collect(); // force .net garbage collection  

 

 

 

Message 9 of 9
(35 Views)