3fb21cf3a2742fdbf60781a22ea4db85d6bcf6a9
[transportpce.git] / common / src / main / java / org / opendaylight / transportpce / common / catalog / CatalogUtils.java
1 /*
2  * Copyright © 2022 Orange, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.transportpce.common.catalog;
9
10 import java.util.HashMap;
11 import java.util.Map;
12 import java.util.Optional;
13 import java.util.concurrent.ExecutionException;
14 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
15 import org.opendaylight.transportpce.common.StringConstants;
16 import org.opendaylight.transportpce.common.network.NetworkTransactionService;
17 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.link.types.rev191129.RatioDB;
18 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.ImpairmentType;
19 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.amplifier.parameters.Amplifier;
20 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.OpenroadmOperationalModes;
21 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.SpecificOperationalModes;
22 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.Amplifiers;
23 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.Roadms;
24 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.XpondersPluggables;
25 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.xponders.pluggables.XponderPluggableOpenroadmOperationalMode;
26 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.openroadm.operational.modes.xponders.pluggables.XponderPluggableOpenroadmOperationalModeKey;
27 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.specific.operational.modes.SpecificOperationalMode;
28 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.catalog.specific.operational.modes.SpecificOperationalModeKey;
29 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.common.amplifier.drop.parameters.OpenroadmOperationalMode;
30 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.common.amplifier.drop.parameters.OpenroadmOperationalModeKey;
31 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.Add;
32 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.add.AddOpenroadmOperationalMode;
33 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.add.parameters.add.AddOpenroadmOperationalModeKey;
34 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.drop.parameters.Drop;
35 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.roadm.express.parameters.Express;
36 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.Penalties;
37 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.PenaltiesKey;
38 import org.opendaylight.yang.gen.v1.http.org.openroadm.operational.mode.catalog.rev211210.operational.mode.transponder.parameters.TXOOBOsnrKey;
39 import org.opendaylight.yang.gen.v1.http.org.openroadm.service.rev211210.OperationalModeCatalog;
40 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Utility class for Catalog. Following methods are used to retrieve parameters
46  * from the specification catalog. They point to either openROADM or specific
47  * operational modes. They provide to the PCE, the OLM and the Renderer, the
48  * required parameters to calculate impairments and set output power levels
49  * according to the specifications.
50  *
51  */
52 public class CatalogUtils {
53
54     private static final Logger LOG = LoggerFactory.getLogger(CatalogUtils.class);
55
56     private static final String OPMODE_MISMATCH_MSG =
57         "Operational Mode {} passed to getPceRoadmAmpParameters does not correspond to an OpenROADM mode"
58         + "Parameters for amplifier and/or ROADMs can not be derived from specific-operational-modes.";
59     private static final Map<CatalogConstant.CatalogNodeType, String> CATALOGNODETYPE_OPERATIONMODEID_MAP = Map.of(
60         CatalogConstant.CatalogNodeType.ADD, CatalogConstant.MWWRCORE,
61         CatalogConstant.CatalogNodeType.DROP, CatalogConstant.MWWRCORE,
62         CatalogConstant.CatalogNodeType.EXPRESS, CatalogConstant.MWMWCORE,
63         CatalogConstant.CatalogNodeType.AMP, CatalogConstant.MWISTANDARD);
64     private static final Map<String, String> TSP_DEFAULT_OM_MAP = Map.of(
65         StringConstants.SERVICE_TYPE_100GE_T, CatalogConstant.ORW100GSC,
66         StringConstants.SERVICE_TYPE_OTU4, CatalogConstant.ORW100GSC,
67         StringConstants.SERVICE_TYPE_OTUC2,  CatalogConstant.ORW200GOFEC316GBD,
68         StringConstants.SERVICE_TYPE_OTUC3, CatalogConstant.ORW300GOFEC631GBD,
69         StringConstants.SERVICE_TYPE_OTUC4, CatalogConstant.ORW400GOFEC631GBD,
70         StringConstants.SERVICE_TYPE_400GE, CatalogConstant.ORW400GOFEC631GBD);
71
72     private final PenaltiesComparator penaltiesComparator = new PenaltiesComparator();
73     private NetworkTransactionService networkTransactionService;
74
75     public CatalogUtils(NetworkTransactionService networkTransactionService) {
76         this.networkTransactionService = networkTransactionService;
77     }
78
79     /**
80      * Following method returns default OperationalModeId for devices that do not expose them.
81      *
82      * @param catalogNodeType
83      *            identifies type of nodes in the catalog
84      * @param serviceType
85      *            allows for Xponder selecting default mode according to the rate
86      *
87      * @return a default operational mode that corresponds to initial specifications
88      *
89      */
90     public String getPceTxTspOperationalModeFromServiceType(CatalogConstant.CatalogNodeType catalogNodeType,
91             String serviceType) {
92         if (CATALOGNODETYPE_OPERATIONMODEID_MAP.containsKey(catalogNodeType)) {
93             return CATALOGNODETYPE_OPERATIONMODEID_MAP.get(catalogNodeType);
94         }
95         if (!catalogNodeType.equals(CatalogConstant.CatalogNodeType.TSP)) {
96             LOG.warn("Unsupported catalogNodeType {}", catalogNodeType);
97             return "";
98         }
99         if (!TSP_DEFAULT_OM_MAP.containsKey(serviceType)) {
100             LOG.warn("Unsupported serviceType {} for TSP catalogNodeType", serviceType);
101             return "";
102         }
103         return TSP_DEFAULT_OM_MAP.get(serviceType);
104     }
105
106     /**
107      * This method retrieves channel-spacing associated with a Xponder TX.
108      *
109      * @param operationalModeId
110      *            operational-mode-Id of the Xponder (OR or Specific)
111      *
112      * @return the channel spacing used to correct OSNR contribution values from
113      *         ROADMs and amplifiers
114      * @throws RuntimeException
115      *             if operationalModeId is not described in the catalog
116      */
117
118     public double getPceTxTspChannelSpacing(String operationalModeId) {
119         double baudRate;
120         double maxRollOff;
121         if (operationalModeId.startsWith("OR")) {
122             InstanceIdentifier<XponderPluggableOpenroadmOperationalMode> omCatalogIid = InstanceIdentifier
123                 .builder(OperationalModeCatalog.class)
124                 .child(OpenroadmOperationalModes.class)
125                 .child(XpondersPluggables.class)
126                 .child(XponderPluggableOpenroadmOperationalMode.class,
127                     new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
128                 .build();
129             try {
130                 Optional<XponderPluggableOpenroadmOperationalMode> omOptional =
131                     networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
132                 if (omOptional.isEmpty()) {
133                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
134                     return 0.0;
135                 }
136                 XponderPluggableOpenroadmOperationalMode orTspOM = omOptional.get();
137                 maxRollOff = orTspOM.getMaxRollOff() == null ? 0 : orTspOM.getMaxRollOff().doubleValue();
138                 baudRate = orTspOM.getBaudRate().doubleValue();
139             } catch (InterruptedException | ExecutionException e) {
140                 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
141                 throw new RuntimeException("Operational mode not populated in Catalog : " + omCatalogIid + " :" + e);
142             } finally {
143                 networkTransactionService.close();
144             }
145         } else {
146             // In other cases, means the mode is a non OpenROADM specific Operational Mode
147             InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
148                 .builder(OperationalModeCatalog.class)
149                 .child(SpecificOperationalModes.class)
150                 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
151                 .build();
152             try {
153                 var somOptional =
154                     networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
155                 if (somOptional.isEmpty()) {
156                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
157                     return 0.0;
158                 }
159                 SpecificOperationalMode speTspOM = somOptional.get();
160                 maxRollOff = speTspOM.getMaxRollOff() == null ? 0 : speTspOM.getMaxRollOff().doubleValue();
161                 baudRate = speTspOM.getBaudRate().doubleValue();
162             } catch (InterruptedException | ExecutionException e) {
163                 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
164                 throw new RuntimeException("Operational mode not populated in Catalog : " + omCatalogIid + " :" + e);
165             } finally {
166                 networkTransactionService.close();
167             }
168         }
169         if (maxRollOff == 0) {
170             if (CatalogConstant.ORW100GSC.contentEquals(operationalModeId)) {
171             // OR 100G SCFEC is the only case where rolloff factor is not mandatory in the catalog
172                 LOG.info("Operational Mode {} associated channel spacing is 50.0", operationalModeId);
173                 return 50.0;
174             }
175             LOG.warn("Missing rolloff factor (mandatory in Catalog) from Operational Mode {}: use default=0.2",
176                 operationalModeId);
177             maxRollOff = 0.2;
178         }
179         double spacing = 12.5 * Math.ceil(baudRate * (1 + maxRollOff) / 12.5);
180         LOG.info("Operational Mode {} associated channel spacing is {}", operationalModeId, spacing);
181         return spacing;
182     }
183
184     /**
185      * This method retrieves performance parameters associated with a Xponder TX.
186      *
187      * @param operationalModeId
188      *            operational-mode-Id of the Xponder (OR or Specific)
189      * @param addDropMuxOperationalModeId
190      *            operational-mode-Id of the Add-Drop bloc the XponderTX is
191      *            associated to (conditions TX-OOB OSNR value)
192      *
193      * @return the linear Optical Noise to signal Ratio
194      * @throws RuntimeException
195      *             if operationalModeId is not described in the catalog
196      */
197     public double getPceTxTspParameters(String operationalModeId, String addDropMuxOperationalModeId) {
198         double txOnsrLin = 0.0;
199         XponderPluggableOpenroadmOperationalMode orTspOM = null;
200         SpecificOperationalMode speTspOM = null;
201         RatioDB minOOBOsnrSingleChannelValue;
202         RatioDB minOOBOsnrMultiChannelValue;
203         if (operationalModeId.startsWith("OR")) {
204             InstanceIdentifier<XponderPluggableOpenroadmOperationalMode> omCatalogIid = InstanceIdentifier
205                 .builder(OperationalModeCatalog.class)
206                 .child(OpenroadmOperationalModes.class)
207                 .child(XpondersPluggables.class)
208                 .child(XponderPluggableOpenroadmOperationalMode.class,
209                     new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
210                 .build();
211             try {
212                 Optional<XponderPluggableOpenroadmOperationalMode> omOptional =
213                     networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
214                 if (omOptional.isEmpty()) {
215                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
216                     return 0.0;
217                 }
218                 orTspOM = omOptional.get();
219                 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM);
220                 TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId);
221                 if (orTspOM.getMinTXOsnr() != null) {
222                     txOnsrLin = 1.0 / Math.pow(10.0, orTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0);
223                 }
224                 if (orTspOM.nonnullTXOOBOsnr().get(key) == null) {
225                     return txOnsrLin;
226                 }
227                 minOOBOsnrSingleChannelValue = orTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue();
228                 minOOBOsnrMultiChannelValue =  orTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrMultiChannelValue();
229             } catch (InterruptedException | ExecutionException e) {
230                 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
231                 throw new RuntimeException(
232                     "readMdSal: Error reading from operational store, Operational Mode Catalog : "
233                         + omCatalogIid + " :" + e);
234             } finally {
235                 networkTransactionService.close();
236             }
237         } else {
238             // In other cases, means the mode is a non OpenROADM specific Operational Mode
239             InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
240                 .builder(OperationalModeCatalog.class)
241                 .child(SpecificOperationalModes.class)
242                 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
243                 .build();
244             try {
245                 var somOptional =
246                     networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
247                 if (somOptional.isEmpty()) {
248                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , empty list", omCatalogIid);
249                     return 0.0;
250                 }
251                 speTspOM = somOptional.get();
252                 LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM);
253                 TXOOBOsnrKey key = new TXOOBOsnrKey(addDropMuxOperationalModeId);
254                 if (speTspOM.getMinTXOsnr() != null) {
255                     txOnsrLin = 1.0 / Math.pow(10.0, speTspOM.getMinTXOsnr().getValue().doubleValue() / 10.0);
256                 }
257                 if (speTspOM.nonnullTXOOBOsnr().get(key) == null) {
258                     return txOnsrLin;
259                 }
260                 minOOBOsnrSingleChannelValue = speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrSingleChannelValue();
261                 minOOBOsnrMultiChannelValue = speTspOM.nonnullTXOOBOsnr().get(key).getMinOOBOsnrMultiChannelValue();
262             } catch (InterruptedException | ExecutionException e) {
263                 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
264                 throw new RuntimeException(
265                     "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
266                         + e);
267             } finally {
268                 networkTransactionService.close();
269             }
270         }
271         if (minOOBOsnrSingleChannelValue != null) {
272             txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrSingleChannelValue.getValue().doubleValue() / 10.0);
273         }
274         if (minOOBOsnrMultiChannelValue != null) {
275             txOnsrLin += 1.0 / Math.pow(10.0, minOOBOsnrMultiChannelValue.getValue().doubleValue() / 10.0);
276         }
277         return txOnsrLin;
278     }
279
280     /**
281      * This method retrieves performance parameters associated with a Xponder RX.
282      * It calls getRxTspPenalty to evaluate the penalty associated with CD/PMD/PDL
283      * It compares expected OSNR with the OSNR resulting from the line degradation,
284      * and finally calculates and return the resulting margin.
285      *
286      * @param operationalModeId
287      *            operational-mode-Id of the Xponder (OR or Specific)
288      * @param calcCd
289      *            accumulated chromatic dispersion across the line
290      * @param calcPmd
291      *            accumulated Polarization mode dispersion across the line
292      * @param calcPdl
293      *            accumulated Polarization Dependant Loss across the line
294      * @param calcOsnrdB
295      *            Optical Signal to Noise Ratio (dB)resulting from the transmission
296      *            on the line, that shall include the Non Linear contribution
297      *
298      * @return the margin on the service path
299      * @throws RuntimeException
300      *             if operationalModeId is not described in the catalog
301      */
302     public double getPceRxTspParameters(String operationalModeId, double calcCd, double calcPmd,
303             double calcPdl, double calcOsnrdB) {
304         double rxOsnrdB = 0.0;
305         XponderPluggableOpenroadmOperationalMode orTspOM = null;
306         SpecificOperationalMode speTspOM = null;
307         Map<PenaltiesKey, Penalties> penaltiesMap = null;
308         if (operationalModeId.startsWith("OR")) {
309             var omCatalogIid = InstanceIdentifier
310                 .builder(OperationalModeCatalog.class)
311                 .child(OpenroadmOperationalModes.class)
312                 .child(XpondersPluggables.class)
313                 .child(XponderPluggableOpenroadmOperationalMode.class,
314                     new XponderPluggableOpenroadmOperationalModeKey(operationalModeId))
315                 .build();
316             try {
317                 Optional<XponderPluggableOpenroadmOperationalMode> omOptional = networkTransactionService
318                     .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
319                 if (omOptional.isPresent()) {
320                     orTspOM = omOptional.get();
321                     LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orTspOM);
322                     if (orTspOM.getMinRXOsnrTolerance() != null) {
323                         rxOsnrdB = orTspOM.getMinRXOsnrTolerance().getValue().doubleValue();
324                     }
325                     penaltiesMap = orTspOM.getPenalties();
326                 }
327             } catch (InterruptedException | ExecutionException e) {
328                 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
329                 throw new RuntimeException(
330                     "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
331                         + e);
332             } finally {
333                 networkTransactionService.close();
334             }
335         } else {
336             // In other cases, means the mode is a non OpenROADM specific Operational Mode
337             // InstanceIdentifier<SpecificOperationalMode> omCatalogIid = InstanceIdentifier
338             var omCatalogIid = InstanceIdentifier
339                 .builder(OperationalModeCatalog.class)
340                 .child(SpecificOperationalModes.class)
341                 .child(SpecificOperationalMode.class, new SpecificOperationalModeKey(operationalModeId))
342                 .build();
343             try {
344                 Optional<SpecificOperationalMode> somOptional = networkTransactionService
345                     .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
346                 if (somOptional.isPresent()) {
347                     speTspOM = somOptional.get();
348                     LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", speTspOM);
349                     if (speTspOM.getMinRXOsnrTolerance() != null) {
350                         rxOsnrdB = speTspOM.getMinRXOsnrTolerance().getValue().doubleValue();
351                     }
352                     penaltiesMap = speTspOM.getPenalties();
353                 }
354             } catch (InterruptedException | ExecutionException e) {
355                 LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist", omCatalogIid);
356                 throw new RuntimeException(
357                     "readMdSal: Error reading from operational store, Operational Mode Catalog : " + omCatalogIid + " :"
358                         + e);
359             } finally {
360                 networkTransactionService.close();
361             }
362         }
363         if (penaltiesMap == null) {
364             LOG.error("Unable to calculate margin as penaltyMap can not be retrieved : Operational mode not populated");
365             return -9999.9;
366         }
367         HashMap<String, Double> impairments = new HashMap<>();
368         double penalty = getRxTspPenalty(calcCd, ImpairmentType.CDPsNm, penaltiesMap);
369         impairments.put("CDpenalty", penalty);
370         double totalPenalty = penalty;
371         penalty = getRxTspPenalty(calcPmd, ImpairmentType.PMDPs, penaltiesMap);
372         impairments.put("PMD Penalty", penalty);
373         totalPenalty += penalty;
374         penalty = getRxTspPenalty(calcPdl, ImpairmentType.PDLDB, penaltiesMap);
375         impairments.put("PDL penalty", penalty);
376         totalPenalty += penalty;
377         // TODO for Future work since at that time we have no way to calculate the following
378         // parameters,even if penalties are defined in the OpenROADM specifications
379         //
380         // impairments.put("Colorless Drop Adjacent Xtalk Penalty", getRxTspPenalty(TBD,
381         // ImpairmentType.ColorlessDropAdjacentChannelCrosstalkGHz, penalitiesMap));
382         // impairments.put("XTalk total Power Penalty", getRxTspPenalty(TBD,
383         // ImpairmentType.CrossTalkTotalPowerDB, penalitiesMap));
384         // impairments.put("Power penalty", getRxTspPenalty(TBD,
385         // ImpairmentType.PowerDBm, penalitiesMap));
386         LOG.info("Penalty resulting from CD, PMD and PDL is {} dB with following contributions {}",
387             totalPenalty, impairments);
388         double margin = calcOsnrdB - totalPenalty - rxOsnrdB;
389         LOG.info("According to RX TSP Specification and calculated impairments Margin is {} dB ", margin);
390         if (margin < 0) {
391             LOG.warn("Negative margin shall result in PCE rejecting the analyzed path");
392         }
393         return margin;
394     }
395
396     /**
397      * This generic method is called from getPceRxTspParameters to provide the
398      * Penalties associated with CD, PMD and DGD for Xponder. It scans a penalty
399      * list that includes penalty values corresponding to an interval between an
400      * upper and a lower boundary for each of the above parameters.
401      *
402      * @param impairmentType
403      *            : the type of impairment (CD/PMD/DGD)
404      * @param calculatedParameter
405      *            calculated accumulated value on the line for the impairment
406      * @param penaltiesMap
407      *            the global map of penalties retrieved by getPceRxTspParameters
408      *            from the Xponder operational mode
409      *
410      * @return the penalty associated with accumulated impairment if it is in the
411      *         range specified in the table, a value that will lead to reject the
412      *         path if this is not the case
413      */
414
415     private double getRxTspPenalty(double calculatedParameter, ImpairmentType impairmentType,
416             Map<PenaltiesKey, Penalties> penalitiesMap) {
417         Penalties penalty = penalitiesMap.values().stream()
418             // We only keep penalties corresponding to the calculated Parameter
419             .filter(val -> val.getParameterAndUnit().getName().equals(impairmentType.getName()))
420             // we sort it according to the comparator (based on up-to-boundary)
421             .sorted(penaltiesComparator)
422             // keep only items for which up to boundary is greater than calculatedParameter
423             .filter(val -> val.getUpToBoundary().doubleValue() >= calculatedParameter)
424             // takes the immediate greater or equal value
425             .findFirst().orElse(null);
426         return penalty == null
427             //means a boundary that is greater than calculatedParameter couldn't be found
428             // Out of specification!
429                 ? 9999.9
430             // In spec, return penalty associated with calculatedParameter
431                 : penalty.getPenaltyValue().getValue().doubleValue();
432     }
433
434     /**
435      * This method retrieves performance parameters associated with ROADMs and
436      * Amplifiers. It calculates the contribution of the node to the degradation of
437      * the signal for CD, DGD, PDL, and OSNR which is calculated through a
438      * polynomial fit described in the catalog. It finally corrects the accumulated
439      * values for these parameters and return them.
440      *
441      * @param catalogNodeType
442      *            crossed node path type (ADD/DROP/EXPRESS/AMP)
443      * @param operationalModeId
444      *            operational-mode-Id of the Node (OpenROADM only)
445      * @param calcCd
446      *            accumulated chromatic dispersion across the line
447      * @param calcDgd2
448      *            Square of accumulated Group velocity dispersion across the line
449      * @param calcPdl2
450      *            Square of the accumulated Polarization Dependant Loss across the
451      *            line
452      * @param pwrIn
453      *            Input power required to calculate OSNR contribution of the node =
454      *            f(pwrIn)
455      * @param calcOnsrLin
456      *            Linear Optical Noise to Signal Ratio resulting from the
457      *            transmission on the line, that shall include the Non Linear
458      *            contribution
459      * @param spacing
460      *            Interchannel spacing used for correction to calculate OSNR
461      *            contribution of the node
462      *
463      * @return Impairment, a map that provides corrected values for all calculated
464      *         parameters which includes the contribution of the node
465      *         (CD/DGD2/PDL2/ONSRLin)
466      * @throws RuntimeException
467      *             if operationalModeId is not described in the catalog
468      */
469
470     public Map<String, Double> getPceRoadmAmpParameters(CatalogConstant.CatalogNodeType catalogNodeType,
471             String operationalModeId, double pwrIn, double calcCd, double calcDgd2, double calcPdl2,
472             double calcOnsrLin, double spacing) {
473         //TODO more refactoring need here
474         double pdl2 = calcPdl2;
475         double dgd2 = calcDgd2;
476         double cd = calcCd;
477         double onsrLin = calcOnsrLin;
478         switch (catalogNodeType) {
479             case ADD:
480                 var omCatalogIid = InstanceIdentifier
481                     .builder(OperationalModeCatalog.class)
482                     .child(OpenroadmOperationalModes.class)
483                     .child(Roadms.class)
484                     .child(Add.class)
485                     .child(AddOpenroadmOperationalMode.class, new AddOpenroadmOperationalModeKey(operationalModeId))
486                     .build();
487                 try {
488                     var omOptional =
489                         networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid).get();
490                     if (omOptional.isEmpty()) {
491                         LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
492                         return new HashMap<>();
493                     }
494                     var orAddOM = omOptional.get();
495                     LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAddOM);
496                     networkTransactionService.close();
497                     onsrLin +=
498                         + Math.pow(10, (-orAddOM.getIncrementalOsnr().getValue().doubleValue()
499                         - Math.log10(spacing / 50.0)) / 10.0);
500                     cd += orAddOM.getMaxIntroducedCd().doubleValue();
501                     pdl2 += Math.pow(orAddOM.getMaxIntroducedPdl().getValue().doubleValue(), 2.0);
502                     dgd2 += Math.pow(orAddOM.getMaxIntroducedDgd().doubleValue(), 2.0);
503                 } catch (InterruptedException | ExecutionException e) {
504                     onsrLin = 1;
505                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
506                         omCatalogIid);
507                     throw new RuntimeException(
508                         "readMdSal: Error reading from operational store, Operational Mode Catalog : "
509                             + omCatalogIid + " :" + e);
510                 } finally {
511                     networkTransactionService.close();
512                 }
513                 break;
514
515             case DROP:
516                 var omCatalogIid1 = InstanceIdentifier
517                     .builder(OperationalModeCatalog.class)
518                     .child(OpenroadmOperationalModes.class)
519                     .child(Roadms.class)
520                     .child(Drop.class)
521                     .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
522                     .build();
523                 try {
524                     var omOptional =
525                         networkTransactionService.read(LogicalDatastoreType.CONFIGURATION, omCatalogIid1).get();
526                     if (omOptional.isEmpty()) {
527                         LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
528                         return new HashMap<>();
529                     }
530                     var orDropOM = omOptional.get();
531                     LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orDropOM);
532                     networkTransactionService.close();
533                     cd += orDropOM.getMaxIntroducedCd().doubleValue();
534                     pdl2 += Math.pow(orDropOM.getMaxIntroducedPdl().getValue().doubleValue(), 2.0);
535                     dgd2 += Math.pow(orDropOM.getMaxIntroducedDgd().doubleValue(), 2);
536                     onsrLin += Math.pow(10,
537                         -(orDropOM.getOsnrPolynomialFit().getA().doubleValue() * Math.pow(pwrIn, 3)
538                             + orDropOM.getOsnrPolynomialFit().getB().doubleValue() * Math.pow(pwrIn, 2)
539                             + orDropOM.getOsnrPolynomialFit().getC().doubleValue() * pwrIn
540                             + orDropOM.getOsnrPolynomialFit().getD().doubleValue()
541                             + 10 * Math.log10(spacing / 50.0)) / 10);
542                 } catch (InterruptedException | ExecutionException e) {
543                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
544                         omCatalogIid1);
545                     throw new RuntimeException(
546                         "readMdSal: Error reading from operational store, Operational Mode Catalog : "
547                             + omCatalogIid1 + " :" + e);
548                 } finally {
549                     networkTransactionService.close();
550                 }
551                 break;
552
553             case EXPRESS:
554                 var omCatalogIid2 = InstanceIdentifier
555                     .builder(OperationalModeCatalog.class)
556                     .child(OpenroadmOperationalModes.class)
557                     .child(Roadms.class)
558                     .child(Express.class)
559                     .child(
560                         org.opendaylight.yang.gen.v1.http
561                             .org.openroadm.operational.mode.catalog.rev211210
562                             .operational.mode.roadm.express.parameters.express.OpenroadmOperationalMode.class,
563                         new org.opendaylight.yang.gen.v1.http
564                             .org.openroadm.operational.mode.catalog.rev211210
565                             .operational.mode.roadm.express.parameters.express.OpenroadmOperationalModeKey(
566                                 operationalModeId))
567                     .build();
568                 try {
569                     var omOptional = networkTransactionService
570                         .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid2)
571                         .get();
572                     if (omOptional.isEmpty()) {
573                         LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
574                         return new HashMap<>();
575                     }
576                     var orExpressOM = omOptional.get();
577                     LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orExpressOM);
578                     cd += orExpressOM.getMaxIntroducedCd().doubleValue();
579                     pdl2 += Math.pow(orExpressOM.getMaxIntroducedPdl().getValue().doubleValue(), 2.0);
580                     dgd2 += Math.pow(orExpressOM.getMaxIntroducedDgd().doubleValue(), 2.0);
581                     onsrLin += Math.pow(10,
582                         -(orExpressOM.getOsnrPolynomialFit().getA().doubleValue() * Math.pow(pwrIn, 3)
583                             + orExpressOM.getOsnrPolynomialFit().getB().doubleValue() * Math.pow(pwrIn, 2)
584                             + orExpressOM.getOsnrPolynomialFit().getC().doubleValue() * pwrIn
585                             + orExpressOM.getOsnrPolynomialFit().getD().doubleValue()
586                             + 10 * Math.log10(spacing / 50.0)) / 10);
587                 } catch (InterruptedException | ExecutionException e) {
588                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
589                         omCatalogIid2);
590                     throw new RuntimeException(
591                         "readMdSal: Error reading from operational store, Operational Mode Catalog : "
592                             + omCatalogIid2 + " :" + e);
593                 } finally {
594                     networkTransactionService.close();
595                 }
596                 break;
597
598             case AMP:
599                 var omCatalogIid3 = InstanceIdentifier
600                     .builder(OperationalModeCatalog.class)
601                     .child(OpenroadmOperationalModes.class)
602                     .child(Amplifiers.class)
603                     .child(Amplifier.class)
604                     .child(OpenroadmOperationalMode.class, new OpenroadmOperationalModeKey(operationalModeId))
605                     .build();
606                 try {
607                     var omOptional = networkTransactionService
608                         .read(LogicalDatastoreType.CONFIGURATION, omCatalogIid3)
609                         .get();
610                     if (omOptional.isEmpty()) {
611                         LOG.error(OPMODE_MISMATCH_MSG, operationalModeId);
612                         return new HashMap<>();
613                     }
614                     var orAmpOM = omOptional.get();
615                     LOG.debug("readMdSal: Operational Mode Catalog: omOptional.isPresent = true {}", orAmpOM);
616                     networkTransactionService.close();
617                     cd += orAmpOM.getMaxIntroducedCd().doubleValue();
618                     pdl2 += Math.pow(orAmpOM.getMaxIntroducedPdl().getValue().doubleValue(), 2.0);
619                     dgd2 += Math.pow(orAmpOM.getMaxIntroducedDgd().doubleValue(), 2.0);
620                     onsrLin += Math.pow(10,
621                         -(orAmpOM.getOsnrPolynomialFit().getA().doubleValue() * Math.pow(pwrIn, 3)
622                             + orAmpOM.getOsnrPolynomialFit().getB().doubleValue() * Math.pow(pwrIn, 2)
623                             + orAmpOM.getOsnrPolynomialFit().getC().doubleValue() * pwrIn
624                             + orAmpOM.getOsnrPolynomialFit().getD().doubleValue()
625                             + 10 * Math.log10(spacing / 50.0)) / 10);
626                 } catch (InterruptedException | ExecutionException e) {
627                     LOG.error("readMdSal: Error reading Operational Mode Catalog {} , Mode does not exist",
628                         omCatalogIid3);
629                     throw new RuntimeException(
630                         "readMdSal: Error reading from operational store, Operational Mode Catalog : "
631                             + omCatalogIid3 + " :" + e);
632                 } finally {
633                     networkTransactionService.close();
634                 }
635                 break;
636             default:
637                 LOG.warn("Unsupported catalogNodeType {}", catalogNodeType);
638                 break;
639         }
640         Map<String, Double> impairments = new HashMap<>();
641         impairments.put("CD", cd);
642         impairments.put("DGD2", dgd2);
643         impairments.put("PDL2", pdl2);
644         impairments.put("ONSRLIN", onsrLin);
645         LOG.info("Accumulated CD is {} ps, DGD2 is {} ps and PDL2 is {} dB", cd, Math.sqrt(dgd2), Math.sqrt(pdl2));
646         LOG.info("Resulting OSNR is {} dB", 10 * Math.log10(1 / onsrLin));
647         return impairments;
648     }
649
650     /**
651      * Non linear contribution computation.
652      * Public method calculating non linear contribution among the path from
653      * launched power and span length Formula comes from
654      * OpenROADM_OSNR_Calculation_20220610 Tool The resulting contribution shall be
655      * calculated for each fiber span and summed up
656      * @param launchedPowerdB
657      *            The power launched in the span (shall account for Optical Distribution
658      *            Frame loss)
659      * @param spanLength
660      *            Length of the span in km
661      * @param spacing
662      *            OpenROADM power and osnr contribution calculations are based on
663      *            spacing between channels : the Media Channel (MC) width
664      *
665      * @return nonLinearOnsrContributionLin
666      *         The inverse of the NL OSNR contribution converted from dB to linear value
667      */
668     public double calculateNLonsrContribution(double launchedPowerdB, double spanLength, double spacing) {
669         double constanteC0 = 0 ;
670         if (spacing > 162.5) {
671             constanteC0 = CatalogConstant.NLCONSTANTC0GT1625;
672         } else if (spacing > 112.5) {
673             constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1625;
674         } else if (spacing > 100.0) {
675             constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1125;
676         } else if (spacing > 87.5) {
677             constanteC0 = CatalogConstant.NLCONSTANTC0UPTO1000;
678         } else {
679             constanteC0 = CatalogConstant.NLCONSTANTC0UPTO875;
680         }
681         double nonLinearOnsrContributionLinDb = launchedPowerdB * CatalogConstant.NLCONSTANTC1
682             + constanteC0 + CatalogConstant.NLCONSTANTCE * Math.exp(CatalogConstant.NLCONSTANTEX * spanLength);
683         LOG.info(" OSNR Non Linear contribution is {} dB", nonLinearOnsrContributionLinDb);
684         return Math.pow(10.0, -nonLinearOnsrContributionLinDb / 10);
685     }
686
687 }