12
12
import org .lfenergy .compas .sct .commons .exception .ScdException ;
13
13
import org .lfenergy .compas .sct .commons .model .cbcom .*;
14
14
import org .lfenergy .compas .sct .commons .model .da_comm .DACOMM ;
15
- import org .lfenergy .compas .sct .commons .model .da_comm .FCDAs ;
16
15
import org .lfenergy .compas .sct .commons .scl .ControlService ;
17
16
import org .lfenergy .compas .sct .commons .scl .SclRootAdapter ;
18
17
import org .lfenergy .compas .sct .commons .scl .ied .IEDAdapter ;
26
25
import java .math .BigInteger ;
27
26
import java .util .*;
28
27
import java .util .stream .Collectors ;
28
+ import java .util .stream .LongStream ;
29
29
import java .util .stream .Stream ;
30
30
31
+ import static java .util .AbstractMap .SimpleEntry ;
31
32
import static org .lfenergy .compas .sct .commons .util .SclConstructorHelper .newAddress ;
32
33
import static org .lfenergy .compas .sct .commons .util .SclConstructorHelper .newP ;
33
34
@@ -44,7 +45,9 @@ public class ControlBlockEditorService implements ControlBlockEditor {
44
45
private static final int APPID_LENGTH = 4 ;
45
46
private static final int VLAN_ID_LENGTH = 3 ;
46
47
private static final String MISSING_ATTRIBUTE_IN_GSE_SMV_CB_COM = "Error in Control Block communication setting file: vlan is missing attribute " ;
48
+ private static final int HEXADECIMAL_BASE = 16 ;
47
49
private final ControlService controlService ;
50
+ private final LdeviceService ldeviceService ;
48
51
49
52
@ Override
50
53
public List <SclReportItem > analyzeDataGroups (SCL scd ) {
@@ -88,19 +91,31 @@ public void removeAllControlBlocksAndDatasetsAndExtRefSrcBindings(final SCL scl)
88
91
89
92
@ Override
90
93
public List <SclReportItem > configureNetworkForAllControlBlocks (SCL scd , CBCom cbCom ) {
94
+ return configureNetworkForAllControlBlocks (scd , cbCom , Collections .emptyList ());
95
+ }
96
+
97
+ @ Override
98
+ public List <SclReportItem > configureNetworkForAllControlBlocks (SCL scd , CBCom cbCom , List <TSubNetwork > subnetworksToReuse ) {
99
+ Map <CbKey , AppIdAndMac > appIdsAndMacsToReuse = subnetworksToReuse != null && !subnetworksToReuse .isEmpty () ?
100
+ computeAppIdsAndMacToReuse (scd , subnetworksToReuse )
101
+ : Collections .emptyMap ();
91
102
return Stream .concat (
92
- configureNetworkForControlBlocks (scd , cbCom , TCBType .GOOSE ),
93
- configureNetworkForControlBlocks (scd , cbCom , TCBType .SV ))
103
+ configureNetworkForControlBlocks (scd , appIdsAndMacsToReuse , cbCom , TCBType .GOOSE ),
104
+ configureNetworkForControlBlocks (scd , appIdsAndMacsToReuse , cbCom , TCBType .SV ))
94
105
.toList ();
95
106
}
96
107
97
- private Stream <SclReportItem > configureNetworkForControlBlocks (SCL scl , CBCom cbCom , TCBType tcbType ) {
108
+ private Stream <SclReportItem > configureNetworkForControlBlocks (SCL scl , Map < CbKey , AppIdAndMac > appIdsAndMacsToReuse , CBCom cbCom , TCBType tcbType ) {
98
109
CbComSettings cbComSettings ;
99
110
try {
100
111
cbComSettings = parseCbCom (cbCom , tcbType );
101
112
} catch (ScdException ex ) {
102
113
return Stream .of (SclReportItem .error ("Control Block Communication setting files" , ex .getMessage ()));
103
114
}
115
+ List <Long > appIdToReuse = appIdsAndMacsToReuse .values ().stream ().map (AppIdAndMac ::appId ).toList ();
116
+ 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 ();
104
119
return scl .getIED ().stream ()
105
120
.flatMap (tied ->
106
121
tied .getAccessPoint ()
@@ -110,8 +125,8 @@ private Stream<SclReportItem> configureNetworkForControlBlocks(SCL scl, CBCom cb
110
125
.map (tlDevice -> new IedApLd (tied , tAccessPoint .getName (), tlDevice ))
111
126
)
112
127
)
113
- .flatMap (iedApLd -> Optional . ofNullable ( iedApLd .lDevice ().getLN0 ()). stream ( )
114
- .flatMap (ln0 -> controlService .getControls (ln0 , ControlBlockEnum .from (tcbType ).getControlBlockClass () ))
128
+ .filter (iedApLd -> iedApLd .lDevice ().isSetLN0 () )
129
+ .flatMap (iedApLd -> controlService .getControls (iedApLd . lDevice (). getLN0 () , ControlBlockEnum .from (tcbType ).getControlBlockClass ())
115
130
.map (tControl -> {
116
131
CriteriaOrError criteriaOrError = getCriteria (iedApLd .ied (), tcbType , tControl .getName ());
117
132
if (criteriaOrError .errorMessage != null ) {
@@ -121,7 +136,8 @@ private Stream<SclReportItem> configureNetworkForControlBlocks(SCL scl, CBCom cb
121
136
if (settings == null ) {
122
137
return newError (iedApLd , tControl , "Cannot configure communication for this ControlBlock because: No controlBlock communication settings found with these " + criteriaOrError .criteria );
123
138
}
124
- return configureControlBlockNetwork (scl .getCommunication (), settings , cbComSettings .appIdIterator , cbComSettings .macAddressIterator , tControl , iedApLd );
139
+ 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 );
125
141
})
126
142
.flatMap (Optional ::stream )
127
143
);
@@ -133,42 +149,48 @@ private CbComSettings parseCbCom(CBCom cbCom, TCBType tcbType) {
133
149
.filter (tRange -> tcbType .equals (tRange .getCBType ()))
134
150
.findFirst ()
135
151
.orElseThrow (() -> new ScdException ("Control Block Communication setting files does not contain AppIdRange for cbType " + tcbType .value ()));
136
- PrimitiveIterator . OfLong appIdIterator = Utils . sequence (Long .parseLong (appIdRange .getStart (), 16 ), Long .parseLong (appIdRange .getEnd (), 16 ) );
152
+ LongStream appIds = LongStream . range (Long .parseLong (appIdRange .getStart (), HEXADECIMAL_BASE ), Long .parseLong (appIdRange .getEnd (), HEXADECIMAL_BASE ) + 1 );
137
153
138
154
TRange macRange = Optional .ofNullable (cbCom .getMacRanges ()).map (MacRanges ::getMacRange ).stream ()
139
155
.flatMap (Collection ::stream )
140
156
.filter (tRange -> tcbType .equals (tRange .getCBType ()))
141
157
.findFirst ()
142
158
.orElseThrow (() -> new ScdException ("Control Block Communication setting files does not contain MacRange for cbType " + tcbType .value ()));
143
- Iterator < String > macAddressIterator = Utils .macAddressSequence (macRange .getStart (), macRange .getEnd ());
159
+ LongStream macAddresses = LongStream . range ( Utils .macAddressToLong (macRange .getStart ()), Utils . macAddressToLong ( macRange .getEnd ()) + 1 );
144
160
145
161
Map <Criteria , Settings > settingsByCriteria = Optional .ofNullable (cbCom .getVlans ()).map (Vlans ::getVlan ).stream ()
146
162
.flatMap (Collection ::stream )
147
163
.filter (vlan -> tcbType .equals (vlan .getCBType ()))
148
164
.collect (Collectors .toMap (this ::vlanToCriteria , this ::vlanToSetting ));
149
165
150
- return new CbComSettings (appIdIterator , macAddressIterator , settingsByCriteria );
166
+ return new CbComSettings (appIds , macAddresses , settingsByCriteria );
151
167
}
152
168
153
- private Optional <SclReportItem > configureControlBlockNetwork (TCommunication tCommunication , Settings settings , PrimitiveIterator .OfLong appIdIterator , Iterator <String > macAddressIterator , TControl tControl , IedApLd iedApLd ) {
154
- if (settings .vlanId () == null ) {
155
- return newError (iedApLd , tControl , "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings" );
156
- }
157
- if (!appIdIterator .hasNext ()) {
158
- return newError (iedApLd , tControl , "Cannot configure communication for this ControlBlock because range of appId is exhausted" );
159
- }
160
- if (!macAddressIterator .hasNext ()) {
161
- return newError (iedApLd , tControl , "Cannot configure communication for this ControlBlock because range of MAC Address is exhausted" );
162
- }
163
-
169
+ private Optional <SclReportItem > configureControlBlockNetwork (TCommunication tCommunication , Settings settings , PrimitiveIterator .OfLong appIdIterator , PrimitiveIterator .OfLong macAddressIterator , TControl tControl , IedApLd iedApLd , AppIdAndMac reuseAppIdAndMac ) {
164
170
Optional <TConnectedAP > optConApAdapter = findConnectedAp (tCommunication , iedApLd .ied .getName (), iedApLd .apName );
165
171
if (optConApAdapter .isEmpty ()) {
166
172
return newError (iedApLd , tControl , "Cannot configure communication for ControlBlock because no ConnectedAP found for AccessPoint" );
167
173
}
168
174
TConnectedAP tConnectedAP = optConApAdapter .get ();
175
+ if (settings .vlanId () == null ) {
176
+ return newError (iedApLd , tControl , "Cannot configure communication for this ControlBlock because no Vlan Id was provided in the settings" );
177
+ }
178
+ AppIdAndMac appIdAndMac ;
179
+ if (reuseAppIdAndMac != null ) {
180
+ appIdAndMac = reuseAppIdAndMac ;
181
+ } else {
182
+ if (!appIdIterator .hasNext ()) {
183
+ return newError (iedApLd , tControl , "Cannot configure communication for this ControlBlock because range of appId is exhausted" );
184
+ }
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 ());
189
+ }
190
+
169
191
List <TP > listOfPs = new ArrayList <>();
170
- listOfPs .add (newP (APPID_P_TYPE , Utils .toHex (appIdIterator . nextLong (), APPID_LENGTH )));
171
- listOfPs .add (newP (MAC_ADDRESS_P_TYPE , macAddressIterator . next ( )));
192
+ listOfPs .add (newP (APPID_P_TYPE , Utils .toHex (appIdAndMac . appId (), APPID_LENGTH )));
193
+ listOfPs .add (newP (MAC_ADDRESS_P_TYPE , Utils . longToMacAddress ( appIdAndMac . mac () )));
172
194
listOfPs .add (newP (VLAN_ID_P_TYPE , Utils .toHex (settings .vlanId (), VLAN_ID_LENGTH )));
173
195
if (settings .vlanPriority () != null ) {
174
196
listOfPs .add (newP (VLAN_PRIORITY_P_TYPE , String .valueOf (settings .vlanPriority ())));
@@ -182,6 +204,26 @@ private Optional<SclReportItem> configureControlBlockNetwork(TCommunication tCom
182
204
return Optional .empty ();
183
205
}
184
206
207
+ private Map <CbKey , AppIdAndMac > computeAppIdsAndMacToReuse (SCL scd , List <TSubNetwork > subnetworksToReuse ) {
208
+ List <CbKey > allControlBlocksInScd = scd .getIED ().stream ()
209
+ .flatMap (tIed -> ldeviceService .getLdevices (tIed )
210
+ .filter (TLDevice ::isSetLN0 )
211
+ .flatMap (tlDevice -> Stream .concat (tlDevice .getLN0 ().getGSEControl ().stream (), tlDevice .getLN0 ().getSampledValueControl ().stream ())
212
+ .map (tControlWithIEDName -> new CbKey (tIed .getName (), tlDevice .getInst (), tControlWithIEDName .getName ()))
213
+ ))
214
+ .toList ();
215
+ return subnetworksToReuse .stream ()
216
+ .flatMap (tSubNetwork -> tSubNetwork .getConnectedAP ().stream ())
217
+ .flatMap (tConnectedAP -> Stream .concat (tConnectedAP .getGSE ().stream (), tConnectedAP .getSMV ().stream ())
218
+ .flatMap (tControlBlock -> AppIdAndMac .from (tControlBlock .getAddress ())
219
+ .map (appIdAndMac -> new SimpleEntry <>(new CbKey (tConnectedAP .getIedName (), tControlBlock .getLdInst (), tControlBlock .getCbName ()), appIdAndMac ))
220
+ .stream ()
221
+ )
222
+ )
223
+ .filter (entry -> allControlBlocksInScd .contains (entry .getKey ()))
224
+ .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ));
225
+ }
226
+
185
227
private Optional <TConnectedAP > findConnectedAp (TCommunication tCommunication , String iedName , String apName ) {
186
228
if (tCommunication == null || !tCommunication .isSetSubNetwork ()) {
187
229
return Optional .empty ();
@@ -388,14 +430,42 @@ public record Settings(Integer vlanId, Byte vlanPriority, TDurationInMilliSec mi
388
430
/**
389
431
* All settings of CbCom in a useful format
390
432
*/
391
- record CbComSettings (PrimitiveIterator . OfLong appIdIterator , Iterator < String > macAddressIterator , Map <Criteria , Settings > settingsByCriteria ) {
433
+ record CbComSettings (LongStream appIds , LongStream macAddresses , Map <Criteria , Settings > settingsByCriteria ) {
392
434
}
393
435
394
436
record IedApLd (TIED ied , String apName , TLDevice lDevice ) {
395
437
String getXPath () {
396
438
return """
397
439
/SCL/IED[@name="%s"]/AccessPoint[@name="%s"]/Server/LDevice[@inst="%s"]""" .formatted (ied .getName (), apName , lDevice .getInst ());
398
440
}
441
+ }
399
442
443
+ /**
444
+ * ControlBlock key. Values that uniquely identify a ControlBlock in a SCD.
445
+ *
446
+ * @param iedName name of IED containing the ControlBlock
447
+ * @param LDInst inst of LD containing the ControlBlock
448
+ * @param cbName name of the ControlBlock
449
+ */
450
+ record CbKey (String iedName , String LDInst , String cbName ) {
451
+ }
452
+
453
+ /**
454
+ * Pair of APPID and MAC-Address
455
+ *
456
+ * @param appId APPID
457
+ * @param mac MAC-Address
458
+ */
459
+ record AppIdAndMac (long appId , long mac ) {
460
+ static Optional <AppIdAndMac > from (TAddress address ) {
461
+ if (address == null ) {
462
+ return Optional .empty ();
463
+ }
464
+ return Utils .extractFromP (APPID_P_TYPE , address .getP ())
465
+ .map (appId -> Integer .parseInt (appId , HEXADECIMAL_BASE ))
466
+ .flatMap (appId -> Utils .extractFromP (MAC_ADDRESS_P_TYPE , address .getP ())
467
+ .map (Utils ::macAddressToLong )
468
+ .map (macAddress -> new AppIdAndMac (appId , macAddress )));
469
+ }
400
470
}
401
471
}
0 commit comments