Skip to content

Commit

Permalink
Merge pull request #66 from carlos-schmidt/config/submodel-only
Browse files Browse the repository at this point in the history
Configuration: Only offer submodels
  • Loading branch information
carlos-schmidt authored Dec 11, 2023
2 parents de86db6 + faf6f99 commit 5956f4b
Show file tree
Hide file tree
Showing 18 changed files with 137 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,30 @@
*/
package de.fraunhofer.iosb.app.aas;

import static java.lang.String.format;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import org.eclipse.edc.spi.EdcException;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import de.fraunhofer.iosb.app.Logger;
import de.fraunhofer.iosb.app.model.aas.*;
import de.fraunhofer.iosb.app.model.aas.CustomAssetAdministrationShell;
import de.fraunhofer.iosb.app.model.aas.CustomAssetAdministrationShellEnvironment;
import de.fraunhofer.iosb.app.model.aas.CustomConceptDescription;
import de.fraunhofer.iosb.app.model.aas.CustomSubmodel;
import de.fraunhofer.iosb.app.model.aas.CustomSubmodelElement;
import de.fraunhofer.iosb.app.model.aas.CustomSubmodelElementCollection;
import de.fraunhofer.iosb.app.model.aas.Identifier;
import de.fraunhofer.iosb.app.util.AASUtil;
import de.fraunhofer.iosb.app.util.Encoder;
import de.fraunhofer.iosb.app.util.HttpRestClient;
Expand All @@ -29,14 +49,6 @@
import io.adminshell.aas.v3.model.impl.DefaultSubmodel;
import jakarta.ws.rs.core.Response;
import okhttp3.OkHttpClient;
import org.eclipse.edc.spi.EdcException;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;

import static java.lang.String.format;

/**
* Communicating with AAS service
Expand Down Expand Up @@ -123,11 +135,11 @@ public Response deleteModel(URL aasServiceUrl, String element) {
* @return AAS model enriched with each elements access URL as string in assetId
* field.
*/
public CustomAssetAdministrationShellEnvironment getAasEnvWithUrls(URL aasServiceUrl)
public CustomAssetAdministrationShellEnvironment getAasEnvWithUrls(URL aasServiceUrl, boolean onlySubmodels)
throws IOException, DeserializationException {
var aasServiceUrlString = aasServiceUrl.toString();

var model = readModel(aasServiceUrl);
var model = readModel(aasServiceUrl, onlySubmodels);
// Add urls to all shells
model.getAssetAdministrationShells().forEach(shell -> shell.setSourceUrl(
format("%s/shells/%s", aasServiceUrlString,
Expand All @@ -139,9 +151,9 @@ public CustomAssetAdministrationShellEnvironment getAasEnvWithUrls(URL aasServic
Encoder.encodeBase64(submodel.getIdentification().getId())));
submodel.getSubmodelElements()
.forEach(elem -> putUrlRec(
format("%s/submodels/%s/submodel/submodel-elements", aasServiceUrlString,
Encoder.encodeBase64(submodel.getIdentification().getId())),
elem));
format("%s/submodels/%s/submodel/submodel-elements", aasServiceUrlString,
Encoder.encodeBase64(submodel.getIdentification().getId())),
elem));
});
model.getSubmodels().forEach(submodel -> AASUtil.getAllSubmodelElements(submodel)
.forEach(element -> element.setSourceUrl(
Expand All @@ -158,36 +170,53 @@ public CustomAssetAdministrationShellEnvironment getAasEnvWithUrls(URL aasServic
/**
* Returns the AAS model.
*/
private CustomAssetAdministrationShellEnvironment readModel(URL aasServiceUrl)
private CustomAssetAdministrationShellEnvironment readModel(URL aasServiceUrl, boolean onlySubmodels)
throws IOException, DeserializationException {
var aasEnv = new CustomAssetAdministrationShellEnvironment();

String shellResponse;
String conceptResponse;
String submodelResponse;
URL shellsUrl;
URL conceptDescriptionsUrl;
URL submodelUrl;
try {
shellResponse =
Objects.requireNonNull(httpRestClient.get(aasServiceUrl.toURI().resolve("/shells").toURL()).body()).string();
submodelResponse =
Objects.requireNonNull(httpRestClient.get(aasServiceUrl.toURI().resolve("/submodels").toURL()).body()).string();
conceptResponse = Objects.requireNonNull(httpRestClient.get(aasServiceUrl.toURI().resolve("/concept" +
"-descriptions").toURL()).body())
.string();
} catch (URISyntaxException e) {
throw new EdcException(e.getMessage());
submodelUrl = aasServiceUrl.toURI().resolve("/submodels").toURL();
shellsUrl = aasServiceUrl.toURI().resolve("/shells").toURL();
conceptDescriptionsUrl = aasServiceUrl.toURI().resolve("/concept-descriptions").toURL();
} catch (URISyntaxException resolveUriException) {
throw new EdcException(
format("Error while building URLs for reading from the AAS Service at %s", aasServiceUrl),
resolveUriException);
}

CustomAssetAdministrationShell[] shells = objectMapper.readValue(shellResponse,
CustomAssetAdministrationShell[].class);
CustomConceptDescription[] conceptDescriptions = objectMapper.readValue(conceptResponse,
CustomConceptDescription[].class);
aasEnv.setSubmodels(readSubmodels(submodelUrl, onlySubmodels));
if (!onlySubmodels) {
aasEnv.setAssetAdministrationShells(readShells(shellsUrl));
aasEnv.setConceptDescriptions(readConceptDescriptions(conceptDescriptionsUrl));
}
return aasEnv;
}

private List<CustomConceptDescription> readConceptDescriptions(URL conceptDescriptionsUrl) throws IOException {

// Because of SMCs "value" field, submodels have to be parsed manually
var conceptResponse = Objects.requireNonNull(httpRestClient.get(conceptDescriptionsUrl).body()).string();

return Arrays.asList(objectMapper.readValue(conceptResponse, CustomConceptDescription[].class));
}

// First, parse into full admin-shell.io submodels:
private List<CustomAssetAdministrationShell> readShells(URL shellsUrl) throws IOException {
var shellHttpResponse = Objects.requireNonNull(httpRestClient.get(shellsUrl).body()).string();

return Arrays.asList(objectMapper.readValue(shellHttpResponse, CustomAssetAdministrationShell[].class));
}

private List<CustomSubmodel> readSubmodels(URL submodelUrl, boolean onlySubmodels)
throws IOException, DeserializationException {
var submodelHttpResponse = Objects.requireNonNull(httpRestClient.get(submodelUrl).body()).string();

// First, parse into "full" admin-shell.io submodels:
JsonDeserializer jsonDeserializer = new JsonDeserializer();
List<DefaultSubmodel> submodels = jsonDeserializer.readReferables(submodelResponse, DefaultSubmodel.class);
List<DefaultSubmodel> submodels = jsonDeserializer.readReferables(submodelHttpResponse, DefaultSubmodel.class);

// Now, create custom submodels from the data of the full submodels
// Now, create customSubmodels from the "full" submodels
List<CustomSubmodel> customSubmodels = new ArrayList<>();
for (Submodel submodel : submodels) {
var customSubmodel = new CustomSubmodel();
Expand All @@ -197,20 +226,14 @@ private CustomAssetAdministrationShellEnvironment readModel(URL aasServiceUrl)
customSubmodel.setIdentification(customIdentification);

customSubmodel.setIdShort(submodel.getIdShort());

// Recursively add submodelElements
var customElements = AASUtil.getCustomSubmodelElementStructureFromSubmodel(submodel);
customSubmodel.setSubmodelElements((List<CustomSubmodelElement>) customElements);

if (!onlySubmodels) {
// Recursively add submodelElements
var customElements = AASUtil.getCustomSubmodelElementStructureFromSubmodel(submodel);
customSubmodel.setSubmodelElements((List<CustomSubmodelElement>) customElements);
}
customSubmodels.add(customSubmodel);
}
var aasEnv = new CustomAssetAdministrationShellEnvironment();

aasEnv.setAssetAdministrationShells(Arrays.asList(shells));
aasEnv.setSubmodels(customSubmodels);
aasEnv.setConceptDescriptions(Arrays.asList(conceptDescriptions));

return aasEnv;
return customSubmodels;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ public Response handleRequest(RequestType requestType, URL url, String... reques
* @throws DeserializationException AAS from service could not be deserialized
* @throws IOException Communication with AAS service failed
*/
public CustomAssetAdministrationShellEnvironment getAasModelWithUrls(URL aasServiceUrl)
public CustomAssetAdministrationShellEnvironment getAasModelWithUrls(URL aasServiceUrl, boolean onlySubmodels)
throws IOException, DeserializationException {
Objects.requireNonNull(aasServiceUrl);
return aasAgent.getAasEnvWithUrls(aasServiceUrl);
return aasAgent.getAasEnvWithUrls(aasServiceUrl, onlySubmodels);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@
package de.fraunhofer.iosb.app.model.aas;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

import java.util.ArrayList;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class CustomAssetAdministrationShellEnvironment {

protected List<CustomAssetAdministrationShell> assetAdministrationShells;
protected List<CustomAssetAdministrationShell> assetAdministrationShells = new ArrayList<>();

protected List<CustomSubmodel> submodels;
protected List<CustomSubmodel> submodels= new ArrayList<>();

protected List<CustomConceptDescription> conceptDescriptions;
protected List<CustomConceptDescription> conceptDescriptions= new ArrayList<>();

public List<CustomAssetAdministrationShell> getAssetAdministrationShells() {
return assetAdministrationShells;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

import java.util.ArrayList;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonAutoDetect
public class CustomSubmodel extends AASElement {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public class Configuration {

@JsonProperty(SETTINGS_PREFIX + "syncPeriod")
private int syncPeriod = 5; // Seconds

@JsonProperty(SETTINGS_PREFIX + "onlySubmodels")
private boolean onlySubmodels = false;

@JsonProperty(SETTINGS_PREFIX + "exposeSelfDescription")
private boolean exposeSelfDescription = true;
Expand Down Expand Up @@ -80,6 +83,14 @@ public int getSyncPeriod() {
return syncPeriod;
}

public boolean isOnlySubmodels() {
return onlySubmodels;
}

public void setOnlySubmodels(boolean onlySubmodels) {
this.onlySubmodels = onlySubmodels;
}

public boolean isExposeSelfDescription() {
return exposeSelfDescription;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import de.fraunhofer.iosb.app.controller.AasController;
import de.fraunhofer.iosb.app.controller.ResourceController;
import de.fraunhofer.iosb.app.model.aas.*;
import de.fraunhofer.iosb.app.model.configuration.Configuration;
import de.fraunhofer.iosb.app.model.ids.SelfDescription;
import de.fraunhofer.iosb.app.model.ids.SelfDescriptionChangeListener;
import de.fraunhofer.iosb.app.model.ids.SelfDescriptionRepository;
Expand All @@ -43,12 +44,14 @@ public class Synchronizer implements SelfDescriptionChangeListener {
private final SelfDescriptionRepository selfDescriptionRepository;
private final AasController aasController;
private final ResourceController resourceController;
private final Configuration configuration;

public Synchronizer(SelfDescriptionRepository selfDescriptionRepository,
AasController aasController, ResourceController resourceController) {
this.selfDescriptionRepository = selfDescriptionRepository;
this.aasController = aasController;
this.resourceController = resourceController;
this.configuration = Configuration.getInstance();
}

/**
Expand All @@ -62,10 +65,12 @@ public void synchronize() {
}

private void synchronize(URL aasServiceUrl) {
var onlySubmodels = this.configuration.isOnlySubmodels();

var oldSelfDescription = selfDescriptionRepository.getSelfDescription(aasServiceUrl);
CustomAssetAdministrationShellEnvironment newEnvironment;
var newEnvironment = fetchCurrentAasModel(aasServiceUrl, onlySubmodels);

newEnvironment = fetchCurrentAasModel(aasServiceUrl);
// Only load submodels or shells, conceptDescriptions, submodelElements as well?

if (Objects.nonNull(oldSelfDescription)) {
var oldEnvironment = oldSelfDescription.getEnvironment();
Expand All @@ -77,9 +82,10 @@ private void synchronize(URL aasServiceUrl) {
// If the element exists in oldEnvironment, copy the old elements into
// newEnvironment, already having an idsContractId/idsAssetId
syncShell(newEnvironment, oldEnvironment);
syncSubmodel(newEnvironment, oldEnvironment);
syncConceptDescription(newEnvironment, oldEnvironment);

syncSubmodel(newEnvironment, oldEnvironment);

removeOldElements(newEnvironment, oldEnvironment);

// Finally, update the self description
Expand All @@ -88,11 +94,11 @@ private void synchronize(URL aasServiceUrl) {
selfDescriptionRepository.updateSelfDescription(aasServiceUrl, newEnvironment);
}

private CustomAssetAdministrationShellEnvironment fetchCurrentAasModel(URL aasServiceUrl) {
private CustomAssetAdministrationShellEnvironment fetchCurrentAasModel(URL aasServiceUrl, boolean onlySubmodels) {
CustomAssetAdministrationShellEnvironment newEnvironment;

try { // Fetch current AAS model from AAS service
newEnvironment = aasController.getAasModelWithUrls(aasServiceUrl);
newEnvironment = aasController.getAasModelWithUrls(aasServiceUrl, onlySubmodels);
} catch (IOException aasServiceUnreachableException) {
throw new EdcException(format("Could not reach AAS service (%s): %s", aasServiceUrl,
aasServiceUnreachableException.getMessage()));
Expand All @@ -105,7 +111,8 @@ private CustomAssetAdministrationShellEnvironment fetchCurrentAasModel(URL aasSe

private void addNewElements(CustomAssetAdministrationShellEnvironment newEnvironment) {
var envElements = AASUtil.getAllElements(newEnvironment);
addAssetsContracts(envElements.stream().filter(element -> Objects.isNull(element.getIdsAssetId()) || Objects.isNull(element.getIdsContractId()))
addAssetsContracts(envElements.stream().filter(
element -> Objects.isNull(element.getIdsAssetId()) || Objects.isNull(element.getIdsContractId()))
.toList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ public void initializeAasAgent() {
aasAgent = new AasAgent(new OkHttpClient());
}

@Test // Can't really test this anymore with new variable "sourceUrl" that is not
// serialized
@Test
public void testGetAasEnvWithUrls() throws IOException, DeserializationException {
var shells = FileManager.loadResource("shells.json");
var submodels = FileManager.loadResource("submodels.json");
Expand All @@ -74,7 +73,27 @@ public void testGetAasEnvWithUrls() throws IOException, DeserializationException
var shouldBeResult = FileManager.loadResource("selfDescriptionWithAccessURLS.json");

var result = new ObjectMapper().writeValueAsString(
aasAgent.getAasEnvWithUrls(new URL(HTTP_LOCALHOST_8080)));
aasAgent.getAasEnvWithUrls(new URL(HTTP_LOCALHOST_8080), false));
result = result.replace("\n", "").replace(" ", "");

assertEquals(shouldBeResult, result);
}

@Test
public void testGetAasEnvWithUrlsOnlySubmodels() throws IOException, DeserializationException {
var shells = FileManager.loadResource("shells.json");
var submodels = FileManager.loadResource("submodels.json");
var conceptDescriptions = FileManager.loadResource("conceptDescriptions.json");

mockServer.when(request().withMethod("GET").withPath("/shells")).respond(response().withBody(shells));
mockServer.when(request().withMethod("GET").withPath("/submodels")).respond(response().withBody(submodels));
mockServer.when(request().withMethod("GET").withPath("/concept-descriptions"))
.respond(response().withBody(conceptDescriptions));

var shouldBeResult = FileManager.loadResource("selfDescriptionWithAccessURLsSubmodelsOnly.json");

var result = new ObjectMapper().writeValueAsString(
aasAgent.getAasEnvWithUrls(new URL(HTTP_LOCALHOST_8080), true));
result = result.replace("\n", "").replace(" ", "");

assertEquals(shouldBeResult, result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class SelfDescriptionTest {
@Test
public void emptyEnvironmentTest() {
SelfDescription selfDescription = new SelfDescription(new CustomAssetAdministrationShellEnvironment());
assertEquals("{\"assetAdministrationShells\":null,\"submodels\":null,\"conceptDescriptions\":null}",
assertEquals("{}",
selfDescription.toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public void synchronizationInitializeTest() {
}

@Test
public void synchronizationRemoveSubmodelElementTest() {
public void synchronizationRemoveAllSubmodelElementsTest() {
startMockServer(port);

prepareDefaultMockedResponse();
Expand Down Expand Up @@ -142,7 +142,7 @@ public void synchronizationRemoveAllTest() {

prepareEmptyMockedResponse();
synchronizer.synchronize();
assertEquals("{\"assetAdministrationShells\":[],\"submodels\":[],\"conceptDescriptions\":[]}",
assertEquals("{}",
selfDescriptionRepo.getSelfDescription(url).toString());
}

Expand Down
Loading

0 comments on commit 5956f4b

Please sign in to comment.