Overview
This chapter provides the information necessary to develop applications with ST-Developer's SDAI C interface. We describe how to compile and run SDAI application with ST-Developer, followed by a description of the SDAI C data types, and some programming techniques for manipulating them.
Compiling and Running SDAI Applications
To compile an SDAI application, you must tell your compiler to use the SDAI header file and libraries by specifying the ST-Developer include path in the compiler invocation, and also include following the statement in the C code:
#include <sdai.h>
Before you can run the application, you must insure that you have the ROSE meta-data and possibly the EXPRESS parse data for your schemas. This information for many common information models is included with ST-Developer; for other information models, you must use the EXPRESS compiler to generate the data:
% expfront -rose infomodel.exp
Compiling SDAI Applications
In every source file that calls an SDAI function, include the <sdai.h> header file. This file is included in the standard ST-Developer header file directory (pointed to by the ROSE_INCLUDE environment variable). Link your application against the sdai library and the rose library.
This example shows how to compile a simple SDAI application, sdai_test.c. The application consists of a single file, are we will compile and link the program in one step. The command to compile the code on a UNIX system is:
% cc -I$ROSE_INCLUDE sdai_test.c -L$ROSE_LIB -lsdai -lrose
or, on a Windows system:
C:> cl /I"%ROSE_INCLUDE%" sdai_test.c /LIBPATH:"%ROSE_LIB%" \ sdai.lib rose.lib
The SDAI Dictionary
The data dictionary describes the structure of an information model to the application. For more information about the contents of the dictionary, see Dictionary Schema. The SDAI library generates the dictionary at run-time from a ROSE schema file, and an EXPRESS parse model. If the EXPRESS parse data is absent or is an old version, the library generated a limited form of the dictionary, which is sufficient for many SDAI applications. With the parse data, the SDAI to generates a complete, correct dictionary containing all the type, rules, and attributes in the EXPRESS information model.
Like all ST-Developer applications, the SDAI requires that you have a ROSE schema file for each information model you wish to use. The release includes schema files for many of the standard schemas (in $ROSE/system_db/schemas); if you are using one of these schemas, the necessary information has already been generated.
If you do not already have the ROSE schema file, use the EXPRESS compiler to create the ROSE schema file and the EXPRESS parse data with the command:
% expfront -rose <expressfile>
where <expressfile> is the EXPRESS file containing the information model your application uses. If you do not require the parse data, you can add the -writenone option to the EXPRESS compiler command line. The parse data is required if your application examines the SDAI dictionary, and needs to determine the exact type of every attribute. (Without the complete dictionary, you will not get the correct types of defined types of primitives.
As with the ROSE schema files, ST-Developer provides the EXPRESS parse data models for a common set of information models. You will only need to generate the parse data for those information models that are not provided in ST-Developer.
SDAI Data Types
The SDAI provides access to EXPRESS defined data from the C programming language. This section describes how EXPRESS data types are mapped to C data types by the SDAI. These types are all defined in the <sdai.h> header file.
SDAI Constants
The <sdai.h> header file defines a number of constants. All constants start with lowercase sdai, with the actual constant name in all capitals.
sdaiNULL Instance or aggregate which does not exist sdaiTRUE sdaiFALSE Boolean and Logical values sdaiUNKNOWN sdaiBIT0 sdaiBIT1 Binary Values sdaiE Mathematical constant e (2.718281...) sdaiPI Mathematical constant pi (3.141592...)
Primitive Types
The SDAI binding defines several data types which are used for parameters for the functions defined in the binding. In general, you should use the SDAI defined type, rather than the underlying C type, since the underlying type may change from one version or implementation to another. These types are:
EXPRESS Type SDAI C Type Underlying C Type BINARY SdaiBinary SdaiBit * BOOLEAN SdaiBoolean int ENUMERATION SdaiEnum char * INTEGER SdaiInteger long LOGICAL SdaiLogical int NUMBER SdaiReal double REAL SdaiReal double STRING SdaiString char *
Strings are ordinary NULL terminated C strings, thus SDAI strings can be used as arguments for the standard C string functions. All strings in the SDAI library and underlying ROSE library are expected to be UTF-8 encoded UNICODE data.
A binary is represented as a NULL-terminated string of sdaiBIT0 and sdaiBIT1. In this implementation, this means that binaries are ASCII strings, of the characters `0' and `1.' To get the length of a binary, use the C function strlen().
Enumeration values are represented as (NULL-terminated) strings.
Entity Instances
An entity instance (or just instance) is represented by the C type SdaiInstance. There are a number of more specific types of SdaiInstance, and these may be used where a handle of type SdaiInstance is required. They are organized into the following conceptual type hierarchy.
SdaiInstance SdaiAppInstance SdaiModel SdaiRep SdaiSession SdaiAttr SdaiExplicitAttr SdaiNamedType SdaiEntity SdaiDefinedType SdaiWhereRule SdaiUniRule SdaiGlobalRule SdaiSchema SdaiSchemaInstance SdaiTrx
Thus, a handle of type SdaiModel may be used as a parameter to the function sdaiGetAttrBN, even though the prototype requires an argument of type SdaiInstance.
Aggregates
Aggregates are represented by the C type SdaiAggr. Aggregates consist of bags, sets, arrays and lists, and are conceptually organized into the following type hierarchy:
SdaiAggr SdaiOrderedAggr SdaiArray SdaiList SdaiUnorderedAggr SdaiSet SdaiBag
Any more specific type may be used in place of a more general type. For example, any of the above types may be used where an SdaiAggr is required.
SDAI Operations
An SDAI session represents a set of operations upon a data set. All SDAI operations occur within the context of a session. The STEP data instances themselves are contained in SDAI models. Each model is contained in a repository. An SDAI application must manipulate sessions, repositories and models to access the actual STEP data. In general, an SDAI application performs the following steps:
- open session
- open repository
- open model(s)
- manipulate data
- close model(s)
- close repository
- close session
This section describes how to perform these operations with the SDAI library.
Memory Management
Strings, enumerations, and binaries are represented as NULL terminated C strings. When a value of one of these types is passed as a parameter to the SDAI library, the library copies the value, if necessary. After the call, the application may dispose of the original string.
When the SDAI returns such a value to the application, the application should treat it as read-only. The contents of the memory will not be disturbed at least until the next call that returns a string, binary, or enumerator.
Error Handling
The SDAI identifies exceptional events by generating errors. Application are notified of errors in two ways error handler functions, and error codes. In addition, many API functions may return special values to indicate that something exceptional occurred during the call.
When an error occurs, the SDAI calls an error handler. The default error handler prints a message identifying the error on standard output and continues. You may wish to replace the error handler with your own function to customize the error handling of your SDAI application. The library maintains a stack of error handlers. To push a handler onto the stack, use the sdaiSetErrorHandler() function. To pop the top handler off the stack and restore a previous handler, use the sdaiRestoreErrorHandler() function.
The sdaiErrorQuery() function returns the error code of the most recent error to occur in the application. The error that is reported did not necessarily originate from the most recent SDAI call, but rather was the result of the most recent call which resulted in an error. This function also clears the error code. If you need to determine if a specific call completed without error, call sdaiErrorQuery() before and after the call--first to clear any old error code, and then after the call to obtain the error that resulted from your call.
The following error codes are defined by the binding:
sdaiNO_ERR No error sdaiSS_OPN Session open sdaiSS_NAVL SDAI not available sdaiSS_NOPN Session is not open sdaiRP_NEXS Repository does not exist sdaiRP_NAVL Repository not available sdaiRP_OPN Repository open sdaiRP_NOPN Repository is not open sdaiTR_EAB Transaction ended abnormally sdaiTR_EXS Transaction exists sdaiTR_NAVL Transaction currently not available sdaiTR_RW Transaction read-write sdaiTR_NRW Transaction not read-write sdaiTR_NEXS Transaction does not exist sdaiMO_NDEQ SDAI-model not domain equivalent sdaiMO_NEXS SDAI-model does not exist sdaiMO_NVLD SDAI-model invalid sdaiMO_DUP SDAI-model duplicate sdaiMX_NRW SDAI-model access not read-write sdaiMX_NDEF SDAI-model access not defined sdaiMX_RW SDAI-model access read-write sdaiMX_RO SDAI-model access read-only sdaiSD_NDEF Schema definition not defined sdaiED_NDEF Entity definition not defined sdaiED_NDEQ Entity definition not domain equivalent sdaiED_NVLD Entity definition invalid sdaiRU_NDEF Rule not defined sdaiEX_NSUP Expression evaluation not supported sdaiAT_NVLD Attribute invalid sdaiAT_NDEF Attribute not defined sdaiSI_DUP Schema instance duplicate sdaiSI_NEXS Schema instance does not exist sdaiEI_NEXS Entity instance does not exist sdaiEI_NAVL Entity instance not available sdaiEI_NVLD Entity instance invalid sdaiEI_NEXP Entity instance not exported sdaiSC_NEXS Scope does not exist sdaiSC_EXS Scope exists sdaiAI_NEXS Aggregate instance does not exist sdaiAI_NVLD Aggregate instance invalid sdaiAI_NSET Aggregate instance is empty sdaiVA_NVLD Value invalid sdaiVA_NEXS Value does not exist sdaiVA_NSET Value not set sdaiVT_NVLD Value type invalid sdaiIR_NEXS Iterator does not exist sdaiIR_NSET Current member is not defined sdaiIX_NVLD Index invalid sdaiER_NSET Event recording not set sdaiOP_NVLD Operator invalid sdaiFN_NAVL Function not available sdaiAB_NEXS ADB does not exist sdaiSY_ERR Underlying system error
SDAI Session
All SDAI operations must be performed in the context of a session. To begin a session call sdaiOpenSession(). After you are finished with the SDAI, use sdaiCloseSession() to terminate the session. There may only be one session open at a time, but sessions may be opened and closed multiple times during the life of the process.
The sdaiOpenSession() function returns the handle of an entity instance for the session object. This instance is known as the SDAI session object. The handle of the session object must be saved for future calls such as sdaiOpenRepository() and sdaiCloseSession(). The SDAI session object provides an application access to the SDAI implementation's internal state. The session object is an entity instance, in the session schema, defined in Part 22 as:
ENTITY sdai_session; sdai_implementation : implementation; recording_active : BOOLEAN; errors : LIST [0:?] OF error_event; known_servers : SET [1:?] OF sdai_repository; active_servers : SET [1:?] OF sdai_repository; active_models : SET [1:?] OF sdai_model; data_dictionary : OPTIONAL schema_instance; INVERSE active_transaction : SET [0:1] OF sdai_transaction FOR owning_session; END_ENTITY;
Repositories
A repository is a collection of SDAI models which represents the physical organization of a database. A repository must be opened before its contents can be accessed. This implementation provides a repository named "standard_3.0" which contains files in the search path defined by the ROSE_DB environment variable. You can use "rose ls" to see which models are in the current path. In the session model, a repository is defined by the entities sdai_repository and sdai_repository_contents:
ENTITY sdai_repository; name : STRING; contents : sdai_repository_contents; description : STRING; INVERSE session : sdai_session FOR known_servers; UNIQUE UR1: name, session; END_ENTITY; ENTITY sdai_repository_contents; models : SET[0:?] OF sdai_model; schemas : SET[0:?] OF schema_instance; INVERSE repository : sdai_repository FOR contents; END_ENTITY;
To open a repository, use either sdaiOpenRepository() or sdaiOpenRepositoryBN(). If you know the name of the repository, use sdaiOpenRepositoryBN():
SdaiSession sess; SdaiRep repo; session = sdaiOpenSession(); repo = sdaiOpenRepositoryBN (session, "standard_3.0");
If you do not know which repositories will be available, you can examine the set of repositories provided in the "known_servers" attribute of the SDAI session object, and open any or all of the repositories found there. The following program prints out all the repositories available to the application:
#include <sdai.h> $include <stdio.h> int main() { SdaiSession sess; SdaiAggr reposet=NULL; SdaiIterator itor; SdaiString reponame; SdaiInstance repo; sess = sdaiOpenSession (); sdaiGetAttrBN (sess, "known_servers", sdaiAGGR, &reposet); itor = sdaiCreateIterator (reposet); while (sdaiNext (itor)) { repo = NULL; sdaiGetAggrByIterator (itor, sdaiINSTANCE, &repo); if (!repo) continue; /* paranoia check, should not happen*/ sdaiGetAttrBN (repo, "name", sdaiSTRING, &reponame); printf ("Repository: %s\n", reponame); } sdaiCloseSession (sess); return 0; }
Schema Instances
A schema instance is a set of models which are conceptually related on some way. Schema instances are used to define a scope over which global and uniqueness rules of an EXPRESS schema are evaluated. A model may belong to multiple schema instances.
Schema are represented as a rose file in a repository based on the schema_instance_schema. This schema contains the following entity which defines a single schema instance:
ENTITY sdai_schema_instance; associated_models : SET [0:?] OF STRING; native_schema : STRING; change_date : STRING; validation_date : STRING; validation_result : LOGICAL; validation_level : INTEGER; END_ENTITY;
When a schema instance is accessed, the SDAI library converts the schema instance to the internal form defined in the population model:
ENTITY schema_instance; name : STRING; associated_models : SET [0:?] OF sdai_model; native_schema : schema_definition; repository : sdai_repository; change_date : OPTIONAL time_stamp; validation_date : time_stamp; validation_result : LOGICAL; validation_level : INTEGER; UNIQUE UR1: name, repository; WHERE WR1: SELF IN SELF.repository.contents.schemas; END_ENTITY;
As an extension to the standard, ST-Developer allows you to specify a model instead of a schema instance for most functions that take a schema instance as a parameter.
Models
Each repository contains a number of models. In the ST-Developer implementation, a model corresponds to a RoseDesign which, in turn, corresponds to a STEP Part 21, or ROSE file (*.stp or *.rose).
A model may be opened in either read-only or read-write mode. In a read-only model, any operation which changes the contents of the model is disallowed and generates an error. When an attribute refers to an instance in a different model, the second model is opened implicitly in read-only mode, if it is not already open. In the session model, a model is defined by the entities sdai_model and sdai_model_contents:
ENTITY sdai_model; name : STRING; contents : sdai_model_contents; underlying_schema : schema_definition; repository : sdai_repository; change_date : OPTIONAL time_stamp; mode : OPTIONAL access_type; INVERSE associated_with : SET [0:?] OF schema_instance FOR associated_models; UNIQUE UR1: repository, name; WHERE WR1: SELF IN SELF.repository.contents.models; END_ENTITY; ENTITY sdai_model_contents; instances : SET[0:?] OF entity_instance; folders : SET[0:?] OF entity_extent; populated_folders : SET[0:?] OF entity_extent; END_ENTITY;
Use the sdaiAccessModelBN() function to open a model by name. If you do not know the model's name, you can look through the set of models associated with a repository by examining contents.sdai_models attribute in an open repository.
New models can be created with sdaiCreateModelBN() or sdaiCreateModel(). After a new model is created, you will need to open it (in read-write mode) before any data can be stored in it:
SdaiModel newmod = sdaiCreateModelBN ("modname","config_control_design"); sdaiAccessModel (newmod, sdaiRW);
Get and Put Functions
There are a number of functions in the binding which retrieve data for the application. Rather than having a separate get and put function for every primitive type, the SDAI provides a single version of each get or put function which takes a parameter identifying the type that is requested. To specify the type of the value being accessed, the <sdai.h> header defines the enumeration type SdaiPrimitiveType which includes the following values:
sdaiAGGR sdaiBINARY sdaiBOOLEAN sdaiENUM sdaiINSTANCE sdaiINTEGER sdaiLOGICAL sdaiREAL sdaiSTRING
Get Functions
All get functions such as sdaiGetAttr() or sdaiGetAggrByIterator() have the same final two parameters: one of type sdaiPrimitiveType called type and one of type void * named value. To use these functions, you must pass the address of an object of the which the SDAI will fill in. The type parameter to identifies the primitive type of object you have passed in. The specified type must correspond to the C type that you are passing in, and not necessarily to the EXPRESS type of the attribute you are retrieving. If necessary and possible, the SDAI library converts the EXPRESS representation to the requested type. If this conversion is not possible an error, usually sdaiVT_NVLD (value type is invalid), is generated.
The following code fragment retrieves the value of the attribute named fooatt from instance inst, using sdaiGetAttrBN(). The return value (the variable val) is initialized to sdaiNULL, so that the application can determine if the call was successful. If sdaiGetAttrBN() fails, an error is generated, and the value is never filled in, leaving val with its previous, undefined value. This may result in a segmentation fault, or other undefined behavior, if the garbage value is ever used in a future SDAI call.
SdaiAppInstance val = sdaiNULL; sdaiGetAttrBN (inst, "fooatt", sdaiINSTANCE, &val);
Because sdaiGetAttrBN() returns the contents of val, it is possible to chain multiple calls.
SdaiInstance ival=sdaiNULL; SdaiREAL realval; sdaiGetAttrBN (sdaiGetAttrBN(inst, "fooatt", sdaiINSTANCE, &inst1), "baratt", sdaiREAL, &realval);
If the requested type had been a primitive (That is, for example, a string, integer, or real) the function would have returned the address that was passed in as parameter value, rather that the value that was placed there.
Put Functions
The SDAI provides a number of functions which allow an application to update the data. Theses functions include sdaiPutAttr(), sdaiPutADBValue(), sdaiAdd(), and many others. All of the put functions require the same final two parameters: one of type SdaiPrimitiveType identifying the type of the data, and another of the requested type containing the actual data.
Warning
The get functions' final parameter is specified in the header files as "...", so be aware that your C compiler is not be able to any catch type mismatch errors, which could result in undefined behavior.
As in the get functions, the type parameter specifies the type of the parameter, and not the type of the underlying attribute which is being set. The value is converted when possible. If the value cannot be converted to the underlying type, the library generates an error (usually sdaiVT_NVLD).
The following example uses the sdaiPutAttr() function to put a real value into an attribute named realatt:
sdaiPutAttrBN (inst, "realatt", sdaiREAL, 3.651);
You could also specify the value as an integer, and the interface converts it to a real value.
sdaiPutAttrBN (inst, "realatt", sdaiRINTEGER, 42);
Instances
Each entity instance consists of a set of attributes, as defined in an EXPRESS information model. These attributes may be retrieved with the functions sdaiGetAttr() and sdaiGetAttrBN(). The sdaiCreateInstance() and sdaiCreateInstanceBN() functions create new entity instance.
SdaiInstance inst; inst = sdaiCreateInstanceBN (model, "cartesian_point");
Each model contains a set of entity extents. An entity extent is the set of all the instances of a specific type and its subtypes in a model. These provide one common method for beginning traversals of a model's data. The entity extents can be found in the sdai_model_contents in the folders and populated_folders attributes, or by using the convenience functions sdaiGetEntityExtent() and sdaiGetEntityExtentBN(). The following code outlines such a traversal over all objects of type curve.
SdaiSet extent = sdaiGetEntityExtentBN (model, "curve"); SditItrerator itor = sdaiCreateIterator (extent); while (sdaiNext(itor)) { SdaiInstance inst=NULL; sdaiGetAggrByItrator (itor, sdaiINSTANCE, &inst); /* Process the value in the instance */ }
Attribute Data Blocks
The SDAI interface provides a mechanism for dealing with arbitrarily-typed data known as Attribute Data Blocks (ADBs). An ADB is a strongly typed union of all the SDAI-defined C types. An ADB may be used in place of a value in any get or put functions.
An ADB is created by using sdaiCreateADB() to create an ADB and sets its value, or by using sdaiCreateEmptyADB() to create an ADB without a value. Set the ADB's value with sdaiPutADBValue() or by passing the ADB to a get function.
Attribute Data Blocks provide a convenient way to copy attributes from one instance to another without having to worry about the actual underlying type. Without ADBs, this would require a large switch statement to process every possible attribute type. The following function copies a single attribute from one instance to another, assuming that the two instances are the same type. This code correctly copies the values of SELECT typed attributes.
void copyAttr (SdaiAttribute att, SdaiAppInstance src, SdaiAppInstance dest) { SdaiADB adb = sdaiCreateEmptyADB(); sdaiGetAttr (src, att, sdaiADB, &adb); /*Get the value to the ADB*/ sdaiPutAttr (dest, att, sdaiADB, adb); /* Put the value */ }
Aggregates
An aggregate is a collection of values defined in an EXPRESS information model. There are four basic types of aggregates: bags, sets, lists and arrays. Bags and sets are unordered, arrays and lists are ordered; arrays and bags may contain the same element multiple times; sets and lists may not. To provide these semantics, the SDAI provides several methods to manipulate arrays.
If an aggregate-typed attribute is unset (such as when its owning instance is newly created), the aggregate must be created with sdaiCreateAggr() before values can be added to the aggregate.
An iterator is an SDAI-defined object which traverses an aggregate. Unlike other access methods, iterators can be used to access on all types of aggregates, and provide a simple way to traverse them. The following is an example of an array traversal using an iterator:
SdaiItr itor; /* The iterator */ itor = sdaiCreateIterator (agg); /* Create an iterator over the aggregate*/ while (sdaiNext (itor)) { /* Get the value. Replace TYPE on the next line with the actual type*/ sdaiGetAggrByIterator (itor, TYPE, &VALUE); /* Process the value */ } sdaiDeleteIterator (itor); /* Clean up */
There are other methods available for retrieving the elements of aggregates, which allow random access by index, such as sdaiGetAggrByIndex(). These may only be used on ordered aggregates. This restriction is strictly enforced for all data types.
Non Persistent Lists
Non Persistent Lists (NPLs) are used as parameters to several SDAI functions, such as sdaiFindInstanceUsedIn(), where the function may fill the NPL with the appropriate values. NPLs may also be used for application specific purposes. The may only contain entity instances (no aggregates or primitives). Use the sdaiCreateNPL() function to create a new, empty NPL. After you are finished using the NPL, use sdaiDeleteNPL() to free the memory used by the non-persistent list.
SELECTS
A SELECT data type is an EXPRESS defined type whose value is one of a set of named types. Each of the types that a SELECT may have is called a element. SELECT types are similar to C union constructs, except that the elements of a SELECT are strongly typed. The STEP AP information models utilize SELECT types to constrain attribute values in ways would be impossible or awkward to do with inheritance. A select is defined in EXPRESS as follows:
TYPE sel = SELECT (type1, type2, type3); END_TYPE;
The element may not be an EXPRESS primitive type such as INTEGER, REAL or STRING. A select can contain defined types of primitives in the SELECT:
TYPE realval = REAL; END_TYPE; TYPE sel = SELECT (type1, realval); END_TYPE;
Since SELECT types are themselves named types, one SELECT may include another as a element:
TYPE innersel = SELECT (type1, type2); END_TYPE; TYPE outersel = SELECT (innersel, type3); END_TYPE;
To use the SDAI to retrieve the value of a SELECT-typed attribute, use either the sdaiGetAttr() or the sdaiGetAttrBN() function. If you do not know the actual primitive type, you must use an Attribute Data Block (ADB) to retrieve the value, then extract it from there. Using the following EXPRESS type definition:
TYPE sel = SELECT (type1, realval); END_TYPE; TYPE seltype = SELECT (a, realval); END_TYPE; ENTITY a; END_ENTITY; ENTITY b; foo : seltype; END_ENTITY;
if you have an instance of ENTITY b stored in the variable b_val, and you wish to retrieve the value of its foo attribute, you could use the following code:
SdaiInstance b_val; SdaiReal rval; sdaiGetAttrBN (b_val, "foo", sdaiREAL, &rval);
In the proceeding example, the sdaiGetAttrBN() call will only succeed if the value of the attribute is a realval type, which in turn is defined as a REAL. If the value of the SELECT attribute was not a realval, the SDAI implementation would generate a VT_NVLD error. If the type of the SELECT is a reference to an instance of entity a, you can retrieve the value as follows:
SdaiInstance instval; sdaiGetAttrBN (b_val, "foo", sdaiINSTANCE, &instval);
In the proceeding example, we assumed a priori knowledge of the SELECT attribute's type. In a non-trivial SDAI application, this is not likely to be the case, so you would use an Attribute Data block to get the value of the attribute:
SdaiADB adb = sdaiCreateEmptyADB(); sdaiGetAttrBN (b_val, "foo", sdaiADB, &adb);
Now the attribute value in stored in the ADB. We can examine type of the value and take appropriate action:
SdaiPrimitiveType type = sdaiGetADBType (adb); if (type == sdaiREAL) { printf ("We have a REAL\n"); } else if (type == sdaiINSTANCE) { printf ("We have a instance\n"); }
In some cases, the underlying primitive type in insufficient to properly identify the type of the SELECT attribute. For these cases the SDAI provides the sdaiGetADBTypePath() function. This function returns the exact type of a SELECT. In the following EXPRESS, the mreal type has several elements that are based on the REAL primitive type.
TYPE a_real = REAL; END_TYPE; TYPE b_real = REAL; END_TYPE; TYPE c_real = REAL; END_TYPE; TYPE mreal = SELECT (a_real, b_real, c_real); END_TYPE; ENTITY mreal_ent; x : mreal; END_ENTITY;
In this example, we will examine the value of the x attribute of an mreal_ent and determine if its type is a_real, b_real, or a c_real. This is done by getting the value (with sdaiGetAttrBN()) into an ADB, then using sdaiGetADBTypePath() to obtain the exact type.
SdaiADB adb = sdaiCreateEmptyADB(); SdaiInstance val; /* This is an mreal_ent */ SdaiInteger count; SdaiString * att_path; char * sel_type; sdaiGetAttrBN (val, "x", sdaiADB, &adb); att_path = sdaiGetADBTypePath (adb, &count); if (count != 1) { /* We have a problem. */ fprintf (stderr, "Expecting exactly one element in the type type\n"); } sel_type = att_path[0]; if (!strcmp (sel_type, "a_real") { /* We have a a_real */ SdaiReal val; sdaiGetADBValue (adb, sdaiREAL, &val); printf ("a_real: value=%f\n", val); } else if (!strcmp (sel_type, "b_real") { /* We have a b_real */ } else if (!strcmp (sel_type, "c_real") { /* We have a c_real */ } else { fprintf (stderr, "Got unexpected type: %s\n", sel_type); }
When the value of a SELECT typed attribute is itself another SELECT, the sdaiGetADBTypePath() function returns an array of strings identifying the element of each nested SELECT in the attribute's value. In the following EXPRESS, the value of attribute a of entity entval, is always ultimately a REAL, but there is a number of possible types that the SELECT can have.
TYPE r1 = REAL; END_TYPE; TYPE r2 = r1; END_TYPE; TYPE sel1 = SELECT (r1, r2); END_TYPE; TYPE sel2 = SELECT (r1, sel1); END_TYPE; TYPE sel3 = SELECT (sel1, sel2); END_TYPE; ENTITY entval; a : sel3; END_ENTITY;
Consider in instance of sel3 with a type consisting of the following SELECT elements:
sel3 -> sel1 -> r2
In this case, the type path would for the attribute would be {"sel1", "r2"}. Note that r1 is not in the path because it is a not a element, but rather is just the definition for the r2 type.
To set the value of a SELECT-typed attribute use the sdaiPutAttr() or sdaiPutAttrBN() functions. Unless you are putting an entity instance into the SELECT-typed attribute, you must use an ADB and set the type path with the sdaiPutADBTypePath() function. If you attempt to set a primitive value directly, or do not set the type path, you will get a VT_NVLD error.
Using the EXPRESS above, we will create and populate an instance of entval:
SdaiInstance val; SdaiModel model; SdaiADB adb; SdaiString type_path[] = {"sel1", "r2"}; int element_count = sizeof(type_path)/sizeof(type_path[0]); val = sdaiCreateInstanceBN (model, "entval"); adb = sdaiCreateADB (sdaiREAL, 2.56); sdaiPutADBTypePath (adb, element_count,type_path); sdaiPutAttrBN (val, "a", sdaiADB, adb);
When you use the sdaiPutADBTypePath() function, you specify an array of SELECT names identifying the type of the attribute. You do not have to specify every element though, but only the final element for primitive types. For entity types, you can omit the type path all together. If you do not provide enough information to uniquely identify the type path, the system will choose a path which can hold the value. If the path specified is impossible to instantiate, the put function generates a VT_NVLD error. If in the previous example the type_path variable were initialized as:
SdaiString type_path[] = {"rx"};
The sdaiPutAttrBN() function would generate an error because there is no rx type in the sel3 SELECT type. On the other hand, is you had initialized the path with the as follows:
SdaiString type_path[] = {"r2"};
the sdaiPutAttrBN function would still correctly update the value, but the exact type path used could be anything that ends in r2. The exact path chosen would be either {"sel1"," r2"} or {"sel2", "sel1", "r2"}.
Part 21 exchange file only keep enough path information to validate WHERE rules of defined types. This means that the previous examples, when written to a STEP file, generate the following line:
#30=ENTVAL(R2(2.56));
Note that there is no sel1 or sel2 anywhere in the entity record.
Inverse Attributes
An inverse attribute is an EXPRESS attribute which is defines a USEDIN construct as a specific attribute. You must have the full dictionary available in order to access inverse attribute values. To retrieve an inverse attribute use sdaiGetAttr() or sdaiGetAttrBN(), with a type of sdaiAGGR, (or sdaiADB). The inverse attribute will be constructed and placed into a newly created NPL. This NPL will only valid until the next inverse attribute is queried. While the SDAI may delete the NPL automatically, the application can also free the non-persistent list with sdaiDeleteNPL() when it is finished using it. (This is a ST-Developer extension to Part 24 for backward compatibility.)
SdaiNPL inv= sdaiNULL; sdaiGetAttrBN (inst, "invatt", sdaiAGGR, &inv); /* Do something with the values */ sdaiDeleteNPL (inv); /* optional as per IS */
In EXPRESS, inverse attributes can have a type of an entity or an aggregate, in the SDAI an inverse attribute is always returned as an aggregate. In this implementation of the SDAI, a traversal over the entire model is required to evaluate an inverse attribute, making it an expensive operation. Thus, inverse values should be cached by the application when possible.
Dictionary Data
The SDAI dictionary contains a representation of the EXPRESS information models used in the session. While it is possible to navigate the dictionary from the session object, it is generally not necessary to do so. The SDAI provides functions to retrieve the most commonly needed data from the dictionary. These functions include sdaiGetSchema(), sdaiGetEntity(), and sdaiGetAttrDefinition(), which retrieve schemas, entity definitions, and attribute definitions, respectively.
If an operation is performed many times, it is generally better to retrieve the definition from the dictionary and avoid using the functions which perform an operation by name. Doing so also helps prevent misspelling entity and attribute names in your application.
For example, the following code which used sdaiGetAttrBN:
sdaiGetAttrBN (inst, "att", sdaiINTEGER, &intval);
performs the exact same function as the following code:
SdaiEntity ent = sdaiGetInstanceType (inst); /* Get the entity definition*/ SdaiAttr att = sdaiGetAttrDefinition (ent, "att"); /* Look up attribute */ sdaiGetAttr (inst, att, sdaiINTEGER, &intval);
The second version is more complex, but if you need to perform the same operation several times, you can avoid many expensive string comparisons in the SDAI by using sdaiGetAttr(), rather than sdaiGetAttrBN().
Interfacing with ROSE
The SDAI library is built on top of the ROSE C++ class library, which it uses for low-level data management and file I/O. It is possible to access and manipulate the underlying ROSE objects from an SDAI application, however there are several issues that must be considered.
- The ROSE class library is not part of the SDAI standard.
- Any code that accesses ROSE must be written in C++. The SDAI binding is a C language interface, so you must compile at least part of your application with a C++ compiler.
- The SDAI interface, and not the ROSE library keeps track of each model's access mode (read-only or read-write). The ROSE library is therefore unable to enforce read-only access to a model. This means that making changes to instances in a read-only model under ROSE may succeed, while the same operation from the SDAI will generate a sdaiMX_NWR error.
- Several SDAI functions internally perform ROSE traversals. You should avoid leaving a traversal open when an SDAI call is made.
The instances and aggregates of the SDAI session model are handled internally by the SDAI and do not have an associated ROSE object. If you attempt to retrieve the RoseObject pointer to such an object, a NULL value is returned. The header <rose.h> must be included before <sdai.h>. If this is not done, the ROSE specific declarations in <sdai.h> will not be made available to the application.
The SDAI/ROSE interface consists of the following functions:
RoseStructure* _sdaiGetRoseStructure (SdaiAppInstance inst); RoseAggregate* _sdaiGetRoseAggregate (SdaiAggr inst); SdaiAppInstance _sdaiGetAppInstance (RoseStructure * obj); SdaiAggr _sdaiGetAggr (RoseAggregate * obj); RoseDesign * _sdaiGetRoseDesign (SdaiModel mod); SdaiModel _sdaiGetModel (RoseDesign * des); SdaiEntity _sdaiGetEntityForDomain (RoseDomain * dom); RoseDomain * _sdaiGetDomainForEntity (SdaiEntity ent); _sdaiGetInstanceSectionModel (SdaiInstance inst);
These functions convert aggregates and instances between ROSE and the SDAI representations. If the conversion cannot be performed, these functions return NULL.
A SELECT in the SDAI is almost transparent, while in ROSE, it is represented as a RoseUnion. RoseUnions may be nested, but in ROSE, a RoseUnion must be explicitly created and populated. If the SELECT is nested, a RoseUnion object must be created for each level of the SELECT.
Part 21 File Accessor functions
The SDAI library contains a few conveniences functions the access and manipulate the STEP Part 21 file information. This functionality is provided in three extension functions which allow you get access the entity identifiers, and the Part 21 header information.
The _sdaiGetEntityId() function returns the STEP Entity ID (the #number) from a STEP file. This is useful for identifying specific instances in error messages.
int id = _sdaiGetEntityId (inst);
The _sdaiGetHeaderDescription() and _sdaiGetHeaderName() functions to allow an SDAI application to access and manipulate the Part 21 file header entities. These entities are defined as follows:
ENTITY file_description; description : LIST [1:?] OF STRING (256); implementation_level : STRING (256); END_ENTITY; ENTITY file_name; name : STRING (256); time_stamp : STRING (256); author : LIST [1:?] OF STRING (256); organization : LIST [1:?] OF STRING (256); preprocessor_version : STRING (256); originating_system : STRING (256); authorization : STRING (256); END_ENTITY;
For example, you can add the author's name of a header field with the following code:
SdaiAggr auth; SdaiInstance name; name = _sdaiHeaderName (model); sdaiGetAttrBN (name, "author", sdaiAGGR, &auth); sdaiInsertByIndex (auth, 0, sdaiSTRING, "Fred Foo");
Low-Level Model API
In the ST-Developer implementation of the SDAI, a model corresponds to a data file. With the support of Edition 2 of Part 21 and the file sections that this provides, this mapping is not a seamless interface between Part 21 and the SDAI interface. For this reason, the SDAI includes a low-level model interface, where a model corresponds to either RoseDesign, or RoseDesignSection. The idea behind the low-level API is to directly use the ROSE interface to read and write files, access design sections, and manage the application life cycle. You can then use the SDAI to access instances, validate rules, and traverse rules. This is especially helpful if you have written utility functions that take SDAI values as parameters, but you want to call then from a ROSE application.
In the following example, the SDAI session, repository, and model do not need to be directly accessed. Instead, the code finds a RoseDesign, and then obtains the corresponding SDAI model.
#include <rose.h> #include <sdai.h> int main () { RoseDesign * des = ROSE.findDesign ("data"); SdaiModel * mod = _sdaiGetModel (des); SdaiSet points = sdaiGetEntityExtentBN (mod, "point"); /* Iterate over the points */ }
The _sdaiGetModel() function can take either a RoseDesign, or a RoseDesignSection. If the function is passed a RoseDesignSection, the model that is created will only refer to the design section. This is how multiple section Part 21 file can be manipulated in the SDAI.
Since a model can be represent either a RoseDesign or a RoseDesignSection, the sdaiGetInstanceModel() function needs to be determine which type of model to return. If the instance is contained in a model that has been explicitly opened with sdaiAccessModel(), the function returns the model corresponding to the instance's RoseDesign. Otherwise, it returns the model that corresponds to the object's design section. This behavior maintains full SDAI semantics for pure SDAI applications, but for hybrid ROSE/SDAI applications, it gets the finest-grained model available or the instance.
Use _sdaiGetInstanceSectionModel() if you need the model for an instance's design section. This function always returns the model corresponding to the design section regardless of whether or not the model was opened through the SDAI, or implicitly through some other method.
Multiple Schemas
The ROSE library supports a list of schema for each model, which the SDAI does not support. In a pure SDAI application, there is a one-to-one correspondence between a model and its schema. When dealing with Part 21 files that have multiple sections with several schemas, you may need to access the model's schema list.
The multiple schema support is handled by _sdaiAddSchema(), which adds a schema a models list of schema, and _sdaiGetSchemaList(), which populates an NPL with the complete set of schemas that the schema contains.
Property Functions
A property is a way for an application to associate additional data with an SDAI instance. By using properties, an application can avoid the traversal costs associated with evaluating an inverse attributes, or making sdaiFindInstanceUsedIn() calls. This can be used, for example, to keep back pointers or to tag an instance with application specific data. The property functions are not specified in Part 24, but rather are an ST-Developer extension to the interface.
A property is represented by the C type void*. The interpretation of the property is entirely up to the application -- it can be a SDAI instance handle, a pointer to an application defined data structure, or the value can be cast to an integer. The values of properties are not saved between sessions.
An instance may have any number of properties attached to it. To distinguish between multiple properties, each property is identified by a property type. Use the _sdaiGetPropType() function to obtain a property type identifier. Each call to this function allocates a new, unique, type identifier, so the return value must be stored in memory locations that will remain accessible for the life of the application. The best way to allocate property types is to do so in the initialization code of your application, then store the values in global variables.
_SdaiPropType name_prop; /* This should be a global variable SdaiAppInstance inst1, inst2; name_prop = _sdaiGetPropType(); /* Find the instances for inst1 and inst2 */ /* Store the properties */ _sdaiSetProp (name_prop, inst1, "testobj1"); _sdaiSetProp (name_prop, inst2, "testobj2"); /* Print the properties out */ printf ("Name=%s\n", (char*)_sdaiGetProp(name_prop, inst1)); printf ("Name=%s\n", (char*)_sdaiGetProp(name_prop, inst2));
After obtaining a type identifier, you can then define properties of instances. The _sdaiSetProp() function sets the value of an instance's property. If the instance already has an existing property of the specified type, the old value is replaced with the new value.
The _sdaiGetProp() function to retrieves the value of a property. The application must then cast this value to the appropriate type to operate on it. This function returns a NULL pointer if the property is currently unset.
To remove a property from an instance, use the _sdaiRemoveProp() function. This function does not clean up any memory referenced by the property. If your property refers to dynamically allocated memory, you should free it before calling _sdaiRemoveProp() to avoid memory leaks in your application.