Skip to main content
Version: v5.0

importModel

Calling importModel from orchestrator step#

The process of importing the model composite from the Bimpk file requires an orchestrator which runs the importModel script in the iaf_import_model Object Model API script.

User interface one-click orchestrator via ProjectSetup component#

For the user interface one-click project setup, the SetupProject component already runs an orchestrator that includes all steps, including the model import step:

async function runOrchestrator() {  let orchestratorConfig = await IafScriptEngine.addDatasource({    _name: "Setup Project",    _description: "Run all scripts needed for setup",    _namespaces: ctx._namespaces,    _userType: "setup_runner",    _params: {      tasks: [        //tasks before        {          _sequenceno: 4,          _name: "Importing Bimpk",          _orchcomp: "default_script_target",          _actualparams: {            userType: "iaf_import_model",            _scriptName: "importModel", // the named script to run in the file above          },        },        //tasks after      ]    }  })}

VS code extension orchestrator setup#

For the VS code extension setup, the importModelFromBimpk script creates a single-step orchestrator.

The script calls IafScriptEngine.addDatasource to create a new orchestrator that calls the importModel function from the iaf_import_model Object Model API script:

let bimpkDatasourceResult = await IafScriptEngine.addDatasource(  {    _name: "BIMPK Import",    _description: "BIMPK Import",    _namespaces: ctx._namespaces,    _userType: "bimpk_import",    _schemaversion: "2.0",    _params: {      tasks: [        {          name: "default_script_target",          _actualparams: {            userType: "iaf_import_model", //OMAPI script            _scriptName: "importModel", //script function          },          _sequenceno: 1,        },      ],    },  },  ctx);

The script runs the orchestrator with IafDataSource.runOrchestrator:

    if (bimpkDatasourceResult) {      const params = {        orchestratorId: bimpkDatasourceResult.id      }      const orchResult = await PlatformApi.IafDataSource.runOrchestrator(        bimpkDatasourceResult.id,         params,         ctx      );

The script uses IafDataSource.getOrchRunStatus to get the status of the orchestrator run:


      let orchRunResult = await PlatformApi.IafDataSource.getOrchRunStatus(orchResult.id, ctx);          console.log(`orchRunResult`, orchRunResult[0].orchrunsteps)
          let orchStepRunStatus = orchRunResult[0].orchrunsteps;

It sets an interval of 10000 milliseconds to poll the orchestrator run for a status update until the run finishes:

      //poll based on in run id until finished      let interval = setInterval(async () => {        let errStatus = _.filter(orchStepRunStatus, run_status => {          return run_status._status === "ERROR";        });        let queuedStatus = _.filter(orchStepRunStatus, run_status => {          return run_status._status === "QUEUED";        });        let runningStatus = _.filter(orchStepRunStatus, run_status => {          return run_status._status === "RUNNING";        });
        console.log(`errStatus`, errStatus)        console.log(`queuedStatus`, queuedStatus)        console.log(`runningStatus`, runningStatus)
        if (!_.isEmpty(errStatus) || (_.isEmpty(queuedStatus) && _.isEmpty(runningStatus))) {          if (_.isEmpty(errStatus)) {            orchStepRunStatus.forEach((step) => step.status = 'COMPLETED');          }          //kills the polling when import is complete          clearInterval(interval);        }        orchRunResult = await PlatformApi.IafDataSource.getOrchRunStatus(orchResult.id, ctx);        orchStepRunStatus = orchRunResult[0].orchrunsteps;      }, 10000);
      console.log(`orchRunResult`, orchStepRunStatus)      }  }

importModel#

First, the script establishes if the bimpk _fileId and _fileVersionId are passed and if not, it fetches them with the local getBimpkFile function:

async function importModel(params, libraries, ctx) {  return new Promise(async (resolve, reject) => {    try {      //checks if the bimpk `_fileId` and `_fileVersionId` are passed      if (        !params?.actualParams?._fileId &&        !params?.actualParams?._fileVersionId      ) {        //if there are no _fileId and _fileVersionId values passed,         //the getBimpkFile function gets the file object and sets the values        const fileObj = await getBimpkFile(params, libraries, ctx);        if (fileObj) {          params.actualParams._fileId = fileObj.file_id;          params.actualParams._fileVersionId = fileObj.fileVersion_id;        }      }

The getBimpkFile searches for a file with "bimpk" in the _name property and then gets the latest version of that file, returning the file id and version id:

const getBimpkFile = async (params, libraries, ctx) => {  const { PlatformApi } = libraries;  //search criteria used in the request  let criteria = { _name: ".*bimpk" };  let fileItem = await PlatformApi.IafFileSvc.getFiles(criteria, ctx);  if (!fileItem) return;  console.log("fileItem", fileItem);  let file_id = fileItem._list[0]._id;  //uses the file id of the found bimpk file to get its versions  let fileVersion = await PlatformApi.IafFile.getFileVerisons(file_id, ctx);  //takes the latest version  const fileVersion_id = fileVersion._list[0]._id;  console.log("fileVersion", fileVersion);  console.log(    "Completed bimpkUploadAndImport.",    "onSuccess ==== fileVersion.",    JSON.stringify(fileVersion)  );  return {    file_id,    fileVersion_id,  };};

Returning to the importModel function, the script gets the file name and file extension

//Helper gets the file's metadataconst helper = new Helper(params, libraries);const { filename, ext } = await helper.getFileMetaData(  params?.actualParams?._fileId);
params.filename = filename;params.ext = ext?.toLowerCase();

Next, the script validates that the model's _fileId, _fileVersionId, and file extension:

//validates const validateInput = new InputValidation(params, libraries, ctx);await validateInput.validate();console.timeEnd(`${params.orchRunId}: validation`);
let result = {};

If it is a valid bimpk file, the script creates a new BimpkImport class object and initializes the import–otherwise, it creates a new SgpkImport class object and initializes the import:

if (params.ext == "bimpk") {  console.time(`${params.orchRunId}: BimpkImport`);  const bimpkImport = new BimpkImport(params, libraries, ctx);
  result = await bimpkImport.initialize();  console.timeEnd(`${params.orchRunId}: BimpkImport`);} else {  const sgpkImport = new SgpkImport(params, libraries, ctx);  result = await sgpkImport.initialize();}
params.result = result;console.log(result);

For more information on the BimpkImport class, see BimpkImport.initialize.

Next, the script initializes a Datasource target for Structural Coordination Zones to process the graphics:

  console.time(`${params.orchRunId}: SczImport`);  const sczImport = new SczTarget(params, libraries, ctx);  await sczImport.intialize();  console.timeEnd(`${params.orchRunId}: SczImport`);
  const res = {    filecolid: result.filecolid,    viewcolid: result.viewcolid,    compositeitemid: result.compositeitemid,  };  if (result.myCollections) {    res.myCollections = result.myCollections;  }  console.log(`Model import is complete, ${params.orchRunId}`);
  resolve(res);} catch (error) {  reject(error);}

BimpkImport.initialize#

In BimpkImport.initialize, the createBIMCollections function creates a NamedCompositeItem for the BIM model, as well as NamedUserCollections for the following:

  • Elements
  • Element props
  • Type elements
  • Geometry files
  • Geometry views
  • Data cache

Once created, these collections are related to the model NamedCompositeItem:

  const relatedColls = await IafScriptEngine.addRelatedCollections(    {      namedCompositeItemId: bimModelId,      relatedCollections: [        model_els_coll._userItemId,        model_els_props_coll._userItemId,        model_type_el_coll._userItemId,        data_cache_coll._userItemId,        model_geom_file_coll._userItemId,        model_geom_views_coll._userItemId,      ],    },    ctx  );

Downloading and extracting the Bimpk file#

The model file is downloaded and deconstructed:

  const zipModelFile = await ModelFileReader.downloadAndUnzipModelFile(param, ctx);  console.log('zipModelFile: ', JSON.stringify(zipModelFile))  const { bimFilePath, manifest } = zipModelFile;  const { files, occurrences } = manifest;

For more information on the structure of a Bimpk file, see Bimpk file structure.

Next, the function loops through the models files and their occurrences to reach the data objects. ModelFileReader.getModelBatchlet reads the model batchlets as JSON data and extractBimpk is called to transform this data for platform consumption. createRelatedItemsAndRelationships relates these object to the previously created collections.

let index = -1;for (const model of files) {  for (const occ of occs) {    const filePath = bimFilePath + "/" + occ.data.objects;    let newOccurrence = true;    let letsLoop = true;
    while (letsLoop) {      const { bimBatch, endOfFile } =        await ModelFileReader.getModelBatchlet(          filePath,          this.params.orchRunId        );      await this.#extractBimpk(model.name, bimBatch, newOccurrence);      await this.#createRelatedItemsAndRelationships(        newOccurrence,        myCols      );    }  }}

For more information on the extractBimpk and createRelatedItemsAndRelationships functions, see extractBimpk and createRelatedItemsAndRelationships.

extractBimpk#

First, the elements are constructed:

  const objects = [];
  for (const obj of bimBatch.objects) {    const batchObj = {      package_id: obj.id,      type_id: obj.type,      relationships: obj.relationships,      source_id: obj.sourceId,      properties: obj.properties,      source_filename: fileName,    };    console.log('BimpkImport extractBimpk batchObj: ', JSON.stringify(batchObj))    objects.push(batchObj);  }

Next the types are extracted:

  if (newOccurrence) {    ...    for (const { id, name, sourceId, properties } of bimBatch.types) {      types.push({ id, name, source_id: sourceId, properties });    }  }

Separate objects are created for the element properties:

  let _myProperties = [];
  for (let obj of objects) {    obj._id = await IafScriptEngine.newID("mongo", { format: "hex" });
    const _myVal = this.#typeMap.get(obj.type_id);    obj.dtCategory = _myVal.dtCategory;    obj.dtType = _myVal.dtType;
    if (_myVal.hasOwnProperty("baType")) {      obj.baType = _myVal.baType;    }
    if (obj.properties?.length > 0) {      obj.properties.forEach((prop) => {        const _myProp = this.#propMap.get(prop.id);        prop.dName = _myProp.dName;      });
      obj.properties = this.#groupBy(obj.properties, "dName");    } else {      obj.properties = {};    }    const myProperty = {      _id: obj._id,      properties: obj.properties    }    _myProperties.push(myProperty);    delete obj.properties;  }

These objects are stored in Script Engine variables:

  const setVars = [    IafScriptEngine.setVar("properties", _myProperties),    IafScriptEngine.setVar("manage_els", objects),  ];
  if (newOccurrence) {    setVars.push(IafScriptEngine.setVar("manage_type_els", types));  }

The elements and types are mapped as related and stored in a Script Engine variable:

  await IafScriptEngine.setVar(    "manage_el_to_type_relations",    this.#mapItemsAsRelated(objects, types, "type_id", "id")  );

createRelatedItemsAndRelationships#

The objects to create as RelatedItems are fetched from the Script Engine variables:

  const getVarCalls = [    await IafScriptEngine.getVar("manage_els"),    await IafScriptEngine.getVar("properties"),    await IafScriptEngine.getVar("manage_el_to_type_relations"),    await IafScriptEngine.getVar("manage_type_els")  ];  const [manage_els, properties, relations, manage_type_els] =    await Promise.all(getVarCalls);

Theses objects are then related to the collections:

  const callList = [    IafScriptEngine.createItemsBulk(      {        _userItemId: _colls.model_els_coll._userItemId,        _namespaces: this.ctx._namespaces,        items: manage_els,      },      this.ctx    ),    IafScriptEngine.createItemsBulk(      {        _userItemId: _colls.model_type_el_coll._userItemId,        _namespaces: this.ctx._namespaces,        items: manage_type_els,      },      this.ctx    )  ];
  const results = await Promise.all(callList);