Rewrite JUnit tests of PowerMgmt implementation
[transportpce.git] / olm / src / main / java / org / opendaylight / transportpce / olm / power / PowerMgmtImpl.java
index 407bfae2d22426a8d2931ff90f49c7cfafa8536e..d3b16f3110fa8d4dcf71e5acb59830e7b1a90057 100644 (file)
@@ -7,7 +7,10 @@
  */
 
 package org.opendaylight.transportpce.olm.power;
+
 import java.math.BigDecimal;
+import java.math.MathContext;
+import java.math.RoundingMode;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
@@ -16,18 +19,19 @@ import org.opendaylight.mdsal.binding.api.DataBroker;
 import org.opendaylight.transportpce.common.crossconnect.CrossConnect;
 import org.opendaylight.transportpce.common.device.DeviceTransactionManager;
 import org.opendaylight.transportpce.common.fixedflex.GridConstant;
+import org.opendaylight.transportpce.common.mapping.PortMapping;
 import org.opendaylight.transportpce.common.openroadminterfaces.OpenRoadmInterfaceException;
 import org.opendaylight.transportpce.common.openroadminterfaces.OpenRoadmInterfaces;
-import org.opendaylight.transportpce.olm.util.OlmUtils;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.ServicePowerSetupInput;
 import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.olm.rev210618.ServicePowerTurndownInput;
-import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev210426.OpenroadmNodeVersion;
-import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev210426.mapping.Mapping;
-import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev210426.mapping.MappingKey;
-import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev210426.network.Nodes;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220316.OpenroadmNodeVersion;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220316.mapping.Mapping;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220316.mapping.MappingKey;
+import org.opendaylight.yang.gen.v1.http.org.opendaylight.transportpce.portmapping.rev220316.network.Nodes;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.common.types.rev161014.OpticalControlMode;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev170206.interfaces.grp.Interface;
 import org.opendaylight.yang.gen.v1.http.org.openroadm.optical.transport.interfaces.rev161014.Interface1;
+import org.opendaylight.yangtools.yang.common.Decimal64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,16 +41,48 @@ public class PowerMgmtImpl implements PowerMgmt {
     private final OpenRoadmInterfaces openRoadmInterfaces;
     private final CrossConnect crossConnect;
     private final DeviceTransactionManager deviceTransactionManager;
+    private final PortMapping portMapping;
     private static final BigDecimal DEFAULT_TPDR_PWR_100G = new BigDecimal(-5);
     private static final BigDecimal DEFAULT_TPDR_PWR_400G = new BigDecimal(0);
     private static final String INTERFACE_NOT_PRESENT = "Interface {} on node {} is not present!";
+    private static final double MC_WIDTH_GRAN = 2 * GridConstant.GRANULARITY;
+
+    private long timer1 = 120000;
+    // openroadm spec value is 120000, functest value is 3000
+    private long timer2 = 20000;
+    // openroadm spec value is 20000, functest value is 2000
+
+    public PowerMgmtImpl(DataBroker db, OpenRoadmInterfaces openRoadmInterfaces, CrossConnect crossConnect,
+            DeviceTransactionManager deviceTransactionManager, PortMapping portMapping) {
+        this.db = db;
+        this.openRoadmInterfaces = openRoadmInterfaces;
+        this.crossConnect = crossConnect;
+        this.deviceTransactionManager = deviceTransactionManager;
+        this.portMapping = portMapping;
+    }
 
     public PowerMgmtImpl(DataBroker db, OpenRoadmInterfaces openRoadmInterfaces,
-                         CrossConnect crossConnect, DeviceTransactionManager deviceTransactionManager) {
+                         CrossConnect crossConnect, DeviceTransactionManager deviceTransactionManager,
+            PortMapping portMapping, String timer1, String timer2) {
         this.db = db;
         this.openRoadmInterfaces = openRoadmInterfaces;
         this.crossConnect = crossConnect;
         this.deviceTransactionManager = deviceTransactionManager;
+        this.portMapping = portMapping;
+        try {
+            this.timer1 = Long.parseLong(timer1);
+        } catch (NumberFormatException e) {
+            this.timer1 = 120000;
+            LOG.warn("Failed to retrieve Olm timer1 value from configuration - using default value {}",
+                this.timer1, e);
+        }
+        try {
+            this.timer2 = Long.parseLong(timer2);
+        } catch (NumberFormatException e) {
+            this.timer2 = 20000;
+            LOG.warn("Failed to retrieve Olm timer2 value from configuration - using default value {}",
+                this.timer2, e);
+        }
     }
 
     /**
@@ -65,16 +101,18 @@ public class PowerMgmtImpl implements PowerMgmt {
         String spectralSlotName = String.join(GridConstant.SPECTRAL_SLOT_SEPARATOR,
                 input.getLowerSpectralSlotNumber().toString(),
                 input.getHigherSpectralSlotNumber().toString());
+        if (input.getNodes() == null) {
+            LOG.error("No Nodes to configure");
+            return false;
+        }
         for (int i = 0; i < input.getNodes().size(); i++) {
             String nodeId = input.getNodes().get(i).getNodeId();
             String destTpId = input.getNodes().get(i).getDestTp();
-            Optional<Nodes> inputNodeOptional = OlmUtils.getNode(nodeId, this.db);
-            if (inputNodeOptional.isEmpty()
-                    || inputNodeOptional.get().getNodeInfo().getNodeType() == null) {
-                LOG.error("OLM-PowerMgmtImpl : Error node type cannot be retrieved for node {}", nodeId);
+            Nodes inputNode = this.portMapping.getNode(nodeId);
+            if (inputNode == null || inputNode.getNodeInfo() == null) {
+                LOG.error("OLM-PowerMgmtImpl : Error retrieving mapping node for {}", nodeId);
                 continue;
             }
-            Nodes inputNode = inputNodeOptional.get();
             OpenroadmNodeVersion openroadmVersion = inputNode.getNodeInfo().getOpenroadmVersion();
 
             switch (inputNode.getNodeInfo().getNodeType()) {
@@ -108,7 +146,7 @@ public class PowerMgmtImpl implements PowerMgmt {
                     LOG.info("Transponder OCH connection: {} power updated ", interfaceName);
                     try {
                         LOG.info("Now going in sleep mode");
-                        Thread.sleep(OlmUtils.OLM_TIMER_1);
+                        Thread.sleep(timer1);
                     } catch (InterruptedException e) {
                         LOG.info("Transponder warmup failed for OCH connection: {}", interfaceName, e);
                         // FIXME shouldn't it be LOG.warn  or LOG.error?
@@ -144,12 +182,13 @@ public class PowerMgmtImpl implements PowerMgmt {
                         destTpId, nodeId, openroadmVersion.getIntValue());
 
                     LOG.info("Spanloss TX is {}", spanLossTx);
-                    if (spanLossTx == null || spanLossTx.intValue() <= 0 || spanLossTx.intValue() > 28) {
-                        LOG.error("Power Value is null: spanLossTx null or out of openROADM range ]0,28] {}",
-                                spanLossTx);
+                    // TODO: The span-loss limits should be obtained from optical specifications
+                    if (spanLossTx == null || spanLossTx.intValue() <= 0 || spanLossTx.intValue() > 27) {
+                        LOG.error("Power Value is null: spanLossTx null or out of openROADM range ]0,27] {}",
+                            spanLossTx);
                         return false;
                     }
-                    BigDecimal powerValue = getRdmPowerValue(spanLossTx, input);
+                    Decimal64 powerValue = Decimal64.valueOf(getRdmPowerValue(spanLossTx, input));
                     try {
                         if (!crossConnect.setPowerLevel(nodeId, OpticalControlMode.Power.getName(), powerValue,
                                 connectionNumber)) {
@@ -159,7 +198,7 @@ public class PowerMgmtImpl implements PowerMgmt {
                             return false;
                         }
                         LOG.info("Roadm-connection: {} updated ", connectionNumber);
-                        Thread.sleep(OlmUtils.OLM_TIMER_2);
+                        Thread.sleep(timer2);
                         // TODO make this timer value configurable via OSGi blueprint
                         // although the value recommended by the white paper is 20 seconds.
                         // At least one vendor product needs 60 seconds
@@ -219,14 +258,11 @@ public class PowerMgmtImpl implements PowerMgmt {
 
     private Map<String, Double> getSRGRxPowerRangeMap(String srgId, String nodeId, Integer openroadmVersion) {
 
-        Optional<Nodes> inputNode = OlmUtils.getNode(nodeId, this.db);
-        int rdmOpenroadmVersion =
-                inputNode.isPresent()
-                    ? inputNode.get().getNodeInfo().getOpenroadmVersion().getIntValue()
-                    : openroadmVersion;
-        Optional<Mapping> mappingObject = inputNode
-                .flatMap(node -> node.nonnullMapping().values().stream()
-                    .filter(o -> o.key().equals(new MappingKey(srgId))).findFirst());
+        Nodes inputNode = this.portMapping.getNode(nodeId);
+        int rdmOpenroadmVersion = inputNode.getNodeInfo().getOpenroadmVersion().getIntValue();
+        Optional<Mapping> mappingObject = inputNode.nonnullMapping().values().stream()
+                .filter(o -> o.key().equals(new MappingKey(srgId)))
+                .findFirst();
 
         if (mappingObject.isEmpty()) {
             return new HashMap<>();
@@ -270,7 +306,7 @@ public class PowerMgmtImpl implements PowerMgmt {
                     }
                     return interfaceOpt.get()
                             .augmentation(Interface1.class)
-                            .getOts().getSpanLossTransmit().getValue();
+                            .getOts().getSpanLossTransmit().getValue().decimalValue();
                 case 2:
                     Optional<org.opendaylight.yang.gen.v1.http.org.openroadm.device.rev181019
                             .interfaces.grp.Interface> interfaceOpt1 =
@@ -288,7 +324,7 @@ public class PowerMgmtImpl implements PowerMgmt {
                     return interfaceOpt1.get()
                             .augmentation(org.opendaylight.yang.gen.v1.http.org
                                 .openroadm.optical.transport.interfaces.rev181019.Interface1.class)
-                            .getOts().getSpanLossTransmit().getValue();
+                            .getOts().getSpanLossTransmit().getValue().decimalValue();
                 // TODO no case 3 ?
                 default:
                     return null;
@@ -339,20 +375,42 @@ public class PowerMgmtImpl implements PowerMgmt {
 
 
     private BigDecimal getRdmPowerValue(BigDecimal spanLossTx, ServicePowerSetupInput input) {
-        BigDecimal powerValue = spanLossTx.subtract(BigDecimal.valueOf(9)).min(BigDecimal.valueOf(2));
+        // TODO: These values will be obtained from the specifications
+        // power-value here refers to the Pin[50GHz]
+        BigDecimal powerValue;
+        if (spanLossTx.doubleValue()  >= 23.0) {
+            powerValue = BigDecimal.valueOf(2.0);
+        } else if (spanLossTx.doubleValue()  >= 8.0) {
+            powerValue = BigDecimal.valueOf(- (8.0 - spanLossTx.doubleValue()) / 3.0 - 3.0);
+        } else if (spanLossTx.doubleValue() >= 6.0) {
+            powerValue = BigDecimal.valueOf(-3.0);
+        } else {
+            powerValue = spanLossTx.subtract(BigDecimal.valueOf(9));
+        }
+        BigDecimal mcWidth = new BigDecimal(50);
         // we work at constant power spectral density (50 GHz channel width @-20dBm=37.5GHz)
         // 87.5 GHz channel width @-20dBm=75GHz
-        if (input.getWidth() != null) {
-            BigDecimal gridSize = input.getWidth().getValue();
-            LOG.debug("Input Gridsize is {}",gridSize);
-            if (gridSize.equals(GridConstant.WIDTH_80)) {
-                powerValue = powerValue.add(BigDecimal.valueOf(3));
-            }
-            // TODO no default or warning for unsupported grid sizes ?
+        if (input.getMcWidth() != null) {
+            // Units of MC-width are in GHz, meaning it should be 40/50/87.5GHz
+            // TODO: Should we validate this units before proceeding?
+            LOG.debug("Input Grid size is {}", input.getMcWidth().getValue());
+
+            // We round-off the mc-width to the nearest grid-value based on the granularity of 12.5 GHz
+            double nbrMcSlots = Math.ceil(input.getMcWidth().getValue().doubleValue() / MC_WIDTH_GRAN);
+            LOG.debug("Nearest (ceil) number of slots {}", nbrMcSlots);
+            mcWidth = new BigDecimal(MC_WIDTH_GRAN * nbrMcSlots);
+            LOG.debug("Given mc-width={}, Rounded mc-width={}", input.getMcWidth().getValue(), mcWidth);
+
+            BigDecimal logVal = mcWidth.divide(new BigDecimal(50));
+            double pdsVal = 10 * Math.log10(logVal.doubleValue());
+            // Addition of PSD value will give Pin[87.5 GHz]
+            powerValue = powerValue.add(new BigDecimal(pdsVal, new MathContext(3, RoundingMode.HALF_EVEN)));
         }
-        // FIXME compliancy with OpenROADM MSA and approximations used
+        // FIXME compliancy with OpenROADM MSA and approximations used -- should be addressed with powermask update
         // cf JIRA ticket https://jira.opendaylight.org/browse/TRNSPRTPCE-494
-        LOG.info("Power Value is {}", powerValue);
+        powerValue = powerValue.setScale(2, RoundingMode.CEILING);
+        // target-output-power yang precision is 2, so we limit here to 2
+        LOG.info("The power value is P1[{}GHz]={} dB for spanloss {}", mcWidth, powerValue, spanLossTx);
         return powerValue;
     }
 
@@ -390,12 +448,12 @@ public class PowerMgmtImpl implements PowerMgmt {
                     input.getNodes().get(i).getSrcTp(), destTpId, spectralSlotName);
             try {
                 if (destTpId.toUpperCase(Locale.getDefault()).contains("DEG")) {
-                    if (!crossConnect.setPowerLevel(nodeId, OpticalControlMode.Power.getName(), new BigDecimal(-60),
-                            connectionNumber)) {
+                    if (!crossConnect.setPowerLevel(nodeId, OpticalControlMode.Power.getName(),
+                            Decimal64.valueOf("-60"), connectionNumber)) {
                         LOG.warn("Power down failed for Roadm-connection: {}", connectionNumber);
                         return false;
                     }
-                    Thread.sleep(OlmUtils.OLM_TIMER_2);
+                    Thread.sleep(timer2);
                     if (!crossConnect.setPowerLevel(nodeId, OpticalControlMode.Off.getName(), null, connectionNumber)) {
                         LOG.warn("Setting power-control mode off failed for Roadm-connection: {}", connectionNumber);
                         return false;