From f14d0e5fd43662091ef36cdd91c1a0bf950c6917 Mon Sep 17 00:00:00 2001 From: Aliou DIAITE Date: Fri, 6 Jan 2023 18:27:55 +0100 Subject: [PATCH 1/9] feat(216): check that Datasets, FCDAs, ControlBlocks (GOOSE, REPORT and SMV) satisfy limits fixed by config Signed-off-by: Aliou DIAITE --- .../compas/sct/commons/scl/ExtRefService.java | 1 + .../compas/sct/commons/scl/SclService.java | 26 +- .../commons/scl/ied/AbstractLNAdapter.java | 33 +- .../commons/scl/ied/AccessPointAdapter.java | 328 ++++++++++++++++++ .../sct/commons/scl/ied/IEDAdapter.java | 51 ++- .../sct/commons/scl/ied/InputsAdapter.java | 1 + .../sct/commons/util/ServicesConfigEnum.java | 14 + .../sct/commons/scl/SclServiceTest.java | 127 ++++++- .../scl/ied/AccessPointAdapterTest.java | 281 +++++++++++++++ .../sct/commons/scl/ied/IEDAdapterTest.java | 88 ++++- .../commons/scl/ied/InputsAdapterTest.java | 28 ++ .../scd_check_coherent_extRefs.xml | 44 +++ ...ck_limitation_binded_ied_controls_fcda.xml | 148 ++++++++ ..._check_limitation_ied_controls_dataset.xml | 89 +++++ ...aset_and_controlblocks_success_analyze.xml | 130 +++++++ 15 files changed, 1353 insertions(+), 36 deletions(-) create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java create mode 100644 sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java create mode 100644 sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml create mode 100644 sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml create mode 100644 sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml create mode 100644 sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml 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 57f516127..6e6f0f9f2 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 @@ -26,6 +26,7 @@ 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. diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java index fa177e8d5..7fad51987 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java @@ -234,10 +234,10 @@ public static List getExtRefInfo(SCL scd, String iedName, String ldI /** * Create LDevice - * @param scd - * @param iedName - * @param ldInst - * @return + * @param scd SCL file in which LDevice should be found + * @param iedName name of IED in which LDevice is localized + * @param ldInst LdInst of LDevice for which adapter is created + * @return created LDevice adapter */ private static LDeviceAdapter createLDeviceAdapter(SCL scd, String iedName, String ldInst) { SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); @@ -572,4 +572,22 @@ public static SclReport updateLDeviceStatus(SCL scd) { sclReport.setSclRootAdapter(sclRootAdapter); return sclReport; } + + /** + * Checks Control Blocks, DataSets and FCDA number limitation into Access Points + * @param scd SCL file for which LDevice should be activated or deactivated + * @return SclReport Object that contain SCL file and set of errors + */ + public static SclReport analyzeDataGroups(SCL scd) { + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + List sclReportItems = sclRootAdapter.streamIEDAdapters() + .map(iedAdapter -> { + List list = new ArrayList<>(); + list.addAll(iedAdapter.checkDataGroupCoherence()); + list.addAll(iedAdapter.checkBindingDataGroupCoherence()); + return list; + }).flatMap(Collection::stream).toList(); + + return new SclReport(sclRootAdapter, sclReportItems); + } } 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 89ab101f5..c7718c026 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 @@ -159,7 +159,9 @@ public String getLnType() { * @return list of LNode ExtRefs elements */ public List getExtRefs() { - return getExtRefs(null); + List tExtRefs = getExtRefs(null); + if(tExtRefs == null || tExtRefs.isEmpty()) return Collections.emptyList(); + else return tExtRefs; } /** @@ -961,14 +963,25 @@ public Optional findControlBlock(String name, ControlBlockE public ControlBlockAdapter createControlBlockIfNotExists(String cbName, String id, String datSet, ControlBlockEnum controlBlockEnum) { return findControlBlock(cbName, controlBlockEnum) - .orElseGet(() -> addControlBlock( - switch (controlBlockEnum) { - case GSE -> new GooseControlBlock(cbName, id, datSet); - case SAMPLED_VALUE -> new SMVControlBlock(cbName, id, datSet); - case REPORT -> new ReportControlBlock(cbName, id, datSet); - default -> throw new IllegalArgumentException("Unsupported ControlBlock Type " + controlBlockEnum); - } - ) - ); + .orElseGet(() -> addControlBlock( + switch (controlBlockEnum) { + case GSE -> new GooseControlBlock(cbName, id, datSet); + case SAMPLED_VALUE -> new SMVControlBlock(cbName, id, datSet); + case REPORT -> new ReportControlBlock(cbName, id, datSet); + default -> throw new IllegalArgumentException("Unsupported ControlBlock Type " + controlBlockEnum); + } + ) + ); + } + + public List getFCDAs(TExtRef tExtRef){ + TControl tControl1 = getTControlsByType(AccessPointAdapter.getControlTypeClass(tExtRef.getServiceType())).stream() + .filter(tControl -> tExtRef.getSrcCBName() != null && tExtRef.getSrcCBName().equals(tControl.getName())) + .findFirst().orElseThrow(() -> new ScdException(String.format("DataSet linked with Control Block %s not found", tExtRef.getSrcCBName()))); + return getCurrentElem().getDataSet().stream() + .filter(tDataSet -> tDataSet.getName().equals(tControl1.getDatSet())) + .map(TDataSet::getFCDA) + .flatMap(Collection::stream) + .toList(); } } 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 new file mode 100644 index 000000000..89de047be --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java @@ -0,0 +1,328 @@ +/* + * // SPDX-FileCopyrightText: 2023 RTE FRANCE + * // + * // SPDX-License-Identifier: Apache-2.0 + */ + +package org.lfenergy.compas.sct.commons.scl.ied; + +import org.lfenergy.compas.scl2007b4.model.*; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; +import org.lfenergy.compas.sct.commons.exception.ScdException; +import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; +import org.lfenergy.compas.sct.commons.util.ServicesConfigEnum; +import org.lfenergy.compas.sct.commons.util.Utils; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * A representation of the model object + * {@link org.lfenergy.compas.scl2007b4.model.TAccessPoint AccessPoint}. + *

+ * The following features are supported: + *

+ *
    + *
  1. Functions
  2. + *
      + *
    • {@link AccessPointAdapter#checkFCDALimitations Returns the value of the name attribute}
    • + *
    • {@link AccessPointAdapter#checkControlsLimitation Returns the value of the Service object}
    • + *
    + *
+ * + */ + +public class AccessPointAdapter extends SclElementAdapter { + + public static final long MAX_OCCURRENCE_NO_LIMIT_VALUE = -1L; + private static final String CLIENT_IED_NAME = "The Client IED "; + + /** + * Constructor + * + * @param parentAdapter Parent container reference + * @param tAccessPoint Current reference + */ + public AccessPointAdapter(IEDAdapter parentAdapter, TAccessPoint tAccessPoint) { + super(parentAdapter, tAccessPoint); + } + + /** + * Check if current element is a child of the parent element + * + * @return true if the currentElem is part of the parentAdapter children + */ + @Override + protected boolean amChildElementRef() { + return parentAdapter.getCurrentElem().getAccessPoint().stream() + .anyMatch(tAccessPoint -> tAccessPoint.getName().equals(currentElem.getName())); + } + + @Override + protected String elementXPath() { + return String.format("AccessPoint[%s]", Utils.xpathAttributeFilter("name", currentElem.isSetName() ? currentElem.getName() : null)); + } + + /** + * Gets XPath path to current element from parent element + * @return path to current element + */ + @Override + public String getXPath(){ + String parentXpath = (parentAdapter != null) ? parentAdapter.getXPath() : ""; + return parentXpath + "/" + elementXPath(); + } + + /** + * Gets all LDevice from AccessPoint + * @return Stream of LDeviceAdapter object as IEDs of SCL + */ + private Stream streamLDeviceAdapters() { + if(!currentElem.isSetServer()) return Stream.empty(); + return currentElem.getServer().getLDevice().stream() + .map(tlDevice -> new LDeviceAdapter(getParentAdapter(), tlDevice)); + } + + /** + * Checks FCDA number limitation into each LDevice of AccessPoint + * @return List of errors encountered for LDevices + */ + public List checkFCDALimitations(){ + long max = getMaxInstanceAutorized(ServicesConfigEnum.FCDA); + if(currentElem.getServer() == null) return new ArrayList<>(); + return currentElem.getServer().getLDevice().stream() + .map(tlDevice -> new LDeviceAdapter(parentAdapter, tlDevice)) + .map(lDeviceAdapter -> + lDeviceAdapter.getLNAdaptersInclundigLN0().stream() + .map(abstractLNAdapter -> abstractLNAdapter.getCurrentElem().getDataSet()) + .flatMap(Collection::stream) + .filter(tDataSet -> max == MAX_OCCURRENCE_NO_LIMIT_VALUE || tDataSet.getFCDA().size() > max) + .map(tDataSet -> SclReportItem.warning(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() + ).flatMap(Collection::stream).toList(); + } + + /** + * 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){ + long max = getMaxInstanceAutorized(servicesConfigEnum); + long value = getNumberOfItems(servicesConfigEnum); + return max == MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.warning(getXPath(), String.format("%s %s", msg, parentAdapter.getName()))); + } + + /** + * Counts all occurence of Control into AccessPoint + * @param servicesConfigEnum type (GOOSE, Report, SampledValue, DataSet) + * @return number of occurrence + */ + private long getNumberOfItems(ServicesConfigEnum servicesConfigEnum) { + if (!currentElem.isSetServer()) return 0L; + return currentElem.getServer().getLDevice().stream() + .map(tlDevice -> new LDeviceAdapter(parentAdapter, tlDevice)) + .map(LDeviceAdapter::getLNAdaptersInclundigLN0) + .flatMap(Collection::stream) + .map(abstractLNAdapter -> { + if(servicesConfigEnum == ServicesConfigEnum.DATASET) return abstractLNAdapter.getCurrentElem().getDataSet(); + else return abstractLNAdapter.getTControlsByType(getControlTypeClass(servicesConfigEnum));}) + .mapToLong(Collection::size) + .sum(); + } + + private Class getControlTypeClass(ServicesConfigEnum servicesConfigEnum) { + return switch (servicesConfigEnum) { + case REPORT -> TReportControl.class; + case GSE -> TGSEControl.class; + case SMV -> TSampledValueControl.class; + default -> null; + }; + } + + /** + * Gets max number authorized in configuration of each elements DataSets, FCDAs, Control Blocks) into an AccessPoint + * @param servicesConfigEnum element type + * @return max number authorized by config + */ + private long getMaxInstanceAutorized(ServicesConfigEnum servicesConfigEnum){ + if(currentElem.getServices() == null) return MAX_OCCURRENCE_NO_LIMIT_VALUE; + TServices tServices = currentElem.getServices(); + + return switch (servicesConfigEnum) { + case DATASET -> (tServices.getConfDataSet() != null && tServices.getConfDataSet().isSetMax()) ? tServices.getConfDataSet().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case FCDA -> (tServices.getConfDataSet() != null && tServices.getConfDataSet().isSetMaxAttributes()) ? tServices.getConfDataSet().getMaxAttributes() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case REPORT -> (tServices.getConfReportControl() != null && tServices.getConfReportControl().isSetMax()) ? tServices.getConfReportControl().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case GSE -> (tServices.getGOOSE() != null && tServices.getGOOSE().isSetMax()) ? tServices.getGOOSE().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case SMV -> (tServices.getSMVsc() != null && tServices.getSMVsc().isSetMax()) ? tServices.getSMVsc().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + }; + } + + + /** + * Checks FCDA number limitation for binded IED + * @param msg message to display hen error occured + * @return Optional of encountered error or empty + */ + public Optional checkLimitationForBindedIEDFCDAs(Set tExtRefs, String msg){ + long value = tExtRefs.stream() + .map( tExtRef -> { + IEDAdapter iedAdapter = getParentAdapter().getParentAdapter().getIEDAdapterByName(tExtRef.getIedName()); + LDeviceAdapter lDeviceAdapter; + if (tExtRef.getSrcLDInst() != null) + lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(tExtRef.getSrcLDInst()); + else lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(tExtRef.getLdInst()); + + AbstractLNAdapter abstractLNAdapter; + if (!tExtRef.isSetSrcLNClass() || tExtRef.getSrcLNClass().contains(TLLN0Enum.LLN_0.value())) + abstractLNAdapter = lDeviceAdapter.getLN0Adapter(); + else + abstractLNAdapter = lDeviceAdapter.getLNAdapter(tExtRef.getSrcLNClass().get(0), tExtRef.getSrcLNInst(), tExtRef.getSrcPrefix()); + + return abstractLNAdapter.getFCDAs(tExtRef); + }).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.warning(getParentAdapter().getXPath(), msg)); + + } + + /** + * + * @param sclReportItems + * @param tExtRefSet + */ + record ExtRefAnalyzeRecord(List sclReportItems, Set tExtRefSet){ + } + + /** + * Returns all ExtRef of the AccessPoint which have SrcCBName set and ServiceType provided + * @return ExtRefAnalyzeRecord object containing Set of ExtRefs and errors list for ExtRefs with SrcCBName and without ServiceType provided + */ + public ExtRefAnalyzeRecord getAllCoherentExtRefForAnalyze() { + List sclReportItems = new ArrayList<>(); + Set tExtRefSet= new HashSet<>(); + streamLDeviceAdapters().map(lDeviceAdapter -> { + Set tExtRefs = lDeviceAdapter.getLN0Adapter().getExtRefs().stream().filter(TExtRef::isSetSrcCBName).collect(Collectors.toSet()); + sclReportItems.addAll(checkExtRefWithoutServiceType(tExtRefs)); + tExtRefs.removeIf(tExtRef -> !tExtRef.isSetServiceType()); + return tExtRefs; + }).flatMap(Collection::stream).forEach(tExtRef -> { + if(tExtRefSet.isEmpty()) + tExtRefSet.add(tExtRef); + if(tExtRefSet.stream().noneMatch(t -> extrefEquals(tExtRef,t))) + tExtRefSet.add(tExtRef); + }); + return new ExtRefAnalyzeRecord(sclReportItems, tExtRefSet); + } + + /** + * Checks all ExtRefs with SrcCBName and without ServiceType provided + * @param tExtRefs Set of ExtRefs to check + * @return errors list + */ + private List checkExtRefWithoutServiceType(Set tExtRefs) { + return tExtRefs.stream() + .filter(tExtRef -> !tExtRef.isSetServiceType()) + .map(tExtRef -> + SclReportItem.warning(getXPath(), "ExtRef signal without ServiceType : " + tExtRef.getDesc())) + .toList(); + } + + /** + * Checks if two ExtRefs fed by same Control Block for SCL limits analyze + * Nota : this equality is only for checking limitation check + * @param t1 extref to compare + * @param t2 extref to compare + * @return true if the two ExtRef are fed by same Control Block, otherwise false + */ + private static boolean extrefEquals(TExtRef t1, TExtRef t2) { + if(!t1.isSetSrcLNClass()) t1.getSrcLNClass().add(TLLN0Enum.LLN_0.value()); + if(!t2.isSetSrcLNClass()) t2.getSrcLNClass().add(TLLN0Enum.LLN_0.value()); + return Utils.equalsOrBothBlank(t1.getIedName(), t2.getIedName()) + && Utils.equalsOrBothBlank(t1.getSrcLDInst(), t2.getSrcLDInst()) + && (t1.getSrcLNClass().equals(t2.getSrcLNClass())) + && Utils.equalsOrBothBlank(t1.getSrcLNInst(), t2.getSrcLNInst()) + && Utils.equalsOrBothBlank(t1.getSrcPrefix(),t2.getSrcPrefix()) + && t1.getServiceType().equals(t2.getServiceType()); + } + + /** + * Checks Control Blocks (Report, Goose, SMV) number limitation for binded IED + * @return List of errors encountered + */ + public List checkLimitationForBindedIEDControls(Set tExtRefs){ + 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); + }; + }).toList().stream() + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + } + + /** + * Checks Control Block number limitation for binded 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) { + long max = getMaxInstanceAutorizedForBindedIED(servicesConfigEnum); + long value = tExtRefs.size(); + return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.warning(getParentAdapter().getXPath(), msg)); + } + + public static Class getControlTypeClass(TServiceType tServiceType) { + if(tServiceType == null) throw new IllegalArgumentException("TServiceType of ExtRef should not be null"); + return switch (tServiceType) { + case REPORT -> TReportControl.class; + case GOOSE -> TGSEControl.class; + case SMV -> TSampledValueControl.class; + case POLL -> throw new ScdException("Unsupported value: " + tServiceType); + }; + } + + /** + * 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 getMaxInstanceAutorizedForBindedIED(ServicesConfigEnum servicesConfigEnum) { + if (currentElem.getServices() == null) { + return AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + } else { + TClientServices tClientServices = currentElem.getServices().getClientServices(); + return switch (servicesConfigEnum) { + case DATASET, FCDA -> tClientServices != null && tClientServices.isSetMaxAttributes() ? tClientServices.getMaxAttributes() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case REPORT -> tClientServices != null && tClientServices.isSetMaxReports() ? tClientServices.getMaxReports() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case GSE -> tClientServices != null && tClientServices.isSetMaxGOOSE() ? tClientServices.getMaxGOOSE() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case SMV -> tClientServices != null && tClientServices.isSetMaxSMV() ? tClientServices.getMaxSMV() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + }; + } + } + + + +} 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 542c9887a..6eac2c2ef 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 @@ -9,17 +9,16 @@ import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.ExtRefBindingInfo; import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo; +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.SclElementAdapter; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; +import org.lfenergy.compas.sct.commons.util.ServicesConfigEnum; import org.lfenergy.compas.sct.commons.util.Utils; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.stream.Stream; /** @@ -320,4 +319,48 @@ public Optional getPrivateHeader(String privateType) { public Optional getPrivateCompasBay() { return PrivateService.extractCompasPrivate(currentElem, TCompasBay.class); } + + /** + * Checks if Controls, DataSets and FCDAs of IED respect config limitation + * @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) + .toList(); + } + + /** + * 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); + AccessPointAdapter.ExtRefAnalyzeRecord extRefAnalyzeRecord = accessPointAdapter.getAllCoherentExtRefForAnalyze(); + List sclReportItems = new ArrayList<>(extRefAnalyzeRecord.sclReportItems()); + accessPointAdapter.checkLimitationForBindedIEDFCDAs(extRefAnalyzeRecord.tExtRefSet(), "There are too much FCDA for the Client IED " + getName()) + .ifPresent(sclReportItems::add); + sclReportItems.addAll(accessPointAdapter.checkLimitationForBindedIEDControls(extRefAnalyzeRecord.tExtRefSet())); + return sclReportItems; + }).flatMap(List::stream).toList(); + + } + + + + } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java index b2e5f33e2..0f0496fc1 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java @@ -420,4 +420,5 @@ private IEDAdapter getIedAdapter() { private SclRootAdapter getSclRootAdapter() { return getIedAdapter().getParentAdapter(); } + } 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 new file mode 100644 index 000000000..cfa6755e0 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.util; + +public enum ServicesConfigEnum { + GSE, + SMV, + REPORT, + DATASET, + FCDA; + +} 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 645615ced..114e477c0 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 @@ -31,6 +31,61 @@ class SclServiceTest { + /* private static Stream sclProviderMissingRequiredObjects() throws Exception { + SCL scl1 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_KO_MissingBeh.scd"); + SCL scl2 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivate.scd"); + SCL scl3 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivateAttribute.scd"); + SCL scl4 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_KO_MissingMod.scd"); + Tuple[] scl1Errors = new Tuple[]{Tuple.tuple("The LDevice doesn't have a DO @name='Beh' OR its associated DA@fc='ST' AND DA@name='stVal'", + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; + Tuple[] scl2Errors = new Tuple[]{Tuple.tuple("The LDevice doesn't have a Private compas:LDevice.", + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; + Tuple[] scl3Errors = new Tuple[]{Tuple.tuple("The Private compas:LDevice doesn't have the attribute 'LDeviceStatus'", + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; + Tuple[] scl4Errors = new Tuple[]{Tuple.tuple("The LDevice doesn't have a DO @name='Mod'", + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; + return Stream.of( + Arguments.of("MissingDOBeh", scl1, scl1Errors), + Arguments.of("MissingLDevicePrivate", scl2, scl2Errors), + Arguments.of("MissingLDevicePrivateAttribute", scl3, scl3Errors), + Arguments.of("MissingDOMod", scl4, scl4Errors) + ); + } + + private static Stream sclProviderBasedLDeviceStatus() throws Exception { + SCL scl1 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_LD_STATUS_ACTIVE.scd"); + SCL scl2 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_LD_STATUS_UNTESTED.scd"); + SCL scl3 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test1_LD_STATUS_INACTIVE.scd"); + Tuple[] scl1Errors = new Tuple[]{Tuple.tuple("The LDevice cannot be set to 'off' but has not been selected into SSD.", + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", + "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", + "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" + )}; + Tuple[] scl2Errors = new Tuple[]{Tuple.tuple("The LDevice cannot be set to 'off' but has not been selected into SSD.", + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", + "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", + "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" + )}; + Tuple[] scl3Errors = new Tuple[]{Tuple.tuple("The LDevice is not qualified into STD but has been selected into SSD.", + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", + "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", + "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" + )}; + return Stream.of( + Arguments.of("ACTIVE", scl1, scl1Errors), + Arguments.of("UNTESTED", scl2, scl2Errors), + Arguments.of("INACTIVE", scl3, scl3Errors) + ); + } + + */ + @Test void testAddHistoryItem() throws ScdException { SclRootAdapter sclRootAdapter = new SclRootAdapter("hId", SclRootAdapter.VERSION, SclRootAdapter.REVISION); @@ -133,7 +188,6 @@ void testAddSubnetworksWithoutImportingIcdAddressAndPhysConn() { assertThat(marshalledScd).doesNotContain("
", "PhysConn"); } - @Test void testGetSubnetwork() { SclRootAdapter sclRootAdapter = new SclRootAdapter("hId", SclRootAdapter.VERSION, SclRootAdapter.REVISION); @@ -285,6 +339,7 @@ void getExtRefSourceInfo_shouldReturnEmptyList_whenExtRefMatchNoFCDA() { extRefInfo.setHolderLnClass(lnClass); //When + List controlBlocks = SclService.getExtRefSourceInfo(scd, extRefInfo); //Then @@ -357,6 +412,7 @@ void updateExtRefSource_shouldThrowScdException_whenBindingInfoNullOrInvalid() { assertThat(extRefInfo.getBindingInfo()).isNotNull(); assertThatThrownBy(() -> SclService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// binding invalid } + @Test void updateExtRefSource_shouldThrowScdException_whenBindingInternalByIedName() { //Given @@ -434,6 +490,7 @@ void updateExtRefSource_shouldThrowScdException_whenSourceInfoNullOrInvalid() { assertThat(extRefInfo.getSourceInfo()).isNotNull(); assertThatThrownBy(() -> SclService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// signal invalid } + @Test void updateExtRefSource_shouldThrowScdException_whenBindingExternalBinding() { //Given @@ -751,7 +808,7 @@ void testImportSTDElementsInSCD() { SCL std = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); - SclRootAdapter expectedScdAdapter = assertDoesNotThrow( () -> SclService.importSTDElementsInSCD( + SclRootAdapter expectedScdAdapter = assertDoesNotThrow(() -> SclService.importSTDElementsInSCD( scdRootAdapter, Set.of(std), DTO.comMap)); assertThat(expectedScdAdapter.getCurrentElem().getIED()).hasSize(1); assertThat(expectedScdAdapter.getCurrentElem().getDataTypeTemplates()).hasNoNullFieldsOrProperties(); @@ -767,13 +824,13 @@ void testImportSTDElementsInSCD_with_Multiple_STD() { SCL std2 = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std_SITESITE1SCU2.xml"); SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); - SclRootAdapter expectedScdAdapter = assertDoesNotThrow( () -> SclService.importSTDElementsInSCD( + SclRootAdapter expectedScdAdapter = assertDoesNotThrow(() -> SclService.importSTDElementsInSCD( scdRootAdapter, Set.of(std0, std1, std2), DTO.comMap)); assertThat(expectedScdAdapter.getCurrentElem().getIED()).hasSize(3); assertThat(expectedScdAdapter.getCurrentElem().getDataTypeTemplates()).hasNoNullFieldsOrProperties(); assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork()).hasSize(2); - assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork().get(0).getConnectedAP()).hasSizeBetween(1,3); - assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork().get(1).getConnectedAP()).hasSizeBetween(1,3); + assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork().get(0).getConnectedAP()).hasSizeBetween(1, 3); + assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork().get(1).getConnectedAP()).hasSizeBetween(1, 3); assertIsMarshallable(scd); } @@ -798,7 +855,7 @@ void testImportSTDElementsInSCD_Compas_ICDHeader_Not_Match() { SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); //When Then Set stds = Set.of(std); - assertThrows(ScdException.class, ()-> SclService.importSTDElementsInSCD(scdRootAdapter, stds, DTO.comMap)); + assertThrows(ScdException.class, () -> SclService.importSTDElementsInSCD(scdRootAdapter, stds, DTO.comMap)); assertIsMarshallable(scd); } @@ -807,9 +864,9 @@ void testImportSTDElementsInSCD_No_STD_Match() { //Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/ssd.xml"); SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); - //When Then + //When Then Set stds = new HashSet<>(); - assertThrows(ScdException.class, ()-> SclService.importSTDElementsInSCD(scdRootAdapter, stds, DTO.comMap)); + assertThrows(ScdException.class, () -> SclService.importSTDElementsInSCD(scdRootAdapter, stds, DTO.comMap)); assertIsMarshallable(scd); } @@ -1024,7 +1081,7 @@ void updateLDeviceStatus_shouldReturnUpdatedFile() { assertEquals("off", getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName3", "LDSUIED").get().getValue()); } - private Optional getLDeviceStatusValue(SCL scl, String iedName, String ldInst){ + private Optional getLDeviceStatusValue(SCL scl, String iedName, String ldInst) { SclRootAdapter sclRootAdapter = new SclRootAdapter(scl); IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(iedName); Optional lDeviceAdapter = iedAdapter.findLDeviceAdapterByLdInst(ldInst); @@ -1032,7 +1089,7 @@ private Optional getLDeviceStatusValue(SCL scl, String iedName, String ldI Optional doiAdapter = ln0Adapter.getDOIAdapters().stream() .filter(doiAdapter1 -> doiAdapter1.getCurrentElem().getName().equals("Mod")) .findFirst(); - if(doiAdapter.isEmpty()) return Optional.empty(); + if (doiAdapter.isEmpty()) return Optional.empty(); return doiAdapter.get().getCurrentElem().getSDIOrDAI().stream() .filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class)) .map(TDAI.class::cast) @@ -1041,4 +1098,54 @@ private Optional getLDeviceStatusValue(SCL scl, String iedName, String ldI .findFirst(); } + + @Test + void analyzeDataGroups_should_success() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter1 = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(9L); + iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxGOOSE(3L); + iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxSMV(2L); + iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxReports(1L); + // When + SclReport sclReport = SclService.analyzeDataGroups(scd); + //Then + assertThat(sclReport.getSclReportItems()).isEmpty(); + + } + + @Test + void analyzeDataGroups_should_return_errors_messages() throws Exception { + + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME2"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMaxAttributes(1L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMax(3L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getSMVsc().setMax(1L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getGOOSE().setMax(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfReportControl().setMax(0L); + // When + SclReport sclReport = SclService.analyzeDataGroups(scd); + //Then + 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"); + } + + } 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 new file mode 100644 index 000000000..103ac5db4 --- /dev/null +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java @@ -0,0 +1,281 @@ +/* + * // SPDX-FileCopyrightText: 2023 RTE FRANCE + * // + * // SPDX-License-Identifier: Apache-2.0 + */ + +package org.lfenergy.compas.sct.commons.scl.ied; + +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.*; +import org.lfenergy.compas.sct.commons.dto.DTO; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; +import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; +import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; +import org.lfenergy.compas.sct.commons.util.ServicesConfigEnum; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class AccessPointAdapterTest { + + @Test + void amChildElementRef() { + //Given + SclRootAdapter sclRootAdapter = new SclRootAdapter("hID","hVersion","hRevision"); + TIED tied = new TIED(); + tied.setName(DTO.HOLDER_IED_NAME); + TAccessPoint tAccessPoint = new TAccessPoint(); + tAccessPoint.setName("AP_NAME"); + tAccessPoint.setServices(new TServices()); + tied.getAccessPoint().add(tAccessPoint); + sclRootAdapter.getCurrentElem().getIED().add(tied); + IEDAdapter iAdapter = sclRootAdapter.getIEDAdapterByName(DTO.HOLDER_IED_NAME); + //When + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iAdapter, iAdapter.getCurrentElem().getAccessPoint().get(0)); + //Then + assertTrue(accessPointAdapter.amChildElementRef()); + assertNotNull(accessPointAdapter.getCurrentElem().getServices()); + } + + @ParameterizedTest + @CsvSource(value = {"AP_NAME;AccessPoint[@name=\"AP_NAME\"]", ";AccessPoint[not(@name)]"} + , delimiter = ';') + void elementXPath(String apName, String message) { + // Given + TAccessPoint tAccessPoint = new TAccessPoint(); + tAccessPoint.setName(apName); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(null, tAccessPoint); + // When + String elementXPathResult = accessPointAdapter.elementXPath(); + // Then + assertThat(elementXPathResult).isEqualTo(message); + } + + @Test + void getXPath() { + // Given + TIED tied = new TIED(); + tied.setName("IED_NAME"); + TAccessPoint tAccessPoint = new TAccessPoint(); + tAccessPoint.setName("AP_NAME"); + tied.getAccessPoint().add(tAccessPoint); + IEDAdapter iedAdapter = new IEDAdapter(null, tied); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, tAccessPoint); + // When + String elementXPathResult = accessPointAdapter.getXPath(); + // Then + assertThat(elementXPathResult).isEqualTo("/IED[@name=\"IED_NAME\"]/AccessPoint[@name=\"AP_NAME\"]"); + } + + @Test + void checkFCDALimitations_should_succed_no_error_message() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + //When + List sclReportItems = accessPointAdapter.checkFCDALimitations(); + //Then + assertThat(sclReportItems).isEmpty(); + } + + @Test + void checkFCDALimitations_should_fail_with_one_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMaxAttributes(2L); + //When + List sclReportItems = accessPointAdapter.checkFCDALimitations(); + //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"); + } + @Test + void checkFCDALimitations_should_fail_with_four_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMaxAttributes(1L); + //When + List sclReportItems = accessPointAdapter.checkFCDALimitations(); + //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"); + } + + + @Test + void checkControlsLimitation_should_fail_for_dataset_with_one_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMax(5L); + String message = "Too much DataSet for"; + //When + Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.DATASET,message); + //Then + assertThat(sclReportItem).isPresent() + .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME"); + } + + @Test + void checkControlsLimitation_should_fail_for_smv_with_one_error_messages() throws Exception { + //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); + //Then + assertThat(sclReportItem).isPresent() + .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME"); + } + + @Test + void checkControlsLimitation_should_fail_for_goose_with_one_error_messages() throws Exception { + //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); + //Then + assertThat(sclReportItem).isPresent() + .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME"); + } + + @Test + void checkControlsLimitation_should_fail_for_report_with_one_error_messages() throws Exception { + //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); + //Then + assertThat(sclReportItem).isPresent() + .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME"); + } + + public static AccessPointAdapter provideAPForCheckLimitationForIED() throws Exception { + 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"); + return new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + } + + @Test + void checkLimitationForBindedIEDFCDAs_should_success_no_error() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_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"; + Set tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefSet(); + //When + Optional sclReportItem = accessPointAdapter.checkLimitationForBindedIEDFCDAs(tExtRefs,message); + + //Then + assertThat(sclReportItem).isEmpty(); + } + + @Test + void checkLimitationForBindedIEDFCDAs_should_fail_one_error_message() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_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); + Set tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefSet(); + String message = "Too much FCDA for IED_NAME1"; + + //When + Optional sclReportItem = accessPointAdapter.checkLimitationForBindedIEDFCDAs(tExtRefs,message); + + //Then + assertThat(sclReportItem).isPresent() + .get() + .extracting(SclReportItem::getMessage) + .isEqualTo(message); + } + + @Test + void checkLimitationForBindedIEDControls_should_fail_three_error_messages() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_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)); + Set tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefSet(); + //When + List sclReportItems = accessPointAdapter.checkLimitationForBindedIEDControls(tExtRefs); + + //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."); + } + + @Test + void checkLimitationForBindedIEDControls_should_succed_no_error_message() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(11L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxGOOSE(5L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxReports(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxSMV(2L); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + Set tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefSet(); + //When + List sclReportItems = accessPointAdapter.checkLimitationForBindedIEDControls(tExtRefs); + + //Then + assertThat(sclReportItems).isEmpty(); + } + + @Test + void getAllCoherentExtRefForAnalyze_succed() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_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)); + //When + AccessPointAdapter.ExtRefAnalyzeRecord extRefAnalyzeRecord = accessPointAdapter.getAllCoherentExtRefForAnalyze(); + //Then + assertThat(extRefAnalyzeRecord) + .extracting(AccessPointAdapter.ExtRefAnalyzeRecord::sclReportItems) + .asList().isEmpty(); + } + + @Test + void getAllCoherentExtRefForAnalyze_fail_with_one_error() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + //When + AccessPointAdapter.ExtRefAnalyzeRecord extRefAnalyzeRecord = accessPointAdapter.getAllCoherentExtRefForAnalyze(); + //Then + assertThat(extRefAnalyzeRecord) + .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/IEDAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java index 635d37bf1..88f867311 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 @@ -5,12 +5,10 @@ package org.lfenergy.compas.sct.commons.scl.ied; import org.junit.jupiter.api.Test; -import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.scl2007b4.model.TIED; -import org.lfenergy.compas.scl2007b4.model.TPrivate; -import org.lfenergy.compas.scl2007b4.model.TServices; +import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.DTO; import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo; +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.SclRootAdapter; @@ -26,6 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; class IEDAdapterTest { @@ -174,8 +173,8 @@ void testMatches() { @Test void addPrivate() { - SclRootAdapter sclRootAdapter = Mockito.mock(SclRootAdapter.class); - SCL scl = Mockito.mock(SCL.class); + SclRootAdapter sclRootAdapter = mock(SclRootAdapter.class); + SCL scl = mock(SCL.class); Mockito.when(sclRootAdapter.getCurrentElem()).thenReturn(scl); TIED tied = new TIED(); Mockito.when(scl.getIED()).thenReturn(List.of(tied)); @@ -191,8 +190,8 @@ void addPrivate() { @Test void elementXPath() { // Given - SclRootAdapter sclRootAdapter = Mockito.mock(SclRootAdapter.class); - SCL scl = Mockito.mock(SCL.class); + SclRootAdapter sclRootAdapter = mock(SclRootAdapter.class); + SCL scl = mock(SCL.class); Mockito.when(sclRootAdapter.getCurrentElem()).thenReturn(scl); TIED tied = new TIED(); tied.setName("iedName"); @@ -204,4 +203,77 @@ void elementXPath() { assertThat(result).isEqualTo("IED[@name=\"iedName\"]"); } + @Test + void checkDataGroupCoherence_should_succed_no_error_message() throws Exception { + //Given + IEDAdapter iedAdapter = provideIEDForCheckLimitationForIED(); + //When + List sclReportItems = iedAdapter.checkDataGroupCoherence(); + //Then + assertThat(sclReportItems).isEmpty(); + } + + @Test + void checkDataGroupCoherence_should_fail_five_error_message() throws Exception { + //Given + IEDAdapter iedAdapter = provideIEDForCheckLimitationForIED(); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMaxAttributes(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMax(5L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getSMVsc().setMax(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getGOOSE().setMax(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfReportControl().setMax(0L); + //When + List sclReportItems = iedAdapter.checkDataGroupCoherence(); + //Then + assertThat(sclReportItems).hasSize(5) + .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"); + } + + @Test + void checkBindingDataGroupCoherence_should_succed_no_error_message() throws Exception { + //Given + IEDAdapter iedAdapter = provideIEDForCheckLimitationForBindedIED(); + TClientServices tClientServices = iedAdapter.getParentAdapter().getIEDAdapterByName("IED_NAME1").getCurrentElem().getAccessPoint().get(0).getServices().getClientServices(); + tClientServices.setMaxAttributes(11L); + tClientServices.setMaxGOOSE(5L); + tClientServices.setMaxReports(2L); + tClientServices.setMaxSMV(2L); + //When + List sclReportItems = iedAdapter.checkBindingDataGroupCoherence(); + //Then + assertThat(sclReportItems).isEmpty(); + } + + @Test + void checkBindingDataGroupCoherence_should_fail_five_error_message() throws Exception { + //Given + IEDAdapter iedAdapter = provideIEDForCheckLimitationForBindedIED(); + //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."); + + } + + public static IEDAdapter provideIEDForCheckLimitationForBindedIED() throws Exception { + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + return sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + } + + public static IEDAdapter provideIEDForCheckLimitationForIED() throws Exception { + 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"); + } } 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 64be79b07..a23706f4d 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 @@ -5,6 +5,7 @@ package org.lfenergy.compas.sct.commons.scl.ied; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -14,6 +15,7 @@ import org.lfenergy.compas.sct.commons.testhelpers.FCDARecord; import org.lfenergy.compas.sct.commons.testhelpers.MarshallerWrapper; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; +import org.mockito.junit.jupiter.MockitoExtension; import org.opentest4j.AssertionFailedError; import java.util.List; @@ -25,6 +27,7 @@ import static org.junit.jupiter.api.Named.named; import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*; +@ExtendWith(MockitoExtension.class) class InputsAdapterTest { @Test @@ -255,5 +258,30 @@ 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/resources/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml new file mode 100644 index 000000000..e09b36fc2 --- /dev/null +++ b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml @@ -0,0 +1,44 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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_binded_ied_controls_fcda.xml new file mode 100644 index 000000000..57382209d --- /dev/null +++ b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml @@ -0,0 +1,148 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml new file mode 100644 index 000000000..6c8c046b7 --- /dev/null +++ b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml @@ -0,0 +1,89 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 new file mode 100644 index 000000000..4cd987c58 --- /dev/null +++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml @@ -0,0 +1,130 @@ + + + + + + +
+ + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + + + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + + From d2e2ebd58760b9e4c15f43095ecfd6cb756c2ae1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Jan 2023 07:12:24 +0000 Subject: [PATCH 2/9] build(deps): bump alex-page/github-project-automation-plus Bumps [alex-page/github-project-automation-plus](https://github.com/alex-page/github-project-automation-plus) from 0.8.2 to 0.8.3. - [Release notes](https://github.com/alex-page/github-project-automation-plus/releases) - [Commits](https://github.com/alex-page/github-project-automation-plus/compare/v0.8.2...v0.8.3) --- updated-dependencies: - dependency-name: alex-page/github-project-automation-plus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/automate_projects.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/automate_projects.yml b/.github/workflows/automate_projects.yml index 40f2e7621..c7e2661d4 100644 --- a/.github/workflows/automate_projects.yml +++ b/.github/workflows/automate_projects.yml @@ -14,14 +14,14 @@ jobs: steps: - name: add-new-issues-to-repository-based-project-column if: github.event_name == 'issues' && github.event.action == 'opened' - uses: alex-page/github-project-automation-plus@v0.8.2 + uses: alex-page/github-project-automation-plus@v0.8.3 with: project: CoMPAS SCT Board column: To do repo-token: ${{ secrets.ORG_GITHUB_ACTION_SECRET }} - name: add-new-pull-request-to-repository-based-project-column if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.action == 'opened' - uses: alex-page/github-project-automation-plus@v0.8.2 + uses: alex-page/github-project-automation-plus@v0.8.3 with: project: CoMPAS SCT Board column: To do @@ -29,14 +29,14 @@ jobs: - name: add-new-issues-to-organization-based-project-column if: github.event_name == 'issues' && github.event.action == 'opened' - uses: alex-page/github-project-automation-plus@v0.8.2 + uses: alex-page/github-project-automation-plus@v0.8.3 with: project: CoMPAS Issues Overview Board column: To do repo-token: ${{ secrets.ORG_GITHUB_ACTION_SECRET }} - name: add-new-pull-request-to-organization-based-project-column if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.action == 'opened' - uses: alex-page/github-project-automation-plus@v0.8.2 + uses: alex-page/github-project-automation-plus@v0.8.3 with: project: CoMPAS Pull Request Overview Board column: To do From dee9358a92608db8210200e1d53a510fab5eb156 Mon Sep 17 00:00:00 2001 From: Aliou DIAITE Date: Fri, 27 Jan 2023 16:24:42 +0100 Subject: [PATCH 3/9] feat(216): check that Datasets, FCDAs, ControlBlocks (GOOSE, REPORT and SMV) satisfy limits fixed by config Signed-off-by: Aliou DIAITE --- .../commons/scl/ied/AbstractLNAdapter.java | 18 +- .../commons/scl/ied/AccessPointAdapter.java | 244 +++++++++--------- .../sct/commons/scl/ied/IEDAdapter.java | 6 +- .../sct/commons/scl/SclServiceTest.java | 61 +---- .../scl/ied/AccessPointAdapterTest.java | 23 +- .../sct/commons/scl/ied/LN0AdapterTest.java | 109 +++++++- 6 files changed, 253 insertions(+), 208 deletions(-) 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 c7718c026..c55f92529 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 @@ -159,9 +159,7 @@ public String getLnType() { * @return list of LNode ExtRefs elements */ public List getExtRefs() { - List tExtRefs = getExtRefs(null); - if(tExtRefs == null || tExtRefs.isEmpty()) return Collections.emptyList(); - else return tExtRefs; + return getExtRefs(null); } /** @@ -974,12 +972,18 @@ public ControlBlockAdapter createControlBlockIfNotExists(String cbName, String i ); } + /** + * Finds all FCDAs in DataSet of Control Block feeding ExtRef + * @param tExtRef Fed ExtRef + * @return list of all FCDA in DataSet of Control Block + */ public List getFCDAs(TExtRef tExtRef){ - TControl tControl1 = getTControlsByType(AccessPointAdapter.getControlTypeClass(tExtRef.getServiceType())).stream() - .filter(tControl -> tExtRef.getSrcCBName() != null && tExtRef.getSrcCBName().equals(tControl.getName())) - .findFirst().orElseThrow(() -> new ScdException(String.format("DataSet linked with Control Block %s not found", tExtRef.getSrcCBName()))); + TControl tControl = getTControlsByType(ControlBlockEnum.from(tExtRef.getServiceType()).getControlBlockClass()).stream() + .filter(tCtrl -> tExtRef.getSrcCBName() != null && tExtRef.getSrcCBName().equals(tCtrl.getName())) + .findFirst().orElseThrow(() -> + new ScdException(String.format("Control Block %s not found in %s", tExtRef.getSrcCBName(), getXPath()))); return getCurrentElem().getDataSet().stream() - .filter(tDataSet -> tDataSet.getName().equals(tControl1.getDatSet())) + .filter(tDataSet -> tDataSet.getName().equals(tControl.getDatSet())) .map(TDataSet::getFCDA) .flatMap(Collection::stream) .toList(); 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 89de047be..4e12b6658 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 @@ -30,7 +30,6 @@ *
  • {@link AccessPointAdapter#checkControlsLimitation Returns the value of the Service object}
  • * * - * */ public class AccessPointAdapter extends SclElementAdapter { @@ -42,7 +41,7 @@ public class AccessPointAdapter extends SclElementAdapter tAccessPoint.getName().equals(currentElem.getName())); + return parentAdapter.getCurrentElem().getAccessPoint().stream() + .anyMatch(tAccessPoint -> tAccessPoint.getName().equals(currentElem.getName())); } @Override @@ -64,59 +63,53 @@ protected String elementXPath() { return String.format("AccessPoint[%s]", Utils.xpathAttributeFilter("name", currentElem.isSetName() ? currentElem.getName() : null)); } - /** - * Gets XPath path to current element from parent element - * @return path to current element - */ - @Override - public String getXPath(){ - String parentXpath = (parentAdapter != null) ? parentAdapter.getXPath() : ""; - return parentXpath + "/" + elementXPath(); - } - /** * Gets all LDevice from AccessPoint + * * @return Stream of LDeviceAdapter object as IEDs of SCL */ private Stream streamLDeviceAdapters() { - if(!currentElem.isSetServer()) return Stream.empty(); + if (!currentElem.isSetServer()) return Stream.empty(); return currentElem.getServer().getLDevice().stream() .map(tlDevice -> new LDeviceAdapter(getParentAdapter(), tlDevice)); } /** * Checks FCDA number limitation into each LDevice of AccessPoint + * * @return List of errors encountered for LDevices */ - public List checkFCDALimitations(){ - long max = getMaxInstanceAutorized(ServicesConfigEnum.FCDA); - if(currentElem.getServer() == null) return new ArrayList<>(); + public List checkFCDALimitations() { + long max = getMaxInstanceAuthorized(ServicesConfigEnum.FCDA); + if (currentElem.getServer() == null || max == MAX_OCCURRENCE_NO_LIMIT_VALUE) return Collections.emptyList(); return currentElem.getServer().getLDevice().stream() .map(tlDevice -> new LDeviceAdapter(parentAdapter, tlDevice)) .map(lDeviceAdapter -> - lDeviceAdapter.getLNAdaptersInclundigLN0().stream() + lDeviceAdapter.getLNAdaptersIncludingLN0().stream() .map(abstractLNAdapter -> abstractLNAdapter.getCurrentElem().getDataSet()) .flatMap(Collection::stream) - .filter(tDataSet -> max == MAX_OCCURRENCE_NO_LIMIT_VALUE || tDataSet.getFCDA().size() > max) - .map(tDataSet -> SclReportItem.warning(getXPath(), String.format("There are too much FCDA for the DataSet %S for the LDevice %S in IED %S", + .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() ).flatMap(Collection::stream).toList(); } /** * 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 + * @param msg error message to display * @return Optional of encountered error or empty */ - public Optional checkControlsLimitation(ServicesConfigEnum servicesConfigEnum, String msg){ - long max = getMaxInstanceAutorized(servicesConfigEnum); + public Optional checkControlsLimitation(ServicesConfigEnum servicesConfigEnum, String msg) { + long max = getMaxInstanceAuthorized(servicesConfigEnum); long value = getNumberOfItems(servicesConfigEnum); - return max == MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.warning(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("%s %s", msg, parentAdapter.getName()))); } /** - * Counts all occurence of Control into AccessPoint + * Counts all occurrence of Control into AccessPoint + * * @param servicesConfigEnum type (GOOSE, Report, SampledValue, DataSet) * @return number of occurrence */ @@ -124,13 +117,22 @@ private long getNumberOfItems(ServicesConfigEnum servicesConfigEnum) { if (!currentElem.isSetServer()) return 0L; return currentElem.getServer().getLDevice().stream() .map(tlDevice -> new LDeviceAdapter(parentAdapter, tlDevice)) - .map(LDeviceAdapter::getLNAdaptersInclundigLN0) + .map(lDeviceAdapter -> { + List> list = new ArrayList<>(); + if (servicesConfigEnum == ServicesConfigEnum.GSE || servicesConfigEnum == ServicesConfigEnum.SMV) + list.add(lDeviceAdapter.getLN0Adapter()); + else list.addAll(lDeviceAdapter.getLNAdaptersIncludingLN0()); + return list; + }) .flatMap(Collection::stream) .map(abstractLNAdapter -> { - if(servicesConfigEnum == ServicesConfigEnum.DATASET) return abstractLNAdapter.getCurrentElem().getDataSet(); - else return abstractLNAdapter.getTControlsByType(getControlTypeClass(servicesConfigEnum));}) - .mapToLong(Collection::size) - .sum(); + if (servicesConfigEnum == ServicesConfigEnum.DATASET) { + return abstractLNAdapter.getCurrentElem().getDataSet(); + } else { + return abstractLNAdapter.getTControlsByType(getControlTypeClass(servicesConfigEnum)); + } + }) + .mapToLong(Collection::size).sum(); } private Class getControlTypeClass(ServicesConfigEnum servicesConfigEnum) { @@ -138,191 +140,201 @@ private Class getControlTypeClass(ServicesConfigEnum service case REPORT -> TReportControl.class; case GSE -> TGSEControl.class; case SMV -> TSampledValueControl.class; - default -> null; + default -> throw new ScdException("Unknown Control Block Type: " + servicesConfigEnum); }; } /** - * Gets max number authorized in configuration of each elements 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 getMaxInstanceAutorized(ServicesConfigEnum servicesConfigEnum){ - if(currentElem.getServices() == null) return MAX_OCCURRENCE_NO_LIMIT_VALUE; + private long getMaxInstanceAuthorized(ServicesConfigEnum servicesConfigEnum) { + if (currentElem.getServices() == null || currentElem.getServices().getConfDataSet() == null) + return MAX_OCCURRENCE_NO_LIMIT_VALUE; TServices tServices = currentElem.getServices(); return switch (servicesConfigEnum) { - case DATASET -> (tServices.getConfDataSet() != null && tServices.getConfDataSet().isSetMax()) ? tServices.getConfDataSet().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; - case FCDA -> (tServices.getConfDataSet() != null && tServices.getConfDataSet().isSetMaxAttributes()) ? tServices.getConfDataSet().getMaxAttributes() : MAX_OCCURRENCE_NO_LIMIT_VALUE; - case REPORT -> (tServices.getConfReportControl() != null && tServices.getConfReportControl().isSetMax()) ? tServices.getConfReportControl().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; - case GSE -> (tServices.getGOOSE() != null && tServices.getGOOSE().isSetMax()) ? tServices.getGOOSE().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; - case SMV -> (tServices.getSMVsc() != null && tServices.getSMVsc().isSetMax()) ? tServices.getSMVsc().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case DATASET -> + tServices.getConfDataSet().isSetMax() ? tServices.getConfDataSet().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case FCDA -> + 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; }; } /** * Checks FCDA number limitation for binded IED - * @param msg message to display hen error occured + * + * @param msg message to display hen error occurred * @return Optional of encountered error or empty */ - public Optional checkLimitationForBindedIEDFCDAs(Set tExtRefs, String msg){ - long value = tExtRefs.stream() - .map( tExtRef -> { - IEDAdapter iedAdapter = getParentAdapter().getParentAdapter().getIEDAdapterByName(tExtRef.getIedName()); - LDeviceAdapter lDeviceAdapter; - if (tExtRef.getSrcLDInst() != null) - lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(tExtRef.getSrcLDInst()); - else lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(tExtRef.getLdInst()); - - AbstractLNAdapter abstractLNAdapter; - if (!tExtRef.isSetSrcLNClass() || tExtRef.getSrcLNClass().contains(TLLN0Enum.LLN_0.value())) - abstractLNAdapter = lDeviceAdapter.getLN0Adapter(); - else - abstractLNAdapter = lDeviceAdapter.getLNAdapter(tExtRef.getSrcLNClass().get(0), tExtRef.getSrcLNInst(), tExtRef.getSrcPrefix()); - - return abstractLNAdapter.getFCDAs(tExtRef); - }).flatMap(Collection::stream) + public Optional checkLimitationForBoundIEDFCDAs(List tExtRefs, String msg) { + long value = tExtRefs.stream() + .map(tExtRef -> { + IEDAdapter iedAdapter = getParentAdapter().getParentAdapter().getIEDAdapterByName(tExtRef.getIedName()); + LDeviceAdapter lDeviceAdapter; + if (tExtRef.getSrcLDInst() != null) { + lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(tExtRef.getSrcLDInst()); + } else { + lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(tExtRef.getLdInst()); + } + AbstractLNAdapter abstractLNAdapter; + if (!tExtRef.isSetSrcLNClass() || tExtRef.getSrcLNClass().contains(TLLN0Enum.LLN_0.value())) { + abstractLNAdapter = lDeviceAdapter.getLN0Adapter(); + } else { + abstractLNAdapter = lDeviceAdapter.getLNAdapter(tExtRef.getSrcLNClass().get(0), tExtRef.getSrcLNInst(), tExtRef.getSrcPrefix()); + } + return abstractLNAdapter.getFCDAs(tExtRef); + }) + .flatMap(Collection::stream) .toList() .size(); long max; - if(currentElem.getServices() == null) { + 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.warning(getParentAdapter().getXPath(), msg)); + return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : + Optional.of(SclReportItem.fatal(getParentAdapter().getXPath(), msg)); } /** - * * @param sclReportItems - * @param tExtRefSet + * @param tExtRefs */ - record ExtRefAnalyzeRecord(List sclReportItems, Set tExtRefSet){ + record ExtRefAnalyzeRecord(List sclReportItems, List tExtRefs) { } /** * Returns all ExtRef of the AccessPoint which have SrcCBName set and ServiceType provided + * * @return ExtRefAnalyzeRecord object containing Set of ExtRefs and errors list for ExtRefs with SrcCBName and without ServiceType provided */ public ExtRefAnalyzeRecord getAllCoherentExtRefForAnalyze() { List sclReportItems = new ArrayList<>(); - Set tExtRefSet= new HashSet<>(); + List tExtRefList = new ArrayList<>(); streamLDeviceAdapters().map(lDeviceAdapter -> { - Set tExtRefs = lDeviceAdapter.getLN0Adapter().getExtRefs().stream().filter(TExtRef::isSetSrcCBName).collect(Collectors.toSet()); - sclReportItems.addAll(checkExtRefWithoutServiceType(tExtRefs)); - tExtRefs.removeIf(tExtRef -> !tExtRef.isSetServiceType()); - return tExtRefs; + List extRefs = lDeviceAdapter.getLN0Adapter().getExtRefs().stream().filter(TExtRef::isSetSrcCBName).collect(Collectors.toCollection(ArrayList::new)); + sclReportItems.addAll(checkExtRefWithoutServiceType(extRefs, lDeviceAdapter.getXPath())); + extRefs.removeIf(tExtRef -> !tExtRef.isSetServiceType()); + return extRefs; }).flatMap(Collection::stream).forEach(tExtRef -> { - if(tExtRefSet.isEmpty()) - tExtRefSet.add(tExtRef); - if(tExtRefSet.stream().noneMatch(t -> extrefEquals(tExtRef,t))) - tExtRefSet.add(tExtRef); + if (tExtRefList.isEmpty()) + tExtRefList.add(tExtRef); + else { + if (tExtRefList.stream().noneMatch(t -> isExtRefFeedBySameControlBlock(tExtRef, t))) + tExtRefList.add(tExtRef); + } }); - return new ExtRefAnalyzeRecord(sclReportItems, tExtRefSet); + return new ExtRefAnalyzeRecord(sclReportItems, tExtRefList); } /** * Checks all ExtRefs with SrcCBName and without ServiceType provided + * * @param tExtRefs Set of ExtRefs to check * @return errors list */ - private List checkExtRefWithoutServiceType(Set tExtRefs) { + private List checkExtRefWithoutServiceType(List tExtRefs, String xPath) { return tExtRefs.stream() .filter(tExtRef -> !tExtRef.isSetServiceType()) .map(tExtRef -> - SclReportItem.warning(getXPath(), "ExtRef signal without ServiceType : " + tExtRef.getDesc())) + SclReportItem.fatal(xPath, "ExtRef signal without ServiceType : " + tExtRef.getDesc())) .toList(); } /** * Checks if two ExtRefs fed by same Control Block for SCL limits analyze * Nota : this equality is only for checking limitation check + * * @param t1 extref to compare * @param t2 extref to compare * @return true if the two ExtRef are fed by same Control Block, otherwise false */ - private static boolean extrefEquals(TExtRef t1, TExtRef t2) { - if(!t1.isSetSrcLNClass()) t1.getSrcLNClass().add(TLLN0Enum.LLN_0.value()); - if(!t2.isSetSrcLNClass()) t2.getSrcLNClass().add(TLLN0Enum.LLN_0.value()); - return Utils.equalsOrBothBlank(t1.getIedName(), t2.getIedName()) + private static boolean isExtRefFeedBySameControlBlock(TExtRef t1, TExtRef t2) { + String srcLNClass1 = (t1.isSetSrcLNClass()) ? t1.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value(); + String srcLNClass2 = (t2.isSetSrcLNClass()) ? t2.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value(); + return Utils.equalsOrBothBlank(t1.getIedName(), t2.getIedName()) && Utils.equalsOrBothBlank(t1.getSrcLDInst(), t2.getSrcLDInst()) - && (t1.getSrcLNClass().equals(t2.getSrcLNClass())) + && srcLNClass1.equals(srcLNClass2) && Utils.equalsOrBothBlank(t1.getSrcLNInst(), t2.getSrcLNInst()) - && Utils.equalsOrBothBlank(t1.getSrcPrefix(),t2.getSrcPrefix()) + && Utils.equalsOrBothBlank(t1.getSrcPrefix(), t2.getSrcPrefix()) && t1.getServiceType().equals(t2.getServiceType()); } /** * Checks Control Blocks (Report, Goose, SMV) number limitation for binded IED + * * @return List of errors encountered */ - public List checkLimitationForBindedIEDControls(Set tExtRefs){ + public List checkLimitationForBoundIEDControls(List tExtRefs) { Map> extRefsByServiceType = tExtRefs.stream() .filter(TExtRef::isSetServiceType) .collect(Collectors.groupingBy(TExtRef::getServiceType, Collectors.toSet())); return extRefsByServiceType.keySet().stream() - .map(tServiceType-> { + .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); + 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); }; - }).toList().stream() - .filter(Optional::isPresent) - .map(Optional::get) + }).flatMap(Optional::stream) .toList(); } /** * Checks Control Block number limitation for binded IED - * @param tExtRefs list of ExtRefs referenced same ied - * @param msg message to display hen error occured + * + * @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) { - long max = getMaxInstanceAutorizedForBindedIED(servicesConfigEnum); + long max = getMaxInstanceAuthorizedForBoundIED(servicesConfigEnum); long value = tExtRefs.size(); - return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.warning(getParentAdapter().getXPath(), msg)); - } - - public static Class getControlTypeClass(TServiceType tServiceType) { - if(tServiceType == null) throw new IllegalArgumentException("TServiceType of ExtRef should not be null"); - return switch (tServiceType) { - case REPORT -> TReportControl.class; - case GOOSE -> TGSEControl.class; - case SMV -> TSampledValueControl.class; - case POLL -> throw new ScdException("Unsupported value: " + tServiceType); - }; + return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.fatal(getParentAdapter().getXPath(), msg)); } /** - * 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 getMaxInstanceAutorizedForBindedIED(ServicesConfigEnum servicesConfigEnum) { - if (currentElem.getServices() == null) { + private long getMaxInstanceAuthorizedForBoundIED(ServicesConfigEnum servicesConfigEnum) { + if (currentElem.getServices() == null || currentElem.getServices().getClientServices() == null) { return AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; - } else { - TClientServices tClientServices = currentElem.getServices().getClientServices(); - return switch (servicesConfigEnum) { - case DATASET, FCDA -> tClientServices != null && tClientServices.isSetMaxAttributes() ? tClientServices.getMaxAttributes() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; - case REPORT -> tClientServices != null && tClientServices.isSetMaxReports() ? tClientServices.getMaxReports() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; - case GSE -> tClientServices != null && tClientServices.isSetMaxGOOSE() ? tClientServices.getMaxGOOSE() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; - case SMV -> tClientServices != null && tClientServices.isSetMaxSMV() ? tClientServices.getMaxSMV() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; - }; } - } + TClientServices tClientServices = currentElem.getServices().getClientServices(); + return switch (servicesConfigEnum) { + case FCDA -> + tClientServices.isSetMaxAttributes() ? tClientServices.getMaxAttributes() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case REPORT -> + tClientServices.isSetMaxReports() ? tClientServices.getMaxReports() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case GSE -> + tClientServices.isSetMaxGOOSE() ? tClientServices.getMaxGOOSE() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case SMV -> + tClientServices.isSetMaxSMV() ? tClientServices.getMaxSMV() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + default -> throw new ScdException("Unsupported value: " + servicesConfigEnum); + }; + } } 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 6eac2c2ef..df3934aac 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 @@ -352,9 +352,9 @@ public List checkBindingDataGroupCoherence() { AccessPointAdapter accessPointAdapter = new AccessPointAdapter(this, tAccessPoint); AccessPointAdapter.ExtRefAnalyzeRecord extRefAnalyzeRecord = accessPointAdapter.getAllCoherentExtRefForAnalyze(); List sclReportItems = new ArrayList<>(extRefAnalyzeRecord.sclReportItems()); - accessPointAdapter.checkLimitationForBindedIEDFCDAs(extRefAnalyzeRecord.tExtRefSet(), "There are too much FCDA for the Client IED " + getName()) - .ifPresent(sclReportItems::add); - sclReportItems.addAll(accessPointAdapter.checkLimitationForBindedIEDControls(extRefAnalyzeRecord.tExtRefSet())); + 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(); 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 114e477c0..082f820b7 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 @@ -31,61 +31,6 @@ class SclServiceTest { - /* private static Stream sclProviderMissingRequiredObjects() throws Exception { - SCL scl1 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_KO_MissingBeh.scd"); - SCL scl2 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivate.scd"); - SCL scl3 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_KO_MissingLDevicePrivateAttribute.scd"); - SCL scl4 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_KO_MissingMod.scd"); - Tuple[] scl1Errors = new Tuple[]{Tuple.tuple("The LDevice doesn't have a DO @name='Beh' OR its associated DA@fc='ST' AND DA@name='stVal'", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; - Tuple[] scl2Errors = new Tuple[]{Tuple.tuple("The LDevice doesn't have a Private compas:LDevice.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; - Tuple[] scl3Errors = new Tuple[]{Tuple.tuple("The Private compas:LDevice doesn't have the attribute 'LDeviceStatus'", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; - Tuple[] scl4Errors = new Tuple[]{Tuple.tuple("The LDevice doesn't have a DO @name='Mod'", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; - return Stream.of( - Arguments.of("MissingDOBeh", scl1, scl1Errors), - Arguments.of("MissingLDevicePrivate", scl2, scl2Errors), - Arguments.of("MissingLDevicePrivateAttribute", scl3, scl3Errors), - Arguments.of("MissingDOMod", scl4, scl4Errors) - ); - } - - private static Stream sclProviderBasedLDeviceStatus() throws Exception { - SCL scl1 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_LD_STATUS_ACTIVE.scd"); - SCL scl2 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_LD_STATUS_UNTESTED.scd"); - SCL scl3 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test1_LD_STATUS_INACTIVE.scd"); - Tuple[] scl1Errors = new Tuple[]{Tuple.tuple("The LDevice cannot be set to 'off' but has not been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), - Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", - "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), - Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", - "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" - )}; - Tuple[] scl2Errors = new Tuple[]{Tuple.tuple("The LDevice cannot be set to 'off' but has not been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), - Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", - "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), - Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", - "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" - )}; - Tuple[] scl3Errors = new Tuple[]{Tuple.tuple("The LDevice is not qualified into STD but has been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), - Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", - "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), - Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", - "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" - )}; - return Stream.of( - Arguments.of("ACTIVE", scl1, scl1Errors), - Arguments.of("UNTESTED", scl2, scl2Errors), - Arguments.of("INACTIVE", scl3, scl3Errors) - ); - } - - */ - @Test void testAddHistoryItem() throws ScdException { SclRootAdapter sclRootAdapter = new SclRootAdapter("hId", SclRootAdapter.VERSION, SclRootAdapter.REVISION); @@ -980,21 +925,21 @@ private static Stream sclProviderBasedLDeviceStatus() { SCL scl2 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_LD_STATUS_UNTESTED.scd"); SCL scl3 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test1_LD_STATUS_INACTIVE.scd"); Tuple[] scl1Errors = new Tuple[]{Tuple.tuple("The LDevice cannot be set to 'off' but has not been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" )}; Tuple[] scl2Errors = new Tuple[]{Tuple.tuple("The LDevice cannot be set to 'off' but has not been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" )}; Tuple[] scl3Errors = new Tuple[]{Tuple.tuple("The LDevice is not qualified into STD but has been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", 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 103ac5db4..1ecfc9daf 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 @@ -18,11 +18,8 @@ import java.util.List; import java.util.Optional; -import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; class AccessPointAdapterTest { @@ -41,8 +38,8 @@ void amChildElementRef() { //When AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iAdapter, iAdapter.getCurrentElem().getAccessPoint().get(0)); //Then - assertTrue(accessPointAdapter.amChildElementRef()); - assertNotNull(accessPointAdapter.getCurrentElem().getServices()); + assertThat(accessPointAdapter.amChildElementRef()).isTrue(); + assertThat(accessPointAdapter.getCurrentElem().getServices()).isNotNull(); } @ParameterizedTest @@ -182,9 +179,9 @@ void checkLimitationForBindedIEDFCDAs_should_success_no_error() throws Exception iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(11L); AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); String message = "Too much FCDA"; - Set tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefSet(); + List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs(); //When - Optional sclReportItem = accessPointAdapter.checkLimitationForBindedIEDFCDAs(tExtRefs,message); + Optional sclReportItem = accessPointAdapter.checkLimitationForBoundIEDFCDAs(tExtRefs, message); //Then assertThat(sclReportItem).isEmpty(); @@ -198,11 +195,11 @@ void checkLimitationForBindedIEDFCDAs_should_fail_one_error_message() throws Exc IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); accessPointAdapter.getCurrentElem().getServices().getClientServices().setMaxAttributes(4L); - Set tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefSet(); + List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs(); String message = "Too much FCDA for IED_NAME1"; //When - Optional sclReportItem = accessPointAdapter.checkLimitationForBindedIEDFCDAs(tExtRefs,message); + Optional sclReportItem = accessPointAdapter.checkLimitationForBoundIEDFCDAs(tExtRefs, message); //Then assertThat(sclReportItem).isPresent() @@ -218,9 +215,9 @@ void checkLimitationForBindedIEDControls_should_fail_three_error_messages() thro SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); - Set tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefSet(); + List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs(); //When - List sclReportItems = accessPointAdapter.checkLimitationForBindedIEDControls(tExtRefs); + List sclReportItems = accessPointAdapter.checkLimitationForBoundIEDControls(tExtRefs); //Then assertThat(sclReportItems).hasSize(3) @@ -241,9 +238,9 @@ void checkLimitationForBindedIEDControls_should_succed_no_error_message() throws iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxReports(2L); iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxSMV(2L); AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); - Set tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefSet(); + List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs(); //When - List sclReportItems = accessPointAdapter.checkLimitationForBindedIEDControls(tExtRefs); + List sclReportItems = accessPointAdapter.checkLimitationForBoundIEDControls(tExtRefs); //Then assertThat(sclReportItems).isEmpty(); 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 84b141887..859fb020b 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 @@ -750,25 +750,112 @@ void createControlBlockIfNotExists_should_create_ReportControl() { sourceLn0.createControlBlockIfNotExists(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, ControlBlockEnum.REPORT); // Then assertThat(sourceLn0.getCurrentElem().getReportControl()) - .hasSize(1); + .hasSize(1); TReportControl tReportControl = sourceLn0.getCurrentElem().getReportControl().get(0); assertThat(tReportControl) - .extracting(TControl::getName, TReportControl::getRptID, TControl::getDatSet, - TReportControl::isBuffered, TReportControl::getBufTime, TReportControl::isIndexed, TControlWithTriggerOpt::getIntgPd, TReportControl::getConfRev) - .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, - true, 0L, true, 60000L, 1L); + .extracting(TControl::getName, TReportControl::getRptID, TControl::getDatSet, + TReportControl::isBuffered, TReportControl::getBufTime, TReportControl::isIndexed, TControlWithTriggerOpt::getIntgPd, TReportControl::getConfRev) + .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, + true, 0L, true, 60000L, 1L); assertThat(tReportControl.getTrgOps()) - .extracting(TTrgOps::isDchg, TTrgOps::isQchg, TTrgOps::isPeriod, TTrgOps::isGi) - .containsOnly(true); + .extracting(TTrgOps::isDchg, TTrgOps::isQchg, TTrgOps::isPeriod, TTrgOps::isGi) + .containsOnly(true); assertThat(tReportControl.getTrgOps().isDupd()).isFalse(); assertThat(tReportControl.getOptFields()) - .extracting(TReportControl.OptFields::isSeqNum, TReportControl.OptFields::isTimeStamp, TReportControl.OptFields::isDataSet, - TReportControl.OptFields::isReasonCode, TReportControl.OptFields::isDataRef, TReportControl.OptFields::isEntryID, - TReportControl.OptFields::isConfigRef) - .containsOnly(false); + .extracting(TReportControl.OptFields::isSeqNum, TReportControl.OptFields::isTimeStamp, TReportControl.OptFields::isDataSet, + TReportControl.OptFields::isReasonCode, TReportControl.OptFields::isDataRef, TReportControl.OptFields::isEntryID, + TReportControl.OptFields::isConfigRef) + .containsOnly(false); assertThat(tReportControl.getOptFields().isBufOvfl()).isTrue(); } + @Test + void getFCDAs_should_return_list_of_FCDAs() { + + TFCDA tfcda = new TFCDA(); + tfcda.setFc(TFCEnum.CF); + TFCDA tfcda1 = new TFCDA(); + tfcda1.setFc(TFCEnum.CF); + TFCDA tfcda2 = new TFCDA(); + tfcda2.setFc(TFCEnum.CF); + + TDataSet tDataSet1 = new TDataSet(); + tDataSet1.setName("gse_dat_set"); + tDataSet1.getFCDA().addAll(List.of(tfcda,tfcda1)); + + TDataSet tDataSet2 = new TDataSet(); + tDataSet2.setName("smv_dat_set"); + tDataSet2.getFCDA().add(tfcda2); + + TGSEControl gseCB = new TGSEControl(); + gseCB.setName("gse1"); + gseCB.setDatSet("gse_dat_set"); + TSampledValueControl smvCB = new TSampledValueControl(); + smvCB.setName("smv1"); + smvCB.setDatSet("smv_dat_set"); + + LN0 ln0 = new LN0(); + ln0.getDataSet().addAll(List.of(tDataSet1, tDataSet2)); + ln0.getGSEControl().add(gseCB); + ln0.getSampledValueControl().add(smvCB); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); + + TExtRef tExtRef = new TExtRef(); + tExtRef.setSrcCBName("gse1"); + tExtRef.setServiceType(TServiceType.GOOSE); + + //When + List result = ln0Adapter.getFCDAs(tExtRef); + + //Then + assertThat(result).hasSize(2) + .containsExactlyInAnyOrder(tfcda, tfcda1); + + } + + @Test + void getFCDAs_should_return_empty_list_of_FCDAs() { + + TDataSet tDataSet2 = new TDataSet(); + tDataSet2.setName("smv_dat_set"); + + TSampledValueControl smvCB = new TSampledValueControl(); + smvCB.setName("smv1"); + smvCB.setDatSet("smv_dat_set"); + + LN0 ln0 = new LN0(); + ln0.getDataSet().add(tDataSet2); + ln0.getSampledValueControl().add(smvCB); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); + + TExtRef tExtRef = new TExtRef(); + tExtRef.setSrcCBName("smv1"); + tExtRef.setServiceType(TServiceType.SMV); + + //When + List result = ln0Adapter.getFCDAs(tExtRef); + + //Then + assertThat(result).isEmpty(); + + } + + @Test + void getFCDAs_should_throw_Exception_when_DataSet_not_present() { + + LN0 ln0 = new LN0(); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); + + TExtRef tExtRef = new TExtRef(); + tExtRef.setSrcCBName("smv1"); + tExtRef.setServiceType(TServiceType.SMV); + + //When Then + assertThatThrownBy(() -> ln0Adapter.getFCDAs(tExtRef)) + .isInstanceOf(ScdException.class) + .hasMessage("Control Block smv1 not found in /LN0"); + } + } From 3bfc64c884881131275a0f35b603c86b1a61b3d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Pupier?= Date: Mon, 9 Jan 2023 21:10:30 +0100 Subject: [PATCH 4/9] chore(217): use built-in Maven cache mechanism of setup-java GitHub Action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #217 Signed-off-by: Aurélien Pupier --- .github/workflows/sonarcloud-analysis.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/sonarcloud-analysis.yml b/.github/workflows/sonarcloud-analysis.yml index 8ad3c7ef2..33e6a53e6 100644 --- a/.github/workflows/sonarcloud-analysis.yml +++ b/.github/workflows/sonarcloud-analysis.yml @@ -26,18 +26,12 @@ jobs: key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - - name: Cache Maven packages - uses: actions/cache@v3 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '17' + cache: 'maven' - name: Set Common Sonar Variables id: sonar_env From 7ffe14546e2a760da072c21d7b47ff354b3a8562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Pupier?= Date: Wed, 18 Jan 2023 18:32:40 +0100 Subject: [PATCH 5/9] Fix typos in docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Aurélien Pupier --- docs/HOME.md | 20 ++++++++++---------- docs/compas-sct.md | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/HOME.md b/docs/HOME.md index fc77382dc..1cea5e2cc 100644 --- a/docs/HOME.md +++ b/docs/HOME.md @@ -39,7 +39,7 @@ Technical Steering Committee [CoMPAS-tsc@lists.lfenergy.org](mailto:CoMPAS-tsc@l By contributing to the CoMPAS project, you accept and agree to the following terms and conditions for your present and future contributions submitted to CoMPAS. All contributions to this project are licensed under the license stipulated at the corresponding sub-repository. -Except where otherwise explicitely indicated, CoMPAS is an open source project licensed under the [Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0/). +Except where otherwise explicitly indicated, CoMPAS is an open source project licensed under the [Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0/). The project requires the use of the [Developer Certificate of Origin (DCO)](https://developercertificate.org/). The DCO is a legally binding statement asserting that you are you have the right to submit your contribution and to license it under the project's applicable license. @@ -64,21 +64,21 @@ Note that checks will be performed during the integration in order to require th ## Community refinements -Every monday there is a CoMPAS Community Refinement at 10AM CET. The event is available at the CoMPAS mailing list [calendar](https://lists.lfenergy.org/g/CoMPAS/calendar) and can be subscribed to. +Every Monday there is a CoMPAS Community Refinement at 10AM CET. The event is available at the CoMPAS mailing list [calendar](https://lists.lfenergy.org/g/CoMPAS/calendar) and can be subscribed to. ### Prepared topics -Every friday before the refinement meeting on Monday, all topics are published on the #compas channel on our [LFEnergy Slack](https://lfenergy.slack.com/). +Every Friday before the refinement meeting on Monday, all topics are published on the #compas channel on our [LFEnergy Slack](https://lfenergy.slack.com/). ### Add your own topics -Everybody can suggest topics for the refinement. To do this, join the [LFEnergy Slack](https://lfenergy.slack.com/) if not already, and put your topics in the #compas channel. You can just do this by writing a message like "I want to add something to the refinement this monday, namely..." or add a comment to the already prepared topics if available (see [Prepared topics](#prepared-topics)). +Everybody can suggest topics for the refinement. To do this, join the [LFEnergy Slack](https://lfenergy.slack.com/) if not already, and put your topics in the #compas channel. You can just do this by writing a message like "I want to add something to the refinement this Monday, namely..." or add a comment to the already prepared topics if available (see [Prepared topics](#prepared-topics)). ### Reporting Bugs and Suggesting Enhancements Bugs and enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue in the repository where it belongs (an issue about the CIM to IEC 61850 mapping belongs in the [CIM Mapping repository](https://github.com/com-pas/compas-cim-mapping) for example) and provide the following information by filling in the template, which is either an issue or a bug. -When an issue is created, it is automatically being added to the `To do` column of the specific repository. This means it's added, but not yet refined. Every monday at 10AM CET, there is a Community Refinement (see our mailing list [calendar](https://lists.lfenergy.org/g/CoMPAS/calendar), everybody can join) where issues are being discussed that are not refined yet. Your issue is one of them. +When an issue is created, it is automatically being added to the `To do` column of the specific repository. This means it's added, but not yet refined. Every Monday at 10AM CET, there is a Community Refinement (see our mailing list [calendar](https://lists.lfenergy.org/g/CoMPAS/calendar), everybody can join) where issues are being discussed that are not refined yet. Your issue is one of them. Once it's accepted and refined, it goes to the `Ready for development` column and it's ready to be implemented/fixed. Before creating bug reports or suggesting enhancement, please **perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Acom-pas)** @@ -118,8 +118,8 @@ Continuous integration is set up to run on all branches automatically and will o ### Tools to contribute Continuous integration is setup automatically on all contributions. However, it's faster to iterate locally to fix problems than waiting for the status checks to finish. -There are many tools that can be used to do the verifications that are enforced by all status checks. The most simple and universal tool is maven, but IDE integrations -can be used to get more immediate feedback. Most of the team uses IntelliJ IDEA, but others IDEs can be used, for exemple the Eclipse IDE. +There are many tools that can be used to do the verifications that are enforced by all status checks. The most simple and universal tool is Maven, but IDE integrations +can be used to get more immediate feedback. Most of the team uses IntelliJ IDEA, but others IDEs can be used, for example the Eclipse IDE. ### Definition of Done @@ -147,7 +147,7 @@ On the [developing](https://com-pas.github.io/contributing/DEVELOPING.html) page #### Open Community Calls -It's good to know that every other monday, we are having a so called Open Community Call. Everyone participating in the CoMPAS project can join +It's good to know that every other Monday, we are having a so called Open Community Call. Everyone participating in the CoMPAS project can join and talk about and ask question about the CoMPAS project. When the Open Community Calls are taking place, can be found at the [General CoMPAS mailing list calendar](https://lists.lfenergy.org/g/CoMPAS/calendar). @@ -167,7 +167,7 @@ A good (open source) project requires documentation. We have two places for our ##### LF Energy Wiki -LF Energy has it's own [CoMPAS specific Wiki](https://wiki.lfenergy.org/display/HOME/CoMPAS). This is the place for documenation +LF Energy has it's own [CoMPAS specific Wiki](https://wiki.lfenergy.org/display/HOME/CoMPAS). This is the place for documentation about CoMPAS in general (like roadmap and the community call agendas). #### Architecture and technologies @@ -178,7 +178,7 @@ please check the source code (duh!) and our [CoMPAS Architecture Github Pages](h ### Copyright and Licensing Copyright and license information is done on per-file basis. We use the specification of [REUSE](https://reuse.software/spec/) -to ensure that copyright information of the project is clear and can be analuzed in an automated fashion. +to ensure that copyright information of the project is clear and can be analyzed in an automated fashion. Every source code repository within CoMPAS has a Github Action for checking against the REUSE specification. diff --git a/docs/compas-sct.md b/docs/compas-sct.md index 4e231e2ef..d6b979443 100644 --- a/docs/compas-sct.md +++ b/docs/compas-sct.md @@ -170,7 +170,7 @@ import it in the temporary SCD file ``` Operations can now be realized and validated on this minimal file (which has the same structure as an ICD file). -Any validation on the minimal file guaranties a valid integration of those chunks into the large file. +Any validation on the minimal file guarantees a valid integration of those chunks into the large file. > **WARNING:** This concern only update/creation operations on SCL file. From 9e3b4e1b8a26ca0bd810284b43da1c8ddbe09299 Mon Sep 17 00:00:00 2001 From: Samir Romdhani Date: Tue, 7 Feb 2023 15:15:57 +0100 Subject: [PATCH 6/9] feat/javadoc: automate Javadoc deployment via gh-pages (#229) fixes maven plugin related to jdk17 Signed-off-by: Samir Romdhani feat/javadoc: automate Javadoc remove PR creation and push directly gh-pages branch --- .github/workflows/automate_javadoc.yml | 50 ++++++++++--------- pom.xml | 1 - .../lfenergy/compas/sct/app/package-info.java | 2 +- .../compas/sct/commons/dto/package-info.java | 4 +- .../sct/commons/scl/com/package-info.java | 4 +- .../sct/commons/scl/dtt/package-info.java | 4 +- .../sct/commons/scl/header/package-info.java | 4 +- .../sct/commons/scl/ied/package-info.java | 4 +- .../compas/sct/commons/scl/package-info.java | 4 +- .../commons/scl/sstation/package-info.java | 4 +- 10 files changed, 41 insertions(+), 40 deletions(-) diff --git a/.github/workflows/automate_javadoc.yml b/.github/workflows/automate_javadoc.yml index 3ac03a52c..2db3fdbca 100644 --- a/.github/workflows/automate_javadoc.yml +++ b/.github/workflows/automate_javadoc.yml @@ -1,27 +1,29 @@ -# SPDX-FileCopyrightText: 2022 RTE FRANCE +# SPDX-FileCopyrightText: 2022 2023 RTE FRANCE # # SPDX-License-Identifier: Apache-2.0 name: Continuous Deployment - JavaDocs on: - push: - branches: - - 'feat/javadoc' + pull_request_target: + types: [ labeled, closed ] + jobs: build: + if: ${{ github.event.pull_request.base.ref == 'develop' && github.actor != 'dependabot[bot]' && github.event.pull_request.merged && contains(github.event.pull_request.labels.*.name, 'javadoc') }} name: Java Docs runs-on: ubuntu-latest env: - from_branch: feat/javadoc - target_branch: feat/117_Documentation_SCD_Generation_Process + DEPLOYMENT_BRANCH: gh-pages + DEPENDABOT_BRANCH: dependabot/javadoc-${{ github.head_ref || github.ref_name }}-${{ github.event.pull_request.number }} steps: - # Use develop as base branch to generate javadoc - - name: Setup Environment + - name: Checkout uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '17' - ref: ${{ env.target_branch }} - name: Create custom Maven Settings.xml uses: whelk-io/maven-settings-xml-action@v21 @@ -33,6 +35,8 @@ jobs: run: | git config --global user.name '${{ secrets.CONFIG_CI_USER_NAME }}' git config --global user.email '${{ secrets.CONFIG_CI_USER_EMAIL }}' + git config pull.rebase false + git fetch origin ${{ env.DEPLOYMENT_BRANCH }} - name: Import GPG key id: import_gpg @@ -42,27 +46,25 @@ jobs: git_user_signingkey: true git_commit_gpgsign: true - - name: Prepare Pull Request branch + # Take note that your GitHub Pages site is currently being built from the /docs folder in the DEPLOYMENT_BRANCH branch. + - name: Prepare Pull Request branch & commit files run: | mvn -s custom_maven_settings.xml clean javadoc:aggregate -P javadoc mkdir -p docs/javadoc yes | cp -Rf target/site/apidocs/* docs/javadoc/ - git checkout -b ${{ env.from_branch }}-pull-request + git checkout -b temp + git add docs/javadoc/ + git commit -m "[dependabot/javadoc/temp]: update javadoc" + git checkout ${{ env.DEPLOYMENT_BRANCH }} + git clean -fdx + git checkout -b ${{ env.DEPENDABOT_BRANCH }} + git cherry-pick -n -X theirs temp + git commit -m "[dependabot/javadoc]: update javadoc" + git checkout ${{ env.DEPLOYMENT_BRANCH }} + git merge ${{ env.DEPENDABOT_BRANCH }} - # Note that will fail if branch already exists. - name: Push Git Branch uses: ad-m/github-push-action@v0.6.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} - branch: ${{ env.from_branch }}-pull-request - - # Note that will silently fail if PR already exists. - - name: Create Pull Request - uses: repo-sync/pull-request@v2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - source_branch: ${{ env.from_branch }}-pull-request - destination_branch: ${{ env.target_branch }} - pr_title: "Docs: Update Java docs repository" - pr_body: "Automatically created from CI workflow" - pr_label: "documentation,javadoc" \ No newline at end of file + branch: ${{ env.DEPLOYMENT_BRANCH }} \ No newline at end of file diff --git a/pom.xml b/pom.xml index a8de927d6..caaaded0e 100644 --- a/pom.xml +++ b/pom.xml @@ -218,7 +218,6 @@ ${javadoc.title} ${javadoc.lint} - --no-module-directories diff --git a/sct-app/src/main/java/org/lfenergy/compas/sct/app/package-info.java b/sct-app/src/main/java/org/lfenergy/compas/sct/app/package-info.java index c83b8e50c..fe9a2d02f 100644 --- a/sct-app/src/main/java/org/lfenergy/compas/sct/app/package-info.java +++ b/sct-app/src/main/java/org/lfenergy/compas/sct/app/package-info.java @@ -3,6 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    sct-app is a group of automation services

    + *

    sct-app is a group of automation services

    */ package org.lfenergy.compas.sct.app; \ No newline at end of file diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/package-info.java index ee24e919f..9f762c71b 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/package-info.java @@ -3,8 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    sct.commons.dto is a group of DTO utils for operating on + *

    sct.commons.dto is a group of DTO utils for operating on * {@link org.lfenergy.compas.scl2007b4.model.SCL SCL} services - *

    + * */ package org.lfenergy.compas.sct.commons.dto; \ No newline at end of file diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/package-info.java index fbf637d8e..8b72f2343 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/package-info.java @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    scl.com is a group of services for operating on + *

    scl.com is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.TCommunication Communication} object - *

    + * * @see org.lfenergy.compas.scl2007b4.model.TCommunication * @see org.lfenergy.compas.scl2007b4.model.TSubNetwork * @see org.lfenergy.compas.scl2007b4.model.TConnectedAP diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/package-info.java index 81afa71b1..020d29d34 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/package-info.java @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    scl.dtt is a group of services for operating on + *

    scl.dtt is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.TDataTypeTemplates DataTypeTemplates} object - *

    + * * @see org.lfenergy.compas.scl2007b4.model.TLNodeType * @see org.lfenergy.compas.scl2007b4.model.TDOType * @see org.lfenergy.compas.scl2007b4.model.TDAType diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/header/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/header/package-info.java index 55eaf1e46..53782746e 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/header/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/header/package-info.java @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    scl.header is a group of services for operating on + *

    scl.header is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.THeader Header} object - *

    + * * @see org.lfenergy.compas.scl2007b4.model.THeader * @see org.lfenergy.compas.scl2007b4.model.THitem * @see Issue !6 diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/package-info.java index 5d8086435..5bbacdaba 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/package-info.java @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    scl.ied is a group of services for operating on + *

    scl.ied is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.TIED IED} object - *

    + * * @see org.lfenergy.compas.scl2007b4.model.TAccessPoint * @see org.lfenergy.compas.scl2007b4.model.TServices * @see org.lfenergy.compas.scl2007b4.model.TLDevice diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/package-info.java index 9385c8a5a..4529100aa 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/package-info.java @@ -3,13 +3,13 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    commons.scl is a group of services for operating on + *

    commons.scl is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.THeader Header} , * {@link org.lfenergy.compas.scl2007b4.model.TSubstation Substation} , * {@link org.lfenergy.compas.scl2007b4.model.TCommunication Communication} , * {@link org.lfenergy.compas.scl2007b4.model.TIED IED} , * {@link org.lfenergy.compas.scl2007b4.model.TDataTypeTemplates DataTypeTemplates} objects - *

    + * * @see org.lfenergy.compas.scl2007b4.model.SCL */ package org.lfenergy.compas.sct.commons.scl; \ No newline at end of file diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/sstation/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/sstation/package-info.java index 2f3987594..45b2f2e0d 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/sstation/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/sstation/package-info.java @@ -2,9 +2,9 @@ // // SPDX-License-Identifier: Apache-2.0 /** - *

    scl.sstation is a group of services for operating on + *

    scl.sstation is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.TSubstation Substation} object - *

    + * * @see org.lfenergy.compas.scl2007b4.model.TVoltageLevel * @see org.lfenergy.compas.scl2007b4.model.TFunction * @see org.lfenergy.compas.scl2007b4.model.TBay From de1a01dd51af023888924c46b50560a560d3160b Mon Sep 17 00:00:00 2001 From: SaintierFr <99645240+SaintierFr@users.noreply.github.com> Date: Tue, 31 Jan 2023 10:56:32 +0100 Subject: [PATCH 7/9] feat(231): Automatic binding: Edit InRef from ExtRef Signed-off-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> --- .github/workflows/sonarcloud-analysis.yml | 2 +- .../compas/sct/commons/dto/ExtrefTarget.java | 9 + .../compas/sct/commons/dto/SclReport.java | 1 - .../sct/commons/scl/ObjectReference.java | 29 +- .../compas/sct/commons/scl/SclService.java | 27 +- .../commons/scl/ied/AbstractDAIAdapter.java | 56 ++- .../sct/commons/scl/ied/DOIAdapter.java | 125 ++++- .../sct/commons/scl/ied/LN0Adapter.java | 64 ++- .../sct/commons/scl/SclServiceTest.java | 84 +++- .../sct/commons/scl/ied/DOIAdapterTest.java | 442 +++++++++++++++--- .../sct/commons/scl/ied/LN0AdapterTest.java | 313 +++++++++---- .../sct/commons/testhelpers/SclHelper.java | 3 + .../scd_update_inref_issue_231_test_ko.xml | 115 +++++ .../scd_update_inref_issue_231_test_ok.xml | 86 ++++ .../scd_update_inref_test.xml | 177 +++++++ 15 files changed, 1303 insertions(+), 230 deletions(-) create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ExtrefTarget.java create mode 100644 sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml create mode 100644 sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml create mode 100644 sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml diff --git a/.github/workflows/sonarcloud-analysis.yml b/.github/workflows/sonarcloud-analysis.yml index 33e6a53e6..09cd08540 100644 --- a/.github/workflows/sonarcloud-analysis.yml +++ b/.github/workflows/sonarcloud-analysis.yml @@ -37,7 +37,7 @@ jobs: id: sonar_env run: | echo "##[set-output name=sonar_opts;]$(echo -Dsonar.host.url=https://sonarcloud.io \ - -Dsonar.projectKey=com-pas_compas-core \ + -Dsonar.projectKey=com-pas_compas-sct \ -Dsonar.organization=com-pas )" - name: Create custom Maven Settings.xml uses: whelk-io/maven-settings-xml-action@v21 diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ExtrefTarget.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ExtrefTarget.java new file mode 100644 index 000000000..d1eb10e5d --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ExtrefTarget.java @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.dto; + +public enum ExtrefTarget { + SRC_REF, SRC_CB +} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/SclReport.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/SclReport.java index 05103dc8d..12e11624a 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/SclReport.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/SclReport.java @@ -39,5 +39,4 @@ public class SclReport { public boolean isSuccess() { return sclReportItems.stream().noneMatch(SclReportItem::isFatal); } - } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java index e37e9cbe4..0701ea0e4 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java @@ -6,7 +6,10 @@ import lombok.Getter; import org.apache.commons.lang3.StringUtils; - +import org.lfenergy.compas.scl2007b4.model.TExtRef; +import org.lfenergy.compas.scl2007b4.model.TLLN0Enum; +import org.lfenergy.compas.sct.commons.dto.ExtrefTarget; +import org.lfenergy.compas.sct.commons.util.Utils; /** * A representation of the model object @@ -38,7 +41,7 @@ public class ObjectReference { private static final String MALFORMED_OBJ_REF = "Malformed ObjRef : %s" ; private final String reference; - //IEDName.LDInst + //IEDNameLDInst private String ldName; //LNPrefixLNClassLNInst private String lNodeName; @@ -49,14 +52,30 @@ public ObjectReference(String reference) { init(); } + public ObjectReference(TExtRef extRef, ExtrefTarget target) { + this.ldName = extRef.getIedName() + extRef.getLdInst(); + if(target.equals(ExtrefTarget.SRC_REF)) { + this.lNodeName = Utils.emptyIfBlank(extRef.getPrefix()) + + (extRef.isSetLnClass() ? extRef.getLnClass().get(0): TLLN0Enum.LLN_0.value()) + + Utils.emptyIfBlank(extRef.getLnInst()); + this.dataAttributes = Utils.emptyIfBlank(extRef.getDoName()); + } + if(target.equals(ExtrefTarget.SRC_CB)) { + this.lNodeName = Utils.emptyIfBlank(extRef.getSrcPrefix()) + + (extRef.isSetSrcLNClass() ? extRef.getSrcLNClass().get(0): TLLN0Enum.LLN_0.value()) + + Utils.emptyIfBlank(extRef.getSrcLNInst()); + this.dataAttributes = Utils.emptyIfBlank(extRef.getSrcCBName()); + } + this.reference = this.ldName + "/" + this.lNodeName + "." + this.dataAttributes; + } + public final void init(){ - String refCopy = reference; - String[] objRefPart = reference.split("[/]"); + String[] objRefPart = reference.split("/"); if(objRefPart.length != 2 || StringUtils.isBlank(objRefPart[0]) || StringUtils.isBlank(objRefPart[1])){ throw new IllegalArgumentException(String.format(MALFORMED_OBJ_REF, reference)); } ldName = objRefPart[0]; - refCopy = objRefPart[1]; + String refCopy = objRefPart[1]; objRefPart = refCopy.split("[.]", 2); if(objRefPart.length != 2 || StringUtils.isBlank(objRefPart[0]) || StringUtils.isBlank(objRefPart[1])){ throw new IllegalArgumentException(String.format(MALFORMED_OBJ_REF, reference)); diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java index 7fad51987..117f37347 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java @@ -234,9 +234,10 @@ public static List getExtRefInfo(SCL scd, String iedName, String ldI /** * Create LDevice - * @param scd SCL file in which LDevice should be found + * + * @param scd SCL file in which LDevice should be found * @param iedName name of IED in which LDevice is localized - * @param ldInst LdInst of LDevice for which adapter is created + * @param ldInst LdInst of LDevice for which adapter is created * @return created LDevice adapter */ private static LDeviceAdapter createLDeviceAdapter(SCL scd, String iedName, String ldInst) { @@ -504,8 +505,8 @@ public static SclRootAdapter importSTDElementsInSCD(@NonNull SclRootAdapter scdR PrivateService.createMapIEDNameAndPrivate(scdRootAdapter).forEach(tPrivate -> { String iedName = PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName(); String icdSysVerUuid = PrivateService.extractCompasICDHeader(tPrivate).map(TCompasICDHeader::getICDSystemVersionUUID) - .orElseThrow(() -> new ScdException(ICD_SYSTEM_VERSION_UUID + " is not present in COMPAS-ICDHeader in LNode") - ); + .orElseThrow(() -> new ScdException(ICD_SYSTEM_VERSION_UUID + " is not present in COMPAS-ICDHeader in LNode") + ); if (!mapICDSystemVersionUuidAndSTDFile.containsKey(icdSysVerUuid)) throw new ScdException("There is no STD file found corresponding to " + PrivateService.stdCheckFormatExceptionMessage(tPrivate)); // import /ied /dtt in Scd @@ -575,6 +576,7 @@ public static SclReport updateLDeviceStatus(SCL scd) { /** * Checks Control Blocks, DataSets and FCDA number limitation into Access Points + * * @param scd SCL file for which LDevice should be activated or deactivated * @return SclReport Object that contain SCL file and set of errors */ @@ -590,4 +592,21 @@ public static SclReport analyzeDataGroups(SCL scd) { return new SclReport(sclRootAdapter, sclReportItems); } + + /** + * Update DAIs of DO InRef in all LN0 of the SCD using matching ExtRef information. + * + * @param scd SCL file for which DOs InRef should be updated with matching ExtRef information + * @return SclReport Object that contain SCL file and set of errors + */ + public static SclReport updateDoInRef(SCL scd) { + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + List sclReportItems = sclRootAdapter.streamIEDAdapters() + .flatMap(IEDAdapter::streamLDeviceAdapters) + .map(LDeviceAdapter::getLN0Adapter) + .map(LN0Adapter::updateDoInRef) + .flatMap(List::stream) + .toList(); + return new SclReport(sclRootAdapter, sclReportItems); + } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java index d612a8cee..5e5924659 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java @@ -43,12 +43,13 @@ * @see org.lfenergy.compas.scl2007b4.model.TDAI * @see Issue !70 */ -public abstract class AbstractDAIAdapter

    extends SclElementAdapter implements IDataAdapter{ +public abstract class AbstractDAIAdapter

    extends SclElementAdapter implements IDataAdapter { /** * Constructor + * * @param parentAdapter Parent container reference - * @param currentElem Current reference + * @param currentElem Current reference */ protected AbstractDAIAdapter(P parentAdapter, TDAI currentElem) { super(parentAdapter, currentElem); @@ -56,9 +57,10 @@ protected AbstractDAIAdapter(P parentAdapter, TDAI currentElem) { /** * Gets SDI from DAI by name + * * @param sName SDI name + * @param expected class type * @return IDataAdapter related object - * @param expected class type * @throws ScdException throws when specified SDI not present in DAI */ public S getStructuredDataAdapterByName(String sName) throws ScdException { @@ -67,33 +69,36 @@ public S getStructuredDataAdapterByName(String sName) t /** * Gets DataAdapter by DAI + * * @param sName DAI name + * @param expected class type * @return IDataAdapter related object - * @param expected class type * @throws ScdException throws when specified DAI unknown */ - public S getDataAdapterByName(String sName) throws ScdException { + public S getDataAdapterByName(String sName) throws ScdException { throw new UnsupportedOperationException("DAI doesn't have any DAI"); } /** * Sets ValImport value + * * @param b value */ - public void setValImport(boolean b){ + public void setValImport(boolean b) { currentElem.setValImport(b); } /** * Cheks ValImport boolean value + * * @return Boolean value of ValImport if define or null */ - public Boolean isValImport(){ + public Boolean isValImport() { return currentElem.isSetValImport() ? currentElem.isValImport() : null; } public AbstractDAIAdapter update(Map daiValues) throws ScdException { - if(daiValues.size() > 1 && daiValues.containsKey(0L)){ + if (daiValues.size() > 1 && daiValues.containsKey(0L)) { update(0L, daiValues.get(0L)); // to be refined (with COMPAS TEAMS) } else { for (Map.Entry mapVal : daiValues.entrySet()) { @@ -105,23 +110,24 @@ public AbstractDAIAdapter update(Map /** * Updates DAI SGroup value + * * @param sGroup SGroup to update - * @param val value + * @param val value * @throws ScdException throws when DAI for which SGroup should be updated is not updatable */ public void update(Long sGroup, String val) throws ScdException { - if(currentElem.isSetValImport() && !currentElem.isValImport()){ + if (currentElem.isSetValImport() && !currentElem.isValImport()) { String msg = String.format( - "DAI(%s) cannot be updated : valImport(false)",currentElem.getName() + "DAI(%s) cannot be updated : valImport(false)", currentElem.getName() ); throw new ScdException(msg); } Stream tValStream = currentElem.getVal().stream(); if (sGroup != null && sGroup != 0) { Optional tVal = tValStream.filter(tValElem -> tValElem.isSetSGroup() && - sGroup.equals(tValElem.getSGroup())) + sGroup.equals(tValElem.getSGroup())) .findFirst(); - if(tVal.isPresent()){ + if (tVal.isPresent()) { tVal.get().setValue(val); } else { TVal newTVal = new TVal(); @@ -130,22 +136,26 @@ public void update(Long sGroup, String val) throws ScdException { currentElem.getVal().add(newTVal); } } else { - Optional tVal = tValStream.findFirst(); - if(tVal.isPresent()){ - tVal.get().setValue(val); - }else { - TVal newTVal = new TVal(); - newTVal.setValue(val); - currentElem.getVal().add(newTVal); - } + updateVal(val); } } - public IDataAdapter addDAI(String name){ + public void updateVal(String s) { + currentElem.getVal().stream().findFirst() + .orElseGet( + () -> { + TVal newTVal = new TVal(); + currentElem.getVal().add(newTVal); + return newTVal; + }) + .setValue(s); + } + + public IDataAdapter addDAI(String name) { throw new UnsupportedOperationException("DAI cannot contain an SDI"); } - public IDataAdapter addSDOI(String sdoName){ + public IDataAdapter addSDOI(String sdoName) { throw new UnsupportedOperationException("DAI cannot contain an DAI"); } } 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 ecd34e231..a12c63ec4 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 @@ -4,14 +4,19 @@ package org.lfenergy.compas.sct.commons.scl.ied; -import org.lfenergy.compas.scl2007b4.model.TAnyLN; -import org.lfenergy.compas.scl2007b4.model.TDAI; -import org.lfenergy.compas.scl2007b4.model.TDOI; -import org.lfenergy.compas.scl2007b4.model.TSDI; +import org.lfenergy.compas.scl2007b4.model.*; +import org.lfenergy.compas.sct.commons.dto.ExtrefTarget; +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.SclElementAdapter; import org.lfenergy.compas.sct.commons.util.Utils; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + /** * A representation of the model object @@ -39,10 +44,18 @@ */ public class DOIAdapter extends SclElementAdapter, TDOI> implements IDataParentAdapter { + protected static final String DA_NAME_SET_SRC_REF = "setSrcRef"; + protected static final String DA_NAME_SET_SRC_CB = "setSrcCB"; + protected static final String DA_NAME_SET_TST_REF = "setTstRef"; + protected static final String DA_NAME_SET_TST_CB = "setTstCB"; + + private static final Comparator EXTREF_DESC_SUFFIX_COMPARATOR = Comparator.comparingInt(extRef -> extractDescSuffix(extRef.getDesc())); + /** * Constructor + * * @param parentAdapter Parent container reference - * @param currentElem Current reference + * @param currentElem Current reference */ protected DOIAdapter(AbstractLNAdapter parentAdapter, TDOI currentElem) { super(parentAdapter, currentElem); @@ -50,6 +63,7 @@ protected DOIAdapter(AbstractLNAdapter parentAdapter, TDOI cur /** * Check if node is child of the reference node + * * @return link parent child existence */ @Override @@ -65,6 +79,7 @@ protected String elementXPath() { /** * Gets SDI by name from current DOI + * * @param sName name of SDI to get * @return RootSDIAdapter object * @throws ScdException throws when specified name of SDI not present in current DOI @@ -76,40 +91,52 @@ public RootSDIAdapter getStructuredDataAdapterByName(String sName) throws ScdExc .filter(tUnNaming -> tUnNaming.getClass().equals(TSDI.class)) .map(TSDI.class::cast) .filter(tsdi -> tsdi.getName().equals(sName)) - .map(tsdi -> new RootSDIAdapter(this,tsdi)) + .map(tsdi -> new RootSDIAdapter(this, tsdi)) .findFirst() .orElseThrow( - ()-> new ScdException( - String.format("Unknown SDI (%s) in DOI (%s)", sName, currentElem.getName()) - ) + () -> new ScdException( + String.format("Unknown SDI (%s) in DOI (%s)", sName, currentElem.getName()) + ) ); } /** * Gets DAI from current DOI + * * @param daName name of DAI to get * @return DAIAdapter object * @throws ScdException throws when specified name of DAI not present in current DOI */ @Override public DAIAdapter getDataAdapterByName(String daName) throws ScdException { - return currentElem.getSDIOrDAI() + return findDataAdapterByName(daName) + .orElseThrow( + () -> new ScdException( + String.format("Unknown DAI (%s) in DOI (%s)", daName, currentElem.getName()) + ) + ); + } + + /** + * Given a DAI[name], returns an associated DAIAdapter + * + * @param daName name of DAI to get + * @return an Optional DAIAdapter if found, Optional.empty() otherwise + */ + public Optional findDataAdapterByName(String daName) { + return currentElem.getSDIOrDAI() .stream() .filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class)) .map(TDAI.class::cast) .filter(tdai -> tdai.getName().equals(daName)) - .map(tdai -> new DAIAdapter(this,tdai)) - .findFirst() - .orElseThrow( - ()-> new ScdException( - String.format("Unknown DAI (%s) in DOI (%s)", daName, currentElem.getName()) - ) - ); + .map(tdai -> new DAIAdapter(this, tdai)) + .findFirst(); } /** * Adds DAI to current DOI - * @param name name of DAI to add + * + * @param name name of DAI to add * @param isUpdatable updatability state of DAI * @return DAIAdapter object as added DAI */ @@ -119,11 +146,12 @@ public DAIAdapter addDAI(String name, boolean isUpdatable) { tdai.setName(name); tdai.setValImport(isUpdatable); currentElem.getSDIOrDAI().add(tdai); - return new DAIAdapter(this,tdai); + return new DAIAdapter(this, tdai); } /** * Adds SDOI to SDI in current DOI + * * @param sdoName name of SDOI to add * @return RootSDIAdapter object as added SDOI */ @@ -132,7 +160,64 @@ public RootSDIAdapter addSDOI(String sdoName) { TSDI tsdi = new TSDI(); tsdi.setName(sdoName); currentElem.getSDIOrDAI().add(tsdi); - return new RootSDIAdapter(this,tsdi); + return new RootSDIAdapter(this, tsdi); + } + + /** + * Update the DAI/Val according to the ExtRef attributes. + * If the ExtRef.desc start with DAI[name='purpose']/Val and end with "_1" (nominal case): + * - DAI[name='setSrcRef']/Val is updated with ExtRef attributes concatenation + * - DAI[name='setSrcCB']/Val is updated with ExtRef attributes concatenation if ExtRef.srcCBName is present + * If the ExtRef.desc start with DAI[name='purpose']/Val and end with "_2" or greater (test case): + * - DAI[name='setTstRef']/Val is updated with ExtRef attributes concatenation + * - DAI[name='setTstCB']/Val is updated with ExtRef attributes concatenation if ExtRef.srcCBName is present + * + * @param tExtRefs all the ExtRefs contained in the current LDevice/LLN0 + * @return a filled SclReportItem if an error occurs, empty SclReportItem otherwise + */ + public Optional updateDaiFromExtRef(List tExtRefs) { + Optional tExtRefMinOptional = tExtRefs.stream().min(EXTREF_DESC_SUFFIX_COMPARATOR); + + if (tExtRefMinOptional.isPresent() && extractDescSuffix(tExtRefMinOptional.get().getDesc()) == 1) { + TExtRef tExtRefMin = tExtRefMinOptional.get(); + findDataAdapterByName(DA_NAME_SET_SRC_REF) + .orElse(addDAI(DA_NAME_SET_SRC_REF, true)) + .updateVal(createInRefValNominalString(tExtRefMin)); + if (tExtRefMin.isSetSrcCBName()) { + findDataAdapterByName(DA_NAME_SET_SRC_CB) + .orElse(addDAI(DA_NAME_SET_SRC_CB, true)) + .updateVal(createInRefValTestString(tExtRefMin)); + } + + Optional tExtRefMaxOptional = tExtRefs.stream().max(Comparator.comparingInt(o -> Integer.parseInt(Objects.requireNonNull(Utils.extractField(o.getDesc(), "_", -1))))); + if (tExtRefMaxOptional.isPresent() && extractDescSuffix(tExtRefMaxOptional.get().getDesc()) > 1) { + TExtRef tExtRefMax = tExtRefMaxOptional.get(); + findDataAdapterByName(DA_NAME_SET_TST_REF) + .orElse(addDAI(DA_NAME_SET_TST_REF, true)) + .updateVal(createInRefValNominalString(tExtRefMax)); + if (tExtRefMax.isSetSrcCBName()) { + findDataAdapterByName(DA_NAME_SET_TST_CB) + .orElse(addDAI(DA_NAME_SET_TST_CB, true)) + .updateVal(createInRefValTestString(tExtRefMax)); + } + } + } else { + return Optional.of(SclReportItem.warning(getXPath(), "The DOI %s can't be bound with an ExtRef".formatted(getXPath()))); + } + + return Optional.empty(); + } + + private static int extractDescSuffix(String desc) throws NumberFormatException { + return Integer.parseInt(Utils.extractField(desc, "_", -1)); + } + + private String createInRefValNominalString(TExtRef extRef) { + return new ObjectReference(extRef, ExtrefTarget.SRC_REF).getReference(); + } + + private String createInRefValTestString(TExtRef extRef) { + return new ObjectReference(extRef, ExtrefTarget.SRC_CB).getReference(); } /** 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 efc341581..e3399ad5d 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 @@ -61,6 +61,7 @@ * LDName = "name" attribute of IEDName element + "inst" attribute of LDevice element * LNName = "prefix" + "lnClass" + "lnInst" * + * * @see org.lfenergy.compas.scl2007b4.model.TLN0 * @see org.lfenergy.compas.sct.commons.scl.ied.AbstractLNAdapter */ @@ -70,17 +71,22 @@ public class LN0Adapter extends AbstractLNAdapter { public static final DaTypeName STVAL_DA_TYPE_NAME = new DaTypeName(STVAL); public static final DoTypeName BEHAVIOUR_DO_TYPE_NAME = new DoTypeName(BEHAVIOUR_DO_NAME); public static final DaTypeName BEHAVIOUR_DA_TYPE_NAME = getDaTypeNameForBeh(); + private static final String DAI_NAME_PURPOSE = "purpose"; + private static final String INREF_PREFIX = "InRef"; + /** * Constructor + * * @param parentAdapter Parent container reference - * @param ln0 Current reference + * @param ln0 Current reference */ public LN0Adapter(LDeviceAdapter parentAdapter, LN0 ln0) { - super(parentAdapter,ln0); + super(parentAdapter, ln0); } /** * Check if node is child of the reference node + * * @return link parent child existence */ @Override @@ -96,6 +102,7 @@ protected String elementXPath() { /** * Gets current LN0 class type + * * @return LN0.class */ @Override @@ -105,6 +112,7 @@ protected Class getElementClassType() { /** * Gets LNClass enum value of current LNO + * * @return LNClass value */ public String getLNClass() { @@ -113,6 +121,7 @@ public String getLNClass() { /** * Gets LNInst value of current LN0 + * * @return "" */ @Override @@ -122,6 +131,7 @@ public String getLNInst() { /** * Gets Prefix value of current LN0 + * * @return "" */ @Override @@ -131,19 +141,22 @@ public String getPrefix() { /** * Gets Inputs node as an adapter + * * @return an InputsAdapter */ - public InputsAdapter getInputsAdapter(){ + public InputsAdapter getInputsAdapter() { return new InputsAdapter(this, currentElem.getInputs()); } - /** Checks if given attibrute corresponds to DataSet or ReportControl or SMVControl or GSEControl in current LN0 + /** + * Checks if given attibrute corresponds to DataSet or ReportControl or SMVControl or GSEControl in current LN0 + * * @param dataAttribute attribute to check * @return Boolean value of check result */ @Override - protected boolean matchesDataAttributes(String dataAttribute){ - return super.matchesDataAttributes(dataAttribute) || + protected boolean matchesDataAttributes(String dataAttribute) { + return super.matchesDataAttributes(dataAttribute) || currentElem.getSampledValueControl().stream().anyMatch(smp -> smp.getName().equals(dataAttribute)) || currentElem.getGSEControl().stream().anyMatch(gse -> gse.getName().equals(dataAttribute)); } @@ -178,6 +191,7 @@ private ResumedDataTemplate createDaiFilter(DoTypeName doTypeName, DaTypeName da /** * Verify and update LDevice status in parent Node + * * @param iedNameLDeviceInstList pair of Ied name and LDevice inst attributes * @return Set of Errors */ @@ -186,7 +200,7 @@ public Optional updateLDeviceStatus(List> ie final String iedName = getParentAdapter().getParentAdapter().getName(); final String ldInst = getParentAdapter().getInst(); ResumedDataTemplate daiBehFilter = createDaiFilter(BEHAVIOUR_DO_TYPE_NAME, BEHAVIOUR_DA_TYPE_NAME); - List daiBehList = getDAI(daiBehFilter, false); + List daiBehList = getDAI(daiBehFilter, false); if (daiBehList.isEmpty()) { return Optional.of(buildFatalReportItem("The LDevice doesn't have a DO @name='Beh' OR its associated DA@fc='ST' AND DA@name='stVal'")); } @@ -206,13 +220,13 @@ public Optional updateLDeviceStatus(List> ie ResumedDataTemplate newDaModToSetInLN0 = optionalModStVal.get(); String initialValue = newDaModToSetInLN0.findFirstValue().orElse(""); lDeviceActivation.checkLDeviceActivationStatus(iedName, ldInst, compasLDeviceStatus, enumValues); - if(lDeviceActivation.isUpdatable()){ - if(!initialValue.equals(lDeviceActivation.getNewVal())) { + if (lDeviceActivation.isUpdatable()) { + if (!initialValue.equals(lDeviceActivation.getNewVal())) { newDaModToSetInLN0.setVal(lDeviceActivation.getNewVal()); updateDAI(newDaModToSetInLN0); } - }else { - if(lDeviceActivation.getErrorMessage() != null) { + } else { + if (lDeviceActivation.getErrorMessage() != null) { return Optional.of(buildFatalReportItem(lDeviceActivation.getErrorMessage())); } } @@ -221,13 +235,13 @@ public Optional updateLDeviceStatus(List> ie public Optional getLDeviceStatus() { return getDaiModStVal() - .flatMap(ResumedDataTemplate::findFirstValue); + .flatMap(ResumedDataTemplate::findFirstValue); } private Optional getDaiModStVal() { ResumedDataTemplate daiModFilter = createDaiFilter(MOD_DO_TYPE_NAME, STVAL_DA_TYPE_NAME); return getDAI(daiModFilter, false).stream() - .findFirst(); + .findFirst(); } private static DaTypeName getDaTypeNameForBeh() { @@ -237,4 +251,28 @@ private static DaTypeName getDaTypeNameForBeh() { daTypeNameBeh.setFc(TFCEnum.ST); return daTypeNameBeh; } + + /** + * Update DAIs of DO InRef in all LN0 of the SCD using matching ExtRef information. + * + * @return A list of SclReport Objects that contain errors + */ + public List updateDoInRef() { + return getDOIAdapters().stream() + .filter(doiAdapter -> doiAdapter.getCurrentElem().isSetName() + && doiAdapter.getCurrentElem().getName().startsWith(INREF_PREFIX) + && doiAdapter.findDataAdapterByName(DAI_NAME_PURPOSE).isPresent()) + .map(doiAdapter -> doiAdapter.getDataAdapterByName(DAI_NAME_PURPOSE).getCurrentElem().getVal().stream() + .findFirst() + .map(tVal -> doiAdapter.updateDaiFromExtRef(getExtRefsByDesc(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) { + return getExtRefs().stream() + .filter(tExtRef -> tExtRef.isSetDesc() && tExtRef.getDesc().contains(desc)) + .toList(); + } } 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 082f820b7..c6f8a7fd8 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 @@ -9,6 +9,7 @@ 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.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.*; @@ -284,8 +285,7 @@ void getExtRefSourceInfo_shouldReturnEmptyList_whenExtRefMatchNoFCDA() { extRefInfo.setHolderLnClass(lnClass); //When - - List controlBlocks = SclService.getExtRefSourceInfo(scd, extRefInfo); + List controlBlocks = SclService.getExtRefSourceInfo(scd, extRefInfo); //Then assertThat(controlBlocks).isEmpty(); @@ -893,10 +893,10 @@ private static Stream sclProviderMissingRequiredObjects() { Tuple[] scl4Errors = new Tuple[]{Tuple.tuple("The LDevice doesn't have a DO @name='Mod'", "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; return Stream.of( - Arguments.of("MissingDOBeh",scl1, scl1Errors), - Arguments.of("MissingLDevicePrivate",scl2, scl2Errors), - Arguments.of("MissingLDevicePrivateAttribute",scl3, scl3Errors), - Arguments.of("MissingDOMod",scl4, scl4Errors) + Arguments.of("MissingDOBeh", scl1, scl1Errors), + Arguments.of("MissingLDevicePrivate", scl2, scl2Errors), + Arguments.of("MissingLDevicePrivateAttribute", scl3, scl3Errors), + Arguments.of("MissingDOMod", scl4, scl4Errors) ); } @@ -1027,25 +1027,81 @@ void updateLDeviceStatus_shouldReturnUpdatedFile() { } private Optional getLDeviceStatusValue(SCL scl, String iedName, String ldInst) { + return getValFromDaiName(scl, iedName, ldInst, "Mod", "stVal"); + } + + @ParameterizedTest(name = "{0}") + @CsvSource({ + "Test update setSrcRef Value,LD_WITH_1_InRef,InRef2,setSrcRef,IED_NAME1LD_WITH_1_InRef/PRANCR1.Do11.sdo11", + "Test update setSrcCB Value,LD_WITH_1_InRef,InRef2,setSrcCB,IED_NAME1LD_WITH_1_InRef/prefixANCR1.GSE1", + "Test update setSrcRef Value,LD_WITH_3_InRef,InRef3,setSrcRef,IED_NAME1LD_WITH_3_InRef/PRANCR1.Do11.sdo11", + "Test update setSrcCB Value,LD_WITH_3_InRef,InRef3,setSrcCB,IED_NAME1LD_WITH_3_InRef/prefixANCR1.GSE1", + "Test update setTstRef Value,LD_WITH_3_InRef,InRef3,setTstRef,IED_NAME1LD_WITH_3_InRef/PRANCR1.Do11.sdo11", + "Test update setTstCB Value,LD_WITH_3_InRef,InRef3,setTstCB,IED_NAME1LD_WITH_3_InRef/prefixANCR3.GSE3" + }) + void updateDoInRef_shouldReturnUpdatedFile(String testName, String ldInst, String doName, String daName, String expected) { + // Given + SCL givenScl = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml"); + + // When + SclReport sclReport = SclService.updateDoInRef(givenScl); + + // Then + assertThat(sclReport.isSuccess()).isTrue(); + assertThat(getValFromDaiName(sclReport.getSclRootAdapter().getCurrentElem(), "IED_NAME1", ldInst, doName, daName) + .map(TVal::getValue)) + .hasValue(expected); + } + + @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" + }) + void updateDoInRef_should_not_update_tst_DAI_When_only_1_ExtRef(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"); + + // When + SclReport sclReport = SclService.updateDoInRef(givenScl); + + // Then + assertThat(sclReport.isSuccess()).isTrue(); + assertThat(getValFromDaiName(sclReport.getSclRootAdapter().getCurrentElem(), "IED_NAME1", ldInst, doName, daName)).isNotPresent(); + } + + @Test + void updateDoInRef_shouldReturnReportWithError_when_ExtRef_not_coherent() { + // Given + SCL givenScl = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml"); + + // When + SclReport sclReport = SclService.updateDoInRef(givenScl); + + // Then + assertThat(sclReport.isSuccess()).isTrue(); + assertThat(sclReport.getSclReportItems()).isNotEmpty(); + assertThat(sclReport.getSclReportItems()).hasSize(4); + } + + private Optional getValFromDaiName(SCL scl, String iedName, String ldInst, String doiName, String daiName) { SclRootAdapter sclRootAdapter = new SclRootAdapter(scl); IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(iedName); Optional lDeviceAdapter = iedAdapter.findLDeviceAdapterByLdInst(ldInst); LN0Adapter ln0Adapter = lDeviceAdapter.get().getLN0Adapter(); Optional doiAdapter = ln0Adapter.getDOIAdapters().stream() - .filter(doiAdapter1 -> doiAdapter1.getCurrentElem().getName().equals("Mod")) + .filter(doiAdapter1 -> doiAdapter1.getCurrentElem().getName().equals(doiName)) .findFirst(); - if (doiAdapter.isEmpty()) return Optional.empty(); - return doiAdapter.get().getCurrentElem().getSDIOrDAI().stream() + return doiAdapter.flatMap(adapter -> adapter.getCurrentElem().getSDIOrDAI().stream() .filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class)) .map(TDAI.class::cast) - .filter(tdai -> tdai.getName().equals("stVal")) + .filter(tdai -> tdai.getName().equals(daiName) && !tdai.getVal().isEmpty()) .map(tdai -> tdai.getVal().get(0)) - .findFirst(); + .findFirst()); } - @Test - void analyzeDataGroups_should_success() throws Exception { + void analyzeDataGroups_should_success() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); @@ -1062,7 +1118,7 @@ void analyzeDataGroups_should_success() throws Exception { } @Test - void analyzeDataGroups_should_return_errors_messages() throws Exception { + void analyzeDataGroups_should_return_errors_messages() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java index 7873ff9a9..5577cd926 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java @@ -9,15 +9,14 @@ import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.DaTypeName; import org.lfenergy.compas.sct.commons.dto.DoTypeName; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; class DOIAdapterTest { @@ -25,137 +24,154 @@ class DOIAdapterTest { @Test void testConstructor() { LN0 ln0 = new LN0(); - LN0Adapter ln0Adapter = new LN0Adapter(null,ln0); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); TDOI tdoi = new TDOI(); tdoi.setName("Do"); ln0.getDOI().add(tdoi); // test amChildElement - DOIAdapter doiAdapter = assertDoesNotThrow(() -> new DOIAdapter(ln0Adapter,tdoi)); + DOIAdapter doiAdapter = assertDoesNotThrow(() -> new DOIAdapter(ln0Adapter, tdoi)); // test tree map TSDI tsdi = new TSDI(); tsdi.setName("sdo2"); tdoi.getSDIOrDAI().add(tsdi); - assertDoesNotThrow(() -> doiAdapter.getStructuredDataAdapterByName("sdo2")); - assertThrows(ScdException.class, () -> doiAdapter.getStructuredDataAdapterByName("sdo3")); + assertThatCode(() -> doiAdapter.getStructuredDataAdapterByName("sdo2")).doesNotThrowAnyException(); + assertThatThrownBy(() -> doiAdapter.getStructuredDataAdapterByName("sdo3")).isInstanceOf(ScdException.class); TDAI tdai = new TDAI(); tdai.setName("angRef"); tdoi.getSDIOrDAI().add(tdai); - assertDoesNotThrow(() -> doiAdapter.getDataAdapterByName("angRef")); - assertThrows(ScdException.class, () -> doiAdapter.getStructuredDataAdapterByName("bda")); - assertThrows(ScdException.class, () -> doiAdapter.getDataAdapterByName("bda")); + assertThatCode(() -> doiAdapter.getDataAdapterByName("angRef")).doesNotThrowAnyException(); + assertThatThrownBy(() -> doiAdapter.getStructuredDataAdapterByName("bda")).isInstanceOf(ScdException.class); + assertThatThrownBy(() -> doiAdapter.getDataAdapterByName("bda")).isInstanceOf(ScdException.class); } - @Test - void testInnerDAIAdapter(){ + void testInnerDAIAdapter() { + // Given final String TOTO = "toto"; - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","angRef"); + // When + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "angRef"); + + // Then assertNull(daiAdapter.isValImport()); daiAdapter.setValImport(true); assertTrue(daiAdapter.isValImport()); // test tree map - assertThrows(UnsupportedOperationException.class, () -> daiAdapter.getDataAdapterByName(TOTO)); - assertThrows( - UnsupportedOperationException.class, - () -> daiAdapter.getStructuredDataAdapterByName(TOTO) - ); + assertThatThrownBy(() -> daiAdapter.getDataAdapterByName(TOTO)).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(() -> daiAdapter.getStructuredDataAdapterByName(TOTO)).isInstanceOf(UnsupportedOperationException.class); - assertThrows(UnsupportedOperationException.class, () -> daiAdapter.addDAI(TOTO)); - assertThrows(UnsupportedOperationException.class, () -> daiAdapter.addSDOI(TOTO)); + assertThatThrownBy(() -> daiAdapter.addDAI(TOTO)).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(() -> daiAdapter.addSDOI(TOTO)).isInstanceOf(UnsupportedOperationException.class); } @Test - void testInnerDAIAdapterTestUpdateWithMapAsArg(){ + void testInnerDAIAdapterTestUpdateWithMapAsArg() { + // Given final String TOTO = "toto"; - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","da"); + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); daiAdapter.setValImport(true); // update DAI val - final Map vals = Collections.singletonMap(0L,TOTO); - assertDoesNotThrow(() -> daiAdapter.update(vals)); - assertFalse(daiAdapter.getCurrentElem().getVal().isEmpty()); + final Map vals = Collections.singletonMap(0L, TOTO); + assertThatCode(() -> daiAdapter.update(vals)).doesNotThrowAnyException(); + assertThat(daiAdapter.getCurrentElem().getVal()).isNotEmpty(); TVal tVal = daiAdapter.getCurrentElem().getVal().get(0); - assertFalse(tVal.isSetSGroup()); + assertThat(tVal.isSetSGroup()).isFalse(); + + final Map vals2 = new HashMap<>(); + vals2.put(1L, TOTO); + vals2.put(0L, TOTO); - final Map vals2 = new HashMap<>(); - vals2.put(1L,TOTO); - vals2.put(0L,TOTO); - assertDoesNotThrow(() -> daiAdapter.update(vals2)); - assertFalse(daiAdapter.getCurrentElem().getVal().isEmpty()); + // When Then + assertThatCode(() -> daiAdapter.update(vals2)).doesNotThrowAnyException(); + assertThat(daiAdapter.getCurrentElem().getVal()).isNotEmpty(); tVal = daiAdapter.getCurrentElem().getVal().get(0); - assertFalse(tVal.isSetSGroup()); + assertThat(tVal.isSetSGroup()).isFalse(); } @Test - void testInnerDAIAdapterTestUpdate(){ + void testInnerDAIAdapterTestUpdate() { + // Given final String TOTO = "toto"; - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","da"); + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); daiAdapter.setValImport(false); - assertThrows(ScdException.class,() -> daiAdapter.update(0L,TOTO) ); + assertThatThrownBy(() -> daiAdapter.update(0L, TOTO)).isInstanceOf(ScdException.class); daiAdapter.setValImport(true); - assertDoesNotThrow(() -> daiAdapter.update(0L,TOTO)); + assertThatCode(() -> daiAdapter.update(0L, TOTO)).doesNotThrowAnyException(); + + final Map vals2 = new HashMap<>(); + vals2.put(1L, TOTO); + vals2.put(2L, TOTO); - final Map vals2 = new HashMap<>(); - vals2.put(1L,TOTO); - vals2.put(2L,TOTO); - assertDoesNotThrow(() -> daiAdapter.update(vals2)); - vals2.put(2L,TOTO + "1"); - assertDoesNotThrow(() -> daiAdapter.update(vals2)); + // When Then + assertThatCode(() -> daiAdapter.update(vals2)).doesNotThrowAnyException(); + vals2.put(2L, TOTO + "1"); + assertThatCode(() -> daiAdapter.update(vals2)).doesNotThrowAnyException(); } @Test void testFindDeepestMatch() throws Exception { + // Given SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); - DOIAdapter doiAdapter = assertDoesNotThrow(()-> ln0Adapter.getDOIAdapterByName("Do")); + DOIAdapter doiAdapter = assertDoesNotThrow(() -> ln0Adapter.getDOIAdapterByName("Do")); DoTypeName doTypeName = new DoTypeName("Do.sdo1.d"); DaTypeName daTypeName = new DaTypeName("antRef.bda1.bda2.bda3"); - Pair pair = doiAdapter.findDeepestMatch( - doTypeName.getStructNames(),0,false + Pair pair = doiAdapter.findDeepestMatch( + doTypeName.getStructNames(), 0, false ); SDIAdapter lastSDOIAdapter = (SDIAdapter) pair.getLeft(); - assertEquals(1,pair.getRight()); - assertNotNull(lastSDOIAdapter); - assertEquals(SDIAdapter.class,lastSDOIAdapter.getClass()); + assertThat(pair.getRight()).isEqualTo(1); + assertThat(lastSDOIAdapter) + .isNotNull() + .isInstanceOf(SDIAdapter.class); SDIAdapter firstDAIAdapter = lastSDOIAdapter.getStructuredDataAdapterByName(daTypeName.getName()); + + // When pair = firstDAIAdapter.findDeepestMatch( - daTypeName.getStructNames(),0,true + daTypeName.getStructNames(), 0, true ); - assertEquals(2,pair.getRight()); - assertNotNull(pair.getLeft()); - assertEquals(SDIAdapter.DAIAdapter.class,pair.getLeft().getClass()); + + // Then + assertThat(pair.getRight()).isEqualTo(2); + assertThat(pair.getLeft()).isNotNull(); + assertThat(pair.getLeft()).isInstanceOf(SDIAdapter.DAIAdapter.class); } - private DOIAdapter.DAIAdapter initInnerDAIAdapter(String doName, String daName){ + private DOIAdapter.DAIAdapter initInnerDAIAdapter(String doName, String daName) { TDOI tdoi = new TDOI(); tdoi.setName(doName); - DOIAdapter doiAdapter = new DOIAdapter(null,tdoi); + DOIAdapter doiAdapter = new DOIAdapter(null, tdoi); TDAI tdai = new TDAI(); tdai.setName(daName); tdoi.getSDIOrDAI().add(tdai); - DOIAdapter.DAIAdapter daiAdapter = assertDoesNotThrow(() -> new DOIAdapter.DAIAdapter(doiAdapter,tdai)); + DOIAdapter.DAIAdapter daiAdapter = assertDoesNotThrow(() -> new DOIAdapter.DAIAdapter(doiAdapter, tdai)); return daiAdapter; } @Test void addPrivate() { - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","da"); + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); TPrivate tPrivate = new TPrivate(); tPrivate.setType("Private Type"); tPrivate.setSource("Private Source"); - assertTrue(daiAdapter.getCurrentElem().getPrivate().isEmpty()); + assertThat(daiAdapter.getCurrentElem().getPrivate()).isEmpty(); + + // When daiAdapter.addPrivate(tPrivate); - assertEquals(1, daiAdapter.getCurrentElem().getPrivate().size()); + + // Then + assertThat(daiAdapter.getCurrentElem().getPrivate()).hasSize(1); } @Test @@ -163,8 +179,8 @@ void elementXPath_doi() { // Given TDOI tdoi = new TDOI(); tdoi.setName("doName"); - DOIAdapter doiAdapter = new DOIAdapter(null,new TDOI()); - DOIAdapter namedDoiAdapter = new DOIAdapter(null,tdoi); + DOIAdapter doiAdapter = new DOIAdapter(null, new TDOI()); + DOIAdapter namedDoiAdapter = new DOIAdapter(null, tdoi); // When String elementXPathResult = doiAdapter.elementXPath(); String namedElementXPathResult = namedDoiAdapter.elementXPath(); @@ -176,11 +192,311 @@ void elementXPath_doi() { @Test void elementXPath_dai() { // Given - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","da"); + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); // When String result = daiAdapter.elementXPath(); // Then assertThat(result).isEqualTo("DAI[@name=\"da\"]"); } + + @Test + void findDataAdapterByName_should_return_DAIAdapter_when_DA_name_exist() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + + // When + Optional result = doiAdapter.findDataAdapterByName("da"); + + // Then + assertThat(result) + .isPresent() + .map(daiAdapter1 -> daiAdapter1.getCurrentElem().getName()) + .isEqualTo(Optional.of("da")); + } + + @Test + void findDataAdapterByName_should_return_DAIAdapter_when_DA_name_dont_exist() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + + // When + Optional result = doiAdapter.findDataAdapterByName("wrong"); + + // Then + assertThat(result).isEmpty(); + } + + @Test + void updateDaiFromExtRef_should_update_setSrcXX_values_when_ExtRef_desc_suffix_ends_with_1() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + + TExtRef extRef1 = givenExtRef(1, true); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/SRC_PREFIX_1LLN0SRC_LN_INST_1.CB_NAME_1"); + } + + private static Optional getDaiValOfDoi(DOIAdapter doiAdapter, String daName) { + return doiAdapter.getDataAdapterByName(daName).getCurrentElem().getVal().stream().findFirst(); + } + + @Test + void updateDaiFromExtRef_should_update_setSrcRef_value_but_not_setSrcCB_when_ExtRef_dont_contains_CB() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + + TExtRef extRef1 = givenExtRef(1, false); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + assertThatThrownBy(() -> getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB)).isInstanceOf(ScdException.class); + } + + @Test + void updateDaiFromExtRef_should_update_setSrcXX_and_setTstXX_values_when_ExtRef_desc_suffix_ends_with_1_and_3() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + TDAI daiTstRef = new TDAI(); + daiTstRef.setName(DOIAdapter.DA_NAME_SET_TST_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstRef); + TDAI daiTstCb = new TDAI(); + daiTstCb.setName(DOIAdapter.DA_NAME_SET_TST_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstCb); + + TExtRef extRef1 = givenExtRef(1, true); + TExtRef extRef3 = givenExtRef(3, true); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1, extRef3)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/SRC_PREFIX_1LLN0SRC_LN_INST_1.CB_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF).get().getValue()) + .isEqualTo("IED_NAME_3LD_INST_3/PREFIX_3ANCR3.DO_NAME_3"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_CB).get().getValue()) + .isEqualTo("IED_NAME_3LD_INST_3/SRC_PREFIX_3LLN0SRC_LN_INST_3.CB_NAME_3"); + } + + private static TExtRef givenExtRef(int num, boolean withCbName) { + TExtRef extRef1 = new TExtRef(); + extRef1.setIedName("IED_NAME_" + num); + extRef1.setDesc("ExtRef_desc_" + num); + extRef1.setLdInst("LD_INST_" + num); + extRef1.setSrcPrefix("SRC_PREFIX_" + num); + extRef1.setSrcLNInst("SRC_LN_INST_" + num); + extRef1.getLnClass().add("ANCR"); + extRef1.setLnInst(Integer.toString(num)); + extRef1.setPrefix("PREFIX_" + num); + extRef1.setDoName("DO_NAME_" + num); + if (withCbName) { + extRef1.setSrcCBName("CB_NAME_" + num); + } + return extRef1; + } + + @Test + void updateDaiFromExtRef_should_update_only_setSrcRef_and_setTstRef_values_when_ExtRef_desc_suffix_ends_with_1_and_3_without_CB() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + TDAI daiTstRef = new TDAI(); + daiTstRef.setName(DOIAdapter.DA_NAME_SET_TST_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstRef); + TDAI daiTstCb = new TDAI(); + daiTstCb.setName(DOIAdapter.DA_NAME_SET_TST_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstCb); + + TExtRef extRef1 = givenExtRef(1, false); + TExtRef extRef3 = givenExtRef(3, false); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1, extRef3)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB)) + .isNotPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF).get().getValue()) + .isEqualTo("IED_NAME_3LD_INST_3/PREFIX_3ANCR3.DO_NAME_3"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_CB)) + .isNotPresent(); + } + + @Test + void updateDaiFromExtRef_should_return_warning_report_when_none_ExtRef_endin_with_1() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + + TExtRef extRef3 = givenExtRef(3, false); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef3)); + + // Then + assertThat(sclReportItems) + .isPresent() + .isNotEmpty(); + assertThat(sclReportItems.get().getMessage()) + .contains("can't be bound with an ExtRef"); + assertThat(doiAdapter.getDataAdapterByName(DOIAdapter.DA_NAME_SET_SRC_REF)).isNotNull(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)).isNotPresent(); + } + + @Test + void updateDaiFromExtRef_should_create_DAI_when_no_DAI_name_setSrcRef() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + + TExtRef extRef1 = givenExtRef(1, false); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(doiAdapter.getDataAdapterByName(DOIAdapter.DA_NAME_SET_SRC_REF)).isNotNull(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)) + .isPresent() + .map(TVal::getValue) + .contains("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + } + + @Test + void updateDaiFromExtRef_should_return_filled_ReportItem_when_no_ExtRef_in_LNode() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of()); + + // Then + assertThat(sclReportItems) + .isPresent() + .isNotEmpty(); + assertThat(sclReportItems.get().getMessage()).contains("can't be bound with an ExtRef"); + } + + @Test + void updateDaiFromExtRef_should_compose_correct_name_when_optional_ExtRef_attributes_are_missing() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + TDAI daiTstRef = new TDAI(); + daiTstRef.setName(DOIAdapter.DA_NAME_SET_TST_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstRef); + TDAI daiTstCb = new TDAI(); + daiTstCb.setName(DOIAdapter.DA_NAME_SET_TST_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstCb); + + TExtRef extRef1 = new TExtRef(); + extRef1.setDesc("ExtRef_desc_1"); + extRef1.setIedName("IED_NAME_1"); + extRef1.setLdInst("LD_INST_1"); + extRef1.getLnClass().add("LN_CLASS_1"); + extRef1.setDoName("DO_NAME_1"); + TExtRef extRef3 = new TExtRef(); + extRef3.setDesc("ExtRef_desc_3"); + extRef3.setIedName("IED_NAME_3"); + extRef3.setLdInst("LD_INST_3"); + extRef3.getLnClass().add("LN_CLASS_3"); + extRef3.setDoName("DO_NAME_3"); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1, extRef3)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/LN_CLASS_1.DO_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB)) + .isNotPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF).get().getValue()) + .isEqualTo("IED_NAME_3LD_INST_3/LN_CLASS_3.DO_NAME_3"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_CB)) + .isNotPresent(); + } + + @Test + void updateDaiFromExtRef_should_throw_exception_when_ExtRef_desc_dont_end_with__1() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + + TExtRef extRef1 = new TExtRef(); + extRef1.setDesc("ExtRefDesc"); + List extRefList = List.of(extRef1); + + // When Then + assertThatThrownBy(() -> doiAdapter.updateDaiFromExtRef(extRefList)) + .isInstanceOf(NumberFormatException.class); + } } 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 859fb020b..e48d8d48e 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 @@ -7,6 +7,7 @@ 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.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.lfenergy.compas.scl2007b4.model.*; @@ -29,6 +30,7 @@ import static org.junit.jupiter.api.Named.named; import static org.lfenergy.compas.scl2007b4.model.TSampledValueControl.SmvOpts; import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findLn0; +import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.getDaiValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -46,11 +48,11 @@ void testAmChildElementRef() throws ScdException { LN0 ln0 = new LN0(); ln0.setLnType("LT1"); when(tlDevice.getLN0()).thenReturn(ln0); - LN0Adapter ln0Adapter = assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + LN0Adapter ln0Adapter = assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); - assertEquals(LN0.class,ln0Adapter.getElementClassType()); - assertEquals("LT1",ln0Adapter.getLnType()); - assertEquals(TLLN0Enum.LLN_0.value(),ln0Adapter.getLNClass()); + assertEquals(LN0.class, ln0Adapter.getElementClassType()); + assertEquals("LT1", ln0Adapter.getLnType()); + assertEquals(TLLN0Enum.LLN_0.value(), ln0Adapter.getLNClass()); assertFalse(ln0Adapter.hasInputs()); ln0.setInputs(new TInputs()); assertTrue(ln0Adapter.hasInputs()); @@ -63,7 +65,7 @@ void testAmChildElementRef() throws ScdException { assertTrue(ln0Adapter.getCurrentElem().getReportControl().isEmpty()); LN0 ln01 = new LN0(); - assertThrows(IllegalArgumentException.class, () -> new LN0Adapter(lDeviceAdapter, ln01)); + assertThrows(IllegalArgumentException.class, () -> new LN0Adapter(lDeviceAdapter, ln01)); } // AbstractLNAdapter class test @@ -74,8 +76,9 @@ void containsFCDA() { when(lDeviceAdapter.getCurrentElem()).thenReturn(tlDevice); LN0 ln0 = new LN0(); when(tlDevice.getLN0()).thenReturn(ln0); - assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); } + @Test void isExtRefExist_shouldThrowScdException_whenNoInputsInLN0() { //Given @@ -114,7 +117,7 @@ void isExtRefExist_shouldThrowScdException_whenSignalNotValid() { @Test - void isExtRefExist_shouldThrowScdException_whenSignalNull(){ + void isExtRefExist_shouldThrowScdException_whenSignalNull() { //Given LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); TLDevice tlDevice = mock(TLDevice.class); @@ -125,7 +128,7 @@ void isExtRefExist_shouldThrowScdException_whenSignalNull(){ tInputs.getExtRef().add(extRef); ln0.setInputs(tInputs); when(tlDevice.getLN0()).thenReturn(ln0); - LN0Adapter ln0Adapter = assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + LN0Adapter ln0Adapter = assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); //When Then assertThatThrownBy(() -> ln0Adapter.isExtRefExist(null)) .isInstanceOf(ScdException.class) @@ -133,7 +136,7 @@ void isExtRefExist_shouldThrowScdException_whenSignalNull(){ } @Test - void isExtRefExist_shouldThrowScdException_whenNotExistInTargetLN(){ + void isExtRefExist_shouldThrowScdException_whenNotExistInTargetLN() { //Given LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); TLDevice tlDevice = mock(TLDevice.class); @@ -153,7 +156,7 @@ void isExtRefExist_shouldThrowScdException_whenNotExistInTargetLN(){ .hasMessage("ExtRef signal does not exist in target LN"); } - @Test + @Test void isExtRefExist_shouldNotThrowException_whenExtRefExist() { //Given LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); @@ -195,13 +198,13 @@ void isExtRefExist_shouldNotThrowException_whenExtRefExistWithPDA() { } @Test - void testGetDataSetWith(){ + void testGetDataSetWith() { LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); TLDevice tlDevice = mock(TLDevice.class); when(lDeviceAdapter.getCurrentElem()).thenReturn(tlDevice); LN0 ln0 = new LN0(); when(tlDevice.getLN0()).thenReturn(ln0); - LN0Adapter ln0Adapter = assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + LN0Adapter ln0Adapter = assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); TDataSet tDataSet = new TDataSet(); ln0.getDataSet().add(tDataSet); @@ -223,7 +226,7 @@ void testGetDataSetWith(){ } @Test - void testGetControlBlocks(){ + void testGetControlBlocks() { LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); IEDAdapter iedAdapter = mock(IEDAdapter.class); TLDevice tlDevice = mock(TLDevice.class); @@ -232,7 +235,7 @@ void testGetControlBlocks(){ when(iedAdapter.getName()).thenReturn("IED_NAME"); LN0 ln0 = new LN0(); when(tlDevice.getLN0()).thenReturn(ln0); - LN0Adapter ln0Adapter = assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + LN0Adapter ln0Adapter = assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); TGSEControl tgseControl = new TGSEControl(); tgseControl.setDatSet("GSE_REF"); TSampledValueControl tSampledValueControl = new TSampledValueControl(); @@ -246,7 +249,7 @@ void testGetControlBlocks(){ TDataSet tDataSetGSE = new TDataSet(); tDataSetGSE.setName(DTO.CB_DATASET_REF); - List controlBlocks = ln0Adapter.getControlBlocks(List.of(tDataSetGSE),null); + List controlBlocks = ln0Adapter.getControlBlocks(List.of(tDataSetGSE), null); assertTrue(controlBlocks.isEmpty()); tDataSetGSE.setName("GSE_REF"); @@ -257,18 +260,18 @@ void testGetControlBlocks(){ List tDataSets = List.of(tDataSetGSE, tDataSetSMV, tDataSetRPT); - controlBlocks = ln0Adapter.getControlBlocks(tDataSets,TServiceType.REPORT); + controlBlocks = ln0Adapter.getControlBlocks(tDataSets, TServiceType.REPORT); assertThat(controlBlocks).hasSize(1); - controlBlocks = ln0Adapter.getControlBlocks(tDataSets,TServiceType.SMV); + controlBlocks = ln0Adapter.getControlBlocks(tDataSets, TServiceType.SMV); assertThat(controlBlocks).hasSize(1); - controlBlocks = ln0Adapter.getControlBlocks(tDataSets,TServiceType.GOOSE); + controlBlocks = ln0Adapter.getControlBlocks(tDataSets, TServiceType.GOOSE); assertThat(controlBlocks).hasSize(1); - controlBlocks = ln0Adapter.getControlBlocks(tDataSets,null); + controlBlocks = ln0Adapter.getControlBlocks(tDataSets, null); assertThat(controlBlocks).hasSize(3); } @Test - void testGetControlSetByBindingInfo(){ + void testGetControlSetByBindingInfo() { LN0 ln0 = new LN0(); LN0Adapter ln0Adapter = mock(LN0Adapter.class); @@ -287,17 +290,17 @@ void testGetControlSetByBindingInfo(){ Mockito.doReturn(List.of(new ReportControlBlock("rpt", "rptID", "rptDatSet"))) .when(ln0Adapter).getControlBlocks( - any(List.class), any(TServiceType.class)); + any(List.class), any(TServiceType.class)); - List controlBlocks = ln0Adapter.getControlBlocksForMatchingFCDA(extRefBindingInfo); + List controlBlocks = ln0Adapter.getControlBlocksForMatchingFCDA(extRefBindingInfo); assertFalse(controlBlocks.isEmpty()); - assertEquals(TServiceType.REPORT,controlBlocks.get(0).getServiceType()); + assertEquals(TServiceType.REPORT, controlBlocks.get(0).getServiceType()); } @Test - void testGetDOIAdapters(){ + void testGetDOIAdapters() { LN0 ln0 = new LN0(); - LN0Adapter ln0Adapter = new LN0Adapter(null,ln0); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); TDOI tdoi = new TDOI(); tdoi.setName("Do"); @@ -307,7 +310,7 @@ void testGetDOIAdapters(){ } @Test - void testGetDOIAdapterByName(){ + void testGetDOIAdapterByName() { IEDAdapter iedAdapter = mock(IEDAdapter.class); TIED tied = new TIED(); when(iedAdapter.getCurrentElem()).thenReturn(tied); @@ -320,7 +323,7 @@ void testGetDOIAdapterByName(){ LN0 ln0 = new LN0(); tlDevice.setLN0(ln0); - LN0Adapter ln0Adapter = new LN0Adapter(lDeviceAdapter,ln0); + LN0Adapter ln0Adapter = new LN0Adapter(lDeviceAdapter, ln0); TDOI tdoi = new TDOI(); tdoi.setName("Do"); @@ -334,16 +337,16 @@ void testFindMatch() { SCL scd = SclTestMarshaller.getSCLFromFile(SCD_IED_U_TEST); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); DoTypeName doTypeName = new DoTypeName("Do.sdo1.d"); DaTypeName daTypeName = new DaTypeName("antRef.bda1.bda2.bda3"); - AbstractDAIAdapter daiAdapter = (AbstractDAIAdapter) assertDoesNotThrow(() -> ln0Adapter.findMatch(doTypeName,daTypeName).get()); - assertEquals("bda3",daiAdapter.getCurrentElem().getName()); - assertEquals("Completed-diff",daiAdapter.getCurrentElem().getVal().get(0).getValue()); + AbstractDAIAdapter daiAdapter = (AbstractDAIAdapter) assertDoesNotThrow(() -> ln0Adapter.findMatch(doTypeName, daTypeName).get()); + assertEquals("bda3", daiAdapter.getCurrentElem().getName()); + assertEquals("Completed-diff", daiAdapter.getCurrentElem().getVal().get(0).getValue()); DoTypeName doTypeName2 = new DoTypeName("Do.sdo1"); - assertFalse(ln0Adapter.findMatch(doTypeName2,daTypeName).isPresent()); + assertFalse(ln0Adapter.findMatch(doTypeName2, daTypeName).isPresent()); } @ParameterizedTest @@ -399,7 +402,7 @@ void hasControlBlock_when_wrong_controlBlockEnum_should_return_false() { void addPrivate() { LN0 tln = new LN0(); tln.getLnClass().add(TLLN0Enum.LLN_0.value()); - LN0Adapter lnAdapter = new LN0Adapter(null,tln); + LN0Adapter lnAdapter = new LN0Adapter(null, tln); TPrivate tPrivate = new TPrivate(); tPrivate.setType("Private Type"); tPrivate.setSource("Private Source"); @@ -415,7 +418,7 @@ void testGetDAI() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.findLDeviceAdapterByLdInst("LDSUIED").get()); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LDSUIED").get()); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ResumedDataTemplate filter = new ResumedDataTemplate(); filter.setLnClass(ln0Adapter.getLNClass()); @@ -429,10 +432,10 @@ void testGetDAI() { daTypeName.setFc(TFCEnum.ST); filter.setDaName(daTypeName); //When - var rDtts = ln0Adapter.getDAI(filter,false); + var rDtts = ln0Adapter.getDAI(filter, false); //Then assertFalse(rDtts.isEmpty()); - assertEquals(1,rDtts.size()); + assertEquals(1, rDtts.size()); assertNotNull(rDtts.get(0).getDaName().getType()); assertEquals("BehaviourModeKind", rDtts.get(0).getDaName().getType()); } @@ -443,7 +446,7 @@ void getEnumValue_shouldReturnNothing_whenEnumUnknow() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); //When Set enumValues = ln0Adapter.getEnumValues("Behaviour"); @@ -457,7 +460,7 @@ void getEnumValue_shouldReturnEnumValues_whenEnumKnown() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.findLDeviceAdapterByLdInst("LDSUIED").get()); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LDSUIED").get()); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); //When Set enumValues = ln0Adapter.getEnumValues("BehaviourModeKind"); @@ -473,7 +476,7 @@ void addControlBlock_should_add_ControlBlock(ControlBlock controlBlock) { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists(controlBlock.getDataSetRef(), controlBlock.getControlBlockEnum()); int initialControlBlockCount = ln0Adapter.getTControlsByType(controlBlock.getControlBlockEnum().getControlBlockClass()).size(); @@ -489,7 +492,7 @@ void addControlBlock_should_add_ReportControlBlock() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("rptDatSet", ControlBlockEnum.REPORT); ReportControlBlock reportControlBlock = new ReportControlBlock("rpt", "rptID", "rptDatSet"); @@ -497,7 +500,7 @@ void addControlBlock_should_add_ReportControlBlock() { //When ln0Adapter.addControlBlock(reportControlBlock); //Then - assertThat(ln0Adapter.getCurrentElem().getReportControl()).hasSize(reportCBInitSize+1); + assertThat(ln0Adapter.getCurrentElem().getReportControl()).hasSize(reportCBInitSize + 1); } @Test @@ -506,7 +509,7 @@ void addControlBlock_should_add_GooseControlBlock() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("datSet", ControlBlockEnum.GSE); GooseControlBlock gooseControlBlock = new GooseControlBlock("gse", "gseID", "datSet"); @@ -524,7 +527,7 @@ void addControlBlock_should_add_SMVControlBlock() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("smvDatSet", ControlBlockEnum.SAMPLED_VALUE); SMVControlBlock smvControlBlock = new SMVControlBlock("smv", "smvID", "smvDatSet"); @@ -532,7 +535,7 @@ void addControlBlock_should_add_SMVControlBlock() { //When ln0Adapter.addControlBlock(smvControlBlock); //Then - assertThat(ln0Adapter.getCurrentElem().getSampledValueControl()).hasSize(reportCBInitSize+1); + assertThat(ln0Adapter.getCurrentElem().getSampledValueControl()).hasSize(reportCBInitSize + 1); } @ParameterizedTest @@ -542,14 +545,14 @@ void addControlBlock_when_accessPoint_does_not_have_capability_should_throw_exce SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("dataSet", ControlBlockEnum.REPORT); ln0Adapter.getParentLDevice().getAccessPoint().setServices(new TServices()); //When & Then assertThatThrownBy(() -> ln0Adapter.addControlBlock(controlBlock)) - .isInstanceOf(ScdException.class) - .hasMessageContaining("because IED/AccessPoint does not have capability to create ControlBlock"); + .isInstanceOf(ScdException.class) + .hasMessageContaining("because IED/AccessPoint does not have capability to create ControlBlock"); } @ParameterizedTest @@ -559,14 +562,14 @@ void addControlBlock_when_controlBlock_already_exists_should_throw_exception(Con SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("dataSet", ControlBlockEnum.REPORT); ln0Adapter.addControlBlock(controlBlock); //When & Then assertThatThrownBy(() -> ln0Adapter.addControlBlock(controlBlock)) - .isInstanceOf(ScdException.class) - .hasMessageContaining("because it already exists"); + .isInstanceOf(ScdException.class) + .hasMessageContaining("because it already exists"); } @ParameterizedTest @@ -576,19 +579,19 @@ void addControlBlock_when_dataSet_does_not_exist_should_throw_exception(ControlB SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); //When & Then assertThatThrownBy(() -> ln0Adapter.addControlBlock(controlBlock)) - .isInstanceOf(ScdException.class) - .hasMessageContaining("because target DataSet dataSet does not exists"); + .isInstanceOf(ScdException.class) + .hasMessageContaining("because target DataSet dataSet does not exists"); } - private static Stream provideControlBlocks(){ + private static Stream provideControlBlocks() { return Stream.of( - Arguments.of(named("ReportControlBlock", new ReportControlBlock("name", "id", "dataSet"))), - Arguments.of(named("GooseControlBlock", new GooseControlBlock("name", "id", "dataSet"))), - Arguments.of(named("SMVControlBlock", new SMVControlBlock("name", "id", "dataSet"))) + Arguments.of(named("ReportControlBlock", new ReportControlBlock("name", "id", "dataSet"))), + Arguments.of(named("GooseControlBlock", new GooseControlBlock("name", "id", "dataSet"))), + Arguments.of(named("SMVControlBlock", new SMVControlBlock("name", "id", "dataSet"))) ); } @@ -604,11 +607,11 @@ void getTControlsByType_should_return_LN0_list_of_controls_for_this_class(Class< assertThat(controlList).isSameAs(getter.apply(ln0Adapter.getCurrentElem())); } - private static Stream provideGetTControlsByType(){ + private static Stream provideGetTControlsByType() { return Stream.of( - Arguments.of(TGSEControl.class, (Function>) LN0::getGSEControl), - Arguments.of(TSampledValueControl.class, (Function>) LN0::getSampledValueControl), - Arguments.of(TReportControl.class, (Function>) LN0::getReportControl) + Arguments.of(TGSEControl.class, (Function>) LN0::getGSEControl), + Arguments.of(TSampledValueControl.class, (Function>) LN0::getSampledValueControl), + Arguments.of(TReportControl.class, (Function>) LN0::getReportControl) ); } @@ -617,7 +620,7 @@ void elementXPath() { // Given LN0 tln = new LN0(); tln.getLnClass().add(TLLN0Enum.LLN_0.value()); - LN0Adapter lnAdapter = new LN0Adapter(null,tln); + LN0Adapter lnAdapter = new LN0Adapter(null, tln); // When String result = lnAdapter.elementXPath(); // Then @@ -630,18 +633,18 @@ void getLDeviceStatus_should_succeed() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); Optional optionalLN0Adapter = sclRootAdapter.streamIEDAdapters() - .flatMap(IEDAdapter::streamLDeviceAdapters) - .filter(lDeviceAdapter -> "IED_NAME1LD_INST13".equals(lDeviceAdapter.getLdName())) - .map(LDeviceAdapter::getLN0Adapter) - .findFirst(); + .flatMap(IEDAdapter::streamLDeviceAdapters) + .filter(lDeviceAdapter -> "IED_NAME1LD_INST13".equals(lDeviceAdapter.getLdName())) + .map(LDeviceAdapter::getLN0Adapter) + .findFirst(); assertThat(optionalLN0Adapter).isPresent(); LN0Adapter ln0Adapter = optionalLN0Adapter.get(); // When Optional result = ln0Adapter.getLDeviceStatus(); // Then assertThat(result) - .isPresent() - .hasValue("test"); + .isPresent() + .hasValue("test"); } @Test @@ -656,8 +659,8 @@ void createDataSetIfNotExists_should_create_dataSet() { assertThat(newDataSet.getCurrentElem().getName()).isEqualTo("newDataSet"); assertThat(newDataSet.getParentAdapter().getParentAdapter().getInst()).isEqualTo("LD_INST11"); assertThat(ln0.getCurrentElem().getDataSet()) - .map(TDataSet::getName) - .containsExactly("newDataSet"); + .map(TDataSet::getName) + .containsExactly("newDataSet"); } @Test @@ -670,8 +673,8 @@ void createDataSetIfNotExists_when_dataset_exists_should_not_create_dataset() { DataSetAdapter newDataSet = ln0.createDataSetIfNotExists("existingDataSet", ControlBlockEnum.GSE); // Then assertThat(ln0.getCurrentElem().getDataSet()).hasSize(1) - .map(TDataSet::getName) - .containsExactly("existingDataSet"); + .map(TDataSet::getName) + .containsExactly("existingDataSet"); assertThat(newDataSet.getCurrentElem().getName()).isEqualTo("existingDataSet"); assertThat(newDataSet.getParentAdapter().getParentAdapter().getInst()).isEqualTo("LD_INST12"); } @@ -683,7 +686,7 @@ void createDataSetIfNotExists_when_ied_does_not_have_creation_capabilities_shoul LN0Adapter ln0 = findLn0(new SclRootAdapter(scd), "IED_NAME2", "LD_INST21"); // When & Then assertThatThrownBy(() -> ln0.createDataSetIfNotExists("existingDataSet", ControlBlockEnum.GSE)) - .isInstanceOf(ScdException.class); + .isInstanceOf(ScdException.class); } @Test @@ -700,11 +703,11 @@ void createControlBlockIfNotExists_should_create_GSEControl() { sourceLn0.createControlBlockIfNotExists(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, ControlBlockEnum.GSE); // Then assertThat(sourceLn0.getCurrentElem().getGSEControl()) - .hasSize(1) - .first().extracting(TControl::getName, TGSEControl::getAppID, TControl::getDatSet, - TGSEControl::getType, TGSEControl::isFixedOffs, TGSEControl::getSecurityEnable, TControlWithIEDName::getConfRev) + .hasSize(1) + .first().extracting(TControl::getName, TGSEControl::getAppID, TControl::getDatSet, + TGSEControl::getType, TGSEControl::isFixedOffs, TGSEControl::getSecurityEnable, TControlWithIEDName::getConfRev) .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, - TGSEControlTypeEnum.GOOSE, false, TPredefinedTypeOfSecurityEnum.NONE, 10000L); + TGSEControlTypeEnum.GOOSE, false, TPredefinedTypeOfSecurityEnum.NONE, 10000L); } @Test @@ -721,18 +724,18 @@ void createControlBlockIfNotExists_should_create_SampledValueControl() { sourceLn0.createControlBlockIfNotExists(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, ControlBlockEnum.SAMPLED_VALUE); // Then assertThat(sourceLn0.getCurrentElem().getSampledValueControl()) - .hasSize(1); + .hasSize(1); TSampledValueControl tSampledValueControl = sourceLn0.getCurrentElem().getSampledValueControl().get(0); assertThat(tSampledValueControl) - .extracting(TControl::getName, TSampledValueControl::getSmvID, TControl::getDatSet, - TSampledValueControl::isMulticast, TSampledValueControl::getSmpRate, TSampledValueControl::getNofASDU, TSampledValueControl::getSmpMod, TSampledValueControl::getSecurityEnable, - TControlWithIEDName::getConfRev) - .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, - true, 4800L, 2L, TSmpMod.SMP_PER_SEC, TPredefinedTypeOfSecurityEnum.NONE, 10000L); + .extracting(TControl::getName, TSampledValueControl::getSmvID, TControl::getDatSet, + TSampledValueControl::isMulticast, TSampledValueControl::getSmpRate, TSampledValueControl::getNofASDU, TSampledValueControl::getSmpMod, TSampledValueControl::getSecurityEnable, + TControlWithIEDName::getConfRev) + .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, + true, 4800L, 2L, TSmpMod.SMP_PER_SEC, TPredefinedTypeOfSecurityEnum.NONE, 10000L); assertThat(tSampledValueControl.getSmvOpts()) - .extracting(SmvOpts::isRefreshTime, SmvOpts::isSampleSynchronized, SmvOpts::isSampleRate, SmvOpts::isDataSet, - SmvOpts::isSecurity, SmvOpts::isTimestamp) - .containsExactly(false, true, true, false, false, false); + .extracting(SmvOpts::isRefreshTime, SmvOpts::isSampleSynchronized, SmvOpts::isSampleRate, SmvOpts::isDataSet, + SmvOpts::isSecurity, SmvOpts::isTimestamp) + .containsExactly(false, true, true, false, false, false); } @Test @@ -858,4 +861,142 @@ void getFCDAs_should_throw_Exception_when_DataSet_not_present() { .hasMessage("Control Block smv1 not found in /LN0"); } + @ParameterizedTest(name = "{0}") + @CsvSource({ + "Case without InRef,LD_WITHOUT_InRef,InRef1", + "Case with no InRef finishing with _1,LD_WITH_1_Bad_InRef,InRef4", + "Case with several ExtRef desc finishing with _1,LD_WITH_2_InRef_same_SUFFIX,InRef5" + }) + void updateDoInRef_should_return_error_message(String testName, String ldInst, String doiName) { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + LN0Adapter sourceLn0 = findLn0(new SclRootAdapter(scd), "IED_NAME1", ldInst); + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + assertThat(sclReportItems).hasSize(1) + .extracting(SclReportItem::getMessage) + .containsExactly("The DOI /SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"" + ldInst + "\"]/LN0/DOI[@name=\"" + doiName + "\"] can't be bound with an ExtRef"); + } + + @Test + void updateDoInRef_should_return_error_message_when_no_Val_in_DAI_purpose() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + LN0Adapter sourceLn0 = findLn0(new SclRootAdapter(scd), "IED_NAME1", "LD_Without_Val_in_DAI_purpose"); + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + assertThat(sclReportItems).hasSize(1) + .extracting(SclReportItem::getMessage) + .containsExactly("The DOI /SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"" + "LD_Without_Val_in_DAI_purpose" + "\"]/LN0 can't be bound with an ExtRef"); + } + + @Test + void updateDoInRef_should_not_treat_LN0_when_DAI_name_purpose_not_compliant() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + LN0Adapter sourceLn0 = findLn0(new SclRootAdapter(scd), "IED_NAME1", "LD_Without_purpose"); + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + DOIAdapter.DAIAdapter finalSetSrcRef = sourceLn0.getDOIAdapterByName("InRef6").getDataAdapterByName(DOIAdapter.DA_NAME_SET_SRC_REF); + assertThat(finalSetSrcRef.getCurrentElem().isSetVal()).isFalse(); + assertThat(sclReportItems).isEmpty(); + } + + @Test + void updateDoInRef_should_update_setSrcRef_and_not_setSrcCB_when_one_ExtRef_desc_matches() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + LN0Adapter sourceLn0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_WITH_1_InRef_without_cbName"); + String doiNameInRef = "InRef7"; + List daiValList = sourceLn0.getDOIAdapterByName(doiNameInRef).getDataAdapterByName(DOIAdapter.DA_NAME_SET_SRC_REF).getCurrentElem().getVal(); + String originalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + String expectedSrcRef = "IED_NAME1LD_WITH_1_InRef/PRANCR1.Do11.sdo11"; + + assertThat(daiValList).isEmpty(); + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + String finalSetSrcRef = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_REF); + String finalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + assertThat(finalSetSrcRef) + .isNotBlank() + .isEqualTo(expectedSrcRef); + assertThat(finalSetSrcCB).isEqualTo(originalSetSrcCB); + assertThat(sclReportItems).isEmpty(); + } + + @Test + void updateDoInRef_should_update_setSrcRef_and_setSrcCB_when_one_ExtRef_desc_matches() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + LN0Adapter sourceLn0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_WITH_1_InRef"); + String doiNameInRef = "InRef2"; + String originalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + String expectedSrcRef = "IED_NAME1LD_WITH_1_InRef/PRANCR1.Do11.sdo11"; + String expectedSrcCb = "IED_NAME1LD_WITH_1_InRef/prefixANCR1.GSE1"; + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + String finalSetSrcRef = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_REF); + String finalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + assertThat(finalSetSrcRef) + .isNotBlank() + .isEqualTo(expectedSrcRef); + assertThat(finalSetSrcCB) + .isNotEqualTo(originalSetSrcCB) + .isEqualTo(expectedSrcCb); + assertThat(sclReportItems).isEmpty(); + } + + @Test + void updateDoInRef_should_update_setSrcRef_and_setSrcCB_and_setTstRef_and_setTstCB_when_ExtRef_desc_matches() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + LN0Adapter sourceLn0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_WITH_3_InRef"); + String doiNameInRef = "InRef3"; + String originalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + String expectedSrcRef = "IED_NAME1LD_WITH_3_InRef/PRANCR1.Do11.sdo11"; + String expectedSrcCb = "IED_NAME1LD_WITH_3_InRef/prefixANCR1.GSE1"; + String originalSetTstCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_TST_CB); + String expectedTstRef = "IED_NAME1LD_WITH_3_InRef/PRANCR1.Do11.sdo11"; + String expectedTstCB = "IED_NAME1LD_WITH_3_InRef/prefixANCR3.GSE3"; + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + // Then + + String finalSetSrcRef = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_REF); + String finalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + String finalSetTstRef = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_TST_REF); + String finalSetTstCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_TST_CB); + assertThat(finalSetSrcRef) + .isNotBlank() + .isEqualTo(expectedSrcRef); + assertThat(finalSetSrcCB) + .isNotEqualTo(originalSetSrcCB) + .isEqualTo(expectedSrcCb); + assertThat(finalSetTstRef) + .isNotBlank() + .isEqualTo(expectedTstRef); + assertThat(finalSetTstCB) + .isNotEqualTo(originalSetTstCB) + .isEqualTo(expectedTstCB); + assertThat(sclReportItems).isEmpty(); + } } 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 0e554cb54..71d3d110a 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 @@ -156,4 +156,7 @@ public static Stream streamAllExtRef(SclRootAdapter sclRootAdapter) { .flatMap(List::stream); } + public static String getDaiValue(AbstractLNAdapter ln, String doiName, String daiName) { + return ln.getDOIAdapterByName(doiName).getDataAdapterByName(daiName).getCurrentElem().getVal().get(0).getValue(); + } } diff --git a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml new file mode 100644 index 000000000..7b81a7164 --- /dev/null +++ b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml @@ -0,0 +1,115 @@ + + + + + + +

    + + + + + + + + + + LD_WITHOUT_InRef_DOI_InRef1 + + + + + + + + + + + LD_WITH_1_Bad_InRef_DOI_InRef4 + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_1_Bad_InRef_DOI_InRef5 + + + + OLD_VAL + + + + + + + + + + + + + + + + + + OLD_VAL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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 new file mode 100644 index 000000000..9b15fc0b5 --- /dev/null +++ b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml @@ -0,0 +1,86 @@ + + + + + + +
    + + + + + + + + + + + LD_WITH_1_InRef_DOI_InRef2 + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_3_InRef_DOI_InRef3 + + + + OLD_VAL + + + + OLD_VAL + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml new file mode 100644 index 000000000..3a26f7493 --- /dev/null +++ b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml @@ -0,0 +1,177 @@ + + + + + + +
    + + + + + + + + + + LD_WITHOUT_InRef_DOI_InRef1 + + + + + + + + + + + LD_WITH_1_InRef_DOI_InRef2 + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_3_InRef_DOI_InRef3 + + + + OLD_VAL + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_1_Bad_InRef_DOI_InRef4 + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_1_Bad_InRef_DOI_InRef5 + + + + OLD_VAL + + + + + + + + + + + + + + + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_1_InRef_without_cbName_DOI_InRef7 + + + + OLD_VAL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 02a8ccfd3bdb34e0c9e1cd8fd268cf7bc88b9e77 Mon Sep 17 00:00:00 2001 From: SaintierFr <99645240+SaintierFr@users.noreply.github.com> Date: Tue, 7 Feb 2023 11:00:38 +0100 Subject: [PATCH 8/9] fix(#235): Error occurs when importing STD file during SCD automated creation Signed-off-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> --- .../sct/commons/scl/PrivateService.java | 13 +++++--- .../compas/sct/commons/scl/SclService.java | 2 +- .../sct/commons/scl/PrivateServiceTest.java | 4 +-- .../sct/commons/scl/SclServiceTest.java | 11 +++++++ ...mpas_icd_header_in_different_functions.xml | 33 +++++++++++++++++++ 5 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/PrivateService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/PrivateService.java index ec65ce786..60602ea3d 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/PrivateService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/PrivateService.java @@ -12,6 +12,7 @@ import javax.xml.bind.JAXBElement; import java.util.*; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import static org.lfenergy.compas.sct.commons.util.CommonConstants.*; @@ -298,12 +299,12 @@ public static String stdCheckFormatExceptionMessage(TPrivate key) throws ScdExce /** - * Creates map of IEDName and related Private for all Privates COMPAS-ICDHeader in /Substation of SCL + * Creates stream of Private for all Privates COMPAS-ICDHeader in /Substation of SCL * * @param scdRootAdapter SCL file in which Private should be found - * @return map of Private and its IEDName parameter + * @return stream of COMPAS-ICDHeader Private */ - public static Stream createMapIEDNameAndPrivate(SclRootAdapter scdRootAdapter) { + public static Stream streamIcdHeaderPrivatesWithDistinctIEDName(SclRootAdapter scdRootAdapter) { return scdRootAdapter.getCurrentElem().getSubstation().get(0).getVoltageLevel().stream() .map(TVoltageLevel::getBay).flatMap(Collection::stream) .map(TBay::getFunction).flatMap(Collection::stream) @@ -311,8 +312,10 @@ public static Stream createMapIEDNameAndPrivate(SclRootAdapter scdRoot .map(TLNode::getPrivate).flatMap(Collection::stream) .filter(tPrivate -> tPrivate.getType().equals(COMPAS_ICDHEADER.getPrivateType()) - && PrivateService.extractCompasICDHeader(tPrivate).isPresent() - && PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName() != null); + && PrivateService.extractCompasICDHeader(tPrivate).map(TCompasICDHeader::getIEDName).isPresent()) + .collect(Collectors.groupingBy(tPrivate -> PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName())) + .values().stream() + .map(tPrivates -> tPrivates.get(0)); } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java index 117f37347..cca4964ad 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java @@ -502,7 +502,7 @@ public static SclRootAdapter importSTDElementsInSCD(@NonNull SclRootAdapter scdR PrivateService.checkSTDCorrespondanceWithLNodeCompasICDHeader(mapICDSystemVersionUuidAndSTDFile); // List all Private and remove duplicated one with same iedName //For each Private.ICDSystemVersionUUID and Private.iedName find STD File - PrivateService.createMapIEDNameAndPrivate(scdRootAdapter).forEach(tPrivate -> { + PrivateService.streamIcdHeaderPrivatesWithDistinctIEDName(scdRootAdapter).forEach(tPrivate -> { String iedName = PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName(); String icdSysVerUuid = PrivateService.extractCompasICDHeader(tPrivate).map(TCompasICDHeader::getICDSystemVersionUUID) .orElseThrow(() -> new ScdException(ICD_SYSTEM_VERSION_UUID + " is not present in COMPAS-ICDHeader in LNode") 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 dc98feb9b..0fd7e179c 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 @@ -437,7 +437,7 @@ void createMapIEDNameAndPrivate_should_return_map_of_three_items() { //When SclRootAdapter sclRootAdapter = SclService.initScl(Optional.empty(), "hv", "hr"); sclRootAdapter.setCurrentElem(scl); - Stream tPrivateStream = PrivateService.createMapIEDNameAndPrivate(sclRootAdapter); + Stream tPrivateStream = PrivateService.streamIcdHeaderPrivatesWithDistinctIEDName(sclRootAdapter); //Then assertThat(tPrivateStream.toList()).hasSize(3) @@ -467,7 +467,7 @@ void createMapIEDNameAndPrivate_should_return_empty_map_when_no_compasicdheader_ //When SclRootAdapter sclRootAdapter = SclService.initScl(Optional.empty(), "hv", "hr"); sclRootAdapter.setCurrentElem(scl); - Stream tPrivateStream = PrivateService.createMapIEDNameAndPrivate(sclRootAdapter); + Stream tPrivateStream = PrivateService.streamIcdHeaderPrivatesWithDistinctIEDName(sclRootAdapter); //Then assertThat(tPrivateStream.toList()).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 c6f8a7fd8..977d395c9 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 @@ -792,6 +792,17 @@ void testImportSTDElementsInSCD_Several_STD_Match_Compas_ICDHeader() { assertIsMarshallable(scd); } + @Test + void test_importSTDElementsInSCD_should_not_throw_exception_when_SCD_file_contains_same_ICDHeader_in_two_different_functions() { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml"); + SCL std = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); + SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); + //When Then + assertDoesNotThrow(() -> SclService.importSTDElementsInSCD(scdRootAdapter, Set.of(std), DTO.comMap)); + assertIsMarshallable(scd); + } + @Test void testImportSTDElementsInSCD_Compas_ICDHeader_Not_Match() { //Given 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 new file mode 100644 index 000000000..c2d4c04d1 --- /dev/null +++ b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml @@ -0,0 +1,33 @@ + + + + + SCD + +
    + + + 0 + + + + + + + + + + + + + + + + + + + From e8c56805df5b397b6c6fc45a325c3d27fbf62c24 Mon Sep 17 00:00:00 2001 From: SaintierFr <99645240+SaintierFr@users.noreply.github.com> Date: Thu, 16 Feb 2023 16:40:53 +0100 Subject: [PATCH 9/9] fix(#239): Exclude DOI Mod-stVal in DAI updatability check (#240) * fix(#239): Exclude DOI Mod-stVal in DAI updatability check * fix(#239): Comment unit test in error while bug #241 is not fixed Signed-off-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> --- .../commons/scl/ied/AbstractDAIAdapter.java | 50 ++++--- .../commons/scl/ied/AbstractLNAdapter.java | 49 +++---- .../sct/commons/scl/SclServiceTest.java | 46 ++++++ .../sct/commons/scl/ied/DOIAdapterTest.java | 4 +- .../sct/commons/scl/ied/LNAdapterTest.java | 133 ++++++++++++++++-- ...ssue_165_enhance_68_Test_Dai_Updatable.scd | 74 ++++++++++ 6 files changed, 289 insertions(+), 67 deletions(-) diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java index 5e5924659..3d6e69e45 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java @@ -5,14 +5,14 @@ package org.lfenergy.compas.sct.commons.scl.ied; import org.lfenergy.compas.scl2007b4.model.TDAI; +import org.lfenergy.compas.scl2007b4.model.TDOI; import org.lfenergy.compas.scl2007b4.model.TPrivate; import org.lfenergy.compas.scl2007b4.model.TVal; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; +import org.lfenergy.compas.sct.commons.util.CommonConstants; import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; /** * A representation of the model object @@ -35,9 +35,6 @@ *
  • {@link AbstractDAIAdapter#addPrivate(TPrivate) Add TPrivate under this object}
  • * *
  • Checklist functions
  • - *
      - *
    • {@link AbstractDAIAdapter#isValImport Check value Of valImport attribute}
    • - *
    * * * @see org.lfenergy.compas.scl2007b4.model.TDAI @@ -88,13 +85,11 @@ public void setValImport(boolean b) { currentElem.setValImport(b); } - /** - * Cheks ValImport boolean value - * - * @return Boolean value of ValImport if define or null - */ - public Boolean isValImport() { - return currentElem.isSetValImport() ? currentElem.isValImport() : null; + private boolean isDOModDAstVal() { + if (parentAdapter.getCurrentElem() instanceof final TDOI tdoi) { + return currentElem.getName().equals(CommonConstants.STVAL) && tdoi.getName().equals(CommonConstants.MOD_DO_NAME); + } + return false; } public AbstractDAIAdapter update(Map daiValues) throws ScdException { @@ -116,30 +111,33 @@ public AbstractDAIAdapter update(Map * @throws ScdException throws when DAI for which SGroup should be updated is not updatable */ public void update(Long sGroup, String val) throws ScdException { - if (currentElem.isSetValImport() && !currentElem.isValImport()) { + if (!isDOModDAstVal() && currentElem.isSetValImport() && !currentElem.isValImport()) { String msg = String.format( - "DAI(%s) cannot be updated : valImport(false)", currentElem.getName() + "DAI(%s) cannot be updated : valImport(false) %s", currentElem.getName(), getXPath() ); throw new ScdException(msg); } - Stream tValStream = currentElem.getVal().stream(); if (sGroup != null && sGroup != 0) { - Optional tVal = tValStream.filter(tValElem -> tValElem.isSetSGroup() && - sGroup.equals(tValElem.getSGroup())) - .findFirst(); - if (tVal.isPresent()) { - tVal.get().setValue(val); - } else { - TVal newTVal = new TVal(); - newTVal.setValue(val); - newTVal.setSGroup(sGroup); - currentElem.getVal().add(newTVal); - } + updateSGroupVal(sGroup, val); } else { updateVal(val); } } + private void updateSGroupVal(Long sGroup, String val) { + currentElem.getVal().stream() + .filter(tValElem -> tValElem.isSetSGroup() && sGroup.equals(tValElem.getSGroup())) + .findFirst() + .orElseGet( + () -> { + TVal newTVal = new TVal(); + newTVal.setSGroup(sGroup); + currentElem.getVal().add(newTVal); + return newTVal; + }) + .setValue(val); + } + public void updateVal(String s) { currentElem.getVal().stream().findFirst() .orElseGet( 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 c55f92529..bc79c4b94 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 @@ -705,46 +705,40 @@ public void updateDAI(@NonNull ResumedDataTemplate rDtt) throws ScdException { DAITracker.MatchResult matchResult = daiTracker.search(); AbstractDAIAdapter daiAdapter = null; IDataParentAdapter doiOrSdoiAdapter; - if (matchResult == DAITracker.MatchResult.FULL_MATCH) { - // update - daiAdapter = (AbstractDAIAdapter) daiTracker.getBdaiOrDaiAdapter(); - if ((daiAdapter.isValImport() != null && daiAdapter.isValImport()) || - (daiAdapter.isValImport() == null && rDtt.isUpdatable())) { - daiAdapter.update(daTypeName.getDaiValues()); - return; - } else { - throw new ScdException(String.format("DAI (%s -%s) cannot be updated", doTypeName, daTypeName)); - } - } - if (rDtt.isUpdatable()) { + if (!rDtt.isUpdatable()) + return; + + if (rDtt.isUpdatable() && matchResult == DAITracker.MatchResult.FULL_MATCH) { + daiAdapter = (AbstractDAIAdapter) daiTracker.getBdaiOrDaiAdapter(); + } else { doiOrSdoiAdapter = daiTracker.getDoiOrSdoiAdapter(); - int idx = daiTracker.getIndexDoType(); + int indexDoType = daiTracker.getIndexDoType(); int doSz = doTypeName.getStructNames().size(); if (matchResult == DAITracker.MatchResult.FAILED) { doiOrSdoiAdapter = addDOI(doTypeName.getName()); - idx = 0; - } else if (idx == -1) { - idx = 0; - } else if (idx == doSz - 1) { - idx = doSz; + indexDoType = 0; + } else if (indexDoType == -1) { + indexDoType = 0; + } else if (indexDoType == doSz - 1) { + indexDoType = doSz; } - for (int i = idx; i < doSz; ++i) { + for (int i = indexDoType; i < doSz; ++i) { String sdoName = doTypeName.getStructNames().get(i); doiOrSdoiAdapter = doiOrSdoiAdapter.addSDOI(sdoName); } IDataParentAdapter daiOrBdaiAdapter = daiTracker.getDoiOrSdoiAdapter(); - idx = daiTracker.getIndexDaType(); + int indexDaType = daiTracker.getIndexDaType(); int daSz = daTypeName.getStructNames().size(); - if (idx <= -1) { - idx = 0; - } else if (idx == daSz - 1) { - idx = daSz; + if (indexDaType <= -1) { + indexDaType = 0; + } else if (indexDaType == daSz - 1) { + indexDaType = daSz; } - for (int i = idx; i < daSz; ++i) { + for (int i = indexDaType; i < daSz; ++i) { String bdaName = daTypeName.getStructNames().get(i); - if (idx == 0) { + if (indexDaType == 0) { daiOrBdaiAdapter = doiOrSdoiAdapter.addSDOI(daTypeName.getName()); } else if (i == daSz - 1) { daiAdapter = daiOrBdaiAdapter.addDAI(bdaName, rDtt.isUpdatable()); @@ -755,9 +749,8 @@ public void updateDAI(@NonNull ResumedDataTemplate rDtt) throws ScdException { if (daiAdapter == null) { daiAdapter = doiOrSdoiAdapter.addDAI(daTypeName.getName(), rDtt.isUpdatable()); } - - daiAdapter.update(daTypeName.getDaiValues()); } + daiAdapter.update(daTypeName.getDaiValues()); } /** 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 977d395c9..94a38dcf4 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 @@ -1037,6 +1037,52 @@ void updateLDeviceStatus_shouldReturnUpdatedFile() { assertEquals("off", getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName3", "LDSUIED").get().getValue()); } + @Test + void updateLDeviceStatus_shouldReturnUpdatedFile_when_DAI_Mod_DO_stVal_whatever_it_is_updatable_or_not() { + // Given + SCL givenScl = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd"); + assertThat(getLDeviceStatusValue(givenScl, "IedName1", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + assertThat(getLDeviceStatusValue(givenScl, "IedName2", "LDSUIED")) + .map(TVal::getValue) + .hasValue("on"); + assertThat(getLDeviceStatusValue(givenScl, "IedName3", "LDSUIED")) + .map(TVal::getValue) + .isNotPresent(); + assertThat(getLDeviceStatusValue(givenScl, "IedName4", "LDSUIED")) + .map(TVal::getValue) + .hasValue("on"); + assertThat(getLDeviceStatusValue(givenScl, "IedName5", "LDSUIED")) + .map(TVal::getValue) + .hasValue("on"); + + // When + SclReport sclReport = SclService.updateLDeviceStatus(givenScl); + + // Then + assertThat(sclReport.isSuccess()).isTrue(); + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName1", "LDSUIED")) + .map(TVal::getValue) + .hasValue("on"); + + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName2", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName3", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName4", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName5", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + } + private Optional getLDeviceStatusValue(SCL scl, String iedName, String ldInst) { return getValFromDaiName(scl, iedName, ldInst, "Mod", "stVal"); } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java index 5577cd926..2058b81c5 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java @@ -55,9 +55,9 @@ void testInnerDAIAdapter() { DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "angRef"); // Then - assertNull(daiAdapter.isValImport()); + assertThat(daiAdapter.getCurrentElem().isSetValImport()).isFalse(); daiAdapter.setValImport(true); - assertTrue(daiAdapter.isValImport()); + assertThat(daiAdapter.getCurrentElem().isSetValImport()).isTrue(); // test tree map assertThatThrownBy(() -> daiAdapter.getDataAdapterByName(TOTO)).isInstanceOf(UnsupportedOperationException.class); 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 c0dc0e129..199b61e32 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 @@ -4,6 +4,7 @@ package org.lfenergy.compas.sct.commons.scl.ied; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -20,8 +21,7 @@ import java.util.Optional; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; class LNAdapterTest { @@ -380,7 +380,7 @@ void testUpdateExtRefSource() { assertThat(extRef.getSrcCBName()).isEqualTo(extRefInfo.getSourceInfo().getSrcCBName()); assertThat(extRef.getSrcLDInst()).isEqualTo(extRefInfo.getSourceInfo().getSrcLDInst()); - assertThat(extRef.getLnClass().contains(extRefInfo.getSourceInfo().getSrcLNClass())).isTrue(); + assertThat(extRef.getLnClass()).contains(extRefInfo.getSourceInfo().getSrcLNClass()); } @@ -434,7 +434,8 @@ void testGetLNodeName() { } @Test - void testUpdateDAI() { + void updateDAI_should_throw_ScdException_when_ResumedDataTemplate_is_empty() { + // Given ResumedDataTemplate rDtt = new ResumedDataTemplate(); SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); @@ -445,28 +446,138 @@ void testUpdateDAI() { .withLnClass(TLLN0Enum.LLN_0.value()) .build(); - assertThrows(ScdException.class, () -> lnAdapter.updateDAI(rDtt)); + // When Then + assertThatThrownBy(() -> lnAdapter.updateDAI(rDtt)) + .isInstanceOf(ScdException.class) + .hasMessage("Cannot update undefined DAI"); + } + + @Test + void updateDAI_should_throw_ScdException_when_ResumedDataTemplate_DA_name_is_not_defined() { + // Given + ResumedDataTemplate rDtt = new ResumedDataTemplate(); + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + AbstractLNAdapter lnAdapter = AbstractLNAdapter.builder() + .withLDeviceAdapter(lDeviceAdapter) + .withLnClass(TLLN0Enum.LLN_0.value()) + .build(); DoTypeName doTypeName = new DoTypeName("Do.sdo1.d"); rDtt.setDoName(doTypeName); - assertThrows(ScdException.class, () -> lnAdapter.updateDAI(rDtt)); + + // When Then + assertThatThrownBy(() -> lnAdapter.updateDAI(rDtt)) + .isInstanceOf(ScdException.class) + .hasMessage("Cannot update undefined DAI"); + } + + @Test + @Disabled(value = "Disable while bug #241 is not fixed") + /** + * @see Issue !241 (UpdateDAI Val does not produce subelements (SDI) as expected) + */ + void updateDAI_should_not_update_DAI_Val_when_DTT_Fc_not_defined() { + // Given + ResumedDataTemplate rDtt = new ResumedDataTemplate(); + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + LN0Adapter lnAdapter = (LN0Adapter) AbstractLNAdapter.builder() + .withLDeviceAdapter(lDeviceAdapter) + .withLnClass(TLLN0Enum.LLN_0.value()) + .build(); + + rDtt.setDoName(new DoTypeName("Do.sdo1.d")); rDtt.setDaName(new DaTypeName("antRef.bda1.bda2.bda3")); TVal tVal = new TVal(); tVal.setValue("newValue"); rDtt.setDaiValues(List.of(tVal)); - assertDoesNotThrow(() -> lnAdapter.updateDAI(rDtt)); - lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS2").get()); - AbstractLNAdapter lnAdapter2 = AbstractLNAdapter.builder() + // When + lnAdapter.updateDAI(rDtt); + + // Then + SDIAdapter.DAIAdapter daiAdapter = lnAdapter + .getDOIAdapterByName("Do") + .getStructuredDataAdapterByName("sdo1") + .getStructuredDataAdapterByName("d") + .getStructuredDataAdapterByName("bda1") + .getStructuredDataAdapterByName("bda2") + .getStructuredDataAdapterByName("bda3") + .getDataAdapterByName("antRef"); + + assertThat(daiAdapter.getCurrentElem().getVal().get(0).getValue()).isEqualTo("Completed-diff"); + + System.out.println(MarshallerWrapper.marshall(scd)); + } + + @Test + @Disabled(value = "Disable while bug #241 is not fixed") + /** + * @see Issue !241 (UpdateDAI Val does not produce subelements (SDI) as expected) + */ + void updateDAI_should_update_DAI_values_when_data_updatable() { + // Given + ResumedDataTemplate rDtt = new ResumedDataTemplate(); + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS2").get()); + LN0Adapter lnAdapter = (LN0Adapter) AbstractLNAdapter.builder() .withLDeviceAdapter(lDeviceAdapter) .withLnClass(TLLN0Enum.LLN_0.value()) .build(); + + rDtt.setDoName(new DoTypeName("Do.sdo1.d")); + rDtt.setDaName(new DaTypeName("antRef.bda1.bda2.bda3")); + TVal tVal = new TVal(); + tVal.setValue("newValue"); rDtt.setValImport(true); rDtt.setFc(TFCEnum.SE); - assertTrue(rDtt.isUpdatable()); - assertDoesNotThrow(() -> lnAdapter2.updateDAI(rDtt)); + assertThat(rDtt.isUpdatable()).isTrue(); + rDtt.setDaiValues(List.of(tVal)); + + // When + lnAdapter.updateDAI(rDtt); + + // Then + SDIAdapter.DAIAdapter daiAdapter = lnAdapter + .getDOIAdapterByName("Do") + .getStructuredDataAdapterByName("sdo1") + .getStructuredDataAdapterByName("d") + .getStructuredDataAdapterByName("antRef") + .getStructuredDataAdapterByName("bda1") + .getStructuredDataAdapterByName("bda2") + .getDataAdapterByName("bda3"); + + assertThat(daiAdapter.getCurrentElem().getVal().get(0).getValue()).isEqualTo("newValue"); System.out.println(MarshallerWrapper.marshall(scd)); + } + + @Test + void updateDAI_should_not_update_DAI_values_when_not_updatable() { + // Given + ResumedDataTemplate rDtt = new ResumedDataTemplate(); + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + LN0Adapter lnAdapter = (LN0Adapter) AbstractLNAdapter.builder() + .withLDeviceAdapter(lDeviceAdapter) + .withLnClass(TLLN0Enum.LLN_0.value()) + .build(); + + rDtt.setDoName(new DoTypeName("Do.sdo1.d")); + rDtt.setDaName(new DaTypeName("antRef.bda1.bda2.bda3")); + rDtt.setValImport(false); + // When Then + assertThat(rDtt.isUpdatable()).isFalse(); + assertThatCode(() -> lnAdapter.updateDAI(rDtt)).doesNotThrowAnyException(); } @Test 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 48c81222d..68a260d66 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 @@ -169,6 +169,80 @@ + + SAMU + SAMU + + + + + + + + + + + + + + + + + 01.00.000 + + + 01.00.000 + + + + + on + + + + + + + + + + + SAMU + SAMU + + + + + + + + + + + + + + + + + 01.00.000 + + + 01.00.000 + + + + + on + + + + + + + + +