Validation Logic#

The Test Orchestrator validates submodels in three main steps:


1. Event-driven Submodel Processing#

The MqttSubscriber listens for submodel creation, update, and deletion events:

client.subscribe(TOPIC_NEW);
client.subscribe(TOPIC_UPDATE);
client.subscribe(TOPIC_DELETE);

On creation/update:

  • Deserializes the submodel

  • Checks the AASX version

  • Validates presence of SemanticId

On deletion:

  • Cleans up related test results in the repository.


2. Validation Logic#

Validation Sequence#

The validation process is illustrated in the sequence diagram below, showing the interactions between the Submodel Repository, Deserializer, Comparator, Recursion Function, SMEComparator, and MQTT/Web UI interface.

ValidationSequence

Deserialization:
Parses all input submodels and templates using IDTA-compatible JSON:

Environment inputEnv = Deserializer.deserializejsonFile(jsonString);

Comparison Logic:
Each input submodel is compared to schema submodels by matching SemanticId:

ComparisonResult result = Comparator.compare(schemaSubmodel, inputSubmodel);

Recursive Validation:
Main logic for comparing submodel elements recursively:

RecursionFunc.compareSubmodelElements(
    schemaSubmodel.getSubmodelElements(),
    inputSubmodel.getSubmodelElements(),
    result
);

This function:

  • Checks multiplicity (One, ZeroToOne, etc.)

  • Verifies qualifiers (required/optional)

  • Compares type, value, and semanticId

  • Recurses into nested SubmodelElementCollections


Multiplicity Checks Example:

if ("One".equals(multiplicity)) {
    SMEComparator.checkMultiplicityOne(schemaElement, inputElementMap, result);
}

All four multiplicity types are supported:

  • One

  • ZeroToOne

  • OneToMany

  • ZeroToMany


3. Test Results & Reporting#

All validation results are written as Submodels in the repository:

ResultSubmodelFactory.addResultToSubmodel(comparisonResult, inputSubmodel);

Example structure of a result:

{
  "ComparedSubmodelId": "...",
  "SemanticId": "...",
  "Errors": "...",
  "Warnings": "...",
  "Differences": "...",
  "Infos": "..."
}

Handling Edge Cases:#

Unsupported AASX Versions#

ResultSubmodelFactory.addUnsupportedVersionResult(rawJson);

Missing SemanticId#

if (submodel.getSemanticId() == null) {
    ResultSubmodelFactory.addUnsuccessfulResultToSubmodel(submodel);
    return;
}

Full Comparison Function Example#

public static ComparisonResult compare(Submodel schemaSubmodel, Submodel inputSubmodel) {
    ComparisonResult result = new ComparisonResult();
    RecursionFunc.compareSubmodelElements(
        schemaSubmodel.getSubmodelElements(),
        inputSubmodel.getSubmodelElements(),
        result
    );
    return result;
}

Handling New Submodel Event#

private void processSubmodel(String submodelJson) {
    if (!isAASXv3Format(submodelJson)) {
        ResultSubmodelFactory.addUnsupportedVersionResult(submodelJson);
        return;
    }

    Submodel submodel = deserializer.read(submodelJson, Submodel.class);

    if (submodel.getSemanticId() == null) {
        ResultSubmodelFactory.addUnsuccessfulResultToSubmodel(submodel);
        return;
    }

    SubmodelFactory.processReceivedSubmodel(submodel);
}