From 75418c73fca4b14c61abe9811154ff337cb923f6 Mon Sep 17 00:00:00 2001 From: Aliou DIAITE Date: Wed, 7 Dec 2022 17:45:39 +0100 Subject: [PATCH 1/3] feat(#189): remove instance number on doNames for pDO in ExtRef while comparing with DO in DataTypeTemplate Signed-off-by: Aliou DIAITE --- .../sct/commons/scl/PrivateService.java | 121 +++++++- .../compas/sct/commons/scl/SclService.java | 176 ++---------- .../sct/commons/scl/dtt/LNodeTypeAdapter.java | 10 +- .../commons/scl/ied/AbstractLNAdapter.java | 38 ++- .../sct/commons/scl/ied/LDeviceAdapter.java | 5 +- .../sct/commons/scl/PrivateServiceTest.java | 271 +++++++++++++++++- .../commons/scl/dtt/LNodeTypeAdapterTest.java | 4 +- 7 files changed, 438 insertions(+), 187 deletions(-) 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 aea8bd9c1..95dbd7c5c 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 @@ -10,12 +10,13 @@ import org.lfenergy.compas.sct.commons.util.PrivateEnum; import javax.xml.bind.JAXBElement; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.function.Predicate; -import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.toList; +import static org.lfenergy.compas.sct.commons.util.CommonConstants.*; +import static org.lfenergy.compas.sct.commons.util.PrivateEnum.COMPAS_ICDHEADER; /** * A representation of the {@link PrivateService PrivateService}. @@ -57,7 +58,7 @@ public static List extractCompasPrivates(List tPrivates, Class< .map(TAnyContentFromOtherNamespace::getContent).flatMap(List::stream) .filter(JAXBElement.class::isInstance).map(JAXBElement.class::cast) .filter(Predicate.not(JAXBElement::isNil)) - .map(JAXBElement::getValue).collect(Collectors.toList()); + .map(JAXBElement::getValue).collect(toList()); List result = new ArrayList<>(); for (Object compasElement : compasElements) { @@ -239,13 +240,121 @@ private static TPrivate createPrivate(JAXBElement jaxbElement) { } + /** + * Sorts in map of ICD_SYSTEM_VERSION_UUID and related Private coupled with all corresponding STD for all given STD + * + * @param stds list of STD to short + * @return map of ICD_SYSTEM_VERSION_UUID attribute in IED/Private:COMPAS-ICDHeader and related Private coupled with + * all corresponding STD + */ + public static Map createMapICDSystemVersionUuidAndSTDFile(Set stds) { + Map stringSCLMap = new HashMap<>(); + stds.forEach(std -> std.getIED().forEach(ied -> ied.getPrivate().forEach(tp -> + PrivateService.extractCompasICDHeader(tp).map(TCompasICDHeader::getICDSystemVersionUUID).ifPresent(icdSysVer -> { + PrivateLinkedToSTDs privateLinkedToSTDs = stringSCLMap.get(icdSysVer); + List list = privateLinkedToSTDs != null ? privateLinkedToSTDs.stdList() : new ArrayList<>(); + list.add(std); + stringSCLMap.put(icdSysVer, new PrivateLinkedToSTDs(tp, list)); + }) + ))); + return stringSCLMap; + } + public record PrivateLinkedToSTDs (TPrivate tPrivate, List stdList) { + } + /** + * Checks SCD and STD compatibilities by checking if there is at least one ICD_SYSTEM_VERSION_UUID in + * Substation/../LNode/Private COMPAS-ICDHeader of SCL not present in IED/Private COMPAS-ICDHeader of STD + * + * @param mapICDSystemVersionUuidAndSTDFile map of ICD_SYSTEM_VERSION_UUID and list of corresponding STD + * @throws ScdException throws when there are several STD files corresponding to ICD_SYSTEM_VERSION_UUID + * from Substation/../LNode/Private COMPAS-ICDHeader of SCL + */ + public static void checkSTDCorrespondanceWithLNodeCompasICDHeader(Map mapICDSystemVersionUuidAndSTDFile) throws ScdException { + mapICDSystemVersionUuidAndSTDFile.values().stream() + .filter(privateLinkedToSTDs -> privateLinkedToSTDs.stdList().size() != 1) + .findFirst() + .ifPresent(pToStd -> { + throw new ScdException("There are several STD files corresponding to " + stdCheckFormatExceptionMessage(pToStd.tPrivate())); + }); + } + /** + * Creates formatted message including data's of Private for Exception + * + * @param key Private causing exception + * @return formatted message + * @throws ScdException throws when parameter not present in Private + */ + public static String stdCheckFormatExceptionMessage(TPrivate key) throws ScdException { + Optional optionalCompasICDHeader = PrivateService.extractCompasICDHeader(key); + return HEADER_ID + " = " + optionalCompasICDHeader.map(TCompasICDHeader::getHeaderId).orElse(null) + " " + + HEADER_VERSION + " = " + optionalCompasICDHeader.map(TCompasICDHeader::getHeaderVersion).orElse(null) + " " + + HEADER_REVISION + " = " + optionalCompasICDHeader.map(TCompasICDHeader::getHeaderRevision).orElse(null) + + " and " + ICD_SYSTEM_VERSION_UUID + " = " + optionalCompasICDHeader.map(TCompasICDHeader::getICDSystemVersionUUID).orElse(null); + } + /** + * Creates map of IEDName and related 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 + */ + public static Stream createMapIEDNameAndPrivate(SclRootAdapter scdRootAdapter) { + return scdRootAdapter.getCurrentElem().getSubstation().get(0).getVoltageLevel().stream() + .map(TVoltageLevel::getBay).flatMap(Collection::stream) + .map(TBay::getFunction).flatMap(Collection::stream) + .map(TFunction::getLNode).flatMap(Collection::stream) + .map(TLNode::getPrivate).flatMap(Collection::stream) + .filter(tPrivate -> + tPrivate.getType().equals(COMPAS_ICDHEADER.getPrivateType()) + && PrivateService.extractCompasICDHeader(tPrivate).isPresent() + && PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName() != null); + } + + + /** + * Compares if two Private:COMPAS-ICDHeader have all attributes equal except IEDNane, BayLabel and IEDinstance + * + * @param iedPrivate Private of IED from STD to compare + * @param scdPrivate Private of LNode fro SCD to compare + * @return Boolean value of check result + * @throws ScdException throws when Private is not COMPAS_ICDHEADER one + */ + public static boolean comparePrivateCompasICDHeaders(TPrivate iedPrivate, TPrivate scdPrivate) throws ScdException { + TCompasICDHeader iedCompasICDHeader = PrivateService.extractCompasICDHeader(iedPrivate) + .orElseThrow(() -> new ScdException(COMPAS_ICDHEADER + "not found in IED Private ")); + TCompasICDHeader scdCompasICDHeader = PrivateService.extractCompasICDHeader(scdPrivate) + .orElseThrow(() -> new ScdException(COMPAS_ICDHEADER + "not found in LNode Private ")); + return Objects.equals(iedCompasICDHeader.getIEDType(), scdCompasICDHeader.getIEDType()) + && Objects.equals(iedCompasICDHeader.getICDSystemVersionUUID(), scdCompasICDHeader.getICDSystemVersionUUID()) + && Objects.equals(iedCompasICDHeader.getVendorName(), scdCompasICDHeader.getVendorName()) + && Objects.equals(iedCompasICDHeader.getIEDredundancy(), scdCompasICDHeader.getIEDredundancy()) + && Objects.equals(iedCompasICDHeader.getIEDmodel(), scdCompasICDHeader.getIEDmodel()) + && Objects.equals(iedCompasICDHeader.getHwRev(), scdCompasICDHeader.getHwRev()) + && Objects.equals(iedCompasICDHeader.getSwRev(), scdCompasICDHeader.getSwRev()) + && Objects.equals(iedCompasICDHeader.getHeaderId(), scdCompasICDHeader.getHeaderId()) + && Objects.equals(iedCompasICDHeader.getHeaderRevision(), scdCompasICDHeader.getHeaderRevision()) + && Objects.equals(iedCompasICDHeader.getHeaderVersion(), scdCompasICDHeader.getHeaderVersion()); + } + + /** + * Copy Private COMPAS_ICDHEADER from LNode of SCD into Private COMPAS_ICDHEADER from IED of STD + * + * @param stdPrivate Private of IED from STD in which to copy new data + * @param lNodePrivate Private of IED from STD from which new data are taken + * @throws ScdException throws when Private is not COMPAS_ICDHEADER one + */ + public static void copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(TPrivate stdPrivate, TPrivate lNodePrivate) throws ScdException { + TCompasICDHeader lNodeCompasICDHeader = extractCompasICDHeader(lNodePrivate) + .orElseThrow(() -> new ScdException(COMPAS_ICDHEADER + " not found in LNode Private ")); + stdPrivate.getContent().clear(); + stdPrivate.getContent().add(objectFactory.createICDHeader(lNodeCompasICDHeader)); + } } 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 8b52b1666..5cd25b45b 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 @@ -22,10 +22,9 @@ import org.lfenergy.compas.sct.commons.util.Utils; import java.util.*; -import java.util.function.Function; import java.util.stream.Collectors; -import static org.lfenergy.compas.sct.commons.util.CommonConstants.*; +import static org.lfenergy.compas.sct.commons.util.CommonConstants.ICD_SYSTEM_VERSION_UUID; import static org.lfenergy.compas.sct.commons.util.PrivateEnum.COMPAS_ICDHEADER; /** @@ -81,7 +80,6 @@ public class SclService { private static final String UNKNOWN_LDEVICE_S_IN_IED_S = "Unknown LDevice (%s) in IED (%s)"; private static final String INVALID_OR_MISSING_ATTRIBUTES_IN_EXT_REF_BINDING_INFO = "Invalid or missing attributes in ExtRef binding info"; - private static final ObjectFactory objectFactory = new ObjectFactory(); private SclService() { throw new IllegalStateException("SclService class"); @@ -217,7 +215,7 @@ public static List getSubnetwork(SCL scd) throws ScdException { CommunicationAdapter communicationAdapter = sclRootAdapter.getCommunicationAdapter(false); return communicationAdapter.getSubNetworkAdapters().stream() .map(SubNetworkDTO::from) - .collect(Collectors.toList()); + .toList(); } /** @@ -230,12 +228,22 @@ public static List getSubnetwork(SCL scd) throws ScdException { * @throws ScdException throws when unknown specified IED or LDevice */ public static List getExtRefInfo(SCL scd, String iedName, String ldInst) throws ScdException { + LDeviceAdapter lDeviceAdapter = createLDeviceAdapter(scd, iedName, ldInst); + return lDeviceAdapter.getExtRefInfo(); + } + /** + * Create LDevice + * @param scd + * @param iedName + * @param ldInst + * @return + */ + private static LDeviceAdapter createLDeviceAdapter(SCL scd, String iedName, String ldInst) { SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(iedName); - LDeviceAdapter lDeviceAdapter = iedAdapter.findLDeviceAdapterByLdInst(ldInst) + return iedAdapter.findLDeviceAdapterByLdInst(ldInst) .orElseThrow(() -> new ScdException(String.format(UNKNOWN_LDEVICE_S_IN_IED_S, ldInst, iedName))); - return lDeviceAdapter.getExtRefInfo(); } /** @@ -252,10 +260,7 @@ public static List getExtRefInfo(SCL scd, String iedName, String ldI * @throws ScdException throws when ExtRef contains inconsistency data */ public static List getExtRefBinders(SCL scd, String iedName, String ldInst, String lnClass, String lnInst, String prefix, ExtRefSignalInfo signalInfo) throws ScdException { - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(iedName); - LDeviceAdapter lDeviceAdapter = iedAdapter.findLDeviceAdapterByLdInst(ldInst) - .orElseThrow(() -> new ScdException(String.format(UNKNOWN_LDEVICE_S_IN_IED_S, ldInst, iedName))); + LDeviceAdapter lDeviceAdapter = createLDeviceAdapter(scd, iedName, ldInst); AbstractLNAdapter abstractLNAdapter = AbstractLNAdapter.builder() .withLDeviceAdapter(lDeviceAdapter) .withLnClass(lnClass) @@ -267,11 +272,11 @@ public static List getExtRefBinders(SCL scd, String iedName, abstractLNAdapter.isExtRefExist(signalInfo); // find potential binders for the signalInfo - return sclRootAdapter.streamIEDAdapters() + return lDeviceAdapter.getParentAdapter().getParentAdapter().streamIEDAdapters() .map(iedAdapter1 -> iedAdapter1.getExtRefBinders(signalInfo)) .flatMap(Collection::stream) .sorted() - .collect(Collectors.toList()); + .toList(); } /** @@ -338,7 +343,7 @@ public static List> getExtRefSourceInfo(SCL scd, ExtRefInfo extR return aLNAdapters.stream() .map(abstractLNAdapter1 -> abstractLNAdapter1.getControlBlocksForMatchingFCDA(extRefInfo)) .flatMap(Collection::stream) - .collect(Collectors.toList()); + .toList(); } @@ -373,10 +378,7 @@ public static TExtRef updateExtRefSource(SCL scd, ExtRefInfo extRefInfo) throws throw new ScdException(INVALID_OR_MISSING_ATTRIBUTES_IN_EXT_REF_BINDING_INFO); } - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(iedName); - LDeviceAdapter lDeviceAdapter = iedAdapter.findLDeviceAdapterByLdInst(ldInst) - .orElseThrow(() -> new ScdException(String.format(UNKNOWN_LDEVICE_S_IN_IED_S, ldInst, iedName))); + LDeviceAdapter lDeviceAdapter = createLDeviceAdapter(scd, iedName, ldInst); AbstractLNAdapter anLNAdapter = AbstractLNAdapter.builder() .withLDeviceAdapter(lDeviceAdapter) .withLnClass(lnClass) @@ -399,11 +401,7 @@ public static TExtRef updateExtRefSource(SCL scd, ExtRefInfo extRefInfo) throws * @throws ScdException SCD illegal arguments exception, missing mandatory data */ public static Set getDAI(SCL scd, String iedName, String ldInst, ResumedDataTemplate rDtt, boolean updatable) throws ScdException { - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - IEDAdapter iedAdapter = new IEDAdapter(sclRootAdapter, iedName); - LDeviceAdapter lDeviceAdapter = iedAdapter.findLDeviceAdapterByLdInst(ldInst) - .orElseThrow(() -> new ScdException(String.format(UNKNOWN_LDEVICE_S_IN_IED_S, ldInst, iedName))); - + LDeviceAdapter lDeviceAdapter = createLDeviceAdapter(scd, iedName, ldInst); return lDeviceAdapter.getDAI(rDtt, updatable); } @@ -499,27 +497,24 @@ public static Set> getEnumTypeElements(SCL scd, String idE public static SclRootAdapter importSTDElementsInSCD(@NonNull SclRootAdapter scdRootAdapter, Set stds, Map, List> comMap) throws ScdException { //Check SCD and STD compatibilities - Map>> mapICDSystemVersionUuidAndSTDFile = createMapICDSystemVersionUuidAndSTDFile(stds); - checkSTDCorrespondanceWithLNodeCompasICDHeader(mapICDSystemVersionUuidAndSTDFile); + Map mapICDSystemVersionUuidAndSTDFile = PrivateService.createMapICDSystemVersionUuidAndSTDFile(stds); + PrivateService.checkSTDCorrespondanceWithLNodeCompasICDHeader(mapICDSystemVersionUuidAndSTDFile); // List all Private and remove duplicated one with same iedName - Map mapIEDNameAndPrivate = createMapIEDNameAndPrivate(scdRootAdapter); //For each Private.ICDSystemVersionUUID and Private.iedName find STD File - for (Map.Entry entry : mapIEDNameAndPrivate.entrySet()) { - String iedName = entry.getKey(); - TPrivate tPrivate = entry.getValue(); + 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") ); - if (!mapICDSystemVersionUuidAndSTDFile.containsKey(icdSysVerUuid)) - throw new ScdException("There is no STD file found corresponding to " + stdCheckFormatExceptionMessage(tPrivate)); + throw new ScdException("There is no STD file found corresponding to " + PrivateService.stdCheckFormatExceptionMessage(tPrivate)); // import /ied /dtt in Scd - SCL std = mapICDSystemVersionUuidAndSTDFile.get(icdSysVerUuid).getRight().get(0); + SCL std = mapICDSystemVersionUuidAndSTDFile.get(icdSysVerUuid).stdList().get(0); SclRootAdapter stdRootAdapter = new SclRootAdapter(std); IEDAdapter stdIedAdapter = new IEDAdapter(stdRootAdapter, std.getIED().get(0)); Optional optionalTPrivate = stdIedAdapter.getPrivateHeader(COMPAS_ICDHEADER.getPrivateType()); - if (optionalTPrivate.isPresent() && comparePrivateCompasICDHeaders(optionalTPrivate.get(), tPrivate)) { - copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(optionalTPrivate.get(), tPrivate); + if (optionalTPrivate.isPresent() && PrivateService.comparePrivateCompasICDHeaders(optionalTPrivate.get(), tPrivate)) { + PrivateService.copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(optionalTPrivate.get(), tPrivate); } else throw new ScdException("COMPAS-ICDHeader is not the same in Substation and in IED"); scdRootAdapter.addIED(std, iedName); @@ -527,119 +522,10 @@ public static SclRootAdapter importSTDElementsInSCD(@NonNull SclRootAdapter scdR CommunicationAdapter comAdapter = stdRootAdapter.getCommunicationAdapter(false); Set subNetworkDTOSet = SubNetworkDTO.createDefaultSubnetwork(iedName, comAdapter, comMap); addSubnetworks(scdRootAdapter.getCurrentElem(), subNetworkDTOSet, Optional.of(std)); - } + }); return scdRootAdapter; } - /** - * Checks SCD and STD compatibilities by checking if there is at least one ICD_SYSTEM_VERSION_UUID in - * Substation/../LNode/Private COMPAS-ICDHeader of SCL not present in IED/Private COMPAS-ICDHeader of STD - * - * @param mapICDSystemVersionUuidAndSTDFile map of ICD_SYSTEM_VERSION_UUID and list of corresponding STD - * @throws ScdException throws when there are several STD files corresponding to ICD_SYSTEM_VERSION_UUID - * from Substation/../LNode/Private COMPAS-ICDHeader of SCL - */ - private static void checkSTDCorrespondanceWithLNodeCompasICDHeader(Map>> mapICDSystemVersionUuidAndSTDFile) throws ScdException { - for (Pair> pairOfPrivateAndSTDs : mapICDSystemVersionUuidAndSTDFile.values()) { - if (pairOfPrivateAndSTDs.getRight().size() != 1) { - TPrivate key = pairOfPrivateAndSTDs.getLeft(); - throw new ScdException("There are several STD files corresponding to " + stdCheckFormatExceptionMessage(key)); - } - } - } - - /** - * Creates formatted message including data's of Private for Exception - * - * @param key Private causing exception - * @return formatted message - * @throws ScdException throws when parameter not present in Private - */ - private static String stdCheckFormatExceptionMessage(TPrivate key) throws ScdException { - Optional optionalCompasICDHeader = PrivateService.extractCompasICDHeader(key); - return HEADER_ID + " = " + optionalCompasICDHeader.map(TCompasICDHeader::getHeaderId).orElse(null) + " " + - HEADER_VERSION + " = " + optionalCompasICDHeader.map(TCompasICDHeader::getHeaderVersion).orElse(null) + " " + - HEADER_REVISION + " = " + optionalCompasICDHeader.map(TCompasICDHeader::getHeaderRevision).orElse(null) + - " and " + ICD_SYSTEM_VERSION_UUID + " = " + optionalCompasICDHeader.map(TCompasICDHeader::getICDSystemVersionUUID).orElse(null); - } - - /** - * Creates map of IEDName and related 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 - */ - private static Map createMapIEDNameAndPrivate(SclRootAdapter scdRootAdapter) { - return scdRootAdapter.getCurrentElem().getSubstation().get(0).getVoltageLevel().stream() - .map(TVoltageLevel::getBay).flatMap(Collection::stream) - .map(TBay::getFunction).flatMap(Collection::stream) - .map(TFunction::getLNode).flatMap(Collection::stream) - .map(TLNode::getPrivate).flatMap(Collection::stream) - .filter(tPrivate -> - tPrivate.getType().equals(COMPAS_ICDHEADER.getPrivateType()) - && PrivateService.extractCompasICDHeader(tPrivate).isPresent() - && PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName() != null) - .collect(Collectors.toMap(tPrivate -> PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName(), Function.identity())); - } - - /** - * Sorts in map of ICD_SYSTEM_VERSION_UUID and related Private coupled with all corresponding STD for all given STD - * - * @param stds list of STD to short - * @return map of ICD_SYSTEM_VERSION_UUID attribute in IED/Private:COMPAS-ICDHeader and related Private coupled with - * all corresponding STD - */ - private static Map>> createMapICDSystemVersionUuidAndSTDFile(Set stds) { - Map>> stringSCLMap = new HashMap<>(); - stds.forEach(std -> std.getIED().forEach(ied -> ied.getPrivate().forEach(tp -> - PrivateService.extractCompasICDHeader(tp).map(TCompasICDHeader::getICDSystemVersionUUID).ifPresent(icdSysVer -> { - Pair> pair = stringSCLMap.get(icdSysVer); - List list = pair != null ? pair.getRight() : new ArrayList<>(); - list.add(std); - stringSCLMap.put(icdSysVer, Pair.of(tp, list)); - }) - ))); - return stringSCLMap; - } - - /** - * Compares if two Private:COMPAS-ICDHeader have all attributes equal except IEDNane, BayLabel and IEDinstance - * - * @param iedPrivate Private of IED from STD to compare - * @param scdPrivate Private of LNode fro SCD to compare - * @return Boolean value of check result - * @throws ScdException throws when Private is not COMPAS_ICDHEADER one - */ - private static boolean comparePrivateCompasICDHeaders(TPrivate iedPrivate, TPrivate scdPrivate) throws ScdException { - TCompasICDHeader iedCompasICDHeader = PrivateService.extractCompasICDHeader(iedPrivate) - .orElseThrow(() -> new ScdException(COMPAS_ICDHEADER + "not found in IED Private ")); - TCompasICDHeader scdCompasICDHeader = PrivateService.extractCompasICDHeader(scdPrivate) - .orElseThrow(() -> new ScdException(COMPAS_ICDHEADER + "not found in LNode Private ")); - return iedCompasICDHeader.getIEDType().equals(scdCompasICDHeader.getIEDType()) - && iedCompasICDHeader.getICDSystemVersionUUID().equals(scdCompasICDHeader.getICDSystemVersionUUID()) - && iedCompasICDHeader.getVendorName().equals(scdCompasICDHeader.getVendorName()) - && iedCompasICDHeader.getIEDredundancy().equals(scdCompasICDHeader.getIEDredundancy()) - && iedCompasICDHeader.getIEDmodel().equals(scdCompasICDHeader.getIEDmodel()) - && iedCompasICDHeader.getHwRev().equals(scdCompasICDHeader.getHwRev()) - && iedCompasICDHeader.getSwRev().equals(scdCompasICDHeader.getSwRev()) - && iedCompasICDHeader.getHeaderId().equals(scdCompasICDHeader.getHeaderId()) - && iedCompasICDHeader.getHeaderRevision().equals(scdCompasICDHeader.getHeaderRevision()) - && iedCompasICDHeader.getHeaderVersion().equals(scdCompasICDHeader.getHeaderVersion()); - } - - /** - * Copy Private COMPAS_ICDHEADER from LNode of SCD into Private COMPAS_ICDHEADER from IED of STD - * - * @param stdPrivate Private of IED from STD in which to copy new data - * @param lNodePrivate Private of IED from STD from which new data are taken - * @throws ScdException throws when Private is not COMPAS_ICDHEADER one - */ - private static void copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(TPrivate stdPrivate, TPrivate lNodePrivate) throws ScdException { - TCompasICDHeader lNodeCompasICDHeader = PrivateService.extractCompasICDHeader(lNodePrivate) - .orElseThrow(() -> new ScdException(COMPAS_ICDHEADER + " not found in LNode Private ")); - stdPrivate.getContent().clear(); - stdPrivate.getContent().add(objectFactory.createICDHeader(lNodeCompasICDHeader)); - } /** * Removes all ControlBlocks and DataSets for all LNs in SCL @@ -649,7 +535,7 @@ private static void copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(TPrivate s public static void removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(final SCL scl) { SclRootAdapter sclRootAdapter = new SclRootAdapter(scl); List lDeviceAdapters = sclRootAdapter.streamIEDAdapters() - .flatMap(IEDAdapter::streamLDeviceAdapters).collect(Collectors.toList()); + .flatMap(IEDAdapter::streamLDeviceAdapters).toList(); // LN0 lDeviceAdapters.stream() @@ -680,7 +566,7 @@ public static SclReport updateLDeviceStatus(SCL scd) { .map(LDeviceAdapter::getLN0Adapter) .map(ln0Adapter -> ln0Adapter.updateLDeviceStatus(iedNameLdInstList)) .flatMap(Optional::stream) - .collect(Collectors.toList()); + .toList(); SclReport sclReport = new SclReport(); sclReport.getSclReportItems().addAll(errors); sclReport.setSclRootAdapter(sclRootAdapter); diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapter.java index ecfe0510a..c085c6057 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapter.java @@ -147,7 +147,7 @@ public String getLNClass() { public Optional getDOTypeId(String doName){ return currentElem.getDO() .stream() - .filter(tdo -> doName.equals(tdo.getName())) + .filter(tdo -> doName.equals(Utils.removeTrailingDigits(tdo.getName()))) .map(TDO::getType) .findFirst(); } @@ -314,9 +314,7 @@ public List getResumedDTTByDaName(DaTypeName daTypeName) th rDtt.getDoName().setName(doAdapter.getCurrentElem().getName()); opRDtt = doTypeAdapter.getResumedDTTByDaName(daTypeName, rDtt); - if(opRDtt.isPresent()){ - rDtts.add(opRDtt.get()); - } + opRDtt.ifPresent(rDtts::add); } return rDtts; } @@ -347,13 +345,13 @@ public String getId() { /** * Find binded DOType info - * @param signalInfo + * @param signalInfo extRef signal info for binding * @return DOType info as object contening name, id and adapter * @throws ScdException throws when DO unknown */ public DataTypeTemplateAdapter.DOTypeInfo findMatchingDOType(ExtRefSignalInfo signalInfo) throws ScdException{ DoTypeName doName = new DoTypeName(signalInfo.getPDO()); - String extDoName = doName.getName(); + String extDoName = Utils.removeTrailingDigits(doName.getName()); String doTypeId = getDOTypeId(extDoName).orElseThrow(() -> new IllegalArgumentException("Unknown doName :" + signalInfo.getPDO())); DOTypeAdapter doTypeAdapter = this.getParentAdapter().getDOTypeAdapterById(doTypeId).orElseThrow(() -> 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 5286d7b2f..2571734f4 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 @@ -168,7 +168,7 @@ public List getDOIAdapters() { return currentElem.getDOI() .stream() .map(tdoi -> new DOIAdapter(this, tdoi)) - .collect(Collectors.toList()); + .toList(); } boolean isLN0() { @@ -211,7 +211,7 @@ public List getExtRefs(ExtRefSignalInfo filter) { Objects.equals(filter.getPDA(), tExtRef.getPDA()) && Objects.equals(filter.getIntAddr(), tExtRef.getIntAddr()) && Objects.equals(filter.getPServT(), tExtRef.getPServT())) - .collect(Collectors.toList()); + .toList(); } /** @@ -344,11 +344,9 @@ public List> getControlBlocksForMatchingFCDA(@NonNull ExtRefInfo * @return list of ControlBlock objects */ protected List> getControlBlocks(List tDataSets, TServiceType serviceType) { - /*List> controlBlocks = new ArrayList<>(); - for (TDataSet tDataSet : tDataSets) { - controlBlocks.addAll(getControlBlocksByDataSetRef(tDataSet.getName(), serviceType)); - }*/ - return tDataSets.stream().map(tDataSet -> getControlBlocksByDataSetRef(tDataSet.getName(), serviceType)).flatMap(Collection::stream).toList(); + return tDataSets.stream() + .map(tDataSet -> getControlBlocksByDataSetRef(tDataSet.getName(), serviceType)) + .flatMap(Collection::stream).toList(); } @@ -432,19 +430,15 @@ private boolean isCBKnown(String cbName) { */ public boolean hasControlBlock(ControlBlock controlBlock) { - switch (controlBlock.getServiceType()) { - case REPORT: - return currentElem.getReportControl().stream() - .anyMatch(control -> control.getName().equals(controlBlock.getName())); - case GOOSE: - return isLN0() && ((LN0) currentElem).getGSEControl().stream() - .anyMatch(control -> control.getName().equals(controlBlock.getName())); - case SMV: - return isLN0() && ((LN0) currentElem).getSampledValueControl().stream() - .anyMatch(reportControl -> reportControl.getName().equals(controlBlock.getName())); - default: - return false; - } + return switch (controlBlock.getServiceType()) { + case REPORT -> currentElem.getReportControl().stream() + .anyMatch(control -> control.getName().equals(controlBlock.getName())); + case GOOSE -> isLN0() && ((LN0) currentElem).getGSEControl().stream() + .anyMatch(control -> control.getName().equals(controlBlock.getName())); + case SMV -> isLN0() && ((LN0) currentElem).getSampledValueControl().stream() + .anyMatch(reportControl -> reportControl.getName().equals(controlBlock.getName())); + default -> false; + }; } /** @@ -619,7 +613,7 @@ public List getDAI(ResumedDataTemplate rDtt, boolean updata resumedDTTs.forEach(this::overrideAttributesFromDAI); if (updatableOnly) { - return resumedDTTs.stream().filter(ResumedDataTemplate::isUpdatable).collect(Collectors.toList()); + return resumedDTTs.stream().filter(ResumedDataTemplate::isUpdatable).toList(); } else { return resumedDTTs; } @@ -880,7 +874,7 @@ public void addDataSet(DataSetInfo dataSetInfo) { TDataSet tDataSet = new TDataSet(); tDataSet.setName(dataSetInfo.getName()); tDataSet.getFCDA().addAll( - dataSetInfo.getFCDAInfos().stream().map(FCDAInfo::getFCDA).collect(Collectors.toList()) + dataSetInfo.getFCDAInfos().stream().map(FCDAInfo::getFCDA).toList() ); currentElem.getDataSet().add(tDataSet); diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java index 3fdd3cd0c..962872c8b 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java @@ -15,7 +15,6 @@ import org.lfenergy.compas.sct.commons.util.Utils; import java.util.*; -import java.util.stream.Collectors; /** * A representation of the model object @@ -142,7 +141,7 @@ public List getLNAdapters(){ return currentElem.getLN() .stream() .map(tln -> new LNAdapter(this,tln)) - .collect(Collectors.toList()); + .toList(); } /** @@ -191,7 +190,7 @@ public List getExtRefBinders(ExtRefSignalInfo signalInfo) { extRefBindingInfo.setPrefix(lnAdapter.getPrefix()); return extRefBindingInfo; }) - .collect(Collectors.toList()); + .toList(); } /** 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 9d55cc596..dc98feb9b 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java @@ -17,13 +17,12 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Optional; +import java.util.*; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; class PrivateServiceTest { @@ -285,4 +284,270 @@ void removePrivates_should_not_set_private() { assertThat(baseElement.isSetPrivate()).isFalse(); } + @Test + void createMapICDSystemVersionUuidAndSTDFile_Should_return_empty_map_when_no_ICDSystemVersionUUID() { + //Given + SCL scl1 = new SCL(); + TIED tied1 = new TIED(); + TCompasICDHeader compasICDHeader1 = new TCompasICDHeader(); + TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1); + tied1.getPrivate().add(tPrivate1); + scl1.getIED().add(tied1); + + //When + Map stringSCLMap = PrivateService.createMapICDSystemVersionUuidAndSTDFile(Set.of(scl1)); + + //Then + assertThat(stringSCLMap.keySet()).isEmpty(); + + } + + @Test + void createMapICDSystemVersionUuidAndSTDFile_Should_return_map_with_two_lines() { + //Given + SCL scl1 = new SCL(); + SCL scl2 = new SCL(); + SCL scl3 = new SCL(); + TIED tied1 = new TIED(); + TIED tied2 = new TIED(); + TIED tied3 = new TIED(); + TCompasICDHeader compasICDHeader1 = new TCompasICDHeader(); + compasICDHeader1.setICDSystemVersionUUID("UUID-1"); + TCompasICDHeader compasICDHeader2 = new TCompasICDHeader(); + compasICDHeader2.setICDSystemVersionUUID("UUID-2"); + TCompasICDHeader compasICDHeader3 = new TCompasICDHeader(); + compasICDHeader3.setICDSystemVersionUUID("UUID-2"); + TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1); + TPrivate tPrivate2 = PrivateService.createPrivate(compasICDHeader2); + TPrivate tPrivate3 = PrivateService.createPrivate(compasICDHeader3); + tied1.getPrivate().add(tPrivate1); + tied2.getPrivate().add(tPrivate2); + tied3.getPrivate().add(tPrivate3); + scl1.getIED().add(tied1); + scl2.getIED().add(tied2); + scl3.getIED().add(tied3); + + //When + Map stringSCLMap = PrivateService.createMapICDSystemVersionUuidAndSTDFile(Set.of(scl1,scl2,scl3)); + + //Then + assertThat(stringSCLMap.keySet()).hasSize(2).containsExactly("UUID-1", "UUID-2"); + assertThat(stringSCLMap.get("UUID-2").stdList()).hasSize(2); + } + + @Test + void checkSTDCorrespondanceWithLNodeCompasICDHeadershoul_throw_scdEception(){ + //Given + TCompasICDHeader compasICDHeader1 = new TCompasICDHeader(); + compasICDHeader1.setICDSystemVersionUUID("UUID-1"); + TCompasICDHeader compasICDHeader2 = new TCompasICDHeader(); + compasICDHeader2.setICDSystemVersionUUID("UUID-2"); + compasICDHeader2.setHeaderId("ID-2"); + compasICDHeader2.setHeaderVersion("VER-2"); + compasICDHeader2.setHeaderRevision("REV-2"); + TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1); + TPrivate tPrivate2 = PrivateService.createPrivate(compasICDHeader2); + + + PrivateService.PrivateLinkedToSTDs privateLinkedToSTDs1 = new PrivateService.PrivateLinkedToSTDs(tPrivate1,Collections.singletonList(new SCL())); + PrivateService.PrivateLinkedToSTDs privateLinkedToSTDs2 = new PrivateService.PrivateLinkedToSTDs(tPrivate2, Arrays.asList(new SCL(), new SCL())); + + + Map stringSCLMap = new HashMap<>(); + stringSCLMap.put("UUID-1", privateLinkedToSTDs1); + stringSCLMap.put("UUID-2", privateLinkedToSTDs2); + + //When Then + assertThatThrownBy(() -> PrivateService.checkSTDCorrespondanceWithLNodeCompasICDHeader(stringSCLMap)) + .isInstanceOf(ScdException.class) + .hasMessage("There are several STD files corresponding to headerId = ID-2 headerVersion = VER-2 headerRevision = REV-2 and ICDSystemVersionUUID = UUID-2"); + + } + + @Test + void checkSTDCorrespondanceWithLNodeCompasICDHeader_should_pass(){ + //Given + TCompasICDHeader compasICDHeader1 = new TCompasICDHeader(); + compasICDHeader1.setICDSystemVersionUUID("UUID-1"); + TCompasICDHeader compasICDHeader2 = new TCompasICDHeader(); + compasICDHeader2.setICDSystemVersionUUID("UUID-2"); + TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1); + TPrivate tPrivate2 = PrivateService.createPrivate(compasICDHeader2); + + + PrivateService.PrivateLinkedToSTDs privateLinkedToSTDs1 = new PrivateService.PrivateLinkedToSTDs(tPrivate1,Collections.singletonList(new SCL())); + PrivateService.PrivateLinkedToSTDs privateLinkedToSTDs2 = new PrivateService.PrivateLinkedToSTDs(tPrivate2, Collections.singletonList(new SCL())); + + + Map stringSCLMap = new HashMap<>(); + stringSCLMap.put("UUID-1", privateLinkedToSTDs1); + stringSCLMap.put("UUID-2", privateLinkedToSTDs2); + + //When Then + assertDoesNotThrow(() -> PrivateService.checkSTDCorrespondanceWithLNodeCompasICDHeader(stringSCLMap)); + + } + + @Test + void stdCheckFormatExceptionMessage_should_return_formatted_message_with_Private_data() { + //Given + TCompasICDHeader compasICDHeader = new TCompasICDHeader(); + compasICDHeader.setHeaderId("ID-1"); + compasICDHeader.setHeaderVersion("VER-1"); + compasICDHeader.setICDSystemVersionUUID("UUID-1"); + TPrivate tPrivate = PrivateService.createPrivate(compasICDHeader); + + //When + String message = PrivateService.stdCheckFormatExceptionMessage(tPrivate); + + //Then + assertThat(message).isEqualTo("headerId = ID-1 headerVersion = VER-1 headerRevision = null and ICDSystemVersionUUID = UUID-1"); + + } + + @Test + void createMapIEDNameAndPrivate_should_return_map_of_three_items() { + //Given + SCL scl = new SCL(); + TLNode tlNode1 = new TLNode(); + TLNode tlNode2 = new TLNode(); + TLNode tlNode3 = new TLNode(); + TCompasICDHeader compasICDHeader1 = new TCompasICDHeader(); + compasICDHeader1.setIEDName("IED-1"); + TCompasICDHeader compasICDHeader2 = new TCompasICDHeader(); + compasICDHeader2.setIEDName("IED-2"); + TCompasICDHeader compasICDHeader3 = new TCompasICDHeader(); + compasICDHeader3.setIEDName("IED-3"); + TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1); + TPrivate tPrivate2 = PrivateService.createPrivate(compasICDHeader2); + TPrivate tPrivate3 = PrivateService.createPrivate(compasICDHeader3); + tlNode1.getPrivate().add(tPrivate1); + tlNode2.getPrivate().add(tPrivate2); + tlNode3.getPrivate().add(tPrivate3); + TFunction tFunction = new TFunction(); + tFunction.getLNode().addAll(Arrays.asList(tlNode1, tlNode2, tlNode3)); + TBay tBay = new TBay(); + tBay.getFunction().add(tFunction); + TVoltageLevel tVoltageLevel = new TVoltageLevel(); + tVoltageLevel.getBay().add(tBay); + TSubstation tSubstation = new TSubstation(); + tSubstation.getVoltageLevel().add(tVoltageLevel); + scl.getSubstation().add(tSubstation); + + //When + SclRootAdapter sclRootAdapter = SclService.initScl(Optional.empty(), "hv", "hr"); + sclRootAdapter.setCurrentElem(scl); + Stream tPrivateStream = PrivateService.createMapIEDNameAndPrivate(sclRootAdapter); + + //Then + assertThat(tPrivateStream.toList()).hasSize(3) + .extracting(tPrivate -> PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName()) + .containsExactlyInAnyOrder("IED-1", "IED-2", "IED-3"); + } + + @Test + void createMapIEDNameAndPrivate_should_return_empty_map_when_no_compasicdheader_present_under_substation() { + //Given + SCL scl = new SCL(); + TLNode tlNode1 = new TLNode(); + TCompasBay compasBay = new TCompasBay(); + compasBay.setUUID("UUID"); + TPrivate tPrivate1 = PrivateService.createPrivate(compasBay); + tlNode1.getPrivate().add(tPrivate1); + TFunction tFunction = new TFunction(); + tFunction.getLNode().add(tlNode1); + TBay tBay = new TBay(); + tBay.getFunction().add(tFunction); + TVoltageLevel tVoltageLevel = new TVoltageLevel(); + tVoltageLevel.getBay().add(tBay); + TSubstation tSubstation = new TSubstation(); + tSubstation.getVoltageLevel().add(tVoltageLevel); + scl.getSubstation().add(tSubstation); + + //When + SclRootAdapter sclRootAdapter = SclService.initScl(Optional.empty(), "hv", "hr"); + sclRootAdapter.setCurrentElem(scl); + Stream tPrivateStream = PrivateService.createMapIEDNameAndPrivate(sclRootAdapter); + + //Then + assertThat(tPrivateStream.toList()).isEmpty(); + } + + @Test + void comparePrivateCompasICDHeaders_should_return_true_equality_not_check_for_IEDNane_BayLabel_IEDinstance() { + // Given + TCompasICDHeader compasICDHeader1 = new TCompasICDHeader(); + compasICDHeader1.setIEDName("IED-1"); + compasICDHeader1.setBayLabel("BAY-1"); + compasICDHeader1.setIEDinstance("1"); + TCompasICDHeader compasICDHeader2 = new TCompasICDHeader(); + TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1); + TPrivate tPrivate2 = PrivateService.createPrivate(compasICDHeader2); + + // When + boolean result = PrivateService.comparePrivateCompasICDHeaders(tPrivate1,tPrivate2); + // Then + assertThat(result).isTrue(); + } + + @Test + void comparePrivateCompasICDHeaders_should_return_false_equality_not_check_for_IEDNane_BayLabel_IEDinstance() { + // Given + TCompasICDHeader compasICDHeader1 = new TCompasICDHeader(); + compasICDHeader1.setIEDName("IED-1"); + compasICDHeader1.setBayLabel("BAY-1"); + compasICDHeader1.setIEDinstance("1"); + compasICDHeader1.setICDSystemVersionUUID("UUID-1"); + TCompasICDHeader compasICDHeader2 = new TCompasICDHeader(); + compasICDHeader2.setICDSystemVersionUUID("UUID-2"); + TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1); + TPrivate tPrivate2 = PrivateService.createPrivate(compasICDHeader2); + + // When + boolean result = PrivateService.comparePrivateCompasICDHeaders(tPrivate1,tPrivate2); + // Then + assertThat(result).isFalse(); + } + + @Test + void comparePrivateCompasICDHeaders_should_return_true() { + // Given + TCompasICDHeader compasICDHeader1 = new TCompasICDHeader(); + compasICDHeader1.setIEDName("IED-1"); + compasICDHeader1.setBayLabel("BAY-1"); + compasICDHeader1.setIEDinstance("1"); + compasICDHeader1.setICDSystemVersionUUID("UUID-1"); + TCompasICDHeader compasICDHeader2 = new TCompasICDHeader(); + compasICDHeader2.setICDSystemVersionUUID("UUID-1"); + TPrivate tPrivate1 = PrivateService.createPrivate(compasICDHeader1); + TPrivate tPrivate2 = PrivateService.createPrivate(compasICDHeader2); + + // When + boolean result = PrivateService.comparePrivateCompasICDHeaders(tPrivate1,tPrivate2); + // Then + assertThat(result).isTrue(); + } + + @Test + void copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate() { + // Given + TCompasICDHeader stdCompasICDHeader = new TCompasICDHeader(); + stdCompasICDHeader.setICDSystemVersionUUID("UUID-1"); + TCompasICDHeader lNodeCompasICDHeader = new TCompasICDHeader(); + lNodeCompasICDHeader.setICDSystemVersionUUID("UUID-2"); + lNodeCompasICDHeader.setIEDName("IED-1"); + lNodeCompasICDHeader.setBayLabel("BAY-1"); + lNodeCompasICDHeader.setIEDinstance("1"); + TPrivate stdTPrivate = PrivateService.createPrivate(stdCompasICDHeader); + TPrivate lNodePrivate = PrivateService.createPrivate(lNodeCompasICDHeader); + + // When + assertThat(stdTPrivate).isNotEqualTo(lNodePrivate); + PrivateService.copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(stdTPrivate,lNodePrivate); + // Then + TCompasICDHeader result = PrivateService.extractCompasICDHeader(stdTPrivate).get(); + assertThat(result).extracting(TCompasICDHeader::getICDSystemVersionUUID, TCompasICDHeader::getIEDName, + TCompasICDHeader::getIEDinstance, TCompasICDHeader::getBayLabel) + .containsExactlyInAnyOrder("UUID-2", "IED-1", "1", "BAY-1"); + } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapterTest.java index e9c932840..cd1c9dd1f 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/dtt/LNodeTypeAdapterTest.java @@ -207,7 +207,7 @@ void elementXPath() throws Exception { void checkMatchingDOType_shouldFindOneDO() { //Given ExtRefSignalInfo signalInfo = new ExtRefSignalInfo(); - signalInfo.setPDO("P_DO"); + signalInfo.setPDO("P_DO12"); SclRootAdapter sclRootAdapter = new SclRootAdapter("hID","hVersion","hRevision"); sclRootAdapter.getCurrentElem().setDataTypeTemplates(new TDataTypeTemplates()); @@ -232,7 +232,7 @@ void checkMatchingDOType_shouldFindOneDO() { DataTypeTemplateAdapter.DOTypeInfo expectedDoTypeInfo = assertDoesNotThrow(() -> lNodeTypeAdapter.findMatchingDOType(signalInfo)); //Then assertThat(expectedDoTypeInfo.getDoTypeId()).isEqualTo("DO1"); - assertThat(expectedDoTypeInfo.getDoTypeName().getName()).isEqualTo("P_DO"); + assertThat(expectedDoTypeInfo.getDoTypeName().getName()).isEqualTo("P_DO12"); assertThat(expectedDoTypeInfo.getDoTypeAdapter()).isNotNull(); } From be66005d881b7991b62f569aa439e19bc1335d1a Mon Sep 17 00:00:00 2001 From: SaintierFr <99645240+SaintierFr@users.noreply.github.com> Date: Thu, 15 Dec 2022 16:29:59 +0100 Subject: [PATCH 2/3] feat(211): fix DataSet and ControlBlock creation capability check Signed-off-by: massifben <105049157+massifben@users.noreply.github.com> Co-authored-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> Co-authored-by: massifben <105049157+massifben@users.noreply.github.com> --- .github/workflows/sonarcloud-analysis.yml | 4 +- pom.xml | 6 +- .../commons/scl/ied/AbstractLNAdapter.java | 1 + .../sct/commons/scl/ied/IEDAdapter.java | 178 +++++------ .../sct/commons/scl/ied/LDeviceAdapter.java | 72 ++++- .../sct/commons/util/ServiceSettingsType.java | 12 + .../sct/commons/scl/ied/IEDAdapterTest.java | 112 +++---- .../commons/scl/ied/LDeviceAdapterTest.java | 278 +++++++++++++++++- .../ied-test-schema-conf/ied_unit_test.xml | 9 +- 9 files changed, 491 insertions(+), 181 deletions(-) create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServiceSettingsType.java diff --git a/.github/workflows/sonarcloud-analysis.yml b/.github/workflows/sonarcloud-analysis.yml index 68cb1fef6..9a7b0513f 100644 --- a/.github/workflows/sonarcloud-analysis.yml +++ b/.github/workflows/sonarcloud-analysis.yml @@ -20,13 +20,13 @@ jobs: distribution: 'zulu' java-version: '17' - name: Cache SonarCloud packages - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v1 + uses: actions/cache@v3 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} diff --git a/pom.xml b/pom.xml index fad18b7a9..a8de927d6 100644 --- a/pom.xml +++ b/pom.xml @@ -136,7 +136,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.10.1 ${java.version} ${java.version} @@ -189,10 +189,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - org.jacoco jacoco-maven-plugin 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 2571734f4..ac326b2d4 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 @@ -938,4 +938,5 @@ private void removeExtRefSourceBinding(final TExtRef tExtRef) { tExtRef.setSrcLNInst(null); tExtRef.unsetSrcLNClass(); } + } 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 993d5a621..3ac4a9940 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 @@ -16,13 +16,13 @@ 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.ServiceSettingsType; 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.stream.Collectors; import java.util.stream.Stream; /** @@ -52,7 +52,6 @@ *
  • Checklist functions
  • *
      *
    • {@link IEDAdapter#isSettingConfig Check whether IIED contain confSG}
    • - *
    • {@link IEDAdapter#hasDataSetCreationCapability Check the ability to support DataSet Creation}
    • *
    * * @@ -68,6 +67,7 @@ public class IEDAdapter extends SclElementAdapter { /** * Constructor + * * @param parentAdapter Parent container reference */ public IEDAdapter(SclRootAdapter parentAdapter) { @@ -76,8 +76,9 @@ public IEDAdapter(SclRootAdapter parentAdapter) { /** * Constructor + * * @param parentAdapter Parent container reference - * @param currentElem Current reference + * @param currentElem Current reference */ public IEDAdapter(SclRootAdapter parentAdapter, TIED currentElem) { super(parentAdapter, currentElem); @@ -85,21 +86,23 @@ public IEDAdapter(SclRootAdapter parentAdapter, TIED currentElem) { /** * Constructor + * * @param parentAdapter Parent container reference - * @param iedName IED name reference + * @param iedName IED name reference */ public IEDAdapter(SclRootAdapter parentAdapter, String iedName) throws ScdException { super(parentAdapter); TIED ied = parentAdapter.getCurrentElem().getIED() - .stream() - .filter(tied -> tied.getName().equals(iedName)) - .findFirst() - .orElseThrow(() -> new ScdException("Unknown IED name :" + iedName)); + .stream() + .filter(tied -> tied.getName().equals(iedName)) + .findFirst() + .orElseThrow(() -> new ScdException("Unknown IED name :" + iedName)); setCurrentElem(ied); } /** * Check if node is child of the reference node + * * @return link parent child existence */ @Override @@ -114,6 +117,7 @@ protected String elementXPath() { /** * Sets IED name in current IED + * * @param iedName new name to set */ public void setIEDName(String iedName) { @@ -122,59 +126,64 @@ public void setIEDName(String iedName) { /** * Gets All LDevice from current IED as LDeviceAdapter + * * @return Stream of LDeviceAdapter object */ public Stream streamLDeviceAdapters() { return streamLDevices() - .map(tlDevice -> new LDeviceAdapter(this, tlDevice)); + .map(tlDevice -> new LDeviceAdapter(this, tlDevice)); } /** * Gets All LDevice from current IED + * * @return Stream of TLDevice object */ private Stream streamLDevices() { - if (!currentElem.isSetAccessPoint()){ + if (!currentElem.isSetAccessPoint()) { return Stream.empty(); } return currentElem.getAccessPoint().stream() - .map(TAccessPoint::getServer) - .filter(Objects::nonNull) - .filter(TServer::isSetLDevice) - .map(TServer::getLDevice) - .flatMap(List::stream); + .map(TAccessPoint::getServer) + .filter(Objects::nonNull) + .filter(TServer::isSetLDevice) + .map(TServer::getLDevice) + .flatMap(List::stream); } /** * Gets LDevice from current IED by ldInst parameter + * * @param ldInst ldInst value of LDevice to get * @return LDeviceAdapter object * @throws ScdException when LDevice is not found */ public LDeviceAdapter getLDeviceAdapterByLdInst(String ldInst) throws ScdException { return findLDeviceAdapterByLdInst(ldInst) - .orElseThrow(() -> new ScdException(String.format(MESSAGE_LDEVICE_INST_NOT_FOUND, ldInst, getName()))); + .orElseThrow(() -> new ScdException(String.format(MESSAGE_LDEVICE_INST_NOT_FOUND, ldInst, getName()))); } /** * Gets LDevice from current IED by ldInst parameter + * * @param ldInst ldInst value of LDevice to get * @return optional of LDeviceAdapter object */ public Optional findLDeviceAdapterByLdInst(String ldInst) { - if (StringUtils.isBlank(ldInst)){ + if (StringUtils.isBlank(ldInst)) { return Optional.empty(); } return streamLDevices() - .filter(tlDevice -> ldInst.equals(tlDevice.getInst())) - .findFirst() - .map(tlDevice -> new LDeviceAdapter(this, tlDevice)); + .filter(tlDevice -> ldInst.equals(tlDevice.getInst())) + .findFirst() + .map(tlDevice -> new LDeviceAdapter(this, tlDevice)); } /** * Updates all LNode type value specified in pairOldNewId as key (oldID), by corresponding value (newID) * in all LDevice of the current IED. * Update LDevice name by combining IED name and LDevice ldInst value + * * @param pairOldNewId map of old ID and new ID. Old ID to find in LNode Type and replace it with New ID * @throws ScdException throws when renaming LDevice and new name has more than 33 caracteres */ @@ -187,49 +196,52 @@ public void updateLDeviceNodesType(Map pairOldNewId) throws ScdE lDeviceAdapter.getCurrentElem().getLN0().setLnType(pairOldNewId.get(lnType)); } lDeviceAdapter.getCurrentElem() - .getLN() - .forEach(tln -> { - if (pairOldNewId.containsKey(tln.getLnType())) { - tln.setLnType(pairOldNewId.get(tln.getLnType())); - } - }); + .getLN() + .forEach(tln -> { + if (pairOldNewId.containsKey(tln.getLnType())) { + tln.setLnType(pairOldNewId.get(tln.getLnType())); + } + }); }); } /** * Gets Services of current IED + * * @return TServices object */ - public TServices getServices(){ + public TServices getServices() { return currentElem.getServices(); } /** * Gets name of current IED + * * @return string name */ - public String getName(){ + public String getName() { return currentElem.getName(); } /** * Checks if given ObjectReference matches with one of current IED LNode * (ie having common reference with DataSet, ReportControl or DataTypeTemplate) + * * @param objRef reference to compare with LNodes data's * @return Boolean value of check result */ - public boolean matches(ObjectReference objRef){ + public boolean matches(ObjectReference objRef) { if (StringUtils.isBlank(getName()) - || !objRef.getLdName().startsWith(getName())) { + || !objRef.getLdName().startsWith(getName())) { return false; } String ldInst = objRef.getLdName().substring(getName().length()); Optional opLD = findLDeviceAdapterByLdInst(ldInst); - if(opLD.isEmpty()) { + if (opLD.isEmpty()) { return false; } LDeviceAdapter lDeviceAdapter = opLD.get(); - if(TLLN0Enum.LLN_0.value().equals(objRef.getLNodeName())) { + if (TLLN0Enum.LLN_0.value().equals(objRef.getLNodeName())) { return lDeviceAdapter.getLN0Adapter().matches(objRef); } @@ -241,6 +253,7 @@ public boolean matches(ObjectReference objRef){ /** * Checks existence of Access Point in curent IED by name + * * @param apName AccessPoint name to check * @return Boolean value of check result */ @@ -252,25 +265,27 @@ public boolean findAccessPointByName(String apName) { /** * Checks all possible ExtRef in current IED which could be bound to given ExtRef as parameter + * * @param signalInfo ExtRef to bind data * @return list of ExtRefBindingInfo object (containing binding data for each LDevice in current IED) * @throws ScdException throws when ExtRef contains inconsistency data */ public List getExtRefBinders(@NonNull ExtRefSignalInfo signalInfo) throws ScdException { - if(!signalInfo.isValid()){ + if (!signalInfo.isValid()) { throw new ScdException("Invalid ExtRef signal (pDO,pDA or intAddr))"); } return streamLDeviceAdapters() - .map(lDeviceAdapter -> lDeviceAdapter.getExtRefBinders(signalInfo)).flatMap(List::stream) - .collect(Collectors.toList()); + .map(lDeviceAdapter -> lDeviceAdapter.getExtRefBinders(signalInfo)).flatMap(List::stream) + .toList(); } /** * Checks for a given LDevice in current IED if Setting Group is well setted + * * @param ldInst ldInst for LDevice for which Setting Group is checked * @return Boolean value of check result */ - public boolean isSettingConfig(String ldInst) { + public boolean isSettingConfig(String ldInst) { TAccessPoint accessPoint = currentElem.getAccessPoint().stream() .filter(tAccessPoint -> (tAccessPoint.getServer() != null) && @@ -291,21 +306,22 @@ public boolean isSettingConfig(String ldInst) { /** * Adds Data Set in specified LNode in current IED + * * @param dataSetInfo Data Set data to add (and LNode path) + * @param serviceSettingsType Type of DataSet to create * @throws ScdException throws when IED is not able to add DataSet */ - public void createDataSet(DataSetInfo dataSetInfo) throws ScdException { - if(!hasDataSetCreationCapability()){ - throw new ScdException("The capability of IED is not allowing DataSet creation"); - } - + public void createDataSet(DataSetInfo dataSetInfo, ServiceSettingsType serviceSettingsType) throws ScdException { LDeviceAdapter lDeviceAdapter = findLDeviceAdapterByLdInst(dataSetInfo.getHolderLDInst()) - .orElseThrow( - () -> new ScdException( - String.format( - "Unknow LDevice(%s) in IED(%s)",dataSetInfo.getHolderLDInst(),currentElem.getName() ) - ) - ); + .orElseThrow( + () -> new ScdException( + String.format( + "Unknow LDevice(%s) in IED(%s)", dataSetInfo.getHolderLDInst(), currentElem.getName()) + ) + ); + if (!lDeviceAdapter.hasDataSetCreationCapability(serviceSettingsType)) { + throw new ScdException("The AccessPoint does not have capability to create DataSet of type " + serviceSettingsType.toString()); + } AbstractLNAdapter lNodeAdapter = AbstractLNAdapter.builder() .withLDeviceAdapter(lDeviceAdapter) .withLnClass(dataSetInfo.getHolderLnClass()) @@ -315,82 +331,46 @@ public void createDataSet(DataSetInfo dataSetInfo) throws ScdException { lNodeAdapter.addDataSet(dataSetInfo); } - /** - * Checks if IED is able to create new Data Set - * @return Boolean value of check result - */ - protected boolean hasDataSetCreationCapability() { - if(currentElem.getServices() == null){ - return false ; - } - boolean hasCapability = false; - if(currentElem.getServices().getLogSettings() != null){ - hasCapability = (TServiceSettingsEnum.CONF.equals(currentElem.getServices().getLogSettings().getDatSet()) || - TServiceSettingsEnum.DYN.equals(currentElem.getServices().getLogSettings().getDatSet())); - } - if(currentElem.getServices().getGSESettings() != null){ - hasCapability = hasCapability || - (TServiceSettingsEnum.CONF.equals(currentElem.getServices().getGSESettings().getDatSet()) || - TServiceSettingsEnum.DYN.equals(currentElem.getServices().getGSESettings().getDatSet())); - - } - if(currentElem.getServices().getReportSettings() != null){ - hasCapability = hasCapability || - (TServiceSettingsEnum.CONF.equals(currentElem.getServices().getReportSettings().getDatSet()) || - TServiceSettingsEnum.DYN.equals(currentElem.getServices().getReportSettings().getDatSet())); - } - if(currentElem.getServices().getSMVSettings() != null){ - hasCapability = hasCapability || - (TServiceSettingsEnum.CONF.equals(currentElem.getServices().getSMVSettings().getDatSet()) || - TServiceSettingsEnum.DYN.equals(currentElem.getServices().getSMVSettings().getDatSet())); - } - return hasCapability ; - } - /** * Creates Control Block in specified LNode in current IED + * * @param controlBlock Control Block data to add (and LNode path) + * @param serviceSettingsType Type of ControlBlock to create * @return created ControlBlock object * @throws ScdException throws when inconsistency between given ControlBlock and IED configuration */ - public ControlBlock createControlBlock(ControlBlock controlBlock) - throws ScdException { - - TServices tServices = currentElem.getServices(); - TServiceSettingsNoDynEnum cbNameAtt = controlBlock.getControlBlockServiceSetting(tServices); - - if(cbNameAtt != TServiceSettingsNoDynEnum.CONF ){ - throw new ScdException("The IED doesn't support ControlBlock's creation or modification"); - } - - + public ControlBlock createControlBlock(ControlBlock controlBlock, ServiceSettingsType serviceSettingsType) + throws ScdException { controlBlock.validateCB(); controlBlock.validateSecurityEnabledValue(this); controlBlock.validateDestination(this.parentAdapter); LDeviceAdapter lDeviceAdapter = findLDeviceAdapterByLdInst(controlBlock.getHolderLDInst()).orElseThrow(); - AbstractLNAdapter lnAdapter = AbstractLNAdapter.builder() + if (!lDeviceAdapter.hasControlBlockCreationCapability(serviceSettingsType)) { + throw new ScdException("The AccessPoint does not have capability to create ControlBlock of type " + serviceSettingsType.toString()); + } + AbstractLNAdapter lnAdapter = AbstractLNAdapter.builder() .withLDeviceAdapter(lDeviceAdapter) .withLnClass(controlBlock.getHolderLnClass()) .withLnInst(controlBlock.getHolderLnInst()) .withLnPrefix(controlBlock.getHolderLnPrefix()) .build(); - if(lnAdapter.hasControlBlock(controlBlock)){ + if (lnAdapter.hasControlBlock(controlBlock)) { throw new ScdException( String.format( - "Control block %s already exist in LNode %s%s%s", - controlBlock.getName(),lnAdapter.getPrefix(),lnAdapter.getLNClass(),lnAdapter.getLNInst() + "Control block %s already exist in LNode %s%s%s", + controlBlock.getName(), lnAdapter.getPrefix(), lnAdapter.getLNClass(), lnAdapter.getLNInst() ) ); } - if(lnAdapter.getDataSetByRef(controlBlock.getDataSetRef()).isEmpty()){ + if (lnAdapter.getDataSetByRef(controlBlock.getDataSetRef()).isEmpty()) { throw new ScdException( String.format( "Control block %s references unknown dataSet in LNode %s%s%s", - controlBlock.getName(),lnAdapter.getPrefix(),lnAdapter.getLNClass(),lnAdapter.getLNInst() + controlBlock.getName(), lnAdapter.getPrefix(), lnAdapter.getLNClass(), lnAdapter.getLNInst() ) ); } @@ -402,10 +382,11 @@ public ControlBlock createControlBlock(ControlBlockTPrivate object */ - public Optional getPrivateHeader(String privateType){ + public Optional getPrivateHeader(String privateType) { return currentElem.getPrivate() .stream() .filter(tPrivate -> tPrivate.getType().equals(privateType)) @@ -414,9 +395,10 @@ public Optional getPrivateHeader(String privateType){ /** * Extract private compas:Bay + * * @return value of private compas:Bay if present, empty Optional otherwise */ - public Optional getPrivateCompasBay(){ + public Optional getPrivateCompasBay() { return PrivateService.extractCompasPrivate(currentElem, TCompasBay.class); } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java index 962872c8b..c9aa409bf 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapter.java @@ -12,6 +12,7 @@ import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; import org.lfenergy.compas.sct.commons.scl.dtt.DataTypeTemplateAdapter; +import org.lfenergy.compas.sct.commons.util.ServiceSettingsType; import org.lfenergy.compas.sct.commons.util.Utils; import java.util.*; @@ -74,6 +75,23 @@ protected boolean amChildElementRef() { .anyMatch(tlDevice -> currentElem.getInst().equals(tlDevice.getInst())); } + public TAccessPoint getAccessPoint(){ + return parentAdapter.getCurrentElem().getAccessPoint() + .stream() + .filter(accessPoint -> + Optional.ofNullable(accessPoint.getServer()) + .filter(TServer::isSetLDevice) + .map(TServer::getLDevice) + .stream() + .flatMap(List::stream) + .map(TLDevice::getInst) + .anyMatch(inst -> inst.equals(currentElem.getInst())) + ) + .findFirst() + .orElseThrow(() -> new IllegalStateException(String.format("LDevice.inst='%s' not found in parent IED.name='%s'", currentElem.getInst(), + parentAdapter.getName()))); + } + @Override protected String elementXPath() { return String.format("LDevice[%s]", Utils.xpathAttributeFilter("inst", currentElem.isSetInst() ? currentElem.getInst() : null)); @@ -213,7 +231,7 @@ public List getExtRefInfo() { * Gets a list of summarized DataTypeTemplate for DataAttribute DAIs (updatableOnly or not) * @param rDtt reference resumed DataTypeTemplate (used as filter) * @param updatableOnly true to retrieve only updatableOnly DAIs, false to retrieve all DAIs - * @return List of ResumedDataTemplate (updatableOnly or not) + * @return Set of ResumedDataTemplate (updatableOnly or not) * @throws ScdException SCD illegal arguments exception */ public Set getDAI(ResumedDataTemplate rDtt, boolean updatableOnly) throws ScdException { @@ -277,4 +295,56 @@ public Set findSourceDA(TExtRef extRef) { .build(); return getDAI(filter, false); } + + /** + * Checks if parent AccessPoint has DataSet creation capability + * + * @param serviceSettingsType the type of DataSet we want to check for creation capability. + * @return true if parent AccessPoint has the capability, false otherwise + */ + protected boolean hasDataSetCreationCapability(ServiceSettingsType serviceSettingsType) { + Objects.requireNonNull(serviceSettingsType); + TAccessPoint accessPoint = getAccessPoint(); + if (!accessPoint.isSetServices()) { + return false; + } + TServices services = accessPoint.getServices(); + return switch (serviceSettingsType) { + case REPORT -> services.isSetReportSettings() && hasDatSetConfOrDyn(services.getReportSettings()); + case GSE -> services.isSetGSESettings() && hasDatSetConfOrDyn(services.getGSESettings()); + case SMV -> services.isSetSMVSettings() && hasDatSetConfOrDyn(services.getSMVSettings()); + case LOG -> services.isSetLogSettings() && hasDatSetConfOrDyn(services.getLogSettings()); + }; + } + + private boolean hasDatSetConfOrDyn(TServiceSettings tServiceSettings) { + return tServiceSettings.isSetDatSet() + && (TServiceSettingsEnum.CONF.equals(tServiceSettings.getDatSet()) || TServiceSettingsEnum.DYN.equals(tServiceSettings.getDatSet())); + } + + /** + * Checks if parent AccessPoint has ControlBlock creation capability + * + * @param serviceSettingsType the type of ControlBlock we want to check for creation capability. + * @return true if parent AccessPoint has the capability, false otherwise + */ + protected boolean hasControlBlockCreationCapability(ServiceSettingsType serviceSettingsType) { + Objects.requireNonNull(serviceSettingsType); + TAccessPoint accessPoint = getAccessPoint(); + if (!accessPoint.isSetServices()) { + return false; + } + TServices services = accessPoint.getServices(); + return switch (serviceSettingsType) { + case REPORT -> services.isSetReportSettings() && hasCBNameConf(services.getReportSettings()); + case GSE -> services.isSetGSESettings() && hasCBNameConf(services.getGSESettings()); + case SMV -> services.isSetSMVSettings() && hasCBNameConf(services.getSMVSettings()); + case LOG -> services.isSetLogSettings() && hasCBNameConf(services.getLogSettings()); + }; + } + + private boolean hasCBNameConf(TServiceSettings tServiceSettings) { + return tServiceSettings.isSetCbName() + && (TServiceSettingsNoDynEnum.CONF.equals(tServiceSettings.getCbName())); + } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServiceSettingsType.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServiceSettingsType.java new file mode 100644 index 000000000..599161c62 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServiceSettingsType.java @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2022 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.util; + +public enum ServiceSettingsType { + REPORT, + LOG, + GSE, + SMV +} 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 e486660bd..cfda76f69 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 @@ -14,6 +14,7 @@ import org.lfenergy.compas.sct.commons.scl.ObjectReference; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; +import org.lfenergy.compas.sct.commons.util.ServiceSettingsType; import org.mockito.Mockito; import java.util.HashMap; @@ -172,107 +173,86 @@ void testMatches() throws Exception { assertTrue(iAdapter.matches(objectReference)); } - @Test - void testCreateDataSet() throws Exception { + void createDataSet_should_create_when_DataSetInfo_is_complete() 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")); - - assertThrows(ScdException.class, () -> iAdapter.createDataSet(new DataSetInfo())); - - TServices tServices = new TServices(); - iAdapter.getCurrentElem().setServices(tServices); - assertFalse(iAdapter.hasDataSetCreationCapability()); - - TLogSettings tLogSettings = new TLogSettings(); - tServices.setLogSettings(tLogSettings); - tLogSettings.setDatSet(TServiceSettingsEnum.CONF); - assertTrue(iAdapter.hasDataSetCreationCapability()); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); TDataSet tDataSet = new TDataSet(); tDataSet.setName("dataset"); TFCDA tfcda = new TFCDA(); tfcda.setFc(TFCEnum.ST); + tfcda.getLnClass().add("LLN0"); tDataSet.getFCDA().add(tfcda); DataSetInfo dataSetInfo = DataSetInfo.from(tDataSet); dataSetInfo.setHolderIEDName("IED_NAME"); - assertThrows(ScdException.class, () -> iAdapter.createDataSet(dataSetInfo)); dataSetInfo.setHolderLDInst("LD_INS2"); dataSetInfo.setHolderLnClass(TLLN0Enum.LLN_0.value()); - assertDoesNotThrow(() -> iAdapter.createDataSet(dataSetInfo)); + assertIsMarshallable(scd); + + // When + iedAdapter.createDataSet(dataSetInfo, ServiceSettingsType.GSE); + // Then + assertThat(iedAdapter.getLDeviceAdapterByLdInst("LD_INS2").getLN0Adapter().getDataSetByRef("dataset")) + .isPresent(); assertIsMarshallable(scd); } @Test - void testHasDataSetCreationCapability() throws Exception { + void createDataSet_should_throw_ScdException_when_DataSetInfo_is_incomplete() 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")); - assertFalse(iAdapter.hasDataSetCreationCapability()); - - TServices tServices = new TServices(); - iAdapter.getCurrentElem().setServices(tServices); - assertFalse(iAdapter.hasDataSetCreationCapability()); - - TLogSettings tLogSettings = new TLogSettings(); - tServices.setLogSettings(tLogSettings); - tLogSettings.setDatSet(TServiceSettingsEnum.CONF); - assertTrue(iAdapter.hasDataSetCreationCapability()); - tLogSettings.setDatSet(TServiceSettingsEnum.DYN); - assertTrue(iAdapter.hasDataSetCreationCapability()); - tLogSettings.setDatSet(TServiceSettingsEnum.FIX); - assertFalse(iAdapter.hasDataSetCreationCapability()); - - TGSESettings tgseSettings = new TGSESettings(); - tServices.setGSESettings(tgseSettings); - - tgseSettings.setDatSet(TServiceSettingsEnum.CONF); - assertTrue(iAdapter.hasDataSetCreationCapability()); - tgseSettings.setDatSet(TServiceSettingsEnum.DYN); - assertTrue(iAdapter.hasDataSetCreationCapability()); - tgseSettings.setDatSet(TServiceSettingsEnum.FIX); - assertFalse(iAdapter.hasDataSetCreationCapability()); - - TReportSettings reportSettings = new TReportSettings(); - tServices.setReportSettings(reportSettings); - - reportSettings.setDatSet(TServiceSettingsEnum.CONF); - assertTrue(iAdapter.hasDataSetCreationCapability()); - reportSettings.setDatSet(TServiceSettingsEnum.DYN); - assertTrue(iAdapter.hasDataSetCreationCapability()); - reportSettings.setDatSet(TServiceSettingsEnum.FIX); - assertFalse(iAdapter.hasDataSetCreationCapability()); - + TDataSet tDataSet = new TDataSet(); + tDataSet.setName("dataset"); + TFCDA tfcda = new TFCDA(); + tfcda.setFc(TFCEnum.ST); + tDataSet.getFCDA().add(tfcda); + DataSetInfo dataSetInfo = DataSetInfo.from(tDataSet); + dataSetInfo.setHolderIEDName("IED_NAME"); - TSMVSettings tsmvSettings = new TSMVSettings(); - tServices.setSMVSettings(tsmvSettings); + // When & Then + assertThatThrownBy(() -> iAdapter.createDataSet(dataSetInfo, ServiceSettingsType.GSE)) + .isInstanceOf(ScdException.class); + } - tsmvSettings.setDatSet(TServiceSettingsEnum.CONF); - assertTrue(iAdapter.hasDataSetCreationCapability()); - tsmvSettings.setDatSet(TServiceSettingsEnum.DYN); - assertTrue(iAdapter.hasDataSetCreationCapability()); - tsmvSettings.setDatSet(TServiceSettingsEnum.FIX); - assertFalse(iAdapter.hasDataSetCreationCapability()); + @Test + void createDataSet_should_throw_ScdException_when_DataSetInfo_is_empty() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + DataSetInfo dataSetInfo = new DataSetInfo(); + // When & Then + assertThatThrownBy(() -> iAdapter.createDataSet(dataSetInfo, ServiceSettingsType.GSE)) + .isInstanceOf(ScdException.class); } @Test void createControlBlock() 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")); - + IEDAdapter iedAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); ReportControlBlock controlBlock = new ReportControlBlock(); + controlBlock.setId("CBID1"); controlBlock.setName("rpt"); controlBlock.setConfRev(2L); - - controlBlock.setHolderIEDName("IED_NAME"); - controlBlock.setHolderIEDName("IED_NAME"); + controlBlock.setDataSetRef("dataSet"); controlBlock.setHolderIEDName("IED_NAME"); + controlBlock.setHolderLDInst("LD_INS2"); + controlBlock.setHolderLnClass("ANCR"); + controlBlock.setHolderLnInst("1"); + controlBlock.setOptFields(new TReportControl.OptFields()); + // When + iedAdapter.createControlBlock(controlBlock, ServiceSettingsType.GSE); + // Then assertIsMarshallable(scd); } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapterTest.java index 16868ac19..17945d785 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LDeviceAdapterTest.java @@ -4,14 +4,15 @@ package org.lfenergy.compas.sct.commons.scl.ied; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; 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.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.scl2007b4.model.TLDevice; -import org.lfenergy.compas.scl2007b4.model.TLLN0Enum; -import org.lfenergy.compas.scl2007b4.model.TPrivate; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; +import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.DTO; import org.lfenergy.compas.sct.commons.dto.ExtRefInfo; import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo; @@ -19,12 +20,15 @@ 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 org.lfenergy.compas.sct.commons.util.ServiceSettingsType; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.*; +import static org.lfenergy.compas.sct.commons.util.ServiceSettingsType.*; class LDeviceAdapterTest { @@ -173,7 +177,273 @@ void getLNAdaptersInclundigLN0() { .hasSize(2) .hasAtLeastOneElementOfType(LN0Adapter.class) .hasAtLeastOneElementOfType(LNAdapter.class); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("provideHasDataSetCreationCapabilityTrue") + void hasDataSetCreationCapability_should_return_true_when_attribute_exists(String testCase, TServices tServices, ServiceSettingsType serviceSettingsType) throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).setServices(tServices); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst("LD_INS1"); + // When + boolean hasCapability = lDeviceAdapter.hasDataSetCreationCapability(serviceSettingsType); + //Then + assertThat(hasCapability).isTrue(); + } + + private static Stream provideHasDataSetCreationCapabilityTrue() { + TServices tServicesLogConf = new TServices(); + TLogSettings tLogSettingsConf = new TLogSettings(); + tLogSettingsConf.setDatSet(TServiceSettingsEnum.CONF); + tServicesLogConf.setLogSettings(tLogSettingsConf); + + TServices tServicesLogDyn = new TServices(); + TLogSettings tLogSettingsDyn = new TLogSettings(); + tLogSettingsDyn.setDatSet(TServiceSettingsEnum.DYN); + tServicesLogDyn.setLogSettings(tLogSettingsDyn); + + TServices tServicesGseConf = new TServices(); + TGSESettings tgseSettingsConf = new TGSESettings(); + tgseSettingsConf.setDatSet(TServiceSettingsEnum.CONF); + tServicesGseConf.setGSESettings(tgseSettingsConf); + + TServices tServicesGseDyn = new TServices(); + TGSESettings tgseSettingsDyn = new TGSESettings(); + tgseSettingsDyn.setDatSet(TServiceSettingsEnum.DYN); + tServicesGseDyn.setGSESettings(tgseSettingsDyn); + + TServices tServicesReportConf = new TServices(); + TReportSettings treportSettingsConf = new TReportSettings(); + treportSettingsConf.setDatSet(TServiceSettingsEnum.CONF); + tServicesReportConf.setReportSettings(treportSettingsConf); + + TServices tServicesReportDyn = new TServices(); + TReportSettings treportSettingsDyn = new TReportSettings(); + treportSettingsDyn.setDatSet(TServiceSettingsEnum.DYN); + tServicesReportDyn.setReportSettings(treportSettingsDyn); + + TServices tServicesSmvConf = new TServices(); + TSMVSettings tsmvSettingsConf = new TSMVSettings(); + tsmvSettingsConf.setDatSet(TServiceSettingsEnum.CONF); + tServicesSmvConf.setSMVSettings(tsmvSettingsConf); + + TServices tServicesSmvDyn = new TServices(); + TSMVSettings tsmvSettingsDyn = new TSMVSettings(); + tsmvSettingsDyn.setDatSet(TServiceSettingsEnum.DYN); + tServicesSmvDyn.setSMVSettings(tsmvSettingsDyn); + + return Stream.of( + Arguments.of("AccessPoint has creation capability when LogSetting.datSet=CONF", tServicesLogConf, LOG), + Arguments.of("AccessPoint has creation capability when LogSetting.datSet=DYN", tServicesLogDyn, LOG), + Arguments.of("AccessPoint has creation capability when GseSetting.datSet=CONF", tServicesGseConf, GSE), + Arguments.of("AccessPoint has creation capability when GseSetting.datSet=DYN", tServicesGseDyn, GSE), + Arguments.of("AccessPoint has creation capability when ReportSetting.datSet=CONF", tServicesReportConf, REPORT), + Arguments.of("AccessPoint has creation capability when ReportSetting.datSet=DYN", tServicesReportDyn, REPORT), + Arguments.of("AccessPoint has creation capability when SmvSetting.datSet=CONF", tServicesSmvConf, SMV), + Arguments.of("AccessPoint has creation capability when SmvSetting.datSet=DYN", tServicesSmvDyn, SMV) + ); + } + + + @ParameterizedTest(name = "{0}") + @MethodSource("provideHasDataSetCreationCapabilityFalse") + void hasDataSetCreationCapability_should_return_false_when_wrong_attribute(String testCase, TServices tServices, ServiceSettingsType serviceSettingsType) throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).setServices(tServices); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst("LD_INS1"); + + // When + boolean hasCapability = lDeviceAdapter.hasDataSetCreationCapability(serviceSettingsType); + //Then + assertThat(hasCapability).isFalse(); + } + + private static Stream provideHasDataSetCreationCapabilityFalse() { + TServices tServicesLogFix = new TServices(); + TLogSettings tLogSettingsFix = new TLogSettings(); + tLogSettingsFix.setDatSet(TServiceSettingsEnum.FIX); + tServicesLogFix.setLogSettings(tLogSettingsFix); + + TServices tServicesGseFix = new TServices(); + TGSESettings tgseSettingsFix = new TGSESettings(); + tgseSettingsFix.setDatSet(TServiceSettingsEnum.FIX); + tServicesGseFix.setGSESettings(tgseSettingsFix); + + TServices tServicesReportFix = new TServices(); + TReportSettings treportSettingsFix = new TReportSettings(); + treportSettingsFix.setDatSet(TServiceSettingsEnum.FIX); + tServicesReportFix.setReportSettings(treportSettingsFix); + + TServices tServicesSmvFix = new TServices(); + TSMVSettings tsmvSettingsFix = new TSMVSettings(); + tsmvSettingsFix.setDatSet(TServiceSettingsEnum.FIX); + tServicesSmvFix.setSMVSettings(tsmvSettingsFix); + + return Stream.of( + Arguments.of("AccessPoint has no creation capability when LogSetting.datSet=FIX", tServicesLogFix, LOG), + Arguments.of("AccessPoint has no creation capability when GseSetting.datSet=FIX", tServicesGseFix, GSE), + Arguments.of("AccessPoint has no creation capability when ReportSetting.datSet=FIX", tServicesReportFix, REPORT), + Arguments.of("AccessPoint has no creation capability when SmvSetting.datSet=FIX", tServicesSmvFix, SMV) + ); + } + + + @ParameterizedTest + @EnumSource(ServiceSettingsType.class) + void hasDataSetCreationCapability_should_return_false_when_no_existing_services_attribute(ServiceSettingsType serviceSettingsType) throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst("LD_INS1"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).setServices(new TServices()); + + // When + boolean hasCapability = lDeviceAdapter.hasDataSetCreationCapability(serviceSettingsType); + //Then + assertThat(hasCapability).isFalse(); + } + + @Test + void hasDataSetCreationCapability_should_throw_exception_when_parameter_is_null() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst("LD_INS1"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).setServices(new TServices()); + + // When & Then + Assertions.assertThatThrownBy(() -> lDeviceAdapter.hasDataSetCreationCapability(null)) + .isInstanceOf(NullPointerException.class); + } + + + @ParameterizedTest(name = "{0}") + @MethodSource("provideHasControlBlockCreationCapabilityTrue") + void hasControlBlockCreationCapability_should_return_true_when_attribute_exists(String testCase, TServices tServices, ServiceSettingsType serviceSettingsType) throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).setServices(tServices); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst("LD_INS1"); + // When + boolean hasCapability = lDeviceAdapter.hasControlBlockCreationCapability(serviceSettingsType); + //Then + assertThat(hasCapability).isTrue(); + } + + private static Stream provideHasControlBlockCreationCapabilityTrue() { + TServices tServicesLogConf = new TServices(); + TLogSettings tLogSettingsConf = new TLogSettings(); + tLogSettingsConf.setCbName(TServiceSettingsNoDynEnum.CONF); + tServicesLogConf.setLogSettings(tLogSettingsConf); + TServices tServicesGseConf = new TServices(); + TGSESettings tgseSettingsConf = new TGSESettings(); + tgseSettingsConf.setCbName(TServiceSettingsNoDynEnum.CONF); + tServicesGseConf.setGSESettings(tgseSettingsConf); + + TServices tServicesReportConf = new TServices(); + TReportSettings treportSettingsConf = new TReportSettings(); + treportSettingsConf.setCbName(TServiceSettingsNoDynEnum.CONF); + tServicesReportConf.setReportSettings(treportSettingsConf); + + TServices tServicesSmvConf = new TServices(); + TSMVSettings tsmvSettingsConf = new TSMVSettings(); + tsmvSettingsConf.setCbName(TServiceSettingsNoDynEnum.CONF); + tServicesSmvConf.setSMVSettings(tsmvSettingsConf); + + + return Stream.of( + Arguments.of("AccessPoint has creation capability when LogSetting.cbName=CONF", tServicesLogConf, LOG), + Arguments.of("AccessPoint has creation capability when GseSetting.cbName=CONF", tServicesGseConf, GSE), + Arguments.of("AccessPoint has creation capability when ReportSetting.cbName=CONF", tServicesReportConf, REPORT), + Arguments.of("AccessPoint has creation capability when SmvSetting.cbName=CONF", tServicesSmvConf, SMV) + ); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("provideHasControlBlockCreationCapabilityFalse") + void hasControlBlockCreationCapability_should_return_false_when_wrong_attribute(String testCase, TServices tServices, ServiceSettingsType serviceSettingsType) throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).setServices(tServices); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst("LD_INS1"); + + // When + boolean hasCapability = lDeviceAdapter.hasControlBlockCreationCapability(serviceSettingsType); + //Then + assertThat(hasCapability).isFalse(); } + + private static Stream provideHasControlBlockCreationCapabilityFalse() { + TServices tServicesLogFix = new TServices(); + TLogSettings tLogSettingsFix = new TLogSettings(); + tLogSettingsFix.setCbName(TServiceSettingsNoDynEnum.FIX); + tServicesLogFix.setLogSettings(tLogSettingsFix); + + TServices tServicesGseFix = new TServices(); + TGSESettings tgseSettingsFix = new TGSESettings(); + tgseSettingsFix.setCbName(TServiceSettingsNoDynEnum.FIX); + tServicesGseFix.setGSESettings(tgseSettingsFix); + + TServices tServicesReportFix = new TServices(); + TReportSettings treportSettingsFix = new TReportSettings(); + treportSettingsFix.setCbName(TServiceSettingsNoDynEnum.FIX); + tServicesReportFix.setReportSettings(treportSettingsFix); + + TServices tServicesSmvFix = new TServices(); + TSMVSettings tsmvSettingsFix = new TSMVSettings(); + tsmvSettingsFix.setCbName(TServiceSettingsNoDynEnum.FIX); + tServicesSmvFix.setSMVSettings(tsmvSettingsFix); + + return Stream.of( + Arguments.of("AccessPoint has no creation capability when LogSetting.cbName=FIX", tServicesLogFix, LOG), + Arguments.of("AccessPoint has no creation capability when GseSetting.cbName=FIX", tServicesGseFix, GSE), + Arguments.of("AccessPoint has no creation capability when ReportSetting.cbName=FIX", tServicesReportFix, REPORT), + Arguments.of("AccessPoint has no creation capability when SmvSetting.cbName=FIX", tServicesSmvFix, SMV) + ); + } + + @ParameterizedTest + @EnumSource(ServiceSettingsType.class) + void hasControlBlockCreationCapability_should_return_false_when_no_existing_services_attribute(ServiceSettingsType serviceSettingsType) throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst("LD_INS1"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).setServices(new TServices()); + + // When + boolean hasCapability = lDeviceAdapter.hasControlBlockCreationCapability(serviceSettingsType); + //Then + assertThat(hasCapability).isFalse(); + } + + @Test + void hasControlBlockCreationCapability_should_throw_exception_when_parameter_is_null() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst("LD_INS1"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).setServices(new TServices()); + + // When & Then + Assertions.assertThatThrownBy(() -> lDeviceAdapter.hasControlBlockCreationCapability(null)) + .isInstanceOf(NullPointerException.class); + } + } diff --git a/sct-commons/src/test/resources/ied-test-schema-conf/ied_unit_test.xml b/sct-commons/src/test/resources/ied-test-schema-conf/ied_unit_test.xml index 5a68217ef..212d76299 100644 --- a/sct-commons/src/test/resources/ied-test-schema-conf/ied_unit_test.xml +++ b/sct-commons/src/test/resources/ied-test-schema-conf/ied_unit_test.xml @@ -7,7 +7,6 @@ - @@ -27,20 +26,19 @@ - + - - + @@ -62,6 +60,7 @@ + @@ -132,4 +131,4 @@ Va -
    \ No newline at end of file +
    From 545062aa537227b5cb8b8aafa9bc51035e697422 Mon Sep 17 00:00:00 2001 From: massifben <105049157+massifben@users.noreply.github.com> Date: Thu, 29 Dec 2022 18:07:53 +0100 Subject: [PATCH 3/3] feat(84): RSR-433 create datasets from extRef Signed-off-by: massifben <105049157+massifben@users.noreply.github.com> --- .../compas/sct/commons/dto/FCDAInfo.java | 2 +- .../compas/sct/commons/scl/ExtRefService.java | 8 +- .../commons/scl/ied/AbstractLNAdapter.java | 69 +++++--- .../sct/commons/scl/ied/DataSetAdapter.java | 142 ++++++++++++++++ .../sct/commons/scl/ied/IEDAdapter.java | 31 +--- .../sct/commons/scl/ied/InputsAdapter.java | 73 +++++--- .../sct/commons/scl/ied/LN0Adapter.java | 4 +- .../compas/sct/commons/scl/ied/LNAdapter.java | 5 +- .../sct/commons/util/ServiceSettingsType.java | 20 ++- .../compas/sct/commons/util/Utils.java | 43 ++++- .../sct/commons/scl/ExtRefServiceTest.java | 66 +++++++- .../commons/scl/ied/DataSetAdapterTest.java | 113 +++++++++++++ .../sct/commons/scl/ied/IEDAdapterTest.java | 62 ------- .../scl/ied/InputsAdapterForDemoTest.java | 121 -------------- .../commons/scl/ied/InputsAdapterTest.java | 156 +++++++++++++++++- .../sct/commons/scl/ied/LN0AdapterTest.java | 45 +++++ .../sct/commons/scl/ied/LNAdapterTest.java | 25 ++- .../sct/commons/testhelpers/FCDARecord.java | 24 +++ .../sct/commons/testhelpers/SclHelper.java | 34 +++- .../compas/sct/commons/util/UtilsTest.java | 74 +++++++++ .../src/test/resources/FcdaCandidates.csv | 4 + .../src/test/resources/logback-test.xml | 4 +- ...eate_dataset_and_controlblocks_success.xml | 30 +++- ...d_controlblocks_success_test_fcda_sort.xml | 130 +++++++++++++++ .../resources/scl-ln-adapter/scd_with_ln.xml | 72 ++++++++ 25 files changed, 1050 insertions(+), 307 deletions(-) create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DataSetAdapter.java create mode 100644 sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DataSetAdapterTest.java delete mode 100644 sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterForDemoTest.java create mode 100644 sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/FCDARecord.java create mode 100644 sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml create mode 100644 sct-commons/src/test/resources/scl-ln-adapter/scd_with_ln.xml diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/FCDAInfo.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/FCDAInfo.java index 921e43424..453f5b590 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/FCDAInfo.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/FCDAInfo.java @@ -94,7 +94,7 @@ public TFCDA getFCDA(){ } if(doName != null && doName.isDefined()){ - tfcda.setDaName(doName.toString()); + tfcda.setDoName(doName.toString()); } if(daName != null && daName.isDefined()){ 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 b97ea6999..57f516127 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 @@ -53,7 +53,7 @@ public static SclReport updateAllExtRefIedNames(SCL scd) { .filter(LN0Adapter::hasInputs) .map(LN0Adapter::getInputsAdapter) .map(inputsAdapter -> inputsAdapter.updateAllExtRefIedNames(icdSystemVersionToIed)) - .flatMap(List::stream).collect(Collectors.toList()); + .flatMap(List::stream).toList(); return new SclReport(sclRootAdapter, extRefErrors); } @@ -79,7 +79,7 @@ private static List checkIedCompasIcdHeaderAttributes(SclRootAdap return null; } ).filter(Objects::nonNull) - .collect(Collectors.toList()); + .toList(); } private static List checkIedUnityOfIcdSystemVersionUuid(SclRootAdapter sclRootAdapter) { @@ -97,7 +97,7 @@ private static List checkIedUnityOfIcdSystemVersionUuid(SclRootAd .collect(Collectors.joining(", ")), "/IED/Private/compas:ICDHeader[@ICDSystemVersionUUID] must be unique" + " but the same ICDSystemVersionUUID was found on several IED.")) - .collect(Collectors.toList()); + .toList(); } public static SclReport createDataSetAndControlBlocks(SCL scd) { @@ -127,7 +127,7 @@ private static SclReport createDataSetAndControlBlocks(SclRootAdapter sclRootAda List sclReportItems = lDeviceAdapters .map(LDeviceAdapter::createDataSetAndControlBlocks) .flatMap(List::stream) - .collect(Collectors.toList()); + .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 ac326b2d4..26e1977a1 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 @@ -16,6 +16,7 @@ import org.lfenergy.compas.sct.commons.scl.dtt.DataTypeTemplateAdapter; import org.lfenergy.compas.sct.commons.scl.dtt.EnumTypeAdapter; import org.lfenergy.compas.sct.commons.scl.dtt.LNodeTypeAdapter; +import org.lfenergy.compas.sct.commons.util.ServiceSettingsType; import java.util.*; import java.util.stream.Collectors; @@ -46,7 +47,7 @@ *
  • {@link AbstractLNAdapter#getDAI Returns the value of the ResumedDataTemplate containment reference By filter}
  • *
  • {@link AbstractLNAdapter#getDAIValues(ResumedDataTemplate) Returns DAI (sGroup, value) containment reference list By ResumedDataTemplate filter}
  • * - *
  • {@link AbstractLNAdapter#getDataSetByRef(String) Returns the value of the TDataSet object reference By the value of the name attribute }
  • + *
  • {@link AbstractLNAdapter#getDataSetByName(String) Returns the value of the TDataSet object reference By the value of the name attribute }
  • * *
  • {@link AbstractLNAdapter#getControlBlocks(List, TServiceType) Returns the value of the ControlBlock containment reference list that match datSet value of given TDataSet }
  • *
  • {@link AbstractLNAdapter#addPrivate Add TPrivate under this object}
  • @@ -140,11 +141,12 @@ protected void addControlBlock(ControlBlock controlBlock) throws ScdException } } - public Optional findDataSetByRef(String dataSetRef) { + public Optional findDataSetByName(String dataSetName) { return currentElem.getDataSet() .stream() - .filter(tDataSet -> Objects.equals(tDataSet.getName(), dataSetRef)) - .findFirst(); + .filter(tDataSet -> Objects.equals(tDataSet.getName(), dataSetName)) + .findFirst() + .map(dataSet -> new DataSetAdapter(this, dataSet)); } public DOIAdapter getDOIAdapterByName(String doiName) throws ScdException { @@ -345,9 +347,8 @@ public List> getControlBlocksForMatchingFCDA(@NonNull ExtRefInfo */ protected List> getControlBlocks(List tDataSets, TServiceType serviceType) { return tDataSets.stream() - .map(tDataSet -> getControlBlocksByDataSetRef(tDataSet.getName(), serviceType)) - .flatMap(Collection::stream).toList(); - + .map(tDataSet -> getControlBlocksByDataSetRef(tDataSet.getName(), serviceType)) + .flatMap(Collection::stream).toList(); } /** @@ -655,14 +656,22 @@ private boolean iedHasConfSG() { return iedAdapter.isSettingConfig(this.parentAdapter.getInst()); } + /** + * Gets linked LDevice as parent + * + * @return IEDAdapter object + */ + private LDeviceAdapter getCurrentLDevice() { + return this.parentAdapter; + } + /** * Gets linked IED as parent * * @return IEDAdapter object */ private IEDAdapter getCurrentIed() { - LDeviceAdapter lDeviceAdapter = this.parentAdapter; - return lDeviceAdapter.getParentAdapter(); + return getCurrentLDevice().getParentAdapter(); } /** @@ -854,32 +863,17 @@ protected boolean matchesDataAttributes(String dataAttribute) { /** * Gets Data Set in LNode by its name * - * @param dataSetRef Data Set name + * @param dataSetName Data Set name * @return optional of DataSetInfo */ - public Optional getDataSetByRef(String dataSetRef) { + public Optional getDataSetByName(String dataSetName) { return currentElem.getDataSet() .stream() - .filter(tDataSet -> tDataSet.getName().equals(dataSetRef)) + .filter(tDataSet -> tDataSet.getName().equals(dataSetName)) .map(DataSetInfo::from) .findFirst(); } - /** - * Adds Data Set to LNode Data Sets - * - * @param dataSetInfo data's of Data Set to add - */ - public void addDataSet(DataSetInfo dataSetInfo) { - TDataSet tDataSet = new TDataSet(); - tDataSet.setName(dataSetInfo.getName()); - tDataSet.getFCDA().addAll( - dataSetInfo.getFCDAInfos().stream().map(FCDAInfo::getFCDA).toList() - ); - currentElem.getDataSet().add(tDataSet); - - } - /** * Gets DAI values for specified DA in summaraized Data Type Template * @@ -939,4 +933,25 @@ private void removeExtRefSourceBinding(final TExtRef tExtRef) { tExtRef.unsetSrcLNClass(); } + /** + * Adds DataSet in specified LNode in current IED/AccessPoint. + * The AccessPoint must have DataSet creation capabilities + * + * @param dataSetName Name of the dataSet + * @param serviceSettingsType GOOSE, SMV or REPORT service type + * @throws ScdException throws when IED does not have DataSet creation capabilities + * @see LDeviceAdapter#hasDataSetCreationCapability + */ + public DataSetAdapter createDataSetIfNotExists(String dataSetName, ServiceSettingsType serviceSettingsType) { + return findDataSetByName(dataSetName).orElseGet(() -> { + if (!getCurrentLDevice().hasDataSetCreationCapability(serviceSettingsType)) { + throw new ScdException("IED/AccessPoint does not have capability to create DataSet of type %s in %s" + .formatted(serviceSettingsType, getXPath())); + } + TDataSet newDataSet = new TDataSet(); + newDataSet.setName(dataSetName); + currentElem.getDataSet().add(newDataSet); + return new DataSetAdapter(this, newDataSet); + }); + } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DataSetAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DataSetAdapter.java new file mode 100644 index 000000000..229c606c3 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DataSetAdapter.java @@ -0,0 +1,142 @@ +// SPDX-FileCopyrightText: 2022 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.scl.ied; + + +import org.apache.commons.lang3.StringUtils; +import org.lfenergy.compas.scl2007b4.model.TAnyLN; +import org.lfenergy.compas.scl2007b4.model.TDataSet; +import org.lfenergy.compas.scl2007b4.model.TFCDA; +import org.lfenergy.compas.scl2007b4.model.TFCEnum; +import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; +import org.lfenergy.compas.sct.commons.util.Utils; + +import java.util.Comparator; +import java.util.Objects; +import java.util.Optional; + +import static org.lfenergy.compas.sct.commons.util.Utils.*; + +/** + * A representation of the model object + * {@link DataSetAdapter DataSetAdapter}. + *

    + * The following features are supported: + *

    + *
      + *
    1. Principal functions
    2. + *
        + *
      • {@link DataSetAdapter#findFCDA look for a FCDA in this DataSet}
      • + *
      • {@link DataSetAdapter#createFCDAIfNotExists create a FCDA in this DataSet
      • + *
      + *
    + *
    + *
    + *      ObjectReference: DataSet
    + *      LDName = "name" key of the DataSet in the parent LN/LN0
    + *  
    + * + * @see TLN0 + * @see AbstractLNAdapter + */ +public class DataSetAdapter extends SclElementAdapter, TDataSet> { + + /** + * IEC 61850 requires FCDA to be oredred inside a DataSet. + * This is the comparator to sort the FCDA inside a DataSet + */ + private static final Comparator fcdaComparator = Comparator + .comparing(TFCDA::getLdInst, Utils::blanksFirstComparator) + .thenComparing(TFCDA::getPrefix, Utils::blanksFirstComparator) + .thenComparing(tfcda -> tfcda.isSetLnClass() ? tfcda.getLnClass().get(0) : null, Utils::blanksFirstComparator) + .thenComparing(tfcda -> tfcda.getLnInst() != null ? Integer.valueOf(tfcda.getLnInst()) : null, Comparator.nullsFirst(Integer::compareTo)) + .thenComparing(TFCDA::getDoName, Utils::blanksFirstComparator) + .thenComparing(TFCDA::getDaName, Utils::blanksFirstComparator); + + public DataSetAdapter(AbstractLNAdapter parentAdapter, TDataSet dataSet) { + super(parentAdapter, dataSet); + } + + /** + * Check if node is child of the reference node + * + * @return link parent child existence + */ + @Override + protected boolean amChildElementRef() { + return parentAdapter.getCurrentElem().isSetDataSet() && parentAdapter.getCurrentElem().getDataSet().stream().anyMatch(dataSet -> + Objects.equals(currentElem.getName(), dataSet.getName())); + } + + /** + * Returns local XPath + * @return XPath for current element (not including parent XPath) + */ + @Override + protected String elementXPath() { + return String.format("DataSet[%s]", xpathAttributeFilter("name", currentElem.getName())); + } + + /** + * Find a FCDA matching all given criteria. + * @param ldInst FCDA ldInst attribute + * @param prefix FCDA prefix attribute + * @param lnClass FCDA lnClass attribute + * @param lnInst FCDA lnInst attribute + * @param doName FCDA doName attribute + * @param daName FCDA daNae attribute + * @param fc FCDA fc attribute + * @return Matching FCDA in this DataSet when found, empty Optional otherwise. + */ + public Optional findFCDA(String ldInst, String prefix, String lnClass, String lnInst, String doName, String daName, TFCEnum fc) { + if (!currentElem.isSetFCDA()) { + return Optional.empty(); + } + return currentElem.getFCDA().stream() + .filter(tfcda -> + Objects.equals(ldInst, tfcda.getLdInst()) + && equalsOrBothBlank(prefix, tfcda.getPrefix()) + && tfcda.getLnClass().contains(lnClass) + && equalsOrBothBlank(lnInst, tfcda.getLnInst()) + && Objects.equals(doName, tfcda.getDoName()) + && Objects.equals(fc, tfcda.getFc()) + && equalsOrBothBlank(daName, tfcda.getDaName())) + .findFirst(); + } + + /** + * Create a new FCDA in this DataSet. + * Does nothing if a FCDA with the given attribute already exists in this DataSet. + * @param ldInst FCDA ldInst attribute + * @param prefix FCDA prefix attribute + * @param lnClass FCDA lnClass attribute + * @param lnInst FCDA lnInst attribute + * @param doName FCDA doName attribute + * @param daName FCDA daNae attribute + * @param fc FCDA fc attribute + * @return created FCDA, or existing FCDA with the given attributes + */ + public TFCDA createFCDAIfNotExists(String ldInst, String prefix, String lnClass, String lnInst, String doName, String daName, TFCEnum fc) { + Objects.requireNonNull(fc); // fc is required by XSD + Optional fcda = findFCDA(ldInst, prefix, lnClass, lnInst, doName, daName, fc); + return fcda + .orElseGet(() -> { + TFCDA newFcda = new TFCDA(); + newFcda.setLdInst(nullIfBlank(ldInst)); + newFcda.setPrefix(nullIfBlank(prefix)); + if (StringUtils.isNotBlank(lnClass)) { + newFcda.getLnClass().add(lnClass); + } + newFcda.setLnInst(nullIfBlank(lnInst)); + newFcda.setDoName(nullIfBlank(doName)); + newFcda.setDaName(nullIfBlank(daName)); + newFcda.setFc(fc); + currentElem.getFCDA().add(newFcda); + currentElem.getFCDA().sort(fcdaComparator); + return newFcda; + }); + } + +} 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 3ac4a9940..a286361c1 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 @@ -8,7 +8,6 @@ import org.apache.commons.lang3.StringUtils; import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.ControlBlock; -import org.lfenergy.compas.sct.commons.dto.DataSetInfo; import org.lfenergy.compas.sct.commons.dto.ExtRefBindingInfo; import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo; import org.lfenergy.compas.sct.commons.exception.ScdException; @@ -44,7 +43,6 @@ *
  • {@link IEDAdapter#getServices Returns the value of the Service object}
  • *
  • {@link IEDAdapter#getPrivateHeader Returns the value of the TPrivate containment reference list}
  • *
  • {@link IEDAdapter#getExtRefBinders Returns the value of the ExtRefBindingInfo containment reference list By ExtRefSignalInfo}
  • - *
  • {@link IEDAdapter#createDataSet Add DataSetInfo describing the children TDataSet that can be created under TAnyLN}
  • *
  • {@link IEDAdapter#createControlBlock Add ControlBlock describing the children TControlBlock that can be created under TAnyLN}
  • *
  • {@link IEDAdapter#addPrivate Add TPrivate under this object}
  • *
  • {@link IEDAdapter#updateLDeviceNodesType Update Type describing the value of the children TAnyLN}
  • @@ -304,33 +302,6 @@ public boolean isSettingConfig(String ldInst) { return srv != null && srv.getSettingGroups() != null && srv.getSettingGroups().getConfSG() != null; } - /** - * Adds Data Set in specified LNode in current IED - * - * @param dataSetInfo Data Set data to add (and LNode path) - * @param serviceSettingsType Type of DataSet to create - * @throws ScdException throws when IED is not able to add DataSet - */ - public void createDataSet(DataSetInfo dataSetInfo, ServiceSettingsType serviceSettingsType) throws ScdException { - LDeviceAdapter lDeviceAdapter = findLDeviceAdapterByLdInst(dataSetInfo.getHolderLDInst()) - .orElseThrow( - () -> new ScdException( - String.format( - "Unknow LDevice(%s) in IED(%s)", dataSetInfo.getHolderLDInst(), currentElem.getName()) - ) - ); - if (!lDeviceAdapter.hasDataSetCreationCapability(serviceSettingsType)) { - throw new ScdException("The AccessPoint does not have capability to create DataSet of type " + serviceSettingsType.toString()); - } - AbstractLNAdapter lNodeAdapter = AbstractLNAdapter.builder() - .withLDeviceAdapter(lDeviceAdapter) - .withLnClass(dataSetInfo.getHolderLnClass()) - .withLnInst(dataSetInfo.getHolderLnInst()) - .withLnPrefix(dataSetInfo.getHolderLnPrefix()) - .build(); - lNodeAdapter.addDataSet(dataSetInfo); - } - /** * Creates Control Block in specified LNode in current IED * @@ -366,7 +337,7 @@ public ControlBlock createControlBlock(ControlBlock updateAllExtRefIedNames(Map icdSy return List.of(getLDeviceAdapter().buildFatalReportItem("The LDevice status is undefined")); } String lDeviceStatus = optionalLDeviceStatus.get(); - switch (lDeviceStatus) { - case ON: + return switch (lDeviceStatus) { + case ON -> { List compasFlows = PrivateService.extractCompasPrivates(currentElem, TCompasFlow.class); - return getExtRefs().stream() + yield getExtRefs().stream() .filter(tExtRef -> StringUtils.isNotBlank(tExtRef.getIedName()) && StringUtils.isNotBlank(tExtRef.getDesc())) .map(extRef -> updateExtRefIedName(extRef, compasFlows, icdSystemVersionToIed.get(extRef.getIedName()))) .flatMap(Optional::stream) .toList(); - case OFF: + } + case OFF -> { getExtRefs().forEach(this::clearBinding); - return Collections.emptyList(); - default: - return List.of(getLDeviceAdapter() - .buildFatalReportItem("The LDevice status is neither \"on\" nor \"off\"")); - } + yield Collections.emptyList(); + } + default -> List.of(getLDeviceAdapter().buildFatalReportItem("The LDevice status is neither \"on\" nor \"off\"")); + }; } /** @@ -289,10 +290,8 @@ private Optional updateSourceDataSetsAndControlBlocks(TExtRef ext if (sourceIedBayUuid.isEmpty()) { return fatalReportItem(extRef, "Source IED is missing Private/compas:Bay@UUID attribute"); } - //TODO: use isBayInternal in issue #84 RSR-433 and remove print to console + boolean isBayInternal = targetBayUuid.equals(sourceIedBayUuid.get()); - System.out.print("\nExtRef.desc=" + extRef.getDesc()); - System.out.print(",isBayInternal=" + isBayInternal); Optional optionalSourceLDevice = sourceIed.findLDeviceAdapterByLdInst(extRef.getLdInst()); if (optionalSourceLDevice.isEmpty()) { @@ -305,30 +304,56 @@ private Optional updateSourceDataSetsAndControlBlocks(TExtRef ext } Optional sclReportItem = removeFilteredSourceDas(extRef, sourceDas); - //TODO: #83 follow-up : grouping ExtRef by serviceType, iedName, ldInst, isBayInternal, and DA@fc - // will be done by calculating DataSet Name in #84 RSR-433 - // TODO: map to FCDA in issue #84 RSR-433 and remove print to console - String daToPrint = sourceDas.stream() - .map(da -> da.getFc() + "#" + da.getDataAttributes()) - .collect(Collectors.joining(",")); - System.out.print("," + (daToPrint.isEmpty() ? "--NO_VALID_SOURCE_DA--" : daToPrint)); - return sclReportItem; + if (sclReportItem.isPresent()) { + return sclReportItem; + } + + try { + sourceDas.forEach(sourceDa -> { + String dataSetName = generateDataSetName(extRef, sourceDa, isBayInternal); + ServiceSettingsType serviceSettingsType = ServiceSettingsType.fromTServiceType(extRef.getServiceType()); + DataSetAdapter dataSet = sourceLDevice.getLN0Adapter().createDataSetIfNotExists(dataSetName, serviceSettingsType); + createFCDAInDataSet(extRef, sourceDa, dataSet); + }); + } catch (ScdException e) { + // ScdException can be thrown if AccessPoint does not have DataSet creation capability + log.error(e.getMessage(), e); + return fatalReportItem(extRef, "Could not create DataSet for this ExtRef : " + e.getMessage()); + } + return Optional.empty(); + } + + private static String generateDataSetName(TExtRef extRef, ResumedDataTemplate sourceDa, boolean isBayInternal) { + return "DS_" + extRef.getLdInst() + "_" + + switch (extRef.getServiceType()) { + case GOOSE -> "G" + ((sourceDa.getFc() == TFCEnum.ST) ? "S" : "M"); + case SMV -> "SV"; + case REPORT -> (sourceDa.getFc() == TFCEnum.ST) ? "DQC" : "CYC"; + case POLL -> throw new IllegalArgumentException("only GOOSE, SMV and REPORT ServiceType are allowed"); + } + + (isBayInternal ? "I" : "E"); + } + + private static void createFCDAInDataSet(TExtRef extRef, ResumedDataTemplate sourceDa, DataSetAdapter dataSet) { + dataSet.createFCDAIfNotExists(extRef.getLdInst(), extRef.getPrefix(), extRef.getLnClass().stream().findFirst().orElse(null), extRef.getLnInst(), + sourceDa.getDoRef(), + (extRef.getServiceType() == TServiceType.REPORT ? null : sourceDa.getDaRef()), + sourceDa.getFc()); } private Optional removeFilteredSourceDas(TExtRef extRef, final Set sourceDas) { - //TODO: Don't forget to test this in issue #84 RSR-433 sourceDas.removeIf(da -> da.getFc() != TFCEnum.MX && da.getFc() != TFCEnum.ST); return switch (extRef.getServiceType()) { case GOOSE, SMV -> { sourceDas.removeIf(Predicate.not(FcdaCandidates.SINGLETON::contains)); yield Optional.empty(); } - case REPORT -> filterReportSourceDa(extRef, sourceDas); + case REPORT -> removeFilterSourceDaForReport(extRef, sourceDas); default -> fatalReportItem(extRef, String.format(MESSAGE_INVALID_SERVICE_TYPE, extRef.getServiceType())); }; } - private Optional filterReportSourceDa(TExtRef extRef, Set sourceDas) { + private Optional removeFilterSourceDaForReport(TExtRef extRef, Set sourceDas) { String daName = Utils.extractField(extRef.getDesc(), EXTREF_DESC_DELIMITER, EXTREF_DESC_DA_NAME_POSITION); if (StringUtils.isBlank(daName)) { return fatalReportItem(extRef, MESSAGE_REPORT_EXTREF_DESC_MALFORMED); 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 5173e0656..efc341581 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 @@ -43,10 +43,8 @@ *
  • {@link LN0Adapter#getDAI Returns the value of the ResumedDataTemplate containment reference By filter}
  • *
  • {@link LN0Adapter#getDAIValues(ResumedDataTemplate) Returns DAI (sGroup, value) containment reference list By ResumedDataTemplate filter}
  • * - *
  • {@link LN0Adapter#getDataSet(ExtRefInfo) Returns the value of the TDataSet containment reference list By ExtRefInfo }
  • - *
  • {@link LN0Adapter#getDataSetByRef(String) Returns the value of the TDataSet object reference By the value of the name attribute }
  • + *
  • {@link LN0Adapter#getDataSetByName(String) Returns the value of the TDataSet object reference By the value of the name attribute }
  • * - *
  • {@link LN0Adapter#getControlSetByExtRefInfo(ExtRefInfo) Returns the value of the ControlBlock containment reference list By ExtRefInfo }
  • *
  • {@link LN0Adapter#getControlBlocks(List, TServiceType) Returns the value of the ControlBlock containment reference list that match datSet value of given TDataSet }
  • *
  • {@link LN0Adapter#addPrivate Add TPrivate under this object}
  • *
  • {@link LN0Adapter#removeAllControlBlocksAndDatasets() Remove all ControlBlock}
  • diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapter.java index 9268e214c..d960ea939 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapter.java @@ -6,7 +6,6 @@ import org.lfenergy.compas.scl2007b4.model.TLN; import org.lfenergy.compas.scl2007b4.model.TServiceType; -import org.lfenergy.compas.sct.commons.dto.ExtRefInfo; import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo; import org.lfenergy.compas.sct.commons.dto.ResumedDataTemplate; import org.lfenergy.compas.sct.commons.scl.ObjectReference; @@ -40,10 +39,8 @@ *
  • {@link LNAdapter#getDAI Returns the value of the ResumedDataTemplate containment reference By filter}
  • *
  • {@link LNAdapter#getDAIValues(ResumedDataTemplate) Returns DAI (sGroup, value) containment reference list By ResumedDataTemplate filter}
  • - *
  • {@link LNAdapter#getDataSet(ExtRefInfo) Returns the value of the TDataSet containment reference list By ExtRefInfo }
  • - *
  • {@link LNAdapter#getDataSetByRef(String) Returns the value of the TDataSet object reference By the value of the name attribute }
  • + *
  • {@link LNAdapter#getDataSetByName(String) Returns the value of the TDataSet object reference By the value of the name attribute }
  • * - *
  • {@link LNAdapter#getControlSetByExtRefInfo(ExtRefInfo) Returns the value of the ControlBlock containment reference list By ExtRefInfo }
  • *
  • {@link LNAdapter#getControlBlocks(List, TServiceType) Returns the value of the ControlBlock containment reference list that match datSet value of given TDataSet }
  • *
  • {@link LNAdapter#addPrivate Add TPrivate under this object}
  • *
  • {@link LNAdapter#removeAllControlBlocksAndDatasets() Remove all ControlBlock}
  • diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServiceSettingsType.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServiceSettingsType.java index 599161c62..33edf3b26 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServiceSettingsType.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServiceSettingsType.java @@ -4,9 +4,23 @@ package org.lfenergy.compas.sct.commons.util; +import org.lfenergy.compas.scl2007b4.model.TServiceType; + public enum ServiceSettingsType { - REPORT, - LOG, GSE, - SMV + SMV, + REPORT, + LOG; + + public static ServiceSettingsType fromTServiceType(TServiceType tServiceType){ + if (tServiceType == null) { + return null; + } + return switch (tServiceType) { + case GOOSE -> GSE; + case SMV -> SMV; + case REPORT -> REPORT; + default -> null; + }; + } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java index 95f49821f..1ba1cde79 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.StringUtils; import java.util.Collection; +import java.util.Comparator; import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; @@ -16,9 +17,12 @@ public final class Utils { private static final String LEAVING_PREFIX = "<<< Leaving: ::"; private static final String ENTERING_PREFIX = ">>> Entering: ::"; + private static final int S1_CONSIDERED_EQUALS_TO_S2 = 0; + private static final int S1_LOWER_THAN_S2 = -1; + private static final int S1_GREATER_THAN_S2 = 1; /** - * Private Controlller, should not be instanced + * Private Constructor, should not be instanced */ private Utils() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); @@ -128,7 +132,7 @@ public static String xpathAttributeFilter(String name, Collection value) /** * Checks if strings are equals or both blank. - * Blank means : null, empty string or whitespaces only string. + * Blank means : null, empty string or whitespaces (as defined by {@link Character#isWhitespace(char)}) only string. * * @param s1 first string * @param s2 seconde string @@ -140,6 +144,37 @@ public static boolean equalsOrBothBlank(String s1, String s2) { || (StringUtils.isBlank(s1) && StringUtils.isBlank(s2)); } + /** + * Comparator for String + * Difference with {@link String#compare(CharSequence, CharSequence)} is that + * blank strings are considered equals and inferior to any not blank String. + * Blank means : null, empty string or whitespaces (as defined by {@link Character#isWhitespace(char)}) only string. + * Note: this comparator imposes orderings that are inconsistent with equals. + * + * @param s1 first String to compare + * @param s2 second String to compare + * @return when s1 and s2 are not blank, same result as {@link String#compare(CharSequence, CharSequence)}, + * zero when s1 and s2 are both blanks, negative integer when s1 is blank and s2 is not, positive integer when s1 is not blank but s2 is. + * @see java.util.Comparator#compare(Object, Object) + * @see org.apache.commons.lang3.StringUtils#isBlank(CharSequence) + * @see java.util.Comparator#nullsFirst(Comparator) + */ + public static int blanksFirstComparator(String s1, String s2) { + if (StringUtils.isBlank(s1)){ + if (StringUtils.isBlank(s2)){ + return S1_CONSIDERED_EQUALS_TO_S2; + } else { + return S1_LOWER_THAN_S2; + } + } else { + if (StringUtils.isBlank(s2)){ + return S1_GREATER_THAN_S2; + } else { + return s1.compareTo(s2); + } + } + } + /** * Remove all digits at the end of the string, if any * @param s input string @@ -175,4 +210,8 @@ public static String extractField(String s, String regexDelimiter, int index) { int column = index < 0 ? split.length + index : index; return 0 <= column && column < split.length ? split[column] : null; } + + public static String nullIfBlank(String s) { + return StringUtils.isBlank(s) ? null : s; + } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java index 0b6b44ed5..eabf3278b 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java @@ -4,19 +4,19 @@ package org.lfenergy.compas.sct.commons.scl; +import org.assertj.core.groups.Tuple; 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.MethodSource; -import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.scl2007b4.model.TCompasFlow; -import org.lfenergy.compas.scl2007b4.model.TExtRef; -import org.lfenergy.compas.scl2007b4.model.TInputs; +import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.SclReport; import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.exception.ScdException; +import org.lfenergy.compas.sct.commons.scl.ied.DataSetAdapter; import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter; import org.lfenergy.compas.sct.commons.scl.ied.LDeviceAdapter; +import org.lfenergy.compas.sct.commons.testhelpers.FCDARecord; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; import java.util.Optional; @@ -24,8 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findExtRef; -import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findLDevice; +import static org.lfenergy.compas.scl2007b4.model.TFCEnum.ST; +import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*; class ExtRefServiceTest { @@ -204,8 +204,27 @@ void createDataSetAndControlBlocks_should_succeed() throws Exception { // When SclReport sclReport = ExtRefService.createDataSetAndControlBlocks(scd); // Then - //TODO: This is only the first part. Test will be updated in issue #84 assertThat(sclReport.getSclReportItems()).isEmpty(); + assertThat(streamAllDataSets(sclReport.getSclRootAdapter())).hasSize(6); + + // Check dataSet names + findDataSet(sclReport.getSclRootAdapter(), "IED_NAME2", "LD_INST21", "DS_LD_INST21_CYCI"); + findDataSet(sclReport.getSclRootAdapter(), "IED_NAME2", "LD_INST21", "DS_LD_INST21_DQCI"); + findDataSet(sclReport.getSclRootAdapter(), "IED_NAME2", "LD_INST21", "DS_LD_INST21_GMI"); + findDataSet(sclReport.getSclRootAdapter(), "IED_NAME2", "LD_INST21", "DS_LD_INST21_SVI"); + findDataSet(sclReport.getSclRootAdapter(), "IED_NAME3", "LD_INST31", "DS_LD_INST31_GSE"); + findDataSet(sclReport.getSclRootAdapter(), "IED_NAME2", "LD_INST21", "DS_LD_INST21_GSI"); + + // Check one DataSet content + DataSetAdapter aDataSet = findDataSet(sclReport.getSclRootAdapter(), "IED_NAME2", "LD_INST21", "DS_LD_INST21_GSI"); + assertThat(aDataSet.getCurrentElem().getFCDA()).hasSize(4); + assertThat(aDataSet.getCurrentElem().getFCDA().stream().map(FCDARecord::toFCDARecord)) + .containsExactly( + new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", "daNameST", ST), + new FCDARecord("LD_INST21", "ANCR", "1", "", "DoWithInst1", "daNameST", ST), + new FCDARecord("LD_INST21", "ANCR", "1", "", "DoWithInst2.subDo", "daNameST", ST), + new FCDARecord("LD_INST21", "ANCR", "1", "", "OtherDoName", "daNameST", ST) + ); } @Test @@ -215,8 +234,19 @@ void createDataSetAndControlBlocks_when_targetIedName_is_provided_should_succeed // When SclReport sclReport = ExtRefService.createDataSetAndControlBlocks(scd, "IED_NAME1"); // Then - //TODO: This is only the first part. Test will be updated in issue #84 assertThat(sclReport.getSclReportItems()).isEmpty(); + assertThat(streamAllDataSets(sclReport.getSclRootAdapter())).hasSize(6); + } + + @Test + void createDataSetAndControlBlocks_when_targetIedName_is_provided_and_no_ext_ref_should_do_nothing() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); + // When + SclReport sclReport = ExtRefService.createDataSetAndControlBlocks(scd, "IED_NAME2"); + // Then + assertThat(sclReport.getSclReportItems()).isEmpty(); + assertThat(streamAllDataSets(sclReport.getSclRootAdapter())).isEmpty(); } @Test @@ -294,4 +324,24 @@ private LDeviceAdapter getLDeviceByLdName(String ldName, SclRootAdapter sclRootA return optionalLDeviceAdapter.get(); } + @Test + void updateAllSourceDataSetsAndControlBlocks_should_sort_FCDA_inside_DataSet_and_avoid_duplicates() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + // When + SclReport sclReport = ExtRefService.createDataSetAndControlBlocks(scd); + // Then + assertThat(sclReport.getSclReportItems()).isEmpty(); + DataSetAdapter dataSetAdapter = findDataSet(sclRootAdapter, "IED_NAME2", "LD_INST21", "DS_LD_INST21_GSI"); + assertThat(dataSetAdapter.getCurrentElem().getFCDA()) + .map(TFCDA::getLnInst, TFCDA::getDoName) + .containsExactly( + Tuple.tuple("1", "FirstDo"), + Tuple.tuple("1", "SecondDo"), + Tuple.tuple("1", "ThirdDo"), + Tuple.tuple("02", "FirstDo") + ); + } + } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DataSetAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DataSetAdapterTest.java new file mode 100644 index 000000000..30c159b6e --- /dev/null +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DataSetAdapterTest.java @@ -0,0 +1,113 @@ +// SPDX-FileCopyrightText: 2022 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.scl.ied; + +import org.junit.jupiter.api.Test; +import org.lfenergy.compas.scl2007b4.model.LN0; +import org.lfenergy.compas.scl2007b4.model.TDataSet; +import org.lfenergy.compas.scl2007b4.model.TFCDA; +import org.lfenergy.compas.scl2007b4.model.TFCEnum; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +class DataSetAdapterTest { + + @Test + void amChildElementRef_should_succeed() { + //Given + LN0 ln0 = new LN0(); + TDataSet dataSet = new TDataSet(); + ln0.getDataSet().add(dataSet); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); + DataSetAdapter dataSetAdapter = new DataSetAdapter(ln0Adapter, dataSet); + //When + boolean result = dataSetAdapter.amChildElementRef(); + //Then + assertThat(result).isTrue(); + } + + @Test + void elementXPath_should_return_relative_xpath() { + //Given + TDataSet dataSet = new TDataSet(); + dataSet.setName("dataSetName"); + DataSetAdapter dataSetAdapter = new DataSetAdapter(null, dataSet); + //When + String elementXPath = dataSetAdapter.elementXPath(); + //Then, + assertThat(elementXPath).isEqualTo("DataSet[@name=\"dataSetName\"]"); + } + + @Test + void findFCDA_should_return_found_FCDA() { + //Given + TFCDA fcda = createFCDA(); + TDataSet dataSet = new TDataSet(); + dataSet.getFCDA().add(fcda); + DataSetAdapter dataSetAdapter = new DataSetAdapter(null, dataSet); + //When + Optional result = dataSetAdapter.findFCDA("LDINST", null, "LLN0", null, "DoName", "daName", TFCEnum.ST); + //Then + assertThat(result).isPresent(); + } + + @Test + void findFCDA_when_no_FCDA_found_should_return_empty() { + //Given + TDataSet dataSet = new TDataSet(); + DataSetAdapter dataSetAdapter = new DataSetAdapter(null, dataSet); + //When + Optional result = dataSetAdapter.findFCDA("any", "any", "any", "any", "any", "any", TFCEnum.ST); + //Then + assertThat(result).isEmpty(); + } + + @Test + void createFCDAIfNotExists_should_create_new_FCDA() { + //Given + TDataSet dataSet = new TDataSet(); + DataSetAdapter dataSetAdapter = new DataSetAdapter(null, dataSet); + //When + TFCDA result = dataSetAdapter.createFCDAIfNotExists("LDINST", null, "LLN0", null, "DoName", "daName", TFCEnum.ST); + //Then + assertThat(dataSet.getFCDA()).hasSize(1) + .first() + .isSameAs(result); + assertThat(dataSet.getFCDA().get(0)) + .extracting(TFCDA::getLdInst, TFCDA::isSetPrefix, TFCDA::getLnClass, TFCDA::isSetLnInst, TFCDA::getDoName, TFCDA::getDaName, TFCDA::getFc) + .containsExactly("LDINST", false, List.of("LLN0"), false, "DoName", "daName", TFCEnum.ST); + } + + @Test + void createFCDAIfNotExists_when_FCDA_already_exists_should_not_create_FCDA() { + //Given + TDataSet dataSet = new TDataSet(); + TFCDA existingFcda = createFCDA(); + dataSet.getFCDA().add(existingFcda); + DataSetAdapter dataSetAdapter = new DataSetAdapter(null, dataSet); + //When + TFCDA result = dataSetAdapter.createFCDAIfNotExists("LDINST", null, "LLN0", null, "DoName", "daName", TFCEnum.ST); + //Then + assertThat(dataSet.getFCDA()).hasSize(1) + .first() + .isSameAs(result) + .isSameAs(existingFcda); + } + + private static TFCDA createFCDA() { + TFCDA existingFcda = new TFCDA(); + existingFcda.setLdInst("LDINST"); + existingFcda.setPrefix(""); + existingFcda.getLnClass().add("LLN0"); + existingFcda.setLnInst(""); + existingFcda.setDoName("DoName"); + existingFcda.setDaName("daName"); + existingFcda.setFc(TFCEnum.ST); + return existingFcda; + } +} 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 cfda76f69..038251791 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 @@ -7,7 +7,6 @@ import org.junit.jupiter.api.Test; import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.DTO; -import org.lfenergy.compas.sct.commons.dto.DataSetInfo; import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo; import org.lfenergy.compas.sct.commons.dto.ReportControlBlock; import org.lfenergy.compas.sct.commons.exception.ScdException; @@ -173,67 +172,6 @@ void testMatches() throws Exception { assertTrue(iAdapter.matches(objectReference)); } - @Test - void createDataSet_should_create_when_DataSetInfo_is_complete() throws Exception { - // Given - SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); - - TDataSet tDataSet = new TDataSet(); - tDataSet.setName("dataset"); - TFCDA tfcda = new TFCDA(); - tfcda.setFc(TFCEnum.ST); - tfcda.getLnClass().add("LLN0"); - tDataSet.getFCDA().add(tfcda); - DataSetInfo dataSetInfo = DataSetInfo.from(tDataSet); - dataSetInfo.setHolderIEDName("IED_NAME"); - dataSetInfo.setHolderLDInst("LD_INS2"); - dataSetInfo.setHolderLnClass(TLLN0Enum.LLN_0.value()); - assertIsMarshallable(scd); - - // When - iedAdapter.createDataSet(dataSetInfo, ServiceSettingsType.GSE); - - // Then - assertThat(iedAdapter.getLDeviceAdapterByLdInst("LD_INS2").getLN0Adapter().getDataSetByRef("dataset")) - .isPresent(); - assertIsMarshallable(scd); - } - - @Test - void createDataSet_should_throw_ScdException_when_DataSetInfo_is_incomplete() 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")); - - TDataSet tDataSet = new TDataSet(); - tDataSet.setName("dataset"); - TFCDA tfcda = new TFCDA(); - tfcda.setFc(TFCEnum.ST); - tDataSet.getFCDA().add(tfcda); - DataSetInfo dataSetInfo = DataSetInfo.from(tDataSet); - dataSetInfo.setHolderIEDName("IED_NAME"); - - // When & Then - assertThatThrownBy(() -> iAdapter.createDataSet(dataSetInfo, ServiceSettingsType.GSE)) - .isInstanceOf(ScdException.class); - } - - @Test - void createDataSet_should_throw_ScdException_when_DataSetInfo_is_empty() throws Exception { - // Given - SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - IEDAdapter iAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); - DataSetInfo dataSetInfo = new DataSetInfo(); - - // When & Then - assertThatThrownBy(() -> iAdapter.createDataSet(dataSetInfo, ServiceSettingsType.GSE)) - .isInstanceOf(ScdException.class); - } - @Test void createControlBlock() throws Exception { // Given diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterForDemoTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterForDemoTest.java deleted file mode 100644 index a4d95cab4..000000000 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterForDemoTest.java +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-FileCopyrightText: 2022 RTE FRANCE -// -// SPDX-License-Identifier: Apache-2.0 - -package org.lfenergy.compas.sct.commons.scl.ied; - -import org.apache.commons.lang3.StringUtils; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.lfenergy.compas.scl2007b4.model.SCL; -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 java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Named.named; -import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findInputs; - -class InputsAdapterForDemoTest { - public static final String NO_VALID_SOURCE_DA = "--NO_VALID_SOURCE_DA--"; - private final PrintStream originalOut = System.out; - private ByteArrayOutputStream outContent; - - @BeforeEach - public void setUpStreams() { - outContent = new ByteArrayOutputStream(); - System.setOut(new PrintStream(outContent)); - } - - @AfterEach - public void restoreStreams() { - System.setOut(originalOut); - System.out.println(outContent.toString(StandardCharsets.UTF_8)); - } - - @ParameterizedTest(name = "{0}") - @MethodSource("provideUpdateAllSourceDataSetsAndControlBlocks") - void updateAllSourceDataSetsAndControlBlocks_should_filter_expected_value(String... expected) throws Exception { - // Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - InputsAdapter inputsAdapter = findInputs(sclRootAdapter, "IED_NAME1", "LD_INST11"); - // When - List sclReportItems = inputsAdapter.updateAllSourceDataSetsAndControlBlocks(); - // Then - assertThat(sclReportItems).isEmpty(); - assertThat(Arrays.stream(outContent.toString(StandardCharsets.UTF_8).split("\n")).filter(StringUtils::isNotBlank)) - .contains(expected); - } - - @ParameterizedTest(name = "{0}") - @MethodSource("provideUpdateAllSourceDataSetsAndControlBlocksIgnoredExtRef") - void updateAllSourceDataSetsAndControlBlocks_should_ignore_extref(String... unexpectedExtRefDesc) throws Exception { - // Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - InputsAdapter inputsAdapter = findInputs(sclRootAdapter, "IED_NAME1", "LD_INST11"); - // When - List sclReportItems = inputsAdapter.updateAllSourceDataSetsAndControlBlocks(); - // Then - assertThat(sclReportItems).isEmpty(); - assertThat(outContent.toString()) - .doesNotContain(unexpectedExtRefDesc); - } - - public static Stream provideUpdateAllSourceDataSetsAndControlBlocks() { - return Stream.of( - Arguments.of(named("should define if signal is internal or external to a Bay", - new String[]{ - "ExtRef.desc=test bay internal,isBayInternal=true," - + "ST#DoName.daNameST", - "ExtRef.desc=test bay external,isBayInternal=false," - + "ST#DoName.daNameST"})), - Arguments.of(named("should keep only fc = ST or MX source DA", - new String[]{ - "ExtRef.desc=test daName ST,isBayInternal=true," - + "ST#DoName.daNameST", - "ExtRef.desc=test daName MX,isBayInternal=true," - + "MX#DoName.daNameMX", - "ExtRef.desc=test daName BL,isBayInternal=true," - + NO_VALID_SOURCE_DA})), - Arguments.of(named("for GOOSE and SMV, should keep only valid fcda candidates", - new String[]{ - "ExtRef.desc=test ServiceType is GOOSE, no daName and DO contains ST and MX, but only ST is FCDA candidate,isBayInternal=true," - + "ST#OtherDoName.daNameST", - "ExtRef.desc=test ServiceType is SMV, no daName and DO contains ST and MX, but only ST is FCDA candidate,isBayInternal=true," - + "ST#OtherDoName.daNameST"})), - Arguments.of(named("for Report, should ignore source DA", - new String[]{ - "ExtRef.desc=test ServiceType is Report_daReportST_1,isBayInternal=true," - + "ST#DoName.daReportST"})), - Arguments.of(named("should ignore instance number when checking FCDA Candidate file", - new String[]{ - "ExtRef.desc=test no daName and doName with instance number,isBayInternal=true," - + "ST#DoWithInst1.daNameST", - "ExtRef.desc=test no daName and doName with instance number and SDO,isBayInternal=true," - + "ST#DoWithInst2.subDo.daNameST"})) - ); - } - - public static Stream provideUpdateAllSourceDataSetsAndControlBlocksIgnoredExtRef() { - return Stream.of( - Arguments.of(named("should ignore binding internal to IED", - new String[]{"test ignore internal binding"})), - Arguments.of(named("should ignore extref with missing binding attributes", - new String[]{"test ignore missing bindings attributes"})), - Arguments.of(named("should ignore extref when compas:Flow.FlowStatus is neither ACTIVE nor UNTESTED", - new String[]{"test ignore when compas:Flow.FlowStatus is neither ACTIVE nor UNTESTED"})) - ); - } -} 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 141617be8..c7682544d 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,18 +5,25 @@ package org.lfenergy.compas.sct.commons.scl.ied; import org.junit.jupiter.api.Test; -import org.lfenergy.compas.scl2007b4.model.LN0; -import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.scl2007b4.model.TInputs; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; +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.opentest4j.AssertionFailedError; import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findInputs; +import static org.junit.jupiter.api.Named.named; +import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*; class InputsAdapterTest { @@ -105,7 +112,148 @@ void updateAllSourceDataSetsAndControlBlocks_should_succeed() throws Exception { List sclReportItems = inputsAdapter.updateAllSourceDataSetsAndControlBlocks(); // Then assertThat(sclReportItems).isEmpty(); + System.out.println(MarshallerWrapper.marshall(scd)); } + @ParameterizedTest + @MethodSource("provideCreateFCDA") + void updateAllSourceDataSetsAndControlBlocks_should_create_dataset_and_fcda_for_valid_extRef(String extRefDesc, String dataSetPath, + List expectedFcda) throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + InputsAdapter inputsAdapter = keepOnlyThisExtRef(sclRootAdapter, extRefDesc); + String[] splitPath = dataSetPath.split("/"); + final int IED_NAME_PART = 0; + final int LDEVICE_INST_PART = 1; + final int DATASET_NAME_PART = 2; + String expectedSourceIedName = splitPath[IED_NAME_PART]; + String expectedSourceLDeviceInst = splitPath[LDEVICE_INST_PART]; + String expectedDataSetName = splitPath[DATASET_NAME_PART]; + // When + List sclReportItems = inputsAdapter.updateAllSourceDataSetsAndControlBlocks(); + // Then + assertThat(sclReportItems).isEmpty(); + DataSetAdapter dataSet = findDataSet(sclRootAdapter, expectedSourceIedName, expectedSourceLDeviceInst, expectedDataSetName); + assertThat(dataSet.getCurrentElem().getFCDA()) + .extracting(TFCDA::getLdInst) + .containsOnly(expectedSourceLDeviceInst); + + assertThat(dataSet.getCurrentElem().getFCDA()) + .map(FCDARecord::toFCDARecord) + .containsExactly(expectedFcda.toArray(new FCDARecord[]{})); + } + + public static Stream provideCreateFCDA() { + return Stream.of( + Arguments.of(named("should include signal internal to a Bay", + "test bay internal"), + "IED_NAME2/LD_INST21/DS_LD_INST21_GSI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", "daNameST", TFCEnum.ST))), + Arguments.of(named("should include signal external to a Bay", + "test bay external"), + "IED_NAME3/LD_INST31/DS_LD_INST31_GSE", + List.of(new FCDARecord("LD_INST31", "ANCR", "1", "", "DoName", "daNameST", TFCEnum.ST))), + Arguments.of(named("keep source DA with fc = ST", + "test daName ST"), + "IED_NAME2/LD_INST21/DS_LD_INST21_GSI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", "daNameST", TFCEnum.ST))), + Arguments.of(named("keep source DA with fc = MX", + "test daName MX"), + "IED_NAME2/LD_INST21/DS_LD_INST21_GMI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", "daNameMX", TFCEnum.MX))), + Arguments.of(named("for GOOSE, should keep only valid fcda candidates", + "test ServiceType is GOOSE, no daName and DO contains ST and MX, but only ST is FCDA candidate"), + "IED_NAME2/LD_INST21/DS_LD_INST21_GSI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "OtherDoName", "daNameST", TFCEnum.ST))), + Arguments.of(named("for SMV, should keep only valid fcda candidates", + "test ServiceType is SMV, no daName and DO contains ST and MX, but only ST is FCDA candidate"), + "IED_NAME2/LD_INST21/DS_LD_INST21_SVI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "OtherDoName", "daNameST", TFCEnum.ST))), + Arguments.of(named("for Report, should get source daName from ExtRef.desc to deduce FC ST", + "test ServiceType is Report_daReportST_1"), + "IED_NAME2/LD_INST21/DS_LD_INST21_DQCI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", null, TFCEnum.ST))), + Arguments.of(named("for Report, should get source daName from ExtRef.desc to deduce FC MX", + "test ServiceType is Report_daReportMX_1"), + "IED_NAME2/LD_INST21/DS_LD_INST21_CYCI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", null, TFCEnum.MX))), + Arguments.of(named("should ignore instance number when checking FCDA Candidate file", + "test no daName and doName with instance number"), + "IED_NAME2/LD_INST21/DS_LD_INST21_GSI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "DoWithInst1", "daNameST", TFCEnum.ST))), + Arguments.of(named("should ignore instance number when checking FCDA Candidate file (DO with SDO)", + "test no daName and doName with instance number and SDO"), + "IED_NAME2/LD_INST21/DS_LD_INST21_GSI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "DoWithInst2.subDo", "daNameST", TFCEnum.ST))), + Arguments.of(named("hould include UNTESTED FlowStatus", + "test include compas:Flow.FlowStatus UNTESTED"), + "IED_NAME2/LD_INST21/DS_LD_INST21_GSI", + List.of(new FCDARecord("LD_INST21", "ANCR", "1", "", "DoName", "daNameST", TFCEnum.ST))) + ); + } + + @ParameterizedTest + @MethodSource("provideDoNotCreateFCDA") + void updateAllSourceDataSetsAndControlBlocks_should_not_create_FCDA_when_no_valid_source_Da_found(String extRefDesc) throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + InputsAdapter inputsAdapter = keepOnlyThisExtRef(sclRootAdapter, extRefDesc); + // When + List sclReportItems = inputsAdapter.updateAllSourceDataSetsAndControlBlocks(); + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(sclRootAdapter.streamIEDAdapters() + .flatMap(IEDAdapter::streamLDeviceAdapters) + .filter(LDeviceAdapter::hasLN0) + .map(LDeviceAdapter::getLN0Adapter)) + .allMatch(ln0Adapter -> !ln0Adapter.getCurrentElem().isSetDataSet()); + } + + public static Stream provideDoNotCreateFCDA() { + return Stream.of( + Arguments.of(named("should not create FCDA for source Da different from MX and ST", + "test daName BL")), + Arguments.of(named("should not create FCDA for extref with a binding internal to the IED", + "test ignore internal binding")), + Arguments.of(named("should not create FCDA for extref with missing binding attributes", + "test ignore missing bindings attributes")), + Arguments.of(named("should not create FCDA for ExtRef with compas:Flow.FlowStatus INACTIVE", + "test ignore when compas:Flow.FlowStatus is neither ACTIVE nor UNTESTED")) + ); + } + + @Test + void updateAllSourceDataSetsAndControlBlocks_when_AceessPoint_does_not_have_dataset_creation_capability_should_report_error() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + InputsAdapter inputsAdapter = keepOnlyThisExtRef(sclRootAdapter, "test bay internal"); + TExtRef extRef = inputsAdapter.getCurrentElem().getExtRef().get(0); + LDeviceAdapter sourceLDevice = findLDevice(sclRootAdapter, extRef.getIedName(), extRef.getLdInst()); + sourceLDevice.getAccessPoint().setServices(new TServices()); + // When + List sclReportItems = inputsAdapter.updateAllSourceDataSetsAndControlBlocks(); + // Then + assertThat(sclReportItems).hasSize(1) + .first().extracting(SclReportItem::getMessage).asString() + .startsWith("Could not create DataSet for this ExtRef : IED/AccessPoint does not have capability to create DataSet of type GSE"); + } + + private static InputsAdapter keepOnlyThisExtRef(SclRootAdapter sclRootAdapter, String extRefDesc) { + InputsAdapter foundInputsAdapter = sclRootAdapter.streamIEDAdapters() + .flatMap(IEDAdapter::streamLDeviceAdapters) + .filter(LDeviceAdapter::hasLN0) + .map(LDeviceAdapter::getLN0Adapter) + .filter(AbstractLNAdapter::hasInputs) + .map(LN0Adapter::getInputsAdapter) + .filter(inputsAdapter -> + inputsAdapter.getCurrentElem().getExtRef().stream().map(TExtRef::getDesc).anyMatch(extRefDesc::equals)) + .findFirst() + .orElseThrow(() -> new AssertionFailedError("ExtRef not found: " + extRefDesc)); + foundInputsAdapter.getCurrentElem().getExtRef().removeIf(Predicate.not(extref -> extRefDesc.equals(extref.getDesc()))); + return foundInputsAdapter; + } } 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 97ae4b083..312c9566b 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 @@ -10,6 +10,7 @@ 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 org.lfenergy.compas.sct.commons.util.ServiceSettingsType; import org.mockito.ArgumentMatchers; import org.mockito.Mockito; @@ -20,6 +21,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.lfenergy.compas.sct.commons.testhelpers.SclHelper.findLn0; class LN0AdapterTest { @@ -521,4 +523,47 @@ void getLDeviceStatus_should_succeed() throws Exception { .isPresent() .hasValue("test"); } + + @Test + void createDataSetIfNotExists_should_create_dataSet() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml"); + LN0Adapter ln0 = findLn0(new SclRootAdapter(scd), "IED_NAME1", "LD_INST11"); + assertThat(ln0.getCurrentElem().getDataSet()).isEmpty(); + // When + DataSetAdapter newDataSet = ln0.createDataSetIfNotExists("newDataSet", ServiceSettingsType.GSE); + // Then + assertThat(newDataSet.getCurrentElem().getName()).isEqualTo("newDataSet"); + assertThat(newDataSet.getParentAdapter().getParentAdapter().getInst()).isEqualTo("LD_INST11"); + assertThat(ln0.getCurrentElem().getDataSet()) + .map(TDataSet::getName) + .containsExactly("newDataSet"); + } + + @Test + void createDataSetIfNotExists_when_dataset_exists_should_not_create_dataset() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml"); + LN0Adapter ln0 = findLn0(new SclRootAdapter(scd), "IED_NAME1", "LD_INST12"); + assertThat(ln0.getCurrentElem().getDataSet()).hasSize(1); + // When + DataSetAdapter newDataSet = ln0.createDataSetIfNotExists("existingDataSet", ServiceSettingsType.GSE); + // Then + assertThat(ln0.getCurrentElem().getDataSet()).hasSize(1) + .map(TDataSet::getName) + .containsExactly("existingDataSet"); + assertThat(newDataSet.getCurrentElem().getName()).isEqualTo("existingDataSet"); + assertThat(newDataSet.getParentAdapter().getParentAdapter().getInst()).isEqualTo("LD_INST12"); + } + + @Test + void createDataSetIfNotExists_when_ied_does_not_have_creation_capabilities_should_throw_exception() throws Exception { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml"); + LN0Adapter ln0 = findLn0(new SclRootAdapter(scd), "IED_NAME2", "LD_INST21"); + // When & Then + assertThatThrownBy(() -> ln0.createDataSetIfNotExists("existingDataSet", ServiceSettingsType.GSE)) + .isInstanceOf(ScdException.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 dc5f526ec..62e08e8d2 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 @@ -17,6 +17,7 @@ import org.mockito.Mockito; import java.util.List; +import java.util.Optional; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -64,17 +65,31 @@ void testGetExtRefs() { } @Test - void testFindDataSetByRef() { + void findDataSetByName_should_return_dataset() { + // Given TLN tln = new TLN(); LNAdapter lnAdapter = initLNAdapter(tln); - - assertTrue(lnAdapter.findDataSetByRef(DTO.CB_DATASET_REF).isEmpty()); TDataSet tDataSet = new TDataSet(); tDataSet.setName(DTO.CB_DATASET_REF); tln.getDataSet().add(tDataSet); + // Then + Optional foundDataSet = lnAdapter.findDataSetByName(DTO.CB_DATASET_REF); + // When + assertThat(foundDataSet).isPresent(); + } - assertTrue(lnAdapter.findDataSetByRef(DTO.CB_DATASET_REF).isPresent()); - + @Test + void findDataSetByName_when_not_found_should_return_empty() { + // Given + TLN tln = new TLN(); + LNAdapter lnAdapter = initLNAdapter(tln); + TDataSet tDataSet = new TDataSet(); + tDataSet.setName(DTO.CB_DATASET_REF); + tln.getDataSet().add(tDataSet); + // Then + Optional foundDataSet = lnAdapter.findDataSetByName("Non existent dataset"); + // When + assertThat(foundDataSet).isEmpty(); } @Test diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/FCDARecord.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/FCDARecord.java new file mode 100644 index 000000000..0441846bf --- /dev/null +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/FCDARecord.java @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2022 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.testhelpers; + +import org.lfenergy.compas.scl2007b4.model.TFCDA; +import org.lfenergy.compas.scl2007b4.model.TFCEnum; + +/** + * Record to easily compare FCDA with AssertJ (xjc does not implement an equals method for TFCDA class) + */ +public record FCDARecord(String ldInst, String lnClass, String lnInst, String prefix, String doName, String daName, TFCEnum fc) { + static public FCDARecord toFCDARecord(TFCDA tfcda) { + return new FCDARecord( + tfcda.getLdInst(), + tfcda.isSetLnClass() ? tfcda.getLnClass().get(0) : null, + tfcda.getLnInst(), + tfcda.getPrefix(), + tfcda.getDoName(), + tfcda.getDaName(), + tfcda.getFc()); + } +} 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 7fb0dcca1..f7703b620 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 @@ -5,14 +5,16 @@ package org.lfenergy.compas.sct.commons.testhelpers; import lombok.experimental.UtilityClass; +import org.lfenergy.compas.scl2007b4.model.TDataSet; import org.lfenergy.compas.scl2007b4.model.TExtRef; import org.lfenergy.compas.sct.commons.dto.SclReport; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; -import org.lfenergy.compas.sct.commons.scl.ied.InputsAdapter; -import org.lfenergy.compas.sct.commons.scl.ied.LDeviceAdapter; -import org.lfenergy.compas.sct.commons.scl.ied.LN0Adapter; +import org.lfenergy.compas.sct.commons.scl.ied.*; import org.opentest4j.AssertionFailedError; +import java.util.List; +import java.util.stream.Stream; + /** * Provides static methods to quickly retrieve SCL elements, to be used in writing tests. * Methods throw AssertionFailedError when element not found (equivalent of calling JUnit Assert.fail()) @@ -20,7 +22,7 @@ */ @UtilityClass public class SclHelper { - private static LDeviceAdapter findLDevice(SclRootAdapter sclRootAdapter, String iedName, String ldInst) { + public static LDeviceAdapter findLDevice(SclRootAdapter sclRootAdapter, String iedName, String ldInst) { return sclRootAdapter.findIedAdapterByName(iedName) .orElseThrow(() -> new AssertionFailedError(String.format("IED.name=%s not found", iedName))).findLDeviceAdapterByLdInst(ldInst).orElseThrow(() -> new AssertionFailedError(String.format("LDevice.inst=%s not found in IED.name=%s", ldInst, iedName))); } @@ -55,4 +57,28 @@ public static LDeviceAdapter findLDevice(SclReport sclReport, String iedName, St return findLDevice(sclReport.getSclRootAdapter(), iedName, ldInst); } + public static LN0Adapter findLn0(SclRootAdapter sclRootAdapter, String iedName, String ldInst) { + LDeviceAdapter lDevice = findLDevice(sclRootAdapter, iedName, ldInst); + if (!lDevice.hasLN0()) { + throw new AssertionFailedError(String.format("LN0 not found in IED.name=%s,LDevice.inst=%s", iedName, ldInst)); + } + return lDevice.getLN0Adapter(); + } + + public static DataSetAdapter findDataSet(SclRootAdapter sclRootAdapter, String iedName, String ldInst, String dataSetName) { + LN0Adapter ln0 = findLn0(sclRootAdapter, iedName, ldInst); + return ln0.findDataSetByName(dataSetName) + .orElseThrow(() -> new AssertionFailedError(String.format("DataSet.name=%s not found id in IED.name=%s,LDevice.inst=%s,LN0", + dataSetName, iedName, ldInst))); + } + + public static Stream streamAllDataSets(SclRootAdapter sclRootAdapter) { + return sclRootAdapter + .streamIEDAdapters() + .flatMap(IEDAdapter::streamLDeviceAdapters) + .filter(LDeviceAdapter::hasLN0) + .map(LDeviceAdapter::getLN0Adapter) + .map(ln0Adapter -> ln0Adapter.getCurrentElem().getDataSet()) + .flatMap(List::stream); + } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/UtilsTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/UtilsTest.java index ec0a5ee97..730c81daf 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/UtilsTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/UtilsTest.java @@ -16,6 +16,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.junit.jupiter.api.Named.named; class UtilsTest { @@ -283,4 +284,77 @@ public static Stream provideExtractField() { Arguments.of("a_b_c", -4, null) ); } + + @ParameterizedTest + @MethodSource("provideBlankFirstComparatorSource") + void blankFirstComparator_should_be_false(String s1, String s2, int expectedResult) { + // Given : parameters + // When + int result = Utils.blanksFirstComparator(s1, s2); + // Then + assertThat(Integer.signum(result)).isEqualTo(Integer.signum(expectedResult)); + } + + public static Stream provideBlankFirstComparatorSource() { + final int EQUALS = 0; + final int GREATER_THAN = 1; + final int LOWER_THAN = -1; + + return Stream.of( + // Standard String.compare when not blank + Arguments.of("a", "a", EQUALS), + Arguments.of("a", "b", LOWER_THAN), + Arguments.of("b", "a", GREATER_THAN), + + // Blank equality + Arguments.of(" ", " ", EQUALS), + Arguments.of(" ", null, EQUALS), + Arguments.of(" ", "", EQUALS), + Arguments.of("", " ", EQUALS), + Arguments.of("", null, EQUALS), + Arguments.of("", "", EQUALS), + Arguments.of(null, " ", EQUALS), + Arguments.of(null, null, EQUALS), + Arguments.of(null, "", EQUALS), + + // Not blank compared to blank + Arguments.of("a", " ", GREATER_THAN), + Arguments.of(" ", "a", LOWER_THAN), + Arguments.of(" ", "b", LOWER_THAN), // transitivity " " < "a" && "a" < "b" => " " < "b" + Arguments.of("a", "", GREATER_THAN), + Arguments.of("", "a", LOWER_THAN), + Arguments.of("", "b", LOWER_THAN), // transitivity "" < "a" && "a" < "b" => "" < "b" + Arguments.of("a", null, GREATER_THAN), + Arguments.of(null, "a", LOWER_THAN), + Arguments.of(null, "b", LOWER_THAN) // transitivity null < "a" && "a" < "b" => null < "b" + ); + } + + @ParameterizedTest + @MethodSource("provideNullIfBlankSource") + void nullIfBlank_when_input_is_blank_should_return_null(String input){ + // Given : Parameter + // When + String result = Utils.nullIfBlank(input); + // Then + assertThat(result).isNull(); + } + + public static Stream provideNullIfBlankSource() { + return Stream.of( + Arguments.of(named("null", null)), + Arguments.of(""), + Arguments.of(" ") + ); + } + + @Test + void nullIfBlank_when_input_is_not_blank_should_return_same_string(){ + // Given + String input = "a"; + // When + String result = Utils.nullIfBlank(input); + // Then + assertThat(result).isEqualTo(input); + } } diff --git a/sct-commons/src/test/resources/FcdaCandidates.csv b/sct-commons/src/test/resources/FcdaCandidates.csv index 3a38c1ff8..6596b7fb6 100644 --- a/sct-commons/src/test/resources/FcdaCandidates.csv +++ b/sct-commons/src/test/resources/FcdaCandidates.csv @@ -9,6 +9,10 @@ ANCR;OtherDoName;daNameST;ST ANCR;DoWithInst;daNameST;ST ANCR;DoWithInst.subDo;daNameST;ST +ANCR;FirstDo;daNameST;ST +ANCR;SecondDo;daNameST;ST +ANCR;ThirdDo;daNameST;ST + LPHD;PhyHealth;stVal;ST LPHD;PhyHealth;q;ST LPHD;PhyHealth;t;ST diff --git a/sct-commons/src/test/resources/logback-test.xml b/sct-commons/src/test/resources/logback-test.xml index 00154e9de..958da6d33 100644 --- a/sct-commons/src/test/resources/logback-test.xml +++ b/sct-commons/src/test/resources/logback-test.xml @@ -7,7 +7,7 @@ - [%green(%t)] %highlight(%-5level) %yellow(%C{1.}): %msg%n%throwable + [%green(%t)] %highlight(%-5level) %yellow(%C): %msg%n%throwable @@ -19,4 +19,4 @@ - \ 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.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml index a1baab1fc..7fb3536a4 100644 --- a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml +++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml @@ -32,8 +32,10 @@ + + @@ -48,16 +50,20 @@ - + - + + + - + + + @@ -102,6 +108,13 @@ + + + + 5 + + + @@ -111,6 +124,9 @@ + + + @@ -125,6 +141,13 @@ + + + + 5 + + + @@ -145,6 +168,7 @@ + diff --git a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml 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_test_fcda_sort.xml @@ -0,0 +1,130 @@ + + + + + + +
    + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + + + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + + diff --git a/sct-commons/src/test/resources/scl-ln-adapter/scd_with_ln.xml b/sct-commons/src/test/resources/scl-ln-adapter/scd_with_ln.xml new file mode 100644 index 000000000..cf46334bb --- /dev/null +++ b/sct-commons/src/test/resources/scl-ln-adapter/scd_with_ln.xml @@ -0,0 +1,72 @@ + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + +