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

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):

#define HOOPS_PRODUCT_PUBLISH_ADVANCED

  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
#define INITIALIZE_A3D_API

#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;
            break;

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

    }

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

    A3DAsmModelFileDelete(model_file);
    model_file = nullptr;

    A3DDllTerminate();
    A3DSDKUnloadLibrary();

    return 0;
}

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

3 Likes