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.topology.rev211210.networks.network.node.termination.point.XpdrNetworkAttributes;
43 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev211210.OpenroadmLinkType;
44 import org.opendaylight.yang.gen.v1.http.org.openroadm.network.types.rev211210.OpenroadmNodeType;
45 import org.opendaylight.yang.gen.v1.http.org.openroadm.otn.common.types.rev210924.OpucnTribSlotDef;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.rev180226.NodeId;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.network.topology.rev180226.LinkId;
48 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.common.Uint16;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
53 public class PostAlgoPathValidator {
55 private static final Logger LOG = LoggerFactory.getLogger(PostAlgoPathValidator.class);
57 public static final Long CONST_OSNR = 1L;
58 public static final double SYS_MARGIN = 0;
59 private Double tpceCalculatedMargin = 0.0;
60 private final NetworkTransactionService networkTransactionService;
62 public PostAlgoPathValidator(NetworkTransactionService networkTransactionService) {
63 this.networkTransactionService = networkTransactionService;
66 @SuppressWarnings("fallthrough")
68 value = "SF_SWITCH_FALLTHROUGH",
69 justification = "intentional fallthrough")
71 public PceResult checkPath(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
72 Map<LinkId, PceLink> allPceLinks, PceResult pceResult, PceConstraints pceHardConstraints,
74 LOG.info("path = {}", path);
75 // check if the path is empty
76 if (path.getEdgeList().isEmpty()) {
77 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
80 int spectralWidthSlotNumber =
81 GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_100G);
82 SpectrumAssignment spectrumAssignment = null;
83 //variable to deal with 1GE (Nb=1) and 10GE (Nb=10) cases
84 switch (serviceType) {
85 case StringConstants.SERVICE_TYPE_OTUC2:
86 case StringConstants.SERVICE_TYPE_OTUC3:
87 case StringConstants.SERVICE_TYPE_OTUC4:
88 case StringConstants.SERVICE_TYPE_400GE:
89 spectralWidthSlotNumber =
90 GridConstant.SPECTRAL_WIDTH_SLOT_NUMBER_MAP.getOrDefault(serviceType, GridConstant.NB_SLOTS_400G);
92 case StringConstants.SERVICE_TYPE_100GE_T:
93 case StringConstants.SERVICE_TYPE_OTU4:
94 spectrumAssignment = getSpectrumAssignment(path, allPceNodes, spectralWidthSlotNumber);
95 pceResult.setServiceType(serviceType);
96 if (spectrumAssignment.getBeginIndex().equals(Uint16.valueOf(0))
97 && spectrumAssignment.getStopIndex().equals(Uint16.valueOf(0))) {
98 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
99 pceResult.setLocalCause(PceResult.LocalCause.NO_PATH_EXISTS);
102 if (spectrumAssignment.getFlexGrid()) {
103 LOG.debug("Spectrum assignment flexgrid mode");
104 pceResult.setResultWavelength(GridConstant.IRRELEVANT_WAVELENGTH_NUMBER);
106 LOG.debug("Spectrum assignment fixedgrid mode");
107 pceResult.setResultWavelength(
108 GridUtils.getWaveLengthIndexFromSpectrumAssigment(spectrumAssignment.getBeginIndex().toJava()));
110 pceResult.setMinFreq(GridUtils.getStartFrequencyFromIndex(spectrumAssignment.getBeginIndex().toJava()));
111 pceResult.setMaxFreq(GridUtils.getStopFrequencyFromIndex(spectrumAssignment.getStopIndex().toJava()));
112 LOG.debug("In PostAlgoPathValidator: spectrum assignment found {} {}", spectrumAssignment, path);
115 CatalogUtils cu = new CatalogUtils(networkTransactionService);
116 if (cu.isCatalogFilled()) {
117 double margin1 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
118 StringConstants.SERVICE_DIRECTION_AZ, cu);
119 double margin2 = checkOSNR(path, allPceNodes, allPceLinks, serviceType,
120 StringConstants.SERVICE_DIRECTION_ZA, cu);
121 if (margin1 < 0 || margin2 < 0 || margin1 == Double.NEGATIVE_INFINITY
122 || margin2 == Double.NEGATIVE_INFINITY) {
123 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
124 pceResult.setLocalCause(PceResult.LocalCause.OUT_OF_SPEC_OSNR);
127 this.tpceCalculatedMargin = Math.min(margin1, margin2);
129 "In PostAlgoPathValidator: Minimum margin estimated by tpce on AtoZ and ZtoA path is of {} dB",
130 this.tpceCalculatedMargin);
132 this.tpceCalculatedMargin = 0.0;
133 LOG.info("In PostAlgoPathValidator: Operational mode Catalog not filled, delegate OSNR calculation"
134 + " to GNPy and margin set to 0");
136 // Check if MaxLatency is defined in the hard constraints
137 if (pceHardConstraints.getMaxLatency() != -1
138 && !checkLatency(pceHardConstraints.getMaxLatency(), path)) {
139 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
140 pceResult.setLocalCause(PceResult.LocalCause.TOO_HIGH_LATENCY);
143 // Check if nodes are included in the hard constraints
144 if (!checkInclude(path, pceHardConstraints)) {
145 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
146 pceResult.setLocalCause(PceResult.LocalCause.HD_NODE_INCLUDE);
149 // TODO here other post algo validations can be added
150 // more data can be sent to PceGraph module via PceResult structure if required
151 pceResult.setRC(ResponseCodes.RESPONSE_OK);
152 pceResult.setLocalCause(PceResult.LocalCause.NONE);
154 case StringConstants.SERVICE_TYPE_100GE_M:
155 case StringConstants.SERVICE_TYPE_10GE:
156 case StringConstants.SERVICE_TYPE_1GE:
157 int tribSlotNb = Map.of(
158 StringConstants.SERVICE_TYPE_100GE_M, 20,
159 StringConstants.SERVICE_TYPE_10GE, 8,
160 StringConstants.SERVICE_TYPE_1GE, 1)
162 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
163 pceResult.setServiceType(serviceType);
164 Map<String, List<Uint16>> tribSlot = chooseTribSlot(path, allPceNodes, tribSlotNb);
165 Map<String, Uint16> tribPort = chooseTribPort(path, allPceNodes, tribSlot, tribSlotNb);
166 List<OpucnTribSlotDef> resultTribPortTribSlot = getMinMaxTpTs(tribPort, tribSlot);
167 if (resultTribPortTribSlot.get(0) != null && resultTribPortTribSlot.get(1) != null) {
168 pceResult.setResultTribPortTribSlot(resultTribPortTribSlot);
169 pceResult.setRC(ResponseCodes.RESPONSE_OK);
170 LOG.info("In PostAlgoPathValidator: found TribPort {} - tribSlot {} - tribSlotNb {}",
171 tribPort, tribSlot, tribSlotNb);
174 case StringConstants.SERVICE_TYPE_ODU4:
175 case StringConstants.SERVICE_TYPE_ODUC2:
176 case StringConstants.SERVICE_TYPE_ODUC3:
177 case StringConstants.SERVICE_TYPE_ODUC4:
178 case StringConstants.SERVICE_TYPE_100GE_S:
179 pceResult.setRC(ResponseCodes.RESPONSE_OK);
180 pceResult.setServiceType(serviceType);
181 LOG.info("In PostAlgoPathValidator: ODU4/ODUCn path found {}", path);
184 pceResult.setRC(ResponseCodes.RESPONSE_FAILED);
185 LOG.warn("In PostAlgoPathValidator checkPath: unsupported serviceType {} found {}",
192 private boolean checkLatency(Long maxLatency, GraphPath<String, PceGraphEdge> path) {
194 for (PceGraphEdge edge : path.getEdgeList()) {
195 if (edge.link() == null || edge.link().getLatency() == null) {
196 LOG.warn("- In checkLatency: the link {} does not contain latency field",
197 edge.link().getLinkId().getValue());
200 latency += edge.link().getLatency();
201 LOG.debug("- In checkLatency: latency of {} = {} units", edge.link().getLinkId().getValue(), latency);
203 return (latency < maxLatency);
206 // Check the inclusion if it is defined in the hard constraints
207 private boolean checkInclude(GraphPath<String, PceGraphEdge> path, PceConstraints pceHardConstraintsInput) {
208 List<ResourcePair> listToInclude = pceHardConstraintsInput.getListToInclude()
209 .stream().sorted((rp1, rp2) -> rp1.getName().compareTo(rp2.getName()))
210 .collect(Collectors.toList());
211 if (listToInclude.isEmpty()) {
214 List<PceGraphEdge> pathEdges = path.getEdgeList();
215 LOG.debug(" in checkInclude vertex list: [{}]", path.getVertexList());
216 List<String> listOfElementsSubNode = new ArrayList<>();
217 listOfElementsSubNode.add(pathEdges.get(0).link().getsourceNetworkSupNodeId());
218 listOfElementsSubNode.addAll(
219 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.NODE, pceHardConstraintsInput));
220 List<String> listOfElementsCLLI = new ArrayList<>();
221 listOfElementsCLLI.add(pathEdges.get(0).link().getsourceCLLI());
222 listOfElementsCLLI.addAll(
223 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.CLLI, pceHardConstraintsInput));
224 List<String> listOfElementsSRLG = new ArrayList<>();
225 // first link is XPONDEROUTPUT, no SRLG for it
226 listOfElementsSRLG.add("NONE");
227 listOfElementsSRLG.addAll(
228 listOfElementsBuild(pathEdges, PceConstraints.ResourceType.SRLG, pceHardConstraintsInput));
229 // validation: check each type for each element
230 return listOfElementsSubNode.containsAll(
232 .stream().filter(rp -> PceConstraints.ResourceType.NODE.equals(rp.getType()))
233 .map(ResourcePair::getName).collect(Collectors.toList()))
234 && listOfElementsSRLG.containsAll(
236 .stream().filter(rp -> PceConstraints.ResourceType.SRLG.equals(rp.getType()))
237 .map(ResourcePair::getName).collect(Collectors.toList()))
238 && listOfElementsCLLI.containsAll(
240 .stream().filter(rp -> PceConstraints.ResourceType.CLLI.equals(rp.getType()))
241 .map(ResourcePair::getName).collect(Collectors.toList()));
244 private List<String> listOfElementsBuild(List<PceGraphEdge> pathEdges, PceConstraints.ResourceType type,
245 PceConstraints pceHardConstraints) {
246 List<String> listOfElements = new ArrayList<>();
247 for (PceGraphEdge link : pathEdges) {
250 listOfElements.add(link.link().getdestNetworkSupNodeId());
253 listOfElements.add(link.link().getdestCLLI());
256 if (link.link().getlinkType() != OpenroadmLinkType.ROADMTOROADM) {
257 listOfElements.add("NONE");
260 // srlg of link is List<Long>. But in this algo we need string representation of
262 // this should be any SRLG mentioned in include constraints if any of them if
264 boolean found = false;
265 for (Long srlg : link.link().getsrlgList()) {
266 String srlgStr = String.valueOf(srlg);
267 if (pceHardConstraints.getSRLGnames().contains(srlgStr)) {
268 listOfElements.add(srlgStr);
269 LOG.info("listOfElementsBuild. FOUND SRLG {} in link {}", srlgStr, link.link());
274 // there is no specific srlg to include. thus add to list just the first one
275 listOfElements.add("NONE");
279 LOG.debug("listOfElementsBuild unsupported resource type");
282 return listOfElements;
285 private Map<String, Uint16> chooseTribPort(GraphPath<String,
286 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, Map<String, List<Uint16>> tribSlotMap, int nbSlot) {
287 LOG.debug("In choosetribPort: edgeList = {} ", path.getEdgeList());
288 Map<String, Uint16> tribPortMap = new HashMap<>();
289 for (PceGraphEdge edge : path.getEdgeList()) {
290 List<Uint16> srcTpnPool =
292 .get(edge.link().getSourceId())
293 .getAvailableTribPorts()
294 .get(edge.link().getSourceTP().getValue());
295 List<Uint16> destTpnPool =
297 .get(edge.link().getDestId())
298 .getAvailableTribPorts()
299 .get(edge.link().getDestTP().getValue());
300 List<Uint16> commonEdgeTpnPool = new ArrayList<>();
301 for (Uint16 srcTpn : srcTpnPool) {
302 if (destTpnPool.contains(srcTpn)) {
303 commonEdgeTpnPool.add(srcTpn);
306 if (commonEdgeTpnPool.isEmpty()) {
309 Integer startTribSlot = tribSlotMap.values().stream().findFirst().get().get(0).toJava();
310 Integer tribPort = (int) Math.ceil((double)startTribSlot / nbSlot);
311 for (Uint16 commonTribPort : commonEdgeTpnPool) {
312 if (tribPort.equals(commonTribPort.toJava())) {
313 tribPortMap.put(edge.link().getLinkId().getValue(), commonTribPort);
317 tribPortMap.forEach((k,v) -> LOG.info("TribPortMap : k = {}, v = {}", k, v));
321 private Map<String, List<Uint16>> chooseTribSlot(GraphPath<String,
322 PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes, int nbSlot) {
323 LOG.debug("In choosetribSlot: edgeList = {} ", path.getEdgeList());
324 Map<String, List<Uint16>> tribSlotMap = new HashMap<>();
325 for (PceGraphEdge edge : path.getEdgeList()) {
326 List<Uint16> srcTsPool =
328 .get(edge.link().getSourceId())
329 .getAvailableTribSlots()
330 .get(edge.link().getSourceTP().getValue());
331 List<Uint16> destTsPool =
333 .get(edge.link().getDestId())
334 .getAvailableTribSlots()
335 .get(edge.link().getDestTP().getValue());
336 List<Uint16> commonEdgeTsPoolList = new ArrayList<>();
337 List<Uint16> tribSlotList = new ArrayList<>();
338 for (Uint16 integer : srcTsPool) {
339 if (destTsPool.contains(integer)) {
340 commonEdgeTsPoolList.add(integer);
343 Collections.sort(commonEdgeTsPoolList);
344 List<Uint16> commonGoodStartEdgeTsPoolList = new ArrayList<>();
345 for (Uint16 startEdgeTsPool : commonEdgeTsPoolList) {
346 if (Integer.valueOf(1).equals(startEdgeTsPool.toJava() % nbSlot) || nbSlot == 1) {
347 commonGoodStartEdgeTsPoolList.add(startEdgeTsPool);
350 Collections.sort(commonGoodStartEdgeTsPoolList);
351 boolean goodTsList = false;
352 for (Uint16 goodStartTsPool : commonGoodStartEdgeTsPoolList) {
353 int goodStartIndex = commonEdgeTsPoolList.indexOf(Uint16.valueOf(goodStartTsPool.intValue()));
354 if (!goodTsList && commonEdgeTsPoolList.size() - goodStartIndex >= nbSlot) {
355 for (int i = 0; i < nbSlot; i++) {
356 if (!commonEdgeTsPoolList.get(goodStartIndex + i)
357 .equals(Uint16.valueOf(goodStartTsPool.toJava() + i))) {
359 tribSlotList.clear();
362 tribSlotList.add(commonEdgeTsPoolList.get(goodStartIndex + i));
367 tribSlotMap.put(edge.link().getLinkId().getValue(), tribSlotList);
369 tribSlotMap.forEach((k,v) -> LOG.info("TribSlotMap : k = {}, v = {}", k, v));
373 private List<OpucnTribSlotDef> getMinMaxTpTs(Map<String, Uint16> tribPort, Map<String, List<Uint16>> tribSlot) {
374 String tribport = tribPort.values().toArray()[0].toString();
375 List<Uint16> tsList = (List<Uint16>) tribSlot.values().toArray()[0];
376 return new ArrayList<>(List.of(
377 OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(0).toString())),
378 OpucnTribSlotDef.getDefaultInstance(String.join(".", tribport, tsList.get(tsList.size() - 1).toString()))));
382 * Calculates the OSNR of a path, according to the direction (AtoZ/ZtoA), using the operational-modes Catalog.
384 * @param path the AtoZ path provided by the PCE.
385 * @param allPceNode The map of chosen/relevant PceNodes build from topology pruning.
386 * @param allPceLinks The map of PceLinks build corresponding to the whole topology.
387 * @param serviceType The service Type used to extrapolate Operational mode when it is not provided.
388 * @param direction The direction used to scan provided path in a direct or reverse way.
389 * @param cu CatalogUtils instance.
390 * @return the calculated margin according to the Transponder performances and path impairments.
392 @SuppressWarnings("deprecation")
393 @edu.umd.cs.findbugs.annotations.SuppressWarnings("DLS_DEAD_LOCAL_STORE")
394 private double checkOSNR(GraphPath<String, PceGraphEdge> path, Map<NodeId, PceNode> allPceNodes,
395 Map<LinkId, PceLink> allPceLinks, String serviceType, String direction, CatalogUtils cu) {
396 double spacing = 50.0;
398 double calcOsnrdB = 0;
401 double calcOnsrLin = 0.0001;
403 double pwrIn = -60.0;
404 double pwrOut = -60.0;
408 boolean transponderPresent = false;
409 if ((StringConstants.SERVICE_DIRECTION_ZA).equals(direction)) {
414 List<String> vertices = path.getVertexList();
415 List<PceGraphEdge> edges = path.getEdgeList();
417 // LOOP that scans the different Nodes/Links of the path and calculates
418 // associated degradations
419 // using CatalogUtils primitives to retrieve physical parameters and make a
420 // first level calculation
421 Map<String, Double> impairments = new HashMap<>();
422 for (int n = 0; n < vertices.size(); n++) {
423 InstanceIdentifier<TerminationPoint1> nwTpIid;
424 PceNode nextNode = null;
425 if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
428 pathElement = vertices.size() - n - 1;
430 PceNode currentNode = allPceNodes.get(new NodeId(vertices.get(pathElement)));
431 if (((pathElement != vertices.size() - 1) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
432 || ((pathElement != 0) && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction))) {
433 nextNode = allPceNodes.get(new NodeId(vertices.get(pathElement + increment)));
435 LOG.debug("loop of check OSNR, n = {} Path Element = {}", n, pathElement);
436 switch (currentNode.getORNodeType()) {
438 transponderPresent = true;
440 if (((pathElement == 0) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
441 || ((pathElement == (vertices.size() - 1)) && (StringConstants.SERVICE_DIRECTION_ZA)
442 .equals(direction))) {
443 //First Xponder of the path TX side
444 nwTpId = getAppropriatePceLink((pathElement + offsetLink), edges, allPceLinks, direction)
445 .getSourceTP().getValue();
447 // last Xponder of the path (RX side)
448 nwTpId = getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
449 .getDestTP().getValue();
451 nwTpIid = InstanceIdentifiers.createNetworkTerminationPoint1IIDBuilder(
452 vertices.get(pathElement), nwTpId);
453 LOG.debug("loop of check OSNR : XPDR, n = {} Path Element = {}", n, pathElement);
455 if (networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
456 .get().isPresent()) {
457 XpdrNetworkAttributes xna = networkTransactionService
458 .read(LogicalDatastoreType.CONFIGURATION, nwTpIid)
459 .get().get().getXpdrNetworkAttributes();
460 // If the operational mode of the Xponder is not consistent or
461 // if the operational mode of the Xponder is not declared in the topology
463 if (currentNode.getXponderOperationalMode(xna).contentEquals(StringConstants.UNKNOWN_MODE)
464 || currentNode.getXponderOperationalMode(xna) == null
465 || currentNode.getXponderOperationalMode(xna).isEmpty()) {
466 // Operational mode is retrieved from the service Type assuming it is supported
468 opMode = cu.getPceOperationalModeFromServiceType(
469 CatalogConstant.CatalogNodeType.TSP, serviceType);
471 // Operational mode is found as an attribute of the network TP
472 opMode = currentNode.getXponderOperationalMode(xna);
474 LOG.debug("Transponder {} corresponding to path Element {} in the path has {} operational "
475 + "mode", currentNode.getNodeId().getValue(), pathElement, opMode);
477 LOG.error("Issue accessing the XponderNetworkAttributes of {} for Transponder {}"
478 + " corresponding to path Element {} in the path ",
479 nwTpId, currentNode.getNodeId().getValue(), pathElement);
480 opMode = cu.getPceOperationalModeFromServiceType(
481 CatalogConstant.CatalogNodeType.TSP, serviceType);
482 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
483 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
485 } catch (InterruptedException | ExecutionException e1) {
486 opMode = cu.getPceOperationalModeFromServiceType(CatalogConstant.CatalogNodeType.TSP,
488 LOG.info("Did not succeed finding network TP {} in Configuration Datastore. Retrieve"
489 + " default Operational Mode {} from serviceType {}", nwTpId, opMode, serviceType);
491 // If TSP is the last of the path
492 if (((pathElement == (vertices.size() - 1))
493 && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction))
494 || ((pathElement == 0) && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction))) {
495 LOG.debug("Loop n = {}, Step5.1, XPDR, tries calculating Margin, just before call", n);
496 // Check that accumulated degradations are compatible with TSP performances
497 // According to OpenROADM spec :
498 // margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2),
499 // getOsnrDbfromOnsrLin(calcOnsrLin));
500 // Calculation modified for pdl according to calculation in Julia's Tool
501 margin = cu.getPceRxTspParameters(opMode, calcCd, Math.sqrt(calcPmd2),
502 (Math.sqrt(calcPdl2)), getOsnrDbfromOnsrLin(calcOnsrLin));
503 LOG.info("Loop n = {}, XPDR, calcosnrdB= {}", n, getOsnrDbfromOnsrLin(calcOnsrLin));
505 // TSP is first element of the path . To correctly evaluate the TX OOB OSNR from
506 // its operational mode, we need to know the type of ADD/DROP Mux it is
509 // If the operational mode of the ADD/DROP MUX is not consistent or
510 // if the operational mode of the ADD/DROP MUX is not declared in the topology
512 if (StringConstants.UNKNOWN_MODE.equals(nextNode.getOperationalMode())
513 || nextNode.getOperationalMode() == null
514 || nextNode.getOperationalMode().isEmpty()) {
515 // Operational mode is set by default to standard opMode for ADD SRGs
516 adnMode = CatalogConstant.MWWRCORE;
518 // Operational mode is found in SRG attributes of the Node
519 adnMode = nextNode.getOperationalMode();
521 LOG.debug("Transponder {} corresponding to path Element {} in the path is connected to SRG "
522 + "which has {} operational mode", currentNode.getNodeId().getValue(), pathElement,
524 // Retrieve the Tx ONSR of the Xponder which results from IB and OOB OSNR
526 calcOnsrLin = cu.getPceTxTspParameters(opMode, adnMode);
527 // Retrieve the spacing associated with Xponder operational mode that is needed
529 spacing = cu.getPceTxTspChannelSpacing(opMode);
530 LOG.info("Transponder {} corresponding to path Element {} in the path has a TX OSNR of {} dB",
531 currentNode.getNodeId().getValue(), pathElement, getOsnrDbfromOnsrLin(calcOnsrLin));
536 // If the operational mode of the ADD/DROP MUX is not consistent or
537 // if the operational mode of the ADD/DROP MUX is not declared in the topology
539 if (StringConstants.UNKNOWN_MODE.equals(currentNode.getOperationalMode())
540 || currentNode.getOperationalMode() == null
541 || currentNode.getOperationalMode().isEmpty()) {
542 // Operational mode is set by default to standard opMode for ADD/DROP SRGs
543 srgMode = CatalogConstant.MWWRCORE;
545 // Operational mode is found in SRG attributes of the Node
546 srgMode = currentNode.getOperationalMode();
548 cnt = CatalogConstant.CatalogNodeType.DROP;
549 LOG.debug("loop of check OSNR : SRG, n = {} Path Element = {}", n, pathElement);
550 if ((pathElement <= 1) && (StringConstants.SERVICE_DIRECTION_AZ).equals(direction)
551 || (pathElement >= vertices.size() - 2)
552 && (StringConstants.SERVICE_DIRECTION_ZA).equals(direction)) {
553 // This is ADD case : First (optical-tunnel) or 2nd (Regular E2E service from
554 // Xponder to Xponder) node element of the path is the ADD SRG.
555 if (!(getAppropriatePceLink((pathElement + offsetLink), edges, allPceLinks, direction)
556 .getlinkType() == OpenroadmLinkType.ADDLINK)) {
557 LOG.error("Error processing Node {} for which output link {} is not an ADDLINK Type",
558 currentNode.getNodeId().toString(), pathElement + offsetLink);
560 cnt = CatalogConstant.CatalogNodeType.ADD;
562 pwrOut = cu.getPceRoadmAmpOutputPower(cnt, srgMode,
563 getAppropriatePceLink((pathElement + 1 + offsetLink * 3), edges, allPceLinks, direction)
564 .getspanLoss(), spacing,
565 getAppropriatePceLink((pathElement + 1 + offsetLink * 3), edges, allPceLinks, direction)
566 .getpowerCorrection());
567 LOG.debug("loop of check OSNR : SRG, n = {} link {} Pout = {}",
568 pathElement, pathElement + 1 + offsetLink * 3, pwrOut);
570 // Other case is DROP, for which cnt is unchanged (.DROP)
571 if (!(getAppropriatePceLink((pathElement - 1 - offsetLink), edges, allPceLinks, direction)
572 .getlinkType() == OpenroadmLinkType.DROPLINK)) {
573 LOG.error("Error processing Node {} for which input link {} is not a DROPLINK Type",
574 currentNode.getNodeId().toString(), pathElement - 1 - offsetLink);
576 pwrIn = pwrOut - getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
577 direction).getspanLoss();
578 // Calculate degradation accumulated across incoming Link and add them to
579 // accumulated impairments
580 calcCd += getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
582 LOG.info("loop of check OSNR : SRG, n = {} CD on preceeding link {} = {} ps", pathElement,
583 pathElement - offsetLink * 3 - 2, getAppropriatePceLink((pathElement - offsetLink * 3 - 2),
584 edges, allPceLinks, direction).getcd());
585 calcPmd2 += getAppropriatePceLink((pathElement - offsetLink * 3 - 2), edges, allPceLinks,
586 direction).getpmd2();
587 // This also includes Non Linear Contribution from the path
588 calcOnsrLin += cu.calculateNLonsrContribution(pwrOut, getAppropriatePceLink((pathElement
589 - offsetLink * 3 - 2), edges, allPceLinks, direction).getLength(), spacing);
591 //calculation of the SRG contribution either for Add and Drop
592 impairments = cu.getPceRoadmAmpParameters(cnt, srgMode,
593 pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
594 calcCd = impairments.get("CD").doubleValue();
595 calcPmd2 = impairments.get("DGD2").doubleValue();
596 calcPdl2 = impairments.get("PDL2").doubleValue();
597 calcOnsrLin = impairments.get("ONSRLIN").doubleValue();
598 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
601 if (pathElement > 1) {
602 // If SRG is not the first or the second element of the Path, it is the DROP
604 // After accumulated degradations are calculated, we also need to calculate
605 // resulting OSNR in dB to pass it to the method that verifies end Xponder
606 // performances are compatible with degradations experienced on the path
608 calcOsnrdB = getOsnrDbfromOnsrLin(calcOnsrLin);
609 LOG.info("checkOSNR loop, last SRG osnr is {} dB", calcOsnrdB);
610 LOG.info("Loop n = {}, DROP, calcOsnrdB= {}", n, calcOsnrdB);
611 } catch (ArithmeticException e) {
612 LOG.debug("In checkOSNR: OSNR is equal to 0 and the number of links is: {}",
613 path.getEdgeList().size());
617 if (CatalogConstant.CatalogNodeType.ADD.equals(cnt)) {
618 // For the ADD, degradation brought by the node are calculated from the MW-WR spec.
619 // The Degree is not considered. This means we must bypass the add-link (ADD)
620 // and the next node (Degree) which are not considered in the impairments.
626 if (nextNode.getORNodeType() != OpenroadmNodeType.DEGREE) {
627 //This is the case of DROP, ROADM degree is not considered
630 LOG.info("loop of check OSNR : DEGREE, n = {} Path Element = {}", n, pathElement);
631 cnt = CatalogConstant.CatalogNodeType.EXPRESS;
632 String degree1Mode = "";
633 String degree2Mode = "";
634 // If the operational mode of the Degree is not consistent or if the operational
635 // mode is not declared in the topology
636 if (StringConstants.UNKNOWN_MODE.equals(currentNode.getOperationalMode())
637 || currentNode.getOperationalMode() == null
638 || currentNode.getOperationalMode().isEmpty()) {
639 // Operational mode is set by default to standard opMode for Degree
640 degree1Mode = CatalogConstant.MWMWCORE;
642 // Operational mode is found in degree-attributes of the Node
643 degree1Mode = currentNode.getOperationalMode();
645 // Same for next node which is the second degree of a ROADM node
646 if (StringConstants.UNKNOWN_MODE.equals(nextNode.getOperationalMode())
647 || nextNode.getOperationalMode() == null
648 || nextNode.getOperationalMode().isEmpty()) {
649 degree2Mode = CatalogConstant.MWMWCORE;
651 degree2Mode = currentNode.getOperationalMode();
653 // At that time OpenROADM provides only one spec for the ROADM nodes
654 if (!degree1Mode.equals(degree2Mode)) {
655 LOG.info("Unsupported Hybrid ROADM configuration with Degree1 {} of {} operational mode"
656 + "and Degree2 {} of {} operational mode. Will by default use operational mode"
657 + "of Degree2", currentNode.getNodeId().toString(),
658 degree1Mode, nextNode.getNodeId().toString(), degree2Mode);
660 pwrIn = pwrOut - getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks,
661 direction).getspanLoss();
662 // Calculate degradation accumulated across incoming Link and add them to
663 // accumulated impairments
664 calcCd += getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
666 calcPmd2 += getAppropriatePceLink((pathElement - offsetLink - 1), edges, allPceLinks, direction)
668 // This also includes Non Linear Contribution from the path
669 calcOnsrLin += cu.calculateNLonsrContribution(pwrOut, getAppropriatePceLink((pathElement
670 - offsetLink - 1), edges, allPceLinks, direction).getLength(), spacing);
671 // Calculate output power for next span (Output of degree 2)
672 pwrOut = cu.getPceRoadmAmpOutputPower(cnt, degree2Mode, getAppropriatePceLink((pathElement
673 + 3 * offsetLink + 1), edges, allPceLinks, direction).getspanLoss(), spacing,
674 getAppropriatePceLink((pathElement + 3 * offsetLink + 1), edges, allPceLinks, direction)
675 .getpowerCorrection());
676 // Adds to accumulated impairments the degradation associated with the Express
677 // path of ROADM : Degree1, express link, Degree2
678 impairments = cu.getPceRoadmAmpParameters(cnt, degree2Mode,
679 pwrIn, calcCd, calcPmd2, calcPdl2, calcOnsrLin, spacing);
680 calcCd = impairments.get("CD").doubleValue();
681 calcPmd2 = impairments.get("DGD2").doubleValue();
682 calcPdl2 = impairments.get("PDL2").doubleValue();
683 calcOnsrLin = impairments.get("ONSRLIN").doubleValue();
684 LOG.debug("Loop n = {}, DEGREE, calcOsnrdB= {}", n, getOsnrDbfromOnsrLin(calcOnsrLin));
685 if (calcOnsrLin == Double.NEGATIVE_INFINITY || calcOnsrLin == Double.POSITIVE_INFINITY) {
688 // increment pathElement so that in next step we will not point to Degree2 but
691 LOG.info("Accumulated degradations in the path including ROADM {} + {} are CD: {}; PMD2: "
692 + "{}; Pdl2 : {}; ONSRdB : {}", currentNode.getNodeId().toString(),
693 nextNode.getNodeId().toString(), calcCd, calcPmd2, calcPdl2, getOsnrDbfromOnsrLin(calcOnsrLin));
696 LOG.error("PostAlgoPathValidator.CheckOSNR : unsupported resource type in the path chain");
699 LOG.info("- In checkOSNR: accumulated CD = {} ps, PMD = {} ps, PDL = {} dB, and resulting OSNR calcOsnrdB = {} "
700 + "dB and ONSR dB exterapolated from calcosnrlin = {}"
701 + " including non linear contributions",
702 calcCd, Math.sqrt(calcPmd2), Math.sqrt(calcPdl2), calcOsnrdB, getOsnrDbfromOnsrLin(calcOnsrLin));
703 if (!transponderPresent) {
704 LOG.info("No transponder in the path, User shall check from CD, PMD, and OSNR values provided "
705 + "that optical tunnel degradations are compatible with external transponder performances");
708 LOG.info("In checkOSNR: Transponder Operational mode {} results in a residual margin of {} dB, according "
709 + "to CD, PMD and DGD induced penalties and set System Margin of {} dB.",
710 opMode, margin - SYS_MARGIN, SYS_MARGIN);
711 String validationMessage = "INVALIDATED";
712 if ((margin - SYS_MARGIN) >= 0) {
713 validationMessage = "VALIDATED";
715 if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
716 LOG.info("- In checkOSNR: A to Z Path from {} to {} {}", vertices.get(0),
717 vertices.get(vertices.size() - 1), validationMessage);
719 LOG.info("- In checkOSNR: Z to A Path from {} to {} {}", vertices.get(vertices.size() - 1),
720 vertices.get(0), validationMessage);
722 return (margin - SYS_MARGIN);
725 // Method to provide either regular link (AtoZ) or Opposite link (ZtoA) in the list of PceGraphEdges
726 private PceLink getAppropriatePceLink(Integer pathEltNber, List<PceGraphEdge> edges,
727 Map<LinkId, PceLink> allPceLinks, String direction) {
728 if ((StringConstants.SERVICE_DIRECTION_AZ).equals(direction)) {
729 // Returns regular link.
730 return edges.get(pathEltNber).link();
732 //For Z to A direction, must return the opposite link
733 return allPceLinks.get(new LinkId(edges.get(pathEltNber).link().getOppositeLink()));
736 private double getOsnrDbfromOnsrLin(double onsrLu) {
737 return (10 * Math.log10(1 / onsrLu));
741 * Get spectrum assignment for path.
743 * @param path the path for which we get spectrum assignment.
744 * @param allPceNodes all optical nodes.
745 * @param spectralWidthSlotNumber number of slot for spectral width. Depends on
747 * @return a spectrum assignment object which contains begin and end index. If
748 * no spectrum assignment found, beginIndex = stopIndex = 0
750 private SpectrumAssignment getSpectrumAssignment(GraphPath<String, PceGraphEdge> path,
751 Map<NodeId, PceNode> allPceNodes, int spectralWidthSlotNumber) {
752 byte[] freqMap = new byte[GridConstant.NB_OCTECTS];
753 Arrays.fill(freqMap, (byte) GridConstant.AVAILABLE_SLOT_VALUE);
754 BitSet result = BitSet.valueOf(freqMap);
755 boolean isFlexGrid = true;
756 LOG.debug("Processing path {} with length {}", path, path.getLength());
757 for (PceGraphEdge edge : path.getEdgeList()) {
758 NodeId srcNodeId = edge.link().getSourceId();
759 LOG.debug("Processing source {} ", srcNodeId);
760 if (allPceNodes.containsKey(srcNodeId)) {
761 PceNode pceNode = allPceNodes.get(srcNodeId);
762 LOG.debug("Processing PCE node {}", pceNode);
763 String pceNodeVersion = pceNode.getVersion();
764 NodeId pceNodeId = pceNode.getNodeId();
765 BigDecimal sltWdthGran = pceNode.getSlotWidthGranularity();
766 BigDecimal ctralFreqGran = pceNode.getCentralFreqGranularity();
767 if (StringConstants.OPENROADM_DEVICE_VERSION_1_2_1.equals(pceNodeVersion)) {
768 LOG.debug("Node {}: version is {} and slot width granularity is {} -> fixed grid mode",
769 pceNodeId, pceNodeVersion, sltWdthGran);
772 if (sltWdthGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)
773 && ctralFreqGran.setScale(0, RoundingMode.CEILING).equals(GridConstant.SLOT_WIDTH_50)) {
774 LOG.debug("Node {}: version is {} with slot width granularity {} and central "
775 + "frequency granularity is {} -> fixed grid mode",
776 pceNodeId, pceNodeVersion, sltWdthGran, ctralFreqGran);
779 BitSet pceNodeFreqMap = pceNode.getBitSetData();
780 LOG.debug("Pce node bitset {}", pceNodeFreqMap);
781 if (pceNodeFreqMap != null) {
782 result.and(pceNodeFreqMap);
783 LOG.debug("intermediate bitset {}", result);
787 LOG.debug("Bitset result {}", result);
788 return computeBestSpectrumAssignment(result, spectralWidthSlotNumber, isFlexGrid);
792 * Compute spectrum assignment from spectrum occupation for spectral width.
794 * @param spectrumOccupation the spectrum occupation BitSet.
795 * @param spectralWidthSlotNumber the nb slots for spectral width.
796 * @param isFlexGrid true if flexible grid, false otherwise.
797 * @return a spectrum assignment object which contains begin and stop index. If
798 * no spectrum assignment found, beginIndex = stopIndex = 0
800 private SpectrumAssignment computeBestSpectrumAssignment(
801 BitSet spectrumOccupation, int spectralWidthSlotNumber, boolean isFlexGrid) {
802 SpectrumAssignmentBuilder spectrumAssignmentBldr = new SpectrumAssignmentBuilder()
803 .setBeginIndex(Uint16.valueOf(0))
804 .setStopIndex(Uint16.valueOf(0))
805 .setFlexGrid(isFlexGrid);
806 BitSet referenceBitSet = new BitSet(spectralWidthSlotNumber);
807 referenceBitSet.set(0, spectralWidthSlotNumber);
808 //higher is the frequency, smallest is the wavelength number
809 //in operational, the allocation is done through wavelength starting from the smallest
810 //so we have to loop from the last element of the spectrum occupation
811 for (int i = spectrumOccupation.size(); i >= spectralWidthSlotNumber;
812 i -= isFlexGrid ? spectralWidthSlotNumber : 1) {
813 if (spectrumOccupation.get(i - spectralWidthSlotNumber, i).equals(referenceBitSet)) {
814 spectrumAssignmentBldr.setBeginIndex(Uint16.valueOf(i - spectralWidthSlotNumber));
815 spectrumAssignmentBldr.setStopIndex(Uint16.valueOf(i - 1));
819 return spectrumAssignmentBldr.build();
822 public Double getTpceCalculatedMargin() {
823 return tpceCalculatedMargin;