IFC SDK and SDAI: Read an Application Data Block from a Set

Egor Vorobyov

December 17, 2020

IFC SDK supports the Standard Data Access Interface (SDAI) which provides a low-level API for manipulating data defined with the EXPRESS Schema. This article describes how to read an application data block from a set.

For details about generally working with SDAI in IFC SDK, see this article.

Assume that your IFC model contains the following instance:

#94 = IFCUNITASSIGNMENT ((#43,#45,#46,#50,#52,#55,#57,#58,#60,#64,#69,#71,#72, #73,#74,#75,#76,#77,#82,#86,#88,#92));

We can see an instance of IfcUnitAssignment. This instance has one field with an aggregate inside. This aggregate is filled by handle IDs. But if we look to the expression scheme, we find that this is the aggregation of select attributes, not instances. So, let’s read it and extract select data.

First, get an application instance from the model by ID. Use _sdaiGetEntityById:

SdaiAppInstance applicationInstance = _sdaiGetEntityById(model, 94);
TEST_ASSERT(applicationInstance != NULL);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

Get an attribute named “units” from the application instance. Check the aggregate — it should not be empty. Call the function sdaiGetMemberCount for the aggregate instance. It should return a value greater than zero.

SdaiAggr  aggregate = NULL;
TEST_ASSERT(sdaiGetAttrBN(applicationInstance, "units", sdaiAGGR, &aggregate) != NULL);

TEST_ASSERT(sdaiGetMemberCount(aggregate) > 0);

Use iterator functionality to get access to aggregate items: sdaiCreateIterator, sdaiBeginning and sdaiNext.

SdaiIterator aggregateIterator = sdaiCreateIterator(aggregate);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);

for (sdaiBeginning(aggregateIterator); sdaiNext(aggregateIterator);)

Create an empty ADB instance using sdaiCreateEmptyADB and pass a pointer to this created instance to sdaiGetAggrByIterator. The sdaiGetAggrByIterator should fill the ADB data from the iterator instance:

SdaiADB adbToGet = sdaiCreateEmptyADB();

TEST_ASSERT(sdaiGetAggrByIterator(aggregateIterator, sdaiADB, &adbToGet) != NULL);

At this point, we should extract the value from the ADB using sdaiGetADBValue. Then we must destroy the ADB to avoid memory leaks:

SdaiInstance  instanceFromAdb = NULL;
TEST_ASSERT(sdaiGetADBValue(adbToGet, sdaiINSTANCE, &instanceFromAdb) != NULL);

sdaiDeleteADB(adbToGet);

There is a simple way to get an ADB value from an aggregate without using the ADB value. We can ask for a typed value from the aggregate using sdaiGetAggrByIterator, and if the ADB value type in the aggregate is convertible to a parameter value type, it returns a valid value:

SdaiInstance  instanceFromAdbSimpleGet = NULL;
TEST_ASSERT(sdaiGetAggrByIterator(aggregateIterator, 
 					    sdaiINSTANCE, 
&instanceFromAdbSimpleGet) != NULL);

To check the received value, compare it with the value received when using the ADB:

TEST_ASSERT(instanceFromAdb == instanceFromAdbSimpleGet);
TEST_ASSERT(sdaiErrorQuery() == sdaiNO_ERR);