diff --git a/src/main/java/org/gridsuite/mapping/server/MappingConstants.java b/src/main/java/org/gridsuite/mapping/server/MappingConstants.java index 6832a33f..6cd965c7 100644 --- a/src/main/java/org/gridsuite/mapping/server/MappingConstants.java +++ b/src/main/java/org/gridsuite/mapping/server/MappingConstants.java @@ -34,5 +34,4 @@ private MappingConstants() { // Loads public static final String LOAD_TYPE_PROPERTY = "loadType"; - public static final String CURRENT_LIMIT_MODEL_CLASS = "CurrentLimitAutomaton"; } diff --git a/src/main/java/org/gridsuite/mapping/server/common/extensions/AbstractSubtypesRegister.java b/src/main/java/org/gridsuite/mapping/server/common/extensions/AbstractSubtypesRegister.java new file mode 100644 index 00000000..9a80c5d4 --- /dev/null +++ b/src/main/java/org/gridsuite/mapping/server/common/extensions/AbstractSubtypesRegister.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.mapping.server.common.extensions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.NamedType; + +import javax.annotation.PostConstruct; +import java.util.*; + +/** + * @author Thang PHAM + */ +public abstract class AbstractSubtypesRegister implements SubtypesRegister { + + private final ObjectMapper objectMapper; + + protected AbstractSubtypesRegister(ObjectMapper objectMapper) { + super(); + this.objectMapper = objectMapper; + } + + @PostConstruct + protected void registerSubtypes() { + + // collect subtypes + List subtypes = new ArrayList<>(); + getSubtypes().forEach((k, v) -> subtypes.add(new NamedType(v, k))); + + // Register subtypes with the ObjectMapper + objectMapper.getSubtypeResolver().registerSubtypes(subtypes.toArray(new NamedType[0])); + } +} diff --git a/src/main/java/org/gridsuite/mapping/server/common/extensions/SubtypesRegister.java b/src/main/java/org/gridsuite/mapping/server/common/extensions/SubtypesRegister.java new file mode 100644 index 00000000..b1fa9c7f --- /dev/null +++ b/src/main/java/org/gridsuite/mapping/server/common/extensions/SubtypesRegister.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.mapping.server.common.extensions; + +import java.lang.reflect.InvocationTargetException; +import java.util.Map; + +/** + * @author Thang PHAM + */ +public interface SubtypesRegister { + Map> getSubtypes(); + + D fromEntity(E entity) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException; + + E toEntity(D dto) throws IllegalAccessException; +} diff --git a/src/main/java/org/gridsuite/mapping/server/dto/InputMapping.java b/src/main/java/org/gridsuite/mapping/server/dto/InputMapping.java index 6b5aea99..563cf6c7 100644 --- a/src/main/java/org/gridsuite/mapping/server/dto/InputMapping.java +++ b/src/main/java/org/gridsuite/mapping/server/dto/InputMapping.java @@ -9,8 +9,13 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; import org.gridsuite.mapping.server.dto.automata.AbstractAutomaton; +import org.gridsuite.mapping.server.dto.automata.extensions.AutomatonSubtypesRegister; +import org.gridsuite.mapping.server.model.AutomatonEntity; import org.gridsuite.mapping.server.model.MappingEntity; +import org.springframework.http.HttpStatus; +import org.springframework.web.client.HttpClientErrorException; import java.util.List; import java.util.stream.Collectors; @@ -21,6 +26,7 @@ @Data @Schema(description = "Mapping") @AllArgsConstructor +@NoArgsConstructor public class InputMapping implements Mapping { @Schema(description = "Name") private String name; @@ -34,19 +40,33 @@ public class InputMapping implements Mapping { @Schema(description = "Mapping should control its parameters") private boolean controlledParameters; - public MappingEntity convertMappingToEntity() { + public MappingEntity convertMappingToEntity(AutomatonSubtypesRegister automatonSubtypesRegister) { MappingEntity convertedMapping = new MappingEntity(); convertedMapping.setName(name); convertedMapping.setControlledParameters(controlledParameters); convertedMapping.setRules(rules.stream().map(rule -> rule.convertRuleToEntity(convertedMapping)).collect(Collectors.toList())); - convertedMapping.setAutomata(automata.stream().map(automaton -> automaton.convertAutomatonToEntity(convertedMapping)).collect(Collectors.toList())); + convertedMapping.setAutomata(automata.stream().map(automaton -> { + try { + AutomatonEntity automatonEntity = automatonSubtypesRegister.toEntity(automaton); + automatonEntity.setMapping(convertedMapping); + return automatonEntity; + } catch (Exception e) { + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + }).collect(Collectors.toList())); return convertedMapping; } - public InputMapping(MappingEntity mappingEntity) { + public InputMapping(MappingEntity mappingEntity, AutomatonSubtypesRegister automatonSubtypesRegister) { name = mappingEntity.getName(); controlledParameters = mappingEntity.isControlledParameters(); rules = mappingEntity.getRules().stream().map(Rule::new).collect(Collectors.toList()); - automata = mappingEntity.getAutomata().stream().map(AbstractAutomaton::instantiateFromEntity).collect(Collectors.toList()); + automata = mappingEntity.getAutomata().stream().map(automaton -> { + try { + return automatonSubtypesRegister.fromEntity(automaton); + } catch (Exception e) { + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, e.getMessage()); + } + }).collect(Collectors.toList()); } } diff --git a/src/main/java/org/gridsuite/mapping/server/dto/automata/AbstractAutomaton.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/AbstractAutomaton.java index 2371fb9c..d556b685 100644 --- a/src/main/java/org/gridsuite/mapping/server/dto/automata/AbstractAutomaton.java +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/AbstractAutomaton.java @@ -6,26 +6,25 @@ */ package org.gridsuite.mapping.server.dto.automata; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import org.gridsuite.mapping.server.model.AutomatonEntity; -import org.gridsuite.mapping.server.model.MappingEntity; +import lombok.NoArgsConstructor; + import org.gridsuite.mapping.server.utils.AutomatonFamily; -import org.springframework.http.HttpStatus; -import org.springframework.web.client.HttpClientErrorException; -import java.util.ArrayList; +import java.util.List; /** * @author Mathieu Scalbert */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "family", visible = true) -@JsonSubTypes({ - @JsonSubTypes.Type(value = CurrentLimitAutomaton.class, name = "CURRENT_LIMIT")}) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "model", visible = true) +@JsonSubTypes({ }) @Data +@NoArgsConstructor public abstract class AbstractAutomaton { @Schema(description = "Automaton family") @JsonProperty @@ -37,19 +36,18 @@ public abstract class AbstractAutomaton { @Schema(description = "Mapped Parameters Set Group ID") private String setGroup; - @Schema(description = "Element watched by the automaton") - private String watchedElement; + @JsonIgnore + public abstract String getExportedId(); + + @JsonIgnore + public abstract String getExportedClassName(); + + @JsonIgnore + public abstract List getExportedProperties(); - public abstract ArrayList convertToBasicProperties(); + public abstract List toProperties(); - public abstract AutomatonEntity convertAutomatonToEntity(MappingEntity parentMapping); + public abstract void fromProperties(List properties); - public static AbstractAutomaton instantiateFromEntity(AutomatonEntity automatonEntity) { - if (automatonEntity.getFamily() == AutomatonFamily.CURRENT_LIMIT) { - return new CurrentLimitAutomaton(automatonEntity); - } else { - throw new HttpClientErrorException(HttpStatus.BAD_REQUEST); - } - } } diff --git a/src/main/java/org/gridsuite/mapping/server/dto/automata/BasicProperty.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/BasicProperty.java index f63323cb..2126af04 100644 --- a/src/main/java/org/gridsuite/mapping/server/dto/automata/BasicProperty.java +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/BasicProperty.java @@ -2,6 +2,7 @@ import lombok.AllArgsConstructor; import lombok.Data; +import org.gridsuite.mapping.server.utils.PropertyType; @Data @AllArgsConstructor @@ -9,4 +10,6 @@ public class BasicProperty { private String name; private String value; + + private PropertyType type; } diff --git a/src/main/java/org/gridsuite/mapping/server/dto/automata/CommonAutomatonSubtypesExtension.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/CommonAutomatonSubtypesExtension.java new file mode 100644 index 00000000..5edf7811 --- /dev/null +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/CommonAutomatonSubtypesExtension.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.mapping.server.dto.automata; + +import com.google.auto.service.AutoService; +import org.gridsuite.mapping.server.dto.automata.extensions.AutomatonSubtypes; +import org.gridsuite.mapping.server.dto.automata.extensions.AutomatonSubtypesExtension; + +/** + * @author Thang PHAM + */ +@AutomatonSubtypes({ + @AutomatonSubtypes.Type(value = CurrentLimitAutomaton.class, name = CurrentLimitAutomaton.MODEL_CLASS), + @AutomatonSubtypes.Type(value = TapChangerBlockingAutomaton.class, name = TapChangerBlockingAutomaton.MODEL_CLASS) +}) +@AutoService(AutomatonSubtypesExtension.class) +public class CommonAutomatonSubtypesExtension implements AutomatonSubtypesExtension { +} diff --git a/src/main/java/org/gridsuite/mapping/server/dto/automata/CurrentLimitAutomaton.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/CurrentLimitAutomaton.java index 0f2f1b73..46e0fa66 100644 --- a/src/main/java/org/gridsuite/mapping/server/dto/automata/CurrentLimitAutomaton.java +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/CurrentLimitAutomaton.java @@ -6,17 +6,17 @@ */ package org.gridsuite.mapping.server.dto.automata; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import org.gridsuite.mapping.server.model.AutomatonEntity; -import org.gridsuite.mapping.server.model.AutomatonPropertyEntity; -import org.gridsuite.mapping.server.model.MappingEntity; import org.gridsuite.mapping.server.utils.PropertyType; import java.util.ArrayList; -import java.util.Optional; -import java.util.UUID; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * @author Mathieu Scalbert @@ -25,44 +25,54 @@ @EqualsAndHashCode(callSuper = true) @NoArgsConstructor public class CurrentLimitAutomaton extends AbstractAutomaton { + + public static final String MODEL_CLASS = "CurrentLimitAutomaton"; + + public static final String PROPERTY_WATCHED_ELEMENT = "watchedElement"; + public static final String PROPERTY_SIDE = "side"; + public static final String PROPERTY_STATIC_ID = "staticId"; + + @Schema(description = "Element watched by the automaton") + @JsonProperty(PROPERTY_WATCHED_ELEMENT) + private String watchedElement; + + @Schema(description = "Side of the automaton") + @JsonProperty(PROPERTY_SIDE) private String side; - public ArrayList convertToBasicProperties() { - ArrayList propertiesList = new ArrayList<>(); - propertiesList.add(new BasicProperty("side", side)); - return propertiesList; + @Override + public String getExportedId() { + return String.format("%s_%s", this.getModel(), watchedElement); + } + + @Override + public String getExportedClassName() { + return MODEL_CLASS; + } + + @Override + public ArrayList getExportedProperties() { + ArrayList properties = new ArrayList<>(); + properties.add(new BasicProperty(PROPERTY_STATIC_ID, "\"" + watchedElement + "\"", PropertyType.STRING)); + properties.add(new BasicProperty(PROPERTY_SIDE, side, PropertyType.STRING)); + + return properties; } - public CurrentLimitAutomaton(AutomatonEntity automatonEntity) { - this.setFamily(automatonEntity.getFamily()); - this.setModel(automatonEntity.getModel()); - this.setSetGroup(automatonEntity.getSetGroup()); - this.setWatchedElement(automatonEntity.getWatchedElement()); - // TODO Create generic function for all properties - Optional foundSideProperty = automatonEntity.getProperties().stream().filter(property -> property.getName().equals("side")).findAny(); - if (foundSideProperty.isPresent()) { - side = foundSideProperty.get().getValue(); - } + @Override + public List toProperties() { + ArrayList properties = new ArrayList<>(); + properties.add(new BasicProperty(PROPERTY_WATCHED_ELEMENT, watchedElement, PropertyType.STRING)); + properties.add(new BasicProperty(PROPERTY_SIDE, side, PropertyType.STRING)); + return properties; } - public AutomatonEntity convertAutomatonToEntity(MappingEntity parentMapping) { - UUID createdId = UUID.randomUUID(); - AutomatonEntity convertedAutomaton = new AutomatonEntity(); - convertedAutomaton.setAutomatonId(createdId); - convertedAutomaton.setFamily(this.getFamily()); - convertedAutomaton.setModel(this.getModel()); - convertedAutomaton.setSetGroup(this.getSetGroup()); - convertedAutomaton.setWatchedElement(this.getWatchedElement()); - convertedAutomaton.setMapping(parentMapping); - ArrayList convertedProperties = new ArrayList<>(); - AutomatonPropertyEntity convertedProperty = new AutomatonPropertyEntity(); - convertedProperty.setAutomatonId(createdId); - convertedProperty.setName("side"); - convertedProperty.setValue(this.getSide()); - convertedProperty.setType(PropertyType.STRING); - convertedProperties.add(convertedProperty); - convertedAutomaton.setProperties(convertedProperties); - return convertedAutomaton; + @Override + public void fromProperties(List properties) { + Map propertiesMap = properties.stream() + .collect(Collectors.toMap(BasicProperty::getName, elem -> elem)); + this.watchedElement = propertiesMap.get(PROPERTY_WATCHED_ELEMENT) != null ? propertiesMap.get(PROPERTY_WATCHED_ELEMENT).getValue() : null; + this.side = propertiesMap.get(PROPERTY_SIDE) != null ? propertiesMap.get(PROPERTY_SIDE).getValue() : null; } } diff --git a/src/main/java/org/gridsuite/mapping/server/dto/automata/TapChangerBlockingAutomaton.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/TapChangerBlockingAutomaton.java new file mode 100644 index 00000000..e73a6c30 --- /dev/null +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/TapChangerBlockingAutomaton.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.mapping.server.dto.automata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.gridsuite.mapping.server.utils.Methods; +import org.gridsuite.mapping.server.utils.PropertyType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Thang PHAM + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class TapChangerBlockingAutomaton extends AbstractAutomaton { + + public static final String MODEL_CLASS = "TapChangerBlockingAutomaton"; + + public static final String PROPERTY_NAME = "name"; + public static final String PROPERTY_U_MEASUREMENTS = "uMeasurements"; + public static final String PROPERTY_TRANSFORMERS = "transformers"; + + @Schema(description = "Name property") + @JsonProperty(PROPERTY_NAME) + private String name; + + @Schema(description = "U Measurement property") + @JsonProperty(PROPERTY_U_MEASUREMENTS) + private List uMeasurements; + + @Schema(description = "Transformers property") + @JsonProperty(PROPERTY_TRANSFORMERS) + private List transformers; + + @Override + public String getExportedId() { + return name; + } + + @Override + public String getExportedClassName() { + return MODEL_CLASS; + } + + @Override + public ArrayList getExportedProperties() { + ArrayList properties = new ArrayList<>(); + properties.add(new BasicProperty("uMeasurement", + uMeasurements.stream().map(elem -> "\"" + elem + "\"").collect(Collectors.joining(", ")), PropertyType.STRING)); + properties.add(new BasicProperty(PROPERTY_TRANSFORMERS, + transformers.stream().map(elem -> "\"" + elem + "\"").collect(Collectors.joining(", ")), PropertyType.STRING)); + return properties; + } + + @Override + public List toProperties() { + ArrayList properties = new ArrayList<>(); + properties.add(new BasicProperty(PROPERTY_NAME, name, PropertyType.STRING)); + properties.add(new BasicProperty(PROPERTY_U_MEASUREMENTS, + uMeasurements != null ? Methods.convertListToString(uMeasurements) : null, PropertyType.STRING)); + properties.add(new BasicProperty(PROPERTY_TRANSFORMERS, + transformers != null ? Methods.convertListToString(transformers) : null, PropertyType.STRING)); + return properties; + } + + @Override + public void fromProperties(List properties) { + Map propertiesMap = properties.stream() + .collect(Collectors.toMap(BasicProperty::getName, elem -> elem)); + this.name = propertiesMap.get(PROPERTY_NAME).getValue(); + this.uMeasurements = Methods.convertStringToList(propertiesMap.get(PROPERTY_U_MEASUREMENTS) != null ? propertiesMap.get(PROPERTY_U_MEASUREMENTS).getValue() : ""); + this.transformers = Methods.convertStringToList(propertiesMap.get(PROPERTY_TRANSFORMERS) != null ? propertiesMap.get(PROPERTY_TRANSFORMERS).getValue() : ""); + } +} diff --git a/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypes.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypes.java new file mode 100644 index 00000000..a56e4250 --- /dev/null +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypes.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.gridsuite.mapping.server.dto.automata.extensions; + +import org.gridsuite.mapping.server.dto.automata.AbstractAutomaton; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Thang PHAM + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface AutomatonSubtypes { + Type[] value(); + @interface Type { + Class value(); + String name() default ""; + } +} diff --git a/src/main/java/org/gridsuite/mapping/server/utils/AutomatonIdProvider.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesExtension.java similarity index 52% rename from src/main/java/org/gridsuite/mapping/server/utils/AutomatonIdProvider.java rename to src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesExtension.java index 048c9c40..f12f0978 100644 --- a/src/main/java/org/gridsuite/mapping/server/utils/AutomatonIdProvider.java +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesExtension.java @@ -1,17 +1,13 @@ /** - * Copyright (c) 2022, RTE (http://www.rte-france.com) + * Copyright (c) 2023, RTE (http://www.rte-france.com) * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -package org.gridsuite.mapping.server.utils; - -import org.gridsuite.mapping.server.dto.automata.AbstractAutomaton; +package org.gridsuite.mapping.server.dto.automata.extensions; /** * @author Thang PHAM */ - -public interface AutomatonIdProvider { - String getId(AbstractAutomaton automaton); +public interface AutomatonSubtypesExtension { } diff --git a/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesRegister.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesRegister.java new file mode 100644 index 00000000..0e8cc6c1 --- /dev/null +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesRegister.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.mapping.server.dto.automata.extensions; + +import org.gridsuite.mapping.server.common.extensions.SubtypesRegister; +import org.gridsuite.mapping.server.dto.automata.AbstractAutomaton; +import org.gridsuite.mapping.server.model.AutomatonEntity; + +/** + * @author Thang PHAM + */ +public interface AutomatonSubtypesRegister extends SubtypesRegister { +} diff --git a/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesRegisterImpl.java b/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesRegisterImpl.java new file mode 100644 index 00000000..e70708ae --- /dev/null +++ b/src/main/java/org/gridsuite/mapping/server/dto/automata/extensions/AutomatonSubtypesRegisterImpl.java @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2023, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.gridsuite.mapping.server.dto.automata.extensions; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Lists; +import org.gridsuite.mapping.server.common.extensions.AbstractSubtypesRegister; +import org.gridsuite.mapping.server.dto.automata.AbstractAutomaton; +import org.gridsuite.mapping.server.dto.automata.BasicProperty; +import org.gridsuite.mapping.server.model.AutomatonEntity; +import org.gridsuite.mapping.server.model.AutomatonPropertyEntity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Thang PHAM + */ +@Component +public class AutomatonSubtypesRegisterImpl + extends AbstractSubtypesRegister + implements AutomatonSubtypesRegister { + + private final Map> types = new HashMap<>(); + + @Autowired + public AutomatonSubtypesRegisterImpl(ObjectMapper objectMapper) { + + super(objectMapper); + + // load pluggable types + List automatonSubtypesExtensions = + Lists.newArrayList(ServiceLoader.load(AutomatonSubtypesExtension.class, AutomatonSubtypesExtension.class.getClassLoader())); + automatonSubtypesExtensions.stream() + .filter(cls -> cls.getClass().isAnnotationPresent(AutomatonSubtypes.class)) + .forEach(cls -> { + AutomatonSubtypes annotation = cls.getClass().getAnnotation(AutomatonSubtypes.class); + AutomatonSubtypes.Type[] subtypes = annotation.value(); + Arrays.stream(subtypes).forEach(type -> types.put(type.name(), type.value())); + }); + + } + + public Map> getSubtypes() { + return types; + } + + @Override + public AbstractAutomaton fromEntity(AutomatonEntity automatonEntity) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Class dtoClass = getSubtypes().get(automatonEntity.getModel()); + Constructor constructor = dtoClass.getConstructor(); + AbstractAutomaton abstractAutomaton = (AbstractAutomaton) constructor.newInstance(); + + // build automaton common fields + abstractAutomaton.setFamily(automatonEntity.getFamily()); + abstractAutomaton.setModel(automatonEntity.getModel()); + abstractAutomaton.setSetGroup(automatonEntity.getSetGroup()); + + // build automaton particular fields + abstractAutomaton.fromProperties(automatonEntity.getProperties().stream() + .map(elem -> new BasicProperty(elem.getName(), elem.getValue(), elem.getType())) + .collect(Collectors.toList())); + + return abstractAutomaton; + } + + @Override + public AutomatonEntity toEntity(AbstractAutomaton dto) { + AutomatonEntity automatonEntity = new AutomatonEntity(dto); + List properties = dto.toProperties(); + + properties.forEach(elem -> automatonEntity.addProperty(new AutomatonPropertyEntity(automatonEntity.getAutomatonId(), + elem.getName(), elem.getValue(), elem.getType(), automatonEntity))); + + return automatonEntity; + } +} diff --git a/src/main/java/org/gridsuite/mapping/server/model/AutomatonEntity.java b/src/main/java/org/gridsuite/mapping/server/model/AutomatonEntity.java index 4725ae56..e9888460 100644 --- a/src/main/java/org/gridsuite/mapping/server/model/AutomatonEntity.java +++ b/src/main/java/org/gridsuite/mapping/server/model/AutomatonEntity.java @@ -7,9 +7,11 @@ package org.gridsuite.mapping.server.model; import lombok.*; +import org.gridsuite.mapping.server.dto.automata.AbstractAutomaton; import org.gridsuite.mapping.server.utils.AutomatonFamily; import javax.persistence.*; +import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -40,9 +42,6 @@ public class AutomatonEntity extends AbstractManuallyAssignedIdentifierEntity properties; @@ -55,6 +54,14 @@ public UUID getId() { return automatonId; } + public AutomatonEntity(AbstractAutomaton automaton) { + UUID newID = UUID.randomUUID(); + this.automatonId = newID; + this.family = automaton.getFamily(); + this.model = automaton.getModel(); + this.setGroup = automaton.getSetGroup(); + } + public AutomatonEntity(MappingEntity mapping, AutomatonEntity automatonToCopy) { UUID newID = UUID.randomUUID(); this.automatonId = newID; @@ -62,8 +69,15 @@ public AutomatonEntity(MappingEntity mapping, AutomatonEntity automatonToCopy) { this.family = automatonToCopy.getFamily(); this.model = automatonToCopy.getModel(); this.setGroup = automatonToCopy.getSetGroup(); - this.watchedElement = automatonToCopy.getWatchedElement(); - this.properties = automatonToCopy.getProperties().stream().map(automatonPropertyEntity -> new AutomatonPropertyEntity(newID, automatonPropertyEntity)).collect(Collectors.toList()); + this.properties = automatonToCopy.getProperties().stream() + .map(automatonPropertyEntity -> new AutomatonPropertyEntity(newID, automatonPropertyEntity)) + .collect(Collectors.toList()); + } + public void addProperty(AutomatonPropertyEntity property) { + if (properties == null) { + properties = new ArrayList<>(); + } + properties.add(property); } } diff --git a/src/main/java/org/gridsuite/mapping/server/service/implementation/MappingServiceImpl.java b/src/main/java/org/gridsuite/mapping/server/service/implementation/MappingServiceImpl.java index f2b88eef..6becc43d 100644 --- a/src/main/java/org/gridsuite/mapping/server/service/implementation/MappingServiceImpl.java +++ b/src/main/java/org/gridsuite/mapping/server/service/implementation/MappingServiceImpl.java @@ -8,6 +8,7 @@ import org.gridsuite.mapping.server.dto.InputMapping; import org.gridsuite.mapping.server.dto.RenameObject; +import org.gridsuite.mapping.server.dto.automata.extensions.AutomatonSubtypesRegister; import org.gridsuite.mapping.server.dto.models.Model; import org.gridsuite.mapping.server.dto.models.ParametersSetsGroup; import org.gridsuite.mapping.server.model.AutomatonEntity; @@ -37,26 +38,29 @@ public class MappingServiceImpl implements MappingService { private final ModelRepository modelRepository; private final MappingRepository mappingRepository; + private final AutomatonSubtypesRegister automatonSubtypesRegister; @Autowired public MappingServiceImpl( MappingRepository mappingRepository, - ModelRepository modelRepository + ModelRepository modelRepository, + AutomatonSubtypesRegister automatonSubtypesRegister ) { this.modelRepository = modelRepository; this.mappingRepository = mappingRepository; + this.automatonSubtypesRegister = automatonSubtypesRegister; } @Override public List getMappingList() { List mappingEntities = mappingRepository.findAll(); - return mappingEntities.stream().map(InputMapping::new).collect(Collectors.toList()); + return mappingEntities.stream().map(entity -> new InputMapping(entity, automatonSubtypesRegister)).collect(Collectors.toList()); } @Override public InputMapping createMapping(String mappingName, InputMapping mapping) { - MappingEntity mappingToSave = mapping.convertMappingToEntity(); + MappingEntity mappingToSave = mapping.convertMappingToEntity(automatonSubtypesRegister); mappingToSave.markNotNew(); if (mappingToSave.isControlledParameters()) { try { @@ -121,7 +125,7 @@ public InputMapping copyMapping(String originalName, String copyName) { MappingEntity copiedMapping = new MappingEntity(copyName, mappingToCopy.get()); try { mappingRepository.save(copiedMapping); - return new InputMapping(copiedMapping); + return new InputMapping(copiedMapping, automatonSubtypesRegister); } catch (DataIntegrityViolationException ex) { throw new ResponseStatusException(HttpStatus.CONFLICT, conflictMappingErrorMessage, ex); } diff --git a/src/main/java/org/gridsuite/mapping/server/service/implementation/NetworkServiceImpl.java b/src/main/java/org/gridsuite/mapping/server/service/implementation/NetworkServiceImpl.java index 42cda2e7..63a91f92 100644 --- a/src/main/java/org/gridsuite/mapping/server/service/implementation/NetworkServiceImpl.java +++ b/src/main/java/org/gridsuite/mapping/server/service/implementation/NetworkServiceImpl.java @@ -98,6 +98,15 @@ public NetworkValues getNetworkValuesFromExistingNetwork(UUID networkUuid) { EquipmentValues loadsEquipmentValues = getLoadsEquipmentValues(network, voltageLevelsPropertyValues, substationsPropertyValues); equipmentValuesList.add(loadsEquipmentValues); + EquipmentValues busesEquipmentValues = getBusesEquipmentValues(network, voltageLevelsPropertyValues, substationsPropertyValues); + equipmentValuesList.add(busesEquipmentValues); + + EquipmentValues linesEquipmentValues = getLinesEquipmentValues(network); + equipmentValuesList.add(linesEquipmentValues); + + EquipmentValues twoWindingsTransformersEquipmentValues = getTwoWindingsTransformersEquipmentValues(network, substationsPropertyValues); + equipmentValuesList.add(twoWindingsTransformersEquipmentValues); + return new NetworkValues(networkUuid, equipmentValuesList); } @@ -168,6 +177,43 @@ private EquipmentValues getLoadsEquipmentValues(Network network, HashMap> voltageLevelsPropertyValues, HashMap> substationsPropertyValues) { + HashMap> busValuesMap = new HashMap<>(); + // Own properties + network.getBusBreakerView().getBuses().forEach(bus -> { + setPropertyMap(busValuesMap, bus.getId(), ID_PROPERTY); + }); + + // Parent properties (merge unnecessary, no overlap in properties + busValuesMap.putAll(voltageLevelsPropertyValues); + busValuesMap.putAll(substationsPropertyValues); + + return new EquipmentValues(EquipmentType.BUS, busValuesMap); + } + + private EquipmentValues getLinesEquipmentValues(Network network) { + HashMap> lineValuesMap = new HashMap<>(); + // Own properties + network.getLines().forEach(line -> { + setPropertyMap(lineValuesMap, line.getId(), ID_PROPERTY); + }); + + return new EquipmentValues(EquipmentType.LINE, lineValuesMap); + } + + private EquipmentValues getTwoWindingsTransformersEquipmentValues(Network network, HashMap> substationsPropertyValues) { + HashMap> twoWindingsTransformersValuesMap = new HashMap<>(); + // Own properties + network.getTwoWindingsTransformers().forEach(twoWindingsTransformer -> { + setPropertyMap(twoWindingsTransformersValuesMap, twoWindingsTransformer.getId(), ID_PROPERTY); + }); + + // Parent properties (merge unnecessary, no overlap in properties + twoWindingsTransformersValuesMap.putAll(substationsPropertyValues); + + return new EquipmentValues(EquipmentType.TWO_WINDINGS_TRANSFORMER, twoWindingsTransformersValuesMap); + } + @Override public NetworkValues getNetworkValues(MultipartFile multipartFile) { HttpHeaders headers = new HttpHeaders(); diff --git a/src/main/java/org/gridsuite/mapping/server/service/implementation/ScriptServiceImpl.java b/src/main/java/org/gridsuite/mapping/server/service/implementation/ScriptServiceImpl.java index 60ce9b60..d134c604 100644 --- a/src/main/java/org/gridsuite/mapping/server/service/implementation/ScriptServiceImpl.java +++ b/src/main/java/org/gridsuite/mapping/server/service/implementation/ScriptServiceImpl.java @@ -9,6 +9,7 @@ import lombok.*; import org.gridsuite.mapping.server.dto.*; import org.gridsuite.mapping.server.dto.automata.AbstractAutomaton; +import org.gridsuite.mapping.server.dto.automata.extensions.AutomatonSubtypesRegister; import org.gridsuite.mapping.server.dto.models.ModelParameterDefinition; import org.gridsuite.mapping.server.dto.models.ParametersSet; import org.gridsuite.mapping.server.dto.models.ParametersSetsGroup; @@ -37,15 +38,18 @@ public class ScriptServiceImpl implements ScriptService { private final ModelRepository modelRepository; private final MappingRepository mappingRepository; private final ScriptRepository scriptRepository; + private final AutomatonSubtypesRegister automatonSubtypesRegister; public ScriptServiceImpl( MappingRepository mappingRepository, ScriptRepository scriptRepository, - ModelRepository modelRepository + ModelRepository modelRepository, + AutomatonSubtypesRegister automatonSubtypesRegister ) { this.modelRepository = modelRepository; this.mappingRepository = mappingRepository; this.scriptRepository = scriptRepository; + this.automatonSubtypesRegister = automatonSubtypesRegister; } String noModelFoundErrorMessage = "No model found with this name"; @@ -54,8 +58,8 @@ public ScriptServiceImpl( public Script createFromMapping(String mappingName, boolean isPersistent) { Optional foundMapping = mappingRepository.findById(mappingName); if (foundMapping.isPresent()) { - SortedMapping sortedMapping = new SortedMapping(new InputMapping(foundMapping.get())); - String createdScript = Templater.mappingToScript(sortedMapping, new AutomatonIdProviderImpl()); + SortedMapping sortedMapping = new SortedMapping(new InputMapping(foundMapping.get(), automatonSubtypesRegister)); + String createdScript = Templater.mappingToScript(sortedMapping); // TODO: Add Date or randomise to ensure uniqueness String savedScriptName = sortedMapping.getName() + "-script"; String createdPar = null; @@ -328,12 +332,5 @@ public InstantiatedModel(AutomatonEntity automaton) { this.setGroup = automaton.getSetGroup(); } } - - private class AutomatonIdProviderImpl implements AutomatonIdProvider { - @Override - public String getId(AbstractAutomaton automaton) { - return String.format("%s_%s", automaton.getModel(), automaton.getWatchedElement()); - } - } } diff --git a/src/main/java/org/gridsuite/mapping/server/utils/AutomatonFamily.java b/src/main/java/org/gridsuite/mapping/server/utils/AutomatonFamily.java index 5687c8af..e752899b 100644 --- a/src/main/java/org/gridsuite/mapping/server/utils/AutomatonFamily.java +++ b/src/main/java/org/gridsuite/mapping/server/utils/AutomatonFamily.java @@ -11,4 +11,5 @@ */ public enum AutomatonFamily { CURRENT_LIMIT, + VOLTAGE, } diff --git a/src/main/java/org/gridsuite/mapping/server/utils/EquipmentType.java b/src/main/java/org/gridsuite/mapping/server/utils/EquipmentType.java index 5da58c26..e253457a 100644 --- a/src/main/java/org/gridsuite/mapping/server/utils/EquipmentType.java +++ b/src/main/java/org/gridsuite/mapping/server/utils/EquipmentType.java @@ -12,5 +12,9 @@ public enum EquipmentType { GENERATOR, LOAD, - CURRENT_LIMIT + CURRENT_LIMIT, + VOLTAGE, + BUS, + LINE, + TWO_WINDINGS_TRANSFORMER, } diff --git a/src/main/java/org/gridsuite/mapping/server/utils/Methods.java b/src/main/java/org/gridsuite/mapping/server/utils/Methods.java index 019329de..d66b5aa8 100644 --- a/src/main/java/org/gridsuite/mapping/server/utils/Methods.java +++ b/src/main/java/org/gridsuite/mapping/server/utils/Methods.java @@ -33,6 +33,9 @@ public static List convertStringToList(String stringArray) { } public static String convertListToString(List array) { + if (array == null) { + return null; + } return array.stream().map(value -> value.replace(",", ESCAPED_COMMA)).collect(Collectors.joining(", ")); } diff --git a/src/main/java/org/gridsuite/mapping/server/utils/Templater.java b/src/main/java/org/gridsuite/mapping/server/utils/Templater.java index 1a1609f4..1641996e 100644 --- a/src/main/java/org/gridsuite/mapping/server/utils/Templater.java +++ b/src/main/java/org/gridsuite/mapping/server/utils/Templater.java @@ -39,7 +39,7 @@ public static String flattenFilters(String composition, List fil return flattenedComposition[0]; } - public static String mappingToScript(ScriptServiceImpl.SortedMapping sortedMapping, AutomatonIdProvider automatonIdProvider) { + public static String mappingToScript(ScriptServiceImpl.SortedMapping sortedMapping) { String scriptTemplate; String sortedRulesTemplate; String ruleTemplate; @@ -79,18 +79,13 @@ public static String mappingToScript(ScriptServiceImpl.SortedMapping sortedMappi // Automata String[] automataScripts = sortedMapping.getAutomata().stream().map(automaton -> { - String familyModel = ""; - switch (automaton.getFamily()) { - case CURRENT_LIMIT: - familyModel = MappingConstants.CURRENT_LIMIT_MODEL_CLASS; - } + String familyModel = automaton.getExportedClassName(); imports.add(MappingConstants.AUTOMATON_IMPORT); ST automatonScript = new ST(automatonTemplate); automatonScript.add("familyModel", familyModel); - automatonScript.add("watchedElement", automaton.getWatchedElement()); - automatonScript.add("automatonId", automatonIdProvider.getId(automaton)); + automatonScript.add("automatonId", automaton.getExportedId()); automatonScript.add("parameterSetId", automaton.getSetGroup()); - String[] propertiesScripts = automaton.convertToBasicProperties().stream().map(property -> { + String[] propertiesScripts = automaton.getExportedProperties().stream().map(property -> { ST propertyScript = new ST(automatonPropertyTemplate); propertyScript.add("name", property.getName()); propertyScript.add("value", property.getValue()); diff --git a/src/main/resources/IEEE14Models.sql b/src/main/resources/IEEE14Models.sql index 55f798b1..1ffad9df 100644 --- a/src/main/resources/IEEE14Models.sql +++ b/src/main/resources/IEEE14Models.sql @@ -835,3 +835,41 @@ VALUES ('GeneratorSynchronousThreeWindingsProportionalRegulations', 'Generator') INSERT INTO models_model_variable_sets (model_name, variable_set_name) VALUES ('GeneratorSynchronousFourWindingsProportionalRegulations', 'Generator'), ('GeneratorSynchronousFourWindingsProportionalRegulations', 'VoltageRegulator'); + +--- model TapChangerBlockingAutomaton for VOLTAGE Equipment type +INSERT INTO models (model_name, equipment_type) +VALUES ('TapChangerBlockingAutomaton', 3); + +INSERT INTO model_parameter_definitions (model_name, name, origin, origin_name, type) +VALUES ('TapChangerBlockingAutomaton', 'tapChangerBlocking_UMin', 2, NULL, 2), + ('TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagBeforeBlocked', 2, NULL, 2), + ('TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagTransBlockedD', 2, NULL, 2), + ('TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagTransBlockedT', 2, NULL, 2); + +INSERT INTO model_sets_group (model_name, name, type) +VALUES ('TapChangerBlockingAutomaton', 'TCB', 0), + ('TapChangerBlockingAutomaton', 'TCB_2_4', 0), + ('TapChangerBlockingAutomaton', 'TCB_2_5', 0); + +INSERT INTO model_parameter_sets (group_name, group_type, model_name, name) +VALUES ('TCB', 0, 'TapChangerBlockingAutomaton', 'TCB'), + ('TCB_2_4', 0, 'TapChangerBlockingAutomaton', 'TCB_2_4'), + ('TCB_2_5', 0, 'TapChangerBlockingAutomaton', 'TCB_2_5'); + +INSERT INTO model_parameters (group_name, group_type, model_name, name, set_name, value_) +VALUES ('TCB', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_UMin', 'TCB', 10.), + ('TCB', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagBeforeBlocked', 'TCB', 100.), + ('TCB', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagTransBlockedD', 'TCB', 1000.), + ('TCB', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagTransBlockedT', 'TCB', 10.); + +INSERT INTO model_parameters (group_name, group_type, model_name, name, set_name, value_) +VALUES ('TCB_2_4', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_UMin', 'TCB_2_4', 14.), + ('TCB_2_4', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagBeforeBlocked', 'TCB_2_4', 104.), + ('TCB_2_4', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagTransBlockedD', 'TCB_2_4', 1004.), + ('TCB_2_4', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagTransBlockedT', 'TCB_2_4', 14.); + +INSERT INTO model_parameters (group_name, group_type, model_name, name, set_name, value_) +VALUES ('TCB_2_5', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_UMin', 'TCB_2_5', 15.), + ('TCB_2_5', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagBeforeBlocked', 'TCB_2_5', 105.), + ('TCB_2_5', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagTransBlockedD', 'TCB_2_5', 1005.), + ('TCB_2_5', 0, 'TapChangerBlockingAutomaton', 'tapChangerBlocking_tLagTransBlockedT', 'TCB_2_5', 15.); diff --git a/src/main/resources/automaton.st b/src/main/resources/automaton.st index a0d3cdf2..b238ced1 100644 --- a/src/main/resources/automaton.st +++ b/src/main/resources/automaton.st @@ -1,5 +1,4 @@ { - staticId "" dynamicModelId "" parameterSetId "" diff --git a/src/main/resources/db/changelog/changesets/changelog_20230515T135427Z.xml b/src/main/resources/db/changelog/changesets/changelog_20230515T135427Z.xml new file mode 100644 index 00000000..eac5b750 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20230515T135427Z.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 92471d8d..83cf7913 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -15,3 +15,7 @@ databaseChangeLog: - include: file: changesets/changelog_20230531T113101Z.xml relativeToChangelogFile: true + + - include: + file: changesets/changelog_20230515T135427Z.xml + relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/mapping/server/MappingControllerTest.java b/src/test/java/org/gridsuite/mapping/server/MappingControllerTest.java index 50c27f38..6ac4d394 100644 --- a/src/test/java/org/gridsuite/mapping/server/MappingControllerTest.java +++ b/src/test/java/org/gridsuite/mapping/server/MappingControllerTest.java @@ -116,10 +116,18 @@ String mapping(String name) { " \"automata\": [\n" + " {\n" + " \"family\": \"CURRENT_LIMIT\",\n" + - " \"model\": \"automaton_model\",\n" + + " \"model\": \"CurrentLimitAutomaton\",\n" + " \"setGroup\": \"automaton_group\",\n" + " \"watchedElement\": \"element_id\",\n" + " \"side\": \"Branch.Side.ONE\"\n" + + " },\n" + + " {\n" + + " \"family\": \"VOLTAGE\",\n" + + " \"model\": \"TapChangerBlockingAutomaton\",\n" + + " \"setGroup\": \"automaton_group_2\",\n" + + " \"name\": \"automaton_name\",\n" + + " \"uMeasurements\": [\"bus_id_1\", \"bus_id_2\"],\n" + + " \"transformers\": [\"load_id_1\", \"load_id_2\"]\n" + " }\n" + " ],\n" + " \"controlledParameters\": false" + diff --git a/src/test/java/org/gridsuite/mapping/server/NetworkControllerTest.java b/src/test/java/org/gridsuite/mapping/server/NetworkControllerTest.java index 85aef21c..9d356ca2 100644 --- a/src/test/java/org/gridsuite/mapping/server/NetworkControllerTest.java +++ b/src/test/java/org/gridsuite/mapping/server/NetworkControllerTest.java @@ -6,11 +6,13 @@ */ package org.gridsuite.mapping.server; +import com.fasterxml.jackson.databind.ObjectMapper; import com.powsybl.commons.PowsyblException; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.test.NetworkTest1Factory; import com.powsybl.network.store.client.NetworkStoreService; import com.powsybl.network.store.client.PreloadingStrategy; +import org.gridsuite.mapping.server.dto.NetworkValues; import org.gridsuite.mapping.server.model.NetworkEntity; import org.gridsuite.mapping.server.repository.NetworkRepository; import org.gridsuite.mapping.server.service.NetworkService; @@ -18,6 +20,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -32,6 +36,7 @@ import org.springframework.test.web.client.ExpectedCount; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.web.client.RestTemplate; @@ -62,9 +67,17 @@ @ContextConfiguration(classes = {MappingApplication.class}) public class NetworkControllerTest { + private static final Logger LOGGER = LoggerFactory.getLogger(NetworkControllerTest.class); + + public static final String RESOURCE_PATH_DELIMETER = "/"; + public static final String TEST_DATA_DIR = RESOURCE_PATH_DELIMETER + "data"; + @Autowired private RestTemplate restTemplate; + @Autowired + private ObjectMapper objectMapper; + @Autowired private NetworkRepository networkRepository; @@ -159,54 +172,22 @@ public void idTest() throws Exception { Network testNetwork = NetworkTest1Factory.create(); Mockito.when(networkStoreService.getNetwork(networkUUID, PreloadingStrategy.COLLECTION)).thenReturn(testNetwork); - String expectedResult = "{\n" + - "\"networkId\": \"" + networkUUID + "\",\n" + - "\"propertyValues\": [\n" + - " {\n" + - " \"type\": \"GENERATOR\",\n" + - " \"values\": {\n" + - " \"terminal.voltageLevel.nominalV\": [\n" + - " \"400.0\"\n" + - " ],\n" + - " \"terminal.voltageLevel.substation.country.name\": [\n" + - " \"FRANCE\"\n" + - " ],\n" + - " \"id\": [\n" + - " \"generator1\"\n" + - - " ],\n" + - " \"energySource\": [\n" + - " \"NUCLEAR\"\n" + - " ],\n" + - " \"voltageRegulatorOn\": [\n" + - " \"true\"\n" + - " ]\n" + - " }\n" + - " },\n" + - " {\n" + - " \"type\": \"LOAD\",\n" + - " \"values\": {\n" + - " \"loadType\": [\n" + - " \"UNDEFINED\"\n" + - " ],\n" + - " \"terminal.voltageLevel.nominalV\": [\n" + - " \"400.0\"\n" + - " ],\n" + - " \"terminal.voltageLevel.substation.country.name\": [\n" + - " \"FRANCE\"\n" + - " ],\n" + - " \"id\": [\n" + - " \"load1\"\n" + - " ]\n" + - " }\n" + - " }\n" + - "]\n" + - "}"; - - mvc.perform(MockMvcRequestBuilders.get("/network/" + networkUUID + "/values") + MvcResult mvcResult = mvc.perform(get("/network/" + networkUUID + "/values") .contentType(APPLICATION_JSON)) .andExpect(status().isOk()) - .andExpect(content().json(expectedResult, true)); + .andReturn(); + + String resultNetworkValuesJson = mvcResult.getResponse().getContentAsString(); + + String networkValuesJson = new String(getClass().getResourceAsStream(TEST_DATA_DIR + RESOURCE_PATH_DELIMETER + "network/networkValues.json").readAllBytes()); + NetworkValues networkValues = objectMapper.readValue(networkValuesJson, NetworkValues.class); + networkValues.setNetworkId(networkUUID); + + String expectNetworkValuesJson = objectMapper.writeValueAsString(networkValues); + LOGGER.info("expect network values = " + expectNetworkValuesJson); + LOGGER.info("result network values = " + resultNetworkValuesJson); + + assertEquals(objectMapper.readTree(expectNetworkValuesJson), objectMapper.readTree(resultNetworkValuesJson)); Mockito.verify(networkService, times(1)).getNetworkValuesFromExistingNetwork(networkUUID); diff --git a/src/test/java/org/gridsuite/mapping/server/ScriptControllerTest.java b/src/test/java/org/gridsuite/mapping/server/ScriptControllerTest.java index 0aad767d..d2450abc 100644 --- a/src/test/java/org/gridsuite/mapping/server/ScriptControllerTest.java +++ b/src/test/java/org/gridsuite/mapping/server/ScriptControllerTest.java @@ -149,10 +149,18 @@ String mapping(String name, String modelName, String groupName) { " \"automata\": [\n" + " {\n" + " \"family\": \"CURRENT_LIMIT\",\n" + - " \"model\": \"automaton_model\",\n" + + " \"model\": \"CurrentLimitAutomaton\",\n" + " \"setGroup\": \"automaton_model\",\n" + " \"watchedElement\": \"element_id\",\n" + " \"side\": \"Branch.Side.ONE\"\n" + + " },\n" + + " {\n" + + " \"family\": \"VOLTAGE\",\n" + + " \"model\": \"TapChangerBlockingAutomaton\",\n" + + " \"setGroup\": \"automaton_group_2\",\n" + + " \"name\": \"automaton_name\",\n" + + " \"uMeasurements\": [\"bus_id_1\", \"bus_id_2\"],\n" + + " \"transformers\": [\"load_id_1\", \"load_id_2\"]\n" + " }\n" + " ],\n" + " \"controlledParameters\": false" + @@ -163,11 +171,18 @@ String mapping(String name, String modelName, String groupName) { String scriptOutput(String scriptName, String parentName) { return "{\"name\":\"" + scriptName + "\",\"parentName\":\"" + parentName + "\",\"script\":\"/**\n * Copyright (c) 2021, RTE (http://www.rte-france.com)\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at http://mozilla.org/MPL/2.0/.\n */\n\nimport com.powsybl.iidm.network.Generator\nimport com.powsybl.dynawaltz.models.automatons.CurrentLimitAutomaton\nimport com.powsybl.iidm.network.Branch\n\nfor (Generator equipment : network.generators) {\n if (equipment.id.equals(\\\"test\\\") && equipment.minP > 3.000000 && [\\\"HYDRO\\\", \\\"OTHERS\\\"].contains(equipment.energySource) && equipment.voltageRegulatorOn != true) {\n GeneratorFourWindings {\n staticId equipment.id\n parameterSetId \\\"GSFWPR\\\" + equipment.id\n }\n }\n}\n\n" + "CurrentLimitAutomaton {\n" + - " staticId \\\"element_id\\\"\n" + - " dynamicModelId \\\"automaton_model_element_id\\\"\n" + + " dynamicModelId \\\"CurrentLimitAutomaton_element_id\\\"\n" + " parameterSetId \\\"automaton_model\\\"\n" + + " staticId \\\"element_id\\\"\n" + " side Branch.Side.ONE\n" + - "}\",\"current\": true, \"parametersFile\": null}"; + "}\n\n" + + "TapChangerBlockingAutomaton {\n" + + " dynamicModelId \\\"automaton_name\\\"\n" + + " parameterSetId \\\"automaton_group_2\\\"\n" + + " uMeasurement \\\"bus_id_1\\\", \\\"bus_id_2\\\"\n" + + " transformers \\\"load_id_1\\\", \\\"load_id_2\\\"\n" + + "}" + + "\",\"current\": true, \"parametersFile\": null}"; } @Test diff --git a/src/test/resources/data/network/networkValues.json b/src/test/resources/data/network/networkValues.json new file mode 100644 index 00000000..b18b6f00 --- /dev/null +++ b/src/test/resources/data/network/networkValues.json @@ -0,0 +1,71 @@ +{ + "networkId": "c2f524f3-0c24-44eb-b828-db73d8d8c2ef", + "propertyValues": [ + { + "type": "GENERATOR", + "values": { + "terminal.voltageLevel.nominalV": [ + "400.0" + ], + "terminal.voltageLevel.substation.country.name": [ + "FRANCE" + ], + "id": [ + "generator1" + ], + "energySource": [ + "NUCLEAR" + ], + "voltageRegulatorOn": [ + "true" + ] + } + }, + { + "type": "LOAD", + "values": { + "loadType": [ + "UNDEFINED" + ], + "terminal.voltageLevel.nominalV": [ + "400.0" + ], + "terminal.voltageLevel.substation.country.name": [ + "FRANCE" + ], + "id": [ + "load1" + ] + } + }, + { + "type": "BUS", + "values": { + "terminal.voltageLevel.nominalV": [ + "400.0" + ], + "terminal.voltageLevel.substation.country.name": [ + "FRANCE" + ], + "id": [ + "voltageLevel1_0", + "voltageLevel1_1" + ] + } + }, + { + "type": "LINE", + "values": { + + } + }, + { + "type": "TWO_WINDINGS_TRANSFORMER", + "values": { + "terminal.voltageLevel.substation.country.name": [ + "FRANCE" + ] + } + } + ] +} \ No newline at end of file diff --git a/src/test/resources/mappingIEEE14Test01.json b/src/test/resources/mappingIEEE14Test01.json index 82caff7c..59924e15 100644 --- a/src/test/resources/mappingIEEE14Test01.json +++ b/src/test/resources/mappingIEEE14Test01.json @@ -56,7 +56,6 @@ ], "automata": [ { - "family": "CURRENT_LIMIT", "family": "CURRENT_LIMIT", "model": "CurrentLimitAutomaton", "setGroup": "CLA_2_4", @@ -64,7 +63,6 @@ "side": "Branch.Side.TWO" }, { - "family": "CURRENT_LIMIT", "family": "CURRENT_LIMIT", "model": "CurrentLimitAutomaton", "setGroup": "CLA_2_5",