From 95ee0d5f2b5ea882aed5684416cb63ef11e3910d Mon Sep 17 00:00:00 2001 From: massifben <105049157+massifben@users.noreply.github.com> Date: Wed, 6 Dec 2023 11:58:21 +0100 Subject: [PATCH] feat(#362): RSR-812 - Use generated JAXB class for configuration of Communication of Control Blocks Signed-off-by: massifben <105049157+massifben@users.noreply.github.com> --- .../SclAutomationServiceIntegrationTest.java | 5 +- sct-commons/pom.xml | 19 +- .../commons/ControlBlockEditorService.java | 430 ++++++++++++++++++ .../sct/commons/ControlBlockService.java | 159 ------- .../sct/commons/api/ControlBlockEditor.java | 16 +- .../dto/ControlBlockNetworkSettings.java | 71 --- .../sct/commons/scl/ControlService.java | 26 ++ .../commons/scl/ied/ControlBlockAdapter.java | 80 ---- .../sct/commons/util/ControlBlockEnum.java | 24 +- .../ControlBlockNetworkSettingsCsvHelper.java | 204 --------- .../src/main/resources/xsd/GSE_SMV_CB_COM.xsd | 152 +++++++ .../{LDEPF_Config_file_v2.xsd => LDEPF.xsd} | 0 ...ava => ControlBlockEditorServiceTest.java} | 416 ++++++++++++----- .../dto/ControlBlockNetworkSettingsTest.java | 235 ---------- .../scl/ied/ControlBlockAdapterTest.java | 87 +--- .../commons/util/ControlBlockEnumTest.java | 41 +- .../ControlBlockCommunicationTemplates.csv | 9 - 17 files changed, 995 insertions(+), 979 deletions(-) create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockEditorService.java delete mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockService.java delete mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettings.java create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ControlService.java delete mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockNetworkSettingsCsvHelper.java create mode 100644 sct-commons/src/main/resources/xsd/GSE_SMV_CB_COM.xsd rename sct-commons/src/main/resources/xsd/{LDEPF_Config_file_v2.xsd => LDEPF.xsd} (100%) rename sct-commons/src/test/java/org/lfenergy/compas/sct/commons/{ControlBlockServiceTest.java => ControlBlockEditorServiceTest.java} (52%) delete mode 100644 sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettingsTest.java delete mode 100644 sct-commons/src/test/resources/ControlBlockCommunicationTemplates.csv 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..d2e8bd735 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,7 @@ 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.ControlService; 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 +34,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 ControlService()) ; private HeaderDTO headerDTO; diff --git a/sct-commons/pom.xml b/sct-commons/pom.xml index c807d1237..285b507b1 100644 --- a/sct-commons/pom.xml +++ b/sct-commons/pom.xml @@ -185,7 +185,7 @@ - ${project.basedir}/src/main/resources/xsd/LDEPF_Config_file_v2.xsd + ${project.basedir}/src/main/resources/xsd/LDEPF.xsd ${project.basedir}/src/main/resources/binding_configuration.xjb @@ -214,6 +214,23 @@ false + + cbcom + + xjc + + + + ${project.basedir}/src/main/resources/xsd/GSE_SMV_CB_COM.xsd + + + ${project.basedir}/src/main/resources/binding_configuration.xjb + + org.lfenergy.compas.sct.commons.model.cbcom + true + false + + diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockEditorService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockEditorService.java new file mode 100644 index 000000000..9f01aa8ec --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockEditorService.java @@ -0,0 +1,430 @@ +// SPDX-FileCopyrightText: 2022 2023 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons; + +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +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.ControlService; +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; +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; + +import java.math.BigInteger; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newAddress; +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 static final String MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM = "Error in Control Block communication setting file: vlan is missing attribute "; + private final ControlService controlService; + + @Override + public List analyzeDataGroups(SCL scd) { + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + return sclRootAdapter.streamIEDAdapters() + .map(iedAdapter -> { + List list = new ArrayList<>(); + list.addAll(iedAdapter.checkDataGroupCoherence()); + list.addAll(iedAdapter.checkBindingDataGroupCoherence()); + return list; + }).flatMap(Collection::stream).toList(); + } + + @Override + public List createDataSetAndControlBlocks(SCL scd, Set allowedFcdas) { + checkFcdaInitDataPresence(allowedFcdas); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + Stream lDeviceAdapters = sclRootAdapter.streamIEDAdapters().flatMap(IEDAdapter::streamLDeviceAdapters); + return createDataSetAndControlBlocks(lDeviceAdapters, allowedFcdas); + } + + @Override + public List createDataSetAndControlBlocks(SCL scd, String targetIedName, Set allowedFcdas) { + checkFcdaInitDataPresence(allowedFcdas); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(targetIedName); + return createDataSetAndControlBlocks(iedAdapter.streamLDeviceAdapters(), allowedFcdas); + + } + + @Override + public List createDataSetAndControlBlocks(SCL scd, String targetIedName, String targetLDeviceInst, Set allowedFcdas) { + requireNotBlank(targetIedName, "IED.name parameter is missing"); + checkFcdaInitDataPresence(allowedFcdas); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(targetIedName); + LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(targetLDeviceInst); + return createDataSetAndControlBlocks(Stream.of(lDeviceAdapter), allowedFcdas); + } + + private void checkFcdaInitDataPresence(Set allowedFcdas) { + if (allowedFcdas == null || allowedFcdas.isEmpty()) { + throw new ScdException("Accepted FCDAs list is empty, you should initialize allowed FCDA lists with CsvHelper class before"); + } + } + + private List createDataSetAndControlBlocks(Stream lDeviceAdapters, Set allowedFcdas) { + return lDeviceAdapters + .map(lDeviceAdapter -> lDeviceAdapter.createDataSetAndControlBlocks(allowedFcdas)) + .flatMap(List::stream) + .toList(); + } + + @Override + public void removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(final SCL scl) { + SclRootAdapter sclRootAdapter = new SclRootAdapter(scl); + List lDeviceAdapters = sclRootAdapter.streamIEDAdapters() + .flatMap(IEDAdapter::streamLDeviceAdapters).toList(); + // LN0 + lDeviceAdapters.stream() + .map(LDeviceAdapter::getLN0Adapter) + .forEach(ln0 -> { + ln0.removeAllControlBlocksAndDatasets(); + ln0.removeAllExtRefSourceBindings(); + }); + // Other LN + lDeviceAdapters.stream() + .map(LDeviceAdapter::getLNAdapters).flatMap(List::stream) + .forEach(LNAdapter::removeAllControlBlocksAndDatasets); + } + + @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 -> controlService.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())); + Iterator macAddressIterator = Utils.macAddressSequence(macRange.getStart(), macRange.getEnd()); + + Map settingsByCriteria = Optional.ofNullable(cbCom.getVlans()).map(Vlans::getVlan).stream() + .flatMap(Collection::stream) + .filter(vlan -> tcbType.equals(vlan.getCBType())) + .collect(Collectors.toMap(this::vlanToCriteria, this::vlanToSetting)); + + return new CbComSettings(appIdIterator, macAddressIterator, settingsByCriteria); + } + + private Optional configureControlBlockNetwork(TCommunication tCommunication, Settings settings, PrimitiveIterator.OfLong appIdIterator, Iterator macAddressIterator, TControl tControl, IedApLd iedApLd) { + if (settings.vlanId() == null) { + return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings"); + } + if (!appIdIterator.hasNext()) { + return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because range of appId is exhausted"); + } + if (!macAddressIterator.hasNext()) { + return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because range of MAC Address is exhausted"); + } + + Optional optConApAdapter = findConnectedAp(tCommunication, iedApLd.ied.getName(), iedApLd.apName); + if (optConApAdapter.isEmpty()) { + return newError(iedApLd, tControl, "Cannot configure communication for ControlBlock because no ConnectedAP 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()))); + } + + switch (tControl) { + case TGSEControl ignored -> updateGseOrCreateIfNotExists(tConnectedAP, iedApLd.lDevice().getInst(), tControl.getName(), listOfPs, SclConstructorHelper.newDurationInMilliSec(settings.minTime), SclConstructorHelper.newDurationInMilliSec(settings.maxTime)); + case TSampledValueControl ignored -> 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 Optional findConnectedAp(TCommunication tCommunication, String iedName, String apName) { + if (tCommunication == null || !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(); + } + + private 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).. + */ + private void updateSmvOrCreateIfNotExists(TConnectedAP tConnectedAP, String ldInst, String cbName, List listOfP) { + Optional optSmv = tConnectedAP.isSetSMV() ? + 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)); + } + + 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 CriteriaOrError getCriteria(TIED tied, TCBType cbType, String cbName) { + Optional compasSystemVersion = PrivateUtils.extractCompasPrivate(tied, TCompasSystemVersion.class); + if (compasSystemVersion.isEmpty()) { + 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 CriteriaOrError(null, "Missing MainSystemVersion or MinorSystemVersion attribute in COMPAS-SystemVersion private of IED"); + } + String systemVersionWithoutV = removeVFromSystemVersion(compasSystemVersion.get()); + Optional compasICDHeader = PrivateUtils.extractCompasPrivate(tied, TCompasICDHeader.class); + if (compasICDHeader.isEmpty()) { + return new CriteriaOrError(null, "No private COMPAS-ICDHeader found in this IED"); + } + if (compasICDHeader.get().getIEDSystemVersioninstance() == null) { + return new CriteriaOrError(null, "No IEDSystemVersioninstance in the COMPAS-ICDHeader of this IED"); + } + 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) { + String[] minorVersionParts = compasSystemVersion.getMinorSystemVersion().split("\\."); + return (minorVersionParts.length == 3) ? + compasSystemVersion.getMainSystemVersion() + "." + minorVersionParts[0] + "." + minorVersionParts[1] + : null; + } + + private Criteria vlanToCriteria(TVlan vlan) { + requireNotNull(vlan.getCBType(), MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM + "CBType"); + requireNotBlank(vlan.getXY(), MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM + "XY"); + requireNotBlank(vlan.getZW(), MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM + "ZW"); + requireNotNull(vlan.getIEDType(), MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM + "IEDType"); + requireNotNull(vlan.getIEDRedundancy(), MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM + "IEDRedundancy"); + requireNotBlank(vlan.getIEDSystemVersionInstance(), MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM + "IEDSystemVersionInstance"); + requireNotNull(vlan.getBayIntOrExt(), MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM + "BayIntOrExt"); + + return new Criteria( + vlan.getCBType(), + vlan.getXY() + "." + vlan.getZW(), + vlan.getIEDType(), + vlan.getIEDRedundancy(), + toIedSystemVersionInstance(vlan.getIEDSystemVersionInstance()), + vlan.getBayIntOrExt() + ); + } + + private Settings vlanToSetting(TVlan vlan) { + return new Settings(toVLanId(vlan.getVlanId()), toVlanPriority(vlan.getVlanPriority()), toDurationInMilliSec(vlan.getMinTime()), toDurationInMilliSec(vlan.getMaxTime())); + } + + private void requireNotBlank(String str, String message) { + if (StringUtils.isBlank(str)) { + throw new ScdException(message); + } + } + + private void requireNotNull(Object o, String message) { + if (Objects.isNull(o)) { + throw new ScdException(message); + } + } + + private BigInteger toIedSystemVersionInstance(String strIedSystemVersionInstance) { + if (StringUtils.isBlank(strIedSystemVersionInstance)) { + return null; + } + BigInteger iedSystemVersionInstance; + try { + iedSystemVersionInstance = new BigInteger(strIedSystemVersionInstance); + } catch (NumberFormatException e) { + throw new ScdException("Error in Control Block communication setting file: IED System Version Instance must be an integer, but got : %s".formatted(strIedSystemVersionInstance)); + } + return iedSystemVersionInstance; + } + + private Integer toVLanId(String strVlanId) { + if (StringUtils.isBlank(strVlanId) || NONE.equalsIgnoreCase(strVlanId)) { + return null; + } + int vlanId; + try { + vlanId = Integer.parseInt(strVlanId); + } catch (NumberFormatException e) { + throw new ScdException("Error in Control Block communication setting file: VLAN ID must be an integer or '%s', but got : %s".formatted(NONE, strVlanId)); + } + if (vlanId < 0 || vlanId > MAX_VLAN_ID) { + throw new ScdException("Error in Control Block communication setting file: VLAN ID must be between 0 and %d, but got : %s".formatted(MAX_VLAN_ID, strVlanId)); + } + return vlanId; + } + + private static Byte toVlanPriority(String strVlanPriority) { + if (StringUtils.isBlank(strVlanPriority) || NONE.equalsIgnoreCase(strVlanPriority)) { + return null; + } + byte vlanPriority; + try { + vlanPriority = Byte.parseByte(strVlanPriority); + } catch (NumberFormatException e) { + throw new ScdException("Error in Control Block communication setting file: VLAN Priority must be an integer or '%s', but got : %s".formatted(NONE, strVlanPriority)); + } + if (vlanPriority < 0 || vlanPriority > MAX_VLAN_PRIORITY) { + throw new ScdException("Error in Control Block communication setting file: VLAN PRIORITY must be between 0 and %d, but got : %s".formatted(MAX_VLAN_PRIORITY, strVlanPriority)); + } + return vlanPriority; + } + + private TDurationInMilliSec toDurationInMilliSec(String strDuration) { + if (StringUtils.isBlank(strDuration) || NONE.equalsIgnoreCase(strDuration)) { + return null; + } + long duration; + try { + duration = Long.parseLong(strDuration); + } catch (NumberFormatException e) { + throw new ScdException("Error in Control Block communication setting file: VLAN MinTime and MaxTime must be an integer or '%s', but got : %s".formatted(NONE, strDuration)); + } + return SclConstructorHelper.newDurationInMilliSec(duration); + } + + /** + * Key to search for a control block communication setting + */ + 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 + */ + public record Settings(Integer vlanId, Byte vlanPriority, TDurationInMilliSec minTime, TDurationInMilliSec maxTime) { + } + + /** + * All settings of CbCom in a useful format + */ + 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/ControlBlockService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockService.java deleted file mode 100644 index 5f28d9e61..000000000 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockService.java +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-FileCopyrightText: 2022 2023 RTE FRANCE -// -// SPDX-License-Identifier: Apache-2.0 - -package org.lfenergy.compas.sct.commons; - -import org.apache.commons.lang3.StringUtils; -import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.sct.commons.api.ControlBlockEditor; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.NetworkRanges; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.RangesPerCbType; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.Settings; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.SettingsOrError; -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.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.Utils; - -import java.util.*; -import java.util.stream.Stream; - -public class ControlBlockService implements ControlBlockEditor { - - - @Override - public List analyzeDataGroups(SCL scd) { - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - return sclRootAdapter.streamIEDAdapters() - .map(iedAdapter -> { - List list = new ArrayList<>(); - list.addAll(iedAdapter.checkDataGroupCoherence()); - list.addAll(iedAdapter.checkBindingDataGroupCoherence()); - return list; - }).flatMap(Collection::stream).toList(); - } - - @Override - public List createDataSetAndControlBlocks(SCL scd, Set allowedFcdas) { - checkFcdaInitDataPresence(allowedFcdas); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - Stream lDeviceAdapters = sclRootAdapter.streamIEDAdapters().flatMap(IEDAdapter::streamLDeviceAdapters); - return createDataSetAndControlBlocks(lDeviceAdapters, allowedFcdas); - } - - @Override - public List createDataSetAndControlBlocks(SCL scd, String targetIedName, Set allowedFcdas) { - checkFcdaInitDataPresence(allowedFcdas); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(targetIedName); - return createDataSetAndControlBlocks(iedAdapter.streamLDeviceAdapters(), allowedFcdas); - - } - - @Override - public List createDataSetAndControlBlocks(SCL scd, String targetIedName, String targetLDeviceInst, Set allowedFcdas) { - if (StringUtils.isBlank(targetIedName)) { - throw new ScdException("IED.name parameter is missing"); - } - checkFcdaInitDataPresence(allowedFcdas); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(targetIedName); - LDeviceAdapter lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(targetLDeviceInst); - return createDataSetAndControlBlocks(Stream.of(lDeviceAdapter), allowedFcdas); - } - - private void checkFcdaInitDataPresence(Set allowedFcdas) { - if (allowedFcdas == null || allowedFcdas.isEmpty()) { - throw new ScdException("Accepted FCDAs list is empty, you should initialize allowed FCDA lists with CsvHelper class before"); - } - } - - private List createDataSetAndControlBlocks(Stream lDeviceAdapters, Set allowedFcdas) { - return lDeviceAdapters - .map(lDeviceAdapter -> lDeviceAdapter.createDataSetAndControlBlocks(allowedFcdas)) - .flatMap(List::stream) - .toList(); - } - - @Override - public List configureNetworkForAllControlBlocks(SCL scd, ControlBlockNetworkSettings controlBlockNetworkSettings, - RangesPerCbType rangesPerCbType) { - List sclReportItems = new ArrayList<>(); - sclReportItems.addAll(configureNetworkForControlBlocks(scd, controlBlockNetworkSettings, rangesPerCbType.gse(), ControlBlockEnum.GSE)); - sclReportItems.addAll(configureNetworkForControlBlocks(scd, controlBlockNetworkSettings, rangesPerCbType.sampledValue(), ControlBlockEnum.SAMPLED_VALUE)); - return sclReportItems; - } - - @Override - public void removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(final SCL scl) { - SclRootAdapter sclRootAdapter = new SclRootAdapter(scl); - List lDeviceAdapters = sclRootAdapter.streamIEDAdapters() - .flatMap(IEDAdapter::streamLDeviceAdapters).toList(); - // LN0 - lDeviceAdapters.stream() - .map(LDeviceAdapter::getLN0Adapter) - .forEach(ln0 -> { - ln0.removeAllControlBlocksAndDatasets(); - ln0.removeAllExtRefSourceBindings(); - }); - // Other LN - lDeviceAdapters.stream() - .map(LDeviceAdapter::getLNAdapters).flatMap(List::stream) - .forEach(LNAdapter::removeAllControlBlocksAndDatasets); - } - - - private List configureNetworkForControlBlocks(SCL scd, ControlBlockNetworkSettings controlBlockNetworkSettings, - NetworkRanges networkRanges, ControlBlockEnum controlBlockEnum) { - PrimitiveIterator.OfLong appIdIterator = Utils.sequence(networkRanges.appIdStart(), networkRanges.appIdEnd()); - Iterator macAddressIterator = Utils.macAddressSequence(networkRanges.macAddressStart(), networkRanges.macAddressEnd()); - - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - return sclRootAdapter.streamIEDAdapters() - .flatMap(iedAdapter -> - iedAdapter.streamLDeviceAdapters() - .filter(LDeviceAdapter::hasLN0) - .map(LDeviceAdapter::getLN0Adapter) - .flatMap(ln0Adapter -> ln0Adapter.streamControlBlocks(controlBlockEnum)) - .map(controlBlockAdapter -> configureControlBlockNetwork(controlBlockNetworkSettings, appIdIterator, macAddressIterator, controlBlockAdapter))) - .flatMap(Optional::stream) - .toList(); - } - - private Optional configureControlBlockNetwork(ControlBlockNetworkSettings controlBlockNetworkSettings, PrimitiveIterator.OfLong appIdIterator, Iterator macAddressIterator, ControlBlockAdapter controlBlockAdapter) { - SettingsOrError settingsOrError = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter); - if (settingsOrError.errorMessage() != null) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure network for this ControlBlock because: " + settingsOrError.errorMessage())); - } - Settings settings = settingsOrError.settings(); - if (settings == null) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure network for this ControlBlock because no settings was provided")); - } - if (settings.vlanId() == null) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure network for this ControlBlock because no Vlan Id was provided in the settings")); - } - if (!appIdIterator.hasNext()) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure network for this ControlBlock because range of appId is exhausted")); - } - if (!macAddressIterator.hasNext()) { - return Optional.of(controlBlockAdapter.buildFatalReportItem( - "Cannot configure network for this ControlBlock because range of MAC Address is exhausted")); - } - - return controlBlockAdapter.configureNetwork(appIdIterator.nextLong(), macAddressIterator.next(), settings.vlanId(), settings.vlanPriority(), - settings.minTime(), settings.maxTime()); - } - -} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ControlBlockEditor.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ControlBlockEditor.java index 7406c23c3..8235dd5ea 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ControlBlockEditor.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ControlBlockEditor.java @@ -6,9 +6,9 @@ import org.lfenergy.compas.scl2007b4.model.SCL; import org.lfenergy.compas.scl2007b4.model.TExtRef; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings; import org.lfenergy.compas.sct.commons.dto.FcdaForDataSetsCreation; import org.lfenergy.compas.sct.commons.dto.SclReportItem; +import org.lfenergy.compas.sct.commons.model.cbcom.CBCom; import org.lfenergy.compas.sct.commons.util.Utils; import java.util.List; @@ -22,7 +22,7 @@ *
    *
  1. {@link ControlBlockEditor#createDataSetAndControlBlocks(SCL, Set) Create DataSet and ControlBlock based on the TExtRef}
  2. *
  3. {@link ControlBlockEditor#createDataSetAndControlBlocks(SCL, String, Set) Create DataSet and ControlBlock based on the TExtRef in given IED}
  4. - *
  5. {@link ControlBlockEditor#createDataSetAndControlBlocks(SCL, String,String, Set) Create DataSet and ControlBlock based on the TExtRef in given IED and LDevice}
  6. + *
  7. {@link ControlBlockEditor#createDataSetAndControlBlocks(SCL, String, String, Set) Create DataSet and ControlBlock based on the TExtRef in given IED and LDevice}
  8. *
  9. {@link ControlBlockEditor#configureNetworkForAllControlBlocks Configure the network for the ControlBlocks}
  10. *
  11. {@link ControlBlockEditor#removeAllControlBlocksAndDatasetsAndExtRefSrcBindings Removes all ControlBlocks and DataSets for all LNs in SCL}
  12. *
  13. {@link ControlBlockEditor#analyzeDataGroups(SCL)} Checks Control Blocks, DataSets and FCDA number limitation into Access Points }
  14. @@ -85,17 +85,11 @@ public interface ControlBlockEditor { * - the Communication/SubNetwork/ConnectedAP/GSE element, for the GSEControl blocks * - the Communication/SubNetwork/ConnectedAP/SMV element, for the SampledValueControl blocks * - * @param scd input SCD object. The object will be modified with the new DataGSESet and SMV elements - * @param controlBlockNetworkSettings a method tha gives the network configuration information for a given ControlBlock - * @param rangesPerCbType provide NetworkRanges for GSEControl and SampledValueControl. NetworkRanges contains : - * start-end app APPID range (long value), start-end MAC-Addresses (Mac-Addresses values: Ex: "01-0C-CD-01-01-FF") + * @param scd input SCD object. The object will be modified with the new GSE and SMV elements + * @param cbCom communication settings to configure Control Block Communication * @return list of encountered errors * @see Utils#macAddressToLong(String) for the expected MAC address format - * @see ControlBlockNetworkSettings - * @see ControlBlockNetworkSettings.RangesPerCbType - * @see ControlBlockNetworkSettings.NetworkRanges */ - List configureNetworkForAllControlBlocks(SCL scd, ControlBlockNetworkSettings controlBlockNetworkSettings, - ControlBlockNetworkSettings.RangesPerCbType rangesPerCbType); + List configureNetworkForAllControlBlocks(SCL scd, CBCom cbCom); } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettings.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettings.java deleted file mode 100644 index 65fcb11f3..000000000 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettings.java +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-FileCopyrightText: 2023 RTE FRANCE -// -// SPDX-License-Identifier: Apache-2.0 - -package org.lfenergy.compas.sct.commons.dto; - -import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec; -import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter; - -/** - * This interface has a single method which provides network settings for a ControlBlock. - * These are used to create: - * - the Communication/SubNetwork/ConnectedAP/GSE element, which is the network configuration of a GSEControl block - * - the Communication/SubNetwork/ConnectedAP/SMV element, which is the network configuration of a SampledValueControl block - * It is a FunctionalInterface, so it can be implemented with a lambda expression. - * - * @see org.lfenergy.compas.sct.commons.util.ControlBlockNetworkSettingsCsvHelper - */ -@FunctionalInterface -public interface ControlBlockNetworkSettings { - - /** - * This method provides a vlanId, vlanPriority, minTime, maxTime for this ControlBlock. - * vlanPriority will be ignored when vlanId is null. - * - * @param controlBlockAdapter ControlBlock for which we want to configure the communication section - * @return network settings to use for configuring Communication section for this ControlBlock. - * An error message can be provided (i.e. errorMessage not null) or a null settings, in order to avoid configuring the ControlBlock. - */ - SettingsOrError getNetworkSettings(ControlBlockAdapter controlBlockAdapter); - - /** - * Network settings for ControlBlock communication - * - * @param vlanId id of the vlan - * @param vlanPriority priority for the vlan - * @param minTime minTime for GSE communication element - * @param maxTime maxTime for GSE communication element - */ - record Settings(Integer vlanId, Byte vlanPriority, TDurationInMilliSec minTime, TDurationInMilliSec maxTime) { - } - - /** - * Network settings for ControlBlock communication or Error message - * - * @param settings Network settings for ControlBlock communication. Can be null when errorMessage is provided - * @param errorMessage should be null if settings is provided - */ - record SettingsOrError(Settings settings, String errorMessage) { - } - - /** - * NetworkRanges for GSEControl and SampledValueControl - * - * @param gse NetworkRanges for GSEControl - * @param sampledValue NetworkRanges for SampledValueControl - */ - record RangesPerCbType(NetworkRanges gse, NetworkRanges sampledValue) { - } - - /** - * Range of APPID and range of MAC-Address - * - * @param appIdStart range start for APPID (inclusive) - * @param appIdEnd range end for APPID (inclusive) - * @param macAddressStart range start for MAC-Addresses (inclusive). Ex: "01-0C-CD-01-00-00" - * @param macAddressEnd range end for MAC-Addresses (inclusive). Ex: "01-0C-CD-01-01-FF" - */ - record NetworkRanges(long appIdStart, long appIdEnd, String macAddressStart, String macAddressEnd) { - } -} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ControlService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ControlService.java new file mode 100644 index 000000000..c1904d913 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ControlService.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 ControlService { + + 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/scl/ied/ControlBlockAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapter.java index 6902f2f5e..f6c539def 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapter.java @@ -7,21 +7,10 @@ import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.ControlBlockTarget; -import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; -import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; -import org.lfenergy.compas.sct.commons.scl.com.ConnectedAPAdapter; -import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter; import org.lfenergy.compas.sct.commons.scl.ln.AbstractLNAdapter; import org.lfenergy.compas.sct.commons.util.ControlBlockEnum; -import org.lfenergy.compas.sct.commons.util.Utils; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newDurationInMilliSec; -import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newP; import static org.lfenergy.compas.sct.commons.util.Utils.xpathAttributeFilter; /** @@ -52,12 +41,6 @@ public class ControlBlockAdapter extends SclElementAdapter, TControl> { private static final long RPT_ENABLED_MAX_DEFAULT = 1L; - private static final String APPID_P_TYPE = "APPID"; - private static final String MAC_ADDRESS_P_TYPE = "MAC-Address"; - private static final String VLAN_ID_P_TYPE = "VLAN-ID"; - private static final String VLAN_PRIORITY_P_TYPE = "VLAN-PRIORITY"; - private static final int APPID_LENGTH = 4; - private static final int VLAN_ID_LENGTH = 3; public ControlBlockAdapter(AbstractLNAdapter parentAdapter, TControl tControl) { super(parentAdapter, tControl); @@ -128,67 +111,4 @@ public void addTargetIfNotExists(AbstractLNAdapter targetLn) { } } - /** - * Configure the Communication section for this ControlBlock - * - Communication/SubNetwork/ConnectedAP/GSE for GSEControl block - * - Communication/SubNetwork/ConnectedAP/SMV for SampledValueControl block - * @param appId value for P type APPID - * @param macAddress value for P type MAC-Address - * @param vlanId value for P type VLAN-ID - * @param vlanPriority value for P type VLAN-PRIORITY - * @param minTime MinTime Element - * @param maxTime MaxTime Element - * @return An empty Optional if network have been configured, else a SclReportItem. - */ - public Optional configureNetwork(long appId, String macAddress, Integer vlanId, Byte vlanPriority, TDurationInMilliSec minTime, - TDurationInMilliSec maxTime) { - String accessPointName = getParentLDeviceAdapter().getAccessPoint().getName(); - - Optional optConApAdapter = getSclRootAdapter().findConnectedApAdapter(getParentIedAdapter().getName(), accessPointName); - if (optConApAdapter.isEmpty()) { - return Optional.of(buildFatalReportItem("Cannot configure network for ControlBlock because no ConnectAP found for parent AccessPoint")); - } - ConnectedAPAdapter connectedAPAdapter = optConApAdapter.get(); - List listOfPs = new ArrayList<>(); - listOfPs.add(newP(APPID_P_TYPE, Utils.toHex(appId, APPID_LENGTH))); - listOfPs.add(newP(MAC_ADDRESS_P_TYPE, macAddress)); - if (vlanId != null) { - listOfPs.add(newP(VLAN_ID_P_TYPE, Utils.toHex(vlanId, VLAN_ID_LENGTH))); - if (vlanPriority != null) { - listOfPs.add(newP(VLAN_PRIORITY_P_TYPE, String.valueOf(vlanPriority))); - } - } - switch (getControlBlockEnum()) { - case GSE -> connectedAPAdapter.updateGseOrCreateIfNotExists(getParentLDeviceAdapter().getInst(), currentElem.getName(), listOfPs, newDurationInMilliSec(minTime), newDurationInMilliSec(maxTime)); - case SAMPLED_VALUE -> connectedAPAdapter.updateSmvOrCreateIfNotExists(getParentLDeviceAdapter().getInst(), currentElem.getName(), listOfPs); - default -> { - return Optional.of(buildFatalReportItem("configureNetwork not yet implemented for %s ControlBlocks".formatted(getControlBlockEnum()))); - } - } - return Optional.empty(); - } - - /** - * Get parent LDevice - * @return ControlBlock's parent lDeviceAdapter - */ - private LDeviceAdapter getParentLDeviceAdapter() { - return getParentAdapter().getParentAdapter(); - } - - /** - * Get parent IED - * @return ControlBlock's parent IEDAdapter - */ - public IEDAdapter getParentIedAdapter() { - return getParentAdapter().getParentIed(); - } - - /** - * Get SCL Root - * @return sclRootAdapter - */ - private SclRootAdapter getSclRootAdapter() { - return getParentIedAdapter().getParentAdapter(); - } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnum.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnum.java index 23cb0bbcb..025b14992 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,23 +5,23 @@ 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; import java.util.Arrays; 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); @@ -40,4 +40,12 @@ public static ControlBlockEnum from(Class tControlClass) { .orElseThrow(() -> new IllegalArgumentException("Unsupported TControl class : " + tControlClass.getSimpleName())); } + public static ControlBlockEnum from(TCBType tcbType) { + return switch (tcbType){ + case GOOSE -> GSE; + case SV -> SAMPLED_VALUE; + default -> throw new IllegalArgumentException("Unsupported TCBType: " + tcbType); + }; + } + } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockNetworkSettingsCsvHelper.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockNetworkSettingsCsvHelper.java deleted file mode 100644 index dc4b2ff6a..000000000 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ControlBlockNetworkSettingsCsvHelper.java +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-FileCopyrightText: 2023 RTE FRANCE -// -// SPDX-License-Identifier: Apache-2.0 - -package org.lfenergy.compas.sct.commons.util; - -import com.opencsv.bean.CsvBindByPosition; -import lombok.ToString; -import org.apache.commons.lang3.StringUtils; -import org.lfenergy.compas.scl2007b4.model.*; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings; -import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter; -import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter; - -import java.io.Reader; -import java.math.BigInteger; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * This class is an implementation example for interface ControlBlockNetworkSettings. - * It relies on a CSV file. - * The first columns of the CSV file are the criteria to match the ControlBlock (controlBlockEnum, systemVersionWithoutV, iedType, iedRedundancy, - * isBayInternal), - * The last columns are the network settings for the matched ControlBlock (as described in {@link ControlBlockNetworkSettings.Settings}). - * - * @see CsvUtils - */ -public class ControlBlockNetworkSettingsCsvHelper implements ControlBlockNetworkSettings { - - private static final int MAX_VLAN_ID = 0x0FFF; - private static final int MAX_VLAN_PRIORITY = 7; - private static final String NONE = "none"; - - private final Map allSettings; - - /** - * Constructor - * Provide the CSV file as a Reader. For example, you can create a reader like this : - * new InputStreamReader(getClass().getClassLoader().getResourceAsStream(fileName), StandardCharsets.UTF_8); - * - * @param csvSource a reader that provides the data as CSV. For example : - */ - public ControlBlockNetworkSettingsCsvHelper(Reader csvSource) { - allSettings = readCsvFile(csvSource); - } - - private Map readCsvFile(Reader csvSource) { - return CsvUtils.parseRows(csvSource, Row.class).stream() - .distinct() - .collect(Collectors.toMap( - ControlBlockNetworkSettingsCsvHelper::rowToCriteria, - ControlBlockNetworkSettingsCsvHelper::rowToSetting - )); - } - - @Override - public SettingsOrError getNetworkSettings(ControlBlockAdapter controlBlockAdapter) { - ControlBlockEnum controlBlockEnum = controlBlockAdapter.getControlBlockEnum(); - IEDAdapter iedAdapter = controlBlockAdapter.getParentIedAdapter(); - Optional compasSystemVersion = iedAdapter.getCompasSystemVersion(); - if (compasSystemVersion.isEmpty()) { - return new SettingsOrError(null, "No private COMPAS-SystemVersion found in this IED"); - } - String systemVersionWithoutV = removeVFromSystemVersion(compasSystemVersion.get()); - Optional compasICDHeader = iedAdapter.getCompasICDHeader(); - if (compasICDHeader.isEmpty()) { - return new SettingsOrError(null, "No private COMPAS-ICDHeader found in this IED"); - } - TCompasIEDType iedType = compasICDHeader.get().getIEDType(); - TCompasIEDRedundancy iedRedundancy = compasICDHeader.get().getIEDredundancy(); - BigInteger iedSystemVersionInstance = compasICDHeader.get().getIEDSystemVersioninstance(); - boolean isBayInternal = controlBlockAdapter.getName().endsWith("I"); - - Criteria criteria = new Criteria(controlBlockEnum, systemVersionWithoutV, iedType, iedRedundancy, iedSystemVersionInstance, isBayInternal); - Settings settings = findSettings(criteria); - return settings != null ? - new SettingsOrError(settings, null) : - new SettingsOrError(null, "No row found with these criteria " + criteria); - } - - private Settings findSettings(Criteria criteria) { - Objects.requireNonNull(criteria); - if (criteria.systemVersionWithoutV() == null - || criteria.iedType() == null - || criteria.iedRedundancy() == null - || criteria.iedSystemVersionInstance == null) { - return null; - } - return allSettings.get(criteria); - } - - private static String removeVFromSystemVersion(TCompasSystemVersion compasSystemVersion) { - if (StringUtils.isBlank(compasSystemVersion.getMainSystemVersion()) - || (StringUtils.isBlank(compasSystemVersion.getMinorSystemVersion()))) { - return null; - } - String[] minorVersionParts = compasSystemVersion.getMinorSystemVersion().split("\\."); - return (minorVersionParts.length == 3) ? - compasSystemVersion.getMainSystemVersion() + "." + minorVersionParts[0] + "." + minorVersionParts[1] - : null; - } - - private static Criteria rowToCriteria(Row row) { - if (StringUtils.isBlank(row.cbType) - || StringUtils.isBlank(row.xy) - || StringUtils.isBlank(row.zw) - || StringUtils.isBlank(row.iedType) - || StringUtils.isBlank(row.iedRedundancy) - || StringUtils.isBlank(row.iedSystemVersionInstance) - || StringUtils.isBlank(row.bindingType) - ) { - throw new IllegalArgumentException("At least one criteria is null in row " + row); - } - ControlBlockEnum controlBlockEnum = switch (row.cbType) { - case "GOOSE" -> ControlBlockEnum.GSE; - case "SV" -> ControlBlockEnum.SAMPLED_VALUE; - default -> throw new IllegalArgumentException("Unsupported Control Block Type : " + row.cbType); - }; - return new Criteria( - controlBlockEnum, - row.xy + "." + row.zw, - TCompasIEDType.fromValue(row.iedType), - TCompasIEDRedundancy.fromValue(row.iedRedundancy), - new BigInteger(row.iedSystemVersionInstance), - row.bindingType.equals("BAY_INTERNAL") - ); - } - - private static Settings rowToSetting(Row row) { - Integer vlanId = toVLanId(row.vlanId); - Byte vlanPriority = toVlanPriority(row.vlanPriority); - TDurationInMilliSec minTime = toDurationInMilliSec(row.minTime); - TDurationInMilliSec maxTime = toDurationInMilliSec(row.maxTime); - return new Settings(vlanId, vlanPriority, minTime, maxTime); - } - - private static Byte toVlanPriority(String strVlanPriority) { - if (StringUtils.isBlank(strVlanPriority) || NONE.equalsIgnoreCase(strVlanPriority)) { - return null; - } - byte vlanPriority = Byte.parseByte(strVlanPriority); - if (vlanPriority < 0 || vlanPriority > MAX_VLAN_PRIORITY) { - throw new IllegalArgumentException("VLAN PRIORITY must be between 0 and %d, but got : %d".formatted(MAX_VLAN_PRIORITY, vlanPriority)); - } - return vlanPriority; - } - - private static Integer toVLanId(String strVlanId) { - if (StringUtils.isBlank(strVlanId) || NONE.equalsIgnoreCase(strVlanId)) { - return null; - } - int vlanId = Integer.parseInt(strVlanId); - if (vlanId < 0 || vlanId > MAX_VLAN_ID) { - throw new IllegalArgumentException("VLAN ID must be between 0 and %d, but got : %d".formatted(MAX_VLAN_ID, vlanId)); - } - return vlanId; - } - - private static TDurationInMilliSec toDurationInMilliSec(String duration) { - if (StringUtils.isBlank(duration) || NONE.equalsIgnoreCase(duration)) { - return null; - } - return SclConstructorHelper.newDurationInMilliSec(Long.parseLong(duration)); - } - - private record Criteria( - ControlBlockEnum controlBlockEnum, - String systemVersionWithoutV, - TCompasIEDType iedType, - TCompasIEDRedundancy iedRedundancy, - BigInteger iedSystemVersionInstance, - boolean isBayInternal) { - } - - @ToString - public static class Row { - @CsvBindByPosition(position = 0) - private String cbType; - @CsvBindByPosition(position = 1) - private String xy; - @CsvBindByPosition(position = 2) - private String zw; - @CsvBindByPosition(position = 3) - private String iedType; - @CsvBindByPosition(position = 4) - private String iedRedundancy; - @CsvBindByPosition(position = 5) - private String iedSystemVersionInstance; - @CsvBindByPosition(position = 6) - private String bindingType; - @CsvBindByPosition(position = 7) - private String vlanId; - @CsvBindByPosition(position = 8) - private String vlanPriority; - @CsvBindByPosition(position = 9) - private String minTime; - @CsvBindByPosition(position = 10) - private String maxTime; - } - -} diff --git a/sct-commons/src/main/resources/xsd/GSE_SMV_CB_COM.xsd b/sct-commons/src/main/resources/xsd/GSE_SMV_CB_COM.xsd new file mode 100644 index 000000000..dbc9bbe3c --- /dev/null +++ b/sct-commons/src/main/resources/xsd/GSE_SMV_CB_COM.xsd @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IED type to be used to identity the set of LDevice.inst handled by the IED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sct-commons/src/main/resources/xsd/LDEPF_Config_file_v2.xsd b/sct-commons/src/main/resources/xsd/LDEPF.xsd similarity index 100% rename from sct-commons/src/main/resources/xsd/LDEPF_Config_file_v2.xsd rename to sct-commons/src/main/resources/xsd/LDEPF.xsd 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 52% 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 9045762fa..3a720f6de 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,16 +7,16 @@ 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; import org.lfenergy.compas.scl2007b4.model.*; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings; import org.lfenergy.compas.sct.commons.dto.ControlBlockTarget; import org.lfenergy.compas.sct.commons.dto.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.ControlService; 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,45 +29,33 @@ 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.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.*; import static org.lfenergy.compas.scl2007b4.model.TFCEnum.ST; -import static org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.*; import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*; import static org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller.assertIsMarshallable; import static org.lfenergy.compas.sct.commons.util.ControlBlockEnum.*; -import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newDurationInMilliSec; -@ExtendWith(MockitoExtension.class) -class ControlBlockServiceTest { +class ControlBlockEditorServiceTest { - @InjectMocks - ControlBlockService controlBlockService; + ControlBlockEditorService controlBlockEditorService; private Set allowedFcdas; - private static final long GSE_APP_ID_MIN = 0x9; - private static final long SMV_APP_ID_MIN = 0x400A; - private static final String GSE_MAC_ADDRESS_PREFIX = "01-02-03-04-"; - private static final String SMV_MAC_ADDRESS_PREFIX = "0A-0B-0C-0D-"; - private static final NetworkRanges GSE_NETWORK_RANGES = new NetworkRanges(GSE_APP_ID_MIN, GSE_APP_ID_MIN + 10, GSE_MAC_ADDRESS_PREFIX + "00-FF", GSE_MAC_ADDRESS_PREFIX + "01-AA"); - private static final NetworkRanges SMV_NETWORK_RANGES = new NetworkRanges(SMV_APP_ID_MIN, SMV_APP_ID_MIN + 10, SMV_MAC_ADDRESS_PREFIX + "00-FF", SMV_MAC_ADDRESS_PREFIX + "01-AA"); - private static final RangesPerCbType RANGES_PER_CB_TYPE = new RangesPerCbType(GSE_NETWORK_RANGES, SMV_NETWORK_RANGES); - - @BeforeEach void init() { + controlBlockEditorService = new ControlBlockEditorService(new ControlService()); allowedFcdas = new HashSet<>(CsvUtils.parseRows("FcdaCandidates.csv", StandardCharsets.UTF_8, FcdaForDataSetsCreation.class)); } @@ -82,7 +70,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(); @@ -100,7 +88,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) @@ -118,13 +106,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 @@ -157,7 +144,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"); } @@ -167,7 +154,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); @@ -198,7 +185,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(); @@ -231,7 +218,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(); @@ -261,7 +248,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"); } @@ -271,7 +258,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); @@ -287,7 +274,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(); @@ -298,7 +285,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"); } @@ -309,7 +296,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"); } @@ -319,7 +306,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(); } @@ -329,7 +316,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"); } @@ -339,7 +326,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'"); } @@ -349,19 +336,18 @@ 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"); } - @Test void updateAllSourceDataSetsAndControlBlocks_should_sort_FCDA_inside_DataSet_and_avoid_duplicates() { // 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"); @@ -376,45 +362,73 @@ void updateAllSourceDataSetsAndControlBlocks_should_sort_FCDA_inside_DataSet_and } @Test - void configureNetworkForAllControlBlocks_should_create_GSE_and_SMV_elements() { + void configureNetworkForAllControlBlocks_should_create_GSE_elements() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); + CBCom cbCom = createCbCom(); + // When + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); + // Then + assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue(); + TGSE gse1 = getCommunicationGSE(scd, "IED_NAME2", "CB_LD_INST21_GSI"); + assertThat(gse1.getLdInst()).isEqualTo("LD_INST21"); + assertThat(SclDuration.from(gse1.getMinTime())).isEqualTo(new SclDuration("10", "s", "m")); + assertThat(SclDuration.from(gse1.getMaxTime())).isEqualTo(new SclDuration("2000", "s", "m")); + assertThat(gse1.getAddress().getP()).extracting(TP::getType, TP::getValue).containsExactlyInAnyOrder( + Tuple.tuple("VLAN-PRIORITY", "1"), + Tuple.tuple("APPID", "0000"), + Tuple.tuple("MAC-Address", "01-0C-CD-01-00-00"), + Tuple.tuple("VLAN-ID", "12D") + ); + TGSE gse2 = getCommunicationGSE(scd, "IED_NAME2", "CB_LD_INST21_GMI"); + assertThat(gse2.getLdInst()).isEqualTo("LD_INST21"); + assertThat(SclDuration.from(gse2.getMinTime())).isEqualTo(new SclDuration("10", "s", "m")); + assertThat(SclDuration.from(gse2.getMaxTime())).isEqualTo(new SclDuration("2000", "s", "m")); + assertThat(gse2.getAddress().getP()).extracting(TP::getType, TP::getValue).containsExactlyInAnyOrder( + Tuple.tuple("VLAN-PRIORITY", "1"), + Tuple.tuple("APPID", "0001"), + Tuple.tuple("MAC-Address", "01-0C-CD-01-00-01"), + Tuple.tuple("VLAN-ID", "12D") + ); + TGSE gse3 = getCommunicationGSE(scd, "IED_NAME3", "CB_LD_INST31_GSE"); + assertThat(gse3.getLdInst()).isEqualTo("LD_INST31"); + assertThat(SclDuration.from(gse3.getMinTime())).isEqualTo(new SclDuration("10", "s", "m")); + assertThat(SclDuration.from(gse3.getMaxTime())).isEqualTo(new SclDuration("2000", "s", "m")); + assertThat(gse3.getAddress().getP()).extracting(TP::getType, TP::getValue).containsExactlyInAnyOrder( + Tuple.tuple("VLAN-PRIORITY", "2"), + Tuple.tuple("APPID", "0002"), + Tuple.tuple("MAC-Address", "01-0C-CD-01-00-02"), + Tuple.tuple("VLAN-ID", "12E") + ); + MarshallerWrapper.assertValidateXmlSchema(scd); + } - TDurationInMilliSec minTime = newDurationInMilliSec(10); - TDurationInMilliSec maxTime = newDurationInMilliSec(2000); - ControlBlockNetworkSettings controlBlockNetworkSettings = controlBlockAdapter -> new SettingsOrError(new Settings(0x1D6, (byte) 4, minTime, maxTime), null); - + @Test + void configureNetworkForAllControlBlocks_should_create_SMV_elements() { + // Given + 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, controlBlockNetworkSettings, RANGES_PER_CB_TYPE); + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); // Then assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue(); - TConnectedAP connectedAP = new SclRootAdapter(scd).findConnectedApAdapter("IED_NAME2", "AP_NAME").get().getCurrentElem(); - TGSE gse = connectedAP.getGSE().stream() - .filter(tgse -> "CB_LD_INST21_GSI".equals(tgse.getCbName())) - .findFirst().get(); - assertThat(gse.getLdInst()).isEqualTo("LD_INST21"); - assertThat(gse.getMinTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue) - .containsExactly("s", "m", new BigDecimal("10")); - assertThat(gse.getMaxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue) - .containsExactly("s", "m", new BigDecimal("2000")); - assertThat(gse.getAddress().getP()).extracting(TP::getType, TP::getValue) - .containsExactlyInAnyOrder( - Tuple.tuple("VLAN-PRIORITY", "4"), - Tuple.tuple("APPID", "0009"), - Tuple.tuple("MAC-Address", "01-02-03-04-00-FF"), - Tuple.tuple("VLAN-ID", "1D6") - ); - TSMV smv = connectedAP.getSMV().stream() - .filter(tsmv -> "CB_LD_INST21_SVI".equals(tsmv.getCbName())) - .findFirst().get(); - assertThat(smv.getLdInst()).isEqualTo("LD_INST21"); - assertThat(smv.getAddress().getP()).extracting(TP::getType, TP::getValue) - .containsExactlyInAnyOrder( - Tuple.tuple("VLAN-PRIORITY", "4"), - Tuple.tuple("APPID", "400A"), - Tuple.tuple("MAC-Address", "0A-0B-0C-0D-00-FF"), - Tuple.tuple("VLAN-ID", "1D6") - ); + TSMV smv1 = getCommunicationSMV(scd, "IED_NAME2", "CB_LD_INST21_SVI"); + assertThat(smv1.getLdInst()).isEqualTo("LD_INST21"); + assertThat(smv1.getAddress().getP()).extracting(TP::getType, TP::getValue).containsExactlyInAnyOrder( + Tuple.tuple("VLAN-PRIORITY", "3"), + Tuple.tuple("APPID", "4000"), + Tuple.tuple("MAC-Address", "01-0C-CD-04-00-00"), + Tuple.tuple("VLAN-ID", "12F") + ); + assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue(); + TSMV smv2 = getCommunicationSMV(scd, "IED_NAME3", "CB_LD_INST31_SVE"); + assertThat(smv2.getLdInst()).isEqualTo("LD_INST31"); + assertThat(smv2.getAddress().getP()).extracting(TP::getType, TP::getValue).containsExactlyInAnyOrder( + Tuple.tuple("VLAN-PRIORITY", "4"), + Tuple.tuple("APPID", "4001"), + Tuple.tuple("MAC-Address", "01-0C-CD-04-00-01"), + Tuple.tuple("VLAN-ID", "130") + ); MarshallerWrapper.assertValidateXmlSchema(scd); } @@ -422,12 +436,13 @@ void configureNetworkForAllControlBlocks_should_create_GSE_and_SMV_elements() { void configureNetworkForAllControlBlocks_should_create_GSE_with_incremental_appid_and_mac_addresses() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); - - TDurationInMilliSec minTime = newDurationInMilliSec(10); - TDurationInMilliSec maxTime = newDurationInMilliSec(2000); - ControlBlockNetworkSettings controlBlockNetworkSettings = controlBlockAdapter -> new SettingsOrError(new Settings(0x1D6, (byte) 4, minTime, maxTime), null); + CBCom cbCom = createCbCom(); + cbCom.getAppIdRanges().getAppIdRange().get(0).setStart("0009"); + cbCom.getAppIdRanges().getAppIdRange().get(0).setEnd("000B"); + 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, controlBlockNetworkSettings, RANGES_PER_CB_TYPE); + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); // Then assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue(); assertThat(streamAllConnectedApGseP(scd, "APPID")) @@ -438,19 +453,160 @@ void configureNetworkForAllControlBlocks_should_create_GSE_with_incremental_appi @ParameterizedTest @MethodSource("provideConfigureNetworkForAllControlBlocksErrors") - void configureNetworkForAllControlBlocks_should_fail_when_no_settings_for_this_controlBlock(ControlBlockNetworkSettings controlBlockNetworkSettings, - RangesPerCbType rangesPerCbType, - String expectedMessage) { + void configureNetworkForAllControlBlocks_should_fail_when_no_settings_for_this_controlBlock(CBCom cbCom, String expectedMessage, String expectedXPath) { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); // When - List sclReportItems = controlBlockService.configureNetworkForAllControlBlocks(scd, controlBlockNetworkSettings, rangesPerCbType); + List sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom); // Then assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isFalse(); assertThat(sclReportItems) - .extracting(SclReportItem::message, SclReportItem::xpath) - .contains(Tuple.tuple(expectedMessage, - "/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint/Server/LDevice[@inst=\"LD_INST21\"]/LN0/GSEControl[@name=\"CB_LD_INST21_GMI\"]")); + .contains(SclReportItem.error(expectedXPath, expectedMessage)); + } + + public static Stream provideConfigureNetworkForAllControlBlocksErrors() { + CBCom cbComWithNoVlan = createCbCom(); + cbComWithNoVlan.getVlans().getVlan().clear(); + CBCom cbComWithMissingVlanId = createCbCom(); + cbComWithMissingVlanId.getVlans().getVlan().get(0).setVlanId(null); + CBCom cbComWithNotEnoughAppId = createCbCom(); + cbComWithNotEnoughAppId.getAppIdRanges().getAppIdRange().get(0).setStart("0000"); + cbComWithNotEnoughAppId.getAppIdRanges().getAppIdRange().get(0).setEnd("00001"); + CBCom cbComWithNotEnoughMacAddress = createCbCom(); + cbComWithNotEnoughMacAddress.getMacRanges().getMacRange().get(0).setStart("01-0C-CD-01-00-00"); + cbComWithNotEnoughMacAddress.getMacRanges().getMacRange().get(0).setEnd("01-0C-CD-01-00-01"); + + 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[@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[@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[@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[@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_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 + 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() { + return Stream.of(Arguments.of( + (Consumer) tVlan -> tVlan.setXY(null), + (Consumer) tVlan -> tVlan.setZW(null), + (Consumer) tVlan -> tVlan.setIEDType(null), + (Consumer) tVlan -> tVlan.setIEDRedundancy(null), + (Consumer) tVlan -> tVlan.setIEDSystemVersionInstance(null), + (Consumer) tVlan -> tVlan.setBayIntOrExt(null) + )); + } + + @ParameterizedTest + @MethodSource("provideMalformedNumbers") + 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 + 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() { + return Stream.of( + Arguments.of((Consumer) tVlan -> tVlan.setIEDSystemVersionInstance("XXX")), + Arguments.of((Consumer) tVlan -> tVlan.setVlanId("XXX")), + Arguments.of((Consumer) tVlan -> tVlan.setVlanPriority("XXX")), + Arguments.of((Consumer) tVlan -> tVlan.setMinTime("XXX")), + Arguments.of((Consumer) tVlan -> tVlan.setMaxTime("XXX")) + ); + } + + @ParameterizedTest + @MethodSource("provideOutOfBoundNumbers") + 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 + 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 + 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 ConnectedAP 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 @@ -458,7 +614,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(); @@ -478,7 +634,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() @@ -493,30 +649,70 @@ void removeControlBlocksAndDatasetAndExtRefSrc_should_remove_controlBlocks_and_D assertIsMarshallable(scl); } - public static Stream provideConfigureNetworkForAllControlBlocksErrors() { - Settings settingsWithNullVlanId = new Settings(null, (byte) 1, newDurationInMilliSec(1), newDurationInMilliSec(2)); - Settings settings = new Settings(1, (byte) 1, newDurationInMilliSec(1), newDurationInMilliSec(2)); - return Stream.of( - Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> new SettingsOrError(null, null), - RANGES_PER_CB_TYPE, - "Cannot configure network for this ControlBlock because no settings was provided"), - Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> new SettingsOrError(null, "Custom error message"), - RANGES_PER_CB_TYPE, - "Cannot configure network for this ControlBlock because: Custom error message"), - Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> new SettingsOrError(settingsWithNullVlanId, null), - RANGES_PER_CB_TYPE, - "Cannot configure network for this ControlBlock because no Vlan Id was provided in the settings"), - Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> new SettingsOrError(settings, null), - new RangesPerCbType( - new NetworkRanges(GSE_APP_ID_MIN, GSE_APP_ID_MIN, GSE_MAC_ADDRESS_PREFIX + "00-FF", GSE_MAC_ADDRESS_PREFIX + "01-AA"), - SMV_NETWORK_RANGES), - "Cannot configure network for this ControlBlock because range of appId is exhausted"), - Arguments.of((ControlBlockNetworkSettings) controlBlockAdapter -> new SettingsOrError(settings, null), - new RangesPerCbType( - new NetworkRanges(GSE_APP_ID_MIN, GSE_APP_ID_MIN + 10, GSE_MAC_ADDRESS_PREFIX + "00-FF", GSE_MAC_ADDRESS_PREFIX + "00-FF"), - SMV_NETWORK_RANGES), - "Cannot configure network for this ControlBlock because range of MAC Address is exhausted") - ); + private static TGSE getCommunicationGSE(SCL scd, String iedName, String cbName) { + return new SclRootAdapter(scd).findConnectedApAdapter(iedName, "AP_NAME").orElseThrow() + .getCurrentElem() + .getGSE().stream() + .filter(tgse -> cbName.equals(tgse.getCbName())) + .findFirst().orElseThrow(); + } + + private static TSMV getCommunicationSMV(SCL scd, String iedName, String cbName) { + return new SclRootAdapter(scd).findConnectedApAdapter(iedName, "AP_NAME").orElseThrow() + .getCurrentElem() + .getSMV().stream() + .filter(tsmv -> cbName.equals(tsmv.getCbName())) + .findFirst().orElseThrow(); + } + + private static CBCom createCbCom() { + CBCom cbCom = new CBCom(); + cbCom.setMacRanges(new MacRanges()); + cbCom.setAppIdRanges(new AppIdRanges()); + cbCom.setVlans(new Vlans()); + cbCom.getMacRanges().getMacRange().add(newRange(TCBType.GOOSE, "01-0C-CD-01-00-00", "01-0C-CD-01-01-FF")); + cbCom.getMacRanges().getMacRange().add(newRange(TCBType.SV, "01-0C-CD-04-00-00", "01-0C-CD-04-FF-FF")); + cbCom.getAppIdRanges().getAppIdRange().add(newRange(TCBType.GOOSE, "0000", "4000")); + cbCom.getAppIdRanges().getAppIdRange().add(newRange(TCBType.SV, "4000", "7FFF")); + cbCom.getVlans().getVlan().addAll(List.of( + newVlan(TCBType.GOOSE, TBayIntOrExt.BAY_INTERNAL, "301", "1"), + newVlan(TCBType.GOOSE, TBayIntOrExt.BAY_EXTERNAL, "302", "2"), + newVlan(TCBType.SV, TBayIntOrExt.BAY_INTERNAL, "303", "3"), + newVlan(TCBType.SV, TBayIntOrExt.BAY_EXTERNAL, "304", "4") + )); + return cbCom; } + private static TRange newRange(TCBType tcbType, String start, String end) { + TRange macRangeGSE = new TRange(); + macRangeGSE.setCBType(tcbType); + macRangeGSE.setStart(start); + macRangeGSE.setEnd(end); + return macRangeGSE; + } + + private static TVlan newVlan(TCBType tcbType, TBayIntOrExt tBayIntOrExt, String vlanId, String vlanPriority) { + TVlan gseVlan = new TVlan(); + gseVlan.setCBType(tcbType); + gseVlan.setXY("01.00"); + gseVlan.setZW("009.001"); + gseVlan.setIEDType(TIEDType.BCU); + gseVlan.setIEDRedundancy(TIEDRedundancy.A); + gseVlan.setIEDSystemVersionInstance("1"); + gseVlan.setBayIntOrExt(tBayIntOrExt); + gseVlan.setVlanId(vlanId); + gseVlan.setVlanPriority(vlanPriority); + gseVlan.setMinTime("10"); + gseVlan.setMaxTime("2000"); + return gseVlan; + } + + /** + * Help comparing TDurationInMilliSec + */ + record SclDuration(String value, String unit, String multiplier) { + static SclDuration from(TDurationInMilliSec tDurationInMilliSec) { + return new SclDuration(tDurationInMilliSec.getValue().toString(), tDurationInMilliSec.getUnit(), tDurationInMilliSec.getMultiplier()); + } + } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettingsTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettingsTest.java deleted file mode 100644 index 89450f8d6..000000000 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/dto/ControlBlockNetworkSettingsTest.java +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-FileCopyrightText: 2023 RTE FRANCE -// -// SPDX-License-Identifier: Apache-2.0 - -package org.lfenergy.compas.sct.commons.dto; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; -import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec; -import org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.SettingsOrError; -import org.lfenergy.compas.sct.commons.util.PrivateUtils; -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.testhelpers.SclTestMarshaller; -import org.lfenergy.compas.sct.commons.util.ControlBlockEnum; -import org.lfenergy.compas.sct.commons.util.ControlBlockNetworkSettingsCsvHelper; -import org.lfenergy.compas.sct.commons.util.CsvUtils; -import org.lfenergy.compas.sct.commons.util.PrivateEnum; - -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringReader; -import java.math.BigDecimal; -import java.util.Objects; -import java.util.function.Consumer; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.Settings; -import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findControlBlock; -import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findIed; - -class ControlBlockNetworkSettingsTest { - - private ControlBlockNetworkSettings controlBlockNetworkSettings; - - @BeforeEach - public void setUp() { - String fileName = "ControlBlockCommunicationTemplates.csv"; - InputStream inputStream = Objects.requireNonNull(CsvUtils.class.getClassLoader().getResourceAsStream(fileName), "Resource not found: " + fileName); - InputStreamReader inputStreamReader = new InputStreamReader(inputStream); - controlBlockNetworkSettings = new ControlBlockNetworkSettingsCsvHelper(inputStreamReader); - } - - @ParameterizedTest - @ValueSource(strings = { - ";01.00;009.001;BCU;A;1;BAY_INTERNAL;300;4;10;2000", - "GOOSE;;009.001;BCU;A;1;BAY_INTERNAL;300;4;10;2000", - "GOOSE;01.00;;BCU;A;1;BAY_INTERNAL;300;4;10;2000", - "GOOSE;01.00;009.001;;A;1;BAY_INTERNAL;300;4;10;2000", - "GOOSE;01.00;009.001;BCU;;1;BAY_INTERNAL;300;4;10;2000", - "GOOSE;01.00;009.001;BCU;A;;BAY_INTERNAL;300;4;10;2000", - "GOOSE;01.00;009.001;BCU;A;1;;300;4;10;2000" - }) - void constructor_when_csv_has_blank_criteria_cells_should_throw_exception(String row) { - //Given - StringReader stringReader = new StringReader(row); - //When & Then - assertThatThrownBy(() -> new ControlBlockNetworkSettingsCsvHelper(stringReader)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("At least one criteria is null in row ControlBlockNetworkSettingsCsvHelper.Row") - .hasMessageContaining("=null,"); - } - - @ParameterizedTest - @ValueSource(strings = { - "GOOSE;01.00;009.001;BCU;A;XXX;BAY_INTERNAL;300;4;10;2000", - "GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;XXX;4;10;2000", - "GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;300;XXX;10;2000", - "GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;300;4;XXX;2000", - "GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;300;4;10;XXX" - }) - void constructor_when_csv_has_malformed_numbers_should_throw_exception(String row) { - //Given - StringReader stringReader = new StringReader(row); - //When & Then - assertThatThrownBy(() -> new ControlBlockNetworkSettingsCsvHelper(stringReader)) - .isInstanceOf(NumberFormatException.class) - .hasMessage("For input string: \"XXX\""); - } - - @ParameterizedTest - @ValueSource(strings = { - "GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;4096;4;10;2000", // VlanId > MAX_VLAN_ID - "GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;-1;4;10;2000", // VlanId < 0 - "GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;300;8;10;2000", // VlanPriority > MAX_VLAN_PRIORITY - "GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;300;-1;10;2000" // VlanPriority < 0 - }) - void constructor_when_csv_has_numbers_our_out_of_bound_should_throw_exception(String row) { - //Given - StringReader stringReader = new StringReader(row); - //When & Then - assertThatThrownBy(() -> new ControlBlockNetworkSettingsCsvHelper(stringReader)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("must be between 0 and "); - } - - @Test - void constructor_when_unsupported_cbType_should_throw_exception() { - //Given - StringReader stringReader = new StringReader("CUSTOM_CB_TYPE;01.00;009.001;BCU;A;1;BAY_INTERNAL;1;4;10;2000"); - //When & Then - assertThatThrownBy(() -> new ControlBlockNetworkSettingsCsvHelper(stringReader)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Unsupported Control Block Type : CUSTOM_CB_TYPE"); - } - - @Test - void getNetworkSettings_should_return_settings_for_bay_internal_controlBlock() { - //Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); - ControlBlockAdapter controlBlockAdapter = findControlBlock(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", ControlBlockEnum.GSE); - - //When - SettingsOrError settingsOrError = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter); - - //Then - assertThat(settingsOrError.errorMessage()).isNull(); - Settings networkSettings = settingsOrError.settings(); - assertThat(networkSettings) - .extracting(Settings::vlanId, Settings::vlanPriority) - .containsExactly(300, (byte) 4); - assertThat(networkSettings.minTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue) - .containsExactly("s", "m", new BigDecimal("10")); - assertThat(networkSettings.maxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue) - .containsExactly("s", "m", new BigDecimal("2000")); - } - - @Test - void getNetworkSettings_should_return_settings_for_bay_external_controlBlock() { - //Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); - ControlBlockAdapter controlBlockAdapter = findControlBlock(scd, "IED_NAME3", "LD_INST31", "CB_LD_INST31_GSE", ControlBlockEnum.GSE); - - //When - SettingsOrError settingsOrError = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter); - - //Then - assertThat(settingsOrError.errorMessage()).isNull(); - Settings networkSettings = settingsOrError.settings(); - assertThat(networkSettings) - .extracting(Settings::vlanId, Settings::vlanPriority) - .containsExactly(301, (byte) 5); - assertThat(networkSettings.minTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue) - .containsExactly("s", "m", new BigDecimal("15")); - assertThat(networkSettings.maxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue) - .containsExactly("s", "m", new BigDecimal("5000")); - } - - @Test - void getNetworkSettings_should_return_vlanId_null_when_column_contains_none() { - //Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); - ControlBlockAdapter controlBlockAdapter = findControlBlock(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_SVI", ControlBlockEnum.SAMPLED_VALUE); - - //When - SettingsOrError settingsOrError = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter); - - //Then - assertThat(settingsOrError.errorMessage()).isNull(); - Settings networkSettings = settingsOrError.settings(); - assertThat(networkSettings.vlanId()).isNull(); - assertThat(networkSettings.vlanPriority()).isNull(); - } - - @Test - void getNetworkSettings_should_return_null_when_row_not_found_in_csv_file() { - //Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - findIed(sclRootAdapter.getCurrentElem(), "IED_NAME2").getCompasSystemVersion().get().setMainSystemVersion("99.99"); - ControlBlockAdapter controlBlockAdapter = findControlBlock(sclRootAdapter.getCurrentElem(), "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", ControlBlockEnum.GSE); - - //When - SettingsOrError settingsOrError = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter); - - //Then - assertThat(settingsOrError.errorMessage()).isEqualTo("No row found with these criteria Criteria[controlBlockEnum=GSE, systemVersionWithoutV=99.99.009.001, iedType=BCU, iedRedundancy=A, iedSystemVersionInstance=1, isBayInternal=true]"); - assertThat(settingsOrError.settings()).isNull(); - } - - @ParameterizedTest - @EnumSource(value = PrivateEnum.class, mode = EnumSource.Mode.INCLUDE, names = {"COMPAS_ICDHEADER", "COMPAS_SYSTEM_VERSION"}) - void getNetworkSettings_should_return_null_when_missing_ied_private(PrivateEnum missingPrivate) { - //Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); - SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); - PrivateUtils.removePrivates(findIed(sclRootAdapter.getCurrentElem(), "IED_NAME2").getCurrentElem(), missingPrivate); - ControlBlockAdapter controlBlockAdapter = findControlBlock(sclRootAdapter.getCurrentElem(), "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", ControlBlockEnum.GSE); - - //When - SettingsOrError settingsOrError = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter); - - //Then - assertThat(settingsOrError.errorMessage()).isEqualTo("No private %s found in this IED".formatted(missingPrivate.getPrivateType())); - assertThat(settingsOrError.settings()).isNull(); - } - - @ParameterizedTest - @MethodSource("provideInvalidCompasAttributes") - void getNetworkSettings_should_return_null_when_missing_ied_private_attributes(Consumer transformIedPrivate) { - //Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml"); - ControlBlockAdapter controlBlockAdapter = findControlBlock(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", ControlBlockEnum.GSE); - IEDAdapter iedAdapter = findIed(scd, "IED_NAME2"); - transformIedPrivate.accept(iedAdapter); - - //When - SettingsOrError settingsOrError = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter); - - //Then - assertThat(settingsOrError.errorMessage()).startsWith("No row found with these criteria "); - assertThat(settingsOrError.settings()).isNull(); - } - - private static Stream provideInvalidCompasAttributes() { - return Stream.of( - Arguments.of((Consumer) iedAdapter -> iedAdapter.getCompasICDHeader().get().setIEDType(null)), - Arguments.of((Consumer) iedAdapter -> iedAdapter.getCompasICDHeader().get().setIEDredundancy(null)), - Arguments.of((Consumer) iedAdapter -> iedAdapter.getCompasICDHeader().get().setIEDSystemVersioninstance(null)), - Arguments.of((Consumer) iedAdapter -> iedAdapter.getCompasSystemVersion().get().setMainSystemVersion(null)), - Arguments.of((Consumer) iedAdapter -> iedAdapter.getCompasSystemVersion().get().setMinorSystemVersion(null)), - Arguments.of((Consumer) iedAdapter -> iedAdapter.getCompasSystemVersion().get().setMinorSystemVersion("1")) // Invalid format for MinorSystemVersion - ); - } - -} diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapterTest.java index 2e419e3d4..2e1d29687 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapterTest.java @@ -4,21 +4,18 @@ package org.lfenergy.compas.sct.commons.scl.ied; -import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import org.lfenergy.compas.scl2007b4.model.*; -import org.lfenergy.compas.sct.commons.dto.SclReportItem; +import org.lfenergy.compas.scl2007b4.model.SCL; +import org.lfenergy.compas.scl2007b4.model.TControl; +import org.lfenergy.compas.scl2007b4.model.TControlWithIEDName; +import org.lfenergy.compas.scl2007b4.model.TGSEControl; import org.lfenergy.compas.sct.commons.scl.ln.LN0Adapter; import org.lfenergy.compas.sct.commons.scl.ln.LNAdapter; -import org.lfenergy.compas.sct.commons.testhelpers.SclHelper; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; import org.lfenergy.compas.sct.commons.util.ControlBlockEnum; -import org.lfenergy.compas.sct.commons.util.SclConstructorHelper; -import java.math.BigDecimal; import java.util.List; -import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findLn; @@ -64,80 +61,4 @@ void addTargetIfNotExists_should_add_target(){ .containsExactly("AP_NAME", "IED_NAME2", "LD_INST21", "1", List.of("ANCR"), "prefix"); } - @Test - @Tag("issue-321") - void configureNetwork_should_add_GSE_element() { - // Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml"); - TConnectedAP connectedAP = SclHelper.addConnectedAp(scd, "SUB_NETWORK_NAME", "AP_NAME", "IED_NAME1"); - LN0Adapter ln0 = findLn0(scd, "IED_NAME1", "LD_INST11"); - // When - ln0.createDataSetIfNotExists("datSet", ControlBlockEnum.GSE); - // When - ControlBlockAdapter controlBlockAdapter = ln0.createControlBlockIfNotExists("cbName", "cbId", "datSet", ControlBlockEnum.GSE); - // When - Optional sclReportItem = controlBlockAdapter.configureNetwork(10L, "00-01-02-04-05", 11, (byte) 12, SclConstructorHelper.newDurationInMilliSec(3), - SclConstructorHelper.newDurationInMilliSec(20)); - // Then - assertThat(sclReportItem).isEmpty(); - assertThat(connectedAP.getGSE()).hasSize(1); - TGSE gse = connectedAP.getGSE().get(0); - assertThat(gse.getLdInst()).isEqualTo("LD_INST11"); - assertThat(gse.getMinTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue) - .containsExactly("s", "m", new BigDecimal("3")); - assertThat(gse.getMaxTime()).extracting(TDurationInMilliSec::getUnit, TDurationInMilliSec::getMultiplier, TDurationInMilliSec::getValue) - .containsExactly("s", "m", new BigDecimal("20")); - assertThat(gse.getAddress().getP()).extracting(TP::getType, TP::getValue) - .containsExactlyInAnyOrder( - Tuple.tuple("APPID", "000A"), - Tuple.tuple("MAC-Address", "00-01-02-04-05"), - Tuple.tuple("VLAN-ID", "00B"), - Tuple.tuple("VLAN-PRIORITY", "12") - ); - } - - @Test - @Tag("issue-321") - void configureNetwork_should_add_SMV_element() { - // Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml"); - TConnectedAP connectedAP = SclHelper.addConnectedAp(scd, "SUB_NETWORK_NAME", "AP_NAME", "IED_NAME1"); - LN0Adapter ln0 = findLn0(scd, "IED_NAME1", "LD_INST11"); - // When - ln0.createDataSetIfNotExists("datSet", ControlBlockEnum.SAMPLED_VALUE); - // When - ControlBlockAdapter controlBlockAdapter = ln0.createControlBlockIfNotExists("cbName", "cbId", "datSet", ControlBlockEnum.SAMPLED_VALUE); - // When - Optional sclReportItem = controlBlockAdapter.configureNetwork(10L, "00-01-02-04-05", 11, (byte) 12, null, null); - // Then - assertThat(sclReportItem).isEmpty(); - assertThat(connectedAP.getSMV()).hasSize(1); - TSMV smv = connectedAP.getSMV().get(0); - assertThat(smv.getLdInst()).isEqualTo("LD_INST11"); - assertThat(smv.getAddress().getP()).extracting(TP::getType, TP::getValue) - .containsExactlyInAnyOrder( - Tuple.tuple("APPID", "000A"), - Tuple.tuple("MAC-Address", "00-01-02-04-05"), - Tuple.tuple("VLAN-ID", "00B"), - Tuple.tuple("VLAN-PRIORITY", "12") - ); - } - - @Test - @Tag("issue-321") - void configureNetwork_when_connectApNotFound_should_return_sclReportItem() { - // Given - SCL scd = SclTestMarshaller.getSCLFromFile("/scl-ln-adapter/scd_with_ln.xml"); - SclHelper.addConnectedAp(scd, "SUB_NETWORK_NAME", "AP_NAME", "IED_NAME2"); // ConnectedAp for IED_NAME2 instead of IED_NAME1 - LN0Adapter ln0 = findLn0(scd, "IED_NAME1", "LD_INST11"); - // When - ln0.createDataSetIfNotExists("datSet", ControlBlockEnum.SAMPLED_VALUE); - // When - ControlBlockAdapter controlBlockAdapter = ln0.createControlBlockIfNotExists("cbName", "cbId", "datSet", ControlBlockEnum.SAMPLED_VALUE); - // When - Optional sclReportItem = controlBlockAdapter.configureNetwork(10L, "00-01-02-04-05", 11, (byte) 12, null, null); - // Then - assertThat(sclReportItem).isPresent(); - } - } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnumTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnumTest.java index 7aa300377..b3444fecb 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnumTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/util/ControlBlockEnumTest.java @@ -7,41 +7,44 @@ 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.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.lfenergy.compas.scl2007b4.model.*; +import org.lfenergy.compas.sct.commons.model.cbcom.TCBType; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThat; +import static org.lfenergy.compas.sct.commons.util.ControlBlockEnum.from; class ControlBlockEnumTest { @ParameterizedTest @MethodSource("provideFromTServiceType") - void from_should_map_ServiceType_to_ControlBlockEnumTest(TServiceType tServiceType, ControlBlockEnum expected) { + void from_TServiceType_should_map_ServiceType_to_ControlBlockEnumTest(TServiceType tServiceType, ControlBlockEnum expected) { //Given : parameter //When - ControlBlockEnum result = ControlBlockEnum.from(tServiceType); + ControlBlockEnum result = from(tServiceType); //Then assertThat(result).isEqualTo(expected); } @Test - void from_POOL_should_throw_exception() { + void from_TServiceType_POOL_should_throw_exception() { //Given TServiceType pollServiceType = TServiceType.POLL; //When & Then - assertThatThrownBy(() -> ControlBlockEnum.from(pollServiceType)) + assertThatThrownBy(() -> from(pollServiceType)) .isInstanceOf(IllegalArgumentException.class); } @ParameterizedTest @MethodSource("provideFromTControlClass") - void from_should_map_Class_to_ControlBlockEnumTest(Class tControlClass, ControlBlockEnum expected) { + void from_TControl_should_map_Class_to_ControlBlockEnumTest(Class tControlClass, ControlBlockEnum expected) { //Given : parameter //When - ControlBlockEnum result = ControlBlockEnum.from(tControlClass); + ControlBlockEnum result = from(tControlClass); //Then assertThat(result).isEqualTo(expected); } @@ -63,4 +66,30 @@ public static Stream provideFromTServiceType() { ); } + @ParameterizedTest + @MethodSource("provideFromTCBType") + public void from_TCBType_when_GOOSE_or_SV_should_succeed(TCBType tcbType, ControlBlockEnum expectedControlBlockEnum){ + // Given : parameter + // When + ControlBlockEnum result = from(tcbType); + // Then + assertThat(result).isEqualTo(expectedControlBlockEnum); + } + + @ParameterizedTest + @EnumSource(value = TCBType.class, mode = EnumSource.Mode.EXCLUDE, names = {"GOOSE", "SV"}) + public void from_TCBType_when_unsupported_TCBType_should_throw_exception(TCBType tcbType){ + // Given : parameter + // When & Then + assertThatThrownBy(() -> from(tcbType)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Unsupported TCBType: " + tcbType.name()); + } + + public static Stream provideFromTCBType() { + return Stream.of( + Arguments.of(TCBType.GOOSE, ControlBlockEnum.GSE), + Arguments.of(TCBType.SV, ControlBlockEnum.SAMPLED_VALUE) + ); + } } diff --git a/sct-commons/src/test/resources/ControlBlockCommunicationTemplates.csv b/sct-commons/src/test/resources/ControlBlockCommunicationTemplates.csv deleted file mode 100644 index 5d149af69..000000000 --- a/sct-commons/src/test/resources/ControlBlockCommunicationTemplates.csv +++ /dev/null @@ -1,9 +0,0 @@ -# SPDX-FileCopyrightText: 2022 RTE FRANCE -# -# SPDX-License-Identifier: Apache-2.0 - -#CB Type;X.Y;Z.W;IedType;IedRedundancy;IedSystemVersionInstance;Bay Internal OR External;VLAN-ID;VLAN-PRIORITY;MINTIME;MAXTIME -GOOSE;01.00;009.001;BCU;A;1;BAY_INTERNAL;300;4;10;2000 -SV;01.00;009.001;BCU;A;1;BAY_INTERNAL;None;None;; -GOOSE;01.00;009.001;BCU;A;1;BAY_EXTERNAL;301;5;15;5000 -SV;01.00;009.001;BCU;A;1;BAY_EXTERNAL;None;None;;