2 * Copyright © 2017 AT&T, Inc. and others. All rights reserved.
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
9 package org.opendaylight.transportpce.pce.graph;
11 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
12 import java.math.RoundingMode;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.BitSet;
16 import java.util.Collections;
17 import java.util.HashMap;
18 import java.util.List;
20 import java.util.concurrent.ExecutionException;
21 import java.util.stream.Collectors;
22 import org.jgrapht.GraphPath;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.transportpce.common.InstanceIdentifiers;
25 import org.opendaylight.transportpce.common.ResponseCodes;
26 import org.opendaylight.transportpce.common.StringConstants;
27 import org.opendaylight.transportpce.common.catalog.CatalogConstant;
28 import org.opendaylight.transportpce.common.catalog.CatalogConstant.CatalogNodeType;
29 import org.opendaylight.transportpce.common.catalog.CatalogUtils;
30 import org.opendaylight.transportpce.common.fixedflex.GridConstant;
31 import org.opendaylight.transportpce.common.fixedflex.GridUtils;
32 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
33 import org.opendaylight.transportpce.pce.constraints.PceConstraints;
34 import org.opendaylight.transportpce.pce.constraints.PceConstraints.ResourcePair;
35 import org.opendaylight.transportpce.pce.networkanalyzer.PceLink;
36 import org.opendaylight.transportpce.pce.networkanalyzer.PceNode;
37 import org.opendaylight.transportpce.pce.networkanalyzer.PceResult;
38 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.SpectrumAssignment;
39 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev220808.SpectrumAssignmentBuilder;
40 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev211210.TerminationPoint1;
41 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev211210.networks.network.node.termination.point.XpdrNetworkAttributes;
42 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev211210.OpenroadmLinkType;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev211210.OpenroadmNodeType;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.otn.common.types.rev210924.OpucnTribSlotDef;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.LinkId;
47 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
48 import org.opendaylight.yangtools.yang.common.Uint16;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
52 public class PostAlgoPathValidator {
54 private static final Logger LOG = LoggerFactory.getLogger(PostAlgoPathValidator.class);
56 public static final Long CONST_OSNR = 1L;
57 public static final double SYS_MARGIN = 0;
58 private Double tpceCalculatedMargin = 0.0;
59 private final NetworkTransactionService networkTransactionService;
61 public PostAlgoPathValidator(NetworkTransactionService networkTransactionService) {
62 this.networkTransactionService = networkTransactionService;
65 @SuppressWarnings("fallthrough")
67 value = "SF_SWITCH_FALLTHROUGH",
68 justification = "intentional fallthrough")
70 public PceResult checkPath(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
71 Map<LinkId, PceLink> allPceLinks, PceResult pceResult, PceConstraints pceHardConstraints,
73 LOG.info("path = {}", path);
74 // check if the path is empty
75 if (path.getEdgeList().isEmpty()) {
76 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
79 int spectralWidthSlotNumber = GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP
80 .getOrDefault(serviceType, GridConstant.NB_SLOTS_100G);
81 SpectrumAssignment spectrumAssignment = null;
82 //variable to deal with 1GE (Nb=1) and 10GE (Nb=10) cases
83 switch (serviceType) {
84 case StringConstants.SERVICE_TYPE_OTUC2:
85 case StringConstants.SERVICE_TYPE_OTUC3:
86 case StringConstants.SERVICE_TYPE_OTUC4:
87 case StringConstants.SERVICE_TYPE_400GE:
88 spectralWidthSlotNumber = GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP
89 .getOrDefault(serviceType, GridConstant.NB_SLOTS_400G);
91 case StringConstants.SERVICE_TYPE_100GE_T:
92 case StringConstants.SERVICE_TYPE_OTU4:
93 spectrumAssignment = getSpectrumAssignment(path, allPceNodes, spectralWidthSlotNumber);
94 pceResult.setServiceType(serviceType);
95 if (spectrumAssignment.getBeginIndex().equals(Uint16.valueOf(0))
96 && spectrumAssignment.getStopIndex().equals(Uint16.valueOf(0))) {
97 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
98 pceResult.setLocalCause(PceResult.LocalCause.NO_PATH_EXISTS);
101 if (spectrumAssignment.getFlexGrid()) {
102 LOG.debug("Spectrum assignment flexgrid mode");
103 pceResult.setResultWavelength(GridConstant.IRRELEVANT_WAVELENGTH_NUMBER);
105 LOG.debug("Spectrum assignment fixedgrid mode");
106 pceResult.setResultWavelength(
107 GridUtils.getWaveLengthIndexFromSpectrumAssigment(spectrumAssignment.getBeginIndex()
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);
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);
127 this.tpceCalculatedMargin = Math.min(margin1, margin2);
129 "In PostAlgoPathValidator: Minimum margin estimated by tpce on AtoZ and ZtoA path is of {} dB",
130 this.tpceCalculatedMargin);
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");
137 // Check if MaxLatency is defined in the hard constraints
138 if ((pceHardConstraints.getMaxLatency() != -1)
139 && (!checkLatency(pceHardConstraints.getMaxLatency(), path))) {
140 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
141 pceResult.setLocalCause(PceResult.LocalCause.TOO_HIGH_LATENCY);
145 // Check if nodes are included in the hard constraints
146 if (!checkInclude(path, pceHardConstraints)) {
147 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
148 pceResult.setLocalCause(PceResult.LocalCause.HD_NODE_INCLUDE);
152 // TODO here other post algo validations can be added
153 // 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);
158 case StringConstants.SERVICE_TYPE_100GE_M:
159 case StringConstants.SERVICE_TYPE_10GE:
160 case StringConstants.SERVICE_TYPE_1GE:
161 Map<String, Integer> tribSlotNbMap = Map.of(
162 StringConstants.SERVICE_TYPE_100GE_M, 20,
163 StringConstants.SERVICE_TYPE_10GE, 8,
164 StringConstants.SERVICE_TYPE_1GE, 1);
165 int tribSlotNb = tribSlotNbMap.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);
172 if (resultTribPortTribSlot.get(0) != null && resultTribPortTribSlot.get(1) != null) {
173 pceResult.setResultTribPortTribSlot(resultTribPortTribSlot);
174 pceResult.setRC(ResponseCodes.RESPONSE_OK);
175 LOG.info("In PostAlgoPathValidator: found TribPort {} - tribSlot {} - tribSlotNb {}",
176 tribPort, tribSlot, tribSlotNb);
179 case StringConstants.SERVICE_TYPE_ODU4:
180 case StringConstants.SERVICE_TYPE_ODUC2:
181 case StringConstants.SERVICE_TYPE_ODUC3:
182 case StringConstants.SERVICE_TYPE_ODUC4:
183 case StringConstants.SERVICE_TYPE_100GE_S:
184 pceResult.setRC(ResponseCodes.RESPONSE_OK);
185 pceResult.setServiceType(serviceType);
186 LOG.info("In PostAlgoPathValidator: ODU4/ODUCn path found {}", path);
189 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
190 LOG.warn("In PostAlgoPathValidator checkPath: unsupported serviceType {} found {}",
198 private boolean checkLatency(Long maxLatency, GraphPath<String, PceGraphEdge> path) {
201 for (PceGraphEdge edge : path.getEdgeList()) {
202 if (edge.link() == null || edge.link().getLatency() == null) {
203 LOG.warn("- In checkLatency: the link {} does not contain latency field",
204 edge.link().getLinkId().getValue());
207 latency += edge.link().getLatency();
208 LOG.debug("- In checkLatency: latency of {} = {} units", edge.link().getLinkId().getValue(), latency);
210 return (latency < maxLatency);
213 // Check the inclusion if it is defined in the hard constraints
214 private boolean checkInclude(GraphPath<String, PceGraphEdge> path, PceConstraints pceHardConstraintsInput) {
215 List<ResourcePair> listToInclude = pceHardConstraintsInput.getListToInclude()
216 .stream().sorted((rp1, rp2) -> rp1.getName().compareTo(rp2.getName()))
217 .collect(Collectors.toList());
218 if (listToInclude.isEmpty()) {
222 List<PceGraphEdge> pathEdges = path.getEdgeList();
223 LOG.debug(" in checkInclude vertex list: [{}]", path.getVertexList());
225 List<String> listOfElementsSubNode = new ArrayList<>();
226 listOfElementsSubNode.add(pathEdges.get(0).link().getsourceNetworkSupNodeId());
227 listOfElementsSubNode.addAll(listOfElementsBuild(pathEdges, PceConstraints.ResourceType.NODE,
228 pceHardConstraintsInput));
230 List<String> listOfElementsCLLI = new ArrayList<>();
231 listOfElementsCLLI.add(pathEdges.get(0).link().getsourceCLLI());
232 listOfElementsCLLI.addAll(listOfElementsBuild(pathEdges, PceConstraints.ResourceType.CLLI,
233 pceHardConstraintsInput));
235 List<String> listOfElementsSRLG = new ArrayList<>();
236 // first link is XPONDEROUTPUT, no SRLG for it
237 listOfElementsSRLG.add("NONE");
238 listOfElementsSRLG.addAll(listOfElementsBuild(pathEdges, PceConstraints.ResourceType.SRLG,
239 pceHardConstraintsInput));
241 // validation: check each type for each element
242 List<String> listNodeToInclude = listToInclude
243 .stream().filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
244 .map(ResourcePair::getName).collect(Collectors.toList());
245 List<String> listSrlgToInclude = listToInclude
246 .stream().filter(rp -> PceConstraints.ResourceType.SRLG.equals(rp.getType()))
247 .map(ResourcePair::getName).collect(Collectors.toList());
248 List<String> listClliToInclude = listToInclude
249 .stream().filter(rp -> PceConstraints.ResourceType.CLLI.equals(rp.getType()))
250 .map(ResourcePair::getName).collect(Collectors.toList());
252 return listOfElementsSubNode.containsAll(listNodeToInclude)
253 && listOfElementsSRLG.containsAll(listSrlgToInclude)
254 && listOfElementsCLLI.containsAll(listClliToInclude);
257 private List<String> listOfElementsBuild(List<PceGraphEdge> pathEdges, PceConstraints.ResourceType type,
258 PceConstraints pceHardConstraints) {
260 List<String> listOfElements = new ArrayList<>();
261 for (PceGraphEdge link : pathEdges) {
264 listOfElements.add(link.link().getdestNetworkSupNodeId());
267 listOfElements.add(link.link().getdestCLLI());
270 if (link.link().getlinkType() != OpenroadmLinkType.ROADMTOROADM) {
271 listOfElements.add("NONE");
274 // srlg of link is List<Long>. But in this algo we need string representation of
276 // this should be any SRLG mentioned in include constraints if any of them if
278 boolean found = false;
279 for (Long srlg : link.link().getsrlgList()) {
280 String srlgStr = String.valueOf(srlg);
281 if (pceHardConstraints.getSRLGnames().contains(srlgStr)) {
282 listOfElements.add(srlgStr);
283 LOG.info("listOfElementsBuild. FOUND SRLG {} in link {}", srlgStr, link.link());
288 // there is no specific srlg to include. thus add to list just the first one
289 listOfElements.add("NONE");
293 LOG.debug("listOfElementsBuild unsupported resource type");
296 return listOfElements;
299 private Map<String, Uint16> chooseTribPort(GraphPath<String,
300 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, Map<String, List<Uint16>> tribSlotMap, int nbSlot) {
301 LOG.debug("In choosetribPort: edgeList = {} ", path.getEdgeList());
302 Map<String, Uint16> tribPortMap = new HashMap<>();
304 for (PceGraphEdge edge : path.getEdgeList()) {
305 NodeId linkSrcNode = edge.link().getSourceId();
306 String linkSrcTp = edge.link().getSourceTP().getValue();
307 NodeId linkDestNode = edge.link().getDestId();
308 String linkDestTp = edge.link().getDestTP().getValue();
309 PceNode pceOtnNodeSrc = allPceNodes.get(linkSrcNode);
310 PceNode pceOtnNodeDest = allPceNodes.get(linkDestNode);
311 List<Uint16> srcTpnPool = pceOtnNodeSrc.getAvailableTribPorts().get(linkSrcTp);
312 List<Uint16> destTpnPool = pceOtnNodeDest.getAvailableTribPorts().get(linkDestTp);
313 List<Uint16> commonEdgeTpnPool = new ArrayList<>();
314 for (Uint16 srcTpn : srcTpnPool) {
315 if (destTpnPool.contains(srcTpn)) {
316 commonEdgeTpnPool.add(srcTpn);
320 if (!commonEdgeTpnPool.isEmpty()) {
321 Integer startTribSlot = tribSlotMap.values().stream().findFirst().get().get(0).toJava();
322 Integer tribPort = (int) Math.ceil((double)startTribSlot / nbSlot);
323 for (Uint16 commonTribPort : commonEdgeTpnPool) {
324 if (tribPort.equals(commonTribPort.toJava())) {
325 tribPortMap.put(edge.link().getLinkId().getValue(), commonTribPort);
330 tribPortMap.forEach((k,v) -> LOG.info("TribPortMap : k = {}, v = {}", k, v));
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<>();
339 for (PceGraphEdge edge : path.getEdgeList()) {
340 NodeId linkSrcNode = edge.link().getSourceId();
341 String linkSrcTp = edge.link().getSourceTP().getValue();
342 NodeId linkDestNode = edge.link().getDestId();
343 String linkDestTp = edge.link().getDestTP().getValue();
344 PceNode pceOtnNodeSrc = allPceNodes.get(linkSrcNode);
345 PceNode pceOtnNodeDest = allPceNodes.get(linkDestNode);
346 List<Uint16> srcTsPool = pceOtnNodeSrc.getAvailableTribSlots().get(linkSrcTp);
347 List<Uint16> destTsPool = pceOtnNodeDest.getAvailableTribSlots().get(linkDestTp);
348 List<Uint16> commonEdgeTsPoolList = new ArrayList<>();
349 List<Uint16> tribSlotList = new ArrayList<>();
350 for (Uint16 integer : srcTsPool) {
351 if (destTsPool.contains(integer)) {
352 commonEdgeTsPoolList.add(integer);
355 Collections.sort(commonEdgeTsPoolList);
356 List<Uint16> commonGoodStartEdgeTsPoolList = new ArrayList<>();
357 for (Uint16 startEdgeTsPool : commonEdgeTsPoolList) {
358 if (Integer.valueOf(1).equals(startEdgeTsPool.toJava() % nbSlot)
360 commonGoodStartEdgeTsPoolList.add(startEdgeTsPool);
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))) {
372 tribSlotList.clear();
375 tribSlotList.add(commonEdgeTsPoolList.get(goodStartIndex + i));
380 tribSlotMap.put(edge.link().getLinkId().getValue(), tribSlotList);
382 tribSlotMap.forEach((k,v) -> LOG.info("TribSlotMap : k = {}, v = {}", k, v));
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")
389 List<Uint16> tsList = (List<Uint16>) tribSlot.values().toArray()[0];
390 OpucnTribSlotDef minOpucnTs = OpucnTribSlotDef
391 .getDefaultInstance(String.join(".", tribport, tsList.get(0).toString()));
392 OpucnTribSlotDef maxOpucnTs = OpucnTribSlotDef
393 .getDefaultInstance(String.join(".", tribport, tsList.get(tsList.size() - 1).toString()));
394 List<OpucnTribSlotDef> minmaxTpTsList = new ArrayList<>();
395 minmaxTpTsList.add(minOpucnTs);
396 minmaxTpTsList.add(maxOpucnTs);
397 return minmaxTpTsList;
401 * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
403 * @param path the AtoZ path provided by the PCE.
404 * @param allPceNode The map of chosen/relevant PceNodes build from topology pruning.
405 * @param allPceLinks The map of PceLinks build corresponding to the whole topology.
406 * @param serviceType The service Type used to extrapolate Operational mode when it is not provided.
407 * @param direction The direction used to scan provided path in a direct or reverse way.
408 * @param cu CatalogUtils instance.
409 * @return the calculated margin according to the Transponder performances and path impairments.
411 @SuppressWarnings("deprecation")
412 @edu.umd.cs.findbugs.annotations.SuppressWarnings("DLS_DEAD_LOCAL_STORE")
413 private double checkOSNR(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
414 Map<LinkId, PceLink> allPceLinks, String serviceType, String direction, CatalogUtils cu) {
415 double spacing = 50.0;
417 double calcOsnrdB = 0;
420 double calcOnsrLin = 0.0001;
422 double pwrIn = -60.0;
423 double pwrOut = -60.0;
427 boolean transponderPresent = false;
428 if ((StringConstants.SERVICE_DIRECTION_ZA).equals(direction)) {
433 List<String> vertices = path.getVertexList();
434 List<PceGraphEdge> edges = path.getEdgeList();
436 // LOOP that scans the different Nodes/Links of the path and calculates
437 // associated degradations
438 // using CatalogUtils primitives to retrieve physical parameters and make a
439 // first level calculation
440 Map<String, Double> impairments = new HashMap<>();
441 for (int n = 0; n < vertices.size(); n++) {
442 InstanceIdentifier<TerminationPoint1> nwTpIid;
443 PceNode nextNode = null;
444 if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
447 pathElement = vertices.size() - n - 1;
449 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
450 if (((pathElement != vertices.size() - 1) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
451 || ((pathElement != 0) && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction))) {
452 nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + increment)));
454 LOG.debug("loop of check OSNR, n = {} Path Element = {}", n, pathElement);
455 switch (currentNode.getORNodeType()) {
457 transponderPresent = true;
459 if (((pathElement == 0) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
460 || ((pathElement == (vertices.size() - 1)) && (StringConstants.SERVICE_DIRECTION_ZA)
461 .equals(direction))) {
462 //First Xponder of the path TX side
463 nwTpId = getAppropriatePceLink((pathElement + offsetLink), edges, allPceLinks, direction)
464 .getSourceTP().getValue();
466 // last Xponder of the path (RX side)
467 nwTpId = getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
468 .getDestTP().getValue();
470 nwTpIid = InstanceIdentifiers.createNetworkTerminationPoint1IIDBuilder(
471 vertices.get(pathElement), nwTpId);
472 LOG.debug("loop of check OSNR : XPDR, n = {} Path Element = {}", n, pathElement);
474 if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
475 .get().isPresent()) {
476 XpdrNetworkAttributes xna = networkTransactionService
477 .read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
478 .get().get().getXpdrNetworkAttributes();
479 // If the operational mode of the Xponder is not consistent or
480 // if the operational mode of the Xponder is not declared in the topology
482 if (currentNode.getXponderOperationalMode(xna).contentEquals(StringConstants.UNKNOWN_MODE)
483 || currentNode.getXponderOperationalMode(xna) == null
484 || currentNode.getXponderOperationalMode(xna).isEmpty()) {
485 // Operational mode is retrieved from the service Type assuming it is supported
487 opMode = cu.getPceOperationalModeFromServiceType(
488 CatalogConstant.CatalogNodeType.TSP, serviceType);
490 // Operational mode is found as an attribute of the network TP
491 opMode = currentNode.getXponderOperationalMode(xna);
493 LOG.debug("Transponder {} corresponding to path Element {} in the path has {} operational "
494 + "mode", currentNode.getNodeId().getValue(), pathElement, opMode);
496 LOG.error("Issue accessing the XponderNetworkAttributes of {} for Transponder {}"
497 + " corresponding to path Element {} in the path ",
498 nwTpId, currentNode.getNodeId().getValue(), pathElement);
499 opMode = cu.getPceOperationalModeFromServiceType(
500 CatalogConstant.CatalogNodeType.TSP, serviceType);
501 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
502 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
504 } catch (InterruptedException | ExecutionException e1) {
505 opMode = cu.getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP,
507 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
508 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
510 // If TSP is the last of the path
511 if (((pathElement == (vertices.size() - 1))
512 && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
513 || ((pathElement == 0) && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction))) {
514 LOG.debug("Loop n = {}, Step5.1, XPDR, tries calculating Margin, just before call", n);
515 // Check that accumulated degradations are compatible with TSP performances
516 // According to OpenROADM spec :
517 // margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2),
518 // getOsnrDbfromOnsrLin(calcOnsrLin));
519 // Calculation modified for pdl according to calculation in Julia's Tool
520 margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2),
521 (Math.sqrt(calcPdl2)), getOsnrDbfromOnsrLin(calcOnsrLin));
522 LOG.info("Loop n = {}, XPDR, calcosnrdB= {}", n, getOsnrDbfromOnsrLin(calcOnsrLin));
524 // TSP is first element of the path . To correctly evaluate the TX OOB OSNR from
525 // its operational mode, we need to know the type of ADD/DROP Mux it is
528 // If the operational mode of the ADD/DROP MUX is not consistent or
529 // if the operational mode of the ADD/DROP MUX is not declared in the topology
531 if (StringConstants.UNKNOWN_MODE.equals(nextNode.getOperationalMode())
532 || nextNode.getOperationalMode() == null
533 || nextNode.getOperationalMode().isEmpty()) {
534 // Operational mode is set by default to standard opMode for ADD SRGs
535 adnMode = CatalogConstant.MWWRCORE;
537 // Operational mode is found in SRG attributes of the Node
538 adnMode = nextNode.getOperationalMode();
540 LOG.debug("Transponder {} corresponding to path Element {} in the path is connected to SRG "
541 + "which has {} operational mode", currentNode.getNodeId().getValue(), pathElement,
543 // Retrieve the Tx ONSR of the Xponder which results from IB and OOB OSNR
545 calcOnsrLin = cu.getPceTxTspParameters(opMode, adnMode);
546 // Retrieve the spacing associated with Xponder operational mode that is needed
548 spacing = cu.getPceTxTspChannelSpacing(opMode);
549 LOG.info("Transponder {} corresponding to path Element {} in the path has a TX OSNR of {} dB",
550 currentNode.getNodeId().getValue(), pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
555 // If the operational mode of the ADD/DROP MUX is not consistent or
556 // if the operational mode of the ADD/DROP MUX is not declared in the topology
558 if (StringConstants.UNKNOWN_MODE.equals(currentNode.getOperationalMode())
559 || currentNode.getOperationalMode() == null
560 || currentNode.getOperationalMode().isEmpty()) {
561 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
562 srgMode = CatalogConstant.MWWRCORE;
564 // Operational mode is found in SRG attributes of the Node
565 srgMode = currentNode.getOperationalMode();
567 cnt = CatalogConstant.CatalogNodeType.DROP;
568 LOG.debug("loop of check OSNR : SRG, n = {} Path Element = {}", n, pathElement);
569 if ((pathElement <= 1) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction)
570 || (pathElement >= vertices.size() - 2)
571 && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction)) {
572 // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
573 // Xponder to Xponder) node element of the path is the ADD SRG.
574 if (!(getAppropriatePceLink((pathElement + offsetLink), edges, allPceLinks, direction)
575 .getlinkType() == OpenroadmLinkType.ADDLINK)) {
576 LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
577 currentNode.getNodeId().toString(), pathElement + offsetLink);
579 cnt = CatalogConstant.CatalogNodeType.ADD;
581 pwrOut = cu.getPceRoadmAmpOutputPower(cnt, srgMode,
582 getAppropriatePceLink((pathElement + 1 + offsetLink * 3), edges, allPceLinks, direction)
583 .getspanLoss(), spacing,
584 getAppropriatePceLink((pathElement + 1 + offsetLink * 3), edges, allPceLinks, direction)
585 .getpowerCorrection());
586 LOG.debug("loop of check OSNR : SRG, n = {} link {} Pout = {}",
587 pathElement, pathElement + 1 + offsetLink * 3, pwrOut);
589 // Other case is DROP, for which cnt is unchanged (.DROP)
590 if (!(getAppropriatePceLink((pathElement - 1 - offsetLink), edges, allPceLinks, direction)
591 .getlinkType() == OpenroadmLinkType.DROPLINK)) {
592 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
593 currentNode.getNodeId().toString(), pathElement - 1 - offsetLink);
595 pwrIn = pwrOut - getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
596 direction).getspanLoss();
597 // Calculate degradation accumulated across incoming Link and add them to
598 // accumulated impairments
599 calcCd += getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
601 LOG.info("loop of check OSNR : SRG, n = {} CD on preceeding link {} = {} ps", pathElement,
602 pathElement - offsetLink * 3 - 2, getAppropriatePceLink((pathElement - offsetLink * 3 - 2),
603 edges, allPceLinks, direction).getcd());
604 calcPmd2 += getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
605 direction).getpmd2();
606 // This also includes Non Linear Contribution from the path
607 calcOnsrLin += cu.calculateNLonsrContribution(pwrOut, getAppropriatePceLink((pathElement
608 - offsetLink * 3 - 2), edges, allPceLinks, direction).getLength(), spacing);
610 //calculation of the SRG contribution either for Add and Drop
611 impairments = cu.getPceRoadmAmpParameters(cnt, srgMode,
612 pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
613 calcCd = impairments.get("CD").doubleValue();
614 calcPmd2 = impairments.get("DGD2").doubleValue();
615 calcPdl2 = impairments.get("PDL2").doubleValue();
616 calcOnsrLin = impairments.get("ONSRLIN").doubleValue();
617 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
620 if (pathElement > 1) {
621 // If SRG is not the first or the second element of the Path, it is the DROP
623 // After accumulated degradations are calculated, we also need to calculate
624 // resulting OSNR in dB to pass it to the method that verifies end Xponder
625 // performances are compatible with degradations experienced on the path
627 calcOsnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
628 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOsnrdB);
629 LOG.info("Loop n = {}, DROP, calcOsnrdB= {}", n, calcOsnrdB);
630 } catch (ArithmeticException e) {
631 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
632 path.getEdgeList().size());
636 if (CatalogConstant.CatalogNodeType.ADD.equals(cnt)) {
637 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
638 // The Degree is not considered. This means we must bypass the add-link (ADD)
639 // and the next node (Degree) which are not considered in the impairments.
645 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
646 //This is the case of DROP, ROADM degree is not considered
649 LOG.info("loop of check OSNR : DEGREE, n = {} Path Element = {}", n, pathElement);
650 cnt = CatalogConstant.CatalogNodeType.EXPRESS;
651 String degree1Mode = "";
652 String degree2Mode = "";
653 // If the operational mode of the Degree is not consistent or if the operational
654 // mode is not declared in the topology
655 if (StringConstants.UNKNOWN_MODE.equals(currentNode.getOperationalMode())
656 || currentNode.getOperationalMode() == null
657 || currentNode.getOperationalMode().isEmpty()) {
658 // Operational mode is set by default to standard opMode for Degree
659 degree1Mode = CatalogConstant.MWMWCORE;
661 // Operational mode is found in degree-attributes of the Node
662 degree1Mode = currentNode.getOperationalMode();
664 // Same for next node which is the second degree of a ROADM node
665 if (StringConstants.UNKNOWN_MODE.equals(nextNode.getOperationalMode())
666 || nextNode.getOperationalMode() == null
667 || nextNode.getOperationalMode().isEmpty()) {
668 degree2Mode = CatalogConstant.MWMWCORE;
670 degree2Mode = currentNode.getOperationalMode();
672 // At that time OpenROADM provides only one spec for the ROADM nodes
673 if (!degree1Mode.equals(degree2Mode)) {
674 LOG.info("Unsupported Hybrid ROADM configuration with Degree1 {} of {} operational mode"
675 + "and Degree2 {} of {} operational mode. Will by default use operational mode"
676 + "of Degree2", currentNode.getNodeId().toString(),
677 degree1Mode, nextNode.getNodeId().toString(), degree2Mode);
679 pwrIn = pwrOut - getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks,
680 direction).getspanLoss();
681 // Calculate degradation accumulated across incoming Link and add them to
682 // accumulated impairments
683 calcCd += getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
685 calcPmd2 += getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
687 // This also includes Non Linear Contribution from the path
688 calcOnsrLin += cu.calculateNLonsrContribution(pwrOut, getAppropriatePceLink((pathElement
689 - offsetLink - 1), edges, allPceLinks, direction).getLength(), spacing);
690 // Calculate output power for next span (Output of degree 2)
691 pwrOut = cu.getPceRoadmAmpOutputPower(cnt, degree2Mode, getAppropriatePceLink((pathElement
692 + 3 * offsetLink + 1), edges, allPceLinks, direction).getspanLoss(), spacing,
693 getAppropriatePceLink((pathElement + 3 * offsetLink + 1), edges, allPceLinks, direction)
694 .getpowerCorrection());
695 // Adds to accumulated impairments the degradation associated with the Express
696 // path of ROADM : Degree1, express link, Degree2
697 impairments = cu.getPceRoadmAmpParameters(cnt, degree2Mode,
698 pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
699 calcCd = impairments.get("CD").doubleValue();
700 calcPmd2 = impairments.get("DGD2").doubleValue();
701 calcPdl2 = impairments.get("PDL2").doubleValue();
702 calcOnsrLin = impairments.get("ONSRLIN").doubleValue();
703 LOG.debug("Loop n = {}, DEGREE, calcOsnrdB= {}", n, getOsnrDbfromOnsrLin(calcOnsrLin));
704 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
707 // increment pathElement so that in next step we will not point to Degree2 but
710 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
711 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId().toString(),
712 nextNode.getNodeId().toString(), calcCd, calcPmd2, calcPdl2, getOsnrDbfromOnsrLin(calcOnsrLin));
715 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
718 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOsnrdB = {} "
719 + "dB and ONSR dB exterapolated from calcosnrlin = {}"
720 + " including non linear contributions",
721 calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2), calcOsnrdB, getOsnrDbfromOnsrLin(calcOnsrLin));
722 if (!transponderPresent) {
723 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
724 + "that optical tunnel degradations are compatible with external transponder performances");
727 LOG.info("In checkOSNR: Transponder Operational mode {} results in a residual margin of {} dB, according "
728 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
729 opMode, margin - SYS_MARGIN, SYS_MARGIN);
730 String validationMessage = "INVALIDATED";
731 if ((margin - SYS_MARGIN) >= 0) {
732 validationMessage = "VALIDATED";
734 if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
735 LOG.info("- In checkOSNR: A to Z Path from {} to {} {}", vertices.get(0),
736 vertices.get(vertices.size() - 1), validationMessage);
738 LOG.info("- In checkOSNR: Z to A Path from {} to {} {}", vertices.get(vertices.size() - 1),
739 vertices.get(0), validationMessage);
741 return (margin - SYS_MARGIN);
744 // Method to provide either regular link (AtoZ) or Opposite link (ZtoA) in the list of PceGraphEdges
745 private PceLink getAppropriatePceLink(Integer pathEltNber, List<PceGraphEdge> edges,
746 Map<LinkId, PceLink> allPceLinks, String direction) {
747 if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
748 // Returns regular link.
749 return edges.get(pathEltNber).link();
751 //For Z to A direction, must return the opposite link
752 return allPceLinks.get(new LinkId(edges.get(pathEltNber).link()
753 .getOppositeLink()));
756 private double getOsnrDbfromOnsrLin(double onsrLu) {
757 return (10 * Math.log10(1 / onsrLu));
761 * Get spectrum assignment for path.
763 * @param path the path for which we get spectrum assignment.
764 * @param allPceNodes all optical nodes.
765 * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
767 * @return a spectrum assignment object which contains begin and end index. If
768 * no spectrum assignment found, beginIndex = stopIndex = 0
770 private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
771 Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
772 byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
773 Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
774 BitSet result = BitSet.valueOf(freqMap);
775 boolean isFlexGrid = true;
776 LOG.debug("Processing path {} with length {}", path, path.getLength());
777 BitSet pceNodeFreqMap;
778 for (PceGraphEdge edge : path.getEdgeList()) {
779 LOG.debug("Processing source {} ", edge.link().getSourceId());
780 if (allPceNodes.containsKey(edge.link().getSourceId())) {
781 PceNode pceNode = allPceNodes.get(edge.link().getSourceId());
782 LOG.debug("Processing PCE node {}", pceNode);
783 if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNode.getVersion())) {
784 LOG.debug("Node {}: version is {} and slot width granularity is {} -> fixed grid mode",
785 pceNode.getNodeId(), pceNode.getVersion(), pceNode.getSlotWidthGranularity());
788 if ((pceNode.getSlotWidthGranularity().setScale(0, RoundingMode.CEILING)
789 .equals(GridConstant.SLOT_WIDTH_50))
790 && (pceNode.getCentralFreqGranularity().setScale(0, RoundingMode.CEILING)
791 .equals(GridConstant.SLOT_WIDTH_50))) {
792 LOG.debug("Node {}: version is {} with slot width granularity {} and central "
793 + "frequency granularity is {} -> fixed grid mode",
794 pceNode.getNodeId(), pceNode.getVersion(), pceNode.getSlotWidthGranularity(),
795 pceNode.getCentralFreqGranularity());
798 pceNodeFreqMap = pceNode.getBitSetData();
799 LOG.debug("Pce node bitset {}", pceNodeFreqMap);
800 if (pceNodeFreqMap != null) {
801 result.and(pceNodeFreqMap);
802 LOG.debug("intermediate bitset {}", result);
806 LOG.debug("Bitset result {}", result);
807 return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
811 * Compute spectrum assignment from spectrum occupation for spectral width.
813 * @param spectrumOccupation the spectrum occupation BitSet.
814 * @param spectralWidthSlotNumber the nb slots for spectral width.
815 * @param isFlexGrid true if flexible grid, false otherwise.
816 * @return a spectrum assignment object which contains begin and stop index. If
817 * no spectrum assignment found, beginIndex = stopIndex = 0
819 private SpectrumAssignment computeBestSpectrumAssignment(BitSet spectrumOccupation, int spectralWidthSlotNumber,
820 boolean isFlexGrid) {
821 SpectrumAssignmentBuilder spectrumAssignmentBldr = new SpectrumAssignmentBuilder()
822 .setBeginIndex(Uint16.valueOf(0))
823 .setStopIndex(Uint16.valueOf(0))
824 .setFlexGrid(isFlexGrid);
825 BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
826 referenceBitSet.set(0, spectralWidthSlotNumber);
827 int nbSteps = isFlexGrid ? spectralWidthSlotNumber : 1;
828 //higher is the frequency, smallest is the wavelength number
829 //in operational, the allocation is done through wavelength starting from the smallest
830 //so we have to loop from the last element of the spectrum occupation
831 for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber; i -= nbSteps) {
832 if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
833 spectrumAssignmentBldr.setBeginIndex(Uint16.valueOf(i - spectralWidthSlotNumber));
834 spectrumAssignmentBldr.setStopIndex(Uint16.valueOf(i - 1));
838 return spectrumAssignmentBldr.build();
841 public Double getTpceCalculatedMargin() {
842 return tpceCalculatedMargin;