Overview

The first two editions of the STEP Part 21 specification dealt with self contained files, with no native ability to reference between files. Some of the STEP information models added entities to describe references, such as document_file for externally referenced CAD shape files, but that was handled at the application layer rather then at the toolkit layer.

Over time STEP and IFC usage became more widespread, files described larger and larger structures, new models like AP242 and IFC4 expanded the range of things that could be described, and gigabyte file sizes became more common. Looking towards the future, new capabilities for addressing Big Product Data would be needed.

In 2012, a third edition of Part 21 was begun to add the ability to reference instances between files using URIs, so that large data sets could be broken across files as best suits the situation without requiring new information model structures. The new edition adds an ANCHOR section that defines externally visible names and a REFERENCE section that defines local handles for instances that exist in other files.

An example is shown below:

ISO-10303-21;
HEADER;
FILE_DESCRIPTION( ... )
FILE_NAME( ... );
FILE_SCHEMA ( ... );
ENDSEC;

ANCHOR;
<tool_tip_facen> = #100;
<tool_tip_usage> = #100;
<tool_tip_unused> = $;
ENDSEC;

REFERENCE;
#1234 = <file://part_Iscar_mill_assembly_246.stp#bottom_face>;
#1235 = <file://part_Iscar_mill_assembly_246.stp#bottom_face_rep>;
ENDSEC;

DATA;
#100= CHAIN_BASED_GEOMETRIC_ITEM_SPECIFIC_USAGE
      ( . . ., #1234, (#1235,#22,#23) . . .);

ENDSEC;
END-ISO-10303-21;

The ROSE library can always read third edition data. When writing files, you can turn on third edition output with the following call. When the max spec version is set to ED3, the file will have an ANCHOR section if objects have names in the RoseDesign nametable. It will have a REFERENCE section if there are any references as described below.

By default, the max spec version is set to ED2, which limits the file to the versions of the spec published in 1994 and 2002.

RoseP21Writer::max_spec_version (PART21_ED3);  // use ED3 features

RoseP21Writer::max_spec_version (PART21_ED2);  // no anchors/references 

Anchors

The API to handle anchors is under development. Currently, objects are given anchors using the RoseDesign name table.

Reference Table

A P21 file reference section is read into memory as a series of RoseReference objects that you can ask about. The objects are special subtypes of RoseStructure with a URI field, and possibly a RoseObject pointer if the reference has been resolved. These are system objects that are kept in a separate area of the design, so they will not normally appear in object traversals.

You can create external references to point to data that lives in an external file and maintain the link to it. Use the rose_ref_make() function to create a new reference, and set the URI that describes the design and anchor of the referenced object.

RoseDesign * d;
RoseReference * ref = rose_ref_make(d, "file://foobar.stp#anchor1");

printf ("The reference URI is %s\n", ref->uri());

if (!ref->resolved())
    printf ("The reference is not resolved\n");
else
    printf ("The referenced object is %p\n", ref->resolved());

When you read a file, you can test if it has external references and then selectively query them and force selected ones to be read and resolved. Future versions of the ROSE library may even let you just read the header, anchor, and reference sections of a file into memory.

The references are kept in a separate RoseDesignSection in the design, just as the header section and data section objects are kept in different sections. The RoseDesign::reference_section() function returns the section. You can traverse all references using a RoseCursor or manually create a reference instead of using rose_ref_make().

RoseDesign * d;
RoseObject * obj;
RoseCursor * objs;
RoseReference * ref;

/* print all refs in the file */
objs.traverse(d->reference_section());
objs.domain(ROSE_DOMAIN(RoseReference));
while ((obj = cur.next()) != 0) {
    ref = ROSE_CAST(RoseReference,obj);
    printf ("Reference to %s\n", ref-> uri());
}


/* equivalent to rose_ref_make() */
ref = pnewIn(d->reference_section()) RoseReference;
ref-> uri ("file://foobar.stp#anchor1");

Reference Usage

When a field contains an external reference, we associate a RoseReference with the field to give destination URI. You can call rose_ref_get() to see if a reference is present, and you can call rose_ref_put() to associate a reference with a particular attribute.

When a reference is unresolved, the C++ access functions for the field (EXPRESS compiler generated ones and RoseObject::getObject) return null. After the reference is resolved, the C++ access functions return the pointer to the resolved object.

There are several versions of the function below. References on entities need an attribute name or pointer. References on aggregates use an index. Select types can only contain a single value so references on them do not require an attribute.

RoseReference * rose_ref_get(
    RoseObject *	obj,
    RoseAttribute * 	att
    );

RoseReference * rose_ref_get(
    RoseObject *	obj,
    const char * 	attname
    );

RoseReference * rose_ref_get(
    RoseAggregate *	obj,
    unsigned 		idx
    );

RoseReference * rose_ref_get(
    RoseUnion *		obj
    );


/* example */
stp_product_definition_formation * pdf;
stp_product * p = pdf-> of_product();

if (!p) {
    RoseReference * ref = rose_ref_get (pdf,"of_product");
    printf ("product is reference to %s\n", ref->uri());
}

To construct and use an external reference, first create the reference object with the URI, and then assign it to a field using the rose_ref_put() function. This will also give the field the resolved value of the reference (if not null).

There are several versions of the rose_ref_put() function which follow the same pattern as rose_ref_get(). Assigning a reference to entities needs an attribute, aggregates needs an index, and selects do not need any additional information.


ROSE_EXTERN RoseRefUsage * rose_ref_put(
    RoseReference * 	ref,
    RoseObject *	obj,
    RoseAttribute * 	att
    );

ROSE_EXTERN RoseRefUsage * rose_ref_put(
    RoseReference * 	ref,
    RoseObject *	obj,
    const char * 	attname
    );

ROSE_EXTERN RoseRefUsage * rose_ref_put(
    RoseReference * 	ref,
    RoseAggregate *	obj,
    unsigned 		idx
    );

ROSE_EXTERN RoseRefUsage * rose_ref_put(
    RoseReference * 	ref,
    RoseUnion *		obj
    );



/* example */
stp_product_definition_formation * pdf;

RoseReference * ref = rose_ref_make(
    pdf->design(), "file://foobar.stp#anchor1"
    );

rose_ref_put (ref, pdf, "of_product");

Reference resolution will happen manually. We will provide functions for automating the process, but these are still under development. In particular, we will need a functions to push out the resolved value of a reference, to traverse a design and build a reference table for objects in memory, but in other designs, and other possibilities.

The references are linked to the fields that use them by the RoseRefUsage structure. This also links a data object to all references used by its fields. This usage object can have some additional information associated with it such as the file line number that the reference originally appeared on.