Skip to content

Commit

Permalink
replace Adapter by Services
Browse files Browse the repository at this point in the history
Signed-off-by: massifben <[email protected]>
  • Loading branch information
massifben committed Dec 7, 2023
1 parent 169706d commit 34b236b
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 134 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@
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;
import org.lfenergy.compas.sct.commons.api.SclEditor;
import org.lfenergy.compas.sct.commons.api.SubstationEditor;
import org.lfenergy.compas.sct.commons.dto.HeaderDTO;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.scl.ConnectedApService;
import org.lfenergy.compas.sct.commons.scl.ControlBlockService;
import org.lfenergy.compas.sct.commons.scl.SclElementAdapter;
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter;
Expand All @@ -33,7 +35,7 @@ class SclAutomationServiceIntegrationTest {
private SclAutomationService sclAutomationService ;
private static final SclEditor sclEditor = new SclService() ;
private static final SubstationEditor substationEditor = new SubstationService() ;
private static final ControlBlockEditor controlBlockEditor = new ControlBlockService() ;
private static final ControlBlockEditor controlBlockEditor = new ControlBlockEditorService(new ControlBlockService(), new ConnectedApService()) ;

private HeaderDTO headerDTO;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@

package org.lfenergy.compas.sct.commons;

import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.lfenergy.compas.scl2007b4.model.SCL;
import org.lfenergy.compas.scl2007b4.model.TCompasICDHeader;
import org.lfenergy.compas.scl2007b4.model.TCompasSystemVersion;
import org.lfenergy.compas.scl2007b4.model.TDurationInMilliSec;
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.api.ControlBlockEditor;
import org.lfenergy.compas.sct.commons.dto.FcdaForDataSetsCreation;
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.model.cbcom.*;
import org.lfenergy.compas.sct.commons.scl.ConnectedApService;
import org.lfenergy.compas.sct.commons.scl.ControlBlockService;
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter;
import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter;
import org.lfenergy.compas.sct.commons.scl.ln.LNAdapter;
import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
import org.lfenergy.compas.sct.commons.util.PrivateUtils;
import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
import org.lfenergy.compas.sct.commons.util.Utils;

Expand All @@ -28,11 +28,22 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ControlBlockService implements ControlBlockEditor {
import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newP;

@RequiredArgsConstructor
public class ControlBlockEditorService implements ControlBlockEditor {

private static final int MAX_VLAN_ID = 0x0FFF;
private static final int MAX_VLAN_PRIORITY = 7;
private static final String NONE = "none";
private static final String APPID_P_TYPE = "APPID";
private static final String MAC_ADDRESS_P_TYPE = "MAC-Address";
private static final String VLAN_ID_P_TYPE = "VLAN-ID";
private static final String VLAN_PRIORITY_P_TYPE = "VLAN-PRIORITY";
private static final int APPID_LENGTH = 4;
private static final int VLAN_ID_LENGTH = 3;
private final ControlBlockService controlBlockService;
private final ConnectedApService connectedApService;

@Override
public List<SclReportItem> analyzeDataGroups(SCL scd) {
Expand Down Expand Up @@ -88,14 +99,6 @@ private List<SclReportItem> createDataSetAndControlBlocks(Stream<LDeviceAdapter>
.toList();
}

@Override
public List<SclReportItem> configureNetworkForAllControlBlocks(SCL scd, CBCom cbCom) {
List<SclReportItem> sclReportItems = new ArrayList<>();
sclReportItems.addAll(configureNetworkForControlBlocks(scd, cbCom, TCBType.GOOSE));
sclReportItems.addAll(configureNetworkForControlBlocks(scd, cbCom, TCBType.SV));
return sclReportItems;
}

@Override
public void removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(final SCL scl) {
SclRootAdapter sclRootAdapter = new SclRootAdapter(scl);
Expand All @@ -114,96 +117,137 @@ public void removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(final SCL scl)
.forEach(LNAdapter::removeAllControlBlocksAndDatasets);
}

private List<SclReportItem> configureNetworkForControlBlocks(SCL scd, CBCom cbCom, TCBType tcbType) {
@Override
public List<SclReportItem> configureNetworkForAllControlBlocks(SCL scd, CBCom cbCom) {
return Stream.concat(
configureNetworkForControlBlocks(scd, cbCom, TCBType.GOOSE),
configureNetworkForControlBlocks(scd, cbCom, TCBType.SV))
.toList();
}

private Stream<SclReportItem> configureNetworkForControlBlocks(SCL scl, CBCom cbCom, TCBType tcbType) {
CbComSettings cbComSettings;
try {
cbComSettings = parseCbCom(cbCom, tcbType);
} catch (ScdException ex) {
return Stream.of(SclReportItem.error("Control Block Communication setting files", ex.getMessage()));
}
return scl.getIED().stream()
.flatMap(tied ->
tied.getAccessPoint()
.stream()
.filter(tAccessPoint -> tAccessPoint.isSetServer() && tAccessPoint.getServer().isSetLDevice())
.flatMap(tAccessPoint -> tAccessPoint.getServer().getLDevice().stream()
.map(tlDevice -> new IedApLd(tied, tAccessPoint.getName(), tlDevice))
)
)
.flatMap(iedApLd -> Optional.ofNullable(iedApLd.lDevice().getLN0()).stream()
.flatMap(ln0 -> controlBlockService.getControls(ln0, ControlBlockEnum.from(tcbType).getControlBlockClass()))
.map(tControl -> {
CriteriaOrError criteriaOrError = getCriteria(iedApLd.ied(), tcbType, tControl.getName());
if (criteriaOrError.errorMessage != null) {
return Optional.of(SclReportItem.error(iedApLd.getXPath(), criteriaOrError.errorMessage));
}
Settings settings = cbComSettings.settingsByCriteria.get(criteriaOrError.criteria);
if (settings == null) {
return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because: No controlBlock communication settings found with these " + criteriaOrError.criteria);
}
return configureControlBlockNetwork(scl.getCommunication(), settings, cbComSettings.appIdIterator, cbComSettings.macAddressIterator, tControl, iedApLd);
})
.flatMap(Optional::stream)
);
}

private CbComSettings parseCbCom(CBCom cbCom, TCBType tcbType) {
TRange appIdRange = Optional.ofNullable(cbCom.getAppIdRanges()).map(AppIdRanges::getAppIdRange).stream()
.flatMap(Collection::stream)
.filter(tRange -> tcbType.equals(tRange.getCBType()))
.findFirst()
.orElseThrow(() -> new ScdException("Control Block Communication setting files does not contain AppIdRange for cbType " + tcbType.value()));
PrimitiveIterator.OfLong appIdIterator = Utils.sequence(Long.parseLong(appIdRange.getStart(), 16), Long.parseLong(appIdRange.getEnd(), 16));

TRange macRange = Optional.ofNullable(cbCom.getMacRanges()).map(MacRanges::getMacRange).stream()
.flatMap(Collection::stream)
.filter(tRange -> tcbType.equals(tRange.getCBType()))
.findFirst()
.orElseThrow(() -> new ScdException("Control Block Communication setting files does not contain MacRange for cbType " + tcbType.value()));

PrimitiveIterator.OfLong appIdIterator = Utils.sequence(Long.parseLong(appIdRange.getStart(), 16), Long.parseLong(appIdRange.getEnd(), 16));
Iterator<String> macAddressIterator = Utils.macAddressSequence(macRange.getStart(), macRange.getEnd());

Map<Criteria, Settings> 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));

SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
return sclRootAdapter.streamIEDAdapters()
.flatMap(iedAdapter ->
iedAdapter.streamLDeviceAdapters()
.filter(LDeviceAdapter::hasLN0)
.map(LDeviceAdapter::getLN0Adapter)
.flatMap(ln0Adapter -> ln0Adapter.streamControlBlocks(ControlBlockEnum.from(tcbType)))
.map(controlBlockAdapter -> configureControlBlockNetwork(settingsByCriteria, appIdIterator, macAddressIterator, controlBlockAdapter)))
.flatMap(Optional::stream)
.toList();
return new CbComSettings(appIdIterator, macAddressIterator, settingsByCriteria);
}

private Optional<SclReportItem> configureControlBlockNetwork(Map<Criteria, Settings> comSettingsByCriteria, PrimitiveIterator.OfLong appIdIterator, Iterator<String> macAddressIterator, ControlBlockAdapter controlBlockAdapter) {
SettingsOrError settingsOrError = getNetworkSettings(controlBlockAdapter, comSettingsByCriteria);
if (settingsOrError.errorMessage() != null) {
return Optional.of(controlBlockAdapter.buildFatalReportItem(
"Cannot configure communication for this ControlBlock because: " + settingsOrError.errorMessage()));
}
Settings settings = settingsOrError.settings();
if (settings == null) {
return Optional.of(controlBlockAdapter.buildFatalReportItem(
"Cannot configure communication for this ControlBlock because no settings was provided"));
}
private Optional<SclReportItem> configureControlBlockNetwork(TCommunication tCommunication, Settings settings, PrimitiveIterator.OfLong appIdIterator, Iterator<String> macAddressIterator, TControl tControl, IedApLd iedApLd) {
if (settings.vlanId() == null) {
return Optional.of(controlBlockAdapter.buildFatalReportItem(
"Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings"));
return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings");
}
if (!appIdIterator.hasNext()) {
return Optional.of(controlBlockAdapter.buildFatalReportItem(
"Cannot configure communication for this ControlBlock because range of appId is exhausted"));
return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because range of appId is exhausted");
}
if (!macAddressIterator.hasNext()) {
return Optional.of(controlBlockAdapter.buildFatalReportItem(
"Cannot configure communication for this ControlBlock because range of MAC Address is exhausted"));
return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because range of MAC Address is exhausted");
}

Optional<TConnectedAP> optConApAdapter = connectedApService.findConnectedAp(tCommunication, iedApLd.ied.getName(), iedApLd.apName);
if (optConApAdapter.isEmpty()) {
return newError(iedApLd, tControl, "Cannot configure communication for ControlBlock because no ConnectAP found for AccessPoint");
}
TConnectedAP tConnectedAP = optConApAdapter.get();
List<TP> listOfPs = new ArrayList<>();
listOfPs.add(newP(APPID_P_TYPE, Utils.toHex(appIdIterator.nextLong(), APPID_LENGTH)));
listOfPs.add(newP(MAC_ADDRESS_P_TYPE, macAddressIterator.next()));
listOfPs.add(newP(VLAN_ID_P_TYPE, Utils.toHex(settings.vlanId(), VLAN_ID_LENGTH)));
if (settings.vlanPriority() != null) {
listOfPs.add(newP(VLAN_PRIORITY_P_TYPE, String.valueOf(settings.vlanPriority())));
}

return controlBlockAdapter.configureNetwork(appIdIterator.nextLong(), macAddressIterator.next(), settings.vlanId(), settings.vlanPriority(),
settings.minTime(), settings.maxTime());
switch (tControl) {
case TGSEControl ignored -> new ConnectedApService().updateGseOrCreateIfNotExists(tConnectedAP, iedApLd.lDevice().getInst(), tControl.getName(), listOfPs, SclConstructorHelper.newDurationInMilliSec(settings.minTime), SclConstructorHelper.newDurationInMilliSec(settings.maxTime));
case TSampledValueControl ignored -> new ConnectedApService().updateSmvOrCreateIfNotExists(tConnectedAP, iedApLd.lDevice().getInst(), tControl.getName(), listOfPs);
default -> throw new ScdException("Unsupported Control Block type for communication configuration : " + tControl.getClass().getName());
}
return Optional.empty();
}

private static Optional<SclReportItem> newError(IedApLd iedApLd, TControl tControl, String message) {
return Optional.of(SclReportItem.error(iedApLd.getXPath() + "/LN0/" + controlBlockXPath(tControl),
message));
}

private static String controlBlockXPath(TControl tControl) {
return ControlBlockEnum.from(tControl.getClass()).getElementName() + "[@name=\"" + tControl.getName() + "\"]";
}

public SettingsOrError getNetworkSettings(ControlBlockAdapter controlBlockAdapter, Map<Criteria, Settings> comSettingsByCriteria) {
TCBType cbType = controlBlockAdapter.getControlBlockEnum().toTCBType();
IEDAdapter iedAdapter = controlBlockAdapter.getParentIedAdapter();
Optional<TCompasSystemVersion> compasSystemVersion = iedAdapter.getCompasSystemVersion();
public CriteriaOrError getCriteria(TIED tied, TCBType cbType, String cbName) {
Optional<TCompasSystemVersion> compasSystemVersion = PrivateUtils.extractCompasPrivate(tied, TCompasSystemVersion.class);
if (compasSystemVersion.isEmpty()) {
return new SettingsOrError(null, "No private COMPAS-SystemVersion found in this IED");
return new CriteriaOrError(null, "No private COMPAS-SystemVersion found in this IED");
}
if (StringUtils.isBlank(compasSystemVersion.get().getMainSystemVersion())
|| (StringUtils.isBlank(compasSystemVersion.get().getMinorSystemVersion()))) {
return new SettingsOrError(null, "Missing MainSystemVersion or MinorSystemVersion attribute in COMPAS-SystemVersion private of IED");
return new CriteriaOrError(null, "Missing MainSystemVersion or MinorSystemVersion attribute in COMPAS-SystemVersion private of IED");
}
String systemVersionWithoutV = removeVFromSystemVersion(compasSystemVersion.get());
Optional<TCompasICDHeader> compasICDHeader = iedAdapter.getCompasICDHeader();
Optional<TCompasICDHeader> compasICDHeader = PrivateUtils.extractCompasPrivate(tied, TCompasICDHeader.class);
if (compasICDHeader.isEmpty()) {
return new SettingsOrError(null, "No private COMPAS-ICDHeader found in this IED");
return new CriteriaOrError(null, "No private COMPAS-ICDHeader found in this IED");
}
if (compasICDHeader.get().getIEDSystemVersioninstance() == null) {
return new SettingsOrError(null, "No IEDSystemVersioninstance in the COMPAS-ICDHeader of this IED");
return new CriteriaOrError(null, "No IEDSystemVersioninstance in the COMPAS-ICDHeader of this IED");
}
TBayIntOrExt bayIntOrExt = controlBlockAdapter.getName().endsWith("I") ? TBayIntOrExt.BAY_INTERNAL : TBayIntOrExt.BAY_EXTERNAL;

Criteria criteria = new Criteria(cbType,
systemVersionWithoutV,
TIEDType.fromValue(compasICDHeader.get().getIEDType().value()),
TIEDRedundancy.fromValue(compasICDHeader.get().getIEDredundancy().value()),
compasICDHeader.get().getIEDSystemVersioninstance(),
bayIntOrExt);
Settings settings = comSettingsByCriteria.get(criteria);
return new SettingsOrError(settings, settings != null ? null : "No controlBlock communication settings found with these " + criteria);
TBayIntOrExt bayIntOrExt = cbName.endsWith("I") ? TBayIntOrExt.BAY_INTERNAL : TBayIntOrExt.BAY_EXTERNAL;

return new CriteriaOrError(
new Criteria(cbType,
systemVersionWithoutV,
TIEDType.fromValue(compasICDHeader.get().getIEDType().value()),
TIEDRedundancy.fromValue(compasICDHeader.get().getIEDredundancy().value()),
compasICDHeader.get().getIEDSystemVersioninstance(),
bayIntOrExt), null);
}

private String removeVFromSystemVersion(TCompasSystemVersion compasSystemVersion) {
Expand Down Expand Up @@ -312,16 +356,29 @@ private TDurationInMilliSec toDurationInMilliSec(String strDuration) {
public record Criteria(TCBType cbType, String systemVersionWithoutV, TIEDType iedType, TIEDRedundancy iedRedundancy, BigInteger iedSystemVersionInstance, TBayIntOrExt bayIntOrExt) {
}

/**
* Communication settings for ControlBlock or Error message
*/
public record CriteriaOrError(Criteria criteria, String errorMessage) {
}

/**
* Communication settings for ControlBlock
*/
public record Settings(Integer vlanId, Byte vlanPriority, TDurationInMilliSec minTime, TDurationInMilliSec maxTime) {
}

/**
* Communication settings for ControlBlock or Error message
* All settings of CbCom in a useful format
*/
public record SettingsOrError(Settings settings, String errorMessage) {
record CbComSettings(PrimitiveIterator.OfLong appIdIterator, Iterator<String> macAddressIterator, Map<Criteria, Settings> 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());
}

}
}
Loading

0 comments on commit 34b236b

Please sign in to comment.