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.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;
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.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 =
80 GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.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 =
89 GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.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().toJava()));
109 pceResult.setMinFreq(GridUtils.getStartFrequencyFromIndex(spectrumAssignment.getBeginIndex().toJava()));
110 pceResult.setMaxFreq(GridUtils.getStopFrequencyFromIndex(spectrumAssignment.getStopIndex().toJava()));
111 LOG.debug("In PostAlgoPathValidator: spectrum assignment found {} {}", spectrumAssignment, path);
114 CatalogUtils cu = new CatalogUtils(networkTransactionService);
115 if (cu.isCatalogFilled()) {
116 double margin1 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
117 StringConstants.SERVICE_DIRECTION_AZ, cu);
118 double margin2 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
119 StringConstants.SERVICE_DIRECTION_ZA, cu);
120 if (margin1 < 0 || margin2 < 0 || margin1 == Double.NEGATIVE_INFINITY
121 || margin2 == Double.NEGATIVE_INFINITY) {
122 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
123 pceResult.setLocalCause(PceResult.LocalCause.OUT_OF_SPEC_OSNR);
126 this.tpceCalculatedMargin = Math.min(margin1, margin2);
128 "In PostAlgoPathValidator: Minimum margin estimated by tpce on AtoZ and ZtoA path is of {} dB",
129 this.tpceCalculatedMargin);
131 this.tpceCalculatedMargin = 0.0;
132 LOG.info("In PostAlgoPathValidator: Operational mode Catalog not filled, delegate OSNR calculation"
133 + " to GNPy and margin set to 0");
135 // Check if MaxLatency is defined in the hard constraints
136 if (pceHardConstraints.getMaxLatency() != -1
137 && !checkLatency(pceHardConstraints.getMaxLatency(), path)) {
138 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
139 pceResult.setLocalCause(PceResult.LocalCause.TOO_HIGH_LATENCY);
142 // Check if nodes are included in the hard constraints
143 if (!checkInclude(path, pceHardConstraints)) {
144 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
145 pceResult.setLocalCause(PceResult.LocalCause.HD_NODE_INCLUDE);
148 // TODO here other post algo validations can be added
149 // more data can be sent to PceGraph module via PceResult structure if required
150 pceResult.setRC(ResponseCodes.RESPONSE_OK);
151 pceResult.setLocalCause(PceResult.LocalCause.NONE);
153 case StringConstants.SERVICE_TYPE_100GE_M:
154 case StringConstants.SERVICE_TYPE_10GE:
155 case StringConstants.SERVICE_TYPE_1GE:
156 int tribSlotNb = Map.of(
157 StringConstants.SERVICE_TYPE_100GE_M, 20,
158 StringConstants.SERVICE_TYPE_10GE, 8,
159 StringConstants.SERVICE_TYPE_1GE, 1)
161 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
162 pceResult.setServiceType(serviceType);
163 Map<String, List<Uint16>> tribSlot = chooseTribSlot(path, allPceNodes, tribSlotNb);
164 Map<String, Uint16> tribPort = chooseTribPort(path, allPceNodes, tribSlot, tribSlotNb);
165 List<OpucnTribSlotDef> resultTribPortTribSlot = getMinMaxTpTs(tribPort, tribSlot);
166 if (resultTribPortTribSlot.get(0) != null && resultTribPortTribSlot.get(1) != null) {
167 pceResult.setResultTribPortTribSlot(resultTribPortTribSlot);
168 pceResult.setRC(ResponseCodes.RESPONSE_OK);
169 LOG.info("In PostAlgoPathValidator: found TribPort {} - tribSlot {} - tribSlotNb {}",
170 tribPort, tribSlot, tribSlotNb);
173 case StringConstants.SERVICE_TYPE_ODU4:
174 case StringConstants.SERVICE_TYPE_ODUC2:
175 case StringConstants.SERVICE_TYPE_ODUC3:
176 case StringConstants.SERVICE_TYPE_ODUC4:
177 case StringConstants.SERVICE_TYPE_100GE_S:
178 pceResult.setRC(ResponseCodes.RESPONSE_OK);
179 pceResult.setServiceType(serviceType);
180 LOG.info("In PostAlgoPathValidator: ODU4/ODUCn path found {}", path);
183 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
184 LOG.warn("In PostAlgoPathValidator checkPath: unsupported serviceType {} found {}",
191 private boolean checkLatency(Long maxLatency, GraphPath<String, PceGraphEdge> path) {
193 for (PceGraphEdge edge : path.getEdgeList()) {
194 if (edge.link() == null || edge.link().getLatency() == null) {
195 LOG.warn("- In checkLatency: the link {} does not contain latency field",
196 edge.link().getLinkId().getValue());
199 latency += edge.link().getLatency();
200 LOG.debug("- In checkLatency: latency of {} = {} units", edge.link().getLinkId().getValue(), latency);
202 return (latency < maxLatency);
205 // Check the inclusion if it is defined in the hard constraints
206 private boolean checkInclude(GraphPath<String, PceGraphEdge> path, PceConstraints pceHardConstraintsInput) {
207 List<ResourcePair> listToInclude = pceHardConstraintsInput.getListToInclude()
208 .stream().sorted((rp1, rp2) -> rp1.getName().compareTo(rp2.getName()))
209 .collect(Collectors.toList());
210 if (listToInclude.isEmpty()) {
213 List<PceGraphEdge> pathEdges = path.getEdgeList();
214 LOG.debug(" in checkInclude vertex list: [{}]", path.getVertexList());
215 List<String> listOfElementsSubNode = new ArrayList<>();
216 listOfElementsSubNode.add(pathEdges.get(0).link().getsourceNetworkSupNodeId());
217 listOfElementsSubNode.addAll(
218 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.NODE, pceHardConstraintsInput));
219 List<String> listOfElementsCLLI = new ArrayList<>();
220 listOfElementsCLLI.add(pathEdges.get(0).link().getsourceCLLI());
221 listOfElementsCLLI.addAll(
222 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.CLLI, pceHardConstraintsInput));
223 List<String> listOfElementsSRLG = new ArrayList<>();
224 // first link is XPONDEROUTPUT, no SRLG for it
225 listOfElementsSRLG.add("NONE");
226 listOfElementsSRLG.addAll(
227 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.SRLG, pceHardConstraintsInput));
228 // validation: check each type for each element
229 return listOfElementsSubNode.containsAll(
231 .stream().filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
232 .map(ResourcePair::getName).collect(Collectors.toList()))
233 && listOfElementsSRLG.containsAll(
235 .stream().filter(rp -> PceConstraints.ResourceType.SRLG.equals(rp.getType()))
236 .map(ResourcePair::getName).collect(Collectors.toList()))
237 && listOfElementsCLLI.containsAll(
239 .stream().filter(rp -> PceConstraints.ResourceType.CLLI.equals(rp.getType()))
240 .map(ResourcePair::getName).collect(Collectors.toList()));
243 private List<String> listOfElementsBuild(List<PceGraphEdge> pathEdges, PceConstraints.ResourceType type,
244 PceConstraints pceHardConstraints) {
245 List<String> listOfElements = new ArrayList<>();
246 for (PceGraphEdge link : pathEdges) {
249 listOfElements.add(link.link().getdestNetworkSupNodeId());
252 listOfElements.add(link.link().getdestCLLI());
255 if (link.link().getlinkType() != OpenroadmLinkType.ROADMTOROADM) {
256 listOfElements.add("NONE");
259 // srlg of link is List<Long>. But in this algo we need string representation of
261 // this should be any SRLG mentioned in include constraints if any of them if
263 boolean found = false;
264 for (Long srlg : link.link().getsrlgList()) {
265 String srlgStr = String.valueOf(srlg);
266 if (pceHardConstraints.getSRLGnames().contains(srlgStr)) {
267 listOfElements.add(srlgStr);
268 LOG.info("listOfElementsBuild. FOUND SRLG {} in link {}", srlgStr, link.link());
273 // there is no specific srlg to include. thus add to list just the first one
274 listOfElements.add("NONE");
278 LOG.debug("listOfElementsBuild unsupported resource type");
281 return listOfElements;
284 private Map<String, Uint16> chooseTribPort(GraphPath<String,
285 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, Map<String, List<Uint16>> tribSlotMap, int nbSlot) {
286 LOG.debug("In choosetribPort: edgeList = {} ", path.getEdgeList());
287 Map<String, Uint16> tribPortMap = new HashMap<>();
288 for (PceGraphEdge edge : path.getEdgeList()) {
289 List<Uint16> srcTpnPool =
291 .get(edge.link().getSourceId())
292 .getAvailableTribPorts()
293 .get(edge.link().getSourceTP().getValue());
294 List<Uint16> destTpnPool =
296 .get(edge.link().getDestId())
297 .getAvailableTribPorts()
298 .get(edge.link().getDestTP().getValue());
299 List<Uint16> commonEdgeTpnPool = new ArrayList<>();
300 for (Uint16 srcTpn : srcTpnPool) {
301 if (destTpnPool.contains(srcTpn)) {
302 commonEdgeTpnPool.add(srcTpn);
305 if (commonEdgeTpnPool.isEmpty()) {
308 Integer startTribSlot = tribSlotMap.values().stream().findFirst().get().get(0).toJava();
309 Integer tribPort = (int) Math.ceil((double)startTribSlot / nbSlot);
310 for (Uint16 commonTribPort : commonEdgeTpnPool) {
311 if (tribPort.equals(commonTribPort.toJava())) {
312 tribPortMap.put(edge.link().getLinkId().getValue(), commonTribPort);
316 tribPortMap.forEach((k,v) -> LOG.info("TribPortMap : k = {}, v = {}", k, v));
320 private Map<String, List<Uint16>> chooseTribSlot(GraphPath<String,
321 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, int nbSlot) {
322 LOG.debug("In choosetribSlot: edgeList = {} ", path.getEdgeList());
323 Map<String, List<Uint16>> tribSlotMap = new HashMap<>();
324 for (PceGraphEdge edge : path.getEdgeList()) {
325 List<Uint16> srcTsPool =
327 .get(edge.link().getSourceId())
328 .getAvailableTribSlots()
329 .get(edge.link().getSourceTP().getValue());
330 List<Uint16> destTsPool =
332 .get(edge.link().getDestId())
333 .getAvailableTribSlots()
334 .get(edge.link().getDestTP().getValue());
335 List<Uint16> commonEdgeTsPoolList = new ArrayList<>();
336 List<Uint16> tribSlotList = new ArrayList<>();
337 for (Uint16 integer : srcTsPool) {
338 if (destTsPool.contains(integer)) {
339 commonEdgeTsPoolList.add(integer);
342 Collections.sort(commonEdgeTsPoolList);
343 List<Uint16> commonGoodStartEdgeTsPoolList = new ArrayList<>();
344 for (Uint16 startEdgeTsPool : commonEdgeTsPoolList) {
345 if (Integer.valueOf(1).equals(startEdgeTsPool.toJava() % nbSlot) || nbSlot == 1) {
346 commonGoodStartEdgeTsPoolList.add(startEdgeTsPool);
349 Collections.sort(commonGoodStartEdgeTsPoolList);
350 boolean goodTsList = false;
351 for (Uint16 goodStartTsPool : commonGoodStartEdgeTsPoolList) {
352 int goodStartIndex = commonEdgeTsPoolList.indexOf(Uint16.valueOf(goodStartTsPool.intValue()));
353 if (!goodTsList && commonEdgeTsPoolList.size() - goodStartIndex >= nbSlot) {
354 for (int i = 0; i < nbSlot; i++) {
355 if (!commonEdgeTsPoolList.get(goodStartIndex + i)
356 .equals(Uint16.valueOf(goodStartTsPool.toJava() + i))) {
358 tribSlotList.clear();
361 tribSlotList.add(commonEdgeTsPoolList.get(goodStartIndex + i));
366 tribSlotMap.put(edge.link().getLinkId().getValue(), tribSlotList);
368 tribSlotMap.forEach((k,v) -> LOG.info("TribSlotMap : k = {}, v = {}", k, v));
372 private List<OpucnTribSlotDef> getMinMaxTpTs(Map<String, Uint16> tribPort, Map<String, List<Uint16>> tribSlot) {
373 String tribport = tribPort.values().toArray()[0].toString();
374 List<Uint16> tsList = (List<Uint16>) tribSlot.values().toArray()[0];
375 return new ArrayList<>(List.of(
376 OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(0).toString())),
377 OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(tsList.size() - 1).toString()))));
381 * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
383 * @param path the AtoZ path provided by the PCE.
384 * @param allPceNode The map of chosen/relevant PceNodes build from topology pruning.
385 * @param allPceLinks The map of PceLinks build corresponding to the whole topology.
386 * @param serviceType The service Type used to extrapolate Operational mode when it is not provided.
387 * @param direction The direction used to scan provided path in a direct or reverse way.
388 * @param cu CatalogUtils instance.
389 * @return the calculated margin according to the Transponder performances and path impairments.
391 @SuppressWarnings("deprecation")
392 @edu.umd.cs.findbugs.annotations.SuppressWarnings("DLS_DEAD_LOCAL_STORE")
393 private double checkOSNR(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
394 Map<LinkId, PceLink> allPceLinks, String serviceType, String direction, CatalogUtils cu) {
395 double spacing = 50.0;
397 double calcOsnrdB = 0;
400 double calcOnsrLin = 0.0001;
402 double pwrIn = -60.0;
403 double pwrOut = -60.0;
406 boolean transponderPresent = false;
407 if (direction.equals(StringConstants.SERVICE_DIRECTION_ZA)) {
412 List<String> vertices = path.getVertexList();
413 List<PceGraphEdge> edges = path.getEdgeList();
415 // LOOP that scans the different Nodes/Links of the path and calculates
416 // associated degradations
417 // using CatalogUtils primitives to retrieve physical parameters and make a
418 // first level calculation
419 Map<String, Double> impairments = new HashMap<>();
420 for (int n = 0; n < vertices.size(); n++) {
421 int pathElement = direction.equals(StringConstants.SERVICE_DIRECTION_AZ) ? n : vertices.size() - n - 1;
422 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
424 pathElement != vertices.size() - 1 && direction.equals(StringConstants.SERVICE_DIRECTION_AZ)
425 || pathElement != 0 && direction.equals(StringConstants.SERVICE_DIRECTION_ZA)
426 ? allPceNodes.get(new NodeId(vertices.get(pathElement + increment)))
428 LOG.debug("loop of check OSNR, n = {} Path Element = {}", n, pathElement);
429 switch (currentNode.getORNodeType()) {
431 transponderPresent = true;
433 pathElement == 0 && direction.equals(StringConstants.SERVICE_DIRECTION_AZ)
434 || pathElement == vertices.size() - 1
435 && direction.equals(StringConstants.SERVICE_DIRECTION_ZA)
436 ? getAppropriatePceLink((pathElement + offsetLink), edges, allPceLinks, direction)
439 // last Xponder of the path (RX side)
440 : getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
443 InstanceIdentifier<TerminationPoint1> nwTpIid =
444 InstanceIdentifiers.createNetworkTerminationPoint1IIDBuilder(vertices.get(pathElement), nwTpId);
445 LOG.debug("loop of check OSNR : XPDR, n = {} Path Element = {}", n, pathElement);
447 if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
448 .get().isPresent()) {
449 // If the Xponder operational mode (setOpMode Arg1) is not consistent or not declared in the topology (Network TP)
450 // Operational mode is retrieved from the service Type assuming it is supported by the Xponder (setOpMode Arg2)
452 currentNode.getXponderOperationalMode(
453 networkTransactionService
454 .read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
455 .get().get().getXpdrNetworkAttributes()),
456 // Operational mode is found as an attribute of the network TP
457 cu.getPceOperationalModeFromServiceType(
458 CatalogConstant.CatalogNodeType.TSP, serviceType));
459 // Operational mode is retrieved from the service Type assuming it is supported
461 LOG.debug("Transponder {} corresponding to path Element {} in the path has {} operational "
462 + "mode", currentNode.getNodeId().getValue(), pathElement, opMode);
464 LOG.error("Issue accessing the XponderNetworkAttributes of {} for Transponder {}"
465 + " corresponding to path Element {} in the path ",
466 nwTpId, currentNode.getNodeId().getValue(), pathElement);
467 opMode = cu.getPceOperationalModeFromServiceType(
468 CatalogConstant.CatalogNodeType.TSP, serviceType);
469 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
470 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
472 } catch (InterruptedException | ExecutionException e1) {
473 opMode = cu.getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP,
475 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
476 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
478 // If TSP is the last of the path
479 if (pathElement == vertices.size() - 1 && direction.equals(StringConstants.SERVICE_DIRECTION_AZ)
480 || pathElement == 0 && direction.equals(StringConstants.SERVICE_DIRECTION_ZA)) {
481 LOG.debug("Loop n = {}, Step5.1, XPDR, tries calculating Margin, just before call", n);
482 // Check that accumulated degradations are compatible with TSP performances
483 // According to OpenROADM spec :
484 // margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2),
485 // getOsnrDbfromOnsrLin(calcOnsrLin));
486 // Calculation modified for pdl according to calculation in Julia's Tool
487 margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2),
488 (Math.sqrt(calcPdl2)), getOsnrDbfromOnsrLin(calcOnsrLin));
489 LOG.info("Loop n = {}, XPDR, calcosnrdB= {}", n, getOsnrDbfromOnsrLin(calcOnsrLin));
491 // TSP is first element of the path . To correctly evaluate the TX OOB OSNR from
492 // its operational mode, we need to know the type of ADD/DROP Mux it is
495 // If the operational mode of the ADD/DROP MUX is not consistent or
496 // if the operational mode of the ADD/DROP MUX is not declared in the topology
498 // Operational mode is set by default to standard opMode for ADD SRGs
499 adnMode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWWRCORE);
500 // Operational mode is found in SRG attributes of the Node
501 LOG.debug("Transponder {} corresponding to path Element {} in the path is connected to SRG "
502 + "which has {} operational mode", currentNode.getNodeId().getValue(), pathElement,
504 // Retrieve the Tx ONSR of the Xponder which results from IB and OOB OSNR
506 calcOnsrLin = cu.getPceTxTspParameters(opMode, adnMode);
507 // Retrieve the spacing associated with Xponder operational mode that is needed
509 spacing = cu.getPceTxTspChannelSpacing(opMode);
510 LOG.info("Transponder {} corresponding to path Element {} in the path has a TX OSNR of {} dB",
511 currentNode.getNodeId().getValue(), pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
515 // If the operational mode of the ADD/DROP MUX is not consistent or is not declared in the topology (Network TP)
516 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
517 String srgMode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE);
518 cnt = CatalogConstant.CatalogNodeType.DROP;
519 LOG.debug("loop of check OSNR : SRG, n = {} Path Element = {}", n, pathElement);
520 if (pathElement <= 1 && direction.equals(StringConstants.SERVICE_DIRECTION_AZ)
521 || pathElement >= vertices.size() - 2
522 && direction.equals(StringConstants.SERVICE_DIRECTION_ZA)) {
523 // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
524 // Xponder to Xponder) node element of the path is the ADD SRG.
525 if (getAppropriatePceLink(pathElement + offsetLink, edges, allPceLinks, direction)
526 .getlinkType() != OpenroadmLinkType.ADDLINK) {
527 LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
528 currentNode.getNodeId().toString(), pathElement + offsetLink);
530 cnt = CatalogConstant.CatalogNodeType.ADD;
532 pwrOut = cu.getPceRoadmAmpOutputPower(cnt, srgMode,
533 getAppropriatePceLink((pathElement + 1 + offsetLink * 3), edges, allPceLinks, direction)
536 getAppropriatePceLink((pathElement + 1 + offsetLink * 3), edges, allPceLinks, direction)
537 .getpowerCorrection());
538 LOG.debug("loop of check OSNR : SRG, n = {} link {} Pout = {}",
539 pathElement, pathElement + 1 + offsetLink * 3, pwrOut);
541 // Other case is DROP, for which cnt is unchanged (.DROP)
542 if (getAppropriatePceLink(pathElement - 1 - offsetLink, edges, allPceLinks, direction)
543 .getlinkType() != OpenroadmLinkType.DROPLINK) {
544 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
545 currentNode.getNodeId().toString(), pathElement - 1 - offsetLink);
547 pwrIn = pwrOut - getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
548 direction).getspanLoss();
549 // Calculate degradation accumulated across incoming Link and add them to
550 // accumulated impairments
551 calcCd += getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
553 LOG.info("loop of check OSNR : SRG, n = {} CD on preceeding link {} = {} ps", pathElement,
554 pathElement - offsetLink * 3 - 2, getAppropriatePceLink((pathElement - offsetLink * 3 - 2),
555 edges, allPceLinks, direction).getcd());
556 calcPmd2 += getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
557 direction).getpmd2();
558 // This also includes Non Linear Contribution from the path
559 calcOnsrLin += cu.calculateNLonsrContribution(pwrOut, getAppropriatePceLink((pathElement
560 - offsetLink * 3 - 2), edges, allPceLinks, direction).getLength(), spacing);
562 //calculation of the SRG contribution either for Add and Drop
563 impairments = cu.getPceRoadmAmpParameters(cnt, srgMode,
564 pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
565 calcCd = impairments.get("CD").doubleValue();
566 calcPmd2 = impairments.get("DGD2").doubleValue();
567 calcPdl2 = impairments.get("PDL2").doubleValue();
568 calcOnsrLin = impairments.get("ONSRLIN").doubleValue();
569 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
572 if (pathElement > 1) {
573 // If SRG is not the first or the second element of the Path, it is the DROP
575 // After accumulated degradations are calculated, we also need to calculate
576 // resulting OSNR in dB to pass it to the method that verifies end Xponder
577 // performances are compatible with degradations experienced on the path
579 calcOsnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
580 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOsnrdB);
581 LOG.info("Loop n = {}, DROP, calcOsnrdB= {}", n, calcOsnrdB);
582 } catch (ArithmeticException e) {
583 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
584 path.getEdgeList().size());
588 if (CatalogConstant.CatalogNodeType.ADD.equals(cnt)) {
589 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
590 // The Degree is not considered. This means we must bypass the add-link (ADD)
591 // and the next node (Degree) which are not considered in the impairments.
597 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
598 //This is the case of DROP, ROADM degree is not considered
601 LOG.info("loop of check OSNR : DEGREE, n = {} Path Element = {}", n, pathElement);
602 cnt = CatalogConstant.CatalogNodeType.EXPRESS;
603 String degree1Mode = "";
604 String degree2Mode = "";
605 // If the operational mode of the Degree is not consistent or declared in the topology
606 // Operational mode is set by default to standard opMode for Degree
607 degree1Mode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWMWCORE);
608 // Same for next node which is the second degree of a ROADM node
609 degree2Mode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWMWCORE);
610 // At that time OpenROADM provides only one spec for the ROADM nodes
611 if (!degree1Mode.equals(degree2Mode)) {
612 LOG.info("Unsupported Hybrid ROADM configuration with Degree1 {} of {} operational mode"
613 + "and Degree2 {} of {} operational mode. Will by default use operational mode"
614 + "of Degree2", currentNode.getNodeId().toString(),
615 degree1Mode, nextNode.getNodeId().toString(), degree2Mode);
617 pwrIn = pwrOut - getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks,
618 direction).getspanLoss();
619 // Calculate degradation accumulated across incoming Link and add them to
620 // accumulated impairments
622 getAppropriatePceLink(pathElement - offsetLink - 1, edges, allPceLinks, direction).getcd();
624 getAppropriatePceLink(pathElement - offsetLink - 1, edges, allPceLinks, direction).getpmd2();
625 // This also includes Non Linear Contribution from the path
626 calcOnsrLin += cu.calculateNLonsrContribution(pwrOut,
627 getAppropriatePceLink(pathElement - offsetLink - 1, edges, allPceLinks, direction).getLength(),
629 // Calculate output power for next span (Output of degree 2)
630 pwrOut = cu.getPceRoadmAmpOutputPower(cnt, degree2Mode,
631 getAppropriatePceLink(pathElement + 3 * offsetLink + 1, edges, allPceLinks, direction)
634 getAppropriatePceLink(pathElement + 3 * offsetLink + 1, edges, allPceLinks, direction)
635 .getpowerCorrection());
636 // Adds to accumulated impairments the degradation associated with the Express
637 // path of ROADM : Degree1, express link, Degree2
638 impairments = cu.getPceRoadmAmpParameters(cnt, degree2Mode,
639 pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
640 calcCd = impairments.get("CD").doubleValue();
641 calcPmd2 = impairments.get("DGD2").doubleValue();
642 calcPdl2 = impairments.get("PDL2").doubleValue();
643 calcOnsrLin = impairments.get("ONSRLIN").doubleValue();
644 LOG.debug("Loop n = {}, DEGREE, calcOsnrdB= {}", n, getOsnrDbfromOnsrLin(calcOnsrLin));
645 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
648 // increment pathElement so that in next step we will not point to Degree2 but
651 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
652 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId().toString(),
653 nextNode.getNodeId().toString(), calcCd, calcPmd2, calcPdl2, getOsnrDbfromOnsrLin(calcOnsrLin));
656 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
659 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOsnrdB = {} "
660 + "dB and ONSR dB exterapolated from calcosnrlin = {}"
661 + " including non linear contributions",
662 calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2), calcOsnrdB, getOsnrDbfromOnsrLin(calcOnsrLin));
663 if (!transponderPresent) {
664 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
665 + "that optical tunnel degradations are compatible with external transponder performances");
668 double delta = margin - SYS_MARGIN;
669 LOG.info("In checkOSNR: Transponder Operational mode {} results in a residual margin of {} dB, according "
670 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
671 opMode, delta, SYS_MARGIN);
672 String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
673 if (direction.equals(StringConstants.SERVICE_DIRECTION_AZ)) {
674 LOG.info("- In checkOSNR: A to Z Path from {} to {} {}",
675 vertices.get(0), vertices.get(vertices.size() - 1), validationMessage);
677 LOG.info("- In checkOSNR: Z to A Path from {} to {} {}",
678 vertices.get(vertices.size() - 1), vertices.get(0), validationMessage);
683 private String setOpMode(String opMode, String defaultMode) {
685 opMode == null || opMode.isEmpty() || opMode.contentEquals(StringConstants.UNKNOWN_MODE)
691 // Method to provide either regular link (AtoZ) or Opposite link (ZtoA) in the list of PceGraphEdges
692 private PceLink getAppropriatePceLink(Integer pathEltNber, List<PceGraphEdge> edges,
693 Map<LinkId, PceLink> allPceLinks, String direction) {
694 if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
695 // Returns regular link.
696 return edges.get(pathEltNber).link();
698 //For Z to A direction, must return the opposite link
699 return allPceLinks.get(new LinkId(edges.get(pathEltNber).link().getOppositeLink()));
702 private double getOsnrDbfromOnsrLin(double onsrLu) {
703 return (10 * Math.log10(1 / onsrLu));
707 * Get spectrum assignment for path.
709 * @param path the path for which we get spectrum assignment.
710 * @param allPceNodes all optical nodes.
711 * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
713 * @return a spectrum assignment object which contains begin and end index. If
714 * no spectrum assignment found, beginIndex = stopIndex = 0
716 private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
717 Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
718 byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
719 Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
720 BitSet result = BitSet.valueOf(freqMap);
721 boolean isFlexGrid = true;
722 LOG.debug("Processing path {} with length {}", path, path.getLength());
723 for (PceGraphEdge edge : path.getEdgeList()) {
724 NodeId srcNodeId = edge.link().getSourceId();
725 LOG.debug("Processing source {} ", srcNodeId);
726 if (allPceNodes.containsKey(srcNodeId)) {
727 PceNode pceNode = allPceNodes.get(srcNodeId);
728 LOG.debug("Processing PCE node {}", pceNode);
729 String pceNodeVersion = pceNode.getVersion();
730 NodeId pceNodeId = pceNode.getNodeId();
731 BigDecimal sltWdthGran = pceNode.getSlotWidthGranularity();
732 BigDecimal ctralFreqGran = pceNode.getCentralFreqGranularity();
733 if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNodeVersion)) {
734 LOG.debug("Node {}: version is {} and slot width granularity is {} -> fixed grid mode",
735 pceNodeId, pceNodeVersion, sltWdthGran);
738 if (sltWdthGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)
739 && ctralFreqGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
740 LOG.debug("Node {}: version is {} with slot width granularity {} and central "
741 + "frequency granularity is {} -> fixed grid mode",
742 pceNodeId, pceNodeVersion, sltWdthGran, ctralFreqGran);
745 BitSet pceNodeFreqMap = pceNode.getBitSetData();
746 LOG.debug("Pce node bitset {}", pceNodeFreqMap);
747 if (pceNodeFreqMap != null) {
748 result.and(pceNodeFreqMap);
749 LOG.debug("intermediate bitset {}", result);
753 LOG.debug("Bitset result {}", result);
754 return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
758 * Compute spectrum assignment from spectrum occupation for spectral width.
760 * @param spectrumOccupation the spectrum occupation BitSet.
761 * @param spectralWidthSlotNumber the nb slots for spectral width.
762 * @param isFlexGrid true if flexible grid, false otherwise.
763 * @return a spectrum assignment object which contains begin and stop index. If
764 * no spectrum assignment found, beginIndex = stopIndex = 0
766 private SpectrumAssignment computeBestSpectrumAssignment(
767 BitSet spectrumOccupation, int spectralWidthSlotNumber, boolean isFlexGrid) {
768 SpectrumAssignmentBuilder spectrumAssignmentBldr = new SpectrumAssignmentBuilder()
769 .setBeginIndex(Uint16.valueOf(0))
770 .setStopIndex(Uint16.valueOf(0))
771 .setFlexGrid(isFlexGrid);
772 BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
773 referenceBitSet.set(0, spectralWidthSlotNumber);
774 //higher is the frequency, smallest is the wavelength number
775 //in operational, the allocation is done through wavelength starting from the smallest
776 //so we have to loop from the last element of the spectrum occupation
777 for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber;
778 i -= isFlexGrid ? spectralWidthSlotNumber : 1) {
779 if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
780 spectrumAssignmentBldr.setBeginIndex(Uint16.valueOf(i - spectralWidthSlotNumber));
781 spectrumAssignmentBldr.setStopIndex(Uint16.valueOf(i - 1));
785 return spectrumAssignmentBldr.build();
788 public Double getTpceCalculatedMargin() {
789 return tpceCalculatedMargin;