Documentation:Tutorial:APIs:C - MdsWiki
Navigation
Personal tools

From MdsWiki

Jump to: navigation, search

Reading and Writing MDSplus data in C

Several data access libraries are available in MDSplus. In particular, one library, called mdslib, is intended to provide easy data access for MDSPlus data. As usual, data access corresponds to the evaluation of a TDI expression, which will specify in most cases the name of a node of a MDSplus tree. In this section we shall present two examples for reading and writing data, respectively. The complete source for both read and write examples is available here and a more complete description of the mdslib library is available here. We shall assume also that the reader is familiar with the C programming language.

As we have already seen in Matlab and IDL, the steps for reading/writing in MDSplus trees are:

  1. Establish a connection with a mdsip server (in the case remote data access is performed)
  2. Open a tree
  3. Read and write data
  4. Close the tree
  5. Disconnect from the mdsip server (for remote access)

When programming in C (and Fortran) things are a bit more complicated than in IDL and Matlab. In the last two languages, in fact, a variable can contain data of any supported type, while in C a variable can only be declared of a given type. As a tree node may contain data of an arbitrary type (string, scalar, array, integer, float etc.), we need a mechanism for mapping the returned data type into the receiving variable. This is achieved by the usage of descriptors. A descriptor specified the kind of variable used for receiving MDSplus data. In particular, a descriptor will specify:

  1. The type of the receiving variable
  2. Its dimension
  3. Its starting address

Before reading MDSplus data, we need therefore to specify a descriptor, and pass its reference to the data access routine. The desc() function is used to create a descriptor, whose prototype is:

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

where:

  • dtype: specifies the type of the associated variable, and can be any of:
    DTYPE_UCHAR
    DTYPE_USHORT
    DTYPE_ULONG
    DTYPE_ULONGLONG
    DTYPE_CHAR
    DTYPE_SHORT
    DTYPE_LONG
    DTYPE_LONGLONG
    DTYPE_FLOAT
    DTYPE_DOUBLE
    DTYPE_CSTRING
  • data: is the starting address of the associated variable, which may be a scalar or an array;
  • dim1: is the dimension (in elements) of the associated variable

It is possible to define additional dimensions for multidimensional arrays (supported by MDSplus, but not described in this tutorial), and in any case it is necessary to finish the argument list with a pointer to a null integer.

The prototypes of the routines of the mdslib interface are

  • int MdsConnect( const char *server )
    For connecting to a mdsip server. The string argument specifies the IP (and optionally the port) of the machine running the mdsip server.
  • int MdsOpen( const char *treename, int *shotnumber )
    For opening a MDSplus tree.
  • int MdsValue( char *expression, int *ptr_to_descriptor1, ... int *ptr_to_zero, int *ptr_to_len )
    For evaluating a TDI expression. The expression is specified as a string in the first argument. The first argument is followed by a sequence of one or more references to descriptors. The last one will specify the variable which will receive the result of the evaluation of the expression. The other optional descriptors (seldomly used) will define variables whose contents will substitute the $n symbols in the TDI expression (it is the same mechanism we have already seen for writing data in IDL and Matlab). A pointer to a null integer will indicate the end of the description list. Finally, the last argument is a pointer to an integer variable, which will contain the number of samples effectively read. (define that pointer as NULL if you are not interested in this information).
  • int MdsPut( char *node, char *expression, int *ptr_to_descriptor1, ... int *ptr_to_zero )
    For writing an expression in a tree node. The first argument specifies the path name (or tag name) of the node. The second argument specifies the TDI expression to be stored, whose symbols $n will be replaced by the contents of the variables associated with the descriptors of the following list terminated by a pointer to a null integer.
    Be aware that tag names (specified in the node name or in the expression) begins with a backslash (\) which within the specification of C strings needs to be duplicated (\\).
  • int MdsClose( char *treename, int *shot )
    For closing a MDSplus tree.

All the MDSplus routines return an integer status indicating whether the operation has been successful. The usual MDSplus convention is that an odd status value means success, while even values describe some sort of failure condition. It is possible to transform the coded status into a string description using the following routine:

char * MdsGetMsg(int status)

which returns the pointer to a string containing a description of the condition described by the passed status.

We are now ready for our example.Consider the [Documentation:Tutorial:MdsLibC|readExample] routine first, which reads the X and Y ales of signal :SIGNAL1 in tree MY_TREE, shot -1:

#include <mdslib.h>
#include <mdsshr.h>

mdslib.h and mdsshr.h contain the prototypes of the used MDSplus routines.

#define statusOk(status) ((status) & 1)

As we have seen, an odd status means success, so the above macro can be used to make code more readable.

socket = MdsConnect("150.178.3.101:8000");
if ( socket == -1 )
{
fprintf(stderr,"Error connecting to mdsip server.\n");
return -1;
}

Mdsip connection establishment. Routine MdsConnect returns the handle of the used communication socket. In our code we simply check that is different from -1, and then we forget about it.

status = MdsOpen("my_tree", &shot);
if ( !statusOk(status) )
{
fprintf(stderr,"Error opening tree for shot %d: %s.\n",shot, MdsGetMsg(status));
return -1;
}

Open my_tree for shot -1

size = getSize(":SIGNAL1");
if ( size < 1 )
{
fprintf(stderr,"Error retrieving length of signal\n");
return -1;
}

As we do not know in advance the number of samples stored in :SIGNAL1, we need to retrieve this information, in order to reserve the right amount of memory to the arrays which will contain the X and Y axis of the signal. Routine getSignal is described later.

sigDesc = descr(&dtypeFloat, data, &size, &null);
timeDesc = descr(&dtypeFloat, timebase, &size, &null);

Create the two descriptor for arrays data and timebase, which will contain the Y and X axis of the signal, respectively.

status = MdsValue(":SIGNAL1", &sigDesc, &null, &retLen );
if ( !statusOk(status) )
{
fprintf(stderr,"Error retrieving signal: %s\n", MdsGetMsg(status));
return -1;
}
status = MdsValue("DIM_OF(:SIGNAL1)", &timeDesc, &null, &retLen);
if ( !statusOk(status) )
{
fprintf(stderr,"Error retrieving timebase: %s\n", MdsGetMsg(status));
free( (void *)data );
free( (void *)timebase );
return -1;
}

Now everything is ready for calling routine MdsValue, a first time for getting the Y samples, and a second time for getting the X samples, which will be stored in arrays data and timebase, respectively. Variable retLen will contain the number of transferred samples, which in this example as to be equal to variable size.

In the above example we have used a support function getSize() to which we passed the name of the node :SIGNAL1. This routine performs in turn the evaluation of the TDI expression SIZE(:SIGNAL1), which returns the number of samples stored in :SIGNAL1.
Being in this case the information to be retrieved composed of a scalar integer value, fewer arguments are passed to function descr:

 lenDescr = descr(&dtypeLong, &retSize, &null);

In fact it is not required now to specify the number of elements of the associated variable (it is a scalar). Observe that the teminator argument is always required.

Routine writeExample should be now easy to understand. In this case we build the two descriptors for the arrays data and timebase containing the Y and X values of the signal to be written before calling routine MdsPut()

 dataDesc = descr(&dtypeFloat, data, &len, &null);
timeDesc = descr(&dtypeFloat, timebase, &len, &null);

status = MdsPut(":SIGNAL1", "BUILD_SIGNAL($1,,$2)", &dataDesc, &timeDesc, &null);

You can use the following commands to compile and link a program using the MDSplus libraries (we assume here that the mdsplus libraries are in /usr/local/mdsplus):

cc -c -I /usr/local/mdsplus/include <my_program>.c
cc -o <my_program> -L/usr/local/mdsplus/lib <my_program>.o -lMdsLib -lMdsShr -lc

Flag -I indicates the directory from which the include files mdslib.h and mdsshr.h are located
Flag -L instructs the linker to search the shared libraries in /usr/local/mdsplus/lib