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().orElseThrow().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 Map<String, Double> signal = new HashMap<>(
406 "spacing", Double.valueOf(50.0),
407 "calcPdl2", Double.valueOf(0),
408 "calcCd", Double.valueOf(0),
409 "calcPmd2", Double.valueOf(0),
410 "calcOnsrLin", Double.valueOf(0.0001),
411 "pwrIn", Double.valueOf(-60.0),
412 "pwrOut", Double.valueOf(-60.0)));
413 double calcOnsrdB = 0;
415 boolean transponderPresent = false;
416 List<String> vertices = path.getVertexList();
417 List<PceGraphEdge> edges = path.getEdgeList();
418 // LOOP that scans the different Nodes/Links of the path and calculates
419 // associated degradations
420 // using CatalogUtils primitives to retrieve physical parameters and make a
421 // first level calculation
422 int bypassDegree = 0;
423 for (int pathElement = 0; pathElement < 2; pathElement++) {
425 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
426 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + 1)));
427 LOG.debug("loop of check OSNR direction AZ, Path Element = {}", pathElement);
428 switch (currentNode.getORNodeType()) {
430 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
431 transponderPresent = true;
432 calcXpdrOSNR(cu, signal,
434 // First transponder on the Path (TX side) / Last Xponder of the path (RX side)
435 ? edges.get(pathElement).link().getSourceTP().getValue()
436 : edges.get(pathElement - 1).link().getDestTP().getValue(),
437 serviceType, currentNode, nextNode, vertices.get(pathElement), pathElement);
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);
447 signal.put("pwrIn", Double.valueOf(0));
448 calcAddContrib(cu, signal, currentNode, edges.get(pathElement + 1).link());
449 LOG.debug("loop of check OSNR direction AZ: SRG, pathElement = {} link {} Pout = {}",
450 pathElement, pathElement + 1, signal.get("pwrOut"));
451 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
452 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
455 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
456 // The Degree is not considered. This means we must bypass the add-link (ADD)
457 // and the next node (Degree) which are not considered in the impairments.
463 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
466 for (int pathElement = 2 + bypassDegree; pathElement < vertices.size() - 1; pathElement++) {
467 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
468 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + 1)));
469 LOG.debug("loop of check OSNR direction AZ: Path Element = {}", pathElement);
470 switch (currentNode.getORNodeType()) {
472 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", pathElement);
473 // Other case is DROP, for which cnt is unchanged (.DROP)
474 if (edges.get(pathElement - 1).link().getlinkType() != OpenroadmLinkType.DROPLINK) {
475 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
476 currentNode.getNodeId(), pathElement - 1);
478 PceLink pceLink = edges.get(pathElement - 2).link();
479 LOG.info("loop of check OSNR : SRG, pathElement = {} CD on preceeding link {} = {} ps",
480 pathElement, pathElement - 2, pceLink.getcd());
481 calcDropContrib(cu, signal, currentNode, pceLink);
482 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
483 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
486 // If SRG is not the first or the second element of the Path, it is the DROP
488 // After accumulated degradations are calculated, we also need to calculate
489 // resulting OSNR in dB to pass it to the method that verifies end Xponder
490 // performances are compatible with degradations experienced on the path
492 calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
493 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
494 LOG.info("Loop pathElement = {}, DROP, calcOnsrdB= {}", pathElement, calcOnsrdB);
495 } catch (ArithmeticException e) {
496 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
497 path.getEdgeList().size());
502 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
503 //This is the case of DROP, ROADM degree is not considered
506 LOG.info("loop of check OSNR direction AZ: DEGREE, Path Element = {}", pathElement);
507 calcBypassContrib(cu, signal, currentNode, nextNode,
508 edges.get(pathElement - 1).link(), edges.get(pathElement + 1).link());
509 double calcOnsrLin = signal.get("calcOnsrLin").doubleValue();
511 "Loop pathElement= {}, DEGREE, calcOnsrdB= {}", pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
512 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
515 // increment pathElement so that in next step we will not point to Degree2 but
518 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
519 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId(), nextNode.getNodeId(),
520 signal.get("calcCd"), signal.get("calcPmd2"), signal.get("calcPdl2"),
521 getOsnrDbfromOnsrLin(calcOnsrLin));
524 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
525 LOG.error("unsupported back to back transponder configuration");
528 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
531 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(vertices.size() - 1)));
532 LOG.debug("loop of check OSNR, Path Element = {}", vertices.size() - 1);
533 switch (currentNode.getORNodeType()) {
535 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", vertices.size() - 1);
536 transponderPresent = true;
537 // TSP is the last of the path
538 margin = getLastXpdrMargin(cu, signal, edges.get(vertices.size() - 2).link().getDestTP().getValue(),
539 serviceType, currentNode, vertices.get(vertices.size() - 1), vertices.size() - 1);
542 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", vertices.size() - 1);
543 // Other case is DROP, for which cnt is unchanged (.DROP)
544 if (edges.get(vertices.size() - 2).link().getlinkType() != OpenroadmLinkType.DROPLINK) {
545 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
546 currentNode.getNodeId(), vertices.size() - 2);
548 PceLink pceLink = edges.get(vertices.size() - 3).link();
549 LOG.info("loop of check OSNR : SRG, pathElement = {} CD on preceeding link {} = {} ps",
550 vertices.size() - 1, vertices.size() - 3, pceLink.getcd());
551 calcDropContrib(cu, signal, currentNode, pceLink);
552 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
553 //commented out to avoid spotbug DLS_DEAD_LOCAL_STORE pwrIn = impairments.get("pwrIn");
554 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
557 // If SRG is not the first or the second element of the Path, it is the DROP
559 // After accumulated degradations are calculated, we also need to calculate
560 // resulting OSNR in dB to pass it to the method that verifies end Xponder
561 // performances are compatible with degradations experienced on the path
563 calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
564 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
565 LOG.info("Loop pathElement = {}, DROP, calcOnsrdB= {}", vertices.size() - 1, calcOnsrdB);
566 } catch (ArithmeticException e) {
567 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
568 path.getEdgeList().size());
574 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain last element");
576 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOnsrdB = {} "
577 + "dB and ONSR dB exterapolated from calcosnrlin = {} including non linear contributions",
578 signal.get("calcCd"), Math.sqrt(signal.get("calcPmd2").doubleValue()),
579 Math.sqrt(signal.get("calcPdl2").doubleValue()), calcOnsrdB,
580 getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue()));
581 if (!transponderPresent) {
582 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
583 + "that optical tunnel degradations are compatible with external transponder performances");
586 double delta = margin - SYS_MARGIN;
587 LOG.info("In checkOSNR: Transponder Operational mode results in a residual margin of {} dB, according "
588 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
590 String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
591 LOG.info("- In checkOSNR: A to Z Path from {} to {} {}",
592 vertices.get(0), vertices.get(vertices.size() - 1), validationMessage);
597 * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
599 * @param path the AtoZ path provided by the PCE.
600 * @param allPceNode The map of chosen/relevant PceNodes build from topology pruning.
601 * @param allPceLinks The map of PceLinks build corresponding to the whole topology.
602 * @param serviceType The service Type used to extrapolate Operational mode when it is not provided.
603 * @param cu CatalogUtils instance.
604 * @return the calculated margin according to the Transponder performances and path impairments.
606 private double checkOSNRza(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
607 Map<LinkId, PceLink> allPceLinks, String serviceType, CatalogUtils cu) {
608 Map<String, Double> signal = new HashMap<>(
610 "spacing", Double.valueOf(50.0),
611 "calcPdl2", Double.valueOf(0),
612 "calcCd", Double.valueOf(0),
613 "calcPmd2", Double.valueOf(0),
614 "calcOnsrLin", Double.valueOf(0.0001),
615 "pwrIn", Double.valueOf(-60.0),
616 "pwrOut", Double.valueOf(-60.0)));
617 double calcOnsrdB = 0;
619 boolean transponderPresent = false;
620 List<String> vertices = path.getVertexList();
621 List<PceGraphEdge> edges = path.getEdgeList();
622 // LOOP that scans the different Nodes/Links of the path and calculates
623 // associated degradations
624 // using CatalogUtils primitives to retrieve physical parameters and make a
625 // first level calculation
626 int bypassDegree = 0;
627 for (int pathElement = vertices.size() - 1; pathElement > vertices.size() - 3; pathElement--) {
629 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
630 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement - 1)));
631 LOG.debug("loop of check OSNR direction ZA: Path Element = {}", pathElement);
632 switch (currentNode.getORNodeType()) {
634 LOG.debug("loop of check OSNR direction ZA: XPDR, Path Element = {}", pathElement);
635 transponderPresent = true;
636 calcXpdrOSNR(cu, signal,
637 pathElement == vertices.size() - 1
638 // First transponder on the Path (TX side) / Last Xponder of the path (RX side)
639 ? getOppPceLink(pathElement - 1, edges, allPceLinks).getSourceTP().getValue()
640 : getOppPceLink((pathElement), edges, allPceLinks).getDestTP().getValue(),
641 serviceType, currentNode, nextNode, vertices.get(pathElement), pathElement);
644 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = {}", pathElement);
645 // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
646 // Xponder to Xponder) node element of the path is the ADD SRG.
647 if (getOppPceLink(pathElement - 1, edges, allPceLinks).getlinkType() != OpenroadmLinkType.ADDLINK) {
648 LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
649 currentNode.getNodeId(), pathElement - 1);
651 signal.put("pwrIn", Double.valueOf(0));
652 calcAddContrib(cu, signal, currentNode, getOppPceLink(pathElement - 2, edges, allPceLinks));
653 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
654 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
657 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
658 // The Degree is not considered. This means we must bypass the add-link (ADD)
659 // and the next node (Degree) which are not considered in the impairments.
665 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
668 for (int pathElement = vertices.size() - 3 - bypassDegree; pathElement > 0; pathElement--) {
669 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
670 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement - 1)));
671 LOG.debug("loop of check OSNR direction ZA: Path Element = {}", pathElement);
672 switch (currentNode.getORNodeType()) {
674 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = {}", pathElement);
675 if (getOppPceLink(pathElement, edges, allPceLinks).getlinkType() != OpenroadmLinkType.DROPLINK) {
676 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
677 currentNode.getNodeId(), pathElement);
679 PceLink pceLink = getOppPceLink(pathElement + 1, edges, allPceLinks);
680 LOG.info("loop of check OSNR direction ZA: SRG, path Element = {} CD on preceeding link {} = {} ps",
681 pathElement, pathElement + 1, pceLink.getcd());
682 calcDropContrib(cu, signal, currentNode, pceLink);
683 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
684 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
687 // If SRG is not the first or the second element of the Path, it is the DROP
689 // After accumulated degradations are calculated, we also need to calculate
690 // resulting OSNR in dB to pass it to the method that verifies end Xponder
691 // performances are compatible with degradations experienced on the path
693 calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
694 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
695 LOG.info("Loop Path Element = {}, DROP, calcOnsrdB= {}", pathElement, calcOnsrdB);
696 } catch (ArithmeticException e) {
697 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
698 path.getEdgeList().size());
703 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
704 //This is the case of DROP, ROADM degree is not considered
707 LOG.info("loop of check OSNR direction ZA: DEGREE, Path Element = {}", pathElement);
708 calcBypassContrib(cu, signal, currentNode, nextNode,
709 getOppPceLink(pathElement, edges, allPceLinks),
710 getOppPceLink(pathElement - 2, edges, allPceLinks));
711 double calcOnsrLin = signal.get("calcOnsrLin").doubleValue();
712 LOG.debug("Loop Path Element = {}, DEGREE, calcOnsrdB= {}",
713 pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
714 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
717 // increment pathElement so that in next step we will not point to Degree2 but
720 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
721 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId(), nextNode.getNodeId(),
722 signal.get("calcCd"), signal.get("calcPmd2"), signal.get("calcPdl2"),
723 getOsnrDbfromOnsrLin(calcOnsrLin));
726 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
727 LOG.error("unsupported back to back transponder configuration");
730 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
733 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(0)));
734 LOG.debug("loop of check OSNR direction ZA: Path Element = 0");
735 switch (currentNode.getORNodeType()) {
737 LOG.debug("loop of check OSNR direction ZA: XPDR, Path Element = 0");
738 transponderPresent = true;
739 // TSP is the last of the path
740 margin = getLastXpdrMargin(cu, signal, getOppPceLink(0, edges, allPceLinks).getDestTP().getValue(),
741 serviceType, currentNode, vertices.get(0), 0);
744 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = 0");
745 if (getOppPceLink(0, edges, allPceLinks).getlinkType() != OpenroadmLinkType.DROPLINK) {
746 LOG.error("Error processing Node {} for which input link 0 is not a DROPLINK Type",
747 currentNode.getNodeId());
749 PceLink pceLink = getOppPceLink(1, edges, allPceLinks);
750 LOG.info("loop of check OSNR direction ZA: SRG, path Element = 0 CD on preceeding link 1 = {} ps",
752 calcDropContrib(cu, signal, currentNode, pceLink);
753 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
754 //commented out to avoid spotbug DLS_DEAD_LOCAL_STORE pwrIn = impairments.get("pwrIn");
755 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
758 // If SRG is not the first or the second element of the Path, it is the DROP
760 // After accumulated degradations are calculated, we also need to calculate
761 // resulting OSNR in dB to pass it to the method that verifies end Xponder
762 // performances are compatible with degradations experienced on the path
764 calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
765 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
766 LOG.info("Loop Path Element = 0, DROP, calcOnsrdB= {}", calcOnsrdB);
767 } catch (ArithmeticException e) {
768 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
769 path.getEdgeList().size());
775 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain last element");
777 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOnsrdB = {} "
778 + "dB and ONSR dB exterapolated from calcosnrlin = {} including non linear contributions",
779 signal.get("calcCd"), Math.sqrt(signal.get("calcPmd2").doubleValue()),
780 Math.sqrt(signal.get("calcPdl2").doubleValue()), calcOnsrdB,
781 getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue()));
782 if (!transponderPresent) {
783 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
784 + "that optical tunnel degradations are compatible with external transponder performances");
787 double delta = margin - SYS_MARGIN;
788 LOG.info("In checkOSNR: Transponder Operational mode results in a residual margin of {} dB, according "
789 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
791 String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
792 LOG.info("- In checkOSNR: Z to A Path from {} to {} {}",
793 vertices.get(vertices.size() - 1), vertices.get(0), validationMessage);
797 private String setOpMode(String opMode, String defaultMode) {
799 opMode == null || opMode.isEmpty() || opMode.contentEquals(StringConstants.UNKNOWN_MODE)
804 private PceLink getOppPceLink(Integer pathEltNber, List<PceGraphEdge> edges,
805 Map<LinkId, PceLink> allPceLinks) {
806 return allPceLinks.get(new LinkId(edges.get(pathEltNber).link().getOppositeLink()));
809 private String getXpdrOpMode(String nwTpId, String vertice, int pathElement, PceNode currentNode,
810 String serviceType, CatalogUtils cu) {
811 InstanceIdentifier<TerminationPoint1> nwTpIid =
812 InstanceIdentifiers.createNetworkTerminationPoint1IIDBuilder(vertice, nwTpId);
813 String opMode = cu.getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, serviceType);
815 if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, nwTpIid).get().isPresent()) {
816 // If the operational mode of the Xponder is not consistent nor declared in the topology (Network TP)
818 currentNode.getXponderOperationalMode(
819 networkTransactionService
820 .read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
821 .get().orElseThrow().getXpdrNetworkAttributes()),
822 // Operational mode is found as an attribute of the network TP
824 // Operational mode is retrieved from the service Type assuming it is supported
827 "Transponder {} corresponding to path Element {} in the path has {} operational mode",
828 currentNode.getNodeId().getValue(), pathElement, opMode);
831 } catch (InterruptedException | ExecutionException e1) {
832 LOG.error("Issue accessing the XponderNetworkAttributes of {} for Transponder {}"
833 + " corresponding to path Element {} in the path ",
834 nwTpId, currentNode.getNodeId().getValue(), pathElement);
836 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
837 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
841 private double getLastXpdrMargin(
842 CatalogUtils cu, Map<String, Double> signal,
843 String nwTpId, String serviceType, PceNode currentNode, String vertice, int pathElement) {
844 LOG.debug("Loop Path Element = {}, Step5.1, XPDR, tries calculating Margin, just before call", pathElement);
845 // Check that accumulated degradations are compatible with TSP performances
846 // According to OpenROADM spec :
847 // margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2),
848 // getOsnrDbfromOnsrLin(calcOnsrLin));
849 // Calculation modified for pdl according to calculation in Julia's Tool
850 double calcOnsrdB = getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue());
851 LOG.info("Loop Path Element = {}, XPDR, calcosnrdB= {}", pathElement, calcOnsrdB);
852 return cu.getPceRxTspParameters(
853 getXpdrOpMode(nwTpId, vertice, pathElement, currentNode, serviceType, cu),
854 signal.get("calcCd").doubleValue(),
855 Math.sqrt(signal.get("calcPmd2").doubleValue()),
856 Math.sqrt(signal.get("calcPdl2").doubleValue()),
860 private void calcXpdrOSNR(
861 CatalogUtils cu, Map<String, Double> signal, String nwTpId, String serviceType,
862 PceNode currentNode, PceNode nextNode, String vertice, int pathElement) {
863 // If the Xponder operational mode (setOpMode Arg1) is not consistent nor declared in the topology (Network TP)
864 // Operational mode is retrieved from the service Type assuming it is supported by the Xponder (setOpMode Arg2)
865 String opMode = getXpdrOpMode(nwTpId, vertice, pathElement, currentNode, serviceType, cu);
866 // If the operational mode of the ADD/DROP MUX is not consistent nor declared in the topology (Network TP)
867 // Operational mode is set by default to standard opMode for ADD SRGs
868 String adnMode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWWRCORE);
869 double calcOnsrLin = cu.getPceTxTspParameters(opMode, adnMode);
871 "Transponder {} corresponding to path Element {} is connected to SRG which has {} operational mode",
872 currentNode.getNodeId().getValue(), pathElement, adnMode);
873 LOG.info("Transponder {} corresponding to path Element {} in the path has a TX OSNR of {} dB",
874 currentNode.getNodeId().getValue(), pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
875 // Return the Tx ONSR of the Xponder which results from IB and OOB OSNR contributions
876 // and the spacing associated with Xponder operational mode that is needed to calculate OSNR
877 signal.put("spacing", Double.valueOf(cu.getPceTxTspChannelSpacing(opMode)));
878 signal.put("calcOnsrLin", Double.valueOf(calcOnsrLin));
881 private void calcDropContrib(
882 CatalogUtils cu, Map<String, Double> signal, PceNode currentNode, PceLink pceLink) {
883 //calculation of the SRG contribution for Drop
884 calcLineDegradation(cu, signal, pceLink);
885 Map<String, Double> impairments = cu.getPceRoadmAmpParameters(
886 CatalogConstant.CatalogNodeType.DROP,
887 setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE),
888 // If the operational mode of the ADD/DROP MUX is not consistent or not declared in the topology (Network TP)
889 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
890 signal.get("pwrIn").doubleValue(),
891 signal.get("calcCd").doubleValue(),
892 signal.get("calcPmd2").doubleValue(),
893 signal.get("calcPdl2").doubleValue(),
894 signal.get("calcOnsrLin").doubleValue(),
895 signal.get("spacing").doubleValue());
898 "calcCd", impairments.get("CD"),
899 "calcPmd2", impairments.get("DGD2"),
900 "calcPdl2", impairments.get("PDL2"),
901 "calcOnsrLin", impairments.get("ONSRLIN")));
904 private void calcAddContrib(
905 CatalogUtils cu, Map<String, Double> signal, PceNode currentNode, PceLink pceLink) {
906 //calculation of the SRG contribution for Add
907 String srgMode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE);
908 // If the operational mode of the ADD/DROP MUX is not consistent or is not declared in the topology (Network TP)
909 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
910 CatalogNodeType cnt = CatalogConstant.CatalogNodeType.ADD;
911 double pwrOut = cu.getPceRoadmAmpOutputPower(
912 cnt, srgMode, pceLink.getspanLoss(), signal.get("spacing").doubleValue(), pceLink.getpowerCorrection());
913 //calculation of the SRG contribution either for Add and Drop
914 Map<String, Double> impairments = cu.getPceRoadmAmpParameters(cnt, srgMode, 0,
915 signal.get("calcCd").doubleValue(), signal.get("calcPmd2").doubleValue(),
916 signal.get("calcPdl2").doubleValue(),
917 signal.get("calcOnsrLin").doubleValue(), signal.get("spacing").doubleValue());
920 "calcCd", impairments.get("CD"),
921 "calcPmd2", impairments.get("DGD2"),
922 "calcPdl2", impairments.get("PDL2"),
923 "calcOnsrLin", impairments.get("ONSRLIN"),
924 "pwrOut", Double.valueOf(pwrOut)));
927 private void calcBypassContrib(CatalogUtils cu, Map<String, Double> signal,
928 PceNode currentNode, PceNode nextNode, PceLink pceLink0, PceLink pceLink1) {
929 // If the operational mode of the Degree is not consistent or declared in the topology
930 // Operational mode is set by default to standard opMode for Degree
931 String degree1Mode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWMWCORE);
932 // Same for next node which is the second degree of a ROADM node
933 String degree2Mode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWMWCORE);
934 // At that time OpenROADM provides only one spec for the ROADM nodes
935 if (!degree1Mode.equals(degree2Mode)) {
936 LOG.warn("Unsupported Hybrid ROADM configuration with Degree1 {} of {} operational mode and Degree2 "
937 + "{} of {} operational mode. Will by default use operational mode of Degree2",
938 currentNode.getNodeId(), degree1Mode, nextNode.getNodeId(), degree2Mode);
940 calcLineDegradation(cu, signal, pceLink0);
941 CatalogNodeType cnt = CatalogConstant.CatalogNodeType.EXPRESS;
942 double pwrOut = cu.getPceRoadmAmpOutputPower(cnt, degree2Mode, pceLink1.getspanLoss(),
943 signal.get("spacing").doubleValue(), pceLink1.getpowerCorrection());
944 // Adds to accumulated impairments the degradation associated with the Express
945 // path of ROADM : Degree1, express link, Degree2
946 Map<String, Double> impairments = cu.getPceRoadmAmpParameters(cnt, degree2Mode,
947 signal.get("pwrIn").doubleValue(), signal.get("calcCd").doubleValue(),
948 signal.get("calcPmd2").doubleValue(), signal.get("calcPdl2").doubleValue(),
949 signal.get("calcOnsrLin").doubleValue(), signal.get("spacing").doubleValue());
952 "calcCd", impairments.get("CD"),
953 "calcPmd2", impairments.get("DGD2"),
954 "calcPdl2", impairments.get("PDL2"),
955 "calcOnsrLin", impairments.get("ONSRLIN"),
956 "pwrOut", Double.valueOf(pwrOut)));
958 //TODO these methods might be more indicated in a catalog utils refactoring
960 private void calcLineDegradation(CatalogUtils cu, Map<String, Double> signal, PceLink pceLink) {
961 // Calculate degradation accumulated across incoming Link and add them to
962 // accumulated impairments
963 // This also includes Non Linear Contribution from the path
964 signal.putAll(Map.of(
965 "pwrIn", Double.valueOf(signal.get("pwrOut").doubleValue() - pceLink.getspanLoss()),
966 "calcCd", Double.valueOf(signal.get("calcCd").doubleValue() + pceLink.getcd()),
967 "calcPmd2", Double.valueOf(signal.get("calcPmd2").doubleValue() + pceLink.getpmd2()),
968 "calcOnsrLin", Double.valueOf(
969 signal.get("calcOnsrLin").doubleValue()
970 + cu.calculateNLonsrContribution(
971 signal.get("pwrOut").doubleValue(), pceLink.getLength(), signal.get("spacing").doubleValue()))));
974 private double getOsnrDbfromOnsrLin(double osnrLu) {
975 return 10 * Math.log10(1 / osnrLu);
979 * Get spectrum assignment for path.
981 * @param path the path for which we get spectrum assignment.
982 * @param allPceNodes all optical nodes.
983 * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
985 * @return a spectrum assignment object which contains begin and end index. If
986 * no spectrum assignment found, beginIndex = stopIndex = 0
988 private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
989 Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
990 byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
991 Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
992 BitSet result = BitSet.valueOf(freqMap);
993 boolean isFlexGrid = true;
994 LOG.debug("Processing path {} with length {}", path, path.getLength());
995 for (PceGraphEdge edge : path.getEdgeList()) {
996 NodeId srcNodeId = edge.link().getSourceId();
997 LOG.debug("Processing source {} ", srcNodeId);
998 if (allPceNodes.containsKey(srcNodeId)) {
999 PceNode pceNode = allPceNodes.get(srcNodeId);
1000 LOG.debug("Processing PCE node {}", pceNode);
1001 String pceNodeVersion = pceNode.getVersion();
1002 NodeId pceNodeId = pceNode.getNodeId();
1003 BigDecimal sltWdthGran = pceNode.getSlotWidthGranularity();
1004 BigDecimal ctralFreqGran = pceNode.getCentralFreqGranularity();
1005 if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNodeVersion)) {
1006 LOG.debug("Node {}: version is {} and slot width granularity is {} -> fixed grid mode",
1007 pceNodeId, pceNodeVersion, sltWdthGran);
1010 if (sltWdthGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)
1011 && ctralFreqGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
1012 LOG.debug("Node {}: version is {} with slot width granularity {} and central "
1013 + "frequency granularity is {} -> fixed grid mode",
1014 pceNodeId, pceNodeVersion, sltWdthGran, ctralFreqGran);
1017 BitSet pceNodeFreqMap = pceNode.getBitSetData();
1018 LOG.debug("Pce node bitset {}", pceNodeFreqMap);
1019 if (pceNodeFreqMap != null) {
1020 result.and(pceNodeFreqMap);
1021 LOG.debug("intermediate bitset {}", result);
1025 LOG.debug("Bitset result {}", result);
1026 return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
1030 * Compute spectrum assignment from spectrum occupation for spectral width.
1032 * @param spectrumOccupation the spectrum occupation BitSet.
1033 * @param spectralWidthSlotNumber the nb slots for spectral width.
1034 * @param isFlexGrid true if flexible grid, false otherwise.
1035 * @return a spectrum assignment object which contains begin and stop index. If
1036 * no spectrum assignment found, beginIndex = stopIndex = 0
1038 private SpectrumAssignment computeBestSpectrumAssignment(
1039 BitSet spectrumOccupation, int spectralWidthSlotNumber, boolean isFlexGrid) {
1040 SpectrumAssignmentBuilder spectrumAssignmentBldr = new SpectrumAssignmentBuilder()
1041 .setBeginIndex(Uint16.valueOf(0))
1042 .setStopIndex(Uint16.valueOf(0))
1043 .setFlexGrid(isFlexGrid);
1044 BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
1045 referenceBitSet.set(0, spectralWidthSlotNumber);
1046 //higher is the frequency, smallest is the wavelength number
1047 //in operational, the allocation is done through wavelength starting from the smallest
1048 //so we have to loop from the last element of the spectrum occupation
1049 for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber;
1050 i -= isFlexGrid ? spectralWidthSlotNumber : 1) {
1051 if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
1052 spectrumAssignmentBldr.setBeginIndex(Uint16.valueOf(i - spectralWidthSlotNumber));
1053 spectrumAssignmentBldr.setStopIndex(Uint16.valueOf(i - 1));
1057 return spectrumAssignmentBldr.build();
1060 public Double getTpceCalculatedMargin() {
1061 return tpceCalculatedMargin;