change roadm app to support EDFA/ROADM/OPS devices, add OPS PowerConfig/LambdaQuery behaviour

Change-Id: Ieb6de727e766fdeb63740c0704f83fd11e44b935
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmFlowViewMessageHandler.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmFlowViewMessageHandler.java
index 08e74f0..d0012d6 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmFlowViewMessageHandler.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmFlowViewMessageHandler.java
@@ -19,8 +19,11 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Range;
 import org.onlab.osgi.ServiceDirectory;
+import org.onlab.util.Frequency;
+import org.onlab.util.Spectrum;
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.FlowEntry;
@@ -42,6 +45,7 @@
 
 import static org.onosproject.ui.JsonUtils.node;
 import static org.onosproject.ui.JsonUtils.number;
+import static org.onosproject.net.Device.Type;
 
 /**
  * Table-View message handler for ROADM flow view.
@@ -60,9 +64,8 @@
     private static final String ROADM_CREATE_FLOW_REQ = "roadmCreateFlowRequest";
     private static final String ROADM_CREATE_FLOW_RESP = "roadmCreateFlowResponse";
 
-    private static final String NO_ROWS_MESSAGE = "No items found";
-
-    private static final String DEV_ID = "devId";
+    private static final String ROADM_SHOW_ITEMS_REQ = "roadmShowFlowItemsRequest";
+    private static final String ROADM_SHOW_ITEMS_RESP = "roadmShowFlowItemsResponse";
 
     private static final String ID = "id";
     private static final String FLOW_ID = "flowId";
@@ -80,28 +83,26 @@
     private static final String CURRENT_POWER = "currentPower";
     private static final String ATTENUATION = "attenuation";
     private static final String HAS_ATTENUATION = "hasAttenuation";
+    private static final String CHANNEL_FREQUENCY = "channelFrequency";
 
     private static final String[] COLUMN_IDS = {
             ID, FLOW_ID, APP_ID, GROUP_ID, TABLE_ID, PRIORITY, TIMEOUT,
-            PERMANENT, STATE, IN_PORT, OUT_PORT, CHANNEL_SPACING,
-            CHANNEL_MULTIPLIER, CURRENT_POWER, ATTENUATION, HAS_ATTENUATION
+            PERMANENT, STATE, IN_PORT, OUT_PORT, CHANNEL_SPACING, CHANNEL_MULTIPLIER,
+            CHANNEL_FREQUENCY, CURRENT_POWER, ATTENUATION, HAS_ATTENUATION
     };
 
-    private static final String NA = "N/A";
-    private static final String UNKNOWN = "Unknown";
-
-    private static final long GHZ = 1_000_000_000L;
-
-    private FlowRuleService flowRuleService;
     private RoadmService roadmService;
+    private DeviceService deviceService;
+    private FlowRuleService flowRuleService;
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     @Override
     public void init(UiConnection connection, ServiceDirectory directory) {
         super.init(connection, directory);
-        flowRuleService = get(FlowRuleService.class);
         roadmService = get(RoadmService.class);
+        deviceService = get(DeviceService.class);
+        flowRuleService = get(FlowRuleService.class);
     }
 
     @Override
@@ -110,7 +111,8 @@
                 new FlowTableDataRequestHandler(),
                 new SetAttenuationRequestHandler(),
                 new DeleteConnectionRequestHandler(),
-                new CreateConnectionRequestHandler()
+                new CreateConnectionRequestHandler(),
+                new CreateShowItemsRequestHandler()
         );
     }
 
@@ -128,7 +130,7 @@
 
         @Override
         protected String noRowsMessage(ObjectNode payload) {
-            return NO_ROWS_MESSAGE;
+            return RoadmUtil.NO_ROWS_MESSAGE;
         }
 
         @Override
@@ -140,8 +142,8 @@
 
         @Override
         protected void populateTable(TableModel tm, ObjectNode payload) {
-            DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
-
+            DeviceId deviceId = DeviceId.deviceId(string(payload, RoadmUtil.DEV_ID));
+            // Update flows
             Iterable<FlowEntry> flowEntries = flowRuleService.getFlowEntries(deviceId);
             for (FlowEntry flowEntry : flowEntries) {
                 populateRow(tm.addRow(), flowEntry, deviceId);
@@ -150,6 +152,17 @@
 
         private void populateRow(TableModel.Row row, FlowEntry entry, DeviceId deviceId) {
             ChannelData cd = ChannelData.fromFlow(entry);
+            String spacing = RoadmUtil.NA, multiplier = RoadmUtil.NA, channelFrequency = "";
+            OchSignal ochSignal = cd.ochSignal();
+            if (ochSignal != null) {
+                Frequency spacingFreq = ochSignal.channelSpacing().frequency();
+                spacing = RoadmUtil.asGHz(spacingFreq);
+                int spacingMult = ochSignal.spacingMultiplier();
+                multiplier = String.valueOf(spacingMult);
+                channelFrequency = String.format(" (%sGHz)",
+                        RoadmUtil.asGHz(Spectrum.CENTER_FREQUENCY.add(spacingFreq.multiply(spacingMult))));
+            }
+
             row.cell(ID, entry.id().value())
                     .cell(FLOW_ID, entry.id().value())
                     .cell(APP_ID, entry.appId())
@@ -159,52 +172,52 @@
                     .cell(STATE, entry.state().toString())
                     .cell(IN_PORT, cd.inPort().toLong())
                     .cell(OUT_PORT, cd.outPort().toLong())
-                    .cell(CHANNEL_SPACING, cd.ochSignal().channelSpacing().frequency().asHz() / GHZ)
-                    .cell(CHANNEL_MULTIPLIER, cd.ochSignal().spacingMultiplier())
+                    .cell(CHANNEL_SPACING, spacing)
+                    .cell(CHANNEL_MULTIPLIER, multiplier)
+                    .cell(CHANNEL_FREQUENCY, channelFrequency)
                     .cell(CURRENT_POWER, getCurrentPower(deviceId, cd))
+                    .cell(HAS_ATTENUATION, hasAttenuation(deviceId, cd))
                     .cell(ATTENUATION, getAttenuation(deviceId, cd));
         }
 
         private String getCurrentPower(DeviceId deviceId, ChannelData channelData) {
-            Range<Long> range =
-                    roadmService.attenuationRange(deviceId,
-                                                  channelData.outPort(),
-                                                  channelData.ochSignal());
-            if (range != null) {
-                Long currentPower =
-                        roadmService.getCurrentChannelPower(deviceId,
-                                                            channelData.outPort(),
-                                                            channelData.ochSignal());
-                if (currentPower != null) {
-                    return String.valueOf(currentPower);
-                }
+            if (hasAttenuation(deviceId, channelData)) {
+                // report channel power if channel exists
+                Long currentPower = roadmService.getCurrentChannelPower(deviceId,
+                        channelData.outPort(), channelData.ochSignal());
+                return RoadmUtil.objectToString(currentPower, RoadmUtil.UNKNOWN);
             }
-            return NA;
+            // otherwise, report port power
+            Type devType = deviceService.getDevice(deviceId).type();
+            PortNumber port = devType == Type.FIBER_SWITCH ? channelData.inPort() : channelData.outPort();
+            Long currentPower = roadmService.getCurrentPortPower(deviceId, port);
+            return RoadmUtil.objectToString(currentPower, RoadmUtil.UNKNOWN);
         }
 
         private String getAttenuation(DeviceId deviceId, ChannelData channelData) {
-            Long attenuation =
-                    roadmService.getAttenuation(deviceId, channelData.outPort(),
-                                                channelData.ochSignal());
-            if (attenuation != null) {
-                return String.valueOf(attenuation);
+            OchSignal signal = channelData.ochSignal();
+            if (signal == null) {
+                return RoadmUtil.NA;
             }
-            return UNKNOWN;
+            Long attenuation = roadmService.getAttenuation(deviceId, channelData.outPort(), signal);
+            return RoadmUtil.objectToString(attenuation, RoadmUtil.UNKNOWN);
+        }
+
+        private boolean hasAttenuation(DeviceId deviceId, ChannelData channelData) {
+            OchSignal signal = channelData.ochSignal();
+            if (signal == null) {
+                return false;
+            }
+            return roadmService.attenuationRange(deviceId, channelData.outPort(), signal) != null;
         }
     }
 
     // Handler for setting attenuation
     private final class SetAttenuationRequestHandler extends RequestHandler {
 
-        // Keys for response message
-        private static final String VALID = "valid";
-        private static final String MESSAGE = "message";
-
         // Error messages to display to user
-        private static final String ATTENUATION_RANGE_MSG =
-                "Attenuation must be in range %s.";
-        private static final String NO_ATTENUATION_MSG =
-                "Cannot set attenuation for this connection";
+        private static final String ATTENUATION_RANGE_MSG = "Attenuation must be in range %s.";
+        private static final String NO_ATTENUATION_MSG = "Cannot set attenuation for this connection";
 
         private SetAttenuationRequestHandler() {
             super(ROADM_SET_ATTENUATION_REQ);
@@ -212,36 +225,31 @@
 
         @Override
         public void process(ObjectNode payload) {
-            DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
+            DeviceId deviceId = DeviceId.deviceId(string(payload, RoadmUtil.DEV_ID));
             FlowId flowId = FlowId.valueOf(number(payload, FLOW_ID));
-            long attenuation = payload.get(ATTENUATION).asLong();
-
             // Get connection information from the flow
             FlowEntry entry = findFlow(deviceId, flowId);
             if (entry == null) {
-                log.error("Unable to find flow rule to set attenuation");
+                log.error("Unable to find flow rule to set attenuation for device {}", deviceId);
                 return;
             }
-            ChannelData cd = ChannelData.fromFlow(entry);
-            Range<Long> range =
-                    roadmService.attenuationRange(deviceId, cd.outPort(),
-                                                  cd.ochSignal());
-
-            boolean validAttenuation = (range != null && range.contains(attenuation));
+            ChannelData channelData = ChannelData.fromFlow(entry);
+            PortNumber port = channelData.outPort();
+            OchSignal signal = channelData.ochSignal();
+            Range<Long> range = roadmService.attenuationRange(deviceId, port, signal);
+            Long attenuation = payload.get(ATTENUATION).asLong();
+            boolean validAttenuation = range != null && range.contains(attenuation);
             if (validAttenuation) {
-                roadmService.setAttenuation(deviceId, cd.outPort(),
-                                            cd.ochSignal(), attenuation);
+                roadmService.setAttenuation(deviceId, port, signal, attenuation);
             }
-
             ObjectNode rootNode = objectNode();
             // Send back flowId so view can identify which callback function to use
             rootNode.put(FLOW_ID, payload.get(FLOW_ID).asText());
-            rootNode.put(VALID, validAttenuation);
-            if (range != null) {
-                rootNode.put(MESSAGE, String.format(ATTENUATION_RANGE_MSG,
-                                                    range.toString()));
+            rootNode.put(RoadmUtil.VALID, validAttenuation);
+            if (range  == null) {
+                rootNode.put(RoadmUtil.MESSAGE, NO_ATTENUATION_MSG);
             } else {
-                rootNode.put(MESSAGE, NO_ATTENUATION_MSG);
+                rootNode.put(RoadmUtil.MESSAGE, String.format(ATTENUATION_RANGE_MSG, range.toString()));
             }
             sendMessage(ROADM_SET_ATTENUATION_RESP, rootNode);
         }
@@ -264,7 +272,7 @@
 
         @Override
         public void process(ObjectNode payload) {
-            DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
+            DeviceId deviceId = DeviceId.deviceId(string(payload, RoadmUtil.DEV_ID));
             FlowId flowId = FlowId.valueOf(payload.get(ID).asLong());
             roadmService.removeConnection(deviceId, flowId);
         }
@@ -276,7 +284,6 @@
         // Keys to load from JSON
         private static final String FORM_DATA = "formData";
         private static final String CHANNEL_SPACING_INDEX = "index";
-        private static final String INCLUDE_ATTENUATION = "includeAttenuation";
 
         // Keys for validation results
         private static final String CONNECTION = "connection";
@@ -285,20 +292,11 @@
         // Error messages to display to user
         private static final String IN_PORT_ERR_MSG = "Invalid input port.";
         private static final String OUT_PORT_ERR_MSG = "Invalid output port.";
-        private static final String CONNECTION_ERR_MSG =
-                "Invalid connection from input port to output port.";
-        private static final String CHANNEL_SPACING_ERR_MSG =
-                "Channel spacing not supported.";
-        private static final String CHANNEL_ERR_MSG =
-                "Channel index must be in range %s.";
-        private static final String CHANNEL_AVAILABLE_ERR_MSG =
-                "Channel is already being used.";
-        private static final String ATTENUATION_ERR_MSG =
-                "Attenuation must be in range %s.";
-
-        // Keys for validation object
-        private static final String VALID = "valid";
-        private static final String MESSAGE = "message";
+        private static final String CONNECTION_ERR_MSG = "Invalid connection from input port to output port.";
+        private static final String CHANNEL_SPACING_ERR_MSG = "Channel spacing not supported.";
+        private static final String CHANNEL_ERR_MSG = "Channel index must be in range %s.";
+        private static final String CHANNEL_AVAILABLE_ERR_MSG = "Channel is already being used.";
+        private static final String ATTENUATION_ERR_MSG = "Attenuation must be in range %s.";
 
         private CreateConnectionRequestHandler() {
             super(ROADM_CREATE_FLOW_REQ);
@@ -306,7 +304,7 @@
 
         @Override
         public void process(ObjectNode payload) {
-            DeviceId did = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
+            DeviceId did = DeviceId.deviceId(string(payload, RoadmUtil.DEV_ID));
             ObjectNode flowNode = node(payload, FORM_DATA);
             int priority = (int) number(flowNode, PRIORITY);
             boolean permanent = bool(flowNode, PERMANENT);
@@ -314,13 +312,12 @@
             PortNumber inPort = PortNumber.portNumber(number(flowNode, IN_PORT));
             PortNumber outPort = PortNumber.portNumber(number(flowNode, OUT_PORT));
             ObjectNode chNode = node(flowNode, CHANNEL_SPACING);
-            ChannelSpacing spacing =
-                    channelSpacing((int) number(chNode, CHANNEL_SPACING_INDEX));
+            ChannelSpacing spacing = channelSpacing((int) number(chNode, CHANNEL_SPACING_INDEX));
             int multiplier = (int) number(flowNode, CHANNEL_MULTIPLIER);
             OchSignal och = OchSignal.newDwdmSlot(spacing, multiplier);
-            boolean includeAttenuation = bool(flowNode, INCLUDE_ATTENUATION);
             long att = number(flowNode, ATTENUATION);
 
+            boolean showItems = deviceService.getDevice(did).type() != Type.FIBER_SWITCH;
             boolean validInPort = roadmService.validInputPort(did, inPort);
             boolean validOutPort = roadmService.validOutputPort(did, outPort);
             boolean validConnect = roadmService.validConnection(did, inPort, outPort);
@@ -329,59 +326,51 @@
             boolean channelAvailable = roadmService.channelAvailable(did, och);
             boolean validAttenuation = roadmService.attenuationInRange(did, outPort, att);
 
-            if (validConnect && validChannel && channelAvailable) {
-                if (includeAttenuation && validAttenuation) {
-                    roadmService.createConnection(did, priority, permanent,
-                                                  timeout, inPort, outPort,
-                                                  och, att);
-                } else if (!includeAttenuation) {
-                    roadmService.createConnection(did, priority, permanent,
-                                                  timeout, inPort, outPort,
-                                                  och);
-                }
-            }
-
-            // Construct error for channel
-            String channelMessage = "Invalid channel";
-            if (!validChannel) {
-                Set<OchSignal> lambdas = roadmService.queryLambdas(did, outPort);
-                if (lambdas != null) {
-                    Range<Integer> range = channelRange(lambdas);
-                    if (range.contains(och.spacingMultiplier())) {
-                        // Channel spacing error
-                        validSpacing = false;
+            if (validConnect) {
+                if (validChannel && channelAvailable) {
+                    if (validAttenuation) {
+                        roadmService.createConnection(did, priority, permanent, timeout, inPort, outPort, och, att);
                     } else {
-                        channelMessage = String.format(CHANNEL_ERR_MSG, range.toString());
+                        roadmService.createConnection(did, priority, permanent, timeout, inPort, outPort, och);
                     }
                 }
             }
 
-            // Construct error for attenuation
+            String channelMessage = "Invalid channel";
             String attenuationMessage = "Invalid attenuation";
-            if (!validAttenuation) {
-                Range<Long> range =
-                        roadmService.attenuationRange(did, outPort, och);
-                if (range != null) {
-                    attenuationMessage =
-                            String.format(ATTENUATION_ERR_MSG, range.toString());
+            if (showItems) {
+                // Construct error for channel
+                if (!validChannel) {
+                    Set<OchSignal> lambdas = roadmService.queryLambdas(did, outPort);
+                    if (lambdas != null) {
+                        Range<Integer> range = channelRange(lambdas);
+                        if (range.contains(och.spacingMultiplier())) {
+                            // Channel spacing error
+                            validSpacing = false;
+                        } else {
+                            channelMessage = String.format(CHANNEL_ERR_MSG, range.toString());
+                        }
+                    }
+                }
+
+                // Construct error for attenuation
+                if (!validAttenuation) {
+                    Range<Long> range = roadmService.attenuationRange(did, outPort, och);
+                    if (range != null) {
+                        attenuationMessage = String.format(ATTENUATION_ERR_MSG, range.toString());
+                    }
                 }
             }
 
             // Build response
             ObjectNode node = objectNode();
-
             node.set(IN_PORT, validationObject(validInPort, IN_PORT_ERR_MSG));
             node.set(OUT_PORT, validationObject(validOutPort, OUT_PORT_ERR_MSG));
             node.set(CONNECTION, validationObject(validConnect, CONNECTION_ERR_MSG));
-            node.set(CHANNEL_SPACING, validationObject(validChannel || validSpacing,
-                                                       CHANNEL_SPACING_ERR_MSG));
-            node.set(CHANNEL_MULTIPLIER, validationObject(validChannel || !validSpacing,
-                                                          channelMessage));
-            node.set(CHANNEL_AVAILABLE, validationObject(!validChannel || channelAvailable,
-                                                         CHANNEL_AVAILABLE_ERR_MSG));
+            node.set(CHANNEL_SPACING, validationObject(validChannel || validSpacing, CHANNEL_SPACING_ERR_MSG));
+            node.set(CHANNEL_MULTIPLIER, validationObject(validChannel || !validSpacing, channelMessage));
+            node.set(CHANNEL_AVAILABLE, validationObject(!validChannel || channelAvailable, CHANNEL_AVAILABLE_ERR_MSG));
             node.set(ATTENUATION, validationObject(validAttenuation, attenuationMessage));
-            node.put(INCLUDE_ATTENUATION, includeAttenuation);
-
             sendMessage(ROADM_CREATE_FLOW_RESP, node);
         }
 
@@ -401,10 +390,10 @@
         // Construct validation object to return to the view
         private ObjectNode validationObject(boolean result, String message) {
             ObjectNode node = objectNode();
-            node.put(VALID, result);
+            node.put(RoadmUtil.VALID, result);
             if (!result) {
                 // return error message to display if validation failed
-                node.put(MESSAGE, message);
+                node.put(RoadmUtil.MESSAGE, message);
             }
             return node;
         }
@@ -418,4 +407,23 @@
             return Range.closed(minOch.spacingMultiplier(), maxOch.spacingMultiplier());
         }
     }
+
+    private final class CreateShowItemsRequestHandler extends RequestHandler {
+        private static final String SHOW_CHANNEL = "showChannel";
+        private static final String SHOW_ATTENUATION = "showAttenuation";
+        private CreateShowItemsRequestHandler() {
+            super(ROADM_SHOW_ITEMS_REQ);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            DeviceId did = DeviceId.deviceId(string(payload, RoadmUtil.DEV_ID));
+            Type devType = deviceService.getDevice(did).type();
+            // Build response
+            ObjectNode node = objectNode();
+            node.put(SHOW_CHANNEL, devType != Type.FIBER_SWITCH);
+            node.put(SHOW_ATTENUATION, devType == Type.ROADM);
+            sendMessage(ROADM_SHOW_ITEMS_RESP, node);
+        }
+    }
 }