Skip to content

Commit 0062f76

Browse files
authored
Merge pull request #479 from com-pas/478-mac-address-should-restart-from-beginning-instead-of-fail
fix(478): MAC address should restart from beginning instead of fail
2 parents b1b37b9 + 039a90d commit 0062f76

File tree

5 files changed

+154
-34
lines changed

5 files changed

+154
-34
lines changed

sct-app/src/test/java/org.lfenergy.compas.sct.app/SclAutomationServiceIntegrationTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class SclAutomationServiceIntegrationTest {
3232
private SclAutomationService sclAutomationService ;
3333
private static final SclEditor sclEditor = new SclService() ;
3434
private static final SubstationEditor substationEditor = new SubstationService(new VoltageLevelService()) ;
35-
private static final ControlBlockEditor controlBlockEditor = new ControlBlockEditorService(new ControlService(), new LdeviceService(new LnService()));
35+
private static final ControlBlockEditor controlBlockEditor = new ControlBlockEditorService(new ControlService(), new LdeviceService(new LnService()), new ConnectedAPService(), new SubNetworkService());
3636

3737
private HeaderDTO headerDTO;
3838

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/ControlBlockEditorService.java

+15-22
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import java.math.BigInteger;
2626
import java.util.*;
27+
import java.util.concurrent.atomic.AtomicInteger;
2728
import java.util.stream.Collectors;
2829
import java.util.stream.LongStream;
2930
import java.util.stream.Stream;
@@ -48,6 +49,8 @@ public class ControlBlockEditorService implements ControlBlockEditor {
4849
private static final int HEXADECIMAL_BASE = 16;
4950
private final ControlService controlService;
5051
private final LdeviceService ldeviceService;
52+
private final ConnectedAPService connectedAPService;
53+
private final SubNetworkService subNetworkService;
5154

5255
@Override
5356
public List<SclReportItem> analyzeDataGroups(SCL scd) {
@@ -114,8 +117,9 @@ private Stream<SclReportItem> configureNetworkForControlBlocks(SCL scl, Map<CbKe
114117
}
115118
List<Long> appIdToReuse = appIdsAndMacsToReuse.values().stream().map(AppIdAndMac::appId).toList();
116119
List<Long> macToReuse = appIdsAndMacsToReuse.values().stream().map(AppIdAndMac::mac).toList();
117-
PrimitiveIterator.OfLong appIdIterator = cbComSettings.appIds.filter(appId -> !appIdToReuse.contains(appId)).iterator();
118-
PrimitiveIterator.OfLong macIterator = cbComSettings.macAddresses.filter(mac -> !macToReuse.contains(mac)).iterator();
120+
PrimitiveIterator.OfLong appIdIterator = cbComSettings.appIds().filter(appId -> !appIdToReuse.contains(appId)).iterator();
121+
List<Long> macAddresseList = cbComSettings.macAddresses().filter(mac -> !macToReuse.contains(mac)).boxed().toList();
122+
AtomicInteger macAddresseIndex = new AtomicInteger(0);
119123
return scl.getIED().stream()
120124
.flatMap(tied ->
121125
tied.getAccessPoint()
@@ -137,7 +141,7 @@ private Stream<SclReportItem> configureNetworkForControlBlocks(SCL scl, Map<CbKe
137141
return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because: No controlBlock communication settings found with these " + criteriaOrError.criteria);
138142
}
139143
AppIdAndMac reuseAppIdAndMac = appIdsAndMacsToReuse.get(new CbKey(iedApLd.ied.getName(), iedApLd.lDevice.getInst(), tControl.getName()));
140-
return configureControlBlockNetwork(scl.getCommunication(), settings, appIdIterator, macIterator, tControl, iedApLd, reuseAppIdAndMac);
144+
return configureControlBlockNetwork(scl.getCommunication(), settings, appIdIterator, macAddresseList, macAddresseIndex, tControl, iedApLd, reuseAppIdAndMac);
141145
})
142146
.flatMap(Optional::stream)
143147
);
@@ -166,12 +170,13 @@ private CbComSettings parseCbCom(CBCom cbCom, TCBType tcbType) {
166170
return new CbComSettings(appIds, macAddresses, settingsByCriteria);
167171
}
168172

169-
private Optional<SclReportItem> configureControlBlockNetwork(TCommunication tCommunication, Settings settings, PrimitiveIterator.OfLong appIdIterator, PrimitiveIterator.OfLong macAddressIterator, TControl tControl, IedApLd iedApLd, AppIdAndMac reuseAppIdAndMac) {
170-
Optional<TConnectedAP> optConApAdapter = findConnectedAp(tCommunication, iedApLd.ied.getName(), iedApLd.apName);
171-
if (optConApAdapter.isEmpty()) {
173+
private Optional<SclReportItem> configureControlBlockNetwork(TCommunication tCommunication, Settings settings, PrimitiveIterator.OfLong appIdIterator, List<Long> macAddressList, AtomicInteger macAddresseIndex, TControl tControl, IedApLd iedApLd, AppIdAndMac reuseAppIdAndMac) {
174+
Optional<TConnectedAP> optionalTConnectedAP = subNetworkService.getSubNetworks(tCommunication)
175+
.flatMap(tSubNetwork -> connectedAPService.getFilteredConnectedAP(tSubNetwork, connectedAP -> iedApLd.ied.getName().equals(connectedAP.getIedName()) && iedApLd.apName.equals(connectedAP.getApName())))
176+
.findFirst();
177+
if (optionalTConnectedAP.isEmpty()) {
172178
return newError(iedApLd, tControl, "Cannot configure communication for ControlBlock because no ConnectedAP found for AccessPoint");
173179
}
174-
TConnectedAP tConnectedAP = optConApAdapter.get();
175180
if (settings.vlanId() == null) {
176181
return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings");
177182
}
@@ -182,10 +187,8 @@ private Optional<SclReportItem> configureControlBlockNetwork(TCommunication tCom
182187
if (!appIdIterator.hasNext()) {
183188
return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because range of appId is exhausted");
184189
}
185-
if (!macAddressIterator.hasNext()) {
186-
return newError(iedApLd, tControl, "Cannot configure communication for this ControlBlock because range of MAC Address is exhausted");
187-
}
188-
appIdAndMac = new AppIdAndMac(appIdIterator.nextLong(), macAddressIterator.nextLong());
190+
// For the MAC adress assignation, we restart from the begining if we reach the end of the range
191+
appIdAndMac = new AppIdAndMac(appIdIterator.nextLong(), macAddressList.get(macAddresseIndex.getAndIncrement() % macAddressList.size()));
189192
}
190193

191194
List<TP> listOfPs = new ArrayList<>();
@@ -196,6 +199,7 @@ private Optional<SclReportItem> configureControlBlockNetwork(TCommunication tCom
196199
listOfPs.add(newP(VLAN_PRIORITY_P_TYPE, String.valueOf(settings.vlanPriority())));
197200
}
198201

202+
TConnectedAP tConnectedAP = optionalTConnectedAP.orElseThrow();
199203
switch (tControl) {
200204
case TGSEControl ignored -> updateGseOrCreateIfNotExists(tConnectedAP, iedApLd.lDevice().getInst(), tControl.getName(), listOfPs, SclConstructorHelper.newDurationInMilliSec(settings.minTime), SclConstructorHelper.newDurationInMilliSec(settings.maxTime));
201205
case TSampledValueControl ignored -> updateSmvOrCreateIfNotExists(tConnectedAP, iedApLd.lDevice().getInst(), tControl.getName(), listOfPs);
@@ -224,17 +228,6 @@ private Map<CbKey, AppIdAndMac> computeAppIdsAndMacToReuse(SCL scd, List<TSubNet
224228
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
225229
}
226230

227-
private Optional<TConnectedAP> findConnectedAp(TCommunication tCommunication, String iedName, String apName) {
228-
if (tCommunication == null || !tCommunication.isSetSubNetwork()) {
229-
return Optional.empty();
230-
}
231-
return tCommunication.getSubNetwork().stream()
232-
.filter(TSubNetwork::isSetConnectedAP)
233-
.flatMap(tSubNetwork -> tSubNetwork.getConnectedAP().stream())
234-
.filter(tConnectedAP -> iedName.equals(tConnectedAP.getIedName()) && apName.equals(tConnectedAP.getApName()))
235-
.findFirst();
236-
}
237-
238231
private void updateGseOrCreateIfNotExists(TConnectedAP tConnectedAP, String ldInst, String cbName, List<TP> listOfP, TDurationInMilliSec minTime, TDurationInMilliSec maxTime) {
239232
Optional<TGSE> optGse = tConnectedAP.isSetGSE() ?
240233
tConnectedAP.getGSE().stream().filter(gse1 -> Objects.equals(ldInst, gse1.getLdInst()) && Objects.equals(cbName, gse1.getCbName())).findFirst()

sct-commons/src/main/java/org/lfenergy/compas/sct/commons/SubNetworkService.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44

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

7-
import org.lfenergy.compas.scl2007b4.model.TSubNetwork;
87
import org.lfenergy.compas.scl2007b4.model.SCL;
8+
import org.lfenergy.compas.scl2007b4.model.TCommunication;
9+
import org.lfenergy.compas.scl2007b4.model.TSubNetwork;
910

1011
import java.util.Optional;
1112
import java.util.function.Predicate;
@@ -23,6 +24,13 @@ public Stream<TSubNetwork> getSubNetworks(SCL scl) {
2324
return scl.getCommunication().getSubNetwork().stream();
2425
}
2526

27+
public Stream<TSubNetwork> getSubNetworks(TCommunication tCommunication) {
28+
if (!tCommunication.isSetSubNetwork()) {
29+
return Stream.empty();
30+
}
31+
return tCommunication.getSubNetwork().stream();
32+
}
33+
2634
public Stream<TSubNetwork> getFilteredSubNetworks(SCL tlNodeType, Predicate<TSubNetwork> tdoPredicate) {
2735
return getSubNetworks(tlNodeType).filter(tdoPredicate);
2836
}

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

+58-10
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class ControlBlockEditorServiceTest {
5050

5151
@BeforeEach
5252
void init() {
53-
controlBlockEditorService = new ControlBlockEditorService(new ControlService(), new LdeviceService(new LnService()));
53+
controlBlockEditorService = new ControlBlockEditorService(new ControlService(), new LdeviceService(new LnService()), new ConnectedAPService(), new SubNetworkService());
5454
}
5555

5656
@Test
@@ -347,14 +347,67 @@ void configureNetworkForAllControlBlocks_should_create_GSE_with_incremental_appi
347347
cbCom.getAppIdRanges().getAppIdRange().getFirst().setEnd("000B");
348348
cbCom.getMacRanges().getMacRange().getFirst().setStart("01-02-03-04-00-FF");
349349
cbCom.getMacRanges().getMacRange().getFirst().setEnd("01-02-03-04-01-01");
350+
351+
// When
352+
List<SclReportItem> sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom);
353+
354+
// Then
355+
assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue();
356+
assertThat(scd.getCommunication().getSubNetwork())
357+
.flatExtracting(TSubNetwork::getConnectedAP)
358+
.flatExtracting(TConnectedAP::getGSE)
359+
.extracting(TControlBlock::getAddress)
360+
.flatExtracting(TAddress::getP)
361+
.extracting(TP::getType, TP::getValue)
362+
.containsExactly(
363+
Tuple.tuple("APPID", "0009"),
364+
Tuple.tuple("MAC-Address", "01-02-03-04-00-FF"),
365+
Tuple.tuple("VLAN-ID", "12D"),
366+
Tuple.tuple("VLAN-PRIORITY", "1"),
367+
Tuple.tuple("APPID", "000A"),
368+
Tuple.tuple("MAC-Address", "01-02-03-04-01-00"),
369+
Tuple.tuple("VLAN-ID", "12D"),
370+
Tuple.tuple("VLAN-PRIORITY", "1"),
371+
Tuple.tuple("APPID", "000B"),
372+
Tuple.tuple("MAC-Address", "01-02-03-04-01-01"),
373+
Tuple.tuple("VLAN-ID", "12E"),
374+
Tuple.tuple("VLAN-PRIORITY", "2")
375+
);
376+
}
377+
378+
@Test
379+
void configureNetworkForAllControlBlocks_should_restart_Mac_adress() {
380+
// Given
381+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-create-dataset-and-controlblocks/scd_create_controlblock_network_configuration.xml");
382+
CBCom cbCom = createCbCom();
383+
cbCom.getMacRanges().getMacRange().getFirst().setStart("01-0C-CD-01-00-00");
384+
cbCom.getMacRanges().getMacRange().getFirst().setEnd("01-0C-CD-01-00-01");
385+
350386
// When
351387
List<SclReportItem> sclReportItems = controlBlockEditorService.configureNetworkForAllControlBlocks(scd, cbCom);
388+
352389
// Then
353390
assertThat(sclReportItems.stream().noneMatch(SclReportItem::isError)).isTrue();
354-
assertThat(streamAllConnectedApGseP(scd, "APPID"))
355-
.containsExactlyInAnyOrder("0009", "000A", "000B");
356-
assertThat(streamAllConnectedApGseP(scd, "MAC-Address"))
357-
.containsExactlyInAnyOrder("01-02-03-04-00-FF", "01-02-03-04-01-00", "01-02-03-04-01-01");
391+
assertThat(scd.getCommunication().getSubNetwork())
392+
.flatExtracting(TSubNetwork::getConnectedAP)
393+
.flatExtracting(TConnectedAP::getGSE)
394+
.extracting(TControlBlock::getAddress)
395+
.flatExtracting(TAddress::getP)
396+
.extracting(TP::getType, TP::getValue)
397+
.containsExactly(
398+
Tuple.tuple("APPID", "0000"),
399+
Tuple.tuple("MAC-Address", "01-0C-CD-01-00-00"),
400+
Tuple.tuple("VLAN-ID", "12D"),
401+
Tuple.tuple("VLAN-PRIORITY", "1"),
402+
Tuple.tuple("APPID", "0001"),
403+
Tuple.tuple("MAC-Address", "01-0C-CD-01-00-01"),
404+
Tuple.tuple("VLAN-ID", "12D"),
405+
Tuple.tuple("VLAN-PRIORITY", "1"),
406+
Tuple.tuple("APPID", "0002"),
407+
Tuple.tuple("MAC-Address", "01-0C-CD-01-00-00"),
408+
Tuple.tuple("VLAN-ID", "12E"),
409+
Tuple.tuple("VLAN-PRIORITY", "2")
410+
);
358411
}
359412

360413
@ParameterizedTest
@@ -378,18 +431,13 @@ public static Stream<Arguments> provideConfigureNetworkForAllControlBlocksErrors
378431
CBCom cbComWithNotEnoughAppId = createCbCom();
379432
cbComWithNotEnoughAppId.getAppIdRanges().getAppIdRange().getFirst().setStart("0000");
380433
cbComWithNotEnoughAppId.getAppIdRanges().getAppIdRange().getFirst().setEnd("00001");
381-
CBCom cbComWithNotEnoughMacAddress = createCbCom();
382-
cbComWithNotEnoughMacAddress.getMacRanges().getMacRange().getFirst().setStart("01-0C-CD-01-00-00");
383-
cbComWithNotEnoughMacAddress.getMacRanges().getMacRange().getFirst().setEnd("01-0C-CD-01-00-01");
384434

385435
return Stream.of(
386436
Arguments.of(cbComWithNoVlan, "Cannot configure communication for this ControlBlock because: No controlBlock communication settings found with these Criteria[cbType=GOOSE, systemVersionWithoutV=01.00.009.001, iedType=BCU, iedRedundancy=A, iedSystemVersionInstance=1, bayIntOrExt=BAY_INTERNAL]",
387437
"/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint[@name=\"AP_NAME\"]/Server/LDevice[@inst=\"LD_INST21\"]/LN0/GSEControl[@name=\"CB_LD_INST21_GMI\"]"),
388438
Arguments.of(cbComWithMissingVlanId, "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings",
389439
"/SCL/IED[@name=\"IED_NAME2\"]/AccessPoint[@name=\"AP_NAME\"]/Server/LDevice[@inst=\"LD_INST21\"]/LN0/GSEControl[@name=\"CB_LD_INST21_GMI\"]"),
390440
Arguments.of(cbComWithNotEnoughAppId, "Cannot configure communication for this ControlBlock because range of appId is exhausted",
391-
"/SCL/IED[@name=\"IED_NAME3\"]/AccessPoint[@name=\"AP_NAME\"]/Server/LDevice[@inst=\"LD_INST31\"]/LN0/GSEControl[@name=\"CB_LD_INST31_GSE\"]"),
392-
Arguments.of(cbComWithNotEnoughMacAddress, "Cannot configure communication for this ControlBlock because range of MAC Address is exhausted",
393441
"/SCL/IED[@name=\"IED_NAME3\"]/AccessPoint[@name=\"AP_NAME\"]/Server/LDevice[@inst=\"LD_INST31\"]/LN0/GSEControl[@name=\"CB_LD_INST31_GSE\"]")
394442
);
395443
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-FileCopyrightText: 2022 RTE FRANCE
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package org.lfenergy.compas.sct.commons;
6+
7+
import org.junit.jupiter.api.Test;
8+
import org.lfenergy.compas.scl2007b4.model.SCL;
9+
import org.lfenergy.compas.scl2007b4.model.TCommunication;
10+
import org.lfenergy.compas.scl2007b4.model.TSubNetwork;
11+
import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller;
12+
13+
import java.util.List;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
import static org.assertj.core.api.Assertions.assertThatCode;
17+
18+
class SubNetworkServiceTest {
19+
20+
private final SubNetworkService subNetworkService = new SubNetworkService();
21+
22+
@Test
23+
void getSubNetworks_from_scd_should_succeed() {
24+
// Given
25+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml");
26+
// When
27+
List<TSubNetwork> tSubNetworks = subNetworkService.getSubNetworks(scd).toList();
28+
// Then
29+
assertThat(tSubNetworks).hasSize(2);
30+
}
31+
32+
@Test
33+
void getSubNetworks_from_communication_should_succeed() {
34+
// Given
35+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml");
36+
TCommunication communication = scd.getCommunication();
37+
38+
// When
39+
List<TSubNetwork> tSubNetworks = subNetworkService.getSubNetworks(communication).toList();
40+
// Then
41+
assertThat(tSubNetworks).hasSize(2);
42+
}
43+
44+
@Test
45+
void findSubNetwork_when_SubNetwork_Exist_should_succeed() {
46+
// Given
47+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml");
48+
// When Then
49+
assertThatCode(() -> subNetworkService.findSubNetwork(scd, tSubNetwork -> "RSPACE_PROCESS_NETWORK".equals(tSubNetwork.getName())).orElseThrow())
50+
.doesNotThrowAnyException();
51+
}
52+
53+
@Test
54+
void findSubNetwork_when_SubNetwork_not_Exist_should_return_empty() {
55+
// Given
56+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml");
57+
// When Then
58+
assertThat(subNetworkService.findSubNetwork(scd, tSubNetwork -> "unknown".equals(tSubNetwork.getName())))
59+
.isEmpty();
60+
}
61+
62+
@Test
63+
void getFilteredSubNetworks() {
64+
// Given
65+
SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml");
66+
// When
67+
List<TSubNetwork> tSubNetworks = subNetworkService.getFilteredSubNetworks(scd, tSubNetwork -> "RSPACE_PROCESS_NETWORK".equals(tSubNetwork.getName())).toList();
68+
// Then
69+
assertThat(tSubNetworks).hasSize(1);
70+
}
71+
}

0 commit comments

Comments
 (0)