BimRv SDK: What is the Extensible Storage Mechanism?

Evgeniy Tikhonov

March 25, 2021

The extensible storage mechanism allows storing user data directly in model elements. This mechanism is not used within a project or family and therefore does not affect the parameters of a project or family. There is a wide set of model elements that can store user data. A common characteristic of this mechanism is the use of schema-based technology.

According to this approach, to store user data in a model element, the data should be defined with the following objects:

  • Field — Contains data name, type, and unit information and is used as the key to access corresponding data in an entity.
  • Schema — A collection of data field objects.
  • Entity — An object containing data corresponding to a schema that can be inserted into an element.

BimRv SDK supports creating and working with schema-based data structures using the following classes that implement a schema, field builders, and helpers:

  • OdBmESSchemaBuilder — Performs creation of schema definitions.
  • OdBmESFieldBuilder — A helper class that is used with OdBmESSchemaBuilder when creating a new field.
  • OdBmESEntityHelper — A helper class that is used to access the field values of an existing extensible entity.
  • OdBmESSchemaHelper — Allows reading of an existing schema's attributes and fields.
  • OdBmESFieldHelper — A helper class that is used with OdBmESSchemaHelper when reading field attributes and definitions.

BimRv SDK extensible helpers support the following data types:

  • bool
  • OdInt16
  • OdInt32
  • double
  • float
  • OdString
  • OdGuid
  • OdBmObjectId
  • OdGePoint2d
  • OdGePoint3d
  • OdArray
  • OdMap (all types are supported for keys except double, float, OdGePoint2d, and OdGePoint3d)
  • OdBmObject (an instance of another schema)

Using attributes, a schema can be configured to restrict access for reading and/or writing for all users, a specific application vendor, or a specific application from a vendor.

The next sections describe simple usage of extensible storage.

Create a Storage Description

To create a new schema, use the OdBmESSchemaBuilder class:

OdBmESSchemaBuilder schemaBuilder(pDb);
  .setDocumentation(L"WireSpliceLocation schema")

The OdBmESFieldBuilder class should be used for creating fields within the storage. This class contains methods to set field attributes, such as:

  • setDocumentation() — Description of the field.
  • setSubSchemaGUID() — Global unique identifier (GUID) of the schema describing the subentity stored in the field.
  • setUnitType() — Type of units represented by values stored in the field.

Here's an example of how to create two simple fields, one array field and one map field:

schemaBuilder.addSimpleField<bool>(L"BooleanField").setDocumentation(L"Boolean value");
schemaBuilder.addSimpleField<OdGePoint3d>(L"XYZField").setUnitType(OdBm::UnitType::UT_Length).setDocumentation(L"XYZ value");
schemaBuilder.addArrayField<OdGUID>(L"GUIDArrayField").setDocumentation(L"GUID array value");
schemaBuilder.addMapField<OdString, OdString>(L"StringMapField").setDocumentation(L"Map value");

Names of the fields are used to access field data for writing and reading the values. After defining all fields in the schema, call the finish() method to finish the schema builder:

OdTfClass* pSchema = schemaBuilder.finish();

To create a schema instance (entity), call the createEntity() method of the OdBmESEntityHelper class:

OdBmObjectPtr pESEntity = OdBmESEntityHelper::createEntity(pSchema);

Field attributes, as well as schema attributes such as unit type, name, and documentation, cannot be changed after creation.

Set Values

To set new values of an entity field, use the OdBmESEntityHelper class:

OdBmESEntityHelper helper(pESEntity);

helper.set<bool>(L"BooleanField", true);
helper.set<OdGePoint3d>(L"XYZField", OdGePoint3d(20., 21., 22.), OdBm::DisplayUnitType::DUT_METERS);

helper.set<OdArray<OdGUID>>(L"GUIDArrayField", guids);

OdBmMap<OdString, OdString> stringMap;
stringMap[L"key1"] = L"value1";
stringMap[L"key2"] = L"value2";
helper.set<OdBmMap<OdString, OdString>>(L"StringMapField", stringMap);

The display unit type parameter is used for float, double, OdGePoint3d and OdGePoint2d data types only and sets the unit type for the field. No conversions are performed inside this method.

Use clone() in the set() method call of the OdBmESEntityHelper class instance for OdBmObjectPtr:

helper.set<OdBmObjectPtr>(L"EntityField", pESEntity->clone());

The template parameter of the set<>() method must exactly match the type of the field (the field in its turn, is specified at the stage of creating the schema).

Store Data

Applications can attach instances of the schema to any element in a model. To avoid locking elements in shared projects, use the special element represented with the OdBmDataStorage class.

OdBmDataStoragePtr pDataStorageElem = OdBmDataStorage::createObject();

  hStorage = pDb->addElement(pDataStorageElem).getHandle();

Read Data from Storage

The following code example illustrates reading data from the storage helper:

OdBmObjectPtr pESEntity = pDataStorageElem->getESEntity(OdGUID(L"720080CB-DA99-40DC-9415-E53F280AA1F0")); 
OdBmESEntityHelper helper(pESEntity);

bool b = helper.get<bool>(L"BooleanField");
OdGePoint3d xyz = helper.get<OdGePoint3d>(L"XYZField", OdBm::DisplayUnitType::DUT_DECIMETERS);

Data type in get<>() method can be one of the listed in the first section. As a creator of the schema, you should know the type of each field. If not, you can process a value as an OdTfVariant object:

OdTfVariant v;
pESEntity->getProperty(L"BooleanField", v);

You can find an example of converting an OdTfVariant object to a string in BimRv/Examples/ExBimDump, specifically in the BmDumper::dumpVariant method implementation.

The display unit type parameter in the get<> () method is used to convert data into desired units, for example, coordinates for the value set previously to the field are equal to OdGePoint3d(200., 210., 220.):

OdArray<OdGUID>guids = helper.get<OdArray<OdGUID>>(L"GUIDArrayField");
OdBmMap<OdString, OdString> strings = helper.get<OdBmMap<OdString, OdString>>(L"StringMapField");

Read Information Routine

To read the recorded information, get the model element, and determine the storage GUID and field names. To get schemas in a database, use the static listSchemas() method of the OdBmESSchemaHelper class:

std::set<OdGUID> schemas = OdBmESSchemaHelper::listSchemas(pDb);

To access elements in a database that are related to the schema, use the static getElements() method:

OdBmObjectIdArray objects = OdBmESSchemaHelper::getElements(guid, pDb);

Another way to retrieve information from fields is to use OdBmESFieldHelper:

OdBmESFieldHelper fieldHelper(helper.getField(L"BooleanField"));

The field helper object provides methods for accessing field attributes:

  • getDocumentation() — Returns the description of the field.
  • getFieldName() — Returns the name of the field.
  • getContainerType() — Returns the type of the field: simple field containing one value or a container for multiple values.
  • getEntryIndex() — Returns the index of the field.
  • getSubSchemaGUID() — Returns the global unique identifier (GUID) of the schema describing the sub-entity stored in the field.
  • getUnitType() — Returns the type of units represented by values stored in the field.

The OdBmESSchemaHelper class helps to get information about the schema:

OdBmESEntityHelper helper(pESEntity);
OdBmESSchemaHelper schHelper(helper.getSchema());

  • getDocumentation() — Returns the description of the schema.
  • getVendorId() — Returns the string identifier of the third-party vendor that can access entities of the schema under the vendor access level.
  • getSchemaName() — Returns the name of the schema.
  • getSchemaGUID() — Returns the global unique identifier (GUID) of the schema.
  • getReadAccessLevel() — Retrieves the current read access level of the schema.
  • getWriteAccessLevel() — Retrieves the current write access level of the schema.