diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/BusbarSectionVMeasurementEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/BusbarSectionVMeasurementEntity.java new file mode 100644 index 000000000..9981b53e5 --- /dev/null +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/BusbarSectionVMeasurementEntity.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2026, 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.modification.server.entities.equipment.modification; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.gridsuite.modification.dto.BusbarSectionVMeasurementInfos; +import org.gridsuite.modification.server.entities.equipment.modification.attribute.BooleanModificationEmbedded; +import org.gridsuite.modification.server.entities.equipment.modification.attribute.DoubleModificationEmbedded; + +import java.util.UUID; + +import static org.gridsuite.modification.server.entities.equipment.modification.attribute.IAttributeModificationEmbeddable.toAttributeModification; + +/** + * @author Mohamed Ben Rejeb + */ +@NoArgsConstructor +@Getter +@Entity +@Table(name = "busbar_section_v_measurement") +public class BusbarSectionVMeasurementEntity { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "id") + private UUID id; + + @Column(name = "busbar_section_id") + private String busbarSectionId; + + @Embedded + @AttributeOverrides(value = { + @AttributeOverride(name = "value", column = @Column(name = "v_measurement_value")), + @AttributeOverride(name = "opType", column = @Column(name = "v_measurement_value_op")) + }) + private DoubleModificationEmbedded vMeasurementValue; + + @Embedded + @AttributeOverrides(value = { + @AttributeOverride(name = "value", column = @Column(name = "v_measurement_validity")), + @AttributeOverride(name = "opType", column = @Column(name = "v_measurement_validity_op")) + }) + private BooleanModificationEmbedded vMeasurementValidity; + + public BusbarSectionVMeasurementEntity(BusbarSectionVMeasurementInfos infos) { + this.busbarSectionId = infos.getBusbarSectionId(); + this.vMeasurementValue = infos.getVMeasurementValue() != null ? new DoubleModificationEmbedded(infos.getVMeasurementValue()) : null; + this.vMeasurementValidity = infos.getVMeasurementValidity() != null ? new BooleanModificationEmbedded(infos.getVMeasurementValidity()) : null; + } + + public BusbarSectionVMeasurementInfos toInfos() { + return BusbarSectionVMeasurementInfos.builder() + .busbarSectionId(busbarSectionId) + .vMeasurementValue(toAttributeModification(vMeasurementValue)) + .vMeasurementValidity(toAttributeModification(vMeasurementValidity)) + .build(); + } +} diff --git a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/VoltageLevelModificationEntity.java b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/VoltageLevelModificationEntity.java index fff3e8350..92a31b575 100644 --- a/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/VoltageLevelModificationEntity.java +++ b/src/main/java/org/gridsuite/modification/server/entities/equipment/modification/VoltageLevelModificationEntity.java @@ -15,16 +15,12 @@ import org.gridsuite.modification.server.entities.equipment.modification.attribute.DoubleModificationEmbedded; import org.gridsuite.modification.server.entities.equipment.modification.attribute.IAttributeModificationEmbeddable; -import jakarta.persistence.AttributeOverride; -import jakarta.persistence.AttributeOverrides; -import jakarta.persistence.Column; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.PrimaryKeyJoinColumn; -import jakarta.persistence.Table; +import jakarta.persistence.*; import org.springframework.util.CollectionUtils; +import java.util.ArrayList; +import java.util.List; + import static org.gridsuite.modification.dto.AttributeModification.toAttributeModification; /** @@ -72,6 +68,11 @@ public class VoltageLevelModificationEntity extends BasicEquipmentModificationEn }) private DoubleModificationEmbedded ipMax; + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) + @JoinColumn(name = "voltage_level_modification_id", nullable = false, + foreignKey = @ForeignKey(name = "busbar_section_v_measurement_vl_id_fk")) + private List busbarSectionVMeasurements; + public VoltageLevelModificationEntity(VoltageLevelModificationInfos voltageLevelModificationInfos) { super(voltageLevelModificationInfos); assignAttributes(voltageLevelModificationInfos); @@ -89,6 +90,15 @@ private void assignAttributes(VoltageLevelModificationInfos voltageLevelModifica this.highVoltageLimit = voltageLevelModificationInfos.getHighVoltageLimit() != null ? new DoubleModificationEmbedded(voltageLevelModificationInfos.getHighVoltageLimit()) : null; this.ipMin = voltageLevelModificationInfos.getIpMin() != null ? new DoubleModificationEmbedded(voltageLevelModificationInfos.getIpMin()) : null; this.ipMax = voltageLevelModificationInfos.getIpMax() != null ? new DoubleModificationEmbedded(voltageLevelModificationInfos.getIpMax()) : null; + if (this.busbarSectionVMeasurements == null) { + this.busbarSectionVMeasurements = new ArrayList<>(); + } + this.busbarSectionVMeasurements.clear(); + if (!CollectionUtils.isEmpty(voltageLevelModificationInfos.getBusbarSectionVMeasurements())) { + voltageLevelModificationInfos.getBusbarSectionVMeasurements().stream() + .map(BusbarSectionVMeasurementEntity::new) + .forEach(this.busbarSectionVMeasurements::add); + } } @Override @@ -110,6 +120,10 @@ public VoltageLevelModificationInfos toModificationInfos() { .highVoltageLimit(IAttributeModificationEmbeddable.toAttributeModification(getHighVoltageLimit())) .ipMin(IAttributeModificationEmbeddable.toAttributeModification(this.getIpMin())) .ipMax(IAttributeModificationEmbeddable.toAttributeModification(this.getIpMax())) + .busbarSectionVMeasurements(CollectionUtils.isEmpty(busbarSectionVMeasurements) ? null : + busbarSectionVMeasurements.stream() + .map(BusbarSectionVMeasurementEntity::toInfos) + .toList()) // properties .properties(CollectionUtils.isEmpty(getProperties()) ? null : getProperties().stream() diff --git a/src/main/resources/db/changelog/changesets/changelog_20260526T094439Z.xml b/src/main/resources/db/changelog/changesets/changelog_20260526T094439Z.xml new file mode 100644 index 000000000..e2092e654 --- /dev/null +++ b/src/main/resources/db/changelog/changesets/changelog_20260526T094439Z.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 2752eba51..cdeb87147 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -486,3 +486,6 @@ databaseChangeLog: - include: file: changesets/changelog_20260522T132309Z.xml relativeToChangelogFile: true + - include: + file: changesets/changelog_20260526T094439Z.xml + relativeToChangelogFile: true diff --git a/src/test/java/org/gridsuite/modification/server/modifications/VoltageLevelModificationTest.java b/src/test/java/org/gridsuite/modification/server/modifications/VoltageLevelModificationTest.java index 85f0f5653..85ba60ef2 100644 --- a/src/test/java/org/gridsuite/modification/server/modifications/VoltageLevelModificationTest.java +++ b/src/test/java/org/gridsuite/modification/server/modifications/VoltageLevelModificationTest.java @@ -7,10 +7,13 @@ package org.gridsuite.modification.server.modifications; import com.fasterxml.jackson.core.type.TypeReference; +import com.powsybl.iidm.network.BusbarSection; import com.powsybl.iidm.network.Network; import com.powsybl.iidm.network.VoltageLevel; import com.powsybl.iidm.network.extensions.IdentifiableShortCircuit; import com.powsybl.iidm.network.extensions.IdentifiableShortCircuitAdder; +import com.powsybl.iidm.network.extensions.Measurement; +import com.powsybl.iidm.network.extensions.Measurements; import org.gridsuite.modification.NetworkModificationException; import org.gridsuite.modification.dto.*; import org.gridsuite.modification.server.utils.NetworkCreation; @@ -19,10 +22,13 @@ import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.UUID; +import static org.assertj.core.api.Assertions.assertThat; + import static org.gridsuite.modification.NetworkModificationException.Type.MODIFY_VOLTAGE_LEVEL_ERROR; import static org.gridsuite.modification.server.report.NetworkModificationServerReportResourceBundle.ERROR_MESSAGE_KEY; import static org.gridsuite.modification.server.utils.TestUtils.assertLogMessage; @@ -39,6 +45,8 @@ class VoltageLevelModificationTest extends AbstractNetworkModificationTest { private static final String PROPERTY_NAME = "property-name"; private static final String PROPERTY_VALUE = "property-value"; + private static final Double MEASUREMENT_V_VALUE = 400.0; + private static final Boolean MEASUREMENT_V_VALID = true; @Override protected Network createNetwork(UUID networkUuid) { @@ -57,6 +65,12 @@ protected ModificationInfos buildModification() { .ipMax(new AttributeModification<>(0.8, OperationType.SET)) .ipMin(new AttributeModification<>(0.7, OperationType.SET)) .properties(List.of(FreePropertyInfos.builder().name(PROPERTY_NAME).value(PROPERTY_VALUE).build())) + .busbarSectionVMeasurements(List.of( + BusbarSectionVMeasurementInfos.builder() + .busbarSectionId("1.1") + .vMeasurementValue(new AttributeModification<>(MEASUREMENT_V_VALUE, OperationType.SET)) + .vMeasurementValidity(new AttributeModification<>(MEASUREMENT_V_VALID, OperationType.SET)) + .build())) .build(); } @@ -71,6 +85,12 @@ protected ModificationInfos buildModificationUpdate() { .highVoltageLimit(new AttributeModification<>(55D, OperationType.SET)) .ipMax(new AttributeModification<>(0.9, OperationType.SET)) .ipMin(new AttributeModification<>(0.5, OperationType.SET)) + .busbarSectionVMeasurements(List.of( + BusbarSectionVMeasurementInfos.builder() + .busbarSectionId("1.1") + .vMeasurementValue(new AttributeModification<>(380D, OperationType.SET)) + .vMeasurementValidity(new AttributeModification<>(false, OperationType.SET)) + .build())) .build(); } @@ -87,6 +107,18 @@ protected void assertAfterNetworkModificationCreation() { assertEquals(0.8, identifiableShortCircuit.getIpMax(), 0); assertEquals(0.7, identifiableShortCircuit.getIpMin(), 0); assertEquals(PROPERTY_VALUE, getNetwork().getVoltageLevel("v1").getProperty(PROPERTY_NAME)); + assertBusbarSectionMeasurement(getNetwork().getBusbarSection("1.1"), MEASUREMENT_V_VALUE, MEASUREMENT_V_VALID); + } + + private void assertBusbarSectionMeasurement(BusbarSection bbs, double expectedValue, boolean expectedValid) { + assertNotNull(bbs); + Measurements measurements = (Measurements) bbs.getExtension(Measurements.class); + assertNotNull(measurements); + Collection voltageMeasurements = measurements.getMeasurements(Measurement.Type.VOLTAGE).stream().toList(); + assertThat(voltageMeasurements).isNotEmpty().allSatisfy(m -> { + assertThat(m.getValue()).isEqualTo(expectedValue); + assertThat(m.isValid()).isEqualTo(expectedValid); + }); } @Override @@ -243,6 +275,30 @@ void testSetIpMaxOnEquipmentWitOnlyIpMaxExtension() throws Exception { assertEquals(targetIpMax, identifiableShortCircuit1.getIpMax(), 0); } + @Test + void testBusbarSectionMeasurementUpdateExistingPath() throws Exception { + // Apply first modification: adds a new voltage measurement to the BBS (add path) + applyModification((VoltageLevelModificationInfos) buildModification()); + assertBusbarSectionMeasurement(getNetwork().getBusbarSection("1.1"), MEASUREMENT_V_VALUE, MEASUREMENT_V_VALID); + + // Apply second modification: updates the existing voltage measurement (update/upsert path) + final double updatedValue = 380.0; + final boolean updatedValidity = false; + VoltageLevelModificationInfos updateModif = VoltageLevelModificationInfos.builder() + .stashed(false) + .equipmentId("v1") + .busbarSectionVMeasurements(List.of( + BusbarSectionVMeasurementInfos.builder() + .busbarSectionId("1.1") + .vMeasurementValue(new AttributeModification<>(updatedValue, OperationType.SET)) + .vMeasurementValidity(new AttributeModification<>(updatedValidity, OperationType.SET)) + .build())) + .build(); + applyModification(updateModif); + + assertBusbarSectionMeasurement(getNetwork().getBusbarSection("1.1"), updatedValue, updatedValidity); + } + private void applyModification(VoltageLevelModificationInfos infos) throws Exception { String body = getJsonBody(infos, null); ResultActions mockMvcResultActions = mockMvc.perform(post(getNetworkModificationUri())