Documentation:Tutorial:Devices - MdsWiki
Navigation
Personal tools

From MdsWiki

(Difference between revisions)
Jump to: navigation, search
Revision as of 15:33, 18 March 2009 (edit)
Manduchi (Talk | contribs)

← Previous diff
Revision as of 15:58, 18 March 2009 (edit)
Manduchi (Talk | contribs)

Next diff →
Line 94: Line 94:
* the path name of the device subtree root; * the path name of the device subtree root;
* the name of the device type; * the name of the device type;
-* the total number of data items in the device subtree (including tits root);+* the total number of data items in the device subtree (including its root);
* a string command to be passed to the python environment to import the python class defining the other device methods. In this case, python package RfxDevices will contain the python class DEMOADC defining the INIT and ADD methods; * a string command to be passed to the python environment to import the python class defining the other device methods. In this case, python package RfxDevices will contain the python class DEMOADC defining the INIT and ADD methods;
* the returned node reference of the created subtree root * the returned node reference of the created subtree root
Line 103: Line 103:
* The usage of the data item: it can be 'TEXT' is strings are going to be stored in the data item, 'NUMERIC' if the associated data is a number or an expression, 'STRUCTURE' is the associated node will not contain data (e.g. it is the head of a channel subtree in the example), 'SIGNAL' if the node is going to contain a signal (the DATA node for each channel of DEMOADC will containg the acquired signal); * The usage of the data item: it can be 'TEXT' is strings are going to be stored in the data item, 'NUMERIC' if the associated data is a number or an expression, 'STRUCTURE' is the associated node will not contain data (e.g. it is the head of a channel subtree in the example), 'SIGNAL' if the node is going to contain a signal (the DATA node for each channel of DEMOADC will containg the acquired signal);
* The initial value of the data item. It must be in accordance with the specified usage (e.g. it cannot be a number is the usage is 'TEXT') for uninitialized data specify an asterisk (*); * The initial value of the data item. It must be in accordance with the specified usage (e.g. it cannot be a number is the usage is 'TEXT') for uninitialized data specify an asterisk (*);
-* additional flags, always *;+* additional flags, written as they were passed to the TCL commands for creating the node. Tehy are typically empty (*), but in this example, the options for the nodes which will contain the acquired signals are /compress_on_put/nomodel_write, i.e. data is compressed when written and it is not possible to write in these nodes in an experiment model (that is, they are expected to contain only acquired data, not setup configuration);
* the returned node reference * the returned node reference
-A sequence of DevAddNode() function calls specify how the nodes are created to instantiate the device subtree. The only subtlety is that instead of barely repeating DevAddNode() calls for every channel, this is achieved in a for loop.+A sequence of DevAddNode() function calls specify how the nodes are created to instantiate the device subtree. The only subtlety is that instead of barely repeating DevAddNode() calls for every channel, this is achieved in a for loop and the current channel is built by concatenating the current path and the channel number via function TEXT(_c, 1) (otherwise, blanks would be inserted between the string and the number).<br />
 +Finally function DevAddAction() will insert and action node into the tree being edited. Its arguments are:
 + 
 +* The path name of the node being created;
 +* The name of the phase in which this action has to be called by the dispatcher (in this case phases and methods happen to have the same name);
 +* The name of the method (INIT or STORE)
 +* The sequence number for this action within the specified phase;
 +* The name of the server class which is expected to execute such method;
 +* The full pathname of the device root;
 +* The returned node reference.
 + 
 + 
 +Once DEMOADC__add. fun has been written and put in the appropiate tdi subdirectory, MDSplus becomes aware of this new device type. You can add a new device instance when editing a tree with the following TCL command:
 + TCL> add node <Device PATH>/model = <device type>
 + 
 +for example you can add a new instance of DEMOADC device whose root is name DEMO with the following command:
 + TCL> add node DEMO/model=DEMOADC
 + 
 +Alternatively, you can do the same thing with jTraverser after opening an experiment model in edit mode, via the popup command "Add Device". You need to type the device root path name and the device type in the dialog which gets activated.<br />
 +As a final remark, observe that creating new device instances is an operation which occurs seldomly. In the normal operation, the experiment model defines the pulse file structure, and a new pulse file is created simply by copying the experiment model and in this case device construciors are not activated. They are only involved when changing the experiment model structure, e.g. for adding new components in our experiment description.<br /><br />

Revision as of 15:58, 18 March 2009

Developing MDSplus Devices

Why Using MDSplus Devices
This section will cover in detail MDSplus devices, an important feature for customizing MDSplus to any target experiment.
So far, we have used MDSplus to store and retrieve data from pulse files, and to set-up automated sequences of operations to be performed during the experimental phases. If this were all the functionality of MDSplus, a lot of work would be required to build a real-world data acquisition system because every hardware device involved in the system would require the development of ad-hoc code for set-up and data retrieval. A software layer for integrating the device in the system would then be required for every kind of used device. If the same device were used somewhere else, however, it would be nice to avoid developing new stuff from scratch by re-using existing code.
In order to ease re-using device-specific code, MDSplus provides a framework for integrating device-specific functionality into the system. Once integrated, the device support code becomes part of the MDSplus system, and can be promptly re-used elsewhere.
This section will explain in detail how define a new device in MDSplus and how to integrate the support code for in the framework so that it can be re-used not only in the same system, but also by every other MDSplus user.
After a first introduction explaining the general concets of MDSplus devices, we shall use a simple test case: a simulated Transient Recorder and will show in details all the steps which are required to integrate such a device.

General Concepts
Every hardware device used in data acquisition requires some associated information. For example, a generic ADC device may require:

  • HW Address: the hardware address of the ADC device;
  • Sampling frequency: the sampling speed;
  • Post Trigger Samples: the number of samples taken after a trigger

In addition to setup information, sampled signals will be read from the device and stored in the pulse file. In MDSPlus all the information associated with a given device will be stored in a subtree of the pulse file. A different set of nodes, organized as a subtree, will be defined for every device declared in the pulse file, but the structure of such subtrees will be the same for all the devices of the same kind. Borrowing some Object Oriented terminology (we shall see that there are several similarities between objects and MDSplus devices), we define a device instance as the actual set of data items organized in a subtree and bringing all the information associated with the device, and a device type (or class), as the description of the way device specific data are organized, as well as the set of operations (methods) which are defined for that kind of device.
In order to add a device instance to a pulse file (this operation is typically carried out when we are editing the experiment model to be used afterwards in the pulse file creation), it is necessary to provide to MDSplus the following information:

  • The path name of the device subtree root;
  • The type of the device.

Based on the type information, MDSplus will then execute the constructor method of the device, which will in turn build the device-specific subtree. Therefore the minimum required development for integrating a new device type in MDSPlus is the development of the device constructor.
Other methods will carry out the specific device functionality. For example, an ADC device will likely define an INIT method, which will read the information associated with that device instance and will then initialize the hardware device, and a STORE method, to be called after the ADC has been triggered, and which will read sampled data and store them in the appropriate data items of the device subtree.
Typically, in addition to the data items describing the device configuration and the acquired data, an action node associated with each device method is defined in the device subtree. In this way, device methods will be called in the automated sequence based on the action data stored in the pulse file (see the previous section on how to configure an automated experiment sequence).
In order to call the appropriate device methods (and constructor), MDSplus uses a naming convention, in which the name of the method is appended to the name of the device type. It is possible to develop the device support methods in native C language, but in this tutorial we shall use a scripting languages to develop such methods. The use of a scripting languages has the great advantage of hiding the internals of MDSplus in the development of device methods, and this is the current approach in device support development.
The native MDSplus scripting language of MDSplus is TDI (briefly described in the previous sections), but here we shall use Python for the implementation of the test case device. Python has been recently integrated in MDSPlus: it retains all the advantages of TDI, but provides a much more complete and rugged scripting environment. We expect therefore that python will be used extensively in MDSplus. MDSplus currently supports Python implementation of device methods with the only exception of the device constructor which has still to be written in TDI.

In addition to the constructor and the other methods for interacting with the hardware device, a method will provide the a device specific graphical forms for entering the device configuration in jTraverser. A device configuration is represented by a set of data items which are written in the experiment model and then used during data acquisition. It is possible to write configuration data using jTraverser browsing the device subtree and putting the appropriate value into each data item. However such operation would be lengthy and tedious (very often device subtrees define tens or hundreds of data items). It would be much better to let jTraverser activating a specific form which will present device configuration in the most appropriate graphical layout, so that data can be easily entered by users during the configuration of the device.
This can be achieved by developing a Setup method, which is then called by jTraverser when requested via the Popup "Setup Device" option.
Image:DevicesTutorialSetupDevice.gif
Unlike other device methods, the Setup method is written in Java. We shall see in the test case how it is possible to develop a device graphical form without writing a single line of Java code. This is possible because MDSplus provides a set of specialized Java beans which can be used in a Bean Builder tool to visually develop new graphical forms.

The Test Case
In this section we shall develop the required methods for a sample ADC module with the following characteristics:

  • 4 channels;
  • +-10V input range;
  • 16 bit a/D conversion;
  • 64K samples stored for each channel

Once initialized, the ADC will start recording data cyclically until the preset number of Post Trigger Samples is recorded after the receipt of a trigger signal.
The device is simulated by a shared library provided in the MDSplus distribution (DemoAdc) which exports two functions:

int initialize(char *name, int clockFreq, int postTriggerSamples)

where:

  • name is a string specifying some sort of hardware address;
  • clockFreq is the sampling clock frequency which can have the values 1000, 5000, 10000, 50000 and 100000
  • postTriggerSamples is the number of samples acquired after the occurrence of a trigger.

and

int acquire(char *name, short *chan1, short *chan2, short *chan3, short *chan4)

where the first argument is the string address of the device and the other argument are pointers to a short arrays (of 64K elements) which will contain the acquired samples. The routine will return four different sinusoidal signals sampled at the preset sampling frequency.
Even if this is a simulated device, it reflects a very common situation in which an hardware device is shipped together its software driver, typically providing a library with some functions for device configuration and data readout.

In order to develop the MDSplus device support for this device, called afterwards DEMOADC, we need first to define how the associated subtree will look. A possible choice is shown below:
Image:DevicesTutorialDemoAdcStructure.gif
The meaning of the above field is summarized as follows:

  • CLOCK_FREQ contains the sampling clock frequency;
  • COMMENT contains a string comment associated with the device instance. It will be shown in the device graphical form.
  • NAME contains the string address of the device;
  • PTS defines the number of Post Trigger Samples;
  • INIT_ACTION is the action data specifying the execution of the INIT method for this device instance;
  • STORE_ACTION is the action data specifying the execution of the STORE method for this device.

In addition to configuration parameters, every channel is represented by a subtree defining two data items START_IDX and END_IDX. These fields define, for each channel the initial and final sample referred to the sample associated with the trigger. For example START_IDX = -1000 and END_IDX = 1000 specify that the STORE method will store in the database a subset of the acquired 64 KSamples, composed of 2001 samples centered around the trigger occurrence.

Before describing methods in detail we need to specify where the device support methods will be put in MDSplus directory tree. MDSplus xpects that device methods will be stored in a (sub)direcotry of $MDS_PATH (normally mointing to subdiretcory tdi in the MDSplus directpry tree. In order to organize the large number of supported devices, a furthe directory level has been defined. For example, tdi/MitDevices will contain the device support methods for devices developed at MIT CMOD; tdi/RfxDevices will contain the device methods developed at RFX amd so on. You may define your own subdirectory of TDI and put there your specific device support methods. The DEMOADC support methods are hosted in tdi/RfxDevices
Regarding the naming convention defined in MDSplus to localize device methods, the convention is:

<device type>__<method name>

Therefore the constructor method we are going to develop for DEMOADC device will be named DEMOADC__add.fun (.fun is the extension of TDI scripts and add is the default name of constructor methods.

Below is the listing of the TDI script for DEMOADC__add.fun

public fun DEMOADC__add(in _path, out _nidout)
{
   DevAddPyStart(_path,'DEMOADC',24,['from RfxDevices import DEMOADC'],_nidout);
   DevAddNode(_path // ':NAME', 'TEXT', *, *, _nid);
   DevAddNode(_path // ':COMMENT', 'TEXT', *, *, _nid);
   DevAddNode(_path // ':CLOCK_FREQ', 'NUMERIC', 10000, *, _nid);
   DevAddNode(_path // ':TRIG_SOURCE', 'NUMERIC', 0, *, _nid);
   DevAddNode(_path // ':PTS', 'NUMERIC', 1000, *, _nid);
   for (_c = 1; _c <=4; _c++)
   {
       _cn = _path // '.CHANNEL_' // TEXT(_c, 1);
       DevAddNode(_cn, 'STRUCTURE', *, *, _nid);
       DevAddNode(_cn // ':START_IDX', 'NUMERIC', 0, *, _nid);
       DevAddNode(_cn // ':END_IDX', 'NUMERIC', 1000, *, _nid);
       DevAddNode(_cn // ':DATA', 'SIGNAL', *, '/compress_on_put/nomodel_write', _nid);
   }
   DevAddAction(_path//':INIT_ACTION', 'INIT', 'INIT', 50,'CAMAC_SERVER', getnci(_path, 'fullpath'), _nid);
   DevAddAction(_path//':STORE_ACTION', 'STORE','STORE', 50,'CAMAC_SERVER',getnci(_path, 'fullpath'), _nid);
   DevAddEnd();
}

The first line defines a TDI function. The two arguments _path and _nidout are passed by the framework (recall that in TDI every variable name begins with a underscore) and indicate the path name of the device subtree root and the (returned) reference of the created device subtree node. Without going into details of TDI syntax, it suffice observing that a device constructor is basically a sequence function calls for inserting new nodes in the experiment model being currently edited. Function DevAddPyStart has to be called at the beginning of the subtree creation in the case the other device support methods are implemented in python. Its arguments are the following:

  • the path name of the device subtree root;
  • the name of the device type;
  • the total number of data items in the device subtree (including its root);
  • a string command to be passed to the python environment to import the python class defining the other device methods. In this case, python package RfxDevices will contain the python class DEMOADC defining the INIT and ADD methods;
  • the returned node reference of the created subtree root

Afterwards, function DevAddNode is used to create new data items in the device subtree. Its arguments are:

  • The path name of the node being created. Observe that it is built in the example by appending the subtree path to the path name of the devuice root passed to the constructor (in TDI operator // means string concatenation);
  • The usage of the data item: it can be 'TEXT' is strings are going to be stored in the data item, 'NUMERIC' if the associated data is a number or an expression, 'STRUCTURE' is the associated node will not contain data (e.g. it is the head of a channel subtree in the example), 'SIGNAL' if the node is going to contain a signal (the DATA node for each channel of DEMOADC will containg the acquired signal);
  • The initial value of the data item. It must be in accordance with the specified usage (e.g. it cannot be a number is the usage is 'TEXT') for uninitialized data specify an asterisk (*);
  • additional flags, written as they were passed to the TCL commands for creating the node. Tehy are typically empty (*), but in this example, the options for the nodes which will contain the acquired signals are /compress_on_put/nomodel_write, i.e. data is compressed when written and it is not possible to write in these nodes in an experiment model (that is, they are expected to contain only acquired data, not setup configuration);
  • the returned node reference

A sequence of DevAddNode() function calls specify how the nodes are created to instantiate the device subtree. The only subtlety is that instead of barely repeating DevAddNode() calls for every channel, this is achieved in a for loop and the current channel is built by concatenating the current path and the channel number via function TEXT(_c, 1) (otherwise, blanks would be inserted between the string and the number).
Finally function DevAddAction() will insert and action node into the tree being edited. Its arguments are:

  • The path name of the node being created;
  • The name of the phase in which this action has to be called by the dispatcher (in this case phases and methods happen to have the same name);
  • The name of the method (INIT or STORE)
  • The sequence number for this action within the specified phase;
  • The name of the server class which is expected to execute such method;
  • The full pathname of the device root;
  • The returned node reference.


Once DEMOADC__add. fun has been written and put in the appropiate tdi subdirectory, MDSplus becomes aware of this new device type. You can add a new device instance when editing a tree with the following TCL command:

TCL> add node <Device PATH>/model = <device type>

for example you can add a new instance of DEMOADC device whose root is name DEMO with the following command:

TCL> add node DEMO/model=DEMOADC

Alternatively, you can do the same thing with jTraverser after opening an experiment model in edit mode, via the popup command "Add Device". You need to type the device root path name and the device type in the dialog which gets activated.
As a final remark, observe that creating new device instances is an operation which occurs seldomly. In the normal operation, the experiment model defines the pulse file structure, and a new pulse file is created simply by copying the experiment model and in this case device construciors are not activated. They are only involved when changing the experiment model structure, e.g. for adding new components in our experiment description.