diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LNodeStatusService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LNodeStatusService.java new file mode 100644 index 000000000..febcf3c33 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LNodeStatusService.java @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: 2024 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons; + +import lombok.RequiredArgsConstructor; +import org.lfenergy.compas.scl2007b4.model.*; +import org.lfenergy.compas.sct.commons.domain.DataAttribute; +import org.lfenergy.compas.sct.commons.domain.DoLinkedToDa; +import org.lfenergy.compas.sct.commons.domain.DoLinkedToDaFilter; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; +import org.lfenergy.compas.sct.commons.util.CommonConstants; +import org.lfenergy.compas.sct.commons.util.PrivateUtils; +import org.lfenergy.compas.sct.commons.util.SclConstructorHelper; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Stream; + +@RequiredArgsConstructor +public class LNodeStatusService { + + private static final String LNODE_STATUS_PRIVATE_TYPE = "COMPAS-LNodeStatus"; + private final LdeviceService ldeviceService; + private final LnService lnService; + private final DataTypeTemplatesService dataTypeTemplatesService; + + public List updateLnModStValBasedOnLNodeStatus(SCL scl) { + return scl.getSubstation().stream() + .flatMap(tSubstation -> tSubstation.getVoltageLevel().stream()) + .flatMap(tVoltageLevel -> tVoltageLevel.getBay().stream()) + .flatMap(tBay -> tBay.getFunction().stream()) + .flatMap(tFunction -> tFunction.getLNode().stream()) + .map(tlNode -> updateSingleLnModStValBasedOnLNodeStatus(scl, tlNode)) + .filter(Objects::nonNull) + .toList(); + } + + private SclReportItem updateSingleLnModStValBasedOnLNodeStatus(SCL scl, TLNode tlNode) { + String lNodeLNS = PrivateUtils.extractStringPrivate(tlNode, LNODE_STATUS_PRIVATE_TYPE).orElse(null); + if (!"on".equals(lNodeLNS) && !"off".equals(lNodeLNS)) { + return SclReportItem.error(lNodePath(tlNode), "The private %s of the LNode has invalid value. Expecting one of [on, off] but got : %s".formatted(LNODE_STATUS_PRIVATE_TYPE, lNodeLNS)); + } + TAnyLN anyLn = findLn(scl, tlNode.getIedName(), tlNode.getLdInst(), tlNode.getLnClass().getFirst(), tlNode.getLnInst(), tlNode.getPrefix()).orElse(null); + if (anyLn == null) { + return SclReportItem.error(lNodePath(tlNode), "LNode in Substation section does not have a matching LN in IED section"); + } + String anyLnLNS = PrivateUtils.extractStringPrivate(anyLn, LNODE_STATUS_PRIVATE_TYPE).orElse(null); + if (anyLnLNS == null) { + return SclReportItem.error(lnPath(tlNode), "LN does not have a private %s".formatted(LNODE_STATUS_PRIVATE_TYPE)); + } + if (!anyLnLNS.contains(lNodeLNS)) { + return SclReportItem.error(lnPath(tlNode), "Cannot set DAI Mod.stVal to %s, because LN private %s is set to %s".formatted(lNodeLNS, LNODE_STATUS_PRIVATE_TYPE, anyLnLNS)); + } + TDAI daiModStVal = lnService.getDaiModStVal(anyLn).orElse(null); + if (daiModStVal == null) { + return null; // do nothing if DAI Mod.stVal is missing + } + List modStValEnumValues = getModStValEnumValues(scl.getDataTypeTemplates(), anyLn.getLnType()).toList(); + if (!modStValEnumValues.contains(lNodeLNS)) { + return SclReportItem.error(lnPath(tlNode), "Cannot set DAI Mod.stVal to '%s' because value is not in EnumType %s".formatted(lNodeLNS, modStValEnumValues)); + } + daiModStVal.getVal().clear(); + daiModStVal.getVal().add(SclConstructorHelper.newVal(lNodeLNS)); + return null; // no error + } + + private static String lnPath(TLNode tlNode) { + return "IED(%s)/LD(%s)/LN[%s,%s,%s]".formatted(tlNode.getIedName(), tlNode.getLdInst(), tlNode.getLnClass().getFirst(), tlNode.getLnInst(), tlNode.getPrefix()); + } + + private static String lNodePath(TLNode tlNode) { + return "LNode(iedName=%s, ldInst=%s, lnClass=%s, lnInst=%s, prefix=%s)".formatted(tlNode.getIedName(), tlNode.getLdInst(), tlNode.getLnClass().getFirst(), tlNode.getLnInst(), tlNode.getPrefix()); + } + + private Stream getModStValEnumValues(TDataTypeTemplates dataTypeTemplates, String lnType) { + return dataTypeTemplatesService.findDoLinkedToDa(dataTypeTemplates, lnType, DoLinkedToDaFilter.from(CommonConstants.MOD_DO_NAME, CommonConstants.STVAL_DA_NAME)) + .map(DoLinkedToDa::dataAttribute) + .filter(dataAttribute -> TPredefinedBasicTypeEnum.ENUM.equals(dataAttribute.getBType())) + .map(DataAttribute::getType) + .flatMap(enumId -> + dataTypeTemplates.getEnumType().stream() + .filter(tEnumType -> tEnumType.getId().equals(enumId)) + .findFirst()) + .stream() + .flatMap(tEnumType -> tEnumType.getEnumVal().stream()) + .map(TEnumVal::getValue); + } + + private Optional findLn(SCL scl, String iedName, String ldInst, String lnClass, String lnInst, String prefix) { + return scl.getIED().stream() + .filter(tied -> iedName.equals(tied.getName())) + .findFirst() + .flatMap(tied -> ldeviceService.findLdevice(tied, tlDevice -> ldInst.equals(tlDevice.getInst()))) + .flatMap(tlDevice -> lnService.findAnyLn(tlDevice, tAnyLN -> lnService.matchesLn(tAnyLN, lnClass, lnInst, prefix))); + + } +} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LdeviceService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LdeviceService.java index 008aad426..f607b0d20 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LdeviceService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LdeviceService.java @@ -41,6 +41,6 @@ public Optional findLdevice(TIED tied, Predicate ldevicePred public Optional getLdeviceStatus(TLDevice tlDevice) { LnService lnService = new LnService(); - return lnService.getDaiModStval(tlDevice.getLN0()); + return lnService.getDaiModStValValue(tlDevice.getLN0()); } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LnService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LnService.java index 2ccad6508..50d55c585 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LnService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/LnService.java @@ -12,7 +12,6 @@ import org.lfenergy.compas.sct.commons.util.ActiveStatus; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.function.Predicate; @@ -60,33 +59,37 @@ public Optional findLn(TLDevice tlDevice, Predicate lnPredicate) { * @return the Lnode Status */ public ActiveStatus getLnStatus(TAnyLN tAnyLN, LN0 ln0) { - Optional ln0Status = getDaiModStval(ln0); - return getDaiModStval(tAnyLN).filter(ActiveStatus.OFF::equals).orElseGet(() -> ln0Status.orElse(ActiveStatus.OFF)); + Optional ln0Status = getDaiModStValValue(ln0); + return getDaiModStValValue(tAnyLN).filter(ActiveStatus.OFF::equals).orElseGet(() -> ln0Status.orElse(ActiveStatus.OFF)); } - public Optional getDaiModStval(TAnyLN tAnyLN) { + public Optional getDaiModStValValue(TAnyLN tAnyLN) { + return getDaiModStVal(tAnyLN) + .stream() + .flatMap(tdai -> tdai.getVal().stream()) + .map(TVal::getValue) + .findFirst() + .map(ActiveStatus::fromValue); + } + + public Optional getDaiModStVal(TAnyLN tAnyLN) { return tAnyLN .getDOI() .stream() .filter(tdoi -> MOD_DO_NAME.equals(tdoi.getName())) - .findFirst() - .flatMap(tdoi -> tdoi.getSDIOrDAI() - .stream() - .filter(dai -> dai.getClass().equals(TDAI.class)) - .map(TDAI.class::cast) - .filter(tdai -> STVAL_DA_NAME.equals(tdai.getName())) - .map(TDAI::getVal) - .flatMap(Collection::stream) - .findFirst() - .map(TVal::getValue)) - .map(ActiveStatus::fromValue); + .flatMap(tdoi -> tdoi.getSDIOrDAI().stream()) + .filter(TDAI.class::isInstance) + .map(TDAI.class::cast) + .filter(tdai -> STVAL_DA_NAME.equals(tdai.getName())) + .findFirst(); } + public Stream getActiveLns(TLDevice tlDevice) { LN0 ln0 = tlDevice.getLN0(); Stream tlnStream = tlDevice.getLN() .stream() .filter(tln -> ActiveStatus.ON.equals(getLnStatus(tln, ln0))); - Stream ln0Stream = Stream.of(ln0).filter(ln02 -> getDaiModStval(ln02).map(ActiveStatus.ON::equals).orElse(false)); + Stream ln0Stream = Stream.of(ln0).filter(ln02 -> getDaiModStValValue(ln02).map(ActiveStatus.ON::equals).orElse(false)); return Stream.concat(ln0Stream, tlnStream); } @@ -99,7 +102,7 @@ public Optional getDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDaFilter doLi return tAnyLN.getDOI().stream().filter(doi -> doi.getName().equals(doLinkedToDaFilter.doName())) .findFirst() .flatMap(doi -> { - if(structNamesList.size() > 1) { + if (structNamesList.size() > 1) { String firstSDIName = structNamesList.removeFirst(); return this.getSdiByName(doi, firstSDIName) .map(intermediateSdi -> findSDIByStructName(intermediateSdi, structNamesList)) @@ -116,7 +119,7 @@ public Optional getDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDaFilter doLi return Optional.empty(); }) .stream().findFirst(); - } else if(structNamesList.size() == 1){ + } else if (structNamesList.size() == 1) { return doi.getSDIOrDAI().stream() .filter(unNaming -> unNaming.getClass().equals(TDAI.class)) .map(TDAI.class::cast) @@ -129,16 +132,16 @@ public Optional getDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDaFilter doLi @Override public void updateOrCreateDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDa doLinkedToDa) { - createDoiSdiDaiChainIfNotExists(tAnyLN, doLinkedToDa.getDataObject(), doLinkedToDa.getDataAttribute()) + createDoiSdiDaiChainIfNotExists(tAnyLN, doLinkedToDa.dataObject(), doLinkedToDa.dataAttribute()) .ifPresent(tdai -> { - List daiVals = doLinkedToDa.getDataAttribute().getDaiValues(); - if(!hasSettingGroup(tdai) && daiVals.size() == 1 && daiVals.getFirst().settingGroup() == null) { + List daiVals = doLinkedToDa.dataAttribute().getDaiValues(); + if (!hasSettingGroup(tdai) && daiVals.size() == 1 && daiVals.getFirst().settingGroup() == null) { String value = daiVals.getFirst().val(); tdai.getVal().stream().findFirst() .ifPresentOrElse(tVal -> tVal.setValue(value), () -> tdai.getVal().add(newVal(value))); } else { - for (DaVal daVal: daiVals) { + for (DaVal daVal : daiVals) { tdai.getVal().stream() .filter(tValElem -> tValElem.isSetSGroup() && tValElem.getSGroup() == daVal.settingGroup()) .findFirst() @@ -152,11 +155,11 @@ public void updateOrCreateDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDa doLinkedT public void completeFromDAInstance(TIED tied, String ldInst, TAnyLN anyLN, DoLinkedToDa doLinkedToDa) { getDOAndDAInstances(anyLN, doLinkedToDa.toFilter()) .ifPresent(tdai -> { - if(tdai.isSetVal()) { - doLinkedToDa.getDataAttribute().addDaVal(tdai.getVal()); + if (tdai.isSetVal()) { + doLinkedToDa.dataAttribute().addDaVal(tdai.getVal()); } - if(doLinkedToDa.getDataAttribute().getFc() == TFCEnum.SG || doLinkedToDa.getDataAttribute().getFc() == TFCEnum.SE) { - if(hasSettingGroup(tdai)) { + if (doLinkedToDa.dataAttribute().getFc() == TFCEnum.SG || doLinkedToDa.dataAttribute().getFc() == TFCEnum.SE) { + if (hasSettingGroup(tdai)) { boolean isIedHasConfSG = tied.isSetAccessPoint() && tied.getAccessPoint().stream() .filter(tAccessPoint -> tAccessPoint.getServer() != null @@ -166,13 +169,13 @@ public void completeFromDAInstance(TIED tied, String ldInst, TAnyLN anyLN, DoLin && tAccessPoint.getServices() != null && tAccessPoint.getServices().getSettingGroups() != null && tAccessPoint.getServices().getSettingGroups().getConfSG() != null); - doLinkedToDa.getDataAttribute().setValImport((!tdai.isSetValImport() || tdai.isValImport()) && isIedHasConfSG); + doLinkedToDa.dataAttribute().setValImport((!tdai.isSetValImport() || tdai.isValImport()) && isIedHasConfSG); } else { - log.warn(String.format("Inconsistency in the SCD file - DAI= %s with fc= %s must have a sGroup attribute", tdai.getName(), doLinkedToDa.getDataAttribute().getFc())); - doLinkedToDa.getDataAttribute().setValImport(false); - } - } else if(tdai.isSetValImport()) { - doLinkedToDa.getDataAttribute().setValImport(tdai.isValImport()); + log.warn(String.format("Inconsistency in the SCD file - DAI= %s with fc= %s must have a sGroup attribute", tdai.getName(), doLinkedToDa.dataAttribute().getFc())); + doLinkedToDa.dataAttribute().setValImport(false); + } + } else if (tdai.isSetValImport()) { + doLinkedToDa.dataAttribute().setValImport(tdai.isValImport()); } }); } @@ -198,22 +201,22 @@ private Optional createDoiSdiDaiChainIfNotExists(TAnyLN tAnyLN, DataObject TDOI doi = tAnyLN.getDOI().stream().filter(doi1 -> doi1.getName().equals(dataObject.getDoName())) .findFirst() - .orElseGet(()-> { + .orElseGet(() -> { TDOI newDOI = new TDOI(); newDOI.setName(dataObject.getDoName()); tAnyLN.getDOI().add(newDOI); return newDOI; }); - if(structInstances.size() > 1){ + if (structInstances.size() > 1) { TSDI firstSDI = findOrCreateSDIFromDOI(doi, structInstances.getFirst()); TSDI lastSDI = findOrCreateSDIByStructName(firstSDI, structInstances); - if(structInstances.size() == 1){ + if (structInstances.size() == 1) { return lastSDI.getSDIOrDAI().stream() .filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class)) .map(TDAI.class::cast) .filter(tdai -> tdai.getName().equals(structInstances.getFirst())) .map(tdai -> { - if(tdai.isSetValImport()) { + if (tdai.isSetValImport()) { tdai.setValImport(dataAttribute.isValImport()); } return tdai; @@ -226,13 +229,13 @@ private Optional createDoiSdiDaiChainIfNotExists(TAnyLN tAnyLN, DataObject return Optional.of(newDAI); }); } - } else if(structInstances.size() == 1){ + } else if (structInstances.size() == 1) { return doi.getSDIOrDAI().stream() .filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class)) .map(TDAI.class::cast) .filter(tdai -> tdai.getName().equals(structInstances.getFirst())) .map(tdai -> { - if(tdai.isSetValImport()) tdai.setValImport(dataAttribute.isValImport()); + if (tdai.isSetValImport()) tdai.setValImport(dataAttribute.isValImport()); return tdai; }) .findFirst() @@ -247,7 +250,7 @@ private Optional createDoiSdiDaiChainIfNotExists(TAnyLN tAnyLN, DataObject } private TSDI findSDIByStructName(TSDI tsdi, List sdiNames) { - if(sdiNames.isEmpty()) return tsdi; + if (sdiNames.isEmpty()) return tsdi; return this.getSdiByName(tsdi, sdiNames.getFirst()) .map(sdi1 -> { sdiNames.removeFirst(); @@ -294,14 +297,13 @@ private Optional getSdiByName(TSDI sdi, String sdiName) { } /** - * - * @param sdi TSDI + * @param sdi TSDI * @param structName list start with sdi name * @return already existing TSDI or newly created TSDI from given TSDI */ private TSDI findOrCreateSDIByStructName(TSDI sdi, List structName) { structName.removeFirst(); - if(structName.isEmpty() || structName.size() == 1) return sdi; + if (structName.isEmpty() || structName.size() == 1) return sdi; return findOrCreateSDIByStructName(findOrCreateSDIFromSDI(sdi, structName.getFirst()), structName); } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/PrivateUtils.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/PrivateUtils.java index 049f1e48a..8d9cbfde1 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/PrivateUtils.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/PrivateUtils.java @@ -7,6 +7,7 @@ import jakarta.xml.bind.JAXBElement; import lombok.NonNull; import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.PrivateLinkedToStds; import org.lfenergy.compas.sct.commons.exception.ScdException; @@ -320,4 +321,13 @@ public static void copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(TPrivate st } + public static Optional extractStringPrivate(TBaseElement tBaseElement, String privateType) { + return tBaseElement.getPrivate().stream() + .filter(tPrivate -> privateType.equals(tPrivate.getType())) + .flatMap(tPrivate -> tPrivate.getContent().stream()) + .filter(String.class::isInstance) + .map(String.class::cast) + .filter(StringUtils::isNotBlank) + .findFirst(); } +} diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/LNodeStatusServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/LNodeStatusServiceTest.java new file mode 100644 index 000000000..65431e392 --- /dev/null +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/LNodeStatusServiceTest.java @@ -0,0 +1,188 @@ +// SPDX-FileCopyrightText: 2024 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.lfenergy.compas.scl2007b4.model.SCL; +import org.lfenergy.compas.scl2007b4.model.TAnyLN; +import org.lfenergy.compas.scl2007b4.model.TLN; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; +import org.lfenergy.compas.sct.commons.testhelpers.SclHelper; +import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Named.named; +import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*; + +class LNodeStatusServiceTest { + + private LNodeStatusService lNodeStatusService; + + @BeforeEach + void setUp() { + lNodeStatusService = new LNodeStatusService(new LdeviceService(), new LnService(), new DataTypeTemplatesService()); + } + + @ParameterizedTest + @MethodSource("provideUpdateModStVal") + void updateLnStatusBasedOnPrivateLNodeStatus_should_update_Mod_stVal(String ldInst, String lnClass, String lnInst, String expected) { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus.scd"); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(findDai(scl, "IED_NAME_1", ldInst, lnClass, lnInst, "", "Mod", "stVal")) + .map(SclHelper::getValue) + .hasValue(expected); + } + + public static Stream provideUpdateModStVal() { + return Stream.of( + Arguments.of(named("LN 'on' à mettre à 'on'", "LDEVICE_1"), "PDIS", "1", "on"), + Arguments.of(named("LN 'off;on' à mettre à 'on'", "LDEVICE_1"), "PDIS", "2", "on"), + Arguments.of(named("LN 'off' à mettre à 'off'", "LDEVICE_1"), "PDIS", "3", "off"), + Arguments.of(named("LN 'off;on' à mettre à 'off'", "LDEVICE_1"), "PDIS", "3", "off"), + Arguments.of(named("LN0 'on' à mettre à 'on'", "LDEVICE_1"), "LLN0", "", "on"), + Arguments.of(named("LN0 'off;on' à mettre à 'on'", "LDEVICE_2"), "LLN0", "", "on"), + Arguments.of(named("LN0 'off' à mettre à 'off'", "LDEVICE_3"), "LLN0", "", "off"), + Arguments.of(named("LN0 'off;on' à mettre à 'off'", "LDEVICE_4"), "LLN0", "", "off") + ); + } + + @Test + void updateLnStatusBasedOnPrivateLNodeStatus_do_nothing_if_DAI_Mod_stVal_is_missing() { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus_without_mod_stval.scd"); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getLDevices(scl.getIED().getFirst()) + .flatMap(tlDevice -> Stream.concat(Stream.of(tlDevice.getLN0()), tlDevice.getLN().stream()))) + .flatMap(TAnyLN::getDOI) + .isEmpty(); + } + + @Test + void updateLnStatusBasedOnPrivateLNodeStatus_when_no_compasLNodeStatus_in_LNode_should_return_error() { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus.scd"); + scl.getSubstation().getFirst().getVoltageLevel().getFirst().getBay().getFirst().getFunction().getFirst().getLNode().getFirst() + .getPrivate().clear(); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).containsExactly( + SclReportItem.error("LNode(iedName=IED_NAME_1, ldInst=LDEVICE_1, lnClass=PDIS, lnInst=1, prefix=)", + "The private COMPAS-LNodeStatus of the LNode has invalid value. Expecting one of [on, off] but got : null") + ); + } + + @Test + void updateLnStatusBasedOnPrivateLNodeStatus_when_invalid_compasLNodeStatus_value_in_LNode_should_return_error() { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus.scd"); + scl.getSubstation().getFirst().getVoltageLevel().getFirst().getBay().getFirst().getFunction().getFirst().getLNode().getFirst() + .getPrivate().getFirst().getContent().set(0, "helloworld"); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).containsExactly( + SclReportItem.error("LNode(iedName=IED_NAME_1, ldInst=LDEVICE_1, lnClass=PDIS, lnInst=1, prefix=)", + "The private COMPAS-LNodeStatus of the LNode has invalid value. Expecting one of [on, off] but got : helloworld") + ); + } + + @Test + void updateLnStatusBasedOnPrivateLNodeStatus_when_LNode_does_not_match_any_LN_should_return_error() { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus.scd"); + ((TLN) findAnyLn(scl, "IED_NAME_1", "LDEVICE_1", "PDIS", "1", "")) + .setPrefix("helloworld"); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).containsExactly( + SclReportItem.error("LNode(iedName=IED_NAME_1, ldInst=LDEVICE_1, lnClass=PDIS, lnInst=1, prefix=)", + "LNode in Substation section does not have a matching LN in IED section") + ); + } + + @Test + void updateLnStatusBasedOnPrivateLNodeStatus_when_no_compasLNodeStatus_in_LN_should_return_error() { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus.scd"); + findAnyLn(scl, "IED_NAME_1", "LDEVICE_1", "PDIS", "1", "") + .getPrivate().clear(); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).containsExactly( + SclReportItem.error("IED(IED_NAME_1)/LD(LDEVICE_1)/LN[PDIS,1,]", + "LN does not have a private COMPAS-LNodeStatus") + ); + } + + @Test + void updateLnStatusBasedOnPrivateLNodeStatus_when_compasLNodeStatus_is_on_in_LNode_but_off_in_LN_should_return_error() { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus.scd"); + findAnyLn(scl, "IED_NAME_1", "LDEVICE_1", "LLN0", "", "") + .getPrivate().getFirst().getContent().set(0, "off"); + findAnyLn(scl, "IED_NAME_1", "LDEVICE_1", "PDIS", "1", "") + .getPrivate().getFirst().getContent().set(0, "off"); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).containsExactlyInAnyOrder( + SclReportItem.error("IED(IED_NAME_1)/LD(LDEVICE_1)/LN[LLN0,,]", + "Cannot set DAI Mod.stVal to on, because LN private COMPAS-LNodeStatus is set to off"), + SclReportItem.error("IED(IED_NAME_1)/LD(LDEVICE_1)/LN[PDIS,1,]", + "Cannot set DAI Mod.stVal to on, because LN private COMPAS-LNodeStatus is set to off") + ); + } + + @Test + void updateLnStatusBasedOnPrivateLNodeStatus_when_compasLNodeStatus_is_off_in_LNode_but_on_in_LN_should_return_error() { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus.scd"); + findAnyLn(scl, "IED_NAME_1", "LDEVICE_4", "LLN0", "", "") + .getPrivate().getFirst().getContent().set(0, "on"); + findAnyLn(scl, "IED_NAME_1", "LDEVICE_1", "PDIS", "4", "") + .getPrivate().getFirst().getContent().set(0, "on"); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).containsExactlyInAnyOrder( + SclReportItem.error("IED(IED_NAME_1)/LD(LDEVICE_4)/LN[LLN0,,]", + "Cannot set DAI Mod.stVal to off, because LN private COMPAS-LNodeStatus is set to on"), + SclReportItem.error("IED(IED_NAME_1)/LD(LDEVICE_1)/LN[PDIS,4,]", + "Cannot set DAI Mod.stVal to off, because LN private COMPAS-LNodeStatus is set to on") + ); + } + + @Test + void updateLnStatusBasedOnPrivateLNodeStatus_when_Mod_stVal_enumType_does_not_contains_value_should_return_error() { + // Given + SCL scl = SclTestMarshaller.getSCLFromFile("/scl-lnodestatus/lnodestatus.scd"); + scl.getDataTypeTemplates().getEnumType().getFirst().getEnumVal().removeIf(tEnumVal -> tEnumVal.getValue().equals("on")); + // When + List sclReportItems = lNodeStatusService.updateLnModStValBasedOnLNodeStatus(scl); + // Then + assertThat(sclReportItems).contains( + SclReportItem.error("IED(IED_NAME_1)/LD(LDEVICE_1)/LN[PDIS,1,]", + "Cannot set DAI Mod.stVal to 'on' because value is not in EnumType [off, test]") + ); + } + +} diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/LnServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/LnServiceTest.java index 8daa222d7..38260e960 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/LnServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/LnServiceTest.java @@ -121,13 +121,13 @@ void getFilteredAnyLns_should_return_lns() { } @Test - void getDaiModStval_should_return_status() { + void getDaiModStValValue_should_return_status() { //Given SCL std = SclTestMarshaller.getSCLFromFile("/std/std_sample.std"); TLDevice tlDevice = std.getIED().getFirst().getAccessPoint().getFirst().getServer().getLDevice().getFirst(); //When - Optional daiModStval = lnService.getDaiModStval(tlDevice.getLN0()); + Optional daiModStval = lnService.getDaiModStValValue(tlDevice.getLN0()); //Then assertThat(daiModStval).contains(ActiveStatus.ON); @@ -212,9 +212,7 @@ void completeFromDataAttributeInstance_should_complete_when_valImport_set_or_not dataObject.setDoName("Do"); DataAttribute dataAttribute = new DataAttribute(); dataAttribute.setDaName("Da"); - DoLinkedToDa doLinkedToDa = new DoLinkedToDa(); - doLinkedToDa.setDataObject(dataObject); - doLinkedToDa.setDataAttribute(dataAttribute); + DoLinkedToDa doLinkedToDa = new DoLinkedToDa(dataObject, dataAttribute); //When LnService lnService = new LnService(); lnService.completeFromDAInstance(tied, "ldInst", tAnyLN, doLinkedToDa); @@ -272,15 +270,13 @@ void completeFromDataAttributeInstance_should_complete_when_settingGroup_set_or_ dataAttribute.setDaName("Da"); dataAttribute.setFc(givenFc); - DoLinkedToDa doLinkedToDa = new DoLinkedToDa(); - doLinkedToDa.setDataObject(dataObject); - doLinkedToDa.setDataAttribute(dataAttribute); + DoLinkedToDa doLinkedToDa = new DoLinkedToDa(dataObject, dataAttribute); //When LnService lnService = new LnService(); lnService.completeFromDAInstance(tied, "ldInst", tAnyLN, doLinkedToDa); //Then - assertThat(doLinkedToDa.getDataAttribute().isValImport()).isEqualTo(expectedValImport); + assertThat(doLinkedToDa.dataAttribute().isValImport()).isEqualTo(expectedValImport); } @@ -294,14 +290,12 @@ void completeFromDataAttributeInstance__should_not_complete_when_not_found() { DataAttribute dataAttribute = new DataAttribute(); dataAttribute.setDaName("Da"); - DoLinkedToDa doLinkedToDa = new DoLinkedToDa(); - doLinkedToDa.setDataObject(dataObject); - doLinkedToDa.setDataAttribute(dataAttribute); + DoLinkedToDa doLinkedToDa = new DoLinkedToDa(dataObject, dataAttribute); //When LnService lnService = new LnService(); lnService.completeFromDAInstance(tied, "ldInst", tAnyLN, doLinkedToDa); //Then - assertThat(doLinkedToDa.getDataAttribute().isValImport()).isFalse();//initialValue + assertThat(doLinkedToDa.dataAttribute().isValImport()).isFalse();//initialValue } @ParameterizedTest @@ -321,15 +315,13 @@ void completeFromDataAttributeInstance_should_complete_when_struct(Boolean input dataAttribute.setDaName("antRef"); dataAttribute.setBdaNames(List.of("bda1","bda2","bda3")); - DoLinkedToDa doLinkedToDa = new DoLinkedToDa(); - doLinkedToDa.setDataObject(dataObject); - doLinkedToDa.setDataAttribute(dataAttribute); + DoLinkedToDa doLinkedToDa = new DoLinkedToDa(dataObject, dataAttribute); //When LnService lnService = new LnService(); lnService.completeFromDAInstance(tied, "ldInst", tAnyLN, doLinkedToDa); //Then - assertThat(doLinkedToDa.getDataAttribute().isValImport()).isEqualTo(expected); + assertThat(doLinkedToDa.dataAttribute().isValImport()).isEqualTo(expected); } @Test @@ -343,9 +335,7 @@ void updateOrCreateDOAndDAInstance_should_create_given_DO_and_DA_instances_when_ dataAttribute.setDaName("stVal"); dataAttribute.getDaiValues().add(new DaVal(1L, "new value")); dataAttribute.getDaiValues().add(new DaVal(2L, "new value 2")); - DoLinkedToDa doLinkedToDa = new DoLinkedToDa(); - doLinkedToDa.setDataObject(dataObject); - doLinkedToDa.setDataAttribute(dataAttribute); + DoLinkedToDa doLinkedToDa = new DoLinkedToDa(dataObject, dataAttribute); //When LnService lnService = new LnService(); lnService.updateOrCreateDOAndDAInstances(tAnyLN, doLinkedToDa); @@ -375,9 +365,7 @@ void updateOrCreateDOAndDAInstance_should_create_given_DO_and_DA_instances_when_ dataAttribute.setDaName("antRef"); dataAttribute.setBdaNames(List.of("bda1")); dataAttribute.getDaiValues().add(new DaVal(null, "new value")); - DoLinkedToDa doLinkedToDa = new DoLinkedToDa(); - doLinkedToDa.setDataObject(dataObject); - doLinkedToDa.setDataAttribute(dataAttribute); + DoLinkedToDa doLinkedToDa = new DoLinkedToDa(dataObject, dataAttribute); //When LnService lnService = new LnService(); @@ -440,9 +428,7 @@ void updateOrCreateDOAndDAInstance_should_complete_DO_and_DA_instances_modificat dataAttribute.setBdaNames(List.of("BdaName1")); dataAttribute.getDaiValues().add(new DaVal(null, "new dai value")); - DoLinkedToDa doLinkedToDa = new DoLinkedToDa(); - doLinkedToDa.setDataObject(dataObject); - doLinkedToDa.setDataAttribute(dataAttribute); + DoLinkedToDa doLinkedToDa = new DoLinkedToDa(dataObject, dataAttribute); //When LnService lnService = new LnService(); diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java index 0d843c335..eb24171e4 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java @@ -21,6 +21,7 @@ import java.util.Optional; import java.util.stream.Stream; +import static org.apache.commons.lang3.StringUtils.trimToEmpty; import static org.assertj.core.api.Assertions.assertThat; import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newConnectedAp; import static org.lfenergy.compas.sct.commons.util.Utils.lnClassEquals; @@ -99,257 +100,279 @@ public static LNAdapter findLn(SCL scl, String iedName, String ldInst, String ln ); } - public static IDataParentAdapter findDoiOrSdi(AbstractLNAdapter lnAdapter, String dataTypeRef) { - if (dataTypeRef.length() < 1) { - Assertions.fail("dataTypeRef must at least contain a DO, but got: " + dataTypeRef); + public static TAnyLN findAnyLn(SCL scl, String iedName, String ldInst, String lnClass, String lnInst, String prefix) { + TIED ied = scl.getIED().stream().filter(tied -> tied.getName().equals(iedName)) + .findFirst().orElseThrow(() -> new AssertionFailedError("IED with name=%s not found".formatted(iedName))); + TLDevice tlDevice = getLDevices(ied) + .filter(tlDevice1 -> tlDevice1.getInst().equals(ldInst)) + .findFirst().orElseThrow(() -> new AssertionFailedError("LDevice with inst=%s not found".formatted(ldInst))); + if (lnClass.equals(TLLN0Enum.LLN_0.value()) && tlDevice.isSetLN0()) { + return tlDevice.getLN0(); } - String[] names = dataTypeRef.split("\\."); - IDataParentAdapter parentAdapter = lnAdapter.getDOIAdapterByName(names[0]); - for (int i = 1; i < names.length; i++) { - parentAdapter = parentAdapter.getStructuredDataAdapterByName(names[i]); - } - return parentAdapter; + return tlDevice.getLN().stream() + .filter(tln -> Utils.lnClassEquals(tln.getLnClass(), lnClass) && trimToEmpty(tln.getInst()).equals(trimToEmpty(lnInst)) && trimToEmpty(tln.getPrefix()).equals(trimToEmpty(prefix))) + .findFirst() + .orElseThrow(() -> new AssertionFailedError("LN (lnClass=%s, lnInst=%s, lnPrefix=%s) not found".formatted(lnClass, lnInst, prefix))); } - public static Optional findDai(SCL scl, String iedName, String ldInst, String doiName, String daiName) { - return scl.getIED().stream().filter(tied -> tied.getName().equals(iedName)) - .flatMap(tied -> tied.getAccessPoint().stream()) - .flatMap(tAccessPoint -> tAccessPoint.getServer().getLDevice().stream()) - .filter(tlDevice -> tlDevice.getInst().equals(ldInst)) - .flatMap(tlDevice -> tlDevice.getLN0().getDOI().stream()) - .filter(tdoi -> tdoi.getName().equals(doiName)) - .flatMap(tdoi -> tdoi.getSDIOrDAI().stream().map(tUnNaming -> (TDAI)tUnNaming)) - .filter(tdai -> tdai.getName().equals(daiName)) - .findFirst(); + public static Stream getLDevices(TIED tied){ + return tied.getAccessPoint().stream() + .filter(TAccessPoint::isSetServer) + .flatMap(tAccessPoint -> tAccessPoint.getServer().getLDevice().stream()); } - public static AbstractDAIAdapter findDai(AbstractLNAdapter lnAdapter, String dataTypeRef) { - String[] names = dataTypeRef.split("\\."); - if (names.length < 2) { - Assertions.fail("dataTypeRef must at least contain a DO and a DA name, but got: " + dataTypeRef); - } - - IDataParentAdapter parentAdapter = findDoiOrSdi(lnAdapter, String.join(".", Arrays.asList(names).subList(0, names.length - 1))); - return parentAdapter.getDataAdapterByName(names[names.length - 1]); +public static IDataParentAdapter findDoiOrSdi(AbstractLNAdapter lnAdapter, String dataTypeRef) { + if (dataTypeRef.length() < 1) { + Assertions.fail("dataTypeRef must at least contain a DO, but got: " + dataTypeRef); } - - public static String getValue(AbstractDAIAdapter daiAdapter) { - return getValue(daiAdapter.getCurrentElem()); + String[] names = dataTypeRef.split("\\."); + IDataParentAdapter parentAdapter = lnAdapter.getDOIAdapterByName(names[0]); + for (int i = 1; i < names.length; i++) { + parentAdapter = parentAdapter.getStructuredDataAdapterByName(names[i]); } + return parentAdapter; +} - public static String getValue(TDAI tdai) { - if (!tdai.isSetVal()) { - Assertions.fail("No value found for DAI " + tdai.getName()); - } else if (tdai.getVal().size() > 1) { - Assertions.fail("Expecting a single value for for DAI " + tdai.getName()); - } - return tdai.getVal().get(0).getValue(); - } +public static Optional findDai(SCL scl, String iedName, String ldInst, String doiName, String daiName) { + return findDai( scl, iedName, ldInst, "LLN0", "", "", doiName, daiName); +} +public static Optional findDai(SCL scl, String iedName, String ldInst, String lnClass, String lnInst, String lnPrefix, String doiName, String daiName) { + return findAnyLn(scl, iedName, ldInst, lnClass, lnInst, lnPrefix) + .getDOI().stream() + .filter(tdoi -> tdoi.getName().equals(doiName)) + .flatMap(tdoi -> tdoi.getSDIOrDAI().stream().map(tUnNaming -> (TDAI) tUnNaming)) + .filter(tdai -> tdai.getName().equals(daiName)) + .findFirst(); +} - public static LDeviceAdapter findLDeviceByLdName(SCL scl, String ldName) { - return new SclRootAdapter(scl).streamIEDAdapters() - .flatMap(IEDAdapter::streamLDeviceAdapters) - .filter(lDeviceAdapter -> ldName.equals(lDeviceAdapter.getLdName())) - .findFirst() - .orElseThrow(() -> new AssertionFailedError("LDevice with ldName=%s not found in SCD".formatted(ldName))); +public static AbstractDAIAdapter findDai(AbstractLNAdapter lnAdapter, String dataTypeRef) { + String[] names = dataTypeRef.split("\\."); + if (names.length < 2) { + Assertions.fail("dataTypeRef must at least contain a DO and a DA name, but got: " + dataTypeRef); } - public static DataSetAdapter findDataSet(SCL scl, String iedName, String ldInst, String dataSetName) { - LN0Adapter ln0 = findLn0(scl, iedName, ldInst); - return ln0.findDataSetByName(dataSetName) - .orElseThrow(() -> new AssertionFailedError(String.format("DataSet.name=%s not found in IED.name=%s,LDevice.inst=%s,LN0", - dataSetName, iedName, ldInst))); - } + IDataParentAdapter parentAdapter = findDoiOrSdi(lnAdapter, String.join(".", Arrays.asList(names).subList(0, names.length - 1))); + return parentAdapter.getDataAdapterByName(names[names.length - 1]); +} - public static T findControlBlock(SCL scl, String iedName, String ldInst, String cbName, Class controlBlockClass) { - LN0Adapter ln0 = findLn0(scl, iedName, ldInst); - return ln0.getTControlsByType(controlBlockClass).stream() - .filter(t -> cbName.equals(t.getName())) - .findFirst() - .orElseThrow(() -> new AssertionFailedError(String.format("%s name=%s not found in IED.name=%s,LDevice.inst=%s,LN0", - controlBlockClass.getSimpleName(), cbName, iedName, ldInst))); - } +public static String getValue(AbstractDAIAdapter daiAdapter) { + return getValue(daiAdapter.getCurrentElem()); +} - public static void assertControlBlockExists(SCL scd, String iedName, String ldInst, String cbName, - String datSet, String id, ControlBlockEnum controlBlockEnum) { - TControl controlBlock = findControlBlock(scd, iedName, ldInst, cbName, controlBlockEnum.getControlBlockClass()); - assertThat(controlBlock.getDatSet()).isEqualTo(datSet); - assertThat(getControlBlockId(controlBlock)).isEqualTo(id); +public static String getValue(TDAI tdai) { + if (!tdai.isSetVal()) { + Assertions.fail("No value found for DAI " + tdai.getName()); + } else if (tdai.getVal().size() > 1) { + Assertions.fail("Expecting a single value for for DAI " + tdai.getName()); } + return tdai.getVal().get(0).getValue(); +} - private static String getControlBlockId(TControl tControl) { - if (tControl instanceof TGSEControl tgseControl) { - return tgseControl.getAppID(); - } - if (tControl instanceof TSampledValueControl tSampledValueControl) { - return tSampledValueControl.getSmvID(); - } - if (tControl instanceof TReportControl tReportControl) { - return tReportControl.getRptID(); - } - throw new AssertionFailedError("Cannot get Id for ControlBlock of type " + tControl.getClass().getSimpleName()); - } - public static Stream streamAllDataSets(SCL scl) { - return streamAllLn0Adapters(scl) - .map(ln0Adapter -> ln0Adapter.getCurrentElem().getDataSet()) - .flatMap(List::stream); - } +public static LDeviceAdapter findLDeviceByLdName(SCL scl, String ldName) { + return new SclRootAdapter(scl).streamIEDAdapters() + .flatMap(IEDAdapter::streamLDeviceAdapters) + .filter(lDeviceAdapter -> ldName.equals(lDeviceAdapter.getLdName())) + .findFirst() + .orElseThrow(() -> new AssertionFailedError("LDevice with ldName=%s not found in SCD".formatted(ldName))); +} - public static Stream streamAllLn0Adapters(SCL scl) { - return new SclRootAdapter(scl) - .streamIEDAdapters() - .flatMap(IEDAdapter::streamLDeviceAdapters) - .filter(LDeviceAdapter::hasLN0) - .map(LDeviceAdapter::getLN0Adapter); - } +public static DataSetAdapter findDataSet(SCL scl, String iedName, String ldInst, String dataSetName) { + LN0Adapter ln0 = findLn0(scl, iedName, ldInst); + return ln0.findDataSetByName(dataSetName) + .orElseThrow(() -> new AssertionFailedError(String.format("DataSet.name=%s not found in IED.name=%s,LDevice.inst=%s,LN0", + dataSetName, iedName, ldInst))); +} - public static Stream streamAllExtRef(SCL scl) { - return streamAllLn0Adapters(scl) - .filter(AbstractLNAdapter::hasInputs) - .map(LN0Adapter::getInputsAdapter) - .map(InputsAdapter::getCurrentElem) - .map(TInputs::getExtRef) - .flatMap(List::stream); - } +public static T findControlBlock(SCL scl, String iedName, String ldInst, String cbName, Class controlBlockClass) { + LN0Adapter ln0 = findLn0(scl, iedName, ldInst); + return ln0.getTControlsByType(controlBlockClass).stream() + .filter(t -> cbName.equals(t.getName())) + .findFirst() + .orElseThrow(() -> new AssertionFailedError(String.format("%s name=%s not found in IED.name=%s,LDevice.inst=%s,LN0", + controlBlockClass.getSimpleName(), cbName, iedName, ldInst))); +} - public static String getDaiValue(AbstractLNAdapter ln, String doiName, String daiName) { - return ln.getDOIAdapterByName(doiName).getDataAdapterByName(daiName).getCurrentElem().getVal().get(0).getValue(); - } +public static void assertControlBlockExists(SCL scd, String iedName, String ldInst, String cbName, + String datSet, String id, ControlBlockEnum controlBlockEnum) { + TControl controlBlock = findControlBlock(scd, iedName, ldInst, cbName, controlBlockEnum.getControlBlockClass()); + assertThat(controlBlock.getDatSet()).isEqualTo(datSet); + assertThat(getControlBlockId(controlBlock)).isEqualTo(id); +} - public static Stream streamAllConnectedApGseP(SCL scd, String pType) { - return scd.getCommunication().getSubNetwork().stream() - .map(TSubNetwork::getConnectedAP) - .flatMap(List::stream) - .map(TConnectedAP::getGSE) - .flatMap(List::stream) - .map(TControlBlock::getAddress) - .map(TAddress::getP) - .flatMap(List::stream) - .filter(tp -> pType.equals(tp.getType())) - .map(TP::getValue); +private static String getControlBlockId(TControl tControl) { + if (tControl instanceof TGSEControl tgseControl) { + return tgseControl.getAppID(); } - - public static SclRootAdapter createSclRootAdapterWithIed(String iedName) { - SCL scl = new SCL(); - scl.setHeader(new THeader()); - TIED ied = new TIED(); - ied.setName(iedName); - scl.getIED().add(ied); - return new SclRootAdapter(scl); + if (tControl instanceof TSampledValueControl tSampledValueControl) { + return tSampledValueControl.getSmvID(); } - - public static SclRootAdapter createSclRootWithConnectedAp(String iedName, String apName) { - SclRootAdapter sclRootAdapter = createSclRootAdapterWithIed(iedName); - SCL scl = sclRootAdapter.getCurrentElem(); - scl.setCommunication(new TCommunication()); - TSubNetwork subNetwork = new TSubNetwork(); - scl.getCommunication().getSubNetwork().add(subNetwork); - subNetwork.getConnectedAP().add(newConnectedAp(iedName, apName)); - return sclRootAdapter; + if (tControl instanceof TReportControl tReportControl) { + return tReportControl.getRptID(); } + throw new AssertionFailedError("Cannot get Id for ControlBlock of type " + tControl.getClass().getSimpleName()); +} - public static TExtRef createExtRefExample(String cbName, TServiceType tServiceType) { - TExtRef tExtRef = new TExtRef(); - tExtRef.setIedName("IED_NAME_2"); - tExtRef.setServiceType(tServiceType); - tExtRef.setSrcLDInst("Inst_2"); - tExtRef.setSrcLNInst("LN"); - tExtRef.setSrcPrefix("Prefix"); - tExtRef.setSrcCBName(cbName); - return tExtRef; - } +public static Stream streamAllDataSets(SCL scl) { + return streamAllLn0Adapters(scl) + .map(ln0Adapter -> ln0Adapter.getCurrentElem().getDataSet()) + .flatMap(List::stream); +} - public static SclRootAdapter createIedsInScl(String lnClass, String doName) { - // DataTypeTemplate - TDO tdo = new TDO(); - tdo.setName(doName); - tdo.setType("REF"); - TLNodeType tlNodeType = new TLNodeType(); - tlNodeType.setId("T1"); - tlNodeType.getLnClass().add(lnClass); - tlNodeType.getDO().add(tdo); - - TDA tda = new TDA(); - tda.setName("setSrcRef"); - tda.setValImport(true); - tda.setBType(TPredefinedBasicTypeEnum.OBJ_REF); - tda.setFc(TFCEnum.SP); - - TDOType tdoType = new TDOType(); - tdoType.setId("REF"); - tdoType.getSDOOrDA().add(tda); - - TDataTypeTemplates tDataTypeTemplates = new TDataTypeTemplates(); - tDataTypeTemplates.getLNodeType().add(tlNodeType); - tDataTypeTemplates.getDOType().add(tdoType); - - - //ied Client - TDOI tdoi = new TDOI(); - tdoi.setName(doName); - TLDevice tlDevice = new TLDevice(); - tlDevice.setInst("LD_ADD"); - TInputs tInputs = new TInputs(); - LN0 ln0 = new LN0(); - ln0.setInputs(tInputs); - tlDevice.setLN0(ln0); - - TLDevice tlDevice1 = new TLDevice(); - tlDevice1.setLN0(new LN0()); - tlDevice1.setInst(LD_SUIED); - TLN tln1 = new TLN(); - tln1.getLnClass().add(lnClass); - tln1.setLnType("T1"); - tln1.getDOI().add(tdoi); - tlDevice1.getLN().add(tln1); - TServer tServer1 = new TServer(); - tServer1.getLDevice().add(tlDevice1); - tServer1.getLDevice().add(tlDevice); - TAccessPoint tAccessPoint1 = new TAccessPoint(); - tAccessPoint1.setName("AP_NAME"); - tAccessPoint1.setServer(tServer1); - TIED tied1 = new TIED(); - tied1.setName(IED_NAME_1); - tied1.getAccessPoint().add(tAccessPoint1); - - //ied Source - TLDevice tlDevice2 = new TLDevice(); - tlDevice2.setInst("Inst_2"); - tlDevice2.setLdName("LD_Name"); - tlDevice2.setLN0(new LN0()); - TServer tServer2 = new TServer(); - tServer2.getLDevice().add(tlDevice2); - TAccessPoint tAccessPoint2 = new TAccessPoint(); - tAccessPoint2.setName("AP_NAME"); - tAccessPoint2.setServer(tServer2); - TIED tied2 = new TIED(); - tied2.setName(IED_NAME_2); - tied2.getAccessPoint().add(tAccessPoint2); - //SCL file - SCL scd = new SCL(); - scd.getIED().add(tied1); - scd.getIED().add(tied2); - THeader tHeader = new THeader(); - tHeader.setRevision("1"); - scd.setHeader(tHeader); - scd.setDataTypeTemplates(tDataTypeTemplates); - - return new SclRootAdapter(scd); - } +public static Stream streamAllLn0Adapters(SCL scl) { + return new SclRootAdapter(scl) + .streamIEDAdapters() + .flatMap(IEDAdapter::streamLDeviceAdapters) + .filter(LDeviceAdapter::hasLN0) + .map(LDeviceAdapter::getLN0Adapter); +} - public static List getDaiValues(LDeviceAdapter lDeviceAdapter, String lnClass, String doName, String daName) { - return getDAIAdapters(lDeviceAdapter, lnClass, doName, daName) - .map(daiAdapter -> daiAdapter.getCurrentElem().getVal()) - .flatMap(List::stream) - .toList(); - } +public static Stream streamAllExtRef(SCL scl) { + return streamAllLn0Adapters(scl) + .filter(AbstractLNAdapter::hasInputs) + .map(LN0Adapter::getInputsAdapter) + .map(InputsAdapter::getCurrentElem) + .map(TInputs::getExtRef) + .flatMap(List::stream); +} - public static Stream getDAIAdapters(LDeviceAdapter lDeviceAdapter, String lnClass, String doName, String daName) { - return lDeviceAdapter.getLNAdapters().stream() - .filter(lnAdapter -> lnClassEquals(lnAdapter.getCurrentElem().getLnClass(), lnClass)) - .map(lnAdapter -> lnAdapter.getDOIAdapterByName(doName)) - .map(doiAdapter -> (DOIAdapter.DAIAdapter) doiAdapter.getDataAdapterByName(daName)); - } +public static String getDaiValue(AbstractLNAdapter ln, String doiName, String daiName) { + return ln.getDOIAdapterByName(doiName).getDataAdapterByName(daiName).getCurrentElem().getVal().get(0).getValue(); +} + +public static Stream streamAllConnectedApGseP(SCL scd, String pType) { + return scd.getCommunication().getSubNetwork().stream() + .map(TSubNetwork::getConnectedAP) + .flatMap(List::stream) + .map(TConnectedAP::getGSE) + .flatMap(List::stream) + .map(TControlBlock::getAddress) + .map(TAddress::getP) + .flatMap(List::stream) + .filter(tp -> pType.equals(tp.getType())) + .map(TP::getValue); +} + +public static SclRootAdapter createSclRootAdapterWithIed(String iedName) { + SCL scl = new SCL(); + scl.setHeader(new THeader()); + TIED ied = new TIED(); + ied.setName(iedName); + scl.getIED().add(ied); + return new SclRootAdapter(scl); +} + +public static SclRootAdapter createSclRootWithConnectedAp(String iedName, String apName) { + SclRootAdapter sclRootAdapter = createSclRootAdapterWithIed(iedName); + SCL scl = sclRootAdapter.getCurrentElem(); + scl.setCommunication(new TCommunication()); + TSubNetwork subNetwork = new TSubNetwork(); + scl.getCommunication().getSubNetwork().add(subNetwork); + subNetwork.getConnectedAP().add(newConnectedAp(iedName, apName)); + return sclRootAdapter; +} + +public static TExtRef createExtRefExample(String cbName, TServiceType tServiceType) { + TExtRef tExtRef = new TExtRef(); + tExtRef.setIedName("IED_NAME_2"); + tExtRef.setServiceType(tServiceType); + tExtRef.setSrcLDInst("Inst_2"); + tExtRef.setSrcLNInst("LN"); + tExtRef.setSrcPrefix("Prefix"); + tExtRef.setSrcCBName(cbName); + return tExtRef; +} + +public static SclRootAdapter createIedsInScl(String lnClass, String doName) { + // DataTypeTemplate + TDO tdo = new TDO(); + tdo.setName(doName); + tdo.setType("REF"); + TLNodeType tlNodeType = new TLNodeType(); + tlNodeType.setId("T1"); + tlNodeType.getLnClass().add(lnClass); + tlNodeType.getDO().add(tdo); + + TDA tda = new TDA(); + tda.setName("setSrcRef"); + tda.setValImport(true); + tda.setBType(TPredefinedBasicTypeEnum.OBJ_REF); + tda.setFc(TFCEnum.SP); + + TDOType tdoType = new TDOType(); + tdoType.setId("REF"); + tdoType.getSDOOrDA().add(tda); + + TDataTypeTemplates tDataTypeTemplates = new TDataTypeTemplates(); + tDataTypeTemplates.getLNodeType().add(tlNodeType); + tDataTypeTemplates.getDOType().add(tdoType); + + + //ied Client + TDOI tdoi = new TDOI(); + tdoi.setName(doName); + TLDevice tlDevice = new TLDevice(); + tlDevice.setInst("LD_ADD"); + TInputs tInputs = new TInputs(); + LN0 ln0 = new LN0(); + ln0.setInputs(tInputs); + tlDevice.setLN0(ln0); + + TLDevice tlDevice1 = new TLDevice(); + tlDevice1.setLN0(new LN0()); + tlDevice1.setInst(LD_SUIED); + TLN tln1 = new TLN(); + tln1.getLnClass().add(lnClass); + tln1.setLnType("T1"); + tln1.getDOI().add(tdoi); + tlDevice1.getLN().add(tln1); + TServer tServer1 = new TServer(); + tServer1.getLDevice().add(tlDevice1); + tServer1.getLDevice().add(tlDevice); + TAccessPoint tAccessPoint1 = new TAccessPoint(); + tAccessPoint1.setName("AP_NAME"); + tAccessPoint1.setServer(tServer1); + TIED tied1 = new TIED(); + tied1.setName(IED_NAME_1); + tied1.getAccessPoint().add(tAccessPoint1); + + //ied Source + TLDevice tlDevice2 = new TLDevice(); + tlDevice2.setInst("Inst_2"); + tlDevice2.setLdName("LD_Name"); + tlDevice2.setLN0(new LN0()); + TServer tServer2 = new TServer(); + tServer2.getLDevice().add(tlDevice2); + TAccessPoint tAccessPoint2 = new TAccessPoint(); + tAccessPoint2.setName("AP_NAME"); + tAccessPoint2.setServer(tServer2); + TIED tied2 = new TIED(); + tied2.setName(IED_NAME_2); + tied2.getAccessPoint().add(tAccessPoint2); + //SCL file + SCL scd = new SCL(); + scd.getIED().add(tied1); + scd.getIED().add(tied2); + THeader tHeader = new THeader(); + tHeader.setRevision("1"); + scd.setHeader(tHeader); + scd.setDataTypeTemplates(tDataTypeTemplates); + + return new SclRootAdapter(scd); +} + +public static List getDaiValues(LDeviceAdapter lDeviceAdapter, String lnClass, String doName, String daName) { + return getDAIAdapters(lDeviceAdapter, lnClass, doName, daName) + .map(daiAdapter -> daiAdapter.getCurrentElem().getVal()) + .flatMap(List::stream) + .toList(); +} + +public static Stream getDAIAdapters(LDeviceAdapter lDeviceAdapter, String lnClass, String doName, String daName) { + return lDeviceAdapter.getLNAdapters().stream() + .filter(lnAdapter -> lnClassEquals(lnAdapter.getCurrentElem().getLnClass(), lnClass)) + .map(lnAdapter -> lnAdapter.getDOIAdapterByName(doName)) + .map(doiAdapter -> (DOIAdapter.DAIAdapter) doiAdapter.getDataAdapterByName(daName)); +} } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/PrivateUtilsTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/PrivateUtilsTest.java index 5a9b20954..6b8ab3469 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/PrivateUtilsTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/PrivateUtilsTest.java @@ -630,4 +630,18 @@ void createPrivate_compas_Topo_should_succeed(){ .map(JAXBElement::getValue) .containsExactly(tCompasTopo1, tCompasTopo2); } + + @Test + void extractStringPrivate_should_succeed(){ + // Given + TIED tied = new TIED(); + TPrivate tPrivate = new TPrivate(); + tPrivate.setType("MyCustomType"); + tPrivate.getContent().add("hello World"); + tied.getPrivate().add(tPrivate); + // When + Optional result = PrivateUtils.extractStringPrivate(tied, "MyCustomType"); + // Then + assertThat(result).hasValue("hello World"); + } } diff --git a/sct-commons/src/test/resources/ied-test-schema-conf/ied_unit_test.xml b/sct-commons/src/test/resources/ied-test-schema-conf/ied_unit_test.xml index 212d76299..c15f75606 100644 --- a/sct-commons/src/test/resources/ied-test-schema-conf/ied_unit_test.xml +++ b/sct-commons/src/test/resources/ied-test-schema-conf/ied_unit_test.xml @@ -113,7 +113,9 @@ - + + myValue + diff --git a/sct-commons/src/test/resources/scl-lnodestatus/lnodestatus.scd b/sct-commons/src/test/resources/scl-lnodestatus/lnodestatus.scd new file mode 100644 index 000000000..120833d95 --- /dev/null +++ b/sct-commons/src/test/resources/scl-lnodestatus/lnodestatus.scd @@ -0,0 +1,125 @@ + + + + + + + + SCD + +
+ + + 90 + + + + on + + + on + + + off + + + off + + + + + on + + + on + + + off + + + off + + + + + + + + + + + + on + + + + + + on;off + + + + + + on + + + + + + on;off + + + + + + off + + + + + + + + on + + + + + + + + on;off + + + + + + + + off + + + + + + + + + + + + + + + + + + + + on + off + test + + + diff --git a/sct-commons/src/test/resources/scl-lnodestatus/lnodestatus_without_mod_stval.scd b/sct-commons/src/test/resources/scl-lnodestatus/lnodestatus_without_mod_stval.scd new file mode 100644 index 000000000..da7204fdc --- /dev/null +++ b/sct-commons/src/test/resources/scl-lnodestatus/lnodestatus_without_mod_stval.scd @@ -0,0 +1,101 @@ + + + + + + + + SCD + +
+ + + 90 + + + + on + + + on + + + off + + + off + + + + + on + + + on + + + off + + + off + + + + + + + + + + + + on + + + on;off + + + on + + + on;off + + + off + + + + + on + + + + + on;off + + + + + off + + + + + + + + + + + + + + + + + on + off + test + + +