From 7375e876c74cde4345f98bd8ee2d48e294c51b94 Mon Sep 17 00:00:00 2001 From: Aliou DIAITE Date: Tue, 12 Dec 2023 18:33:53 +0100 Subject: [PATCH] feat(#352): remove compasflow and extref bindings Signed-off-by: Aliou DIAITE --- pom.xml | 2 +- ...fService.java => ExtRefEditorService.java} | 346 ++++++++++-------- .../compas/sct/commons/api/ExtRefEditor.java | 8 + .../compas/sct/commons/scl/ExtRefService.java | 106 ++++++ .../commons/scl/ied/AccessPointAdapter.java | 2 +- .../sct/commons/scl/ied/InputsAdapter.java | 79 +--- ...Test.java => ExtRefEditorServiceTest.java} | 192 ++++++++-- .../sct/commons/scl/ExtRefServiceTest.java | 161 ++++++++ .../scd_extref_flow_debind_success.xml | 101 +++++ ...extref_flow_debind_volatagelevelname_0.xml | 76 ++++ ..._flow_debind_volatagelevelname_unknown.xml | 76 ++++ .../scd_extref_flow_not_debind.xml | 101 +++++ ...w_not_debind_volatagelevelname_unknown.xml | 76 ++++ 13 files changed, 1077 insertions(+), 249 deletions(-) rename sct-commons/src/main/java/org/lfenergy/compas/sct/commons/{ExtRefService.java => ExtRefEditorService.java} (86%) create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java rename sct-commons/src/test/java/org/lfenergy/compas/sct/commons/{ExtRefServiceTest.java => ExtRefEditorServiceTest.java} (78%) create mode 100644 sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java create mode 100644 sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_success.xml create mode 100644 sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_0.xml create mode 100644 sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_unknown.xml create mode 100644 sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_not_debind.xml create mode 100644 sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_not_debind_volatagelevelname_unknown.xml diff --git a/pom.xml b/pom.xml index 93499459c..2665d6890 100644 --- a/pom.xml +++ b/pom.xml @@ -36,7 +36,7 @@ sct-coverage/** ../sct-coverage/target/site/jacoco-aggregate/jacoco.xml ${basedir}/${aggregate.report.dir} - 0.17.0 + 0.18.0 0.0.4 3.4.1 3.2.1 diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefEditorService.java similarity index 86% rename from sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefService.java rename to sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefEditorService.java index ac5f6f4ff..7480eaa29 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefEditorService.java @@ -4,6 +4,7 @@ package org.lfenergy.compas.sct.commons; +import lombok.RequiredArgsConstructor; import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.api.ExtRefEditor; import org.lfenergy.compas.sct.commons.dto.*; @@ -12,6 +13,7 @@ import org.lfenergy.compas.sct.commons.model.epf.TCBscopeType; import org.lfenergy.compas.sct.commons.model.epf.TChannel; import org.lfenergy.compas.sct.commons.model.epf.TChannelType; +import org.lfenergy.compas.sct.commons.scl.ExtRefService; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter; import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter; @@ -30,8 +32,164 @@ import static org.lfenergy.compas.sct.commons.util.CommonConstants.*; import static org.lfenergy.compas.sct.commons.util.Utils.isExtRefFeedBySameControlBlock; -public class ExtRefService implements ExtRefEditor { +@RequiredArgsConstructor +public class ExtRefEditorService implements ExtRefEditor { private static final String INVALID_OR_MISSING_ATTRIBUTES_IN_EXT_REF_BINDING_INFO = "Invalid or missing attributes in ExtRef binding info"; + private static final Map voltageCodification = Map.of( + "3", "HT", + "4", "HT", + "5", "THT", + "6", "THT", + "7", "THT" + ); + private final ExtRefService extRefService; + + /** + * Remove ExtRef which are fed by same Control Block + * + * @return list ExtRefs without duplication + */ + public static List filterDuplicatedExtRefs(List tExtRefs) { + List filteredList = new ArrayList<>(); + tExtRefs.forEach(tExtRef -> { + if (filteredList.stream().noneMatch(t -> isExtRefFeedBySameControlBlock(tExtRef, t))) + filteredList.add(tExtRef); + }); + return filteredList; + } + + /** + * Provides valid IED sources according to EPF configuration.
+ * EPF verification include:
+ * 1. COMPAS-Bay verification that should be closed to the provided Flow Kind
+ * 2. COMPAS-ICDHeader verification that should match the provided parameters
+ * 3. Active LDevice source object that should match the provided parameters
+ * 4. Active LNode source object that should match the provided parameters
+ * 5. Valid DataTypeTemplate Object hierarchy that should match the DO/DA/BDA parameters
+ * + * @param sclRootAdapter SCL scl object + * @param compasBay TCompasBay represent Bay Private + * @param channel TChannel represent parameters + * @return the IED sources matching the LDEPF parameters + */ + private static List getIedSources(SclRootAdapter sclRootAdapter, TCompasBay compasBay, TChannel channel) { + return sclRootAdapter.streamIEDAdapters() + .filter(iedAdapter -> (channel.getBayScope().equals(TCBscopeType.BAY_EXTERNAL) + && iedAdapter.getPrivateCompasBay().stream().noneMatch(bay -> bay.getUUID().equals(compasBay.getUUID()))) + || (channel.getBayScope().equals(TCBscopeType.BAY_INTERNAL) + && iedAdapter.getPrivateCompasBay().stream().anyMatch(bay -> bay.getUUID().equals(compasBay.getUUID())))) + .filter(iedAdapter -> doesIcdHeaderMatchLDEPFChannel(iedAdapter, channel)) + .filter(iedAdapter -> getActiveSourceLDeviceByLDEPFChannel(iedAdapter, channel) + .map(lDeviceAdapter -> getActiveLNSourceByLDEPFChannel(lDeviceAdapter, channel) + .map(lnAdapter -> isValidDataTypeTemplate(lnAdapter, channel)) + .orElse(false)) + .orElse(false)) + .map(IEDAdapter::getCurrentElem) + .limit(2) + .toList(); + } + + /** + * Verify if an Extref matches the EPF Channel or not. + * + * @param extRef TExtRef + * @param tChannel TChannel + * @return true if the TExtRef matches the EPF channel + */ + private static Boolean doesExtRefMatchLDEPFChannel(TExtRef extRef, TChannel tChannel) { + Boolean doesExtRefDescMatchAnalogChannel = tChannel.getChannelType().equals(TChannelType.ANALOG) + && extRef.getDesc().startsWith("DYN_LDEPF_ANALOG CHANNEL " + tChannel.getChannelNum() + "_1_AnalogueValue") + && extRef.getDesc().endsWith("_" + tChannel.getDAName() + "_1"); + Boolean doesExtRefDescMatchDigitalChannel = tChannel.getChannelType().equals(TChannelType.DIGITAL) + && extRef.getDesc().startsWith("DYN_LDEPF_DIGITAL CHANNEL " + tChannel.getChannelNum() + "_1_BOOLEEN") + && extRef.getDesc().endsWith("_" + tChannel.getDAName() + "_1"); + return extRef.isSetDesc() && (doesExtRefDescMatchAnalogChannel || doesExtRefDescMatchDigitalChannel) + && extRef.isSetPLN() && Utils.lnClassEquals(extRef.getPLN(), tChannel.getLNClass()) + && extRef.isSetPDO() && extRef.getPDO().equals(tChannel.getDOName()); + } + + /** + * Verify whether the IED satisfies the EPF channel for the private element `TCompasICDHeader` + * + * @param iedAdapter IEDAdapter + * @param channel TChannel + * @return true if the TCompasICDHeader matches the EPF channel + */ + private static boolean doesIcdHeaderMatchLDEPFChannel(IEDAdapter iedAdapter, TChannel channel) { + return iedAdapter.getCompasICDHeader() + .map(compasICDHeader -> compasICDHeader.getIEDType().value().equals(channel.getIEDType()) + && compasICDHeader.getIEDredundancy().value().equals(channel.getIEDRedundancy().value()) + && compasICDHeader.getIEDSystemVersioninstance().toString().equals(channel.getIEDSystemVersionInstance())) + .orElse(false); + } + + /** + * Provides Active LDevice according to EPF channel's inst attribute + * + * @param iedAdapter IEDAdapter + * @param channel TChannel + * @return LDeviceAdapter object that matches the EPF channel + */ + private static Optional getActiveSourceLDeviceByLDEPFChannel(IEDAdapter iedAdapter, TChannel channel) { + LdeviceService ldeviceService = new LdeviceService(); + return ldeviceService.findLdevice(iedAdapter.getCurrentElem(), tlDevice -> tlDevice.getInst().equals(channel.getLDInst())) + .filter(tlDevice -> ldeviceService.getLdeviceStatus(tlDevice).map(ActiveStatus.ON::equals).orElse(false)) + .map(tlDevice -> new LDeviceAdapter(iedAdapter, tlDevice)); + } + + /** + * Provides Active LN Object that satisfies the EPF channel attributes (lnClass, lnInst, prefix) + * + * @param lDeviceAdapter LDeviceAdapter + * @param channel TChannel + * @return AbstractLNAdapter object that matches the EPF channel + */ + private static Optional> getActiveLNSourceByLDEPFChannel(LDeviceAdapter lDeviceAdapter, TChannel channel) { + return lDeviceAdapter.getLNAdaptersIncludingLN0() + .stream() + .filter(lnAdapter -> lnAdapter.getLNClass().equals(channel.getLNClass()) + && lnAdapter.getLNInst().equals(channel.getLNInst()) + && trimToEmpty(channel.getLNPrefix()).equals(trimToEmpty(lnAdapter.getPrefix()))) + .findFirst() + .filter(lnAdapter -> lnAdapter.getDaiModStValValue() + .map(status -> status.equals(ActiveStatus.ON.getValue())) + .orElse(true)); + } + + /** + * Verify whether the LN satisfies the EPF channel parameters for Data Type Template elements. + * + * @param lnAdapter AbstractLNAdapter + * @param channel TChannel + * @return true if the LN matches the EPF channel + */ + private static boolean isValidDataTypeTemplate(AbstractLNAdapter lnAdapter, TChannel channel) { + if (isBlank(channel.getDOName())) { + return true; + } + String doName = isBlank(channel.getDOInst()) || channel.getDOInst().equals("0") ? channel.getDOName() : channel.getDOName() + channel.getDOInst(); + DoTypeName doTypeName = new DoTypeName(doName); + if (isNotBlank(channel.getSDOName())) { + doTypeName.getStructNames().add(channel.getSDOName()); + } + DaTypeName daTypeName = new DaTypeName(channel.getDAName()); + if (isNotBlank(channel.getBDAName())) { + daTypeName.setBType(TPredefinedBasicTypeEnum.STRUCT); + daTypeName.getStructNames().add(channel.getBDAName()); + } + if (isNotBlank(channel.getSBDAName())) { + daTypeName.getStructNames().add(channel.getSBDAName()); + } + return lnAdapter.getDataTypeTemplateAdapter().getLNodeTypeAdapterById(lnAdapter.getLnType()) + .filter(lNodeTypeAdapter -> { + try { + lNodeTypeAdapter.checkDoAndDaTypeName(doTypeName, daTypeName); + } catch (ScdException ex) { + return false; + } + return true; + }).isPresent(); + } @Override public void updateExtRefBinders(SCL scd, ExtRefInfo extRefInfo) throws ScdException { @@ -119,7 +277,7 @@ public List updateAllExtRefIedNames(SCL scd) { @Override public List manageBindingForLDEPF(SCL scd, EPF epf) { List sclReportItems = new ArrayList<>(); - if(!epf.isSetChannels()) return sclReportItems; + if (!epf.isSetChannels()) return sclReportItems; SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); sclRootAdapter.streamIEDAdapters() .filter(iedAdapter -> !iedAdapter.getName().contains("TEST")) @@ -187,153 +345,12 @@ private List checkIedUnityOfIcdSystemVersionUuid(SclRootAdapter s .toList(); } - /** - * Remove ExtRef which are fed by same Control Block - * - * @return list ExtRefs without duplication - */ - public static List filterDuplicatedExtRefs(List tExtRefs) { - List filteredList = new ArrayList<>(); - tExtRefs.forEach(tExtRef -> { - if (filteredList.stream().noneMatch(t -> isExtRefFeedBySameControlBlock(tExtRef, t))) - filteredList.add(tExtRef); - }); - return filteredList; - } - - /** - * Provides valid IED sources according to EPF configuration.
- * EPF verification include:
- * 1. COMPAS-Bay verification that should be closed to the provided Flow Kind
- * 2. COMPAS-ICDHeader verification that should match the provided parameters
- * 3. Active LDevice source object that should match the provided parameters
- * 4. Active LNode source object that should match the provided parameters
- * 5. Valid DataTypeTemplate Object hierarchy that should match the DO/DA/BDA parameters
- * @param sclRootAdapter SCL scl object - * @param compasBay TCompasBay represent Bay Private - * @param channel TChannel represent parameters - * @return the IED sources matching the LDEPF parameters - */ - private static List getIedSources(SclRootAdapter sclRootAdapter, TCompasBay compasBay, TChannel channel) { - return sclRootAdapter.streamIEDAdapters() - .filter(iedAdapter -> (channel.getBayScope().equals(TCBscopeType.BAY_EXTERNAL) - && iedAdapter.getPrivateCompasBay().stream().noneMatch(bay -> bay.getUUID().equals(compasBay.getUUID()))) - || (channel.getBayScope().equals(TCBscopeType.BAY_INTERNAL) - && iedAdapter.getPrivateCompasBay().stream().anyMatch(bay -> bay.getUUID().equals(compasBay.getUUID())))) - .filter(iedAdapter -> doesIcdHeaderMatchLDEPFChannel(iedAdapter, channel)) - .filter(iedAdapter -> getActiveSourceLDeviceByLDEPFChannel(iedAdapter, channel) - .map(lDeviceAdapter -> getActiveLNSourceByLDEPFChannel(lDeviceAdapter, channel) - .map(lnAdapter -> isValidDataTypeTemplate(lnAdapter, channel)) - .orElse(false)) - .orElse(false)) - .map(IEDAdapter::getCurrentElem) - .limit(2) - .toList(); - } - - /** - * Verify if an Extref matches the EPF Channel or not. - * @param extRef TExtRef - * @param tChannel TChannel - * @return true if the TExtRef matches the EPF channel - */ - private static Boolean doesExtRefMatchLDEPFChannel(TExtRef extRef, TChannel tChannel) { - Boolean doesExtRefDescMatchAnalogChannel = tChannel.getChannelType().equals(TChannelType.ANALOG) - && extRef.getDesc().startsWith("DYN_LDEPF_ANALOG CHANNEL " + tChannel.getChannelNum()+"_1_AnalogueValue") - && extRef.getDesc().endsWith("_" + tChannel.getDAName() + "_1"); - Boolean doesExtRefDescMatchDigitalChannel = tChannel.getChannelType().equals(TChannelType.DIGITAL) - && extRef.getDesc().startsWith("DYN_LDEPF_DIGITAL CHANNEL " + tChannel.getChannelNum()+"_1_BOOLEEN") - && extRef.getDesc().endsWith("_" + tChannel.getDAName() + "_1"); - return extRef.isSetDesc() && (doesExtRefDescMatchAnalogChannel || doesExtRefDescMatchDigitalChannel) - && extRef.isSetPLN() && Utils.lnClassEquals(extRef.getPLN(), tChannel.getLNClass()) - && extRef.isSetPDO() && extRef.getPDO().equals(tChannel.getDOName()); - } - - /** - * Verify whether the IED satisfies the EPF channel for the private element `TCompasICDHeader` - * @param iedAdapter IEDAdapter - * @param channel TChannel - * @return true if the TCompasICDHeader matches the EPF channel - */ - private static boolean doesIcdHeaderMatchLDEPFChannel(IEDAdapter iedAdapter, TChannel channel) { - return iedAdapter.getCompasICDHeader() - .map(compasICDHeader -> compasICDHeader.getIEDType().value().equals(channel.getIEDType()) - && compasICDHeader.getIEDredundancy().value().equals(channel.getIEDRedundancy().value()) - && compasICDHeader.getIEDSystemVersioninstance().toString().equals(channel.getIEDSystemVersionInstance())) - .orElse(false); - } - - /** - * Provides Active LDevice according to EPF channel's inst attribute - * @param iedAdapter IEDAdapter - * @param channel TChannel - * @return LDeviceAdapter object that matches the EPF channel - */ - private static Optional getActiveSourceLDeviceByLDEPFChannel(IEDAdapter iedAdapter, TChannel channel) { - LdeviceService ldeviceService = new LdeviceService(); - return ldeviceService.findLdevice(iedAdapter.getCurrentElem(), tlDevice -> tlDevice.getInst().equals(channel.getLDInst())) - .filter(tlDevice -> ldeviceService.getLdeviceStatus(tlDevice).map(ActiveStatus.ON::equals).orElse(false)) - .map(tlDevice -> new LDeviceAdapter(iedAdapter, tlDevice)); - } - - /** - * Provides Active LN Object that satisfies the EPF channel attributes (lnClass, lnInst, prefix) - * @param lDeviceAdapter LDeviceAdapter - * @param channel TChannel - * @return AbstractLNAdapter object that matches the EPF channel - */ - private static Optional> getActiveLNSourceByLDEPFChannel(LDeviceAdapter lDeviceAdapter, TChannel channel) { - return lDeviceAdapter.getLNAdaptersIncludingLN0() - .stream() - .filter(lnAdapter -> lnAdapter.getLNClass().equals(channel.getLNClass()) - && lnAdapter.getLNInst().equals(channel.getLNInst()) - && trimToEmpty(channel.getLNPrefix()).equals(trimToEmpty(lnAdapter.getPrefix()))) - .findFirst() - .filter(lnAdapter -> lnAdapter.getDaiModStValValue() - .map(status -> status.equals(ActiveStatus.ON.getValue())) - .orElse(true)); - } - - /** - * Verify whether the LN satisfies the EPF channel parameters for Data Type Template elements. - * @param lnAdapter AbstractLNAdapter - * @param channel TChannel - * @return true if the LN matches the EPF channel - */ - private static boolean isValidDataTypeTemplate(AbstractLNAdapter lnAdapter, TChannel channel) { - if(isBlank(channel.getDOName())){ - return true; - } - String doName = isBlank(channel.getDOInst()) || channel.getDOInst().equals("0") ? channel.getDOName() : channel.getDOName() + channel.getDOInst(); - DoTypeName doTypeName = new DoTypeName(doName); - if(isNotBlank(channel.getSDOName())){ - doTypeName.getStructNames().add(channel.getSDOName()); - } - DaTypeName daTypeName = new DaTypeName(channel.getDAName()); - if(isNotBlank(channel.getBDAName())){ - daTypeName.setBType(TPredefinedBasicTypeEnum.STRUCT); - daTypeName.getStructNames().add(channel.getBDAName()); - } - if(isNotBlank(channel.getSBDAName())){ - daTypeName.getStructNames().add(channel.getSBDAName()); - } - return lnAdapter.getDataTypeTemplateAdapter().getLNodeTypeAdapterById(lnAdapter.getLnType()) - .filter(lNodeTypeAdapter -> { - try { - lNodeTypeAdapter.checkDoAndDaTypeName(doTypeName, daTypeName); - } catch (ScdException ex) { - return false; - } - return true; - }).isPresent(); - } - private void updateLDEPFExtRefBinding(TExtRef extRef, TIED iedSource, TChannel setting) { extRef.setIedName(iedSource.getName()); extRef.setLdInst(setting.getLDInst()); extRef.getLnClass().add(setting.getLNClass()); extRef.setLnInst(setting.getLNInst()); - if(!isBlank(setting.getLNPrefix())) { + if (!isBlank(setting.getLNPrefix())) { extRef.setPrefix(setting.getLNPrefix()); } String doName = isBlank(setting.getDOInst()) || setting.getDOInst().equals("0") ? setting.getDOName() : setting.getDOName() + setting.getDOInst(); @@ -377,9 +394,6 @@ private Optional updateVal(AbstractLNAdapter lnAdapter, String return lnAdapter.getDOIAdapterByName(doName).updateDAI(daName, value); } - private record DoNameAndDaName(String doName, String daName) { - } - private String computeDaiValue(AbstractLNAdapter lnAdapter, TExtRef extRef, String daName) { if (LN_PREFIX_B.equals(lnAdapter.getPrefix()) || LN_PREFIX_A.equals(lnAdapter.getPrefix())) { return extRef.getIedName() + @@ -399,4 +413,42 @@ private String computeDaiValue(AbstractLNAdapter lnAdapter, TExtRef extRef, S } } + @Override + public void debindCompasFlowsAndExtRefsBasedOnVoltageLevel(SCL scd) { + LdeviceService ldeviceService = new LdeviceService(); + scd.getSubstation() + .stream() + .flatMap(tSubstation -> tSubstation.getVoltageLevel().stream()) + .map(TVoltageLevel::getName) + .filter(tVoltageLevelName -> !"0".equals(tVoltageLevelName)) + .forEach(tVoltageLevelName -> { + scd.getIED().stream() + .flatMap(ldeviceService::getLdevices) + .forEach(tlDevice -> { + String flowSource = voltageCodification.get(tVoltageLevelName); + TInputs tInputs = tlDevice.getLN0().getInputs(); + PrivateUtils.getPrivateStream(tInputs.getPrivate(), TCompasFlow.class) + .filter(TCompasFlow::isSetFlowSourceVoltageLevel) + .filter(TCompasFlow::isSetExtRefiedName) + .forEach(tCompasFlow -> { + if (flowSource == null) { + //debind all compas flow + extRefService.clearCompasFlowBinding(tCompasFlow); + } else if (!tCompasFlow.getFlowSourceVoltageLevel().equals(flowSource)) { + //debind extRef + extRefService.getMatchingTextRef(tInputs, tCompasFlow) + .forEach(extRefService::clearExtRefBinding); + //debind compas flow + extRefService.clearCompasFlowBinding(tCompasFlow); + + } + }); + }); + }); + } + + private record DoNameAndDaName(String doName, String daName) { + } + + } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java index 5b49ffb81..4b656468f 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java @@ -56,10 +56,18 @@ public interface ExtRefEditor { /** * ExtRef Binding For LDevice (inst=LDEPF) that matching EPF configuration + * * @param scd SCL * @param epf EPF * @return list of encountered errors */ List manageBindingForLDEPF(SCL scd, EPF epf); + /** + * Debinding of Private CompasFlows and ExtRef signals based on voltageLevel + * + * @param scd SCL file in which ExtRef and Private CompasFlow should be debind + */ + void debindCompasFlowsAndExtRefsBasedOnVoltageLevel(SCL scd); + } 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 new file mode 100644 index 000000000..58437ceb6 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java @@ -0,0 +1,106 @@ +/* + * // SPDX-FileCopyrightText: 2023 RTE FRANCE + * // + * // SPDX-License-Identifier: Apache-2.0 + */ + +package org.lfenergy.compas.sct.commons.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.sct.commons.util.PrivateUtils; +import org.lfenergy.compas.sct.commons.util.Utils; + +import java.util.stream.Stream; + +public class ExtRefService { + + /** + * List all ExtRefs in this Inputs + * + * @return list of ExtRefs. List is modifiable. + */ + public Stream getExtRefs(TInputs inputs) { + if (inputs == null || !inputs.isSetExtRef()) { + return Stream.empty(); + } + return inputs.getExtRef().stream(); + } + + /** + * Find CompasFlows that match given ExtRef + * + * @param inputs inputs containing Privates CompasFlow and TExtRefs + * @param tExtRef corresponding to CompasFlow we are searching + * @return list of matching CompasFlows + */ + public Stream getMatchingCompasFlows(TInputs inputs, TExtRef tExtRef) { + return PrivateUtils.extractCompasPrivates(inputs, TCompasFlow.class) + .filter(compasFlow -> isMatchingExtRef(compasFlow, tExtRef)); + } + + /** + * Retrieves ExtRefs corresponding to given CompasFlow + * + * @param inputs node containing CompasFlows and ExtRefs + * @param tCompasFlow corresponding to Extrefs we are searching + * @return stream of matching ExtRefs + */ + public Stream getMatchingTextRef(TInputs inputs, TCompasFlow tCompasFlow) { + return getExtRefs(inputs) + .filter(tExtRef -> isMatchingExtRef(tCompasFlow, tExtRef)); + } + + /** + * Debind ExtRef + * + * @param extRef to debind + */ + public void clearExtRefBinding(TExtRef extRef) { + extRef.setIedName(null); + extRef.setLdInst(null); + extRef.setPrefix(null); + extRef.setLnInst(null); + extRef.setDoName(null); + extRef.setDaName(null); + extRef.setServiceType(null); + extRef.setSrcLDInst(null); + extRef.setSrcPrefix(null); + extRef.setSrcLNInst(null); + extRef.setSrcCBName(null); + extRef.unsetLnClass(); + extRef.unsetSrcLNClass(); + } + + /** + * Debind CompasFlow + * + * @param tCompasFlow to debind + */ + public void clearCompasFlowBinding(TCompasFlow tCompasFlow) { + tCompasFlow.setExtRefiedName(null); + tCompasFlow.setExtRefldinst(null); + tCompasFlow.setExtReflnClass(null); + tCompasFlow.setExtReflnInst(null); + tCompasFlow.setExtRefprefix(null); + } + + /** + * Check if extRef matches CompasFlow + * + * @param compasFlow compasFlow + * @param extRef extRef + * @return true if all required attributes matches. Note that empty string, whitespaces only string and null values are considered as matching + * (missing attributes matches attribute with empty string value or whitespaces only). Return false otherwise. + */ + private boolean isMatchingExtRef(TCompasFlow compasFlow, TExtRef extRef) { + String extRefLnClass = extRef.isSetLnClass() ? extRef.getLnClass().get(0) : null; + return Utils.equalsOrBothBlank(compasFlow.getDataStreamKey(), extRef.getDesc()) + && Utils.equalsOrBothBlank(compasFlow.getExtRefiedName(), extRef.getIedName()) + && Utils.equalsOrBothBlank(compasFlow.getExtRefldinst(), extRef.getLdInst()) + && Utils.equalsOrBothBlank(compasFlow.getExtRefprefix(), extRef.getPrefix()) + && Utils.equalsOrBothBlank(compasFlow.getExtReflnClass(), extRefLnClass) + && Utils.equalsOrBothBlank(compasFlow.getExtReflnInst(), extRef.getLnInst()); + } +} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java index 9c64f66da..afa9958c8 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java @@ -19,7 +19,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.lfenergy.compas.sct.commons.ExtRefService.filterDuplicatedExtRefs; +import static org.lfenergy.compas.sct.commons.ExtRefEditorService.filterDuplicatedExtRefs; /** * A representation of the model object diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java index b15ed6bfc..b25094698 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java @@ -8,11 +8,12 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.lfenergy.compas.scl2007b4.model.*; -import org.lfenergy.compas.sct.commons.ExtRefService; +import org.lfenergy.compas.sct.commons.ExtRefEditorService; import org.lfenergy.compas.sct.commons.dto.DataAttributeRef; import org.lfenergy.compas.sct.commons.dto.FcdaForDataSetsCreation; import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.exception.ScdException; +import org.lfenergy.compas.sct.commons.scl.ExtRefService; import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter; @@ -63,6 +64,8 @@ public class InputsAdapter extends SclElementAdapter { private static final int EXTREF_DESC_DA_NAME_POSITION = -2; + private final ExtRefService extRefService = new ExtRefService(); + /** * Constructor * @@ -101,13 +104,13 @@ public List updateAllExtRefIedNames(Map icdSy try { ActiveStatus lDeviceStatus = ActiveStatus.fromValue(optionalLDeviceStatus.get()); return switch (lDeviceStatus) { - case ON -> getExtRefs().stream() + case ON -> extRefService.getExtRefs(currentElem) .filter(tExtRef -> StringUtils.isNotBlank(tExtRef.getIedName()) && StringUtils.isNotBlank(tExtRef.getDesc())) .map(extRef -> updateExtRefIedName(extRef, icdSystemVersionToIed.get(extRef.getIedName()))) .flatMap(Optional::stream) .toList(); case OFF -> { - getExtRefs().forEach(this::clearBinding); + extRefService.getExtRefs(currentElem).forEach(extRefService::clearExtRefBinding); yield Collections.emptyList(); } }; @@ -123,19 +126,19 @@ public List updateAllExtRefIedNames(Map icdSy * @return Error if ExtRef could not be updated */ private Optional updateExtRefIedName(TExtRef extRef, IEDAdapter sourceIed) { - List matchingCompasFlows = getMatchingCompasFlows(extRef); + List matchingCompasFlows = extRefService.getMatchingCompasFlows(currentElem, extRef).toList(); if (!singleMatch(matchingCompasFlows)) { return fatalReportItem(extRef, matchingCompasFlows.isEmpty() ? MESSAGE_NO_MATCHING_COMPAS_FLOW : MESSAGE_TOO_MANY_MATCHING_COMPAS_FLOWS); } TCompasFlow compasFlow = matchingCompasFlows.get(0); if (compasFlow.getFlowStatus() == TCompasFlowStatus.INACTIVE) { - clearBinding(extRef); + extRefService.clearExtRefBinding(extRef); return Optional.empty(); } Optional sourceValidationError = validateExtRefSource(extRef, sourceIed); if (sourceValidationError.isPresent()) { - clearBinding(extRef); + extRefService.clearExtRefBinding(extRef); return sourceValidationError; } String sourceIedName = PrivateUtils.extractCompasPrivate(sourceIed.getCurrentElem(), TCompasICDHeader.class) @@ -146,18 +149,6 @@ private Optional updateExtRefIedName(TExtRef extRef, IEDAdapter s return Optional.empty(); } - /** - * List all ExtRefs in this Inputs - * - * @return list of ExtRefs. List is modifiable. - */ - private List getExtRefs() { - if (!currentElem.isSetExtRef()) { - return Collections.emptyList(); - } - return currentElem.getExtRef(); - } - private Optional validateExtRefSource(TExtRef extRef, IEDAdapter sourceIed) { if (sourceIed == null) { return warningReportItem(extRef, MESSAGE_EXTREF_IEDNAME_DOES_NOT_MATCH_ANY_SYSTEM_VERSION_UUID); @@ -192,22 +183,6 @@ private boolean singleMatch(List matchingCompasFlows) { return matchingCompasFlows.size() == 1; } - private void clearBinding(TExtRef extRef) { - extRef.setIedName(null); - extRef.setLdInst(null); - extRef.setPrefix(null); - extRef.setLnInst(null); - extRef.setDoName(null); - extRef.setDaName(null); - extRef.setServiceType(null); - extRef.setSrcLDInst(null); - extRef.setSrcPrefix(null); - extRef.setSrcLNInst(null); - extRef.setSrcCBName(null); - extRef.unsetLnClass(); - extRef.unsetSrcLNClass(); - } - private Optional warningReportItem(TExtRef extRef, String message) { return Optional.of(SclReportItem.warning(extRefXPath(extRef.getDesc()), message)); } @@ -221,36 +196,6 @@ private String extRefXPath(String extRefDesc) { Utils.xpathAttributeFilter("desc", extRefDesc)); } - /** - * Find CompasFlows that match given ExtRef - * - * @param extRef extRef to match - * @return list of matching CompasFlows - */ - private List getMatchingCompasFlows(TExtRef extRef) { - return PrivateUtils.extractCompasPrivates(currentElem, TCompasFlow.class) - .filter(compasFlow -> isMatchingExtRef(compasFlow, extRef)) - .toList(); - } - - /** - * Check if extRef matches CompasFlow - * - * @param compasFlow compasFlow - * @param extRef extRef - * @return true if all required attributes matches. Note that empty string, whitespaces only string and null values are considered as matching - * (missing attributes matches attribute with empty string value or whitespaces only). Return false otherwise. - */ - private boolean isMatchingExtRef(TCompasFlow compasFlow, TExtRef extRef) { - String extRefLnClass = extRef.isSetLnClass() ? extRef.getLnClass().get(0) : null; - return Utils.equalsOrBothBlank(compasFlow.getDataStreamKey(), extRef.getDesc()) - && Utils.equalsOrBothBlank(compasFlow.getExtRefiedName(), extRef.getIedName()) - && Utils.equalsOrBothBlank(compasFlow.getExtRefldinst(), extRef.getLdInst()) - && Utils.equalsOrBothBlank(compasFlow.getExtRefprefix(), extRef.getPrefix()) - && Utils.equalsOrBothBlank(compasFlow.getExtReflnClass(), extRefLnClass) - && Utils.equalsOrBothBlank(compasFlow.getExtReflnInst(), extRef.getLnInst()); - } - private LDeviceAdapter getLDeviceAdapter() { return parentAdapter.getParentAdapter(); } @@ -264,7 +209,7 @@ public List updateAllSourceDataSetsAndControlBlocks(Set updateAllSourceDataSetsAndControlBlocks(Set flowStatus == TCompasFlowStatus.ACTIVE || flowStatus == TCompasFlowStatus.UNTESTED) .isPresent(); @@ -429,7 +374,7 @@ private SclRootAdapter getSclRootAdapter() { * @return list ExtRefs without duplication */ public List filterDuplicatedExtRefs() { - return ExtRefService.filterDuplicatedExtRefs(getExtRefs()); + return ExtRefEditorService.filterDuplicatedExtRefs(extRefService.getExtRefs(currentElem).toList()); } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefEditorServiceTest.java similarity index 78% rename from sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefServiceTest.java rename to sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefEditorServiceTest.java index 7cfb52394..df0e36403 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefEditorServiceTest.java @@ -5,9 +5,10 @@ package org.lfenergy.compas.sct.commons; import org.assertj.core.api.Assertions; +import org.assertj.core.groups.Tuple; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -15,41 +16,39 @@ import org.lfenergy.compas.sct.commons.dto.*; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.model.epf.*; +import org.lfenergy.compas.sct.commons.scl.ExtRefService; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter; import org.lfenergy.compas.sct.commons.scl.ln.AbstractLNAdapter; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; import org.lfenergy.compas.sct.commons.util.PrivateUtils; -import org.mockito.InjectMocks; -import org.mockito.junit.jupiter.MockitoExtension; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.math.BigInteger; import java.util.List; -import java.util.Objects; 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; +import static org.lfenergy.compas.sct.commons.ExtRefEditorService.filterDuplicatedExtRefs; import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*; import static org.lfenergy.compas.sct.commons.util.CommonConstants.*; -@ExtendWith(MockitoExtension.class) -class ExtRefServiceTest { +class ExtRefEditorServiceTest { - @InjectMocks - ExtRefService extRefService; + ExtRefEditorService extRefEditorService; + + @BeforeEach + void init() { + extRefEditorService = new ExtRefEditorService(new ExtRefService()); + } @Test void updateAllExtRefIedNames_should_update_iedName_and_ExtRefIedName() { // Given : An ExtRef with a matching compas:Flow SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_success.xml"); // When - extRefService.updateAllExtRefIedNames(scd); + extRefEditorService.updateAllExtRefIedNames(scd); // Then TExtRef extRef = findExtRef(scd, "IED_NAME1", "LD_INST11", "STAT_LDSUIED_LPDO 1 Sortie_13_BOOLEAN_18_stVal_1"); assertThat(extRef.getIedName()).isEqualTo("IED_NAME2"); @@ -68,7 +67,7 @@ void updateAllExtRefIedNames_should_return_success_status() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_success.xml"); // When - List sclReportItems = extRefService.updateAllExtRefIedNames(scd); + List sclReportItems = extRefEditorService.updateAllExtRefIedNames(scd); // Then assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)) .overridingErrorMessage(String.valueOf(sclReportItems)) @@ -80,7 +79,7 @@ void updateAllExtRefIedNames_should_return_success_status() { void updateAllExtRefIedNames_should_report_errors(String testCase, SCL scl, SclReportItem... errors) { // Given : scl parameter // When - List sclReportItems = extRefService.updateAllExtRefIedNames(scl); + List sclReportItems = extRefEditorService.updateAllExtRefIedNames(scl); // Then : the sclReport should report all errors described in the comments in the SCD file assertThat(sclReportItems).isNotNull(); assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isFalse(); @@ -154,7 +153,7 @@ void updateAllExtRefIedNames_when_not_bindable_should_clear_binding() { // Given : see comments in SCD file SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml"); // When - extRefService.updateAllExtRefIedNames(scd); + extRefEditorService.updateAllExtRefIedNames(scd); // Then assertExtRefIsNotBound(findExtRef(scd, "IED_NAME1", "LD_INST12", "ExtRef target LDevice status is off")); assertExtRefIsNotBound(findExtRef(scd, "IED_NAME1", "LD_INST11", "Match compas:Flow but FlowStatus is INACTIVE")); @@ -169,7 +168,7 @@ void updateAllExtRefIedNames_when_lDevice_off_should_remove_binding() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml"); // When - List sclReportItems = extRefService.updateAllExtRefIedNames(scd); + List sclReportItems = extRefEditorService.updateAllExtRefIedNames(scd); // Then assertThat(sclReportItems).isNotNull(); LDeviceAdapter lDeviceAdapter = findLDeviceByLdName(scd, "IED_NAME1LD_INST12"); @@ -183,7 +182,7 @@ void updateAllExtRefIedNames_when_FlowStatus_INACTIVE_should_remove_binding() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml"); // When - List sclReportItems = extRefService.updateAllExtRefIedNames(scd); + List sclReportItems = extRefEditorService.updateAllExtRefIedNames(scd); // Then assertThat(sclReportItems).isNotNull(); LDeviceAdapter lDeviceAdapter = findLDeviceByLdName(scd, "IED_NAME1LD_INST11"); @@ -221,7 +220,7 @@ void filterDuplicatedExtRefs_should_remove_duplicated_extrefs() { List tExtRefList = List.of(tExtRef, tExtRefLnClass, createExtRefExample("CB", TServiceType.GOOSE), createExtRefExample("CB", TServiceType.GOOSE)); // When - List result = extRefService.filterDuplicatedExtRefs(tExtRefList); + List result = filterDuplicatedExtRefs(tExtRefList); // Then assertThat(result).hasSizeLessThan(tExtRefList.size()) .hasSize(2); @@ -241,7 +240,7 @@ void filterDuplicatedExtRefs_should_not_remove_not_duplicated_extrefs() { List tExtRefList = List.of(tExtRefIedName, tExtRefLdInst, tExtRefLnInst, tExtRefPrefix, createExtRefExample("CB_1", TServiceType.GOOSE), createExtRefExample("CB_1", TServiceType.SMV)); // When - List result = extRefService.filterDuplicatedExtRefs(tExtRefList); + List result = filterDuplicatedExtRefs(tExtRefList); // Then assertThat(result).hasSameSizeAs(tExtRefList) .hasSize(6); @@ -273,7 +272,7 @@ void manageBindingForLDEPF_whenFlowKindIsInternalAndAllExtRefInSameBay_should_re channels.getChannel().add(channel); epf.setChannels(channels); // When - List sclReportItems = extRefService.manageBindingForLDEPF(scd, epf); + List sclReportItems = extRefEditorService.manageBindingForLDEPF(scd, epf); // Then assertThat(sclReportItems).isEmpty(); TExtRef extRef1 = findExtRef(scd, "IED_NAME1", "LDEPF", "DYN_LDEPF_DIGITAL CHANNEL 1_1_BOOLEEN_1_general_1"); @@ -342,7 +341,7 @@ void manageBindingForLDEPF_when_internalBindingMatchEPFChannel_should_update_suc channels.getChannel().add(channel); epf.setChannels(channels); // When - List sclReportItems = extRefService.manageBindingForLDEPF(scd, epf); + List sclReportItems = extRefEditorService.manageBindingForLDEPF(scd, epf); // Then assertThat(sclReportItems).isEmpty(); SclTestMarshaller.assertIsMarshallable(new SclRootAdapter(scd).getCurrentElem()); @@ -405,7 +404,7 @@ void manageBindingForLDEPF_when_manyIedSourceFound_should_return_reportMassages( channels.getChannel().add(channel); epf.setChannels(channels); // When - List sclReportItems = extRefService.manageBindingForLDEPF(scd, epf); + List sclReportItems = extRefEditorService.manageBindingForLDEPF(scd, epf); // Then assertThat(sclReportItems).hasSize(2) .extracting(SclReportItem::message) @@ -493,7 +492,7 @@ void manageBindingForLDEPF_when_extRefMatchFlowKindInternalOrExternal_should_upd channels.getChannel().add(analogueChannel10WithBayExternalBayScope); epf.setChannels(channels); // When - List sclReportItems = extRefService.manageBindingForLDEPF(scd, epf); + List sclReportItems = extRefEditorService.manageBindingForLDEPF(scd, epf); // Then assertThat(sclReportItems).isEmpty(); SclTestMarshaller.assertIsMarshallable(scd); @@ -542,6 +541,76 @@ private void assertExtRefIsBoundAccordingTOLDEPF(TExtRef extRef, TChannel settin assertThat(extRef.getDoName()).isEqualTo(setting.getDOName()); } + @ParameterizedTest(name = "{0}") + @MethodSource("provideExtRefInfoInvalid") + void updateExtRefBinders(String testCase, ExtRefInfo extRefInfo, String message) { + //Given + SCL scd = createSclRootAdapterWithIed("IED_NAME").getCurrentElem(); + //When + //Then + assertThatThrownBy(() -> extRefEditorService.updateExtRefBinders(scd, extRefInfo)) + .isInstanceOf(ScdException.class) + .hasMessage(message); + } + + private static Stream provideExtRefInfoInvalid() { + ExtRefInfo withBindingInfo = new ExtRefInfo(); + withBindingInfo.setBindingInfo(new ExtRefBindingInfo()); + ExtRefInfo withSignalInfo = new ExtRefInfo(); + withSignalInfo.setSignalInfo(new ExtRefSignalInfo()); + ExtRefInfo withUnknownLD = new ExtRefInfo(); + withUnknownLD.setHolderIEDName("IED_NAME"); + withUnknownLD.setHolderLDInst("Unknown LD"); + ExtRefBindingInfo extRefBindingInfo = new ExtRefBindingInfo(); + ExtRefSignalInfo extRefSignalInfo = new ExtRefSignalInfo(); + withUnknownLD.setBindingInfo(extRefBindingInfo); + withUnknownLD.setSignalInfo(extRefSignalInfo); + return Stream.of( + Arguments.of("Should throw exception when BindingInfo is null", withSignalInfo, "ExtRef Signal and/or Binding information are missing"), + Arguments.of("Should throw exception when SignalInfo is null", withBindingInfo, "ExtRef Signal and/or Binding information are missing"), + Arguments.of("Should throw exception when LD is not present in IED", withUnknownLD, "Unknown LDevice (Unknown LD) in IED (IED_NAME)") + ); + } + + @Test + void updateExtRefBinders_should_thowException_when_AbstractLnAdapterUpdateExtRefBinders_Throws_Exception() { + //Given + SCL scd = createIedsInScl("ANCR", "do1").getCurrentElem(); + ExtRefInfo extRefInfo = new ExtRefInfo(); + extRefInfo.setHolderIEDName(IED_NAME_1); + extRefInfo.setHolderLDInst(LD_SUIED); + extRefInfo.setHolderLnClass("LLN0"); + ExtRefBindingInfo extRefBindingInfo = new ExtRefBindingInfo(); + ExtRefSignalInfo extRefSignalInfo = new ExtRefSignalInfo(); + extRefInfo.setBindingInfo(extRefBindingInfo); + extRefInfo.setSignalInfo(extRefSignalInfo); + //When + //Then + assertThatThrownBy(() -> extRefEditorService.updateExtRefBinders(scd, extRefInfo)) + .isInstanceOf(ScdException.class) + .hasMessage("ExtRef mandatory binding data are missing"); + } + + @Test + void updateExtRefBinders_should_succed_when_AbstractLnAdapterUpdateExtRefBinders_succed() { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + ExtRefInfo extRefInfo = DTO.createExtRefInfo(); + extRefInfo.setHolderIEDName("IED_NAME"); + extRefInfo.setHolderLDInst("LD_INS2"); + extRefInfo.setHolderLnClass("ANCR"); + extRefInfo.setHolderLnInst("1"); + extRefInfo.setHolderLnPrefix(null); + extRefInfo.getSignalInfo().setPDO("StrVal.sdo2"); + extRefInfo.getSignalInfo().setPDA("antRef.bda1.bda2.bda3"); + extRefInfo.getSignalInfo().setIntAddr("INT_ADDR2"); + extRefInfo.getSignalInfo().setDesc(null); + extRefInfo.getSignalInfo().setPServT(null); + //When + //Then + assertDoesNotThrow(() -> extRefEditorService.updateExtRefBinders(scd, extRefInfo)); + } + @Test @Tag("issue-321") void updateExtRefSource_whenSignalInfoNullOrInvalid_shouldThrowScdException() { @@ -553,12 +622,12 @@ void updateExtRefSource_whenSignalInfoNullOrInvalid_shouldThrowScdException() { extRefInfo.setHolderLnClass(TLLN0Enum.LLN_0.value()); assertThat(extRefInfo.getSignalInfo()).isNull(); //When Then - assertThatThrownBy(() -> extRefService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class); // signal = null + assertThatThrownBy(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class); // signal = null //Given extRefInfo.setSignalInfo(new ExtRefSignalInfo()); assertThat(extRefInfo.getSignalInfo()).isNotNull(); //When Then - assertThatThrownBy(() -> extRefService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// signal invalid + assertThatThrownBy(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// signal invalid } @Test @@ -578,13 +647,13 @@ void updateExtRefSource_whenBindingInfoNullOrInvalid_shouldThrowScdException() { extRefInfo.setSignalInfo(extRefSignalInfo); assertThat(extRefInfo.getBindingInfo()).isNull(); //When Then - assertThatThrownBy(() -> extRefService.updateExtRefSource(scd, extRefInfo)) + assertThatThrownBy(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)) .isInstanceOf(ScdException.class); // binding = null //Given extRefInfo.setBindingInfo(new ExtRefBindingInfo()); assertThat(extRefInfo.getBindingInfo()).isNotNull(); //When Then - assertThatThrownBy(() -> extRefService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// binding invalid + assertThatThrownBy(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// binding invalid } @Test @@ -608,7 +677,7 @@ void updateExtRefSource_whenBindingInternalByIedName_shouldThrowScdException() { extRefBindingInfo.setLnClass(TLLN0Enum.LLN_0.value()); extRefInfo.setBindingInfo(new ExtRefBindingInfo()); //When Then - assertThatThrownBy(() -> extRefService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class); // CB not allowed + assertThatThrownBy(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class); // CB not allowed } @Test @@ -633,7 +702,7 @@ void updateExtRefSource_whenBindingInternaByServiceType_shouldThrowScdException( extRefBindingInfo.setServiceType(TServiceType.POLL); extRefInfo.setBindingInfo(new ExtRefBindingInfo()); //When Then - assertThatThrownBy(() -> extRefService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class); // CB not allowed + assertThatThrownBy(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class); // CB not allowed } @Test @@ -660,12 +729,12 @@ void updateExtRefSource_whenSourceInfoNullOrInvalid_shouldThrowScdException() { assertThat(extRefInfo.getSourceInfo()).isNull(); //When Then - assertThatThrownBy(() -> extRefService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class); // signal = null + assertThatThrownBy(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class); // signal = null //Given extRefInfo.setSourceInfo(new ExtRefSourceInfo()); assertThat(extRefInfo.getSourceInfo()).isNotNull(); //When Then - assertThatThrownBy(() -> extRefService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// signal invalid + assertThatThrownBy(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// signal invalid } @Test @@ -696,11 +765,68 @@ void updateExtRefSource_whenBindingExternalBinding_shouldThrowScdException() { extRefInfo.setSourceInfo(sourceInfo); //When - TExtRef extRef = assertDoesNotThrow(() -> extRefService.updateExtRefSource(scd, extRefInfo)); + TExtRef extRef = assertDoesNotThrow(() -> extRefEditorService.updateExtRefSource(scd, extRefInfo)); //Then assertThat(extRef.getSrcCBName()).isEqualTo(extRefInfo.getSourceInfo().getSrcCBName()); assertThat(extRef.getSrcLDInst()).isEqualTo(extRefInfo.getBindingInfo().getLdInst()); assertThat(extRef.getSrcLNClass()).contains(extRefInfo.getBindingInfo().getLnClass()); } + @ParameterizedTest(name = "{0}") + @MethodSource("provideFlowAndExtRefForDebinding") + void debindCompasFlowsAndExtRefsBasedOnVoltageLevel(String testCase, SCL scd, Tuple extRef1, Tuple flow1, Tuple extRef2, Tuple flow2) { + //Given + //Then + extRefEditorService.debindCompasFlowsAndExtRefsBasedOnVoltageLevel(scd); + //When + TInputs tInputs = findInputs(scd); + assertThat(tInputs.getExtRef().stream().filter(tExtRef -> tExtRef.getDesc().equals("Desc_1"))) + .extracting(TExtRef::getIedName, TExtRef::getLdInst) + .containsExactly(extRef1); + assertThat(PrivateUtils.getPrivateStream(tInputs.getPrivate(), TCompasFlow.class).filter(tCompasFlow -> tCompasFlow.getDataStreamKey().equals("Desc_1"))) + .extracting(TCompasFlow::getExtRefiedName, TCompasFlow::getExtRefldinst, TCompasFlow::getExtReflnClass, TCompasFlow::getExtReflnInst) + .containsExactly(flow1); + assertThat(tInputs.getExtRef().stream().filter(tExtRef -> tExtRef.getDesc().equals("Desc_2"))) + .extracting(TExtRef::getIedName, TExtRef::getLdInst) + .containsExactly(extRef2); + assertThat(PrivateUtils.getPrivateStream(tInputs.getPrivate(), TCompasFlow.class).filter(tCompasFlow -> tCompasFlow.getDataStreamKey().equals("Desc_2"))) + .extracting(TCompasFlow::getExtRefiedName, TCompasFlow::getExtRefldinst, TCompasFlow::getExtReflnClass, TCompasFlow::getExtReflnInst) + .containsExactly(flow2); + } + + private static Stream provideFlowAndExtRefForDebinding(){ + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-flow-debind/scd_extref_flow_debind_success.xml"); + SCL scdVoltageLevel0 = SclTestMarshaller.getSCLFromFile("/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_0.xml"); + SCL scdVoltageLevelUnknown = SclTestMarshaller.getSCLFromFile("/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_unknown.xml"); + SCL scdUnsetExtRefIedName = SclTestMarshaller.getSCLFromFile("/scd-extref-flow-debind/scd_extref_flow_not_debind.xml"); + SCL scdVLevelUnknownUnsetFlowSourceVoltageLevel = SclTestMarshaller.getSCLFromFile("/scd-extref-flow-debind/scd_extref_flow_not_debind_volatagelevelname_unknown.xml"); + Tuple tupleExtRef1 = Tuple.tuple("System_Version_IED_NAME1", "LD_INST11"); + Tuple tupleExtRef2 = Tuple.tuple("System_Version_IED_NAME2", "LD_INST21"); + Tuple tupleExtRefDebind = Tuple.tuple(null, null); + Tuple tupleFlow1 = Tuple.tuple("System_Version_IED_NAME1", "LD_INST11", "LLN0", null); + Tuple tupleFlow2 = Tuple.tuple("System_Version_IED_NAME2", "LD_INST21", "ANCR", "1"); + Tuple tupleFlowNoExtRefIedName = Tuple.tuple(null, "LD_INST21", "ANCR", "1"); + Tuple tupleFlowDebind = Tuple.tuple(null, null, null, null); + + return Stream.of( + Arguments.of("case known voltageLevel should debind THT flow and corresponding ExtRef", scd, tupleExtRef1, tupleFlow1, tupleExtRefDebind, tupleFlowDebind), + Arguments.of("case voltageLevel 0 should do nothing", scdVoltageLevel0, tupleExtRef1, tupleFlow1, tupleExtRef2, tupleFlow2), + Arguments.of("case unknown voltageLevel should debind all", scdVoltageLevelUnknown, tupleExtRef1, tupleFlowDebind, tupleExtRef2, tupleFlowDebind), + Arguments.of("case known voltageLevel should not debind because no ExtRefIedName", scdUnsetExtRefIedName, tupleExtRef1, tupleFlow1, tupleExtRef2, tupleFlowNoExtRefIedName), + Arguments.of("case unknown voltageLevel should not debind because unset FlowSourceVoltageLevel", scdVLevelUnknownUnsetFlowSourceVoltageLevel, tupleExtRef1, tupleFlow1, tupleExtRef2, tupleFlow2) + ); + + } + + + private TInputs findInputs(SCL scd) { + IedService iedService = new IedService(); + LdeviceService ldeviceService = new LdeviceService(); + return iedService.findIed(scd, tied -> tied.getName().equals("IED_NAME1")) + .flatMap(tied -> ldeviceService.findLdevice(tied, tlDevice -> tlDevice.getInst().equals("LD_INST11"))) + .map(tlDevice -> tlDevice.getLN0().getInputs()) + .orElseThrow(); + + } + } 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 new file mode 100644 index 000000000..acb6f0e1a --- /dev/null +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ExtRefServiceTest.java @@ -0,0 +1,161 @@ +/* + * // SPDX-FileCopyrightText: 2023 RTE FRANCE + * // + * // SPDX-License-Identifier: Apache-2.0 + */ + +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.*; +import org.lfenergy.compas.sct.commons.util.PrivateEnum; + +import java.util.List; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + + +class ExtRefServiceTest { + + private static final ObjectFactory objectFactory = new ObjectFactory(); + ExtRefService extRefService = new ExtRefService(); + + @ParameterizedTest(name = "{0}") + @MethodSource("provideTAnyLns") + void getExtRefs(String testCase, TInputs tInputs, int size) { + //Given + //When + //Then + assertThat(extRefService.getExtRefs(tInputs)).hasSize(size); + } + + private static Stream provideTAnyLns() { + TInputs tInputsEmpty = new TInputs(); + TInputs tInputs = new TInputs(); + TExtRef tExtRef1 = new TExtRef(); + TExtRef tExtRef2 = new TExtRef(); + tInputs.getExtRef().add(tExtRef1); + tInputs.getExtRef().add(tExtRef2); + + return Stream.of( + Arguments.of("Ln without Inputs node should return empty stream", null, 0), + Arguments.of("Ln with empty Inputs should return empty stream", tInputsEmpty, 0), + Arguments.of("Ln0 with Inputs node should return stream 2 extrefs", tInputs, 2)); + } + + @Test + void getMatchingCompasFlows() { + //Given + TInputs tInputs = new TInputs(); + TExtRef tExtRef1 = createExtRef("Desc_1", "IED_Name_1", "LD_INST_1"); + tInputs.getExtRef().add(tExtRef1); + TCompasFlow tCompasFlow1 = createCompasFlow("Desc_1", "IED_Name_1", "LD_INST_1"); + TCompasFlow tCompasFlow2 = createCompasFlow("Desc_2", "IED_Name_2", "LD_INST_2"); + tInputs.getPrivate().add(createPrivateCompasFlow(List.of(tCompasFlow1, tCompasFlow2))); + //When + Stream matchingCompasFlows = extRefService.getMatchingCompasFlows(tInputs, tExtRef1); + //Then + assertThat(matchingCompasFlows).hasSize(1) + .map(TCompasFlow::getDataStreamKey, TCompasFlow::getExtRefiedName) + .containsExactly(Tuple.tuple("Desc_1", "IED_Name_1")); + } + + @Test + void getMatchingTextRef() { + //Given + TLN tln = new TLN(); + TInputs tInputs = new TInputs(); + TExtRef tExtRef1 = createExtRef("Desc_1", "IED_Name_1", "LD_INST_1"); + TExtRef tExtRef2 = createExtRef("Desc_2", "IED_Name_2", "LD_INST_2"); + tInputs.getExtRef().add(tExtRef1); + tInputs.getExtRef().add(tExtRef2); + TCompasFlow tCompasFlow = createCompasFlow("Desc_1", "IED_Name_1", "LD_INST_1"); + tln.setInputs(tInputs); + //When + Stream tExtRefStream = extRefService.getMatchingTextRef(tInputs, tCompasFlow); + //Then + assertThat(tExtRefStream).hasSize(1) + .map(TExtRef::getIedName, TExtRef::getLdInst, TExtRef::getDesc) + .containsExactly(Tuple.tuple("IED_Name_1", "LD_INST_1", "Desc_1")); + } + + @Test + void getMatchingTextRef_success_when_lnclass_null() { + //Given + TLN tln = new TLN(); + TInputs tInputs = new TInputs(); + TExtRef tExtRef1 = createExtRef("Desc_1", "IED_Name_1", "LD_INST_1"); + tExtRef1.getLnClass().clear(); + TExtRef tExtRef2 = createExtRef("Desc_2", "IED_Name_2", "LD_INST_2"); + tInputs.getExtRef().add(tExtRef1); + tInputs.getExtRef().add(tExtRef2); + TCompasFlow tCompasFlow = createCompasFlow("Desc_1", "IED_Name_1", "LD_INST_1"); + tCompasFlow.setExtReflnClass(null); + tln.setInputs(tInputs); + //When + Stream tExtRefStream = extRefService.getMatchingTextRef(tInputs, tCompasFlow); + //Then + assertThat(tExtRefStream).hasSize(1) + .map(TExtRef::getIedName, TExtRef::getLdInst, TExtRef::getDesc) + .containsExactly(Tuple.tuple("IED_Name_1", "LD_INST_1", "Desc_1")); + } + + @Test + void clearBinding() { + //Given + TExtRef tExtRef = createExtRef("Desc_1", "IED_Name_1", "LD_INST_1"); + //When + extRefService.clearExtRefBinding(tExtRef); + //Then + assertThat(tExtRef) + .extracting(TExtRef::getIedName, TExtRef::getLdInst, TExtRef::getLnInst) + .containsOnlyNulls(); + assertThat(tExtRef.getDesc()).isEqualTo("Desc_1"); + } + + @Test + void clearCompasFlowBinding() { + //Given + TCompasFlow compasFlow = createCompasFlow("Desc_1", "IED_Name_1", "LD_INST_1"); + //When + extRefService.clearCompasFlowBinding(compasFlow); + //Then + assertThat(compasFlow) + .extracting(TCompasFlow::getExtRefiedName, TCompasFlow::getExtRefldinst, TCompasFlow::getExtReflnClass, TCompasFlow::getExtReflnInst, TCompasFlow::getExtRefprefix) + .containsOnlyNulls(); + assertThat(compasFlow.getDataStreamKey()).isEqualTo("Desc_1"); + } + + private static TPrivate createPrivateCompasFlow(List compasFlows) { + TPrivate tPrivate = new TPrivate(); + tPrivate.setType(PrivateEnum.COMPAS_FLOW.getPrivateType()); + tPrivate.getContent().addAll(compasFlows.stream().map(objectFactory::createFlow).toList()); + return tPrivate; + } + + private TExtRef createExtRef(String desc, String iedName, String ldInst) { + TExtRef tExtRef1 = new TExtRef(); + tExtRef1.setDesc(desc); + tExtRef1.setIedName(iedName); + tExtRef1.setLdInst(ldInst); + tExtRef1.getLnClass().add("LN"); + tExtRef1.setLnInst("1"); + return tExtRef1; + } + + private TCompasFlow createCompasFlow(String dataStreamKey, String extRefIedName, String extRefLdInst) { + TCompasFlow tCompasFlow = new TCompasFlow(); + tCompasFlow.setDataStreamKey(dataStreamKey); + tCompasFlow.setExtRefiedName(extRefIedName); + tCompasFlow.setExtRefldinst(extRefLdInst); + tCompasFlow.setExtReflnClass("LN"); + tCompasFlow.setExtReflnInst("1"); + return tCompasFlow; + } + +} \ No newline at end of file diff --git a/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_success.xml b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_success.xml new file mode 100644 index 000000000..df00a06ea --- /dev/null +++ b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_success.xml @@ -0,0 +1,101 @@ + + + + + + +
+ + + 90 + + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + + diff --git a/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_0.xml b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_0.xml new file mode 100644 index 000000000..e55aa6aef --- /dev/null +++ b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_0.xml @@ -0,0 +1,76 @@ + + + + + + +
+ + + 90 + + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + + diff --git a/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_unknown.xml b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_unknown.xml new file mode 100644 index 000000000..ff48290d5 --- /dev/null +++ b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_debind_volatagelevelname_unknown.xml @@ -0,0 +1,76 @@ + + + + + + +
+ + + 90 + + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + + diff --git a/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_not_debind.xml b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_not_debind.xml new file mode 100644 index 000000000..07c30b052 --- /dev/null +++ b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_not_debind.xml @@ -0,0 +1,101 @@ + + + + + + +
+ + + 90 + + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + + diff --git a/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_not_debind_volatagelevelname_unknown.xml b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_not_debind_volatagelevelname_unknown.xml new file mode 100644 index 000000000..44eb48233 --- /dev/null +++ b/sct-commons/src/test/resources/scd-extref-flow-debind/scd_extref_flow_not_debind_volatagelevelname_unknown.xml @@ -0,0 +1,76 @@ + + + + + + +
+ + + 90 + + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + +