Add perltidy to pre-commit linters
[transportpce.git] / pce / src / main / java / org / opendaylight / transportpce / pce / graph / PostAlgoPathValidator.java
1 /*
2  * Copyright © 2017 AT&T, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.transportpce.pce.graph;
10
11 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12 import java.math.BigDecimal;
13 import java.math.RoundingMode;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.BitSet;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.LinkedHashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.concurrent.ExecutionException;
24 import java.util.stream.Collectors;
25 import org.jgrapht.GraphPath;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.transportpce.common.InstanceIdentifiers;
28 import org.opendaylight.transportpce.common.ResponseCodes;
29 import org.opendaylight.transportpce.common.StringConstants;
30 import org.opendaylight.transportpce.common.catalog.CatalogConstant;
31 import org.opendaylight.transportpce.common.catalog.CatalogConstant.CatalogNodeType;
32 import org.opendaylight.transportpce.common.catalog.CatalogUtils;
33 import org.opendaylight.transportpce.common.fixedflex.GridConstant;
34 import org.opendaylight.transportpce.common.fixedflex.GridUtils;
35 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
36 import org.opendaylight.transportpce.pce.constraints.PceConstraints;
37 import org.opendaylight.transportpce.pce.constraints.PceConstraints.ResourcePair;
38 import org.opendaylight.transportpce.pce.networkanalyzer.PceLink;
39 import org.opendaylight.transportpce.pce.networkanalyzer.PceNode;
40 import org.opendaylight.transportpce.pce.networkanalyzer.PceResult;
41 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.PceConstraintMode;
42 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.SpectrumAssignment;
43 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.SpectrumAssignmentBuilder;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev230526.TerminationPoint1;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev230526.OpenroadmLinkType;
46 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev230526.OpenroadmNodeType;
47 import org.opendaylight.yang.gen.v1.http.org.openroadm.otn.common.types.rev210924.OpucnTribSlotDef;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.LinkId;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.Uint16;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 public class PostAlgoPathValidator {
56     /* Logging. */
57     private static final Logger LOG = LoggerFactory.getLogger(PostAlgoPathValidator.class);
58
59     public static final Long CONST_OSNR = 1L;
60     public static final double SYS_MARGIN = 0;
61
62     private Double tpceCalculatedMargin = 0.0;
63     private final NetworkTransactionService networkTransactionService;
64     private final BitSet spectrumConstraint;
65
66     public PostAlgoPathValidator(NetworkTransactionService networkTransactionService, BitSet spectrumConstraint) {
67         this.networkTransactionService = networkTransactionService;
68         this.spectrumConstraint = spectrumConstraint;
69     }
70
71     @SuppressWarnings("fallthrough")
72     @SuppressFBWarnings(
73         value = "SF_SWITCH_FALLTHROUGH",
74         justification = "intentional fallthrough")
75     public PceResult checkPath(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
76             Map<LinkId, PceLink> allPceLinks, PceResult pceResult, PceConstraints pceHardConstraints,
77             String serviceType, PceConstraintMode mode) {
78         LOG.info("path = {}", path);
79         // check if the path is empty
80         if (path.getEdgeList().isEmpty()) {
81             pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
82             return pceResult;
83         }
84         int spectralWidthSlotNumber =
85             GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_100G);
86         SpectrumAssignment spectrumAssignment = null;
87         //variable to deal with 1GE (Nb=1) and 10GE (Nb=10) cases
88         switch (serviceType) {
89             case StringConstants.SERVICE_TYPE_OTUC2:
90             case StringConstants.SERVICE_TYPE_OTUC3:
91             case StringConstants.SERVICE_TYPE_OTUC4:
92             case StringConstants.SERVICE_TYPE_400GE:
93                 spectralWidthSlotNumber =
94                     GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_400G);
95             //fallthrough
96             case StringConstants.SERVICE_TYPE_100GE_T:
97             case StringConstants.SERVICE_TYPE_OTU4:
98                 spectrumAssignment = getSpectrumAssignment(path, allPceNodes, spectralWidthSlotNumber);
99                 pceResult.setServiceType(serviceType);
100                 if (spectrumAssignment.getBeginIndex().equals(Uint16.valueOf(0))
101                         && spectrumAssignment.getStopIndex().equals(Uint16.valueOf(0))) {
102                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
103                     pceResult.setLocalCause(PceResult.LocalCause.NO_PATH_EXISTS);
104                     return pceResult;
105                 }
106                 if (spectrumAssignment.getFlexGrid()) {
107                     LOG.debug("Spectrum assignment flexgrid mode");
108                     pceResult.setResultWavelength(GridConstant.IRRELEVANT_WAVELENGTH_NUMBER);
109                 } else {
110                     LOG.debug("Spectrum assignment fixedgrid mode");
111                     pceResult.setResultWavelength(
112                         GridUtils.getWaveLengthIndexFromSpectrumAssigment(spectrumAssignment.getBeginIndex().toJava()));
113                 }
114                 pceResult.setMinFreq(GridUtils.getStartFrequencyFromIndex(spectrumAssignment.getBeginIndex().toJava()));
115                 pceResult.setMaxFreq(GridUtils.getStopFrequencyFromIndex(spectrumAssignment.getStopIndex().toJava()));
116                 LOG.debug("In PostAlgoPathValidator: spectrum assignment found {} {}", spectrumAssignment, path);
117
118                 // Check the OSNR
119                 CatalogUtils cu = new CatalogUtils(networkTransactionService);
120                 if (cu.isCatalogFilled()) {
121                     double margin1 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
122                             StringConstants.SERVICE_DIRECTION_AZ, cu);
123                     double margin2 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
124                             StringConstants.SERVICE_DIRECTION_ZA, cu);
125                     if (margin1 < 0 || margin2 < 0 || margin1 == Double.NEGATIVE_INFINITY
126                             || margin2 == Double.NEGATIVE_INFINITY) {
127                         pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
128                         pceResult.setLocalCause(PceResult.LocalCause.OUT_OF_SPEC_OSNR);
129                         return pceResult;
130                     }
131                     this.tpceCalculatedMargin = Math.min(margin1, margin2);
132                     LOG.info(
133                         "In PostAlgoPathValidator: Minimum margin estimated by tpce on AtoZ and ZtoA path is of  {} dB",
134                         this.tpceCalculatedMargin);
135                 } else {
136                     this.tpceCalculatedMargin = 0.0;
137                     LOG.info("In PostAlgoPathValidator: Operational mode Catalog not filled, delegate OSNR calculation"
138                         + " to GNPy and margin set to 0");
139                 }
140                 // Check if MaxLatency is defined in the hard constraints
141                 if (pceHardConstraints.getMaxLatency() != -1
142                         && !checkLatency(pceHardConstraints.getMaxLatency(), path)) {
143                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
144                     pceResult.setLocalCause(PceResult.LocalCause.TOO_HIGH_LATENCY);
145                     return pceResult;
146                 }
147                 // Check if nodes are included in the hard constraints
148                 if (!checkInclude(path, pceHardConstraints, mode)) {
149                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
150                     pceResult.setLocalCause(PceResult.LocalCause.HD_NODE_INCLUDE);
151                     return pceResult;
152                 }
153                 // TODO here other post algo validations can be added
154                 // more data can be sent to PceGraph module via PceResult structure if required
155                 pceResult.setRC(ResponseCodes.RESPONSE_OK);
156                 pceResult.setLocalCause(PceResult.LocalCause.NONE);
157                 return pceResult;
158             case StringConstants.SERVICE_TYPE_100GE_M:
159             case StringConstants.SERVICE_TYPE_10GE:
160             case StringConstants.SERVICE_TYPE_1GE:
161                 int tribSlotNb = Map.of(
162                         StringConstants.SERVICE_TYPE_100GE_M, 20,
163                         StringConstants.SERVICE_TYPE_10GE, 8,
164                         StringConstants.SERVICE_TYPE_1GE, 1)
165                     .get(serviceType);
166                 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
167                 pceResult.setServiceType(serviceType);
168                 Map<String, List<Uint16>> tribSlot = chooseTribSlot(path, allPceNodes, tribSlotNb);
169                 Map<String, Uint16> tribPort = chooseTribPort(path, allPceNodes, tribSlot, tribSlotNb);
170                 List<OpucnTribSlotDef> resultTribPortTribSlot = getMinMaxTpTs(tribPort, tribSlot);
171                 if (resultTribPortTribSlot.get(0) != null && resultTribPortTribSlot.get(1) != null) {
172                     pceResult.setResultTribPortTribSlot(resultTribPortTribSlot);
173                     pceResult.setRC(ResponseCodes.RESPONSE_OK);
174                     LOG.info("In PostAlgoPathValidator: found TribPort {} - tribSlot {} - tribSlotNb {}",
175                         tribPort, tribSlot, tribSlotNb);
176                 }
177                 return pceResult;
178             case StringConstants.SERVICE_TYPE_ODU4:
179             case StringConstants.SERVICE_TYPE_ODUC2:
180             case StringConstants.SERVICE_TYPE_ODUC3:
181             case StringConstants.SERVICE_TYPE_ODUC4:
182             case StringConstants.SERVICE_TYPE_100GE_S:
183                 pceResult.setRC(ResponseCodes.RESPONSE_OK);
184                 pceResult.setServiceType(serviceType);
185                 LOG.info("In PostAlgoPathValidator: ODU4/ODUCn path found {}", path);
186                 return pceResult;
187             default:
188                 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
189                 LOG.warn("In PostAlgoPathValidator checkPath: unsupported serviceType {} found {}",
190                     serviceType, path);
191                 return pceResult;
192         }
193     }
194
195     // Check the latency
196     private boolean checkLatency(Long maxLatency, GraphPath<String, PceGraphEdge> path) {
197         double latency = 0;
198         for (PceGraphEdge edge : path.getEdgeList()) {
199             if (edge.link() == null || edge.link().getLatency() == null) {
200                 LOG.warn("- In checkLatency: the link {} does not contain latency field",
201                     edge.link().getLinkId().getValue());
202                 return false;
203             }
204             latency += edge.link().getLatency();
205             LOG.debug("- In checkLatency: latency of {} = {} units", edge.link().getLinkId().getValue(), latency);
206         }
207         return (latency < maxLatency);
208     }
209
210     // Check the inclusion if it is defined in the hard constraints
211     //TODO: remove this checkstyle false positive warning when the checkstyle bug will be fixed
212     @SuppressWarnings("MissingSwitchDefault")
213     private boolean checkInclude(GraphPath<String, PceGraphEdge> path, PceConstraints pceHardConstraintsInput,
214             PceConstraintMode mode) {
215         List<ResourcePair> listToInclude = pceHardConstraintsInput.getListToInclude();
216         if (listToInclude.isEmpty()) {
217             return true;
218         }
219         List<PceGraphEdge> pathEdges = path.getEdgeList();
220         LOG.debug(" in checkInclude vertex list: [{}]", path.getVertexList());
221         LOG.debug("listToInclude = {}", listToInclude);
222         List<String> listOfElementsSubNode = new ArrayList<>();
223         listOfElementsSubNode.add(pathEdges.get(0).link().getsourceNetworkSupNodeId());
224         listOfElementsSubNode.addAll(
225             listOfElementsBuild(pathEdges, PceConstraints.ResourceType.NODE, pceHardConstraintsInput));
226         List<String> listOfElementsCLLI = new ArrayList<>();
227         listOfElementsCLLI.add(pathEdges.get(0).link().getsourceCLLI());
228         listOfElementsCLLI.addAll(
229             listOfElementsBuild(pathEdges, PceConstraints.ResourceType.CLLI, pceHardConstraintsInput));
230         List<String> listOfElementsSRLG = new ArrayList<>();
231         // first link is XPONDEROUTPUT, no SRLG for it
232         listOfElementsSRLG.add("NONE");
233         listOfElementsSRLG.addAll(
234             listOfElementsBuild(pathEdges, PceConstraints.ResourceType.SRLG, pceHardConstraintsInput));
235         // validation: check each type for each element
236         LOG.debug("listOfElementsSubNode = {}", listOfElementsSubNode);
237         return switch (mode) {
238             case Loose -> listOfElementsSubNode
239                 .containsAll(listToInclude.stream()
240                     .filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
241                     .map(ResourcePair::getName).collect(Collectors.toList()));
242             case Strict -> listOfElementsSubNode
243                 .equals(listToInclude.stream()
244                     .filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
245                     .map(ResourcePair::getName).collect(Collectors.toList()));
246         }
247             && listOfElementsSRLG.containsAll(
248                 listToInclude
249                     .stream().filter(rp -> PceConstraints.ResourceType.SRLG.equals(rp.getType()))
250                     .map(ResourcePair::getName).collect(Collectors.toList()))
251             && listOfElementsCLLI.containsAll(
252                 listToInclude
253                     .stream().filter(rp -> PceConstraints.ResourceType.CLLI.equals(rp.getType()))
254                     .map(ResourcePair::getName).collect(Collectors.toList()));
255     }
256
257     private List<String> listOfElementsBuild(List<PceGraphEdge> pathEdges, PceConstraints.ResourceType type,
258             PceConstraints pceHardConstraints) {
259         Set<String> listOfElements = new LinkedHashSet<>();
260         for (PceGraphEdge link : pathEdges) {
261             switch (type) {
262                 case NODE:
263                     listOfElements.add(link.link().getdestNetworkSupNodeId());
264                     break;
265                 case CLLI:
266                     listOfElements.add(link.link().getdestCLLI());
267                     break;
268                 case SRLG:
269                     if (link.link().getlinkType() != OpenroadmLinkType.ROADMTOROADM) {
270                         listOfElements.add("NONE");
271                         break;
272                     }
273                     // srlg of link is List<Long>. But in this algo we need string representation of
274                     // one SRLG
275                     // this should be any SRLG mentioned in include constraints if any of them if
276                     // mentioned
277                     boolean found = false;
278                     for (Long srlg : link.link().getsrlgList()) {
279                         String srlgStr = String.valueOf(srlg);
280                         if (pceHardConstraints.getSRLGnames().contains(srlgStr)) {
281                             listOfElements.add(srlgStr);
282                             LOG.info("listOfElementsBuild. FOUND SRLG {} in link {}", srlgStr, link.link());
283                             found = true;
284                         }
285                     }
286                     if (!found) {
287                         // there is no specific srlg to include. thus add to list just the first one
288                         listOfElements.add("NONE");
289                     }
290                     break;
291                 default:
292                     LOG.debug("listOfElementsBuild unsupported resource type");
293             }
294         }
295         return new ArrayList<>(listOfElements);
296     }
297
298     private Map<String, Uint16> chooseTribPort(GraphPath<String,
299             PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, Map<String, List<Uint16>> tribSlotMap, int nbSlot) {
300         LOG.debug("In choosetribPort: edgeList = {} ", path.getEdgeList());
301         Map<String, Uint16> tribPortMap = new HashMap<>();
302         for (PceGraphEdge edge : path.getEdgeList()) {
303             List<Uint16> srcTpnPool =
304                 allPceNodes
305                     .get(edge.link().getSourceId())
306                     .getAvailableTribPorts()
307                     .get(edge.link().getSourceTP().getValue());
308             List<Uint16> destTpnPool =
309                 allPceNodes
310                     .get(edge.link().getDestId())
311                     .getAvailableTribPorts()
312                     .get(edge.link().getDestTP().getValue());
313             List<Uint16> commonEdgeTpnPool = new ArrayList<>();
314             for (Uint16 srcTpn : srcTpnPool) {
315                 if (destTpnPool.contains(srcTpn)) {
316                     commonEdgeTpnPool.add(srcTpn);
317                 }
318             }
319             if (commonEdgeTpnPool.isEmpty()) {
320                 continue;
321             }
322             Integer startTribSlot = tribSlotMap.values().stream().findFirst().orElseThrow().get(0).toJava();
323             Integer tribPort = (int) Math.ceil((double)startTribSlot / nbSlot);
324             for (Uint16 commonTribPort : commonEdgeTpnPool) {
325                 if (tribPort.equals(commonTribPort.toJava())) {
326                     tribPortMap.put(edge.link().getLinkId().getValue(), commonTribPort);
327                 }
328             }
329         }
330         tribPortMap.forEach((k,v) -> LOG.info("TribPortMap : k = {}, v = {}", k, v));
331         return tribPortMap;
332     }
333
334     private Map<String, List<Uint16>> chooseTribSlot(GraphPath<String,
335             PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, int nbSlot) {
336         LOG.debug("In choosetribSlot: edgeList = {} ", path.getEdgeList());
337         Map<String, List<Uint16>> tribSlotMap = new HashMap<>();
338         for (PceGraphEdge edge : path.getEdgeList()) {
339             List<Uint16> srcTsPool =
340                 allPceNodes
341                     .get(edge.link().getSourceId())
342                     .getAvailableTribSlots()
343                     .get(edge.link().getSourceTP().getValue());
344             List<Uint16> destTsPool =
345                 allPceNodes
346                     .get(edge.link().getDestId())
347                     .getAvailableTribSlots()
348                     .get(edge.link().getDestTP().getValue());
349             List<Uint16> commonEdgeTsPoolList = new ArrayList<>();
350             List<Uint16> tribSlotList = new ArrayList<>();
351             for (Uint16 integer : srcTsPool) {
352                 if (destTsPool.contains(integer)) {
353                     commonEdgeTsPoolList.add(integer);
354                 }
355             }
356             Collections.sort(commonEdgeTsPoolList);
357             List<Uint16> commonGoodStartEdgeTsPoolList = new ArrayList<>();
358             for (Uint16 startEdgeTsPool : commonEdgeTsPoolList) {
359                 if (Integer.valueOf(1).equals(startEdgeTsPool.toJava() % nbSlot) || nbSlot == 1) {
360                     commonGoodStartEdgeTsPoolList.add(startEdgeTsPool);
361                 }
362             }
363             Collections.sort(commonGoodStartEdgeTsPoolList);
364             boolean goodTsList = false;
365             for (Uint16 goodStartTsPool : commonGoodStartEdgeTsPoolList) {
366                 int goodStartIndex = commonEdgeTsPoolList.indexOf(Uint16.valueOf(goodStartTsPool.intValue()));
367                 if (!goodTsList && commonEdgeTsPoolList.size() - goodStartIndex >= nbSlot) {
368                     for (int i = 0; i < nbSlot; i++) {
369                         if (!commonEdgeTsPoolList.get(goodStartIndex + i)
370                                 .equals(Uint16.valueOf(goodStartTsPool.toJava() + i))) {
371                             goodTsList = false;
372                             tribSlotList.clear();
373                             break;
374                         }
375                         tribSlotList.add(commonEdgeTsPoolList.get(goodStartIndex + i));
376                         goodTsList = true;
377                     }
378                 }
379             }
380             tribSlotMap.put(edge.link().getLinkId().getValue(), tribSlotList);
381         }
382         tribSlotMap.forEach((k,v) -> LOG.info("TribSlotMap : k = {}, v = {}", k, v));
383         return tribSlotMap;
384     }
385
386     private List<OpucnTribSlotDef> getMinMaxTpTs(Map<String, Uint16> tribPort, Map<String, List<Uint16>> tribSlot) {
387         String tribport = tribPort.values().toArray()[0].toString();
388         @SuppressWarnings("unchecked") List<Uint16> tsList = (List<Uint16>) tribSlot.values().toArray()[0];
389         return new ArrayList<>(List.of(
390             OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(0).toString())),
391             OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(tsList.size() - 1).toString()))));
392     }
393
394     private double checkOSNR(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
395             Map<LinkId, PceLink> allPceLinks, String serviceType, String direction, CatalogUtils cu) {
396         switch (direction) {
397             case StringConstants.SERVICE_DIRECTION_AZ:
398                 return checkOSNRaz(path, allPceNodes, allPceLinks, serviceType, cu);
399             case StringConstants.SERVICE_DIRECTION_ZA:
400                 return checkOSNRza(path, allPceNodes, allPceLinks, serviceType, cu);
401             default:
402                 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported direction {}", direction);
403                 return 0.0;
404         }
405     }
406
407     /**
408      * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
409      *
410      * @param path                      the AtoZ path provided by the PCE.
411      * @param allPceNode                The map of chosen/relevant PceNodes build from topology pruning.
412      * @param allPceLinks               The map of PceLinks build corresponding to the whole topology.
413      * @param serviceType               The service Type used to extrapolate Operational mode when it is not provided.
414      * @param cu                        CatalogUtils instance.
415      * @return the calculated margin according to the Transponder performances and path impairments.
416      */
417     private double checkOSNRaz(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
418             Map<LinkId, PceLink> allPceLinks, String serviceType, CatalogUtils cu) {
419         Map<String, Double> signal = new HashMap<>(
420             Map.of(
421                 "spacing", Double.valueOf(50.0),
422                 "calcPdl2", Double.valueOf(0),
423                 "calcCd", Double.valueOf(0),
424                 "calcPmd2", Double.valueOf(0),
425                 "calcOnsrLin", Double.valueOf(0.0001),
426                 "pwrIn", Double.valueOf(-60.0),
427                 "pwrOut", Double.valueOf(-60.0)));
428         double calcOnsrdB = 0;
429         double margin = 0;
430         boolean transponderPresent = false;
431         List<String> vertices = path.getVertexList();
432         List<PceGraphEdge> edges = path.getEdgeList();
433         // LOOP that scans the different Nodes/Links of the path and calculates
434         // associated degradations
435         // using CatalogUtils primitives to retrieve physical parameters and make a
436         // first level calculation
437         int bypassDegree = 0;
438         for (int pathElement = 0; pathElement < 2; pathElement++) {
439             bypassDegree = 0;
440             PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
441             PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + 1)));
442             LOG.debug("loop of check OSNR direction AZ, Path Element = {}", pathElement);
443             switch (currentNode.getORNodeType()) {
444                 case XPONDER:
445                     LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
446                     transponderPresent = true;
447                     calcXpdrOSNR(cu, signal,
448                         pathElement == 0
449                             // First transponder on the Path (TX side) / Last Xponder of the path (RX side)
450                             ? edges.get(pathElement).link().getSourceTP().getValue()
451                             : edges.get(pathElement - 1).link().getDestTP().getValue(),
452                         serviceType, currentNode, nextNode, vertices.get(pathElement), pathElement);
453                     break;
454                 case SRG:
455                     LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", pathElement);
456                     // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
457                     // Xponder to Xponder) node element of the path is the ADD SRG.
458                     if (edges.get(pathElement).link().getlinkType() != OpenroadmLinkType.ADDLINK) {
459                         LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
460                             currentNode.getNodeId(), pathElement);
461                     }
462                     signal.put("pwrIn", Double.valueOf(0));
463                     calcAddContrib(cu, signal, currentNode, edges.get(pathElement + 1).link());
464                     LOG.debug("loop of check OSNR direction AZ: SRG, pathElement = {} link {} Pout = {}",
465                         pathElement, pathElement + 1, signal.get("pwrOut"));
466                     double calcOnsr = signal.get("calcOnsrLin").doubleValue();
467                     if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
468                         return -1.0;
469                     }
470                     // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
471                     // The Degree is not considered. This means we must bypass the add-link (ADD)
472                     // and the next node (Degree) which are not considered in the impairments.
473                     pathElement++;
474                     bypassDegree = 1;
475                     break;
476                 case DEGREE:
477                 default:
478                     LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
479             }
480         }
481         for (int pathElement = 2 + bypassDegree; pathElement < vertices.size() - 1; pathElement++) {
482             PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
483             PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + 1)));
484             LOG.debug("loop of check OSNR direction AZ: Path Element = {}", pathElement);
485             switch (currentNode.getORNodeType()) {
486                 case SRG:
487                     LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", pathElement);
488                     // Other case is DROP, for which cnt is unchanged (.DROP)
489                     if (edges.get(pathElement - 1).link().getlinkType() != OpenroadmLinkType.DROPLINK) {
490                         LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
491                             currentNode.getNodeId(), pathElement - 1);
492                     }
493                     PceLink pceLink = edges.get(pathElement - 2).link();
494                     LOG.info("loop of check OSNR : SRG, pathElement = {} CD on preceeding link {} = {} ps",
495                         pathElement, pathElement - 2, pceLink.getcd());
496                     calcDropContrib(cu, signal, currentNode, pceLink);
497                     double calcOnsr = signal.get("calcOnsrLin").doubleValue();
498                     if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
499                         return -1.0;
500                     }
501                     // If SRG is not the first or the second element of the Path, it is the DROP
502                     // side.
503                     // After accumulated degradations are calculated, we also need to calculate
504                     // resulting OSNR in dB to pass it to the method that verifies end Xponder
505                     // performances are compatible with degradations experienced on the path
506                     try {
507                         calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
508                         LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
509                         LOG.info("Loop pathElement = {}, DROP, calcOnsrdB= {}", pathElement, calcOnsrdB);
510                     } catch (ArithmeticException e) {
511                         LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
512                             path.getEdgeList().size());
513                         return -1.0;
514                     }
515                     break;
516                 case DEGREE:
517                     if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
518                         //This is the case of DROP, ROADM degree is not considered
519                         break;
520                     }
521                     LOG.info("loop of check OSNR direction AZ: DEGREE, Path Element = {}", pathElement);
522                     calcBypassContrib(cu, signal, currentNode, nextNode,
523                         edges.get(pathElement - 1).link(), edges.get(pathElement + 1).link());
524                     double calcOnsrLin = signal.get("calcOnsrLin").doubleValue();
525                     LOG.debug(
526                         "Loop pathElement= {}, DEGREE, calcOnsrdB= {}", pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
527                     if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
528                         return -1.0;
529                     }
530                     // increment pathElement so that in next step we will not point to Degree2 but
531                     // next node
532                     pathElement++;
533                     LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
534                         + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId(), nextNode.getNodeId(),
535                         signal.get("calcCd"), signal.get("calcPmd2"), signal.get("calcPdl2"),
536                         getOsnrDbfromOnsrLin(calcOnsrLin));
537                     break;
538                 case XPONDER:
539                     LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
540                     LOG.error("unsupported back to back transponder configuration");
541                     return -1.0;
542                 default:
543                     LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
544             }
545         }
546         PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(vertices.size() - 1)));
547         LOG.debug("loop of check OSNR, Path Element = {}", vertices.size() - 1);
548         switch (currentNode.getORNodeType()) {
549             case XPONDER:
550                 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", vertices.size() - 1);
551                 transponderPresent = true;
552                 // TSP is the last of the path
553                 margin = getLastXpdrMargin(cu, signal, edges.get(vertices.size() - 2).link().getDestTP().getValue(),
554                     serviceType, currentNode, vertices.get(vertices.size() - 1), vertices.size() - 1);
555                 break;
556             case SRG:
557                 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", vertices.size() - 1);
558                 // Other case is DROP, for which cnt is unchanged (.DROP)
559                 if (edges.get(vertices.size() - 2).link().getlinkType() != OpenroadmLinkType.DROPLINK) {
560                     LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
561                         currentNode.getNodeId(), vertices.size() - 2);
562                 }
563                 PceLink pceLink = edges.get(vertices.size() - 3).link();
564                 LOG.info("loop of check OSNR : SRG, pathElement = {} CD on preceeding link {} = {} ps",
565                     vertices.size() - 1, vertices.size() - 3, pceLink.getcd());
566                 calcDropContrib(cu, signal, currentNode, pceLink);
567                 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
568                 //commented out to avoid spotbug DLS_DEAD_LOCAL_STORE pwrIn = impairments.get("pwrIn");
569                 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
570                     return -1.0;
571                 }
572                 // If SRG is not the first or the second element of the Path, it is the DROP
573                 // side.
574                 // After accumulated degradations are calculated, we also need to calculate
575                 // resulting OSNR in dB to pass it to the method that verifies end Xponder
576                 // performances are compatible with degradations experienced on the path
577                 try {
578                     calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
579                     LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
580                     LOG.info("Loop pathElement = {}, DROP, calcOnsrdB= {}", vertices.size() - 1, calcOnsrdB);
581                 } catch (ArithmeticException e) {
582                     LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
583                         path.getEdgeList().size());
584                     return -1.0;
585                 }
586                 break;
587             case DEGREE:
588             default:
589                 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain last element");
590         }
591         LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOnsrdB = {} "
592             + "dB and ONSR dB exterapolated from calcosnrlin = {} including non linear contributions",
593             signal.get("calcCd"), Math.sqrt(signal.get("calcPmd2").doubleValue()),
594             Math.sqrt(signal.get("calcPdl2").doubleValue()), calcOnsrdB,
595             getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue()));
596         if (!transponderPresent) {
597             LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
598                 + "that optical tunnel degradations are compatible with external transponder performances");
599             return 0.0;
600         }
601         double delta = margin - SYS_MARGIN;
602         LOG.info("In checkOSNR: Transponder Operational mode results in a residual margin of {} dB, according "
603             + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
604             delta, SYS_MARGIN);
605         String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
606         LOG.info("- In checkOSNR: A to Z Path from {} to {} {}",
607                 vertices.get(0), vertices.get(vertices.size() - 1), validationMessage);
608         return delta;
609     }
610
611     /**
612      * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
613      *
614      * @param path                      the AtoZ path provided by the PCE.
615      * @param allPceNode                The map of chosen/relevant PceNodes build from topology pruning.
616      * @param allPceLinks               The map of PceLinks build corresponding to the whole topology.
617      * @param serviceType               The service Type used to extrapolate Operational mode when it is not provided.
618      * @param cu                        CatalogUtils instance.
619      * @return the calculated margin according to the Transponder performances and path impairments.
620      */
621     private double checkOSNRza(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
622             Map<LinkId, PceLink> allPceLinks, String serviceType, CatalogUtils cu) {
623         Map<String, Double> signal = new HashMap<>(
624             Map.of(
625                 "spacing", Double.valueOf(50.0),
626                 "calcPdl2", Double.valueOf(0),
627                 "calcCd", Double.valueOf(0),
628                 "calcPmd2", Double.valueOf(0),
629                 "calcOnsrLin", Double.valueOf(0.0001),
630                 "pwrIn", Double.valueOf(-60.0),
631                 "pwrOut", Double.valueOf(-60.0)));
632         double calcOnsrdB = 0;
633         double margin = 0;
634         boolean transponderPresent = false;
635         List<String> vertices = path.getVertexList();
636         List<PceGraphEdge> edges = path.getEdgeList();
637         // LOOP that scans the different Nodes/Links of the path and calculates
638         // associated degradations
639         // using CatalogUtils primitives to retrieve physical parameters and make a
640         // first level calculation
641         int bypassDegree = 0;
642         for (int pathElement = vertices.size() - 1; pathElement > vertices.size() - 3; pathElement--) {
643             bypassDegree = 0;
644             PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
645             PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement - 1)));
646             LOG.debug("loop of check OSNR direction ZA:  Path Element = {}", pathElement);
647             switch (currentNode.getORNodeType()) {
648                 case XPONDER:
649                     LOG.debug("loop of check OSNR direction ZA: XPDR, Path Element = {}", pathElement);
650                     transponderPresent = true;
651                     calcXpdrOSNR(cu, signal,
652                         pathElement == vertices.size() - 1
653                             // First transponder on the Path (TX side) / Last Xponder of the path (RX side)
654                             ? getOppPceLink(pathElement - 1, edges, allPceLinks).getSourceTP().getValue()
655                             : getOppPceLink((pathElement), edges, allPceLinks).getDestTP().getValue(),
656                         serviceType, currentNode, nextNode, vertices.get(pathElement), pathElement);
657                     break;
658                 case SRG:
659                     LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = {}", pathElement);
660                     // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
661                     // Xponder to Xponder) node element of the path is the ADD SRG.
662                     if (getOppPceLink(pathElement - 1, edges, allPceLinks).getlinkType() != OpenroadmLinkType.ADDLINK) {
663                         LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
664                             currentNode.getNodeId(), pathElement - 1);
665                     }
666                     signal.put("pwrIn", Double.valueOf(0));
667                     calcAddContrib(cu, signal, currentNode, getOppPceLink(pathElement - 2, edges, allPceLinks));
668                     double calcOnsr = signal.get("calcOnsrLin").doubleValue();
669                     if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
670                         return -1.0;
671                     }
672                     // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
673                     // The Degree is not considered. This means we must bypass the add-link (ADD)
674                     // and the next node (Degree) which are not considered in the impairments.
675                     pathElement--;
676                     bypassDegree = 1;
677                     break;
678                 case DEGREE:
679                 default:
680                     LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
681             }
682         }
683         for (int pathElement = vertices.size() - 3 - bypassDegree; pathElement > 0; pathElement--) {
684             PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
685             PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement - 1)));
686             LOG.debug("loop of check OSNR direction ZA: Path Element = {}", pathElement);
687             switch (currentNode.getORNodeType()) {
688                 case SRG:
689                     LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = {}", pathElement);
690                     if (getOppPceLink(pathElement, edges, allPceLinks).getlinkType() != OpenroadmLinkType.DROPLINK) {
691                         LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
692                             currentNode.getNodeId(), pathElement);
693                     }
694                     PceLink pceLink = getOppPceLink(pathElement + 1, edges, allPceLinks);
695                     LOG.info("loop of check OSNR direction ZA: SRG, path Element = {} CD on preceeding link {} = {} ps",
696                         pathElement, pathElement + 1, pceLink.getcd());
697                     calcDropContrib(cu, signal, currentNode, pceLink);
698                     double calcOnsr = signal.get("calcOnsrLin").doubleValue();
699                     if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
700                         return -1.0;
701                     }
702                     // If SRG is not the first or the second element of the Path, it is the DROP
703                     // side.
704                     // After accumulated degradations are calculated, we also need to calculate
705                     // resulting OSNR in dB to pass it to the method that verifies end Xponder
706                     // performances are compatible with degradations experienced on the path
707                     try {
708                         calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
709                         LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
710                         LOG.info("Loop Path Element = {}, DROP, calcOnsrdB= {}", pathElement, calcOnsrdB);
711                     } catch (ArithmeticException e) {
712                         LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
713                             path.getEdgeList().size());
714                         return -1.0;
715                     }
716                     break;
717                 case DEGREE:
718                     if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
719                         //This is the case of DROP, ROADM degree is not considered
720                         break;
721                     }
722                     LOG.info("loop of check OSNR direction ZA: DEGREE, Path Element = {}", pathElement);
723                     calcBypassContrib(cu, signal, currentNode, nextNode,
724                         getOppPceLink(pathElement, edges, allPceLinks),
725                         getOppPceLink(pathElement - 2, edges, allPceLinks));
726                     double calcOnsrLin = signal.get("calcOnsrLin").doubleValue();
727                     LOG.debug("Loop Path Element = {}, DEGREE, calcOnsrdB= {}",
728                             pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
729                     if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
730                         return -1.0;
731                     }
732                     // increment pathElement so that in next step we will not point to Degree2 but
733                     // next node
734                     pathElement--;
735                     LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
736                         + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId(), nextNode.getNodeId(),
737                         signal.get("calcCd"), signal.get("calcPmd2"), signal.get("calcPdl2"),
738                         getOsnrDbfromOnsrLin(calcOnsrLin));
739                     break;
740                 case XPONDER:
741                     LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
742                     LOG.error("unsupported back to back transponder configuration");
743                     return -1.0;
744                 default:
745                     LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
746             }
747         }
748         PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(0)));
749         LOG.debug("loop of check OSNR direction ZA: Path Element = 0");
750         switch (currentNode.getORNodeType()) {
751             case XPONDER:
752                 LOG.debug("loop of check OSNR direction ZA: XPDR, Path Element = 0");
753                 transponderPresent = true;
754                 // TSP is the last of the path
755                 margin = getLastXpdrMargin(cu, signal, getOppPceLink(0, edges, allPceLinks).getDestTP().getValue(),
756                     serviceType, currentNode, vertices.get(0), 0);
757                 break;
758             case SRG:
759                 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = 0");
760                 if (getOppPceLink(0, edges, allPceLinks).getlinkType() != OpenroadmLinkType.DROPLINK) {
761                     LOG.error("Error processing Node {} for which input link 0 is not a DROPLINK Type",
762                         currentNode.getNodeId());
763                 }
764                 PceLink pceLink = getOppPceLink(1, edges, allPceLinks);
765                 LOG.info("loop of check OSNR direction ZA: SRG, path Element = 0 CD on preceeding link 1 = {} ps",
766                     pceLink.getcd());
767                 calcDropContrib(cu, signal, currentNode, pceLink);
768                 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
769                 //commented out to avoid spotbug DLS_DEAD_LOCAL_STORE pwrIn = impairments.get("pwrIn");
770                 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
771                     return -1.0;
772                 }
773                 // If SRG is not the first or the second element of the Path, it is the DROP
774                 // side.
775                 // After accumulated degradations are calculated, we also need to calculate
776                 // resulting OSNR in dB to pass it to the method that verifies end Xponder
777                 // performances are compatible with degradations experienced on the path
778                 try {
779                     calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
780                     LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
781                     LOG.info("Loop Path Element = 0, DROP, calcOnsrdB= {}", calcOnsrdB);
782                 } catch (ArithmeticException e) {
783                     LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
784                         path.getEdgeList().size());
785                     return -1.0;
786                 }
787                 break;
788             case DEGREE:
789             default:
790                 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain last element");
791         }
792         LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOnsrdB = {} "
793             + "dB and ONSR dB exterapolated from calcosnrlin = {} including non linear contributions",
794             signal.get("calcCd"), Math.sqrt(signal.get("calcPmd2").doubleValue()),
795             Math.sqrt(signal.get("calcPdl2").doubleValue()), calcOnsrdB,
796             getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue()));
797         if (!transponderPresent) {
798             LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
799                 + "that optical tunnel degradations are compatible with external transponder performances");
800             return 0.0;
801         }
802         double delta = margin - SYS_MARGIN;
803         LOG.info("In checkOSNR: Transponder Operational mode results in a residual margin of {} dB, according "
804             + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
805             delta, SYS_MARGIN);
806         String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
807         LOG.info("- In checkOSNR: Z to A Path from {} to {} {}",
808                 vertices.get(vertices.size() - 1), vertices.get(0), validationMessage);
809         return delta;
810     }
811
812     private String setOpMode(String opMode, String defaultMode) {
813         return
814             opMode == null || opMode.isEmpty() || opMode.contentEquals(StringConstants.UNKNOWN_MODE)
815                 ? defaultMode
816                 : opMode;
817     }
818
819     private PceLink getOppPceLink(Integer pathEltNber, List<PceGraphEdge> edges,
820             Map<LinkId, PceLink> allPceLinks) {
821         return allPceLinks.get(new LinkId(edges.get(pathEltNber).link().getOppositeLink()));
822     }
823
824     private String getXpdrOpMode(String nwTpId, String vertice, int pathElement, PceNode currentNode,
825             String serviceType, CatalogUtils cu) {
826         InstanceIdentifier<TerminationPoint1> nwTpIid =
827             InstanceIdentifiers.createNetworkTerminationPoint1IIDBuilder(vertice, nwTpId);
828         String opMode = cu.getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, serviceType);
829         try {
830             if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, nwTpIid).get().isPresent()) {
831                 // If the operational mode of the Xponder is not consistent nor declared in the topology (Network TP)
832                 opMode = setOpMode(
833                     currentNode.getXponderOperationalMode(
834                         networkTransactionService
835                                 .read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
836                                 .get().orElseThrow().getXpdrNetworkAttributes()),
837                     // Operational mode is found as an attribute of the network TP
838                     opMode);
839                     // Operational mode is retrieved from the service Type assuming it is supported
840                     // by the Xponder
841                 LOG.debug(
842                     "Transponder {} corresponding to path Element {} in the path has {} operational mode",
843                     currentNode.getNodeId().getValue(), pathElement, opMode);
844                 return opMode;
845             }
846         } catch (InterruptedException | ExecutionException e1) {
847             LOG.error("Issue accessing the XponderNetworkAttributes of {} for Transponder {}"
848                 + " corresponding to path Element {} in the path ",
849                 nwTpId, currentNode.getNodeId().getValue(), pathElement);
850         }
851         LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
852             + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
853         return opMode;
854     }
855
856     private double getLastXpdrMargin(
857             CatalogUtils cu, Map<String, Double> signal,
858             String nwTpId, String serviceType, PceNode currentNode, String vertice, int pathElement) {
859         LOG.debug("Loop Path Element = {}, Step5.1, XPDR, tries calculating Margin, just before call", pathElement);
860         // Check that accumulated degradations are compatible with TSP performances
861         // According to OpenROADM spec :
862         // margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2),
863         //              getOsnrDbfromOnsrLin(calcOnsrLin));
864         // Calculation modified for pdl according to calculation in Julia's Tool
865         double calcOnsrdB = getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue());
866         LOG.info("Loop Path Element = {}, XPDR, calcosnrdB= {}", pathElement, calcOnsrdB);
867         return cu.getPceRxTspParameters(
868             getXpdrOpMode(nwTpId, vertice, pathElement, currentNode, serviceType, cu),
869             signal.get("calcCd").doubleValue(),
870             Math.sqrt(signal.get("calcPmd2").doubleValue()),
871             Math.sqrt(signal.get("calcPdl2").doubleValue()),
872             calcOnsrdB);
873     }
874
875     private void calcXpdrOSNR(
876             CatalogUtils cu, Map<String, Double> signal, String nwTpId, String serviceType,
877             PceNode currentNode, PceNode nextNode, String vertice, int pathElement) {
878         // If the Xponder operational mode (setOpMode Arg1) is not consistent nor declared in the topology (Network TP)
879         // Operational mode is retrieved from the service Type assuming it is supported by the Xponder (setOpMode Arg2)
880         String opMode = getXpdrOpMode(nwTpId, vertice, pathElement, currentNode, serviceType, cu);
881         // If the operational mode of the ADD/DROP MUX is not consistent nor declared in the topology (Network TP)
882         // Operational mode is set by default to standard opMode for ADD SRGs
883         String adnMode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWWRCORE);
884         double calcOnsrLin = cu.getPceTxTspParameters(opMode, adnMode);
885         LOG.debug(
886             "Transponder {} corresponding to path Element {} is connected to SRG which has {} operational mode",
887             currentNode.getNodeId().getValue(), pathElement, adnMode);
888         LOG.info("Transponder {} corresponding to path Element {} in the path has a TX OSNR of {} dB",
889             currentNode.getNodeId().getValue(), pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
890         // Return the Tx ONSR of the Xponder which results from IB and OOB OSNR contributions
891         // and the spacing associated with Xponder operational mode that is needed to calculate OSNR
892         signal.put("spacing", Double.valueOf(cu.getPceTxTspChannelSpacing(opMode)));
893         signal.put("calcOnsrLin", Double.valueOf(calcOnsrLin));
894     }
895
896     private void calcDropContrib(
897             CatalogUtils cu, Map<String, Double> signal, PceNode currentNode, PceLink pceLink) {
898         //calculation of the SRG contribution for Drop
899         calcLineDegradation(cu, signal, pceLink);
900         Map<String, Double> impairments = cu.getPceRoadmAmpParameters(
901             CatalogConstant.CatalogNodeType.DROP,
902             setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE),
903         // If the operational mode of the ADD/DROP MUX is not consistent or not declared in the topology (Network TP)
904         // Operational mode is set by default to standard opMode for ADD/DROP SRGs
905             signal.get("pwrIn").doubleValue(),
906             signal.get("calcCd").doubleValue(),
907             signal.get("calcPmd2").doubleValue(),
908             signal.get("calcPdl2").doubleValue(),
909             signal.get("calcOnsrLin").doubleValue(),
910             signal.get("spacing").doubleValue());
911         signal.putAll(
912             Map.of(
913                 "calcCd", impairments.get("CD"),
914                 "calcPmd2", impairments.get("DGD2"),
915                 "calcPdl2", impairments.get("PDL2"),
916                 "calcOnsrLin", impairments.get("ONSRLIN")));
917     }
918
919     private void calcAddContrib(
920             CatalogUtils cu, Map<String, Double> signal, PceNode currentNode, PceLink pceLink) {
921         //calculation of the SRG contribution for Add
922         String srgMode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE);
923         // If the operational mode of the ADD/DROP MUX is not consistent or is not declared in the topology (Network TP)
924         // Operational mode is set by default to standard opMode for ADD/DROP SRGs
925         CatalogNodeType cnt = CatalogConstant.CatalogNodeType.ADD;
926         double pwrOut = cu.getPceRoadmAmpOutputPower(
927                 cnt, srgMode, pceLink.getspanLoss(), signal.get("spacing").doubleValue(), pceLink.getpowerCorrection());
928         //calculation of the SRG contribution either for Add and Drop
929         Map<String, Double> impairments = cu.getPceRoadmAmpParameters(cnt, srgMode, 0,
930             signal.get("calcCd").doubleValue(), signal.get("calcPmd2").doubleValue(),
931             signal.get("calcPdl2").doubleValue(),
932             signal.get("calcOnsrLin").doubleValue(), signal.get("spacing").doubleValue());
933         signal.putAll(
934             Map.of(
935                 "calcCd", impairments.get("CD"),
936                 "calcPmd2", impairments.get("DGD2"),
937                 "calcPdl2", impairments.get("PDL2"),
938                 "calcOnsrLin", impairments.get("ONSRLIN"),
939                 "pwrOut", Double.valueOf(pwrOut)));
940     }
941
942     private void calcBypassContrib(CatalogUtils cu, Map<String, Double> signal,
943             PceNode currentNode, PceNode nextNode, PceLink pceLink0, PceLink pceLink1) {
944         // If the operational mode of the Degree is not consistent or declared in the topology
945         // Operational mode is set by default to standard opMode for Degree
946         String degree1Mode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWMWCORE);
947         // Same for next node which is the second degree of a ROADM node
948         String degree2Mode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWMWCORE);
949         // At that time OpenROADM provides only one spec for the ROADM nodes
950         if (!degree1Mode.equals(degree2Mode)) {
951             LOG.warn("Unsupported Hybrid ROADM configuration with Degree1 {} of {} operational mode and Degree2 "
952                 + "{} of {} operational mode. Will by default use operational mode of Degree2",
953                 currentNode.getNodeId(), degree1Mode, nextNode.getNodeId(), degree2Mode);
954         }
955         calcLineDegradation(cu, signal, pceLink0);
956         CatalogNodeType cnt = CatalogConstant.CatalogNodeType.EXPRESS;
957         double pwrOut = cu.getPceRoadmAmpOutputPower(cnt, degree2Mode, pceLink1.getspanLoss(),
958             signal.get("spacing").doubleValue(), pceLink1.getpowerCorrection());
959         // Adds to accumulated impairments the degradation associated with the Express
960         // path of ROADM : Degree1, express link, Degree2
961         Map<String, Double> impairments = cu.getPceRoadmAmpParameters(cnt, degree2Mode,
962             signal.get("pwrIn").doubleValue(), signal.get("calcCd").doubleValue(),
963             signal.get("calcPmd2").doubleValue(), signal.get("calcPdl2").doubleValue(),
964             signal.get("calcOnsrLin").doubleValue(), signal.get("spacing").doubleValue());
965         signal.putAll(
966             Map.of(
967                 "calcCd", impairments.get("CD"),
968                 "calcPmd2", impairments.get("DGD2"),
969                 "calcPdl2", impairments.get("PDL2"),
970                 "calcOnsrLin", impairments.get("ONSRLIN"),
971                 "pwrOut", Double.valueOf(pwrOut)));
972     }
973     //TODO these methods might be more indicated in a catalog utils refactoring
974
975     private void calcLineDegradation(CatalogUtils cu, Map<String, Double> signal, PceLink pceLink) {
976         // Calculate degradation accumulated across incoming Link and add them to
977         // accumulated impairments
978         // This also includes Non Linear Contribution from the path
979         signal.putAll(Map.of(
980             "pwrIn", Double.valueOf(signal.get("pwrOut").doubleValue() - pceLink.getspanLoss()),
981             "calcCd", Double.valueOf(signal.get("calcCd").doubleValue() + pceLink.getcd()),
982             "calcPmd2", Double.valueOf(signal.get("calcPmd2").doubleValue() + pceLink.getpmd2()),
983             "calcOnsrLin", Double.valueOf(
984                 signal.get("calcOnsrLin").doubleValue()
985                 + cu.calculateNLonsrContribution(
986                     signal.get("pwrOut").doubleValue(), pceLink.getLength(), signal.get("spacing").doubleValue()))));
987     }
988
989     private double getOsnrDbfromOnsrLin(double osnrLu) {
990         return 10 * Math.log10(1 / osnrLu);
991     }
992
993     /**
994      * Get spectrum assignment for path.
995      *
996      * @param path                    the path for which we get spectrum assignment.
997      * @param allPceNodes             all optical nodes.
998      * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
999      *                                service type.
1000      * @return a spectrum assignment object which contains begin and end index. If
1001      *         no spectrum assignment found, beginIndex = stopIndex = 0
1002      */
1003     private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
1004             Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
1005         byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
1006         Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
1007         BitSet result = BitSet.valueOf(freqMap);
1008         boolean isFlexGrid = true;
1009         LOG.debug("Processing path {} with length {}", path, path.getLength());
1010         BitSet pceNodeFreqMap;
1011         Set<PceNode> pceNodes = new LinkedHashSet<>();
1012
1013         for (PceGraphEdge edge : path.getEdgeList()) {
1014             NodeId srcId = edge.link().getSourceId();
1015             NodeId dstId = edge.link().getDestId();
1016             LOG.debug("Processing {} to {}", srcId.getValue(), dstId.getValue());
1017             if (allPceNodes.containsKey(srcId)) {
1018                 pceNodes.add(allPceNodes.get(srcId));
1019             }
1020             if (allPceNodes.containsKey(dstId)) {
1021                 pceNodes.add(allPceNodes.get(dstId));
1022             }
1023         }
1024
1025         for (PceNode pceNode : pceNodes) {
1026             LOG.debug("Processing PCE node {}", pceNode);
1027             pceNodeFreqMap = pceNode.getBitSetData();
1028             LOG.debug("Pce node bitset {}", pceNodeFreqMap);
1029             if (pceNodeFreqMap != null) {
1030                 result.and(pceNodeFreqMap);
1031                 LOG.debug("intermediate bitset {}", result);
1032             }
1033             String pceNodeVersion = pceNode.getVersion();
1034             BigDecimal sltWdthGran = pceNode.getSlotWidthGranularity();
1035             if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNodeVersion)) {
1036                 LOG.debug("Node {}: version is {} with slot width granularity {} - fixed grid mode",
1037                     pceNode.getNodeId(), pceNodeVersion, sltWdthGran);
1038                 isFlexGrid = false;
1039                 continue;
1040             }
1041             if (!sltWdthGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
1042                 continue;
1043             }
1044             BigDecimal ctralFreqGran = pceNode.getCentralFreqGranularity();
1045             if (!ctralFreqGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
1046                 continue;
1047             }
1048             LOG.debug(
1049                 "Node {}: version is {} with slot width and central frequency granularities {} {} - fixed grid mode",
1050                 pceNode.getNodeId(), pceNodeVersion, sltWdthGran, ctralFreqGran);
1051             isFlexGrid = false;
1052         }
1053         if (spectrumConstraint != null) {
1054             result.and(spectrumConstraint);
1055         }
1056
1057         LOG.debug("Bitset result {}", result);
1058         return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
1059     }
1060
1061     /**
1062      * Compute spectrum assignment from spectrum occupation for spectral width.
1063      *
1064      * @param spectrumOccupation      the spectrum occupation BitSet.
1065      * @param spectralWidthSlotNumber the nb slots for spectral width.
1066      * @param isFlexGrid              true if flexible grid, false otherwise.
1067      * @return a spectrum assignment object which contains begin and stop index. If
1068      *         no spectrum assignment found, beginIndex = stopIndex = 0
1069      */
1070     private SpectrumAssignment computeBestSpectrumAssignment(
1071             BitSet spectrumOccupation, int spectralWidthSlotNumber, boolean isFlexGrid) {
1072         SpectrumAssignmentBuilder spectrumAssignmentBldr = new SpectrumAssignmentBuilder()
1073             .setBeginIndex(Uint16.valueOf(0))
1074             .setStopIndex(Uint16.valueOf(0))
1075             .setFlexGrid(isFlexGrid);
1076         BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
1077         referenceBitSet.set(0, spectralWidthSlotNumber);
1078         //higher is the frequency, smallest is the wavelength number
1079         //in operational, the allocation is done through wavelength starting from the smallest
1080         //so we have to loop from the last element of the spectrum occupation
1081         for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber;
1082                 i -= isFlexGrid ? spectralWidthSlotNumber : 1) {
1083             if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
1084                 spectrumAssignmentBldr.setBeginIndex(Uint16.valueOf(i - spectralWidthSlotNumber));
1085                 spectrumAssignmentBldr.setStopIndex(Uint16.valueOf(i - 1));
1086                 break;
1087             }
1088         }
1089         return spectrumAssignmentBldr.build();
1090     }
1091
1092     public Double getTpceCalculatedMargin() {
1093         return tpceCalculatedMargin;
1094     }
1095 }