Skip to content

Commit dc6db52

Browse files
committed
WIP
Signed-off-by: massifben <105049157+massifben@users.noreply.github.com>
1 parent 8e20791 commit dc6db52

File tree

10 files changed

+524
-288
lines changed

10 files changed

+524
-288
lines changed

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

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

77
import lombok.RequiredArgsConstructor;
8-
import org.lfenergy.compas.scl2007b4.model.SCL;
9-
import org.lfenergy.compas.scl2007b4.model.TLDevice;
8+
import org.lfenergy.compas.scl2007b4.model.*;
9+
import org.lfenergy.compas.sct.commons.DataTypeTemplatesService;
1010
import org.lfenergy.compas.sct.commons.LdeviceService;
1111
import org.lfenergy.compas.sct.commons.LnService;
12+
import org.lfenergy.compas.sct.commons.domain.DoLinkedToDaFilter;
1213
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
14+
import org.lfenergy.compas.sct.commons.util.CommonConstants;
15+
import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
1316

1417
import java.util.List;
18+
import java.util.Map;
19+
import java.util.Objects;
20+
import java.util.stream.Stream;
21+
22+
import static org.lfenergy.compas.sct.commons.util.CommonConstants.CTL_MODEL_DA_NAME;
23+
import static org.lfenergy.compas.sct.commons.util.CommonConstants.MOD_DO_NAME;
1524

1625
@RequiredArgsConstructor
1726
public class CtlModelService {
1827

28+
private static final DoLinkedToDaFilter DAI_CTL_MODEL = DoLinkedToDaFilter.from(MOD_DO_NAME, CTL_MODEL_DA_NAME);
29+
private static final Map<String, String> MOD_STVAL_TO_CTL_MODEL = Map.of(
30+
"on", "direct-with-enhanced-security",
31+
"off", "status-only");
32+
private final DataTypeTemplatesService dataTypeTemplatesService;
1933
private final LdeviceService ldeviceService;
2034
private final LnService lnService;
2135

2236
public List<SclReportItem> update(SCL scl) { // BMA fix name
23-
scl.getIED().stream()
24-
.flatMap(ldeviceService::getLdevices)
25-
.filter(TLDevice::isSetLN0)
26-
.map(TLDevice::getLN0)
27-
.map(ln0 -> {
28-
lnService.getDaiModStVal()
37+
return scl.getIED().stream()
38+
.flatMap(tied -> ldeviceService.getLdevices(tied)
39+
.filter(TLDevice::isSetLN0)
40+
.flatMap(tlDevice -> {
41+
LN0 ln0 = tlDevice.getLN0();
42+
String modStValValue = lnService.getDaiModStVal(ln0)
43+
.flatMap(modStVal -> modStVal.getVal().stream().map(TVal::getValue).findFirst())
44+
.orElse("");
45+
String newCtlModelVal = MOD_STVAL_TO_CTL_MODEL.get(modStValValue);
46+
if (newCtlModelVal == null) {
47+
return Stream.of(SclReportItem.error(lnPath(tied, tlDevice, ln0), "LN0 DAI %s value should be in [on, off], but got : '%s'".formatted(CommonConstants.DAI_FILTER_MOD_STVAL, modStValValue)));
48+
}
49+
TDAI ctlModel = lnService.getDOAndDAInstances(ln0, DAI_CTL_MODEL).orElse(null);
50+
if (ctlModel == null) {
51+
return Stream.of(SclReportItem.error(lnPath(tied, tlDevice, ln0), "LN0 is missing DAI " + DAI_CTL_MODEL));
52+
}
53+
List<String> ctlModelEnumValues = dataTypeTemplatesService.getEnumValues(scl.getDataTypeTemplates(), ln0.getLnType(), DAI_CTL_MODEL).toList();
54+
if (!ctlModelEnumValues.contains(newCtlModelVal)) {
55+
return Stream.of(SclReportItem.error(lnPath(tied, tlDevice, ln0), "Cannot set DAI Mod.ctlModel to '%s' because value is not in EnumType %s".formatted(newCtlModelVal, ctlModelEnumValues)));
56+
}
57+
ctlModel.getVal().clear();
58+
ctlModel.getVal().add(SclConstructorHelper.newVal(newCtlModelVal));
59+
return tlDevice.getLN().stream().map(tln -> updateLn(tln, modStValValue)); // no error
60+
}))
61+
.filter(Objects::nonNull)
62+
.toList();
63+
}
2964

30-
})
65+
private SclReportItem updateLn(TLN tln, String ln0ModStValValue) {
66+
String modStValValue = lnService.getDaiModStVal(tln)
67+
.flatMap(modStVal -> modStVal.getVal().stream().map(TVal::getValue).findFirst())
68+
.orElse(ln0ModStValValue);
69+
return null;
70+
}
3171

32-
return List.of();
72+
private static String lnPath(TIED tied, TLDevice tlDevice, TAnyLN anyLN) {
73+
String lnId = switch (anyLN) {
74+
case TLN0 ignored -> "LN0";
75+
case TLN tln -> "LN[lnClass=%s,inst=%s,prefix=%s]".formatted(tln.getLnClass().getFirst(), tln.getInst(), tln.getPrefix());
76+
default -> throw new IllegalStateException("Unexpected value: " + anyLN);
77+
};
78+
return "IED(%s)/LDevice(%s)/%s".formatted(tied.getName(), tlDevice.getInst(), lnId);
3379
}
80+
3481
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,20 @@ public Optional<DoLinkedToDa> findDoLinkedToDa(TDataTypeTemplates dtt, String lN
149149
));
150150
}
151151

152+
public Stream<String> getEnumValues(TDataTypeTemplates dataTypeTemplates, String lnType, DoLinkedToDaFilter doLinkedToDaFilter) {
153+
return findDoLinkedToDa(dataTypeTemplates, lnType, doLinkedToDaFilter)
154+
.map(DoLinkedToDa::dataAttribute)
155+
.filter(dataAttribute -> TPredefinedBasicTypeEnum.ENUM.equals(dataAttribute.getBType()))
156+
.map(DataAttribute::getType)
157+
.flatMap(enumId ->
158+
dataTypeTemplates.getEnumType().stream()
159+
.filter(tEnumType -> tEnumType.getId().equals(enumId))
160+
.findFirst())
161+
.stream()
162+
.flatMap(tEnumType -> tEnumType.getEnumVal().stream())
163+
.map(TEnumVal::getValue);
164+
}
165+
152166
private Optional<TDAType> getDATypeByDaName(TDataTypeTemplates dtt, TDOType tdoType, String daName) {
153167
return sdoOrDAService.findDA(tdoType, tda -> tda.getName().equals(daName))
154168
.flatMap(tda -> daTypeService.findDaType(dtt, tda.getType()));

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

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

77
import lombok.RequiredArgsConstructor;
8-
import org.lfenergy.compas.scl2007b4.model.*;
9-
import org.lfenergy.compas.sct.commons.domain.DataAttribute;
10-
import org.lfenergy.compas.sct.commons.domain.DoLinkedToDa;
11-
import org.lfenergy.compas.sct.commons.domain.DoLinkedToDaFilter;
8+
import org.lfenergy.compas.scl2007b4.model.SCL;
9+
import org.lfenergy.compas.scl2007b4.model.TAnyLN;
10+
import org.lfenergy.compas.scl2007b4.model.TDAI;
11+
import org.lfenergy.compas.scl2007b4.model.TLNode;
1212
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
1313
import org.lfenergy.compas.sct.commons.util.CommonConstants;
1414
import org.lfenergy.compas.sct.commons.util.PrivateUtils;
@@ -17,7 +17,6 @@
1717
import java.util.List;
1818
import java.util.Objects;
1919
import java.util.Optional;
20-
import java.util.stream.Stream;
2120

2221
@RequiredArgsConstructor
2322
public class LNodeStatusService {
@@ -61,7 +60,7 @@ private SclReportItem updateSingleLnModStValBasedOnLNodeStatus(SCL scl, TLNode t
6160
if (daiModStVal == null) {
6261
return null; // do nothing if DAI Mod.stVal is missing
6362
}
64-
List<String> modStValEnumValues = getModStValEnumValues(scl.getDataTypeTemplates(), anyLn.getLnType()).toList();
63+
List<String> modStValEnumValues = dataTypeTemplatesService.getEnumValues(scl.getDataTypeTemplates(), anyLn.getLnType(), CommonConstants.DAI_FILTER_MOD_STVAL).toList();
6564
if (!modStValEnumValues.contains(lNodeLNS)) {
6665
return SclReportItem.error(lnPath(tlNode), "Cannot set DAI Mod.stVal to '%s' because value is not in EnumType %s".formatted(lNodeLNS, modStValEnumValues));
6766
}
@@ -78,20 +77,6 @@ private static String lNodePath(TLNode tlNode) {
7877
return "LNode(iedName=%s, ldInst=%s, lnClass=%s, lnInst=%s, prefix=%s)".formatted(tlNode.getIedName(), tlNode.getLdInst(), tlNode.getLnClass().getFirst(), tlNode.getLnInst(), tlNode.getPrefix());
7978
}
8079

81-
private Stream<String> getModStValEnumValues(TDataTypeTemplates dataTypeTemplates, String lnType) {
82-
return dataTypeTemplatesService.findDoLinkedToDa(dataTypeTemplates, lnType, DoLinkedToDaFilter.from(CommonConstants.MOD_DO_NAME, CommonConstants.STVAL_DA_NAME))
83-
.map(DoLinkedToDa::dataAttribute)
84-
.filter(dataAttribute -> TPredefinedBasicTypeEnum.ENUM.equals(dataAttribute.getBType()))
85-
.map(DataAttribute::getType)
86-
.flatMap(enumId ->
87-
dataTypeTemplates.getEnumType().stream()
88-
.filter(tEnumType -> tEnumType.getId().equals(enumId))
89-
.findFirst())
90-
.stream()
91-
.flatMap(tEnumType -> tEnumType.getEnumVal().stream())
92-
.map(TEnumVal::getValue);
93-
}
94-
9580
private Optional<TAnyLN> findLn(SCL scl, String iedName, String ldInst, String lnClass, String lnInst, String prefix) {
9681
return scl.getIED().stream()
9782
.filter(tied -> iedName.equals(tied.getName()))

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

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
import java.util.function.Predicate;
1818
import java.util.stream.Stream;
1919

20-
import static org.lfenergy.compas.sct.commons.util.CommonConstants.MOD_DO_NAME;
21-
import static org.lfenergy.compas.sct.commons.util.CommonConstants.STVAL_DA_NAME;
20+
import static org.lfenergy.compas.sct.commons.util.CommonConstants.DAI_FILTER_MOD_STVAL;
2221
import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newVal;
2322

2423
@Slf4j
@@ -73,15 +72,7 @@ public Optional<ActiveStatus> getDaiModStValValue(TAnyLN tAnyLN) {
7372
}
7473

7574
public Optional<TDAI> getDaiModStVal(TAnyLN tAnyLN) {
76-
return tAnyLN
77-
.getDOI()
78-
.stream()
79-
.filter(tdoi -> MOD_DO_NAME.equals(tdoi.getName()))
80-
.flatMap(tdoi -> tdoi.getSDIOrDAI().stream())
81-
.filter(TDAI.class::isInstance)
82-
.map(TDAI.class::cast)
83-
.filter(tdai -> STVAL_DA_NAME.equals(tdai.getName()))
84-
.findFirst();
75+
return getDOAndDAInstances(tAnyLN, DAI_FILTER_MOD_STVAL);
8576
}
8677

8778
public Stream<TAnyLN> getActiveLns(TLDevice tlDevice) {

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/domain/DoLinkedToDaFilter.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,37 @@ public DoLinkedToDaFilter(String doName, List<String> sdoNames, String daName, L
1919
this.daName = StringUtils.isBlank(daName) ? null : daName;
2020
this.bdaNames = bdaNames == null ? Collections.emptyList() : List.copyOf(bdaNames);
2121
}
22-
public DoLinkedToDaFilter(){
23-
this(null,null,null,null);
22+
23+
public DoLinkedToDaFilter() {
24+
this(null, null, null, null);
2425
}
2526

2627
public static DoLinkedToDaFilter from(String doNames, String daNames) {
2728
String doName = null;
2829
List<String> sdoNames = null;
2930
String daName = null;
3031
List<String> bdaNames = null;
31-
if (StringUtils.isNotBlank(doNames)){
32+
if (StringUtils.isNotBlank(doNames)) {
3233
doName = doNames.split("\\.")[0];
3334
sdoNames = Arrays.stream(doNames.split("\\.")).skip(1).toList();
3435
}
35-
if (StringUtils.isNotBlank(daNames)){
36+
if (StringUtils.isNotBlank(daNames)) {
3637
daName = daNames.split("\\.")[0];
3738
bdaNames = Arrays.stream(daNames.split("\\.")).skip(1).toList();
3839
}
3940
return new DoLinkedToDaFilter(doName, sdoNames, daName, bdaNames);
4041
}
4142

42-
public String getDoRef() {
43-
return doName + (sdoNames().isEmpty() ? StringUtils.EMPTY : "." + String.join(".", sdoNames()));
44-
}
43+
public String getDoRef() {
44+
return doName + (sdoNames().isEmpty() ? StringUtils.EMPTY : "." + String.join(".", sdoNames()));
45+
}
4546

46-
public String getDaRef() {
47-
return daName + (bdaNames().isEmpty() ? StringUtils.EMPTY : "." + String.join(".", bdaNames()));
48-
}
47+
public String getDaRef() {
48+
return daName + (bdaNames().isEmpty() ? StringUtils.EMPTY : "." + String.join(".", bdaNames()));
49+
}
4950

51+
@Override
52+
public String toString() {
53+
return getDoRef() + "." + getDaRef();
54+
}
5055
}

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

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

77

8+
import org.lfenergy.compas.sct.commons.domain.DoLinkedToDaFilter;
9+
10+
import java.util.List;
11+
812
/**
913
* A representation of constants used in application
1014
*/
@@ -18,6 +22,8 @@ public final class CommonConstants {
1822
public static final String BEHAVIOUR_DO_NAME = "Beh";
1923
public static final String MOD_DO_NAME = "Mod";
2024
public static final String STVAL_DA_NAME = "stVal";
25+
public static final DoLinkedToDaFilter DAI_FILTER_MOD_STVAL = new DoLinkedToDaFilter(MOD_DO_NAME, List.of(), STVAL_DA_NAME, List.of());
26+
public static final String CTL_MODEL_DA_NAME = "ctlModel";
2127
public static final String ATTRIBUTE_VALUE_SEPARATOR = "_";
2228
public static final String CONTROLBLOCK_NAME_PREFIX = "CB" + ATTRIBUTE_VALUE_SEPARATOR;
2329
public static final String DATASET_NAME_PREFIX = "DS" + ATTRIBUTE_VALUE_SEPARATOR;

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

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

77
import org.junit.jupiter.api.BeforeEach;
8+
import org.junit.jupiter.api.Test;
89
import org.junit.jupiter.params.ParameterizedTest;
910
import org.junit.jupiter.params.provider.Arguments;
1011
import org.junit.jupiter.params.provider.MethodSource;
12+
import org.junit.jupiter.params.provider.ValueSource;
1113
import org.lfenergy.compas.scl2007b4.model.SCL;
14+
import org.lfenergy.compas.scl2007b4.model.TDAI;
15+
import org.lfenergy.compas.sct.commons.DataTypeTemplatesService;
16+
import org.lfenergy.compas.sct.commons.LdeviceService;
17+
import org.lfenergy.compas.sct.commons.LnService;
1218
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
1319
import org.lfenergy.compas.sct.commons.testhelpers.SclHelper;
1420
import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
21+
import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
1522

1623
import java.util.List;
1724
import java.util.stream.Stream;
1825

1926
import static org.assertj.core.api.Assertions.assertThat;
2027
import static org.junit.jupiter.api.Named.named;
2128
import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findDai;
29+
import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findDoi;
2230

2331
class CtlModelServiceTest {
2432

2533
private CtlModelService ctlModelService;
2634

2735
@BeforeEach
2836
void setUp() {
29-
ctlModelService = new CtlModelService();
37+
ctlModelService = new CtlModelService(new DataTypeTemplatesService(), new LdeviceService(), new LnService());
3038
}
3139

3240
@ParameterizedTest
3341
@MethodSource("provideUpdateModCtlModel")
34-
void update_should_succeed(String ldInst, String lnClass, String lnInst, String expected) {
42+
void update_should_succeed(String ldInst, String expected) {
3543
// Given
36-
SCL scl = SclTestMarshaller.getSCLFromFile("/scl-ctlmodel/ctlmodel.scd");
44+
SCL scl = SclTestMarshaller.getSCLFromFile("/scl-ctlmodel/ctlmodel_ln0.scd");
3745
// When
3846
List<SclReportItem> sclReportItems = ctlModelService.update(scl);
3947
// Then
4048
assertThat(sclReportItems).isEmpty();
41-
assertThat(findDai(scl, "IED_NAME_1", ldInst, lnClass, lnInst, "", "Mod", "ctlModel"))
49+
assertThat(findDai(scl, "IED_NAME_1", ldInst, "LLN0", "", "", "Mod", "ctlModel"))
4250
.map(SclHelper::getValue)
4351
.hasValue(expected);
4452
}
4553

4654
public static Stream<Arguments> provideUpdateModCtlModel() {
4755
return Stream.of(
4856
// Tests on LN0
49-
Arguments.of(named("LN0 'on' to set to 'direct-with-enhanced-security'", "LDEVICE_1"), "LLN0", "", "direct-with-enhanced-security"),
50-
Arguments.of(named("LN0 'off' to set to 'status-only'", "LDEVICE_2"), "LLN0", "", "status-only")
57+
Arguments.of(named("LN0 'on' to set to 'direct-with-enhanced-security'", "LDEVICE_1"), "direct-with-enhanced-security"),
58+
Arguments.of(named("LN0 'off' to set to 'status-only'", "LDEVICE_2"), "status-only")
5159
);
5260
}
5361

62+
@Test
63+
void update_when_ln0_ModStVal_is_missing_should_return_error() {
64+
// Given
65+
SCL scl = SclTestMarshaller.getSCLFromFile("/scl-ctlmodel/ctlmodel_ln0.scd");
66+
findDoi(scl, "IED_NAME_1", "LDEVICE_1", "LLN0", "", "", "Mod").getSDIOrDAI()
67+
.removeIf(tUnNaming -> tUnNaming instanceof TDAI tdai && "stVal".equals(tdai.getName()));
68+
// When
69+
List<SclReportItem> sclReportItems = ctlModelService.update(scl);
70+
// Then
71+
assertThat(sclReportItems)
72+
.hasSize(1)
73+
.first().isEqualTo(SclReportItem.error("IED(IED_NAME_1)/LDevice(LDEVICE_1)/LN0",
74+
"LN0 DAI Mod.stVal value should be in [on, off], but got : ''"));
75+
}
76+
77+
@ParameterizedTest
78+
@ValueSource(strings = {"", "hello world"})
79+
void update_when_invalid_ln0_ModStVal_should_return_error(String modStValValue) {
80+
// Given
81+
SCL scl = SclTestMarshaller.getSCLFromFile("/scl-ctlmodel/ctlmodel_ln0.scd");
82+
TDAI modStVal = findDai(scl, "IED_NAME_1", "LDEVICE_1", "LLN0", "", "", "Mod", "stVal").orElseThrow();
83+
modStVal.getVal().clear();
84+
modStVal.getVal().add(SclConstructorHelper.newVal(modStValValue));
85+
// When
86+
List<SclReportItem> sclReportItems = ctlModelService.update(scl);
87+
// Then
88+
assertThat(sclReportItems)
89+
.hasSize(1)
90+
.first().isEqualTo(SclReportItem.error("IED(IED_NAME_1)/LDevice(LDEVICE_1)/LN0",
91+
"LN0 DAI Mod.stVal value should be in [on, off], but got : '" + modStValValue + "'"));
92+
}
93+
94+
@Test
95+
void update_when_ln0_ctlModel_is_missing_should_return_error() {
96+
// Given
97+
SCL scl = SclTestMarshaller.getSCLFromFile("/scl-ctlmodel/ctlmodel_ln0.scd");
98+
findDoi(scl, "IED_NAME_1", "LDEVICE_1", "LLN0", "", "", "Mod").getSDIOrDAI()
99+
.removeIf(tUnNaming -> tUnNaming instanceof TDAI tdai && "ctlModel".equals(tdai.getName()));
100+
// When
101+
List<SclReportItem> sclReportItems = ctlModelService.update(scl);
102+
// Then
103+
assertThat(sclReportItems)
104+
.hasSize(1)
105+
.first().isEqualTo(SclReportItem.error("IED(IED_NAME_1)/LDevice(LDEVICE_1)/LN0",
106+
"LN0 is missing DAI Mod.ctlModel"));
107+
}
108+
109+
@Test
110+
void update_when_ln0_ctlModel_venum_does_not_allow_value_should_return_error() {
111+
// Given
112+
SCL scl = SclTestMarshaller.getSCLFromFile("/scl-ctlmodel/ctlmodel_ln0.scd");
113+
scl.getDataTypeTemplates().getEnumType().getLast().getEnumVal()
114+
.forEach(tEnumVal -> tEnumVal.setValue(tEnumVal.getValue() + "_hello_world"));
115+
// When
116+
List<SclReportItem> sclReportItems = ctlModelService.update(scl);
117+
// Then
118+
assertThat(sclReportItems).containsExactlyInAnyOrder(
119+
SclReportItem.error("IED(IED_NAME_1)/LDevice(LDEVICE_1)/LN0", "Cannot set DAI Mod.ctlModel to 'direct-with-enhanced-security' because value is not in EnumType [status-only_hello_world, direct-with-enhanced-security_hello_world]"),
120+
SclReportItem.error("IED(IED_NAME_1)/LDevice(LDEVICE_2)/LN0", "Cannot set DAI Mod.ctlModel to 'status-only' because value is not in EnumType [status-only_hello_world, direct-with-enhanced-security_hello_world]"));
121+
}
54122

55123
}

0 commit comments

Comments
 (0)