Skip to content

Commit 56c5869

Browse files
committed
feat: LDEPF post configuration, inref should not be configured when inactive, closes #510, RSR-1240
Signed-off-by: Samir Romdhani <[email protected]>
1 parent 5b0f6c3 commit 56c5869

File tree

5 files changed

+182
-40
lines changed

5 files changed

+182
-40
lines changed

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefEditorService.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
public class ExtRefEditorService implements ExtRefEditor {
3838
private static final String INVALID_OR_MISSING_ATTRIBUTES_IN_EXT_REF_BINDING_INFO = "Invalid or missing attributes in ExtRef binding info";
3939
private static final String COMPAS_LNODE_STATUS = "COMPAS-LNodeStatus";
40+
private static final String LPHD0_PROXY = "LPHD0.Proxy";
4041
private static final List<DoNameAndDaName> DO_DA_MAPPINGS = List.of(
4142
new DoNameAndDaName(CHNUM1_DO_NAME, DU_DA_NAME),
4243
new DoNameAndDaName(LEVMOD_DO_NAME, SETVAL_DA_NAME),
@@ -268,7 +269,7 @@ public List<SclReportItem> manageBindingForLDEPF(SCL scd, EPF epf) {
268269
if (!epf.isSetChannels()) return errorHandler;
269270
iedService.getFilteredIeds(scd, ied -> !ied.getName().contains("TEST"))
270271
.forEach(tied -> ldeviceService.findLdevice(tied, tlDevice -> tlDevice.getInst().equals(LDEVICE_LDEPF))
271-
.filter(ldepfLdevice -> PrivateUtils.extractStringPrivate(ldepfLdevice.getLN0(), COMPAS_LNODE_STATUS).map(status -> !status.equals("off")).orElse(false))
272+
.filter(ldepfLdevice -> PrivateUtils.extractStringPrivate(ldepfLdevice.getLN0(), COMPAS_LNODE_STATUS).map(status -> !status.equals(ActiveStatus.OFF.getValue())).orElse(false))
272273
.ifPresent(ldepfLdevice -> getExtRefWithBayReferenceInLDEPF(tied, ldepfLdevice)
273274
.forEach(extRefBayRef -> epf.getChannels().getChannel().stream().filter(tChannel -> doesExtRefMatchLDEPFChannel(extRefBayRef.extRef(), tChannel))
274275
.findFirst().ifPresent(channel -> {
@@ -293,13 +294,15 @@ public List<SclReportItem> manageBindingForLDEPF(SCL scd, EPF epf) {
293294
public void epfPostProcessing(SCL scd) {
294295
iedService.getFilteredIeds(scd, ied -> !ied.getName().contains("TEST"))
295296
.forEach(tied -> ldeviceService.findLdevice(tied, LDEVICE_LDEPF)
296-
.ifPresent(tlDevice -> tlDevice.getLN0().getDOI()
297+
.filter(ldepfLdevice -> PrivateUtils.extractStringPrivate(ldepfLdevice.getLN0(), COMPAS_LNODE_STATUS).map(status -> !status.equals(ActiveStatus.OFF.getValue())).orElse(false))
298+
.ifPresent(ldepfLdevice -> ldepfLdevice.getLN0().getDOI()
297299
.stream().filter(tdoi -> tdoi.getName().startsWith(INREF_PREFIX))
298300
.forEach(tdoi -> {
301+
LN0 ln0 = ldepfLdevice.getLN0();
299302
DoLinkedToDaFilter doLinkedToSetSrcRef = new DoLinkedToDaFilter(tdoi.getName(), List.of(), SETSRCREF_DA_NAME, List.of());
300-
Optional<TDAI> setSrcRefDAI = lnEditor.getDOAndDAInstances(tlDevice.getLN0(), doLinkedToSetSrcRef);
303+
Optional<TDAI> setSrcRefDAI = lnEditor.getDOAndDAInstances(ln0, doLinkedToSetSrcRef);
301304
DoLinkedToDaFilter doLinkedPurPose = new DoLinkedToDaFilter(tdoi.getName(), List.of(), PURPOSE_DA_NAME, List.of());
302-
Optional<TDAI> purPoseDAI = lnEditor.getDOAndDAInstances(tlDevice.getLN0(), doLinkedPurPose);
305+
Optional<TDAI> purPoseDAI = lnEditor.getDOAndDAInstances(ln0, doLinkedPurPose);
303306

304307
boolean isSetSrcRefExistAndEmpty = setSrcRefDAI.isPresent()
305308
&& (!setSrcRefDAI.get().isSetVal()
@@ -314,9 +317,9 @@ public void epfPostProcessing(SCL scd) {
314317
dataObject.setDoName(tdoi.getName());
315318
DataAttribute dataAttribute = new DataAttribute();
316319
dataAttribute.setDaName(SETSRCREF_DA_NAME);
317-
dataAttribute.setDaiValues(List.of(new DaVal(null, tied.getName()+tlDevice.getInst()+"/LPHD0.Proxy")));
320+
dataAttribute.setDaiValues(List.of(new DaVal(null, ldepfLdevice.getLdName()+"/"+LPHD0_PROXY)));
318321
DoLinkedToDa doLinkedToDa = new DoLinkedToDa(dataObject, dataAttribute);
319-
lnEditor.updateOrCreateDOAndDAInstances(tlDevice.getLN0(), doLinkedToDa);
322+
lnEditor.updateOrCreateDOAndDAInstances(ln0, doLinkedToDa);
320323
}
321324
})));
322325
}

sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefEditorServiceTest.java

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
package org.lfenergy.compas.sct.commons;
66

7-
import org.assertj.core.api.SoftAssertions;
87
import org.junit.jupiter.api.BeforeEach;
98
import org.junit.jupiter.api.Tag;
109
import org.junit.jupiter.api.Test;
@@ -22,7 +21,6 @@
2221
import org.lfenergy.compas.sct.commons.util.PrivateEnum;
2322

2423
import java.util.List;
25-
import java.util.Optional;
2624
import java.util.stream.Stream;
2725

2826
import static org.assertj.core.api.Assertions.assertThat;
@@ -595,37 +593,40 @@ void epfPostProcessing_when_exist_unused_channel_should_update_setSrcRef() {
595593
// When
596594
extRefEditorService.epfPostProcessing(scd);
597595
// Then
598-
SoftAssertions softly = new SoftAssertions();
599-
600-
Optional<TDAI> setSrcRefInInRef1 = findDai(scd, "IED_NAME1", "LDEPF", "InRef1", "setSrcRef");
601-
Optional<TDAI> purposeInInRef1 = findDai(scd, "IED_NAME1", "LDEPF", "InRef1", "purpose");
602-
assertThat(purposeInInRef1).isPresent();
603-
softly.assertThat(purposeInInRef1.get().getVal().getFirst().getValue()).doesNotStartWith("DYN_LDEPF_DIGITAL CHANNEL");
604-
softly.assertThat(purposeInInRef1.get().getVal().getFirst().getValue()).doesNotStartWith("DYN_LDEPF_ANALOG CHANNEL");
605-
assertThat(setSrcRefInInRef1).isPresent();
606-
softly.assertThat(setSrcRefInInRef1.get().isSetVal()).isFalse();
607-
608-
Optional<TDAI> setSrcRefInInRef2 = findDai(scd, "IED_NAME1", "LDEPF", "InRef2", "setSrcRef");
609-
Optional<TDAI> purposeInInRef2 = findDai(scd, "IED_NAME1", "LDEPF", "InRef2", "purpose");
610-
assertThat(purposeInInRef2).isPresent();
611-
softly.assertThat(purposeInInRef2.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_DIGITAL CHANNEL");
612-
assertThat(setSrcRefInInRef2).isPresent();
613-
softly.assertThat(setSrcRefInInRef2.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
614-
615-
Optional<TDAI> setSrcRefInInRef3 = findDai(scd, "IED_NAME1", "LDEPF", "InRef3", "setSrcRef");
616-
Optional<TDAI> purposeInInRef3 = findDai(scd, "IED_NAME1", "LDEPF", "InRef3", "purpose");
617-
assertThat(purposeInInRef3).isPresent();
618-
softly.assertThat(purposeInInRef3.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_DIGITAL CHANNEL");
619-
assertThat(setSrcRefInInRef3).isPresent();
620-
softly.assertThat(setSrcRefInInRef3.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
621-
622-
Optional<TDAI> setSrcRefInInRef4 = findDai(scd, "IED_NAME1", "LDEPF", "InRef4", "setSrcRef");
623-
Optional<TDAI> purposeInInRef4 = findDai(scd, "IED_NAME1", "LDEPF", "InRef4", "purpose");
624-
assertThat(purposeInInRef4).isPresent();
625-
softly.assertThat(purposeInInRef4.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_ANALOG CHANNEL");
626-
assertThat(setSrcRefInInRef4).isPresent();
627-
softly.assertThat(setSrcRefInInRef4.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
628-
softly.assertAll();
596+
assertThat(scd.getIED())
597+
.filteredOn(tied -> tied.getName().equals("IED_NAME1"))
598+
.flatExtracting(TIED::getAccessPoint)
599+
.extracting(TAccessPoint::getServer)
600+
.flatExtracting(TServer::getLDevice)
601+
.filteredOn(tlDevice -> tlDevice.getInst().equals(LDEVICE_LDEPF))
602+
.extracting(tlDevice -> tlDevice.getLN0().getDOI())
603+
.allSatisfy(tdois -> {
604+
assertThat(tdois)
605+
.filteredOn(tdoi -> tdoi.getName().equals("InRef1"))
606+
.allSatisfy(tdoi -> {
607+
assertThat(getDai(tdoi,"purpose").getVal().getFirst().getValue()).doesNotStartWith("DYN_LDEPF_DIGITAL CHANNEL");
608+
assertThat(getDai(tdoi,"purpose").getVal().getFirst().getValue()).doesNotStartWith("DYN_LDEPF_ANALOG CHANNEL");
609+
assertThat(getDai(tdoi, "setSrcRef").isSetVal()).isFalse();
610+
});
611+
assertThat(tdois)
612+
.filteredOn(tdoi -> tdoi.getName().equals("InRef2"))
613+
.allSatisfy(tdoi -> {
614+
assertThat(getDaiValue(tdoi,"purpose")).startsWith("DYN_LDEPF_DIGITAL CHANNEL");
615+
assertThat(getDaiValue(tdoi, "setSrcRef")).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
616+
});
617+
assertThat(tdois)
618+
.filteredOn(tdoi -> tdoi.getName().equals("InRef3"))
619+
.allSatisfy(tdoi -> {
620+
assertThat(getDaiValue(tdoi,"purpose")).startsWith("DYN_LDEPF_DIGITAL CHANNEL");
621+
assertThat(getDaiValue(tdoi, "setSrcRef")).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
622+
});
623+
assertThat(tdois)
624+
.filteredOn(tdoi -> tdoi.getName().equals("InRef4"))
625+
.allSatisfy(tdoi -> {
626+
assertThat(getDaiValue(tdoi,"purpose")).startsWith("DYN_LDEPF_ANALOG CHANNEL");
627+
assertThat(getDaiValue(tdoi, "setSrcRef")).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
628+
});
629+
});
629630
}
630631

631632
@ParameterizedTest()
@@ -996,4 +997,23 @@ void manageBindingForLDEPF_should_update_binding_properties_when_external_bindin
996997
});
997998
}
998999

1000+
@Test
1001+
void epfPostProcessing_when_exist_unused_channel_and_ldepf_off_should_not_update_Inref_setSrcRef() {
1002+
// Given
1003+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ldepf/scd_ldepf_postProcessing_when_ln0_off.xml");
1004+
// When
1005+
extRefEditorService.epfPostProcessing(scd);
1006+
// Then
1007+
assertThat(scd.getIED())
1008+
.filteredOn(tied -> tied.getName().equals("IED_NAME1"))
1009+
.flatExtracting(TIED::getAccessPoint)
1010+
.extracting(TAccessPoint::getServer)
1011+
.flatExtracting(TServer::getLDevice)
1012+
.filteredOn(tlDevice -> tlDevice.getInst().equals(LDEVICE_LDEPF))
1013+
.flatExtracting(tlDevice -> tlDevice.getLN0().getDOI())
1014+
.filteredOn(tdoi -> tdoi.getName().startsWith("InRef"))
1015+
.extracting(tdoi -> getDai(tdoi, "setSrcRef"))
1016+
.filteredOn(TDAI::isSetVal)
1017+
.allSatisfy(tdai -> assertThat(tdai.getVal().getFirst().getValue()).isNotEqualTo("IED_NAME1LDEPF/LPHD0.Proxy"));
1018+
}
9991019
}

sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,12 +241,25 @@ public static Stream<TDAI> getDai(TAnyLN tAnyLN, String doiName, String daiName)
241241
.filter(tdai -> daiName.equals(tdai.getName()));
242242
}
243243

244+
public static TDAI getDai(TDOI tdoi, String daiName) {
245+
return tdoi.getSDIOrDAI().stream()
246+
.filter(TDAI.class::isInstance)
247+
.map(TDAI.class::cast)
248+
.filter(tdai -> daiName.equals(tdai.getName()))
249+
.findFirst()
250+
.orElseThrow(() -> new AssertionFailedError(String.format("DAI %s not found in DOI %s", daiName, tdoi.getName())));
251+
}
252+
244253
public static String getDaiValue(TAnyLN tAnyLN, String doiName, String daiName) {
245254
TDAI tdai = getDai(tAnyLN, doiName, daiName).findFirst()
246255
.orElseThrow(() -> new AssertionFailedError(String.format("DAI %s.%s not found in LN", doiName, daiName)));
247256
return getValue(tdai);
248257
}
249258

259+
public static String getDaiValue(TDOI tdoi, String daiName) {
260+
return getValue(getDai(tdoi, daiName));
261+
}
262+
250263
public static SclRootAdapter createSclRootAdapterWithIed(String iedName) {
251264
SCL scl = new SCL();
252265
scl.setHeader(new THeader());

sct-commons/src/test/resources/scd-ldepf/scd_ldepf_postProcessing.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<!-- SPDX-FileCopyrightText: 2023 2024 RTE FRANCE -->
2+
<!-- SPDX-FileCopyrightText: 2023 2024 2025 RTE FRANCE -->
33
<!-- -->
44
<!-- SPDX-License-Identifier: Apache-2.0 -->
55
<SCL version="2007" revision="B" release="4" xmlns="http://www.iec.ch/61850/2003/SCL" xmlns:compas="https://www.lfenergy.org/compas/extension/v1">
@@ -22,6 +22,7 @@
2222
<Authentication/>
2323
<LDevice inst="LDEPF" ldName="IED_NAME1LDEPF">
2424
<LN0 lnClass="LLN0" inst="" lnType="LLN0_ID1">
25+
<Private type="COMPAS-LNodeStatus">on</Private>
2526
<DOI name="Mod">
2627
<DAI name="stVal" valImport="true">
2728
<Val>on</Val>
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!-- SPDX-FileCopyrightText: 2023 2024 2025 RTE FRANCE -->
3+
<!-- -->
4+
<!-- SPDX-License-Identifier: Apache-2.0 -->
5+
<SCL version="2007" revision="B" release="4" xmlns="http://www.iec.ch/61850/2003/SCL" xmlns:compas="https://www.lfenergy.org/compas/extension/v1">
6+
<Private type="COMPAS-SclFileType">
7+
<compas:SclFileType>SCD</compas:SclFileType>
8+
</Private>
9+
<Header id="hId" version="2007" revision="B" toolID="COMPAS"/>
10+
<IED name="IEDTEST">
11+
<AccessPoint name="AP_NAME"/>
12+
</IED>
13+
<IED name="IED_NAME1">
14+
<Private type="COMPAS-Bay">
15+
<compas:Bay UUID="bayUUID" BayCodif="CB00001101" NumBay="1" BayCount="1" MainShortLabel="aa"/>
16+
</Private>
17+
<Private type="COMPAS-ICDHeader">
18+
<compas:ICDHeader IEDType="BCU" IEDSubstationinstance="11" IEDSystemVersioninstance="1" BayLabel="3THEIX1" IEDName="PLOE53THEIX1BCU1" ICDSystemVersionUUID="IED3e4c26f08244476f890fe38f62609a56" VendorName="Efacec" IEDredundancy="None" IEDmodel="TPU L500-3-1-F-3-C-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-X-XXXX-6-4-8-X-CXXX-A2B1C2E3F2G1H1" hwRev="1.00" swRev="2.06.000" headerId="TEMPLATE" headerVersion="1.515" headerRevision="1"/>
19+
</Private>
20+
<AccessPoint name="AP_NAME">
21+
<Server>
22+
<Authentication/>
23+
<LDevice inst="LDEPF" ldName="IED_NAME1LDEPF">
24+
<LN0 lnClass="LLN0" inst="" lnType="LLN0_ID1">
25+
<Private type="COMPAS-LNodeStatus">off</Private>
26+
<DOI name="Mod">
27+
<DAI name="stVal" valImport="true">
28+
<Val>off</Val>
29+
</DAI>
30+
</DOI>
31+
<DOI name="InRef1">
32+
<DAI name="purpose" valKind="RO" valImport="false">
33+
<Val>DYN_LDAGSA2_Circuit I phase A amplitude_1_Vector</Val>
34+
</DAI>
35+
<DAI name="setSrcRef" valKind="RO" valImport="false"/>
36+
</DOI>
37+
<DOI name="InRef2">
38+
<DAI name="purpose" valKind="RO" valImport="false">
39+
<Val>DYN_LDEPF_DIGITAL CHANNEL 11 ADF</Val>
40+
</DAI>
41+
<DAI name="setSrcRef" valKind="RO" valImport="false"/>
42+
</DOI>
43+
<DOI name="InRef3">
44+
<DAI name="purpose" valKind="RO" valImport="false">
45+
<Val>DYN_LDEPF_DIGITAL CHANNEL 12 ADF</Val>
46+
</DAI>
47+
<DAI name="setSrcRef" valKind="RO" valImport="false">
48+
<Val/>
49+
</DAI>
50+
</DOI>
51+
<DOI name="InRef4">
52+
<DAI name="purpose" valKind="RO" valImport="false">
53+
<Val>DYN_LDEPF_ANALOG CHANNEL 11 ADF</Val>
54+
</DAI>
55+
<DAI name="setSrcRef" valKind="RO" valImport="false"/>
56+
</DOI>
57+
<Inputs>
58+
<ExtRef ldInst="LD_INST_1" lnClass="ANCR" lnInst="1" doName="DoName1" daName="daName1" intAddr="INT_ADDR11" desc="STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1"/>
59+
</Inputs>
60+
</LN0>
61+
</LDevice>
62+
<LDevice inst="LD_INST_1" ldName="IED_NAME1LD_INST_1">
63+
<LN0 lnClass="LLN0" inst="" lnType="LLN0_ID1">
64+
<DOI name="Mod">
65+
<DAI name="stVal">
66+
<Val>on</Val>
67+
</DAI>
68+
</DOI>
69+
<DOI name="InRef1">
70+
<DAI name="purpose" valKind="RO" valImport="false">
71+
<Val>DYN_LDAGSA2_Circuit I phase A amplitude_1_Vector</Val>
72+
</DAI>
73+
<DAI name="setSrcRef" valKind="RO" valImport="false">
74+
</DAI>
75+
</DOI>
76+
</LN0>
77+
</LDevice>
78+
</Server>
79+
</AccessPoint>
80+
</IED>
81+
<DataTypeTemplates>
82+
<LNodeType lnClass="LLN0" id="LLN0_ID1">
83+
<DO name="Mod" type="DO2" transient="false" />
84+
<DO name="InRef1" type="InRefType"/>
85+
<DO name="InRef2" type="InRefType"/>
86+
<DO name="InRef3" type="InRefType"/>
87+
<DO name="InRef4" type="InRefType"/>
88+
</LNodeType>
89+
<DOType cdc="ENC" id="DO2">
90+
<DA fc="ST" dchg="true" qchg="false" dupd="false" name="stVal" bType="Enum" type="BehaviourModeKind" valImport="true" />
91+
<DA fc="BL" name="daName1" bType="BOOLEAN"/>
92+
</DOType>
93+
<DOType cdc="ORG" id="InRefType">
94+
<DA fc="SP" dchg="true" qchg="false" dupd="false" name="setSrcRef" bType="ObjRef"/>
95+
<DA fc="DC" dchg="false" qchg="false" dupd="false" name="purpose" bType="VisString255" valKind="RO" valImport="false"/>
96+
</DOType>
97+
<EnumType id="BehaviourModeKind">
98+
<EnumVal ord="1">on</EnumVal>
99+
<EnumVal ord="2">off</EnumVal>
100+
<EnumVal ord="3">blocked</EnumVal>
101+
<EnumVal ord="4">test</EnumVal>
102+
<EnumVal ord="5">test/blocked</EnumVal>
103+
</EnumType>
104+
</DataTypeTemplates>
105+
</SCL>

0 commit comments

Comments
 (0)