iTwin / itwinjs-core

Monorepo for iTwin.js Library
https://www.itwinjs.org
MIT License
600 stars 210 forks source link

Failed to merge imodels using transformer related methods #6487

Closed MYDJH closed 5 months ago

MYDJH commented 6 months ago

"I encountered some issues while using the transformer to merge two iModels. When executing the method, I encountered error messages. I tried several approaches to merge, but all of them failed."

Attempting solutions 1:

async count(iModelDb: IModelDb, classFullName: string): Promise<number> {
    return iModelDb.withPreparedStatement(
      `SELECT COUNT(*) FROM ${classFullName}`,
      (statement: ECSqlStatement): number => {
        return DbResult.BE_SQLITE_ROW === statement.step()
          ? statement.getValue(0).getInteger()
          : 0;
      }
    );
  }
  async mergeFrist(req: any, res: any): Promise<string> {
    IModelHost.startup();
    const SourceFileName1 = "C:\\Users\\wwr\\Downloads\\a\\a.bim";
    const sourceFileName2 = "C:\\Users\\wwr\\Downloads\\b\\b.bim";
    const targetFile = "C:\\Users\\wwr\\Downloads\\c\\c.bim";

    const SrouceModel1 = SnapshotDb.openFile(SourceFileName1);
    const SrouceModel2 = SnapshotDb.openFile(sourceFileName2);

    const num1 = await this.count(SrouceModel1, "BisCore.Element"); 
    const num2 = await this.count(SrouceModel2, "BisCore.Element"); 
    console.log("num1 = " + num1.toString()); //Output 1194
    console.log("num2 = " + num2.toString()); //Output 11943
    if (IModelJsFs.existsSync(targetFile)) {
      IModelJsFs.removeSync(targetFile);
    }
    const targetDbProps: CreateIModelProps = {
      rootSubject: { name: "Clone-Target" },
      ecefLocation: SrouceModel1.ecefLocation,
    };
    // Create a blank Imodel
    const targetDb = SnapshotDb.createEmpty(targetFile, targetDbProps);
    const transformer = new IModelTransformer(SrouceModel1,targetDb);
    await transformer.processSchemas();//Error Message:Error importing schema
    await transformer.processAll();//Error Message:attempt to clone with unknown class
    targetDb.saveChanges();

    const num4 = await this.count(targetDb, "BisCore.Element");

    SrouceModel1.close();
    targetDb.close();
    SrouceModel2.close();

    IModelHost.shutdown();
    return "Frist";
  }

Error Message:Error importing schema; Error Message:attempt to clone with unknown class;

Attempting solutions 2:

  async mergeSecond(req: any, res: any): Promise<string> {
    IModelHost.startup();
    const SourceFileName1 = "C:\\Users\\wwr\\Downloads\\a\\a.bim";
    const sourceFileName2 = "C:\\Users\\wwr\\Downloads\\b\\b.bim";
    const SrouceModel1 = SnapshotDb.openFile(SourceFileName1);
    const SrouceModel2 = SnapshotDb.openFile(sourceFileName2);
    const num1 = await this.count(SrouceModel1, "BisCore.Element"); //1194
    const num2 = await this.count(SrouceModel2, "BisCore.Element"); //11943
    const targetFileName = "C:\\Users\\wwr\\Downloads\\c\\c.bim";
    if (IModelJsFs.existsSync(targetFileName)) {
      IModelJsFs.removeSync(targetFileName);
    }
    const targetDbProps: CreateIModelProps = {
      rootSubject: { name: "Clone-Target" },
      ecefLocation: SrouceModel1.ecefLocation,
    };

    // Create a readable and writable file using a seed
    let TargetIModel1 = SnapshotDb.createFrom(SrouceModel1, targetFileName);
    const parentSubject = TargetIModel1.elements.getRootSubject().id;
    const subjectId1 = Subject.insert(TargetIModel1, parentSubject, "subject1");
    const subjectId2 = Subject.insert(TargetIModel1, parentSubject, "subject2");

    if (TargetIModel1.isReadonly) {
      console.log("readable");
    } else {
      console.log("readable and writable");
    }
    const transformer = new IModelTransformer(SrouceModel2,TargetIModel1,{
      targetScopeElementId: subjectId1,
    });
    await transformer.processSchemas();
    await transformer.processAll();//Error Error: attempt to clone with unknown class
    TargetIModel1.saveChanges();
    const num4 = await this.count(TargetIModel1, "BisCore.Element");
    SrouceModel1.close();
    SrouceModel2.close();
    TargetIModel1.close();
    return "Second";
  }
}

The actual error location is reported when executing the transformer. processAll() method

  public async processAll(): Promise<void> {
    Logger.logTrace(loggerCategory, "processAll()");
    this.logSettings();
    this.validateScopeProvenance();
    await this.initialize();
    await this.exporter.exportCodeSpecs();
    await this.exporter.exportFonts();
    await this.exporter.exportChildElements(IModel.rootSubjectId); Subject
    await this.exporter.exportModelContents(IModel.repositoryModelId, Element.classFullName, true);
    await this.exporter.exportSubModels(IModel.repositoryModelId); //Actual Error Position
    await this.exporter["exportAllAspects"](); 
    await this.exporter.exportRelationships(ElementRefersToElements.classFullName);
    await this.processDeferredElements();
    if (this.shouldDetectDeletes()) {
      await this.detectElementDeletes();
      await this.detectRelationshipDeletes();
    }

    if (this._options.optimizeGeometry)
      this.importer.optimizeGeometry(this._options.optimizeGeometry);

    this.importer.computeProjectExtents();
    this.finalizeTransformation();
  }

Error Message:Error Error: attempt to clone with unknown class

"I sincerely hope for your assistance in analyzing whether the problem resides within my code or the environment. Additionally, any insights or suggestions you may have for an improved implementation plan would be greatly appreciated. Thank you for your consideration and help."

The file I want to merge is. mergeFile.zip

pmconne commented 6 months ago

Sounds like the same or similar issue as #6332. @MichaelBelousov, you indicated you would investigate further - did you find anything?

@MYDJH you can expect better results by synchronizing multiple input files (.rvt, .dgn, etc) to a single iModel than by trying to merge them after converting each to a separate iModel.

MichaelBelousov commented 6 months ago

Yes @pmconne is right, this is very likely a duplicate of #6332.

The investigation yielded that while we work on making merging of dynamic schemas more automated, you must manually merge the two divergent dynamic schemas and then remap all instances to the new one during the merge transformation, using the Transformer's JavaScript API. Please let me know if you would like me elaborate.

I do recommend to try what @pmconne mentioned, where possible synchronize the original source into one iModel.

MYDJH commented 6 months ago

If you could provide a detailed explanation, I would greatly appreciate it. @MichaelBelousov

MYDJH commented 6 months ago

Sounds like the same or similar issue as #6332. @MichaelBelousov, you indicated you would investigate further - did you find anything?

@MYDJH you can expect better results by synchronizing multiple input files (.rvt, .dgn, etc) to a single iModel than by trying to merge them after converting each to a separate iModel.

I didn't quite understand your meaning. Could you please provide a detailed explanation incorporating itwin's API or pseudocode? I truly appreciate your advice and response

MichaelBelousov commented 6 months ago

@MYDJH please consider pmconne's approach first. It really is usually much easier, so before I do an in-depth explanation of manual schema merging which can be bug-prone and tedius, please consider that.

Since you said you don't understand his meaning, I'll try to explain it, but without code because it shouldn't be necessary.

How are you connecting/synchronizing source files (.dgn,.rvt) into these two original iModels that you want to merge? Can you create a new iModel and connect the same source files into the new iModel, instead of a programmatic post-connection merge of two separate iModels? How to do this would be based on how you created the original iModels.

MYDJH commented 6 months ago

@MichaelBelousov "Thank you very much for your response. I have browsed through the official website of itwin.js and some test demos, but I still haven't figured out how to implement it. As you mentioned last time, @pmconne's approach involves creating an empty iModel and then obtaining the SnapshotDb of the iModel I want to merge. However, I didn't understand how to achieve synchronization for iModels. In this field, my understanding is limited. If you have the time, I hope you can provide more hints or insights. Looking forward to your next response."

MYDJH commented 6 months ago

Using the iTwin Snapshot lightweight conversion tool, I generated multiple files into a single model (IModel). However, I've noticed that I need to switch to view the corresponding model views. Is there a way to structurally merge them so that they can all be displayed in a single view?Thank you for viewing。 5356AB27D231DE7452DB9A3756551561

pmconne commented 6 months ago

The desktop starter app you're using looks pretty bare-bones. Most viewer applications include a tree view that allows you to turn on and off the visibility of individual models. Alternatively, you can change the visibility programmatically using the view's model selector. You can create a view that displays all the models using ViewCreator3d.

MYDJH commented 6 months ago

"Thank you very much for your help."@pmconne