Skip to content

Commit

Permalink
feat(#351): RSR-894 Update Compasflow and extref prebindings based on…
Browse files Browse the repository at this point in the history
… LNode

Signed-off-by: massifben <[email protected]>
massifben committed Dec 15, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent e1373c0 commit ca976d0
Showing 11 changed files with 420 additions and 202 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: 2022 RTE FRANCE
// SPDX-FileCopyrightText: 2023 RTE FRANCE
//
// SPDX-License-Identifier: Apache-2.0

package org.lfenergy.compas.sct.commons;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.api.ExtRefEditor;
import org.lfenergy.compas.sct.commons.dto.*;
@@ -24,13 +25,13 @@
import org.lfenergy.compas.sct.commons.util.PrivateUtils;
import org.lfenergy.compas.sct.commons.util.Utils;

import java.math.BigInteger;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.StringUtils.*;
import static org.lfenergy.compas.sct.commons.util.CommonConstants.*;
import static org.lfenergy.compas.sct.commons.util.Utils.isExtRefFeedBySameControlBlock;

@RequiredArgsConstructor
public class ExtRefEditorService implements ExtRefEditor {
@@ -42,21 +43,9 @@ public class ExtRefEditorService implements ExtRefEditor {
"6", "THT",
"7", "THT"
);
private final ExtRefService extRefService;

/**
* Remove ExtRef which are fed by same Control Block
*
* @return list ExtRefs without duplication
*/
public static List<TExtRef> filterDuplicatedExtRefs(List<TExtRef> tExtRefs) {
List<TExtRef> filteredList = new ArrayList<>();
tExtRefs.forEach(tExtRef -> {
if (filteredList.stream().noneMatch(t -> isExtRefFeedBySameControlBlock(tExtRef, t)))
filteredList.add(tExtRef);
});
return filteredList;
}
private final LdeviceService ldeviceService;
private final ExtRefService extRefService;

/**
* Provides valid IED sources according to EPF configuration.<br/>
@@ -425,8 +414,7 @@ public void debindCompasFlowsAndExtRefsBasedOnVoltageLevel(SCL scd) {
.flatMap(ldeviceService::getLdevices)
.forEach(tlDevice -> {
String flowSource = voltageCodification.get(tVoltageLevelName);
TInputs tInputs = tlDevice.getLN0().getInputs();
PrivateUtils.getPrivateStream(tInputs.getPrivate(), TCompasFlow.class)
extRefService.getCompasFlows(tlDevice)
.filter(TCompasFlow::isSetFlowSourceVoltageLevel)
.filter(TCompasFlow::isSetExtRefiedName)
.forEach(tCompasFlow -> {
@@ -435,19 +423,66 @@ public void debindCompasFlowsAndExtRefsBasedOnVoltageLevel(SCL scd) {
extRefService.clearCompasFlowBinding(tCompasFlow);
} else if (!tCompasFlow.getFlowSourceVoltageLevel().equals(flowSource)) {
//debind extRef
extRefService.getMatchingExtRef(tInputs, tCompasFlow)
extRefService.getMatchingExtRefs(tlDevice, tCompasFlow)
.forEach(extRefService::clearExtRefBinding);
//debind compas flow
extRefService.clearCompasFlowBinding(tCompasFlow);

}
});
})
);
}

private record DoNameAndDaName(String doName, String daName) {

@Override
public void updateIedNameBasedOnLnode(SCL scl) {
Map<TopoKey, TBay> bayByTopoKey = scl.getSubstation().stream()
.flatMap(tSubstation -> tSubstation.getVoltageLevel().stream())
.flatMap(tVoltageLevel -> tVoltageLevel.getBay().stream())
.map(tBay -> PrivateUtils.extractCompasPrivate(tBay, TCompasTopo.class)
.filter(tCompasTopo -> isNotBlank(tCompasTopo.getNode()) && Objects.nonNull(tCompasTopo.getNodeOrder()))
.map(tCompasTopo -> new BayTopoKey(tBay, new TopoKey(tCompasTopo.getNode(), tCompasTopo.getNodeOrder())))
)
.flatMap(Optional::stream)
.collect(Collectors.toMap(BayTopoKey::topoKey, BayTopoKey::bay));

scl.getIED().stream()
.flatMap(ldeviceService::getLdevices)
.forEach(tlDevice ->
extRefService.getCompasFlows(tlDevice)
.filter(tCompasFlow -> Objects.nonNull(tCompasFlow.getFlowSourceBayNode()) && Objects.nonNull(tCompasFlow.getFlowSourceBayNodeOrder()))
.forEach(tCompasFlow ->
Optional.ofNullable(bayByTopoKey.get(new TopoKey(tCompasFlow.getFlowSourceBayNode().toString(), tCompasFlow.getFlowSourceBayNodeOrder())))
.flatMap(tBay -> tBay.getFunction().stream()
.flatMap(tFunction -> tFunction.getLNode().stream())
.filter(tlNode -> Objects.equals(tlNode.getLdInst(), tCompasFlow.getExtRefldinst())
&& Objects.equals(tlNode.getLnInst(), tCompasFlow.getExtReflnInst())
&& Utils.lnClassEquals(tlNode.getLnClass(), tCompasFlow.getExtReflnClass())
&& Objects.equals(tlNode.getPrefix(), tCompasFlow.getExtRefprefix()))
.map(TLNode::getIedName)
.filter(StringUtils::isNotBlank)
.findFirst()
)
.ifPresentOrElse(iedName -> {
extRefService.getMatchingExtRefs(tlDevice, tCompasFlow).forEach(tExtRef -> tExtRef.setIedName(iedName));
tCompasFlow.setExtRefiedName(iedName);
},
() -> {
extRefService.getMatchingExtRefs(tlDevice, tCompasFlow).forEach(extRefService::clearExtRefBinding);
extRefService.clearCompasFlowBinding(tCompasFlow);
}
)
)
);
}

record TopoKey(String FlowNode, BigInteger FlowNodeOrder) {
}

record BayTopoKey(TBay bay, TopoKey topoKey) {
}

private record DoNameAndDaName(String doName, String daName) {
}

}
Original file line number Diff line number Diff line change
@@ -70,4 +70,9 @@ public interface ExtRefEditor {
*/
void debindCompasFlowsAndExtRefsBasedOnVoltageLevel(SCL scd);

/**
* Update compas:Flow.ExtRefiedName and ExtRef.iedName, based on Substation LNode iedName
*/
void updateIedNameBasedOnLnode(SCL scd);

}
Original file line number Diff line number Diff line change
@@ -6,26 +6,38 @@

package org.lfenergy.compas.sct.commons.scl;

import org.lfenergy.compas.scl2007b4.model.TCompasFlow;
import org.lfenergy.compas.scl2007b4.model.TExtRef;
import org.lfenergy.compas.scl2007b4.model.TInputs;
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.util.PrivateUtils;
import org.lfenergy.compas.sct.commons.util.Utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

public class ExtRefService {

/**
* List all ExtRefs in this Inputs
* List all ExtRefs in this LDevice
*
* @return list of ExtRefs. List is modifiable.
*/
public Stream<TExtRef> getExtRefs(TInputs inputs) {
if (inputs == null || !inputs.isSetExtRef()) {
return Stream.empty();
}
return inputs.getExtRef().stream();
public Stream<TExtRef> getExtRefs(TLDevice tlDevice) {
return getInputs(tlDevice)
.filter(TInputs::isSetExtRef)
.stream()
.flatMap(tInputs -> tInputs.getExtRef().stream());
}

/**
* List all CompasFlows in this LDevice
*
* @return list of ExtRefs. List is modifiable.
*/
public Stream<TCompasFlow> getCompasFlows(TLDevice tlDevice) {
return getInputs(tlDevice).stream()
.flatMap(tInputs -> PrivateUtils.extractCompasPrivates(tlDevice.getLN0().getInputs(), TCompasFlow.class));
}

/**
@@ -43,12 +55,12 @@ public Stream<TCompasFlow> getMatchingCompasFlows(TInputs inputs, TExtRef tExtRe
/**
* Retrieves ExtRefs corresponding to given CompasFlow
*
* @param inputs node containing CompasFlows and ExtRefs
* @param tlDevice LDevice containing CompasFlows and ExtRefs
* @param tCompasFlow corresponding to Extrefs we are searching
* @return stream of matching ExtRefs
*/
public Stream<TExtRef> getMatchingExtRef(TInputs inputs, TCompasFlow tCompasFlow) {
return getExtRefs(inputs)
public Stream<TExtRef> getMatchingExtRefs(TLDevice tlDevice, TCompasFlow tCompasFlow) {
return getExtRefs(tlDevice)
.filter(tExtRef -> isMatchingExtRef(tCompasFlow, tExtRef));
}

@@ -103,4 +115,45 @@ private boolean isMatchingExtRef(TCompasFlow compasFlow, TExtRef extRef) {
&& Utils.equalsOrBothBlank(compasFlow.getExtReflnClass(), extRefLnClass)
&& Utils.equalsOrBothBlank(compasFlow.getExtReflnInst(), extRef.getLnInst());
}

/**
* Checks if two ExtRefs fed by same Control Block
*
* @param t1 extref to compare
* @param t2 extref to compare
* @return true if the two ExtRef are fed by same Control Block, otherwise false
*/
public boolean isExtRefFeedBySameControlBlock(TExtRef t1, TExtRef t2) {
String srcLNClass1 = (t1.isSetSrcLNClass()) ? t1.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value();
String srcLNClass2 = (t2.isSetSrcLNClass()) ? t2.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value();
return Utils.equalsOrBothBlank(t1.getIedName(), t2.getIedName())
&& Utils.equalsOrBothBlank(t1.getSrcLDInst(), t2.getSrcLDInst())
&& srcLNClass1.equals(srcLNClass2)
&& Utils.equalsOrBothBlank(t1.getSrcLNInst(), t2.getSrcLNInst())
&& Utils.equalsOrBothBlank(t1.getSrcPrefix(), t2.getSrcPrefix())
&& Utils.equalsOrBothBlank(t1.getSrcCBName(), t2.getSrcCBName())
&& Objects.equals(t1.getServiceType(), t2.getServiceType());
}

/**
* Remove ExtRef which are fed by same Control Block
*
* @return list ExtRefs without duplication
*/
public List<TExtRef> filterDuplicatedExtRefs(List<TExtRef> tExtRefs) {
List<TExtRef> filteredList = new ArrayList<>();
tExtRefs.forEach(tExtRef -> {
if (filteredList.stream().noneMatch(t -> isExtRefFeedBySameControlBlock(tExtRef, t)))
filteredList.add(tExtRef);
});
return filteredList;
}

private Optional<TInputs> getInputs(TLDevice tlDevice){
if (!tlDevice.isSetLN0() || !tlDevice.getLN0().isSetInputs()) {
return Optional.empty();
}
return Optional.of(tlDevice.getLN0().getInputs());
}

}
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.scl.ExtRefService;
import org.lfenergy.compas.sct.commons.scl.SclElementAdapter;
import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter;
import org.lfenergy.compas.sct.commons.scl.ln.AbstractLNAdapter;
@@ -19,8 +20,6 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.lfenergy.compas.sct.commons.ExtRefEditorService.filterDuplicatedExtRefs;

/**
* A representation of the model object
* <em><b>{@link org.lfenergy.compas.scl2007b4.model.TAccessPoint AccessPoint}</b></em>.
@@ -245,7 +244,7 @@ public ExtRefAnalyzeRecord getAllCoherentExtRefForAnalyze() {
return extRefs;
}).flatMap(Collection::stream)
.toList();
return new ExtRefAnalyzeRecord(sclReportItems, filterDuplicatedExtRefs(tExtRefList));
return new ExtRefAnalyzeRecord(sclReportItems, new ExtRefService().filterDuplicatedExtRefs(tExtRefList));
}

/**
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.ExtRefEditorService;
import org.lfenergy.compas.sct.commons.dto.DataAttributeRef;
import org.lfenergy.compas.sct.commons.dto.FcdaForDataSetsCreation;
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
@@ -104,13 +103,13 @@ public List<SclReportItem> updateAllExtRefIedNames(Map<String, IEDAdapter> icdSy
try {
ActiveStatus lDeviceStatus = ActiveStatus.fromValue(optionalLDeviceStatus.get());
return switch (lDeviceStatus) {
case ON -> extRefService.getExtRefs(currentElem)
case ON -> currentElem.getExtRef().stream()
.filter(tExtRef -> StringUtils.isNotBlank(tExtRef.getIedName()) && StringUtils.isNotBlank(tExtRef.getDesc()))
.map(extRef -> updateExtRefIedName(extRef, icdSystemVersionToIed.get(extRef.getIedName())))
.flatMap(Optional::stream)
.toList();
case OFF -> {
extRefService.getExtRefs(currentElem).forEach(extRefService::clearExtRefBinding);
currentElem.getExtRef().forEach(extRefService::clearExtRefBinding);
yield Collections.emptyList();
}
};
@@ -209,7 +208,7 @@ public List<SclReportItem> updateAllSourceDataSetsAndControlBlocks(Set<FcdaForDa
if (StringUtils.isBlank(currentBayUuid)) {
return List.of(getIedAdapter().buildFatalReportItem(MESSAGE_IED_MISSING_COMPAS_BAY_UUID));
}
return extRefService.getExtRefs(currentElem)
return currentElem.getExtRef().stream()
.filter(this::areBindingAttributesPresent)
.filter(this::isExternalBound)
.filter(this::matchingCompasFlowIsActiveOrUntested)
@@ -374,7 +373,7 @@ private SclRootAdapter getSclRootAdapter() {
* @return list ExtRefs without duplication
*/
public List<TExtRef> filterDuplicatedExtRefs() {
return ExtRefEditorService.filterDuplicatedExtRefs(extRefService.getExtRefs(currentElem).toList());
return new ExtRefService().filterDuplicatedExtRefs(currentElem.getExtRef());
}

}
Original file line number Diff line number Diff line change
@@ -10,12 +10,7 @@
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.util.JAXBSource;
import org.apache.commons.lang3.StringUtils;
import org.lfenergy.compas.scl2007b4.model.TExtRef;
import org.lfenergy.compas.scl2007b4.model.TLLN0Enum;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.scl.ln.AbstractLNAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter;
import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter;

import javax.xml.namespace.QName;
import java.util.*;
@@ -371,23 +366,4 @@ public static <T> T copySclElement(T object, Class<T> clazz) {
}
}

/**
* Checks if two ExtRefs fed by same Control Block
*
* @param t1 extref to compare
* @param t2 extref to compare
* @return true if the two ExtRef are fed by same Control Block, otherwise false
*/
public static boolean isExtRefFeedBySameControlBlock(TExtRef t1, TExtRef t2) {
String srcLNClass1 = (t1.isSetSrcLNClass()) ? t1.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value();
String srcLNClass2 = (t2.isSetSrcLNClass()) ? t2.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value();
return Utils.equalsOrBothBlank(t1.getIedName(), t2.getIedName())
&& Utils.equalsOrBothBlank(t1.getSrcLDInst(), t2.getSrcLDInst())
&& srcLNClass1.equals(srcLNClass2)
&& Utils.equalsOrBothBlank(t1.getSrcLNInst(), t2.getSrcLNInst())
&& Utils.equalsOrBothBlank(t1.getSrcPrefix(), t2.getSrcPrefix())
&& Utils.equalsOrBothBlank(t1.getSrcCBName(), t2.getSrcCBName())
&& Objects.equals(t1.getServiceType(), t2.getServiceType());
}

}
Original file line number Diff line number Diff line change
@@ -30,7 +30,6 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.lfenergy.compas.sct.commons.ExtRefEditorService.filterDuplicatedExtRefs;
import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*;
import static org.lfenergy.compas.sct.commons.util.CommonConstants.*;

@@ -40,7 +39,7 @@ class ExtRefEditorServiceTest {

@BeforeEach
void init() {
extRefEditorService = new ExtRefEditorService(new ExtRefService());
extRefEditorService = new ExtRefEditorService(new LdeviceService(), new ExtRefService());
}

@Test
@@ -211,41 +210,6 @@ private void assertExtRefIsNotBound(TExtRef extRef) {
assertThat(extRef.isSetSrcCBName()).isFalse();
}

@Test
void filterDuplicatedExtRefs_should_remove_duplicated_extrefs() {
// Given
TExtRef tExtRefLnClass = createExtRefExample("CB_Name1", TServiceType.GOOSE);
tExtRefLnClass.getSrcLNClass().add(TLLN0Enum.LLN_0.value());
TExtRef tExtRef = createExtRefExample("CB_Name1", TServiceType.GOOSE);
List<TExtRef> tExtRefList = List.of(tExtRef, tExtRefLnClass, createExtRefExample("CB", TServiceType.GOOSE),
createExtRefExample("CB", TServiceType.GOOSE));
// When
List<TExtRef> result = filterDuplicatedExtRefs(tExtRefList);
// Then
assertThat(result).hasSizeLessThan(tExtRefList.size())
.hasSize(2);
}

@Test
void filterDuplicatedExtRefs_should_not_remove_not_duplicated_extrefs() {
// Given
TExtRef tExtRefIedName = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefIedName.setIedName("IED_XXX");
TExtRef tExtRefLdInst = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLdInst.setSrcLDInst("LD_XXX");
TExtRef tExtRefLnInst = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLnInst.setSrcLNInst("X");
TExtRef tExtRefPrefix = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefPrefix.setSrcPrefix("X");
List<TExtRef> tExtRefList = List.of(tExtRefIedName, tExtRefLdInst, tExtRefLnInst, tExtRefPrefix,
createExtRefExample("CB_1", TServiceType.GOOSE), createExtRefExample("CB_1", TServiceType.SMV));
// When
List<TExtRef> result = filterDuplicatedExtRefs(tExtRefList);
// Then
assertThat(result).hasSameSizeAs(tExtRefList)
.hasSize(6);
}

@Test
void manageBindingForLDEPF_whenFlowKindIsInternalAndAllExtRefInSameBay_should_return_noReportAndExtRefUpdateSuccessfully() {
//Given
@@ -532,7 +496,6 @@ void manageBindingForLDEPF_when_extRefMatchFlowKindInternalOrExternal_should_upd
assertThat(extRefBindExternally.getIedName()).isEqualTo("IED_NAME2");
assertExtRefIsBoundAccordingTOLDEPF(extRefBindExternally, analogueChannel10WithBayExternalBayScope);
}

private void assertExtRefIsBoundAccordingTOLDEPF(TExtRef extRef, TChannel setting) {
assertThat(extRef.getLdInst()).isEqualTo(setting.getLDInst());
assertThat(extRef.getLnClass()).contains(setting.getLNClass());
@@ -829,4 +792,32 @@ private TInputs findInputs(SCL scd) {

}

@Test
void updateIedNameBasedOnLnode_should_update_CompasFlow_and_ExtRef_iedName(){
// Given
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_based_on_lnode_success.xml");
// When
extRefEditorService.updateIedNameBasedOnLnode(scd);
// Then
assertThat(findCompasFlow(scd, "IED_NAME1", "LD_INST11", "STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1").getExtRefiedName())
.isEqualTo("IED_NAME2");
assertThat(findExtRef(scd, "IED_NAME1", "LD_INST11", "STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1").getIedName())
.isEqualTo("IED_NAME2");
}

@Test
void updateIedNameBasedOnLnode_when_no_matching_lnode_should_clear_binding(){
// Given
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_based_on_lnode_success.xml");
PrivateUtils.extractCompasPrivate(scd.getSubstation().get(0).getVoltageLevel().get(0).getBay().get(0), TCompasTopo.class).orElseThrow().setNode("99");
// When
extRefEditorService.updateIedNameBasedOnLnode(scd);
// Then
TCompasFlow compasFlow = findCompasFlow(scd, "IED_NAME1", "LD_INST11", "STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1");
assertThat(compasFlow)
.extracting(TCompasFlow::getExtRefiedName, TCompasFlow::getExtRefldinst, TCompasFlow::getExtRefprefix, TCompasFlow::getExtReflnClass, TCompasFlow::getExtReflnInst)
.containsOnlyNulls();
assertExtRefIsNotBound(findExtRef(scd, "IED_NAME1", "LD_INST11", "STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1"));

}
}
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
package org.lfenergy.compas.sct.commons.scl;

import org.assertj.core.groups.Tuple;
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;
@@ -18,38 +19,74 @@
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.createExtRefExample;


class ExtRefServiceTest {

private static final ObjectFactory objectFactory = new ObjectFactory();
ExtRefService extRefService = new ExtRefService();
ExtRefService extRefService;

@ParameterizedTest(name = "{0}")
@MethodSource("provideTAnyLns")
void getExtRefs(String testCase, TInputs tInputs, int size) {
//Given
//When
//Then
assertThat(extRefService.getExtRefs(tInputs)).hasSize(size);
@BeforeEach
void setUp() {
extRefService = new ExtRefService();
}

private static Stream<Arguments> provideTAnyLns() {
TInputs tInputsEmpty = new TInputs();
@Test
void getExtRefs_should_return_extRefs() {
//Given
TLDevice tlDevice = new TLDevice();
tlDevice.setLN0(new LN0());
TInputs tInputs = new TInputs();
tlDevice.getLN0().setInputs(tInputs);
TExtRef tExtRef1 = new TExtRef();
TExtRef tExtRef2 = new TExtRef();
tInputs.getExtRef().add(tExtRef1);
tInputs.getExtRef().add(tExtRef2);
//When
Stream<TExtRef> result = extRefService.getExtRefs(tlDevice);
//Then
assertThat(result).hasSize(2);
}

@ParameterizedTest
@MethodSource("provideLDevices")
void getExtRefs_should_return_empty_stream(TLDevice tlDevice) {
//Given : parameters
//When
Stream<TExtRef> result = extRefService.getExtRefs(tlDevice);
//Then
assertThat(result).isEmpty();
}

private static Stream<Arguments> provideLDevices() {
TLDevice tlDeviceWithoutLn0 = new TLDevice();
tlDeviceWithoutLn0.setLN0(new LN0());
return Stream.of(
Arguments.of("Ln without Inputs node should return empty stream", null, 0),
Arguments.of("Ln with empty Inputs should return empty stream", tInputsEmpty, 0),
Arguments.of("Ln0 with Inputs node should return stream 2 extrefs", tInputs, 2));
Arguments.of(named("LDevice without LN0 should return empty stream", tlDeviceWithoutLn0)),
Arguments.of(named("LDevice with empty Inputs should return empty stream", new TLDevice()))
);
}

@Test
void getCompasFlows_should_return_compasFlow() {
// Given
TLDevice tlDevice = new TLDevice();
tlDevice.setLN0(new LN0());
TInputs tInputs = new TInputs();
tlDevice.getLN0().setInputs(tInputs);
TPrivate tPrivate = new TPrivate();
tPrivate.setType(PrivateEnum.COMPAS_FLOW.getPrivateType());
tPrivate.getContent().add(new ObjectFactory().createFlow(new TCompasFlow()));
tInputs.getPrivate().add(tPrivate);
// When
Stream<TCompasFlow> result = extRefService.getCompasFlows(tlDevice);
// Then
assertThat(result).hasSize(1);
}

@Test
void getMatchingCompasFlows() {
void getMatchingCompasFlows_should_succeed() {
//Given
TInputs tInputs = new TInputs();
TExtRef tExtRef1 = createExtRef("Desc_1", "IED_Name_1", "LD_INST_1");
@@ -66,47 +103,27 @@ void getMatchingCompasFlows() {
}

@Test
void getMatchingTextRef() {
void getMatchingExtRefs_should_succeed() {
//Given
TLN tln = new TLN();
TLDevice tlDevice = new TLDevice();
tlDevice.setLN0(new LN0());
TInputs tInputs = new TInputs();
TExtRef tExtRef1 = createExtRef("Desc_1", "IED_Name_1", "LD_INST_1");
TExtRef tExtRef2 = createExtRef("Desc_2", "IED_Name_2", "LD_INST_2");
tInputs.getExtRef().add(tExtRef1);
tInputs.getExtRef().add(tExtRef2);
TCompasFlow tCompasFlow = createCompasFlow("Desc_1", "IED_Name_1", "LD_INST_1");
tln.setInputs(tInputs);
tlDevice.getLN0().setInputs(tInputs);
//When
Stream<TExtRef> tExtRefStream = extRefService.getMatchingExtRef(tInputs, tCompasFlow);
Stream<TExtRef> tExtRefStream = extRefService.getMatchingExtRefs(tlDevice, tCompasFlow);
//Then
assertThat(tExtRefStream).hasSize(1)
.map(TExtRef::getIedName, TExtRef::getLdInst, TExtRef::getDesc)
.containsExactly(Tuple.tuple("IED_Name_1", "LD_INST_1", "Desc_1"));
}

@Test
void getMatchingTextRef_success_when_lnclass_null() {
//Given
TLN tln = new TLN();
TInputs tInputs = new TInputs();
TExtRef tExtRef1 = createExtRef("Desc_1", "IED_Name_1", "LD_INST_1");
tExtRef1.getLnClass().clear();
TExtRef tExtRef2 = createExtRef("Desc_2", "IED_Name_2", "LD_INST_2");
tInputs.getExtRef().add(tExtRef1);
tInputs.getExtRef().add(tExtRef2);
TCompasFlow tCompasFlow = createCompasFlow("Desc_1", "IED_Name_1", "LD_INST_1");
tCompasFlow.setExtReflnClass(null);
tln.setInputs(tInputs);
//When
Stream<TExtRef> tExtRefStream = extRefService.getMatchingExtRef(tInputs, tCompasFlow);
//Then
assertThat(tExtRefStream).hasSize(1)
.map(TExtRef::getIedName, TExtRef::getLdInst, TExtRef::getDesc)
.containsExactly(Tuple.tuple("IED_Name_1", "LD_INST_1", "Desc_1"));
}

@Test
void clearBinding() {
void clearExtRefBinding_should_remove_binding() {
//Given
TExtRef tExtRef = createExtRef("Desc_1", "IED_Name_1", "LD_INST_1");
//When
@@ -119,7 +136,7 @@ void clearBinding() {
}

@Test
void clearCompasFlowBinding() {
void clearCompasFlowBinding_should_remove_binding() {
//Given
TCompasFlow compasFlow = createCompasFlow("Desc_1", "IED_Name_1", "LD_INST_1");
//When
@@ -131,10 +148,106 @@ void clearCompasFlowBinding() {
assertThat(compasFlow.getDataStreamKey()).isEqualTo("Desc_1");
}

@ParameterizedTest
@MethodSource("provideExtRefsFedBySameControlBlock")
void isExtRefFeedBySameControlBlock_should_return_true(TExtRef tExtRef1, TExtRef tExtRef2) {
// Given : parameter
// When
boolean result = extRefService.isExtRefFeedBySameControlBlock(tExtRef1, tExtRef2);
// Then
assertThat(result).isTrue();
}

private static Stream<Arguments> provideExtRefsFedBySameControlBlock() {
TExtRef tExtRefLnClass = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLnClass.getSrcLNClass().add(TLLN0Enum.LLN_0.value());

return Stream.of(
Arguments.of(createExtRefExample("CB_1", TServiceType.GOOSE), createExtRefExample("CB_1", TServiceType.GOOSE)),
Arguments.of(tExtRefLnClass, createExtRefExample("CB_1", TServiceType.GOOSE)),
Arguments.of(createExtRefExample("CB_1", TServiceType.GOOSE), tExtRefLnClass)
);
}

@ParameterizedTest
@MethodSource("provideExtRefsToCompare")
void isExtRefFeedBySameControlBlock_should_return_false(TExtRef tExtRef1, TExtRef tExtRef2) {
// Given : parameters
// When
boolean result = extRefService.isExtRefFeedBySameControlBlock(tExtRef1, tExtRef2);
// Then
assertThat(result).isFalse();
}

private static Stream<Arguments> provideExtRefsToCompare() {
TExtRef tExtRefLnClass = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLnClass.getSrcLNClass().add("XXX");
TExtRef tExtRefIedName = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefIedName.setIedName("IED_XXX");
TExtRef tExtRefLdInst = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLdInst.setSrcLDInst("LD_XXX");
TExtRef tExtRefLnInst = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLnInst.setSrcLNInst("X");
TExtRef tExtRefPrefix = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefPrefix.setSrcPrefix("X");

return Stream.of(
Arguments.of(named("ExtRef is not fed by same CB when different ServiceType", createExtRefExample("CB_1", TServiceType.GOOSE)),
createExtRefExample("CB_1", TServiceType.SMV)),
Arguments.of(named("ExtRef is not fed by same CB when different SrcCBName", createExtRefExample("CB_1", TServiceType.GOOSE)),
createExtRefExample("CB_2", TServiceType.GOOSE)),
Arguments.of(named("ExtRef is not fed by same CB when different SrcLnClass", createExtRefExample("CB_1", TServiceType.GOOSE)),
tExtRefLnClass),
Arguments.of(named("ExtRef is not fed by same CB when different IedName", createExtRefExample("CB_1", TServiceType.GOOSE)),
tExtRefIedName),
Arguments.of(named("ExtRef is not fed by same CB when different SrcLdInst", createExtRefExample("CB_1", TServiceType.GOOSE)),
tExtRefLdInst),
Arguments.of(named("ExtRef is not fed by same CB when different SrcLnInst", createExtRefExample("CB_1", TServiceType.GOOSE)),
tExtRefLnInst),
Arguments.of(named("ExtRef is not fed by same CB when different SrcPrefix", createExtRefExample("CB_1", TServiceType.GOOSE)),
tExtRefPrefix)
);
}

@Test
void filterDuplicatedExtRefs_should_remove_duplicated_extrefs() {
// Given
TExtRef tExtRefLnClass = createExtRefExample("CB_Name1", TServiceType.GOOSE);
tExtRefLnClass.getSrcLNClass().add(TLLN0Enum.LLN_0.value());
TExtRef tExtRef = createExtRefExample("CB_Name1", TServiceType.GOOSE);
List<TExtRef> tExtRefList = List.of(tExtRef, tExtRefLnClass, createExtRefExample("CB", TServiceType.GOOSE),
createExtRefExample("CB", TServiceType.GOOSE));
// When
List<TExtRef> result = extRefService.filterDuplicatedExtRefs(tExtRefList);
// Then
assertThat(result).hasSizeLessThan(tExtRefList.size())
.hasSize(2);
}

@Test
void filterDuplicatedExtRefs_should_not_remove_not_duplicated_extrefs() {
// Given
TExtRef tExtRefIedName = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefIedName.setIedName("IED_XXX");
TExtRef tExtRefLdInst = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLdInst.setSrcLDInst("LD_XXX");
TExtRef tExtRefLnInst = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLnInst.setSrcLNInst("X");
TExtRef tExtRefPrefix = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefPrefix.setSrcPrefix("X");
List<TExtRef> tExtRefList = List.of(tExtRefIedName, tExtRefLdInst, tExtRefLnInst, tExtRefPrefix,
createExtRefExample("CB_1", TServiceType.GOOSE), createExtRefExample("CB_1", TServiceType.SMV));
// When
List<TExtRef> result = extRefService.filterDuplicatedExtRefs(tExtRefList);
// Then
assertThat(result).hasSameSizeAs(tExtRefList)
.hasSize(6);
}

private static TPrivate createPrivateCompasFlow(List<TCompasFlow> compasFlows) {
TPrivate tPrivate = new TPrivate();
tPrivate.setType(PrivateEnum.COMPAS_FLOW.getPrivateType());
tPrivate.getContent().addAll(compasFlows.stream().map(objectFactory::createFlow).toList());
tPrivate.getContent().addAll(compasFlows.stream().map(value -> new ObjectFactory().createFlow(value)).toList());
return tPrivate;
}

@@ -158,4 +271,4 @@ private TCompasFlow createCompasFlow(String dataStreamKey, String extRefIedName,
return tCompasFlow;
}

}
}
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import org.lfenergy.compas.sct.commons.scl.ln.LN0Adapter;
import org.lfenergy.compas.sct.commons.scl.ln.LNAdapter;
import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
import org.lfenergy.compas.sct.commons.util.PrivateUtils;
import org.lfenergy.compas.sct.commons.util.Utils;
import org.opentest4j.AssertionFailedError;

@@ -69,7 +70,14 @@ public static TExtRef findExtRef(SCL scl, String iedName, String ldInst, String
.stream()
.filter(extRef -> extRefDesc.equals(extRef.getDesc()))
.findFirst()
.orElseThrow(() -> new AssertionFailedError(String.format("ExtRef.des=%s not found in IED.name=%s,LDevice.inst=%s", extRefDesc, iedName, ldInst)));
.orElseThrow(() -> new AssertionFailedError(String.format("ExtRef.desc=%s not found in IED.name=%s,LDevice.inst=%s", extRefDesc, iedName, ldInst)));
}

public static TCompasFlow findCompasFlow(SCL scl, String iedName, String ldInst, String compasFlowDataStreamKey) {
return PrivateUtils.extractCompasPrivates(findInputs(scl, iedName, ldInst).getCurrentElem(), TCompasFlow.class)
.filter(compasFlow -> compasFlowDataStreamKey.equals(compasFlow.getDataStreamKey()))
.findFirst()
.orElseThrow(() -> new AssertionFailedError(String.format("CompasFlow.dataStreamKey=%s not found in IED.name=%s,LDevice.inst=%s", compasFlowDataStreamKey, iedName, ldInst)));
}

public static LN0Adapter findLn0(SCL scl, String iedName, String ldInst) {
Original file line number Diff line number Diff line change
@@ -11,18 +11,15 @@
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.platform.commons.support.ReflectionSupport;
import org.lfenergy.compas.scl2007b4.model.TExtRef;
import org.lfenergy.compas.scl2007b4.model.TLLN0Enum;
import org.lfenergy.compas.scl2007b4.model.TLN;
import org.lfenergy.compas.scl2007b4.model.TServiceType;
import org.lfenergy.compas.sct.commons.dto.FCDAInfo;
import org.lfenergy.compas.sct.commons.exception.ScdException;

import java.util.*;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.*;
import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.createExtRefExample;
import static org.lfenergy.compas.sct.commons.util.Utils.copySclElement;


@@ -514,57 +511,6 @@ void toHex_should_return_hexadecimal(long number, int length, String expected) {
assertThat(result).isEqualTo(expected);
}

@Test
void isExtRefFeedBySameControlBlock_should_return_true() {
// Given
TExtRef tExtRefLnClass = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLnClass.getSrcLNClass().add(TLLN0Enum.LLN_0.value());
TExtRef tExtRef = createExtRefExample("CB_1", TServiceType.GOOSE);
// When
// Then
assertThat(Utils.isExtRefFeedBySameControlBlock(tExtRef, tExtRefLnClass)).isTrue();
assertThat(Utils.isExtRefFeedBySameControlBlock(createExtRefExample("CB_1", TServiceType.GOOSE), tExtRef)).isTrue();
}

@ParameterizedTest(name = "{0}")
@MethodSource("provideExtRefsToCompare")
void isExtRefFeedBySameControlBlock_should_return_false(String testCase, TExtRef tExtRef1, TExtRef tExtRef2) {
// Given
// When
// Then
assertThat(Utils.isExtRefFeedBySameControlBlock(tExtRef1, tExtRef2)).isFalse();
}

private static Stream<Arguments> provideExtRefsToCompare() {
TExtRef tExtRefLnClass = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLnClass.getSrcLNClass().add("XXX");
TExtRef tExtRefIedName = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefIedName.setIedName("IED_XXX");
TExtRef tExtRefLdInst = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLdInst.setSrcLDInst("LD_XXX");
TExtRef tExtRefLnInst = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefLnInst.setSrcLNInst("X");
TExtRef tExtRefPrefix = createExtRefExample("CB_1", TServiceType.GOOSE);
tExtRefPrefix.setSrcPrefix("X");

return Stream.of(
Arguments.of("ExtRef is not fed by same CB when different ServiceType", createExtRefExample("CB_1", TServiceType.GOOSE),
createExtRefExample("CB_1", TServiceType.SMV)),
Arguments.of("ExtRef is not fed by same CB when different SrcCBName", createExtRefExample("CB_1", TServiceType.GOOSE),
createExtRefExample("CB_2", TServiceType.GOOSE)),
Arguments.of("ExtRef is not fed by same CB when different SrcLnClass", createExtRefExample("CB_1", TServiceType.GOOSE),
tExtRefLnClass),
Arguments.of("ExtRef is not fed by same CB when different IedName", createExtRefExample("CB_1", TServiceType.GOOSE),
tExtRefIedName),
Arguments.of("ExtRef is not fed by same CB when different SrcLdInst", createExtRefExample("CB_1", TServiceType.GOOSE),
tExtRefLdInst),
Arguments.of("ExtRef is not fed by same CB when different SrcLnInst", createExtRefExample("CB_1", TServiceType.GOOSE),
tExtRefLnInst),
Arguments.of("ExtRef is not fed by same CB when different SrcPrefix", createExtRefExample("CB_1", TServiceType.GOOSE),
tExtRefPrefix)
);
}

@Test
void copySclElement_should_copy_by_value() {
// Given
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- SPDX-FileCopyrightText: 2023 RTE FRANCE -->
<!-- -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
<SCL version="2007" revision="B" release="4" xmlns="http://www.iec.ch/61850/2003/SCL" xmlns:compas="https://www.lfenergy.org/compas/extension/v1">
<Header id="hId" version="2007" revision="B" toolID="COMPAS"/>
<Substation name="SITE">
<VoltageLevel nomFreq="50" numPhases="3" name="0">
<Voltage unit="V" multiplier="k">0</Voltage>
<Bay name="BAY_1">
<Private type="COMPAS-Topo">
<compas:Topo Node="101" NodeOrder="2" Direction="Down"/>
</Private>
<Private type="COMPAS-Bay">
<compas:Bay BayCodif="TG00000001" UUID="9cd6f05b-1bbd-4ba3-86c5-41c99103e06d" Version="1"
MainShortLabel="SITE1" SecondLabel="SITE-TGENE" NumBay="7" BayCount="1"/>
</Private>
<Function name="FUNCTION_1">
<Private type="COMPAS-Function">
<compas:Function UUID="8f4cda3f-828c-4006-9b87-2af96b19304b" Label="FUNCTION_1"/>
</Private>
<LNode iedName="IED_NAME2" ldInst="LD_INST21" lnClass="ANCR" lnInst="1"/>
</Function>
</Bay>
</VoltageLevel>
</Substation>
<IED name="IED_NAME1">
<Private type="COMPAS-ICDHeader">
<compas:ICDHeader ICDSystemVersionUUID="System_Version_IED_NAME1" IEDType="BCU" IEDSubstationinstance="11" IEDSystemVersioninstance="1" IEDName="IED_NAME1" VendorName="SCLE SFE" IEDmodel="ARKENS-SV1120-HGAAA-EB5" IEDredundancy="A" BayLabel="3THEIX2" hwRev="0.0.2." swRev="1.0a"
headerId="ARKENS-SV1120-HGAAA-EB5_SCU" headerVersion="1.2a" headerRevision="412995"/>
</Private>
<AccessPoint name="AP_NAME">
<Server>
<Authentication/>
<LDevice inst="LD_INST11" ldName="IED_NAME1LD_INST11">
<LN0 lnClass="LLN0" inst="" lnType="LNEX1">
<DOI name="Mod">
<DAI name="stVal">
<Val>on</Val>
</DAI>
</DOI>
<Inputs>
<Private type="COMPAS-Flow">
<compas:Flow FlowSourceBayNode="101" FlowSourceBayNodeOrder="2" dataStreamKey="STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1" ExtRefiedName="IED_NAME_A_CHANGER" ExtRefldinst="LD_INST21" ExtRefprefix="" ExtReflnClass="ANCR" ExtReflnInst="1" FlowID="1" FlowStatus="ACTIVE" FlowKind="BAY_INTERNAL"/>
</Private>
<ExtRef iedName="IED_NAME_A_CHANGER" ldInst="LD_INST21" lnClass="ANCR" lnInst="1" doName="DoName1" daName="daName1" intAddr="INT_ADDR11" pDO="Do11.sdo11" pDA="da11.bda111.bda112.bda113" desc="STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1"/>
</Inputs>
</LN0>
</LDevice>
</Server>
</AccessPoint>
</IED>
<IED name="IED_NAME2">
<Private type="COMPAS-ICDHeader">
<compas:ICDHeader ICDSystemVersionUUID="System_Version_IED_NAME2" IEDType="BCU" IEDSubstationinstance="22" IEDSystemVersioninstance="1" IEDName="IED_NAME2" VendorName="SCLE SFE" IEDmodel="ARKENS-SV1120-HGAAA-EB5" IEDredundancy="A" BayLabel="3THEIX2" hwRev="0.0.2." swRev="1.0a"
headerId="ARKENS-SV1120-HGAAA-EB5_SCU" headerVersion="1.2a" headerRevision="412995"/>
</Private>
<AccessPoint name="AP_NAME">
<Server>
<Authentication/>
<LDevice inst="LD_INST21">
<LN0 lnClass="LLN0" inst="" lnType="LNEX1">
<DOI name="Mod">
<DAI name="stVal">
<Val>on</Val>
</DAI>
</DOI>
</LN0>
<LN lnClass="ANCR" inst="1" lnType="lnType"/>
</LDevice>
</Server>
</AccessPoint>
</IED>
<DataTypeTemplates>
<LNodeType lnClass="LLN0" id="LNEX1">
<DO name="Mod" type="DO1"/>
</LNodeType>
<LNodeType lnClass="ANCR" id="lnType">
<DO name="DoName1" type="DO2"/>
</LNodeType>
<DOType cdc="ENC" id="DO1">
<DA fc="ST" name="stVal" bType="Enum" type="BehaviourModeKind"/>
</DOType>
<DOType cdc="ENC" id="DO2">
<DA fc="BL" name="daName1" bType="BOOLEAN"/>
</DOType>
<EnumType id="BehaviourModeKind">
<EnumVal ord="1">on</EnumVal>
<EnumVal ord="2">off</EnumVal>
<EnumVal ord="3">test</EnumVal>
</EnumType>
</DataTypeTemplates>
</SCL>

0 comments on commit ca976d0

Please sign in to comment.