The Common Data Access (CDA) mechanism is used by various applications (e.g., OpenIFCViewer, ExIfcCDAWalker, etc.)
to access structural information and object properties of imported (open) databases in the most abstract manner without the need to know about the imported format.
The structure is provided as a tree, where the nodes of the tree are connected with imported instances.
Specific levels of the hierarchy in the tree depend on the domain, as well as the connection of this perceived hierarchy with the database content.
CDA technology is based on the RxProperties
mechanism. To get more information,
see Work with Non-COM Properties.
Before working with CDA, make sure the corresponding RxProperties
module is built for your current ODA version.
To use the CDA mechanism, call the odIfcInitialize()
global function with the first parameter set to true
.
This function initializes the IfcCore
module, loads the RxProperties
module and performs the necessary calls for working with Common Data Access.
You can also check whether CDA is enabled by using one of the following methods:
odIfcIsCDAInitialized
global function which returns true
if CDA is initialized and can be used for IFC data.OdIfcCoreModule::isCDAEnabled
method which returns true
if the Common Data Access interface is available for the core IFC module.The rest of the API for working with CDA is part of Kernel SDK. It is strongly recommended to read the Common Data Access section before proceeding with the example.
The ExIfcCDAWalker
example application shows how to traverse through the spatial structure of an IFC model from a specified file using the CDA mechanism.
Example location: Ifc/Examples/ExIfcCDAWalker/ExIfcCDAWalker.cpp
. The example has two parts: part one — preparations and application setup (points 1-7),
and part two — traversing through the CDA tree (point 8 and its sub-points).
Overview of the application:
AbstractNotifier
interface defines the notification methods to be called in the following situations:
Hierarchy
attribute is found.Geometrical Representation Representation
for the currently walked node is found.
class AbstractNotifier
{
public:
virtual ~AbstractNotifier() {};
virtual void onLevelBegin(const OdRxObject *inst) = 0;
virtual void onLevelEnd(const OdRxObject *inst) = 0;
virtual void onHierarchyAttributeFound(const OdRxObject *inst, OdRxMember *member) = 0;
virtual void onHasRepresentation(const OdRxObject *inst, const OdRxObject *reprInst) = 0;
};
CDATreePrinter
class is designed
to print information about nodes that are traversed. This class inherits AbstractNotifier
and implements the notification methods defined in AbstractNotifier
.
class CDATreePrinter : public AbstractNotifier
{
int m_level;
public:
CDATreePrinter()
: m_level(0)
{}
virtual void onLevelBegin(const OdRxObject *inst)
{
++m_level;
odifcPrintConsoleString(L"%sHierarchy level: %d\n", tabulation(m_level).c_str(), m_level);
const OdString &className = inst->isA()->name();
odifcPrintConsoleString(L"%sClass: %s\n", tabulation(m_level).c_str(), className.c_str());
OdString entityName;
OdRxPropertyPtr propName = OdRxMemberQueryEngine::theEngine()->find(inst, L"Name");
if (!propName.isNull())
{
OdRxValue valName;
(eOk == propName->getValue(inst, valName)) &&
(valName >> entityName);
}
odifcPrintConsoleString(L"%sName: `%s`\n", tabulation(m_level).c_str(), entityName.c_str());
}
virtual void onLevelEnd(const OdRxObject *inst)
{
--m_level;
}
virtual void onHierarchyAttributeFound(const OdRxObject *inst, OdRxMember *member)
{
const OdString &propName = member->name();
odifcPrintConsoleString(L"%sHierarchy property name: %s\n", tabulation(m_level).c_str(), propName.c_str());
}
virtual void onHasRepresentation(const OdRxObject *inst, const OdRxObject *reprInst)
{
const OdString &reprClassName = reprInst->isA()->name();
odifcPrintConsoleString(L"%sCan be vectorized here (Representation: %s)\n", tabulation(m_level).c_str(), reprClassName.c_str());
// vectorize(inst); // not reprInst
}
protected:
OdString tabulation(int tab)
{
if (tab < 0)
tab = 0;
OdString res;
while (tab > 0)
{
res += OD_T(" ");
--tab;
}
return res;
}
};
CDAWalker
class is designed to traverse the CDA tree and has a pointer to a notifier object as a private field.
The public method run()
walks through the CDA tree.
class CDAWalker
{
AbstractNotifier *m_pNotifier;
public:
CDAWalker(AbstractNotifier *notifier)
: m_pNotifier(notifier)
{}
~CDAWalker()
{
delete m_pNotifier;
}
void run(const OdRxObject *inst)
{
return walkCDA(inst);
}
protected:
void walkCDA(const OdRxObject *inst)
{
...
}
};
main
function, and get command line arguments. The command line argument
should be a full path to the IFC file for which you want to print CDA tree information. Also, display information about the application to the console.
#if defined(OD_USE_WMAIN)
int wmain(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif
{
int nRes = 0; // Return value for main
#if defined(TARGET_OS_MAC) && !defined(__MACH__)
argc = ccommand(&argv);
#endif
setlocale(LC_TIME, ""); // set current user locale (not OD_T("C")), for strftime
/**********************************************************************/
/* Create a Services object */
/**********************************************************************/
OdStaticRxObject<MyServices> svcs;
/**********************************************************************/
/* Display the Product and Version that created the executable */
/**********************************************************************/
odPrintConsoleString(L"\nExIfcCDAWalker developed using %ls ver %ls",
svcs.product().c_str(), svcs.versionString().c_str());
...
}// main
/**********************************************************************/
/* Parse Command Line inputs */
/**********************************************************************/
bool bInvalidArgs = (argc != 2);
if (bInvalidArgs)
{
bInvalidArgs = true;
nRes = 1;
}
if (bInvalidArgs)
{
odPrintConsoleString(L"\n\nusage: ExIfcCDAWalker <filename>");
odPrintConsoleString(L"\n<filename> - input .ifc file\n");
return nRes;
}
#if !defined(_TOOLKIT_IN_DLL_)
ODRX_INIT_STATIC_MODULE_MAP();
#endif
IfcCore
module using the odIfcInitialize()
function. Note that the first passed parameter to this function should be true
.
/**********************************************************************/
/* Initialize ODA SDK */
/**********************************************************************/
odrxInitialize(&svcs);
/**********************************************************************/
/* Initialize IfcCore */
/**********************************************************************/
odIfcInitialize(true /* CDA */, false /* No geometry calculation needed */);
CDAWalker
for the specified
file and handles errors, if they occur.
try
{
OdString ifcFileName(argv[1]);
OdIfcFilePtr pIfcFile = svcs.createDatabase();
OdResult res = pIfcFile->readFile(ifcFileName);
if (res == eOk)
{
odPrintConsoleString(L"\nFile opened successfully.");
}
else
{
odPrintConsoleString(L"\nFile open error.");
}
odPrintConsoleString(L"\n");
CDAWalker walker(new CDATreePrinter);
walker.run(pIfcFile);
std::cout << "Press any key.";
getchar();
}
catch(OdError& e)
{
odPrintConsoleString(L"\n\nError: %ls", e.description().c_str());
nRes = -1;
}
catch(...)
{
odPrintConsoleString(L"\n\nUnexpected error.");
nRes = -1;
throw;
}
main()
function, uninitialize modules and SDKs in reverse order.
/**********************************************************************/
/* Uninitialize IfcCore */
/**********************************************************************/
odIfcUninitialize();
/**********************************************************************/
/* Uninitialize ODA SDK */
/**********************************************************************/
odrxUninitialize();
return nRes;
} //main
walkCDA()
method is a protected method of CDAWalker
. It is called from
the run()
method and performs the following actions:
onLevelBegin()
method of a notifier. This method is called each time
a new object hierarchy level is entered. It gets and prints information about the hierarchy level, class name,
and the name property of an entity (this property is part of IfcRoot, e.g., OdIfc4x3_rc3::IfcRoot
).
void walkCDA(const OdRxObject *inst)
{
if (m_pNotifier)
m_pNotifier->onLevelBegin(inst);
...
Representation
attribute of an instance. If such an attribute exists
and has a value, the onHasRepresentation()
method is called. For more information about the
Representation
attribute, see Access to Representation Body Geometry.
The onHasRepresentation()
method finds the class name of the
passed OdRxObject
object (that is a representation instance in this case)
and prints it to the console.
... // void walkCDA(const OdRxObject *inst)
//
// Try to find attribute called Representation, if it isn't null, the instance can be vectorized.
//
OdRxMember *memRepr = OdRxMemberQueryEngine::theEngine()->find(inst, L"Representation");
if (memRepr)
{
if (memRepr->isKindOf(OdRxProperty::desc()))
{
OdRxProperty *propRepr = OdRxProperty::cast(memRepr);
OdRxValue idRepresentation;
if (eOk == propRepr->getValue(inst, idRepresentation))
{
const IOdRxReferenceType *reference = idRepresentation.type().reference();
if (reference)
{
OdRxObjectPtr reprInst = reference->dereference(idRepresentation, IOdRxReferenceType::kForRead);
if (!reprInst.isNull())
{
if (m_pNotifier)
m_pNotifier->onHasRepresentation(inst, reprInst);
}
}
}
}
}
...
Hierarchy
attribute.
When a Hierarchy
attribute is found, the onHierarchyAttributeFound()
method
is called which prints the name of the hierarchy property.
... // void walkCDA(const OdRxObject *inst)
//
// Walk along all Rx Properties of instance and try to find Hierarchy Attribute
//
OdRxMemberIteratorPtr it = OdRxMemberQueryEngine::theEngine()->newMemberIterator(inst);
for (; !it->done(); it->next())
{
OdRxMember *member = it->current();
OdRxAttributeCollection &attrs = member->attributes();
int numAttrs = attrs.count();
for (int i = 0; i < numAttrs; ++i)
{
OdRxAttribute *attr = attrs.getAt(i);
OdRxClass* pRx = attr->isA();
if (pRx->isDerivedFrom(OdRxHierarchyLevelAttribute::desc()))
{
if (m_pNotifier)
m_pNotifier->onHierarchyAttributeFound(inst, member);
if (member->isKindOf(OdRxProperty::desc()))
{
OdRxProperty *prop = dynamic_cast<OdRxProperty*>(member);
OdRxValue idHierarchy;
if (eOk == prop->getValue(inst, idHierarchy))
{
const IOdRxReferenceType *reference = idHierarchy.type().reference();
if (reference)
{
OdGiDrawablePtr pInst = reference->dereference(idHierarchy, IOdRxReferenceType::kForRead);
walkCDA(pInst);
}
}
}
else
if (member->isKindOf(OdRxCollectionProperty::desc()))
{
OdRxCollectionProperty *collection = dynamic_cast<OdRxCollectionProperty*>(member);
OdRxValueIteratorPtr itValues = collection->newValueIterator(inst);
if (!itValues.isNull())
for (; !itValues->done(); itValues->next())
{
OdRxValue treeNodeId = itValues->current();
const IOdRxReferenceType *reference = treeNodeId.type().reference();
if (reference)
{
OdGiDrawablePtr pInst = reference->dereference(treeNodeId, IOdRxReferenceType::kForRead);
walkCDA(pInst);
}
}
}
}
}
}
...
Note: The currently referenced member can be a single property or a collection of properties. To check these cases, use the following code:
// Check if current member is a single property
if(member->isKindOf(OdRxProperty::desc()))
...
// Check if current member is a collection of properties
if(member->isKindOf(OdRxCollectionProperty::desc()))
...
onLevelEnd()
method to decrement the current hierarchy level value when
the current tree node and all its sub-nodes, if they exist, are traversed.
... // void walkCDA(const OdRxObject *inst)
if (m_pNotifier)
m_pNotifier->onLevelEnd(inst);
} //walkCDA
Example of successful application execution:
Additionally, you can use the following code example to process object properties of the CDA tree.
The hierarchy can be started from OdIfcFile
instance, and then continued as in Ifc/Examples/ExIFCCDAWalker
.
Every instance can be checked with this sample code:
OdRxMemberIteratorPtr memberIterator = OdRxMemberQueryEngine::theEngine()->newMemberIterator(inst); // inst is any OdRxClass, including OdDAI::ApplicationInstance
if (memberIterator)
{
OdUInt64 memberCount = memberIterator->size();
for (; !memberIterator->done(); memberIterator->next())
{
OdRxMember *member = memberIterator->current();
OdString name = member->name();
odPrintConsoleString(L"\nAttribute %s: ", name.c_str());
if (member->isKindOf(OdRxProperty::desc()))
{
//
// Process single property
//
OdRxProperty *prop = OdRxProperty::cast(member); // dynamic_cast<OdRxProperty*>(member);
OdRxValue rxVal;
if (eOk == prop->getValue(inst, rxVal))
{
const OdRxValueType &vt = rxVal.type();
if (vt == OdRxValueType::Desc<OdDAIObjectId>::value())
{
OdDAIObjectId idVal = *rxvalue_cast<OdDAIObjectId>(&rxVal);
odPrintConsoleString(L"#%ul", idVal.getHandle());
}
if (vt == OdRxValueType::Desc<OdAnsiString>::value())
{
OdAnsiString strVal = *rxvalue_cast<OdAnsiString>(&rxVal);
odPrintConsoleString(L"'%hs'", strVal.c_str());
}
if (vt == OdRxValueType::Desc<OdString>::value())
{
OdString strVal = *rxvalue_cast<OdString>(&rxVal);
odPrintConsoleString(L"'%s'", strVal.c_str());
}
if (vt == OdRxValueType::Desc<double>::value())
{
double dblVal = *rxvalue_cast<double>(&rxVal);
odPrintConsoleString(L"%.4f", dblVal);
}
if (vt.isEnum())
{
OdAnsiString strVal;
rxVal >> strVal;
odPrintConsoleString(L"'%hs'", strVal.c_str());
}
// ...
}
}
else
if (member->isKindOf(OdRxCollectionProperty::desc()))
{
//
// Process collection
//
OdRxCollectionProperty *collection = dynamic_cast<OdRxCollectionProperty*>(member);
OdRxValueIteratorPtr itValues = collection->newValueIterator(inst);
if (!itValues.isNull())
{
for (; !itValues->done(); itValues->next())
{
OdRxValue rxVal = itValues->current();
// Typed values extraction as for single attribute
}
}
}
}
}
Copyright © 2002 – 2022. Open Design Alliance. All rights reserved.
|