Storing instrument screen images via OpenTap SDK in our plugin

We are trying to obtain screen grabs from the instrument console to add to our pdf report, which is part of the ResultListener.

The Scpi command to obtain screen grab works when we run the following scpi commands directly:

MMEM:STOR:IMAG "pic.png" 

The above command saves the screen grab into a file called pic.png on the instrument
The below command then transfer the same file to the client machine

MMEM:DATA? "pic.png" > pic.png 

However when we run these commands via C# code using ScpiCommand / ScpiQuery methods of ScpiInstrument, we get a sequence of numbers in an array as the output.

How can we save the image to our machine at a particular location via the OpenTap SDK?

EDIT : Keysight command expert returns us a list of comma separated integers, which are supposed to be 488.2 block format. We believe that we need a way to convert this list of comma separated integers into an image - perhaps a bmp?

EDIT 2 : attempted to write this into a png file thus:

using System.Drawing;
using System.Text.RegularExpressions;

string intArrayString = "[137,80,78,71,13,10,2]"; // huge string, partly provided here
int[] intArray = Regex.Matches(intArrayString, @"\d+")
                                 .Cast<Match>()
                                 .Select(m => int.Parse(m.Value))
                                 .ToArray();


// Convert integer array to byte array
byte[] byteArray = new byte[intArray.Length * sizeof(int)];

Buffer.BlockCopy(intArray, 0, byteArray, 0, byteArray.Length);

using (MemoryStream ms = new(byteArray))
{
    Image image = Image.FromStream(ms);
    image.Save("abc.png", System.Drawing.Imaging.ImageFormat.Png);
}

This thows an error

Unhandled exception. System.ArgumentException: Parameter is not valid.
   at Windows.Win32.Graphics.GdiPlus.StatusExtensions.ThrowIfFailed(Status status)
   at System.Drawing.Image.LoadGdipImageFromStream(IStream* stream, Boolean useEmbeddedColorManagement)
   at System.Drawing.Image.LoadGdipImageFromStream(Stream stream, Boolean useEmbeddedColorManagement)
   at System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement, Boolean validateImageData)
   at System.Drawing.Image.FromStream(Stream stream, Boolean useEmbeddedColorManagement)
   at System.Drawing.Image.FromStream(Stream stream)
   at Program.<Main>$(String[] args) in Program.cs

which doesn’t make sense because that would mean the output from the scpi query is invalid. Or could there be another reason, better yet is there a simpler way to get the image from this binary block?

For what it’s worth, here’s code I use for saving screenshots from a Keysight MXA:

Thanks @david-wsd ! Well we did find a ReadIEEEBlock() method - a private one in OpenTap.ScpiInstrument class from which our instrument class is derived! - but couldn’t figure out how to actually use it in our code (because private method).

We are curious to understand, what is your current class (whose instance is represented by this) - derived from in the OpenTap universe? Or, is there another IO library you are using for this purpose?

Thanks in advance

We could find Use a C# program to perform a screen capture on the 33500B/33600B/3446xA/532x0A instruments. - Technical Support Knowledge Center Open - it talks about

using Ivi.Visa.Interop;

We’ll try this and will share the results for everybody’s benefit.

You might be interested in using the Artifacts feature (Available since OpenTAP 9.22). This is better suited for binary data such as images, waveforms, etc. You can read about it here: Developer Guide / Artifacts.

The idea would be that your PDF result listener implements the IArtifactListener interface and your test step publishes an artifact using the TestStep.StepRun.PublishArtifact method.

Your result listener then gets a callback with stream and a name pointing to the artifact by implementing this method:

void OnArtifactPublished(TestRun run, Stream artifactStream, string artifactName);

Hi @alkuma, in our solution we have lower-level instrument classes which are independent of opentap; the code I pasted is from such a class.

You are correct, I am using the Ivi.Visa.Interop namespace, specifically the FormattedIO488 interface. Here is my code which establishes communication, creating the FormattedIO488 instance.

    public static FormattedIO488 GetEqIO488(string address)
    {
        FormattedIO488 EqIO;
        ResourceManager rm = new ResourceManager();
        IMessage IMsg;

        try
        {
            EqIO = new FormattedIO488();
            IMsg = (rm.Open(address, AccessMode.NO_LOCK, 2000, "")) as IMessage;
            EqIO.IO = IMsg;
            return EqIO;
        }
        catch (Exception e)
        {
            string msg = "Failed to connect to equipment at visa address " + address;
            throw new Exception(msg, e);
        }
    }

Coming back to post this after not being able to locate the Ivi.Visa.Interop namespace in any of the publicly available nuget packages, one of them being authored by Keysight.

Searching for Ivi.Visa.Interop gives us nothing, while searching for Ivi.Visa gives

Kelary.Ivi.Visa
Ldx.Ni.Visa
IviFoundation.Visa
hyf.Ivi.Visa

But none of them contains the Ivi.Visa.Interop namespace and so we aren’t able to compile the C# console app provided at https://docs.keysight.com/kkbopen/use-a-c-program-to-perform-a-screen-capture-on-the-33500b-33600b-3446xa-532x0a-instruments-620693023.html - is this available in a private nuget package instead?

Hi @alkuma,

Did you try using ScpiQueryBlock? That returns a IEEE block.

// returns a byte array with the contents of pic.png.
byte[] pic = instrument.ScpiQueryBlock<byte>("MMEM:DATA? \"pic.png\""); 

Regarding visa:

If you install IO Libraries these should be available from the global assembly cache. They might not be available from nuget.

In OpenTAP we have taken a slightly different approach. We load the C visa (libvisa) DLL, you are welcome to use that: opentap/Engine/Visa.cs at main · opentap/opentap · GitHub

Hi @alkuma, you may want to take a look at the Network Analyzer plugin, the test step is located here:

and the instrument code is here:

hope this helps,

Carlos Montes