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.LinkedHashSet;
20 import java.util.List;
23 import java.util.concurrent.ExecutionException;
24 import java.util.stream.Collectors;
25 import org.jgrapht.GraphPath;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.transportpce.common.InstanceIdentifiers;
28 import org.opendaylight.transportpce.common.ResponseCodes;
29 import org.opendaylight.transportpce.common.StringConstants;
30 import org.opendaylight.transportpce.common.catalog.CatalogConstant;
31 import org.opendaylight.transportpce.common.catalog.CatalogConstant.CatalogNodeType;
32 import org.opendaylight.transportpce.common.catalog.CatalogUtils;
33 import org.opendaylight.transportpce.common.fixedflex.GridConstant;
34 import org.opendaylight.transportpce.common.fixedflex.GridUtils;
35 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
36 import org.opendaylight.transportpce.pce.constraints.PceConstraints;
37 import org.opendaylight.transportpce.pce.constraints.PceConstraints.ResourcePair;
38 import org.opendaylight.transportpce.pce.networkanalyzer.PceLink;
39 import org.opendaylight.transportpce.pce.networkanalyzer.PceNode;
40 import org.opendaylight.transportpce.pce.networkanalyzer.PceResult;
41 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.PceConstraintMode;
42 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.SpectrumAssignment;
43 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.pce.rev240205.SpectrumAssignmentBuilder;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.topology.rev230526.TerminationPoint1;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev230526.OpenroadmLinkType;
46 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev230526.OpenroadmNodeType;
47 import org.opendaylight.yang.gen.v1.http.org.openroadm.otn.common.types.rev210924.OpucnTribSlotDef;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.LinkId;
50 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
51 import org.opendaylight.yangtools.yang.common.Uint16;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
55 public class PostAlgoPathValidator {
57 private static final Logger LOG = LoggerFactory.getLogger(PostAlgoPathValidator.class);
59 public static final Long CONST_OSNR = 1L;
60 public static final double SYS_MARGIN = 0;
62 private Double tpceCalculatedMargin = 0.0;
63 private final NetworkTransactionService networkTransactionService;
64 private final BitSet spectrumConstraint;
66 public PostAlgoPathValidator(NetworkTransactionService networkTransactionService, BitSet spectrumConstraint) {
67 this.networkTransactionService = networkTransactionService;
68 this.spectrumConstraint = spectrumConstraint;
71 @SuppressWarnings("fallthrough")
73 value = "SF_SWITCH_FALLTHROUGH",
74 justification = "intentional fallthrough")
75 public PceResult checkPath(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
76 Map<LinkId, PceLink> allPceLinks, PceResult pceResult, PceConstraints pceHardConstraints,
77 String serviceType, PceConstraintMode mode) {
78 LOG.info("path = {}", path);
79 // check if the path is empty
80 if (path.getEdgeList().isEmpty()) {
81 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
84 int spectralWidthSlotNumber =
85 GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_100G);
86 SpectrumAssignment spectrumAssignment = null;
87 //variable to deal with 1GE (Nb=1) and 10GE (Nb=10) cases
88 switch (serviceType) {
89 case StringConstants.SERVICE_TYPE_OTUC2:
90 case StringConstants.SERVICE_TYPE_OTUC3:
91 case StringConstants.SERVICE_TYPE_OTUC4:
92 case StringConstants.SERVICE_TYPE_400GE:
93 spectralWidthSlotNumber =
94 GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_400G);
96 case StringConstants.SERVICE_TYPE_100GE_T:
97 case StringConstants.SERVICE_TYPE_OTU4:
98 spectrumAssignment = getSpectrumAssignment(path, allPceNodes, spectralWidthSlotNumber);
99 pceResult.setServiceType(serviceType);
100 if (spectrumAssignment.getBeginIndex().equals(Uint16.valueOf(0))
101 && spectrumAssignment.getStopIndex().equals(Uint16.valueOf(0))) {
102 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
103 pceResult.setLocalCause(PceResult.LocalCause.NO_PATH_EXISTS);
106 if (spectrumAssignment.getFlexGrid()) {
107 LOG.debug("Spectrum assignment flexgrid mode");
108 pceResult.setResultWavelength(GridConstant.IRRELEVANT_WAVELENGTH_NUMBER);
110 LOG.debug("Spectrum assignment fixedgrid mode");
111 pceResult.setResultWavelength(
112 GridUtils.getWaveLengthIndexFromSpectrumAssigment(spectrumAssignment.getBeginIndex().toJava()));
114 pceResult.setMinFreq(GridUtils.getStartFrequencyFromIndex(spectrumAssignment.getBeginIndex().toJava()));
115 pceResult.setMaxFreq(GridUtils.getStopFrequencyFromIndex(spectrumAssignment.getStopIndex().toJava()));
116 LOG.debug("In PostAlgoPathValidator: spectrum assignment found {} {}", spectrumAssignment, path);
119 CatalogUtils cu = new CatalogUtils(networkTransactionService);
120 if (cu.isCatalogFilled()) {
121 double margin1 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
122 StringConstants.SERVICE_DIRECTION_AZ, cu);
123 double margin2 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
124 StringConstants.SERVICE_DIRECTION_ZA, cu);
125 if (margin1 < 0 || margin2 < 0 || margin1 == Double.NEGATIVE_INFINITY
126 || margin2 == Double.NEGATIVE_INFINITY) {
127 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
128 pceResult.setLocalCause(PceResult.LocalCause.OUT_OF_SPEC_OSNR);
131 this.tpceCalculatedMargin = Math.min(margin1, margin2);
133 "In PostAlgoPathValidator: Minimum margin estimated by tpce on AtoZ and ZtoA path is of {} dB",
134 this.tpceCalculatedMargin);
136 this.tpceCalculatedMargin = 0.0;
137 LOG.info("In PostAlgoPathValidator: Operational mode Catalog not filled, delegate OSNR calculation"
138 + " to GNPy and margin set to 0");
140 // Check if MaxLatency is defined in the hard constraints
141 if (pceHardConstraints.getMaxLatency() != -1
142 && !checkLatency(pceHardConstraints.getMaxLatency(), path)) {
143 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
144 pceResult.setLocalCause(PceResult.LocalCause.TOO_HIGH_LATENCY);
147 // Check if nodes are included in the hard constraints
148 if (!checkInclude(path, pceHardConstraints, mode)) {
149 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
150 pceResult.setLocalCause(PceResult.LocalCause.HD_NODE_INCLUDE);
153 // TODO here other post algo validations can be added
154 // more data can be sent to PceGraph module via PceResult structure if required
155 pceResult.setRC(ResponseCodes.RESPONSE_OK);
156 pceResult.setLocalCause(PceResult.LocalCause.NONE);
158 case StringConstants.SERVICE_TYPE_100GE_M:
159 case StringConstants.SERVICE_TYPE_10GE:
160 case StringConstants.SERVICE_TYPE_1GE:
161 int tribSlotNb = Map.of(
162 StringConstants.SERVICE_TYPE_100GE_M, 20,
163 StringConstants.SERVICE_TYPE_10GE, 8,
164 StringConstants.SERVICE_TYPE_1GE, 1)
166 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
167 pceResult.setServiceType(serviceType);
168 Map<String, List<Uint16>> tribSlot = chooseTribSlot(path, allPceNodes, tribSlotNb);
169 Map<String, Uint16> tribPort = chooseTribPort(path, allPceNodes, tribSlot, tribSlotNb);
170 List<OpucnTribSlotDef> resultTribPortTribSlot = getMinMaxTpTs(tribPort, tribSlot);
171 if (resultTribPortTribSlot.get(0) != null && resultTribPortTribSlot.get(1) != null) {
172 pceResult.setResultTribPortTribSlot(resultTribPortTribSlot);
173 pceResult.setRC(ResponseCodes.RESPONSE_OK);
174 LOG.info("In PostAlgoPathValidator: found TribPort {} - tribSlot {} - tribSlotNb {}",
175 tribPort, tribSlot, tribSlotNb);
178 case StringConstants.SERVICE_TYPE_ODU4:
179 case StringConstants.SERVICE_TYPE_ODUC2:
180 case StringConstants.SERVICE_TYPE_ODUC3:
181 case StringConstants.SERVICE_TYPE_ODUC4:
182 case StringConstants.SERVICE_TYPE_100GE_S:
183 pceResult.setRC(ResponseCodes.RESPONSE_OK);
184 pceResult.setServiceType(serviceType);
185 LOG.info("In PostAlgoPathValidator: ODU4/ODUCn path found {}", path);
188 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
189 LOG.warn("In PostAlgoPathValidator checkPath: unsupported serviceType {} found {}",
196 private boolean checkLatency(Long maxLatency, GraphPath<String, PceGraphEdge> path) {
198 for (PceGraphEdge edge : path.getEdgeList()) {
199 if (edge.link() == null || edge.link().getLatency() == null) {
200 LOG.warn("- In checkLatency: the link {} does not contain latency field",
201 edge.link().getLinkId().getValue());
204 latency += edge.link().getLatency();
205 LOG.debug("- In checkLatency: latency of {} = {} units", edge.link().getLinkId().getValue(), latency);
207 return (latency < maxLatency);
210 // Check the inclusion if it is defined in the hard constraints
211 //TODO: remove this checkstyle false positive warning when the checkstyle bug will be fixed
212 @SuppressWarnings("MissingSwitchDefault")
213 private boolean checkInclude(GraphPath<String, PceGraphEdge> path, PceConstraints pceHardConstraintsInput,
214 PceConstraintMode mode) {
215 List<ResourcePair> listToInclude = pceHardConstraintsInput.getListToInclude();
216 if (listToInclude.isEmpty()) {
219 List<PceGraphEdge> pathEdges = path.getEdgeList();
220 LOG.debug(" in checkInclude vertex list: [{}]", path.getVertexList());
221 LOG.debug("listToInclude = {}", listToInclude);
222 List<String> listOfElementsSubNode = new ArrayList<>();
223 listOfElementsSubNode.add(pathEdges.get(0).link().getsourceNetworkSupNodeId());
224 listOfElementsSubNode.addAll(
225 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.NODE, pceHardConstraintsInput));
226 List<String> listOfElementsCLLI = new ArrayList<>();
227 listOfElementsCLLI.add(pathEdges.get(0).link().getsourceCLLI());
228 listOfElementsCLLI.addAll(
229 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.CLLI, pceHardConstraintsInput));
230 List<String> listOfElementsSRLG = new ArrayList<>();
231 // first link is XPONDEROUTPUT, no SRLG for it
232 listOfElementsSRLG.add("NONE");
233 listOfElementsSRLG.addAll(
234 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.SRLG, pceHardConstraintsInput));
235 // validation: check each type for each element
236 LOG.debug("listOfElementsSubNode = {}", listOfElementsSubNode);
237 return switch (mode) {
238 case Loose -> listOfElementsSubNode
239 .containsAll(listToInclude.stream()
240 .filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
241 .map(ResourcePair::getName).collect(Collectors.toList()));
242 case Strict -> listOfElementsSubNode
243 .equals(listToInclude.stream()
244 .filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
245 .map(ResourcePair::getName).collect(Collectors.toList()));
247 && listOfElementsSRLG.containsAll(
249 .stream().filter(rp -> PceConstraints.ResourceType.SRLG.equals(rp.getType()))
250 .map(ResourcePair::getName).collect(Collectors.toList()))
251 && listOfElementsCLLI.containsAll(
253 .stream().filter(rp -> PceConstraints.ResourceType.CLLI.equals(rp.getType()))
254 .map(ResourcePair::getName).collect(Collectors.toList()));
257 private List<String> listOfElementsBuild(List<PceGraphEdge> pathEdges, PceConstraints.ResourceType type,
258 PceConstraints pceHardConstraints) {
259 Set<String> listOfElements = new LinkedHashSet<>();
260 for (PceGraphEdge link : pathEdges) {
263 listOfElements.add(link.link().getdestNetworkSupNodeId());
266 listOfElements.add(link.link().getdestCLLI());
269 if (link.link().getlinkType() != OpenroadmLinkType.ROADMTOROADM) {
270 listOfElements.add("NONE");
273 // srlg of link is List<Long>. But in this algo we need string representation of
275 // this should be any SRLG mentioned in include constraints if any of them if
277 boolean found = false;
278 for (Long srlg : link.link().getsrlgList()) {
279 String srlgStr = String.valueOf(srlg);
280 if (pceHardConstraints.getSRLGnames().contains(srlgStr)) {
281 listOfElements.add(srlgStr);
282 LOG.info("listOfElementsBuild. FOUND SRLG {} in link {}", srlgStr, link.link());
287 // there is no specific srlg to include. thus add to list just the first one
288 listOfElements.add("NONE");
292 LOG.debug("listOfElementsBuild unsupported resource type");
295 return new ArrayList<>(listOfElements);
298 private Map<String, Uint16> chooseTribPort(GraphPath<String,
299 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, Map<String, List<Uint16>> tribSlotMap, int nbSlot) {
300 LOG.debug("In choosetribPort: edgeList = {} ", path.getEdgeList());
301 Map<String, Uint16> tribPortMap = new HashMap<>();
302 for (PceGraphEdge edge : path.getEdgeList()) {
303 List<Uint16> srcTpnPool =
305 .get(edge.link().getSourceId())
306 .getAvailableTribPorts()
307 .get(edge.link().getSourceTP().getValue());
308 List<Uint16> destTpnPool =
310 .get(edge.link().getDestId())
311 .getAvailableTribPorts()
312 .get(edge.link().getDestTP().getValue());
313 List<Uint16> commonEdgeTpnPool = new ArrayList<>();
314 for (Uint16 srcTpn : srcTpnPool) {
315 if (destTpnPool.contains(srcTpn)) {
316 commonEdgeTpnPool.add(srcTpn);
319 if (commonEdgeTpnPool.isEmpty()) {
322 Integer startTribSlot = tribSlotMap.values().stream().findFirst().orElseThrow().get(0).toJava();
323 Integer tribPort = (int) Math.ceil((double)startTribSlot / nbSlot);
324 for (Uint16 commonTribPort : commonEdgeTpnPool) {
325 if (tribPort.equals(commonTribPort.toJava())) {
326 tribPortMap.put(edge.link().getLinkId().getValue(), commonTribPort);
330 tribPortMap.forEach((k,v) -> LOG.info("TribPortMap : k = {}, v = {}", k, v));
334 private Map<String, List<Uint16>> chooseTribSlot(GraphPath<String,
335 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, int nbSlot) {
336 LOG.debug("In choosetribSlot: edgeList = {} ", path.getEdgeList());
337 Map<String, List<Uint16>> tribSlotMap = new HashMap<>();
338 for (PceGraphEdge edge : path.getEdgeList()) {
339 List<Uint16> srcTsPool =
341 .get(edge.link().getSourceId())
342 .getAvailableTribSlots()
343 .get(edge.link().getSourceTP().getValue());
344 List<Uint16> destTsPool =
346 .get(edge.link().getDestId())
347 .getAvailableTribSlots()
348 .get(edge.link().getDestTP().getValue());
349 List<Uint16> commonEdgeTsPoolList = new ArrayList<>();
350 List<Uint16> tribSlotList = new ArrayList<>();
351 for (Uint16 integer : srcTsPool) {
352 if (destTsPool.contains(integer)) {
353 commonEdgeTsPoolList.add(integer);
356 Collections.sort(commonEdgeTsPoolList);
357 List<Uint16> commonGoodStartEdgeTsPoolList = new ArrayList<>();
358 for (Uint16 startEdgeTsPool : commonEdgeTsPoolList) {
359 if (Integer.valueOf(1).equals(startEdgeTsPool.toJava() % nbSlot) || nbSlot == 1) {
360 commonGoodStartEdgeTsPoolList.add(startEdgeTsPool);
363 Collections.sort(commonGoodStartEdgeTsPoolList);
364 boolean goodTsList = false;
365 for (Uint16 goodStartTsPool : commonGoodStartEdgeTsPoolList) {
366 int goodStartIndex = commonEdgeTsPoolList.indexOf(Uint16.valueOf(goodStartTsPool.intValue()));
367 if (!goodTsList && commonEdgeTsPoolList.size() - goodStartIndex >= nbSlot) {
368 for (int i = 0; i < nbSlot; i++) {
369 if (!commonEdgeTsPoolList.get(goodStartIndex + i)
370 .equals(Uint16.valueOf(goodStartTsPool.toJava() + i))) {
372 tribSlotList.clear();
375 tribSlotList.add(commonEdgeTsPoolList.get(goodStartIndex + i));
380 tribSlotMap.put(edge.link().getLinkId().getValue(), tribSlotList);
382 tribSlotMap.forEach((k,v) -> LOG.info("TribSlotMap : k = {}, v = {}", k, v));
386 private List<OpucnTribSlotDef> getMinMaxTpTs(Map<String, Uint16> tribPort, Map<String, List<Uint16>> tribSlot) {
387 String tribport = tribPort.values().toArray()[0].toString();
388 @SuppressWarnings("unchecked") List<Uint16> tsList = (List<Uint16>) tribSlot.values().toArray()[0];
389 return new ArrayList<>(List.of(
390 OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(0).toString())),
391 OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(tsList.size() - 1).toString()))));
394 private double checkOSNR(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
395 Map<LinkId, PceLink> allPceLinks, String serviceType, String direction, CatalogUtils cu) {
397 case StringConstants.SERVICE_DIRECTION_AZ:
398 return checkOSNRaz(path, allPceNodes, allPceLinks, serviceType, cu);
399 case StringConstants.SERVICE_DIRECTION_ZA:
400 return checkOSNRza(path, allPceNodes, allPceLinks, serviceType, cu);
402 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported direction {}", direction);
408 * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
410 * @param path the AtoZ path provided by the PCE.
411 * @param allPceNode The map of chosen/relevant PceNodes build from topology pruning.
412 * @param allPceLinks The map of PceLinks build corresponding to the whole topology.
413 * @param serviceType The service Type used to extrapolate Operational mode when it is not provided.
414 * @param cu CatalogUtils instance.
415 * @return the calculated margin according to the Transponder performances and path impairments.
417 private double checkOSNRaz(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
418 Map<LinkId, PceLink> allPceLinks, String serviceType, CatalogUtils cu) {
419 Map<String, Double> signal = new HashMap<>(
421 "spacing", Double.valueOf(50.0),
422 "calcPdl2", Double.valueOf(0),
423 "calcCd", Double.valueOf(0),
424 "calcPmd2", Double.valueOf(0),
425 "calcOnsrLin", Double.valueOf(0.0001),
426 "pwrIn", Double.valueOf(-60.0),
427 "pwrOut", Double.valueOf(-60.0)));
428 double calcOnsrdB = 0;
430 boolean transponderPresent = false;
431 List<String> vertices = path.getVertexList();
432 List<PceGraphEdge> edges = path.getEdgeList();
433 // LOOP that scans the different Nodes/Links of the path and calculates
434 // associated degradations
435 // using CatalogUtils primitives to retrieve physical parameters and make a
436 // first level calculation
437 int bypassDegree = 0;
438 for (int pathElement = 0; pathElement < 2; pathElement++) {
440 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
441 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + 1)));
442 LOG.debug("loop of check OSNR direction AZ, Path Element = {}", pathElement);
443 switch (currentNode.getORNodeType()) {
445 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
446 transponderPresent = true;
447 calcXpdrOSNR(cu, signal,
449 // First transponder on the Path (TX side) / Last Xponder of the path (RX side)
450 ? edges.get(pathElement).link().getSourceTP().getValue()
451 : edges.get(pathElement - 1).link().getDestTP().getValue(),
452 serviceType, currentNode, nextNode, vertices.get(pathElement), pathElement);
455 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", pathElement);
456 // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
457 // Xponder to Xponder) node element of the path is the ADD SRG.
458 if (edges.get(pathElement).link().getlinkType() != OpenroadmLinkType.ADDLINK) {
459 LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
460 currentNode.getNodeId(), pathElement);
462 signal.put("pwrIn", Double.valueOf(0));
463 calcAddContrib(cu, signal, currentNode, edges.get(pathElement + 1).link());
464 LOG.debug("loop of check OSNR direction AZ: SRG, pathElement = {} link {} Pout = {}",
465 pathElement, pathElement + 1, signal.get("pwrOut"));
466 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
467 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
470 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
471 // The Degree is not considered. This means we must bypass the add-link (ADD)
472 // and the next node (Degree) which are not considered in the impairments.
478 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
481 for (int pathElement = 2 + bypassDegree; pathElement < vertices.size() - 1; pathElement++) {
482 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
483 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + 1)));
484 LOG.debug("loop of check OSNR direction AZ: Path Element = {}", pathElement);
485 switch (currentNode.getORNodeType()) {
487 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", pathElement);
488 // Other case is DROP, for which cnt is unchanged (.DROP)
489 if (edges.get(pathElement - 1).link().getlinkType() != OpenroadmLinkType.DROPLINK) {
490 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
491 currentNode.getNodeId(), pathElement - 1);
493 PceLink pceLink = edges.get(pathElement - 2).link();
494 LOG.info("loop of check OSNR : SRG, pathElement = {} CD on preceeding link {} = {} ps",
495 pathElement, pathElement - 2, pceLink.getcd());
496 calcDropContrib(cu, signal, currentNode, pceLink);
497 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
498 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
501 // If SRG is not the first or the second element of the Path, it is the DROP
503 // After accumulated degradations are calculated, we also need to calculate
504 // resulting OSNR in dB to pass it to the method that verifies end Xponder
505 // performances are compatible with degradations experienced on the path
507 calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
508 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
509 LOG.info("Loop pathElement = {}, DROP, calcOnsrdB= {}", pathElement, calcOnsrdB);
510 } catch (ArithmeticException e) {
511 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
512 path.getEdgeList().size());
517 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
518 //This is the case of DROP, ROADM degree is not considered
521 LOG.info("loop of check OSNR direction AZ: DEGREE, Path Element = {}", pathElement);
522 calcBypassContrib(cu, signal, currentNode, nextNode,
523 edges.get(pathElement - 1).link(), edges.get(pathElement + 1).link());
524 double calcOnsrLin = signal.get("calcOnsrLin").doubleValue();
526 "Loop pathElement= {}, DEGREE, calcOnsrdB= {}", pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
527 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
530 // increment pathElement so that in next step we will not point to Degree2 but
533 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
534 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId(), nextNode.getNodeId(),
535 signal.get("calcCd"), signal.get("calcPmd2"), signal.get("calcPdl2"),
536 getOsnrDbfromOnsrLin(calcOnsrLin));
539 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
540 LOG.error("unsupported back to back transponder configuration");
543 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
546 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(vertices.size() - 1)));
547 LOG.debug("loop of check OSNR, Path Element = {}", vertices.size() - 1);
548 switch (currentNode.getORNodeType()) {
550 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", vertices.size() - 1);
551 transponderPresent = true;
552 // TSP is the last of the path
553 margin = getLastXpdrMargin(cu, signal, edges.get(vertices.size() - 2).link().getDestTP().getValue(),
554 serviceType, currentNode, vertices.get(vertices.size() - 1), vertices.size() - 1);
557 LOG.debug("loop of check OSNR direction AZ: SRG, Path Element = {}", vertices.size() - 1);
558 // Other case is DROP, for which cnt is unchanged (.DROP)
559 if (edges.get(vertices.size() - 2).link().getlinkType() != OpenroadmLinkType.DROPLINK) {
560 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
561 currentNode.getNodeId(), vertices.size() - 2);
563 PceLink pceLink = edges.get(vertices.size() - 3).link();
564 LOG.info("loop of check OSNR : SRG, pathElement = {} CD on preceeding link {} = {} ps",
565 vertices.size() - 1, vertices.size() - 3, pceLink.getcd());
566 calcDropContrib(cu, signal, currentNode, pceLink);
567 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
568 //commented out to avoid spotbug DLS_DEAD_LOCAL_STORE pwrIn = impairments.get("pwrIn");
569 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
572 // If SRG is not the first or the second element of the Path, it is the DROP
574 // After accumulated degradations are calculated, we also need to calculate
575 // resulting OSNR in dB to pass it to the method that verifies end Xponder
576 // performances are compatible with degradations experienced on the path
578 calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
579 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
580 LOG.info("Loop pathElement = {}, DROP, calcOnsrdB= {}", vertices.size() - 1, calcOnsrdB);
581 } catch (ArithmeticException e) {
582 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
583 path.getEdgeList().size());
589 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain last element");
591 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOnsrdB = {} "
592 + "dB and ONSR dB exterapolated from calcosnrlin = {} including non linear contributions",
593 signal.get("calcCd"), Math.sqrt(signal.get("calcPmd2").doubleValue()),
594 Math.sqrt(signal.get("calcPdl2").doubleValue()), calcOnsrdB,
595 getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue()));
596 if (!transponderPresent) {
597 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
598 + "that optical tunnel degradations are compatible with external transponder performances");
601 double delta = margin - SYS_MARGIN;
602 LOG.info("In checkOSNR: Transponder Operational mode results in a residual margin of {} dB, according "
603 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
605 String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
606 LOG.info("- In checkOSNR: A to Z Path from {} to {} {}",
607 vertices.get(0), vertices.get(vertices.size() - 1), validationMessage);
612 * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
614 * @param path the AtoZ path provided by the PCE.
615 * @param allPceNode The map of chosen/relevant PceNodes build from topology pruning.
616 * @param allPceLinks The map of PceLinks build corresponding to the whole topology.
617 * @param serviceType The service Type used to extrapolate Operational mode when it is not provided.
618 * @param cu CatalogUtils instance.
619 * @return the calculated margin according to the Transponder performances and path impairments.
621 private double checkOSNRza(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
622 Map<LinkId, PceLink> allPceLinks, String serviceType, CatalogUtils cu) {
623 Map<String, Double> signal = new HashMap<>(
625 "spacing", Double.valueOf(50.0),
626 "calcPdl2", Double.valueOf(0),
627 "calcCd", Double.valueOf(0),
628 "calcPmd2", Double.valueOf(0),
629 "calcOnsrLin", Double.valueOf(0.0001),
630 "pwrIn", Double.valueOf(-60.0),
631 "pwrOut", Double.valueOf(-60.0)));
632 double calcOnsrdB = 0;
634 boolean transponderPresent = false;
635 List<String> vertices = path.getVertexList();
636 List<PceGraphEdge> edges = path.getEdgeList();
637 // LOOP that scans the different Nodes/Links of the path and calculates
638 // associated degradations
639 // using CatalogUtils primitives to retrieve physical parameters and make a
640 // first level calculation
641 int bypassDegree = 0;
642 for (int pathElement = vertices.size() - 1; pathElement > vertices.size() - 3; pathElement--) {
644 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
645 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement - 1)));
646 LOG.debug("loop of check OSNR direction ZA: Path Element = {}", pathElement);
647 switch (currentNode.getORNodeType()) {
649 LOG.debug("loop of check OSNR direction ZA: XPDR, Path Element = {}", pathElement);
650 transponderPresent = true;
651 calcXpdrOSNR(cu, signal,
652 pathElement == vertices.size() - 1
653 // First transponder on the Path (TX side) / Last Xponder of the path (RX side)
654 ? getOppPceLink(pathElement - 1, edges, allPceLinks).getSourceTP().getValue()
655 : getOppPceLink((pathElement), edges, allPceLinks).getDestTP().getValue(),
656 serviceType, currentNode, nextNode, vertices.get(pathElement), pathElement);
659 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = {}", pathElement);
660 // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
661 // Xponder to Xponder) node element of the path is the ADD SRG.
662 if (getOppPceLink(pathElement - 1, edges, allPceLinks).getlinkType() != OpenroadmLinkType.ADDLINK) {
663 LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
664 currentNode.getNodeId(), pathElement - 1);
666 signal.put("pwrIn", Double.valueOf(0));
667 calcAddContrib(cu, signal, currentNode, getOppPceLink(pathElement - 2, edges, allPceLinks));
668 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
669 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
672 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
673 // The Degree is not considered. This means we must bypass the add-link (ADD)
674 // and the next node (Degree) which are not considered in the impairments.
680 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
683 for (int pathElement = vertices.size() - 3 - bypassDegree; pathElement > 0; pathElement--) {
684 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
685 PceNode nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement - 1)));
686 LOG.debug("loop of check OSNR direction ZA: Path Element = {}", pathElement);
687 switch (currentNode.getORNodeType()) {
689 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = {}", pathElement);
690 if (getOppPceLink(pathElement, edges, allPceLinks).getlinkType() != OpenroadmLinkType.DROPLINK) {
691 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
692 currentNode.getNodeId(), pathElement);
694 PceLink pceLink = getOppPceLink(pathElement + 1, edges, allPceLinks);
695 LOG.info("loop of check OSNR direction ZA: SRG, path Element = {} CD on preceeding link {} = {} ps",
696 pathElement, pathElement + 1, pceLink.getcd());
697 calcDropContrib(cu, signal, currentNode, pceLink);
698 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
699 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
702 // If SRG is not the first or the second element of the Path, it is the DROP
704 // After accumulated degradations are calculated, we also need to calculate
705 // resulting OSNR in dB to pass it to the method that verifies end Xponder
706 // performances are compatible with degradations experienced on the path
708 calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
709 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
710 LOG.info("Loop Path Element = {}, DROP, calcOnsrdB= {}", pathElement, calcOnsrdB);
711 } catch (ArithmeticException e) {
712 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
713 path.getEdgeList().size());
718 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
719 //This is the case of DROP, ROADM degree is not considered
722 LOG.info("loop of check OSNR direction ZA: DEGREE, Path Element = {}", pathElement);
723 calcBypassContrib(cu, signal, currentNode, nextNode,
724 getOppPceLink(pathElement, edges, allPceLinks),
725 getOppPceLink(pathElement - 2, edges, allPceLinks));
726 double calcOnsrLin = signal.get("calcOnsrLin").doubleValue();
727 LOG.debug("Loop Path Element = {}, DEGREE, calcOnsrdB= {}",
728 pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
729 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
732 // increment pathElement so that in next step we will not point to Degree2 but
735 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
736 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId(), nextNode.getNodeId(),
737 signal.get("calcCd"), signal.get("calcPmd2"), signal.get("calcPdl2"),
738 getOsnrDbfromOnsrLin(calcOnsrLin));
741 LOG.debug("loop of check OSNR direction AZ: XPDR, Path Element = {}", pathElement);
742 LOG.error("unsupported back to back transponder configuration");
745 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
748 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(0)));
749 LOG.debug("loop of check OSNR direction ZA: Path Element = 0");
750 switch (currentNode.getORNodeType()) {
752 LOG.debug("loop of check OSNR direction ZA: XPDR, Path Element = 0");
753 transponderPresent = true;
754 // TSP is the last of the path
755 margin = getLastXpdrMargin(cu, signal, getOppPceLink(0, edges, allPceLinks).getDestTP().getValue(),
756 serviceType, currentNode, vertices.get(0), 0);
759 LOG.debug("loop of check OSNR direction ZA: SRG, Path Element = 0");
760 if (getOppPceLink(0, edges, allPceLinks).getlinkType() != OpenroadmLinkType.DROPLINK) {
761 LOG.error("Error processing Node {} for which input link 0 is not a DROPLINK Type",
762 currentNode.getNodeId());
764 PceLink pceLink = getOppPceLink(1, edges, allPceLinks);
765 LOG.info("loop of check OSNR direction ZA: SRG, path Element = 0 CD on preceeding link 1 = {} ps",
767 calcDropContrib(cu, signal, currentNode, pceLink);
768 double calcOnsr = signal.get("calcOnsrLin").doubleValue();
769 //commented out to avoid spotbug DLS_DEAD_LOCAL_STORE pwrIn = impairments.get("pwrIn");
770 if (calcOnsr == Double.NEGATIVE_INFINITY || calcOnsr == Double.POSITIVE_INFINITY) {
773 // If SRG is not the first or the second element of the Path, it is the DROP
775 // After accumulated degradations are calculated, we also need to calculate
776 // resulting OSNR in dB to pass it to the method that verifies end Xponder
777 // performances are compatible with degradations experienced on the path
779 calcOnsrdB = getOsnrDbfromOnsrLin(calcOnsr);
780 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOnsrdB);
781 LOG.info("Loop Path Element = 0, DROP, calcOnsrdB= {}", calcOnsrdB);
782 } catch (ArithmeticException e) {
783 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
784 path.getEdgeList().size());
790 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain last element");
792 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOnsrdB = {} "
793 + "dB and ONSR dB exterapolated from calcosnrlin = {} including non linear contributions",
794 signal.get("calcCd"), Math.sqrt(signal.get("calcPmd2").doubleValue()),
795 Math.sqrt(signal.get("calcPdl2").doubleValue()), calcOnsrdB,
796 getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue()));
797 if (!transponderPresent) {
798 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
799 + "that optical tunnel degradations are compatible with external transponder performances");
802 double delta = margin - SYS_MARGIN;
803 LOG.info("In checkOSNR: Transponder Operational mode results in a residual margin of {} dB, according "
804 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
806 String validationMessage = delta >= 0 ? "VALIDATED" : "INVALIDATED";
807 LOG.info("- In checkOSNR: Z to A Path from {} to {} {}",
808 vertices.get(vertices.size() - 1), vertices.get(0), validationMessage);
812 private String setOpMode(String opMode, String defaultMode) {
814 opMode == null || opMode.isEmpty() || opMode.contentEquals(StringConstants.UNKNOWN_MODE)
819 private PceLink getOppPceLink(Integer pathEltNber, List<PceGraphEdge> edges,
820 Map<LinkId, PceLink> allPceLinks) {
821 return allPceLinks.get(new LinkId(edges.get(pathEltNber).link().getOppositeLink()));
824 private String getXpdrOpMode(String nwTpId, String vertice, int pathElement, PceNode currentNode,
825 String serviceType, CatalogUtils cu) {
826 InstanceIdentifier<TerminationPoint1> nwTpIid =
827 InstanceIdentifiers.createNetworkTerminationPoint1IIDBuilder(vertice, nwTpId);
828 String opMode = cu.getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP, serviceType);
830 if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, nwTpIid).get().isPresent()) {
831 // If the operational mode of the Xponder is not consistent nor declared in the topology (Network TP)
833 currentNode.getXponderOperationalMode(
834 networkTransactionService
835 .read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
836 .get().orElseThrow().getXpdrNetworkAttributes()),
837 // Operational mode is found as an attribute of the network TP
839 // Operational mode is retrieved from the service Type assuming it is supported
842 "Transponder {} corresponding to path Element {} in the path has {} operational mode",
843 currentNode.getNodeId().getValue(), pathElement, opMode);
846 } catch (InterruptedException | ExecutionException e1) {
847 LOG.error("Issue accessing the XponderNetworkAttributes of {} for Transponder {}"
848 + " corresponding to path Element {} in the path ",
849 nwTpId, currentNode.getNodeId().getValue(), pathElement);
851 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
852 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
856 private double getLastXpdrMargin(
857 CatalogUtils cu, Map<String, Double> signal,
858 String nwTpId, String serviceType, PceNode currentNode, String vertice, int pathElement) {
859 LOG.debug("Loop Path Element = {}, Step5.1, XPDR, tries calculating Margin, just before call", pathElement);
860 // Check that accumulated degradations are compatible with TSP performances
861 // According to OpenROADM spec :
862 // margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2),
863 // getOsnrDbfromOnsrLin(calcOnsrLin));
864 // Calculation modified for pdl according to calculation in Julia's Tool
865 double calcOnsrdB = getOsnrDbfromOnsrLin(signal.get("calcOnsrLin").doubleValue());
866 LOG.info("Loop Path Element = {}, XPDR, calcosnrdB= {}", pathElement, calcOnsrdB);
867 return cu.getPceRxTspParameters(
868 getXpdrOpMode(nwTpId, vertice, pathElement, currentNode, serviceType, cu),
869 signal.get("calcCd").doubleValue(),
870 Math.sqrt(signal.get("calcPmd2").doubleValue()),
871 Math.sqrt(signal.get("calcPdl2").doubleValue()),
875 private void calcXpdrOSNR(
876 CatalogUtils cu, Map<String, Double> signal, String nwTpId, String serviceType,
877 PceNode currentNode, PceNode nextNode, String vertice, int pathElement) {
878 // If the Xponder operational mode (setOpMode Arg1) is not consistent nor declared in the topology (Network TP)
879 // Operational mode is retrieved from the service Type assuming it is supported by the Xponder (setOpMode Arg2)
880 String opMode = getXpdrOpMode(nwTpId, vertice, pathElement, currentNode, serviceType, cu);
881 // If the operational mode of the ADD/DROP MUX is not consistent nor declared in the topology (Network TP)
882 // Operational mode is set by default to standard opMode for ADD SRGs
883 String adnMode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWWRCORE);
884 double calcOnsrLin = cu.getPceTxTspParameters(opMode, adnMode);
886 "Transponder {} corresponding to path Element {} is connected to SRG which has {} operational mode",
887 currentNode.getNodeId().getValue(), pathElement, adnMode);
888 LOG.info("Transponder {} corresponding to path Element {} in the path has a TX OSNR of {} dB",
889 currentNode.getNodeId().getValue(), pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
890 // Return the Tx ONSR of the Xponder which results from IB and OOB OSNR contributions
891 // and the spacing associated with Xponder operational mode that is needed to calculate OSNR
892 signal.put("spacing", Double.valueOf(cu.getPceTxTspChannelSpacing(opMode)));
893 signal.put("calcOnsrLin", Double.valueOf(calcOnsrLin));
896 private void calcDropContrib(
897 CatalogUtils cu, Map<String, Double> signal, PceNode currentNode, PceLink pceLink) {
898 //calculation of the SRG contribution for Drop
899 calcLineDegradation(cu, signal, pceLink);
900 Map<String, Double> impairments = cu.getPceRoadmAmpParameters(
901 CatalogConstant.CatalogNodeType.DROP,
902 setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE),
903 // If the operational mode of the ADD/DROP MUX is not consistent or not declared in the topology (Network TP)
904 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
905 signal.get("pwrIn").doubleValue(),
906 signal.get("calcCd").doubleValue(),
907 signal.get("calcPmd2").doubleValue(),
908 signal.get("calcPdl2").doubleValue(),
909 signal.get("calcOnsrLin").doubleValue(),
910 signal.get("spacing").doubleValue());
913 "calcCd", impairments.get("CD"),
914 "calcPmd2", impairments.get("DGD2"),
915 "calcPdl2", impairments.get("PDL2"),
916 "calcOnsrLin", impairments.get("ONSRLIN")));
919 private void calcAddContrib(
920 CatalogUtils cu, Map<String, Double> signal, PceNode currentNode, PceLink pceLink) {
921 //calculation of the SRG contribution for Add
922 String srgMode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWWRCORE);
923 // If the operational mode of the ADD/DROP MUX is not consistent or is not declared in the topology (Network TP)
924 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
925 CatalogNodeType cnt = CatalogConstant.CatalogNodeType.ADD;
926 double pwrOut = cu.getPceRoadmAmpOutputPower(
927 cnt, srgMode, pceLink.getspanLoss(), signal.get("spacing").doubleValue(), pceLink.getpowerCorrection());
928 //calculation of the SRG contribution either for Add and Drop
929 Map<String, Double> impairments = cu.getPceRoadmAmpParameters(cnt, srgMode, 0,
930 signal.get("calcCd").doubleValue(), signal.get("calcPmd2").doubleValue(),
931 signal.get("calcPdl2").doubleValue(),
932 signal.get("calcOnsrLin").doubleValue(), signal.get("spacing").doubleValue());
935 "calcCd", impairments.get("CD"),
936 "calcPmd2", impairments.get("DGD2"),
937 "calcPdl2", impairments.get("PDL2"),
938 "calcOnsrLin", impairments.get("ONSRLIN"),
939 "pwrOut", Double.valueOf(pwrOut)));
942 private void calcBypassContrib(CatalogUtils cu, Map<String, Double> signal,
943 PceNode currentNode, PceNode nextNode, PceLink pceLink0, PceLink pceLink1) {
944 // If the operational mode of the Degree is not consistent or declared in the topology
945 // Operational mode is set by default to standard opMode for Degree
946 String degree1Mode = setOpMode(currentNode.getOperationalMode(), CatalogConstant.MWMWCORE);
947 // Same for next node which is the second degree of a ROADM node
948 String degree2Mode = setOpMode(nextNode.getOperationalMode(), CatalogConstant.MWMWCORE);
949 // At that time OpenROADM provides only one spec for the ROADM nodes
950 if (!degree1Mode.equals(degree2Mode)) {
951 LOG.warn("Unsupported Hybrid ROADM configuration with Degree1 {} of {} operational mode and Degree2 "
952 + "{} of {} operational mode. Will by default use operational mode of Degree2",
953 currentNode.getNodeId(), degree1Mode, nextNode.getNodeId(), degree2Mode);
955 calcLineDegradation(cu, signal, pceLink0);
956 CatalogNodeType cnt = CatalogConstant.CatalogNodeType.EXPRESS;
957 double pwrOut = cu.getPceRoadmAmpOutputPower(cnt, degree2Mode, pceLink1.getspanLoss(),
958 signal.get("spacing").doubleValue(), pceLink1.getpowerCorrection());
959 // Adds to accumulated impairments the degradation associated with the Express
960 // path of ROADM : Degree1, express link, Degree2
961 Map<String, Double> impairments = cu.getPceRoadmAmpParameters(cnt, degree2Mode,
962 signal.get("pwrIn").doubleValue(), signal.get("calcCd").doubleValue(),
963 signal.get("calcPmd2").doubleValue(), signal.get("calcPdl2").doubleValue(),
964 signal.get("calcOnsrLin").doubleValue(), signal.get("spacing").doubleValue());
967 "calcCd", impairments.get("CD"),
968 "calcPmd2", impairments.get("DGD2"),
969 "calcPdl2", impairments.get("PDL2"),
970 "calcOnsrLin", impairments.get("ONSRLIN"),
971 "pwrOut", Double.valueOf(pwrOut)));
973 //TODO these methods might be more indicated in a catalog utils refactoring
975 private void calcLineDegradation(CatalogUtils cu, Map<String, Double> signal, PceLink pceLink) {
976 // Calculate degradation accumulated across incoming Link and add them to
977 // accumulated impairments
978 // This also includes Non Linear Contribution from the path
979 signal.putAll(Map.of(
980 "pwrIn", Double.valueOf(signal.get("pwrOut").doubleValue() - pceLink.getspanLoss()),
981 "calcCd", Double.valueOf(signal.get("calcCd").doubleValue() + pceLink.getcd()),
982 "calcPmd2", Double.valueOf(signal.get("calcPmd2").doubleValue() + pceLink.getpmd2()),
983 "calcOnsrLin", Double.valueOf(
984 signal.get("calcOnsrLin").doubleValue()
985 + cu.calculateNLonsrContribution(
986 signal.get("pwrOut").doubleValue(), pceLink.getLength(), signal.get("spacing").doubleValue()))));
989 private double getOsnrDbfromOnsrLin(double osnrLu) {
990 return 10 * Math.log10(1 / osnrLu);
994 * Get spectrum assignment for path.
996 * @param path the path for which we get spectrum assignment.
997 * @param allPceNodes all optical nodes.
998 * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
1000 * @return a spectrum assignment object which contains begin and end index. If
1001 * no spectrum assignment found, beginIndex = stopIndex = 0
1003 private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
1004 Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
1005 byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
1006 Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
1007 BitSet result = BitSet.valueOf(freqMap);
1008 boolean isFlexGrid = true;
1009 LOG.debug("Processing path {} with length {}", path, path.getLength());
1010 BitSet pceNodeFreqMap;
1011 Set<PceNode> pceNodes = new LinkedHashSet<>();
1013 for (PceGraphEdge edge : path.getEdgeList()) {
1014 NodeId srcId = edge.link().getSourceId();
1015 NodeId dstId = edge.link().getDestId();
1016 LOG.debug("Processing {} to {}", srcId.getValue(), dstId.getValue());
1017 if (allPceNodes.containsKey(srcId)) {
1018 pceNodes.add(allPceNodes.get(srcId));
1020 if (allPceNodes.containsKey(dstId)) {
1021 pceNodes.add(allPceNodes.get(dstId));
1025 for (PceNode pceNode : pceNodes) {
1026 LOG.debug("Processing PCE node {}", pceNode);
1027 pceNodeFreqMap = pceNode.getBitSetData();
1028 LOG.debug("Pce node bitset {}", pceNodeFreqMap);
1029 if (pceNodeFreqMap != null) {
1030 result.and(pceNodeFreqMap);
1031 LOG.debug("intermediate bitset {}", result);
1033 String pceNodeVersion = pceNode.getVersion();
1034 BigDecimal sltWdthGran = pceNode.getSlotWidthGranularity();
1035 if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNodeVersion)) {
1036 LOG.debug("Node {}: version is {} with slot width granularity {} - fixed grid mode",
1037 pceNode.getNodeId(), pceNodeVersion, sltWdthGran);
1041 if (!sltWdthGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
1044 BigDecimal ctralFreqGran = pceNode.getCentralFreqGranularity();
1045 if (!ctralFreqGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
1049 "Node {}: version is {} with slot width and central frequency granularities {} {} - fixed grid mode",
1050 pceNode.getNodeId(), pceNodeVersion, sltWdthGran, ctralFreqGran);
1053 if (spectrumConstraint != null) {
1054 result.and(spectrumConstraint);
1057 LOG.debug("Bitset result {}", result);
1058 return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
1062 * Compute spectrum assignment from spectrum occupation for spectral width.
1064 * @param spectrumOccupation the spectrum occupation BitSet.
1065 * @param spectralWidthSlotNumber the nb slots for spectral width.
1066 * @param isFlexGrid true if flexible grid, false otherwise.
1067 * @return a spectrum assignment object which contains begin and stop index. If
1068 * no spectrum assignment found, beginIndex = stopIndex = 0
1070 private SpectrumAssignment computeBestSpectrumAssignment(
1071 BitSet spectrumOccupation, int spectralWidthSlotNumber, boolean isFlexGrid) {
1072 SpectrumAssignmentBuilder spectrumAssignmentBldr = new SpectrumAssignmentBuilder()
1073 .setBeginIndex(Uint16.valueOf(0))
1074 .setStopIndex(Uint16.valueOf(0))
1075 .setFlexGrid(isFlexGrid);
1076 BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
1077 referenceBitSet.set(0, spectralWidthSlotNumber);
1078 //higher is the frequency, smallest is the wavelength number
1079 //in operational, the allocation is done through wavelength starting from the smallest
1080 //so we have to loop from the last element of the spectrum occupation
1081 for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber;
1082 i -= isFlexGrid ? spectralWidthSlotNumber : 1) {
1083 if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
1084 spectrumAssignmentBldr.setBeginIndex(Uint16.valueOf(i - spectralWidthSlotNumber));
1085 spectrumAssignmentBldr.setStopIndex(Uint16.valueOf(i - 1));
1089 return spectrumAssignmentBldr.build();
1092 public Double getTpceCalculatedMargin() {
1093 return tpceCalculatedMargin;