Documentation:Reference:C - MdsWiki
Navigation
Personal tools

From MdsWiki

Jump to: navigation, search

Calling MDSplus from C Programs

Justin Burruss burruss@fusion.gat.com
Updated 2002-06-28

Introduction

This document explains the C API to MDSplus. This guide is intended for users that are comfortable with programming in the C programming language.

Header Files & Libraries

C source code that calls the MDSplus API must include the MDSplus header file mdslib.h. Also, you must link in the MDSplus libraries. /usr/local/mdsplus/include and /usr/local/mdsplus/lib are the default directories for header files and libraries for new installations of MDSplus (on UNIX).

Here is an example of a typical compile on an HP-UX UNIX machine:

triton 114: /usr/bin/c89 -o mdsplus_example mdsplus_example.c
 -I/usr/local/mdsplus/include -L/usr/local/mdsplus/lib
 -lMdsLib

Descriptors

MDSplus uses descriptors to pass around data. A descriptor is basically just a struct with the raw data and a description of that raw data.

Use the descr() function to create a descriptor. Here's the function declaration:

  int descr (int *dtype, void *data, int *dim1, ...);

The ... indicates a variable number of arguments, terminated with a pointer to an int with value 0. The dtype argument takes an int; use these #defined dtypes:

  DTYPE_UCHAR
  DTYPE_USHORT
  DTYPE_ULONG
  DTYPE_ULONGLONG
  DTYPE_CHAR
  DTYPE_SHORT
  DTYPE_LONG
  DTYPE_LONGLONG
  DTYPE_FLOAT
  DTYPE_DOUBLE
  DTYPE_COMPLEX
  DTYPE_COMPLEX_DOUBLE
  DTYPE_CSTRING
  DTYPE_EVENT_NOTIFY
  DTYPE_EVENT

Use dim1, etc. to specify the dimensions of the data. Omit the dim argument for scalar quantities.

So for example, to build a descriptor for a scalar float:

  int null = 0;
  int dtype_flt = DTYPE_FLOAT;
  float data;
  int flt_descr = descr(dtype_flt, &data, &null);

To build a descriptor for a 1-dimensional signal with 1000 elements:

  int null = 0;
  int dtype_flt = DTYPE_FLOAT;
  float data[1000];
  int sig_descr = descr(dtype_flt, data, 1000, &null);

One final note on descriptors: there can be no more than 256 descriptors at any one time. This is because the descriptor numbers (the return value from the descr() function call) are actually indicies in a global array of descriptors.

Functions

Function Status

The MDSplus functions (except for the MdsConnect function) return a status int. The status is OK if the least significant bit (LSB) is set. So to check the status, do a bitwise AND on the LSB. You may want to include the following function in your code:


  /*
    status_ok

    Description:
      Returns 1 if OK, 0 otherwise.  Status is OK if the
      LSB is set.

    Dependencies:
      none.
  */
  int status_ok( int status )
  {
    return ( (status & 1) == 1 );
  }

Alternatively, you could define a macro:

  #define status_ok(status) (((status) & 1) == 1)

Note that testing the least significant bit is the same as testing to see if a number is odd or even. If the LSB is set, then the status will be an odd number. If the LSB is not set, then the status will be an even number.

Function Summary

Here's a summary of the MDSplus functions:

MdsConnect
Use MdsConnect to connect to the MDSplus server.
Usage: int connection_id = MdsConnect( const char *server );.
MdsConnect will return a -1 value if the connection fails.
MdsOpen
Use MdsOpen to open an MDSplus tree.
Usage: int status = MdsOpen( const char *treename, int *shotnumber );.
MdsSetDefault
Use MdsSetDefault to set your location in the MDSplus tree.
Usage: int status = MdsSetDefault( const char *path );.
MdsValue
MdsValue is used to evaluate TDI expressions.
Usage: int status = MdsValue( char *expression, int *ptr_to_descriptor1, ... int *ptr_to_zero, int *ptr_to_len );.
MdsClose
MdsClose is used to close an open tree.
Usage: int MdsClose( char *tree, int *shot );.
MdsPut
MdsPut is used to put a TDI expression into an MDSplus node.
Usage: int MdsPut( char *node, char *expression, int *ptr_to_descriptor1, ... int *ptr_to_zero );.

Using MDSplus Functions

You can first use MdsConnect to connect to a MDSplus server. (If no MdsConnect is performed the calls will use local direct access to the data.) Then use MdsOpen to open the tree you need to access.

Since you need to know the size of the signal before you build a descriptor for it, you should first use MdsValue to get the size of the signal, then build a descriptor using that size. Here is a function for getting the length of a signal in MDSplus:

 /*
   get_signal_length
 
   Description:
     Returns the length of the specified signal
     if successful, -1 if not successful.  Assumes
     the the user is already connected to the MDSplus
     server, and that the tree is already open.  Uses
     the tdi intrinsic function SIZE() to get the
     signal length.
 
   Dependencies:
     stdio.h (for fprintf, sprintf)
     mdslib.h (for MdsValue, descr)
     string.h (for memset)
 */
 int get_signal_length(const char *signal)
 {
   /* local vars */
   int dtype_long = DTYPE_LONG;
   char buf[1024];
   int size;
   int null = 0;
   int idesc = descr(&dtype_long, &size, &null);
   int status;
 
   /* init buffer */
   memset(buf,0,sizeof(buf));
 
   /* put SIZE() TDI function around signal name */
   snprintf(buf,sizeof(buf)-1,"SIZE(%s)",signal);
 
   /* use MdsValue to get the signal length */
   status = MdsValue(buf, &idesc, &null, 0);
   if ( !( (status & 1) == 1 ) )
   {
     fprintf(stderr,"Unable to get length of %s.\n",signal);
     return -1;
   }
 
   /* return signal length */
   return size;
 
 }
 

So for example, if we have a signal called PRESSURE in a tree called MYTREE on a server named atlas.gat.com, we could do something like this:

 #include <stdio.h>
 #include <string.h>
 #include <mdslib.h>
 #include "myheaderfile.h" /* include status_ok() and
                              get_signal_length() */
 
 int main(int argc, char *argv[])
 {
    /* local vars */
    int dtype_float = DTYPE_FLOAT;
    int null = 0;
    int status;
    int socket;
    float *data;     /* array of floats used for signal */
    float *timebase; /* array of floats used for timebase */
    int sigdesc;     /* signal descriptor */
    int timedesc;    /* descriptor for timebase */
    int size;        /* length of signal */
    int i, len;
    int shot = 100;  /* just an example shot number */
 
    /* Connect to MDSplus */
    socket = MdsConnect("atlas.gat.com");
    if ( socket == -1 )
    {
      fprintf(stderr,"Error connecting to Atlas.\n");
      return -1;
    }
 
    /* Open tree */
    status = MdsOpen("MYTREE", &shot);
    if ( !status_ok(status) )
    {
      fprintf(stderr,"Error opening tree for shot %l.\n",shot);
      return -1;
    }
 
    /* use get_signal_length to get size of signal */
    size = get_signal_length("PRESSURE");
    if ( size < 1 )
    {
      /* error */
      fprintf(stderr,"Error retrieving length of signal\n");
      return -1;
    }
 
    /* use malloc() to allocate memory for the signal */
    data = (float *)malloc(size * sizeof(float));
    if ( !data )
    {
      fprintf(stderr,"Error allocating memory for data\n");
      return -1;
    }
 
    /* create a descriptor for this signal */
    sigdesc = descr(&dtype_float, data, &size, &null);
 
    /* retrieve signal */
    status = MdsValue("PRESSURE", &sigdesc, &null, &len );
    if ( !status_ok(status) )
    {
      /* error */
      fprintf(stderr,"Error retrieving signal\n");
      return -1;
    }
 
    /* use malloc() to allocate memory for the timebase */
    timebase = (float *)malloc(size * sizeof(float));
    if ( !timebase )
    {
      fprintf(stderr,"Error allocating memory for timebase\n");
      free( (void *)data );
      return -1;
    }
 
    /* create a descriptor for the timebase */
    timedesc = descr(&dtype_float, timebase, &size, &null);
 
    /* retrieve timebase of signal */
    status = MdsValue("DIM_OF(PRESSURE)", &timedesc, &null, 0);
    if ( !status_ok(status) )
    {
      /* error */
      fprintf(stderr,"Error retrieving timebase\n");
      free( (void *)data );
      free( (void *)timebase );
      return -1;
    }
 
    /* do whatever with the data */
 
    /* example: print ALL the data */
    for ( i = 0 ; i < size ; i++ )
      printf("%i  X:%f  Y:%f\n", i, timebase[i], data[i]);
 
    /* free the dynamically allocated memory when done */
    free( (void *)data );
    free( (void *)timebase );
 
    /* done */
    return 0;
 }
 

Execute TCL Commands using MdsValue()

You can send TCL commands to the MDSplus server via the TDI function TCL(). Normally, output from the TCL commands executed through the TCL() function goes to stdout. Since the commands are executed on the server, your client program will not see the command output (which will end up in the server logfile). However, it is possible to provide the TCL() function with an optional output argument in order to see any output from the TCL commands executed through the function.

Like any TDI expression, evaluate the TDI function TCL() using MdsValue(). The following code demonstrates how to do this.

 #include <stdio.h>
 #include <mdslib.h>
 
 /* Define a macro to check status of MDSplus functions */
 #define status_ok(status) (((status) & 1) == 1)
 
 int main()
 {
 
   int dtype_long = DTYPE_LONG;
   int dtype_cstring = DTYPE_CSTRING;
   int status, tstat, len, shot=100, null=0, socket;
   int bufsize = 1024;
   char buf[1024];
   int idesc = descr(&dtype_long, &tstat, &null);
   int sdesc = descr(&dtype_cstring, buf, &null, &bufsize);
 
   /* Connect to MDSplus server */
   puts("Connecting to MDSplus server.");
   socket = MdsConnect("atlas.gat.com");
   if ( socket == -1 )
   {
     fprintf(stderr,"Error connecting to MDSplus server.\n");
     return -1;
   }
 
   /* Open tree */
   puts("Opening tree.");
   status = MdsOpen("MYTREE", &shot);
   if ( !status_ok(status) )
   {
     fprintf(stderr,"Error opening tree for shot %l.\n",shot);
     return -1;
   }
 
   /* Demonstrate use of the TDI function TCL() to send TCL commands
      to the MDSplus server. */
 
   /* First, send the TCL command to the server. */
   puts("Demonstrating use of TCL() function.");
   status = MdsValue("TCL(\"SHOW DEF\",_output)",&idesc,&null,&len);
   if ( !status_ok(status) )
   {
     fprintf(stderr,"Error with SHOW DEF command.\n");
     return -1;
   }
   printf("Status of TCL(\"SHOW DEF\") = %i\n",tstat);
 
   /* If the command was successful, print its output. */
   if ( status_ok(tstat) )
   {
     status = MdsValue("_output",&sdesc,&null,&len);
     if ( !status_ok(status) )
     {
       fprintf(stderr,"Error getting _output from SHOW DEF command.\n");
       return -1;
     }
     printf("Output of TCL(\"SHOW DEF\") = %s\n",buf);
   }
 
   return 0;
 }
 

Sample Code for HP-UX

Here's some sample code for HP-UX. This example works at DIII-D on Hydra (which is running HP-UX 11i). It connects to Atlas, opens the EFIT01 tree for shot 107000, and prints the first 10 data points in the AMINOR signal.

To run this example, copy the Makefile and the source file into a directory. Use the command make to build the code. Run the code with the command ./example. Note the use of ./ in front of example. This is to make sure that you run the example code in the current directory, not any programs called example that might be in a directory in your $PATH environment variable.

Note that the MDSplus header files and libraries used by this Makefile are not in the default area, so you will most likely need to modify the Makefile to point to the right directories.

Makefile

CC = /usr/bin/c89
FLAGS = -I/f/mdsplus/hp/mdsplus/include -L/f/mdsplus/hp/mdsplus/lib \
 -lMdsLib

example: example.c
        $(CC) $(FLAGS) example.c -o example

debug_example: example.c
        $(CC) $(FLAGS) -g example.c -o example

clean:
        rm -f *~ core example

Remember that makefiles require tabs (not spaces) in front of the commands. If you cut and paste the above makefile, make sure that the file has tabs, not spaces.

If you have trouble with the makefile and you're sure you're using tabs (not spaces), then use the command which make to see which version of make you are using. The above makefile was tested with /usr/bin/make.

example.c

 #include <stdio.h>
 #include <string.h>
 #include <mdslib.h>
 
 #define EXIT_FAILURE -1
 #define status_ok(status) (((status) & 1) == 1)
 
 int get_signal_length(const char *signal)
 {
   /* local vars */
   int dtype_long = DTYPE_LONG;
   char buf[1024];
   int size;
   int null = 0;
   int idesc = descr(&dtype_long, &size, &null);
   int status;
 
   /* init buffer */
   memset(buf,0,sizeof(buf));
 
   /* put SIZE() TDI function around signal name */
   snprintf(buf,sizeof(buf)-1,"SIZE(%s)",signal);
 
   /* use MdsValue to get the signal length */
   status = MdsValue(buf, &idesc, &null, 0);
   if ( !status_ok(status) )
   {
     fprintf(stderr,"Unable to get length of %s.\n",signal);
     return EXIT_FAILURE;
   }
 
   /* return signal length */
   return size;
 
 }
 
 int main(int argc, char *argv[])
 {
    /* local vars */
    int dtype_float = DTYPE_FLOAT;
    int null = 0;
    int shot = 107000;
    int status;
    int socket;
    float *data;     /* array of floats used for signal */
    float *timebase; /* array of floats used for timebase */
    int sigdesc;     /* signal descriptor */
    int timedesc;    /* descriptor for timebase */
    int size;        /* length of signal */
    int i, len;
 
    /* Connect to MDSplus */
    socket = MdsConnect("atlas.gat.com");
    if ( socket == -1 )
    {
      fprintf(stderr,"Error connecting to Atlas.\n");
      return EXIT_FAILURE;
    }
 
    /* Open tree */
    status = MdsOpen("EFIT01", &shot);
    if ( !status_ok(status) )
    {
      fprintf(stderr,"Error opening EFIT01 tree for shot %l.\n",shot);
      return EXIT_FAILURE;
    }
 
    /* use get_signal_length to get size of signal */
    size = get_signal_length("\\AMINOR");
    if ( size < 1 )
    {
      /* error */
      fprintf(stderr,"Error retrieving length of signal\n");
      return EXIT_FAILURE;
    }
 
    /* use malloc() to allocate memory for the signal */
    data = (float *)malloc(size * sizeof(float));
    if ( !data )
    {
      fprintf(stderr,"Error allocating memory for data\n");
      return EXIT_FAILURE;
    }
 
    /* create a descriptor for this signal */
    sigdesc = descr(&dtype_float, data, &size, &null);
 
    /* retrieve signal */
    status = MdsValue("\\AMINOR",&sigdesc, &null, &len );
    if ( !status_ok(status) )
    {
      /* error */
      fprintf(stderr,"Error retrieving signal\n");
      free( (void *)data );
      return EXIT_FAILURE;
    }
 
    /* use malloc() to allocate memory for the timebase */
    timebase = (float *)malloc(size * sizeof(float));
    if ( !timebase )
    {
      fprintf(stderr,"Error allocating memory for timebase");
      free( (void *)data );
      return EXIT_FAILURE;
    }
 
    /* create a descriptor for the timebase */
    timedesc = descr(&dtype_float, timebase, &size, &null);
 
    /* retrieve timebase of signal */
    status = MdsValue("DIM_OF(\\AMINOR)", &timedesc, &null, 0);
    if ( !status_ok(status) )
    {
      /* error */
      fprintf(stderr,"Error retrieving timebase\n");
      free( (void *)data );
      free( (void *)timebase );
      return EXIT_FAILURE;
    }
 
    /* do whatever with the data */
 
    /* example: print first 10 data points */
    for ( i = 0 ; i < size && i < 10 ; i++ )
      printf("%i  X:%f  Y:%f\n", i, timebase[i], data[i]);
 
    /* free the dynamically allocated memory when done */
    free( (void *)data );
    free( (void *)timebase );
 
    /* done */
    return 0;
 }
 

Note that it is necessary to quote the backslash character with another backslash when building the string \AMINOR.

If you run into bugs while writing your own MDSplus code, try compiling with the -g option and use a debugger.

More example C code is available.