Skip to content

Commit

Permalink
feat(#359): use a POJO for allowed FCDA list in CB Report for HMI cre…
Browse files Browse the repository at this point in the history
…ation

Signed-off-by: Aliou DIAITE <aliou.diaite@rte-france.com>
  • Loading branch information
AliouDIAITE committed Dec 7, 2023
1 parent b4d97c6 commit 2c2f828
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 41 deletions.
19 changes: 19 additions & 0 deletions sct-commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,25 @@
<clearOutputDir>false</clearOutputDir>
</configuration>
</execution>
<execution>
<id>cb_po</id>
<goals>
<goal>xjc</goal>
</goals>
<configuration>
<sources>
<source>
${project.basedir}/src/main/resources/xsd/CB_REPORT_SUPERVISION_Config_file.xsd
</source>
</sources>
<xjbSources>
<xjbSource>${project.basedir}/src/main/resources/binding_configuration.xjb</xjbSource>
</xjbSources>
<packageName>org.lfenergy.compas.sct.commons.model.cb_po</packageName>
<noPackageLevelAnnotations>true</noPackageLevelAnnotations>
<clearOutputDir>false</clearOutputDir>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@
package org.lfenergy.compas.sct.commons;

import org.lfenergy.compas.scl2007b4.model.SCL;
import org.lfenergy.compas.scl2007b4.model.TFCDA;
import org.lfenergy.compas.sct.commons.api.HmiEditor;
import org.lfenergy.compas.sct.commons.model.cb_po.PO;
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter;

import java.util.List;

public class HmiService implements HmiEditor {

@Override
public void createAllHmiReportControlBlocks(SCL scd, List<TFCDA> fcdas) {
public void createAllHmiReportControlBlocks(SCL scd, PO po) {
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
sclRootAdapter.streamIEDAdapters()
.flatMap(IEDAdapter::streamLDeviceAdapters)
.forEach(lDeviceAdapter -> lDeviceAdapter.createHmiReportControlBlocks(fcdas));
.forEach(lDeviceAdapter -> lDeviceAdapter.createHmiReportControlBlocks(po));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@
package org.lfenergy.compas.sct.commons.api;

import org.lfenergy.compas.scl2007b4.model.SCL;
import org.lfenergy.compas.scl2007b4.model.TFCDA;
import org.lfenergy.compas.scl2007b4.model.TReportControl;

import java.util.List;
import org.lfenergy.compas.sct.commons.model.cb_po.PO;

/**
* Service class that will be used to manage elements related to the HMI {@link TReportControl <em>Report Control Blocks</em>}.
Expand All @@ -20,7 +18,7 @@ public interface HmiEditor {
/**
* Create the DataSet and ReportControl Blocks for the HMI with the given FCDAs.
*
* @param fcdas List of FCDA for which we must create the DataSet and ReportControl Blocks
* @param po object containing list of FCDA for which we must create the DataSet and ReportControl Blocks
*/
void createAllHmiReportControlBlocks(SCL scd, List<TFCDA> fcdas);
void createAllHmiReportControlBlocks(SCL scd, PO po);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.dto.*;
import org.lfenergy.compas.sct.commons.exception.ScdException;
import org.lfenergy.compas.sct.commons.model.cb_po.PO;
import org.lfenergy.compas.sct.commons.model.cb_po.TFCDAFilter;
import org.lfenergy.compas.sct.commons.scl.SclElementAdapter;
import org.lfenergy.compas.sct.commons.scl.dtt.DataTypeTemplateAdapter;
import org.lfenergy.compas.sct.commons.scl.ied.ControlBlockAdapter;
Expand Down Expand Up @@ -81,20 +83,20 @@ public LDeviceAdapter(IEDAdapter parentAdapter, TLDevice currentElem) {
* Create DataSet and ReportControl Blocks for the HMI with the given FCDAs.
* DataSet and ReportControl are created in LN0, even if FCDA refers to another LN.
*
* @param fcdas List of FCDA for which we must create the DataSet and ReportControl
* @param po object containing list of FCDA for which we must create the DataSet and ReportControl
*/
public void createHmiReportControlBlocks(List<TFCDA> fcdas) {
public void createHmiReportControlBlocks(PO po) {
LN0Adapter ln0 = getLN0Adapter();
if (!ln0.getDaiModStValValue().map(ActiveStatus::fromValue).map(ActiveStatus.ON::equals).orElse(false)) return;
fcdas.stream()
.filter(fcda -> getInst().equals(fcda.getLdInst()) && fcda.isSetLnClass())
.forEach(fcda -> (fcda.getLnClass().get(0).equals(TLLN0Enum.LLN_0.value()) ?
po.getFCDAs().getFCDA().stream()
.filter(tfcdaFilter -> getInst().equals(tfcdaFilter.getLdInst()) && tfcdaFilter.isSetLnClass())
.forEach(tfcdaFilter -> (tfcdaFilter.getLnClass().equals(TLLN0Enum.LLN_0.value()) ?
Optional.of(ln0) // ln0 Mod stVal "ON" has already been checked, no need to check it again
:
findLnAdapter(fcda.getLnClass().get(0), fcda.getLnInst(), fcda.getPrefix()).filter(lnAdapter -> lnAdapter.getDaiModStValValue().map(ActiveStatus::fromValue).map(ActiveStatus.ON::equals).orElse(true)))
.map(sourceLn -> sourceLn.getDAI(new DataAttributeRef(fcda), false))
.filter(das -> das.stream().anyMatch(da -> fcda.getFc() == da.getFc())) // getDAI does not filter on DA.
.ifPresent(dataAttributeRefs -> createHmiReportCB(ln0, fcda)));
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))));
}

private void createHmiReportCB(LN0Adapter ln0, TFCDA fcda) {
Expand Down Expand Up @@ -514,4 +516,15 @@ public List<ExtRefInfo.ExtRefBayReference> getExtRefBayReferenceForActifLDEPF(fi
return extRefBayReferenceList;
}

private TFCDA toFCDA(TFCDAFilter tfcdaFilter) {
TFCDA tfcda = new TFCDA();
tfcda.setLdInst(tfcdaFilter.getLdInst());
tfcda.getLnClass().add(tfcdaFilter.getLnClass());
tfcda.setPrefix(tfcdaFilter.getPrefix());
tfcda.setLnInst(tfcdaFilter.getLnInst());
tfcda.setDoName(tfcdaFilter.getDoName());
tfcda.setFc(TFCEnum.fromValue(tfcdaFilter.getFc().value()));
return tfcda;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2023 RTE FRANCE
SPDX-License-Identifier: Apache-2.0
-->
<xs:schema xmlns="http://www.rte-france.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.rte-france.com" elementFormDefault="qualified" attributeFormDefault="unqualified"
version="1">

<xs:element name="PO">
<xs:complexType>
<xs:sequence>
<xs:element ref="History"/>
<xs:element ref="Version"/>
<xs:element ref="FCDAs"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:element name="History">
<xs:complexType>
<xs:sequence>
<xs:element name="Hitem" type="tHitem" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:complexType name="tHitem" mixed="true">
<xs:attribute name="version" type="xs:normalizedString" use="required"/>
<xs:attribute name="when" type="xs:normalizedString" use="required"/>
<xs:attribute name="who" type="xs:normalizedString"/>
<xs:attribute name="what" type="xs:normalizedString"/>
</xs:complexType>

<xs:element name="Version">
<xs:complexType>
<xs:sequence>
<xs:element name="SystemVersion" type="tSystemVersion" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:complexType name="tSystemVersion" mixed="true">
<xs:attribute name="MainSystemVersion" type="xs:normalizedString" use="required"/>
</xs:complexType>

<xs:element name="FCDAs">
<xs:complexType>
<xs:sequence>
<xs:element name="FCDA" type="tFCDAFilter" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>

<xs:complexType name="tFCDAFilter">
<xs:attribute name="ldInst" type="xs:string" use="required"/>
<xs:attribute name="prefix" type="xs:string" use="optional"/>
<xs:attribute name="lnClass" type="xs:string" use="required"/>
<xs:attribute name="lnInst" type="xs:string" use="required"/>
<xs:attribute name="doName" type="xs:string" use="required"/>
<xs:attribute name="fc" type="tfc" use="required"/>
</xs:complexType>

<xs:simpleType name="tfc">
<xs:restriction base="xs:string">
<xs:enumeration value="ST"/>
<xs:enumeration value="MX"/>
</xs:restriction>
</xs:simpleType>

</xs:schema>
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,53 @@

package org.lfenergy.compas.sct.commons;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.lfenergy.compas.scl2007b4.model.*;
import org.lfenergy.compas.sct.commons.model.cb_po.FCDAs;
import org.lfenergy.compas.sct.commons.model.cb_po.PO;
import org.lfenergy.compas.sct.commons.model.cb_po.TFCDAFilter;
import org.lfenergy.compas.sct.commons.model.cb_po.Tfc;
import org.lfenergy.compas.sct.commons.scl.ied.DataSetAdapter;
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.SclTestMarshaller;
import org.lfenergy.compas.sct.commons.util.CommonConstants;
import org.lfenergy.compas.sct.commons.util.ActiveStatus;
import org.lfenergy.compas.sct.commons.util.CommonConstants;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*;
import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newFcda;

@ExtendWith(MockitoExtension.class)
class HmiServiceTest {

@InjectMocks
HmiService hmiService;

private final PO po = new PO();

@BeforeEach
void setUp() {
FCDAs fcdAs = new FCDAs();
po.setFCDAs(fcdAs);
}

@Test
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");
TFCDA fcda = newFcda("LdInst11", "ANCR", "1", null, "DoName1", null, TFCEnum.ST);
TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST);
po.getFCDAs().getFCDA().add(tfcdaFilter);
// When
hmiService.createAllHmiReportControlBlocks(scd, List.of(fcda));
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(fcda);
.usingRecursiveComparison().isEqualTo(toFCDA(tfcdaFilter));
// Check ControlBlock is created
LN0Adapter ln0 = findLn0(scd, "IedName1", "LdInst11");
assertThat(ln0.getTControlsByType(TReportControl.class)).hasSize(1);
Expand All @@ -58,14 +69,15 @@ 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");
TFCDA fcda = newFcda("LdInst11", "PVOC", "1", null, "DoName2", null, TFCEnum.MX);
TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "PVOC", "1", null, "DoName2", Tfc.MX);
po.getFCDAs().getFCDA().add(tfcdaFilter);
// When
hmiService.createAllHmiReportControlBlocks(scd, List.of(fcda));
hmiService.createAllHmiReportControlBlocks(scd, po);
// Then
// Check DataSet is created
DataSetAdapter dataSet = findDataSet(scd, "IedName1", "LdInst11", "DS_LDINST11_CYPO");
assertThat(dataSet.getCurrentElem().getFCDA()).hasSize(1).first()
.usingRecursiveComparison().isEqualTo(fcda);
.usingRecursiveComparison().isEqualTo(toFCDA(tfcdaFilter));
// Check ControlBlock is created
LN0Adapter ln0 = findLn0(scd, "IedName1", "LdInst11");
assertThat(ln0.getTControlsByType(TReportControl.class)).hasSize(1);
Expand All @@ -85,14 +97,15 @@ void createAllIhmReportControlBlocks_with_fc_MX_should_create_dataset_and_contro
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");
TFCDA fcda = newFcda("LdInst11", "LLN0", null, null, "DoName0", null, TFCEnum.ST);
TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "LLN0", null, null, "DoName0", Tfc.ST);
po.getFCDAs().getFCDA().add(tfcdaFilter);
// When
hmiService.createAllHmiReportControlBlocks(scd, List.of(fcda));
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(fcda);
.usingRecursiveComparison().isEqualTo(toFCDA(tfcdaFilter));
// Check ControlBlock is created
LN0Adapter ln0 = findLn0(scd, "IedName1", "LdInst11");
assertThat(ln0.getTControlsByType(TReportControl.class)).hasSize(1);
Expand All @@ -113,14 +126,15 @@ 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();
TFCDA fcda = newFcda("LdInst11", "ANCR", "1", null, "DoName1", null, TFCEnum.ST);
TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST);
po.getFCDAs().getFCDA().add(tfcdaFilter);
// When
hmiService.createAllHmiReportControlBlocks(scd, List.of(fcda));
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(fcda);
.usingRecursiveComparison().isEqualTo(toFCDA(tfcdaFilter));
// Check ControlBlock is created
LN0Adapter ln0 = findLn0(scd, "IedName1", "LdInst11");
assertThat(ln0.getTControlsByType(TReportControl.class)).hasSize(1);
Expand All @@ -137,9 +151,10 @@ 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");
TFCDA fcda = newFcda("LdInst11", "ANCR", "1", null, "DoName1", null, TFCEnum.ST);
TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST);
po.getFCDAs().getFCDA().add(tfcdaFilter);
// When
hmiService.createAllHmiReportControlBlocks(scd, List.of(fcda));
hmiService.createAllHmiReportControlBlocks(scd, po);
// Then
assertThat(streamAllDataSets(scd)).isEmpty();
assertThat(streamAllControlBlocks(scd, TReportControl.class)).isEmpty();
Expand All @@ -152,9 +167,10 @@ 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());
TFCDA fcda = newFcda("LdInst11", "ANCR", "1", null, "DoName1", null, TFCEnum.ST);
TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST);
po.getFCDAs().getFCDA().add(tfcdaFilter);
// When
hmiService.createAllHmiReportControlBlocks(scd, List.of(fcda));
hmiService.createAllHmiReportControlBlocks(scd, po);
// Then
assertThat(streamAllDataSets(scd)).isEmpty();
assertThat(streamAllControlBlocks(scd, TReportControl.class)).isEmpty();
Expand All @@ -167,12 +183,35 @@ 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();
TFCDA fcda = newFcda("LdInst11", "ANCR", "1", null, "DoName1", null, TFCEnum.ST);
TFCDAFilter tfcdaFilter = createFCDAFilter("LdInst11", "ANCR", "1", null, "DoName1", Tfc.ST);
po.getFCDAs().getFCDA().add(tfcdaFilter);
// When
hmiService.createAllHmiReportControlBlocks(scd, List.of(fcda));
hmiService.createAllHmiReportControlBlocks(scd, po);
// Then
assertThat(streamAllDataSets(scd)).isEmpty();
assertThat(streamAllControlBlocks(scd, TReportControl.class)).isEmpty();
}

private static TFCDAFilter createFCDAFilter(String ldInst, String lnClass, String lnInst, String prefix, String doName, Tfc tfc) {
TFCDAFilter tfcdaFilter = new TFCDAFilter();
tfcdaFilter.setLdInst("LdInst11");
tfcdaFilter.setLnClass(lnClass);
tfcdaFilter.setPrefix(null);
tfcdaFilter.setDoName(doName);
tfcdaFilter.setLnInst(lnInst);
tfcdaFilter.setFc(tfc);
return tfcdaFilter;
}

private static TFCDA toFCDA(TFCDAFilter tfcdaFilter) {
TFCDA tfcda = new TFCDA();
tfcda.setLdInst(tfcdaFilter.getLdInst());
tfcda.getLnClass().add(tfcdaFilter.getLnClass());
tfcda.setPrefix(tfcdaFilter.getPrefix());
tfcda.setLnInst(tfcdaFilter.getLnInst());
tfcda.setDoName(tfcdaFilter.getDoName());
tfcda.setFc(TFCEnum.fromValue(tfcdaFilter.getFc().value()));
return tfcda;
}

}

0 comments on commit 2c2f828

Please sign in to comment.