Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.lfenergy.compas.scl2007b4.model.TDO;
import org.lfenergy.compas.scl2007b4.model.TDOType;
Expand All @@ -20,7 +21,6 @@
import org.lfenergy.compas.sct.commons.util.Utils;

import java.util.*;
import java.util.stream.Collectors;

/**
* A representation of the model object
Expand All @@ -41,7 +41,7 @@
* <li>{@link LNodeTypeAdapter#getId() <em>Returns the value of the <b>id </b>attribute</em>}</li>
* <li>{@link LNodeTypeAdapter#getLNClass <em>Returns the value of the <b>lnClass </b>attribute</em>}</li>
* <li>{@link LNodeTypeAdapter#getDataAttributeRefs(DataAttributeRef)} <em>Returns <b>DataAttributeRef </b> list</em>}</li>
* <li>{@link LNodeTypeAdapter#getDataAttributeRefs(String)} <em>Returns <b>DataAttributeRef </b> list</em>}</li>
* <li>{@link LNodeTypeAdapter#getDataAttributeRef(String)} <em>Returns <b>DataAttributeRef </b> list</em>}</li>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bof

* </ul>
* <li>Checklist functions</li>
* <ul>
Expand Down Expand Up @@ -95,10 +95,8 @@ public boolean hasSameContentAs(TLNodeType tlNodeType) {
return false;
}

if (Objects.equals(
currentElem.getLnClass().toArray(new String[0]),
tlNodeType.getLnClass().toArray(new String[0])
) || !Objects.equals(currentElem.getIedType(), tlNodeType.getIedType())) {
if (Objects.equals(currentElem.getLnClass(), tlNodeType.getLnClass())
|| !Objects.equals(currentElem.getIedType(), tlNodeType.getIedType())) {
return false;
}

Expand All @@ -112,9 +110,9 @@ public boolean hasSameContentAs(TLNodeType tlNodeType) {
TDO inTDO = inTDOs.get(i);
TDO thisTDO = thisTDOs.get(i);
if (!thisTDO.getType().equals(inTDO.getType())
|| !thisTDO.getName().equals(inTDO.getName())
|| thisTDO.isTransient() != inTDO.isTransient()
|| !Objects.equals(thisTDO.getAccessControl(), inTDO.getAccessControl())) {
|| !thisTDO.getName().equals(inTDO.getName())
|| thisTDO.isTransient() != inTDO.isTransient()
|| !Objects.equals(thisTDO.getAccessControl(), inTDO.getAccessControl())) {
return false;
}
}
Expand All @@ -139,7 +137,7 @@ public boolean containsDOWithDOTypeId(String doTypeId) {
*/
public String getLNClass() {
if (!currentElem.getLnClass().isEmpty()) {
return currentElem.getLnClass().get(0);
return currentElem.getLnClass().getFirst();
}
return null;
}
Expand Down Expand Up @@ -195,7 +193,7 @@ public List<DataAttributeRef> getDataAttributeRefs(@NonNull DataAttributeRef fil
}

/**
* Return a list of summarized Data Attribute References beginning from given this LNodeType.
* Return a DataAttributeRef beginning from this LNodeType.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good

* The key point in the algorithm is to find where the DO/SDO part ends and the DA/BDA part begins.
* Once it is found, we can use the usual method DOTypeAdapter#getDataAttributeRefs to retrieve the DataAttributeRef
* For example, with input "Do1.da1", we want to find the "da1" which is the DA/BDA part and also the DOTypeAdapter of "Do1" (to call DOTypeAdapter#getDataAttributeRefs)
Expand All @@ -209,19 +207,19 @@ public List<DataAttributeRef> getDataAttributeRefs(@NonNull DataAttributeRef fil
*
* @param dataRef complete reference of Data Attribute including DO name, SDO names (optional), DA name, BDA names (optional).
* Ex: Do.sdo1.sdo2.da.bda1.bda2
* @return list of completed Data Attribute References beginning from this LNodeType.
* @return the Data Attribute References beginning from this LNodeType.
* @apiNote This method doesn't check relationship between DO/SDO and DA. Check should be done by caller
*/
public DataAttributeRef getDataAttributeRefs(@NonNull String dataRef) {
public Optional<DataAttributeRef> getDataAttributeRef(@NonNull String dataRef) {
LinkedList<String> dataRefList = new LinkedList<>(Arrays.asList(dataRef.split("\\.")));
if (dataRefList.size() < 2) {
throw new ScdException("Invalid data reference %s. At least DO name and DA name are required".formatted(dataRef));
if (dataRefList.size() < 2 || dataRefList.stream().anyMatch(StringUtils::isBlank)) {
throw new ScdException("Invalid data reference '%s'. At least DO name and DA name are required".formatted(dataRef));
}
// 1. Get the DO
String doName = dataRefList.remove();
return getDOAdapterByName(doName)
.flatMap(doAdapter -> getDataTypeTemplateAdapter().getDOTypeAdapterById(doAdapter.getType()))
.map(doTypeAdapter -> {
.flatMap(doTypeAdapter -> {
// 2. find the SDOs, if any
List<String> sdoAccumulator = new ArrayList<>();
List<String> daAccumulator = new ArrayList<>();
Expand Down Expand Up @@ -251,17 +249,14 @@ public DataAttributeRef getDataAttributeRefs(@NonNull String dataRef) {
rootDataAttributeRef.setDoName(doTypeName);
DataAttributeRef filter = new DataAttributeRef();
filter.setDaName(new DaTypeName(String.join(".", daAccumulator)));
List<DataAttributeRef> dataAttributeRefs = deepestDo.getDataAttributeRefs(rootDataAttributeRef, filter);
// We want exactly one result
if (dataAttributeRefs.size() > 1) {
throw new ScdException("Multiple Data Attribute found for this data reference %s in LNodeType.lnClass=%s, LNodeType.id=%s. Found DA : %s ".formatted(dataRef, getLNClass(), getId(), dataAttributeRefs.stream().map(DataAttributeRef::getDataAttributes).collect(Collectors.joining(", "))));
}
if (dataAttributeRefs.isEmpty() || !dataRef.equals(dataAttributeRefs.get(0).getDataAttributes())) {
return null;
}
return dataAttributeRefs.get(0);
}).orElseThrow(() ->
new ScdException("No Data Attribute found with this reference %s for LNodeType.lnClass=%s, LNodeType.id=%s ".formatted(dataRef, getLNClass(), getId())));
return deepestDo.getDataAttributeRefs(rootDataAttributeRef, filter)
.stream()
.filter(dataAttributeRef -> dataRef.equals(dataAttributeRef.getDataAttributes()))
.reduce((dar1, dar2) -> {
// 2 data attribute ref with the same name in the same LNodeType means the SCL is not valid, according to XSD
throw new ScdException("Multiple Data Attribute found for this data reference '%s' in LNodeType.lnClass=%s, LNodeType.id=%s. Found DA : '%s' ".formatted(dataRef, getLNClass(), getId(), dar1));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it tested ?

});
});
}

/**
Expand Down Expand Up @@ -353,7 +348,7 @@ public void checkDoAndDaTypeName(@NonNull DoTypeName doTypeName, @NonNull DaType
daTypeName.setFc(daAdapter.getCurrentElem().getFc());
DATypeAdapter daTypeAdapter = parentAdapter.getDATypeAdapterById(daAdapter.getType())
.orElseThrow(() -> new ScdException(String.format("Unknown DAType (%s) referenced by DA(%s)", daAdapter.getType(), daAdapter.getName()))
);
);
daTypeAdapter.check(daTypeName);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class DataTypeTemplateTestUtils {
public static final String SCD_DTT = "/dtt-test-schema-conf/scd_dtt_import_test.xml";
public static final String SCD_DTT_DIFF_CONTENT_SAME_ID = "/dtt-test-schema-conf/scd_dtt_import_sameid-diff-content-test.xml";
public static final String SCD_DTT_DO_SDO_DA_BDA = "/dtt-test-schema-conf/scd_dtt_do_sdo_da_bda.xml";
public static final String SCD_DTT_DO_MULTIPLE_DA = "/dtt-test-schema-conf/scd_dtt_do_multiple_da.xml";

public static DataTypeTemplateAdapter initDttAdapterFromFile(String fileName) {
SCL scd = SclTestMarshaller.getSCLFromFile(fileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got 2 test failing in my IDE :
org.lfenergy.compas.sct.commons.scl.dtt.LNodeTypeAdapterTest#testHasSameContentAs
and
org.lfenergy.compas.sct.commons.scl.dtt.DataTypeTemplateAdapterTest#testImportLNodeType
org.lfenergy.compas.sct.commons.scl.dtt.DataTypeTemplateAdapterTest#testImportDTT

import java.util.List;
import java.util.Optional;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
Expand Down Expand Up @@ -126,7 +127,7 @@ void containsDOWithDOTypeId_should_check_if_LNodeType_contains_DO_with_specific_
void testGetDataAttributeRefs() {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").get());
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
DataAttributeRef rootDataAttributeRef = new DataAttributeRef();
rootDataAttributeRef.setDoName(new DoTypeName("Op"));
DataAttributeRef filter = new DataAttributeRef();
Expand All @@ -151,63 +152,98 @@ void testGetDataAttributeRefs() {

@Test
@Tag("issue-321")
void testGetDataAttributeRefsString() {
void getDataAttributeRef_should_succeed() {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").get());
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
// When
var dataAttributeRefs = lNodeTypeAdapter.getDataAttributeRefs("StrVal.origin.origin.ctlVal");
Optional<DataAttributeRef> dataAttributeRefs = lNodeTypeAdapter.getDataAttributeRef("StrVal.origin.origin.ctlVal");
// Then
assertThat(dataAttributeRefs).isNotNull();
assertThat(dataAttributeRefs).isPresent();
}

@Test
void getDataAttributeRefs_should_find_DO_SDO_DA_and_BDA() {
void getDataAttributeRef_should_find_DO_SDO_DA_and_BDA() {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT_DO_SDO_DA_BDA);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
// When
DataAttributeRef dataAttributeRefs = lNodeTypeAdapter.getDataAttributeRefs("Do1.sdo1.sdo2.da2.bda1.bda2");
Optional<DataAttributeRef> optDataAttributeRef = lNodeTypeAdapter.getDataAttributeRef("Do1.sdo1.sdo2.da2.bda1.bda2");
// Then
assertThat(dataAttributeRefs).extracting(DataAttributeRef::getDoRef, DataAttributeRef::getDaRef)
assertThat(optDataAttributeRef).isPresent();
DataAttributeRef dataAttributeRef = optDataAttributeRef.get();
assertThat(dataAttributeRef).extracting(DataAttributeRef::getDoRef, DataAttributeRef::getDaRef)
.containsExactly("Do1.sdo1.sdo2", "da2.bda1.bda2");
assertThat(dataAttributeRefs.getDoName().getCdc()).isEqualTo(TPredefinedCDCEnum.WYE);
assertThat(dataAttributeRefs.getDaName()).extracting(DaTypeName::getBType, DaTypeName::getFc)
assertThat(dataAttributeRef.getDoName().getCdc()).isEqualTo(TPredefinedCDCEnum.WYE);
assertThat(dataAttributeRef.getDaName()).extracting(DaTypeName::getBType, DaTypeName::getFc)
.containsExactly(TPredefinedBasicTypeEnum.ENUM, TFCEnum.ST);
}

@Test
void getDataAttributeRefs_should_find_DO_and_DA() {
void getDataAttributeRef_should_find_DO_and_DA() {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT_DO_SDO_DA_BDA);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
// When
DataAttributeRef dataAttributeRefs = lNodeTypeAdapter.getDataAttributeRefs("Do1.da1");
Optional<DataAttributeRef> optDataAttributeRefs = lNodeTypeAdapter.getDataAttributeRef("Do1.da1");
// Then
assertThat(optDataAttributeRefs).isPresent();
DataAttributeRef dataAttributeRefs = optDataAttributeRefs.get();
assertThat(dataAttributeRefs).extracting(DataAttributeRef::getDoRef, DataAttributeRef::getDaRef)
.containsExactly("Do1", "da1");
assertThat(dataAttributeRefs.getDoName().getCdc()).isEqualTo(TPredefinedCDCEnum.WYE);
assertThat(dataAttributeRefs.getDaName()).extracting(DaTypeName::getBType, DaTypeName::getFc)
.containsExactly(TPredefinedBasicTypeEnum.BOOLEAN, TFCEnum.ST);
}


@Test
void getDataAttributeRef_when_same_da_in_multiple_sdo_should_find_the_correct_DO_DA() {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT_DO_MULTIPLE_DA);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
// When
Optional<DataAttributeRef> optDataAttributeRef = lNodeTypeAdapter.getDataAttributeRef("Do1.da1");
// Then
assertThat(optDataAttributeRef).isPresent();
DataAttributeRef dataAttributeRef = optDataAttributeRef.get();
assertThat(dataAttributeRef).extracting(DataAttributeRef::getDoRef, DataAttributeRef::getDaRef)
.containsExactly("Do1", "da1");
assertThat(dataAttributeRef.getDoName().getCdc()).isEqualTo(TPredefinedCDCEnum.WYE);
assertThat(dataAttributeRef.getDaName()).extracting(DaTypeName::getBType, DaTypeName::getFc)
.containsExactly(TPredefinedBasicTypeEnum.BOOLEAN, TFCEnum.ST);
}


@ParameterizedTest
@ValueSource(strings = {"", "malformed", "Do1", "InexistantDo.da1", "Do1.inexistantDa", "Do1.da1.inexistantBda", "Do1.sdo1.inexistantSdo.da2", "Do1.sdo1.sdo2.da2.bda1.inexistantBda"})
void getDataAttributeRefs_when_dataRef_not_found_should_throw_exception(String dataRef) {
@ValueSource(strings = {"", "malformed", "Do1", "Do1.", ".da1"})
void getDataAttributeRef_when_dataRef_parameter_malformed_should_throw_exception(String dataRef) {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT_DO_SDO_DA_BDA);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
// When & Then
assertThatThrownBy(() -> lNodeTypeAdapter.getDataAttributeRefs(dataRef))
assertThatThrownBy(() -> lNodeTypeAdapter.getDataAttributeRef(dataRef))
.isInstanceOf(ScdException.class);
}

@ParameterizedTest
@ValueSource(strings = {"InexistantDo.da1", "Do1.inexistantDa", "Do1.da1.inexistantBda", "Do1.sdo1.inexistantSdo.da2", "Do1.sdo1.sdo2.da2.bda1.inexistantBda"})
void getDataAttributeRef_when_dataRef_not_found_should_return_empty(String dataRef) {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT_DO_SDO_DA_BDA);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
// When & Then
Optional<DataAttributeRef> optDataAttributeRef = lNodeTypeAdapter.getDataAttributeRef(dataRef);
// Then
assertThat(optDataAttributeRef).isEmpty();
}

@Test
@Tag("issue-321")
void testCheck() {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").get());
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
DoTypeName doTypeName1 = new DoTypeName("");
DaTypeName daTypeName1 = new DaTypeName("");
// When Then
Expand Down Expand Up @@ -239,7 +275,7 @@ void getDataAttributeRefByDaName_should_return_list_of_DataAttributeRef() {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT_DIFF_CONTENT_SAME_ID);
DaTypeName daTypeName = new DaTypeName("antRef","origin.ctlVal");
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").get());
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
// When
List<DataAttributeRef> dataAttributeRefs = assertDoesNotThrow(()-> lNodeTypeAdapter.getDataAttributeRefByDaName(daTypeName));
// Then
Expand Down Expand Up @@ -271,7 +307,7 @@ void addPrivate_with_type_and_source_should_create_Private() {
void elementXPath_should_return_expected_xpath_value() {
// Given
DataTypeTemplateAdapter dttAdapter = initDttAdapterFromFile(SCD_DTT);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() ->dttAdapter.getLNodeTypeAdapterById("LN1").get());
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("LN1").orElseThrow());
// When
String result = lNodeTypeAdapter.elementXPath();
// Then
Expand Down Expand Up @@ -299,7 +335,7 @@ void findMatchingDOType_shouldFindOneDO() {
dttAdapter.getCurrentElem().getDOType().add(tdoType);

dttAdapter.getCurrentElem().getLNodeType().add(tlNodeType);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("ID").get());
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("ID").orElseThrow());

ExtRefSignalInfo signalInfo = new ExtRefSignalInfo();
signalInfo.setPDO("P_DO12");
Expand Down Expand Up @@ -335,7 +371,7 @@ void checkMatchingDOType_whenDOUnknown_shouldThrowException() {
dttAdapter.getCurrentElem().getDOType().add(tdoType);

dttAdapter.getCurrentElem().getLNodeType().add(tlNodeType);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("ID").get());
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("ID").orElseThrow());
//When Then
assertThatThrownBy(() -> lNodeTypeAdapter.findMatchingDOType(signalInfo))
.isInstanceOf(IllegalArgumentException.class)
Expand Down Expand Up @@ -368,7 +404,7 @@ void checkMatchingDOType_whenDONotReferenced_shouldThrowException() {
dttAdapter.getCurrentElem().getDOType().add(tdoType);

dttAdapter.getCurrentElem().getLNodeType().add(tlNodeType);
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("ID").get());
LNodeTypeAdapter lNodeTypeAdapter = assertDoesNotThrow(() -> dttAdapter.getLNodeTypeAdapterById("ID").orElseThrow());
//When Then
assertThatThrownBy(() -> lNodeTypeAdapter.findMatchingDOType(signalInfo))
.isInstanceOf(IllegalArgumentException.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!-- SPDX-FileCopyrightText: 2023 RTE FRANCE -->
<!-- -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
<Header id="HeaderID" version="version" revision="Revision" toolID="toolID"/>
<DataTypeTemplates>
<LNodeType id="LN1" lnClass="ANCR">
<DO name="Do1" type="DO1"/>
</LNodeType>
<DOType cdc="WYE" id="DO1">
<SDO name="sdo1" type="SDO1"/>
<SDO name="sdo2" type="SDO1"/>
<DA name="da1" bType="BOOLEAN" fc="ST"/>
<DA name="unused" bType="BOOLEAN" fc="ST"/>
</DOType>
<DOType cdc="WYE" id="SDO1">
<DA name="da1" bType="BOOLEAN" fc="ST"/>
<DA name="unused" bType="BOOLEAN" fc="ST"/>
</DOType>
</DataTypeTemplates>
</SCL>
Loading