Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added schema documentation for sg1 import #432

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions core/src/main/java/org/intocps/maestro/core/dto/MultiModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,58 @@

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;

import java.util.List;
import java.util.Map;

public class MultiModel {
@JsonProperty("fmus")
@JsonPropertyDescription("A map of from fmu names on the form \"{FMU}\" to the path of the .fmu file")
private final Map<String, String> fmus;
@JsonProperty("connections")
@JsonPropertyDescription("A map of connections where the key and value strings are on the form form \"{FMU}.instanceName.signal\". The mapping is from output to inputs. As a single output can be connected to multiple inputs")
private final Map<String, List<String>> connections;
@JsonProperty("parameters")
@JsonPropertyDescription("A map from signal to its value. The key must be on the form \"{FMU}.instanceName.signal\" and value can be any value matching the signal.")
private final Map<String, Object> parameters;
@JsonProperty("environmentParameters")
@JsonPropertyDescription("A list of parameters which should be obtained from the environment. The strings are on the form form \"{FMU}.instanceName.signal\".")
private final List<String> environmentParameters;
@JsonProperty("logVariables")
@JsonPropertyDescription("A map from fmu instance \"{FMU}.instanceName\" to a list of signal names.")
private final Map<String, List<String>> logVariables;
@JsonProperty("parallelSimulation")
@JsonPropertyDescription("If true the dostep part of the algorithm is run in parallel.")
private final boolean parallelSimulation;
@JsonProperty("stabalizationEnabled")
@JsonPropertyDescription("Enable stabilization generation during initialization.")
private final boolean stabalizationEnabled;
@JsonProperty("global_absolute_tolerance")
@JsonPropertyDescription("The global absolute tolerance.")
private final double global_absolute_tolerance;
@JsonProperty("global_relative_tolerance")
@JsonPropertyDescription("The global relative tolerance.")
private final double global_relative_tolerance;
@JsonProperty("loggingOn")
@JsonPropertyDescription("Enable logging.")
private final boolean loggingOn;
@JsonProperty("visible")
@JsonPropertyDescription("The enable visible in FMI.")
private final boolean visible;
@JsonProperty("simulationProgramDelay")
@JsonPropertyDescription("Delay simulation steps. i.e. steps are slowed down to near real time.")
private final boolean simulationProgramDelay;

@JsonProperty("overrideLogLevel")
@JsonPropertyDescription("Override global log level of the simulator")
private final InitializeLogLevel overrideLogLevel;
@JsonProperty("algorithm")
@JsonPropertyDescription("The step algorithm to be used, fixed or variable")
private final IAlgorithmConfig algorithm;

@JsonProperty("logLevels")
@JsonPropertyDescription("A map of enabled log levels for each FMU. The key is \"{FMU}.instanceName\" and the log levels are the categories specified for each FMU")
private final Map<String, List<String>> logLevels;

@JsonProperty("faultInjectConfigurationPath")
Expand All @@ -49,21 +65,28 @@ public class MultiModel {
private final int convergenceAttempts;

@JsonProperty("modelTransfers")
@JsonPropertyDescription("A mapping from current instance name to the new instance name of that instance being transferred. i.e. A->A if A is transferred as is.")
public Map<String, String> modelTransfers;

public static class ModelSwap {
@JsonProperty("swapInstance")
@JsonPropertyDescription("The name of the instance swapped in")
public String swapInstance;
@JsonProperty("stepCondition")
@JsonPropertyDescription("The condition that should be true for the step to be allowed. It could be (true)")
public String stepCondition;
@JsonProperty("swapCondition")
@JsonPropertyDescription("The condition that should be true for the model to be swapped in i.e. replacing the previous. The condition could look like (controller.valve ==true) if the instance controller exist and have value as a boolean signal")
public String swapCondition;
@JsonProperty("swapConnections")
@JsonPropertyDescription("A map of new connections like the connections but only with the delta for this new instance. Keys and values are on the form \"{FMU}.instanceName.signal\". The key is the output and values the inputs")

public Map<String, List<String>> swapConnections;

public ModelSwap() {}
}
@JsonProperty("modelSwaps")
@JsonPropertyDescription("A map of from an instance name to its swap configuration. Thus the key is the instance name in this simulation.")
public Map<String, ModelSwap> modelSwaps;


Expand Down
24 changes: 24 additions & 0 deletions maestro/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,30 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>


<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-generator</artifactId>
<version>4.31.1</version>
</dependency>
<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-module-jackson</artifactId>
<version>4.31.1</version>
</dependency>
<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-module-jakarta-validation</artifactId>
<version>4.31.1</version>
</dependency>

<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
21 changes: 21 additions & 0 deletions maestro/src/main/java/org/intocps/maestro/cli/ImportCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.intocps.maestro.Mabl;
import org.intocps.maestro.core.Framework;
import org.intocps.maestro.core.dto.MultiModel;
import org.intocps.maestro.framework.fmi2.Fmi2SimulationEnvironmentConfiguration;
import org.intocps.maestro.plugin.JacobianStepConfig;
import org.intocps.maestro.template.MaBLTemplateConfiguration;
import picocli.CommandLine;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.util.*;
Expand All @@ -34,6 +36,9 @@ public class ImportCmd implements Callable<Integer> {
@CommandLine.Option(names = {"-di", "--dump-intermediate"}, description = "Dump all intermediate expansions", negatable = true)
boolean dumpIntermediate;

@CommandLine.Option(names = {"-ds", "--dump-schemas"}, description = "Dump the json schemas for the input files", negatable = true)
boolean dumpSchemas;

// @CommandLine.Option(names = {"-el", "--expansion-limit"}, description = "Stop expansion after this amount of loops")
// int expansionLimit;
@CommandLine.Option(names = {"-v", "--verbose"}, description = "Verbose")
Expand Down Expand Up @@ -105,6 +110,14 @@ public static void resolveFmuPaths(List<File> fmuSearchPaths, Map<String, String
@Override
public Integer call() throws Exception {

if (type == ImportType.Sg1) {

if (dumpSchemas) {
dumpSchemaFiles();
return 0;
}
}

Mabl.MableSettings settings = new Mabl.MableSettings();
settings.dumpIntermediateSpecs = dumpIntermediate;
settings.preserveFrameworkAnnotations = preserveAnnotations;
Expand All @@ -128,6 +141,9 @@ public Integer call() throws Exception {


if (type == ImportType.Sg1) {



if (!importSg1(util, fmuSearchPaths, sourceFiles)) {
return 1;
}
Expand Down Expand Up @@ -167,6 +183,11 @@ public Integer call() throws Exception {
return 0;
}

private void dumpSchemaFiles() throws IOException {
MaestroV1SimulationConfiguration.JsonSchemaGenerator.generate(MaestroV1SimulationConfiguration.class,output.toPath());
MaestroV1SimulationConfiguration.JsonSchemaGenerator.generate(MultiModel.class,output.toPath());
}

// https://stackoverflow.com/questions/9895041/merging-two-json-documents-using-jackson
public static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
package org.intocps.maestro.cli;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.victools.jsonschema.generator.*;
import com.github.victools.jsonschema.module.jackson.JacksonModule;
import com.github.victools.jsonschema.module.jakarta.validation.JakartaValidationModule;
import org.apache.commons.io.FileUtils;
import org.intocps.maestro.core.Framework;
import org.intocps.maestro.core.dto.IAlgorithmConfig;
import org.intocps.maestro.core.dto.MultiModel;
Expand All @@ -19,33 +26,45 @@

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public class MaestroV1SimulationConfiguration extends MultiModel {

@JsonProperty("startTime")
@JsonPropertyDescription("The start time of the simulation. Usually 0.")
private final double startTime;
@JsonProperty("endTime")
@JsonPropertyDescription("The duration of the simulation in seconds. This is used by the fmus to allocate memory.")
private final Double endTime;
@JsonProperty("reportProgress")
@JsonPropertyDescription("If true the simulation will attempt to report progress.")
private final Boolean reportProgress;

@JsonCreator
public MaestroV1SimulationConfiguration(@JsonProperty("fmus") Map<String, String> fmus,
@JsonProperty("connections") Map<String, List<String>> connections, @JsonProperty("parameters") Map<String, Object> parameters,
@JsonProperty("logVariables") Map<String, List<String>> logVariables, @JsonProperty("parallelSimulation") boolean parallelSimulation,
@JsonProperty("stabalizationEnabled") boolean stabalizationEnabled,
@JsonProperty("global_absolute_tolerance") double global_absolute_tolerance,
@JsonProperty("global_relative_tolerance") double global_relative_tolerance, @JsonProperty("loggingOn") boolean loggingOn,
@JsonProperty("visible") boolean visible, @JsonProperty("simulationProgramDelay") boolean simulationProgramDelay,
@JsonProperty("algorithm") IAlgorithmConfig algorithm, @JsonProperty("overrideLogLevel") InitializeLogLevel overrideLogLevel,
@JsonProperty("environmentParameters") List<String> environmentParameters, @JsonProperty("logLevels") Map<String, List<String>> logLevels,
@JsonProperty("startTime") double startTime, @JsonProperty("endTime") Double endTime,
@JsonProperty("reportProgress") Boolean reportProgress, @JsonProperty("faultInjectConfigurationPath") String faultInjectConfigurationPath,
@JsonProperty("faultInjectInstances") Map<String, String> faultInjectInstances,
@JsonProperty("convergenceAttempts") int convergenceAttempts, @JsonProperty("modelTransfers") Map<String, String> modelTransfers,
@JsonProperty("modelSwaps") Map<String, ModelSwap> modelSwaps) {
@JsonProperty("connections") Map<String, List<String>> connections,
@JsonProperty("parameters") Map<String, Object> parameters,
@JsonProperty("logVariables") Map<String, List<String>> logVariables,
@JsonProperty("parallelSimulation") boolean parallelSimulation,
@JsonProperty("stabalizationEnabled") boolean stabalizationEnabled,
@JsonProperty("global_absolute_tolerance") double global_absolute_tolerance,
@JsonProperty("global_relative_tolerance") double global_relative_tolerance,
@JsonProperty("loggingOn") boolean loggingOn,
@JsonProperty("visible") boolean visible, @JsonProperty("simulationProgramDelay") boolean simulationProgramDelay,
@JsonProperty("algorithm") IAlgorithmConfig algorithm,
@JsonProperty("overrideLogLevel") InitializeLogLevel overrideLogLevel,
@JsonProperty("environmentParameters") List<String> environmentParameters,
@JsonProperty("logLevels") Map<String, List<String>> logLevels,
@JsonProperty("startTime") double startTime, @JsonProperty("endTime") Double endTime,
@JsonProperty("reportProgress") Boolean reportProgress,
@JsonProperty("faultInjectConfigurationPath") String faultInjectConfigurationPath,
@JsonProperty("faultInjectInstances") Map<String, String> faultInjectInstances,
@JsonProperty("convergenceAttempts") int convergenceAttempts,
@JsonProperty("modelTransfers") Map<String, String> modelTransfers,
@JsonProperty("modelSwaps") Map<String, ModelSwap> modelSwaps) {
super(fmus, connections, parameters, logVariables, parallelSimulation, stabalizationEnabled, global_absolute_tolerance,
global_relative_tolerance, loggingOn, visible, simulationProgramDelay, algorithm, overrideLogLevel, environmentParameters, logLevels,
faultInjectConfigurationPath, faultInjectInstances, convergenceAttempts, modelTransfers, modelSwaps);
Expand Down Expand Up @@ -178,4 +197,64 @@ private static JsonNode merge(JsonNode mainNode, JsonNode updateNode) {
}
return mainNode;
}

public static class JsonSchemaGenerator {
private static SchemaGenerator generator = null;

public static void generate(Class clz, Path location) throws IOException {
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(
SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON);
configBuilder.with(new JacksonModule());
configBuilder.with(new JakartaValidationModule());

configBuilder.with(new CustomMapTypeResolver(() -> generator));

generator = new SchemaGenerator(configBuilder.build());
String schema = generator.generateSchema(clz).toPrettyString();


var schemaFile = location.resolve(clz.getSimpleName() + ".json").toFile();
if (schemaFile.getParentFile() != null) {
schemaFile.getParentFile().mkdirs();
}
FileUtils.write(schemaFile, schema, "UTF-8");
}


public static class CustomMapTypeResolver implements CustomDefinitionProvider {

final Supplier<SchemaGenerator> generator;

public CustomMapTypeResolver(Supplier<SchemaGenerator> generator) {
this.generator = generator;
}

@Override
public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, TypeContext context) {
if (javaType.isInstanceOf(Map.class) && javaType.getTypeBindings().size() == 2 && javaType.getTypeBindings().getBoundType(0)
.isInstanceOf(String.class)) {
ObjectNode customSchema = JsonNodeFactory.instance.objectNode();
customSchema.put("type", "object");
ObjectNode additionalProperties = JsonNodeFactory.instance.objectNode();
ResolvedType mapTarget = javaType.getTypeBindings().getBoundType(1);
if (mapTarget.isInstanceOf(String.class)) {
additionalProperties.put("type", "string");
customSchema.set("additionalProperties", additionalProperties);
return new CustomDefinition(customSchema);
} else if (mapTarget.isInstanceOf(List.class)) {
additionalProperties.put("type", "array");
additionalProperties.set("items", JsonNodeFactory.instance.objectNode().put("type", "string"));
customSchema.set("additionalProperties", additionalProperties);
return new CustomDefinition(customSchema);
} else if (mapTarget.isInstanceOf(MultiModel.ModelSwap.class)) {
customSchema.set("additionalProperties", generator.get().generateSchema(mapTarget));
return new CustomDefinition(customSchema);
}
}
return null;
}
}


}
}
18 changes: 15 additions & 3 deletions maestro/src/test/java/org/intocps/maestro/ImportCliTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,29 @@ public void importTestExistingFmu3Mix() {
}

@Test
public void importTestExistingFmu3Ft() {
public void importTestSchemaDump() {

String arguments = String.format(Locale.US, "import sg1 --interpret --dump-intermediate --inline-framework-config -output %s %s",
getOutputPath().toAbsolutePath(), "/Users/kgl/data/au/into-cps-association/maestro/maestro/src/test/resources/fmi3/reference/siggen-feedthrough/mm.json");
String arguments = String.format(Locale.US, "import sg1 --dump-schemas -output %s",
getOutputPath().toAbsolutePath());
String[] s = arguments.split(" ");

int exitCode = new CommandLine(new Main()).setCaseInsensitiveEnumValuesAllowed(true).execute(s);
Assert.assertEquals(0, exitCode);

}

// @Test
// public void importTestExistingFmu3Ft() {
//
// String arguments = String.format(Locale.US, "import sg1 --interpret --dump-intermediate --inline-framework-config -output %s %s",
// getOutputPath().toAbsolutePath(), "/Users/kgl/data/au/into-cps-association/maestro/maestro/src/test/resources/fmi3/reference/siggen-feedthrough/mm.json");
// String[] s = arguments.split(" ");
//
// int exitCode = new CommandLine(new Main()).setCaseInsensitiveEnumValuesAllowed(true).execute(s);
// Assert.assertEquals(0, exitCode);
//
// }

// @Test
// public void importTestRelativeFmus() {
//
Expand Down
Loading