How to one-to-one correspondence between the nodes fetched in the HoopsExchange code and the SCS node IDs generated by the HoopsCommunicator Converter

Because the AssemblyTree inside the SCS generated by Converter is generated by Converter, and the Node ID in it is also generated by Converter, it is not possible to do business filtering, I want to get a more accurate structure through Exchange, and decide the hierarchy of the assembly structure by myself, but I can’t find a correspondence between the nodes of Exchange and the nodes inside the SCS file, I want the user to select the Exchange nodes above my own generated structure tree, which corresponds to selecting the nodes inside the SCS file. I can’t find the correspondence between Exchange nodes and SCS internal nodes, I want the user to select the Exchange nodes on top of my own generated structure tree, corresponding to the nodes inside the SCS file, usually the default SCS Assembly is too redundant, which is useful for customizing your own BOM in PLM system, do I have to pass the ExchangeID in order to correspond to HE<–>HC’s Node Relationship

Hi @Rui.Zhang03 ! This is possible with the Exchange SC mapping functionality. Here’s a brief getting started guide:

This functionality allows you to create IDs that are consistent between Exchange and SC models.

  1. First, in the Communicator package, update the easy_convert.js file found in quick_start/scripts:

Make sure the following lines are added to the process_args, if not already there:

‘–output_sc’, output_sc_path,

‘–output_scs’, output_scs_path,

‘–output_xml_assemblytree’, output_xml_tree_path,

‘–add_exchange_ids’, true,

‘–output_prc’, output_prc_path

This will give you the SC model and PRC model with linked “Exchange IDs”. I also show the XML assembly tree being exported so you can parse the data for each node’s Exchange ID.

Note that the PRC and SC files must be exported at the same time / part of the same conversion process, or else it will not be possible to get the same IDs (they are uniquely generated during each conversion).

  1. In Exchange, you’ll need to include the macro for HOOPS Publish (this is the package that allows for the ID mapping):


  1. To load the model, you must use A3DAsmModelFileLoadFromPrcFile instead of A3DAsmModelFileLoadFromFile.

  2. Now, the model should be loaded with the Exchange IDs that match what you’d get in Communicator when you call hwv.model.getNodeExchangeId(nodeId). You can find them with A3DPrcIdMapFindEntity().

Here’s a sample .cpp file which simply checks if a particular entity is a Product Occurrence type, and confirming the ID mapping has succeeded:

// ExchangeScMappingSimple.cpp : This file contains the 'main' function. Program execution begins and ends there.
// This is a simple demonstration of using the Exchange SC mapping functionality to confirm whether an entity is a Product Occurrence type.

#include <hoops_license.h>

#define HOOPS_PRODUCT_PUBLISH_ADVANCED //Needed for A3DPrcIdMap functions

#include <A3DSDKIncludes.h>

#include <iostream>
#include <string>

std::string getName(A3DEntity* ntt) {
    std::string name;
    A3DRootBaseData rbd;
    A3D_INITIALIZE_DATA(A3DRootBaseData, rbd);

    if (A3D_SUCCESS == A3DRootBaseGet(ntt, &rbd)) {
        name = rbd.m_pcName ? rbd.m_pcName : "";

    A3DRootBaseGet(nullptr, &rbd);

    return name;

int main()
    A3DBool const is_loaded = A3DSDKLoadLibraryA("X:/HOOPS_Exchange_Publish_2023_SP1_U1/bin/win64_v142/"); // update with your path

    if (!is_loaded) {
        std::cerr << "Unable to load HOOPS Exchange." << is_loaded << std::endl;
        return -1;

    else {
        std::cout << "Loaded." << std::endl;

    A3DStatus const license_status = A3DLicPutUnifiedLicense(HOOPS_LICENSE);
    if (A3D_SUCCESS != license_status) {
        std::cerr << "Unable to license HOOPS Exchange." << std::endl;
        std::cerr << "Status: " << A3DMiscGetErrorMsg(license_status) << std::endl;
        return -1;

    else {
        std::cout << "Licensed." << std::endl;

    A3DStatus const init_status = A3DDllInitialize(A3D_DLL_MAJORVERSION, A3D_DLL_MINORVERSION);
    if (A3D_SUCCESS != init_status) {
        std::cerr << "Unable to initialize HOOPS Exchange." << std::endl;
        std::cerr << "Status: " << A3DMiscGetErrorMsg(init_status) << std::endl;
        return -1;
    else {
        std::cout << "Ready for use." << std::endl;

    A3DRWParamsLoadData load_params;
    A3D_INITIALIZE_DATA(A3DRWParamsLoadData, load_params);

    A3DAsmModelFile* model_file = nullptr;

    // Update with your path to the converter-generated prc file:
    auto const input_file = "X:/HOOPS_Communicator_2023_SP2/quick_start/converted_models/user/prc_models/_micro engine.prc"; //
    A3DStatus load_status = A3DAsmModelFileLoadFromPrcFile(input_file, nullptr, &model_file); // Use this function to load PRC file with Exchange IDs

    if (A3D_SUCCESS != load_status) {
        std::cerr << "Unable to load the specified file: " << input_file << std::endl;
        std::cerr << "Status: " << A3DMiscGetErrorMsg(load_status) << std::endl;
        return -1;
    else {
        std::cout << "Loaded file: " << input_file << std::endl;
        std::cout << getName(model_file) << std::endl;
    A3DPrcIdMap* map = nullptr;

    if (A3D_SUCCESS == A3DPrcIdMapCreate(model_file, &map)) { // Create a map linking Exchange IDs and A3DEntities
        std::cout << "Map success" << std::endl;
    else {
        std::cerr << "Map failed" << std::endl;
        return -1;

    A3DEntity* exchange_entity3 = nullptr, * exchange_entity_father3 = nullptr;

    A3DStatus status = A3DPrcIdMapFindEntity(map,
        "HOUSING(HOUSING.1).c84d3a3194fa594fcda02a26a7f1644da02900" //retrieved this from XML file generated with converter, this entity is a known "product occurrence" type
        , &exchange_entity3, &exchange_entity_father3);

    if (A3D_SUCCESS == status) {
        std::cout << "ID found!" << std::endl;
    else {
        std::cerr << "No id found" << std::endl;
        return -1;

    // Retrieve the entity type, and print "product occurrence" if it's that type:
    A3DEEntityType eType;
    if (A3DEntityGetType(exchange_entity3, &eType) == A3D_SUCCESS) {
        switch (eType) {

        case kA3DTypeAsmProductOccurrence:
            std::cout << "Entity type (enum): " << eType << std::endl;
            std::cout << "  Product occurrence" << std::endl;

            std::cout << "Entity type (enum): " << eType << std::endl;


    A3DPrcIdMapCreate(model_file, 0); // Free the memory

    model_file = nullptr;


    return 0;

Let me know if you have any more questions about this or if I’ve missed anything you are asking about!