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")
69 public PceResult checkPath(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
70 Map<LinkId, PceLink> allPceLinks, PceResult pceResult, PceConstraints pceHardConstraints,
72 LOG.info("path = {}", path);
73 // check if the path is empty
74 if (path.getEdgeList().isEmpty()) {
75 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
78 int spectralWidthSlotNumber =
79 GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_100G);
80 SpectrumAssignment spectrumAssignment = null;
81 //variable to deal with 1GE (Nb=1) and 10GE (Nb=10) cases
82 switch (serviceType) {
83 case StringConstants.SERVICE_TYPE_OTUC2:
84 case StringConstants.SERVICE_TYPE_OTUC3:
85 case StringConstants.SERVICE_TYPE_OTUC4:
86 case StringConstants.SERVICE_TYPE_400GE:
87 spectralWidthSlotNumber =
88 GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_400G);
90 case StringConstants.SERVICE_TYPE_100GE_T:
91 case StringConstants.SERVICE_TYPE_OTU4:
92 spectrumAssignment = getSpectrumAssignment(path, allPceNodes, spectralWidthSlotNumber);
93 pceResult.setServiceType(serviceType);
94 if (spectrumAssignment.getBeginIndex().equals(Uint16.valueOf(0))
95 && spectrumAssignment.getStopIndex().equals(Uint16.valueOf(0))) {
96 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
97 pceResult.setLocalCause(PceResult.LocalCause.NO_PATH_EXISTS);
100 if (spectrumAssignment.getFlexGrid()) {
101 LOG.debug("Spectrum assignment flexgrid mode");
102 pceResult.setResultWavelength(GridConstant.IRRELEVANT_WAVELENGTH_NUMBER);
104 LOG.debug("Spectrum assignment fixedgrid mode");
105 pceResult.setResultWavelength(
106 GridUtils.getWaveLengthIndexFromSpectrumAssigment(spectrumAssignment.getBeginIndex().toJava()));
108 pceResult.setMinFreq(GridUtils.getStartFrequencyFromIndex(spectrumAssignment.getBeginIndex().toJava()));
109 pceResult.setMaxFreq(GridUtils.getStopFrequencyFromIndex(spectrumAssignment.getStopIndex().toJava()));
110 LOG.debug("In PostAlgoPathValidator: spectrum assignment found {} {}", spectrumAssignment, path);
113 CatalogUtils cu = new CatalogUtils(networkTransactionService);
114 if (cu.isCatalogFilled()) {
115 double margin1 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
116 StringConstants.SERVICE_DIRECTION_AZ, cu);
117 double margin2 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
118 StringConstants.SERVICE_DIRECTION_ZA, cu);
119 if (margin1 < 0 || margin2 < 0 || margin1 == Double.NEGATIVE_INFINITY
120 || margin2 == Double.NEGATIVE_INFINITY) {
121 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
122 pceResult.setLocalCause(PceResult.LocalCause.OUT_OF_SPEC_OSNR);
125 this.tpceCalculatedMargin = Math.min(margin1, margin2);
127 "In PostAlgoPathValidator: Minimum margin estimated by tpce on AtoZ and ZtoA path is of {} dB",
128 this.tpceCalculatedMargin);
130 this.tpceCalculatedMargin = 0.0;
131 LOG.info("In PostAlgoPathValidator: Operational mode Catalog not filled, delegate OSNR calculation"
132 + " to GNPy and margin set to 0");
134 // Check if MaxLatency is defined in the hard constraints
135 if (pceHardConstraints.getMaxLatency() != -1
136 && !checkLatency(pceHardConstraints.getMaxLatency(), path)) {
137 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
138 pceResult.setLocalCause(PceResult.LocalCause.TOO_HIGH_LATENCY);
141 // Check if nodes are included in the hard constraints
142 if (!checkInclude(path, pceHardConstraints)) {
143 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
144 pceResult.setLocalCause(PceResult.LocalCause.HD_NODE_INCLUDE);
147 // TODO here other post algo validations can be added
148 // more data can be sent to PceGraph module via PceResult structure if required
149 pceResult.setRC(ResponseCodes.RESPONSE_OK);
150 pceResult.setLocalCause(PceResult.LocalCause.NONE);
152 case StringConstants.SERVICE_TYPE_100GE_M:
153 case StringConstants.SERVICE_TYPE_10GE:
154 case StringConstants.SERVICE_TYPE_1GE:
155 int tribSlotNb = Map.of(
156 StringConstants.SERVICE_TYPE_100GE_M, 20,
157 StringConstants.SERVICE_TYPE_10GE, 8,
158 StringConstants.SERVICE_TYPE_1GE, 1)
160 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
161 pceResult.setServiceType(serviceType);
162 Map<String, List<Uint16>> tribSlot = chooseTribSlot(path, allPceNodes, tribSlotNb);
163 Map<String, Uint16> tribPort = chooseTribPort(path, allPceNodes, tribSlot, tribSlotNb);
164 List<OpucnTribSlotDef> resultTribPortTribSlot = getMinMaxTpTs(tribPort, tribSlot);
165 if (resultTribPortTribSlot.get(0) != null && resultTribPortTribSlot.get(1) != null) {
166 pceResult.setResultTribPortTribSlot(resultTribPortTribSlot);
167 pceResult.setRC(ResponseCodes.RESPONSE_OK);
168 LOG.info("In PostAlgoPathValidator: found TribPort {} - tribSlot {} - tribSlotNb {}",
169 tribPort, tribSlot, tribSlotNb);
172 case StringConstants.SERVICE_TYPE_ODU4:
173 case StringConstants.SERVICE_TYPE_ODUC2:
174 case StringConstants.SERVICE_TYPE_ODUC3:
175 case StringConstants.SERVICE_TYPE_ODUC4:
176 case StringConstants.SERVICE_TYPE_100GE_S:
177 pceResult.setRC(ResponseCodes.RESPONSE_OK);
178 pceResult.setServiceType(serviceType);
179 LOG.info("In PostAlgoPathValidator: ODU4/ODUCn path found {}", path);
182 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
183 LOG.warn("In PostAlgoPathValidator checkPath: unsupported serviceType {} found {}",
190 private boolean checkLatency(Long maxLatency, GraphPath<String, PceGraphEdge> path) {
192 for (PceGraphEdge edge : path.getEdgeList()) {
193 if (edge.link() == null || edge.link().getLatency() == null) {
194 LOG.warn("- In checkLatency: the link {} does not contain latency field",
195 edge.link().getLinkId().getValue());
198 latency += edge.link().getLatency();
199 LOG.debug("- In checkLatency: latency of {} = {} units", edge.link().getLinkId().getValue(), latency);
201 return (latency < maxLatency);
204 // Check the inclusion if it is defined in the hard constraints
205 private boolean checkInclude(GraphPath<String, PceGraphEdge> path, PceConstraints pceHardConstraintsInput) {
206 List<ResourcePair> listToInclude = pceHardConstraintsInput.getListToInclude()
207 .stream().sorted((rp1, rp2) -> rp1.getName().compareTo(rp2.getName()))
208 .collect(Collectors.toList());
209 if (listToInclude.isEmpty()) {
212 List<PceGraphEdge> pathEdges = path.getEdgeList();
213 LOG.debug(" in checkInclude vertex list: [{}]", path.getVertexList());
214 List<String> listOfElementsSubNode = new ArrayList<>();
215 listOfElementsSubNode.add(pathEdges.get(0).link().getsourceNetworkSupNodeId());
216 listOfElementsSubNode.addAll(
217 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.NODE, pceHardConstraintsInput));
218 List<String> listOfElementsCLLI = new ArrayList<>();
219 listOfElementsCLLI.add(pathEdges.get(0).link().getsourceCLLI());
220 listOfElementsCLLI.addAll(
221 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.CLLI, pceHardConstraintsInput));
222 List<String> listOfElementsSRLG = new ArrayList<>();
223 // first link is XPONDEROUTPUT, no SRLG for it
224 listOfElementsSRLG.add("NONE");
225 listOfElementsSRLG.addAll(
226 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.SRLG, pceHardConstraintsInput));
227 // validation: check each type for each element
228 return listOfElementsSubNode.containsAll(
230 .stream().filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
231 .map(ResourcePair::getName).collect(Collectors.toList()))
232 && listOfElementsSRLG.containsAll(
234 .stream().filter(rp -> PceConstraints.ResourceType.SRLG.equals(rp.getType()))
235 .map(ResourcePair::getName).collect(Collectors.toList()))
236 && listOfElementsCLLI.containsAll(
238 .stream().filter(rp -> PceConstraints.ResourceType.CLLI.equals(rp.getType()))
239 .map(ResourcePair::getName).collect(Collectors.toList()));
242 private List<String> listOfElementsBuild(List<PceGraphEdge> pathEdges, PceConstraints.ResourceType type,
243 PceConstraints pceHardConstraints) {
244 List<String> listOfElements = new ArrayList<>();
245 for (PceGraphEdge link : pathEdges) {
248 listOfElements.add(link.link().getdestNetworkSupNodeId());
251 listOfElements.add(link.link().getdestCLLI());
254 if (link.link().getlinkType() != OpenroadmLinkType.ROADMTOROADM) {
255 listOfElements.add("NONE");
258 // srlg of link is List<Long>. But in this algo we need string representation of
260 // this should be any SRLG mentioned in include constraints if any of them if
262 boolean found = false;
263 for (Long srlg : link.link().getsrlgList()) {
264 String srlgStr = String.valueOf(srlg);
265 if (pceHardConstraints.getSRLGnames().contains(srlgStr)) {
266 listOfElements.add(srlgStr);
267 LOG.info("listOfElementsBuild. FOUND SRLG {} in link {}", srlgStr, link.link());
272 // there is no specific srlg to include. thus add to list just the first one
273 listOfElements.add("NONE");
277 LOG.debug("listOfElementsBuild unsupported resource type");
280 return listOfElements;
283 private Map<String, Uint16> chooseTribPort(GraphPath<String,
284 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, Map<String, List<Uint16>> tribSlotMap, int nbSlot) {
285 LOG.debug("In choosetribPort: edgeList = {} ", path.getEdgeList());
286 Map<String, Uint16> tribPortMap = new HashMap<>();
287 for (PceGraphEdge edge : path.getEdgeList()) {
288 List<Uint16> srcTpnPool =
290 .get(edge.link().getSourceId())
291 .getAvailableTribPorts()
292 .get(edge.link().getSourceTP().getValue());
293 List<Uint16> destTpnPool =
295 .get(edge.link().getDestId())
296 .getAvailableTribPorts()
297 .get(edge.link().getDestTP().getValue());
298 List<Uint16> commonEdgeTpnPool = new ArrayList<>();
299 for (Uint16 srcTpn : srcTpnPool) {
300 if (destTpnPool.contains(srcTpn)) {
301 commonEdgeTpnPool.add(srcTpn);
304 if (commonEdgeTpnPool.isEmpty()) {
307 Integer startTribSlot = tribSlotMap.values().stream().findFirst().get().get(0).toJava();
308 Integer tribPort = (int) Math.ceil((double)startTribSlot / nbSlot);
309 for (Uint16 commonTribPort : commonEdgeTpnPool) {
310 if (tribPort.equals(commonTribPort.toJava())) {
311 tribPortMap.put(edge.link().getLinkId().getValue(), commonTribPort);
315 tribPortMap.forEach((k,v) -> LOG.info("TribPortMap : k = {}, v = {}", k, v));
319 private Map<String, List<Uint16>> chooseTribSlot(GraphPath<String,
320 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, int nbSlot) {
321 LOG.debug("In choosetribSlot: edgeList = {} ", path.getEdgeList());
322 Map<String, List<Uint16>> tribSlotMap = new HashMap<>();
323 for (PceGraphEdge edge : path.getEdgeList()) {
324 List<Uint16> srcTsPool =
326 .get(edge.link().getSourceId())
327 .getAvailableTribSlots()
328 .get(edge.link().getSourceTP().getValue());
329 List<Uint16> destTsPool =
331 .get(edge.link().getDestId())
332 .getAvailableTribSlots()
333 .get(edge.link().getDestTP().getValue());
334 List<Uint16> commonEdgeTsPoolList = new ArrayList<>();
335 List<Uint16> tribSlotList = new ArrayList<>();
336 for (Uint16 integer : srcTsPool) {
337 if (destTsPool.contains(integer)) {
338 commonEdgeTsPoolList.add(integer);
341 Collections.sort(commonEdgeTsPoolList);
342 List<Uint16> commonGoodStartEdgeTsPoolList = new ArrayList<>();
343 for (Uint16 startEdgeTsPool : commonEdgeTsPoolList) {
344 if (Integer.valueOf(1).equals(startEdgeTsPool.toJava() % nbSlot) || nbSlot == 1) {
345 commonGoodStartEdgeTsPoolList.add(startEdgeTsPool);
348 Collections.sort(commonGoodStartEdgeTsPoolList);
349 boolean goodTsList = false;
350 for (Uint16 goodStartTsPool : commonGoodStartEdgeTsPoolList) {
351 int goodStartIndex = commonEdgeTsPoolList.indexOf(Uint16.valueOf(goodStartTsPool.intValue()));
352 if (!goodTsList && commonEdgeTsPoolList.size() - goodStartIndex >= nbSlot) {
353 for (int i = 0; i < nbSlot; i++) {
354 if (!commonEdgeTsPoolList.get(goodStartIndex + i)
355 .equals(Uint16.valueOf(goodStartTsPool.toJava() + i))) {
357 tribSlotList.clear();
360 tribSlotList.add(commonEdgeTsPoolList.get(goodStartIndex + i));
365 tribSlotMap.put(edge.link().getLinkId().getValue(), tribSlotList);
367 tribSlotMap.forEach((k,v) -> LOG.info("TribSlotMap : k = {}, v = {}", k, v));
371 private List<OpucnTribSlotDef> getMinMaxTpTs(Map<String, Uint16> tribPort, Map<String, List<Uint16>> tribSlot) {
372 String tribport = tribPort.values().toArray()[0].toString();
373 List<Uint16> tsList = (List<Uint16>) tribSlot.values().toArray()[0];
374 return new ArrayList<>(List.of(
375 OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(0).toString())),
376 OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(tsList.size() - 1).toString()))));
379 private double checkOSNR(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
380 Map<LinkId, PceLink> allPceLinks, String serviceType, String direction, CatalogUtils cu) {
382 case StringConstants.SERVICE_DIRECTION_AZ:
383 return checkOSNRaz(path, allPceNodes, allPceLinks, serviceType, cu);
384 case StringConstants.SERVICE_DIRECTION_ZA:
385 return checkOSNRza(path, allPceNodes, allPceLinks, serviceType, cu);
387 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported direction {}", direction);
393 * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
395 * @param path the AtoZ path provided by the PCE.
396 * @param allPceNode The map of chosen/relevant PceNodes build from topology pruning.
397 * @param allPceLinks The map of PceLinks build corresponding to the whole topology.
398 * @param serviceType The service Type used to extrapolate Operational mode when it is not provided.
399 * @param cu CatalogUtils instance.
400 * @return the calculated margin according to the Transponder performances and path impairments.
402 private double checkOSNRaz(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
403 Map<LinkId, PceLink> allPceLinks, String serviceType, CatalogUtils cu) {
404 double spacing = 50.0;
406 double calcOsnrdB = 0;
409 double calcOnsrLin = 0.0001;
411 double pwrIn = -60.0;
412 double pwrOut = -60.0;
413 boolean transponderPresent = false;
414 List<String> vertices = path.getVertexList();
415 List<PceGraphEdge> edges = path.getEdgeList();
416 // LOOP that scans the different Nodes/Links of the path and calculates
417 // associated degradations
418 // using CatalogUtils primitives to retrieve physical parameters and make a
419 // first level calculation
420 int bypassDegree = 0;
421 for (int pathElement = 0; pathElement < 2; pathElement++) {
423 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
424 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + 1)));
425 LOG.debug("loop of check OSNR direction AZ, Path Element = {}", pathElement);
426 switch (currentNode.getORNodeType()) {
428 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
429 transponderPresent = true;
430 Map<String, Double> results = calcXpdrOSNR(cu,
432 // First transponder on the Path (TX side) / Last Xponder of the path (RX side)
433 ? edges.get(pathElement).link().getSourceTP().getValue()
434 : edges.get(pathElement - 1).link().getDestTP().getValue(),
435 serviceType, currentNode, nextNode, vertices.get(pathElement), pathElement);
436 calcOnsrLin = results.get("calcOnsrLin");
437 spacing = results.get("spacing");
440 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", pathElement);
441 // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
442 // Xponder to Xponder) node element of the path is the ADD SRG.
443 if (edges.get(pathElement).link().getlinkType() != OpenroadmLinkType.ADDLINK) {
444 LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
445 currentNode.getNodeId(), pathElement);
448 Map<String, Double> impairments = calcAddContrib(
449 cu, currentNode, edges.get(pathElement + 1).link(),
450 calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
451 calcCd = impairments.get("calcCd");
452 calcPmd2 = impairments.get("calcPmd2");
453 calcPdl2 = impairments.get("calcPdl2");
454 calcOnsrLin = impairments.get("calcOnsrLin");
455 pwrOut = impairments.get("pwrOut");
456 LOG.debug("loop of check OSNR direction AZ: SRG, pathElement = {} link {} Pout = {}",
457 pathElement, pathElement + 1, pwrOut);
458 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
461 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
462 // The Degree is not considered. This means we must bypass the add-link (ADD)
463 // and the next node (Degree) which are not considered in the impairments.
469 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
472 for (int pathElement = 2 + bypassDegree; pathElement < vertices.size() - 1; pathElement++) {
473 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
474 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + 1)));
475 LOG.debug("loop of check OSNR direction AZ: Path Element = {}", pathElement);
476 switch (currentNode.getORNodeType()) {
478 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", pathElement);
479 // Other case is DROP, for which cnt is unchanged (.DROP)
480 if (edges.get(pathElement - 1).link().getlinkType() != OpenroadmLinkType.DROPLINK) {
481 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
482 currentNode.getNodeId(), pathElement - 1);
484 PceLink pceLink = edges.get(pathElement - 2).link();
485 LOG.info("loop of check OSNR : SRG, pathElement = {} CD on preceeding link {} = {} ps",
486 pathElement, pathElement - 2, pceLink.getcd());
487 Map<String, Double> impairments = calcDropContrib(
488 cu, currentNode, pceLink, pwrOut, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
489 calcCd = impairments.get("calcCd");
490 calcPmd2 = impairments.get("calcPmd2");
491 calcPdl2 = impairments.get("calcPdl2");
492 calcOnsrLin = impairments.get("calcOnsrLin");
493 pwrIn = impairments.get("pwrIn");
494 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
497 // If SRG is not the first or the second element of the Path, it is the DROP
499 // After accumulated degradations are calculated, we also need to calculate
500 // resulting OSNR in dB to pass it to the method that verifies end Xponder
501 // performances are compatible with degradations experienced on the path
503 calcOsnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
504 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOsnrdB);
505 LOG.info("Loop pathElement = {}, DROP, calcOsnrdB= {}", pathElement, calcOsnrdB);
506 } catch (ArithmeticException e) {
507 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
508 path.getEdgeList().size());
513 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
514 //This is the case of DROP, ROADM degree is not considered
517 LOG.info("loop of check OSNR direction AZ: DEGREE, Path Element = {}", pathElement);
518 Map<String, Double> impairments0 = calcBypassContrib(
519 cu, currentNode, nextNode,
520 edges.get(pathElement - 1).link(), edges.get(pathElement + 1).link(),
521 pwrOut, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
522 calcCd = impairments0.get("CD").doubleValue();
523 calcPmd2 = impairments0.get("DGD2").doubleValue();
524 calcPdl2 = impairments0.get("PDL2").doubleValue();
525 calcOnsrLin = impairments0.get("ONSRLIN").doubleValue();
526 //TODO rename impariments0 var and/or adapt catalog utils
527 pwrIn = impairments0.get("pwrIn").doubleValue();
528 pwrOut = impairments0.get("pwrOut").doubleValue();
530 "Loop pathElement= {}, DEGREE, calcOsnrdB= {}", pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
531 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
534 // increment pathElement so that in next step we will not point to Degree2 but
537 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
538 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId(),
539 nextNode.getNodeId(), calcCd, calcPmd2, calcPdl2, getOsnrDbfromOnsrLin(calcOnsrLin));
542 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
543 LOG.error("unsupported back to back transponder configuration");
546 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
549 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(vertices.size() - 1)));
550 LOG.debug("loop of check OSNR, Path Element = {}", vertices.size() - 1);
551 switch (currentNode.getORNodeType()) {
553 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", vertices.size() - 1);
554 transponderPresent = true;
555 // TSP is the last of the path
556 margin = getLastXpdrMargin(cu, edges.get(vertices.size() - 2).link().getDestTP().getValue(),
557 serviceType, currentNode, vertices.get(vertices.size() - 1), vertices.size() - 1,
558 calcCd, calcPmd2, calcPdl2, calcOnsrLin);
561 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", vertices.size() - 1);
562 // Other case is DROP, for which cnt is unchanged (.DROP)
563 if (edges.get(vertices.size() - 2).link().getlinkType() != OpenroadmLinkType.DROPLINK) {
564 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
565 currentNode.getNodeId(), vertices.size() - 2);
567 PceLink pceLink = edges.get(vertices.size() - 3).link();
568 LOG.info("loop of check OSNR : SRG, pathElement = {} CD on preceeding link {} = {} ps",
569 vertices.size() - 1, vertices.size() - 3, pceLink.getcd());
570 Map<String, Double> impairments = calcDropContrib(
571 cu, currentNode, pceLink, pwrOut, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
572 calcCd = impairments.get("calcCd");
573 calcPmd2 = impairments.get("calcPmd2");
574 calcPdl2 = impairments.get("calcPdl2");
575 calcOnsrLin = impairments.get("calcOnsrLin");
576 //commented out to avoid spotbug DLS_DEAD_LOCAL_STORE pwrIn = impairments.get("pwrIn");
577 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
580 // If SRG is not the first or the second element of the Path, it is the DROP
582 // After accumulated degradations are calculated, we also need to calculate
583 // resulting OSNR in dB to pass it to the method that verifies end Xponder
584 // performances are compatible with degradations experienced on the path
586 calcOsnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
587 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOsnrdB);
588 LOG.info("Loop pathElement = {}, DROP, calcOsnrdB= {}", vertices.size() - 1, calcOsnrdB);
589 } catch (ArithmeticException e) {
590 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
591 path.getEdgeList().size());
597 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain last element");
599 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOsnrdB = {} "
600 + "dB and ONSR dB exterapolated from calcosnrlin = {} including non linear contributions",
601 calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2), calcOsnrdB, getOsnrDbfromOnsrLin(calcOnsrLin));
602 if (!transponderPresent) {
603 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
604 + "that optical tunnel degradations are compatible with external transponder performances");
607 double delta = margin - SYS_MARGIN;
608 LOG.info("In checkOSNR: Transponder Operational mode results in a residual margin of {} dB, according "
609 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
611 String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
612 LOG.info("- In checkOSNR: A to Z Path from {} to {} {}",
613 vertices.get(0), vertices.get(vertices.size() - 1), validationMessage);
618 * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
620 * @param path the AtoZ path provided by the PCE.
621 * @param allPceNode The map of chosen/relevant PceNodes build from topology pruning.
622 * @param allPceLinks The map of PceLinks build corresponding to the whole topology.
623 * @param serviceType The service Type used to extrapolate Operational mode when it is not provided.
624 * @param cu CatalogUtils instance.
625 * @return the calculated margin according to the Transponder performances and path impairments.
627 private double checkOSNRza(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
628 Map<LinkId, PceLink> allPceLinks, String serviceType, CatalogUtils cu) {
629 double spacing = 50.0;
631 double calcOsnrdB = 0;
634 double calcOnsrLin = 0.0001;
636 double pwrIn = -60.0;
637 double pwrOut = -60.0;
638 boolean transponderPresent = false;
639 List<String> vertices = path.getVertexList();
640 List<PceGraphEdge> edges = path.getEdgeList();
641 // LOOP that scans the different Nodes/Links of the path and calculates
642 // associated degradations
643 // using CatalogUtils primitives to retrieve physical parameters and make a
644 // first level calculation
645 int bypassDegree = 0;
646 for (int pathElement = vertices.size() - 1; pathElement > vertices.size() - 3; pathElement--) {
648 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
649 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement - 1)));
650 LOG.debug("loop of check OSNR direction ZA: Path Element = {}", pathElement);
651 switch (currentNode.getORNodeType()) {
653 LOG.debug("loop of check OSNR direction ZA: XPDR, Path Element = {}", pathElement);
654 transponderPresent = true;
655 Map<String, Double> results = calcXpdrOSNR(cu,
656 pathElement == vertices.size() - 1
657 // First transponder on the Path (TX side) / Last Xponder of the path (RX side)
658 ? getOppPceLink(pathElement - 1, edges, allPceLinks).getSourceTP().getValue()
659 : getOppPceLink((pathElement), edges, allPceLinks).getDestTP().getValue(),
660 serviceType, currentNode, nextNode, vertices.get(pathElement), pathElement);
661 calcOnsrLin = results.get("calcOnsrLin");
662 spacing = results.get("spacing");
665 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = {}", pathElement);
666 // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
667 // Xponder to Xponder) node element of the path is the ADD SRG.
668 if (getOppPceLink(pathElement - 1, edges, allPceLinks).getlinkType() != OpenroadmLinkType.ADDLINK) {
669 LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
670 currentNode.getNodeId(), pathElement - 1);
674 Map<String, Double> impairments = calcAddContrib(
675 cu, currentNode, getOppPceLink(pathElement - 2, edges, allPceLinks),
676 calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
677 calcCd = impairments.get("calcCd");
678 calcPmd2 = impairments.get("calcPmd2");
679 calcPdl2 = impairments.get("calcPdl2");
680 calcOnsrLin = impairments.get("calcOnsrLin");
681 pwrOut = impairments.get("pwrOut");
682 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
685 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
686 // The Degree is not considered. This means we must bypass the add-link (ADD)
687 // and the next node (Degree) which are not considered in the impairments.
693 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
696 for (int pathElement = vertices.size() - 3 - bypassDegree; pathElement > 0; pathElement--) {
697 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
698 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement - 1)));
699 LOG.debug("loop of check OSNR direction ZA: Path Element = {}", pathElement);
700 switch (currentNode.getORNodeType()) {
702 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = {}", pathElement);
703 if (getOppPceLink(pathElement, edges, allPceLinks).getlinkType() != OpenroadmLinkType.DROPLINK) {
704 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
705 currentNode.getNodeId(), pathElement);
707 PceLink pceLink = getOppPceLink(pathElement + 1, edges, allPceLinks);
708 LOG.info("loop of check OSNR direction ZA: SRG, path Element = {} CD on preceeding link {} = {} ps",
709 pathElement, pathElement + 1, pceLink.getcd());
710 Map<String, Double> impairments = calcDropContrib(
711 cu, currentNode, pceLink, pwrOut, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
712 calcCd = impairments.get("calcCd");
713 calcPmd2 = impairments.get("calcPmd2");
714 calcPdl2 = impairments.get("calcPdl2");
715 calcOnsrLin = impairments.get("calcOnsrLin");
716 pwrIn = impairments.get("pwrIn");
717 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
720 // If SRG is not the first or the second element of the Path, it is the DROP
722 // After accumulated degradations are calculated, we also need to calculate
723 // resulting OSNR in dB to pass it to the method that verifies end Xponder
724 // performances are compatible with degradations experienced on the path
726 calcOsnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
727 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOsnrdB);
728 LOG.info("Loop Path Element = {}, DROP, calcOsnrdB= {}", pathElement, calcOsnrdB);
729 } catch (ArithmeticException e) {
730 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
731 path.getEdgeList().size());
736 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
737 //This is the case of DROP, ROADM degree is not considered
740 LOG.info("loop of check OSNR direction ZA: DEGREE, Path Element = {}", pathElement);
741 Map<String, Double> impairments0 = calcBypassContrib(
742 cu, currentNode, nextNode,
743 getOppPceLink(pathElement, edges, allPceLinks),
744 getOppPceLink(pathElement - 2, edges, allPceLinks),
745 pwrOut, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
746 calcCd = impairments0.get("CD").doubleValue();
747 calcPmd2 = impairments0.get("DGD2").doubleValue();
748 calcPdl2 = impairments0.get("PDL2").doubleValue();
749 calcOnsrLin = impairments0.get("ONSRLIN").doubleValue();
750 //TODO rename impariments0 var and/or adapt catalog utils
751 pwrIn = impairments0.get("pwrIn").doubleValue();
752 pwrOut = impairments0.get("pwrOut").doubleValue();
753 LOG.debug("Loop Path Element = {}, DEGREE, calcOsnrdB= {}",
754 pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
755 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
758 // increment pathElement so that in next step we will not point to Degree2 but
761 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
762 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId(),
763 nextNode.getNodeId(), calcCd, calcPmd2, calcPdl2, getOsnrDbfromOnsrLin(calcOnsrLin));
766 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
767 LOG.error("unsupported back to back transponder configuration");
770 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
773 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(0)));
774 LOG.debug("loop of check OSNR direction ZA: Path Element = 0");
775 switch (currentNode.getORNodeType()) {
777 LOG.debug("loop of check OSNR direction ZA: XPDR, Path Element = 0");
778 transponderPresent = true;
779 // TSP is the last of the path
780 margin = getLastXpdrMargin(cu, getOppPceLink(0, edges, allPceLinks).getDestTP().getValue(),
781 serviceType, currentNode, vertices.get(0), 0, calcCd, calcPmd2, calcPdl2, calcOnsrLin);
784 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = 0");
785 if (getOppPceLink(0, edges, allPceLinks).getlinkType() != OpenroadmLinkType.DROPLINK) {
786 LOG.error("Error processing Node {} for which input link 0 is not a DROPLINK Type",
787 currentNode.getNodeId());
789 PceLink pceLink = getOppPceLink(1, edges, allPceLinks);
790 LOG.info("loop of check OSNR direction ZA: SRG, path Element = 0 CD on preceeding link 1 = {} ps",
792 Map<String, Double> impairments = calcDropContrib(
793 cu, currentNode, pceLink, pwrOut, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
794 calcCd = impairments.get("calcCd");
795 calcPmd2 = impairments.get("calcPmd2");
796 calcPdl2 = impairments.get("calcPdl2");
797 calcOnsrLin = impairments.get("calcOnsrLin");
798 //commented out to avoid spotbug DLS_DEAD_LOCAL_STORE pwrIn = impairments.get("pwrIn");
799 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
802 // If SRG is not the first or the second element of the Path, it is the DROP
804 // After accumulated degradations are calculated, we also need to calculate
805 // resulting OSNR in dB to pass it to the method that verifies end Xponder
806 // performances are compatible with degradations experienced on the path
808 calcOsnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
809 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOsnrdB);
810 LOG.info("Loop Path Element = 0, DROP, calcOsnrdB= {}", calcOsnrdB);
811 } catch (ArithmeticException e) {
812 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
813 path.getEdgeList().size());
819 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain last element");
821 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOsnrdB = {} "
822 + "dB and ONSR dB exterapolated from calcosnrlin = {} including non linear contributions",
823 calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2), calcOsnrdB, getOsnrDbfromOnsrLin(calcOnsrLin));
824 if (!transponderPresent) {
825 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
826 + "that optical tunnel degradations are compatible with external transponder performances");
829 double delta = margin - SYS_MARGIN;
830 LOG.info("In checkOSNR: Transponder Operational mode results in a residual margin of {} dB, according "
831 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
833 String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
834 LOG.info("- In checkOSNR: Z to A Path from {} to {} {}",
835 vertices.get(vertices.size() - 1), vertices.get(0), validationMessage);
839 private String setOpMode(String opMode, String defaultMode) {
841 opMode == null || opMode.isEmpty() || opMode.contentEquals(StringConstants.UNKNOWN_MODE)
846 private PceLink getOppPceLink(Integer pathEltNber, List<PceGraphEdge> edges,
847 Map<LinkId, PceLink> allPceLinks) {
848 return allPceLinks.get(new LinkId(edges.get(pathEltNber).link().getOppositeLink()));
851 private String getXpdrOpMode(String nwTpId, String vertice, int pathElement, PceNode currentNode,
852 String serviceType, CatalogUtils cu) {
853 InstanceIdentifier<TerminationPoint1> nwTpIid =
854 InstanceIdentifiers.createNetworkTerminationPoint1IIDBuilder(vertice, nwTpId);
855 String opMode = cu.getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, serviceType);
857 if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, nwTpIid).get().isPresent()) {
858 // If the operational mode of the Xponder is not consistent nor declared in the topology (Network TP)
860 currentNode.getXponderOperationalMode(
861 networkTransactionService
862 .read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
863 .get().get().getXpdrNetworkAttributes()),
864 // Operational mode is found as an attribute of the network TP
866 // Operational mode is retrieved from the service Type assuming it is supported
869 "Transponder {} corresponding to path Element {} in the path has {} operational mode",
870 currentNode.getNodeId().getValue(), pathElement, opMode);
873 } catch (InterruptedException | ExecutionException e1) {
874 LOG.error("Issue accessing the XponderNetworkAttributes of {} for Transponder {}"
875 + " corresponding to path Element {} in the path ",
876 nwTpId, currentNode.getNodeId().getValue(), pathElement);
878 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
879 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
883 private double getLastXpdrMargin(
884 CatalogUtils cu, String nwTpId, String serviceType, PceNode currentNode, String vertice, int pathElement,
885 double calcCd, double calcPmd2, double calcPdl2, double calcOnsrLin) {
886 LOG.debug("Loop Path Element = {}, Step5.1, XPDR, tries calculating Margin, just before call", pathElement);
887 // Check that accumulated degradations are compatible with TSP performances
888 // According to OpenROADM spec :
889 // margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2),
890 // getOsnrDbfromOnsrLin(calcOnsrLin));
891 // Calculation modified for pdl according to calculation in Julia's Tool
892 double calcosnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
893 LOG.info("Loop Path Element = {}, XPDR, calcosnrdB= {}", pathElement, calcosnrdB);
894 return cu.getPceRxTspParameters(
895 getXpdrOpMode(nwTpId, vertice, pathElement, currentNode, serviceType, cu),
896 calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2), calcosnrdB);
899 private Map<String, Double> calcXpdrOSNR(
900 CatalogUtils cu, String nwTpId, String serviceType,
901 PceNode currentNode, PceNode nextNode, String vertice, int pathElement) {
902 // If the Xponder operational mode (setOpMode Arg1) is not consistent nor declared in the topology (Network TP)
903 // Operational mode is retrieved from the service Type assuming it is supported by the Xponder (setOpMode Arg2)
904 String opMode = getXpdrOpMode(nwTpId, vertice, pathElement, currentNode, serviceType, cu);
905 // If the operational mode of the ADD/DROP MUX is not consistent nor declared in the topology (Network TP)
906 // Operational mode is set by default to standard opMode for ADD SRGs
907 String adnMode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWWRCORE);
908 double calcOnsrLin = cu.getPceTxTspParameters(opMode, adnMode);
910 "Transponder {} corresponding to path Element {} is connected to SRG which has {} operational mode",
911 currentNode.getNodeId().getValue(), pathElement, adnMode);
912 LOG.info("Transponder {} corresponding to path Element {} in the path has a TX OSNR of {} dB",
913 currentNode.getNodeId().getValue(), pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
914 // Return the Tx ONSR of the Xponder which results from IB and OOB OSNR contributions
915 // and the spacing associated with Xponder operational mode that is needed to calculate OSNR
917 "spacing", cu.getPceTxTspChannelSpacing(opMode),
918 "calcOnsrLin", calcOnsrLin);
921 private Map<String, Double> calcDropContrib(
922 CatalogUtils cu, PceNode currentNode, PceLink pceLink,
923 double pwrOut, double calcCd, double calcPmd2, double calcPdl2, double calcOnsrLin, double spacing) {
924 //calculation of the SRG contribution for Drop
925 Map<String, Double> impairments =
926 calcLineDegradation(cu, pceLink, pwrOut, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
927 double pwrIn = impairments.get("pwrIn");
928 impairments = cu.getPceRoadmAmpParameters(
929 CatalogConstant.CatalogNodeType.DROP,
930 setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE),
931 // If the operational mode of the ADD/DROP MUX is not consistent or not declared in the topology (Network TP)
932 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
934 impairments.get("calcCd").doubleValue(),
935 impairments.get("calcPmd2").doubleValue(),
937 impairments.get("calcOnsrLin").doubleValue(),
940 "calcCd", impairments.get("CD"),
941 "calcPmd2", impairments.get("DGD2"),
942 "calcPdl2", impairments.get("PDL2"),
943 "calcOnsrLin", impairments.get("ONSRLIN"),
947 private Map<String, Double> calcAddContrib(
948 CatalogUtils cu, PceNode currentNode, PceLink pceLink,
949 double calcCd, double calcPmd2, double calcPdl2, double calcOnsrLin, double spacing) {
950 //calculation of the SRG contribution for Add
951 String srgMode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE);
952 // If the operational mode of the ADD/DROP MUX is not consistent or is not declared in the topology (Network TP)
953 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
954 CatalogNodeType cnt = CatalogConstant.CatalogNodeType.ADD;
955 double pwrOut = cu.getPceRoadmAmpOutputPower(
956 cnt, srgMode, pceLink.getspanLoss(), spacing, pceLink.getpowerCorrection());
957 //calculation of the SRG contribution either for Add and Drop
958 Map<String, Double> impairments = cu.getPceRoadmAmpParameters(
959 cnt, srgMode, 0, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
961 "calcCd", impairments.get("CD"),
962 "calcPmd2", impairments.get("DGD2"),
963 "calcPdl2", impairments.get("PDL2"),
964 "calcOnsrLin", impairments.get("ONSRLIN"),
968 private Map<String, Double> calcBypassContrib(
969 CatalogUtils cu, PceNode currentNode, PceNode nextNode, PceLink pceLink0, PceLink pceLink1,
970 double pwrOut, double calcCd, double calcPmd2, double calcPdl2, double calcOnsrLin, double spacing) {
971 // If the operational mode of the Degree is not consistent or declared in the topology
972 // Operational mode is set by default to standard opMode for Degree
973 String degree1Mode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWMWCORE);
974 // Same for next node which is the second degree of a ROADM node
975 String degree2Mode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWMWCORE);
976 // At that time OpenROADM provides only one spec for the ROADM nodes
977 if (!degree1Mode.equals(degree2Mode)) {
978 LOG.warn("Unsupported Hybrid ROADM configuration with Degree1 {} of {} operational mode and Degree2 "
979 + "{} of {} operational mode. Will by default use operational mode of Degree2",
980 currentNode.getNodeId(), degree1Mode, nextNode.getNodeId(), degree2Mode);
982 Map<String, Double> impairments = calcLineDegradation(
983 cu, pceLink0, pwrOut, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
984 calcCd = impairments.get("calcCd");
985 calcPmd2 = impairments.get("calcPmd2");
986 calcOnsrLin = impairments.get("calcOnsrLin");
987 CatalogNodeType cnt0 = CatalogConstant.CatalogNodeType.EXPRESS;
988 double pwrIn = impairments.get("pwrIn");
989 pwrOut = cu.getPceRoadmAmpOutputPower(
990 cnt0, degree2Mode, pceLink1.getspanLoss(), spacing, pceLink1.getpowerCorrection());
991 // Adds to accumulated impairments the degradation associated with the Express
992 // path of ROADM : Degree1, express link, Degree2
993 impairments = cu.getPceRoadmAmpParameters(
994 cnt0, degree2Mode, pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
995 impairments.put("pwrIn", pwrIn);
996 impairments.put("pwrOut", pwrOut);
999 //TODO these methods might be more indicated in a catalog utils refactoring
1001 private Map<String, Double> calcLineDegradation(
1002 CatalogUtils cu, PceLink pceLink,
1003 double pwrOut, double calcCd, double calcPmd2, double calcPdl2, double calcOnsrLin, double spacing) {
1004 // Calculate degradation accumulated across incoming Link and add them to
1005 // accumulated impairments
1006 // This also includes Non Linear Contribution from the path
1008 "pwrIn", pwrOut - pceLink.getspanLoss(),
1009 "calcCd", calcCd + pceLink.getcd(),
1010 "calcPmd2", calcPmd2 + pceLink.getpmd2(),
1011 "calcOnsrLin", calcOnsrLin + cu.calculateNLonsrContribution(pwrOut, pceLink.getLength(), spacing));
1014 private double getOsnrDbfromOnsrLin(double onsrLu) {
1015 return 10 * Math.log10(1 / onsrLu);
1019 * Get spectrum assignment for path.
1021 * @param path the path for which we get spectrum assignment.
1022 * @param allPceNodes all optical nodes.
1023 * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
1025 * @return a spectrum assignment object which contains begin and end index. If
1026 * no spectrum assignment found, beginIndex = stopIndex = 0
1028 private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
1029 Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
1030 byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
1031 Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
1032 BitSet result = BitSet.valueOf(freqMap);
1033 boolean isFlexGrid = true;
1034 LOG.debug("Processing path {} with length {}", path, path.getLength());
1035 for (PceGraphEdge edge : path.getEdgeList()) {
1036 NodeId srcNodeId = edge.link().getSourceId();
1037 LOG.debug("Processing source {} ", srcNodeId);
1038 if (allPceNodes.containsKey(srcNodeId)) {
1039 PceNode pceNode = allPceNodes.get(srcNodeId);
1040 LOG.debug("Processing PCE node {}", pceNode);
1041 String pceNodeVersion = pceNode.getVersion();
1042 NodeId pceNodeId = pceNode.getNodeId();
1043 BigDecimal sltWdthGran = pceNode.getSlotWidthGranularity();
1044 BigDecimal ctralFreqGran = pceNode.getCentralFreqGranularity();
1045 if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNodeVersion)) {
1046 LOG.debug("Node {}: version is {} and slot width granularity is {} -> fixed grid mode",
1047 pceNodeId, pceNodeVersion, sltWdthGran);
1050 if (sltWdthGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)
1051 && ctralFreqGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
1052 LOG.debug("Node {}: version is {} with slot width granularity {} and central "
1053 + "frequency granularity is {} -> fixed grid mode",
1054 pceNodeId, pceNodeVersion, sltWdthGran, ctralFreqGran);
1057 BitSet pceNodeFreqMap = pceNode.getBitSetData();
1058 LOG.debug("Pce node bitset {}", pceNodeFreqMap);
1059 if (pceNodeFreqMap != null) {
1060 result.and(pceNodeFreqMap);
1061 LOG.debug("intermediate bitset {}", result);
1065 LOG.debug("Bitset result {}", result);
1066 return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
1070 * Compute spectrum assignment from spectrum occupation for spectral width.
1072 * @param spectrumOccupation the spectrum occupation BitSet.
1073 * @param spectralWidthSlotNumber the nb slots for spectral width.
1074 * @param isFlexGrid true if flexible grid, false otherwise.
1075 * @return a spectrum assignment object which contains begin and stop index. If
1076 * no spectrum assignment found, beginIndex = stopIndex = 0
1078 private SpectrumAssignment computeBestSpectrumAssignment(
1079 BitSet spectrumOccupation, int spectralWidthSlotNumber, boolean isFlexGrid) {
1080 SpectrumAssignmentBuilder spectrumAssignmentBldr = new SpectrumAssignmentBuilder()
1081 .setBeginIndex(Uint16.valueOf(0))
1082 .setStopIndex(Uint16.valueOf(0))
1083 .setFlexGrid(isFlexGrid);
1084 BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
1085 referenceBitSet.set(0, spectralWidthSlotNumber);
1086 //higher is the frequency, smallest is the wavelength number
1087 //in operational, the allocation is done through wavelength starting from the smallest
1088 //so we have to loop from the last element of the spectrum occupation
1089 for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber;
1090 i -= isFlexGrid ? spectralWidthSlotNumber : 1) {
1091 if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
1092 spectrumAssignmentBldr.setBeginIndex(Uint16.valueOf(i - spectralWidthSlotNumber));
1093 spectrumAssignmentBldr.setStopIndex(Uint16.valueOf(i - 1));
1097 return spectrumAssignmentBldr.build();
1100 public Double getTpceCalculatedMargin() {
1101 return tpceCalculatedMargin;