# SUBSYSTEM OVERVIEW

Model Access (MDA) Subsystem

The (MDA) subsystem allows an application to create instances of entities defined in a DDF. A collection of these entity instances is called a model. An application can have several models open at once, each with its own DDF. Each entity has an ID which uniquely identifies it and the model it is defined in. The MDA subsystem swaps entities to and from disk so that the total model size is limited only by available disk space.

Entities are represented as C data structures within an application. These entity structures are swapped in and out of memory as they are needed by the application. An application accesses fields within the entity by dereferencing a structure pointer to member fields.

The physical storage of entities consists of two levels. The first level is a memory-resident cache which contains entity structures. The entity cache allows an application to repeatedly access an entity without it being swapped to and from disk. The second level provides backing for the entity cache and consists of a page-buffered disk file called the entity swap file.

An application can also create entity structures which do not reside in the database. This allows an application to take advantage of the MDA subsystem's memory management without actually creating entities in the model. These entities can then be committed to the database at a later time to make them permanent.

A directory of entities is maintained for each model which contains information about each entity such as its type, current location, and status. This entity directory is entirely memory resident and is the only data structure in the system which is limited in size by the operating system's virtual memory size. Since each entity directory entry typically uses only 16 bytes, most systems should be capable of a nearly unlimited number of objects. In reality, the true limit is the maximum file size supported by the system, typically 2^32-1 bytes.

# DATABASE POINTERS

Each entity is identified by an unsigned 32 bit value called its database pointer. Database pointers are declared using the MDA_PTR data type. A database pointer of MDA_NULL_PTR indicates a null entity reference (similar to a NULL pointer in the C language). The database pointer is composed of two pieces. The first of portion is the model number which the entity is defined in. The second portion is an index into the entity directory data structure for that model. Each entity structure which has been created in the model has its database pointer stored within it. This allows an application to retrieve the database pointer of an entity from the entity structure (see the function mda_GetDbptr). Note that the database pointers assigned to deleted entities are reused.

# MODELS

A model defines a collection of entities which are specified in a single DDF. The application can have up to 64 models open concurrently (or fewer depending on how many file descriptors the application uses, each open model requires one file descriptor). Before entities may be created or accessed, a model must be defined to contain them. The call sequence to do this is:

    #include "mda_Interface.h"
    #include "app_ddf.h"     /*  Application DDF  */

       MDA_MODEL_INFO m_info;
       int model;
            .
            .
       mda_GetModelDefaults (MDA_NOOPT, &m_info);
       m_info.ddf = &app_ddf;      /*  Set the DDF pointer  */
            .
    /*  Other m_info fields set  */
            .
       mda_AllocateModel (MDA_NOOPT, &m_info, &model);

The MDA_MODEL_INFO structure contains all of the information which an application can specify about a model. The manual page for mda_GetModelDefaults describes each field in the structure which the application may change. The mda_GetModelDefaults function sets each field in the structure to an acceptable default value. The application can then change these values as needed. One field which the application must set is the DDF pointer. All other fields in the structure may be left at their default values. Another field which the application may wish to set is the backptrs field. Setting this enables bi-directional pointers between entities. This capability is described in detail under the BACKPOINTERS section. Some of the MDA_MODEL_INFO fields allow an application routine to be hooked into an MDA subsystem routine. For example, setting the "setcur" member of the structure to point to an application routine will cause that application routine to be called whenever mda_SetCurModel is called with that model as the argument. Other fields in the structure allow an application to specify its own routine in place of a generic MDA routine. For example, setting the "copy_ent" field to point to an application routine will cause that routine to be used when mda_CopyEntity is called (instead of the normal MDA subsystem copy).

A model may be closed at any time by calling mda_FreeModel. All information in the model will be discarded and the model will be made available for re-use. Note that pdx_Terminate will automatically free any open models.

# CREATING ENTITIES

Once a model has been defined, an application may proceed to populate it with entities. The MDA subsystem supports two methods of creating entities. The first allows an application to generate a temporary copy of the entity without committing it to the model until it is completed (thus making it easy to back out of creating an entity by simply not writing the entity to the model). The second method creates an entity in the model and locks it in memory so that the application may add information to it when appropriate. The call sequence to create an entity using the first method is:

    #include "mda_Interface.h"
    #include "app_structs.h"

       T_CIRCLE *circle;
       MDA_PTR  db_ptr;
           .
           .
       mda_MallocEntity (MDA_NOOPT, CIRCLE, &circle);
       circle->center[0] = 1.0;
       circle->center[1] = 1.0;
       circle->radius = 5.0;
       mda_CreateEntity (MDA_NOOPT, circle, &db_ptr);

This code sequence creates an instance of a circle entity in the current model. Once the function mda_CreateEntity has been called the entity fields can not be accessed without first locking the entity (describe below). Note that by not calling mda_CreateEntity the entity will not be written to the model. In this case the function mda_FreeEntity must be called to deallocate memory associated with the entity. Using mda_MallocEntity in conjunction with mda_FreeEntity allows an application to take advantage of the entity structures defined in the DDF and the memory management facilities of the database without actually creating entity instances in the model.

The following code shows an example of using the second creation method:

    #include "mda_Interface.h"
    #include "app_structs.h"

       T_CIRCLE *circle;
       MDA_PTR  db_ptr;
           .
           .
       mda_AllocateEntity (MDA_NOOPT, CIRCLE, &circle, &db_ptr);
       circle->center[0] = 1.0;
       circle->center[1] = 1.0;
       circle->radius = 5.0;
       mda_UnlockEntity (MDA_NOOPT, db_ptr);

The mda_AllocateEntity call creates an instance of the entity in the model and returns the location of the entity in memory. The entity fields are then set in the same manner as the first creation method. Once the entity fields have been set it is unlocked using mda_UnlockEntity.

Both methods generate the same entity, the choice of which to use depends on the application. Regardless of which method is used, the entity fields do not have to be completely defined before calling mda_CreateEntity or mda_UnlockEntity. Note that the function mda_TraverseEntity (described below) should not be called on an incomplete entity.

When an entity is initially allocated, its contents will be set to 0s. The function mda_DefaultEntity can be called to initialize the contents to the default values as specified in the DDF. For fields that did not have a default value specified, the appropriate form of 0 will be stored depending on the field type.

In some instances, it may be desirable to allocate a database pointer to serve as a place holder for an entity which will be filled in at a later time. The function mda_ReserveEntity returns a database pointer which can be later allocated using mda_AllocateEntity or mda_CreateEntity.

Most of the function mentioned above take options which change the default behavior of how entities are created. The function man pages describe these options in detail.

Entities which contain variable arrays require memory to be allocated before the entity can be completed. These arrays may be either "sized" or "unsized". In the former case, one or more count fields within the entity structure define the size of the array. In the latter case, the array size is determined by how much memory is allocated to the array. Both types of arrays are allocated and manipulated the same way. The MDA subsystem provides counterparts to the standard C memory allocation functions malloc, calloc, realloc, free, and strdup. These functions are named mda_Malloc, mda_Calloc, mda_Realloc, mda_Free, and mda_Strdup. The following code shows how an array of points would be declared using the mda_Malloc function:

    #include "mda_Interface.h"
    #include "app_structs.h"

      /*
          The polyline entity is defined as:
             entity 1:0 POLYLINE {
                count npoints;
                float points[npoints][2];
             }
      */
      T_POLYLINE *polyline;
      MDA_PTR  db_ptr;
      int i;
           .
           .
      mda_AllocateEntity (MDA_NOOPT, POLYLINE, &polyline, &db_ptr);
      mda_Malloc (MDA_NOOPT, polyline, npnts*sizeof(float)*2,
                  &polyline->points);
      for (i=0; i<npnts; i++) {
         polyline->points[i][0] = 0.0;
         polyline->points[i][1] = 0.0;
      }
      polyline->npoints = npnts;
      mda_UnlockEntity (MDA_NOOPT, db_ptr);

This function allocates an entity and then allocates memory to hold an array of floats. Note that the mda_Malloc call requires the address of the entity to which the memory is being allocated and the location of the pointer to the memory.

To allocate memory for variable arrays the functions mda_MallocArray or mda_CallocArray must be used. Memory allocated with these functions can be reallocated with mda_ReallocArray or freed with mda_FreeArray. Memory allocated by the mda_Malloc family of functions can not be interchanged with memory allocated by the mda_XxxxxArray functions. The mda_MallocArray, mda_CallocArray, and mda_ReallocArray functions have variable argument lists and take the number of dimensions and the size of each dimension and then return a block of memory which can be referenced using normal C subscripts. These functions should only be used for variable array type fields.

All memory allocated via mda allocate functions is initialized to 0.

Memory may also be allocated without being associated to an entity by passing NULL for the entity argument to the mda allocation functions. This memory can then be later associated with an entity by calling mda_AddMemory. See the mda_AddMemory manual page for additional information.

# ACCESSING ENTITIES

Entities are accessed by locking them into memory and then dereferencing a structure pointer as shown below:

    #include "mda_Interface.h"
    #include "app_structs.h"

       T_CIRCLE *circle;
       MDA_PTR  db_ptr;
           .
           .
       mda_LockEntity (MDA_NOOPT, db_ptr, &circle);
       radius = circle->radius;
           .
           .
       mda_UnlockEntity (MDA_NOOPT, db_ptr);

Once the entity is locked in the cache it may be dereferenced as many times as needed. If the entity is modified while locked in the cache, the MDA_INVALIDATE option should be specified to mda_UnlockEntity to invalidate the cache entry unless the changes are to be made permanent (described below).

An alternate method of accessing individual fields within an entity is to use the mda_GetField macro. This allows a single field (or element in an array field) to be returned without explicitly locking and unlocking the entity. The example above could also be written as:

    #include "mda_Interface.h"
    #include "app_structs.h"

       T_CIRCLE *circle;
       MDA_PTR  db_ptr;
           .
           .
       mda_GetField (db_ptr, T_CIRCLE, radius, &radius);
           .
           .
       mda_UnlockEntity (MDA_NOOPT, db_ptr);

# MODIFYING ENTITIES

Entities are modified by locking them in the cache, making the appropriate modifications, and then unlocking them with the MDA_MODIFIED option. The following code shows the calling sequence.

    #include "mda_Interface.h"
    #include "app_structs.h"

       T_CIRCLE *circle;
       MDA_PTR  db_ptr;
           .
           .
       mda_LockEntity (MDA_NOOPT, db_ptr, &circle);
       circle->radius = 2.0;
       mda_UnlockEntity (MDA_MODIFIED, db_ptr);

# DELETING ENTITIES

Entities are deleted from the model using the mda_DeleteEntity function. It is up to the application to clean up any references to the entity before performing the delete. If backpointers are enabled for the model and the entity to be deleted is referenced from other entities, an error will be returned and the entity will not be deleted (the BACKPOINTERS section describes this in more detail). The following code sequence shows how an entity is deleted.

    #include "mda_Interface.h"
    #include "app_structs.h"

       MDA_PTR  db_ptr;
           .
           .
       mda_DeleteEntity (MDA_NOOPT, db_ptr);

# OBJECT TREE AND OBJECT LIST INTERFACE

While the normal entity access functions allow variable length lists of data to be accessed as an array, there are times when it is preferable to access the information as a linked list or tree. The MDA subsystem provides several functions to allow object list and object tree information to be manipulated as entity information. See the manual pages for the OTM and OLM subsystems for information on object trees and lists. The field being copied to/from the object tree or list must be of type "pointer to fixed" (i.e. a field with one or more dimensions of which the first is defined by a count field). The following code sequence shows how an object list can be used to store a list of coordinates.

    #include "mda_Interface.h"
    #include "app_structs.h"

       T_POLYLINE *polyline;
       MDA_PTR  db_ptr;
       OLM_LIST *coords;
       double point[2];
       int i;
           .
           .
       mda_AllocateEntity (MDA_NOOPT, POLYLINE, &polyline, &db_ptr);
       coords = olm_CreateList (OLM_NOOPT, sizeof(polyline->points[0]),
                                  10, NULL);
       for (i=0; i<npnts; i++) {
          point[0] = (double) i;
          point[1] = (double) i;
          olm_AddObject (OLM_NOOPT, coords, point);
       }
       mda_CopyFromOList (MDA_NOOPT, polyline, coords,
                          &polyline->npoints,
                          &polyline->points);
       mda_UnlockEntity (MDA_NOOPT, db_ptr);
       olm_DeleteList (OLM_NOOPT, coords);

If the polyline->points field already referenced an array it must be freed using mda_Free before the call to mda_CopyFromOList is made.

The following code sequence shows how data can be copied from a field into an object list for further manipulation.

    #include "mda_Interface.h"
    #include "app_structs.h"

       T_POLYLINE *polyline;
       MDA_PTR  db_ptr;
       OLM_LIST *coords;
       int i;
           .
           .
       mda_LockEntity (MDA_NOOPT, db_ptr, &polyline);
       coords = olm_CreateList (OLM_NOOPT, sizeof(polyline->points[0]),
                                10, NULL);
       mda_CopyToOList (MDA_NOOPT, coords, polyline->npoints,
                        polyline->points);
           .
           .

The interfaces to object trees (mda_CopyToOTree and mda_CopyFromOTree) work in the same manner. Entity data can be easily sorted by copying it to an object tree (with the appropriate comparison function) and then copying it back to the entity field. Note that in this case the original array pointer should be freed using mda_Free before the information is copied back to the entity.

# BACKPOINTERS

The MDA subsystem can optionally support bi-directional pointers so that the parents of a given entity can be easily retrieved. These parent references are called backpointers. By default backpointers are disabled because they add an overhead of approximately 5-10% depending on the particular model. Backpointers can be enabled for a model by setting the backptrs field within the model_info data structure to 1 before calling mda_AllocateModel. Once backpointers are enabled the function mda_AddPtr must be used when storing a pointer in an entity. The mda_AddPtr function establishes the bi-directional reference. To modify a pointer field within an entity the mda_ModifyPtr function must be used. To delete a pointer in an entity the mda_DeletePtr function must be used. A debug function is provided to ensure that all pointers are being manipulated with these functions. This function, mda_DbgCheckBackPtrs will find any inconsistencies in the backpointer information such as assigning a pointer without using mda_AddPtr.

As an alternative to the above functions, mda_SetBackPtrs can be used to set all of the backpointer information in a model at once. This method allows an application to not use the mda_XxxxPtr functions but still gain access to the backpointer information. The disadvantage of this approach is that mda_SetBackPtrs traverses each entity in the model which can result in a fair amount of overhead.

The function mda_GetParents will retrieve the list of parents for an entity. The list is returned as a dynamically allocated array of MDA_PTRs to the parents. The application is responsible for freeing the memory used by the list.

By using backpointers an application can easily replace an entity with a new one and update all existing references to it. The mda_ModifyParents function takes a list of parent entities, a child entity referenced from each of the parents, and a pointer which is to be substituted for the old child pointer.

When backpointers are enabled mda_DeleteEntity will not allow an entity which is referenced from another entity to be deleted unless the MDA_FORCE_DELETE option is used. Normally an application should clean up references to the entity to be deleted before calling mda_DeleteEntity. If it is necessary to delete the entity before removing references to it then the following guidelines must be followed:

1)

The MDA_FORCE_DELETE option must be passed to mda_DeleteEntity.

2)

References to the deleted entity must be corrected by the application.

3)

The functions mda_DeletePtr and mda_ModifyPtr can not be called with the deleted entity specified as the child argument. To remove a reference to the deleted entity, explicitly set the pointer field in the parent to MDA_NULL_PTR or use mda_AddPtr with a child of MDA_NULL_PTR. To modify a reference to the deleted entity to point to another entity use mda_AddPtr with the new reference for the child.

4)

The function mda_GetParents can not be called on the deleted entity.

# SAVED MODEL FILES

The contents of a model can be saved as an ASCII file using the function mda_SaveModel. This file can later be restored as a model using the function mda_RestoreModel. Currently only a single model can be saved per model file. This capability will be extended in the future to support multiple models being saved per model file.

Any PDElib model can be saved as a saved model file. When the model is restored, the model specified must have the same DDF as the original model. A model may be saved anytime between being allocated and freed. To restore a model, the application must allocate a model with mda_AllocateModel before calling mda_RestoreModel.

The index portion of database pointers is maintained bewteen the original model and the restored model. However, the model portion of the database pointer will change if the restored model number differs from the original model number. To reference the same entity in the original model and the restored model, an application must obtain the original index using the function mda_GetPtrIndex in the saving application. After the model has been restored, this same entity can be referenced by constructing a new database pointer with mda_MakePtr by passing the new model number and the index that was returned from mda_GetPtrIndex.

Notes about saved model files:

1)

The mda_SaveModel function will convert cross-model pointers into MDA_NULL_PTR references.

2)

The original database pointer information is not preserved. All entities will refer to themselves as their original entities.

3)

Application data is not preserved. If an application wishes to preserve application data, it must make provisions for saving and restoring it.

4)

Arrays in entities which are dimensioned to one or more count fields but have been allocated to a larger size will be restored such that they are allocated to the actual size defined by the count fields.

5)

mda_RestoreModel makes several checks to ensure that the DDF which the saved model file was created with is compatible with the model which was passed to mda_RestoreModel. It does this by ensuring that the db_name and db_version fields are the same. To allow upwards compatible changes (such as adding new entities) to be made to a DDF without invalidating existing saved models, the release field in the DDF can be incremented. If the release field does not match that in the saved model file, a warning will be issued but the model will still be restored.

6)

If entities were deleted in the original model before it was saved, the model will be restored with the corresponding entities marked as reserved (see mda_ReserveEntity). The application does not need to do anything with these entities.

Saved Model File Format

The first line in a saved model file must have the format:

    BEGIN_MODEL model_num db_name db_version release

Where the parameters are:

model_num

The model number which this saved model was stored in before being saved.

db_name

The name of the DDF for the saved model. This is the db_name field from the DDF which defined the model. When a model is restored, this field must match the DDF name of the model passed to mda_RestoreModel.

db_version

The db_version field from the DDF which defined the model. When a model is restored, this field must match the db_version of the model passed to mda_RestoreModel.

release

The release field from the DDF which defined the model. When a model is restored, a warning will be issued if this field does not match the release field of the model passed to mda_RestoreModel.

The BEGIN_MODEL line is followed by 0-N entity records of the form:

    #index = entity_name(field1, field2, ..., fieldn);

Where:

index

The database index of the entity (this is the value returned by calling mda_GetPtrIndex on a database pointer). Indices do not need to be in sequential or increasing order, however, they must be unique in the file.

entity_name

The lowercase name of the entity as defined in the DDF.

field1, ...

The entity data listed in the same order as it is defined in memory. In addition to the fields defined in the entity, additional count fields are inserted for unsized arrays and an index field is inserted for unions. The list of fields may span multiple lines in the file although a single field can not be split across lines and the field delimiter must appear on the same line as the field.

Following the entity records is a termination record of the form:

    END_MODEL model_num

Where model_num is the same as that found in the BEGIN_MODEL record.

For the basic C data types (such as int, float, double, etc.), ANSI C formatting is used. For VSTRING/FSTRING fields, the string is stored as a double quoted string (e.g. "this is a string"). Double quotes may be embedded using a backslash, backslashes may be embedded using two backslashes. For PTR fields, the value is stored in model:index form (e.g. 2:57 refers to index 57 in model 2). A PTR with value MDA_NULL_PTR is stored as "NULL" (without the quotes).

Null entity arrays (i.e. those with a pointer of NULL or dimensioned to a count field whose value is 0) are stored as an empty field (,,).

For the most part, the entity fields are simply a dump of what mda_TraverseEntity passes to a traverse function. There are two exceptions to this- unions and unsized arrays.

For unions, the index (starting at 0) of the selected union member is stored before the union value. For example, a union with the DDF entry:

    union data {
       char  c;
       int   i;
       float f;
    }

and a union type of DATA_I with data.u.i = 100, would be stored as "1, 100".

For unsized arrays, the total number of elements in the unsized array is stored before the first array element. For example, an unsized array with DDF fields:

    count c2;                /*  Set to 2  */
    count c3;                /*  Set to 3  */
    int   iarray[][c2][c3];  /*  Allocated to be [2][c2][c3]  */

would be stored as 12 followed by the values in iarray.

Sample Saved Model File

For the DDF:

    db_name sample
    db_version 1 release 0
    type_forms = (0:0, 1:0)
    entity 0:0 attribute {
       vstring label;
       union   font { /*  Union set to be type FONT_NUM  */
          ptr  def;
          int  num;   /*  Set to 5  */
       };
    };
    entity 1:0 polyline {
       ptr     attrs;
       count   npoints;   /*  Set to 3  */
       double  points[npoints][2];
       int     point_nums[npoints];  /*  Set to NULL  */
    };

A corresponding saved model file is:

    BEGIN_MODEL 0 sample 1 0
    #0 = attribute(
       "polyline 1",1,5);
    #1 = polyline(0:0,3,1.0,2.0,1.0,1.0,2.0,1.0, );
    END_MODEL 0

Restrictions

1)

VSTRING and FSTRING fields must always be the first field on a line (fields of other data types may follow the string on the same line).

2)

The maximum length of a line in a saved model file is 32768 characters.

# ENTITY LISTS

The MDA subsystem allows an application to create linked lists of entities. These linked lists can then be cycled to manipulate only certain entities with the model. Each entity list is partitioned into 1 or more \fIthreads\fP. Each thread is a linked list of entity database pointers. The number of threads in a given entity list is specified when the list is allocated. The only restriction on the use of threads is that each entity can appear in only one thread per entity list. Stated another way, a given entity can appear in any number of entity lists but can only appears in one thread per list. An example of using entity lists and threads would be a situation where an application wanted to store a linked list of all the annotation entities. If there are 10 types of annotation entities defined this can be accomplished by creating an entity list with 10 threads. Each thread in the entity list is a linked list of annotation entities of a specific type. Since an entity can only have a single type, no entity will appear in more than one thread. This same information could also be stored in 10 separate entity lists, each with only one thread. Using multiple threads within an entity list has much less memory overhead (< 50 bytes) than creating multiple entity lists so this approach is recommended when possible. The system does not impose any limit on the number of entity lists you can create nor the number of threads in each entity list. The following code shows how to create the entity list described above, cycle it, and free it.

    #include "mda_Interface.h"
       MDA_ENTITY_LIST *elist;
       MDA_PTR db_ptr;
       mda_AllocateEntityList (MDA_NOOPT, 10, &elist);
    /*  Obtain database pointers and add each with:  */
       mda_AddToEntityList (elist, thread, db_ptr);
    /*  Cycle the first thread  */
       while (mda_CycleEntityList (elist, 0, &db_ptr)) == MDA_NOERR) {
          .
          .
          .
       }
    /*  Delete the entity list  */
       mda_FreeEntityList (elist);

# APPLICATION DATA

The MDA subsystem can store application information keyed by database pointer. This is most useful when storing some type of information with all (or nearly all) the entities in a model. For example, an application may wish to store a flag with each entity indicating if it was processed, unsupported, or invalid. This can be accomplished using application data. Since the data is keyed by database pointer, an application can quickly retrieve the processing status of a given entity. The following code shows how to create, write, read, and free application data.

    #include "mda_Interface.h"
       MDA_APP_DATA *ad_ptr;
       int proc_flag;
    /*
        Allocate app data to hold processing status.
        The MDA_ZERO_DATA option initializes all data to 0.
    */
       mda_AllocateAppData (MDA_ZERO_DATA, sizeof (int), &ad_ptr);
    /*  Process entities and save result in app data  */
             .
             .
       proc_flag = 1;
       mda_WriteAppData (ad_ptr, db_ptr, &proc_flag);
             .
             .
    /*  Check to see if an entity has been processed  */
             .
             .
       mda_ReadAppData (ad_ptr, db_ptr, &proc_flag);
       if (!proc_flag) {
             .
             .
       }
    /*  Free the app data  */
       mda_FreeAppData (ad_ptr);

# TOLERANCES

The MDA subsystem allows an application to associate tolerance information with a model. The tolerances are defined as name-value pairs where the name is a character string and the value is a double precision tolerance. A tolerance is defined or changed using the function mda_SetTol. The value of a previously set tolerance can be obtained using mda_GetTol. A third function, mda_SetDefaultTol, allows the application to define the tolerance to be returned when an undefined tolerance is passed to mda_GetTol. If an unknown tolerance is requested from mda_GetTol, and mda_SetDefaultTol was not previously called, the value DBL_EPSILON will be returned. All of these functions use the current model. Model specific versions have a 2 appended to the name (e.g. mda_SetTol2). The example below shows how to set/retrieve tolerance for a model.

    #include "mda_Interface.h"
          .
          .
          .
    /*  Set default tolerance to .001  */
       mda_SetDefaultTol (.001);
    /*  Set the tolerance for tol_1  */
       mda_SetTol ("tol_1", .002);
          .
          .
          .
    /*  And now retrieve it  */
       tol_1 = mda_GetTol ("tol_1");
    /*  This will return .001 because tol_2 was not defined  */
       tol_2 = mda_GetTol ("tol_2");

A debug function, mda_DbgDumpTols, is provided to list the tolerance values which are currently set within a specified model.

# CALLBACKS

By using callback functions, an application can specify that when a certain event occurs, a function should be called. A callback function is set using mda_SetCallBack and cleared using mda_ClearCallBack. The function specified to mda_SetCallBack takes as parameters an integer indicating the event and a void pointer to data pertaining to the specific callback (e.g. the address of a database pointer which was modified). The event type is passed so that a single callback function can service multiple event types. The following code example will display the type/form of each entity when it is unlocked with the MDA_MODIFIED option set.

    #include "mda_Interface.h"
          .
          .
          .
    /*  Register the callback function  */
       mda_SetCallBack (MDA_NOOPT, model, MDA_ENT_MODIFIED, print_tf);
          .
          .
          .

    /*  Callback function  */
    PDX_MSGT print_tf (int event, void *data)
    {
       int type, form;
       mda_ReadTypeForm (*(MDA_PTR *) data, &type, &form);
       printf ("%d:%dn", type, form);
    }

# TRACKING COPIED ENTITIES

Some applications need to create a new entity from an existing one either at the application level or by calling a library function (such as geometric entity conversions). In this situation it is useful to know what the original entity was that the new entity was generated from. The MDA subsystem provides a mechanism to maintain this information. Each entity has an internal field defined which contains the database pointer of the entity from which it was derived. If the entity is an original (i.e. not created from another), this field will contain the entity's own database pointer. This is termed an "original" entity. If the entity has been generated from another entity then this field will contain the database pointer of the entity it was created from. This field is called the original database pointer field and is accessed using the functions mda_SetOrigDbptr and mda_GetOrigDbptr.

Most applications will only need to use mda_GetOrigDbptr. The following code example shows how to obtain the original database pointer of an entity:

    #include "mda_Interface.h"
          .
          .
          .
    /*  Get the original entity  */
       orig_db_ptr = mda_GetOrigDbptr (MDA_NOOPT, ent);

In this example, orig_db_ptr will be set to either mda_GetDbptr(ent) or the original entity that ent was created from.

The original database pointer field is simply copied when a new entity is created from an original entity. This means that even after several iterations of creating an entity from a previously created one, the final entity will still contain the database pointer of the first (i.e. true original) entity.

The mda_SetOrigDbptr can be used by an application to maintain its own set of original entities.

The functions mda_Read/WriteOrigDbptr perform the same operations as mda_Get/SetOrigDbptr but take a database pointer rather than an entity structure as input.

# DEBUGGING FUNCTIONS

Several debugging functions are provided with the MDA subsystem. A summary of these appears below. Note that on some systems these can be called interactively from the system debugger. See the appropriate manual page for usage information.

Funcion | Description
–
mda_DbgDumpCache | Lists contents of the entity cache
mda_DbgDumpDirEntry | Lists an entity's directory entry
mda_DbgDumpEntity | Simple entity printer
mda_DbgCheckBackPtrs | Validates backpointers
mda_DbgDumpMBlocks | Lists memory allocated to an entity
mda_DbgCheckModel | Validates a model
mda_DbgCheckModelIndEntities
| Check independent entities for a model
mda_DbgCheckAllModels | Validates all allocated models
mda_DbgCheckEntity | Validates an entity
mda_DbgGetLockedCount | Returns count of entities locked in a model
mda_DbgDumpModel | Dumps all the entities in a model
mda_DbgDumpLocks | Lists all currently locked entities
mda_DbgDumpTols | Dumps tolerances associated with a model
mda_DbgSaveModel | Saves a model in a saved model file
mda_DbgSaveEntity | Saves an entity in a saved model file
mda_DbgSaveEntities | Saves multiple entities in a saved model file
mda_DbgTraverseEntity | Dumps fields passed by mda_TraverseEntity