Refactor PCE graph PostAlgoPathValidator step 1
[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.List;
20 import java.util.Map;
21 import java.util.concurrent.ExecutionException;
22 import java.util.stream.Collectors;
23 import org.jgrapht.GraphPath;
24 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
25 import org.opendaylight.transportpce.common.InstanceIdentifiers;
26 import org.opendaylight.transportpce.common.ResponseCodes;
27 import org.opendaylight.transportpce.common.StringConstants;
28 import org.opendaylight.transportpce.common.catalog.CatalogConstant;
29 import org.opendaylight.transportpce.common.catalog.CatalogConstant.CatalogNodeType;
30 import org.opendaylight.transportpce.common.catalog.CatalogUtils;
31 import org.opendaylight.transportpce.common.fixedflex.GridConstant;
32 import org.opendaylight.transportpce.common.fixedflex.GridUtils;
33 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
34 import org.opendaylight.transportpce.pce.constraints.PceConstraints;
35 import org.opendaylight.transportpce.pce.constraints.PceConstraints.ResourcePair;
36 import org.opendaylight.transportpce.pce.networkanalyzer.PceLink;
37 import org.opendaylight.transportpce.pce.networkanalyzer.PceNode;
38 import org.opendaylight.transportpce.pce.networkanalyzer.PceResult;
39 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.SpectrumAssignment;
40 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.SpectrumAssignmentBuilder;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev211210.TerminationPoint1;
42 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev211210.networks.network.node.termination.point.XpdrNetworkAttributes;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev211210.OpenroadmLinkType;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev211210.OpenroadmNodeType;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.otn.common.types.rev210924.OpucnTribSlotDef;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.LinkId;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.common.Uint16;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 public class PostAlgoPathValidator {
54     /* Logging. */
55     private static final Logger LOG = LoggerFactory.getLogger(PostAlgoPathValidator.class);
56
57     public static final Long CONST_OSNR = 1L;
58     public static final double SYS_MARGIN = 0;
59     private Double tpceCalculatedMargin = 0.0;
60     private final NetworkTransactionService networkTransactionService;
61
62     public PostAlgoPathValidator(NetworkTransactionService networkTransactionService) {
63         this.networkTransactionService = networkTransactionService;
64     }
65
66     @SuppressWarnings("fallthrough")
67     @SuppressFBWarnings(
68         value = "SF_SWITCH_FALLTHROUGH",
69         justification = "intentional fallthrough")
70
71     public PceResult checkPath(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
72             Map<LinkId, PceLink> allPceLinks, PceResult pceResult, PceConstraints pceHardConstraints,
73             String serviceType) {
74         LOG.info("path = {}", path);
75         // check if the path is empty
76         if (path.getEdgeList().isEmpty()) {
77             pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
78             return pceResult;
79         }
80         int spectralWidthSlotNumber =
81             GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_100G);
82         SpectrumAssignment spectrumAssignment = null;
83         //variable to deal with 1GE (Nb=1) and 10GE (Nb=10) cases
84         switch (serviceType) {
85             case StringConstants.SERVICE_TYPE_OTUC2:
86             case StringConstants.SERVICE_TYPE_OTUC3:
87             case StringConstants.SERVICE_TYPE_OTUC4:
88             case StringConstants.SERVICE_TYPE_400GE:
89                 spectralWidthSlotNumber =
90                     GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_400G);
91             //fallthrough
92             case StringConstants.SERVICE_TYPE_100GE_T:
93             case StringConstants.SERVICE_TYPE_OTU4:
94                 spectrumAssignment = getSpectrumAssignment(path, allPceNodes, spectralWidthSlotNumber);
95                 pceResult.setServiceType(serviceType);
96                 if (spectrumAssignment.getBeginIndex().equals(Uint16.valueOf(0))
97                         && spectrumAssignment.getStopIndex().equals(Uint16.valueOf(0))) {
98                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
99                     pceResult.setLocalCause(PceResult.LocalCause.NO_PATH_EXISTS);
100                     return pceResult;
101                 }
102                 if (spectrumAssignment.getFlexGrid()) {
103                     LOG.debug("Spectrum assignment flexgrid mode");
104                     pceResult.setResultWavelength(GridConstant.IRRELEVANT_WAVELENGTH_NUMBER);
105                 } else {
106                     LOG.debug("Spectrum assignment fixedgrid mode");
107                     pceResult.setResultWavelength(
108                         GridUtils.getWaveLengthIndexFromSpectrumAssigment(spectrumAssignment.getBeginIndex().toJava()));
109                 }
110                 pceResult.setMinFreq(GridUtils.getStartFrequencyFromIndex(spectrumAssignment.getBeginIndex().toJava()));
111                 pceResult.setMaxFreq(GridUtils.getStopFrequencyFromIndex(spectrumAssignment.getStopIndex().toJava()));
112                 LOG.debug("In PostAlgoPathValidator: spectrum assignment found {} {}", spectrumAssignment, path);
113
114                 // Check the OSNR
115                 CatalogUtils cu = new CatalogUtils(networkTransactionService);
116                 if (cu.isCatalogFilled()) {
117                     double margin1 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
118                             StringConstants.SERVICE_DIRECTION_AZ, cu);
119                     double margin2 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
120                             StringConstants.SERVICE_DIRECTION_ZA, cu);
121                     if (margin1 < 0 || margin2 < 0 || margin1 == Double.NEGATIVE_INFINITY
122                             || margin2 == Double.NEGATIVE_INFINITY) {
123                         pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
124                         pceResult.setLocalCause(PceResult.LocalCause.OUT_OF_SPEC_OSNR);
125                         return pceResult;
126                     }
127                     this.tpceCalculatedMargin = Math.min(margin1, margin2);
128                     LOG.info(
129                         "In PostAlgoPathValidator: Minimum margin estimated by tpce on AtoZ and ZtoA path is of  {} dB",
130                         this.tpceCalculatedMargin);
131                 } else {
132                     this.tpceCalculatedMargin = 0.0;
133                     LOG.info("In PostAlgoPathValidator: Operational mode Catalog not filled, delegate OSNR calculation"
134                         + " to GNPy and margin set to 0");
135                 }
136                 // Check if MaxLatency is defined in the hard constraints
137                 if (pceHardConstraints.getMaxLatency() != -1
138                         && !checkLatency(pceHardConstraints.getMaxLatency(), path)) {
139                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
140                     pceResult.setLocalCause(PceResult.LocalCause.TOO_HIGH_LATENCY);
141                     return pceResult;
142                 }
143                 // Check if nodes are included in the hard constraints
144                 if (!checkInclude(path, pceHardConstraints)) {
145                     pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
146                     pceResult.setLocalCause(PceResult.LocalCause.HD_NODE_INCLUDE);
147                     return pceResult;
148                 }
149                 // TODO here other post algo validations can be added
150                 // more data can be sent to PceGraph module via PceResult structure if required
151                 pceResult.setRC(ResponseCodes.RESPONSE_OK);
152                 pceResult.setLocalCause(PceResult.LocalCause.NONE);
153                 return pceResult;
154             case StringConstants.SERVICE_TYPE_100GE_M:
155             case StringConstants.SERVICE_TYPE_10GE:
156             case StringConstants.SERVICE_TYPE_1GE:
157                 int tribSlotNb = Map.of(
158                         StringConstants.SERVICE_TYPE_100GE_M, 20,
159                         StringConstants.SERVICE_TYPE_10GE, 8,
160                         StringConstants.SERVICE_TYPE_1GE, 1)
161                     .get(serviceType);
162                 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
163                 pceResult.setServiceType(serviceType);
164                 Map<String, List<Uint16>> tribSlot = chooseTribSlot(path, allPceNodes, tribSlotNb);
165                 Map<String, Uint16> tribPort = chooseTribPort(path, allPceNodes, tribSlot, tribSlotNb);
166                 List<OpucnTribSlotDef> resultTribPortTribSlot = getMinMaxTpTs(tribPort, tribSlot);
167                 if (resultTribPortTribSlot.get(0) != null && resultTribPortTribSlot.get(1) != null) {
168                     pceResult.setResultTribPortTribSlot(resultTribPortTribSlot);
169                     pceResult.setRC(ResponseCodes.RESPONSE_OK);
170                     LOG.info("In PostAlgoPathValidator: found TribPort {} - tribSlot {} - tribSlotNb {}",
171                         tribPort, tribSlot, tribSlotNb);
172                 }
173                 return pceResult;
174             case StringConstants.SERVICE_TYPE_ODU4:
175             case StringConstants.SERVICE_TYPE_ODUC2:
176             case StringConstants.SERVICE_TYPE_ODUC3:
177             case StringConstants.SERVICE_TYPE_ODUC4:
178             case StringConstants.SERVICE_TYPE_100GE_S:
179                 pceResult.setRC(ResponseCodes.RESPONSE_OK);
180                 pceResult.setServiceType(serviceType);
181                 LOG.info("In PostAlgoPathValidator: ODU4/ODUCn path found {}", path);
182                 return pceResult;
183             default:
184                 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
185                 LOG.warn("In PostAlgoPathValidator checkPath: unsupported serviceType {} found {}",
186                     serviceType, path);
187                 return pceResult;
188         }
189     }
190
191     // Check the latency
192     private boolean checkLatency(Long maxLatency, GraphPath<String, PceGraphEdge> path) {
193         double latency = 0;
194         for (PceGraphEdge edge : path.getEdgeList()) {
195             if (edge.link() == null || edge.link().getLatency() == null) {
196                 LOG.warn("- In checkLatency: the link {} does not contain latency field",
197                     edge.link().getLinkId().getValue());
198                 return false;
199             }
200             latency += edge.link().getLatency();
201             LOG.debug("- In checkLatency: latency of {} = {} units", edge.link().getLinkId().getValue(), latency);
202         }
203         return (latency < maxLatency);
204     }
205
206     // Check the inclusion if it is defined in the hard constraints
207     private boolean checkInclude(GraphPath<String, PceGraphEdge> path, PceConstraints pceHardConstraintsInput) {
208         List<ResourcePair> listToInclude = pceHardConstraintsInput.getListToInclude()
209             .stream().sorted((rp1, rp2) -> rp1.getName().compareTo(rp2.getName()))
210             .collect(Collectors.toList());
211         if (listToInclude.isEmpty()) {
212             return true;
213         }
214         List<PceGraphEdge> pathEdges = path.getEdgeList();
215         LOG.debug(" in checkInclude vertex list: [{}]", path.getVertexList());
216         List<String> listOfElementsSubNode = new ArrayList<>();
217         listOfElementsSubNode.add(pathEdges.get(0).link().getsourceNetworkSupNodeId());
218         listOfElementsSubNode.addAll(
219             listOfElementsBuild(pathEdges, PceConstraints.ResourceType.NODE, pceHardConstraintsInput));
220         List<String> listOfElementsCLLI = new ArrayList<>();
221         listOfElementsCLLI.add(pathEdges.get(0).link().getsourceCLLI());
222         listOfElementsCLLI.addAll(
223             listOfElementsBuild(pathEdges, PceConstraints.ResourceType.CLLI, pceHardConstraintsInput));
224         List<String> listOfElementsSRLG = new ArrayList<>();
225         // first link is XPONDEROUTPUT, no SRLG for it
226         listOfElementsSRLG.add("NONE");
227         listOfElementsSRLG.addAll(
228             listOfElementsBuild(pathEdges, PceConstraints.ResourceType.SRLG, pceHardConstraintsInput));
229         // validation: check each type for each element
230         return listOfElementsSubNode.containsAll(
231                 listToInclude
232                     .stream().filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
233                     .map(ResourcePair::getName).collect(Collectors.toList()))
234             && listOfElementsSRLG.containsAll(
235                 listToInclude
236                     .stream().filter(rp -> PceConstraints.ResourceType.SRLG.equals(rp.getType()))
237                     .map(ResourcePair::getName).collect(Collectors.toList()))
238             && listOfElementsCLLI.containsAll(
239                 listToInclude
240                     .stream().filter(rp -> PceConstraints.ResourceType.CLLI.equals(rp.getType()))
241                     .map(ResourcePair::getName).collect(Collectors.toList()));
242     }
243
244     private List<String> listOfElementsBuild(List<PceGraphEdge> pathEdges, PceConstraints.ResourceType type,
245             PceConstraints pceHardConstraints) {
246         List<String> listOfElements = new ArrayList<>();
247         for (PceGraphEdge link : pathEdges) {
248             switch (type) {
249                 case NODE:
250                     listOfElements.add(link.link().getdestNetworkSupNodeId());
251                     break;
252                 case CLLI:
253                     listOfElements.add(link.link().getdestCLLI());
254                     break;
255                 case SRLG:
256                     if (link.link().getlinkType() != OpenroadmLinkType.ROADMTOROADM) {
257                         listOfElements.add("NONE");
258                         break;
259                     }
260                     // srlg of link is List<Long>. But in this algo we need string representation of
261                     // one SRLG
262                     // this should be any SRLG mentioned in include constraints if any of them if
263                     // mentioned
264                     boolean found = false;
265                     for (Long srlg : link.link().getsrlgList()) {
266                         String srlgStr = String.valueOf(srlg);
267                         if (pceHardConstraints.getSRLGnames().contains(srlgStr)) {
268                             listOfElements.add(srlgStr);
269                             LOG.info("listOfElementsBuild. FOUND SRLG {} in link {}", srlgStr, link.link());
270                             found = true;
271                         }
272                     }
273                     if (!found) {
274                         // there is no specific srlg to include. thus add to list just the first one
275                         listOfElements.add("NONE");
276                     }
277                     break;
278                 default:
279                     LOG.debug("listOfElementsBuild unsupported resource type");
280             }
281         }
282         return listOfElements;
283     }
284
285     private Map<String, Uint16> chooseTribPort(GraphPath<String,
286             PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, Map<String, List<Uint16>> tribSlotMap, int nbSlot) {
287         LOG.debug("In choosetribPort: edgeList = {} ", path.getEdgeList());
288         Map<String, Uint16> tribPortMap = new HashMap<>();
289         for (PceGraphEdge edge : path.getEdgeList()) {
290             List<Uint16> srcTpnPool =
291                 allPceNodes
292                     .get(edge.link().getSourceId())
293                     .getAvailableTribPorts()
294                     .get(edge.link().getSourceTP().getValue());
295             List<Uint16> destTpnPool =
296                 allPceNodes
297                     .get(edge.link().getDestId())
298                     .getAvailableTribPorts()
299                     .get(edge.link().getDestTP().getValue());
300             List<Uint16> commonEdgeTpnPool = new ArrayList<>();
301             for (Uint16 srcTpn : srcTpnPool) {
302                 if (destTpnPool.contains(srcTpn)) {
303                     commonEdgeTpnPool.add(srcTpn);
304                 }
305             }
306             if (commonEdgeTpnPool.isEmpty()) {
307                 continue;
308             }
309             Integer startTribSlot = tribSlotMap.values().stream().findFirst().get().get(0).toJava();
310             Integer tribPort = (int) Math.ceil((double)startTribSlot / nbSlot);
311             for (Uint16 commonTribPort : commonEdgeTpnPool) {
312                 if (tribPort.equals(commonTribPort.toJava())) {
313                     tribPortMap.put(edge.link().getLinkId().getValue(), commonTribPort);
314                 }
315             }
316         }
317         tribPortMap.forEach((k,v) -> LOG.info("TribPortMap : k = {}, v = {}", k, v));
318         return tribPortMap;
319     }
320
321     private Map<String, List<Uint16>> chooseTribSlot(GraphPath<String,
322             PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, int nbSlot) {
323         LOG.debug("In choosetribSlot: edgeList = {} ", path.getEdgeList());
324         Map<String, List<Uint16>> tribSlotMap = new HashMap<>();
325         for (PceGraphEdge edge : path.getEdgeList()) {
326             List<Uint16> srcTsPool =
327                 allPceNodes
328                     .get(edge.link().getSourceId())
329                     .getAvailableTribSlots()
330                     .get(edge.link().getSourceTP().getValue());
331             List<Uint16> destTsPool =
332                 allPceNodes
333                     .get(edge.link().getDestId())
334                     .getAvailableTribSlots()
335                     .get(edge.link().getDestTP().getValue());
336             List<Uint16> commonEdgeTsPoolList = new ArrayList<>();
337             List<Uint16> tribSlotList = new ArrayList<>();
338             for (Uint16 integer : srcTsPool) {
339                 if (destTsPool.contains(integer)) {
340                     commonEdgeTsPoolList.add(integer);
341                 }
342             }
343             Collections.sort(commonEdgeTsPoolList);
344             List<Uint16> commonGoodStartEdgeTsPoolList = new ArrayList<>();
345             for (Uint16 startEdgeTsPool : commonEdgeTsPoolList) {
346                 if (Integer.valueOf(1).equals(startEdgeTsPool.toJava() % nbSlot) || nbSlot == 1) {
347                     commonGoodStartEdgeTsPoolList.add(startEdgeTsPool);
348                 }
349             }
350             Collections.sort(commonGoodStartEdgeTsPoolList);
351             boolean goodTsList = false;
352             for (Uint16 goodStartTsPool : commonGoodStartEdgeTsPoolList) {
353                 int goodStartIndex = commonEdgeTsPoolList.indexOf(Uint16.valueOf(goodStartTsPool.intValue()));
354                 if (!goodTsList && commonEdgeTsPoolList.size() - goodStartIndex >= nbSlot) {
355                     for (int i = 0; i < nbSlot; i++) {
356                         if (!commonEdgeTsPoolList.get(goodStartIndex + i)
357                                 .equals(Uint16.valueOf(goodStartTsPool.toJava() + i))) {
358                             goodTsList = false;
359                             tribSlotList.clear();
360                             break;
361                         }
362                         tribSlotList.add(commonEdgeTsPoolList.get(goodStartIndex + i));
363                         goodTsList = true;
364                     }
365                 }
366             }
367             tribSlotMap.put(edge.link().getLinkId().getValue(), tribSlotList);
368         }
369         tribSlotMap.forEach((k,v) -> LOG.info("TribSlotMap : k = {}, v = {}", k, v));
370         return tribSlotMap;
371     }
372
373     private List<OpucnTribSlotDef> getMinMaxTpTs(Map<String, Uint16> tribPort, Map<String, List<Uint16>> tribSlot) {
374         String tribport = tribPort.values().toArray()[0].toString();
375         List<Uint16> tsList = (List<Uint16>) tribSlot.values().toArray()[0];
376         return new ArrayList<>(List.of(
377             OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(0).toString())),
378             OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(tsList.size() - 1).toString()))));
379     }
380
381     /**
382      * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
383      *
384      * @param path                      the AtoZ path provided by the PCE.
385      * @param allPceNode                The map of chosen/relevant PceNodes build from topology pruning.
386      * @param allPceLinks               The map of PceLinks build corresponding to the whole topology.
387      * @param serviceType               The service Type used to extrapolate Operational mode when it is not provided.
388      * @param direction                 The direction used to scan provided path in a direct or reverse way.
389      * @param cu                        CatalogUtils instance.
390      * @return the calculated margin according to the Transponder performances and path impairments.
391      */
392     @SuppressWarnings("deprecation")
393     @edu.umd.cs.findbugs.annotations.SuppressWarnings("DLS_DEAD_LOCAL_STORE")
394     private double checkOSNR(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
395             Map<LinkId, PceLink> allPceLinks, String serviceType, String direction, CatalogUtils cu) {
396         double spacing = 50.0;
397         double calcPdl2 = 0;
398         double calcOsnrdB = 0;
399         double calcCd = 0;
400         double calcPmd2 = 0;
401         double calcOnsrLin = 0.0001;
402         double margin = 0;
403         double pwrIn = -60.0;
404         double pwrOut = -60.0;
405         int pathElement = 0;
406         int increment = 1;
407         int offsetLink = 0;
408         boolean transponderPresent = false;
409         if ((StringConstants.SERVICE_DIRECTION_ZA).equals(direction)) {
410             increment = - 1;
411             offsetLink = -1;
412         }
413         CatalogNodeType cnt;
414         List<String> vertices = path.getVertexList();
415         List<PceGraphEdge> edges = path.getEdgeList();
416         String opMode = "";
417         // LOOP that scans the different Nodes/Links of the path and calculates
418         // associated degradations
419         // using CatalogUtils primitives to retrieve physical parameters and make a
420         // first level calculation
421         Map<String, Double> impairments = new HashMap<>();
422         for (int n = 0; n < vertices.size(); n++) {
423             InstanceIdentifier<TerminationPoint1> nwTpIid;
424             PceNode nextNode = null;
425             if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
426                 pathElement = n ;
427             } else {
428                 pathElement = vertices.size() - n - 1;
429             }
430             PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
431             if (((pathElement != vertices.size() - 1) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
432                     || ((pathElement != 0) && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction))) {
433                 nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + increment)));
434             }
435             LOG.debug("loop of check OSNR, n = {} Path Element = {}", n, pathElement);
436             switch (currentNode.getORNodeType()) {
437                 case XPONDER:
438                     transponderPresent = true;
439                     String nwTpId = "";
440                     if (((pathElement == 0) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
441                             || ((pathElement == (vertices.size() - 1)) && (StringConstants.SERVICE_DIRECTION_ZA)
442                                 .equals(direction))) {
443                         //First Xponder of the path TX side
444                         nwTpId = getAppropriatePceLink((pathElement + offsetLink), edges, allPceLinks, direction)
445                             .getSourceTP().getValue();
446                     } else {
447                         // last Xponder of the path (RX side)
448                         nwTpId = getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
449                         .getDestTP().getValue();
450                     }
451                     nwTpIid = InstanceIdentifiers.createNetworkTerminationPoint1IIDBuilder(
452                             vertices.get(pathElement), nwTpId);
453                     LOG.debug("loop of check OSNR : XPDR, n = {} Path Element = {}", n, pathElement);
454                     try {
455                         if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
456                                 .get().isPresent()) {
457                             XpdrNetworkAttributes xna = networkTransactionService
458                                 .read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
459                                 .get().get().getXpdrNetworkAttributes();
460                             // If the operational mode of the Xponder is not consistent or
461                             // if the operational mode of the Xponder is not declared in the topology
462                             // (Network TP)
463                             if (currentNode.getXponderOperationalMode(xna).contentEquals(StringConstants.UNKNOWN_MODE)
464                                     || currentNode.getXponderOperationalMode(xna) == null
465                                     || currentNode.getXponderOperationalMode(xna).isEmpty()) {
466                                 // Operational mode is retrieved from the service Type assuming it is supported
467                                 // by the Xponder
468                                 opMode = cu.getPceOperationalModeFromServiceType(
469                                     CatalogConstant.CatalogNodeType.TSP, serviceType);
470                             } else {
471                                 // Operational mode is found as an attribute of the network TP
472                                 opMode = currentNode.getXponderOperationalMode(xna);
473                             }
474                             LOG.debug("Transponder {} corresponding to path Element {} in the path has {} operational "
475                                     + "mode", currentNode.getNodeId().getValue(), pathElement, opMode);
476                         } else {
477                             LOG.error("Issue accessing the XponderNetworkAttributes of {} for Transponder {}"
478                                 + " corresponding to path Element {} in the path ",
479                                 nwTpId, currentNode.getNodeId().getValue(), pathElement);
480                             opMode = cu.getPceOperationalModeFromServiceType(
481                                 CatalogConstant.CatalogNodeType.TSP, serviceType);
482                             LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
483                                 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
484                         }
485                     } catch (InterruptedException | ExecutionException e1) {
486                         opMode = cu.getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP,
487                             serviceType);
488                         LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
489                             + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
490                     }
491                     // If TSP is the last of the path
492                     if (((pathElement == (vertices.size() - 1))
493                             && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
494                             || ((pathElement == 0) && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction))) {
495                         LOG.debug("Loop n = {}, Step5.1, XPDR, tries calculating Margin, just before call", n);
496                         // Check that accumulated degradations are compatible with TSP performances
497                         // According to OpenROADM spec :
498                         // margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2),
499                         //              getOsnrDbfromOnsrLin(calcOnsrLin));
500                         // Calculation modified for pdl according to calculation in Julia's Tool
501                         margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2),
502                             (Math.sqrt(calcPdl2)), getOsnrDbfromOnsrLin(calcOnsrLin));
503                         LOG.info("Loop n = {}, XPDR, calcosnrdB= {}", n, getOsnrDbfromOnsrLin(calcOnsrLin));
504                     } else {
505                         // TSP is first element of the path . To correctly evaluate the TX OOB OSNR from
506                         // its operational mode, we need to know the type of ADD/DROP Mux it is
507                         // connected to
508                         String adnMode = "";
509                         // If the operational mode of the ADD/DROP MUX is not consistent or
510                         // if the operational mode of the ADD/DROP MUX is not declared in the topology
511                         // (Network TP)
512                         if (StringConstants.UNKNOWN_MODE.equals(nextNode.getOperationalMode())
513                                 || nextNode.getOperationalMode() == null
514                                 || nextNode.getOperationalMode().isEmpty()) {
515                             // Operational mode is set by default to standard opMode for ADD SRGs
516                             adnMode = CatalogConstant.MWWRCORE;
517                         } else {
518                             // Operational mode is found in SRG attributes of the Node
519                             adnMode = nextNode.getOperationalMode();
520                         }
521                         LOG.debug("Transponder {} corresponding to path Element {} in the path is connected to SRG "
522                             + "which has {} operational mode", currentNode.getNodeId().getValue(), pathElement,
523                             adnMode);
524                         // Retrieve the Tx ONSR of the Xponder which results from IB and OOB OSNR
525                         // contributions
526                         calcOnsrLin = cu.getPceTxTspParameters(opMode, adnMode);
527                         // Retrieve the spacing associated with Xponder operational mode that is needed
528                         // to calculate OSNR
529                         spacing = cu.getPceTxTspChannelSpacing(opMode);
530                         LOG.info("Transponder {} corresponding to path Element {} in the path has a TX OSNR of {} dB",
531                             currentNode.getNodeId().getValue(), pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
532                     }
533                     break;
534                 case SRG:
535                     String srgMode = "";
536                     // If the operational mode of the ADD/DROP MUX is not consistent or
537                     // if the operational mode of the ADD/DROP MUX is not declared in the topology
538                     // (Network TP)
539                     if (StringConstants.UNKNOWN_MODE.equals(currentNode.getOperationalMode())
540                             || currentNode.getOperationalMode() == null
541                             || currentNode.getOperationalMode().isEmpty()) {
542                         // Operational mode is set by default to standard opMode for ADD/DROP SRGs
543                         srgMode = CatalogConstant.MWWRCORE;
544                     } else {
545                         // Operational mode is found in SRG attributes of the Node
546                         srgMode = currentNode.getOperationalMode();
547                     }
548                     cnt = CatalogConstant.CatalogNodeType.DROP;
549                     LOG.debug("loop of check OSNR : SRG, n = {} Path Element = {}", n, pathElement);
550                     if ((pathElement <= 1) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction)
551                             || (pathElement >= vertices.size() - 2)
552                             && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction)) {
553                         // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
554                         // Xponder to Xponder) node element of the path is the ADD SRG.
555                         if (!(getAppropriatePceLink((pathElement + offsetLink), edges, allPceLinks, direction)
556                                 .getlinkType() == OpenroadmLinkType.ADDLINK)) {
557                             LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
558                                 currentNode.getNodeId().toString(), pathElement + offsetLink);
559                         }
560                         cnt = CatalogConstant.CatalogNodeType.ADD;
561                         pwrIn = 0.0;
562                         pwrOut = cu.getPceRoadmAmpOutputPower(cnt, srgMode,
563                             getAppropriatePceLink((pathElement + 1 + offsetLink * 3), edges, allPceLinks, direction)
564                             .getspanLoss(), spacing,
565                             getAppropriatePceLink((pathElement + 1 + offsetLink * 3), edges, allPceLinks, direction)
566                             .getpowerCorrection());
567                         LOG.debug("loop of check OSNR : SRG, n = {} link {} Pout = {}",
568                             pathElement, pathElement + 1 + offsetLink * 3, pwrOut);
569                     } else {
570                         // Other case is DROP, for which cnt is unchanged (.DROP)
571                         if (!(getAppropriatePceLink((pathElement - 1 - offsetLink), edges, allPceLinks, direction)
572                                 .getlinkType() == OpenroadmLinkType.DROPLINK)) {
573                             LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
574                                 currentNode.getNodeId().toString(), pathElement - 1 - offsetLink);
575                         }
576                         pwrIn = pwrOut - getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
577                             direction).getspanLoss();
578                         // Calculate degradation accumulated across incoming Link and add them to
579                         // accumulated impairments
580                         calcCd += getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
581                             direction).getcd();
582                         LOG.info("loop of check OSNR : SRG, n = {} CD on preceeding link {} = {} ps", pathElement,
583                             pathElement - offsetLink * 3 - 2, getAppropriatePceLink((pathElement - offsetLink * 3 - 2),
584                                 edges, allPceLinks, direction).getcd());
585                         calcPmd2 += getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
586                             direction).getpmd2();
587                         // This also includes Non Linear Contribution from the path
588                         calcOnsrLin += cu.calculateNLonsrContribution(pwrOut, getAppropriatePceLink((pathElement
589                             - offsetLink * 3 - 2), edges, allPceLinks, direction).getLength(), spacing);
590                     }
591                     //calculation of the SRG contribution either for Add and Drop
592                     impairments = cu.getPceRoadmAmpParameters(cnt, srgMode,
593                         pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
594                     calcCd = impairments.get("CD").doubleValue();
595                     calcPmd2 = impairments.get("DGD2").doubleValue();
596                     calcPdl2 = impairments.get("PDL2").doubleValue();
597                     calcOnsrLin = impairments.get("ONSRLIN").doubleValue();
598                     if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
599                         return -1.0;
600                     }
601                     if (pathElement > 1) {
602                         // If SRG is not the first or the second element of the Path, it is the DROP
603                         // side.
604                         // After accumulated degradations are calculated, we also need to calculate
605                         // resulting OSNR in dB to pass it to the method that verifies end Xponder
606                         // performances are compatible with degradations experienced on the path
607                         try {
608                             calcOsnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
609                             LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOsnrdB);
610                             LOG.info("Loop n = {}, DROP, calcOsnrdB= {}", n, calcOsnrdB);
611                         } catch (ArithmeticException e) {
612                             LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
613                                 path.getEdgeList().size());
614                             return -1.0;
615                         }
616                     }
617                     if (CatalogConstant.CatalogNodeType.ADD.equals(cnt)) {
618                         // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
619                         // The Degree is not considered. This means we must bypass the add-link (ADD)
620                         // and the next node (Degree) which are not considered in the impairments.
621                         n++;
622                     }
623                     impairments.clear();
624                     break;
625                 case DEGREE:
626                     if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
627                         //This is the case of DROP, ROADM degree is not considered
628                         break;
629                     }
630                     LOG.info("loop of check OSNR : DEGREE, n = {} Path Element = {}", n, pathElement);
631                     cnt = CatalogConstant.CatalogNodeType.EXPRESS;
632                     String degree1Mode = "";
633                     String degree2Mode = "";
634                     // If the operational mode of the Degree is not consistent or if the operational
635                     // mode is not declared in the topology
636                     if (StringConstants.UNKNOWN_MODE.equals(currentNode.getOperationalMode())
637                             || currentNode.getOperationalMode() == null
638                             || currentNode.getOperationalMode().isEmpty()) {
639                         // Operational mode is set by default to standard opMode for Degree
640                         degree1Mode = CatalogConstant.MWMWCORE;
641                     } else {
642                         // Operational mode is found in degree-attributes of the Node
643                         degree1Mode = currentNode.getOperationalMode();
644                     }
645                     // Same for next node which is the second degree of a ROADM node
646                     if (StringConstants.UNKNOWN_MODE.equals(nextNode.getOperationalMode())
647                             || nextNode.getOperationalMode() == null
648                             || nextNode.getOperationalMode().isEmpty()) {
649                         degree2Mode = CatalogConstant.MWMWCORE;
650                     } else {
651                         degree2Mode = currentNode.getOperationalMode();
652                     }
653                     // At that time OpenROADM provides only one spec for the ROADM nodes
654                     if (!degree1Mode.equals(degree2Mode)) {
655                         LOG.info("Unsupported Hybrid ROADM configuration with Degree1 {} of {} operational mode"
656                             + "and Degree2 {} of {} operational mode. Will by default use operational mode"
657                             + "of Degree2", currentNode.getNodeId().toString(),
658                             degree1Mode, nextNode.getNodeId().toString(), degree2Mode);
659                     }
660                     pwrIn = pwrOut - getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks,
661                         direction).getspanLoss();
662                     // Calculate degradation accumulated across incoming Link and add them to
663                     // accumulated impairments
664                     calcCd += getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
665                         .getcd();
666                     calcPmd2 += getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
667                         .getpmd2();
668                     // This also includes Non Linear Contribution from the path
669                     calcOnsrLin += cu.calculateNLonsrContribution(pwrOut, getAppropriatePceLink((pathElement
670                         - offsetLink - 1), edges, allPceLinks, direction).getLength(), spacing);
671                     // Calculate output power for next span (Output of degree 2)
672                     pwrOut = cu.getPceRoadmAmpOutputPower(cnt, degree2Mode, getAppropriatePceLink((pathElement
673                         + 3 * offsetLink + 1), edges, allPceLinks, direction).getspanLoss(), spacing,
674                         getAppropriatePceLink((pathElement + 3 * offsetLink + 1), edges, allPceLinks, direction)
675                         .getpowerCorrection());
676                     // Adds to accumulated impairments the degradation associated with the Express
677                     // path of ROADM : Degree1, express link, Degree2
678                     impairments = cu.getPceRoadmAmpParameters(cnt, degree2Mode,
679                         pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
680                     calcCd = impairments.get("CD").doubleValue();
681                     calcPmd2 = impairments.get("DGD2").doubleValue();
682                     calcPdl2 = impairments.get("PDL2").doubleValue();
683                     calcOnsrLin = impairments.get("ONSRLIN").doubleValue();
684                     LOG.debug("Loop n = {}, DEGREE, calcOsnrdB= {}", n, getOsnrDbfromOnsrLin(calcOnsrLin));
685                     if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
686                         return -1.0;
687                     }
688                     // increment pathElement so that in next step we will not point to Degree2 but
689                     // next node
690                     n++;
691                     LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
692                         + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId().toString(),
693                         nextNode.getNodeId().toString(), calcCd, calcPmd2, calcPdl2, getOsnrDbfromOnsrLin(calcOnsrLin));
694                     break;
695                 default:
696                     LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
697             }
698         }
699         LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOsnrdB = {} "
700             + "dB and ONSR dB exterapolated from calcosnrlin = {}"
701             + " including non linear contributions",
702             calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2), calcOsnrdB, getOsnrDbfromOnsrLin(calcOnsrLin));
703         if (!transponderPresent) {
704             LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
705                 + "that optical tunnel degradations are compatible with external transponder performances");
706             return 0.0;
707         }
708         LOG.info("In checkOSNR: Transponder Operational mode {} results in a residual margin of {} dB, according "
709             + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
710             opMode, margin - SYS_MARGIN, SYS_MARGIN);
711         String validationMessage = "INVALIDATED";
712         if ((margin - SYS_MARGIN) >= 0) {
713             validationMessage = "VALIDATED";
714         }
715         if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
716             LOG.info("- In checkOSNR: A to Z Path from {} to {} {}", vertices.get(0),
717                 vertices.get(vertices.size() - 1), validationMessage);
718         } else {
719             LOG.info("- In checkOSNR: Z to A Path from {} to {} {}", vertices.get(vertices.size() - 1),
720                 vertices.get(0), validationMessage);
721         }
722         return (margin - SYS_MARGIN);
723     }
724
725     // Method to provide either regular link (AtoZ) or Opposite link (ZtoA) in the list of PceGraphEdges
726     private PceLink getAppropriatePceLink(Integer pathEltNber, List<PceGraphEdge> edges,
727             Map<LinkId, PceLink> allPceLinks, String direction) {
728         if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
729             // Returns regular link.
730             return edges.get(pathEltNber).link();
731         }
732             //For Z to A direction, must return the opposite link
733         return allPceLinks.get(new LinkId(edges.get(pathEltNber).link().getOppositeLink()));
734     }
735
736     private double getOsnrDbfromOnsrLin(double onsrLu) {
737         return (10 * Math.log10(1 / onsrLu));
738     }
739
740     /**
741      * Get spectrum assignment for path.
742      *
743      * @param path                    the path for which we get spectrum assignment.
744      * @param allPceNodes             all optical nodes.
745      * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
746      *                                service type.
747      * @return a spectrum assignment object which contains begin and end index. If
748      *         no spectrum assignment found, beginIndex = stopIndex = 0
749      */
750     private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
751             Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
752         byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
753         Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
754         BitSet result = BitSet.valueOf(freqMap);
755         boolean isFlexGrid = true;
756         LOG.debug("Processing path {} with length {}", path, path.getLength());
757         for (PceGraphEdge edge : path.getEdgeList()) {
758             NodeId srcNodeId = edge.link().getSourceId();
759             LOG.debug("Processing source {} ", srcNodeId);
760             if (allPceNodes.containsKey(srcNodeId)) {
761                 PceNode pceNode = allPceNodes.get(srcNodeId);
762                 LOG.debug("Processing PCE node {}", pceNode);
763                 String pceNodeVersion = pceNode.getVersion();
764                 NodeId pceNodeId = pceNode.getNodeId();
765                 BigDecimal sltWdthGran = pceNode.getSlotWidthGranularity();
766                 BigDecimal ctralFreqGran = pceNode.getCentralFreqGranularity();
767                 if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNodeVersion)) {
768                     LOG.debug("Node {}: version is {} and slot width granularity is {} -> fixed grid mode",
769                         pceNodeId, pceNodeVersion, sltWdthGran);
770                     isFlexGrid = false;
771                 }
772                 if (sltWdthGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)
773                         && ctralFreqGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
774                     LOG.debug("Node {}: version is {} with slot width granularity {} and central "
775                             + "frequency granularity is {} -> fixed grid mode",
776                         pceNodeId, pceNodeVersion, sltWdthGran, ctralFreqGran);
777                     isFlexGrid = false;
778                 }
779                 BitSet pceNodeFreqMap = pceNode.getBitSetData();
780                 LOG.debug("Pce node bitset {}", pceNodeFreqMap);
781                 if (pceNodeFreqMap != null) {
782                     result.and(pceNodeFreqMap);
783                     LOG.debug("intermediate bitset {}", result);
784                 }
785             }
786         }
787         LOG.debug("Bitset result {}", result);
788         return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
789     }
790
791     /**
792      * Compute spectrum assignment from spectrum occupation for spectral width.
793      *
794      * @param spectrumOccupation      the spectrum occupation BitSet.
795      * @param spectralWidthSlotNumber the nb slots for spectral width.
796      * @param isFlexGrid              true if flexible grid, false otherwise.
797      * @return a spectrum assignment object which contains begin and stop index. If
798      *         no spectrum assignment found, beginIndex = stopIndex = 0
799      */
800     private SpectrumAssignment computeBestSpectrumAssignment(
801             BitSet spectrumOccupation, int spectralWidthSlotNumber, boolean isFlexGrid) {
802         SpectrumAssignmentBuilder spectrumAssignmentBldr = new SpectrumAssignmentBuilder()
803             .setBeginIndex(Uint16.valueOf(0))
804             .setStopIndex(Uint16.valueOf(0))
805             .setFlexGrid(isFlexGrid);
806         BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
807         referenceBitSet.set(0, spectralWidthSlotNumber);
808         //higher is the frequency, smallest is the wavelength number
809         //in operational, the allocation is done through wavelength starting from the smallest
810         //so we have to loop from the last element of the spectrum occupation
811         for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber;
812                 i -= isFlexGrid ? spectralWidthSlotNumber : 1) {
813             if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
814                 spectrumAssignmentBldr.setBeginIndex(Uint16.valueOf(i - spectralWidthSlotNumber));
815                 spectrumAssignmentBldr.setStopIndex(Uint16.valueOf(i - 1));
816                 break;
817             }
818         }
819         return spectrumAssignmentBldr.build();
820     }
821
822     public Double getTpceCalculatedMargin() {
823         return tpceCalculatedMargin;
824     }
825 }