Skip to content

Commit 25e13a2

Browse files
feat(404 - rsr-1004): Add constraints to update compas flow bindings
- modification of ExtRefEditorService and add a test - modification of ExtRefEditor interface to return List<SclReportItem> with updateIedNameBasedOnLnode() Signed-off-by: JAFFRE Guillaume <[email protected]>
1 parent 1167ab8 commit 25e13a2

File tree

6 files changed

+298
-14
lines changed

6 files changed

+298
-14
lines changed

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

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

77
import lombok.RequiredArgsConstructor;
8-
import org.apache.commons.lang3.StringUtils;
98
import org.lfenergy.compas.scl2007b4.model.*;
109
import org.lfenergy.compas.sct.commons.api.ExtRefEditor;
1110
import org.lfenergy.compas.sct.commons.dto.*;
@@ -27,6 +26,7 @@
2726

2827
import java.math.BigInteger;
2928
import java.util.*;
29+
import java.util.function.BinaryOperator;
3030
import java.util.function.Function;
3131
import java.util.stream.Collectors;
3232

@@ -90,7 +90,7 @@ private static List<TIED> getIedSources(SclRootAdapter sclRootAdapter, TCompasBa
9090
*/
9191
private List<ExtRefInfo.ExtRefWithBayReference> getExtRefWithBayReferenceInLDEPF(TDataTypeTemplates dataTypeTemplates, TIED tied, final TLDevice tlDevice, final List<SclReportItem> sclReportItems) {
9292
List<ExtRefInfo.ExtRefWithBayReference> extRefBayReferenceList = new ArrayList<>();
93-
String lDevicePath = "SCL/IED[@name=\""+ tied.getName() + "\"]/AccessPoint/Server/LDevice[@inst=\"" + tlDevice.getInst() + "\"]";
93+
String lDevicePath = "SCL/IED[@name=\"" + tied.getName() + "\"]/AccessPoint/Server/LDevice[@inst=\"" + tlDevice.getInst() + "\"]";
9494
Optional<TCompasBay> tCompasBay = PrivateUtils.extractCompasPrivate(tied, TCompasBay.class);
9595
if (tCompasBay.isEmpty()) {
9696
sclReportItems.add(SclReportItem.error(lDevicePath, "The IED has no Private Bay"));
@@ -405,8 +405,7 @@ private List<SclReportItem> updateLDEPFDos(LDeviceAdapter lDeviceAdapter, TExtRe
405405
private Optional<SclReportItem> updateVal(AbstractLNAdapter<?> lnAdapter, String doName, String daName, TExtRef extRef, TChannel setting) {
406406
String value = switch (daName) {
407407
case DU_DA_NAME -> setting.getChannelShortLabel();
408-
case SETVAL_DA_NAME ->
409-
LN_PREFIX_B.equals(lnAdapter.getPrefix()) || LN_PREFIX_A.equals(lnAdapter.getPrefix()) ? setting.getChannelLevModQ().value() : setting.getChannelLevMod().value();
408+
case SETVAL_DA_NAME -> LN_PREFIX_B.equals(lnAdapter.getPrefix()) || LN_PREFIX_A.equals(lnAdapter.getPrefix()) ? setting.getChannelLevModQ().value() : setting.getChannelLevMod().value();
410409
case STVAL_DA_NAME -> ActiveStatus.ON.getValue();
411410
case SETSRCREF_DA_NAME -> computeDaiValue(lnAdapter, extRef, setting.getDAName());
412411
default -> null;
@@ -464,7 +463,7 @@ public void debindCompasFlowsAndExtRefsBasedOnVoltageLevel(SCL scd) {
464463

465464

466465
@Override
467-
public void updateIedNameBasedOnLnode(SCL scl) {
466+
public List<SclReportItem> updateIedNameBasedOnLnode(SCL scl) {
468467
Map<TopoKey, TBay> bayByTopoKey = scl.getSubstation().stream()
469468
.flatMap(tSubstation -> tSubstation.getVoltageLevel().stream())
470469
.flatMap(tVoltageLevel -> tVoltageLevel.getBay().stream())
@@ -475,6 +474,7 @@ public void updateIedNameBasedOnLnode(SCL scl) {
475474
.flatMap(Optional::stream)
476475
.collect(Collectors.toMap(BayTopoKey::topoKey, BayTopoKey::bay));
477476

477+
List<SclReportItem> sclReportItems = new ArrayList<>();
478478
scl.getIED().stream()
479479
.flatMap(ldeviceService::getLdevices)
480480
.forEach(tlDevice ->
@@ -488,9 +488,21 @@ public void updateIedNameBasedOnLnode(SCL scl) {
488488
&& Objects.equals(tlNode.getLnInst(), tCompasFlow.getExtReflnInst())
489489
&& Utils.lnClassEquals(tlNode.getLnClass(), tCompasFlow.getExtReflnClass())
490490
&& Objects.equals(tlNode.getPrefix(), tCompasFlow.getExtRefprefix()))
491+
.filter(tlNode -> {
492+
Optional<TCompasICDHeader> tCompasICDHeader = PrivateUtils.extractCompasPrivate(tlNode, TCompasICDHeader.class);
493+
if (tCompasICDHeader.isPresent()) {
494+
return Objects.equals(tCompasFlow.getFlowSourceIEDType(), tCompasICDHeader.get().getIEDType())
495+
&& Objects.equals(tCompasFlow.getFlowIEDSystemVersioninstance(), tCompasICDHeader.get().getIEDSystemVersioninstance())
496+
&& Objects.equals(tCompasFlow.getFlowSourceIEDredundancy(), tCompasICDHeader.get().getIEDredundancy());
497+
} else {
498+
sclReportItems.add(SclReportItem.error("", ("The substation LNode with following attributes : IedName:%s / LdInst:%s / LnClass:%s / LnInst:%s " +
499+
"does not contain the needed (COMPAS - ICDHeader) private")
500+
.formatted(tlNode.getIedName(), tlNode.getLdInst(), tlNode.getLnClass().getFirst(), tlNode.getLnInst())));
501+
return false;
502+
}
503+
})
491504
.map(TLNode::getIedName)
492-
.filter(StringUtils::isNotBlank)
493-
.findFirst()
505+
.reduce(checkOnlyOneIed(tCompasFlow, tBay, sclReportItems))
494506
)
495507
.ifPresentOrElse(iedName -> {
496508
extRefService.getMatchingExtRefs(tlDevice, tCompasFlow).forEach(tExtRef -> tExtRef.setIedName(iedName));
@@ -503,6 +515,17 @@ public void updateIedNameBasedOnLnode(SCL scl) {
503515
)
504516
)
505517
);
518+
return sclReportItems;
519+
}
520+
521+
private static BinaryOperator<String> checkOnlyOneIed(TCompasFlow tCompasFlow, TBay tBay, List<SclReportItem> sclReportItems) {
522+
return (iedName1, iedName2) -> {
523+
sclReportItems.add(SclReportItem.error("",
524+
("Several LNode@IedName ('%s', '%s') are found in the bay '%s' for the following compas-flow attributes :" +
525+
" @FlowSourceIEDType '%s' @FlowSourceIEDredundancy '%s' @FlowIEDSystemVersioninstance '%s'").
526+
formatted(iedName1, iedName2, tBay.getName(), tCompasFlow.getFlowSourceIEDType(), tCompasFlow.getFlowSourceIEDredundancy(), tCompasFlow.getFlowIEDSystemVersioninstance())));
527+
return iedName1;
528+
};
506529
}
507530

508531
record TopoKey(String FlowNode, BigInteger FlowNodeOrder) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ public interface ExtRefEditor {
7272

7373
/**
7474
* Update compas:Flow.ExtRefiedName and ExtRef.iedName, based on Substation LNode iedName
75+
* @param scd SCL
76+
* @return list of encoutered errors
7577
*/
76-
void updateIedNameBasedOnLnode(SCL scd);
78+
List<SclReportItem> updateIedNameBasedOnLnode(SCL scd);
7779

7880
}

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ void manageBindingForLDEPF_when_DO_Mod_and_DA_stVal_NotFoundInDataTypeTemplate_s
595595
.extracting(SclReportItem::message, SclReportItem::xpath)
596596
.containsExactly(
597597
Tuple.tuple("DO@name=Mod/DA@name=stVal not found in DataTypeTemplate",
598-
"SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LDEPF\"]"),
598+
"SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LDEPF\"]"),
599599
Tuple.tuple("DO@name=Mod/DA@name=stVal not found in DataTypeTemplate",
600600
"SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LDEPF\"]"),
601601
Tuple.tuple("DO@name=Mod/DA@name=stVal not found in DataTypeTemplate",
@@ -865,7 +865,7 @@ void debindCompasFlowsAndExtRefsBasedOnVoltageLevel(String testCase, SCL scd, Tu
865865
.containsExactly(flow2);
866866
}
867867

868-
private static Stream<Arguments> provideFlowAndExtRefForDebinding(){
868+
private static Stream<Arguments> provideFlowAndExtRefForDebinding() {
869869
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-flow-debind/scd_extref_flow_debind_success.xml");
870870
SCL scdVoltageLevel0 = SclTestMarshaller.getSCLFromFile("/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_0.xml");
871871
SCL scdVoltageLevelUnknown = SclTestMarshaller.getSCLFromFile("/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_unknown.xml");
@@ -901,7 +901,7 @@ private TInputs findInputs(SCL scd) {
901901
}
902902

903903
@Test
904-
void updateIedNameBasedOnLnode_should_update_CompasFlow_and_ExtRef_iedName(){
904+
void updateIedNameBasedOnLnode_should_update_CompasFlow_and_ExtRef_iedName() {
905905
// Given
906906
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_based_on_lnode_success.xml");
907907
// When
@@ -914,7 +914,7 @@ void updateIedNameBasedOnLnode_should_update_CompasFlow_and_ExtRef_iedName(){
914914
}
915915

916916
@Test
917-
void updateIedNameBasedOnLnode_when_no_matching_lnode_should_clear_binding(){
917+
void updateIedNameBasedOnLnode_when_no_matching_lnode_should_clear_binding() {
918918
// Given
919919
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_based_on_lnode_success.xml");
920920
PrivateUtils.extractCompasPrivate(scd.getSubstation().get(0).getVoltageLevel().get(0).getBay().get(0), TCompasTopo.class).orElseThrow().setNode("99");
@@ -928,4 +928,31 @@ void updateIedNameBasedOnLnode_when_no_matching_lnode_should_clear_binding(){
928928
assertExtRefIsNotBound(findExtRef(scd, "IED_NAME1", "LD_INST11", "STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1"));
929929

930930
}
931+
932+
@Test
933+
void updateIedNameBasedOnLnode_when_several_ied_match_compasFlow_should_return_an_error() {
934+
// Given
935+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_based_on_lnode_several_ied_matching_compasFlow.xml");
936+
// When
937+
List<SclReportItem> result = extRefEditorService.updateIedNameBasedOnLnode(scd);
938+
// Then
939+
assertThat(result)
940+
.extracting(SclReportItem::isError, SclReportItem::message)
941+
.containsExactly(Tuple.tuple(true,
942+
"Several LNode@IedName ('IED_NAME2', 'IED_NAME3') are found in the bay 'BAY_1' for the following compas-flow attributes " +
943+
": @FlowSourceIEDType 'SCU' @FlowSourceIEDredundancy 'A' @FlowIEDSystemVersioninstance '1'"));
944+
}
945+
946+
@Test
947+
void updateIedNameBasedOnLnode_when_no_Compas_ICD_Header_should_return_an_error() {
948+
// Given
949+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_fails_no_Compas_ICDHeader.xml");
950+
// When
951+
List<SclReportItem> result = extRefEditorService.updateIedNameBasedOnLnode(scd);
952+
// Then
953+
assertThat(result)
954+
.extracting(SclReportItem::isError, SclReportItem::message)
955+
.containsExactly(Tuple.tuple(true,
956+
"The substation LNode with following attributes : IedName:IED_NAME2 / LdInst:LD_INST21 / LnClass:ANCR / LnInst:1 does not contain the needed (COMPAS - ICDHeader) private"));
957+
}
931958
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<!-- SPDX-FileCopyrightText: 2023 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+
<Header id="hId" version="2007" revision="B" toolID="COMPAS"/>
7+
<Substation name="SITE">
8+
<VoltageLevel nomFreq="50" numPhases="3" name="0">
9+
<Voltage unit="V" multiplier="k">0</Voltage>
10+
<Bay name="BAY_1">
11+
<Private type="COMPAS-Topo">
12+
<compas:Topo Node="101" NodeOrder="2" Direction="Down"/>
13+
</Private>
14+
<Private type="COMPAS-Bay">
15+
<compas:Bay BayCodif="TG00000001" UUID="9cd6f05b-1bbd-4ba3-86c5-41c99103e06d" Version="1"
16+
MainShortLabel="SITE1" SecondLabel="SITE-TGENE" NumBay="7" BayCount="1"/>
17+
</Private>
18+
<Function name="FUNCTION_1">
19+
<Private type="COMPAS-Function">
20+
<compas:Function UUID="8f4cda3f-828c-4006-9b87-2af96b19304b" Label="FUNCTION_1"/>
21+
</Private>
22+
<LNode iedName="IED_NAME2" ldInst="LD_INST21" lnClass="ANCR" lnInst="1">
23+
<Private type="COMPAS-ICDHeader">
24+
<compas:ICDHeader xmlns:compas="https://www.lfenergy.org/compas/extension/v1" ICDSystemVersionUUID="IED7949eaedc51747c9947e9152eb09e9cf" IEDType="SCU" IEDSubstationinstance="12" IEDSystemVersioninstance="1" BayLabel="4CBO.1" IEDName="PUYPE4CBO1BCU1" VendorName="Efacec" IEDredundancy="A" IEDmodel="TPU L500-3-1-F-3-B-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="3.07.004" headerId="BCU_CBO_v2" headerVersion="1.484" headerRevision="1"/>
25+
</Private>
26+
<Private type="COMPAS-LNodeStatus">on</Private>
27+
</LNode>
28+
</Function>
29+
<Function name="FUNCTION_2">
30+
<Private type="COMPAS-Function">
31+
<compas:Function UUID="8f4cda3f-828c-4006-9b87-2af96b19304b" Label="FUNCTION_2"/>
32+
</Private>
33+
<LNode iedName="IED_NAME3" ldInst="LD_INST21" lnClass="ANCR" lnInst="1">
34+
<Private type="COMPAS-ICDHeader">
35+
<compas:ICDHeader xmlns:compas="https://www.lfenergy.org/compas/extension/v1" ICDSystemVersionUUID="IED7949eaedc51747c9947e9152eb09e9cf" IEDType="SCU" IEDSubstationinstance="12" IEDSystemVersioninstance="1" BayLabel="4CBO.1" IEDName="PUYPE4CBO1BCU1" VendorName="Efacec" IEDredundancy="A" IEDmodel="TPU L500-3-1-F-3-B-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="3.07.004" headerId="BCU_CBO_v2" headerVersion="1.484" headerRevision="1"/>
36+
</Private>
37+
<Private type="COMPAS-LNodeStatus">on</Private>
38+
</LNode>
39+
</Function>
40+
</Bay>
41+
</VoltageLevel>
42+
</Substation>
43+
<IED name="IED_NAME1">
44+
<Private type="COMPAS-ICDHeader">
45+
<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"
46+
headerId="ARKENS-SV1120-HGAAA-EB5_SCU" headerVersion="1.2a" headerRevision="412995"/>
47+
</Private>
48+
<AccessPoint name="AP_NAME">
49+
<Server>
50+
<Authentication/>
51+
<LDevice inst="LD_INST11" ldName="IED_NAME1LD_INST11">
52+
<LN0 lnClass="LLN0" inst="" lnType="LNEX1">
53+
<DOI name="Mod">
54+
<DAI name="stVal">
55+
<Val>on</Val>
56+
</DAI>
57+
</DOI>
58+
<Inputs>
59+
<Private type="COMPAS-Flow">
60+
<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"
61+
FlowStatus="ACTIVE" FlowKind="BAY_INTERNAL" FlowSourceIEDType="SCU" FlowSourceIEDredundancy="A" FlowIEDSystemVersioninstance="1"/>
62+
</Private>
63+
<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"/>
64+
</Inputs>
65+
</LN0>
66+
</LDevice>
67+
</Server>
68+
</AccessPoint>
69+
</IED>
70+
<IED name="IED_NAME2">
71+
<Private type="COMPAS-ICDHeader">
72+
<compas:ICDHeader ICDSystemVersionUUID="System_Version_IED_NAME2" IEDType="SCU" 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"
73+
headerId="ARKENS-SV1120-HGAAA-EB5_SCU" headerVersion="1.2a" headerRevision="412995"/>
74+
</Private>
75+
<AccessPoint name="AP_NAME">
76+
<Server>
77+
<Authentication/>
78+
<LDevice inst="LD_INST21">
79+
<LN0 lnClass="LLN0" inst="" lnType="LNEX1">
80+
<DOI name="Mod">
81+
<DAI name="stVal">
82+
<Val>on</Val>
83+
</DAI>
84+
</DOI>
85+
</LN0>
86+
<LN lnClass="ANCR" inst="1" lnType="lnType"/>
87+
</LDevice>
88+
</Server>
89+
</AccessPoint>
90+
</IED>
91+
<IED name="IED_NAME3">
92+
<Private type="COMPAS-ICDHeader">
93+
<compas:ICDHeader ICDSystemVersionUUID="System_Version_IED_NAME2" IEDType="SCU" 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"
94+
headerId="ARKENS-SV1120-HGAAA-EB5_SCU" headerVersion="1.2a" headerRevision="412995"/>
95+
</Private>
96+
<AccessPoint name="AP_NAME">
97+
<Server>
98+
<Authentication/>
99+
<LDevice inst="LD_INST21">
100+
<LN0 lnClass="LLN0" inst="" lnType="LNEX1">
101+
<DOI name="Mod">
102+
<DAI name="stVal">
103+
<Val>on</Val>
104+
</DAI>
105+
</DOI>
106+
</LN0>
107+
<LN lnClass="ANCR" inst="1" lnType="lnType"/>
108+
</LDevice>
109+
</Server>
110+
</AccessPoint>
111+
</IED>
112+
<DataTypeTemplates>
113+
<LNodeType lnClass="LLN0" id="LNEX1">
114+
<DO name="Mod" type="DO1"/>
115+
</LNodeType>
116+
<LNodeType lnClass="ANCR" id="lnType">
117+
<DO name="DoName1" type="DO2"/>
118+
</LNodeType>
119+
<DOType cdc="ENC" id="DO1">
120+
<DA fc="ST" name="stVal" bType="Enum" type="BehaviourModeKind"/>
121+
</DOType>
122+
<DOType cdc="ENC" id="DO2">
123+
<DA fc="BL" name="daName1" bType="BOOLEAN"/>
124+
</DOType>
125+
<EnumType id="BehaviourModeKind">
126+
<EnumVal ord="1">on</EnumVal>
127+
<EnumVal ord="2">off</EnumVal>
128+
<EnumVal ord="3">test</EnumVal>
129+
</EnumType>
130+
</DataTypeTemplates>
131+
</SCL>

0 commit comments

Comments
 (0)