489 lines
22 KiB
HTML
489 lines
22 KiB
HTML
<TITLE>GDD User's Guide</TITLE>
|
|
<HR><P><img src="gdd.gif">
|
|
<P><H1>General Data Descriptor Library User's Guide</H1>
|
|
|
|
<P><HR><H2>General Overview</H2>
|
|
The purpose of the General Data Descriptor Library (GDD library) is to
|
|
fully describe, hold, and manage scalar and array data. Using a GDD,
|
|
you can describe a piece of data's type, dimensionality, and structure.
|
|
In addition you can identify the data with an integer application type value
|
|
which can be translated into a text string. A user can store data into and
|
|
retreived data from a GDD in any desired data type. A GDD can contain
|
|
a list of GDDs. This allows users to create and describe hierarchical
|
|
data structures dynamically. GDDs organized in this fashion are known
|
|
as containers.
|
|
<P>
|
|
As mentioned above, a GDD can describe an n-dimension array. The user
|
|
can describe the bounds of the n-dimensional array. To facilitate the
|
|
use of large, and perhaps shared data arrays, a GDD allows a user to
|
|
store a reference to an array of data. In addition, a destructor function
|
|
can be registed in the GDD to inform the owner of the data array when
|
|
the GDD referencing the data goes away. The purpose of the destructor
|
|
function is to delete the array of data, since the GDD does not know
|
|
what to do with referenced data.
|
|
<P>
|
|
To manage GDDs in a multi-tasking system or a system that
|
|
uses many layers of software, GDDs implement reference counting. With
|
|
reference counting, only one instance of a GDD can be shared by many subsystems.
|
|
The GDD creator function can pass it to many other functions without
|
|
worrying about deleting it, only when the last function using the GDD requests
|
|
that it be destroyed does it really go away.
|
|
<P>
|
|
As menitioned above, a GDD allows the user to describe the data in terms
|
|
of the application. This is done by the user by assigning an arbitrary
|
|
integer identifier to a GDD. The user places a meaning on the identifiers
|
|
such as 1=high-alarm-limit, 2=low-alarm-limit. This identifier is termed
|
|
application type. A second component of the GDD library known as the
|
|
Application Type Table is used to manage the application type identifiers.
|
|
Application type values are registered in the table along with a text
|
|
string and optionally a prototype GDD. The prototype GDD can be a
|
|
container GDD. The table allows users to retreive GDDs of a specific
|
|
application type.
|
|
<P>
|
|
A GDD describes and manages a piece of data using the following information:
|
|
<P>
|
|
<UL>
|
|
<LI>Application Type (user assigned integer value)
|
|
<LI>Primitive Type (storage type identifier - int/double/short/etc.)
|
|
<LI>Dimension (of array data)
|
|
<LI>Bounds (array data structure information)
|
|
<LI>Destructor (to delete referenced array data)
|
|
<LI>Time Stamp (last time updated)
|
|
<LI>Status (user assign data status field)
|
|
<LI>Reference Count
|
|
<LI>Data Field
|
|
</UL>
|
|
|
|
<P>
|
|
The GDD library is a C++ class library and therefore requires using the
|
|
C++ compiler.
|
|
|
|
<P><HR><H2>GDD Description</H2>
|
|
The gdd class is the main class in the GDD library. It controls almost
|
|
all actions performed on the data. The gdd class is further broken down
|
|
into three subclasses for performing operations on specific types of GDDs:
|
|
gddContainer, gddAtomic, and gddScalar. The gddContainer class aids in the
|
|
creation of container type GDDs. It adds functions such as "insert GDD",
|
|
"remove GDD", and "give me GDD x from the container" to the basic gdd class.
|
|
The gddAtomic class helps create and manage array data GDDs. The gddScalar
|
|
class makes it easy to create scalar GDDs.
|
|
<P>
|
|
All GDDs must be created dynamically, <B>a GDD cannot be created on the
|
|
stack</B> as a local variable. The gdd class forbids the user from deleting
|
|
the gdd. In order for reference counting to work correctly the <B>user must
|
|
"unreference" the gdd instance instead of deleting it</B>. The gdd class does
|
|
take over the memory management routines for itself and all it's subclasses.
|
|
This means that you are not using the malloc()/free() memory management when
|
|
GDDs are created and destroyed. It is important to remember that since
|
|
reference counting is used, <B>a GDD passed into a function must be referenced
|
|
before the function returns if the GDD is to be kept </B>for longer then the
|
|
running of that function. In other words, if you are creating a library
|
|
function "add" that records process variable names in a linked list, and the
|
|
process variable names are passed to you as GDDs, then you must reference
|
|
the GDD since the linked list exists after the return of the "add" function.
|
|
If you are creating a GDD, you must unreference it when you are finished
|
|
with it, even it you have passed it into other library functions.
|
|
Generalizing on this, <B>it is the responsibility of the GDD creator or
|
|
GDD referencer to unreference the GDD instance</B> when they are finished
|
|
with it.
|
|
<P>
|
|
For a GDD to be useful after it is created, it must be given information
|
|
to describe the data to will hold. This data was summarized in the overview
|
|
section. To further understand the meaning of all these items and use
|
|
them correctly, each needs to be discussed.
|
|
|
|
<P><HR><H3>Primitive Types</H3>
|
|
As mentioned in the overview, a piece of descriptive information that a
|
|
GDD maintains is the primitive type code. This field
|
|
describes the format of the data (storage type).
|
|
The primitive type field of a GDD is an enumeration
|
|
of all the general storage types supported by the compilers. A user can
|
|
determine dynamically what the storage format of the data in a GDD is using
|
|
the primitive type code information. The GDD library redefines the general
|
|
storage class names for integers and floating point numbers and enumerates
|
|
them in the "aitTypes.h" header file. The redefined names describe the
|
|
format and the bit length so that they can be used across architectures.
|
|
The initial part of the header file name "aitTypes.h" is ait which
|
|
stands for "Architecture Independant Types". The standard data types
|
|
supported by the GDD library are
|
|
<PRE>
|
|
aitInt8 8 bit character
|
|
aitUint8 8 bit unsigned character
|
|
aitInt16 16 bit short
|
|
aitUint16 16 bit unsigned short
|
|
aitEnum16 16 enumerated value
|
|
aitInt32 32 bit integer
|
|
aitUint32 32 bit unsigned integer
|
|
aitFloat32 32 bit floating point number
|
|
aitFloat64 64 bit floating point number
|
|
aitPointer Standard pointer
|
|
aitIndex 32 bit index value
|
|
aitStatus 32 bit unsigned integer for status value
|
|
aitFixedString 40 byte string of characters
|
|
aitString Variable length string data type
|
|
aitTimeStamp Two 32 bit integers describing time (seconds/nanoseconds)
|
|
</PRE>
|
|
These data types should be used whenever possible to prevent problems when
|
|
compiling programs for different architectures. Most of the data types
|
|
described above are enumerated for use as a primitive type code. The
|
|
enumerated names are just the above type names with the word "Enum"
|
|
inserted after "ait". It should be noted that aitTimeStamp is not a
|
|
standard primitive type.
|
|
<PRE>
|
|
typedef enum {
|
|
aitEnumInvalid=0,
|
|
aitEnumInt8,
|
|
aitEnumUint8,
|
|
aitEnumInt16,
|
|
aitEnumUint16,
|
|
aitEnumEnum16,
|
|
aitEnumInt32,
|
|
aitEnumUint32,
|
|
aitEnumFloat32,
|
|
aitEnumFloat64,
|
|
aitEnumFixedString,
|
|
aitEnumString,
|
|
aitEnumContainer
|
|
} aitEnum;
|
|
</PRE>
|
|
The enumerated type code allows a user to dynamically convert from one
|
|
type to another. The AIT portion of the GDD library contains a large
|
|
primitive type conversion matrix. The conversion matrix is indexed by
|
|
the source and destination enumeration type codes. The 12x12 matrix
|
|
contains functions pointers for each type of conversion that can take
|
|
place. Generally this matrix is never accessed directly by the user,
|
|
a convert function:
|
|
<PRE>
|
|
void aitConvert(aitEnum dest_type, void* dest_data,
|
|
aitEnum src_type, const void* src_data,
|
|
aitIndex element_count)
|
|
</PRE>
|
|
runs the correct function to perform the calculation. This function is used
|
|
extensively in the gdd class when data is put into or retrieved from a GDD.
|
|
<P>
|
|
The primitive type code really only describes the storage format and length
|
|
of an element within a GDD. This code does not imply or assign any meaning
|
|
to the data. Assigning meaning to the data is the job of the application
|
|
type code.
|
|
|
|
<P><HR><H3>Application Types</H3>
|
|
The application type is a 32 bit integer value that is user assigned. Each
|
|
value is arbitrary and is designed to give meaning to the chunk of data.
|
|
Normally each value has a character string associated with it which can
|
|
be looked up through a standard hash table. The GDD library predefines
|
|
many values and structures for standard control system applications;
|
|
this portion of the library will be dicussed in detail later in this document.
|
|
<P>
|
|
A typical way that application type codes are used is in defining structures.
|
|
A type code of 54 may be assigned the meaning "Temperature Reading". The
|
|
"Temperature Reading" structure may be a container with four GDDs in it:
|
|
a value, a high alarm limit, a low alarm limit, and units. Each
|
|
of these GDDs also have an application type code. A generic program can
|
|
actually be written to completely discover the contents of the
|
|
"Temperature Reading" structure and configure itself to display meaningful
|
|
data.
|
|
|
|
<P><HR><H3>Time Stamp and Status</H3>
|
|
Each GDD can store a time stamp and status value. The time stamp is a
|
|
standard 32-bit seconds, 32-bit nanoseconds since a standard epoch. The
|
|
status is a 32-bit value that is user assigned. The purpose of the time
|
|
stamp is to reflect the time when the data held within the GDD was read
|
|
and stored. The status is intented to reflect the validity of the data
|
|
within the GDD. In the context of EPICS, the status field is really a
|
|
16-bit status and 16-bit severity code.
|
|
<P>
|
|
Storing time stamp and status information in each GDD was really a processing
|
|
versus storage compromise. Most data in a control system that is
|
|
constantly changing and needs to be transfered in GDDs requires a status
|
|
and time stamp. Transfering this type of data in one GDD in fairly easy.
|
|
If GDDs did not contain time stamps and status, then this actively changing
|
|
data would need to be transfered as a structure of three GDDs: one for
|
|
the value, one for the status, and one for the time stamp. In addition
|
|
to the three GDDs, a fourth that describes the GDD container will need to
|
|
be transfered.
|
|
|
|
<P><HR><H3>Dimension and Bounds Information</H3>
|
|
A GDD that is a scalar is self contained. All information to describe
|
|
the data within the GDD is contained within the GDD. If the GDD describes
|
|
a structure (a container), or the GDD describes an array, then dimension
|
|
and array bounds information are needed. A GDD can describe an array
|
|
of any dimension. Each dimension is required to have a description
|
|
of it's size in terms of bounds.
|
|
<P>
|
|
Bounds, and consequencely a single dimension, in GDD are described by two
|
|
fields, a start and an element count. With this setup, and n-dimensional
|
|
space can be described. For example, a typical three dimension array
|
|
would be described as A(5,4,6), which is a 5x4x6 cube. In a GDD this
|
|
cube would be described with dimension=3, bounds={(0,5)(0,4)(0,6)}. If
|
|
we want to describe a subset of this array, we would normally do so by
|
|
giving two endpoints of the sub-cube, such as (1,2,3)->(2,3,5). In GDD
|
|
terms, this would be bounds={(1,2),(2,3),(3,5)}.
|
|
<P>
|
|
The dimension information is stored directly within the GDD. The bounds
|
|
information is not. If bounds are required, then they are allocated as
|
|
a single dimensional array and referenced by the GDD. Methods exist in
|
|
the GDD class to automatically manipulate, allocate, and deallocate
|
|
bounds structures. Typically GDD are assigned a dimension when created
|
|
and do not morph into a different space.
|
|
|
|
<P><HR><H3>Data Field</H3>
|
|
A GDD contains a data field large enough to hold any scalar value or
|
|
a pointer. This field is the union of all the primitive data types.
|
|
Currently it takes up 8 bytes, which is the size of a double. If a GDD
|
|
refers to an array of data, then the pointer to the actual data is stored
|
|
in the data field. If the dimension is greater then zero, then the data
|
|
field references the actual data.
|
|
|
|
<P><HR><H3>Destructor</H3>
|
|
Since GDDs can reference arrays of data, the user can optionally register
|
|
a destructor to be called when the GDD is destroyed. The GDD can store
|
|
a reference to a user destructor. The GDD library contains a destructor
|
|
base class called gddDestructor. A "destroy" method exists in this class
|
|
that is called when the GDD is being destroyed. Users must derive a class
|
|
from gddDestructor and define a "run" method. The "run" method gets
|
|
invoked with the address of the data array. The user, in the "run"
|
|
method, casts the address to the appropriate data type and deletes
|
|
the array. The default behavior of the gddDestuctor if the user does not
|
|
override the "run" method will be to cast the data array to a
|
|
character array and delete it.
|
|
<P>
|
|
The gddDestructor allows reference counting similar to the GDD class.
|
|
Typically a data array will be associated with one instance of the
|
|
gddDestructor class. If more then one GDD needs to reference the array,
|
|
then each GDD is registered with the same gddDestructor. Each time
|
|
the gddDestructor is registered, the reference count should be bumped
|
|
up. The gddDestructor "run" method will only be invoked when the
|
|
reference count drops to zero.
|
|
|
|
<P><HR><H3>Strings and Fixed Strings</H3>
|
|
Strings are special cases in the GDD library. There is a class called
|
|
aitString designed to work with and manage strings. Strings is the GDD
|
|
library generally contain two components: a reference to a character
|
|
array and a length. Storing a string in a GDD always results in a
|
|
reference to a character array, even if the GDD is a scalar. Character
|
|
strings are first put into an aitString and then inserted into the GDD.
|
|
The GDD data field can hold an instance of the aitString class for use
|
|
when the GDD is a scalar with one string in it. A scalar string GDD
|
|
still references the actual string within the aitString class.
|
|
<P>
|
|
A fixed string class exists in the GDD library in order to support
|
|
certain features of EPICS. Fixed strings are used internally and
|
|
should generally not be used when creating and maniplulating strings with
|
|
the GDD library. Fixed strings are too big to fit into a GDD and also
|
|
always referenced, even if the GDD is a scalar.
|
|
|
|
<P><HR><H3>GDD Summary</H3>
|
|
Several important issues related to where the actual data is stored
|
|
must be remembered when using GDDs:
|
|
|
|
<OL>
|
|
<LI>If a gdd is a scalar, then the gdd holds the data it describes, the
|
|
dimension is zero, and the bounds are empty.
|
|
<LI>If a gdd is an array, then the gdd refers to the data it describes,
|
|
refers to bounds that describe it's structure, has a dimension
|
|
greater then zero, and optionally
|
|
refers to a user's data destructor.
|
|
<LI>If a gdd is a container, then the dimension is fixed at one and the
|
|
bounds describe how many elements (GDDs) are in the container.
|
|
The destructor in the container case knows how to free up all the
|
|
GDDs in the container.
|
|
</OL>
|
|
|
|
<P><HR><H2>Creating and Using GDDs</H2>
|
|
<PRE>
|
|
#include "gdd.h"
|
|
.
|
|
.
|
|
// my destructor ------------------
|
|
class myDest : public gddDestructor
|
|
{
|
|
public:
|
|
myDest(void) : gddDestructor() { }
|
|
void run(void*);
|
|
}
|
|
|
|
void myDest::run(void* v)
|
|
{
|
|
aitInt16* i16 = (aitInt16*)v;
|
|
delete [] i16;
|
|
}
|
|
// --------------------------------
|
|
.
|
|
.
|
|
.
|
|
int app_type_code = 100;
|
|
|
|
// create a scalar GDD of type Int32 and put the value 5 into it
|
|
aitInt32 ival = 5;
|
|
aitFloat64 sval;
|
|
gdd* dds = new gddScalar(app_type_code,aitEnumInt32);
|
|
dds->put(ival);
|
|
dds->getConvert(sval);
|
|
dds->dump();
|
|
|
|
aitString str = "test string";
|
|
gdd* dd_str = new gddScalar(++app_type_code,aitEnumString);
|
|
dd_str->put(str);
|
|
printf("string length = %n",str->length());
|
|
dd_str->dump();
|
|
|
|
// create an array GDD and of dimension 1 and bound of 20 elements
|
|
// reference the array into the container
|
|
aitUint32 tot_elements = 20;
|
|
int dim = 1;
|
|
aitFloat64 a[20];
|
|
gdd* dda = new gddAtomic(++app_type_code,aitEnumFloat64,dim,&tot_elements);
|
|
dda->putRef(a);
|
|
dda->dump();
|
|
|
|
aitInt16* i16 = new aitInt16[tot_elements];
|
|
gdd* ddb = new gddAtomic(++app_type_code,aitEnumInt16,dim,&tot_elements);
|
|
ddb->putRef(i16,new myDest);
|
|
ddb->dump();
|
|
|
|
// create a container GDD that holds to GDDs and put the previously
|
|
// created GDDs into it.
|
|
int tot_in_container = 2;
|
|
gddContainer* ddc = new gddContainer(++app_type_code,tot_in_container);
|
|
ddc->insert(dds);
|
|
ddc->insert(dda);
|
|
ddc->dump();
|
|
|
|
// clean up the container GDD, this will clean up all member GDDs,
|
|
// myDest run() should also be invoked
|
|
delete ddc;
|
|
.
|
|
.
|
|
.
|
|
</PRE>
|
|
|
|
<P><HR><H2>Application Type Table Description</H2>
|
|
A facility within the GDD library is designed to store application
|
|
type code to string mappings. The facility can also be used to store
|
|
and retrieve prototype GDDs by application type code. The application
|
|
type table is really a simple database containing records with the following
|
|
information:
|
|
<UL>
|
|
<LI>Application type code (32-bit integer)
|
|
<LI>Application type name (variable length character string)
|
|
<LI>Prototype GDD (gdd scalar, atomic, or container)
|
|
<LI>Indexing maps
|
|
</UL>
|
|
The type table has methods for registering or inserting records into the
|
|
database. Methods also exist to query the type name given a type code, and
|
|
to query the type code given the type name. Type codes are values
|
|
assigned to a type name when the type name is registered; the user is given the
|
|
type code value when the registeration takes place. A type code sometimes has
|
|
a prototype GDD associated with it, especially if it is a container GDD.
|
|
Methods also exist to retrieve a new GDD given an application type code. The
|
|
new GDD structure will be a copy of the prototype GDD for that type code.
|
|
The application type table
|
|
is commonly used create GDDs. A given application type code usually
|
|
implies a certain structure to the data. The prototype mechanism allows
|
|
this imposed structure to be adhered to when the GDD is created. This is
|
|
particularly important when it comes to container type GDDs. The type
|
|
table basically copies the prototype when the user requests a GDD with a
|
|
particular type code. The application type table also packs GDDs in
|
|
an efficient manage, which allows very high performance creation and
|
|
deletion of container GDDs and atomic GDDs.
|
|
<P>
|
|
GDDs in general have several components that are references, they do not
|
|
hold all the information they need. Creating and using array GDDs or
|
|
container GDDs
|
|
can be very time consuming. For an array GDD, the bounds and a destructor
|
|
must be allocated in addition to the GDD itself and referenced into the
|
|
GDD. For a container GDD, the GDD elements are really stored as a linked
|
|
list. To access the third element of a container, the linked list must be
|
|
traversed. With aitString GDDs the situation is worse yet. The GDD class
|
|
allow for a given GDD to be packed or flattened into a single linear buffer.
|
|
This mechanism includes packing GDD containers. In the array case,
|
|
the actual GDD is the first thing in the buffer, followed by any bounds
|
|
information, followed by the actual data array. The fields of the GDD
|
|
work exactly as before, except that they reference bounds and data that is
|
|
in the same buffer. In the case of containers, all the GDDs are stored as
|
|
an array of GDDs at the front of the buffer, followed by the bounds
|
|
information for each of the GDDs, followed by each of the GDD's data arrays if
|
|
present. There are many advantages of this configuration. Since all the
|
|
GDDs in a container are stored as an array instead of a linked list, the
|
|
user can directly index a particular GDD within the container. The
|
|
application type table performs this packing on a GDD that is registered
|
|
as a prototype for a particular type code. Since the GDD for a given
|
|
type code is now a fixed size, the type code table can manage the GDDs on
|
|
a free list. Each type code database entry with a prototype GDD contains
|
|
a free list
|
|
of GDDs for that type code. Creating a GDD using the type code table
|
|
involves retrieving a preallocated, packed GDD from a particular free list
|
|
and giving it to the user. This operation is very fast and efficient since
|
|
complex GDD structures are already constructed.
|
|
<P>
|
|
As stated above, container GDDs managed by the application type table can
|
|
be directly indexed as an array by the user. Unfortunetly the application
|
|
type code and indexes have no correlation - you cannot use the application
|
|
type code to index the GDD container. The type table has mapping functions
|
|
that convert between an application type code and an index into a container
|
|
GDD. Of course each type code maintains it's own index map and the
|
|
container GDD type code determines which mapping is to be used. A mechanism
|
|
exists in the GDD library for generating "#define" statements that
|
|
label container index values with a unique string. Preregistered containers
|
|
use this mechanism to generate indexing labels. The index label is a
|
|
concatenation of the type code names. If a container GDD type code name is
|
|
"TemperatureReading" and the first element is "Value", then the index
|
|
label generated will be "TemperatureReading_Value". At any time in a
|
|
running application, the user can request that index labels for all
|
|
registered prototypes be generated and dumped into a file.
|
|
|
|
The library preregisters a number of application type names:
|
|
|
|
<UL>
|
|
<LI>"units"
|
|
<LI>"maxElements"
|
|
<LI>"precision"
|
|
<LI>"graphicHigh"
|
|
<LI>"graphicLow"
|
|
<LI>"controlHigh"
|
|
<LI>"controlLow"
|
|
<LI>"alarmHigh"
|
|
<LI>"alarmLow"
|
|
<LI>"alarmHighWarning"
|
|
<LI>"alarmLowWarning"
|
|
<LI>"value"
|
|
<LI>"enums"
|
|
<LI>"menuitem"
|
|
<LI>"status"
|
|
<LI>"severity"
|
|
<LI>"seconds"
|
|
<LI>"nanoseconds"
|
|
<LI>"name"
|
|
<LI>"all"
|
|
<LI>"attributes"
|
|
</UL>
|
|
In addition, if EPICS is used then the following are registered:
|
|
<UL>
|
|
<LI>"dbr_gr_short"
|
|
<LI>"dbr_gr_float"
|
|
<LI>"dbr_gr_enum"
|
|
<LI>"dbr_gr_char"
|
|
<LI>"dbr_gr_long"
|
|
<LI>"dbr_gr_double"
|
|
<LI>"dbr_ctrl_short"
|
|
<LI>"dbr_ctrl_float"
|
|
<LI>"dbr_ctrl_enum"
|
|
<LI>"dbr_ctrl_char"
|
|
<LI>"dbr_ctrl_long"
|
|
<LI>"dbr_ctrl_double"
|
|
</UL>
|
|
|
|
The file gddApps.h contains all the index label defines for the EPICS
|
|
related GDD containers.
|
|
|
|
<P><HR><H2>Using the Application Type Table</H2>
|
|
|
|
<HR><P><A HREF="gddref.html">GDD Reference Manual</A>
|
|
<HR>Home page for <A HREF="http://www.aps.anl.gov/asd/controls/hideos/jimk.html">Jim Kowalkowski</A>.<BR>
|
|
|
|
<HR>Argonne National Laboratory
|
|
<A HREF="http://www.aps.anl.gov/asd/controls/epics_copyright.html">Copyright</A>
|
|
Information<BR>
|
|
<ADDRESS>jbk@aps.anl.gov (Jim Kowalkowski)</ADDRESS> updated 9/13/96<BR>
|
|
|