Skip to content

Commit 72617bc

Browse files
Merge pull request #417 from com-pas/feat/413-ldepf-rsr-1049-unused-channel-assignment
feat(#413): RSR-1049 LDEPF Post processing - unused CHANNEL assignment
2 parents 354b68d + d4d5674 commit 72617bc

File tree

7 files changed

+210
-7
lines changed

7 files changed

+210
-7
lines changed

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import lombok.RequiredArgsConstructor;
88
import org.lfenergy.compas.scl2007b4.model.*;
99
import org.lfenergy.compas.sct.commons.api.ExtRefEditor;
10+
import org.lfenergy.compas.sct.commons.api.LnEditor;
11+
import org.lfenergy.compas.sct.commons.domain.*;
1012
import org.lfenergy.compas.sct.commons.dto.*;
1113
import org.lfenergy.compas.sct.commons.exception.ScdException;
1214
import org.lfenergy.compas.sct.commons.model.epf.EPF;
@@ -46,6 +48,7 @@ public class ExtRefEditorService implements ExtRefEditor {
4648

4749
private final IedService iedService;
4850
private final LdeviceService ldeviceService;
51+
private final LnEditor lnEditor;
4952
private final ExtRefService extRefService;
5053
private final DataTypeTemplatesService dataTypeTemplatesService;
5154

@@ -323,6 +326,41 @@ public List<SclReportItem> manageBindingForLDEPF(SCL scd, EPF epf) {
323326
return sclReportItems;
324327
}
325328

329+
@Override
330+
public void epfPostProcessing(SCL scd) {
331+
iedService.getFilteredIeds(scd, ied -> !ied.getName().contains("TEST"))
332+
.forEach(tied -> ldeviceService.findLdevice(tied, tlDevice -> LDEVICE_LDEPF.equals(tlDevice.getInst()))
333+
.ifPresent(tlDevice -> tlDevice.getLN0().getDOI()
334+
.stream().filter(tdoi -> tdoi.getName().startsWith(INREF_PREFIX))
335+
.forEach(tdoi -> {
336+
DoLinkedToDaFilter doLinkedToSetSrcRef = new DoLinkedToDaFilter(tdoi.getName(), List.of(), SETSRCREF_DA_NAME, List.of());
337+
Optional<TDAI> setSrcRefDAI = lnEditor.getDOAndDAInstances(tlDevice.getLN0(), doLinkedToSetSrcRef);
338+
DoLinkedToDaFilter doLinkedPurPose = new DoLinkedToDaFilter(tdoi.getName(), List.of(), PURPOSE_DA_NAME, List.of());
339+
Optional<TDAI> purPoseDAI = lnEditor.getDOAndDAInstances(tlDevice.getLN0(), doLinkedPurPose);
340+
341+
boolean isSetSrcRefExistAndEmpty = setSrcRefDAI.isPresent()
342+
&& (!setSrcRefDAI.get().isSetVal()
343+
|| (setSrcRefDAI.get().isSetVal()
344+
&& setSrcRefDAI.get().getVal().getFirst().getValue().isEmpty()));
345+
boolean isPurposeExistAndMatchChannel = purPoseDAI.isPresent()
346+
&& purPoseDAI.get().isSetVal()
347+
&& (purPoseDAI.get().getVal().getFirst().getValue().startsWith("DYN_LDEPF_DIGITAL CHANNEL")
348+
|| purPoseDAI.get().getVal().getFirst().getValue().startsWith("DYN_LDEPF_ANALOG CHANNEL"));
349+
if(isSetSrcRefExistAndEmpty && isPurposeExistAndMatchChannel) {
350+
351+
DoLinkedToDa doLinkedToDa = new DoLinkedToDa();
352+
DataObject dataObject = new DataObject();
353+
dataObject.setDoName(tdoi.getName());
354+
doLinkedToDa.setDataObject(dataObject);
355+
DataAttribute dataAttribute = new DataAttribute();
356+
dataAttribute.setDaName(SETSRCREF_DA_NAME);
357+
dataAttribute.setDaiValues(List.of(new DaVal(null, tied.getName()+tlDevice.getInst()+"/LPHD0.Proxy")));
358+
doLinkedToDa.setDataAttribute(dataAttribute);
359+
lnEditor.updateOrCreateDOAndDAInstances(tlDevice.getLN0(), doLinkedToDa);
360+
}
361+
})));
362+
}
363+
326364
private List<SclReportItem> validateIed(SclRootAdapter sclRootAdapter) {
327365
List<SclReportItem> iedErrors = new ArrayList<>(checkIedCompasIcdHeaderAttributes(sclRootAdapter));
328366
iedErrors.addAll(checkIedUnityOfIcdSystemVersionUuid(sclRootAdapter));

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ public interface ExtRefEditor {
6363
*/
6464
List<SclReportItem> manageBindingForLDEPF(SCL scd, EPF epf);
6565

66+
/**
67+
* Pointing an unused channel to an existing object LPHD0.Proxy of the concerned IED.
68+
* An unused channel is characterized by the value DAI name ="setSrcRef"/Val (should be empty) in InRef**
69+
* that have a purpose beginning by DYN_LDEPF_DIGITAL CHANNEL or DYN_LDEPF_ANALOG CHANNEL
70+
* @param scd SCL
71+
*/
72+
void epfPostProcessing(SCL scd);
73+
6674
/**
6775
* Debinding of Private CompasFlows and ExtRef signals based on voltageLevel
6876
*

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
import java.util.regex.Pattern;
2121
import java.util.stream.Collectors;
2222

23-
import static org.lfenergy.compas.sct.commons.util.CommonConstants.BEHAVIOUR_DO_NAME;
24-
import static org.lfenergy.compas.sct.commons.util.CommonConstants.STVAL_DA_NAME;
23+
import static org.lfenergy.compas.sct.commons.util.CommonConstants.*;
2524

2625
/**
2726
* A representation of the model object
@@ -71,8 +70,6 @@ public class LN0Adapter extends AbstractLNAdapter<LN0> {
7170

7271
public static final DoTypeName BEHAVIOUR_DO_TYPE_NAME = new DoTypeName(BEHAVIOUR_DO_NAME);
7372
public static final DaTypeName BEHAVIOUR_DA_TYPE_NAME = getDaTypeNameForBeh();
74-
private static final String DAI_NAME_PURPOSE = "purpose";
75-
private static final String INREF_PREFIX = "InRef";
7673
private static final Pattern LDEFP_DIGITAL_CHANNEL_PATTERN = Pattern.compile("DYN_LDEPF_DIGITAL CHANNEL \\d+_\\d+_BOOLEAN");
7774

7875
/**
@@ -233,8 +230,8 @@ public List<SclReportItem> updateDoInRef() {
233230
return getDOIAdapters().stream()
234231
.filter(doiAdapter -> doiAdapter.getCurrentElem().isSetName()
235232
&& doiAdapter.getCurrentElem().getName().startsWith(INREF_PREFIX)
236-
&& doiAdapter.findDataAdapterByName(DAI_NAME_PURPOSE).isPresent())
237-
.map(doiAdapter -> doiAdapter.getDataAdapterByName(DAI_NAME_PURPOSE).getCurrentElem().getVal().stream()
233+
&& doiAdapter.findDataAdapterByName(PURPOSE_DA_NAME).isPresent())
234+
.map(doiAdapter -> doiAdapter.getDataAdapterByName(PURPOSE_DA_NAME).getCurrentElem().getVal().stream()
238235
.findFirst()
239236
.map(tVal -> doiAdapter.updateDaiFromExtRef(getExtRefsBoundToInRef(tVal.getValue())))
240237
.orElse(List.of(SclReportItem.warning(getXPath(), "The DOI %s can't be bound with an ExtRef".formatted(getXPath()))))

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/CommonConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ public final class CommonConstants {
3636
public static final String SETSRCCB_DA_NAME = "setSrcCB";
3737
public static final String SETTSTREF_DA_NAME = "setTstRef";
3838
public static final String SETTSTCB_DA_NAME = "setTstCB";
39+
public static final String PURPOSE_DA_NAME = "purpose";
3940
public static final String Q_DA_NAME = "q";
4041
public static final String IED_TEST_NAME = "IEDTEST";
42+
public static final String INREF_PREFIX = "InRef";
4143

4244
/**
4345
* Private Controlller, should not be instanced

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

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.lfenergy.compas.sct.commons;
66

77
import org.assertj.core.api.Assertions;
8+
import org.assertj.core.api.SoftAssertions;
89
import org.assertj.core.groups.Tuple;
910
import org.junit.jupiter.api.BeforeEach;
1011
import org.junit.jupiter.api.Tag;
@@ -39,7 +40,7 @@ class ExtRefEditorServiceTest {
3940

4041
@BeforeEach
4142
void init() {
42-
extRefEditorService = new ExtRefEditorService(new IedService(), new LdeviceService(), new ExtRefService(), new DataTypeTemplatesService());
43+
extRefEditorService = new ExtRefEditorService(new IedService(), new LdeviceService(), new LnService(), new ExtRefService(), new DataTypeTemplatesService());
4344
}
4445

4546
@Test
@@ -955,4 +956,44 @@ void updateIedNameBasedOnLnode_when_no_Compas_ICD_Header_should_return_an_error(
955956
.containsExactly(Tuple.tuple(true,
956957
"The substation LNode with following attributes : IedName:IED_NAME2 / LdInst:LD_INST21 / LnClass:ANCR / LnInst:1 does not contain the needed (COMPAS - ICDHeader) private"));
957958
}
959+
960+
@Test
961+
void epfPostProcessing_when_exist_unused_channel_should_update_setSrcRef() {
962+
// Given
963+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ldepf/scd_ldepf_postProcessing.xml");
964+
// When
965+
extRefEditorService.epfPostProcessing(scd);
966+
// Then
967+
SoftAssertions softly = new SoftAssertions();
968+
969+
Optional<TDAI> setSrcRefInInRef1 = findDai(scd, "IED_NAME1", "LDEPF", "InRef1", "setSrcRef");
970+
Optional<TDAI> purposeInInRef1 = findDai(scd, "IED_NAME1", "LDEPF", "InRef1", "purpose");
971+
assertThat(purposeInInRef1).isPresent();
972+
softly.assertThat(purposeInInRef1.get().getVal().getFirst().getValue()).doesNotStartWith("DYN_LDEPF_DIGITAL CHANNEL");
973+
softly.assertThat(purposeInInRef1.get().getVal().getFirst().getValue()).doesNotStartWith("DYN_LDEPF_ANALOG CHANNEL");
974+
assertThat(setSrcRefInInRef1).isPresent();
975+
softly.assertThat(setSrcRefInInRef1.get().isSetVal()).isFalse();
976+
977+
Optional<TDAI> setSrcRefInInRef2 = findDai(scd, "IED_NAME1", "LDEPF", "InRef2", "setSrcRef");
978+
Optional<TDAI> purposeInInRef2 = findDai(scd, "IED_NAME1", "LDEPF", "InRef2", "purpose");
979+
assertThat(purposeInInRef2).isPresent();
980+
softly.assertThat(purposeInInRef2.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_DIGITAL CHANNEL");
981+
assertThat(setSrcRefInInRef2).isPresent();
982+
softly.assertThat(setSrcRefInInRef2.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
983+
984+
Optional<TDAI> setSrcRefInInRef3 = findDai(scd, "IED_NAME1", "LDEPF", "InRef3", "setSrcRef");
985+
Optional<TDAI> purposeInInRef3 = findDai(scd, "IED_NAME1", "LDEPF", "InRef3", "purpose");
986+
assertThat(purposeInInRef3).isPresent();
987+
softly.assertThat(purposeInInRef3.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_DIGITAL CHANNEL");
988+
assertThat(setSrcRefInInRef3).isPresent();
989+
softly.assertThat(setSrcRefInInRef3.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
990+
991+
Optional<TDAI> setSrcRefInInRef4 = findDai(scd, "IED_NAME1", "LDEPF", "InRef4", "setSrcRef");
992+
Optional<TDAI> purposeInInRef4 = findDai(scd, "IED_NAME1", "LDEPF", "InRef4", "purpose");
993+
assertThat(purposeInInRef4).isPresent();
994+
softly.assertThat(purposeInInRef4.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_ANALOG CHANNEL");
995+
assertThat(setSrcRefInInRef4).isPresent();
996+
softly.assertThat(setSrcRefInInRef4.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy");
997+
softly.assertAll();
998+
}
958999
}

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
@@ -19,6 +19,7 @@
1919

2020
import java.util.Arrays;
2121
import java.util.List;
22+
import java.util.Optional;
2223
import java.util.stream.Stream;
2324

2425
import static org.assertj.core.api.Assertions.assertThat;
@@ -118,6 +119,18 @@ public static IDataParentAdapter findDoiOrSdi(AbstractLNAdapter<?> lnAdapter, St
118119
return parentAdapter;
119120
}
120121

122+
public static Optional<TDAI> findDai(SCL scl, String iedName, String ldInst, String doiName, String daiName) {
123+
return scl.getIED().stream().filter(tied -> tied.getName().equals(iedName))
124+
.flatMap(tied -> tied.getAccessPoint().stream())
125+
.flatMap(tAccessPoint -> tAccessPoint.getServer().getLDevice().stream())
126+
.filter(tlDevice -> tlDevice.getInst().equals(ldInst))
127+
.flatMap(tlDevice -> tlDevice.getLN0().getDOI().stream())
128+
.filter(tdoi -> tdoi.getName().equals(doiName))
129+
.flatMap(tdoi -> tdoi.getSDIOrDAI().stream().map(tUnNaming -> (TDAI)tUnNaming))
130+
.filter(tdai -> tdai.getName().equals(daiName))
131+
.findFirst();
132+
}
133+
121134
public static AbstractDAIAdapter<?> findDai(AbstractLNAdapter<?> lnAdapter, String dataTypeRef) {
122135
String[] names = dataTypeRef.split("\\.");
123136
if (names.length < 2) {
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!-- SPDX-FileCopyrightText: 2023 2024 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+
<DOI name="Mod">
26+
<DAI name="stVal" valImport="true">
27+
<Val>on</Val>
28+
</DAI>
29+
</DOI>
30+
<DOI name="InRef1">
31+
<DAI name="purpose" valKind="RO" valImport="false">
32+
<Val>DYN_LDAGSA2_Circuit I phase A amplitude_1_Vector</Val>
33+
</DAI>
34+
<DAI name="setSrcRef" valKind="RO" valImport="false"/>
35+
</DOI>
36+
<DOI name="InRef2">
37+
<DAI name="purpose" valKind="RO" valImport="false">
38+
<Val>DYN_LDEPF_DIGITAL CHANNEL 11 ADF</Val>
39+
</DAI>
40+
<DAI name="setSrcRef" valKind="RO" valImport="false"/>
41+
</DOI>
42+
<DOI name="InRef3">
43+
<DAI name="purpose" valKind="RO" valImport="false">
44+
<Val>DYN_LDEPF_DIGITAL CHANNEL 12 ADF</Val>
45+
</DAI>
46+
<DAI name="setSrcRef" valKind="RO" valImport="false">
47+
<Val/>
48+
</DAI>
49+
</DOI>
50+
<DOI name="InRef4">
51+
<DAI name="purpose" valKind="RO" valImport="false">
52+
<Val>DYN_LDEPF_ANALOG CHANNEL 11 ADF</Val>
53+
</DAI>
54+
<DAI name="setSrcRef" valKind="RO" valImport="false"/>
55+
</DOI>
56+
<Inputs>
57+
<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"/>
58+
</Inputs>
59+
</LN0>
60+
</LDevice>
61+
<LDevice inst="LD_INST_1" ldName="IED_NAME1LD_INST_1">
62+
<LN0 lnClass="LLN0" inst="" lnType="LLN0_ID1">
63+
<DOI name="Mod">
64+
<DAI name="stVal">
65+
<Val>on</Val>
66+
</DAI>
67+
</DOI>
68+
<DOI name="InRef1">
69+
<DAI name="purpose" valKind="RO" valImport="false">
70+
<Val>DYN_LDAGSA2_Circuit I phase A amplitude_1_Vector</Val>
71+
</DAI>
72+
<DAI name="setSrcRef" valKind="RO" valImport="false">
73+
</DAI>
74+
</DOI>
75+
</LN0>
76+
</LDevice>
77+
</Server>
78+
</AccessPoint>
79+
</IED>
80+
<DataTypeTemplates>
81+
<LNodeType lnClass="LLN0" id="LLN0_ID1">
82+
<DO name="Mod" type="DO2" transient="false" />
83+
<DO name="InRef1" type="InRefType"/>
84+
<DO name="InRef2" type="InRefType"/>
85+
<DO name="InRef3" type="InRefType"/>
86+
<DO name="InRef4" type="InRefType"/>
87+
</LNodeType>
88+
<DOType cdc="ENC" id="DO2">
89+
<DA fc="ST" dchg="true" qchg="false" dupd="false" name="stVal" bType="Enum" type="BehaviourModeKind" valImport="true" />
90+
<DA fc="BL" name="daName1" bType="BOOLEAN"/>
91+
</DOType>
92+
<DOType cdc="ORG" id="InRefType">
93+
<DA fc="SP" dchg="true" qchg="false" dupd="false" name="setSrcRef" bType="ObjRef"/>
94+
<DA fc="DC" dchg="false" qchg="false" dupd="false" name="purpose" bType="VisString255" valKind="RO" valImport="false"/>
95+
</DOType>
96+
<EnumType id="BehaviourModeKind">
97+
<EnumVal ord="1">on</EnumVal>
98+
<EnumVal ord="2">off</EnumVal>
99+
<EnumVal ord="3">blocked</EnumVal>
100+
<EnumVal ord="4">test</EnumVal>
101+
<EnumVal ord="5">test/blocked</EnumVal>
102+
</EnumType>
103+
</DataTypeTemplates>
104+
</SCL>

0 commit comments

Comments
 (0)