diff --git a/pom.xml b/pom.xml
index caaaded0e..c1c3695ad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -37,7 +37,7 @@
sct-coverage/**
../sct-coverage/target/site/jacoco-aggregate/jacoco.xml
${basedir}/${aggregate.report.dir}
- 0.12.0
+ 0.13.0
0.0.4
3.4.1
3.2.1
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettings.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettings.java
new file mode 100644
index 000000000..1755d29f9
--- /dev/null
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettings.java
@@ -0,0 +1,62 @@
+// SPDX-FileCopyrightText: 2023 RTE FRANCE
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfenergy.compas.sct.commons.dto;
+
+import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec;
+import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter;
+
+/**
+ * This interface has a single method which provides network settings for a ControlBlock.
+ * These are used to create:
+ * - the Communication/SubNetwork/ConnectedAP/GSE element, which is the network configuration of a GSEControl block
+ * - the Communication/SubNetwork/ConnectedAP/SMV element, which is the network configuration of a SampledValueControl block
+ * It is a FunctionalInterface, so it can be implemented with a lambda expression.
+ *
+ * @see org.lfenergy.compas.sct.commons.util.ControlBlockNetworkSettingsCsvHelper
+ */
+@FunctionalInterface
+public interface ControlBlockNetworkSettings {
+
+ /**
+ * This method provides a vlanId, vlanPriority, minTime, maxTime for this ControlBlock.
+ * vlanPriority will be ignored when vlanId is null.
+ *
+ * @param controlBlockAdapter ControlBlock for which we want to configure the communication section
+ * @return network settings : All fields are optional (meaning fields can be null).
+ * When the return value itself is null, the communication section will not be configured for this ControlBlock.
+ */
+ Settings getNetworkSettings(ControlBlockAdapter controlBlockAdapter);
+
+ /**
+ * Network settings for ControlBlock communication
+ *
+ * @param vlanId id of the vlan
+ * @param vlanPriority priority for the vlan
+ * @param minTime minTime for GSE communication element
+ * @param maxTime maxTime for GSE communication element
+ */
+ record Settings(Integer vlanId, Byte vlanPriority, TDurationInMilliSec minTime, TDurationInMilliSec maxTime) {
+ }
+
+ /**
+ * NetworkRanges for GSEControl and SampledValueControl
+ *
+ * @param gse NetworkRanges for GSEControl
+ * @param sampledValue NetworkRanges for SampledValueControl
+ */
+ record RangesPerCbType(NetworkRanges gse, NetworkRanges sampledValue) {
+ }
+
+ /**
+ * Range of APPID and range of MAC-Address
+ *
+ * @param appIdStart range start for APPID (inclusive)
+ * @param appIdEnd range end for APPID (inclusive)
+ * @param macAddressStart range start for MAC-Addresses (inclusive). Ex: "01-0C-CD-01-00-00"
+ * @param macAddressEnd range end for MAC-Addresses (inclusive). Ex: "01-0C-CD-01-01-FF"
+ */
+ record NetworkRanges(long appIdStart, long appIdEnd, String macAddressStart, String macAddressEnd) {
+ }
+}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java
index 6e6f0f9f2..b4740a201 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java
@@ -9,24 +9,31 @@
import org.lfenergy.compas.scl2007b4.model.SCL;
import org.lfenergy.compas.scl2007b4.model.TCompasICDHeader;
import org.lfenergy.compas.scl2007b4.model.TIED;
+import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings;
+import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.Settings;
import org.lfenergy.compas.sct.commons.dto.SclReport;
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
import org.lfenergy.compas.sct.commons.exception.ScdException;
+import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.LDeviceAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.LN0Adapter;
+import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
import org.lfenergy.compas.sct.commons.util.PrivateEnum;
+import org.lfenergy.compas.sct.commons.util.Utils;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.NetworkRanges;
+import static org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.RangesPerCbType;
+
@UtilityClass
public class ExtRefService {
private static final String MESSAGE_MISSING_IED_NAME_PARAMETER = "IED.name parameter is missing";
- private static final String CLIENT_IED_NAME = "The Client IED ";
/**
* Updates iedName attribute of all ExtRefs in the Scd.
@@ -40,21 +47,21 @@ public static SclReport updateAllExtRefIedNames(SCL scd) {
return new SclReport(sclRootAdapter, iedErrors);
}
Map icdSystemVersionToIed = sclRootAdapter.streamIEDAdapters()
- .collect(Collectors.toMap(
- iedAdapter -> PrivateService.extractCompasPrivate(iedAdapter.getCurrentElem(), TCompasICDHeader.class)
- .map(TCompasICDHeader::getICDSystemVersionUUID)
- .orElseThrow(), // Value presence is checked by method validateIed called above
- Function.identity()
- ));
+ .collect(Collectors.toMap(
+ iedAdapter -> iedAdapter.getCompasICDHeader()
+ .map(TCompasICDHeader::getICDSystemVersionUUID)
+ .orElseThrow(), // Value presence is checked by method validateIed called above
+ Function.identity()
+ ));
List extRefErrors = sclRootAdapter.streamIEDAdapters()
- .flatMap(IEDAdapter::streamLDeviceAdapters)
- .filter(LDeviceAdapter::hasLN0)
- .map(LDeviceAdapter::getLN0Adapter)
- .filter(LN0Adapter::hasInputs)
- .map(LN0Adapter::getInputsAdapter)
- .map(inputsAdapter -> inputsAdapter.updateAllExtRefIedNames(icdSystemVersionToIed))
- .flatMap(List::stream).toList();
+ .flatMap(IEDAdapter::streamLDeviceAdapters)
+ .filter(LDeviceAdapter::hasLN0)
+ .map(LDeviceAdapter::getLN0Adapter)
+ .filter(LN0Adapter::hasInputs)
+ .map(LN0Adapter::getInputsAdapter)
+ .map(inputsAdapter -> inputsAdapter.updateAllExtRefIedNames(icdSystemVersionToIed))
+ .flatMap(List::stream).toList();
return new SclReport(sclRootAdapter, extRefErrors);
}
@@ -67,46 +74,59 @@ private static List validateIed(SclRootAdapter sclRootAdapter) {
private static List checkIedCompasIcdHeaderAttributes(SclRootAdapter sclRootAdapter) {
return sclRootAdapter.streamIEDAdapters()
- .map(iedAdapter -> {
- Optional compasPrivate = PrivateService.extractCompasPrivate(iedAdapter.getCurrentElem(), TCompasICDHeader.class);
- if (compasPrivate.isEmpty()) {
- return iedAdapter.buildFatalReportItem(String.format("IED has no Private %s element", PrivateEnum.COMPAS_ICDHEADER.getPrivateType()));
- }
- if (StringUtils.isBlank(compasPrivate.get().getICDSystemVersionUUID())
- || StringUtils.isBlank(compasPrivate.get().getIEDName())) {
- return iedAdapter.buildFatalReportItem(String.format("IED private %s as no icdSystemVersionUUID or iedName attribute",
- PrivateEnum.COMPAS_ICDHEADER.getPrivateType()));
- }
- return null;
- }
- ).filter(Objects::nonNull)
- .toList();
+ .map(iedAdapter -> {
+ Optional compasPrivate = iedAdapter.getCompasICDHeader();
+ if (compasPrivate.isEmpty()) {
+ return iedAdapter.buildFatalReportItem(String.format("IED has no Private %s element", PrivateEnum.COMPAS_ICDHEADER.getPrivateType()));
+ }
+ if (StringUtils.isBlank(compasPrivate.get().getICDSystemVersionUUID())
+ || StringUtils.isBlank(compasPrivate.get().getIEDName())) {
+ return iedAdapter.buildFatalReportItem(String.format("IED private %s as no icdSystemVersionUUID or iedName attribute",
+ PrivateEnum.COMPAS_ICDHEADER.getPrivateType()));
+ }
+ return null;
+ }
+ ).filter(Objects::nonNull)
+ .toList();
}
private static List checkIedUnityOfIcdSystemVersionUuid(SclRootAdapter sclRootAdapter) {
Map> systemVersionToIedList = sclRootAdapter.getCurrentElem().getIED().stream()
- .collect(Collectors.groupingBy(ied -> PrivateService.extractCompasPrivate(ied, TCompasICDHeader.class)
- .map(TCompasICDHeader::getICDSystemVersionUUID)
- .orElse("")));
+ .collect(Collectors.groupingBy(ied -> PrivateService.extractCompasPrivate(ied, TCompasICDHeader.class)
+ .map(TCompasICDHeader::getICDSystemVersionUUID)
+ .orElse("")));
return systemVersionToIedList.entrySet().stream()
- .filter(entry -> StringUtils.isNotBlank(entry.getKey()))
- .filter(entry -> entry.getValue().size() > 1)
- .map(entry -> SclReportItem.fatal(entry.getValue().stream()
- .map(tied -> new IEDAdapter(sclRootAdapter, tied))
- .map(IEDAdapter::getXPath)
- .collect(Collectors.joining(", ")),
- "/IED/Private/compas:ICDHeader[@ICDSystemVersionUUID] must be unique" +
- " but the same ICDSystemVersionUUID was found on several IED."))
- .toList();
+ .filter(entry -> StringUtils.isNotBlank(entry.getKey()))
+ .filter(entry -> entry.getValue().size() > 1)
+ .map(entry -> SclReportItem.fatal(entry.getValue().stream()
+ .map(tied -> new IEDAdapter(sclRootAdapter, tied))
+ .map(IEDAdapter::getXPath)
+ .collect(Collectors.joining(", ")),
+ "/IED/Private/compas:ICDHeader[@ICDSystemVersionUUID] must be unique" +
+ " but the same ICDSystemVersionUUID was found on several IED."))
+ .toList();
}
+ /**
+ * Create All DataSet and ControlBlock in the SCL based on the ExtRef
+ *
+ * @param scd input SCD object. It could be modified by adding new DataSet and ControlBlocks
+ * @return a report with all errors encountered
+ */
public static SclReport createDataSetAndControlBlocks(SCL scd) {
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
Stream lDeviceAdapters = sclRootAdapter.streamIEDAdapters().flatMap(IEDAdapter::streamLDeviceAdapters);
return createDataSetAndControlBlocks(sclRootAdapter, lDeviceAdapters);
}
+ /**
+ * Create All DataSet and ControlBlock for the ExtRef in given IED
+ *
+ * @param scd input SCD object. The object will be modified with the new DataSet and ControlBlocks
+ * @param targetIedName the name of the IED where the ExtRef are
+ * @return a report with all the errors encountered
+ */
public static SclReport createDataSetAndControlBlocks(SCL scd, String targetIedName) {
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(targetIedName);
@@ -114,6 +134,14 @@ public static SclReport createDataSetAndControlBlocks(SCL scd, String targetIedN
}
+ /**
+ * Create All DataSet and ControlBlock for the ExtRef in this LDevice
+ *
+ * @param scd input SCD object. The object will be modified with the new DataSet and ControlBlocks
+ * @param targetIedName the name of the IED where the ExtRef are
+ * @param targetLDeviceInst the name of the LDevice where the ExtRef are
+ * @return a report with all encountered errors
+ */
public static SclReport createDataSetAndControlBlocks(SCL scd, String targetIedName, String targetLDeviceInst) {
if (StringUtils.isBlank(targetIedName)) {
throw new ScdException(MESSAGE_MISSING_IED_NAME_PARAMETER);
@@ -126,9 +154,74 @@ public static SclReport createDataSetAndControlBlocks(SCL scd, String targetIedN
private static SclReport createDataSetAndControlBlocks(SclRootAdapter sclRootAdapter, Stream lDeviceAdapters) {
List sclReportItems = lDeviceAdapters
- .map(LDeviceAdapter::createDataSetAndControlBlocks)
- .flatMap(List::stream)
- .toList();
+ .map(LDeviceAdapter::createDataSetAndControlBlocks)
+ .flatMap(List::stream)
+ .toList();
return new SclReport(sclRootAdapter, sclReportItems);
}
+
+ /**
+ * Configure the network for all the ControlBlocks.
+ * Create (or update if already existing) these elements
+ * - the Communication/SubNetwork/ConnectedAP/GSE element, for the GSEControl blocks
+ * - the Communication/SubNetwork/ConnectedAP/SMV element, for the SampledValueControl blocks
+ *
+ * @param scd input SCD object. The object will be modified with the new DataGSESet and SMV elements
+ * @param controlBlockNetworkSettings a method tha gives the network configuration information for a given ControlBlock
+ * @param rangesPerCbType provide NetworkRanges for GSEControl and SampledValueControl. NetworkRanges contains :
+ * start-end app APPID range (long value), start-end MAC-Addresses (Mac-Addresses values: Ex: "01-0C-CD-01-01-FF")
+ * @return a report with all the errors encountered
+ * @see Utils#macAddressToLong(String) for the expected MAC address format
+ * @see ControlBlockNetworkSettings
+ * @see ControlBlockNetworkSettings.RangesPerCbType
+ * @see ControlBlockNetworkSettings.NetworkRanges
+ */
+ public static SclReport configureNetworkForAllControlBlocks(SCL scd, ControlBlockNetworkSettings controlBlockNetworkSettings,
+ RangesPerCbType rangesPerCbType) {
+ List sclReportItems = new ArrayList<>();
+ sclReportItems.addAll(configureNetworkForControlBlocks(scd, controlBlockNetworkSettings, rangesPerCbType.gse(), ControlBlockEnum.GSE));
+ sclReportItems.addAll(configureNetworkForControlBlocks(scd, controlBlockNetworkSettings, rangesPerCbType.sampledValue(), ControlBlockEnum.SAMPLED_VALUE));
+ return new SclReport(new SclRootAdapter(scd), sclReportItems);
+ }
+
+ private static List configureNetworkForControlBlocks(SCL scd, ControlBlockNetworkSettings controlBlockNetworkSettings,
+ NetworkRanges networkRanges, ControlBlockEnum controlBlockEnum) {
+ PrimitiveIterator.OfLong appIdIterator = Utils.sequence(networkRanges.appIdStart(), networkRanges.appIdEnd());
+ Iterator macAddressIterator = Utils.macAddressSequence(networkRanges.macAddressStart(), networkRanges.macAddressEnd());
+
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ return sclRootAdapter.streamIEDAdapters()
+ .flatMap(iedAdapter ->
+ iedAdapter.streamLDeviceAdapters()
+ .filter(LDeviceAdapter::hasLN0)
+ .map(LDeviceAdapter::getLN0Adapter)
+ .flatMap(ln0Adapter -> ln0Adapter.streamControlBlocks(controlBlockEnum))
+ .map(controlBlockAdapter -> configureControlBlockNetwork(controlBlockNetworkSettings, appIdIterator, macAddressIterator, controlBlockAdapter)))
+ .flatMap(Optional::stream)
+ .toList();
+ }
+
+ private static Optional configureControlBlockNetwork(ControlBlockNetworkSettings controlBlockNetworkSettings, PrimitiveIterator.OfLong appIdIterator, Iterator macAddressIterator, ControlBlockAdapter controlBlockAdapter) {
+ Settings settings = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter);
+
+ if (settings == null) {
+ return Optional.of(controlBlockAdapter.buildFatalReportItem(
+ "Cannot configure network for this ControlBlock because no settings was provided"));
+ }
+ if (settings.vlanId() == null) {
+ return Optional.of(controlBlockAdapter.buildFatalReportItem(
+ "Cannot configure network for this ControlBlock because no Vlan Id was provided in the settings"));
+ }
+ if (!appIdIterator.hasNext()) {
+ return Optional.of(controlBlockAdapter.buildFatalReportItem(
+ "Cannot configure network for this ControlBlock because range of appId is exhausted"));
+ }
+ if (!macAddressIterator.hasNext()) {
+ return Optional.of(controlBlockAdapter.buildFatalReportItem(
+ "Cannot configure network for this ControlBlock because range of MAC Address is exhausted"));
+ }
+
+ return controlBlockAdapter.configureNetwork(appIdIterator.nextLong(), macAddressIterator.next(), settings.vlanId(), settings.vlanPriority(),
+ settings.minTime(), settings.maxTime());
+ }
}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclRootAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclRootAdapter.java
index 07934c74d..2876173ea 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclRootAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclRootAdapter.java
@@ -12,6 +12,7 @@
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.scl.com.CommunicationAdapter;
+import org.lfenergy.compas.sct.commons.scl.com.ConnectedAPAdapter;
import org.lfenergy.compas.sct.commons.scl.dtt.DataTypeTemplateAdapter;
import org.lfenergy.compas.sct.commons.scl.header.HeaderAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter;
@@ -291,4 +292,20 @@ public IEDAdapter checkObjRef(String val) throws ScdException {
throw new ScdException("Invalid ObjRef: " + val);
}
+ /**
+ * Find a ConnectedAp element withe given iedName and apName
+ * @param iedName iedName
+ * @param apName apName
+ * @return the first ConnectedAp which match the given iedName and apName, or empty Optional if none found
+ */
+ public Optional findConnectedApAdapter(String iedName, String apName) {
+ if (!currentElem.isSetCommunication()) {
+ return Optional.empty();
+ }
+ CommunicationAdapter communicationAdapter = new CommunicationAdapter(this, currentElem.getCommunication());
+ return communicationAdapter.getSubNetworkAdapters().stream()
+ .map(subNetworkAdapter -> subNetworkAdapter.findConnectedAPAdapter(iedName, apName))
+ .flatMap(Optional::stream)
+ .findFirst();
+ }
}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/ConnectedAPAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/ConnectedAPAdapter.java
index 3f4a076c3..f2fcfb0e9 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/ConnectedAPAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/ConnectedAPAdapter.java
@@ -4,13 +4,16 @@
package org.lfenergy.compas.sct.commons.scl.com;
-import org.lfenergy.compas.scl2007b4.model.SCL;
-import org.lfenergy.compas.scl2007b4.model.TConnectedAP;
+import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.scl.SclElementAdapter;
import org.lfenergy.compas.sct.commons.util.Utils;
+import java.util.List;
+import java.util.Objects;
import java.util.Optional;
+import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newAddress;
+
/**
* A representation of the model object
* {@link org.lfenergy.compas.scl2007b4.model.TConnectedAP ConnectedAP}.
@@ -49,7 +52,7 @@ protected boolean amChildElementRef() {
protected String elementXPath() {
return String.format("ConnectedAP[%s and %s]",
Utils.xpathAttributeFilter("apName", currentElem.isSetApName() ? currentElem.getApName() : null),
- Utils.xpathAttributeFilter("iedName", currentElem.isSetIedName() ? currentElem.getApName() : null));
+ Utils.xpathAttributeFilter("iedName", currentElem.isSetIedName() ? currentElem.getIedName() : null));
}
/**
@@ -86,7 +89,66 @@ public void copyAddressAndPhysConnFromIcd(Optional icd) {
currentElem.setAddress(connectedAP.getAddress());
currentElem.getPhysConn().addAll(connectedAP.getPhysConn());
});
+ }
+ }
+
+ private Optional findGse(String ldInst, String cbName){
+ if (!currentElem.isSetGSE()){
+ return Optional.empty();
+ }
+ return currentElem.getGSE().stream().filter(gse -> Objects.equals(ldInst, gse.getLdInst()) && Objects.equals(cbName, gse.getCbName()))
+ .findFirst();
+ }
+ private Optional findSmv(String ldInst, String cbName){
+ if (!currentElem.isSetSMV()){
+ return Optional.empty();
}
+ return currentElem.getSMV().stream().filter(smv -> Objects.equals(ldInst, smv.getLdInst()) && Objects.equals(cbName, smv.getCbName()))
+ .findFirst();
}
+
+ /**
+ * Create A GSE Section or update an existing GSE Section (the network configuration of a GSEControl block).
+ *
+ * @param ldInst ldInst
+ * @param cbName cbName
+ * @param listOfP list of P elements
+ * @param minTime minTime
+ * @param maxTime maxTime
+ */
+ public void updateGseOrCreateIfNotExists(String ldInst, String cbName, List listOfP, TDurationInMilliSec minTime, TDurationInMilliSec maxTime) {
+ TGSE gse = findGse(ldInst, cbName)
+ .orElseGet(() -> {
+ TGSE newGse = new TGSE();
+ newGse.setLdInst(ldInst);
+ newGse.setCbName(cbName);
+ currentElem.getGSE().add(newGse);
+ return newGse;
+ }
+ );
+ gse.setAddress(newAddress(listOfP));
+ gse.setMinTime(minTime);
+ gse.setMaxTime(maxTime);
+ }
+
+ /**
+ * Create A SMV Section or update an existing SMV Section (the network configuration of a SampledValueControl block)..
+ * @param ldInst ldInst
+ * @param cbName cbName
+ * @param listOfP list of P elements
+ */
+ public void updateSmvOrCreateIfNotExists(String ldInst, String cbName, List listOfP) {
+ TSMV smv = findSmv(ldInst, cbName)
+ .orElseGet(() -> {
+ TSMV newSmv = new TSMV();
+ newSmv.setLdInst(ldInst);
+ newSmv.setCbName(cbName);
+ currentElem.getSMV().add(newSmv);
+ return newSmv;
+ }
+ );
+ smv.setAddress(newAddress(listOfP));
+ }
+
}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/SubNetworkAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/SubNetworkAdapter.java
index efe7a9838..90dbc5b8e 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/SubNetworkAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/SubNetworkAdapter.java
@@ -9,11 +9,12 @@
import org.lfenergy.compas.scl2007b4.model.TSubNetwork;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.scl.SclElementAdapter;
+import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
import org.lfenergy.compas.sct.commons.util.Utils;
import java.util.List;
import java.util.Objects;
-import java.util.stream.Collectors;
+import java.util.Optional;
/**
* A representation of the model object
@@ -72,9 +73,7 @@ public ConnectedAPAdapter addConnectedAP(@NonNull String iedName, @NonNull Strin
.orElse(null);
if(tConnectedAP == null){
- tConnectedAP = new TConnectedAP();
- tConnectedAP.setApName(apName);
- tConnectedAP.setIedName(iedName);
+ tConnectedAP = SclConstructorHelper.newConnectedAp(iedName, apName);
currentElem.getConnectedAP().add(tConnectedAP);
}
return new ConnectedAPAdapter(this,tConnectedAP);
@@ -104,28 +103,40 @@ public List getConnectedAPAdapters() {
return currentElem.getConnectedAP()
.stream()
.map(ap -> new ConnectedAPAdapter(this,ap))
- .collect(Collectors.toList());
+ .toList();
}
/**
* Gets ConnectedAP from Subnetwork
+ *
* @param iedName IED name
- * @param apName AccessPoint name
+ * @param apName AccessPoint name
* @return the ConnectedAPAdapter object
- * @throws ScdException
+ * @throws ScdException when ConnectedAP not found
*/
public ConnectedAPAdapter getConnectedAPAdapter(String iedName, String apName) throws ScdException {
- return currentElem.getConnectedAP()
- .stream()
- .filter(ap -> ap.getIedName().equals(iedName) && ap.getApName().equals(apName))
- .map(ap -> new ConnectedAPAdapter(this,ap))
- .findFirst()
- .orElseThrow(
- () -> new ScdException(
- String.format(
- "Unknown connected AP (%s,%s) for subnetwork %s", iedName, apName, getName()
- )
+ return findConnectedAPAdapter(iedName, apName)
+ .orElseThrow(
+ () -> new ScdException(
+ String.format(
+ "Unknown connected AP (%s,%s) for subnetwork %s", iedName, apName, getName()
)
- );
+ )
+ );
+ }
+
+ /**
+ * Find ConnectedAP from Subnetwork
+ *
+ * @param iedName IED name
+ * @param apName AccessPoint name
+ * @return the ConnectedAPAdapter object
+ */
+ public Optional findConnectedAPAdapter(String iedName, String apName) throws ScdException {
+ return currentElem.getConnectedAP()
+ .stream()
+ .filter(ap -> ap.getIedName().equals(iedName) && ap.getApName().equals(apName))
+ .map(ap -> new ConnectedAPAdapter(this, ap))
+ .findFirst();
}
}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapter.java
index c085c6057..ffe1396b1 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapter.java
@@ -344,7 +344,7 @@ public String getId() {
}
/**
- * Find binded DOType info
+ * Find bound DOType info
* @param signalInfo extRef signal info for binding
* @return DOType info as object contening name, id and adapter
* @throws ScdException throws when DO unknown
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java
index bc79c4b94..37cc26a11 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java
@@ -371,15 +371,15 @@ private List getControlBlocksByDataSetRef(String dataSetRef, TServ
* @return all Control Blocks matching a Service Type
* @param inference parameter for Type of Control Block to find
*/
- public List extends TControl> getTControlsByType(Class cls) {
+ public List getTControlsByType(Class cls) {
if (TGSEControl.class.equals(cls) && isLN0()) {
- return ((LN0) currentElem).getGSEControl();
+ return (List) ((LN0) currentElem).getGSEControl();
} else if (TSampledValueControl.class.equals(cls) && isLN0()) {
- return ((LN0) currentElem).getSampledValueControl();
+ return (List) ((LN0) currentElem).getSampledValueControl();
} else if (TReportControl.class.equals(cls)) {
- return currentElem.getReportControl();
+ return (List) currentElem.getReportControl();
}
- throw new IllegalArgumentException("Unsupported ControlBlock " + cls.getSimpleName());
+ throw new IllegalArgumentException("Unsupported ControlBlock %s for %s element".formatted(cls.getSimpleName(), elementXPath()));
}
/**
@@ -952,6 +952,11 @@ public Optional findControlBlock(String name, ControlBlockE
.map(tControl -> new ControlBlockAdapter(this, tControl));
}
+ public Stream streamControlBlocks(ControlBlockEnum controlBlockEnum) {
+ return getTControlsByType(controlBlockEnum.getControlBlockClass()).stream()
+ .map(tControl -> new ControlBlockAdapter(this, tControl));
+ }
+
public ControlBlockAdapter createControlBlockIfNotExists(String cbName, String id, String datSet, ControlBlockEnum controlBlockEnum) {
return findControlBlock(cbName, controlBlockEnum)
.orElseGet(() -> addControlBlock(
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java
index 4e12b6658..a0fb6bacc 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java
@@ -89,8 +89,10 @@ public List checkFCDALimitations() {
.map(abstractLNAdapter -> abstractLNAdapter.getCurrentElem().getDataSet())
.flatMap(Collection::stream)
.filter(tDataSet -> tDataSet.getFCDA().size() > max)
- .map(tDataSet -> SclReportItem.fatal(getXPath(), String.format("There are too much FCDA for the DataSet %S for the LDevice %S in IED %S",
- tDataSet.getName(), lDeviceAdapter.getInst(), parentAdapter.getName()))).toList()
+ .map(tDataSet -> SclReportItem.fatal(getXPath(), String.format("There are too much FCDA for the DataSet %s for the LDevice %s"
+ + " in IED %s: %d > %d max", tDataSet.getName(), lDeviceAdapter.getInst(), parentAdapter.getName(),
+ tDataSet.getFCDA().size(), max))
+ ).toList()
).flatMap(Collection::stream).toList();
}
@@ -98,13 +100,13 @@ public List checkFCDALimitations() {
* Checks if occurrences of specified tpe (DataSet, Controls) exceeds config limitation
*
* @param servicesConfigEnum type of element for which limitation is checked
- * @param msg error message to display
* @return Optional of encountered error or empty
*/
- public Optional checkControlsLimitation(ServicesConfigEnum servicesConfigEnum, String msg) {
+ public Optional checkControlsLimitation(ServicesConfigEnum servicesConfigEnum) {
long max = getMaxInstanceAuthorized(servicesConfigEnum);
long value = getNumberOfItems(servicesConfigEnum);
- return max == MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.fatal(getXPath(), String.format("%s %s", msg, parentAdapter.getName())));
+ return max == MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.fatal(getXPath(),
+ String.format("There are too much %ss for the IED %s: %d > %d max", servicesConfigEnum.getDisplayName(), parentAdapter.getName(), value, max)));
}
/**
@@ -145,36 +147,48 @@ private Class extends TControl> getControlTypeClass(ServicesConfigEnum service
}
/**
- * Gets max number authorized in configuration of each element DataSets, FCDAs, Control Blocks) into an AccessPoint
+ * Gets max number authorized in configuration of each element (DataSets, FCDAs, Control Blocks) into an AccessPoint
*
* @param servicesConfigEnum element type
* @return max number authorized by config
*/
private long getMaxInstanceAuthorized(ServicesConfigEnum servicesConfigEnum) {
- if (currentElem.getServices() == null || currentElem.getServices().getConfDataSet() == null)
+ if (currentElem.getServices() == null)
return MAX_OCCURRENCE_NO_LIMIT_VALUE;
TServices tServices = currentElem.getServices();
return switch (servicesConfigEnum) {
case DATASET ->
- tServices.getConfDataSet().isSetMax() ? tServices.getConfDataSet().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
+ tServices.isSetConfDataSet() && tServices.getConfDataSet().isSetMax() ?
+ tServices.getConfDataSet().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
case FCDA ->
- tServices.getConfDataSet().isSetMaxAttributes() ? tServices.getConfDataSet().getMaxAttributes() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
+ tServices.isSetConfDataSet() && tServices.getConfDataSet().isSetMaxAttributes() ?
+ tServices.getConfDataSet().getMaxAttributes() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
case REPORT ->
- tServices.getConfReportControl().isSetMax() ? tServices.getConfReportControl().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
- case GSE -> tServices.getGOOSE().isSetMax() ? tServices.getGOOSE().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
- case SMV -> tServices.getSMVsc().isSetMax() ? tServices.getSMVsc().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
+ tServices.isSetConfReportControl() && tServices.getConfReportControl().isSetMax() ?
+ tServices.getConfReportControl().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
+ case GSE -> tServices.isSetGOOSE() && tServices.getGOOSE().isSetMax() ? tServices.getGOOSE().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
+ case SMV -> tServices.isSetSMVsc() && tServices.getSMVsc().isSetMax() ? tServices.getSMVsc().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
};
}
/**
- * Checks FCDA number limitation for binded IED
+ * Checks FCDA number limitation for bound IED
*
- * @param msg message to display hen error occurred
* @return Optional of encountered error or empty
*/
- public Optional checkLimitationForBoundIEDFCDAs(List tExtRefs, String msg) {
+ public Optional checkLimitationForBoundIedFcdas(List tExtRefs) {
+ long max;
+ if (currentElem.getServices() == null) {
+ max = MAX_OCCURRENCE_NO_LIMIT_VALUE;
+ } else {
+ TClientServices tClientServices = currentElem.getServices().getClientServices();
+ max = tClientServices != null && tClientServices.isSetMaxAttributes() ? tClientServices.getMaxAttributes() : MAX_OCCURRENCE_NO_LIMIT_VALUE;
+ }
+ if (max == MAX_OCCURRENCE_NO_LIMIT_VALUE){
+ return Optional.empty();
+ }
long value = tExtRefs.stream()
.map(tExtRef -> {
IEDAdapter iedAdapter = getParentAdapter().getParentAdapter().getIEDAdapterByName(tExtRef.getIedName());
@@ -195,17 +209,10 @@ public Optional checkLimitationForBoundIEDFCDAs(List tEx
.flatMap(Collection::stream)
.toList()
.size();
- long max;
- if (currentElem.getServices() == null) {
- max = AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE;
- } else {
- TClientServices tClientServices = currentElem.getServices().getClientServices();
- max = tClientServices != null && tClientServices.isSetMaxAttributes() ? tClientServices.getMaxAttributes() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE;
- }
-
- return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() :
- Optional.of(SclReportItem.fatal(getParentAdapter().getXPath(), msg));
+ return value <= max ? Optional.empty() :
+ Optional.of(SclReportItem.fatal(getParentAdapter().getXPath(),
+ "The Client IED %s subscribes to too much FCDA: %d > %d max".formatted(getParentAdapter().getName(), value, max)));
}
/**
@@ -225,7 +232,7 @@ public ExtRefAnalyzeRecord getAllCoherentExtRefForAnalyze() {
List tExtRefList = new ArrayList<>();
streamLDeviceAdapters().map(lDeviceAdapter -> {
List extRefs = lDeviceAdapter.getLN0Adapter().getExtRefs().stream().filter(TExtRef::isSetSrcCBName).collect(Collectors.toCollection(ArrayList::new));
- sclReportItems.addAll(checkExtRefWithoutServiceType(extRefs, lDeviceAdapter.getXPath()));
+ sclReportItems.addAll(checkExtRefWithoutServiceType(extRefs, lDeviceAdapter.getLN0Adapter().getXPath()));
extRefs.removeIf(tExtRef -> !tExtRef.isSetServiceType());
return extRefs;
}).flatMap(Collection::stream).forEach(tExtRef -> {
@@ -249,7 +256,9 @@ private List checkExtRefWithoutServiceType(List tExtRefs
return tExtRefs.stream()
.filter(tExtRef -> !tExtRef.isSetServiceType())
.map(tExtRef ->
- SclReportItem.fatal(xPath, "ExtRef signal without ServiceType : " + tExtRef.getDesc()))
+ SclReportItem.fatal("%s/Inputs/ExtRef[%s]".formatted(xPath,
+ Utils.xpathAttributeFilter("desc", tExtRef.getDesc())),
+ "ExtRef is missing ServiceType attribute"))
.toList();
}
@@ -273,7 +282,7 @@ private static boolean isExtRefFeedBySameControlBlock(TExtRef t1, TExtRef t2) {
}
/**
- * Checks Control Blocks (Report, Goose, SMV) number limitation for binded IED
+ * Checks Control Blocks (Report, Goose, SMV) number limitation for bound IED
*
* @return List of errors encountered
*/
@@ -281,34 +290,27 @@ public List checkLimitationForBoundIEDControls(List tExt
Map> extRefsByServiceType = tExtRefs.stream()
.filter(TExtRef::isSetServiceType)
.collect(Collectors.groupingBy(TExtRef::getServiceType, Collectors.toSet()));
- return extRefsByServiceType.keySet().stream()
- .map(tServiceType -> {
- Set tExtRefSet = extRefsByServiceType.get(tServiceType);
- return switch (tServiceType) {
- case REPORT ->
- checkLimitationForOneControlType(tExtRefSet, CLIENT_IED_NAME + getParentAdapter().getName() + " subscribes to too much REPORT Control Blocks.", ServicesConfigEnum.REPORT);
- case GOOSE ->
- checkLimitationForOneControlType(tExtRefSet, CLIENT_IED_NAME + getParentAdapter().getName() + " subscribes to too much GOOSE Control Blocks.", ServicesConfigEnum.GSE);
- case SMV ->
- checkLimitationForOneControlType(tExtRefSet, CLIENT_IED_NAME + getParentAdapter().getName() + " subscribes to too much SMV Control Blocks.", ServicesConfigEnum.SMV);
- default -> throw new ScdException("Unsupported value: " + tServiceType);
- };
- }).flatMap(Optional::stream)
+ return extRefsByServiceType.entrySet().stream()
+ .map(entry -> checkLimitationForOneControlType(entry.getValue(), ServicesConfigEnum.from(entry.getKey())))
+ .flatMap(Optional::stream)
.toList();
}
/**
- * Checks Control Block number limitation for binded IED
+ * Checks Control Block number limitation for bound IED
*
* @param tExtRefs list of ExtRefs referenced same ied
* @param msg message to display hen error occured
* @param servicesConfigEnum type of Control Block for which check is done
* @return Optional of encountered error or empty
*/
- private Optional checkLimitationForOneControlType(Set tExtRefs, String msg, ServicesConfigEnum servicesConfigEnum) {
+ private Optional checkLimitationForOneControlType(Set tExtRefs, ServicesConfigEnum servicesConfigEnum) {
long max = getMaxInstanceAuthorizedForBoundIED(servicesConfigEnum);
long value = tExtRefs.size();
- return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.fatal(getParentAdapter().getXPath(), msg));
+ return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() :
+ Optional.of(SclReportItem.fatal(getParentAdapter().getXPath(),
+ "The Client IED %s subscribes to too much %ss: %d > %d max".formatted(getParentAdapter().getName(), servicesConfigEnum.getDisplayName(),
+ value, max)));
}
/**
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapter.java
index 4b7167f6b..d7259ca8a 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapter.java
@@ -7,8 +7,17 @@
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.dto.ControlBlockTarget;
+import org.lfenergy.compas.sct.commons.dto.SclReportItem;
import org.lfenergy.compas.sct.commons.scl.SclElementAdapter;
+import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
+import org.lfenergy.compas.sct.commons.scl.com.ConnectedAPAdapter;
import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
+import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
+import org.lfenergy.compas.sct.commons.util.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
import static org.lfenergy.compas.sct.commons.util.Utils.xpathAttributeFilter;
@@ -40,6 +49,12 @@
public class ControlBlockAdapter extends SclElementAdapter, TControl> {
private static final long RPT_ENABLED_MAX_DEFAULT = 1L;
+ private static final String APPID_P_TYPE = "APPID";
+ private static final String MAC_ADDRESS_P_TYPE = "MAC-Address";
+ private static final String VLAN_ID_P_TYPE = "VLAN-ID";
+ private static final String VLAN_PRIORITY_P_TYPE = "VLAN-PRIORITY";
+ private static final int APPID_LENGTH = 4;
+ private static final int VLAN_ID_LENGTH = 3;
public ControlBlockAdapter(AbstractLNAdapter extends TAnyLN> parentAdapter, TControl tControl) {
super(parentAdapter, tControl);
@@ -75,6 +90,14 @@ protected String elementXPath() {
return String.format("%s[%s]", tag, xpathAttributeFilter("name", currentElem.getName()));
}
+ /**
+ * Get the name of this ControlBlock
+ * @return name of ControlBlock
+ */
+ public String getName(){
+ return currentElem.getName();
+ }
+
/**
* Add a ClientLN to ReportControl or IEDName to GSEControl/SampleValueControl, if it does not already exist.
* @param targetLn target LN (where the target ExtRef is)
@@ -102,4 +125,74 @@ public void addTargetIfNotExists(AbstractLNAdapter> targetLn) {
}
}
+ /**
+ * Configure the Communication section for this ControlBlock
+ * - Communication/SubNetwork/ConnectedAP/GSE for GSEControl block
+ * - Communication/SubNetwork/ConnectedAP/SMV for SampledValueControl block
+ * @param appId value for P type APPID
+ * @param macAddress value for P type MAC-Address
+ * @param vlanId value for P type VLAN-ID
+ * @param vlanPriority value for P type VLAN-PRIORITY
+ * @param minTime MinTime Element
+ * @param maxTime MaxTime Element
+ * @return An empty Optional if network have been configured, else a SclReportItem.
+ */
+ public Optional configureNetwork(long appId, String macAddress, Integer vlanId, Byte vlanPriority, TDurationInMilliSec minTime,
+ TDurationInMilliSec maxTime) {
+ String accessPointName = getParentLDeviceAdapter().getAccessPoint().getName();
+
+ Optional optConApAdapter = getSclRootAdapter().findConnectedApAdapter(getParentIedAdapter().getName(), accessPointName);
+ if (optConApAdapter.isEmpty()) {
+ return Optional.of(buildFatalReportItem("Cannot configure network for ControlBlock because no ConnectAP found for parent AccessPoint"));
+ }
+ ConnectedAPAdapter connectedAPAdapter = optConApAdapter.get();
+ List listOfPs = new ArrayList<>();
+ listOfPs.add(SclConstructorHelper.newP(APPID_P_TYPE, Utils.toHex(appId, APPID_LENGTH)));
+ listOfPs.add(SclConstructorHelper.newP(MAC_ADDRESS_P_TYPE, macAddress));
+ if (vlanId != null) {
+ listOfPs.add(SclConstructorHelper.newP(VLAN_ID_P_TYPE, Utils.toHex(vlanId, VLAN_ID_LENGTH)));
+ if (vlanPriority != null) {
+ listOfPs.add(SclConstructorHelper.newP(VLAN_PRIORITY_P_TYPE, String.valueOf(vlanPriority)));
+ }
+ }
+ switch (getControlBlockEnum()) {
+ case GSE -> connectedAPAdapter.updateGseOrCreateIfNotExists(getParentLDeviceAdapter().getInst(), currentElem.getName(), listOfPs, clone(minTime), clone(maxTime));
+ case SAMPLED_VALUE -> connectedAPAdapter.updateSmvOrCreateIfNotExists(getParentLDeviceAdapter().getInst(), currentElem.getName(), listOfPs);
+ default -> {
+ return Optional.of(buildFatalReportItem("configureNetwork not yet implemented for %s ControlBlocks".formatted(getControlBlockEnum())));
+ }
+ }
+ return Optional.empty();
+ }
+
+ private TDurationInMilliSec clone(TDurationInMilliSec tDurationInMilliSec){
+ if (tDurationInMilliSec == null) {
+ return null;
+ }
+ return SclConstructorHelper.newDurationInMilliSec(tDurationInMilliSec.getValue(), tDurationInMilliSec.getUnit(), tDurationInMilliSec.getMultiplier());
+ }
+
+ /**
+ * Get parent LDevice
+ * @return ControlBlock's parent lDeviceAdapter
+ */
+ private LDeviceAdapter getParentLDeviceAdapter() {
+ return getParentAdapter().getParentAdapter();
+ }
+
+ /**
+ * Get parent IED
+ * @return ControlBlock's parent IEDAdapter
+ */
+ public IEDAdapter getParentIedAdapter() {
+ return getParentAdapter().getParentIed();
+ }
+
+ /**
+ * Get SCL Root
+ * @return sclRootAdapter
+ */
+ private SclRootAdapter getSclRootAdapter() {
+ return getParentIedAdapter().getParentAdapter();
+ }
}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java
index a12c63ec4..573d21d50 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java
@@ -189,7 +189,7 @@ public Optional updateDaiFromExtRef(List tExtRefs) {
.updateVal(createInRefValTestString(tExtRefMin));
}
- Optional tExtRefMaxOptional = tExtRefs.stream().max(Comparator.comparingInt(o -> Integer.parseInt(Objects.requireNonNull(Utils.extractField(o.getDesc(), "_", -1)))));
+ Optional tExtRefMaxOptional = tExtRefs.stream().max(EXTREF_DESC_SUFFIX_COMPARATOR);
if (tExtRefMaxOptional.isPresent() && extractDescSuffix(tExtRefMaxOptional.get().getDesc()) > 1) {
TExtRef tExtRefMax = tExtRefMaxOptional.get();
findDataAdapterByName(DA_NAME_SET_TST_REF)
@@ -209,7 +209,7 @@ public Optional updateDaiFromExtRef(List tExtRefs) {
}
private static int extractDescSuffix(String desc) throws NumberFormatException {
- return Integer.parseInt(Utils.extractField(desc, "_", -1));
+ return Integer.parseInt(Objects.requireNonNull(Utils.extractField(desc, "_", -1)));
}
private String createInRefValNominalString(TExtRef extRef) {
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapter.java
index df3934aac..e24f8da89 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapter.java
@@ -18,7 +18,11 @@
import org.lfenergy.compas.sct.commons.util.ServicesConfigEnum;
import org.lfenergy.compas.sct.commons.util.Utils;
-import java.util.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
import java.util.stream.Stream;
/**
@@ -325,20 +329,18 @@ public Optional getPrivateCompasBay() {
* @return empty list if all IED respect limits, otherwise list of errors
*/
public List checkDataGroupCoherence() {
- return currentElem.getAccessPoint().stream()
- .map(tAccessPoint -> {
- AccessPointAdapter accessPointAdapter = new AccessPointAdapter(this, tAccessPoint);
- List sclReportItems = new ArrayList<>(accessPointAdapter.checkFCDALimitations());
- accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.DATASET, "There are too much DataSets for the IED")
- .ifPresent(sclReportItems::add);
- accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.REPORT, "There are too much Report Control Blocks for the IED")
- .ifPresent(sclReportItems::add);
- accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.GSE, "There are too much GOOSE Control Blocks for the IED")
- .ifPresent(sclReportItems::add);
- accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.SMV, "There are too much SMV Control Blocks for the IED")
- .ifPresent(sclReportItems::add);
- return sclReportItems;
- }).flatMap(Collection::stream)
+ return streamAccessPointAdapters()
+ .flatMap(accessPointAdapter ->
+ Stream.concat(
+ accessPointAdapter.checkFCDALimitations().stream(),
+ Stream.of(
+ accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.DATASET),
+ accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.REPORT),
+ accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.GSE),
+ accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.SMV))
+ .flatMap(Optional::stream)
+ )
+ )
.toList();
}
@@ -346,21 +348,37 @@ public List checkDataGroupCoherence() {
* Checks if Controls and FCDAs of source IEDs respect config limitation
* @return empty list if all IED respect limits, otherwise list of errors
*/
- public List checkBindingDataGroupCoherence() {
- return currentElem.getAccessPoint().stream()
- .map(tAccessPoint -> {
- AccessPointAdapter accessPointAdapter = new AccessPointAdapter(this, tAccessPoint);
+ public List checkBindingDataGroupCoherence() {
+ return streamAccessPointAdapters()
+ .flatMap(accessPointAdapter -> {
AccessPointAdapter.ExtRefAnalyzeRecord extRefAnalyzeRecord = accessPointAdapter.getAllCoherentExtRefForAnalyze();
- List sclReportItems = new ArrayList<>(extRefAnalyzeRecord.sclReportItems());
- accessPointAdapter.checkLimitationForBoundIEDFCDAs(extRefAnalyzeRecord.tExtRefs(), "There are too much FCDA for the Client IED " + getName())
- .ifPresent(sclReportItems::add);
- sclReportItems.addAll(accessPointAdapter.checkLimitationForBoundIEDControls(extRefAnalyzeRecord.tExtRefs()));
- return sclReportItems;
- }).flatMap(List::stream).toList();
-
+ return Stream.of(
+ extRefAnalyzeRecord.sclReportItems().stream(),
+ accessPointAdapter.checkLimitationForBoundIedFcdas(extRefAnalyzeRecord.tExtRefs()).stream(),
+ accessPointAdapter.checkLimitationForBoundIEDControls(extRefAnalyzeRecord.tExtRefs()).stream())
+ .flatMap(Function.identity());
+ }).toList();
}
+ private Stream streamAccessPointAdapters() {
+ return currentElem.getAccessPoint().stream()
+ .map(tAccessPoint -> new AccessPointAdapter(this, tAccessPoint));
+ }
+ /**
+ * Get value of private type COMPAS-ICDHeader
+ * @return COMPAS-ICDHeader private value if present, else empty Optional
+ */
+ public Optional getCompasICDHeader() {
+ return PrivateService.extractCompasPrivate(currentElem, TCompasICDHeader.class);
+ }
+ /**
+ * Get value of private type COMPAS-SystemVersion
+ * @return COMPAS-SystemVersion private value if present, else empty Optional
+ */
+ public Optional getCompasSystemVersion() {
+ return PrivateService.extractCompasPrivate(currentElem, TCompasSystemVersion.class);
+ }
}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java
index 89e91a3dc..50cbcbb22 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java
@@ -171,13 +171,7 @@ public List getLNAdapters(){
* @throws ScdException thros when specified LNode not found in current IED
*/
public LNAdapter getLNAdapter(String lnClass, String lnInst, String prefix) throws ScdException {
- return currentElem.getLN()
- .stream()
- .filter(tln -> Utils.lnClassEquals(tln.getLnClass(), lnClass)
- && tln.getInst().equals(lnInst)
- && Utils.equalsOrBothBlank(prefix, tln.getPrefix()))
- .map(tln -> new LNAdapter(this,tln))
- .findFirst()
+ return findLnAdapter(lnClass, lnInst, prefix)
.orElseThrow(
()-> new ScdException(
String.format(
@@ -187,6 +181,27 @@ public LNAdapter getLNAdapter(String lnClass, String lnInst, String prefix) thro
}
+ /**
+ * Find a specific LN from current LDevice
+ * @param lnClass LNode lnClass value
+ * @param lnInst LNode lnInst value
+ * @param prefix LNode prefix value
+ * @return LNAdapter object
+ * @throws ScdException thros when specified LNode not found in current IED
+ */
+ public Optional findLnAdapter(String lnClass, String lnInst, String prefix) {
+ if (!currentElem.isSetLN()){
+ return Optional.empty();
+ }
+ return currentElem.getLN()
+ .stream()
+ .filter(tln -> Utils.lnClassEquals(tln.getLnClass(), lnClass)
+ && tln.getInst().equals(lnInst)
+ && Utils.equalsOrBothBlank(prefix, tln.getPrefix()))
+ .map(tln -> new LNAdapter(this, tln))
+ .findFirst();
+ }
+
/**
* Checks all possible ExtRef in current LDevice which could be bound to given ExtRef as parameter
* @param signalInfo ExtRef to bind data
@@ -234,13 +249,13 @@ public List getExtRefInfo() {
* @throws ScdException SCD illegal arguments exception
*/
public Set getDAI(ResumedDataTemplate rDtt, boolean updatableOnly) throws ScdException {
- List> lnAdapters = new ArrayList<>();
+ List extends AbstractLNAdapter>> lnAdapters;
if(StringUtils.isBlank(rDtt.getLnClass())){
lnAdapters = getLNAdaptersIncludingLN0();
} else if(rDtt.getLnClass().equals(TLLN0Enum.LLN_0.value())){
- lnAdapters.add(getLN0Adapter());
+ lnAdapters = hasLN0() ? Collections.singletonList(getLN0Adapter()) : Collections.emptyList();
} else {
- lnAdapters.add(getLNAdapter(rDtt.getLnClass(),rDtt.getLnInst(),rDtt.getPrefix()));
+ lnAdapters = findLnAdapter(rDtt.getLnClass(),rDtt.getLnInst(),rDtt.getPrefix()).stream().toList();
}
Set resumedDataTemplateSet = new HashSet<>();
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LN0Adapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LN0Adapter.java
index e3399ad5d..fc010cf5d 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LN0Adapter.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LN0Adapter.java
@@ -264,15 +264,16 @@ public List updateDoInRef() {
&& doiAdapter.findDataAdapterByName(DAI_NAME_PURPOSE).isPresent())
.map(doiAdapter -> doiAdapter.getDataAdapterByName(DAI_NAME_PURPOSE).getCurrentElem().getVal().stream()
.findFirst()
- .map(tVal -> doiAdapter.updateDaiFromExtRef(getExtRefsByDesc(tVal.getValue())))
+ .map(tVal -> doiAdapter.updateDaiFromExtRef(getBoundExtRefsByDesc(tVal.getValue())))
.orElse(Optional.of(SclReportItem.warning(getXPath(), "The DOI %s can't be bound with an ExtRef".formatted(getXPath())))))
.flatMap(Optional::stream)
.toList();
}
- private List getExtRefsByDesc(String desc) {
+ private List getBoundExtRefsByDesc(String desc) {
return getExtRefs().stream()
- .filter(tExtRef -> tExtRef.isSetDesc() && tExtRef.getDesc().contains(desc))
+ .filter(tExtRef -> tExtRef.isSetIedName() && tExtRef.isSetLdInst() && tExtRef.isSetLnClass() && tExtRef.isSetDoName() &&
+ tExtRef.isSetDesc() && tExtRef.getDesc().contains(desc))
.toList();
}
}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockNetworkSettingsCsvHelper.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockNetworkSettingsCsvHelper.java
new file mode 100644
index 000000000..b62e27ad0
--- /dev/null
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockNetworkSettingsCsvHelper.java
@@ -0,0 +1,193 @@
+// SPDX-FileCopyrightText: 2023 RTE FRANCE
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfenergy.compas.sct.commons.util;
+
+import com.opencsv.bean.CsvBindByPosition;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+import org.lfenergy.compas.scl2007b4.model.TCompasICDHeader;
+import org.lfenergy.compas.scl2007b4.model.TCompasIEDRedundancy;
+import org.lfenergy.compas.scl2007b4.model.TCompasIEDType;
+import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec;
+import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings;
+import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter;
+import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter;
+
+import java.io.Reader;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * This class is an implementation example for interface ControlBlockNetworkSettings.
+ * It relies on a CSV file.
+ * The first columns of the CSV file are the criteria to match the ControlBlock (controlBlockEnum, systemVersionWithoutV, iedType, iedRedundancy,
+ * isBayInternal),
+ * The last columns are the network settings for the matched ControlBlock (as described in {@link ControlBlockNetworkSettings.Settings}).
+ *
+ * @see CsvUtils
+ */
+public class ControlBlockNetworkSettingsCsvHelper implements ControlBlockNetworkSettings {
+
+ private static final int MAX_VLAN_ID = 0x0FFF;
+ private static final int MAX_VLAN_PRIORITY = 7;
+ private static final String NONE = "none";
+
+ private final Map settings;
+
+ /**
+ * Constructor
+ * Provide the CSV file as a Reader. For example, you can create a reader like this :
+ * new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName), StandardCharsets.UTF_8);
+ *
+ * @param csvSource a reader that provides the data as CSV. For example :
+ */
+ public ControlBlockNetworkSettingsCsvHelper(Reader csvSource) {
+ settings = readCsvFile(csvSource);
+ }
+
+ private Map readCsvFile(Reader csvSource) {
+ return CsvUtils.parseRows(csvSource, Row.class).stream()
+ .distinct()
+ .collect(Collectors.toMap(
+ ControlBlockNetworkSettingsCsvHelper::rowToCriteria,
+ ControlBlockNetworkSettingsCsvHelper::rowToSetting
+ ));
+ }
+
+ @Override
+ public Settings getNetworkSettings(ControlBlockAdapter controlBlockAdapter) {
+ ControlBlockEnum controlBlockEnum = controlBlockAdapter.getControlBlockEnum();
+ IEDAdapter iedAdapter = controlBlockAdapter.getParentIedAdapter();
+ String systemVersion = iedAdapter.getCompasSystemVersion()
+ .map(version -> version.getMainSystemVersion() + "." + version.getMinorSystemVersion())
+ .orElse(null);
+ String systemVersionWithoutV = removeVFromSystemVersion(systemVersion);
+ TCompasIEDType iedType = iedAdapter.getCompasICDHeader().map(TCompasICDHeader::getIEDType).orElse(null);
+ TCompasIEDRedundancy iedRedundancy = iedAdapter.getCompasICDHeader().map(TCompasICDHeader::getIEDredundancy).orElse(null);
+ boolean isBayInternal = controlBlockAdapter.getName().endsWith("I");
+ return findSettings(new Criteria(controlBlockEnum, systemVersionWithoutV, iedType, iedRedundancy, isBayInternal));
+ }
+
+ private Settings findSettings(Criteria criteria) {
+ Objects.requireNonNull(criteria);
+ if (criteria.systemVersionWithoutV() == null
+ || criteria.iedType() == null
+ || criteria.iedRedundancy() == null) {
+ return null;
+ }
+ return settings.get(criteria);
+ }
+
+ private static String removeVFromSystemVersion(String systemVersion) {
+ if (systemVersion == null) {
+ return null;
+ }
+ String[] systemVersionParts = systemVersion.split("\\.");
+ if (systemVersionParts.length < 4) {
+ return systemVersion;
+ }
+ return systemVersionParts[0] + "." + systemVersionParts[1] + "." + systemVersionParts[2] + "." + systemVersionParts[3];
+ }
+
+ private static Criteria rowToCriteria(Row row) {
+ if (StringUtils.isBlank(row.cbType)
+ || StringUtils.isBlank(row.xy)
+ || StringUtils.isBlank(row.zw)
+ || StringUtils.isBlank(row.iedType)
+ || StringUtils.isBlank(row.bindingType)
+ ) {
+ throw new IllegalArgumentException("At least one criteria (cbType, xy, zw, iedType, bindingType) is blank");
+ }
+ ControlBlockEnum controlBlockEnum = switch (row.cbType) {
+ case "GOOSE" -> ControlBlockEnum.GSE;
+ case "SV" -> ControlBlockEnum.SAMPLED_VALUE;
+ default -> throw new IllegalArgumentException("Unsupported Control Block Type : " + row.cbType);
+ };
+ return new Criteria(
+ controlBlockEnum,
+ row.xy + "." + row.zw,
+ TCompasIEDType.fromValue(row.iedType),
+ TCompasIEDRedundancy.fromValue(row.iedRedundancy),
+ row.bindingType.equals("BAY_INTERNAL")
+ );
+ }
+
+ private static Settings rowToSetting(Row row) {
+ Integer vlanId = toVLanId(row.vlanId);
+ Byte vlanPriority = toVlanPriority(row.vlanPriority);
+ TDurationInMilliSec minTime = toDurationInMilliSec(row.minTime);
+ TDurationInMilliSec maxTime = toDurationInMilliSec(row.maxTime);
+ return new Settings(vlanId, vlanPriority, minTime, maxTime);
+ }
+
+ private static Byte toVlanPriority(String strVlanPriority) {
+ if (StringUtils.isBlank(strVlanPriority) || NONE.equalsIgnoreCase(strVlanPriority)) {
+ return null;
+ }
+ byte vlanPriority = Byte.parseByte(strVlanPriority);
+ if (vlanPriority < 0 || vlanPriority > MAX_VLAN_PRIORITY) {
+ throw new IllegalArgumentException("VLAN PRIORITY must be between 0 and %d, but got : %d".formatted(MAX_VLAN_PRIORITY, vlanPriority));
+ }
+ return vlanPriority;
+ }
+
+ private static Integer toVLanId(String strVlanId) {
+ if (StringUtils.isBlank(strVlanId) || NONE.equalsIgnoreCase(strVlanId)) {
+ return null;
+ }
+ int vlanId = Integer.parseInt(strVlanId);
+ if (vlanId < 0 || vlanId > MAX_VLAN_ID) {
+ throw new IllegalArgumentException("VLAN ID must be between 0 and %d, but got : %d".formatted(MAX_VLAN_ID, vlanId));
+ }
+ return vlanId;
+ }
+
+ private static TDurationInMilliSec toDurationInMilliSec(String duration) {
+ if (StringUtils.isBlank(duration) || NONE.equalsIgnoreCase(duration)) {
+ return null;
+ }
+ return SclConstructorHelper.newDurationInMilliSec(Long.parseLong(duration));
+ }
+
+ private record Criteria(
+ ControlBlockEnum controlBlockEnum,
+ String systemVersionWithoutV,
+ TCompasIEDType iedType,
+ TCompasIEDRedundancy iedRedundancy,
+ boolean isBayInternal) {
+ }
+
+ @NoArgsConstructor
+ @Getter
+ @Setter
+ @EqualsAndHashCode
+ public static class Row {
+ @CsvBindByPosition(position = 0)
+ private String cbType;
+ @CsvBindByPosition(position = 1)
+ private String xy;
+ @CsvBindByPosition(position = 2)
+ private String zw;
+ @CsvBindByPosition(position = 3)
+ private String iedType;
+ @CsvBindByPosition(position = 4)
+ private String iedRedundancy;
+ @CsvBindByPosition(position = 5)
+ private String bindingType;
+ @CsvBindByPosition(position = 6)
+ private String vlanId;
+ @CsvBindByPosition(position = 7)
+ private String vlanPriority;
+ @CsvBindByPosition(position = 8)
+ private String minTime;
+ @CsvBindByPosition(position = 9)
+ private String maxTime;
+ }
+
+}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/CsvUtils.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/CsvUtils.java
new file mode 100644
index 000000000..23a8d4377
--- /dev/null
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/CsvUtils.java
@@ -0,0 +1,68 @@
+// SPDX-FileCopyrightText: 2023 RTE FRANCE
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfenergy.compas.sct.commons.util;
+
+import com.opencsv.bean.ColumnPositionMappingStrategy;
+import com.opencsv.bean.CsvToBeanBuilder;
+import com.opencsv.enums.CSVReaderNullFieldIndicator;
+import lombok.experimental.UtilityClass;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Utility class to parse CSV files.
+ * This utility class intention is to normalize all CSV inputs in the project:
+ * - Separator is {@link CsvUtils#SEPARATOR}.
+ * - Lines starting with {@link CsvUtils#COMMENT_PREFIX} will be ignored. Allow to write copyright and headers at the beginning of the file for example.
+ * - blank lines are ignored
+ */
+@UtilityClass
+public class CsvUtils {
+ private static final char SEPARATOR = ';';
+ private static final String COMMENT_PREFIX = "#";
+
+ /**
+ * Read CSV from a resource
+ *
+ * @param resourcePath path of the resource
+ * @param charset charset of the resource
+ * @param targetClass Each row will be mapped to this class.
+ * @return list of rows, mapped as targetClass
+ */
+ public static List parseRows(String resourcePath, Charset charset, Class targetClass) {
+ InputStream inputStream = Objects.requireNonNull(CsvUtils.class.getClassLoader().getResourceAsStream(resourcePath), "Resource not found: " + resourcePath);
+ InputStreamReader csvReader = new InputStreamReader(inputStream, charset);
+ return parseRows(csvReader, targetClass);
+ }
+
+ /**
+ * Read CSV from a Reader.
+ * Reader will be automatically closed when the method returns or throw an exception.
+ * @param csvSource CSV input
+ * @param targetClass Each row will be mapped to this class.
+ * @return list of rows, mapped as targetClass
+ */
+ public static List parseRows(Reader csvSource, Class targetClass) {
+ ColumnPositionMappingStrategy columnPositionMappingStrategy = new ColumnPositionMappingStrategy<>();
+ columnPositionMappingStrategy.setType(targetClass);
+ try (csvSource) {
+ return new CsvToBeanBuilder(csvSource)
+ .withType(targetClass)
+ .withSeparator(SEPARATOR)
+ .withIgnoreLeadingWhiteSpace(true)
+ .withIgnoreEmptyLine(true)
+ .withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS)
+ .withFilter(line -> line != null && line.length > 0 && (line[0] == null || !line[0].stripLeading().startsWith(COMMENT_PREFIX)))
+ .withMappingStrategy(columnPositionMappingStrategy)
+ .build()
+ .parse();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/FcdaCandidates.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/FcdaCandidates.java
index 32d4b17f9..0858f28e4 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/FcdaCandidates.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/FcdaCandidates.java
@@ -4,28 +4,19 @@
package org.lfenergy.compas.sct.commons.util;
-import com.opencsv.bean.ColumnPositionMappingStrategy;
import com.opencsv.bean.CsvBindByPosition;
-import com.opencsv.bean.CsvToBeanBuilder;
-import com.opencsv.enums.CSVReaderNullFieldIndicator;
import lombok.*;
import org.apache.commons.lang3.StringUtils;
import org.lfenergy.compas.sct.commons.dto.ResumedDataTemplate;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
public enum FcdaCandidates {
SINGLETON;
private static final String FCDA_CONSTRAINTS_FILE_NAME = "FcdaCandidates.csv";
- private static final char SEPARATOR = ';';
- public static final int HEADER_LINES = 5;
private Set candidates;
@@ -51,30 +42,11 @@ boolean contains(String lnClass, String doName, String daName, String fc) {
if (candidates == null) {
// using a HashSet because "HashSet.contains" is faster than "ArrayList.contains"
- candidates = new HashSet<>(readFcdaCandidatesFile());
+ candidates = new HashSet<>(CsvUtils.parseRows(FCDA_CONSTRAINTS_FILE_NAME, StandardCharsets.UTF_8, FcdaCandidate.class));
}
return candidates.contains(new FcdaCandidate(lnClass, doName, daName, fc));
}
- private List readFcdaCandidatesFile() {
- try (InputStreamReader inputStreamReader = new InputStreamReader(
- getClass().getClassLoader().getResourceAsStream(FCDA_CONSTRAINTS_FILE_NAME), StandardCharsets.UTF_8)) {
- ColumnPositionMappingStrategy columnPositionMappingStrategy = new ColumnPositionMappingStrategy<>();
- columnPositionMappingStrategy.setType(FcdaCandidate.class);
- return new CsvToBeanBuilder(inputStreamReader)
- .withSeparator(SEPARATOR)
- .withIgnoreLeadingWhiteSpace(true)
- .withSkipLines(HEADER_LINES)
- .withType(FcdaCandidate.class)
- .withFieldAsNull(CSVReaderNullFieldIndicator.EMPTY_SEPARATORS)
- .withMappingStrategy(columnPositionMappingStrategy)
- .build()
- .parse();
- } catch (IOException e) {
- throw new UncheckedIOException("Error when closing file " + FCDA_CONSTRAINTS_FILE_NAME, e);
- }
- }
-
@NoArgsConstructor
@AllArgsConstructor
@Getter
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/SclConstructorHelper.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/SclConstructorHelper.java
new file mode 100644
index 000000000..ce0d5af4e
--- /dev/null
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/SclConstructorHelper.java
@@ -0,0 +1,93 @@
+/*
+ * // SPDX-FileCopyrightText: 2022 RTE FRANCE
+ * //
+ * // SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lfenergy.compas.sct.commons.util;
+
+import lombok.experimental.UtilityClass;
+import org.lfenergy.compas.scl2007b4.model.TAddress;
+import org.lfenergy.compas.scl2007b4.model.TConnectedAP;
+import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec;
+import org.lfenergy.compas.scl2007b4.model.TP;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * A helper class that provides constructors to easily create instances of Jaxb classes generated by xjc.
+ * xjc only provides NoArgConstructors on generated classes.
+ * This utility class provides constructors with the most relevant attributes.
+ *
+ */
+@UtilityClass
+public class SclConstructorHelper {
+
+ private static final String SECOND = "s";
+ private static final String MILLI = "m";
+
+ /**
+ * Create new instance of TP
+ * @param type type attribute
+ * @param value value attribute
+ * @return new instance of TP initialized with the given parameters
+ */
+ public static TP newP(String type, String value){
+ TP tp = new TP();
+ tp.setType(type);
+ tp.setValue(value);
+ return tp;
+ }
+
+ /**
+ * Create new instance of TDurationInMilliSec
+ * @param value value attribute
+ * @param unit unit attribute
+ * @param multiplier multiplier attribute
+ * @return new instance of TDurationInMilliSec initialized with the given parameters
+ */
+ public static TDurationInMilliSec newDurationInMilliSec(BigDecimal value, String unit, String multiplier) {
+ TDurationInMilliSec minTime = new TDurationInMilliSec();
+ minTime.setUnit(unit);
+ minTime.setMultiplier(multiplier);
+ minTime.setValue(value);
+ return minTime;
+ }
+
+ /**
+ * Create new instance of TDurationInMilliSec with unit SECOND and multiplier MILLI.
+ * @param value value attribute
+ * @return new instance of TDurationInMilliSec initialized with the given parameters
+ * @see #newDurationInMilliSec(BigDecimal, String, String)
+ */
+ public static TDurationInMilliSec newDurationInMilliSec(long value) {
+ return newDurationInMilliSec(new BigDecimal(value), SECOND, MILLI);
+ }
+
+ /**
+ * Create new instance of TAddress
+ * @param listOfP list of TP for setting the P attribute of the new TAddress
+ * @return new instance of TAddress initialized with the given parameters.
+ * P attribute of TAddress is a new list which contains the elements of listOfP (the given TP are not cloned).
+ */
+ public static TAddress newAddress(List listOfP) {
+ TAddress address = new TAddress();
+ address.getP().addAll(listOfP);
+ return address;
+ }
+
+ /**
+ * Create new instance of TConnectedAP
+ * @param iedName iedName attribute
+ * @param apName apName attribute
+ * @return new instance of TConnectedAP initialized with the given parameters
+ */
+ public static TConnectedAP newConnectedAp(String iedName, String apName) {
+ TConnectedAP tConnectedAP = new TConnectedAP();
+ tConnectedAP.setIedName(iedName);
+ tConnectedAP.setApName(apName);
+ return tConnectedAP;
+ }
+
+}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java
index cfa6755e0..d976a3603 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java
@@ -4,11 +4,29 @@
package org.lfenergy.compas.sct.commons.util;
+import lombok.Getter;
+import org.lfenergy.compas.scl2007b4.model.TServiceType;
+
+@Getter
public enum ServicesConfigEnum {
- GSE,
- SMV,
- REPORT,
- DATASET,
- FCDA;
+ GSE("GOOSE Control Block"),
+ SMV("SMV Control Block"),
+ REPORT("Report Control Block"),
+ DATASET("DataSet"),
+ FCDA("FCDA");
+
+ private final String displayName;
+
+ ServicesConfigEnum(String displayName) {
+ this.displayName = displayName;
+ }
+ public static ServicesConfigEnum from(TServiceType tServiceType){
+ return switch (tServiceType){
+ case GOOSE -> GSE;
+ case SMV -> SMV;
+ case REPORT -> REPORT;
+ default -> throw new IllegalArgumentException("Unsupported TServiceType " + tServiceType);
+ };
+ }
}
diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java
index 0b5da5b75..f69176a2d 100644
--- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java
+++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java
@@ -6,13 +6,12 @@
import org.apache.commons.lang3.StringUtils;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.LongStream;
public final class Utils {
@@ -21,6 +20,8 @@ public final class Utils {
private static final int S1_CONSIDERED_EQUALS_TO_S2 = 0;
private static final int S1_LOWER_THAN_S2 = -1;
private static final int S1_GREATER_THAN_S2 = 1;
+ private static final long MAC_ADDRESS_MAX_VALUE = 0xFFFFFFFFFFFFL;
+ private static final Pattern MAC_ADDRESS_PATTERN = Pattern.compile("[0-9A-F]{2}([-:][0-9A-F]{2}){5}", Pattern.CASE_INSENSITIVE);
/**
* Private Constructor, should not be instanced
@@ -253,4 +254,103 @@ public static boolean lnClassEquals(List lnClass1, String lnClass2){
}
return equalsOrBothBlank(lnClass1.get(0), lnClass2);
}
+
+ /**
+ * Create a new Iterator that provides all ordered long value in the given range
+ * @param startInclusive the first long in the range (inclusive)
+ * @param endInclusive the last long in the range (inclusive). Cannot exceed Long.MAX_VALUE - 1.
+ * @return new Iterator. When endInclusive < startInclusive, return an empty iterator.
+ */
+ public static PrimitiveIterator.OfLong sequence(long startInclusive, long endInclusive) {
+ if (endInclusive >= Long.MAX_VALUE){
+ throw new IllegalArgumentException("End cannot exceed Long.MAX_VALUE - 1");
+ }
+ return LongStream.range(startInclusive, endInclusive + 1).iterator();
+ }
+
+ /**
+ * Create a new Iterator that provides all ordered MAC-Addresses value included in given range.
+ * See macAddressToLong for the format of MAC-Addresses range,
+ * and see longToMacAddress for the format of MAC-Addresses output
+ * @param startInclusive the first MAC-Address in the range (inclusive)
+ * @param endInclusive the last MAC-Address in the range (inclusive)
+ * @return new Iterator
+ *
+ * @see Utils#macAddressToLong(String)
+ * @see Utils#longToMacAddress(long)
+ */
+ public static Iterator macAddressSequence(String startInclusive, String endInclusive) {
+ PrimitiveIterator.OfLong iterator = sequence(macAddressToLong(startInclusive), macAddressToLong(endInclusive));
+ return new Iterator<>() {
+ @Override
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ @Override
+ public String next() {
+ return longToMacAddress(iterator.next());
+ }
+ };
+ }
+
+ /**
+ * Converts long representation of a MAC-Address, by converting it to hexadecimal and separating every 2 characters by a hyphen(-).
+ * See macAddressToLong for the reversing method.
+ * @param macAddress a long between 0 and 0xFFFFFFFFFFFF
+ * @return MAC address separated by hyphens(-). Letters are uppercase (A to F)
+ *
+ * @see Utils#macAddressToLong(String)
+ */
+ public static String longToMacAddress(long macAddress){
+ if (macAddress < 0){
+ throw new IllegalArgumentException("macAddress must be a positive integer but got : %d".formatted(macAddress));
+ }
+ if (macAddress > MAC_ADDRESS_MAX_VALUE){
+ throw new IllegalArgumentException("macAddress cannot exceed %d but got : %d".formatted(MAC_ADDRESS_MAX_VALUE, macAddress));
+ }
+ String macText = toHex(macAddress, 12);
+ return
+ macText.substring(0, 2) + "-"
+ + macText.substring(2, 4) + "-"
+ + macText.substring(4, 6) + "-"
+ + macText.substring(6, 8) + "-"
+ + macText.substring(8, 10) + "-"
+ + macText.substring(10, 12);
+ }
+
+ /**
+ * Converts a MAC-Address to its long representation, by concatenating the digits and converting it from hexadecimal to long.
+ * See longToMacAddress for the reversing method.
+ * @param macAddress macAddress should be 6 groups of 2 hexadecimal digits (0 to 9 and A to F or a to f) separated by hyphens(–) or
+ * colons(:)
+ * @return long between 0 and 0xFFFFFFFFFFFF representing this MAC-Address
+ *
+ * @see Utils#longToMacAddress(long)
+ */
+ public static long macAddressToLong(String macAddress){
+ if (!MAC_ADDRESS_PATTERN.matcher(macAddress).matches()) {
+ throw new IllegalArgumentException(("macAddress should be 6 groups of 2 hexadecimal digits (0 to 9 and A to F) separated by hyphens(–) "
+ + "or colons(:), but got : %s").formatted(macAddress));
+ }
+ String hex = macAddress.substring(0, 2)
+ + macAddress.substring(3, 5)
+ + macAddress.substring(6, 8)
+ + macAddress.substring(9, 11)
+ + macAddress.substring(12, 14)
+ + macAddress.substring(15, 17);
+ return Long.valueOf(hex, 16);
+ }
+
+ /**
+ * Convert number to hexadecimal, with uppercase letters (A to F) and a minimum length (using left padding with zero when necessary).
+ * @param number number to be converted in hexadecimal
+ * @param length minimum length of resulting string.
+ * When hexadecimal form of number does not reach length, left padding with "0" is done.
+ * @return hexadecimal, with uppercase letters (A to F) and minimum length of length.
+ * Note that the length of return value can exceed "length" parameter when number hexadecimal form is longer than "length" parameter.
+ */
+ public static String toHex(long number, int length) {
+ return StringUtils.leftPad(Long.toHexString(number).toUpperCase(), length, "0");
+ }
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettingsTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettingsTest.java
new file mode 100644
index 000000000..6987bd3c8
--- /dev/null
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettingsTest.java
@@ -0,0 +1,178 @@
+// SPDX-FileCopyrightText: 2023 RTE FRANCE
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfenergy.compas.sct.commons.dto;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.lfenergy.compas.scl2007b4.model.SCL;
+import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec;
+import org.lfenergy.compas.sct.commons.scl.PrivateService;
+import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
+import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter;
+import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
+import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
+import org.lfenergy.compas.sct.commons.util.ControlBlockNetworkSettingsCsvHelper;
+import org.lfenergy.compas.sct.commons.util.CsvUtils;
+import org.lfenergy.compas.sct.commons.util.PrivateEnum;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.util.Objects;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.Settings;
+import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findControlBlock;
+import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findIed;
+
+class ControlBlockNetworkSettingsTest {
+
+ private ControlBlockNetworkSettings controlBlockNetworkSettings;
+
+ @BeforeEach
+ public void setUp() {
+ String fileName = "ControlBlockCommunicationTemplates.csv";
+ InputStream inputStream = Objects.requireNonNull(CsvUtils.class.getClassLoader().getResourceAsStream(fileName), "Resource not found: " + fileName);
+ InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
+ controlBlockNetworkSettings = new ControlBlockNetworkSettingsCsvHelper(inputStreamReader);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ ";01.00;009.001;BCU;A;BAY_INTERNAL;300;4;10;2000",
+ "GOOSE;;009.001;BCU;A;BAY_INTERNAL;300;4;10;2000",
+ "GOOSE;01.00;;BCU;A;BAY_INTERNAL;300;4;10;2000",
+ "GOOSE;01.00;009.001;;A;BAY_INTERNAL;300;4;10;2000",
+ "GOOSE;01.00;009.001;BCU;;BAY_INTERNAL;300;4;10;2000",
+ "GOOSE;01.00;009.001;BCU;A;;300;4;10;2000"
+ })
+ void constructor_when_csv_has_blank_criteria_cells_should_throw_exception(String row) {
+ //Given
+ StringReader stringReader = new StringReader(row);
+ //When & Then
+ assertThatThrownBy(() -> new ControlBlockNetworkSettingsCsvHelper(stringReader))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "GOOSE;01.00;009.001;BCU;A;BAY_INTERNAL;XXX;4;10;2000",
+ "GOOSE;01.00;009.001;BCU;A;BAY_INTERNAL;300;XXX;10;2000",
+ "GOOSE;01.00;009.001;BCU;A;BAY_INTERNAL;300;4;XXX;2000",
+ "GOOSE;01.00;009.001;BCU;A;BAY_INTERNAL;300;4;10;XXX"
+ })
+ void constructor_when_csv_has_malformed_numbers_should_throw_exception(String row) {
+ //Given
+ StringReader stringReader = new StringReader(row);
+ //When & Then
+ assertThatThrownBy(() -> new ControlBlockNetworkSettingsCsvHelper(stringReader))
+ .isInstanceOf(NumberFormatException.class);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "GOOSE;01.00;009.001;BCU;A;BAY_INTERNAL;4096;4;10;2000", // VlanId > MAX_VLAN_ID
+ "GOOSE;01.00;009.001;BCU;A;BAY_INTERNAL;300;8;10;2000" // VlanPriority > MAX_VLAN_PRIORITY
+ })
+ void constructor_when_csv_has_numbers_our_out_of_bound_should_throw_exception(String row) {
+ //Given
+ StringReader stringReader = new StringReader(row);
+ //When & Then
+ assertThatThrownBy(() -> new ControlBlockNetworkSettingsCsvHelper(stringReader))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @Test
+ void getNetworkSettings_should_return_settings_for_bay_internal_controlBlock() {
+ //Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ ControlBlockAdapter controlBlockAdapter = findControlBlock(sclRootAdapter, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", ControlBlockEnum.GSE);
+
+ //When
+ ControlBlockNetworkSettings.Settings networkSettings = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter);
+
+ //Then
+ assertThat(networkSettings)
+ .extracting(Settings::vlanId, Settings::vlanPriority)
+ .containsExactly(300, (byte) 4);
+ assertThat(networkSettings.minTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("10"));
+ assertThat(networkSettings.maxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("2000"));
+ }
+
+ @Test
+ void getNetworkSettings_should_return_settings_for_bay_external_controlBlock() {
+ //Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ ControlBlockAdapter controlBlockAdapter = findControlBlock(sclRootAdapter, "IED_NAME3", "LD_INST31", "CB_LD_INST31_GSE", ControlBlockEnum.GSE);
+
+ //When
+ ControlBlockNetworkSettings.Settings networkSettings = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter);
+
+ //Then
+ assertThat(networkSettings)
+ .extracting(Settings::vlanId, Settings::vlanPriority)
+ .containsExactly(301, (byte) 5);
+ assertThat(networkSettings.minTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("15"));
+ assertThat(networkSettings.maxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("5000"));
+ }
+
+ @Test
+ void getNetworkSettings_should_return_vlanId_null_when_column_contains_none() {
+ //Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ ControlBlockAdapter controlBlockAdapter = findControlBlock(sclRootAdapter, "IED_NAME2", "LD_INST21", "CB_LD_INST21_SVI", ControlBlockEnum.SAMPLED_VALUE);
+
+ //When
+ ControlBlockNetworkSettings.Settings networkSettings = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter);
+
+ //Then
+ assertThat(networkSettings.vlanId()).isNull();
+ assertThat(networkSettings.vlanPriority()).isNull();
+ }
+
+ @Test
+ void getNetworkSettings_should_return_null_when_row_not_found_in_csv_file() {
+ //Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ findIed(sclRootAdapter, "IED_NAME2").getCompasSystemVersion().get().setMainSystemVersion("99.99");
+ ControlBlockAdapter controlBlockAdapter = findControlBlock(sclRootAdapter, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", ControlBlockEnum.GSE);
+
+ //When
+ ControlBlockNetworkSettings.Settings networkSettings = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter);
+
+ //Then
+ assertThat(networkSettings).isNull();
+ }
+
+ @ParameterizedTest
+ @EnumSource(value = PrivateEnum.class, mode = EnumSource.Mode.INCLUDE, names = {"COMPAS_ICDHEADER", "COMPAS_SYSTEM_VERSION"})
+ void getNetworkSettings_should_return_null_when_missing_ied_private(PrivateEnum privateEnum) {
+ //Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ PrivateService.removePrivates(findIed(sclRootAdapter, "IED_NAME2").getCurrentElem(), privateEnum);
+ ControlBlockAdapter controlBlockAdapter = findControlBlock(sclRootAdapter, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", ControlBlockEnum.GSE);
+
+ //When
+ ControlBlockNetworkSettings.Settings networkSettings = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter);
+
+ //Then
+ assertThat(networkSettings).isNull();
+ }
+
+}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/DTO.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/DTO.java
index 194be6745..fb54f52c0 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/DTO.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/DTO.java
@@ -18,6 +18,7 @@ public class DTO {
/* ConnectedAPDTO */
/*-----------------------------------------------*/
public static final String AP_NAME = "AP_NAME";
+ public static final String AP_NAME_2 = "AP_NAME_2";
public static ConnectedApDTO createCapDTO() {
@@ -32,6 +33,7 @@ public static ConnectedApDTO createCapDTO() {
/* ExtRefInfo */
/*-----------------------------------------------*/
public static final String HOLDER_IED_NAME = "IED_NAME";
+ public static final String HOLDER_IED_NAME_2 = "IED_NAME_2";
public static final String HOLDER_LD_INST = "LD_INST_H";
public static final String HOLDER_LN_INST = "1";
public static final String HOLDER_LN_CLASS = "LN_CLASS_H";
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java
index 7855773f3..46d59cf8d 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java
@@ -10,6 +10,7 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.lfenergy.compas.scl2007b4.model.*;
+import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings;
import org.lfenergy.compas.sct.commons.dto.ControlBlockTarget;
import org.lfenergy.compas.sct.commons.dto.SclReport;
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
@@ -21,7 +22,7 @@
import org.lfenergy.compas.sct.commons.testhelpers.MarshallerWrapper;
import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
-import java.util.Collections;
+import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@@ -29,11 +30,21 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.lfenergy.compas.scl2007b4.model.TFCEnum.ST;
+import static org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.*;
import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*;
import static org.lfenergy.compas.sct.commons.util.ControlBlockEnum.*;
+import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newDurationInMilliSec;
class ExtRefServiceTest {
+ private static final long GSE_APP_ID_MIN = 0x9;
+ private static final long SMV_APP_ID_MIN = 0x400A;
+ private static final String GSE_MAC_ADDRESS_PREFIX = "01-02-03-04-";
+ private static final String SMV_MAC_ADDRESS_PREFIX = "0A-0B-0C-0D-";
+ private static final NetworkRanges GSE_NETWORK_RANGES = new NetworkRanges(GSE_APP_ID_MIN, GSE_APP_ID_MIN + 10, GSE_MAC_ADDRESS_PREFIX + "00-FF", GSE_MAC_ADDRESS_PREFIX + "01-AA");
+ private static final NetworkRanges SMV_NETWORK_RANGES = new NetworkRanges(SMV_APP_ID_MIN, SMV_APP_ID_MIN + 10, SMV_MAC_ADDRESS_PREFIX + "00-FF", SMV_MAC_ADDRESS_PREFIX + "01-AA");
+ private static final RangesPerCbType RANGES_PER_CB_TYPE = new RangesPerCbType(GSE_NETWORK_RANGES, SMV_NETWORK_RANGES);
+
@Test
void updateAllExtRefIedNames_should_update_iedName_and_ExtRefIedName() {
// Given : An ExtRef with a matching compas:Flow
@@ -45,12 +56,12 @@ void updateAllExtRefIedNames_should_update_iedName_and_ExtRefIedName() {
assertThat(extRef.getIedName()).isEqualTo("IED_NAME2");
TInputs inputs = findLDevice(sclReport, "IED_NAME1", "LD_INST11")
- .getLN0Adapter()
- .getCurrentElem()
- .getInputs();
+ .getLN0Adapter()
+ .getCurrentElem()
+ .getInputs();
assertThat(PrivateService.extractCompasPrivate(inputs, TCompasFlow.class))
- .map(TCompasFlow::getExtRefiedName)
- .hasValue("IED_NAME2");
+ .map(TCompasFlow::getExtRefiedName)
+ .hasValue("IED_NAME2");
}
@Test
@@ -61,8 +72,8 @@ void updateAllExtRefIedNames_should_return_success_status() {
SclReport sclReport = ExtRefService.updateAllExtRefIedNames(scd);
// Then
assertThat(sclReport.isSuccess())
- .overridingErrorMessage(String.valueOf(sclReport.getSclReportItems()))
- .isTrue();
+ .overridingErrorMessage(String.valueOf(sclReport.getSclReportItems()))
+ .isTrue();
}
@ParameterizedTest(name = "{0}")
@@ -79,64 +90,64 @@ void updateAllExtRefIedNames_should_report_errors(String testCase, SCL scl, SclR
public static Stream updateAllExtRefIedNamesErrors() {
return
- Stream.of(Arguments.of(
- "Errors on ExtRefs",
- SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml"),
- new SclReportItem[]{
- SclReportItem.fatal(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
- "/LN0/Inputs/ExtRef[@desc=\"No matching compas:Flow\"]",
- "The signal ExtRef has no matching compas:Flow Private"),
- SclReportItem.fatal(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
- "/LN0/Inputs/ExtRef[@desc=\"Matching two compas:Flow\"]",
- "The signal ExtRef has more than one matching compas:Flow Private"),
- SclReportItem.fatal(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST13\"]",
- "The LDevice status is neither \"on\" nor \"off\""),
- SclReportItem.fatal(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST14\"]",
- "The LDevice status is undefined"),
- SclReportItem.warning(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
- "/LN0/Inputs/ExtRef[@desc=\"ExtRef does not match any ICDSystemVersionUUID\"]",
- "The signal ExtRef iedName does not match any IED/Private/compas:ICDHeader@ICDSystemVersionUUID"),
- SclReportItem.warning(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
- "/LN0/Inputs/ExtRef[@desc=\"ExtRefldinst does not match any LDevice inst in source IED\"]",
- "The signal ExtRef ExtRefldinst does not match any LDevice with same inst attribute in source IED /SCL/IED[@name=\"IED_NAME2\"]"),
- SclReportItem.warning(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
- "/LN0/Inputs/ExtRef[@desc=\"ExtRef does not match any LN in source LDevice\"]",
- "The signal ExtRef lninst, doName or daName does not match any source in LDevice " +
- "/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST21\"]"),
- SclReportItem.warning(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
- "/LN0/Inputs/ExtRef[@desc=\"Source LDevice is off for this ExtRef\"]",
- "The signal ExtRef source LDevice /SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST22\"] status is off"),
- SclReportItem.fatal(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
- "/LN0/Inputs/ExtRef[@desc=\"Source LDevice is undefined for this ExtRef\"]",
- "The signal ExtRef source LDevice /SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST23\"] status is " +
- "undefined"),
- SclReportItem.fatal(
- "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
- "/LN0/Inputs/ExtRef[@desc=\"Source LDevice is neither on nor off for this ExtRef\"]",
- "The signal ExtRef source LDevice /SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST24\"] " +
- "status is neither \"on\" nor \"off\"")
- }),
- Arguments.of(
- "Errors on IEDs",
- SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_ied_errors.xml"),
- new SclReportItem[]{
- SclReportItem.fatal(
- "/SCL/IED[@name=\"IED_NAME1\"], /SCL/IED[@name=\"IED_NAME2\"]",
- "/IED/Private/compas:ICDHeader[@ICDSystemVersionUUID] must be unique but the same ICDSystemVersionUUID was found on several IED."),
- SclReportItem.fatal("/SCL/IED[@name=\"IED_NAME3\"]", "IED has no Private COMPAS-ICDHeader element"),
- SclReportItem.fatal("/SCL/IED[@name=\"IED_NAME4\"]", "IED private COMPAS-ICDHeader as no icdSystemVersionUUID or iedName attribute"),
- SclReportItem.fatal("/SCL/IED[@name=\"IED_NAME5\"]", "IED private COMPAS-ICDHeader as no icdSystemVersionUUID or iedName attribute")
- })
- );
+ Stream.of(Arguments.of(
+ "Errors on ExtRefs",
+ SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml"),
+ new SclReportItem[]{
+ SclReportItem.fatal(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
+ "/LN0/Inputs/ExtRef[@desc=\"No matching compas:Flow\"]",
+ "The signal ExtRef has no matching compas:Flow Private"),
+ SclReportItem.fatal(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
+ "/LN0/Inputs/ExtRef[@desc=\"Matching two compas:Flow\"]",
+ "The signal ExtRef has more than one matching compas:Flow Private"),
+ SclReportItem.fatal(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST13\"]",
+ "The LDevice status is neither \"on\" nor \"off\""),
+ SclReportItem.fatal(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST14\"]",
+ "The LDevice status is undefined"),
+ SclReportItem.warning(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
+ "/LN0/Inputs/ExtRef[@desc=\"ExtRef does not match any ICDSystemVersionUUID\"]",
+ "The signal ExtRef iedName does not match any IED/Private/compas:ICDHeader@ICDSystemVersionUUID"),
+ SclReportItem.warning(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
+ "/LN0/Inputs/ExtRef[@desc=\"ExtRefldinst does not match any LDevice inst in source IED\"]",
+ "The signal ExtRef ExtRefldinst does not match any LDevice with same inst attribute in source IED /SCL/IED[@name=\"IED_NAME2\"]"),
+ SclReportItem.warning(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
+ "/LN0/Inputs/ExtRef[@desc=\"ExtRef does not match any LN in source LDevice\"]",
+ "The signal ExtRef lninst, doName or daName does not match any source in LDevice " +
+ "/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST21\"]"),
+ SclReportItem.warning(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
+ "/LN0/Inputs/ExtRef[@desc=\"Source LDevice is off for this ExtRef\"]",
+ "The signal ExtRef source LDevice /SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST22\"] status is off"),
+ SclReportItem.fatal(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
+ "/LN0/Inputs/ExtRef[@desc=\"Source LDevice is undefined for this ExtRef\"]",
+ "The signal ExtRef source LDevice /SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST23\"] status is " +
+ "undefined"),
+ SclReportItem.fatal(
+ "/SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST11\"]" +
+ "/LN0/Inputs/ExtRef[@desc=\"Source LDevice is neither on nor off for this ExtRef\"]",
+ "The signal ExtRef source LDevice /SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST24\"] " +
+ "status is neither \"on\" nor \"off\"")
+ }),
+ Arguments.of(
+ "Errors on IEDs",
+ SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_ied_errors.xml"),
+ new SclReportItem[]{
+ SclReportItem.fatal(
+ "/SCL/IED[@name=\"IED_NAME1\"], /SCL/IED[@name=\"IED_NAME2\"]",
+ "/IED/Private/compas:ICDHeader[@ICDSystemVersionUUID] must be unique but the same ICDSystemVersionUUID was found on several IED."),
+ SclReportItem.fatal("/SCL/IED[@name=\"IED_NAME3\"]", "IED has no Private COMPAS-ICDHeader element"),
+ SclReportItem.fatal("/SCL/IED[@name=\"IED_NAME4\"]", "IED private COMPAS-ICDHeader as no icdSystemVersionUUID or iedName attribute"),
+ SclReportItem.fatal("/SCL/IED[@name=\"IED_NAME5\"]", "IED private COMPAS-ICDHeader as no icdSystemVersionUUID or iedName attribute")
+ })
+ );
}
@Test
@@ -146,28 +157,12 @@ void updateAllExtRefIedNames_when_not_bindable_should_clear_binding() {
// When
SclReport sclReport = ExtRefService.updateAllExtRefIedNames(scd);
// Then
- assertThatExtRefBindingInfoIsMissing(findExtRef(sclReport, "IED_NAME1", "LD_INST12", "ExtRef target LDevice status is off"));
- assertThatExtRefBindingInfoIsMissing(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "Match compas:Flow but FlowStatus is INACTIVE"));
- assertThatExtRefBindingInfoIsMissing(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "ExtRef does not match any ICDSystemVersionUUID"));
- assertThatExtRefBindingInfoIsMissing(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "ExtRefldinst does not match any LDevice inst in source IED"));
- assertThatExtRefBindingInfoIsMissing(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "ExtRef does not match any LN in source LDevice"));
- assertThatExtRefBindingInfoIsMissing(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "Source LDevice is off for this ExtRef"));
- }
-
- private void assertThatExtRefBindingInfoIsMissing(TExtRef extRef) {
- assertThat(extRef.isSetIedName()).isFalse();
- assertThat(extRef.isSetLdInst()).isFalse();
- assertThat(extRef.isSetPrefix()).isFalse();
- assertThat(extRef.isSetLnClass()).isFalse();
- assertThat(extRef.isSetLnInst()).isFalse();
- assertThat(extRef.isSetDoName()).isFalse();
- assertThat(extRef.isSetDaName()).isFalse();
- assertThat(extRef.isSetServiceType()).isFalse();
- assertThat(extRef.isSetSrcLDInst()).isFalse();
- assertThat(extRef.isSetPrefix()).isFalse();
- assertThat(extRef.isSetSrcLNClass()).isFalse();
- assertThat(extRef.isSetLnInst()).isFalse();
- assertThat(extRef.isSetSrcCBName()).isFalse();
+ assertExtRefIsNotBound(findExtRef(sclReport, "IED_NAME1", "LD_INST12", "ExtRef target LDevice status is off"));
+ assertExtRefIsNotBound(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "Match compas:Flow but FlowStatus is INACTIVE"));
+ assertExtRefIsNotBound(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "ExtRef does not match any ICDSystemVersionUUID"));
+ assertExtRefIsNotBound(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "ExtRefldinst does not match any LDevice inst in source IED"));
+ assertExtRefIsNotBound(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "ExtRef does not match any LN in source LDevice"));
+ assertExtRefIsNotBound(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "Source LDevice is off for this ExtRef"));
}
@Test
@@ -181,7 +176,7 @@ void updateAllExtRefIedNames_when_lDevice_off_should_remove_binding() {
LDeviceAdapter lDeviceAdapter = findLDeviceByLdName(sclReport.getSclRootAdapter(), "IED_NAME1LD_INST12");
assertThat(lDeviceAdapter.getLDeviceStatus()).hasValue("off");
assertThat(lDeviceAdapter.getLN0Adapter().getInputsAdapter().getCurrentElem().getExtRef())
- .allSatisfy(this::assertExtRefIsNotBound);
+ .allSatisfy(this::assertExtRefIsNotBound);
}
@Test
@@ -195,8 +190,8 @@ void updateAllExtRefIedNames_when_FlowStatus_INACTIVE_should_remove_binding() {
LDeviceAdapter lDeviceAdapter = findLDeviceByLdName(sclReport.getSclRootAdapter(), "IED_NAME1LD_INST11");
assertThat(lDeviceAdapter.getLDeviceStatus()).hasValue("on");
Optional optionalTExtRef = lDeviceAdapter.getCurrentElem().getLN0().getInputs().getExtRef().stream()
- .filter(tExtRef -> "Match compas:Flow but FlowStatus is INACTIVE".equals(tExtRef.getDesc()))
- .findFirst();
+ .filter(tExtRef -> "Match compas:Flow but FlowStatus is INACTIVE".equals(tExtRef.getDesc()))
+ .findFirst();
assertThat(optionalTExtRef).isPresent();
TExtRef extRef = optionalTExtRef.get();
assertExtRefIsNotBound(extRef);
@@ -224,12 +219,12 @@ void createDataSetAndControlBlocks_should_create_DataSet() {
DataSetAdapter aDataSet = findDataSet(sclReport.getSclRootAdapter(), "IED_NAME2", "LD_INST21", "DS_LD_INST21_GSI");
assertThat(aDataSet.getCurrentElem().getFCDA()).hasSize(4);
assertThat(aDataSet.getCurrentElem().getFCDA().stream().map(FCDARecord::toFCDARecord))
- .containsExactly(
- new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", "daNameST", ST),
- new FCDARecord("LD_INST21", "ANCR", "1", "", "DoWithInst1", "daNameST", ST),
- new FCDARecord("LD_INST21", "ANCR", "1", "", "DoWithInst2.subDo", "daNameST", ST),
- new FCDARecord("LD_INST21", "ANCR", "1", "", "OtherDoName", "daNameST", ST)
- );
+ .containsExactly(
+ new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", "daNameST", ST),
+ new FCDARecord("LD_INST21", "ANCR", "1", "", "DoWithInst1", "daNameST", ST),
+ new FCDARecord("LD_INST21", "ANCR", "1", "", "DoWithInst2.subDo", "daNameST", ST),
+ new FCDARecord("LD_INST21", "ANCR", "1", "", "OtherDoName", "daNameST", ST)
+ );
}
@@ -255,17 +250,17 @@ void createDataSetAndControlBlocks_should_create_ControlBlocks() {
assertThat(reportControlBlock.getCurrentElem()).isInstanceOf(TReportControl.class);
TReportControl tReportControl = (TReportControl) reportControlBlock.getCurrentElem();
assertThat(tReportControl).extracting(TReportControl::getConfRev, TReportControl::isBuffered, TReportControl::getBufTime, TReportControl::isIndexed,
- TControlWithTriggerOpt::getIntgPd)
- .containsExactly(1L, true, 0L, true, 60000L);
+ TControlWithTriggerOpt::getIntgPd)
+ .containsExactly(1L, true, 0L, true, 60000L);
assertThat(tReportControl.getTrgOps())
- .extracting(TTrgOps::isDchg, TTrgOps::isQchg, TTrgOps::isDupd, TTrgOps::isPeriod, TTrgOps::isGi)
- .containsExactly(false, false, false, true, true);
+ .extracting(TTrgOps::isDchg, TTrgOps::isQchg, TTrgOps::isDupd, TTrgOps::isPeriod, TTrgOps::isGi)
+ .containsExactly(false, false, false, true, true);
assertThat(tReportControl.getRptEnabled().getMax()).isEqualTo(1L);
assertThat(tReportControl.getRptEnabled().getClientLN().stream().map(ControlBlockTarget::from))
- .containsExactly(
- new ControlBlockTarget("AP_NAME", "IED_NAME1", "LD_INST11", "", "LLN0", "", ""));
+ .containsExactly(
+ new ControlBlockTarget("AP_NAME", "IED_NAME1", "LD_INST11", "", "LLN0", "", ""));
}
@Test
@@ -279,22 +274,22 @@ void createDataSetAndControlBlocks_should_set_ExtRef_srcXXX_attributes() {
// assert all ExtRef.srcPrefix srcLNClass srcLNInst are not set
assertThat(streamAllExtRef(sclReport.getSclRootAdapter()))
- .extracting(TExtRef::getSrcPrefix, TExtRef::getSrcLNClass, TExtRef::getSrcLNInst)
- .containsOnly(Tuple.tuple(null, Collections.emptyList(), null));
+ .extracting(TExtRef::getSrcPrefix, TExtRef::isSetSrcLNClass, TExtRef::getSrcLNInst)
+ .containsOnly(Tuple.tuple(null, false, null));
// check some ExtRef
assertThat(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "test bay internal"))
- .extracting(TExtRef::getSrcCBName, TExtRef::getSrcLDInst)
- .containsExactly("CB_LD_INST21_GSI", "LD_INST21");
+ .extracting(TExtRef::getSrcCBName, TExtRef::getSrcLDInst)
+ .containsExactly("CB_LD_INST21_GSI", "LD_INST21");
assertThat(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "test bay external"))
- .extracting(TExtRef::getSrcCBName, TExtRef::getSrcLDInst)
- .containsExactly("CB_LD_INST31_GSE", "LD_INST31");
+ .extracting(TExtRef::getSrcCBName, TExtRef::getSrcLDInst)
+ .containsExactly("CB_LD_INST31_GSE", "LD_INST31");
assertThat(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "test ServiceType is SMV, no daName and DO contains ST and MX, but only ST is FCDA candidate"))
- .extracting(TExtRef::getSrcCBName, TExtRef::getSrcLDInst)
- .containsExactly("CB_LD_INST21_SVI", "LD_INST21");
+ .extracting(TExtRef::getSrcCBName, TExtRef::getSrcLDInst)
+ .containsExactly("CB_LD_INST21_SVI", "LD_INST21");
assertThat(findExtRef(sclReport, "IED_NAME1", "LD_INST11", "test ServiceType is Report_daReportMX_1"))
- .extracting(TExtRef::getSrcCBName, TExtRef::getSrcLDInst)
- .containsExactly("CB_LD_INST21_CYCI", "LD_INST21");
+ .extracting(TExtRef::getSrcCBName, TExtRef::getSrcLDInst)
+ .containsExactly("CB_LD_INST21_CYCI", "LD_INST21");
}
@Test
@@ -330,8 +325,8 @@ void createDataSetAndControlBlocks_when_targetIedName_is_not_found_should_throw_
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml");
// When & Then
assertThatThrownBy(() -> ExtRefService.createDataSetAndControlBlocks(scd, "non_existing_IED_name"))
- .isInstanceOf(ScdException.class)
- .hasMessage("IED.name 'non_existing_IED_name' not found in SCD");
+ .isInstanceOf(ScdException.class)
+ .hasMessage("IED.name 'non_existing_IED_name' not found in SCD");
}
@Test
@@ -350,8 +345,8 @@ void createDataSetAndControlBlocks_when_targetIedName_is_not_found_and_targetLDe
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml");
// When & Then
assertThatThrownBy(() -> ExtRefService.createDataSetAndControlBlocks(scd, "non_existing_IED_name", "LD_INST11"))
- .isInstanceOf(ScdException.class)
- .hasMessage("IED.name 'non_existing_IED_name' not found in SCD");
+ .isInstanceOf(ScdException.class)
+ .hasMessage("IED.name 'non_existing_IED_name' not found in SCD");
}
@Test
@@ -360,8 +355,8 @@ void createDataSetAndControlBlocks_when_targetIedName_and_targetLDeviceInst_is_n
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml");
// When & Then
assertThatThrownBy(() -> ExtRefService.createDataSetAndControlBlocks(scd, "IED_NAME1", "non_existing_LDevice_inst"))
- .isInstanceOf(ScdException.class)
- .hasMessage("LDevice.inst 'non_existing_LDevice_inst' not found in IED 'IED_NAME1'");
+ .isInstanceOf(ScdException.class)
+ .hasMessage("LDevice.inst 'non_existing_LDevice_inst' not found in IED 'IED_NAME1'");
}
@Test
@@ -370,8 +365,8 @@ void createDataSetAndControlBlocks_when_targetLDeviceInst_is_provided_without_ta
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml");
// When & Then
assertThatThrownBy(() -> ExtRefService.createDataSetAndControlBlocks(scd, null, "LD_INST11"))
- .isInstanceOf(ScdException.class)
- .hasMessage("IED.name parameter is missing");
+ .isInstanceOf(ScdException.class)
+ .hasMessage("IED.name parameter is missing");
}
private void assertExtRefIsNotBound(TExtRef extRef) {
@@ -401,13 +396,114 @@ void updateAllSourceDataSetsAndControlBlocks_should_sort_FCDA_inside_DataSet_and
assertThat(sclReport.getSclReportItems()).isEmpty();
DataSetAdapter dataSetAdapter = findDataSet(sclRootAdapter, "IED_NAME2", "LD_INST21", "DS_LD_INST21_GSI");
assertThat(dataSetAdapter.getCurrentElem().getFCDA())
- .map(TFCDA::getLnInst, TFCDA::getDoName)
- .containsExactly(
- Tuple.tuple("1", "FirstDo"),
- Tuple.tuple("1", "SecondDo"),
- Tuple.tuple("1", "ThirdDo"),
- Tuple.tuple("02", "FirstDo")
- );
+ .map(TFCDA::getLnInst, TFCDA::getDoName)
+ .containsExactly(
+ Tuple.tuple("1", "FirstDo"),
+ Tuple.tuple("1", "SecondDo"),
+ Tuple.tuple("1", "ThirdDo"),
+ Tuple.tuple("02", "FirstDo")
+ );
+ }
+
+ @Test
+ void configureNetworkForAllControlBlocks_should_create_GSE_and_SMV_elements() {
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
+
+ TDurationInMilliSec minTime = newDurationInMilliSec(10);
+ TDurationInMilliSec maxTime = newDurationInMilliSec(2000);
+ ControlBlockNetworkSettings controlBlockNetworkSettings = controlBlockAdapter -> new Settings(0x1D6, (byte) 4, minTime, maxTime);
+
+ // When
+ SclReport sclReport = ExtRefService.configureNetworkForAllControlBlocks(scd, controlBlockNetworkSettings, RANGES_PER_CB_TYPE);
+ // Then
+ assertThat(sclReport.isSuccess()).isTrue();
+ TConnectedAP connectedAP = sclReport.getSclRootAdapter().findConnectedApAdapter("IED_NAME2", "AP_NAME").get().getCurrentElem();
+ TGSE gse = connectedAP.getGSE().stream()
+ .filter(tgse -> "CB_LD_INST21_GSI".equals(tgse.getCbName()))
+ .findFirst().get();
+ assertThat(gse.getLdInst()).isEqualTo("LD_INST21");
+ assertThat(gse.getMinTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("10"));
+ assertThat(gse.getMaxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("2000"));
+ assertThat(gse.getAddress().getP()).extracting(TP::getType, TP::getValue)
+ .containsExactlyInAnyOrder(
+ Tuple.tuple("VLAN-PRIORITY", "4"),
+ Tuple.tuple("APPID", "0009"),
+ Tuple.tuple("MAC-Address", "01-02-03-04-00-FF"),
+ Tuple.tuple("VLAN-ID", "1D6")
+ );
+ TSMV smv = connectedAP.getSMV().stream()
+ .filter(tsmv -> "CB_LD_INST21_SVI".equals(tsmv.getCbName()))
+ .findFirst().get();
+ assertThat(smv.getLdInst()).isEqualTo("LD_INST21");
+ assertThat(smv.getAddress().getP()).extracting(TP::getType, TP::getValue)
+ .containsExactlyInAnyOrder(
+ Tuple.tuple("VLAN-PRIORITY", "4"),
+ Tuple.tuple("APPID", "400A"),
+ Tuple.tuple("MAC-Address", "0A-0B-0C-0D-00-FF"),
+ Tuple.tuple("VLAN-ID", "1D6")
+ );
+ MarshallerWrapper.assertValidateXmlSchema(scd);
+ }
+
+ @Test
+ void configureNetworkForAllControlBlocks_should_create_GSE_with_incremental_appid_and_mac_addresses() {
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
+
+ TDurationInMilliSec minTime = newDurationInMilliSec(10);
+ TDurationInMilliSec maxTime = newDurationInMilliSec(2000);
+ ControlBlockNetworkSettings controlBlockNetworkSettings = controlBlockAdapter -> new Settings(0x1D6, (byte) 4, minTime, maxTime);
+ // When
+ SclReport sclReport = ExtRefService.configureNetworkForAllControlBlocks(scd, controlBlockNetworkSettings, RANGES_PER_CB_TYPE);
+ // Then
+ assertThat(sclReport.isSuccess()).isTrue();
+ assertThat(streamAllConnectedApGseP(scd, "APPID"))
+ .containsExactlyInAnyOrder("0009", "000A", "000B");
+ assertThat(streamAllConnectedApGseP(scd, "MAC-Address"))
+ .containsExactlyInAnyOrder("01-02-03-04-00-FF", "01-02-03-04-01-00", "01-02-03-04-01-01");
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideConfigureNetworkForAllControlBlocksErrors")
+ void configureNetworkForAllControlBlocks_should_fail_when_no_settings_for_this_controlBlock(ControlBlockNetworkSettings controlBlockNetworkSettings,
+ RangesPerCbType rangesPerCbType,
+ String expectedMessage) {
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
+ // When
+ SclReport sclReport = ExtRefService.configureNetworkForAllControlBlocks(scd, controlBlockNetworkSettings, rangesPerCbType);
+ // Then
+ assertThat(sclReport.isSuccess()).isFalse();
+ assertThat(sclReport.getSclReportItems())
+ .extracting(SclReportItem::getMessage, SclReportItem::getXpath)
+ .contains(Tuple.tuple(expectedMessage,
+ "/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST21\"]/LN0/GSEControl[@name=\"CB_LD_INST21_GMI\"]"));
+ }
+
+ public static Stream provideConfigureNetworkForAllControlBlocksErrors() {
+ Settings settingsWithNullVlanId = new Settings(null, (byte) 1, newDurationInMilliSec(1), newDurationInMilliSec(2));
+ Settings settings = new Settings(1, (byte) 1, newDurationInMilliSec(1), newDurationInMilliSec(2));
+ return Stream.of(
+ Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> null,
+ RANGES_PER_CB_TYPE,
+ "Cannot configure network for this ControlBlock because no settings was provided"),
+ Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> settingsWithNullVlanId,
+ RANGES_PER_CB_TYPE,
+ "Cannot configure network for this ControlBlock because no Vlan Id was provided in the settings"),
+ Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> settings,
+ new RangesPerCbType(
+ new NetworkRanges(GSE_APP_ID_MIN, GSE_APP_ID_MIN, GSE_MAC_ADDRESS_PREFIX + "00-FF", GSE_MAC_ADDRESS_PREFIX + "01-AA"),
+ SMV_NETWORK_RANGES),
+ "Cannot configure network for this ControlBlock because range of appId is exhausted"),
+ Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> settings,
+ new RangesPerCbType(
+ new NetworkRanges(GSE_APP_ID_MIN, GSE_APP_ID_MIN + 10, GSE_MAC_ADDRESS_PREFIX + "00-FF", GSE_MAC_ADDRESS_PREFIX + "00-FF"),
+ SMV_NETWORK_RANGES),
+ "Cannot configure network for this ControlBlock because range of MAC Address is exhausted")
+ );
}
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java
index 0fd7e179c..456293765 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java
@@ -17,6 +17,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.math.BigInteger;
import java.util.*;
import java.util.stream.Stream;
@@ -479,7 +480,7 @@ void comparePrivateCompasICDHeaders_should_return_true_equality_not_check_for_IE
TCompasICDHeader compasICDHeader1 = new TCompasICDHeader();
compasICDHeader1.setIEDName("IED-1");
compasICDHeader1.setBayLabel("BAY-1");
- compasICDHeader1.setIEDinstance("1");
+ compasICDHeader1.setIEDSubstationinstance(BigInteger.ONE);
TCompasICDHeader compasICDHeader2 = new TCompasICDHeader();
TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1);
TPrivate tPrivate2 = PrivateService.createPrivate(compasICDHeader2);
@@ -496,7 +497,7 @@ void comparePrivateCompasICDHeaders_should_return_false_equality_not_check_for_I
TCompasICDHeader compasICDHeader1 = new TCompasICDHeader();
compasICDHeader1.setIEDName("IED-1");
compasICDHeader1.setBayLabel("BAY-1");
- compasICDHeader1.setIEDinstance("1");
+ compasICDHeader1.setIEDSubstationinstance(BigInteger.ONE);
compasICDHeader1.setICDSystemVersionUUID("UUID-1");
TCompasICDHeader compasICDHeader2 = new TCompasICDHeader();
compasICDHeader2.setICDSystemVersionUUID("UUID-2");
@@ -515,7 +516,7 @@ void comparePrivateCompasICDHeaders_should_return_true() {
TCompasICDHeader compasICDHeader1 = new TCompasICDHeader();
compasICDHeader1.setIEDName("IED-1");
compasICDHeader1.setBayLabel("BAY-1");
- compasICDHeader1.setIEDinstance("1");
+ compasICDHeader1.setIEDSubstationinstance(BigInteger.ONE);
compasICDHeader1.setICDSystemVersionUUID("UUID-1");
TCompasICDHeader compasICDHeader2 = new TCompasICDHeader();
compasICDHeader2.setICDSystemVersionUUID("UUID-1");
@@ -537,17 +538,17 @@ void copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate() {
lNodeCompasICDHeader.setICDSystemVersionUUID("UUID-2");
lNodeCompasICDHeader.setIEDName("IED-1");
lNodeCompasICDHeader.setBayLabel("BAY-1");
- lNodeCompasICDHeader.setIEDinstance("1");
+ lNodeCompasICDHeader.setIEDSubstationinstance(BigInteger.ONE);
TPrivate stdTPrivate = PrivateService.createPrivate(stdCompasICDHeader);
TPrivate lNodePrivate = PrivateService.createPrivate(lNodeCompasICDHeader);
+ assertThat(stdTPrivate).isNotEqualTo(lNodePrivate);
// When
- assertThat(stdTPrivate).isNotEqualTo(lNodePrivate);
PrivateService.copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(stdTPrivate,lNodePrivate);
// Then
TCompasICDHeader result = PrivateService.extractCompasICDHeader(stdTPrivate).get();
assertThat(result).extracting(TCompasICDHeader::getICDSystemVersionUUID, TCompasICDHeader::getIEDName,
- TCompasICDHeader::getIEDinstance, TCompasICDHeader::getBayLabel)
- .containsExactlyInAnyOrder("UUID-2", "IED-1", "1", "BAY-1");
+ TCompasICDHeader::getIEDSubstationinstance, TCompasICDHeader::getBayLabel)
+ .containsExactlyInAnyOrder("UUID-2", "IED-1", BigInteger.ONE, "BAY-1");
}
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclRootAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclRootAdapterTest.java
index aea4e215f..3b7b5e720 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclRootAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclRootAdapterTest.java
@@ -6,11 +6,11 @@
import org.junit.jupiter.api.Test;
import org.lfenergy.compas.scl2007b4.model.SCL;
-import org.lfenergy.compas.scl2007b4.model.THeader;
-import org.lfenergy.compas.scl2007b4.model.TIED;
import org.lfenergy.compas.scl2007b4.model.TPrivate;
import org.lfenergy.compas.sct.commons.exception.ScdException;
+import org.lfenergy.compas.sct.commons.scl.com.ConnectedAPAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter;
+import org.lfenergy.compas.sct.commons.testhelpers.SclHelper;
import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
import java.util.Optional;
@@ -46,7 +46,7 @@ void testConstruction() {
}
@Test
- void addIED() throws Exception {
+ void addIED() {
SCL scd = SclTestMarshaller.getSCLFromFile("/scl-root-test-schema-conf/add_ied_test.xml");
SCL icd1 = SclTestMarshaller.getSCLFromFile("/scl-root-test-schema-conf/icd1_to_add_test.xml");
@@ -62,7 +62,7 @@ void addIED() throws Exception {
}
@Test
- void addPrivate() throws Exception {
+ void addPrivate() {
SCL scd = SclTestMarshaller.getSCLFromFile("/scl-root-test-schema-conf/add_ied_test.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
TPrivate tPrivate = new TPrivate();
@@ -77,7 +77,7 @@ void addPrivate() throws Exception {
@Test
void getIEDAdapterByName_should_return_ied(){
// Given
- SclRootAdapter sclRootAdapter = createSclRootAdapterWithIed("IED_NAME");
+ SclRootAdapter sclRootAdapter = SclHelper.createSclRootAdapterWithIed("IED_NAME");
// When
IEDAdapter resultIed = sclRootAdapter.getIEDAdapterByName("IED_NAME");
// Then
@@ -87,7 +87,7 @@ void getIEDAdapterByName_should_return_ied(){
@Test
void getIEDAdapterByName_should_throw_exception(){
// Given
- SclRootAdapter sclRootAdapter = createSclRootAdapterWithIed("IED_NAME");
+ SclRootAdapter sclRootAdapter = SclHelper.createSclRootAdapterWithIed("IED_NAME");
// When & Then
assertThatThrownBy(() -> sclRootAdapter.getIEDAdapterByName("NON_EXISTING_IED"))
.isInstanceOf(ScdException.class)
@@ -97,7 +97,7 @@ void getIEDAdapterByName_should_throw_exception(){
@Test
void findIEDAdapterByName_should_return_ied(){
// Given
- SclRootAdapter sclRootAdapter = createSclRootAdapterWithIed("IED_NAME");
+ SclRootAdapter sclRootAdapter = SclHelper.createSclRootAdapterWithIed("IED_NAME");
// When
Optional resultOptionalIed = sclRootAdapter.findIedAdapterByName("IED_NAME");
// Then
@@ -108,19 +108,32 @@ void findIEDAdapterByName_should_return_ied(){
@Test
void findIEDAdapterByName_should_return_empty(){
// Given
- SclRootAdapter sclRootAdapter = createSclRootAdapterWithIed("IED_NAME");
+ SclRootAdapter sclRootAdapter = SclHelper.createSclRootAdapterWithIed("IED_NAME");
// When
Optional resultOptionalIed = sclRootAdapter.findIedAdapterByName("NON_EXISTING_IED");
// Then
assertThat(resultOptionalIed).isEmpty();
}
- private SclRootAdapter createSclRootAdapterWithIed(String iedName) {
- SCL scl = new SCL();
- scl.setHeader(new THeader());
- TIED ied = new TIED();
- ied.setName(iedName);
- scl.getIED().add(ied);
- return new SclRootAdapter(scl);
+ @Test
+ void findConnectedApAdapter_should_return_adapter(){
+ // Given
+ SclRootAdapter sclRootAdapter = SclHelper.createSclRootWithConnectedAp("iedName", "apName");
+ // When
+ Optional result = sclRootAdapter.findConnectedApAdapter("iedName", "apName");
+ // Then
+ assertThat(result).get().extracting(ConnectedAPAdapter::getIedName, ConnectedAPAdapter::getApName)
+ .containsExactly("iedName", "apName");
}
+
+ @Test
+ void findConnectedApAdapter_should_return_empty(){
+ // Given
+ SclRootAdapter sclRootAdapter = SclHelper.createSclRootWithConnectedAp("iedName", "apName");
+ // When
+ Optional result = sclRootAdapter.findConnectedApAdapter("iedName2", "apName2");
+ // Then
+ assertThat(result).isEmpty();
+ }
+
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclServiceTest.java
index 94a38dcf4..ca831e518 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclServiceTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclServiceTest.java
@@ -1108,14 +1108,18 @@ void updateDoInRef_shouldReturnUpdatedFile(String testName, String ldInst, Strin
assertThat(getValFromDaiName(sclReport.getSclRootAdapter().getCurrentElem(), "IED_NAME1", ldInst, doName, daName)
.map(TVal::getValue))
.hasValue(expected);
+ assertIsMarshallable(sclReport.getSclRootAdapter().getCurrentElem());
}
@ParameterizedTest(name = "{0}")
@CsvSource({
"Test with only 1 ExtRef should not update srcTstCB,LD_WITH_1_InRef,InRef2,setTstRef",
- "Test with only 1 ExtRef should not update setTstCB Value,LD_WITH_1_InRef,InRef2,setTstCB"
+ "Test with only 1 ExtRef should not update setTstCB Value,LD_WITH_1_InRef,InRef2,setTstCB",
+ "Test with only 1 ExtRef should not update DO when IedName not present,LD_WITH_1_InRef_ExtRef_Without_IedName,InRef4,setSrcRef",
+ "Test with only 1 ExtRef should not update DO when LdInst not present,LD_WITH_1_InRef_ExtRef_Without_LdInst,InRef5,setSrcRef",
+ "Test with only 1 ExtRef should not update DO when lnClass not present,LD_WITH_1_InRef_ExtRef_Without_LnClass,InRef6,setSrcRef"
})
- void updateDoInRef_should_not_update_tst_DAI_When_only_1_ExtRef(String testName, String ldInst, String doName, String daName) {
+ void updateDoInRef_should_not_update_DAI(String testName, String ldInst, String doName, String daName) {
// Given
SCL givenScl = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml");
@@ -1160,7 +1164,7 @@ private Optional getValFromDaiName(SCL scl, String iedName, String ldInst,
@Test
void analyzeDataGroups_should_success() {
// Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml");
+ SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter1 = sclRootAdapter.getIEDAdapterByName("IED_NAME1");
iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(9L);
@@ -1178,7 +1182,7 @@ void analyzeDataGroups_should_success() {
void analyzeDataGroups_should_return_errors_messages() {
// Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml");
+ SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME2");
iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMaxAttributes(1L);
@@ -1192,17 +1196,17 @@ void analyzeDataGroups_should_return_errors_messages() {
assertThat(sclReport.getSclReportItems()).hasSize(11)
.extracting(SclReportItem::getMessage)
.containsExactlyInAnyOrder(
- "There are too much FCDA for the Client IED IED_NAME1",
- "The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks.",
- "The Client IED IED_NAME1 subscribes to too much REPORT Control Blocks.",
- "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks.",
- "There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST21 in IED IED_NAME2",
- "There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST22 in IED IED_NAME2",
- "There are too much FCDA for the DataSet DATASET5 for the LDevice LD_INST22 in IED IED_NAME2",
- "There are too much DataSets for the IED IED_NAME2",
- "There are too much Report Control Blocks for the IED IED_NAME2",
- "There are too much GOOSE Control Blocks for the IED IED_NAME2",
- "There are too much SMV Control Blocks for the IED IED_NAME2");
+ "The Client IED IED_NAME1 subscribes to too much FCDA: 9 > 8 max",
+ "The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks: 3 > 2 max",
+ "The Client IED IED_NAME1 subscribes to too much Report Control Blocks: 1 > 0 max",
+ "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks: 2 > 1 max",
+ "There are too much FCDA for the DataSet dataset6 for the LDevice LD_INST21 in IED IED_NAME2: 2 > 1 max",
+ "There are too much FCDA for the DataSet dataset6 for the LDevice LD_INST22 in IED IED_NAME2: 2 > 1 max",
+ "There are too much FCDA for the DataSet dataset5 for the LDevice LD_INST22 in IED IED_NAME2: 2 > 1 max",
+ "There are too much DataSets for the IED IED_NAME2: 6 > 3 max",
+ "There are too much Report Control Blocks for the IED IED_NAME2: 1 > 0 max",
+ "There are too much GOOSE Control Blocks for the IED IED_NAME2: 3 > 2 max",
+ "There are too much SMV Control Blocks for the IED IED_NAME2: 3 > 1 max");
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/com/ConnectedAPAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/com/ConnectedAPAdapterTest.java
index 01229afbe..987b8b268 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/com/ConnectedAPAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/com/ConnectedAPAdapterTest.java
@@ -4,22 +4,24 @@
package org.lfenergy.compas.sct.commons.scl.com;
+import org.assertj.core.groups.Tuple;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
-import org.lfenergy.compas.scl2007b4.model.SCL;
-import org.lfenergy.compas.scl2007b4.model.TConnectedAP;
-import org.lfenergy.compas.scl2007b4.model.TPrivate;
-import org.lfenergy.compas.scl2007b4.model.TSubNetwork;
+import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.dto.DTO;
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
+import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
+import java.math.BigDecimal;
+import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
+import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.*;
class ConnectedAPAdapterTest {
@@ -28,9 +30,7 @@ class ConnectedAPAdapterTest {
@BeforeEach
void setUp() {
subNetworkAdapter = new SubNetworkAdapter(null, new TSubNetwork());
- TConnectedAP tConnectedAP = new TConnectedAP();
- tConnectedAP.setIedName(DTO.HOLDER_IED_NAME);
- tConnectedAP.setApName(DTO.AP_NAME);
+ TConnectedAP tConnectedAP = newConnectedAp(DTO.HOLDER_IED_NAME, DTO.AP_NAME);
subNetworkAdapter.getCurrentElem().getConnectedAP().add(tConnectedAP);
}
@@ -56,7 +56,7 @@ void addPrivate() {
}
@Test
- void testCopyAddressAndPhysConnFromIcd_withFilledCommunication() throws Exception {
+ void testCopyAddressAndPhysConnFromIcd_withFilledCommunication() {
// GIVEN
ConnectedAPAdapter connectedAPAdapter = assertDoesNotThrow(
() -> subNetworkAdapter.getConnectedAPAdapter(DTO.HOLDER_IED_NAME, DTO.AP_NAME)
@@ -93,18 +93,92 @@ void testCopyAddressAndPhysConnFromIcd_withEmptyIcd() {
}
@ParameterizedTest
- @CsvSource(value = {"IED_NAME;AP_NAME;ConnectedAP[@apName=\"IED_NAME\" and @iedName=\"IED_NAME\"]", ";;ConnectedAP[not(@apName) and not(@iedName)]"}
+ @CsvSource(value = {"IED_NAME;AP_NAME;ConnectedAP[@apName=\"AP_NAME\" and @iedName=\"IED_NAME\"]", ";;ConnectedAP[not(@apName) and not(@iedName)]"}
, delimiter = ';')
void elementXPath(String iedName, String apName, String message) {
// Given
- TConnectedAP tConnectedAP = new TConnectedAP();
- tConnectedAP.setApName(iedName);
- tConnectedAP.setIedName(apName);
- ConnectedAPAdapter connectedAPAdapter = new ConnectedAPAdapter(null, tConnectedAP);
+ ConnectedAPAdapter connectedAPAdapter = newConnectedApAdapter(iedName, apName);
// When
String elementXPath = connectedAPAdapter.elementXPath();
// Then
assertThat(elementXPath).isEqualTo(message);
}
+ @Test
+ void updateGseOrCreateIfNotExists_should_create_GSE(){
+ // Given
+ ConnectedAPAdapter connectedAPAdapter = newConnectedApAdapter("IED_NAME", "AP_NAME");
+ // When
+ connectedAPAdapter.updateGseOrCreateIfNotExists("ldinst", "cbName", List.of(SclConstructorHelper.newP("APPID", "0001")),
+ newDurationInMilliSec(5), newDurationInMilliSec(10));
+ // Then
+ assertThat(connectedAPAdapter.getCurrentElem().getGSE()).hasSize(1);
+ TGSE gse = connectedAPAdapter.getCurrentElem().getGSE().get(0);
+ assertThat(gse.getLdInst()).isEqualTo("ldinst");
+ assertThat(gse.getMinTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("5"));
+ assertThat(gse.getMaxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("10"));
+ assertThat(gse.getAddress().getP()).extracting(TP::getType, TP::getValue)
+ .containsExactly(Tuple.tuple("APPID", "0001"));
+ }
+
+ @Test
+ void updateGseOrCreateIfNotExists_when_exists_should_update_GSE(){
+ // Given
+ ConnectedAPAdapter connectedAPAdapter = newConnectedApAdapter("IED_NAME", "AP_NAME");
+ connectedAPAdapter.updateGseOrCreateIfNotExists("ldinst", "cbName", List.of(SclConstructorHelper.newP("APPID", "0001")),
+ newDurationInMilliSec(5), newDurationInMilliSec(10));
+ // When
+ connectedAPAdapter.updateGseOrCreateIfNotExists("ldinst", "cbName", List.of(SclConstructorHelper.newP("APPID", "0004")),
+ newDurationInMilliSec(30), newDurationInMilliSec(50));
+ // Then
+ assertThat(connectedAPAdapter.getCurrentElem().getGSE()).hasSize(1);
+ TGSE gse = connectedAPAdapter.getCurrentElem().getGSE().get(0);
+ assertThat(gse.getLdInst()).isEqualTo("ldinst");
+ assertThat(gse.getMinTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("30"));
+ assertThat(gse.getMaxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("50"));
+ assertThat(gse.getAddress().getP()).extracting(TP::getType, TP::getValue)
+ .containsExactly(Tuple.tuple("APPID", "0004"));
+ }
+
+ @Test
+ void updateSmvOrCreateIfNotExists_should_create_SMV(){
+ // Given
+ ConnectedAPAdapter connectedAPAdapter = newConnectedApAdapter("IED_NAME", "AP_NAME");
+ // When
+ connectedAPAdapter.updateSmvOrCreateIfNotExists("ldinst", "cbName", List.of(SclConstructorHelper.newP("APPID", "0001")));
+ // Then
+ assertThat(connectedAPAdapter.getCurrentElem().getSMV()).hasSize(1);
+ TSMV smv = connectedAPAdapter.getCurrentElem().getSMV().get(0);
+ assertThat(smv.getLdInst()).isEqualTo("ldinst");
+ assertThat(smv.getAddress().getP()).extracting(TP::getType, TP::getValue)
+ .containsExactly(Tuple.tuple("APPID", "0001"));
+ }
+
+ @Test
+ void updateSmvOrCreateIfNotExists_when_exists_should_update_SMV(){
+ // Given
+ ConnectedAPAdapter connectedAPAdapter = newConnectedApAdapter("IED_NAME", "AP_NAME");
+ TSMV newSmv = new TSMV();
+ newSmv.setLdInst("ldinst");
+ newSmv.setCbName("cbName");
+ newSmv.setAddress(newAddress(List.of(SclConstructorHelper.newP("APPID", "0001"))));
+ connectedAPAdapter.getCurrentElem().getSMV().add(newSmv);
+ // When
+ connectedAPAdapter.updateSmvOrCreateIfNotExists("ldinst", "cbName", List.of(SclConstructorHelper.newP("APPID", "0004")));
+ // Then
+ assertThat(connectedAPAdapter.getCurrentElem().getSMV()).hasSize(1);
+ TSMV smv = connectedAPAdapter.getCurrentElem().getSMV().get(0);
+ assertThat(smv.getLdInst()).isEqualTo("ldinst");
+ assertThat(smv.getAddress().getP()).extracting(TP::getType, TP::getValue)
+ .containsExactly(Tuple.tuple("APPID", "0004"));
+ }
+
+ private ConnectedAPAdapter newConnectedApAdapter(String iedName, String apName){
+ return new ConnectedAPAdapter(null, newConnectedAp(iedName, apName));
+ }
+
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/com/SubNetworkAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/com/SubNetworkAdapterTest.java
index 494968162..9e339474c 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/com/SubNetworkAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/com/SubNetworkAdapterTest.java
@@ -4,6 +4,8 @@
package org.lfenergy.compas.sct.commons.scl.com;
+import org.assertj.core.api.Assertions;
+import org.assertj.core.groups.Tuple;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@@ -14,6 +16,9 @@
import org.lfenergy.compas.sct.commons.dto.DTO;
import org.lfenergy.compas.sct.commons.exception.ScdException;
+import java.util.List;
+import java.util.Optional;
+
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
@@ -51,20 +56,63 @@ void testAddConnectedAP() {
assertEquals(1,subNetworkAdapter.getCurrentElem().getConnectedAP().size());
}
+ @Test
+ void getConnectedAPAdapters_should_return_all_ConnectedAP() {
+ // Given
+ SubNetworkAdapter subNetworkAdapter = new SubNetworkAdapter(null, new TSubNetwork());
+ subNetworkAdapter.addConnectedAP(DTO.HOLDER_IED_NAME, DTO.AP_NAME);
+ subNetworkAdapter.addConnectedAP(DTO.HOLDER_IED_NAME_2, DTO.AP_NAME_2);
+ // When
+ List connectedAPAdapter = subNetworkAdapter.getConnectedAPAdapters();
+ // Then
+ assertThat(connectedAPAdapter).extracting(ConnectedAPAdapter::getIedName, ConnectedAPAdapter::getApName)
+ .containsExactly(
+ Tuple.tuple(DTO.HOLDER_IED_NAME, DTO.AP_NAME),
+ Tuple.tuple(DTO.HOLDER_IED_NAME_2, DTO.AP_NAME_2)
+ );
+ }
@Test
- void getConnectedAPAdapters() {
+ void getConnectedAPAdapter_should_get_element() {
+ // Given
SubNetworkAdapter subNetworkAdapter = new SubNetworkAdapter(null, new TSubNetwork());
- assertTrue(subNetworkAdapter.getCurrentElem().getConnectedAP().isEmpty());
subNetworkAdapter.addConnectedAP(DTO.HOLDER_IED_NAME,DTO.AP_NAME);
- assertEquals(1,subNetworkAdapter.getConnectedAPAdapters().size());
+ // When
+ ConnectedAPAdapter connectedAPAdapter = subNetworkAdapter.getConnectedAPAdapter(DTO.HOLDER_IED_NAME, DTO.AP_NAME);
+ // Then
+ assertThat(connectedAPAdapter).extracting(ConnectedAPAdapter::getIedName, ConnectedAPAdapter::getApName)
+ .containsExactly(DTO.HOLDER_IED_NAME, DTO.AP_NAME);
+ }
+
+ @Test
+ void getConnectedAPAdapter_when_not_found_should_throw_exception() {
+ // Given
+ SubNetworkAdapter subNetworkAdapter = new SubNetworkAdapter(null, new TSubNetwork());
+ // When & Then
+ Assertions.assertThatThrownBy(() -> subNetworkAdapter.getConnectedAPAdapter(DTO.HOLDER_IED_NAME, DTO.AP_NAME))
+ .isInstanceOf(ScdException.class);
+ }
- assertDoesNotThrow( () -> subNetworkAdapter.getConnectedAPAdapter(DTO.HOLDER_IED_NAME,DTO.AP_NAME));
+ @Test
+ void findConnectedAPAdapter_should_get_element() {
+ // Given
+ SubNetworkAdapter subNetworkAdapter = new SubNetworkAdapter(null, new TSubNetwork());
+ subNetworkAdapter.addConnectedAP(DTO.HOLDER_IED_NAME,DTO.AP_NAME);
+ // When
+ Optional connectedAPAdapter = subNetworkAdapter.findConnectedAPAdapter(DTO.HOLDER_IED_NAME, DTO.AP_NAME);
+ // Then
+ assertThat(connectedAPAdapter).get().extracting(ConnectedAPAdapter::getIedName, ConnectedAPAdapter::getApName)
+ .containsExactly(DTO.HOLDER_IED_NAME, DTO.AP_NAME);
+ }
- assertThrows(
- ScdException.class,
- () -> subNetworkAdapter.getConnectedAPAdapter(DTO.HOLDER_IED_NAME,DTO.AP_NAME + "1")
- );
+ @Test
+ void findConnectedAPAdapter_when_not_found_shouldreturn_empty() {
+ // Given
+ SubNetworkAdapter subNetworkAdapter = new SubNetworkAdapter(null, new TSubNetwork());
+ // When
+ Optional connectedAPAdapter = subNetworkAdapter.findConnectedAPAdapter(DTO.HOLDER_IED_NAME, DTO.AP_NAME);
+ // Then
+ assertThat(connectedAPAdapter).isEmpty();
}
@Test
@@ -94,4 +142,4 @@ void elementXPath(String sName, String sType, String message) {
}
-}
\ No newline at end of file
+}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java
index 1ecfc9daf..4f0e805a7 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java
@@ -73,7 +73,7 @@ void getXPath() {
}
@Test
- void checkFCDALimitations_should_succed_no_error_message() throws Exception {
+ void checkFCDALimitations_should_succeed_no_error_message() {
//Given
AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
//When
@@ -83,7 +83,7 @@ void checkFCDALimitations_should_succed_no_error_message() throws Exception {
}
@Test
- void checkFCDALimitations_should_fail_with_one_error_messages() throws Exception {
+ void checkFCDALimitations_should_fail_with_one_error_messages() {
//Given
AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMaxAttributes(2L);
@@ -92,10 +92,10 @@ void checkFCDALimitations_should_fail_with_one_error_messages() throws Exception
//Then
assertThat(sclReportItems).hasSize(1)
.extracting(SclReportItem::getMessage)
- .containsExactlyInAnyOrder("There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST21 in IED IED_NAME");
+ .containsExactlyInAnyOrder("There are too much FCDA for the DataSet dataset6 for the LDevice LD_INST21 in IED IED_NAME: 3 > 2 max");
}
@Test
- void checkFCDALimitations_should_fail_with_four_error_messages() throws Exception {
+ void checkFCDALimitations_should_fail_with_four_error_messages() {
//Given
AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMaxAttributes(1L);
@@ -104,66 +104,62 @@ void checkFCDALimitations_should_fail_with_four_error_messages() throws Exceptio
//Then
assertThat(sclReportItems).hasSize(4)
.extracting(SclReportItem::getMessage)
- .containsExactlyInAnyOrder("There are too much FCDA for the DataSet DATASET3 for the LDevice LD_INST21 in IED IED_NAME",
- "There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST21 in IED IED_NAME",
- "There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST22 in IED IED_NAME",
- "There are too much FCDA for the DataSet DATASET5 for the LDevice LD_INST22 in IED IED_NAME");
+ .containsExactlyInAnyOrder("There are too much FCDA for the DataSet dataset3 for the LDevice LD_INST21 in IED IED_NAME: 2 > 1 max",
+ "There are too much FCDA for the DataSet dataset6 for the LDevice LD_INST21 in IED IED_NAME: 3 > 1 max",
+ "There are too much FCDA for the DataSet dataset6 for the LDevice LD_INST22 in IED IED_NAME: 2 > 1 max",
+ "There are too much FCDA for the DataSet dataset5 for the LDevice LD_INST22 in IED IED_NAME: 2 > 1 max");
}
@Test
- void checkControlsLimitation_should_fail_for_dataset_with_one_error_messages() throws Exception {
+ void checkControlsLimitation_should_fail_for_dataset_with_one_error_messages() {
//Given
AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMax(5L);
- String message = "Too much DataSet for";
//When
- Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.DATASET,message);
+ Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.DATASET);
//Then
assertThat(sclReportItem).isPresent()
- .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME");
+ .get().extracting(SclReportItem::getMessage).isEqualTo("There are too much DataSets for the IED IED_NAME: 6 > 5 max");
}
@Test
- void checkControlsLimitation_should_fail_for_smv_with_one_error_messages() throws Exception {
+ void checkControlsLimitation_should_fail_for_smv_with_one_error_messages() {
//Given
AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
accessPointAdapter.getCurrentElem().getServices().getSMVsc().setMax(2L);
- String message = "Too much SMV Control for";
//When
- Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.SMV,message);
+ Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.SMV);
//Then
assertThat(sclReportItem).isPresent()
- .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME");
+ .get().extracting(SclReportItem::getMessage).isEqualTo("There are too much SMV Control Blocks for the IED IED_NAME: 3 > 2 max");
}
@Test
- void checkControlsLimitation_should_fail_for_goose_with_one_error_messages() throws Exception {
+ void checkControlsLimitation_should_fail_for_goose_with_one_error_messages() {
//Given
AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
accessPointAdapter.getCurrentElem().getServices().getGOOSE().setMax(2L);
- String message = "Too much Goose Control for";
//When
- Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.GSE,message);
+ Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.GSE);
//Then
assertThat(sclReportItem).isPresent()
- .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME");
+ .get().extracting(SclReportItem::getMessage).isEqualTo("There are too much GOOSE Control Blocks for the IED IED_NAME: 3 > 2 max");
}
@Test
- void checkControlsLimitation_should_fail_for_report_with_one_error_messages() throws Exception {
+ void checkControlsLimitation_should_fail_for_report_with_one_error_messages() {
//Given
AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
accessPointAdapter.getCurrentElem().getServices().getConfReportControl().setMax(0L);
- String message = "Too much Report Control for";
//When
- Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.REPORT,message);
+ Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.REPORT);
//Then
assertThat(sclReportItem).isPresent()
- .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME");
+ .get().extracting(SclReportItem::getMessage).isEqualTo("There are too much Report Control Blocks for the IED IED_NAME: 1 > 0 max");
}
- public static AccessPointAdapter provideAPForCheckLimitationForIED() throws Exception {
+ public static AccessPointAdapter provideAPForCheckLimitationForIED() {
SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME");
@@ -171,47 +167,89 @@ public static AccessPointAdapter provideAPForCheckLimitationForIED() throws Exce
}
@Test
- void checkLimitationForBindedIEDFCDAs_should_success_no_error() throws Exception {
+ void checkControlsLimitation_should_succeed_when_ConfReportControl_is_missing() {
//Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml");
+ AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
+ accessPointAdapter.getCurrentElem().getServices().setConfReportControl(null);
+ //When
+ Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.REPORT);
+ //Then
+ assertThat(sclReportItem).isEmpty();
+ }
+
+ @Test
+ void checkControlsLimitation_should_succeed_when_GOOSE_is_missing() {
+ //Given
+ AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
+ accessPointAdapter.getCurrentElem().getServices().setGOOSE(null);
+ //When
+ Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.GSE);
+ //Then
+ assertThat(sclReportItem).isEmpty();
+ }
+
+ @Test
+ void checkControlsLimitation_should_succeed_when_ConfDataSet_is_missing() {
+ //Given
+ AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
+ accessPointAdapter.getCurrentElem().getServices().setConfDataSet(null);
+ //When
+ Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.DATASET);
+ //Then
+ assertThat(sclReportItem).isEmpty();
+ }
+
+ @Test
+ void checkControlsLimitation_should_succeed_when_Services_is_missing() {
+ //Given
+ AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED();
+ accessPointAdapter.getCurrentElem().setServices(null);
+ //When
+ Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.SMV);
+ //Then
+ assertThat(sclReportItem).isEmpty();
+ }
+
+ @Test
+ void checkLimitationForBoundIEDFCDAs_should_success_no_error() {
+ //Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1");
iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(11L);
AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0));
- String message = "Too much FCDA";
List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs();
//When
- Optional sclReportItem = accessPointAdapter.checkLimitationForBoundIEDFCDAs(tExtRefs, message);
+ Optional sclReportItem = accessPointAdapter.checkLimitationForBoundIedFcdas(tExtRefs);
//Then
assertThat(sclReportItem).isEmpty();
}
@Test
- void checkLimitationForBindedIEDFCDAs_should_fail_one_error_message() throws Exception {
+ void checkLimitationForBoundIEDFCDAs_should_fail_one_error_message() {
//Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml");
+ SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1");
AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0));
accessPointAdapter.getCurrentElem().getServices().getClientServices().setMaxAttributes(4L);
List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs();
- String message = "Too much FCDA for IED_NAME1";
//When
- Optional sclReportItem = accessPointAdapter.checkLimitationForBoundIEDFCDAs(tExtRefs, message);
+ Optional sclReportItem = accessPointAdapter.checkLimitationForBoundIedFcdas(tExtRefs);
//Then
assertThat(sclReportItem).isPresent()
.get()
.extracting(SclReportItem::getMessage)
- .isEqualTo(message);
+ .isEqualTo("The Client IED IED_NAME1 subscribes to too much FCDA: 9 > 4 max");
}
@Test
- void checkLimitationForBindedIEDControls_should_fail_three_error_messages() throws Exception {
+ void checkLimitationForBoundIEDControls_should_fail_three_error_messages() {
//Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml");
+ SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1");
AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0));
@@ -222,15 +260,15 @@ void checkLimitationForBindedIEDControls_should_fail_three_error_messages() thro
//Then
assertThat(sclReportItems).hasSize(3)
.extracting(SclReportItem::getMessage)
- .containsExactlyInAnyOrder("The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks.",
- "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks.",
- "The Client IED IED_NAME1 subscribes to too much REPORT Control Blocks.");
+ .containsExactlyInAnyOrder("The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks: 3 > 2 max",
+ "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks: 2 > 1 max",
+ "The Client IED IED_NAME1 subscribes to too much Report Control Blocks: 1 > 0 max");
}
@Test
- void checkLimitationForBindedIEDControls_should_succed_no_error_message() throws Exception {
+ void checkLimitationForBoundIEDControls_should_succeed_no_error_message() {
//Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml");
+ SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1");
iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(11L);
@@ -247,9 +285,9 @@ void checkLimitationForBindedIEDControls_should_succed_no_error_message() throws
}
@Test
- void getAllCoherentExtRefForAnalyze_succed() throws Exception {
+ void getAllCoherentExtRefForAnalyze_succeed() {
//Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml");
+ SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1");
AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0));
@@ -262,7 +300,7 @@ void getAllCoherentExtRefForAnalyze_succed() throws Exception {
}
@Test
- void getAllCoherentExtRefForAnalyze_fail_with_one_error() throws Exception {
+ void getAllCoherentExtRefForAnalyze_fail_with_one_error() {
//Given
SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
@@ -275,4 +313,5 @@ void getAllCoherentExtRefForAnalyze_fail_with_one_error() throws Exception {
.extracting(AccessPointAdapter.ExtRefAnalyzeRecord::sclReportItems)
.asList().hasSize(1);
}
-}
\ No newline at end of file
+}
+
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapterTest.java
index 7589dd415..5e7abdcf8 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapterTest.java
@@ -4,16 +4,19 @@
package org.lfenergy.compas.sct.commons.scl.ied;
+import org.assertj.core.groups.Tuple;
import org.junit.jupiter.api.Test;
-import org.lfenergy.compas.scl2007b4.model.SCL;
-import org.lfenergy.compas.scl2007b4.model.TControl;
-import org.lfenergy.compas.scl2007b4.model.TControlWithIEDName;
-import org.lfenergy.compas.scl2007b4.model.TGSEControl;
+import org.lfenergy.compas.scl2007b4.model.*;
+import org.lfenergy.compas.sct.commons.dto.SclReportItem;
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
+import org.lfenergy.compas.sct.commons.testhelpers.SclHelper;
import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
+import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
+import java.math.BigDecimal;
import java.util.List;
+import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findLn;
@@ -21,6 +24,18 @@
class ControlBlockAdapterTest {
+ @Test
+ void getName_should_return_name(){
+ // Given
+ TGSEControl tgseControl = new TGSEControl();
+ tgseControl.setName("cbName");
+ ControlBlockAdapter controlBlockAdapter = new ControlBlockAdapter(null, tgseControl);
+ // When
+ String result = controlBlockAdapter.getName();
+ // Then
+ assertThat(result).isEqualTo("cbName");
+ }
+
@Test
void addTargetIfNotExists_should_add_target(){
// Given
@@ -45,4 +60,75 @@ void addTargetIfNotExists_should_add_target(){
.containsExactly("AP_NAME", "IED_NAME2", "LD_INST21", "1", List.of("ANCR"), "prefix");
}
+ @Test
+ void configureNetwork_should_add_GSE_element() {
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml");
+ TConnectedAP connectedAP = SclHelper.addConnectedAp(scd, "SUB_NETWORK_NAME", "AP_NAME", "IED_NAME1");
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ LN0Adapter ln0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_INST11");
+ ln0.createDataSetIfNotExists("datSet", ControlBlockEnum.GSE);
+ ControlBlockAdapter controlBlockAdapter = ln0.createControlBlockIfNotExists("cbName", "cbId", "datSet", ControlBlockEnum.GSE);
+ // When
+ Optional sclReportItem = controlBlockAdapter.configureNetwork(10L, "00-01-02-04-05", 11, (byte) 12, SclConstructorHelper.newDurationInMilliSec(3),
+ SclConstructorHelper.newDurationInMilliSec(20));
+ // Then
+ assertThat(sclReportItem).isEmpty();
+ assertThat(connectedAP.getGSE()).hasSize(1);
+ TGSE gse = connectedAP.getGSE().get(0);
+ assertThat(gse.getLdInst()).isEqualTo("LD_INST11");
+ assertThat(gse.getMinTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("3"));
+ assertThat(gse.getMaxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue)
+ .containsExactly("s", "m", new BigDecimal("20"));
+ assertThat(gse.getAddress().getP()).extracting(TP::getType, TP::getValue)
+ .containsExactlyInAnyOrder(
+ Tuple.tuple("APPID", "000A"),
+ Tuple.tuple("MAC-Address", "00-01-02-04-05"),
+ Tuple.tuple("VLAN-ID", "00B"),
+ Tuple.tuple("VLAN-PRIORITY", "12")
+ );
+ }
+
+ @Test
+ void configureNetwork_should_add_SMV_element() {
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml");
+ TConnectedAP connectedAP = SclHelper.addConnectedAp(scd, "SUB_NETWORK_NAME", "AP_NAME", "IED_NAME1");
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ LN0Adapter ln0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_INST11");
+ ln0.createDataSetIfNotExists("datSet", ControlBlockEnum.SAMPLED_VALUE);
+ ControlBlockAdapter controlBlockAdapter = ln0.createControlBlockIfNotExists("cbName", "cbId", "datSet", ControlBlockEnum.SAMPLED_VALUE);
+ // When
+ Optional sclReportItem = controlBlockAdapter.configureNetwork(10L, "00-01-02-04-05", 11, (byte) 12, null, null);
+ // Then
+ assertThat(sclReportItem).isEmpty();
+ assertThat(connectedAP.getSMV()).hasSize(1);
+ TSMV smv = connectedAP.getSMV().get(0);
+ assertThat(smv.getLdInst()).isEqualTo("LD_INST11");
+ assertThat(smv.getAddress().getP()).extracting(TP::getType, TP::getValue)
+ .containsExactlyInAnyOrder(
+ Tuple.tuple("APPID", "000A"),
+ Tuple.tuple("MAC-Address", "00-01-02-04-05"),
+ Tuple.tuple("VLAN-ID", "00B"),
+ Tuple.tuple("VLAN-PRIORITY", "12")
+ );
+ }
+
+ @Test
+ void configureNetwork_when_connectApNotFound_should_return_sclReportItem() {
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml");
+ SclHelper.addConnectedAp(scd, "SUB_NETWORK_NAME", "AP_NAME", "IED_NAME2"); // ConnectedAp for IED_NAME2 instead of IED_NAME1
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ LN0Adapter ln0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_INST11");
+ ln0.createDataSetIfNotExists("datSet", ControlBlockEnum.SAMPLED_VALUE);
+ ControlBlockAdapter controlBlockAdapter = ln0.createControlBlockIfNotExists("cbName", "cbId", "datSet", ControlBlockEnum.SAMPLED_VALUE);
+ // When
+ Optional sclReportItem = controlBlockAdapter.configureNetwork(10L, "00-01-02-04-05", 11, (byte) 12, null, null);
+ // Then
+ assertThat(sclReportItem).isPresent();
+ }
+
+
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java
index 88f867311..7a3878135 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java
@@ -11,6 +11,7 @@
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.scl.ObjectReference;
+import org.lfenergy.compas.sct.commons.scl.PrivateService;
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
import org.mockito.Mockito;
@@ -204,7 +205,7 @@ void elementXPath() {
}
@Test
- void checkDataGroupCoherence_should_succed_no_error_message() throws Exception {
+ void checkDataGroupCoherence_should_succeed_no_error_message() throws Exception {
//Given
IEDAdapter iedAdapter = provideIEDForCheckLimitationForIED();
//When
@@ -225,19 +226,19 @@ void checkDataGroupCoherence_should_fail_five_error_message() throws Exception {
//When
List sclReportItems = iedAdapter.checkDataGroupCoherence();
//Then
- assertThat(sclReportItems).hasSize(5)
+ assertThat(sclReportItems)
.extracting(SclReportItem::getMessage)
- .containsExactlyInAnyOrder("There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST21 in IED IED_NAME",
- "There are too much DataSets for the IED IED_NAME",
- "There are too much Report Control Blocks for the IED IED_NAME",
- "There are too much GOOSE Control Blocks for the IED IED_NAME",
- "There are too much SMV Control Blocks for the IED IED_NAME");
+ .containsExactlyInAnyOrder("There are too much FCDA for the DataSet dataset6 for the LDevice LD_INST21 in IED IED_NAME: 3 > 2 max",
+ "There are too much DataSets for the IED IED_NAME: 6 > 5 max",
+ "There are too much Report Control Blocks for the IED IED_NAME: 1 > 0 max",
+ "There are too much GOOSE Control Blocks for the IED IED_NAME: 3 > 2 max",
+ "There are too much SMV Control Blocks for the IED IED_NAME: 3 > 2 max");
}
@Test
- void checkBindingDataGroupCoherence_should_succed_no_error_message() throws Exception {
+ void checkBindingDataGroupCoherence_should_succeed_no_error_message() {
//Given
- IEDAdapter iedAdapter = provideIEDForCheckLimitationForBindedIED();
+ IEDAdapter iedAdapter = provideIEDForCheckLimitationForBoundIED();
TClientServices tClientServices = iedAdapter.getParentAdapter().getIEDAdapterByName("IED_NAME1").getCurrentElem().getAccessPoint().get(0).getServices().getClientServices();
tClientServices.setMaxAttributes(11L);
tClientServices.setMaxGOOSE(5L);
@@ -250,30 +251,62 @@ void checkBindingDataGroupCoherence_should_succed_no_error_message() throws Exce
}
@Test
- void checkBindingDataGroupCoherence_should_fail_five_error_message() throws Exception {
+ void checkBindingDataGroupCoherence_should_fail_five_error_message() {
//Given
- IEDAdapter iedAdapter = provideIEDForCheckLimitationForBindedIED();
+ IEDAdapter iedAdapter = provideIEDForCheckLimitationForBoundIED();
//When
List sclReportItems = iedAdapter.checkBindingDataGroupCoherence();
//Then
assertThat(sclReportItems).hasSize(4)
.extracting(SclReportItem::getMessage)
- .containsExactlyInAnyOrder("There are too much FCDA for the Client IED IED_NAME1",
- "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks.",
- "The Client IED IED_NAME1 subscribes to too much REPORT Control Blocks.",
- "The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks.");
+ .containsExactlyInAnyOrder("The Client IED IED_NAME1 subscribes to too much FCDA: 9 > 8 max",
+ "The Client IED IED_NAME1 subscribes to too much Report Control Blocks: 1 > 0 max",
+ "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks: 2 > 1 max",
+ "The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks: 3 > 2 max");
}
- public static IEDAdapter provideIEDForCheckLimitationForBindedIED() throws Exception {
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml");
+ public static IEDAdapter provideIEDForCheckLimitationForBoundIED() {
+ SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
return sclRootAdapter.getIEDAdapterByName("IED_NAME1");
}
- public static IEDAdapter provideIEDForCheckLimitationForIED() throws Exception {
+ public static IEDAdapter provideIEDForCheckLimitationForIED() {
SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml");
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
return sclRootAdapter.getIEDAdapterByName("IED_NAME");
}
+
+ @Test
+ void getCompasICDHeader_should_return_compas_icd_header(){
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile(SCD_IED_U_TEST);
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME");
+ TCompasICDHeader tCompasICDHeader = new TCompasICDHeader();
+ tCompasICDHeader.setHeaderId("HEADER_ID");
+ iedAdapter.getCurrentElem().getPrivate().add(PrivateService.createPrivate(tCompasICDHeader));
+
+ // When
+ Optional compasICDHeader = iedAdapter.getCompasICDHeader();
+ // Then
+ assertThat(compasICDHeader).map(TCompasICDHeader::getHeaderId).hasValue("HEADER_ID");
+ }
+
+ @Test
+ void getCompasSystemVersion_should_return_compas_icd_header(){
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile(SCD_IED_U_TEST);
+ SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
+ IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME");
+ TCompasSystemVersion tCompasSystemVersion = new TCompasSystemVersion();
+ tCompasSystemVersion.setMainSystemVersion("01.00");
+ iedAdapter.getCurrentElem().getPrivate().add(PrivateService.createPrivate(tCompasSystemVersion));
+
+ // When
+ Optional compasSystemVersion = iedAdapter.getCompasSystemVersion();
+ // Then
+ assertThat(compasSystemVersion).map(TCompasSystemVersion::getMainSystemVersion).hasValue("01.00");
+ }
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterTest.java
index a23706f4d..feb993360 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterTest.java
@@ -258,30 +258,5 @@ private static InputsAdapter keepOnlyThisExtRef(SclRootAdapter sclRootAdapter, S
foundInputsAdapter.getCurrentElem().getExtRef().removeIf(Predicate.not(extref -> extRefDesc.equals(extref.getDesc())));
return foundInputsAdapter;
}
- /* @Test
- void checkSourceDataGroupCoherence_should_fail_one_error_messages() throws Exception {
- //Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml");
- SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
- InputsAdapter inputsAdapter = keepOnlyThisExtRef(sclRootAdapter, "a");
- //When
- List sclReportItems = inputsAdapter.checkSourceDataGroupCoherence();
- //Then
- assertThat(sclReportItems).hasSize(1)
- .extracting(SclReportItem::getMessage)
- .containsExactlyInAnyOrder("The Client IED IED_NAME1 subscribes to much GOOSE Control Blocks.");
- }
- @Test
- void checkSourceDataGroupCoherence_should_succed_no_error_message() throws Exception {
- //Given
- SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml");
- SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
- InputsAdapter inputsAdapter = keepOnlyThisExtRef(sclRootAdapter, "a");
- sclRootAdapter.getIEDAdapterByName("IED_NAME1").getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxGOOSE(1L);
- //When
- List sclReportItems = inputsAdapter.checkSourceDataGroupCoherence();
- //Then
- assertThat(sclReportItems).isEmpty();
- }*/
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapterTest.java
index 835c2c2b4..f70c4fe04 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapterTest.java
@@ -62,6 +62,17 @@ void testGetLNAdapters() {
assertThrows(ScdException.class, () -> lDeviceAdapter.getLNAdapter("ANCR","1","pr"));
}
+ @Test
+ void findLnAdapter_should_return_adapter(){
+ // Given
+ LDeviceAdapter lDeviceAdapter = iAdapter.findLDeviceAdapterByLdInst("LD_INS2").get();
+ // When
+ Optional lnAdapter = lDeviceAdapter.findLnAdapter("ANCR", "1", null);
+ // Then
+ assertThat(lnAdapter).get().extracting(LNAdapter::getLNClass, LNAdapter::getLNInst, LNAdapter::getPrefix)
+ .containsExactly("ANCR", "1", "");
+ }
+
@Test
void getExtRefBinders_shouldReturnExtRefBindingInfo_whenExist() {
//Given
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LN0AdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LN0AdapterTest.java
index e48d8d48e..829a8f8eb 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LN0AdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LN0AdapterTest.java
@@ -999,4 +999,31 @@ void updateDoInRef_should_update_setSrcRef_and_setSrcCB_and_setTstRef_and_setTst
.isEqualTo(expectedTstCB);
assertThat(sclReportItems).isEmpty();
}
+
+ @Test
+ void streamControlBlocks_should_return_all_GSEControlBlocks(){
+ // Given
+ IEDAdapter iedAdapter = mock(IEDAdapter.class);
+ TLDevice tlDevice = new TLDevice();
+ LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class);
+ when(lDeviceAdapter.hasDataSetCreationCapability(any())).thenReturn(true);
+ when(lDeviceAdapter.hasControlBlockCreationCapability(any())).thenReturn(true);
+ when(lDeviceAdapter.getParentAdapter()).thenReturn(iedAdapter);
+ LN0 ln0 = new LN0();
+ tlDevice.setLN0(ln0);
+ when(lDeviceAdapter.getCurrentElem()).thenReturn(tlDevice);
+ LN0Adapter ln0Adapter = new LN0Adapter(lDeviceAdapter, ln0);
+ ln0Adapter.createDataSetIfNotExists("datSet1", ControlBlockEnum.GSE);
+ ln0Adapter.createDataSetIfNotExists("datSet2", ControlBlockEnum.SAMPLED_VALUE);
+ ln0Adapter.createControlBlockIfNotExists("cbNameGSE", "cbId1", "datSet1", ControlBlockEnum.GSE);
+ ln0Adapter.createControlBlockIfNotExists("cbNameSMV", "cbId2", "datSet2", ControlBlockEnum.SAMPLED_VALUE);
+ // When
+ Stream result = ln0Adapter.streamControlBlocks(ControlBlockEnum.GSE);
+ // Then
+ assertThat(result)
+ .hasSize(1)
+ .extracting(ControlBlockAdapter::getName)
+ .containsExactly("cbNameGSE");
+ }
+
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapterTest.java
index 199b61e32..69b06a6e2 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapterTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapterTest.java
@@ -23,6 +23,7 @@
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
+import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findLn;
class LNAdapterTest {
@@ -650,4 +651,34 @@ void getControlBlocks_should_find_ControlBlock_by_name() {
assertThat(tControls).isNotEmpty()
.hasSize(1);
}
+
+ @Test
+ void getTControlsByType_should_return_list_of_report_control_blocks() {
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml");
+ LNAdapter lnAdapter= findLn(new SclRootAdapter(scd), "IED4d4fe1a8cda64cf88a5ee4176a1a0eef", "LDSUIED", "LPAI", "1", null);
+ // When
+ List tControlsByType = lnAdapter.getTControlsByType(TReportControl.class);
+ // Then
+ assertThat(tControlsByType).isSameAs(lnAdapter.getCurrentElem().getReportControl());
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideGetTControlsByTypeException")
+ void getTControlsByType_should_throw_when_unsupported_controlBlock_for_ln(Class extends TControl> tControlClass) {
+ // Given
+ SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml");
+ LNAdapter lnAdapter= findLn(new SclRootAdapter(scd), "IED4d4fe1a8cda64cf88a5ee4176a1a0eef", "LDSUIED", "LPAI", "1", null);
+ // When & Then
+ assertThatThrownBy(() -> lnAdapter.getTControlsByType(tControlClass))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ private static Stream provideGetTControlsByTypeException() {
+ return Stream.of(
+ Arguments.of(TGSEControl.class),
+ Arguments.of(TSampledValueControl.class)
+ );
+ }
+
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java
index 71d3d110a..44358ad88 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java
@@ -17,6 +17,7 @@
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newConnectedAp;
/**
* Provides static methods to quickly retrieve SCL elements, to be used in writing tests.
@@ -25,9 +26,14 @@
*/
@UtilityClass
public class SclHelper {
- public static LDeviceAdapter findLDevice(SclRootAdapter sclRootAdapter, String iedName, String ldInst) {
+
+ public static IEDAdapter findIed(SclRootAdapter sclRootAdapter, String iedName) {
return sclRootAdapter.findIedAdapterByName(iedName)
- .orElseThrow(() -> new AssertionFailedError(String.format("IED.name=%s not found", iedName))).findLDeviceAdapterByLdInst(ldInst).orElseThrow(() -> new AssertionFailedError(String.format("LDevice.inst=%s not found in IED.name=%s", ldInst, iedName)));
+ .orElseThrow(() -> new AssertionFailedError(String.format("IED.name=%s not found", iedName)));
+ }
+
+ public static LDeviceAdapter findLDevice(SclRootAdapter sclRootAdapter, String iedName, String ldInst) {
+ return findIed(sclRootAdapter, iedName).findLDeviceAdapterByLdInst(ldInst).orElseThrow(() -> new AssertionFailedError(String.format("LDevice.inst=%s not found in IED.name=%s", ldInst, iedName)));
}
public static InputsAdapter findInputs(SclRootAdapter sclRootAdapter, String iedName, String ldInst) {
@@ -159,4 +165,46 @@ public static Stream streamAllExtRef(SclRootAdapter sclRootAdapter) {
public static String getDaiValue(AbstractLNAdapter> ln, String doiName, String daiName) {
return ln.getDOIAdapterByName(doiName).getDataAdapterByName(daiName).getCurrentElem().getVal().get(0).getValue();
}
+
+ public static Stream streamAllConnectedApGseP(SCL scd, String pType) {
+ return scd.getCommunication().getSubNetwork().stream()
+ .map(TSubNetwork::getConnectedAP)
+ .flatMap(List::stream)
+ .map(TConnectedAP::getGSE)
+ .flatMap(List::stream)
+ .map(TControlBlock::getAddress)
+ .map(TAddress::getP)
+ .flatMap(List::stream)
+ .filter(tp -> pType.equals(tp.getType()))
+ .map(TP::getValue);
+ }
+
+ public static TConnectedAP addConnectedAp(SCL scd, String subNetworkName, String apName, String iedName) {
+ scd.setCommunication(new TCommunication());
+ TSubNetwork subNetwork = new TSubNetwork();
+ subNetwork.setName(subNetworkName);
+ scd.getCommunication().getSubNetwork().add(subNetwork);
+ TConnectedAP connectedAP = newConnectedAp(iedName, apName);
+ subNetwork.getConnectedAP().add(connectedAP);
+ return connectedAP;
+ }
+
+ public static SclRootAdapter createSclRootAdapterWithIed(String iedName) {
+ SCL scl = new SCL();
+ scl.setHeader(new THeader());
+ TIED ied = new TIED();
+ ied.setName(iedName);
+ scl.getIED().add(ied);
+ return new SclRootAdapter(scl);
+ }
+
+ public static SclRootAdapter createSclRootWithConnectedAp(String iedName, String apName) {
+ SclRootAdapter sclRootAdapter = createSclRootAdapterWithIed(iedName);
+ SCL scl = sclRootAdapter.getCurrentElem();
+ scl.setCommunication(new TCommunication());
+ TSubNetwork subNetwork = new TSubNetwork();
+ scl.getCommunication().getSubNetwork().add(subNetwork);
+ subNetwork.getConnectedAP().add(newConnectedAp(iedName, apName));
+ return sclRootAdapter;
+ }
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/CsvUtilsTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/CsvUtilsTest.java
new file mode 100644
index 000000000..d245b7b3f
--- /dev/null
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/CsvUtilsTest.java
@@ -0,0 +1,124 @@
+// SPDX-FileCopyrightText: 2023 RTE FRANCE
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfenergy.compas.sct.commons.util;
+
+import com.opencsv.bean.CsvBindByPosition;
+import lombok.*;
+import org.assertj.core.groups.Tuple;
+import org.junit.jupiter.api.Test;
+
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CsvUtilsTest {
+
+ private static final Tuple ROW_1 = Tuple.tuple("cel1x1", "cel1x2", "cel1x3");
+ private static final Tuple ROW_2 = Tuple.tuple("cel2x1", "cel2x2", "cel2x3");
+
+ @Test
+ void parseRows_should_parse_rows() {
+ //Given
+ StringReader csvReader = new StringReader("""
+ cel1x1;cel1x2;cel1x3
+ cel2x1;cel2x2;cel2x3
+ """);
+ //When
+ List rows = CsvUtils.parseRows(csvReader, Row.class);
+ //Then
+ assertThat(rows).extracting(Row::getCol1, Row::getCol2, Row::getCol3)
+ .containsExactly(
+ ROW_1,
+ ROW_2
+ );
+ }
+
+ @Test
+ void parseRows_should_ignore_empty_lines() {
+ //Given
+ StringReader csvReader = new StringReader("""
+ cel1x1;cel1x2;cel1x3
+
+ cel2x1;cel2x2;cel2x3
+ """);
+ //When
+ List rows = CsvUtils.parseRows(csvReader, Row.class);
+ //Then
+ assertThat(rows).extracting(Row::getCol1, Row::getCol2, Row::getCol3)
+ .containsExactly(
+ ROW_1,
+ ROW_2
+ );
+ }
+
+ @Test
+ void parseRows_should_treat_empty_string_as_null() {
+ //Given
+ StringReader csvReader = new StringReader("""
+ ;cel1x2;cel1x3
+ cel2x1;;cel2x3
+ cel3x1;cel3x2;
+ """);
+ //When
+ List rows = CsvUtils.parseRows(csvReader, Row.class);
+ //Then
+ assertThat(rows).extracting(Row::getCol1, Row::getCol2, Row::getCol3)
+ .containsExactly(
+ Tuple.tuple(null, "cel1x2", "cel1x3"),
+ Tuple.tuple("cel2x1", null, "cel2x3"),
+ Tuple.tuple("cel3x1", "cel3x2", null)
+ );
+ }
+
+ @Test
+ void parseRows_should_ignore_comment_lines() {
+ //Given
+ StringReader csvReader = new StringReader("""
+ cel1x1;cel1x2;cel1x3
+
+ # other comment line with indentation
+ line with # in the middle should not be ignored;a;b
+ """);
+ //When
+ List rows = CsvUtils.parseRows(csvReader, Row.class);
+ //Then
+ assertThat(rows).extracting(Row::getCol1, Row::getCol2, Row::getCol3)
+ .containsExactly(
+ ROW_1,
+ Tuple.tuple("line with # in the middle should not be ignored", "a", "b")
+ );
+ }
+
+ @Test
+ void parseRows_with_resource_path_should_parse_rows() {
+ //Given
+ String resourcePath = "csvutils/csv_utils_test_file.csv";
+ //When
+ List rows = CsvUtils.parseRows(resourcePath, StandardCharsets.UTF_8, Row.class);
+ //Then
+ assertThat(rows).extracting(Row::getCol1, Row::getCol2, Row::getCol3)
+ .containsExactly(
+ ROW_1,
+ ROW_2
+ );
+ }
+
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @Getter
+ @Setter
+ @EqualsAndHashCode
+ public static class Row {
+ @CsvBindByPosition(position = 0)
+ private String col1;
+ @CsvBindByPosition(position = 1)
+ private String col2;
+ @CsvBindByPosition(position = 2)
+ private String col3;
+ }
+
+}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/FcdaCandidatesTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/FcdaCandidatesTest.java
index 35eedbf73..16b73210c 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/FcdaCandidatesTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/FcdaCandidatesTest.java
@@ -59,13 +59,4 @@ void contains_when_a_parameter_is_blank_should_throw_exception() {
.isInstanceOf(IllegalArgumentException.class);
}
- @Test
- void contains_should_ignore_first_lines() {
- //Given
- FcdaCandidates fcdaCandidates = FcdaCandidates.SINGLETON;
- //When
- boolean result = fcdaCandidates.contains("FCDA.lnClass", "FCDA.doName", "FCDA.daName", "FCDA.fc");
- //Then
- assertThat(result).isFalse();
- }
}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/SclConstructorHelperTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/SclConstructorHelperTest.java
new file mode 100644
index 000000000..4c3b9ad98
--- /dev/null
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/SclConstructorHelperTest.java
@@ -0,0 +1,91 @@
+// SPDX-FileCopyrightText: 2023 RTE FRANCE
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package org.lfenergy.compas.sct.commons.util;
+
+import org.junit.jupiter.api.Test;
+import org.junit.platform.commons.support.ReflectionSupport;
+import org.lfenergy.compas.scl2007b4.model.TAddress;
+import org.lfenergy.compas.scl2007b4.model.TConnectedAP;
+import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec;
+import org.lfenergy.compas.scl2007b4.model.TP;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class SclConstructorHelperTest {
+
+ @Test
+ void constructor_should_throw_exception() {
+ // Given
+ Class testedClass = SclConstructorHelper.class;
+ // When & Then
+ assertThatThrownBy(() -> ReflectionSupport.newInstance(testedClass))
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
+ void newP_should_create_new_instance_with_given_parameters() {
+ //Given
+ String pType = "pType";
+ String pValue = "pValue";
+ //When
+ TP tp = SclConstructorHelper.newP(pType, pValue);
+ //Then
+ assertThat(tp).extracting(TP::getType, TP::getValue)
+ .containsExactly(pType, pValue);
+ }
+
+ @Test
+ void newDurationInMilliSec_should_create_new_instance_with_given_parameters() {
+ //Given
+ BigDecimal value = new BigDecimal(5);
+ String unit = "unit";
+ String multiplier = "multiplier";
+ //When
+ TDurationInMilliSec durationInMilliSec = SclConstructorHelper.newDurationInMilliSec(value, unit, multiplier);
+ //Then
+ assertThat(durationInMilliSec).extracting(TDurationInMilliSec::getValue, TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier)
+ .containsExactly(value, unit, multiplier);
+ }
+
+ @Test
+ void testNewDurationInMilliSec_should_create_new_instance_with_given_parameters() {
+ //Given
+ long value = 5L;
+ //When
+ TDurationInMilliSec durationInMilliSec = SclConstructorHelper.newDurationInMilliSec(value);
+ //Then
+ assertThat(durationInMilliSec).extracting(TDurationInMilliSec::getValue, TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier)
+ .containsExactly(new BigDecimal(value), "s", "m");
+ }
+
+ @Test
+ void newAddress_should_create_new_instance_with_given_parameters() {
+ //Given
+ List listOfP = List.of(
+ SclConstructorHelper.newP("type1", "value1"),
+ SclConstructorHelper.newP("type2", "value2"));
+ //When
+ TAddress tAddress = SclConstructorHelper.newAddress(listOfP);
+ //Then
+ assertThat(tAddress.getP())
+ .containsExactlyInAnyOrder(listOfP.toArray(new TP[0]));
+ }
+
+ @Test
+ void newConnectedAp_should_create_new_instance_with_given_parameters() {
+ //Given
+ String iedName = "iedName";
+ String apName = "apName";
+ //When
+ TConnectedAP tConnectedAP = SclConstructorHelper.newConnectedAp(iedName, apName);
+ //Then
+ assertThat(tConnectedAP).extracting(TConnectedAP::getIedName, TConnectedAP::getApName)
+ .containsExactly(iedName, apName);
+ }
+}
diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/UtilsTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/UtilsTest.java
index ec66b9a3e..525239889 100644
--- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/UtilsTest.java
+++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/UtilsTest.java
@@ -7,7 +7,9 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
import org.junit.platform.commons.support.ReflectionSupport;
import java.util.*;
@@ -436,4 +438,110 @@ void lnClassEquals_when_lnClass_has_more_than_one_value_should_throw_exception()
assertThatThrownBy(() -> Utils.lnClassEquals(lnClass1, lnClass2))
.isInstanceOf(IllegalArgumentException.class);
}
+
+ @Test
+ void sequence_should_return_range_of_numbers(){
+ // Given
+ long startInclusive = 1;
+ long endInclusive = 3;
+ // When
+ PrimitiveIterator.OfLong result = Utils.sequence(startInclusive, endInclusive);
+ // Then
+ assertThat(result.next()).isEqualTo(1);
+ assertThat(result.next()).isEqualTo(2);
+ assertThat(result.next()).isEqualTo(3);
+ assertThat(result.hasNext()).isFalse();
+ assertThatThrownBy(result::nextLong).isInstanceOf(NoSuchElementException.class);
+ }
+
+ @Test
+ void sequence_should_return_empty_iterator(){
+ // Given
+ long startInclusive = 1;
+ long endInclusive = 0;
+ // When
+ PrimitiveIterator.OfLong result = Utils.sequence(startInclusive, endInclusive);
+ // Then
+ assertThat(result.hasNext()).isFalse();
+ assertThatThrownBy(result::nextLong).isInstanceOf(NoSuchElementException.class);
+ }
+
+ @Test
+ void sequence_should_throw_exception_when_MAX_VALUE(){
+ // Given
+ long startInclusive = 1;
+ long endInclusive = Long.MAX_VALUE;
+ // When & Then
+ assertThatThrownBy(() -> Utils.sequence(startInclusive, endInclusive))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("End cannot exceed Long.MAX_VALUE - 1");
+ }
+
+ @Test
+ void macAddressSequence_should_return_range_of_macAddresses(){
+ // Given
+ String startInclusive = "01-0C-CD-01-00-FE";
+ String endInclusive = "01-0C-CD-01-01-01";
+ // When
+ Iterator result = Utils.macAddressSequence(startInclusive, endInclusive);
+ // Then
+ assertThat(result.next()).isEqualTo("01-0C-CD-01-00-FE");
+ assertThat(result.next()).isEqualTo("01-0C-CD-01-00-FF");
+ assertThat(result.next()).isEqualTo("01-0C-CD-01-01-00");
+ assertThat(result.next()).isEqualTo("01-0C-CD-01-01-01");
+ assertThat(result.hasNext()).isFalse();
+ assertThatThrownBy(result::next).isInstanceOf(NoSuchElementException.class);
+ }
+
+ @ParameterizedTest
+ @CsvSource({"0x123456789ABC,12-34-56-78-9A-BC", "0xAA,00-00-00-00-00-AA"})
+ void longToMacAddress_should_convert_long_to_mac_address(long macAddress, String expected){
+ // Given : parameter
+ // When
+ String result = Utils.longToMacAddress(macAddress);
+ // Then
+ assertThat(result.toUpperCase()).isEqualTo(expected);
+ }
+
+ @ParameterizedTest
+ @CsvSource(value = {"-1,macAddress must be a positive integer but got : -1", // Negative number
+ "281474976710656,macAddress cannot exceed 281474976710655 but got : 281474976710656", //FF-FF-FF-FF-FF-FF
+ "9223372036854775807,macAddress cannot exceed 281474976710655 but got : 9223372036854775807"}) //Long MAX_VALUE
+ void longToMacAddress_when_long_out_of_range_should_throw_exception(long macAddress, String expectedMessage){
+ // Given : parameter
+ // When & Then
+ assertThatThrownBy(() -> Utils.longToMacAddress(macAddress))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessage(expectedMessage);
+ }
+
+ @ParameterizedTest
+ @CsvSource({"00-00-00-00-00-00,0", "12-34-56-78-9A-BC,20015998343868", "FF-FF-FF-FF-FF-FF,281474976710655",
+ "11:ab:FF:01:c8:2A,19430415386666"})
+ void macAddressToLong_should_convert_mac_address_to_long(String macAddress, long expected){
+ // Given : parameters
+ // When
+ long result = Utils.macAddressToLong(macAddress);
+ // Then
+ assertThat(result).isEqualTo(expected);
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"", "123", "AB-CD", "FF_FF_FF_FF_FF_FF", "LK-JI-HG-FE-DC-AB", "AB-AB-AB-AB-AB-AB-AB"})
+ void macAddressToLong_when_malformed_macAddress_should_throw_exception(String macAddress){
+ // Given : parameters
+ // When & Then
+ assertThatThrownBy(() -> Utils.macAddressToLong(macAddress))
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
+ @ParameterizedTest
+ @CsvSource({"0,6,000000", "255,1,FF", "255,2,FF", "255,3,0FF", "10,2,0A"})
+ void toHex_should_return_hexadecimal(long number, int length, String expected){
+ // Given
+ // When
+ String result = Utils.toHex(number, length);
+ // Then
+ assertThat(result).isEqualTo(expected);
+ }
}
diff --git a/sct-commons/src/test/resources/ControlBlockCommunicationTemplates.csv b/sct-commons/src/test/resources/ControlBlockCommunicationTemplates.csv
new file mode 100644
index 000000000..37184ecb7
--- /dev/null
+++ b/sct-commons/src/test/resources/ControlBlockCommunicationTemplates.csv
@@ -0,0 +1,9 @@
+# SPDX-FileCopyrightText: 2022 RTE FRANCE
+#
+# SPDX-License-Identifier: Apache-2.0
+
+#CB Type;X.Y;Z.W;IedType;IedRedundancy;Bay Internal OR External;VLAN-ID;VLAN-PRIORITY;MINTIME;MAXTIME
+GOOSE;01.00;009.001;BCU;A;BAY_INTERNAL;300;4;10;2000
+SV;01.00;009.001;BCU;A;BAY_INTERNAL;None;None;;
+GOOSE;01.00;009.001;BCU;A;BAY_EXTERNAL;301;5;15;5000
+SV;01.00;009.001;BCU;A;BAY_EXTERNAL;None;None;;
diff --git a/sct-commons/src/test/resources/FcdaCandidates.csv b/sct-commons/src/test/resources/FcdaCandidates.csv
index 6596b7fb6..1cdfa1861 100644
--- a/sct-commons/src/test/resources/FcdaCandidates.csv
+++ b/sct-commons/src/test/resources/FcdaCandidates.csv
@@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: 2022 RTE FRANCE
#
# SPDX-License-Identifier: Apache-2.0
-# !!! WARNING !!! The 5 first lines are skipped
+
FCDA.lnClass;FCDA.doName;FCDA.daName;FCDA.fc
ANCR;DoName;daNameST;ST
ANCR;DoName;daNameMX;MX
diff --git a/sct-commons/src/test/resources/csvutils/csv_utils_test_file.csv b/sct-commons/src/test/resources/csvutils/csv_utils_test_file.csv
new file mode 100644
index 000000000..77151a08a
--- /dev/null
+++ b/sct-commons/src/test/resources/csvutils/csv_utils_test_file.csv
@@ -0,0 +1,7 @@
+# SPDX-FileCopyrightText: 2022 RTE FRANCE
+#
+# SPDX-License-Identifier: Apache-2.0
+
+#col1;col2;col3
+cel1x1;cel1x2;cel1x3
+cel2x1;cel2x2;cel2x3
diff --git a/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml
similarity index 100%
rename from sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml
rename to sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_bound_ied_controls_fcda.xml
diff --git a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml
new file mode 100644
index 000000000..5e9f4d991
--- /dev/null
+++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ on
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ on
+
+
+
+ IED_NAME1
+
+
+ IED_NAME1
+
+
+ IED_NAME1
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ on
+
+
+
+ IED_NAME1
+
+
+ IED_NAME1
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ on
+ off
+ test
+
+
+
diff --git a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_extref_errors.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_extref_errors.xml
index 8abf4bb0c..0aa4abf82 100644
--- a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_extref_errors.xml
+++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_extref_errors.xml
@@ -7,7 +7,7 @@
-
+
@@ -39,7 +39,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_ied_errors.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_ied_errors.xml
index 410fbc21f..2eac0b60f 100644
--- a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_ied_errors.xml
+++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_ied_errors.xml
@@ -8,7 +8,7 @@
-
+
@@ -33,7 +33,7 @@
-
+
@@ -57,7 +57,7 @@
-
+
@@ -85,7 +85,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml
index 041e1f8d7..1c6acbf12 100644
--- a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml
+++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml
@@ -7,7 +7,7 @@
-
+
@@ -91,7 +91,7 @@
-
+
@@ -122,7 +122,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml
index 4cd987c58..177659525 100644
--- a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml
+++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml
@@ -7,7 +7,7 @@
-
+
@@ -41,7 +41,7 @@
-
+
@@ -75,7 +75,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml
index adf03d363..567eb21ac 100644
--- a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml
+++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml
@@ -7,7 +7,7 @@
-
+
@@ -41,7 +41,7 @@
-
+
@@ -75,7 +75,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_success.xml b/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_success.xml
index 66c8cc057..c7869dfd7 100644
--- a/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_success.xml
+++ b/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_success.xml
@@ -7,7 +7,7 @@
-
+
@@ -33,7 +33,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml b/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml
index 424308740..3fd8ae780 100644
--- a/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml
+++ b/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml
@@ -7,7 +7,7 @@
-
+
@@ -104,7 +104,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_with_ied_errors.xml b/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_with_ied_errors.xml
index b8571944a..5955cca60 100644
--- a/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_with_ied_errors.xml
+++ b/sct-commons/src/test/resources/scd-extref-iedname/scd_set_extref_iedname_with_ied_errors.xml
@@ -8,7 +8,7 @@
-
+
@@ -21,7 +21,7 @@
-
+
@@ -46,7 +46,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd.xml b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd.xml
index 1248f24fb..61c829f27 100644
--- a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd.xml
+++ b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd.xml
@@ -25,7 +25,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_lnode_with_many_compas_icdheader.xml b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_lnode_with_many_compas_icdheader.xml
index 5542476fb..e89ea6852 100644
--- a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_lnode_with_many_compas_icdheader.xml
+++ b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_lnode_with_many_compas_icdheader.xml
@@ -25,13 +25,13 @@
-
+
-
+
-
+
diff --git a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml
index c2d4c04d1..d06f64392 100644
--- a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml
+++ b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml
@@ -16,14 +16,14 @@
-
+
-
+
diff --git a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/ssd.xml b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/ssd.xml
index d6f0de482..591e03dd0 100644
--- a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/ssd.xml
+++ b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/ssd.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test1_LD_STATUS_INACTIVE.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test1_LD_STATUS_INACTIVE.scd
index 9aa92348e..cad226b1d 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test1_LD_STATUS_INACTIVE.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test1_LD_STATUS_INACTIVE.scd
@@ -30,12 +30,12 @@
-
+
-
+
@@ -75,7 +75,7 @@
-
+
@@ -112,7 +112,7 @@
-
+
@@ -149,7 +149,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test2_LD_STATUS_INACTIVE.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test2_LD_STATUS_INACTIVE.scd
index 316933867..76dd3fce0 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test2_LD_STATUS_INACTIVE.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test2_LD_STATUS_INACTIVE.scd
@@ -30,7 +30,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -107,7 +107,7 @@
-
+
@@ -144,7 +144,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_Dai_Not_Updatable.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_Dai_Not_Updatable.scd
index 48c81222d..730ca6fd7 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_Dai_Not_Updatable.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_Dai_Not_Updatable.scd
@@ -30,7 +30,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -107,7 +107,7 @@
-
+
@@ -144,7 +144,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingBeh.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingBeh.scd
index ee8ac2930..b29dcd927 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingBeh.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingBeh.scd
@@ -30,7 +30,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivate.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivate.scd
index eba19585c..09f288ffa 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivate.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivate.scd
@@ -30,7 +30,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivateAttribute.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivateAttribute.scd
index c2e919b1b..83961dcac 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivateAttribute.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivateAttribute.scd
@@ -30,7 +30,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingMod.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingMod.scd
index 1c6850178..41c519502 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingMod.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_KO_MissingMod.scd
@@ -30,7 +30,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_LD_STATUS_ACTIVE.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_LD_STATUS_ACTIVE.scd
index 85a71fb25..5dc4b89f4 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_LD_STATUS_ACTIVE.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_LD_STATUS_ACTIVE.scd
@@ -30,7 +30,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -107,7 +107,7 @@
-
+
@@ -144,7 +144,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_LD_STATUS_UNTESTED.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_LD_STATUS_UNTESTED.scd
index 205bcff51..3cf0dad8e 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_LD_STATUS_UNTESTED.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_LD_STATUS_UNTESTED.scd
@@ -30,7 +30,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -107,7 +107,7 @@
-
+
@@ -144,7 +144,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_Template.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_Template.scd
index 651c5eff3..7a79a5be2 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_Template.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue68_Test_Template.scd
@@ -30,7 +30,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -107,7 +107,7 @@
-
+
@@ -144,7 +144,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd
index 68a260d66..e3fae2ae9 100644
--- a/sct-commons/src/test/resources/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd
+++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd
@@ -30,7 +30,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
@@ -107,7 +107,7 @@
-
+
@@ -144,7 +144,7 @@
-
+
@@ -176,7 +176,7 @@
-
+
@@ -213,7 +213,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-substation-import-ssd/scd_with_substation.xml b/sct-commons/src/test/resources/scd-substation-import-ssd/scd_with_substation.xml
index 49e15f457..60df1a663 100644
--- a/sct-commons/src/test/resources/scd-substation-import-ssd/scd_with_substation.xml
+++ b/sct-commons/src/test/resources/scd-substation-import-ssd/scd_with_substation.xml
@@ -27,7 +27,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-substation-import-ssd/ssd.xml b/sct-commons/src/test/resources/scd-substation-import-ssd/ssd.xml
index c4688b459..37b4f304d 100644
--- a/sct-commons/src/test/resources/scd-substation-import-ssd/ssd.xml
+++ b/sct-commons/src/test/resources/scd-substation-import-ssd/ssd.xml
@@ -23,14 +23,14 @@
-
+
-
+
@@ -57,7 +57,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml
index 9b15fc0b5..fbbbd0dfd 100644
--- a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml
+++ b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml
@@ -65,13 +65,79 @@
-
+
+
+
+
+
+ LD_WITH_1_Bad_InRef_DOI_InRef4
+
+
+
+ OLD_VAL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LD_WITH_1_Bad_InRef_DOI_InRef4
+
+
+
+ OLD_VAL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LD_WITH_1_Bad_InRef_DOI_InRef4
+
+
+
+ OLD_VAL
+
+
+
+
+
+
+
+
+
+
@@ -79,6 +145,13 @@
+
+
+
+
+
+
+
diff --git a/sct-commons/src/test/resources/scl-ln-adapter/scd_with_ln.xml b/sct-commons/src/test/resources/scl-ln-adapter/scd_with_ln.xml
index d40d1cc89..d1493e76f 100644
--- a/sct-commons/src/test/resources/scl-ln-adapter/scd_with_ln.xml
+++ b/sct-commons/src/test/resources/scl-ln-adapter/scd_with_ln.xml
@@ -8,7 +8,7 @@
-
+
@@ -42,7 +42,7 @@
-
+
diff --git a/sct-commons/src/test/resources/scl-remove-controlBlocks-dataSet-extRefSrc/scl-with-control-blocks.xml b/sct-commons/src/test/resources/scl-remove-controlBlocks-dataSet-extRefSrc/scl-with-control-blocks.xml
index 94db16aa8..d395939f3 100644
--- a/sct-commons/src/test/resources/scl-remove-controlBlocks-dataSet-extRefSrc/scl-with-control-blocks.xml
+++ b/sct-commons/src/test/resources/scl-remove-controlBlocks-dataSet-extRefSrc/scl-with-control-blocks.xml
@@ -33,7 +33,7 @@
-
+