Skip to content

Commit 82aace9

Browse files
authored
Merge pull request #518 from com-pas/feat/517-rsr-1249-controlblock-id-should-be-14-alphanumeric-characters
feat(#517): RSR-1249 ControlBlock id should be 14 alphanumeric characters
2 parents 588e406 + 62a0a84 commit 82aace9

File tree

6 files changed

+72
-62
lines changed

6 files changed

+72
-62
lines changed

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

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@
2121
import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
2222
import org.lfenergy.compas.sct.commons.util.Utils;
2323

24-
import java.util.*;
24+
import java.util.List;
25+
import java.util.Locale;
26+
import java.util.Optional;
27+
import java.util.Set;
2528
import java.util.function.Predicate;
2629

2730
import static org.lfenergy.compas.sct.commons.util.CommonConstants.*;
@@ -39,9 +42,9 @@
3942
public class InputsAdapter extends SclElementAdapter<LN0Adapter, TInputs> {
4043

4144
private static final String MESSAGE_SOURCE_LDEVICE_NOT_FOUND = "The signal ExtRef ExtRefldinst does not match any " +
42-
"LDevice with same inst attribute in source IED %s";
45+
"LDevice with same inst attribute in source IED %s";
4346
private static final String MESSAGE_SOURCE_LN_NOT_FOUND = "The signal ExtRef lninst, doName or daName does not match any " +
44-
"source in LDevice %s";
47+
"source in LDevice %s";
4548
private static final String MESSAGE_SERVICE_TYPE_MISSING = "The signal ExtRef is missing ServiceType attribute";
4649
private static final String MESSAGE_INVALID_SERVICE_TYPE = "The signal ExtRef ServiceType attribute is unexpected : %s";
4750
private static final String MESSAGE_IED_MISSING_COMPAS_BAY_UUID = "IED is missing Private/compas:Bay@UUID attribute";
@@ -118,10 +121,10 @@ private boolean isExternalBound(TExtRef tExtRef) {
118121

119122
private boolean areBindingAttributesPresent(TExtRef tExtRef) {
120123
return StringUtils.isNotBlank(tExtRef.getIedName())
121-
&& StringUtils.isNotBlank(tExtRef.getDesc())
122-
&& StringUtils.isNotBlank(tExtRef.getLdInst())
123-
&& tExtRef.getLnClass().stream().findFirst().filter(StringUtils::isNotBlank).isPresent()
124-
&& StringUtils.isNotBlank(tExtRef.getDoName());
124+
&& StringUtils.isNotBlank(tExtRef.getDesc())
125+
&& StringUtils.isNotBlank(tExtRef.getLdInst())
126+
&& tExtRef.getLnClass().stream().findFirst().filter(StringUtils::isNotBlank).isPresent()
127+
&& StringUtils.isNotBlank(tExtRef.getDoName());
125128
}
126129

127130
private Optional<SclReportItem> updateSourceDataSetsAndControlBlocks(TExtRef extRef, String targetBayUuid, List<TFCDA> allowedFcdas) {
@@ -186,8 +189,8 @@ private void createDataSetWithFCDA(TExtRef extRef, LDeviceAdapter sourceLDevice,
186189

187190
private void createControlBlockWithTarget(TExtRef extRef, LDeviceAdapter sourceLDevice, DataAttributeRef sourceDa, String cbName, String datSet) {
188191
String sourceLDName = sourceLDevice.getLdName();
189-
String cbId = getParentAdapter().generateControlBlockId(sourceLDName, cbName);
190-
ControlBlockAdapter controlBlockAdapter = sourceLDevice.getLN0Adapter().createControlBlockIfNotExists(cbName, cbId, datSet, ControlBlockEnum.from(extRef.getServiceType()));
192+
String idSeed = "%s/%s.%s".formatted(StringUtils.trimToEmpty(sourceLDName), TLLN0Enum.LLN_0.value(), cbName);
193+
ControlBlockAdapter controlBlockAdapter = sourceLDevice.getLN0Adapter().createControlBlockIfNotExists(cbName, idSeed, datSet, ControlBlockEnum.from(extRef.getServiceType()));
191194
if (sourceDa.getFc() != TFCEnum.ST && controlBlockAdapter.getCurrentElem() instanceof TReportControl tReportControl) {
192195
tReportControl.getTrgOps().setDchg(false);
193196
tReportControl.getTrgOps().setQchg(false);
@@ -206,13 +209,13 @@ private void setExtRefSrcAttributes(TExtRef extRef, String cbName) {
206209

207210
private static String generateDataSetSuffix(TExtRef extRef, DataAttributeRef sourceDa, boolean isBayInternal) {
208211
return extRef.getLdInst().toUpperCase(Locale.ENGLISH) + ATTRIBUTE_VALUE_SEPARATOR
209-
+ switch (extRef.getServiceType()) {
212+
+ switch (extRef.getServiceType()) {
210213
case GOOSE -> "G" + ((sourceDa.getFc() == TFCEnum.ST) ? "S" : "M");
211214
case SMV -> "SV";
212215
case REPORT -> (sourceDa.getFc() == TFCEnum.ST) ? "DQC" : "CYC";
213216
case POLL -> throw new IllegalArgumentException(MESSAGE_POLL_SERVICE_TYPE_NOT_SUPPORTED);
214217
}
215-
+ (isBayInternal ? "I" : "E");
218+
+ (isBayInternal ? "I" : "E");
216219
}
217220

218221
private Optional<SclReportItem> removeFilteredSourceDas(TExtRef extRef, final Set<DataAttributeRef> sourceDas, List<TFCDA> allowedFcdas) {
@@ -236,9 +239,9 @@ private boolean isFcdaAllowed(DataAttributeRef dataAttributeRef, List<TFCDA> all
236239
throw new IllegalArgumentException("parameters must not be blank");
237240
}
238241
return allowedFcdas.stream().anyMatch(tfcda -> tfcda.getDoName().equals(doName)
239-
&& tfcda.getDaName().equals(daName)
240-
&& tfcda.getLnClass().equals(lnClass)
241-
&& tfcda.getFc().value().equals(fc));
242+
&& tfcda.getDaName().equals(daName)
243+
&& tfcda.getLnClass().equals(lnClass)
244+
&& tfcda.getFc().value().equals(fc));
242245
}
243246

244247
private Optional<SclReportItem> removeFilterSourceDaForReport(TExtRef extRef, Set<DataAttributeRef> sourceDas) {
@@ -264,7 +267,7 @@ private SclRootAdapter getSclRootAdapter() {
264267
* @return list ExtRefs without duplication
265268
*/
266269
public List<TExtRef> filterDuplicatedExtRefs() {
267-
return new ExtRefService().filterDuplicatedExtRefs(currentElem.getExtRef());
270+
return new ExtRefService().filterDuplicatedExtRefs(currentElem.getExtRef());
268271
}
269272

270273
}

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

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.lfenergy.compas.sct.commons.scl.ied.*;
2121
import org.lfenergy.compas.sct.commons.scl.ldevice.LDeviceAdapter;
2222
import org.lfenergy.compas.sct.commons.util.ControlBlockEnum;
23+
import org.lfenergy.compas.sct.commons.util.Utils;
2324

2425
import java.util.*;
2526
import java.util.stream.Collectors;
@@ -53,9 +54,6 @@
5354
* <li>{@link AbstractLNAdapter#getDAI <em>Returns the value of the <b>DataAttributeRef </b> containment reference By filter</em>}</li>
5455
* <li>{@link AbstractLNAdapter#getDAIValues(DataAttributeRef) <em>Returns <b>DAI (sGroup, value) </b> containment reference list By <b>DataAttributeRef </b> filter</em>}</li>
5556
*
56-
* <li>{@link AbstractLNAdapter#getDataSetByName(String) <em>Returns the value of the <b>TDataSet </b>object reference By the value of the <b>name </b>attribute </em>}</li>
57-
*
58-
* <li>{@link AbstractLNAdapter#getControlBlocks(List, TServiceType) <em>Returns the value of the <b>ControlBlock </b>containment reference list that match <b>datSet </b> value of given <b>TDataSet</b> </em>}</li>
5957
* <li>{@link AbstractLNAdapter#addPrivate <em>Add <b>TPrivate </b>under this object</em>}</li>
6058
* <li>{@link AbstractLNAdapter#removeAllControlBlocksAndDatasets() <em>Remove all <b>ControlBlock</b></em>}</li>
6159
* </ul>
@@ -81,7 +79,7 @@ public abstract class AbstractLNAdapter<T extends TAnyLN> extends SclElementAdap
8179

8280
public static final DoTypeName MOD_DO_TYPE_NAME = new DoTypeName(MOD_DO_NAME);
8381
public static final DaTypeName STVAL_DA_TYPE_NAME = new DaTypeName(STVAL_DA_NAME);
84-
private static final String DAI_MOD_STVAL_VALUE_ON = "on";
82+
private static final int CONTROLBLOCK_ID_MAX_LENGTH = 14;
8583

8684
/**
8785
* Constructor
@@ -264,7 +262,7 @@ public void updateExtRefBinders(ExtRefInfo extRefInfo) throws ScdException {
264262
log.warn("More the one desc for ExtRef [pDO({}),intAddr({})] in {}{}/{}",
265263
signalInfo.getPDO(), signalInfo.getIntAddr(), iedName, ldInst, getLNClass());
266264
}
267-
TExtRef extRef = tExtRefs.get(0);
265+
TExtRef extRef = tExtRefs.getFirst();
268266
// update ExtRef with binding info
269267
updateExtRefBindingInfo(extRef, extRefInfo);
270268
}
@@ -555,7 +553,7 @@ public TExtRef extractExtRefFromExtRefInfo(@NonNull ExtRefInfo extRefInfo) {
555553
log.error(msg);
556554
throw new ScdException(msg);
557555
}
558-
TExtRef extRef = extRefs.get(0);// to be refined : what's the criteria for ExtRef's uniqueness
556+
TExtRef extRef = extRefs.getFirst();// to be refined : what's the criteria for ExtRef's uniqueness
559557
ExtRefBindingInfo bindingInfo = extRefInfo.getBindingInfo();
560558
if (!bindingInfo.isWrappedIn(extRef)) {
561559
String msg = "No relation between binding info and the matched TExtRef";
@@ -952,46 +950,29 @@ public Stream<ControlBlockAdapter> streamControlBlocks(ControlBlockEnum controlB
952950

953951
/**
954952
* Create ControlBlock if there is no ControlBlock of the same type (controlBlockEnum) and with the same cbName in this LN/LN0.
955-
* When the controlBlock already exists, the id and datSet attributes are NOT updated with the given values.
953+
* When the controlBlock already exists, the idSeed and datSet attributes are NOT updated with the given values.
956954
*
957955
* @param cbName cbName of the controlBlock to look for. When not found, the cbName of the controlBlock to create.
958-
* @param id When controlBlock not found, the id of the controlBlock to create
956+
* @param idSeed When controlBlock not found, the text that will be used to compute the controlBlock Id (appId, smvId, rptId)
959957
* @param datSet the datSet of the controlBlock to create
960958
* @param controlBlockEnum the type of ControlBlock to create
961959
* @return existing controlBlock if a controlBlock of the same type and with same cbName was found in this LN/LN0, otherwise the created ControlBlock.
962960
* The returned ControlBlock is always a child of this LN/LN0.
963961
*/
964-
public ControlBlockAdapter createControlBlockIfNotExists(String cbName, String id, String datSet, ControlBlockEnum controlBlockEnum) {
962+
public ControlBlockAdapter createControlBlockIfNotExists(String cbName, String idSeed, String datSet, ControlBlockEnum controlBlockEnum) {
963+
String computedId = Utils.sha256(idSeed).substring(0, CONTROLBLOCK_ID_MAX_LENGTH);
965964
return findControlBlock(cbName, controlBlockEnum)
966965
.orElseGet(() -> addControlBlock(
967966
switch (controlBlockEnum) {
968-
case GSE -> new GooseControlBlock(cbName, id, datSet);
969-
case SAMPLED_VALUE -> new SMVControlBlock(cbName, id, datSet);
970-
case REPORT -> new ReportControlBlock(cbName, id, datSet);
967+
case GSE -> new GooseControlBlock(cbName, computedId, datSet);
968+
case SAMPLED_VALUE -> new SMVControlBlock(cbName, computedId, datSet);
969+
case REPORT -> new ReportControlBlock(cbName, computedId, datSet);
971970
default -> throw new IllegalArgumentException("Unsupported ControlBlock Type " + controlBlockEnum);
972971
}
973972
)
974973
);
975974
}
976975

977-
/**
978-
* Generate a ControlBlock Id based on the current LN and the given ldName (ldName can be different from the parent LD.name)
979-
*
980-
* @param ldName LD name to use for generating the id
981-
* @param cbName name of the ControlBlock
982-
* @return "ldName/LnPrefixLnClassLnInst.cbName". Blank values are omitted (e.g "IEDNAME1LD1/LLN0.CBNAME1")
983-
*/
984-
public String generateControlBlockId(String ldName, String cbName) {
985-
String s = getLNInst();
986-
String s1 = getPrefix();
987-
return StringUtils.trimToEmpty(ldName)
988-
+ "/"
989-
+ StringUtils.trimToEmpty(s1)
990-
+ StringUtils.defaultString(getLNClass(), "")
991-
+ StringUtils.trimToEmpty(s)
992-
+ "."
993-
+ StringUtils.trimToEmpty(cbName);
994-
}
995976

996977
/**
997978
* Finds all FCDAs in DataSet of Control Block feeding ExtRef

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/Utils.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@
44

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

7-
import jakarta.xml.bind.*;
7+
import jakarta.xml.bind.JAXBContext;
8+
import jakarta.xml.bind.JAXBElement;
9+
import jakarta.xml.bind.JAXBException;
10+
import jakarta.xml.bind.Unmarshaller;
811
import jakarta.xml.bind.util.JAXBSource;
912
import org.apache.commons.lang3.StringUtils;
1013
import org.lfenergy.compas.scl2007b4.model.*;
1114
import org.lfenergy.compas.sct.commons.exception.ScdException;
1215

1316
import javax.xml.namespace.QName;
17+
import java.nio.charset.StandardCharsets;
18+
import java.security.MessageDigest;
19+
import java.security.NoSuchAlgorithmException;
1420
import java.util.*;
1521
import java.util.function.Function;
1622
import java.util.function.Predicate;
@@ -26,6 +32,7 @@ public final class Utils {
2632
private static final int S1_GREATER_THAN_S2 = 1;
2733
private static final long MAC_ADDRESS_MAX_VALUE = 0xFFFFFFFFFFFFL;
2834
private static final Pattern MAC_ADDRESS_PATTERN = Pattern.compile("[0-9A-F]{2}([-:][0-9A-F]{2}){5}", Pattern.CASE_INSENSITIVE);
35+
private static final HexFormat HEX_FORMAT = HexFormat.of().withUpperCase();
2936

3037
private static JAXBContext jaxbContext = null;
3138

@@ -448,4 +455,14 @@ private static void updateUnNaming(TUnNaming unNaming, TUnNaming unNamingSource)
448455
}
449456
}
450457

458+
public static String sha256(String text) {
459+
MessageDigest messageDigest;
460+
try {
461+
messageDigest = MessageDigest.getInstance("SHA-256");
462+
} catch (NoSuchAlgorithmException e) {
463+
throw new UnsupportedOperationException(e);
464+
}
465+
byte[] digest = messageDigest.digest(text.getBytes(StandardCharsets.UTF_8));
466+
return HEX_FORMAT.formatHex(digest);
467+
}
451468
}

sct-commons/src/test/java/org/lfenergy/compas/sct/commons/ControlBlockEditorServiceTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,12 @@ void createDataSetAndControlBlocks_should_create_ControlBlocks() {
167167
assertThat(sclReportItems).isEmpty();
168168

169169
// Check ControlBlock names, id and datSet
170-
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_CYCI", "DS_LD_INST21_CYCI", "IED_NAME2LD_INST21/LLN0.CB_LD_INST21_CYCI", REPORT);
171-
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_DQCI", "DS_LD_INST21_DQCI", "IED_NAME2LD_INST21/LLN0.CB_LD_INST21_DQCI", REPORT);
172-
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GMI", "DS_LD_INST21_GMI", "IED_NAME2LD_INST21/LLN0.CB_LD_INST21_GMI", GSE);
173-
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_SVI", "DS_LD_INST21_SVI", "IED_NAME2LD_INST21/LLN0.CB_LD_INST21_SVI", SAMPLED_VALUE);
174-
assertControlBlockExists(scd, "IED_NAME3", "LD_INST31", "CB_LD_INST31_GSE", "DS_LD_INST31_GSE", "IED_NAME3LD_INST31/LLN0.CB_LD_INST31_GSE", GSE);
175-
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", "DS_LD_INST21_GSI", "IED_NAME2LD_INST21/LLN0.CB_LD_INST21_GSI", GSE);
170+
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_CYCI", "DS_LD_INST21_CYCI", "F10424E5461799", REPORT);
171+
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_DQCI", "DS_LD_INST21_DQCI", "BFF9CA40F6C426", REPORT);
172+
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GMI", "DS_LD_INST21_GMI", "D2927E49982E88", GSE);
173+
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_SVI", "DS_LD_INST21_SVI", "74C94A5A8C06B8", SAMPLED_VALUE);
174+
assertControlBlockExists(scd, "IED_NAME3", "LD_INST31", "CB_LD_INST31_GSE", "DS_LD_INST31_GSE", "08FF9C43689DD3", GSE);
175+
assertControlBlockExists(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_GSI", "DS_LD_INST21_GSI", "2FB2C6E1C5955E", GSE);
176176

177177
// Check one ControlBlock content (ReportControl with sourceDA.fc=MX)
178178
TReportControl tReportControl = findControlBlock(scd, "IED_NAME2", "LD_INST21", "CB_LD_INST21_CYCI", TReportControl.class);

0 commit comments

Comments
 (0)