3

**Note: Cross-posted at LabVIEW forums: http://forums.ni.com/t5/LabVIEW/C-VISA-wait-on-RQS/td-p/3122939

I'm trying to write a simple C# (.NET 4.0) program to control a Keithley 2400 SMU over VISA GPIB and I'm having trouble with getting the program to wait for the Service Request that the Keithley sends at the end of the sweep.

The sweep is a simple linear voltage sweep, controlled internally by the Keithley unit. I've got the unit set up to send a ServiceRequest signal at the end of the sweep or when compliance is reached.

I'm able to send the commands to the SMU and read the data buffer, but only if I manually enter a timeout between the sweep start command and the read data command.

One issue I'm having is that I'm quite new to C# - I'm using this project (porting parts of my LV code) to learn it.

Here is what I have so far for my C# code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using NationalInstruments.VisaNS;

private void OnServiceRequest(object sender, MessageBasedSessionEventArgs e)
{
    Console.WriteLine("Service Request Received!");
}

// The following code is in a class method, but
public double[,] RunSweep()
{
    // Create the session and message-based session
    MessageBasedSession mbSession = null;
    Session mySession = null;
    string responseString = null;

    // open the address
    Console.WriteLine("Sending Commands to Instrument");
    instrAddr = "GPIB0::25::INSTR";
    mySession = ResourceManager.GetLocalManager().Open(instrAddr);

    // Cast to message-based session
    mbSession = (MessageBasedSession)mySession;

    // Here's where things get iffy for me... Enabling the event and whatnot
    mbSession.ServiceRequest += new MessageBasedSessionEventHandler(OnServiceRequest);
    MessageBasedSessionEventType srq = MessageBasedSessionEventType.ServiceRequest;
    mbSession.EnableEvent(srq, EventMechanism.Handler);

    // Start the sweep (SMU was set up earlier)
    Console.WriteLine("Starting Sweep");
    mbSession.Write(":OUTP ON;:INIT");

    int timeout = 10000;             // milliseconds
    // Thread.Sleep(10000);          // using this line works fine, but it means the test always takes 10s even if compliance is hit early

    // This raises error saying that the event is not enabled.
    mbSession.WaitOnEvent(srq, timeout);

    // Turn off the SMU.
    Console.WriteLine("I hope the sweep is done, cause I'm tired of waiting");
    mbSession.Write(":OUTP OFF;:TRAC:FEED:CONT NEV");

    // Get the data 
    string data = mbSession.Query(":TRAC:DATA?");

    // Close session
    mbSession.Dispose();

    // For now, create a dummy array, 3x3, to return. The array after is the starting value.
    double[,] dummyArray = new double[3, 3] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

    return dummyArray;
}

All the above is supposed to mimic this LabVIEW code: simple Keithley Sweep

So, any ideas on where I'm going wrong?

Thanks,

Edit:

After a little more fiddling, I've found that the Service Request function OnServiceRequest is actually fired at the right time ("Service Request Received!" is printed to the console).

John Saunders
  • 160,644
  • 26
  • 247
  • 397
dthor
  • 1,749
  • 1
  • 18
  • 45
  • 1
    I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Apr 22 '15 at 16:18
  • Thanks @JohnSaunders, sorry about that. – dthor Apr 22 '15 at 16:18

2 Answers2

2

It turns out that I need to enable the event as a Queue rather than a handler. This line:

mbSession.EnableEvent(srq, EventMechanism.Handler);

Should actually be:

mbSession.EnableEvent(srq, EventMechanism.Queue);

Source: The documentation under "Remarks". It was a pain to find the docs on it... NI needs to make it easier :-(.

With this change, I also don't need to create the MessageBasedSessionEventHandler.

The final, working code looks like:

rm = ResourceManager.GetLocalManager().Open("GPIB0::25::INSTR");
MessageBasedSession mbSession = (MessageBasedSession)rm;
MessageBasedSessionEventType srq = MessageBasedSessionEventType.ServiceRequest;
mbSession.EnableEvent(srq, EventMechanism.Queue);    // Note QUEUE, not HANDLER
int timeout = 10000;

// Start the sweep
mbSession.Write(":OUTP ON;:INIT");

// This waits for the Service Request
mbSession.WaitOnEvent(srq, timeout);

// After the Service Request, turn off the SMUs and get the data
mbSession.Write(":OUTP OFF;:TRAC:FEED:CONT NEV");
string data = mbSession.Query(":TRAC:DATA?");
mbSession.Dispose();
dthor
  • 1,749
  • 1
  • 18
  • 45
  • Welcome to C#. Most of the session methods, including `Write` could cause an exception, breaking the flow of execution. So, you should place the call to `Dispose` in a `finally` block. This is such a common pattern in .NET, C# has the [using statement](https://msdn.microsoft.com/en-us/library/yh598w02.aspx) so you don't actually have to write out the try-finally just to call `Dispose`. – Tom Blodget Apr 22 '15 at 23:34
  • Thanks @TomBlodget. I will be placing most things in try...catch...finally blocks when I finish up the code. I'm primarily a Python developer and am used to putting most things in those blocks :-). I didn't include them in this post so that things were a bit easier to read. But good info about the `using` statement, I didn't know that one. – dthor Apr 23 '15 at 22:12
1

What you're doing looks correct to me so it's possible that there's an issue with the NI library.

The only thing I can think of to try is waiting for "all events" instead of just "ServiceRequest." like this:

mbSession.WaitOnEvent(MessageBasedSessionEventType.AllEnabledEvents, timeout);

Note: that it doesn't look like you can "enable" all events (so don't change that part).

I also looked for some examples of how other people are doing Keithley sweeps and I found this and this(Matlab ex). As I suspected in both they don't use events to determine when the sweep is finished, but rather a "while loop that keeps polling the Keithley" (the first link actually uses threads, but it's the same idea). This makes me think that maybe that's your best bet. So you could just do this:

        int timeout = 10000;
        int cycleWait = 1000;

        for (int i = 0; i < timeout / cycleWait; i++)
        {
            try
            {
                string data = mbSession.Query(":TRAC:DATA?");
                break;
            }
            catch
            {
                Thread.Sleep(cycleWait); 
            }
        }

(You may also have to check if data is null, but there has to be some way of knowing when the sweep is finished).

Charlie
  • 2,004
  • 6
  • 20
  • 40
  • I'd like to avoid polling if at all possible, but thanks for the info. I'll use it as a fallback. I've edited the post with more info regarding the service request: the Service Request handler is called at the right time, so perhaps I can use that to continue the program. – dthor Apr 22 '15 at 15:54