From 2e55bdcd20f08f3ab142ee6b62835d883397bc74 Mon Sep 17 00:00:00 2001 From: massifben <105049157+massifben@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:26:55 +0100 Subject: [PATCH] replace Adapter by Services Signed-off-by: massifben <105049157+massifben@users.noreply.github.com> --- .../SclAutomationServiceIntegrationTest.java | 6 +- ...ce.java => ControlBlockEditorService.java} | 191 ++++++++++++------ .../sct/commons/scl/ConnectedApService.java | 68 +++++++ .../sct/commons/scl/ControlBlockService.java | 26 +++ .../sct/commons/util/ControlBlockEnum.java | 15 +- ...ava => ControlBlockEditorServiceTest.java} | 164 +++++++++------ 6 files changed, 336 insertions(+), 134 deletions(-) rename sct-commons/src/main/java/org/lfenergy/compas/sct/commons/{ControlBlockService.java => ControlBlockEditorService.java} (61%) create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ConnectedApService.java create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ControlBlockService.java rename sct-commons/src/test/java/org/lfenergy/compas/sct/commons/{ControlBlockServiceTest.java => ControlBlockEditorServiceTest.java} (78%) diff --git a/sct-app/src/test/java/org.lfenergy.compas.sct.app/SclAutomationServiceIntegrationTest.java b/sct-app/src/test/java/org.lfenergy.compas.sct.app/SclAutomationServiceIntegrationTest.java index d4aaea1ca..9f9422485 100644 --- a/sct-app/src/test/java/org.lfenergy.compas.sct.app/SclAutomationServiceIntegrationTest.java +++ b/sct-app/src/test/java/org.lfenergy.compas.sct.app/SclAutomationServiceIntegrationTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; import org.lfenergy.compas.scl2007b4.model.LN0; import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.sct.commons.ControlBlockService; +import org.lfenergy.compas.sct.commons.ControlBlockEditorService; import org.lfenergy.compas.sct.commons.SclService; import org.lfenergy.compas.sct.commons.SubstationService; import org.lfenergy.compas.sct.commons.api.ControlBlockEditor; @@ -16,6 +16,8 @@ import org.lfenergy.compas.sct.commons.api.SubstationEditor; import org.lfenergy.compas.sct.commons.dto.HeaderDTO; import org.lfenergy.compas.sct.commons.exception.ScdException; +import org.lfenergy.compas.sct.commons.scl.ConnectedApService; +import org.lfenergy.compas.sct.commons.scl.ControlBlockService; 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; @@ -33,7 +35,7 @@ class SclAutomationServiceIntegrationTest { private SclAutomationService sclAutomationService ; private static final SclEditor sclEditor = new SclService() ; private static final SubstationEditor substationEditor = new SubstationService() ; - private static final ControlBlockEditor controlBlockEditor = new ControlBlockService() ; + private static final ControlBlockEditor controlBlockEditor = new ControlBlockEditorService(new ControlBlockService(), new ConnectedApService()) ; private HeaderDTO headerDTO; diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockEditorService.java similarity index 61% rename from sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockService.java rename to sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockEditorService.java index f020686f5..72fdf0ed0 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockEditorService.java @@ -4,22 +4,22 @@ package org.lfenergy.compas.sct.commons; +import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; -import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.scl2007b4.model.TCompasICDHeader; -import org.lfenergy.compas.scl2007b4.model.TCompasSystemVersion; -import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec; +import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.api.ControlBlockEditor; 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.model.cbcom.*; +import org.lfenergy.compas.sct.commons.scl.ConnectedApService; +import org.lfenergy.compas.sct.commons.scl.ControlBlockService; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; -import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter; import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter; import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter; import org.lfenergy.compas.sct.commons.scl.ln.LNAdapter; import org.lfenergy.compas.sct.commons.util.ControlBlockEnum; +import org.lfenergy.compas.sct.commons.util.PrivateUtils; import org.lfenergy.compas.sct.commons.util.SclConstructorHelper; import org.lfenergy.compas.sct.commons.util.Utils; @@ -28,11 +28,22 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public class ControlBlockService implements ControlBlockEditor { +import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newP; + +@RequiredArgsConstructor +public class ControlBlockEditorService implements ControlBlockEditor { private static final int MAX_VLAN_ID = 0x0FFF; private static final int MAX_VLAN_PRIORITY = 7; private static final String NONE = "none"; + private static final String APPID_P_TYPE = "APPID"; + private static final String MAC_ADDRESS_P_TYPE = "MAC-Address"; + private static final String VLAN_ID_P_TYPE = "VLAN-ID"; + private static final String VLAN_PRIORITY_P_TYPE = "VLAN-PRIORITY"; + private static final int APPID_LENGTH = 4; + private static final int VLAN_ID_LENGTH = 3; + private final ControlBlockService controlBlockService; + private final ConnectedApService connectedApService; @Override public List analyzeDataGroups(SCL scd) { @@ -88,14 +99,6 @@ private List createDataSetAndControlBlocks(Stream .toList(); } - @Override - public List configureNetworkForAllControlBlocks(SCL scd, CBCom cbCom) { - List sclReportItems = new ArrayList<>(); - sclReportItems.addAll(configureNetworkForControlBlocks(scd, cbCom, TCBType.GOOSE)); - sclReportItems.addAll(configureNetworkForControlBlocks(scd, cbCom, TCBType.SV)); - return sclReportItems; - } - @Override public void removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(final SCL scl) { SclRootAdapter sclRootAdapter = new SclRootAdapter(scl); @@ -114,20 +117,60 @@ public void removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(final SCL scl) .forEach(LNAdapter::removeAllControlBlocksAndDatasets); } - private List configureNetworkForControlBlocks(SCL scd, CBCom cbCom, TCBType tcbType) { + @Override + public List configureNetworkForAllControlBlocks(SCL scd, CBCom cbCom) { + return Stream.concat( + configureNetworkForControlBlocks(scd, cbCom, TCBType.GOOSE), + configureNetworkForControlBlocks(scd, cbCom, TCBType.SV)) + .toList(); + } + + private Stream configureNetworkForControlBlocks(SCL scl, CBCom cbCom, TCBType tcbType) { + CbComSettings cbComSettings; + try { + cbComSettings = parseCbCom(cbCom, tcbType); + } catch (ScdException ex) { + return Stream.of(SclReportItem.error("Control Block Communication setting files", ex.getMessage())); + } + return scl.getIED().stream() + .flatMap(tied -> + tied.getAccessPoint() + .stream() + .filter(tAccessPoint -> tAccessPoint.isSetServer() && tAccessPoint.getServer().isSetLDevice()) + .flatMap(tAccessPoint -> tAccessPoint.getServer().getLDevice().stream() + .map(tlDevice -> new IedApLd(tied, tAccessPoint.getName(), tlDevice)) + ) + ) + .flatMap(iedApLd -> Optional.ofNullable(iedApLd.lDevice().getLN0()).stream() + .flatMap(ln0 -> controlBlockService.getControls(ln0, ControlBlockEnum.from(tcbType).getControlBlockClass())) + .map(tControl -> { + CriteriaOrError criteriaOrError = getCriteria(iedApLd.ied(), tcbType, tControl.getName()); + if (criteriaOrError.errorMessage != null) { + return Optional.of(SclReportItem.error(iedApLd.getXPath(), criteriaOrError.errorMessage)); + } + Settings settings = cbComSettings.settingsByCriteria.get(criteriaOrError.criteria); + if (settings == null) { + return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because: No controlBlock communication settings found with these " + criteriaOrError.criteria); + } + return configureControlBlockNetwork(scl.getCommunication(), settings, cbComSettings.appIdIterator, cbComSettings.macAddressIterator, tControl, iedApLd); + }) + .flatMap(Optional::stream) + ); + } + + private CbComSettings parseCbCom(CBCom cbCom, TCBType tcbType) { TRange appIdRange = Optional.ofNullable(cbCom.getAppIdRanges()).map(AppIdRanges::getAppIdRange).stream() .flatMap(Collection::stream) .filter(tRange -> tcbType.equals(tRange.getCBType())) .findFirst() .orElseThrow(() -> new ScdException("Control Block Communication setting files does not contain AppIdRange for cbType " + tcbType.value())); + PrimitiveIterator.OfLong appIdIterator = Utils.sequence(Long.parseLong(appIdRange.getStart(), 16), Long.parseLong(appIdRange.getEnd(), 16)); TRange macRange = Optional.ofNullable(cbCom.getMacRanges()).map(MacRanges::getMacRange).stream() .flatMap(Collection::stream) .filter(tRange -> tcbType.equals(tRange.getCBType())) .findFirst() .orElseThrow(() -> new ScdException("Control Block Communication setting files does not contain MacRange for cbType " + tcbType.value())); - - PrimitiveIterator.OfLong appIdIterator = Utils.sequence(Long.parseLong(appIdRange.getStart(), 16), Long.parseLong(appIdRange.getEnd(), 16)); Iterator macAddressIterator = Utils.macAddressSequence(macRange.getStart(), macRange.getEnd()); Map settingsByCriteria = Optional.ofNullable(cbCom.getVlans()).map(Vlans::getVlan).stream() @@ -135,75 +178,76 @@ private List configureNetworkForControlBlocks(SCL scd, CBCom cbCo .filter(vlan -> tcbType.equals(vlan.getCBType())) .collect(Collectors.toMap(this::vlanToCriteria, this::vlanToSetting)); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - return sclRootAdapter.streamIEDAdapters() - .flatMap(iedAdapter -> - iedAdapter.streamLDeviceAdapters() - .filter(LDeviceAdapter::hasLN0) - .map(LDeviceAdapter::getLN0Adapter) - .flatMap(ln0Adapter -> ln0Adapter.streamControlBlocks(ControlBlockEnum.from(tcbType))) - .map(controlBlockAdapter -> configureControlBlockNetwork(settingsByCriteria, appIdIterator, macAddressIterator, controlBlockAdapter))) - .flatMap(Optional::stream) - .toList(); + return new CbComSettings(appIdIterator, macAddressIterator, settingsByCriteria); } - private Optional configureControlBlockNetwork(Map comSettingsByCriteria, PrimitiveIterator.OfLong appIdIterator, Iterator macAddressIterator, ControlBlockAdapter controlBlockAdapter) { - SettingsOrError settingsOrError = getNetworkSettings(controlBlockAdapter, comSettingsByCriteria); - if (settingsOrError.errorMessage() != null) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure communication for this ControlBlock because: " + settingsOrError.errorMessage())); - } - Settings settings = settingsOrError.settings(); - if (settings == null) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure communication for this ControlBlock because no settings was provided")); - } + private Optional configureControlBlockNetwork(TCommunication tCommunication, Settings settings, PrimitiveIterator.OfLong appIdIterator, Iterator macAddressIterator, TControl tControl, IedApLd iedApLd) { if (settings.vlanId() == null) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings")); + return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings"); } if (!appIdIterator.hasNext()) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure communication for this ControlBlock because range of appId is exhausted")); + return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because range of appId is exhausted"); } if (!macAddressIterator.hasNext()) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure communication for this ControlBlock because range of MAC Address is exhausted")); + return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because range of MAC Address is exhausted"); + } + + Optional optConApAdapter = connectedApService.findConnectedAp(tCommunication, iedApLd.ied.getName(), iedApLd.apName); + if (optConApAdapter.isEmpty()) { + return newError(iedApLd, tControl, "Cannot configure communication for ControlBlock because no ConnectAP found for AccessPoint"); + } + TConnectedAP tConnectedAP = optConApAdapter.get(); + List listOfPs = new ArrayList<>(); + listOfPs.add(newP(APPID_P_TYPE, Utils.toHex(appIdIterator.nextLong(), APPID_LENGTH))); + listOfPs.add(newP(MAC_ADDRESS_P_TYPE, macAddressIterator.next())); + listOfPs.add(newP(VLAN_ID_P_TYPE, Utils.toHex(settings.vlanId(), VLAN_ID_LENGTH))); + if (settings.vlanPriority() != null) { + listOfPs.add(newP(VLAN_PRIORITY_P_TYPE, String.valueOf(settings.vlanPriority()))); } - return controlBlockAdapter.configureNetwork(appIdIterator.nextLong(), macAddressIterator.next(), settings.vlanId(), settings.vlanPriority(), - settings.minTime(), settings.maxTime()); + switch (tControl) { + case TGSEControl ignored -> new ConnectedApService().updateGseOrCreateIfNotExists(tConnectedAP, iedApLd.lDevice().getInst(), tControl.getName(), listOfPs, SclConstructorHelper.newDurationInMilliSec(settings.minTime), SclConstructorHelper.newDurationInMilliSec(settings.maxTime)); + case TSampledValueControl ignored -> new ConnectedApService().updateSmvOrCreateIfNotExists(tConnectedAP, iedApLd.lDevice().getInst(), tControl.getName(), listOfPs); + default -> throw new ScdException("Unsupported Control Block type for communication configuration : " + tControl.getClass().getName()); + } + return Optional.empty(); + } + + private static Optional newError(IedApLd iedApLd, TControl tControl, String message) { + return Optional.of(SclReportItem.error(iedApLd.getXPath() + "/LN0/" + controlBlockXPath(tControl), + message)); + } + + private static String controlBlockXPath(TControl tControl) { + return ControlBlockEnum.from(tControl.getClass()).getElementName() + "[@name=\"" + tControl.getName() + "\"]"; } - public SettingsOrError getNetworkSettings(ControlBlockAdapter controlBlockAdapter, Map comSettingsByCriteria) { - TCBType cbType = controlBlockAdapter.getControlBlockEnum().toTCBType(); - IEDAdapter iedAdapter = controlBlockAdapter.getParentIedAdapter(); - Optional compasSystemVersion = iedAdapter.getCompasSystemVersion(); + public CriteriaOrError getCriteria(TIED tied, TCBType cbType, String cbName) { + Optional compasSystemVersion = PrivateUtils.extractCompasPrivate(tied, TCompasSystemVersion.class); if (compasSystemVersion.isEmpty()) { - return new SettingsOrError(null, "No private COMPAS-SystemVersion found in this IED"); + return new CriteriaOrError(null, "No private COMPAS-SystemVersion found in this IED"); } if (StringUtils.isBlank(compasSystemVersion.get().getMainSystemVersion()) || (StringUtils.isBlank(compasSystemVersion.get().getMinorSystemVersion()))) { - return new SettingsOrError(null, "Missing MainSystemVersion or MinorSystemVersion attribute in COMPAS-SystemVersion private of IED"); + return new CriteriaOrError(null, "Missing MainSystemVersion or MinorSystemVersion attribute in COMPAS-SystemVersion private of IED"); } String systemVersionWithoutV = removeVFromSystemVersion(compasSystemVersion.get()); - Optional compasICDHeader = iedAdapter.getCompasICDHeader(); + Optional compasICDHeader = PrivateUtils.extractCompasPrivate(tied, TCompasICDHeader.class); if (compasICDHeader.isEmpty()) { - return new SettingsOrError(null, "No private COMPAS-ICDHeader found in this IED"); + return new CriteriaOrError(null, "No private COMPAS-ICDHeader found in this IED"); } if (compasICDHeader.get().getIEDSystemVersioninstance() == null) { - return new SettingsOrError(null, "No IEDSystemVersioninstance in the COMPAS-ICDHeader of this IED"); + return new CriteriaOrError(null, "No IEDSystemVersioninstance in the COMPAS-ICDHeader of this IED"); } - TBayIntOrExt bayIntOrExt = controlBlockAdapter.getName().endsWith("I") ? TBayIntOrExt.BAY_INTERNAL : TBayIntOrExt.BAY_EXTERNAL; - - Criteria criteria = new Criteria(cbType, - systemVersionWithoutV, - TIEDType.fromValue(compasICDHeader.get().getIEDType().value()), - TIEDRedundancy.fromValue(compasICDHeader.get().getIEDredundancy().value()), - compasICDHeader.get().getIEDSystemVersioninstance(), - bayIntOrExt); - Settings settings = comSettingsByCriteria.get(criteria); - return new SettingsOrError(settings, settings != null ? null : "No controlBlock communication settings found with these " + criteria); + TBayIntOrExt bayIntOrExt = cbName.endsWith("I") ? TBayIntOrExt.BAY_INTERNAL : TBayIntOrExt.BAY_EXTERNAL; + + return new CriteriaOrError( + new Criteria(cbType, + systemVersionWithoutV, + TIEDType.fromValue(compasICDHeader.get().getIEDType().value()), + TIEDRedundancy.fromValue(compasICDHeader.get().getIEDredundancy().value()), + compasICDHeader.get().getIEDSystemVersioninstance(), + bayIntOrExt), null); } private String removeVFromSystemVersion(TCompasSystemVersion compasSystemVersion) { @@ -312,6 +356,12 @@ private TDurationInMilliSec toDurationInMilliSec(String strDuration) { public record Criteria(TCBType cbType, String systemVersionWithoutV, TIEDType iedType, TIEDRedundancy iedRedundancy, BigInteger iedSystemVersionInstance, TBayIntOrExt bayIntOrExt) { } + /** + * Communication settings for ControlBlock or Error message + */ + public record CriteriaOrError(Criteria criteria, String errorMessage) { + } + /** * Communication settings for ControlBlock */ @@ -319,9 +369,16 @@ public record Settings(Integer vlanId, Byte vlanPriority, TDurationInMilliSec mi } /** - * Communication settings for ControlBlock or Error message + * All settings of CbCom in a useful format */ - public record SettingsOrError(Settings settings, String errorMessage) { + record CbComSettings(PrimitiveIterator.OfLong appIdIterator, Iterator macAddressIterator, Map settingsByCriteria) { } + record IedApLd(TIED ied, String apName, TLDevice lDevice) { + String getXPath() { + return """ + /SCL/IED[@name="%s"]/AccessPoint[@name="%s"]/Server/LDevice[@inst="%s"]""".formatted(ied.getName(), apName, lDevice.getInst()); + } + + } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ConnectedApService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ConnectedApService.java new file mode 100644 index 000000000..f843041b2 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ConnectedApService.java @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2023 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.scl; + +import org.lfenergy.compas.scl2007b4.model.*; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newAddress; + +public class ConnectedApService { + + public Optional findConnectedAp(TCommunication tCommunication, String iedName, String apName) { + if (!tCommunication.isSetSubNetwork()) { + return Optional.empty(); + } + return tCommunication.getSubNetwork().stream() + .filter(TSubNetwork::isSetConnectedAP) + .flatMap(tSubNetwork -> tSubNetwork.getConnectedAP().stream()) + .filter(tConnectedAP -> iedName.equals(tConnectedAP.getIedName()) && apName.equals(tConnectedAP.getApName())) + .findFirst(); + } + + /** + * Create A GSE Section or update an existing GSE Section (the network configuration of a GSEControl block). + */ + public void updateGseOrCreateIfNotExists(TConnectedAP tConnectedAP, String ldInst, String cbName, List listOfP, TDurationInMilliSec minTime, TDurationInMilliSec maxTime) { + Optional optGse = tConnectedAP.isSetGSE() ? + tConnectedAP.getGSE().stream().filter(gse1 -> Objects.equals(ldInst, gse1.getLdInst()) && Objects.equals(cbName, gse1.getCbName())).findFirst() + : Optional.empty(); + TGSE gse = optGse + .orElseGet(() -> { + TGSE newGse = new TGSE(); + newGse.setLdInst(ldInst); + newGse.setCbName(cbName); + tConnectedAP.getGSE().add(newGse); + return newGse; + } + ); + gse.setAddress(newAddress(listOfP)); + gse.setMinTime(minTime); + gse.setMaxTime(maxTime); + } + + /** + * Create A SMV Section or update an existing SMV Section (the network configuration of a SampledValueControl block).. + */ + public void updateSmvOrCreateIfNotExists(TConnectedAP tConnectedAP, String ldInst, String cbName, List listOfP) { + Optional optSmv = tConnectedAP.isSetGSE() ? + tConnectedAP.getSMV().stream().filter(smv1 -> Objects.equals(ldInst, smv1.getLdInst()) && Objects.equals(cbName, smv1.getCbName())).findFirst() + : Optional.empty(); + TSMV smv = optSmv + .orElseGet(() -> { + TSMV newSmv = new TSMV(); + newSmv.setLdInst(ldInst); + newSmv.setCbName(cbName); + tConnectedAP.getSMV().add(newSmv); + return newSmv; + } + ); + smv.setAddress(newAddress(listOfP)); + } + +} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ControlBlockService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ControlBlockService.java new file mode 100644 index 000000000..427fa8735 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ControlBlockService.java @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.scl; + +import org.lfenergy.compas.scl2007b4.model.*; + +import java.util.stream.Stream; + +public class ControlBlockService { + + public Stream getControls(TAnyLN tAnyLN, Class tControlClass){ + if (tControlClass == TGSEControl.class && tAnyLN instanceof TLN0 tln0 && tln0.isSetGSEControl()){ + return tln0.getGSEControl().stream().map(tControlClass::cast); + } else if (tControlClass == TSampledValueControl.class && tAnyLN instanceof TLN0 tln0 && tln0.isSetSampledValueControl()){ + return tln0.getSampledValueControl().stream().map(tControlClass::cast); + } else if (tControlClass == TReportControl.class && tAnyLN.isSetReportControl()){ + return tAnyLN.getReportControl().stream().map(tControlClass::cast); + } else if (tControlClass == TLogControl.class){ + return tAnyLN.getLogControl().stream().map(tControlClass::cast); + } + return Stream.empty(); + } + +} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnum.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnum.java index 2283fb1a1..8f0a5e92f 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnum.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnum.java @@ -5,6 +5,7 @@ package org.lfenergy.compas.sct.commons.util; import lombok.Getter; +import lombok.RequiredArgsConstructor; import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.model.cbcom.TCBType; @@ -12,17 +13,15 @@ import java.util.Objects; @Getter +@RequiredArgsConstructor public enum ControlBlockEnum { - GSE(TGSEControl.class), - SAMPLED_VALUE(TSampledValueControl.class), - REPORT(TReportControl.class), - LOG(TLogControl.class); + GSE(TGSEControl.class, "GSEControl"), + SAMPLED_VALUE(TSampledValueControl.class, "SampledValueControl"), + REPORT(TReportControl.class, "ReportControl"), + LOG(TLogControl.class, "LogControl"); private final Class controlBlockClass; - - ControlBlockEnum(Class controlBlockClass) { - this.controlBlockClass = controlBlockClass; - } + private final String elementName; public static ControlBlockEnum from(TServiceType tServiceType) { Objects.requireNonNull(tServiceType); diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ControlBlockServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ControlBlockEditorServiceTest.java similarity index 78% rename from sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ControlBlockServiceTest.java rename to sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ControlBlockEditorServiceTest.java index 5d323610c..18b4663e5 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ControlBlockServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ControlBlockEditorServiceTest.java @@ -7,7 +7,6 @@ import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.BeforeEach; 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; @@ -17,6 +16,8 @@ import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.model.cbcom.*; +import org.lfenergy.compas.sct.commons.scl.ConnectedApService; +import org.lfenergy.compas.sct.commons.scl.ControlBlockService; import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; import org.lfenergy.compas.sct.commons.scl.ied.DataSetAdapter; @@ -29,8 +30,8 @@ import org.lfenergy.compas.sct.commons.testhelpers.MarshallerWrapper; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; import org.lfenergy.compas.sct.commons.util.CsvUtils; -import org.mockito.InjectMocks; -import org.mockito.junit.jupiter.MockitoExtension; +import org.lfenergy.compas.sct.commons.util.PrivateEnum; +import org.lfenergy.compas.sct.commons.util.PrivateUtils; import java.nio.charset.StandardCharsets; import java.util.Collections; @@ -47,16 +48,15 @@ import static org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller.assertIsMarshallable; import static org.lfenergy.compas.sct.commons.util.ControlBlockEnum.*; -@ExtendWith(MockitoExtension.class) -class ControlBlockServiceTest { +class ControlBlockEditorServiceTest { - @InjectMocks - ControlBlockService controlBlockService; + ControlBlockEditorService controlBlockEditorService; private Set allowedFcdas; @BeforeEach void init() { + controlBlockEditorService = new ControlBlockEditorService(new ControlBlockService(), new ConnectedApService()); allowedFcdas = new HashSet<>(CsvUtils.parseRows("FcdaCandidates.csv", StandardCharsets.UTF_8, FcdaForDataSetsCreation.class)); } @@ -71,7 +71,7 @@ void analyzeDataGroups_should_success() { iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxSMV(2L); iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxReports(1L); // When - List sclReportItems = controlBlockService.analyzeDataGroups(scd); + List sclReportItems = controlBlockEditorService.analyzeDataGroups(scd); //Then assertThat(sclReportItems).isEmpty(); @@ -89,7 +89,7 @@ void analyzeDataGroups_should_return_errors_messages() { iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getGOOSE().setMax(2L); iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfReportControl().setMax(0L); // When - List sclReportItems = controlBlockService.analyzeDataGroups(scd); + List sclReportItems = controlBlockEditorService.analyzeDataGroups(scd); //Then assertThat(sclReportItems).hasSize(11) .extracting(SclReportItem::message) @@ -107,13 +107,12 @@ void analyzeDataGroups_should_return_errors_messages() { "There are too much SMV Control Blocks for the IED IED_NAME2: 3 > 1 max"); } - @Test void removeControlBlocksAndDatasetAndExtRefSrc_should_remove_srcXXX_attributes_on_ExtRef() { // Given SCL scl = SclTestMarshaller.getSCLFromFile("/scl-remove-controlBlocks-dataSet-extRefSrc/scl-with-control-blocks.xml"); // When - controlBlockService.removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(scl); + controlBlockEditorService.removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(scl); // Then SclRootAdapter scdRootAdapter = new SclRootAdapter(scl); List extRefs = scdRootAdapter @@ -146,7 +145,7 @@ void createDataSetAndControlBlocks_should_Throw_Exception_when_list_allowed_fcda // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When Then - assertThatCode(() -> controlBlockService.createDataSetAndControlBlocks(scd, fcdaForDataSets)) + assertThatCode(() -> controlBlockEditorService.createDataSetAndControlBlocks(scd, fcdaForDataSets)) .isInstanceOf(ScdException.class) .hasMessage("Accepted FCDAs list is empty, you should initialize allowed FCDA lists with CsvHelper class before"); } @@ -156,7 +155,7 @@ void createDataSetAndControlBlocks_should_create_DataSet() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When - List sclReportItems = controlBlockService.createDataSetAndControlBlocks(scd, allowedFcdas); + List sclReportItems = controlBlockEditorService.createDataSetAndControlBlocks(scd, allowedFcdas); // Then assertThat(sclReportItems).isEmpty(); assertThat(streamAllDataSets(scd)).hasSize(6); @@ -187,7 +186,7 @@ void createDataSetAndControlBlocks_should_create_ControlBlocks() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When - List sclReportItems = controlBlockService.createDataSetAndControlBlocks(scd, allowedFcdas); + List sclReportItems = controlBlockEditorService.createDataSetAndControlBlocks(scd, allowedFcdas); // Then assertThat(sclReportItems).isEmpty(); @@ -220,7 +219,7 @@ void createDataSetAndControlBlocks_should_set_ExtRef_srcXXX_attributes() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When - List sclReportItems = controlBlockService.createDataSetAndControlBlocks(scd, allowedFcdas); + List sclReportItems = controlBlockEditorService.createDataSetAndControlBlocks(scd, allowedFcdas); // Then assertThat(sclReportItems).isEmpty(); @@ -250,7 +249,7 @@ void createDataSetAndControlBlocks_with_targetIedName_should_Throw_Exception_whe // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When Then - assertThatCode(() -> controlBlockService.createDataSetAndControlBlocks(scd, "IED_NAME1", fcdaForDataSets)) + assertThatCode(() -> controlBlockEditorService.createDataSetAndControlBlocks(scd, "IED_NAME1", fcdaForDataSets)) .isInstanceOf(ScdException.class) .hasMessage("Accepted FCDAs list is empty, you should initialize allowed FCDA lists with CsvHelper class before"); } @@ -260,7 +259,7 @@ void createDataSetAndControlBlocks_when_targetIedName_is_provided_should_succeed // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When - List sclReportItems = controlBlockService.createDataSetAndControlBlocks(scd, "IED_NAME1", allowedFcdas); + List sclReportItems = controlBlockEditorService.createDataSetAndControlBlocks(scd, "IED_NAME1", allowedFcdas); // Then assertThat(sclReportItems).isEmpty(); assertThat(streamAllDataSets(scd)).hasSize(6); @@ -276,7 +275,7 @@ void createDataSetAndControlBlocks_when_targetIedName_is_provided_and_no_ext_ref // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When - List sclReportItems = controlBlockService.createDataSetAndControlBlocks(scd, "IED_NAME2", allowedFcdas); + List sclReportItems = controlBlockEditorService.createDataSetAndControlBlocks(scd, "IED_NAME2", allowedFcdas); // Then assertThat(sclReportItems).isEmpty(); assertThat(streamAllDataSets(scd)).isEmpty(); @@ -287,7 +286,7 @@ void createDataSetAndControlBlocks_when_targetIedName_is_not_found_should_throw_ // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When & Then - assertThatThrownBy(() -> controlBlockService.createDataSetAndControlBlocks(scd, "non_existing_IED_name", allowedFcdas)) + assertThatThrownBy(() -> controlBlockEditorService.createDataSetAndControlBlocks(scd, "non_existing_IED_name", allowedFcdas)) .isInstanceOf(ScdException.class) .hasMessage("IED.name 'non_existing_IED_name' not found in SCD"); } @@ -298,7 +297,7 @@ void createDataSetAndControlBlocks_with_targetIedName_and_targetLDeviceInst_shou // Given SCL scd = new SCL(); // When Then - assertThatCode(() -> controlBlockService.createDataSetAndControlBlocks(scd, "IED_NAME1", "LD_INST11", fcdaForDataSets)) + assertThatCode(() -> controlBlockEditorService.createDataSetAndControlBlocks(scd, "IED_NAME1", "LD_INST11", fcdaForDataSets)) .isInstanceOf(ScdException.class) .hasMessage("Accepted FCDAs list is empty, you should initialize allowed FCDA lists with CsvHelper class before"); } @@ -308,7 +307,7 @@ void createDataSetAndControlBlocks_when_targetIedName_and_targetLDeviceInst_is_p // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When - List sclReportItems = controlBlockService.createDataSetAndControlBlocks(scd, "IED_NAME1", "LD_INST11", allowedFcdas); + List sclReportItems = controlBlockEditorService.createDataSetAndControlBlocks(scd, "IED_NAME1", "LD_INST11", allowedFcdas); // Then assertThat(sclReportItems).isEmpty(); } @@ -318,7 +317,7 @@ void createDataSetAndControlBlocks_when_targetIedName_is_not_found_and_targetLDe // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When & Then - assertThatThrownBy(() -> controlBlockService.createDataSetAndControlBlocks(scd, "non_existing_IED_name", "LD_INST11", allowedFcdas)) + assertThatThrownBy(() -> controlBlockEditorService.createDataSetAndControlBlocks(scd, "non_existing_IED_name", "LD_INST11", allowedFcdas)) .isInstanceOf(ScdException.class) .hasMessage("IED.name 'non_existing_IED_name' not found in SCD"); } @@ -328,7 +327,7 @@ void createDataSetAndControlBlocks_when_targetIedName_and_targetLDeviceInst_is_n // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When & Then - assertThatThrownBy(() -> controlBlockService.createDataSetAndControlBlocks(scd, "IED_NAME1", "non_existing_LDevice_inst", allowedFcdas)) + assertThatThrownBy(() -> controlBlockEditorService.createDataSetAndControlBlocks(scd, "IED_NAME1", "non_existing_LDevice_inst", allowedFcdas)) .isInstanceOf(ScdException.class) .hasMessage("LDevice.inst 'non_existing_LDevice_inst' not found in IED 'IED_NAME1'"); } @@ -338,7 +337,7 @@ void createDataSetAndControlBlocks_when_targetLDeviceInst_is_provided_without_ta // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success.xml"); // When & Then - assertThatThrownBy(() -> controlBlockService.createDataSetAndControlBlocks(scd, null, "LD_INST11", allowedFcdas)) + assertThatThrownBy(() -> controlBlockEditorService.createDataSetAndControlBlocks(scd, null, "LD_INST11", allowedFcdas)) .isInstanceOf(ScdException.class) .hasMessage("IED.name parameter is missing"); } @@ -349,7 +348,7 @@ void updateAllSourceDataSetsAndControlBlocks_should_sort_FCDA_inside_DataSet_and // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_test_fcda_sort.xml"); // When - List sclReportItems = controlBlockService.createDataSetAndControlBlocks(scd, allowedFcdas); + List sclReportItems = controlBlockEditorService.createDataSetAndControlBlocks(scd, allowedFcdas); // Then assertThat(sclReportItems).isEmpty(); DataSetAdapter dataSetAdapter = findDataSet(scd, "IED_NAME2", "LD_INST21", "DS_LD_INST21_GSI"); @@ -369,7 +368,7 @@ void configureNetworkForAllControlBlocks_should_create_GSE_elements() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); CBCom cbCom = createCbCom(); // When - List sclReportItems = controlBlockService.configureNetworkForAllControlBlocks(scd, cbCom); + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); // Then assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue(); TGSE gse1 = getCommunicationGSE(scd, "IED_NAME2", "CB_LD_INST21_GSI"); @@ -411,7 +410,7 @@ void configureNetworkForAllControlBlocks_should_create_SMV_elements() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); CBCom cbCom = createCbCom(); // When - List sclReportItems = controlBlockService.configureNetworkForAllControlBlocks(scd, cbCom); + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); // Then assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue(); TSMV smv1 = getCommunicationSMV(scd, "IED_NAME2", "CB_LD_INST21_SVI"); @@ -444,7 +443,7 @@ void configureNetworkForAllControlBlocks_should_create_GSE_with_incremental_appi cbCom.getMacRanges().getMacRange().get(0).setStart("01-02-03-04-00-FF"); cbCom.getMacRanges().getMacRange().get(0).setEnd("01-02-03-04-01-01"); // When - List sclReportItems = controlBlockService.configureNetworkForAllControlBlocks(scd, cbCom); + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); // Then assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue(); assertThat(streamAllConnectedApGseP(scd, "APPID")) @@ -459,7 +458,7 @@ void configureNetworkForAllControlBlocks_should_fail_when_no_settings_for_this_c // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); // When - List sclReportItems = controlBlockService.configureNetworkForAllControlBlocks(scd, cbCom); + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); // Then assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isFalse(); assertThat(sclReportItems) @@ -480,27 +479,30 @@ public static Stream provideConfigureNetworkForAllControlBlocksErrors return Stream.of( Arguments.of(cbComWithNoVlan, "Cannot configure communication for this ControlBlock because: No controlBlock communication settings found with these Criteria[cbType=GOOSE, systemVersionWithoutV=01.00.009.001, iedType=BCU, iedRedundancy=A, iedSystemVersionInstance=1, bayIntOrExt=BAY_INTERNAL]", - "/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST21\"]/LN0/GSEControl[@name=\"CB_LD_INST21_GMI\"]"), + "/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint[@name=\"AP_NAME\"]/Server/LDevice[@inst=\"LD_INST21\"]/LN0/GSEControl[@name=\"CB_LD_INST21_GMI\"]"), Arguments.of(cbComWithMissingVlanId, "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings", - "/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST21\"]/LN0/GSEControl[@name=\"CB_LD_INST21_GMI\"]"), + "/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint[@name=\"AP_NAME\"]/Server/LDevice[@inst=\"LD_INST21\"]/LN0/GSEControl[@name=\"CB_LD_INST21_GMI\"]"), Arguments.of(cbComWithNotEnoughAppId, "Cannot configure communication for this ControlBlock because range of appId is exhausted", - "/SCL/IED[@name=\"IED_NAME3\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST31\"]/LN0/GSEControl[@name=\"CB_LD_INST31_GSE\"]"), + "/SCL/IED[@name=\"IED_NAME3\"]/AccessPoint[@name=\"AP_NAME\"]/Server/LDevice[@inst=\"LD_INST31\"]/LN0/GSEControl[@name=\"CB_LD_INST31_GSE\"]"), Arguments.of(cbComWithNotEnoughMacAddress, "Cannot configure communication for this ControlBlock because range of MAC Address is exhausted", - "/SCL/IED[@name=\"IED_NAME3\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST31\"]/LN0/GSEControl[@name=\"CB_LD_INST31_GSE\"]") + "/SCL/IED[@name=\"IED_NAME3\"]/AccessPoint[@name=\"AP_NAME\"]/Server/LDevice[@inst=\"LD_INST31\"]/LN0/GSEControl[@name=\"CB_LD_INST31_GSE\"]") ); } @ParameterizedTest @MethodSource("provideBlankCriteria") - void configureNetworkForAllControlBlocks_when_setting_files_has_blank_criteria_should_throw_exception(Consumer setCriteriaBlank) { + void configureNetworkForAllControlBlocks_when_setting_files_has_blank_criteria_should_return_error(Consumer setCriteriaBlank) { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); CBCom cbCom = createCbCom(); setCriteriaBlank.accept(cbCom.getVlans().getVlan().get(0)); - //When & Then - assertThatThrownBy(() -> controlBlockService.configureNetworkForAllControlBlocks(scd, cbCom)) - .isInstanceOf(ScdException.class) - .hasMessageStartingWith("Error in Control Block communication setting file: vlan is missing attribute "); + //When + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); + //Then + assertThat(sclReportItems).hasSize(1); + assertThat(sclReportItems.get(0)).extracting(SclReportItem::isError, SclReportItem::xpath) + .containsExactly(true, "Control Block Communication setting files"); + assertThat(sclReportItems.get(0).message()).matches("Error in Control Block communication setting file: vlan is missing attribute .*"); } private static Stream provideBlankCriteria() { @@ -516,16 +518,18 @@ private static Stream provideBlankCriteria() { @ParameterizedTest @MethodSource("provideMalformedNumbers") - void configureNetworkForAllControlBlocks_when_malformed_numbers_should_throw_exception(Consumer setMalformedNumber) { + void configureNetworkForAllControlBlocks_when_malformed_numbers_should_return_error(Consumer setMalformedNumber) { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); CBCom cbCom = createCbCom(); setMalformedNumber.accept(cbCom.getVlans().getVlan().get(0)); - //When & Then - assertThatThrownBy(() -> controlBlockService.configureNetworkForAllControlBlocks(scd, cbCom)) - .isInstanceOf(ScdException.class) - .hasMessageStartingWith("Error in Control Block communication setting file: ") - .hasMessageMatching("Error in Control Block communication setting file: .+ must be an integer( or 'none')?, but got : XXX"); + //When + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); + //Then + assertThat(sclReportItems).hasSize(1); + assertThat(sclReportItems.get(0)).extracting(SclReportItem::isError, SclReportItem::xpath) + .containsExactly(true, "Control Block Communication setting files"); + assertThat(sclReportItems.get(0).message()).matches("Error in Control Block communication setting file: .+ must be an integer( or 'none')?, but got : XXX"); } private static Stream provideMalformedNumbers() { @@ -540,24 +544,70 @@ private static Stream provideMalformedNumbers() { @ParameterizedTest @MethodSource("provideOutOfBoundNumbers") - void configureNetworkForAllControlBlocks_when_out_of_bound_numbers_should_throw_exception(Consumer setMalformedNumber) { + void configureNetworkForAllControlBlocks_when_out_of_bound_numbers_should_return_error(Consumer setMalformedNumber) { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); CBCom cbCom = createCbCom(); setMalformedNumber.accept(cbCom.getVlans().getVlan().get(0)); - //When & Then - assertThatThrownBy(() -> controlBlockService.configureNetworkForAllControlBlocks(scd, cbCom)) - .isInstanceOf(ScdException.class) - .hasMessageMatching("Error in Control Block communication setting file: VLAN (ID|Priority) must be between 0 and [0-9]+, but got : .*"); + //When + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); + //Then + assertThat(sclReportItems).hasSize(1); + assertThat(sclReportItems.get(0)).extracting(SclReportItem::isError, SclReportItem::xpath) + .containsExactly(true, "Control Block Communication setting files"); + assertThat(sclReportItems.get(0).message()).matches("Error in Control Block communication setting file: VLAN (ID|PRIORITY) must be between 0 and [0-9]+, but got : .*"); } private static Stream provideOutOfBoundNumbers() { - return Stream.of(Arguments.of( - (Consumer) tVlan -> tVlan.setVlanId("4096"), // VlanId > MAX_VLAN_ID - (Consumer) tVlan -> tVlan.setVlanId("-1"), // VlanId < 0 - (Consumer) tVlan -> tVlan.setVlanPriority("8"), // VlanPriority > MAX_VLAN_PRIORITY - (Consumer) tVlan -> tVlan.setVlanPriority("-1") // VlanPriority < 0 - )); + return Stream.of( + Arguments.of((Consumer) tVlan -> tVlan.setVlanId("4096")), // VlanId > MAX_VLAN_ID + Arguments.of((Consumer) tVlan -> tVlan.setVlanId("-1")), // VlanId < 0 + Arguments.of((Consumer) tVlan -> tVlan.setVlanPriority("8")), // VlanPriority > MAX_VLAN_PRIORITY + Arguments.of((Consumer) tVlan -> tVlan.setVlanPriority("-1")) // VlanPriority < 0 + ); + } + + @Test + void configureNetworkForAllControlBlocks_when_missing_connectedAp_should_return_error() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); + scd.getCommunication().getSubNetwork().get(0).getConnectedAP().remove(0); + CBCom cbCom = createCbCom(); + //When + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); + //Then + assertThat(sclReportItems).hasSize(3); + assertThat(sclReportItems).extracting(SclReportItem::isError).containsOnly(true); + assertThat(sclReportItems).extracting(SclReportItem::message).containsOnly("Cannot configure communication for ControlBlock because no ConnectAP found for AccessPoint"); + assertThat(sclReportItems).extracting(SclReportItem::xpath).allMatch(xpath -> xpath.startsWith(""" + /SCL/IED[@name="IED_NAME2"]/AccessPoint[@name="AP_NAME"]/Server/LDevice[@inst="LD_INST21"]/LN0/""")); + } + + @ParameterizedTest + @MethodSource("provideRemovePrivateInfo") + void configureNetworkForAllControlBlocks_when_missing_IED_privates_should_return_error(Consumer removePrivateInfo) { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); + CBCom cbCom = createCbCom(); + removePrivateInfo.accept(scd.getIED().get(1)); + //When + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); + //Then + assertThat(sclReportItems).hasSize(3); + assertThat(sclReportItems).extracting(SclReportItem::isError, SclReportItem::xpath) + .containsOnly(Tuple.tuple(true, """ + /SCL/IED[@name="IED_NAME2"]/AccessPoint[@name="AP_NAME"]/Server/LDevice[@inst="LD_INST21"]""")); + assertThat(sclReportItems).extracting(SclReportItem::message).allSatisfy(message -> assertThat(message).containsAnyOf("COMPAS-ICDHeader", "COMPAS-SystemVersion")); + } + + private static Stream provideRemovePrivateInfo() { + return Stream.of( + Arguments.of((Consumer) tied -> PrivateUtils.removePrivates(tied, PrivateEnum.COMPAS_SYSTEM_VERSION)), + Arguments.of((Consumer) tied -> PrivateUtils.extractCompasPrivate(tied, TCompasSystemVersion.class).ifPresent(tCompasSystemVersion -> tCompasSystemVersion.setMainSystemVersion(null))), + Arguments.of((Consumer) tied -> PrivateUtils.extractCompasPrivate(tied, TCompasSystemVersion.class).ifPresent(tCompasSystemVersion -> tCompasSystemVersion.setMinorSystemVersion(null))), + Arguments.of((Consumer) tied -> PrivateUtils.removePrivates(tied, PrivateEnum.COMPAS_ICDHEADER)), + Arguments.of((Consumer) tied -> PrivateUtils.extractCompasPrivate(tied, TCompasICDHeader.class).ifPresent(tCompasICDHeader -> tCompasICDHeader.setIEDSystemVersioninstance(null))) + ); } @Test @@ -565,7 +615,7 @@ void removeControlBlocksAndDatasetAndExtRefSrc_should_remove_controlBlocks_and_D // Given SCL scl = SclTestMarshaller.getSCLFromFile("/scl-remove-controlBlocks-dataSet-extRefSrc/scl-with-control-blocks.xml"); // When - controlBlockService.removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(scl); + controlBlockEditorService.removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(scl); // Then SclRootAdapter scdRootAdapter = new SclRootAdapter(scl); List lDevices = scdRootAdapter.streamIEDAdapters().flatMap(IEDAdapter::streamLDeviceAdapters).toList(); @@ -585,7 +635,7 @@ void removeControlBlocksAndDatasetAndExtRefSrc_should_remove_controlBlocks_and_D // Given SCL scl = SclTestMarshaller.getSCLFromFile("/scl-remove-controlBlocks-dataSet-extRefSrc/scl-with-control-blocks.xml"); // When - controlBlockService.removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(scl); + controlBlockEditorService.removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(scl); // Then SclRootAdapter scdRootAdapter = new SclRootAdapter(scl); List lns = scdRootAdapter.streamIEDAdapters()