Skip to content

Commit f21f422

Browse files
authored
Merge pull request #263 from com-pas/258-compas-creation-of-report-cb-for-the-hmi
feat(#258): RSR-649 add service to create datasets and controlBlocks for HMI
2 parents 6cb2401 + ff1e3e4 commit f21f422

31 files changed

+838
-270
lines changed

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ControlBlockTarget.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
import java.util.Objects;
1212

1313
import static org.lfenergy.compas.scl2007b4.model.TControlWithIEDName.IEDName;
14-
import static org.lfenergy.compas.sct.commons.util.Utils.emptyIfBlank;
15-
import static org.lfenergy.compas.sct.commons.util.Utils.nullIfBlank;
1614

1715
/**
1816
* Record that hold the data for TClientLN and TControlWithIEDName.IEDName which have the same fields.
@@ -50,8 +48,8 @@ public TClientLN toTClientLn() {
5048
newTClientLn.getLnClass().add(lnClass);
5149
}
5250
// LnInst is required on TClientLN, so it cannot be null, but it can be empty
53-
newTClientLn.setLnInst(emptyIfBlank(lnInst));
54-
newTClientLn.setPrefix(nullIfBlank(prefix));
51+
newTClientLn.setLnInst(StringUtils.trimToEmpty(lnInst));
52+
newTClientLn.setPrefix(StringUtils.trimToNull(prefix));
5553
newTClientLn.setDesc(desc);
5654
return newTClientLn;
5755
}
@@ -69,8 +67,8 @@ public IEDName toIedName() {
6967
newIedName.getLnClass().add(lnClass);
7068
}
7169
// LnInst is optional on IEDName, so it can be null, but cannot be empty
72-
newIedName.setLnInst(nullIfBlank(lnInst));
73-
newIedName.setPrefix(nullIfBlank(prefix));
70+
newIedName.setLnInst(StringUtils.trimToNull(lnInst));
71+
newIedName.setPrefix(StringUtils.trimToNull(prefix));
7472
return newIedName;
7573
}
7674

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ResumedDataTemplate.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import com.fasterxml.jackson.annotation.JsonIgnore;
88
import lombok.*;
9+
import org.apache.commons.lang3.StringUtils;
910
import org.lfenergy.compas.scl2007b4.model.*;
1011
import org.lfenergy.compas.sct.commons.scl.ied.AbstractLNAdapter;
1112
import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
@@ -15,7 +16,7 @@
1516
import java.util.Optional;
1617

1718
import static org.lfenergy.compas.sct.commons.util.CommonConstants.MOD_DO_NAME;
18-
import static org.lfenergy.compas.sct.commons.util.CommonConstants.STVAL;
19+
import static org.lfenergy.compas.sct.commons.util.CommonConstants.STVAL_DA_NAME;
1920

2021

2122
/**
@@ -54,13 +55,23 @@ public class ResumedDataTemplate {
5455
/**
5556
* Constructor
5657
*/
57-
public ResumedDataTemplate(AbstractLNAdapter<?> lnAdapter, String doName, String daName) {
58+
public ResumedDataTemplate(AbstractLNAdapter<?> lnAdapter, DoTypeName doName, DaTypeName daName) {
5859
this.lnClass = lnAdapter.getLNClass();
5960
this.lnInst = lnAdapter.getLNInst();
6061
this.prefix = lnAdapter.getPrefix();
6162
this.lnType = lnAdapter.getLnType();
62-
this.doName = new DoTypeName(doName);
63-
this.daName = new DaTypeName(daName);
63+
this.doName = doName;
64+
this.daName = daName;
65+
}
66+
67+
public ResumedDataTemplate(TFCDA fcda) {
68+
this.lnClass = fcda.isSetLnClass() ? fcda.getLnClass().get(0) : null;
69+
this.lnInst = fcda.getLnInst();
70+
this.prefix = fcda.getPrefix();
71+
this.lnType = null;
72+
this.doName = new DoTypeName(StringUtils.trimToEmpty(fcda.getDoName()));
73+
this.daName = new DaTypeName(StringUtils.trimToEmpty(fcda.getDaName()));
74+
if (daName.isDefined()) this.setFc(fcda.getFc());
6475
}
6576

6677
/**
@@ -94,7 +105,7 @@ public boolean isUpdatable(){
94105
* @return true if DO is "Mod" and DA is "stVal", false otherwise
95106
*/
96107
private boolean isDOModDAstVal(){
97-
return doName.getName().equals(MOD_DO_NAME) && daName.getName().equals(STVAL);
108+
return doName.getName().equals(MOD_DO_NAME) && daName.getName().equals(STVAL_DA_NAME);
98109
}
99110

100111
@JsonIgnore

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
package org.lfenergy.compas.sct.commons.scl;
66

7-
import lombok.experimental.UtilityClass;
87
import org.apache.commons.lang3.StringUtils;
98
import org.lfenergy.compas.scl2007b4.model.SCL;
109
import org.lfenergy.compas.scl2007b4.model.TCompasICDHeader;
@@ -30,11 +29,14 @@
3029
import static org.lfenergy.compas.sct.commons.dto.ControlBlockNetworkSettings.*;
3130
import static org.lfenergy.compas.sct.commons.util.Utils.isExtRefFeedBySameControlBlock;
3231

33-
@UtilityClass
34-
public class ExtRefService {
32+
public final class ExtRefService {
3533

3634
private static final String MESSAGE_MISSING_IED_NAME_PARAMETER = "IED.name parameter is missing";
3735

36+
private ExtRefService() {
37+
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
38+
}
39+
3840
/**
3941
* Updates iedName attribute of all ExtRefs in the Scd.
4042
*
@@ -203,7 +205,7 @@ private static List<SclReportItem> configureNetworkForControlBlocks(SCL scd, Con
203205

204206
private static Optional<SclReportItem> configureControlBlockNetwork(ControlBlockNetworkSettings controlBlockNetworkSettings, PrimitiveIterator.OfLong appIdIterator, Iterator<String> macAddressIterator, ControlBlockAdapter controlBlockAdapter) {
205207
SettingsOrError settingsOrError = controlBlockNetworkSettings.getNetworkSettings(controlBlockAdapter);
206-
if (settingsOrError.errorMessage() != null){
208+
if (settingsOrError.errorMessage() != null) {
207209
return Optional.of(controlBlockAdapter.buildFatalReportItem(
208210
"Cannot configure network for this ControlBlock because: " + settingsOrError.errorMessage()));
209211
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-FileCopyrightText: 2023 RTE FRANCE
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package org.lfenergy.compas.sct.commons.scl;
6+
7+
import org.lfenergy.compas.scl2007b4.model.SCL;
8+
import org.lfenergy.compas.scl2007b4.model.TFCDA;
9+
import org.lfenergy.compas.sct.commons.scl.ied.IEDAdapter;
10+
11+
import java.util.List;
12+
13+
public final class HmiService {
14+
15+
private HmiService() {
16+
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
17+
}
18+
19+
/**
20+
* Create the DataSet and ReportControl Blocks for the HMI with the given FCDAs.
21+
*
22+
* @param fcdas List of FCDA for which we must create the DataSet and ReportControl Blocks
23+
*/
24+
public static void createAllHmiReportControlBlocks(SCL scd, List<TFCDA> fcdas) {
25+
SclRootAdapter sclRootAdapter = new SclRootAdapter(scd);
26+
sclRootAdapter.streamIEDAdapters()
27+
.flatMap(IEDAdapter::streamLDeviceAdapters)
28+
.forEach(lDeviceAdapter -> lDeviceAdapter.createHmiReportControlBlocks(fcdas));
29+
}
30+
31+
}

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import org.lfenergy.compas.scl2007b4.model.TExtRef;
1010
import org.lfenergy.compas.scl2007b4.model.TLLN0Enum;
1111
import org.lfenergy.compas.sct.commons.dto.ExtrefTarget;
12-
import org.lfenergy.compas.sct.commons.util.Utils;
1312

1413
/**
1514
* A representation of the model object
@@ -55,16 +54,22 @@ public ObjectReference(String reference) {
5554
public ObjectReference(TExtRef extRef, ExtrefTarget target) {
5655
this.ldName = extRef.getIedName() + extRef.getLdInst();
5756
if(target.equals(ExtrefTarget.SRC_REF)) {
58-
this.lNodeName = Utils.emptyIfBlank(extRef.getPrefix())
59-
+ (extRef.isSetLnClass() ? extRef.getLnClass().get(0): TLLN0Enum.LLN_0.value())
60-
+ Utils.emptyIfBlank(extRef.getLnInst());
61-
this.dataAttributes = Utils.emptyIfBlank(extRef.getDoName());
57+
String s1 = extRef.getLnInst();
58+
String s2 = extRef.getPrefix();
59+
this.lNodeName = StringUtils.trimToEmpty(s2)
60+
+ (extRef.isSetLnClass() ? extRef.getLnClass().get(0) : TLLN0Enum.LLN_0.value())
61+
+ StringUtils.trimToEmpty(s1);
62+
String s = extRef.getDoName();
63+
this.dataAttributes = StringUtils.trimToEmpty(s);
6264
}
6365
if(target.equals(ExtrefTarget.SRC_CB)) {
64-
this.lNodeName = Utils.emptyIfBlank(extRef.getSrcPrefix())
65-
+ (extRef.isSetSrcLNClass() ? extRef.getSrcLNClass().get(0): TLLN0Enum.LLN_0.value())
66-
+ Utils.emptyIfBlank(extRef.getSrcLNInst());
67-
this.dataAttributes = Utils.emptyIfBlank(extRef.getSrcCBName());
66+
String s1 = extRef.getSrcLNInst();
67+
String s2 = extRef.getSrcPrefix();
68+
this.lNodeName = StringUtils.trimToEmpty(s2)
69+
+ (extRef.isSetSrcLNClass() ? extRef.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value())
70+
+ StringUtils.trimToEmpty(s1);
71+
String s = extRef.getSrcCBName();
72+
this.dataAttributes = StringUtils.trimToEmpty(s);
6873
}
6974
this.reference = this.ldName + "/" + this.lNodeName + "." + this.dataAttributes;
7075
}

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void setValImport(boolean b) {
5757

5858
private boolean isDOModDAstVal() {
5959
if (parentAdapter.getCurrentElem() instanceof final TDOI tdoi) {
60-
return currentElem.getName().equals(CommonConstants.STVAL) && tdoi.getName().equals(CommonConstants.MOD_DO_NAME);
60+
return currentElem.getName().equals(CommonConstants.STVAL_DA_NAME) && tdoi.getName().equals(CommonConstants.MOD_DO_NAME);
6161
}
6262
return false;
6363
}

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
import java.util.stream.Collectors;
2323
import java.util.stream.Stream;
2424

25+
import static org.lfenergy.compas.sct.commons.util.CommonConstants.MOD_DO_NAME;
26+
import static org.lfenergy.compas.sct.commons.util.CommonConstants.STVAL_DA_NAME;
27+
2528

2629
/**
2730
* A representation of the model object
@@ -73,6 +76,10 @@
7376
public abstract class AbstractLNAdapter<T extends TAnyLN> extends SclElementAdapter<LDeviceAdapter, T> {
7477

7578

79+
public static final DoTypeName MOD_DO_TYPE_NAME = new DoTypeName(MOD_DO_NAME);
80+
public static final DaTypeName STVAL_DA_TYPE_NAME = new DaTypeName(STVAL_DA_NAME);
81+
private static final String DAI_MOD_STVAL_VALUE_ON = "on";
82+
7683
/**
7784
* Constructor
7885
*
@@ -967,6 +974,17 @@ public Stream<ControlBlockAdapter> streamControlBlocks(ControlBlockEnum controlB
967974
.map(tControl -> new ControlBlockAdapter(this, tControl));
968975
}
969976

977+
/**
978+
* Create ControlBlock if there is no ControlBlock of the same type (controlBlockEnum) and with the same cbName in this LN/LN0.
979+
* When the controlBlock already exists, the id and datSet attributes are NOT updated with the given values.
980+
*
981+
* @param cbName cbName of the controlBlock to look for. When not found, the cbName of the controlBlock to create.
982+
* @param id When controlBlock not found, the id of the controlBlock to create
983+
* @param datSet the datSet of the controlBlock to create
984+
* @param controlBlockEnum the type of ControlBlock to create
985+
* @return existing controlBlock if a controlBlock of the same type and with same cbName was found in this LN/LN0, otherwise the created ControlBlock.
986+
* The returned ControlBlock is always a child of this LN/LN0.
987+
*/
970988
public ControlBlockAdapter createControlBlockIfNotExists(String cbName, String id, String datSet, ControlBlockEnum controlBlockEnum) {
971989
return findControlBlock(cbName, controlBlockEnum)
972990
.orElseGet(() -> addControlBlock(
@@ -980,6 +998,25 @@ public ControlBlockAdapter createControlBlockIfNotExists(String cbName, String i
980998
);
981999
}
9821000

1001+
/**
1002+
* Generate a ControlBlock Id based on the current LN and the given ldName (ldName can be different from the parent LD.name)
1003+
*
1004+
* @param ldName LD name to use for generating the id
1005+
* @param cbName name of the ControlBlock
1006+
* @return "ldName/LnPrefixLnClassLnInst.cbName". Blank values are omitted (e.g "IEDNAME1LD1/LLN0.CBNAME1")
1007+
*/
1008+
public String generateControlBlockId(String ldName, String cbName) {
1009+
String s = getLNInst();
1010+
String s1 = getPrefix();
1011+
return StringUtils.trimToEmpty(ldName)
1012+
+ "/"
1013+
+ StringUtils.trimToEmpty(s1)
1014+
+ StringUtils.defaultString(getLNClass(), "")
1015+
+ StringUtils.trimToEmpty(s)
1016+
+ "."
1017+
+ StringUtils.trimToEmpty(cbName);
1018+
}
1019+
9831020
/**
9841021
* Finds all FCDAs in DataSet of Control Block feeding ExtRef
9851022
*
@@ -997,4 +1034,29 @@ public List<TFCDA> getFCDAs(TExtRef tExtRef) {
9971034
.flatMap(Collection::stream)
9981035
.toList();
9991036
}
1037+
1038+
/**
1039+
* Get the value of "Mod.stVal" of the current LN
1040+
*
1041+
* @return Mod.stVal value if present, else empty Optional
1042+
*/
1043+
public Optional<String> getDaiModStValValue() {
1044+
return getDaiModStVal()
1045+
.flatMap(ResumedDataTemplate::findFirstValue);
1046+
}
1047+
1048+
/**
1049+
* Check the status ("Mod.stVal" value) of the current LN
1050+
*
1051+
* @return true if data Mod.stVal is present and has value equals to LnStatus.ON
1052+
*/
1053+
public boolean isDaiModStValOn() {
1054+
return DAI_MOD_STVAL_VALUE_ON.equals(getDaiModStValValue().orElse(null));
1055+
}
1056+
1057+
protected Optional<ResumedDataTemplate> getDaiModStVal() {
1058+
ResumedDataTemplate daiModFilter = new ResumedDataTemplate(this, MOD_DO_TYPE_NAME, STVAL_DA_TYPE_NAME);
1059+
return getDAI(daiModFilter, false).stream()
1060+
.findFirst();
1061+
}
10001062
}

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/ControlBlockAdapter.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@
1212
import org.lfenergy.compas.sct.commons.scl.SclRootAdapter;
1313
import org.lfenergy.compas.sct.commons.scl.com.ConnectedAPAdapter;
1414
import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
15-
import org.lfenergy.compas.sct.commons.util.SclConstructorHelper;
1615
import org.lfenergy.compas.sct.commons.util.Utils;
1716

1817
import java.util.ArrayList;
1918
import java.util.List;
2019
import java.util.Optional;
2120

21+
import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newDurationInMilliSec;
22+
import static org.lfenergy.compas.sct.commons.util.SclConstructorHelper.newP;
2223
import static org.lfenergy.compas.sct.commons.util.Utils.xpathAttributeFilter;
2324

2425
/**
@@ -147,16 +148,16 @@ public Optional<SclReportItem> configureNetwork(long appId, String macAddress, I
147148
}
148149
ConnectedAPAdapter connectedAPAdapter = optConApAdapter.get();
149150
List<TP> listOfPs = new ArrayList<>();
150-
listOfPs.add(SclConstructorHelper.newP(APPID_P_TYPE, Utils.toHex(appId, APPID_LENGTH)));
151-
listOfPs.add(SclConstructorHelper.newP(MAC_ADDRESS_P_TYPE, macAddress));
151+
listOfPs.add(newP(APPID_P_TYPE, Utils.toHex(appId, APPID_LENGTH)));
152+
listOfPs.add(newP(MAC_ADDRESS_P_TYPE, macAddress));
152153
if (vlanId != null) {
153-
listOfPs.add(SclConstructorHelper.newP(VLAN_ID_P_TYPE, Utils.toHex(vlanId, VLAN_ID_LENGTH)));
154+
listOfPs.add(newP(VLAN_ID_P_TYPE, Utils.toHex(vlanId, VLAN_ID_LENGTH)));
154155
if (vlanPriority != null) {
155-
listOfPs.add(SclConstructorHelper.newP(VLAN_PRIORITY_P_TYPE, String.valueOf(vlanPriority)));
156+
listOfPs.add(newP(VLAN_PRIORITY_P_TYPE, String.valueOf(vlanPriority)));
156157
}
157158
}
158159
switch (getControlBlockEnum()) {
159-
case GSE -> connectedAPAdapter.updateGseOrCreateIfNotExists(getParentLDeviceAdapter().getInst(), currentElem.getName(), listOfPs, clone(minTime), clone(maxTime));
160+
case GSE -> connectedAPAdapter.updateGseOrCreateIfNotExists(getParentLDeviceAdapter().getInst(), currentElem.getName(), listOfPs, newDurationInMilliSec(minTime), newDurationInMilliSec(maxTime));
160161
case SAMPLED_VALUE -> connectedAPAdapter.updateSmvOrCreateIfNotExists(getParentLDeviceAdapter().getInst(), currentElem.getName(), listOfPs);
161162
default -> {
162163
return Optional.of(buildFatalReportItem("configureNetwork not yet implemented for %s ControlBlocks".formatted(getControlBlockEnum())));
@@ -165,13 +166,6 @@ public Optional<SclReportItem> configureNetwork(long appId, String macAddress, I
165166
return Optional.empty();
166167
}
167168

168-
private TDurationInMilliSec clone(TDurationInMilliSec tDurationInMilliSec){
169-
if (tDurationInMilliSec == null) {
170-
return null;
171-
}
172-
return SclConstructorHelper.newDurationInMilliSec(tDurationInMilliSec.getValue(), tDurationInMilliSec.getUnit(), tDurationInMilliSec.getMultiplier());
173-
}
174-
175169
/**
176170
* Get parent LDevice
177171
* @return ControlBlock's parent lDeviceAdapter

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55
package org.lfenergy.compas.sct.commons.scl.ied;
66

77
import org.lfenergy.compas.scl2007b4.model.*;
8-
import org.lfenergy.compas.sct.commons.dto.ExtrefTarget;
9-
import org.lfenergy.compas.sct.commons.dto.ResumedDataTemplate;
10-
import org.lfenergy.compas.sct.commons.dto.SclReportItem;
8+
import org.lfenergy.compas.sct.commons.dto.*;
119
import org.lfenergy.compas.sct.commons.scl.ObjectReference;
1210
import org.lfenergy.compas.sct.commons.scl.SclElementAdapter;
1311
import org.lfenergy.compas.sct.commons.util.Utils;
@@ -136,7 +134,7 @@ public List<SclReportItem> updateDaiFromExtRef(List<TExtRef> tExtRefs) {
136134
}
137135

138136
private Optional<SclReportItem> updateDAI(String daName, String value) {
139-
ResumedDataTemplate daiFilterSrcRef = new ResumedDataTemplate(getParentAdapter(), getName(), daName);
137+
ResumedDataTemplate daiFilterSrcRef = new ResumedDataTemplate(getParentAdapter(), new DoTypeName(getName()), new DaTypeName(daName));
140138
Optional<ResumedDataTemplate> foundDais = getParentAdapter().getDAI(daiFilterSrcRef, true).stream().findFirst();
141139
if (foundDais.isEmpty()) {
142140
return Optional.of(SclReportItem.warning(getXPath() + "/DAI@name=\"" + daName + "\"/Val", "The DAI cannot be updated"));

0 commit comments

Comments
 (0)