The Generic API is divided into two layers, the TCL layer and the C++ layer. the TCL layer is what the end-user "sees" -- they write code which the TCL layer executes. The C++ layer, on the other hand, is available only indirectly.
TCL provides a mechanism for extending the command-set by writing functions in C or C++. These functions are loaded into TCL as a shared object using the package requires or load command. When this is done, the extended commands become available. The C++ layer consists of these extended commands and the memory space which they use to store and manipulate data.
In order to create C or C++ functions which TCL can understand, one must write them in a special format. In LDAS, this is handled by a program known as SWIG (Simplified Wrapper and Interface Generator). The SWIG program uses 1 or more SWIG interface files to create a set of C functions which TCL can understand. These functions are written to a source file (.c or .cc) which is then compiled into the shared object.
This document is divided into 6 areas:
The generic API creates a variety of objects in the C++ layer which need to be manipulated by TCL. In order to do so, each of these objects must have a unique label. The pointer value of the object in the C++ layer is used as the label. This value is conveted by SWIG into an ascii representation of the pointer with the format:
_xxxxxxxx_classname_p
where 'xxxxxxxx' is a hexadecimal number representing the pointer value (it may have fewer than 8 characters) and 'classname' is the name of the class. For example, a pointer to an ObjectSpace TCP Socket class (os_tcp_socket) may appear as:
_80afc18_os_tcp_socket_p
Error messages contain 3 parts:
The general format for an error message is:
ldas_exception: error information [filename:line]
or
ldas_exception [filename:line]The colon after the label is omitted if there is no extra error information.
Each exception which is thrown by a generic API command will consist of one or more error messages, concatenated together. The label and information (if it exists) for the most recent message will be returned by the function. For example:
illegal_format: 'asci' format unknown
or
invalid_element
The remaining error messages and location information can be obtained via the 'errorInfo' TCL variable. For example, a 'puts $errorInfo' command might return:
illegal_format: 'asci' format unknown [genericAPI.cc:1045]
invoked from within
"set e1 [putElement $bstring asci]"
...
or
illegal_element [LdasElement.cc:415]
invalid_attribute: 'formatt' attribute unknown [LdasElement.cc:719]
invoked from within
"set e1 [putElement "<real_4 formatt='ascii'>1.34</real_4>"]"
...
The generic API instantiates many objects in the C++ layer. Pointers to these objects are returned to the TCL layer, where they are used as parameters in other generic API function calls. This creates two problems:
In order to prevent this from happening, the Generic API uses a utility class called 'ObjectRegistry'.
The ObjectRegistry is a templated class which keeps track of dynamically instantiated objects. Whenever the generic api creates an object, the pointer to this object is registered with the 'ObjectRegistry' class. When an object is destructed, the reference is removed from the class. Every time a method is called which takes a pointer to an object as a parameter, the code checks to see whether or not this object exists in the object registry. If the object does not exist, then an exception is thrown ( e.g., 'invalid_socket' or 'invalid_server' ). If the object does exist, then the method continues.
The ObjectRegsitry object is instantiated as a global variable in the genericAPI.cc file. When the genericAPI shared object is loaded, this object is instantiated. When the shared object is unloaded, the destructor for this object is called. The ObjectRegistry destructor destructs all of its referenced objects.
The reset extended TCL command causes all ObjectRegistry objects to destruct their contents. This ensures a 'clean slate' and restores the API to its original state. This will also reset other loaded API's which have registered themselves with this API.
The save command writes all ObjectRegistry objects to a file.
The restore command reads ObjectRegistry objects from a file.
The Generic API has three commands which are supposed to affect not only itself, but any other API's which have been loaded into the TCL interpreter. These commands are reset, save, and restore.
In order for these commands to execute properly, the Generic API must know about all of the loaded API's. This is done through the Api Store.
The Api Store is a C++ class (ApiStore) which stores "proxies" (ApiProxy objects) which contain information about a loaded API. A proxy stores:
When an API is loaded, it registers its proxy with the API store. The Generic API reset, save, and restore functions then access the API store in order to call the appropriate function in the other API's. The API is registered by simply creating a static instance of an ApiProxy object in the shared object, for example:
namespace
{
Registry::ApiProxy frameApiProxy(
"Frame", &resetFrameApi, &saveFrameApi, &restoreFrameApi );
}
The Internal Light-Weight Data Format is a subset of XML. It is designed to consist of the minimum number of elements needed to move data through sockets and between the TCL and C++ layers in the LDAS API's. It is primarily meant to be a machine oriented data format, however it is also meant to be viewable in ASCII form without requiring a special browser.
There are four different types of ILWD elements:
Array elements are used to store multi-dimensional numerical data. They possess the following tag names:
Array elements also possess the following attributes:
A string element is used to store a list of ASCII strings. The tag for a string element is lstring.
If there is more than one string in the element, the strings are delimited by a "\," (backslash-comma) character sequence. Backslashes appearing within a string must be escaped with another backslash.
Strings possess the following attributes:
A container element is used to store other elements, including other containers. The tag for a container element is ilwd,
A container possesses the following attributes:
An external element is used to hold information which is not in the ILWD format. The contents of an external element is completeley ignored when parsing. The "size" attribute is used to skip over the data. The tag for an external element is external.
An external element possesses the followin attributes:
The Generic API binary commands implement the following features:
The Generic API element commands implement the following features:
One of the responsibilities of the Generic API is the manipulation of data in the Internal Light-Weight Data (ILWD) format. The purpose of the ILWD format is to store information in a way which enables the user to easily view and interpret the data. The format chosen for ILWD is very similar to XML.
When an ILWD object is written in "XML" form, it can have three different formats:
When ILWD data is stored as an object in memory, it is stored in its uncompressed binary form. For example, the data for an array element such as <real_4> will be stored as a normal C array of type REAL_4 (REAL_4 is normally a float). When it is time to convert the element to XML form (for example, when writing to a file) the user specifies what format and compression he or she wants.
However, sometimes the user may want to write a container object where the contained elements have different formats. For example, we might have:
<ilwd name='strain' size='2'>
<int_4u name='time' comment='GPS time in seconds' format='ascii'>45981475</int_4u>
<real_8 name='data' dims='1024' format='base64' compression='gzip'>A5Bas391 ... </real_8>
</ilwd>
In this example the user way want to be able to read the time at which the data was taken, but they may not need to have the data in readable form.
In order to support this, each element object can also store a write format and compression. These specify the default format and compression which the element should use when writing itself. These attributes are set via the setWriteFormat command. The current defaults can be obtained via the getWriteFormat command. In order to write an element using the default format or compression, the user can use a format of "user" and a compression of "user". These are the defaults for the commands which write an element in XML form (getElement, writeElement, and sendElementAscii). When an element is read into memory from its XML form, the format and compression used become the defaults. Functions which do this are putElement, readElement, and recvElementAscii.
Along with the data, the element stores a default write format and compression.
The Generic API socket commands implement the following features:
The Generic API utility commands implement the following features:
1.5.4