From a8bf1b8793f76c5e00927f911ba47059a77076d4 Mon Sep 17 00:00:00 2001 From: massifben <105049157+massifben@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:37:13 +0200 Subject: [PATCH 1/3] feat(#414): RSR-1047 - add reportType attribute to HMI controlblock creation Signed-off-by: massifben <105049157+massifben@users.noreply.github.com> --- .../commons/scl/ldevice/LDeviceAdapter.java | 15 +++--- .../xsd/CB_REPORT_SUPERVISION_Config_file.xsd | 3 +- .../compas/sct/commons/HmiServiceTest.java | 51 +++++++++++++++---- 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ldevice/LDeviceAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ldevice/LDeviceAdapter.java index 34c14a62f..b12be812f 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ldevice/LDeviceAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ldevice/LDeviceAdapter.java @@ -66,6 +66,7 @@ public class LDeviceAdapter extends SclElementAdapter { private static final long INTG_PD_VALUE_FOR_FC_MX = 2000L; private static final String DA_SETSRCREF = "setSrcRef"; + private static final String CYC_REPORT_TYPE = "CYC"; /** * Constructor @@ -94,19 +95,19 @@ public void createHmiReportControlBlocks(PO po) { findLnAdapter(tfcdaFilter.getLnClass(), tfcdaFilter.getLnInst(), tfcdaFilter.getPrefix()).filter(lnAdapter -> lnAdapter.getDaiModStValValue().map(ActiveStatus::fromValue).map(ActiveStatus.ON::equals).orElse(true))) .map(sourceLn -> sourceLn.getDAI(new DataAttributeRef(toFCDA(tfcdaFilter)), false)) .filter(das -> das.stream().anyMatch(da -> TFCEnum.fromValue(tfcdaFilter.getFc().value()) == da.getFc())) // getDAI does not filter on DA. - .ifPresent(dataAttributeRefs -> createHmiReportCB(ln0, toFCDA(tfcdaFilter)))); + .ifPresent(dataAttributeRefs -> createHmiReportCB(ln0, tfcdaFilter))); } - private void createHmiReportCB(LN0Adapter ln0, TFCDA fcda) { - boolean isFcMx = fcda.getFc() == TFCEnum.MX; - String dataSetSuffix = getInst().toUpperCase(Locale.ENGLISH) + ATTRIBUTE_VALUE_SEPARATOR + (isFcMx ? "CYPO" : "DQPO"); + private void createHmiReportCB(LN0Adapter ln0, TFCDAFilter tfcdaFilter) { + TFCDA fcda = toFCDA(tfcdaFilter); + String dataSetSuffix = getInst().toUpperCase(Locale.ENGLISH) + ATTRIBUTE_VALUE_SEPARATOR + tfcdaFilter.getReportType().substring(0, 2) + "PO"; String dataSetName = DATASET_NAME_PREFIX + dataSetSuffix; DataSetAdapter dataSet = ln0.createDataSetIfNotExists(dataSetName, ControlBlockEnum.REPORT); - dataSet.createFCDAIfNotExists(fcda.getLdInst(), fcda.getPrefix(), fcda.getLnClass().get(0), fcda.getLnInst(), fcda.getDoName(), fcda.getDaName(), fcda.getFc()); + dataSet.createFCDAIfNotExists(fcda.getLdInst(), fcda.getPrefix(), fcda.getLnClass().getFirst(), fcda.getLnInst(), fcda.getDoName(), fcda.getDaName(), fcda.getFc()); String cbName = CONTROLBLOCK_NAME_PREFIX + dataSetSuffix; String cbId = ln0.generateControlBlockId(getLdName(), cbName); ControlBlockAdapter controlBlockAdapter = ln0.createControlBlockIfNotExists(cbName, cbId, dataSetName, ControlBlockEnum.REPORT); - if (isFcMx) { + if (tfcdaFilter.getReportType().equals(CYC_REPORT_TYPE)) { TReportControl tReportControl = (TReportControl) controlBlockAdapter.getCurrentElem(); tReportControl.setIntgPd(INTG_PD_VALUE_FOR_FC_MX); tReportControl.getTrgOps().setDchg(false); @@ -478,7 +479,7 @@ private void updateNewCreatedLnDaiValue(TLN tln, TExtRef tExtRef, String lnInst, private String createVal(TExtRef tExtRef) { String sourceLdName = getParentAdapter().getParentAdapter().getIEDAdapterByName(tExtRef.getIedName()) .getLDeviceAdapterByLdInst(tExtRef.getSrcLDInst()).getLdName(); - String lnClass = !tExtRef.isSetSrcLNClass() ? TLLN0Enum.LLN_0.value() : tExtRef.getSrcLNClass().get(0); + String lnClass = !tExtRef.isSetSrcLNClass() ? TLLN0Enum.LLN_0.value() : tExtRef.getSrcLNClass().getFirst(); return sourceLdName + "/" + lnClass + "." + tExtRef.getSrcCBName(); } diff --git a/sct-commons/src/main/resources/xsd/CB_REPORT_SUPERVISION_Config_file.xsd b/sct-commons/src/main/resources/xsd/CB_REPORT_SUPERVISION_Config_file.xsd index 8f4264086..307fc0f88 100644 --- a/sct-commons/src/main/resources/xsd/CB_REPORT_SUPERVISION_Config_file.xsd +++ b/sct-commons/src/main/resources/xsd/CB_REPORT_SUPERVISION_Config_file.xsd @@ -60,6 +60,7 @@ SPDX-License-Identifier: Apache-2.0 + @@ -69,4 +70,4 @@ SPDX-License-Identifier: Apache-2.0 - \ No newline at end of file + diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/HmiServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/HmiServiceTest.java index 1a041bcb4..500065cc4 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/HmiServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/HmiServiceTest.java @@ -27,6 +27,8 @@ @ExtendWith(MockitoExtension.class) class HmiServiceTest { + private static final String DQC_REPORT_TYPE = "DQC"; + private static final String CYC_REPORT_TYPE = "CYC"; @InjectMocks HmiService hmiService; @@ -42,7 +44,7 @@ void setUp() { void createAllIhmReportControlBlocks_with_fc_ST_should_create_dataset_and_controlblock() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-hmi-create-report-cb/scd_create_dataset_and_controlblocks_for_hmi.xml"); - TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST); + TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST, DQC_REPORT_TYPE); po.getFCDAs().getFCDA().add(tfcdaFilter); // When hmiService.createAllHmiReportControlBlocks(scd, po); @@ -69,7 +71,7 @@ void createAllIhmReportControlBlocks_with_fc_ST_should_create_dataset_and_contro void createAllIhmReportControlBlocks_with_fc_MX_should_create_dataset_and_controlblock() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-hmi-create-report-cb/scd_create_dataset_and_controlblocks_for_hmi.xml"); - TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "PVOC", "1", null, "DoName2", Tfc.MX); + TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "PVOC", "1", null, "DoName2", Tfc.MX, CYC_REPORT_TYPE); po.getFCDAs().getFCDA().add(tfcdaFilter); // When hmiService.createAllHmiReportControlBlocks(scd, po); @@ -93,11 +95,39 @@ void createAllIhmReportControlBlocks_with_fc_MX_should_create_dataset_and_contro assertThat(reportControl.getRptEnabled().isSetClientLN()).isFalse(); } + @Test + void createAllIhmReportControlBlocks_with_fc_MX_and_DQC_REPORT_TYPE_should_create_dataset_and_controlblock_with_suffix_DQPO() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-hmi-create-report-cb/scd_create_dataset_and_controlblocks_for_hmi.xml"); + TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "PVOC", "1", null, "DoName2", Tfc.MX, DQC_REPORT_TYPE); + po.getFCDAs().getFCDA().add(tfcdaFilter); + // When + hmiService.createAllHmiReportControlBlocks(scd, po); + // Then + // Check DataSet is created + DataSetAdapter dataSet = findDataSet(scd, "IedName1", "LdInst11", "DS_LDINST11_DQPO"); + assertThat(dataSet.getCurrentElem().getFCDA()).hasSize(1).first() + .usingRecursiveComparison().isEqualTo(toFCDA(tfcdaFilter)); + // Check ControlBlock is created + LN0Adapter ln0 = findLn0(scd, "IedName1", "LdInst11"); + assertThat(ln0.getTControlsByType(TReportControl.class)).hasSize(1); + + TReportControl reportControl = findControlBlock(scd, "IedName1", "LdInst11", "CB_LDINST11_DQPO", TReportControl.class); + assertThat(reportControl).extracting(TReportControl::getRptID, TControl::getDatSet, TReportControl::getConfRev, TReportControl::isBuffered, TReportControl::getBufTime, TReportControl::isIndexed, + TControlWithTriggerOpt::getIntgPd) + .containsExactly("IedName1LdInst11/LLN0.CB_LDINST11_DQPO", "DS_LDINST11_DQPO", 1L, true, 0L, true, 60000L); + assertThat(reportControl.getTrgOps()) + .extracting(TTrgOps::isDchg, TTrgOps::isQchg, TTrgOps::isDupd, TTrgOps::isPeriod, TTrgOps::isGi) + .containsExactly(true, true, false, true, true); + assertThat(reportControl.getRptEnabled().getMax()).isEqualTo(1); + assertThat(reportControl.getRptEnabled().isSetClientLN()).isFalse(); + } + @Test void createAllIhmReportControlBlocks_with_FCDA_on_ln0_should_create_dataset_and_controlblock() { // Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-hmi-create-report-cb/scd_create_dataset_and_controlblocks_for_hmi.xml"); - TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "LLN0", null, null, "DoName0", Tfc.ST); + TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "LLN0", null, null, "DoName0", Tfc.ST, DQC_REPORT_TYPE); po.getFCDAs().getFCDA().add(tfcdaFilter); // When hmiService.createAllHmiReportControlBlocks(scd, po); @@ -126,7 +156,7 @@ void createAllIhmReportControlBlocks_when_lDevice_ON_but_LN_Mod_StVal_missing_sh SCL scd = SclTestMarshaller.getSCLFromFile("/scd-hmi-create-report-cb/scd_create_dataset_and_controlblocks_for_hmi.xml"); LNAdapter ln = findLn(scd, "IedName1", "LdInst11", "ANCR", "1", null); ln.getCurrentElem().unsetDOI(); - TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST); + TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST, DQC_REPORT_TYPE); po.getFCDAs().getFCDA().add(tfcdaFilter); // When hmiService.createAllHmiReportControlBlocks(scd, po); @@ -151,7 +181,7 @@ void createAllIhmReportControlBlocks_when_lDevice_ON_but_LN_Mod_StVal_OFF_should LNAdapter ln = findLn(scd, "IedName1", "LdInst11", "ANCR", "1", null); ln.getDOIAdapterByName(CommonConstants.MOD_DO_NAME).getDataAdapterByName(CommonConstants.STVAL_DA_NAME).setVal("off"); assertThat(ln.getDaiModStValValue()).hasValue("off"); - TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST); + TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST, DQC_REPORT_TYPE); po.getFCDAs().getFCDA().add(tfcdaFilter); // When hmiService.createAllHmiReportControlBlocks(scd, po); @@ -167,7 +197,7 @@ void createAllIhmReportControlBlocks_when_lDevice_OFF_should_not_create_dataset( LN0Adapter ln0 = findLn0(scd, "IedName1", "LdInst11"); ln0.getDOIAdapterByName(CommonConstants.MOD_DO_NAME).getDataAdapterByName(CommonConstants.STVAL_DA_NAME).setVal("off"); assertThat(findLDevice(scd, "IedName1", "LdInst11").getLDeviceStatus()).hasValue(ActiveStatus.OFF.getValue()); - TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST); + TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST, DQC_REPORT_TYPE); po.getFCDAs().getFCDA().add(tfcdaFilter); // When hmiService.createAllHmiReportControlBlocks(scd, po); @@ -183,7 +213,7 @@ void createAllIhmReportControlBlocks_when_LDevice_has_no_status_should_not_creat LN0Adapter ln0 = findLn0(scd, "IedName1", "LdInst11"); ln0.getDOIAdapterByName(CommonConstants.MOD_DO_NAME).getDataAdapterByName(CommonConstants.STVAL_DA_NAME).getCurrentElem().unsetVal(); assertThat(findLDevice(scd, "IedName1", "LdInst11").getLDeviceStatus()).isEmpty(); - TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST); + TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST, DQC_REPORT_TYPE); po.getFCDAs().getFCDA().add(tfcdaFilter); // When hmiService.createAllHmiReportControlBlocks(scd, po); @@ -192,14 +222,15 @@ void createAllIhmReportControlBlocks_when_LDevice_has_no_status_should_not_creat assertThat(streamAllControlBlocks(scd, TReportControl.class)).isEmpty(); } - private static TFCDAFilter createFCDAFilter(String ldInst, String lnClass, String lnInst, String prefix, String doName, Tfc tfc) { + private static TFCDAFilter createFCDAFilter(String ldInst, String lnClass, String lnInst, String prefix, String doName, Tfc tfc, String reportType) { TFCDAFilter tfcdaFilter = new TFCDAFilter(); - tfcdaFilter.setLdInst("LdInst11"); + tfcdaFilter.setLdInst(ldInst); tfcdaFilter.setLnClass(lnClass); - tfcdaFilter.setPrefix(null); + tfcdaFilter.setPrefix(prefix); tfcdaFilter.setDoName(doName); tfcdaFilter.setLnInst(lnInst); tfcdaFilter.setFc(tfc); + tfcdaFilter.setReportType(reportType); return tfcdaFilter; } From 606abc0e8108b771aec9ae792a595c24d6dd4eb7 Mon Sep 17 00:00:00 2001 From: massifben <105049157+massifben@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:06:53 +0200 Subject: [PATCH 2/3] feat(#412): RSR-1048 add special case for InRef binding for LDEPF DIGITAL BOOLEAN channels Signed-off-by: massifben <105049157+massifben@users.noreply.github.com> --- .../compas/sct/commons/scl/ln/LN0Adapter.java | 21 +++++--- .../sct/commons/scl/ln/LN0AdapterTest.java | 48 ++++++++++++++++--- .../scd_update_inref_test.xml | 26 +++++++++- 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java index cab641278..49cbf7ada 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java @@ -8,15 +8,16 @@ import org.apache.commons.lang3.tuple.Pair; import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.*; -import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceActivation; import org.lfenergy.compas.sct.commons.scl.ObjectReference; import org.lfenergy.compas.sct.commons.scl.ied.InputsAdapter; +import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceActivation; import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter; import org.lfenergy.compas.sct.commons.util.PrivateUtils; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.lfenergy.compas.sct.commons.util.CommonConstants.BEHAVIOUR_DO_NAME; @@ -47,9 +48,6 @@ *
  • {@link LN0Adapter#getDAI Returns the value of the DataAttributeRef containment reference By filter}
  • *
  • {@link LN0Adapter#getDAIValues(DataAttributeRef) Returns DAI (sGroup, value) containment reference list By DataAttributeRef filter}
  • * - *
  • {@link LN0Adapter#getDataSetByName(String) Returns the value of the TDataSet object reference By the value of the name attribute }
  • - * - *
  • {@link LN0Adapter#getControlBlocks(List, TServiceType) Returns the value of the ControlBlock containment reference list that match datSet value of given TDataSet }
  • *
  • {@link LN0Adapter#addPrivate Add TPrivate under this object}
  • *
  • {@link LN0Adapter#removeAllControlBlocksAndDatasets() Remove all ControlBlock}
  • * @@ -75,6 +73,7 @@ public class LN0Adapter extends AbstractLNAdapter { public static final DaTypeName BEHAVIOUR_DA_TYPE_NAME = getDaTypeNameForBeh(); private static final String DAI_NAME_PURPOSE = "purpose"; private static final String INREF_PREFIX = "InRef"; + private static final Pattern LDEFP_DIGITAL_CHANNEL_PATTERN = Pattern.compile("DYN_LDEPF_DIGITAL CHANNEL \\d+_\\d+_BOOLEAN"); /** * Constructor @@ -188,7 +187,7 @@ public Optional updateLDeviceStatus(List> ie if (daiBehList.isEmpty()) { return Optional.of(buildFatalReportItem("The LDevice doesn't have a DO @name='Beh' OR its associated DA@fc='ST' AND DA@name='stVal'")); } - Set enumValues = getEnumValues(daiBehList.get(0).getDaName().getType()); + Set enumValues = getEnumValues(daiBehList.getFirst().getDaName().getType()); Optional optionalTCompasLDevice = PrivateUtils.extractCompasPrivate(getParentAdapter().getCurrentElem(), TCompasLDevice.class); if (optionalTCompasLDevice.isEmpty()) { return Optional.of(buildFatalReportItem("The LDevice doesn't have a Private compas:LDevice.")); @@ -237,13 +236,23 @@ public List updateDoInRef() { && doiAdapter.findDataAdapterByName(DAI_NAME_PURPOSE).isPresent()) .map(doiAdapter -> doiAdapter.getDataAdapterByName(DAI_NAME_PURPOSE).getCurrentElem().getVal().stream() .findFirst() - .map(tVal -> doiAdapter.updateDaiFromExtRef(getBoundExtRefsByDesc(tVal.getValue()))) + .map(tVal -> doiAdapter.updateDaiFromExtRef(getExtRefsBoundToInRef(tVal.getValue()))) .orElse(List.of(SclReportItem.warning(getXPath(), "The DOI %s can't be bound with an ExtRef".formatted(getXPath())))) ) .flatMap(List::stream) .collect(Collectors.toList()); } + private List getExtRefsBoundToInRef(String desc) { + List boundExtRefs = getBoundExtRefsByDesc(desc); + // Special case for LDEPF DIGITAL CHANNEL of type BOOLEAN RSR-1048 + if (boundExtRefs.isEmpty() && LDEFP_DIGITAL_CHANNEL_PATTERN.matcher(desc).matches()) { + String descWithoutType = desc.substring(0, desc.lastIndexOf("_")); + return getBoundExtRefsByDesc(descWithoutType); + } + return boundExtRefs; + } + private List getBoundExtRefsByDesc(String desc) { return getExtRefs().stream() .filter(tExtRef -> tExtRef.isSetIedName() && tExtRef.isSetLdInst() && tExtRef.isSetLnClass() && tExtRef.isSetDoName() && diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ln/LN0AdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ln/LN0AdapterTest.java index 10233f345..8de69781a 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ln/LN0AdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ln/LN0AdapterTest.java @@ -302,7 +302,7 @@ void getControlBlocksForMatchingFCDA_should_return_expected_Items(LN0 ln0, TServ List controlBlocks = ln0Adapter.getControlBlocksForMatchingFCDA(extRefInfo).toList(); // Then assertThat(controlBlocks).hasSize(1); - assertThat(controlBlocks.get(0).getServiceType()).isEqualTo(serviceType); + assertThat(controlBlocks.getFirst().getServiceType()).isEqualTo(serviceType); } private static Stream provideServiceType() { @@ -335,7 +335,7 @@ void getDOIAdapters_should_return_expected_list_of_DOIAdapter() { LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); // When Then assertThat(ln0Adapter.getDOIAdapters()).isNotEmpty(); - assertThat(ln0Adapter.getDOIAdapters().get(0).getCurrentElem().getName()).isEqualTo("Do"); + assertThat(ln0Adapter.getDOIAdapters().getFirst().getCurrentElem().getName()).isEqualTo("Do"); } @Test @@ -434,7 +434,7 @@ void testFindMatch() { // When Then AbstractDAIAdapter daiAdapter = (AbstractDAIAdapter) assertDoesNotThrow(() -> ln0Adapter.findMatch(doTypeName, daTypeName).get()); assertThat(daiAdapter.getCurrentElem().getName()).isEqualTo("bda3"); - assertThat(daiAdapter.getCurrentElem().getVal().get(0).getValue()).isEqualTo("Completed-diff"); + assertThat(daiAdapter.getCurrentElem().getVal().getFirst().getValue()).isEqualTo("Completed-diff"); DoTypeName doTypeName2 = new DoTypeName("Do.sdo1"); // When Then assertThat(ln0Adapter.findMatch(doTypeName2, daTypeName)).isEmpty(); @@ -540,7 +540,7 @@ void testGetDAI() { var dataAttributeRefs = ln0Adapter.getDAI(filter, false); // Then assertThat(dataAttributeRefs).hasSize(1); - assertThat(dataAttributeRefs.get(0).getDaName().getType()).isEqualTo("BehaviourModeKind"); + assertThat(dataAttributeRefs.getFirst().getDaName().getType()).isEqualTo("BehaviourModeKind"); } @Test @@ -864,7 +864,7 @@ void createControlBlockIfNotExists_should_create_SampledValueControl() { // Then assertThat(sourceLn0.getCurrentElem().getSampledValueControl()) .hasSize(1); - TSampledValueControl tSampledValueControl = sourceLn0.getCurrentElem().getSampledValueControl().get(0); + TSampledValueControl tSampledValueControl = sourceLn0.getCurrentElem().getSampledValueControl().getFirst(); assertThat(tSampledValueControl) .extracting(TControl::getName, TSampledValueControl::getSmvID, TControl::getDatSet, TSampledValueControl::isMulticast, TSampledValueControl::getSmpRate, TSampledValueControl::getNofASDU, TSampledValueControl::getSmpMod, TSampledValueControl::getSecurityEnable, @@ -895,7 +895,7 @@ void createControlBlockIfNotExists_should_create_ReportControl() { // Then assertThat(sourceLn0.getCurrentElem().getReportControl()) .hasSize(1); - TReportControl tReportControl = sourceLn0.getCurrentElem().getReportControl().get(0); + TReportControl tReportControl = sourceLn0.getCurrentElem().getReportControl().getFirst(); assertThat(tReportControl) .extracting(TControl::getName, TReportControl::getRptID, TControl::getDatSet, TReportControl::isBuffered, TReportControl::getBufTime, TReportControl::isIndexed, TControlWithTriggerOpt::getIntgPd, TReportControl::getConfRev) @@ -1140,6 +1140,42 @@ void updateDoInRef_when_ExtRef_desc_matches_should_update_setSrcRef_and_setSrcCB assertThat(sclReportItems).isEmpty(); } + @Test + void updateDoInRef_when_DAI_purpose_of_Ldevice_LDEPF_ends_with_BOOLEAN_should_match_desc_that_ends_with_enum_and_update_setSrcRef_and_setSrcCB_and_setTstRef_and_setTstCB() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + LN0Adapter sourceLn0 = findLn0(scd, "IED_NAME1", "LDEPF"); + String doiNameInRef = "InRef3"; + String originalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, SETSRCCB_DA_NAME); + String expectedSrcRef = "IED_NAME1LDEPF/PRANCR1.Do11.sdo11"; + String expectedSrcCb = "IED_NAME1LDEPF/prefixANCR1.GSE1"; + String originalSetTstCB = getDaiValue(sourceLn0, doiNameInRef, SETTSTCB_DA_NAME); + String expectedTstRef = "IED_NAME1LDEPF/PRANCR1.Do11.sdo11"; + String expectedTstCB = "IED_NAME1LDEPF/prefixANCR3.GSE3"; + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + // Then + + String finalSetSrcRef = getDaiValue(sourceLn0, doiNameInRef, SETSRCREF_DA_NAME); + String finalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, SETSRCCB_DA_NAME); + String finalSetTstRef = getDaiValue(sourceLn0, doiNameInRef, SETTSTREF_DA_NAME); + String finalSetTstCB = getDaiValue(sourceLn0, doiNameInRef, SETTSTCB_DA_NAME); + assertThat(finalSetSrcRef) + .isNotBlank() + .isEqualTo(expectedSrcRef); + assertThat(finalSetSrcCB) + .isNotEqualTo(originalSetSrcCB) + .isEqualTo(expectedSrcCb); + assertThat(finalSetTstRef) + .isNotBlank() + .isEqualTo(expectedTstRef); + assertThat(finalSetTstCB) + .isNotEqualTo(originalSetTstCB) + .isEqualTo(expectedTstCB); + assertThat(sclReportItems).isEmpty(); + } + @Test void updateDoInRef_when_ExtRef_desc_matches_and_dais_not_updatable_should_not_update_setSrcRef_and_setSrcCB_and_setTstRef_and_setTstCB() { // Given diff --git a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml index 8cc947077..0a3bbea4f 100644 --- a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml +++ b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml @@ -67,6 +67,30 @@ + + + + + + DYN_LDEPF_DIGITAL CHANNEL 36_1_BOOLEAN + + + + OLD_VAL + + + + OLD_VAL + + + + + + + + + + @@ -179,4 +203,4 @@ - \ No newline at end of file + From d4d56740736175ef0b9fb6ac8f6bcb507575df47 Mon Sep 17 00:00:00 2001 From: Samir Romdhani Date: Thu, 22 Aug 2024 14:51:30 +0200 Subject: [PATCH 3/3] feat(#413): LDEPF Post processing - unused CHANNEL assignment LDEPF Post processing - reviews Signed-off-by: Samir Romdhani --- .../sct/commons/ExtRefEditorService.java | 38 +++++++ .../compas/sct/commons/api/ExtRefEditor.java | 8 ++ .../compas/sct/commons/scl/ln/LN0Adapter.java | 9 +- .../sct/commons/util/CommonConstants.java | 2 + .../sct/commons/ExtRefEditorServiceTest.java | 43 +++++++- .../sct/commons/testhelpers/SclHelper.java | 13 +++ .../scd-ldepf/scd_ldepf_postProcessing.xml | 104 ++++++++++++++++++ 7 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 sct-commons/src/test/resources/scd-ldepf/scd_ldepf_postProcessing.xml diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefEditorService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefEditorService.java index 7d6aeb621..010873e38 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefEditorService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ExtRefEditorService.java @@ -7,6 +7,8 @@ import lombok.RequiredArgsConstructor; import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.api.ExtRefEditor; +import org.lfenergy.compas.sct.commons.api.LnEditor; +import org.lfenergy.compas.sct.commons.domain.*; import org.lfenergy.compas.sct.commons.dto.*; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.model.epf.EPF; @@ -46,6 +48,7 @@ public class ExtRefEditorService implements ExtRefEditor { private final IedService iedService; private final LdeviceService ldeviceService; + private final LnEditor lnEditor; private final ExtRefService extRefService; private final DataTypeTemplatesService dataTypeTemplatesService; @@ -323,6 +326,41 @@ public List manageBindingForLDEPF(SCL scd, EPF epf) { return sclReportItems; } + @Override + public void epfPostProcessing(SCL scd) { + iedService.getFilteredIeds(scd, ied -> !ied.getName().contains("TEST")) + .forEach(tied -> ldeviceService.findLdevice(tied, tlDevice -> LDEVICE_LDEPF.equals(tlDevice.getInst())) + .ifPresent(tlDevice -> tlDevice.getLN0().getDOI() + .stream().filter(tdoi -> tdoi.getName().startsWith(INREF_PREFIX)) + .forEach(tdoi -> { + DoLinkedToDaFilter doLinkedToSetSrcRef = new DoLinkedToDaFilter(tdoi.getName(), List.of(), SETSRCREF_DA_NAME, List.of()); + Optional setSrcRefDAI = lnEditor.getDOAndDAInstances(tlDevice.getLN0(), doLinkedToSetSrcRef); + DoLinkedToDaFilter doLinkedPurPose = new DoLinkedToDaFilter(tdoi.getName(), List.of(), PURPOSE_DA_NAME, List.of()); + Optional purPoseDAI = lnEditor.getDOAndDAInstances(tlDevice.getLN0(), doLinkedPurPose); + + boolean isSetSrcRefExistAndEmpty = setSrcRefDAI.isPresent() + && (!setSrcRefDAI.get().isSetVal() + || (setSrcRefDAI.get().isSetVal() + && setSrcRefDAI.get().getVal().getFirst().getValue().isEmpty())); + boolean isPurposeExistAndMatchChannel = purPoseDAI.isPresent() + && purPoseDAI.get().isSetVal() + && (purPoseDAI.get().getVal().getFirst().getValue().startsWith("DYN_LDEPF_DIGITAL CHANNEL") + || purPoseDAI.get().getVal().getFirst().getValue().startsWith("DYN_LDEPF_ANALOG CHANNEL")); + if(isSetSrcRefExistAndEmpty && isPurposeExistAndMatchChannel) { + + DoLinkedToDa doLinkedToDa = new DoLinkedToDa(); + DataObject dataObject = new DataObject(); + dataObject.setDoName(tdoi.getName()); + doLinkedToDa.setDataObject(dataObject); + DataAttribute dataAttribute = new DataAttribute(); + dataAttribute.setDaName(SETSRCREF_DA_NAME); + dataAttribute.setDaiValues(List.of(new DaVal(null, tied.getName()+tlDevice.getInst()+"/LPHD0.Proxy"))); + doLinkedToDa.setDataAttribute(dataAttribute); + lnEditor.updateOrCreateDOAndDAInstances(tlDevice.getLN0(), doLinkedToDa); + } + }))); + } + private List validateIed(SclRootAdapter sclRootAdapter) { List iedErrors = new ArrayList<>(checkIedCompasIcdHeaderAttributes(sclRootAdapter)); iedErrors.addAll(checkIedUnityOfIcdSystemVersionUuid(sclRootAdapter)); diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java index f083a5a34..f817f382b 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/api/ExtRefEditor.java @@ -63,6 +63,14 @@ public interface ExtRefEditor { */ List manageBindingForLDEPF(SCL scd, EPF epf); + /** + * Pointing an unused channel to an existing object LPHD0.Proxy of the concerned IED. + * An unused channel is characterized by the value DAI name ="setSrcRef"/Val (should be empty) in InRef** + * that have a purpose beginning by DYN_LDEPF_DIGITAL CHANNEL or DYN_LDEPF_ANALOG CHANNEL + * @param scd SCL + */ + void epfPostProcessing(SCL scd); + /** * Debinding of Private CompasFlows and ExtRef signals based on voltageLevel * diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java index 49cbf7ada..09836f9cc 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ln/LN0Adapter.java @@ -20,8 +20,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -import static org.lfenergy.compas.sct.commons.util.CommonConstants.BEHAVIOUR_DO_NAME; -import static org.lfenergy.compas.sct.commons.util.CommonConstants.STVAL_DA_NAME; +import static org.lfenergy.compas.sct.commons.util.CommonConstants.*; /** * A representation of the model object @@ -71,8 +70,6 @@ public class LN0Adapter extends AbstractLNAdapter { public static final DoTypeName BEHAVIOUR_DO_TYPE_NAME = new DoTypeName(BEHAVIOUR_DO_NAME); public static final DaTypeName BEHAVIOUR_DA_TYPE_NAME = getDaTypeNameForBeh(); - private static final String DAI_NAME_PURPOSE = "purpose"; - private static final String INREF_PREFIX = "InRef"; private static final Pattern LDEFP_DIGITAL_CHANNEL_PATTERN = Pattern.compile("DYN_LDEPF_DIGITAL CHANNEL \\d+_\\d+_BOOLEAN"); /** @@ -233,8 +230,8 @@ public List updateDoInRef() { return getDOIAdapters().stream() .filter(doiAdapter -> doiAdapter.getCurrentElem().isSetName() && doiAdapter.getCurrentElem().getName().startsWith(INREF_PREFIX) - && doiAdapter.findDataAdapterByName(DAI_NAME_PURPOSE).isPresent()) - .map(doiAdapter -> doiAdapter.getDataAdapterByName(DAI_NAME_PURPOSE).getCurrentElem().getVal().stream() + && doiAdapter.findDataAdapterByName(PURPOSE_DA_NAME).isPresent()) + .map(doiAdapter -> doiAdapter.getDataAdapterByName(PURPOSE_DA_NAME).getCurrentElem().getVal().stream() .findFirst() .map(tVal -> doiAdapter.updateDaiFromExtRef(getExtRefsBoundToInRef(tVal.getValue()))) .orElse(List.of(SclReportItem.warning(getXPath(), "The DOI %s can't be bound with an ExtRef".formatted(getXPath())))) diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/CommonConstants.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/CommonConstants.java index 145c01d93..be03bfd63 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/CommonConstants.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/CommonConstants.java @@ -36,8 +36,10 @@ public final class CommonConstants { public static final String SETSRCCB_DA_NAME = "setSrcCB"; public static final String SETTSTREF_DA_NAME = "setTstRef"; public static final String SETTSTCB_DA_NAME = "setTstCB"; + public static final String PURPOSE_DA_NAME = "purpose"; public static final String Q_DA_NAME = "q"; public static final String IED_TEST_NAME = "IEDTEST"; + public static final String INREF_PREFIX = "InRef"; /** * Private Controlller, should not be instanced diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefEditorServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefEditorServiceTest.java index 66f997e98..098c92533 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefEditorServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ExtRefEditorServiceTest.java @@ -5,6 +5,7 @@ package org.lfenergy.compas.sct.commons; import org.assertj.core.api.Assertions; +import org.assertj.core.api.SoftAssertions; import org.assertj.core.groups.Tuple; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; @@ -39,7 +40,7 @@ class ExtRefEditorServiceTest { @BeforeEach void init() { - extRefEditorService = new ExtRefEditorService(new IedService(), new LdeviceService(), new ExtRefService(), new DataTypeTemplatesService()); + extRefEditorService = new ExtRefEditorService(new IedService(), new LdeviceService(), new LnService(), new ExtRefService(), new DataTypeTemplatesService()); } @Test @@ -955,4 +956,44 @@ void updateIedNameBasedOnLnode_when_no_Compas_ICD_Header_should_return_an_error( .containsExactly(Tuple.tuple(true, "The substation LNode with following attributes : IedName:IED_NAME2 / LdInst:LD_INST21 / LnClass:ANCR / LnInst:1 does not contain the needed (COMPAS - ICDHeader) private")); } + + @Test + void epfPostProcessing_when_exist_unused_channel_should_update_setSrcRef() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ldepf/scd_ldepf_postProcessing.xml"); + // When + extRefEditorService.epfPostProcessing(scd); + // Then + SoftAssertions softly = new SoftAssertions(); + + Optional setSrcRefInInRef1 = findDai(scd, "IED_NAME1", "LDEPF", "InRef1", "setSrcRef"); + Optional purposeInInRef1 = findDai(scd, "IED_NAME1", "LDEPF", "InRef1", "purpose"); + assertThat(purposeInInRef1).isPresent(); + softly.assertThat(purposeInInRef1.get().getVal().getFirst().getValue()).doesNotStartWith("DYN_LDEPF_DIGITAL CHANNEL"); + softly.assertThat(purposeInInRef1.get().getVal().getFirst().getValue()).doesNotStartWith("DYN_LDEPF_ANALOG CHANNEL"); + assertThat(setSrcRefInInRef1).isPresent(); + softly.assertThat(setSrcRefInInRef1.get().isSetVal()).isFalse(); + + Optional setSrcRefInInRef2 = findDai(scd, "IED_NAME1", "LDEPF", "InRef2", "setSrcRef"); + Optional purposeInInRef2 = findDai(scd, "IED_NAME1", "LDEPF", "InRef2", "purpose"); + assertThat(purposeInInRef2).isPresent(); + softly.assertThat(purposeInInRef2.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_DIGITAL CHANNEL"); + assertThat(setSrcRefInInRef2).isPresent(); + softly.assertThat(setSrcRefInInRef2.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy"); + + Optional setSrcRefInInRef3 = findDai(scd, "IED_NAME1", "LDEPF", "InRef3", "setSrcRef"); + Optional purposeInInRef3 = findDai(scd, "IED_NAME1", "LDEPF", "InRef3", "purpose"); + assertThat(purposeInInRef3).isPresent(); + softly.assertThat(purposeInInRef3.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_DIGITAL CHANNEL"); + assertThat(setSrcRefInInRef3).isPresent(); + softly.assertThat(setSrcRefInInRef3.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy"); + + Optional setSrcRefInInRef4 = findDai(scd, "IED_NAME1", "LDEPF", "InRef4", "setSrcRef"); + Optional purposeInInRef4 = findDai(scd, "IED_NAME1", "LDEPF", "InRef4", "purpose"); + assertThat(purposeInInRef4).isPresent(); + softly.assertThat(purposeInInRef4.get().getVal().getFirst().getValue()).startsWith("DYN_LDEPF_ANALOG CHANNEL"); + assertThat(setSrcRefInInRef4).isPresent(); + softly.assertThat(setSrcRefInInRef4.get().getVal().getFirst().getValue()).isEqualTo("IED_NAME1LDEPF/LPHD0.Proxy"); + softly.assertAll(); + } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java index 89987741d..f006c370f 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -118,6 +119,18 @@ public static IDataParentAdapter findDoiOrSdi(AbstractLNAdapter lnAdapter, St return parentAdapter; } + public static Optional findDai(SCL scl, String iedName, String ldInst, String doiName, String daiName) { + return scl.getIED().stream().filter(tied -> tied.getName().equals(iedName)) + .flatMap(tied -> tied.getAccessPoint().stream()) + .flatMap(tAccessPoint -> tAccessPoint.getServer().getLDevice().stream()) + .filter(tlDevice -> tlDevice.getInst().equals(ldInst)) + .flatMap(tlDevice -> tlDevice.getLN0().getDOI().stream()) + .filter(tdoi -> tdoi.getName().equals(doiName)) + .flatMap(tdoi -> tdoi.getSDIOrDAI().stream().map(tUnNaming -> (TDAI)tUnNaming)) + .filter(tdai -> tdai.getName().equals(daiName)) + .findFirst(); + } + public static AbstractDAIAdapter findDai(AbstractLNAdapter lnAdapter, String dataTypeRef) { String[] names = dataTypeRef.split("\\."); if (names.length < 2) { diff --git a/sct-commons/src/test/resources/scd-ldepf/scd_ldepf_postProcessing.xml b/sct-commons/src/test/resources/scd-ldepf/scd_ldepf_postProcessing.xml new file mode 100644 index 000000000..f54474b67 --- /dev/null +++ b/sct-commons/src/test/resources/scd-ldepf/scd_ldepf_postProcessing.xml @@ -0,0 +1,104 @@ + + + + + + + SCD + +
    + + + + + + + + + + + + + + + + + + on + + + + + DYN_LDAGSA2_Circuit I phase A amplitude_1_Vector + + + + + + DYN_LDEPF_DIGITAL CHANNEL 11 ADF + + + + + + DYN_LDEPF_DIGITAL CHANNEL 12 ADF + + + + + + + + DYN_LDEPF_ANALOG CHANNEL 11 ADF + + + + + + + + + + + + + on + + + + + DYN_LDAGSA2_Circuit I phase A amplitude_1_Vector + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + blocked + test + test/blocked + + + \ No newline at end of file