Skip to content

Commit

Permalink
feat(#257): manage (update and/or create) monitoring LNs LSVS and LGO…
Browse files Browse the repository at this point in the history
…S for Control Blocks GOOSE and SMV

Signed-off-by: Aliou DIAITE <[email protected]>
  • Loading branch information
AliouDIAITE committed Mar 23, 2023
1 parent 7c2bdcd commit dab27ae
Show file tree
Hide file tree
Showing 3 changed files with 289 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import java.util.*;

import static org.lfenergy.compas.sct.commons.util.Utils.copySclElement;

/**
* A representation of the model object
* <em><b>{@link org.lfenergy.compas.scl2007b4.model.TLDevice LDevice}</b></em>.
Expand Down Expand Up @@ -50,10 +52,14 @@
@Slf4j
public class LDeviceAdapter extends SclElementAdapter<IEDAdapter, TLDevice> {

private static final String DO_GOCBREF = "GoCBRef";
private static final String DA_SETSRCREF = "setSrcRef";

/**
* Constructor
*
* @param parentAdapter Parent container reference
* @param currentElem Current reference
* @param currentElem Current reference
*/
public LDeviceAdapter(IEDAdapter parentAdapter, TLDevice currentElem) {
super(parentAdapter, currentElem);
Expand Down Expand Up @@ -359,6 +365,69 @@ protected boolean hasControlBlockCreationCapability(ControlBlockEnum controlBloc

private boolean hasCBNameConf(TServiceSettings tServiceSettings) {
return tServiceSettings.isSetCbName()
&& (TServiceSettingsNoDynEnum.CONF.equals(tServiceSettings.getCbName()));
&& (TServiceSettingsNoDynEnum.CONF.equals(tServiceSettings.getCbName()));
}

/**
* Update and/or create Monitoring LNs (LSVS and LGOS) into LDSUIED from ExtRefs binding
*
* @param tExtRefs ExtRefs for which source Control Blocks (Goose or SMV) should be monitored
* @param tSystemLNGroupEnum LNClass value for LN to monitor (Only LGOS and LSVS are taken into this enum)
* @return a list of SclReport Objects that contains errors
*/
public List<SclReportItem> manageMonitoringLns(List<TExtRef> tExtRefs, TSystemLNGroupEnum tSystemLNGroupEnum) {
if (tExtRefs.isEmpty() || (!TSystemLNGroupEnum.LGOS.equals(tSystemLNGroupEnum)
&& !TSystemLNGroupEnum.LSVS.equals(tSystemLNGroupEnum))) {
return Collections.emptyList();
}
List<SclReportItem> sclReportItems = new ArrayList<>();
getLNAdapters().stream().filter(lnAdapter -> tSystemLNGroupEnum.value().equals(lnAdapter.getLNClass())).findFirst()
.ifPresentOrElse(lnAdapter -> {
TLN ln = lnAdapter.getCurrentElem();
int i = 1;
for (TExtRef tExtRef : tExtRefs) {
if (i != 1) this.getCurrentElem().getLN().add(ln);
createMonitoringLnForExtRef(ln, tExtRef, String.valueOf(i)).ifPresent(sclReportItems::add);
i++;
ln = copySclElement(lnAdapter.getCurrentElem(), TLN.class);
}
}, () -> sclReportItems.add(SclReportItem.warning(getXPath(), "There is no LN %s present in LDevice".formatted(tSystemLNGroupEnum.value()))));
return sclReportItems;
}

private Optional<SclReportItem> createMonitoringLnForExtRef(TLN tln, TExtRef tExtRef, String lnInst) {
LNAdapter lnAdapter = new LNAdapter(this, tln);
ResumedDataTemplate filter = createDaiFilter(lnAdapter);
String value = createVal(tExtRef);
if (lnAdapter.getDAI(filter, true).isEmpty()) {
return Optional.of(SclReportItem.warning(lnAdapter.getXPath() + "/DOI@name=\"GoCBRef\"/DAI@name=\"setSrcRef\"/Val",
"The DAI cannot be updated with the value %s".formatted(value)));
}
tln.setInst(lnInst);
filter.setVal(value);
lnAdapter.updateDAI(filter);
return Optional.empty();
}

private String createVal(TExtRef tExtRef) {
String sourceLdName = getParentAdapter().getParentAdapter().getIEDAdapterByName(tExtRef.getIedName())
.getLDeviceAdapterByLdInst(tExtRef.getSrcLDInst()).getLdName();
String lnClass = tExtRef.getSrcLNClass().isEmpty() ? TLLN0Enum.LLN_0.value() : tExtRef.getSrcLNClass().get(0);
return sourceLdName + "/" + lnClass + "." + tExtRef.getSrcCBName();
}

private ResumedDataTemplate createDaiFilter(LNAdapter lnAdapter) {
ResumedDataTemplate filter = new ResumedDataTemplate();
filter.setLnClass(lnAdapter.getLNClass());
filter.setLnInst(lnAdapter.getLNInst());
filter.setPrefix(lnAdapter.getPrefix());
filter.setLnType(lnAdapter.getLnType());

DoTypeName doTypeName = new DoTypeName(DO_GOCBREF);
DaTypeName daTypeName = new DaTypeName(DA_SETSRCREF);

filter.setDoName(doTypeName);
filter.setDaName(daTypeName);
return filter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
package org.lfenergy.compas.sct.commons.util;

import org.apache.commons.lang3.StringUtils;
import org.lfenergy.compas.sct.commons.exception.ScdException;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.util.JAXBSource;
import javax.xml.namespace.QName;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
Expand Down Expand Up @@ -353,4 +359,15 @@ public static long macAddressToLong(String macAddress){
public static String toHex(long number, int length) {
return StringUtils.leftPad(Long.toHexString(number).toUpperCase(), length, "0");
}

public static <T> T copySclElement(T object, Class<T> clazz) {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
JAXBElement<T> contentObject = new JAXBElement<>(new QName(clazz.getSimpleName()), clazz, object);
JAXBSource source = new JAXBSource(jaxbContext, contentObject);
return jaxbContext.createUnmarshaller().unmarshal(source, clazz).getValue();
} catch (JAXBException e) {
throw new ScdException(e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,21 @@
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.dto.DTO;
import org.lfenergy.compas.sct.commons.dto.ExtRefInfo;
import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo;
import org.lfenergy.compas.sct.commons.dto.ResumedDataTemplate;
import org.lfenergy.compas.sct.commons.dto.*;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;

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

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.lfenergy.compas.sct.commons.util.ControlBlockEnum.*;
import static org.lfenergy.compas.sct.commons.util.Utils.lnClassEquals;

class LDeviceAdapterTest {

Expand Down Expand Up @@ -453,7 +452,204 @@ void hasControlBlockCreationCapability_should_throw_exception_when_parameter_is_

// When & Then
Assertions.assertThatThrownBy(() -> lDeviceAdapter.hasControlBlockCreationCapability(null))
.isInstanceOf(NullPointerException.class);
.isInstanceOf(NullPointerException.class);
}

@ParameterizedTest
@EnumSource(value = TSystemLNGroupEnum.class, names = {"LGOS", "LSVS"})
void manageMonitoringLns_should_not_update_ln_lgos_when_no_extRef(TSystemLNGroupEnum lnClassEnum) {
// Given
LDeviceAdapter lDeviceAdapter = createIedsInScl(lnClassEnum).getIEDAdapterByName("IED_NAME_1").getLDeviceAdapterByLdInst("LDSUIED");
// When
List<SclReportItem> sclReportItems = lDeviceAdapter.manageMonitoringLns(new ArrayList<>(), lnClassEnum);
// Then
assertThat(sclReportItems).isEmpty();
assertThat(lDeviceAdapter.getLNAdapters())
.hasSize(1);
assertThat(lDeviceAdapter.getLNAdapters().get(0).getLNInst()).isNull();
}

@ParameterizedTest
@EnumSource(value = TSystemLNGroupEnum.class, names = {"LGOS", "LSVS"}, mode = EnumSource.Mode.EXCLUDE)
void manageMonitoringLns_should_not_update_ln_lgos_when_unknown_lnClass(TSystemLNGroupEnum lnClassEnum) {
// Given
LDeviceAdapter lDeviceAdapter = createIedsInScl(lnClassEnum).getIEDAdapterByName("IED_NAME_1").getLDeviceAdapterByLdInst("LDSUIED");
// When
List<SclReportItem> sclReportItems = lDeviceAdapter.manageMonitoringLns(List.of(new TExtRef()), lnClassEnum);
// Then
assertThat(sclReportItems).isEmpty();
assertThat(lDeviceAdapter.getLNAdapters())
.hasSize(1);
assertThat(lDeviceAdapter.getLNAdapters().get(0).getLNInst()).isNull();
}

@ParameterizedTest
@EnumSource(value = TSystemLNGroupEnum.class, names = {"LGOS", "LSVS"})
void manageMonitoringLns_should_not_create_ln_lgos_when_no_init_lgos(TSystemLNGroupEnum lnClassEnum) {
// Given
LDeviceAdapter lDeviceAdapter = createIedsInScl(lnClassEnum).getIEDAdapterByName("IED_NAME_1").getLDeviceAdapterByLdInst("LDSUIED");
lDeviceAdapter.getCurrentElem().unsetLN();
// When
List<SclReportItem> sclReportItems = lDeviceAdapter.manageMonitoringLns(List.of(new TExtRef()), lnClassEnum);
// Then
assertThat(sclReportItems).isNotEmpty()
.extracting(SclReportItem::getMessage)
.containsExactly("There is no LN " + lnClassEnum.value() + " present in LDevice");
assertThat(lDeviceAdapter.getLNAdapters()).isEmpty();
}

@ParameterizedTest
@EnumSource(value = TSystemLNGroupEnum.class, names = {"LGOS", "LSVS"})
void manageMonitoringLns_should_update_ln_lgos_when_one_extRef_and_dai_updatable(TSystemLNGroupEnum lnClassEnum) {
// Given
LDeviceAdapter lDeviceAdapter = createIedsInScl(lnClassEnum).getIEDAdapterByName("IED_NAME_1").getLDeviceAdapterByLdInst("LDSUIED");
TExtRef tExtRef = createExtRefExample("CB_Name");

// When
List<SclReportItem> sclReportItems = lDeviceAdapter.manageMonitoringLns(List.of(tExtRef), lnClassEnum);
// Then
assertThat(sclReportItems).isEmpty();
assertThat(lDeviceAdapter.getLNAdapters())
.hasSize(1)
.map(LNAdapter::getLNInst)
.isEqualTo(List.of("1"));
assertThat(getDaiValue(lDeviceAdapter, lnClassEnum.value()))
.hasSize(1)
.extracting(TVal::getValue)
.containsExactly("LD_Name/LLN0.CB_Name");
}

@ParameterizedTest
@EnumSource(value = TSystemLNGroupEnum.class, names = {"LGOS", "LSVS"})
void manageMonitoringLns_should_not_update_ln_lgos_when_one_extRef_and_dai_not_updatable(TSystemLNGroupEnum lnClassEnum) {
// Given
SclRootAdapter sclRootAdapter = createIedsInScl(lnClassEnum);
sclRootAdapter.getDataTypeTemplateAdapter().getDOTypeAdapterById("REF").get().getDAAdapterByName("setSrcRef")
.get().getCurrentElem().setValImport(false);
LDeviceAdapter lDeviceAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME_1").getLDeviceAdapterByLdInst("LDSUIED");
TExtRef tExtRef = createExtRefExample("CB_Name");

// When
List<SclReportItem> sclReportItems = lDeviceAdapter.manageMonitoringLns(List.of(tExtRef), lnClassEnum);
// Then
assertThat(sclReportItems).isNotEmpty()
.extracting(SclReportItem::getMessage)
.containsExactly("The DAI cannot be updated with the value LD_Name/LLN0.CB_Name");
assertThat(lDeviceAdapter.getLNAdapters())
.hasSize(1);
assertThat(lDeviceAdapter.getLNAdapters().get(0).getLNInst()).isNull();
}

@ParameterizedTest
@EnumSource(value = TSystemLNGroupEnum.class, names = {"LGOS", "LSVS"})
void manageMonitoringLns_should_update_ln_lgos_when_2_extRef_and_dai_updatable(TSystemLNGroupEnum lnClassEnum) {
// Given
LDeviceAdapter lDeviceAdapter = createIedsInScl(lnClassEnum).getIEDAdapterByName("IED_NAME_1").getLDeviceAdapterByLdInst("LDSUIED");
TExtRef tExtRef1 = createExtRefExample("CB_Name_1");
TExtRef tExtRef2 = createExtRefExample("CB_Name_2");

// When
List<SclReportItem> sclReportItems = lDeviceAdapter.manageMonitoringLns(List.of(tExtRef1, tExtRef2), lnClassEnum);
// Then
assertThat(sclReportItems).isEmpty();
assertThat(lDeviceAdapter.getLNAdapters())
.hasSize(2)
.map(LNAdapter::getLNInst)
.isEqualTo(List.of("1", "2"));
assertThat(getDaiValue(lDeviceAdapter, lnClassEnum.value()))
.hasSize(2)
.extracting(TVal::getValue)
.containsExactly("LD_Name/LLN0.CB_Name_1", "LD_Name/LLN0.CB_Name_2");
}

List<TVal> getDaiValue(LDeviceAdapter lDeviceAdapter, String lnClass) {
return lDeviceAdapter.getLNAdapters().stream()
.filter(lnAdapter -> lnClassEquals(lnAdapter.getCurrentElem().getLnClass(), lnClass))
.map(lnAdapter -> lnAdapter.getDOIAdapterByName("GoCBRef"))
.map(doiAdapter -> (DOIAdapter.DAIAdapter) doiAdapter.getDataAdapterByName("setSrcRef"))
.map(daiAdapter -> daiAdapter.getCurrentElem().getVal())
.flatMap(List::stream)
.toList();
}

TExtRef createExtRefExample(String cbName) {
TExtRef tExtRef = new TExtRef();
tExtRef.setIedName("IED_NAME_2");
tExtRef.setServiceType(TServiceType.GOOSE);
tExtRef.setSrcLDInst("Inst_2");
tExtRef.setSrcLNInst("LN");
tExtRef.setSrcCBName(cbName);
return tExtRef;
}

SclRootAdapter createIedsInScl(TSystemLNGroupEnum lnClassEnum) {
// DataTypeTemplate
TDO tdo = new TDO();
tdo.setName("GoCBRef");
tdo.setType("REF");
TLNodeType tlNodeType = new TLNodeType();
tlNodeType.setId("T1");
tlNodeType.getLnClass().add(lnClassEnum.value());
tlNodeType.getDO().add(tdo);

TDA tda = new TDA();
tda.setName("setSrcRef");
tda.setValImport(true);
tda.setBType(TPredefinedBasicTypeEnum.OBJ_REF);
tda.setFc(TFCEnum.SP);

TDOType tdoType = new TDOType();
tdoType.setId("REF");
tdoType.getSDOOrDA().add(tda);

TDataTypeTemplates tDataTypeTemplates = new TDataTypeTemplates();
tDataTypeTemplates.getLNodeType().add(tlNodeType);
tDataTypeTemplates.getDOType().add(tdoType);


//ied Client
TDOI tdoi = new TDOI();
tdoi.setName("GoCBRef");
TLDevice tlDevice1 = new TLDevice();
tlDevice1.setLN0(new LN0());
tlDevice1.setInst("LDSUIED");
TLN tln1 = new TLN();
tln1.getLnClass().add(lnClassEnum.value());
tln1.setLnType("T1");
tln1.getDOI().add(tdoi);
tlDevice1.getLN().add(tln1);
TServer tServer1 = new TServer();
tServer1.getLDevice().add(tlDevice1);
TAccessPoint tAccessPoint1 = new TAccessPoint();
tAccessPoint1.setName("AP_NAME");
tAccessPoint1.setServer(tServer1);
TIED tied1 = new TIED();
tied1.setName("IED_NAME_1");
tied1.getAccessPoint().add(tAccessPoint1);

//ied Source
TLDevice tlDevice2 = new TLDevice();
tlDevice2.setInst("Inst_2");
tlDevice2.setLdName("LD_Name");
tlDevice2.setLN0(new LN0());
TServer tServer2 = new TServer();
tServer2.getLDevice().add(tlDevice2);
TAccessPoint tAccessPoint2 = new TAccessPoint();
tAccessPoint2.setName("AP_NAME");
tAccessPoint2.setServer(tServer2);
TIED tied2 = new TIED();
tied2.setName("IED_NAME_2");
tied2.getAccessPoint().add(tAccessPoint2);
//SCL file
SCL scd = new SCL();
scd.getIED().add(tied1);
scd.getIED().add(tied2);
THeader tHeader = new THeader();
tHeader.setRevision("1");
scd.setHeader(tHeader);
scd.setDataTypeTemplates(tDataTypeTemplates);

return new SclRootAdapter(scd);
}

}
Expand Down

0 comments on commit dab27ae

Please sign in to comment.