Skip to content

Commit

Permalink
feat(#447): RSR-1116 - update LN Mod.stVal based on COMPAS-LNodeStatus
Browse files Browse the repository at this point in the history
  • Loading branch information
massifben committed Dec 18, 2024
1 parent 8a9da1f commit dd3367d
Show file tree
Hide file tree
Showing 11 changed files with 846 additions and 295 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: 2024 RTE FRANCE
//
// SPDX-License-Identifier: Apache-2.0

package org.lfenergy.compas.sct.commons;

import lombok.RequiredArgsConstructor;
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.domain.DataAttribute;
import org.lfenergy.compas.sct.commons.domain.DoLinkedToDa;
import org.lfenergy.compas.sct.commons.domain.DoLinkedToDaFilter;
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
import org.lfenergy.compas.sct.commons.util.CommonConstants;
import org.lfenergy.compas.sct.commons.util.PrivateUtils;
import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

@RequiredArgsConstructor
public class LNodeStatusService {

private static final String LNODE_STATUS_PRIVATE_TYPE = "COMPAS-LNodeStatus";
private final LdeviceService ldeviceService;
private final LnService lnService;
private final DataTypeTemplatesService dataTypeTemplatesService;

public List<SclReportItem> updateLnModStValBasedOnLNodeStatus(SCL scl) {
return scl.getSubstation().stream()
.flatMap(tSubstation -> tSubstation.getVoltageLevel().stream())
.flatMap(tVoltageLevel -> tVoltageLevel.getBay().stream())
.flatMap(tBay -> tBay.getFunction().stream())
.flatMap(tFunction -> tFunction.getLNode().stream())
.map(tlNode -> updateSingleLnModStValBasedOnLNodeStatus(scl, tlNode))
.filter(Objects::nonNull)
.toList();
}

private SclReportItem updateSingleLnModStValBasedOnLNodeStatus(SCL scl, TLNode tlNode) {
String lNodeLNS = PrivateUtils.extractStringPrivate(tlNode, LNODE_STATUS_PRIVATE_TYPE).orElse(null);
if (!"on".equals(lNodeLNS) && !"off".equals(lNodeLNS)) {
return SclReportItem.error(lNodePath(tlNode), "The private %s of the LNode has invalid value. Expecting one of [on, off] but got : %s".formatted(LNODE_STATUS_PRIVATE_TYPE, lNodeLNS));
}
TAnyLN anyLn = findLn(scl, tlNode.getIedName(), tlNode.getLdInst(), tlNode.getLnClass().getFirst(), tlNode.getLnInst(), tlNode.getPrefix()).orElse(null);
if (anyLn == null) {
return SclReportItem.error(lNodePath(tlNode), "LNode in Substation section does not have a matching LN in IED section");
}
String anyLnLNS = PrivateUtils.extractStringPrivate(anyLn, LNODE_STATUS_PRIVATE_TYPE).orElse(null);
if (anyLnLNS == null) {
return SclReportItem.error(lnPath(tlNode), "LN does not have a private %s".formatted(LNODE_STATUS_PRIVATE_TYPE));
}
if (!anyLnLNS.contains(lNodeLNS)) {
return SclReportItem.error(lnPath(tlNode), "Cannot set DAI Mod.stVal to %s, because LN private %s is set to %s".formatted(lNodeLNS, LNODE_STATUS_PRIVATE_TYPE, anyLnLNS));
}
TDAI daiModStVal = lnService.getDaiModStVal(anyLn).orElse(null);
if (daiModStVal == null) {
return null; // do nothing if DAI Mod.stVal is missing
}
List<String> modStValEnumValues = getModStValEnumValues(scl.getDataTypeTemplates(), anyLn.getLnType()).toList();
if (!modStValEnumValues.contains(lNodeLNS)) {
return SclReportItem.error(lnPath(tlNode), "Cannot set DAI Mod.stVal to '%s' because value is not in EnumType %s".formatted(lNodeLNS, modStValEnumValues));
}
daiModStVal.getVal().clear();
daiModStVal.getVal().add(SclConstructorHelper.newVal(lNodeLNS));
return null; // no error
}

private static String lnPath(TLNode tlNode) {
return "IED(%s)/LD(%s)/LN[%s,%s,%s]".formatted(tlNode.getIedName(), tlNode.getLdInst(), tlNode.getLnClass().getFirst(), tlNode.getLnInst(), tlNode.getPrefix());
}

private static String lNodePath(TLNode tlNode) {
return "LNode(iedName=%s, ldInst=%s, lnClass=%s, lnInst=%s, prefix=%s)".formatted(tlNode.getIedName(), tlNode.getLdInst(), tlNode.getLnClass().getFirst(), tlNode.getLnInst(), tlNode.getPrefix());
}

private Stream<String> getModStValEnumValues(TDataTypeTemplates dataTypeTemplates, String lnType) {
return dataTypeTemplatesService.findDoLinkedToDa(dataTypeTemplates, lnType, DoLinkedToDaFilter.from(CommonConstants.MOD_DO_NAME, CommonConstants.STVAL_DA_NAME))
.map(DoLinkedToDa::dataAttribute)
.filter(dataAttribute -> TPredefinedBasicTypeEnum.ENUM.equals(dataAttribute.getBType()))
.map(DataAttribute::getType)
.flatMap(enumId ->
dataTypeTemplates.getEnumType().stream()
.filter(tEnumType -> tEnumType.getId().equals(enumId))
.findFirst())
.stream()
.flatMap(tEnumType -> tEnumType.getEnumVal().stream())
.map(TEnumVal::getValue);
}

private Optional<TAnyLN> findLn(SCL scl, String iedName, String ldInst, String lnClass, String lnInst, String prefix) {
return scl.getIED().stream()
.filter(tied -> iedName.equals(tied.getName()))
.findFirst()
.flatMap(tied -> ldeviceService.findLdevice(tied, tlDevice -> ldInst.equals(tlDevice.getInst())))
.flatMap(tlDevice -> lnService.findAnyLn(tlDevice, tAnyLN -> lnService.matchesLn(tAnyLN, lnClass, lnInst, prefix)));

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ public Optional<TLDevice> findLdevice(TIED tied, Predicate<TLDevice> ldevicePred

public Optional<ActiveStatus> getLdeviceStatus(TLDevice tlDevice) {
LnService lnService = new LnService();
return lnService.getDaiModStval(tlDevice.getLN0());
return lnService.getDaiModStValValue(tlDevice.getLN0());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import org.lfenergy.compas.sct.commons.util.ActiveStatus;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
Expand Down Expand Up @@ -60,33 +59,37 @@ public Optional<TLN> findLn(TLDevice tlDevice, Predicate<TLN> lnPredicate) {
* @return the Lnode Status
*/
public ActiveStatus getLnStatus(TAnyLN tAnyLN, LN0 ln0) {
Optional<ActiveStatus> ln0Status = getDaiModStval(ln0);
return getDaiModStval(tAnyLN).filter(ActiveStatus.OFF::equals).orElseGet(() -> ln0Status.orElse(ActiveStatus.OFF));
Optional<ActiveStatus> ln0Status = getDaiModStValValue(ln0);
return getDaiModStValValue(tAnyLN).filter(ActiveStatus.OFF::equals).orElseGet(() -> ln0Status.orElse(ActiveStatus.OFF));
}

public Optional<ActiveStatus> getDaiModStval(TAnyLN tAnyLN) {
public Optional<ActiveStatus> getDaiModStValValue(TAnyLN tAnyLN) {
return getDaiModStVal(tAnyLN)
.stream()
.flatMap(tdai -> tdai.getVal().stream())
.map(TVal::getValue)
.findFirst()
.map(ActiveStatus::fromValue);
}

public Optional<TDAI> getDaiModStVal(TAnyLN tAnyLN) {
return tAnyLN
.getDOI()
.stream()
.filter(tdoi -> MOD_DO_NAME.equals(tdoi.getName()))
.findFirst()
.flatMap(tdoi -> tdoi.getSDIOrDAI()
.stream()
.filter(dai -> dai.getClass().equals(TDAI.class))
.map(TDAI.class::cast)
.filter(tdai -> STVAL_DA_NAME.equals(tdai.getName()))
.map(TDAI::getVal)
.flatMap(Collection::stream)
.findFirst()
.map(TVal::getValue))
.map(ActiveStatus::fromValue);
.flatMap(tdoi -> tdoi.getSDIOrDAI().stream())
.filter(TDAI.class::isInstance)
.map(TDAI.class::cast)
.filter(tdai -> STVAL_DA_NAME.equals(tdai.getName()))
.findFirst();
}

public Stream<TAnyLN> getActiveLns(TLDevice tlDevice) {
LN0 ln0 = tlDevice.getLN0();
Stream<TLN> tlnStream = tlDevice.getLN()
.stream()
.filter(tln -> ActiveStatus.ON.equals(getLnStatus(tln, ln0)));
Stream<LN0> ln0Stream = Stream.of(ln0).filter(ln02 -> getDaiModStval(ln02).map(ActiveStatus.ON::equals).orElse(false));
Stream<LN0> ln0Stream = Stream.of(ln0).filter(ln02 -> getDaiModStValValue(ln02).map(ActiveStatus.ON::equals).orElse(false));
return Stream.concat(ln0Stream, tlnStream);
}

Expand All @@ -99,7 +102,7 @@ public Optional<TDAI> getDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDaFilter doLi
return tAnyLN.getDOI().stream().filter(doi -> doi.getName().equals(doLinkedToDaFilter.doName()))
.findFirst()
.flatMap(doi -> {
if(structNamesList.size() > 1) {
if (structNamesList.size() > 1) {
String firstSDIName = structNamesList.removeFirst();
return this.getSdiByName(doi, firstSDIName)
.map(intermediateSdi -> findSDIByStructName(intermediateSdi, structNamesList))
Expand All @@ -116,7 +119,7 @@ public Optional<TDAI> getDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDaFilter doLi
return Optional.empty();
})
.stream().findFirst();
} else if(structNamesList.size() == 1){
} else if (structNamesList.size() == 1) {
return doi.getSDIOrDAI().stream()
.filter(unNaming -> unNaming.getClass().equals(TDAI.class))
.map(TDAI.class::cast)
Expand All @@ -129,16 +132,16 @@ public Optional<TDAI> getDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDaFilter doLi

@Override
public void updateOrCreateDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDa doLinkedToDa) {
createDoiSdiDaiChainIfNotExists(tAnyLN, doLinkedToDa.getDataObject(), doLinkedToDa.getDataAttribute())
createDoiSdiDaiChainIfNotExists(tAnyLN, doLinkedToDa.dataObject(), doLinkedToDa.dataAttribute())
.ifPresent(tdai -> {
List<DaVal> daiVals = doLinkedToDa.getDataAttribute().getDaiValues();
if(!hasSettingGroup(tdai) && daiVals.size() == 1 && daiVals.getFirst().settingGroup() == null) {
List<DaVal> daiVals = doLinkedToDa.dataAttribute().getDaiValues();
if (!hasSettingGroup(tdai) && daiVals.size() == 1 && daiVals.getFirst().settingGroup() == null) {
String value = daiVals.getFirst().val();
tdai.getVal().stream().findFirst()
.ifPresentOrElse(tVal -> tVal.setValue(value),
() -> tdai.getVal().add(newVal(value)));
} else {
for (DaVal daVal: daiVals) {
for (DaVal daVal : daiVals) {
tdai.getVal().stream()
.filter(tValElem -> tValElem.isSetSGroup() && tValElem.getSGroup() == daVal.settingGroup())
.findFirst()
Expand All @@ -152,11 +155,11 @@ public void updateOrCreateDOAndDAInstances(TAnyLN tAnyLN, DoLinkedToDa doLinkedT
public void completeFromDAInstance(TIED tied, String ldInst, TAnyLN anyLN, DoLinkedToDa doLinkedToDa) {
getDOAndDAInstances(anyLN, doLinkedToDa.toFilter())
.ifPresent(tdai -> {
if(tdai.isSetVal()) {
doLinkedToDa.getDataAttribute().addDaVal(tdai.getVal());
if (tdai.isSetVal()) {
doLinkedToDa.dataAttribute().addDaVal(tdai.getVal());
}
if(doLinkedToDa.getDataAttribute().getFc() == TFCEnum.SG || doLinkedToDa.getDataAttribute().getFc() == TFCEnum.SE) {
if(hasSettingGroup(tdai)) {
if (doLinkedToDa.dataAttribute().getFc() == TFCEnum.SG || doLinkedToDa.dataAttribute().getFc() == TFCEnum.SE) {
if (hasSettingGroup(tdai)) {
boolean isIedHasConfSG = tied.isSetAccessPoint() &&
tied.getAccessPoint().stream()
.filter(tAccessPoint -> tAccessPoint.getServer() != null
Expand All @@ -166,13 +169,13 @@ public void completeFromDAInstance(TIED tied, String ldInst, TAnyLN anyLN, DoLin
&& tAccessPoint.getServices() != null
&& tAccessPoint.getServices().getSettingGroups() != null
&& tAccessPoint.getServices().getSettingGroups().getConfSG() != null);
doLinkedToDa.getDataAttribute().setValImport((!tdai.isSetValImport() || tdai.isValImport()) && isIedHasConfSG);
doLinkedToDa.dataAttribute().setValImport((!tdai.isSetValImport() || tdai.isValImport()) && isIedHasConfSG);
} else {
log.warn(String.format("Inconsistency in the SCD file - DAI= %s with fc= %s must have a sGroup attribute", tdai.getName(), doLinkedToDa.getDataAttribute().getFc()));
doLinkedToDa.getDataAttribute().setValImport(false);
}
} else if(tdai.isSetValImport()) {
doLinkedToDa.getDataAttribute().setValImport(tdai.isValImport());
log.warn(String.format("Inconsistency in the SCD file - DAI= %s with fc= %s must have a sGroup attribute", tdai.getName(), doLinkedToDa.dataAttribute().getFc()));
doLinkedToDa.dataAttribute().setValImport(false);
}
} else if (tdai.isSetValImport()) {
doLinkedToDa.dataAttribute().setValImport(tdai.isValImport());
}
});
}
Expand All @@ -198,22 +201,22 @@ private Optional<TDAI> createDoiSdiDaiChainIfNotExists(TAnyLN tAnyLN, DataObject

TDOI doi = tAnyLN.getDOI().stream().filter(doi1 -> doi1.getName().equals(dataObject.getDoName()))
.findFirst()
.orElseGet(()-> {
.orElseGet(() -> {
TDOI newDOI = new TDOI();
newDOI.setName(dataObject.getDoName());
tAnyLN.getDOI().add(newDOI);
return newDOI;
});
if(structInstances.size() > 1){
if (structInstances.size() > 1) {
TSDI firstSDI = findOrCreateSDIFromDOI(doi, structInstances.getFirst());
TSDI lastSDI = findOrCreateSDIByStructName(firstSDI, structInstances);
if(structInstances.size() == 1){
if (structInstances.size() == 1) {
return lastSDI.getSDIOrDAI().stream()
.filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class))
.map(TDAI.class::cast)
.filter(tdai -> tdai.getName().equals(structInstances.getFirst()))
.map(tdai -> {
if(tdai.isSetValImport()) {
if (tdai.isSetValImport()) {
tdai.setValImport(dataAttribute.isValImport());
}
return tdai;
Expand All @@ -226,13 +229,13 @@ private Optional<TDAI> createDoiSdiDaiChainIfNotExists(TAnyLN tAnyLN, DataObject
return Optional.of(newDAI);
});
}
} else if(structInstances.size() == 1){
} else if (structInstances.size() == 1) {
return doi.getSDIOrDAI().stream()
.filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class))
.map(TDAI.class::cast)
.filter(tdai -> tdai.getName().equals(structInstances.getFirst()))
.map(tdai -> {
if(tdai.isSetValImport()) tdai.setValImport(dataAttribute.isValImport());
if (tdai.isSetValImport()) tdai.setValImport(dataAttribute.isValImport());
return tdai;
})
.findFirst()
Expand All @@ -247,7 +250,7 @@ private Optional<TDAI> createDoiSdiDaiChainIfNotExists(TAnyLN tAnyLN, DataObject
}

private TSDI findSDIByStructName(TSDI tsdi, List<String> sdiNames) {
if(sdiNames.isEmpty()) return tsdi;
if (sdiNames.isEmpty()) return tsdi;
return this.getSdiByName(tsdi, sdiNames.getFirst())
.map(sdi1 -> {
sdiNames.removeFirst();
Expand Down Expand Up @@ -294,14 +297,13 @@ private Optional<TSDI> getSdiByName(TSDI sdi, String sdiName) {
}

/**
*
* @param sdi TSDI
* @param sdi TSDI
* @param structName list start with sdi name
* @return already existing TSDI or newly created TSDI from given TSDI
*/
private TSDI findOrCreateSDIByStructName(TSDI sdi, List<String> structName) {
structName.removeFirst();
if(structName.isEmpty() || structName.size() == 1) return sdi;
if (structName.isEmpty() || structName.size() == 1) return sdi;
return findOrCreateSDIByStructName(findOrCreateSDIFromSDI(sdi, structName.getFirst()), structName);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import jakarta.xml.bind.JAXBElement;
import lombok.NonNull;
import lombok.experimental.UtilityClass;
import org.apache.commons.lang3.StringUtils;
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.dto.PrivateLinkedToStds;
import org.lfenergy.compas.sct.commons.exception.ScdException;
Expand Down Expand Up @@ -320,4 +321,13 @@ public static void copyCompasICDHeaderFromLNodePrivateIntoSTDPrivate(TPrivate st
}


public static Optional<String> extractStringPrivate(TBaseElement tBaseElement, String privateType) {
return tBaseElement.getPrivate().stream()
.filter(tPrivate -> privateType.equals(tPrivate.getType()))
.flatMap(tPrivate -> tPrivate.getContent().stream())
.filter(String.class::isInstance)
.map(String.class::cast)
.filter(StringUtils::isNotBlank)
.findFirst();
}
}
Loading

0 comments on commit dd3367d

Please sign in to comment.