diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0e10dd9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2021 Alliander N.V. +# +# SPDX-License-Identifier: Apache-2.0 + +version: 2 + +registries: + maven-github: + type: maven-repository + url: https://maven.pkg.github.com/com-pas/* + username: OWNER + password: ${{ secrets.DB_GITHUB_PACKAGES }} + +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 5 + + # Maintain dependencies for Maven + - package-ecosystem: "maven" + directory: "/" + registries: + - maven-github + schedule: + interval: "daily" + open-pull-requests-limit: 5 + ignore: + # Next two dependencies shouldn't be upgrade, because Quarkus isn't using newer version of EL. + - dependency-name: org.hibernate.validator:hibernate-validator + versions: [ "[7.0,)" ] + - dependency-name: org.glassfish:jakarta.el + versions: [ "[4.0,)" ] diff --git a/.github/workflows/build-project.yml b/.github/workflows/build-project.yml index d108fe8..34b8858 100644 --- a/.github/workflows/build-project.yml +++ b/.github/workflows/build-project.yml @@ -15,9 +15,10 @@ jobs: steps: - uses: actions/checkout@v2 - name: Set up JDK 1.11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v2.3.0 with: - java-version: 1.11 + distribution: 'zulu' + java-version: '11' - name: Create custom Maven Settings.xml uses: whelk-io/maven-settings-xml-action@v18 with: diff --git a/.github/workflows/release-project.yml b/.github/workflows/release-project.yml index 9aa0de7..f875e11 100644 --- a/.github/workflows/release-project.yml +++ b/.github/workflows/release-project.yml @@ -24,10 +24,11 @@ jobs: shell: bash # Extra the tagname form the git reference, value of GITHUB_REF will be something like refs/tags/. run: echo "##[set-output name=tagname;]$(echo ${GITHUB_REF##*/})" - - uses: actions/setup-java@v2 + - name: Set up JDK 1.11 + uses: actions/setup-java@v2.3.0 with: + distribution: 'zulu' java-version: '11' - distribution: 'adopt' - name: Create custom Maven Settings.xml uses: whelk-io/maven-settings-xml-action@v18 with: diff --git a/.github/workflows/sonarcloud-analysis.yml b/.github/workflows/sonarcloud-analysis.yml index 69223d9..503f044 100644 --- a/.github/workflows/sonarcloud-analysis.yml +++ b/.github/workflows/sonarcloud-analysis.yml @@ -16,18 +16,19 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - - name: Set up JDK 11 - uses: actions/setup-java@v1 + - name: Set up JDK 1.11 + uses: actions/setup-java@v2.3.0 with: - java-version: 1.11 + distribution: 'zulu' + java-version: '11' - name: Cache SonarCloud packages - uses: actions/cache@v1 + uses: actions/cache@v2.1.6 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v1 + uses: actions/cache@v2.1.6 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/MAPPING.md b/MAPPING.md index 3634531..0121f02 100644 --- a/MAPPING.md +++ b/MAPPING.md @@ -17,6 +17,7 @@ There is an IEC document describing the mapping, namely IEC/TS 62361-102, but no | id | name | | | name | desc | | | List<cim:VoltageLevel> | List<TVoltageLevel> | (1) | +| List<*PowerTransformer*> | List<TPowerTransformer> | | (1): The list of VoltageLevels that belong to the Substation. @@ -24,12 +25,15 @@ There is an IEC document describing the mapping, namely IEC/TS 62361-102, but no | -------------------------------- | -------------------------------- | --------- | | *cim:VoltageLevel* | *TVoltageLevel* | | | name or id | name | | +| '0' | nomFreq | (1) | | nominalV | voltage.value | | | 'k' | voltage.multiplier | | | 'V' | voltage.unit | | -| List<Bay> | List<TBay> | (1) | +| List<Bay> | List<TBay> | (2) | +| List<*PowerTransformer*> | List<TPowerTransformer> | | -(1): The list of Bays that belong to the VoltageLevel. +(1): The nomFreq will be set to 0 if there is a Switch connected to it of the type 'DCLineSegment'. +(2): The list of Bays that belong to the VoltageLevel. | CIM Class | IEC Class | Remark | | -------------------------------- | -------------------------------- | --------- | @@ -37,6 +41,7 @@ There is an IEC document describing the mapping, namely IEC/TS 62361-102, but no | name or id | name | | | List<cim:ConnectivityNode> | List<TConnectivityNode> | (1) | | List<*Switches*> | List<TConductingEquipment> | (2) | +| List<*PowerTransformer*> | List<TPowerTransformer> | | (1): ConnectivityNode in IEC CIM can be linked to a Bay, but also to the VoltageLevel. In IEC 61850 a ConnectivityNode can only be added to a Bay, so also the ConnectivityNode from the VoltageLevel are added to the Bay. This causes those @@ -46,7 +51,30 @@ ProtectedSwitch.These classes are all mapped in the same way on IEC 61850 | CIM Class | IEC Class | Remark | | -------------------------------- | -------------------------------- | --------- | -| *cim:ConnectivityNode* | *TConnectivityNode | | +| *cim:PowerTransformer* | *TPowerTransformer* | | +| name or id | name | | +| description | desc | | +| 'PTR' | type | | + +| CIM Class | IEC Class | Remark | +| -------------------------------- | -------------------------------- | --------- | +| *cim:PowerTransformerEnd* | *TTransformerWinding* | | +| name or id | name | | +| 'PTW' | type | | +| Terminal | List<TTerminal> | (1) | + +(1): The terminal found in IEC CIM will be added as List to IEC 61850. + +| CIM Class | IEC Class | Remark | +| -------------------------------- | -------------------------------- | --------- | +| *cim:RatioTapChanger* | *TTapChanger* | | +| *cim:PhaseTapChanger* | *TTapChanger* | | +| name or id | name | | +| 'LTC' | type | | + +| CIM Class | IEC Class | Remark | +| -------------------------------- | -------------------------------- | --------- | +| *cim:ConnectivityNode* | *TConnectivityNode* | | | name or id | name | | | - | pathName | (1) | @@ -56,22 +84,63 @@ ConnectivityNode. | CIM Class | IEC Class | Remark | | -------------------------------- | -------------------------------- | --------- | -| *Switches* | *TConductingEquipment | (1) | +| *Switches* | *TConductingEquipment* | (1) | | name or id | name | | | type | type | (2) | | List<cim:Terminal> | List<TTerminal> | (3) | (1): Switches in IEC CIM can be the following types, cim:Switch cim:Breaker cim:Disconnector cim:LoadBreakSwitch cim: -ProtectedSwitch.These classes are all mapped in the same way on IEC 61850 -(2): The mapping between types is described in 5.6.2 of IEC/TS 62361-102. +ProtectedSwitch.These classes are all mapped in the same way on IEC 61850 +(2): The mapping between types is described in 5.6.2 of IEC/TS 62361-102. Below is a table describing how the mapper +implements this mapping. (3): The list of Terminal that belong to the Switch. | CIM Class | IEC Class | Remark | | -------------------------------- | -------------------------------- | --------- | -| *cim:Terminal* | *TTerminal | | +| *cim:Terminal* | *TTerminal* | | | name or id | name | | | - | connectivityNode | (1) | | - | CNodeName | (1) | (1): Use the ID of the ConnectivityNode to find the name or pathName of that ConnectivityNode. A map is saved of all ConnectivityNode that are processed for each Bay. + +## Mapping from Cim Switch Type to IEC TConductingEquipment + +| IEC 61850 Type | CIM Type | +| --------------- | --------------------- | +| BSH | Connector | +| CAB | ACLineSegment | +| CAB | DCLineSegment | +| CAP | ShuntCompensator | +| CAP | SeriesCompensator | +| CBR | ProtectedSwitch | +| CBR | Breaker | +| CBR | Recloser | +| CON | FrequencyConverter | +| CTR | CurrentTransformer | +| DIS | Switch | +| DIS | Disconnector | +| DIS | Fuse | +| DIS | Jumper | +| DIS | LoadBreakSwitch | +| DIS | GroundDisconnector | +| DIS | Sectionaliser | +| EFN | PetersenCoil | +| GEN | GeneratingUnit | +| LTC | TapChanger | +| LTC | RatioTapChanger | +| LTC | PhaseTapChanger | +| MOT | AsynchronousMachine | +| PSH | GroundingImpedance | +| PTR | PowerTransformer | +| PTW | TransformerEnd | +| PTW | PowerTransformerEnd | +| PTW | TransformerTankEnd | +| RES | EarthFaultCompensator | +| SAR | SurgeArrester | +| SCR | ACDCConverter | +| SMC | SynchronousMachine | +| TCR | StaticVarCompensator | +| TNK | TransformerTank | +| VTR | PotentialTransformer | diff --git a/README.md b/README.md index 9699662..5f6f07e 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,26 @@ You can then execute your native executable with: `./app/target/code-with-quarku If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.html . + +## Environment variables + +Below environment variable(s) can be used to configure which claims are used to fill user information, for instance Who +in History Record. + +| Environment variable | Java Property | Description | Example | +| -------------------------------- | ------------------------------ | --------------------------------------------- | ---------------- | +| USERINFO_WHO_CLAIMNAME | compas.userinfo.who.claimname | The Name of the user used in the Who History. | name | + +## Security + +To use most of the endpoints the users needs to be authenticated using JWT in the authorization header. There are 4 +environment variables that can be set in the container to configure the validation/processing of the JWT. + +| Environment variable | Java Property | Description | Example | +| -------------------------------- | -------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------- | +| JWT_VERIFY_KEY | smallrye.jwt.verify.key.location | Location of certificates to verify the JWT. | http://localhost:8089/auth/realms/compas/protocol/openid-connect/certs | +| JWT_VERIFY_ISSUER | mp.jwt.verify.issuer | The issuer of the JWT. | http://localhost:8089/auth/realms/compas | +| JWT_VERIFY_CLIENT_ID | mp.jwt.verify.audiences | The Client ID that should be in the "aud" claim. | cim-mapping | +| JWT_GROUPS_PATH | smallrye.jwt.path.groups | The JSON Path where to find the roles of the user. | resource_access/cim-mapping/roles | + +There are no roles defined in this service, only need to be authenticated. diff --git a/app/pom.xml b/app/pom.xml index 526f267..3e0e36d 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -18,7 +18,7 @@ SPDX-License-Identifier: Apache-2.0 jar - 2.0.0.Final + 2.2.1.Final @@ -94,7 +94,7 @@ SPDX-License-Identifier: Apache-2.0 io.quarkus - quarkus-test-security + quarkus-test-security-jwt test diff --git a/app/src/main/java/org/lfenergy/compas/cim/mapping/rest/UserInfoProperties.java b/app/src/main/java/org/lfenergy/compas/cim/mapping/rest/UserInfoProperties.java new file mode 100644 index 0000000..b800ecb --- /dev/null +++ b/app/src/main/java/org/lfenergy/compas/cim/mapping/rest/UserInfoProperties.java @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2021 Alliander N.V. +// +// SPDX-License-Identifier: Apache-2.0 +package org.lfenergy.compas.cim.mapping.rest; + +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithName; + +@ConfigMapping(prefix = "compas.userinfo") +public interface UserInfoProperties { + @WithName("who.claimname") + String who(); +} diff --git a/app/src/main/java/org/lfenergy/compas/cim/mapping/rest/v1/CompasCimMappingResource.java b/app/src/main/java/org/lfenergy/compas/cim/mapping/rest/v1/CompasCimMappingResource.java index 5efa426..1cd1893 100644 --- a/app/src/main/java/org/lfenergy/compas/cim/mapping/rest/v1/CompasCimMappingResource.java +++ b/app/src/main/java/org/lfenergy/compas/cim/mapping/rest/v1/CompasCimMappingResource.java @@ -4,10 +4,13 @@ package org.lfenergy.compas.cim.mapping.rest.v1; import io.quarkus.security.Authenticated; -import io.quarkus.security.identity.SecurityIdentity; +import org.eclipse.microprofile.jwt.JsonWebToken; +import org.lfenergy.compas.cim.mapping.rest.UserInfoProperties; import org.lfenergy.compas.cim.mapping.rest.v1.model.MapRequest; import org.lfenergy.compas.cim.mapping.rest.v1.model.MapResponse; import org.lfenergy.compas.cim.mapping.service.CompasCimMappingService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; @@ -22,10 +25,15 @@ @RequestScoped @Path("/cim/v1/") public class CompasCimMappingResource { + private static final Logger LOGGER = LoggerFactory.getLogger(CompasCimMappingResource.class); + private CompasCimMappingService compasCimMappingService; @Inject - SecurityIdentity securityIdentity; + JsonWebToken jsonWebToken; + + @Inject + UserInfoProperties userInfoProperties; @Inject public CompasCimMappingResource(CompasCimMappingService compasCimMappingService) { @@ -37,8 +45,11 @@ public CompasCimMappingResource(CompasCimMappingService compasCimMappingService) @Consumes(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML) public MapResponse mapCimToScl(@Valid MapRequest request) { + String who = jsonWebToken.getClaim(userInfoProperties.who()); + LOGGER.trace("Username used for Who {}", who); + var response = new MapResponse(); - response.setScl(compasCimMappingService.map(request.getCimData(), securityIdentity.getPrincipal())); + response.setScl(compasCimMappingService.map(request.getCimData(), who)); return response; } } \ No newline at end of file diff --git a/app/src/main/resources/application.properties b/app/src/main/resources/application.properties index fbfa191..2f841a2 100644 --- a/app/src/main/resources/application.properties +++ b/app/src/main/resources/application.properties @@ -2,6 +2,8 @@ # # SPDX-License-Identifier: Apache-2.0 +compas.userinfo.who.claimname = ${USERINFO_WHO_CLAIMNAME:name} + quarkus.http.cors = false quarkus.http.root-path = /compas-cim-mapping/ quarkus.http.limits.max-body-size = 150M diff --git a/app/src/test/java/org/lfenergy/compas/cim/mapping/rest/v1/CompasCimMappingResourceTest.java b/app/src/test/java/org/lfenergy/compas/cim/mapping/rest/v1/CompasCimMappingResourceTest.java index 793c8b2..04d25c6 100644 --- a/app/src/test/java/org/lfenergy/compas/cim/mapping/rest/v1/CompasCimMappingResourceTest.java +++ b/app/src/test/java/org/lfenergy/compas/cim/mapping/rest/v1/CompasCimMappingResourceTest.java @@ -7,6 +7,8 @@ import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.mockito.InjectMock; import io.quarkus.test.security.TestSecurity; +import io.quarkus.test.security.jwt.Claim; +import io.quarkus.test.security.jwt.JwtSecurity; import io.restassured.http.ContentType; import org.junit.jupiter.api.Test; import org.lfenergy.compas.cim.mapping.model.CimData; @@ -18,7 +20,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.security.Principal; import java.util.List; import static io.restassured.RestAssured.given; @@ -32,6 +33,10 @@ @QuarkusTest @TestHTTPEndpoint(CompasCimMappingResource.class) @TestSecurity(user = "test-mapper") +@JwtSecurity(claims = { + // Default the claim "name" is configured for Who, so we will set this claim for the test. + @Claim(key = "name", value = "Test User") +}) class CompasCimMappingResourceTest { @InjectMock private CompasCimMappingService compasCimMappingService; @@ -47,7 +52,7 @@ void mapCimToScl_WhenCalled_ThenCorrectMessageIsRetrieved() throws IOException { var scl = new SCL(); scl.setVersion("2007"); - when(compasCimMappingService.map(any(), any(Principal.class))).thenReturn(scl); + when(compasCimMappingService.map(any(), eq("Test User"))).thenReturn(scl); var response = given() .contentType(ContentType.XML) @@ -65,7 +70,7 @@ void mapCimToScl_WhenCalled_ThenCorrectMessageIsRetrieved() throws IOException { var sclVersion = xmlPath.getString("cms:MapResponse.scl:SCL.@version"); assertNotNull(sclVersion); assertEquals("2007", sclVersion); - verify(compasCimMappingService, times(1)).map(any(), any(Principal.class)); + verify(compasCimMappingService, times(1)).map(any(), eq("Test User")); } private String readFile() throws IOException { diff --git a/pom.xml b/pom.xml index b1e9634..2e4d55c 100644 --- a/pom.xml +++ b/pom.xml @@ -24,15 +24,15 @@ SPDX-License-Identifier: Apache-2.0 3.0.0-M5 3.2.0 - 0.2.0 + 0.2.1 2.0.2 - 1.7.31 + 1.7.32 4.3.1 1.4.2.Final 5.7.2 - 3.11.2 + 3.12.4 0.9.1 @@ -156,7 +156,7 @@ SPDX-License-Identifier: Apache-2.0 org.glassfish jakarta.el - 3.0.3 + 3.0.4 test @@ -183,7 +183,7 @@ SPDX-License-Identifier: Apache-2.0 org.jboss.jandex jandex-maven-plugin - 1.1.0 + 1.1.1 make-index @@ -211,7 +211,7 @@ SPDX-License-Identifier: Apache-2.0 java - 0.8.6 + 0.8.7 jacoco target/site/jacoco/jacoco.xml, @@ -265,7 +265,7 @@ SPDX-License-Identifier: Apache-2.0 org.apache.maven.plugins maven-source-plugin - 3.2.0 + 3.2.1 attach-sources diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/CimMappingConstants.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/CimMappingConstants.java index c912645..9463d5d 100644 --- a/service/src/main/java/org/lfenergy/compas/cim/mapping/CimMappingConstants.java +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/CimMappingConstants.java @@ -13,4 +13,6 @@ public class CimMappingConstants { public static final String SCL_NS_URI = "http://www.iec.ch/61850/2003/SCL"; public static final String RDF_NS_URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + public static final String DC_LINE_SEGMENT_TYPE = "DCLineSegment"; + } diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/cgmes/CgmesCimReader.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/cgmes/CgmesCimReader.java index 593fc91..c59c69a 100644 --- a/service/src/main/java/org/lfenergy/compas/cim/mapping/cgmes/CgmesCimReader.java +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/cgmes/CgmesCimReader.java @@ -36,11 +36,11 @@ public CgmesCimReader(ElementConverter converter) { } /** - * Use PowSyBl to convert a CIM XML InputStream to the PowSyBl IIDM Model. + * Use PowSyBl to convert a CIM XML InputStream to the PowSyBl Cgmes Model. * Multiple InputStream Objects can be passed if needed. * * @param cimData The different InputStream Objects that combined define the CIM Model. - * @return The IIDM Network model that can be used to convert further to IEC 61850. + * @return The Cgmes Model that can be used to convert further to IEC 61850. */ public CgmesModel readModel(List cimData) { LOGGER.debug("Create a ReadOnlyDataSource from the input data."); diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapper.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapper.java index 8115496..2bb7085 100644 --- a/service/src/main/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapper.java +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapper.java @@ -10,7 +10,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Optional; +import java.math.BigDecimal; + +import static org.lfenergy.compas.cim.mapping.CimMappingConstants.DC_LINE_SEGMENT_TYPE; /** * Interface for MapStruct to configure how a Cim Model is Mapped to a SCL IEC Model, @@ -27,7 +29,7 @@ public abstract class CimToSclMapper { /** * Top level mapping method to start the mapping of all known elements from Cgmes Model - * and IIDM Network Model to the IEC SCL Model. + * to the IEC SCL Model. * * @param context Holding all data from which the SCL (also passed) needs to be filled. */ @@ -47,8 +49,15 @@ protected void beforeTNaming(@MappingTarget TNaming tNaming, context.addLast(tNaming); } + @BeforeMapping + protected void beforeSubstationToTSubstation(@MappingTarget TSubstation tSubstation, + @Context CimToSclMapperContext context) { + // Reset the Map of ConnectivityNodes, because we only need them inside processing of the Substation. + context.resetTConnectivityNodeMap(); + } + @Mapping(target = "name", source = "id") - @Mapping(target = "desc", source = "optionalName") + @Mapping(target = "desc", source = "name") protected abstract TSubstation mapSubstationToTSubstation(CgmesSubstation substation, @Context CimToSclMapperContext context); @@ -56,12 +65,16 @@ protected abstract TSubstation mapSubstationToTSubstation(CgmesSubstation substa protected void afterSubstationToTSubstation(CgmesSubstation substation, @MappingTarget TSubstation tSubstation, @Context CimToSclMapperContext context) { - // The bays need to be mapped in a special way, because IIDM doesn't know them. context.getVoltageLevelsBySubstation(substation.getId()) .stream() .map(voltageLevel -> mapVoltageLevelToTVoltageLevel(voltageLevel, context)) .forEach(tVoltageLevel -> tSubstation.getVoltageLevel().add(tVoltageLevel)); + // PowerTransformers coupled to the Bay Level. + context.getTransformers(substation.getId()) + .stream() + .map(transformer -> mapTransformerToTPowerTransformer(transformer, context)) + .forEach(tPowerTransformer -> tSubstation.getPowerTransformer().add(tPowerTransformer)); } @Mapping(target = "name", source = "nameOrId") @@ -75,29 +88,29 @@ protected abstract TVoltageLevel mapVoltageLevelToTVoltageLevel(CgmesVoltageLeve protected void afterVoltageLevelToTVoltageLevel(CgmesVoltageLevel cgmesVoltageLevel, @MappingTarget TVoltageLevel tVoltageLevel, @Context CimToSclMapperContext context) { - // The bays need to be mapped in a special way, because IIDM doesn't know them. context.getBaysByVoltageLevel(cgmesVoltageLevel.getId()) .stream() - .map(bay -> mapBayToTBay(bay, cgmesVoltageLevel, context)) + .map(bay -> mapBayToTBay(bay, cgmesVoltageLevel, tVoltageLevel, context)) .forEach(tBay -> tVoltageLevel.getBay().add(tBay)); - } - @BeforeMapping - protected void beforeBayToTBay(@MappingTarget TBay tBay, - @Context CimToSclMapperContext context) { - // Reset the Map of ConnectivityNodes, because we only need them inside processing of the Bay. - context.resetTConnectivityNodeMap(); + // PowerTransformers coupled to the Bay Level. + context.getTransformers(cgmesVoltageLevel.getId()) + .stream() + .map(transformer -> mapTransformerToTPowerTransformer(transformer, context)) + .forEach(tPowerTransformer -> tVoltageLevel.getPowerTransformer().add(tPowerTransformer)); } @Mapping(target = "name", source = "nameOrId") protected abstract TBay mapBayToTBay(CgmesBay cgmesBay, @Context CgmesVoltageLevel cgmesVoltageLevel, + @Context TVoltageLevel tVoltageLevel, @Context CimToSclMapperContext context); @AfterMapping protected void afterBayToTBay(CgmesBay cgmesBay, @MappingTarget TBay tBay, @Context CgmesVoltageLevel cgmesVoltageLevel, + @Context TVoltageLevel tVoltageLevel, @Context CimToSclMapperContext context) { // First we will process the Connectivity Nodes, because their path names are needed in the Terminal // of a Conduction Equipment. @@ -110,10 +123,62 @@ protected void afterBayToTBay(CgmesBay cgmesBay, // Now we can process the Conduction Equipment with their terminals. context.getSwitches(cgmesBay.getId()) .stream() - .map(cgmesSwitch -> mapSwitchToTConductingEquipment(cgmesSwitch, context)) + .map(cgmesSwitch -> mapSwitchToTConductingEquipment(cgmesSwitch, tVoltageLevel, context)) .forEach(tConductingEquipment -> tBay.getConductingEquipment().add(tConductingEquipment)); + + // PowerTransformers coupled to the Bay Level. + context.getTransformers(cgmesBay.getId()) + .stream() + .map(transformer -> mapTransformerToTPowerTransformer(transformer, context)) + .forEach(tPowerTransformer -> tBay.getPowerTransformer().add(tPowerTransformer)); } + @Mapping(target = "name", source = "nameOrId") + @Mapping(target = "desc", source = "description") + @Mapping(target = "type", constant = "PTR") + protected abstract TPowerTransformer mapTransformerToTPowerTransformer(CgmesTransformer transformer, + @Context CimToSclMapperContext context); + + @AfterMapping + protected void afterTransformerToTPowerTransformer(CgmesTransformer transformer, + @MappingTarget TPowerTransformer tPowerTransformer, + @Context CimToSclMapperContext context) { + // PowerTransformer Ends coupled to the PowerTransformer. + context.getTransformerEnds(transformer.getId()) + .stream() + .map(transformerEnd -> mapTransformerEndToTTransformerWinding(transformerEnd, context)) + .forEach(tTransformerWinding -> tPowerTransformer.getTransformerWinding().add(tTransformerWinding)); + } + + @Mapping(target = "name", source = "nameOrId") + @Mapping(target = "type", constant = "PTW") + protected abstract TTransformerWinding mapTransformerEndToTTransformerWinding(CgmesTransformerEnd transformerEnd, + @Context CimToSclMapperContext context); + + @AfterMapping + protected void afterTransformerEndToTTransformerWinding(CgmesTransformerEnd transformerEnd, + @MappingTarget TTransformerWinding tTransformerWinding, + @Context CimToSclMapperContext context) { + // Convert the Ratio-/PhaseTapChanger from IEC CIM to IEC 61850. + context.getTapChanger(transformerEnd.getId()) + .ifPresent(cgmesTapChanger -> { + var tTapChanger = mapTapChangerToTTapChanger(cgmesTapChanger, context); + tTransformerWinding.setTapChanger(tTapChanger); + }); + + context.getTerminal(transformerEnd.getTerminalId()) + .ifPresent(cgmesTerminal -> { + var tTerminal = mapTerminalToTTerminal(cgmesTerminal, context); + tTransformerWinding.getTerminal().add(tTerminal); + }); + + } + + @Mapping(target = "name", source = "nameOrId") + @Mapping(target = "type", constant = "LTC") + protected abstract TTapChanger mapTapChangerToTTapChanger(CgmesTapChanger tapChanger, + @Context CimToSclMapperContext context); + @Mapping(target = "name", source = "nameOrId") protected abstract TConnectivityNode mapConnectivityNodeToTConnectivityNode(CgmesConnectivityNode cgmesConnectivityNode, @Context CimToSclMapperContext context); @@ -130,12 +195,19 @@ protected void afterConnectivityNodeToTConnectivityNode(CgmesConnectivityNode cg @Mapping(target = "name", source = "nameOrId") @Mapping(target = "type", expression = "java( org.lfenergy.compas.cim.mapping.model.SwitchType.convertSwitchType(cgmesSwitch.getType()).name() )") protected abstract TConductingEquipment mapSwitchToTConductingEquipment(CgmesSwitch cgmesSwitch, + @Context TVoltageLevel tVoltageLevel, @Context CimToSclMapperContext context); @AfterMapping protected void afterSwitchToTConductingEquipment(CgmesSwitch cgmesSwitch, @MappingTarget TConductingEquipment tConductingEquipment, + @Context TVoltageLevel tVoltageLevel, @Context CimToSclMapperContext context) { + // For DCLineSegment the nomFreq from the Voltage Level to 0 + if (DC_LINE_SEGMENT_TYPE.equals(cgmesSwitch.getType())) { + tVoltageLevel.setNomFreq(BigDecimal.ZERO); + } + context.getTerminals(cgmesSwitch.getId()) .stream() .map(cgmesTerminal -> mapTerminalToTTerminal(cgmesTerminal, context)) @@ -149,10 +221,6 @@ protected void afterSwitchToTConductingEquipment(CgmesSwitch cgmesSwitch, protected abstract TTerminal mapTerminalToTTerminal(CgmesTerminal cgmesTerminal, @Context CimToSclMapperContext context); - protected String optionalString(Optional value) { - return value.orElse(null); - } - @AfterMapping protected void afterTNaming(@MappingTarget TNaming tNaming, @Context CimToSclMapperContext context) { diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperContext.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperContext.java index a715b09..c31a35e 100644 --- a/service/src/main/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperContext.java +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperContext.java @@ -4,6 +4,7 @@ package org.lfenergy.compas.cim.mapping.mapper; import com.powsybl.cgmes.model.CgmesModel; +import com.powsybl.triplestore.api.PropertyBags; import org.lfenergy.compas.cim.mapping.model.*; import org.lfenergy.compas.scl2007b4.model.TConnectivityNode; import org.lfenergy.compas.scl2007b4.model.TNaming; @@ -12,6 +13,26 @@ import java.util.stream.Collectors; public class CimToSclMapperContext { + public static final String SUBSTATION_PROP = "Substation"; + public static final String VOLTAGE_LEVEL_PROP = "VoltageLevel"; + public static final String BAY_PROP = "Bay"; + public static final String POWER_TRANSFORMER_PROP = "PowerTransformer"; + public static final String TRANSFORMER_END_PROP = "TransformerEnd"; + public static final String RATIO_TAP_CHANGER_PROP = "RatioTapChanger"; + public static final String PHASE_TAP_CHANGER_PROP = "PhaseTapChanger"; + public static final String SWITCH_PROP = "Switch"; + public static final String TERMINAL_PROP = "Terminal"; + public static final String CONNECTIVITY_NODE_PROP = "ConnectivityNode"; + public static final String CONNECTIVITY_NODE_CONTAINER_PROP = "ConnectivityNodeContainer"; + public static final String CONDUCTING_EQUIPMENT_PROP = "ConductingEquipment"; + public static final String EQUIPMENT_CONTAINER_PROP = "EquipmentContainer"; + public static final String NAME_PROP = "name"; + public static final String DESCRIPTION_PROP = "description"; + public static final String NOMINAL_VOLTAGE_PROP = "nominalVoltage"; + public static final String TYPE_PROP = "type"; + public static final String TERMINAL_1_PROP = "Terminal1"; + public static final String TERMINAL_2_PROP = "Terminal2"; + private final CgmesModel cgmesModel; public CimToSclMapperContext(CgmesModel cgmesModel) { @@ -19,7 +40,7 @@ public CimToSclMapperContext(CgmesModel cgmesModel) { } /** - * Search the CGMES Model for all Substations that below to the network. + * Search the CGMES Model for all Substations. * * @return The List of converted CGMES Substations that were found. */ @@ -27,13 +48,13 @@ public List getSubstations() { return cgmesModel.substations() .stream() .map(propertyBag -> new CgmesSubstation( - propertyBag.getId("Substation"), - propertyBag.get("name"))) + propertyBag.getId(SUBSTATION_PROP), + propertyBag.get(NAME_PROP))) .collect(Collectors.toList()); } /** - * Search the CGMES Model for VoltageLevels that below to a specific substation. + * Search the CGMES Model for VoltageLevels that are coupled to a specific substation. * * @param substationId The ID of the Substation. * @return The List of converted CGMES VoltageLevels that were found. @@ -41,16 +62,16 @@ public List getSubstations() { public List getVoltageLevelsBySubstation(String substationId) { return cgmesModel.voltageLevels() .stream() - .filter(propertyBag -> substationId.equals(propertyBag.getId("Substation"))) + .filter(propertyBag -> substationId.equals(propertyBag.getId(SUBSTATION_PROP))) .map(propertyBag -> new CgmesVoltageLevel( - propertyBag.getId("VoltageLevel"), - propertyBag.get("name"), - propertyBag.asDouble("nominalVoltage"))) + propertyBag.getId(VOLTAGE_LEVEL_PROP), + propertyBag.get(NAME_PROP), + propertyBag.asDouble(NOMINAL_VOLTAGE_PROP))) .collect(Collectors.toList()); } /** - * Search for bays that belong to a specific Voltage Level. Because CGMES Model doesn't support this + * Search for bays that are coupled to a specific Voltage Level. Because CGMES Model doesn't support this, * a SparQL is executed against the TripleStore. * * @param voltageLevelId The ID of the Voltage Level to filter on. @@ -58,21 +79,90 @@ public List getVoltageLevelsBySubstation(String substationId) */ public List getBaysByVoltageLevel(String voltageLevelId) { return cgmesModel.tripleStore().query( - "SELECT *\n" + - "WHERE {\n" + - "GRAPH ?graph {\n" + - " ?Bay\n" + - " a cim:Bay ;\n" + - " cim:IdentifiedObject.name ?name ;\n" + - " cim:Bay.VoltageLevel ?VoltageLevel ;\n" + - " FILTER (str(?VoltageLevel) = \"http://default-cgmes-model/#" + voltageLevelId + "\") " + - "}}").stream() - .map(bag -> new CgmesBay(bag.getId("Bay"), bag.get("name"))) + "SELECT *\n" + + "WHERE {\n" + + "GRAPH ?graph {\n" + + " ?Bay\n" + + " a cim:Bay ;\n" + + " cim:IdentifiedObject.name ?name ;\n" + + " cim:Bay.VoltageLevel ?VoltageLevel ;\n" + + " FILTER (str(?VoltageLevel) = \"http://default-cgmes-model/#" + voltageLevelId + "\") " + + "}}").stream() + .map(bag -> new CgmesBay(bag.getId(BAY_PROP), bag.get(NAME_PROP))) + .collect(Collectors.toList()); + } + + /** + * Search the CGMES Model for Power-Transformers that are coupled to a specific container. Because CGMES Model + * doesn't support filtering on Container ID, a SparQL is executed against the TripleStore. + * + * @param containerId The ID of the Container. + * @return The List of converted CGMES Power-Transformers that were found. + */ + public List getTransformers(String containerId) { + return cgmesModel.tripleStore().query( + "SELECT *\n" + + "{ GRAPH ?graph {\n" + + " ?PowerTransformer\n" + + " a cim:PowerTransformer ;\n" + + " cim:IdentifiedObject.name ?name ;\n" + + " cim:Equipment.EquipmentContainer ?EquipmentContainer .\n" + + " OPTIONAL { ?PowerTransformer cim:IdentifiedObject.description ?description } \n" + + " FILTER (str(?EquipmentContainer) = \"http://default-cgmes-model/#" + containerId + "\") " + + "}}") + .stream() + .map(propertyBag -> new CgmesTransformer( + propertyBag.getId(POWER_TRANSFORMER_PROP), + propertyBag.get(NAME_PROP), + propertyBag.get(DESCRIPTION_PROP))) + .collect(Collectors.toList()); + } + + /** + * Search the CGMES Model for Power-Transformer Ends that are coupled to a specific Power-Transformer. + * + * @param powerTransformerId The ID of the Power-Transformer. + * @return The List of converted CGMES Power-Transformer Ends that were found. + */ + public List getTransformerEnds(String powerTransformerId) { + return cgmesModel.transformerEnds() + .stream() + .filter(propertyBag -> powerTransformerId.equals(propertyBag.getId(POWER_TRANSFORMER_PROP))) + .map(propertyBag -> new CgmesTransformerEnd( + propertyBag.getId(TRANSFORMER_END_PROP), + propertyBag.get(NAME_PROP), + propertyBag.getId(TERMINAL_PROP))) .collect(Collectors.toList()); } /** - * Search the CGMES Model for Connectivity Nodes that below to a specific container. + * Search the CGMES Model for a RatioTapChanger or PhaseTapChanger that are coupled to a Power-Transformer End. + * + * @param powerTransformerEndId The ID of the Power-Transformer End. + * @return The converted CGMEs TapChanger found, or Empty Optional if non. + */ + public Optional getTapChanger(String powerTransformerEndId) { + // Convert all the RatioTapChangers from CIM. + var tapChanger = getTapChanger(cgmesModel.ratioTapChangers(), powerTransformerEndId, RATIO_TAP_CHANGER_PROP); + if (!tapChanger.isPresent()) { + // Convert all the PhaseTapChangers from CIM. + tapChanger = getTapChanger(cgmesModel.phaseTapChangers(), powerTransformerEndId, PHASE_TAP_CHANGER_PROP); + } + return tapChanger; + } + + private Optional getTapChanger(PropertyBags tapChangers, String powerTransformerEndId, String idName) { + return tapChangers + .stream() + .filter(propertyBag -> powerTransformerEndId.equals(propertyBag.getId(TRANSFORMER_END_PROP))) + .map(propertyBag -> new CgmesTapChanger( + propertyBag.getId(idName), + propertyBag.get(NAME_PROP))) + .findFirst(); + } + + /** + * Search the CGMES Model for Connectivity Nodes that are coupled to a specific container. * * @param containerId The ID of the Container. * @return The List of converted CGMES Connectivity Nodes that were found. @@ -80,15 +170,15 @@ public List getBaysByVoltageLevel(String voltageLevelId) { public List getConnectivityNode(String containerId) { return cgmesModel.connectivityNodes() .stream() - .filter(propertyBag -> containerId.equals(propertyBag.getId("ConnectivityNodeContainer"))) + .filter(propertyBag -> containerId.equals(propertyBag.getId(CONNECTIVITY_NODE_CONTAINER_PROP))) .map(propertyBag -> new CgmesConnectivityNode( - propertyBag.getId("ConnectivityNode"), - propertyBag.get("name"))) + propertyBag.getId(CONNECTIVITY_NODE_PROP), + propertyBag.get(NAME_PROP))) .collect(Collectors.toList()); } /** - * Search the CGMES Model for Switches (Breakers, Disconnector and more) that below to a specific container. + * Search the CGMES Model for Switches (Breakers, Disconnector and more) that are coupled to a specific container. * * @param containerId The ID of the Container. * @return The List of converted CGMES Switches that were found. @@ -96,18 +186,18 @@ public List getConnectivityNode(String containerId) { public List getSwitches(String containerId) { return cgmesModel.switches() .stream() - .filter(propertyBag -> containerId.equals(propertyBag.getId("EquipmentContainer"))) + .filter(propertyBag -> containerId.equals(propertyBag.getId(EQUIPMENT_CONTAINER_PROP))) .map(propertyBag -> new CgmesSwitch( - propertyBag.getId("Switch"), - propertyBag.get("name"), - propertyBag.getLocal("type"), - propertyBag.getId("Terminal1"), - propertyBag.getId("Terminal2"))) + propertyBag.getId(SWITCH_PROP), + propertyBag.get(NAME_PROP), + propertyBag.getLocal(TYPE_PROP), + propertyBag.getId(TERMINAL_1_PROP), + propertyBag.getId(TERMINAL_2_PROP))) .collect(Collectors.toList()); } /** - * Search the CGMES Model for Terminals that below to a specific Conducting Equipment. + * Search the CGMES Model for Terminals that are coupled to a specific Conducting Equipment. * * @param conductingEquipmentId The ID of the Conducting Equipment. * @return The List of converted CGMES Terminals that were found. @@ -115,14 +205,31 @@ public List getSwitches(String containerId) { public List getTerminals(String conductingEquipmentId) { return cgmesModel.terminals() .stream() - .filter(propertyBag -> conductingEquipmentId.equals(propertyBag.getId("ConductingEquipment"))) + .filter(propertyBag -> conductingEquipmentId.equals(propertyBag.getId(CONDUCTING_EQUIPMENT_PROP))) .map(propertyBag -> new CgmesTerminal( - propertyBag.getId("Terminal"), - propertyBag.get("name"), - propertyBag.getId("ConnectivityNode"))) + propertyBag.getId(TERMINAL_PROP), + propertyBag.get(NAME_PROP), + propertyBag.getId(CONNECTIVITY_NODE_PROP))) .collect(Collectors.toList()); } + /** + * Search the CGMES Model for a Terminal with a specific ID. + * + * @param terminalId The ID of the Terminal. + * @return The converted CGMES Terminal that is found. + */ + public Optional getTerminal(String terminalId) { + return cgmesModel.terminals() + .stream() + .filter(propertyBag -> terminalId.equals(propertyBag.getId(TERMINAL_PROP))) + .map(propertyBag -> new CgmesTerminal( + propertyBag.getId(TERMINAL_PROP), + propertyBag.get(NAME_PROP), + propertyBag.getId(CONNECTIVITY_NODE_PROP))) + .findFirst(); + } + /* * Below part contains methods to keep track of all the naming elements passed. * At the end this list is used to create a PathName for the ConnectivityNode. diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/model/AbstractCgmesEntity.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/AbstractCgmesEntity.java index 42959c6..c134728 100644 --- a/service/src/main/java/org/lfenergy/compas/cim/mapping/model/AbstractCgmesEntity.java +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/AbstractCgmesEntity.java @@ -3,8 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 package org.lfenergy.compas.cim.mapping.model; -import java.util.Optional; - public abstract class AbstractCgmesEntity { private String id; private String name; @@ -30,11 +28,7 @@ public void setName(String name) { this.name = name; } - public Optional getOptionalName() { - return Optional.ofNullable(getName()); - } - public String getNameOrId() { - return getOptionalName().orElseGet(this::getId); + return getName() != null ? getName() : getId(); } } diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTapChanger.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTapChanger.java new file mode 100644 index 0000000..e087b06 --- /dev/null +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTapChanger.java @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2021 Alliander N.V. +// +// SPDX-License-Identifier: Apache-2.0 +package org.lfenergy.compas.cim.mapping.model; + +public class CgmesTapChanger extends AbstractCgmesEntity { + public CgmesTapChanger(String id, String name) { + super(id, name); + } +} diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformer.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformer.java new file mode 100644 index 0000000..e2390b1 --- /dev/null +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformer.java @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2021 Alliander N.V. +// +// SPDX-License-Identifier: Apache-2.0 +package org.lfenergy.compas.cim.mapping.model; + +public class CgmesTransformer extends AbstractCgmesEntity { + private String description; + + public CgmesTransformer(String id, String name, String description) { + super(id, name); + this.description = description; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerEnd.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerEnd.java new file mode 100644 index 0000000..33ff12f --- /dev/null +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerEnd.java @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2021 Alliander N.V. +// +// SPDX-License-Identifier: Apache-2.0 +package org.lfenergy.compas.cim.mapping.model; + +public class CgmesTransformerEnd extends AbstractCgmesEntity { + private String terminalId; + + public CgmesTransformerEnd(String id, String name, String terminalId) { + super(id, name); + this.terminalId = terminalId; + } + + public String getTerminalId() { + return terminalId; + } + + public void setTerminalId(String terminalId) { + this.terminalId = terminalId; + } +} diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/model/SwitchType.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/SwitchType.java index d70ab36..ff62284 100644 --- a/service/src/main/java/org/lfenergy/compas/cim/mapping/model/SwitchType.java +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/model/SwitchType.java @@ -8,6 +8,7 @@ import java.util.Arrays; import java.util.List; +import static org.lfenergy.compas.cim.mapping.CimMappingConstants.DC_LINE_SEGMENT_TYPE; import static org.lfenergy.compas.cim.mapping.exception.CompasCimMappingErrorCode.UNKNOWN_TYPE_ERROR_CODE; /** @@ -17,7 +18,7 @@ */ public enum SwitchType { BSH("Connector"), - // CAB("ACLineSegment", "DCLineSegment"), + CAB("ACLineSegment", DC_LINE_SEGMENT_TYPE), CAP("ShuntCompensator", "SeriesCompensator"), CBR("ProtectedSwitch", "Breaker", "Recloser"), CON("FrequencyConverter"), @@ -25,21 +26,15 @@ public enum SwitchType { DIS("Switch", "Disconnector", "Fuse", "Jumper", "LoadBreakSwitch", "GroundDisconnector", "Sectionaliser"), EFN("PetersenCoil"), GEN("GeneratingUnit"), - // GIL("ACLineSegment", "DCLineSegment"), - IFL("ACLineSegment", "DCLineSegment", "EquivalentBranch"), - // LIN("ACLineSegment", "DCLineSegment"), LTC("TapChanger", "RatioTapChanger", "PhaseTapChanger"), MOT("AsynchronousMachine"), PSH("GroundingImpedance"), PTR("PowerTransformer"), PTW("TransformerEnd", "PowerTransformerEnd", "TransformerTankEnd"), - // REA("ShuntCompensator", "SeriesCompensator"), RES("EarthFaultCompensator"), - // RRC("SynchronousMachine"), SAR("SurgeArrester"), SCR("ACDCConverter"), SMC("SynchronousMachine"), - // TCF("FrequencyConverter"), TCR("StaticVarCompensator"), TNK("TransformerTank"), VTR("PotentialTransformer"); diff --git a/service/src/main/java/org/lfenergy/compas/cim/mapping/service/CompasCimMappingService.java b/service/src/main/java/org/lfenergy/compas/cim/mapping/service/CompasCimMappingService.java index e425459..c20047e 100644 --- a/service/src/main/java/org/lfenergy/compas/cim/mapping/service/CompasCimMappingService.java +++ b/service/src/main/java/org/lfenergy/compas/cim/mapping/service/CompasCimMappingService.java @@ -14,7 +14,6 @@ import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; -import java.security.Principal; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; @@ -22,7 +21,7 @@ import java.util.stream.Collectors; /** - * Mapping Service to process the passed CIM XML(s) (RDF Format) and convert these to a IIDM Model + * Mapping Service to process the passed CIM XML(s) (RDF Format) and convert these to a Cgmes Model * that is used to create a IEC SCL Model, including some basic data being filled in the Header. */ @ApplicationScoped @@ -43,12 +42,12 @@ public CompasCimMappingService(CgmesCimReader cgmesCimReader, /** * Map the passed CIM XML to IEC SCL Model. * - * @param cimData The CIM XML Data. - * @param principal + * @param cimData The CIM XML Data. + * @param who The name of the user who created the SCL from the CIM Data. * @return The created SCL Model. */ - public SCL map(List cimData, Principal principal) { - var scl = createBasicSCL(cimData, principal); + public SCL map(List cimData, String who) { + var scl = createBasicSCL(cimData, who); if (cimData != null && !cimData.isEmpty()) { // Convert the Data to the Network Model from PowSyBl @@ -60,12 +59,14 @@ public SCL map(List cimData, Principal principal) { } /** - * Create a basic SCL Obejct with common values filled. + * Create a basic SCL Object with common values filled. * + * @param cimData The CIM XML Data. + * @param who The name of the user who created the SCL from the CIM Data. * @return The created SCL Model. */ - SCL createBasicSCL(List cimData, Principal principal) { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss"); + SCL createBasicSCL(List cimData, String who) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssXXX"); ObjectFactory factory = new ObjectFactory(); // Create the SCL and set some default values. @@ -87,7 +88,7 @@ SCL createBasicSCL(List cimData, Principal principal) { item.setVersion(INITIAL_VERSION); item.setRevision(INITIAL_REVISION); item.setWhen(formatter.format(new Date())); - item.setWho(principal.getName()); + item.setWho(who); // Add all CIM filenames that where used to create the SCL Content. String what = "SCL created from CIM File(s)"; diff --git a/service/src/test/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperContextTest.java b/service/src/test/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperContextTest.java index f08c514..2295905 100644 --- a/service/src/test/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperContextTest.java +++ b/service/src/test/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperContextTest.java @@ -19,6 +19,7 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.*; +import static org.lfenergy.compas.cim.mapping.mapper.CimToSclMapperContext.*; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -36,9 +37,9 @@ void getSubstations_WhenCalled_ThenPropertyBagsIsConvertedToCgmesSubstation() { var substationId = "SubstationId"; var substationName = "Name Substation"; var bags = new PropertyBags(); - var bag = new PropertyBag(List.of("Substation", "name")); - bag.put("Substation", substationId); - bag.put("name", substationName); + var bag = new PropertyBag(List.of(SUBSTATION_PROP, NAME_PROP)); + bag.put(SUBSTATION_PROP, substationId); + bag.put(NAME_PROP, substationName); bags.add(bag); when(cgmesModel.substations()).thenReturn(bags); @@ -57,18 +58,18 @@ void getVoltageLevelsBySubstation_WhenCalledWithKnownId_ThenPropertyBagsIsFilter var voltageLevelName = "Name VoltageLevel"; var substationId = "Known Substation ID"; var bags = new PropertyBags(); - var bag = new PropertyBag(List.of("VoltageLevel", "name", "nominalVoltage", "Substation")); - bag.put("VoltageLevel", voltageLevelId); - bag.put("name", voltageLevelName); - bag.put("nominalVoltage", "1.0"); - bag.put("Substation", substationId); + var bag = new PropertyBag(List.of(VOLTAGE_LEVEL_PROP, NAME_PROP, NOMINAL_VOLTAGE_PROP, SUBSTATION_PROP)); + bag.put(VOLTAGE_LEVEL_PROP, voltageLevelId); + bag.put(NAME_PROP, voltageLevelName); + bag.put(NOMINAL_VOLTAGE_PROP, "1.0"); + bag.put(SUBSTATION_PROP, substationId); bags.add(bag); - bag = new PropertyBag(List.of("VoltageLevel", "name", "nominalVoltage", "Substation")); - bag.put("VoltageLevel", "Other ID"); - bag.put("name", "Other Name"); - bag.put("nominalVoltage", "1.1"); - bag.put("Substation", "Unknown Container ID"); + bag = new PropertyBag(List.of(VOLTAGE_LEVEL_PROP, NAME_PROP, NOMINAL_VOLTAGE_PROP, SUBSTATION_PROP)); + bag.put(VOLTAGE_LEVEL_PROP, "Other ID"); + bag.put(NAME_PROP, "Other Name"); + bag.put(NOMINAL_VOLTAGE_PROP, "1.1"); + bag.put(SUBSTATION_PROP, "Unknown Container ID"); bags.add(bag); when(cgmesModel.voltageLevels()).thenReturn(bags); @@ -86,9 +87,9 @@ void getBaysByVoltageLevel_WhenSparQLReturnsBags_ThenPropertyBagIsConvertedToCgm var bayId = "BayId"; var bayName = "Name Bay"; var bags = new PropertyBags(); - var bag = new PropertyBag(List.of("Bay", "name")); - bag.put("Bay", bayId); - bag.put("name", bayName); + var bag = new PropertyBag(List.of(BAY_PROP, NAME_PROP)); + bag.put(BAY_PROP, bayId); + bag.put(NAME_PROP, bayName); bags.add(bag); var tripleStore = mock(TripleStore.class); @@ -104,22 +105,137 @@ void getBaysByVoltageLevel_WhenSparQLReturnsBags_ThenPropertyBagIsConvertedToCgm assertEquals(bayName, bay.getName()); } + @Test + void getTransformers_WhenSparQLReturnsBags_ThenPropertyBagIsConvertedToCgmesTransformer() { + var pwId = "PowertransformerId"; + var pwName = "Name Powertransformer"; + var pwDesc = "Desc Powertransformer"; + var pwContainerId = "Known Container ID"; + var bags = new PropertyBags(); + var bag = new PropertyBag(List.of(POWER_TRANSFORMER_PROP, NAME_PROP, DESCRIPTION_PROP, EQUIPMENT_CONTAINER_PROP)); + bag.put(POWER_TRANSFORMER_PROP, pwId); + bag.put(NAME_PROP, pwName); + bag.put(DESCRIPTION_PROP, pwDesc); + bag.put(EQUIPMENT_CONTAINER_PROP, pwContainerId); + bags.add(bag); + + var tripleStore = mock(TripleStore.class); + when(cgmesModel.tripleStore()).thenReturn(tripleStore); + when(tripleStore.query(anyString())).thenReturn(bags); + + var result = context.getTransformers(pwContainerId); + + assertNotNull(result); + assertEquals(1, result.size()); + var transformer = result.get(0); + assertEquals(pwId, transformer.getId()); + assertEquals(pwName, transformer.getName()); + assertEquals(pwDesc, transformer.getDescription()); + } + + @Test + void getTransformerEnds_WhenCalledWithKnownId_ThenPropertyBagsIsFilteredOnIdAndConvertedToCgmesTransformerEnd() { + var tfeId = "TfeId"; + var tfeName = "Name Tfe"; + var terminalId = "Known Terminal ID"; + var tfId = "Known Transformer ID"; + var bags = new PropertyBags(); + var bag = new PropertyBag(List.of(TRANSFORMER_END_PROP, NAME_PROP, POWER_TRANSFORMER_PROP, TERMINAL_PROP)); + bag.put(TRANSFORMER_END_PROP, tfeId); + bag.put(NAME_PROP, tfeName); + bag.put(POWER_TRANSFORMER_PROP, tfId); + bag.put(TERMINAL_PROP, terminalId); + bags.add(bag); + + bag = new PropertyBag(List.of(TRANSFORMER_END_PROP, NAME_PROP, POWER_TRANSFORMER_PROP, TERMINAL_PROP)); + bag.put(TRANSFORMER_END_PROP, "Other ID"); + bag.put(NAME_PROP, "Other Name"); + bag.put(POWER_TRANSFORMER_PROP, "Unknown Transformer ID"); + bag.put(TERMINAL_PROP, "Other Terminal ID"); + bags.add(bag); + + when(cgmesModel.transformerEnds()).thenReturn(bags); + + var result = context.getTransformerEnds(tfId); + assertNotNull(result); + assertEquals(1, result.size()); + var ccn = result.get(0); + assertEquals(tfeId, ccn.getId()); + assertEquals(tfeName, ccn.getName()); + assertEquals(terminalId, ccn.getTerminalId()); + } + + @Test + void getTapChanger_WhenNoTapChangersFound_ThenEmptyOptionalReturned() { + var tfeId = "Known Transformer End ID"; + + when(cgmesModel.ratioTapChangers()).thenReturn(new PropertyBags()); + when(cgmesModel.phaseTapChangers()).thenReturn(new PropertyBags()); + + var result = context.getTapChanger(tfeId); + assertFalse(result.isPresent()); + } + + @Test + void getTapChanger_WhenRatioTapChangersFound_ThenConvertedRatioTapChangerReturned() { + var tcId = "TapChangerId"; + var tcName = "Name TapChanger"; + var tfeId = "Known Transformer End ID"; + + var bags = new PropertyBags(); + var bag = new PropertyBag(List.of(RATIO_TAP_CHANGER_PROP, NAME_PROP, TRANSFORMER_END_PROP)); + bag.put(RATIO_TAP_CHANGER_PROP, tcId); + bag.put(NAME_PROP, tcName); + bag.put(TRANSFORMER_END_PROP, tfeId); + bags.add(bag); + when(cgmesModel.ratioTapChangers()).thenReturn(bags); + + var result = context.getTapChanger(tfeId); + assertTrue(result.isPresent()); + var tapChanger = result.get(); + assertEquals(tcId, tapChanger.getId()); + assertEquals(tcName, tapChanger.getName()); + } + + @Test + void getTapChanger_WhenNoRatioTapChangerFoundButPhaseTapChangersFound_ThenConvertedPhaseTapChangerReturned() { + var tcId = "TapChangerId"; + var tcName = "Name TapChanger"; + var tfeId = "Known Transformer End ID"; + + when(cgmesModel.ratioTapChangers()).thenReturn(new PropertyBags()); + + var bags = new PropertyBags(); + var bag = new PropertyBag(List.of(PHASE_TAP_CHANGER_PROP, NAME_PROP, TRANSFORMER_END_PROP)); + bag.put(PHASE_TAP_CHANGER_PROP, tcId); + bag.put(NAME_PROP, tcName); + bag.put(TRANSFORMER_END_PROP, tfeId); + bags.add(bag); + when(cgmesModel.phaseTapChangers()).thenReturn(bags); + + var result = context.getTapChanger(tfeId); + assertTrue(result.isPresent()); + var tapChanger = result.get(); + assertEquals(tcId, tapChanger.getId()); + assertEquals(tcName, tapChanger.getName()); + } + @Test void getConnectivityNode_WhenCalledWithKnownId_ThenPropertyBagsIsFilteredOnIdAndConvertedToCgmesConnectivityNode() { var ccnId = "CcnId"; var ccnName = "Name Ccn"; var ccnContainerId = "Known Container ID"; var bags = new PropertyBags(); - var bag = new PropertyBag(List.of("ConnectivityNode", "name", "ConnectivityNodeContainer")); - bag.put("ConnectivityNode", ccnId); - bag.put("name", ccnName); - bag.put("ConnectivityNodeContainer", ccnContainerId); + var bag = new PropertyBag(List.of(CONNECTIVITY_NODE_PROP, NAME_PROP, CONNECTIVITY_NODE_CONTAINER_PROP)); + bag.put(CONNECTIVITY_NODE_PROP, ccnId); + bag.put(NAME_PROP, ccnName); + bag.put(CONNECTIVITY_NODE_CONTAINER_PROP, ccnContainerId); bags.add(bag); - bag = new PropertyBag(List.of("ConnectivityNode", "name", "ConnectivityNodeContainer")); - bag.put("ConnectivityNode", "Other ID"); - bag.put("name", "Other Name"); - bag.put("ConnectivityNodeContainer", "Unknown Container ID"); + bag = new PropertyBag(List.of(CONNECTIVITY_NODE_PROP, NAME_PROP, CONNECTIVITY_NODE_CONTAINER_PROP)); + bag.put(CONNECTIVITY_NODE_PROP, "Other ID"); + bag.put(NAME_PROP, "Other Name"); + bag.put(CONNECTIVITY_NODE_CONTAINER_PROP, "Unknown Container ID"); bags.add(bag); when(cgmesModel.connectivityNodes()).thenReturn(bags); @@ -138,20 +254,20 @@ void getSwitches_WhenCalledWithKnownId_ThenPropertyBagsIsFilteredOnIdAndConverte var switchName = "Name Switch"; var containerId = "Known Container ID"; var bags = new PropertyBags(); - var bag = new PropertyBag(List.of("Switch", "name", "type", "EquipmentContainer", "Terminal1", "Terminal2")); - bag.put("Switch", switchId); - bag.put("name", switchName); - bag.put("type", "Breaker"); - bag.put("EquipmentContainer", containerId); - bag.put("Terminal1", "Terminal1 ID"); - bag.put("Terminal2", "Terminal2 ID"); + var bag = new PropertyBag(List.of(SWITCH_PROP, NAME_PROP, TYPE_PROP, EQUIPMENT_CONTAINER_PROP, TERMINAL_1_PROP, TERMINAL_2_PROP)); + bag.put(SWITCH_PROP, switchId); + bag.put(NAME_PROP, switchName); + bag.put(TYPE_PROP, "Breaker"); + bag.put(EQUIPMENT_CONTAINER_PROP, containerId); + bag.put(TERMINAL_1_PROP, "Terminal1 ID"); + bag.put(TERMINAL_2_PROP, "Terminal2 ID"); bags.add(bag); - bag = new PropertyBag(List.of("Switch", "name", "type", "EquipmentContainer", "Terminal1", "Terminal2")); - bag.put("Switch", "Other ID"); - bag.put("name", "Other Name"); - bag.put("type", "Breaker"); - bag.put("EquipmentContainer", "Unknown Container ID"); + bag = new PropertyBag(List.of(SWITCH_PROP, NAME_PROP, TYPE_PROP, EQUIPMENT_CONTAINER_PROP, TERMINAL_1_PROP, TERMINAL_2_PROP)); + bag.put(SWITCH_PROP, "Other ID"); + bag.put(NAME_PROP, "Other Name"); + bag.put(TYPE_PROP, "Breaker"); + bag.put(EQUIPMENT_CONTAINER_PROP, "Unknown Container ID"); bags.add(bag); when(cgmesModel.switches()).thenReturn(bags); @@ -171,18 +287,18 @@ void getTerminals_WhenCalledWithKnownId_ThenPropertyBagsIsFilteredOnIdAndConvert var ccnNode = "Connectivity Node ID"; var containerId = "Known Container ID"; var bags = new PropertyBags(); - var bag = new PropertyBag(List.of("Terminal", "name", "ConnectivityNode", "ConductingEquipment")); - bag.put("Terminal", terminalId); - bag.put("name", terminalName); - bag.put("ConnectivityNode", ccnNode); - bag.put("ConductingEquipment", containerId); + var bag = new PropertyBag(List.of(TERMINAL_PROP, NAME_PROP, CONNECTIVITY_NODE_PROP, CONDUCTING_EQUIPMENT_PROP)); + bag.put(TERMINAL_PROP, terminalId); + bag.put(NAME_PROP, terminalName); + bag.put(CONNECTIVITY_NODE_PROP, ccnNode); + bag.put(CONDUCTING_EQUIPMENT_PROP, containerId); bags.add(bag); - bag = new PropertyBag(List.of("Terminal", "name", "ConnectivityNode", "ConductingEquipment")); - bag.put("Terminal", "Other ID"); - bag.put("name", "Other Name"); - bag.put("ConnectivityNode", "Some Other ID"); - bag.put("ConductingEquipment", "Unknown Container ID"); + bag = new PropertyBag(List.of(TERMINAL_PROP, NAME_PROP, CONNECTIVITY_NODE_PROP, CONDUCTING_EQUIPMENT_PROP)); + bag.put(TERMINAL_PROP, "Other ID"); + bag.put(NAME_PROP, "Other Name"); + bag.put(CONNECTIVITY_NODE_PROP, "Some Other ID"); + bag.put(CONDUCTING_EQUIPMENT_PROP, "Unknown Container ID"); bags.add(bag); when(cgmesModel.terminals()).thenReturn(bags); @@ -196,6 +312,37 @@ void getTerminals_WhenCalledWithKnownId_ThenPropertyBagsIsFilteredOnIdAndConvert assertEquals(ccnNode, terminal.getConnectivityNodeId()); } + @Test + void getTerminal_WhenCalledWithKnownId_ThenPropertyBagsIsFilteredOnIdAndConvertedToCgmesTerminal() { + var terminalId = "TerminalId"; + var terminalName = "Name Terminal"; + var ccnNode = "Connectivity Node ID"; + var containerId = "Known Container ID"; + var bags = new PropertyBags(); + var bag = new PropertyBag(List.of(TERMINAL_PROP, NAME_PROP, CONNECTIVITY_NODE_PROP, CONDUCTING_EQUIPMENT_PROP)); + bag.put(TERMINAL_PROP, terminalId); + bag.put(NAME_PROP, terminalName); + bag.put(CONNECTIVITY_NODE_PROP, ccnNode); + bag.put(CONDUCTING_EQUIPMENT_PROP, containerId); + bags.add(bag); + + bag = new PropertyBag(List.of(TERMINAL_PROP, NAME_PROP, CONNECTIVITY_NODE_PROP, CONDUCTING_EQUIPMENT_PROP)); + bag.put(TERMINAL_PROP, "Other ID"); + bag.put(NAME_PROP, "Other Name"); + bag.put(CONNECTIVITY_NODE_PROP, "Some Other ID"); + bag.put(CONDUCTING_EQUIPMENT_PROP, "Unknown Container ID"); + bags.add(bag); + + when(cgmesModel.terminals()).thenReturn(bags); + + var result = context.getTerminal(terminalId); + assertNotNull(result); + var terminal = result.get(); + assertEquals(terminalId, terminal.getId()); + assertEquals(terminalName, terminal.getName()); + assertEquals(ccnNode, terminal.getConnectivityNodeId()); + } + @Test void createPathName_WhenCalledWithNoStack_ThenEmptyStringIsReturned() { assertEquals("", context.createPathName()); diff --git a/service/src/test/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperTest.java b/service/src/test/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperTest.java index b025940..f591c1c 100644 --- a/service/src/test/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperTest.java +++ b/service/src/test/java/org/lfenergy/compas/cim/mapping/mapper/CimToSclMapperTest.java @@ -9,8 +9,7 @@ import org.lfenergy.compas.cim.mapping.cgmes.CgmesCimReader; import org.lfenergy.compas.cim.mapping.model.*; import org.lfenergy.compas.core.commons.ElementConverter; -import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.scl2007b4.model.TConnectivityNode; +import org.lfenergy.compas.scl2007b4.model.*; import org.mapstruct.factory.Mappers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -25,6 +24,7 @@ import static java.util.Objects.requireNonNull; import static org.junit.jupiter.api.Assertions.*; +import static org.lfenergy.compas.cim.mapping.CimMappingConstants.DC_LINE_SEGMENT_TYPE; import static org.lfenergy.compas.cim.mapping.CimMappingConstants.RDF_NS_URI; import static org.mockito.Mockito.*; @@ -40,12 +40,21 @@ class CimToSclMapperTest { @Mock private CgmesBay cgmesBay; @Mock + private CgmesTransformer cgmesTransformer; + @Mock + private CgmesTransformerEnd cgmesTransformerEnd; + @Mock + private CgmesTapChanger cgmesTapChanger; + @Mock private CgmesConnectivityNode cgmesConnectivityNode; @Mock private CgmesSwitch cgmesSwitch; @Mock private CgmesTerminal cgmesTerminal; + @Mock + private TVoltageLevel tVoltageLevel; + private CimToSclMapper mapper; @BeforeEach @@ -74,10 +83,22 @@ void map_WhenWithCimData_ThenSclMapped() throws IOException { assertEquals("_af9a4ae3-ba2e-4c34-8e47-5af894ee20f4", substation.getName()); assertEquals("Sub1", substation.getDesc()); + assertEquals(2, substation.getPowerTransformer().size()); + var powerTransformer = substation.getPowerTransformer().get(1); + assertPowerTransformer(powerTransformer); + + assertEquals(3, powerTransformer.getTransformerWinding().size()); + var transformerWinding = powerTransformer.getTransformerWinding().get(0); + assertTransformerWinding(transformerWinding); + assertTapChanger(transformerWinding.getTapChanger()); + assertTerminal(transformerWinding.getTerminal(), 1, "T3_0", "CONNECTIVITY_NODE88", + "_af9a4ae3-ba2e-4c34-8e47-5af894ee20f4/S1 380kV/BAY_T3_0/CONNECTIVITY_NODE88"); + assertEquals(3, substation.getVoltageLevel().size()); var voltageLevel = substation.getVoltageLevel().get(0); assertEquals("S1 380kV", voltageLevel.getName()); assertNotNull(voltageLevel.getVoltage()); + assertNull(voltageLevel.getNomFreq()); assertEquals(BigDecimal.valueOf(380.0), voltageLevel.getVoltage().getValue()); assertEquals("k", voltageLevel.getVoltage().getMultiplier()); assertEquals("V", voltageLevel.getVoltage().getUnit()); @@ -87,20 +108,53 @@ void map_WhenWithCimData_ThenSclMapped() throws IOException { assertEquals("BAY_T4_2", bay.getName()); assertEquals(4, bay.getConnectivityNode().size()); - var connectivityNode = bay.getConnectivityNode().get(0); - assertEquals("CONNECTIVITY_NODE82", connectivityNode.getName()); - assertEquals("_af9a4ae3-ba2e-4c34-8e47-5af894ee20f4/S1 380kV/BAY_T4_2/CONNECTIVITY_NODE82", connectivityNode.getPathName()); + assertConnectivityNode(bay.getConnectivityNode().get(0)); assertEquals(3, bay.getConductingEquipment().size()); var conductingEquipment = bay.getConductingEquipment().get(0); + assertConductingEquipment(conductingEquipment); + + assertTerminal(conductingEquipment.getTerminal(), 2, "T4_2_ADDB1", "CONNECTIVITY_NODE83", + "_af9a4ae3-ba2e-4c34-8e47-5af894ee20f4/S1 380kV/BAY_T4_2/CONNECTIVITY_NODE83"); + } + + private void assertPowerTransformer(TPowerTransformer powerTransformer) { + assertNotNull(powerTransformer); + assertEquals("T3", powerTransformer.getName()); + assertEquals(TPowerTransformerEnum.PTR, powerTransformer.getType()); + assertEquals("Trafo-5", powerTransformer.getDesc()); + } + + private void assertTransformerWinding(TTransformerWinding powerTransformerEnd) { + assertNotNull(powerTransformerEnd); + assertEquals("T3", powerTransformerEnd.getName()); + assertEquals(TTransformerWindingEnum.PTW, powerTransformerEnd.getType()); + } + + private void assertConnectivityNode(TConnectivityNode connectivityNode) { + assertNotNull(connectivityNode); + assertEquals("CONNECTIVITY_NODE82", connectivityNode.getName()); + assertEquals("_af9a4ae3-ba2e-4c34-8e47-5af894ee20f4/S1 380kV/BAY_T4_2/CONNECTIVITY_NODE82", connectivityNode.getPathName()); + } + + private void assertConductingEquipment(TConductingEquipment conductingEquipment) { + assertNotNull(conductingEquipment); assertEquals("BREAKER25", conductingEquipment.getName()); assertEquals("CBR", conductingEquipment.getType()); + } + + private void assertTapChanger(TTapChanger tapChanger) { + assertNotNull(tapChanger); + assertEquals("T3", tapChanger.getName()); + assertEquals("LTC", tapChanger.getType()); + } - assertEquals(2, conductingEquipment.getTerminal().size()); - var terminal = conductingEquipment.getTerminal().get(0); - assertEquals("T4_2_ADDB1", terminal.getName()); - assertEquals("CONNECTIVITY_NODE83", terminal.getCNodeName()); - assertEquals("_af9a4ae3-ba2e-4c34-8e47-5af894ee20f4/S1 380kV/BAY_T4_2/CONNECTIVITY_NODE83", terminal.getConnectivityNode()); + private void assertTerminal(List terminals, int size, String name, String nodeName, String connectivityNode) { + assertEquals(size, terminals.size()); + var terminal = terminals.get(0); + assertEquals(name, terminal.getName()); + assertEquals(nodeName, terminal.getCNodeName()); + assertEquals(connectivityNode, terminal.getConnectivityNode()); } private String readFile() throws IOException { @@ -115,15 +169,15 @@ void mapSubstationToTSubstation_WhenCalledWithSubstation_ThenPropertiesMappedToT var expectedDesc = "Some description"; when(cgmesSubstation.getId()).thenReturn(expectedId); - when(cgmesSubstation.getOptionalName()).thenReturn(Optional.of(expectedDesc)); + when(cgmesSubstation.getName()).thenReturn(expectedDesc); var sclSubstation = mapper.mapSubstationToTSubstation(cgmesSubstation, context); assertNotNull(sclSubstation); assertEquals(expectedId, sclSubstation.getName()); assertEquals(expectedDesc, sclSubstation.getDesc()); - verify(cgmesSubstation, times(2)).getId(); - verify(cgmesSubstation, times(1)).getOptionalName(); + verify(cgmesSubstation, times(3)).getId(); + verify(cgmesSubstation, times(1)).getName(); verify(context, times(1)).addLast(sclSubstation); verifyNoMoreInteractions(cgmesSubstation); } @@ -141,7 +195,7 @@ void mapVoltageLevelToTVoltageLevel_WhenCalledWithVoltageLevel_ThenPropertiesMap assertNotNull(sclVoltageLevel); assertEquals(expectedName, sclVoltageLevel.getName()); assertEquals(expectedVoltage, sclVoltageLevel.getVoltage().getValue()); - verify(cgmesVoltageLevel, times(1)).getId(); + verify(cgmesVoltageLevel, times(2)).getId(); verify(cgmesVoltageLevel, times(1)).getNameOrId(); verify(cgmesVoltageLevel, times(1)).getNominalV(); verify(context, times(1)).addLast(sclVoltageLevel); @@ -154,16 +208,68 @@ void mapBayToTBay_WhenCalledWithCgmesBay_ThenPropertiesMappedToTBay() { when(cgmesBay.getNameOrId()).thenReturn(expectedName); - var sclBay = mapper.mapBayToTBay(cgmesBay, cgmesVoltageLevel, context); + var sclBay = mapper.mapBayToTBay(cgmesBay, cgmesVoltageLevel, tVoltageLevel, context); assertNotNull(sclBay); assertEquals(expectedName, sclBay.getName()); - verify(cgmesBay, times(2)).getId(); + verify(cgmesBay, times(3)).getId(); verify(cgmesBay, times(1)).getNameOrId(); verify(context, times(1)).addLast(sclBay); verifyNoMoreInteractions(cgmesBay); } + @Test + void mapTransformerToTPowerTransformer_WhenCalledWithCgmesTransformer_ThenPropertiesMappedToTPowerTransformer() { + var expectedName = "TheName"; + var expectedDesc = "Desc"; + + when(cgmesTransformer.getNameOrId()).thenReturn(expectedName); + when(cgmesTransformer.getDescription()).thenReturn(expectedDesc); + + var sclPowerTransformer = mapper.mapTransformerToTPowerTransformer(cgmesTransformer, context); + + assertNotNull(sclPowerTransformer); + assertEquals(expectedName, sclPowerTransformer.getName()); + assertEquals(expectedDesc, sclPowerTransformer.getDesc()); + assertEquals(TPowerTransformerEnum.PTR, sclPowerTransformer.getType()); + verify(cgmesTransformer, times(1)).getId(); + verify(cgmesTransformer, times(1)).getNameOrId(); + verify(cgmesTransformer, times(1)).getDescription(); + verifyNoMoreInteractions(cgmesTransformer); + } + + @Test + void mapTransformerEndToTTransformerWinding_WhenCalledWithCgmesTransformerEnd_ThenPropertiesMappedToTTransformerWinding() { + var expectedName = "TheName"; + + when(cgmesTransformerEnd.getNameOrId()).thenReturn(expectedName); + + var sclTransformerWinding = mapper.mapTransformerEndToTTransformerWinding(cgmesTransformerEnd, context); + + assertNotNull(sclTransformerWinding); + assertEquals(expectedName, sclTransformerWinding.getName()); + assertEquals(TTransformerWindingEnum.PTW, sclTransformerWinding.getType()); + verify(cgmesTransformerEnd, times(1)).getId(); + verify(cgmesTransformerEnd, times(1)).getNameOrId(); + verify(cgmesTransformerEnd, times(1)).getTerminalId(); + verifyNoMoreInteractions(cgmesTransformerEnd); + } + + @Test + void mapTapChangerToTTapChanger_WhenCalledWithCgmesTapChanger_ThenPropertiesMappedToTTapChanger() { + var expectedName = "TheName"; + + when(cgmesTapChanger.getNameOrId()).thenReturn(expectedName); + + var sclTapChanger = mapper.mapTapChangerToTTapChanger(cgmesTapChanger, context); + + assertNotNull(sclTapChanger); + assertEquals(expectedName, sclTapChanger.getName()); + assertEquals("LTC", sclTapChanger.getType()); + verify(cgmesTapChanger, times(1)).getNameOrId(); + verifyNoMoreInteractions(cgmesTapChanger); + } + @Test void mapConnectivityNodeToTConnectivityNode_WhenCalledWithCgmesConnectivityNode_ThenPropertiesMappedToTConnectivityNode() { var expectedId = "Id"; @@ -187,21 +293,43 @@ void mapConnectivityNodeToTConnectivityNode_WhenCalledWithCgmesConnectivityNode_ } @Test - void mapSwitchToTConductingEquipment_WhenCalledWithCgmesSwitch_ThenPropertiesMappedToTConductingEquipment() { + void mapSwitchToTConductingEquipment_WhenCalledWithCgmesSwitchOtherType_ThenPropertiesMappedToTConductingEquipment() { var expectedName = "TheName"; var expectedType = SwitchType.CBR.name(); when(cgmesSwitch.getNameOrId()).thenReturn(expectedName); when(cgmesSwitch.getType()).thenReturn(SwitchType.CBR.getCimTypes().get(0)); - var sclConductingEquipment = mapper.mapSwitchToTConductingEquipment(cgmesSwitch, context); + var sclConductingEquipment = mapper.mapSwitchToTConductingEquipment(cgmesSwitch, tVoltageLevel, context); + + assertNotNull(sclConductingEquipment); + assertEquals(expectedName, sclConductingEquipment.getName()); + assertEquals(expectedType, sclConductingEquipment.getType()); + verify(cgmesSwitch, times(1)).getId(); + verify(cgmesSwitch, times(1)).getNameOrId(); + verify(cgmesSwitch, times(2)).getType(); + verify(tVoltageLevel, never()).setNomFreq(any(BigDecimal.class)); + verify(context, times(1)).addLast(sclConductingEquipment); + verifyNoMoreInteractions(cgmesSwitch); + } + + @Test + void mapSwitchToTConductingEquipment_WhenCalledWithCgmesSwitchDCLineSegment_ThenPropertiesMappedToTConductingEquipment() { + var expectedName = "TheName"; + var expectedType = SwitchType.CAB.name(); + + when(cgmesSwitch.getNameOrId()).thenReturn(expectedName); + when(cgmesSwitch.getType()).thenReturn(DC_LINE_SEGMENT_TYPE); + + var sclConductingEquipment = mapper.mapSwitchToTConductingEquipment(cgmesSwitch, tVoltageLevel, context); assertNotNull(sclConductingEquipment); assertEquals(expectedName, sclConductingEquipment.getName()); assertEquals(expectedType, sclConductingEquipment.getType()); verify(cgmesSwitch, times(1)).getId(); verify(cgmesSwitch, times(1)).getNameOrId(); - verify(cgmesSwitch, times(1)).getType(); + verify(cgmesSwitch, times(2)).getType(); + verify(tVoltageLevel, times(1)).setNomFreq(BigDecimal.ZERO); verify(context, times(1)).addLast(sclConductingEquipment); verifyNoMoreInteractions(cgmesSwitch); } @@ -229,22 +357,5 @@ void mapTerminalToTTerminal_WhenCalledWithCgmesTerminal_ThenPropertiesMappedToTT verify(context, times(1)).getPathnameFromConnectivityNode(expectedConnectivityNode); verify(context, times(1)).getNameFromConnectivityNode(expectedConnectivityNode); verifyNoMoreInteractions(cgmesTerminal, context); - - } - - @Test - void optionalString_WhenCalledWithFilledOptional_ThenStringValueReturned() { - var expectedValue = "Some string"; - - var value = mapper.optionalString(Optional.of(expectedValue)); - - assertEquals(expectedValue, value); - } - - @Test - void optionalString_WhenCalledWithEmptyOptional_ThenBlankStringReturned() { - var value = mapper.optionalString(Optional.empty()); - - assertNull(value); } } \ No newline at end of file diff --git a/service/src/test/java/org/lfenergy/compas/cim/mapping/model/AbstractCgmesEntityTest.java b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/AbstractCgmesEntityTest.java index c3fbc47..58de8e1 100644 --- a/service/src/test/java/org/lfenergy/compas/cim/mapping/model/AbstractCgmesEntityTest.java +++ b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/AbstractCgmesEntityTest.java @@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; class AbstractCgmesEntityTest extends AbstractPojoTester { @Override @@ -13,32 +13,12 @@ protected Class getClassToBeTested() { return AbstractCgmesEntity.class; } - @Test - void getOptionalName_WhenCalledWithName_ThenOptionalWithNameReturned() { - var id = "id"; - var name = "name"; - var model = new CgmesBay(id, name); - - assertNotNull(model.getOptionalName()); - assertEquals(name, model.getOptionalName().get()); - } - - @Test - void getOptionalName_WhenCalledWithoutName_ThenOptionalEmptyReturned() { - var id = "id"; - var model = new CgmesBay(id, null); - - assertNotNull(model.getOptionalName()); - assertFalse(model.getOptionalName().isPresent()); - } - @Test void getNameOrId_WhenCalledWithName_ThenNameReturned() { var id = "id"; var name = "name"; var model = new CgmesBay(id, name); - assertNotNull(model.getOptionalName()); assertEquals(name, model.getNameOrId()); } @@ -47,7 +27,6 @@ void getNameOrId_WhenCalledWithoutName_ThenIdReturned() { var id = "id"; var model = new CgmesBay(id, null); - assertNotNull(model.getOptionalName()); assertEquals(id, model.getNameOrId()); } } diff --git a/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTapChangerTest.java b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTapChangerTest.java new file mode 100644 index 0000000..1cb3fd9 --- /dev/null +++ b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTapChangerTest.java @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2021 Alliander N.V. +// +// SPDX-License-Identifier: Apache-2.0 +package org.lfenergy.compas.cim.mapping.model; + +class CgmesTapChangerTest extends AbstractPojoTester { + @Override + protected Class getClassToBeTested() { + return CgmesTapChanger.class; + } +} diff --git a/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerEndTest.java b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerEndTest.java new file mode 100644 index 0000000..3086085 --- /dev/null +++ b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerEndTest.java @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2021 Alliander N.V. +// +// SPDX-License-Identifier: Apache-2.0 +package org.lfenergy.compas.cim.mapping.model; + +class CgmesTransformerEndTest extends AbstractPojoTester { + @Override + protected Class getClassToBeTested() { + return CgmesTransformerEnd.class; + } +} diff --git a/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerTest.java b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerTest.java new file mode 100644 index 0000000..e62822f --- /dev/null +++ b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/CgmesTransformerTest.java @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2021 Alliander N.V. +// +// SPDX-License-Identifier: Apache-2.0 +package org.lfenergy.compas.cim.mapping.model; + +class CgmesTransformerTest extends AbstractPojoTester { + @Override + protected Class getClassToBeTested() { + return CgmesTransformer.class; + } +} diff --git a/service/src/test/java/org/lfenergy/compas/cim/mapping/model/SwitchTypeTest.java b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/SwitchTypeTest.java index db1e179..221b9d3 100644 --- a/service/src/test/java/org/lfenergy/compas/cim/mapping/model/SwitchTypeTest.java +++ b/service/src/test/java/org/lfenergy/compas/cim/mapping/model/SwitchTypeTest.java @@ -16,7 +16,7 @@ class SwitchTypeTest { @Test void convertSwitchType_WhenCalledWithKnownCimType_ThenSCLTypeIsReturned() { - var expectedType = SwitchType.IFL; + var expectedType = SwitchType.CAB; var result = SwitchType.convertSwitchType(expectedType.getCimTypes().get(0)); assertEquals(expectedType, result); diff --git a/service/src/test/java/org/lfenergy/compas/cim/mapping/service/CompasCimMappingServiceTest.java b/service/src/test/java/org/lfenergy/compas/cim/mapping/service/CompasCimMappingServiceTest.java index 008eb1f..fc0ce54 100644 --- a/service/src/test/java/org/lfenergy/compas/cim/mapping/service/CompasCimMappingServiceTest.java +++ b/service/src/test/java/org/lfenergy/compas/cim/mapping/service/CompasCimMappingServiceTest.java @@ -15,7 +15,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.security.Principal; import java.util.Collections; import java.util.List; @@ -27,8 +26,6 @@ class CompasCimMappingServiceTest { @Mock private CgmesModel cgmesModel; - @Mock - private Principal principal; @Mock private CgmesCimReader cgmesCimReader; @@ -43,7 +40,7 @@ void map_WhenCalledWithData_ThenReaderAndMapperAreCalled() { when(cgmesCimReader.readModel(any())).thenReturn(cgmesModel); var cimDataList = List.of(new CimData()); - var scl = compasCimMappingService.map(cimDataList, principal); + var scl = compasCimMappingService.map(cimDataList, "username"); assertNotNull(scl); verify(cgmesCimReader, times(1)).readModel(cimDataList); @@ -53,7 +50,7 @@ void map_WhenCalledWithData_ThenReaderAndMapperAreCalled() { @Test void map_WhenCalledWithoutData_ThenReaderAndMapperAreNotCalled() { - var scl = compasCimMappingService.map(Collections.emptyList(), principal); + var scl = compasCimMappingService.map(Collections.emptyList(), "username"); assertNotNull(scl); verifyNoInteractions(cgmesCimReader, cimToSclMapper); @@ -61,7 +58,7 @@ void map_WhenCalledWithoutData_ThenReaderAndMapperAreNotCalled() { @Test void map_WhenCalledWithNullValue_ThenReaderAndMapperAreNotCalled() { - var scl = compasCimMappingService.map(null, principal); + var scl = compasCimMappingService.map(null, "username"); assertNotNull(scl); verifyNoInteractions(cgmesCimReader, cimToSclMapper); @@ -88,9 +85,8 @@ void createBasicSCL_WhenCalledWithNullValue_ThenNewSCLInstanceReturnedWithPartsF private void createBasicSCL_WhenCalled_ThenExpectedName(List cimDataList) { var expectedName = "Mr. Name"; - when(principal.getName()).thenReturn(expectedName); - var scl = compasCimMappingService.createBasicSCL(cimDataList, principal); + var scl = compasCimMappingService.createBasicSCL(cimDataList, expectedName); assertNotNull(scl); assertEquals("2007", scl.getVersion());