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/ChannelData.java b/apps/roadm/src/main/java/org/onosproject/roadm/ChannelData.java
index 07c6c41..7011f1d8 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/ChannelData.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/ChannelData.java
@@ -58,8 +58,7 @@
         PortNumber inPort = ((PortCriterion) in).port();
 
         Criterion och = rule.selector().getCriterion(Criterion.Type.OCH_SIGID);
-        checkNotNull(och);
-        OchSignal ochSignal = ((OchSignalCriterion) och).lambda();
+        OchSignal ochSignal = och == null ? null : ((OchSignalCriterion) och).lambda();
 
         PortNumber outPort = null;
         List<Instruction> instructions = rule.treatment().allInstructions();
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmDeviceViewMessageHandler.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmDeviceViewMessageHandler.java
index d2a6fe2..6577426 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmDeviceViewMessageHandler.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmDeviceViewMessageHandler.java
@@ -17,18 +17,21 @@
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableSet;
+import org.onlab.osgi.ServiceDirectory;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiConnection;
 import org.onosproject.ui.UiMessageHandler;
 import org.onosproject.ui.table.TableModel;
 import org.onosproject.ui.table.TableRequestHandler;
 
 import java.util.Collection;
 
-import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.net.Device.Type;
 
 /**
  * Table-View message handler for ROADM device view.
@@ -39,10 +42,9 @@
     private static final String ROADM_DEVICE_DATA_RESP = "roadmDeviceDataResponse";
     private static final String ROADM_DEVICES = "roadmDevices";
 
-    private static final String NO_ROWS_MESSAGE = "No items found";
-
     private static final String ID = "id";
-    private static final String FRIENDLY_NAME = "name";
+    private static final String NAME = "name";
+    private static final String TYPE = "type";
     private static final String MASTER = "master";
     private static final String PORTS = "ports";
     private static final String VENDOR = "vendor";
@@ -51,27 +53,22 @@
     private static final String PROTOCOL = "protocol";
 
     private static final String[] COLUMN_IDS = {
-            ID, FRIENDLY_NAME, MASTER, PORTS, VENDOR, HW_VERSION, SW_VERSION,
-            PROTOCOL
+            ID, NAME, TYPE, MASTER, PORTS, VENDOR, HW_VERSION, SW_VERSION, PROTOCOL
     };
 
+    private DeviceService deviceService;
+    private MastershipService mastershipService;
+
+    @Override
+    public void init(UiConnection connection, ServiceDirectory directory) {
+        super.init(connection, directory);
+        deviceService = get(DeviceService.class);
+        mastershipService = get(MastershipService.class);
+    }
+
     @Override
     protected Collection<RequestHandler> createRequestHandlers() {
-        return ImmutableSet.of(
-                new DeviceTableDataRequestHandler()
-        );
-    }
-
-    // Returns friendly name of the device from the annotations
-    private static String deviceName(Device device) {
-        String name = device.annotations().value(AnnotationKeys.NAME);
-        return isNullOrEmpty(name) ? device.id().toString() : name;
-    }
-
-    // Returns the device protocol from annotations
-    private static String deviceProtocol(Device device) {
-        String protocol = device.annotations().value(PROTOCOL);
-        return protocol != null ? protocol : "N/A";
+        return ImmutableSet.of(new DeviceTableDataRequestHandler());
     }
 
     // Handler for sample table requests
@@ -88,28 +85,31 @@
 
         @Override
         protected String noRowsMessage(ObjectNode payload) {
-            return NO_ROWS_MESSAGE;
+            return RoadmUtil.NO_ROWS_MESSAGE;
         }
 
         @Override
         protected void populateTable(TableModel tm, ObjectNode payload) {
-            DeviceService ds = get(DeviceService.class);
-            MastershipService ms = get(MastershipService.class);
-            for (Device device : ds.getDevices(Device.Type.ROADM)) {
-                populateRow(tm.addRow(), device, ds, ms);
+            for (Device device : deviceService.getDevices()) {
+                Type type = device.type();
+                if (type == Type.ROADM || type == Type.OPTICAL_AMPLIFIER || type == Type.FIBER_SWITCH) {
+                    populateRow(tm.addRow(), device);
+                }
             }
         }
 
-        private void populateRow(TableModel.Row row, Device device, DeviceService ds,
-                MastershipService ms) {
-            row.cell(ID, device.id().toString())
-                    .cell(FRIENDLY_NAME, deviceName(device))
-                    .cell(MASTER, ms.getMasterFor(device.id()))
-                    .cell(PORTS, ds.getPorts(device.id()).size())
+        private void populateRow(TableModel.Row row, Device device) {
+            DeviceId devId = device.id();
+            String id = devId.toString();
+            row.cell(ID, id)
+                    .cell(NAME, RoadmUtil.getAnnotation(device.annotations(), AnnotationKeys.PORT_NAME, id))
+                    .cell(TYPE, RoadmUtil.objectToString(device.type(), RoadmUtil.UNKNOWN))
+                    .cell(MASTER, mastershipService.getMasterFor(devId))
+                    .cell(PORTS, deviceService.getPorts(devId).size())
                     .cell(VENDOR, device.manufacturer())
                     .cell(HW_VERSION, device.hwVersion())
                     .cell(SW_VERSION, device.swVersion())
-                    .cell(PROTOCOL, deviceProtocol(device));
+                    .cell(PROTOCOL, RoadmUtil.getAnnotation(device.annotations(), PROTOCOL));
         }
     }
 }
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);
+        }
+    }
 }
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmManager.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmManager.java
index 011c393..a0cace9 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmManager.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmManager.java
@@ -25,6 +25,7 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Direction;
@@ -34,6 +35,9 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.LambdaQuery;
 import org.onosproject.net.behaviour.PowerConfig;
+import org.onosproject.net.behaviour.protection.ProtectionConfigBehaviour;
+import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointState;
+import org.onosproject.net.behaviour.protection.TransportEndpointState;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
@@ -48,15 +52,21 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.optical.OpticalAnnotations;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
+
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
@@ -101,94 +111,46 @@
         log.info("Stopped");
     }
 
-    private PowerConfig<Object> getPowerConfig(DeviceId deviceId) {
-        Device device = deviceService.getDevice(deviceId);
-        if (device != null && device.is(PowerConfig.class)) {
-            return device.as(PowerConfig.class);
-        }
-        log.warn("Unable to load PowerConfig for {}", deviceId);
-        return null;
-    }
-
-    private LambdaQuery getLambdaQuery(DeviceId deviceId) {
-        Device device = deviceService.getDevice(deviceId);
-        if (device != null && device.is(LambdaQuery.class)) {
-            return device.as(LambdaQuery.class);
-        }
-        return null;
-    }
-
-    private void initDevices() {
-        for (Device device : deviceService.getDevices(Device.Type.ROADM)) {
-            initDevice(device.id());
-            setAllInitialTargetPortPowers(device.id());
-        }
-    }
-
-    // Initialize RoadmStore for a device to support target power
-    private void initDevice(DeviceId deviceId) {
-        if (!roadmStore.deviceAvailable(deviceId)) {
-            roadmStore.addDevice(deviceId);
-        }
-        log.info("Initialized device {}", deviceId);
-    }
-
-    // Sets the target port powers for a port on a device
-    // Attempts to read target powers from store. If no value is found then
-    // default value is used instead.
-    private void setInitialTargetPortPower(DeviceId deviceId, PortNumber portNumber) {
-        PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
-        if (powerConfig == null) {
-            log.warn("Unable to set default initial powers for port {} on device {}",
-                     portNumber, deviceId);
+    @Override
+    public void setProtectionSwitchWorkingPath(DeviceId deviceId, int index) {
+        checkNotNull(deviceId);
+        ProtectionConfigBehaviour behaviour = getProtectionConfig(deviceId);
+        if (behaviour == null) {
             return;
         }
-
-        Optional<Range<Long>> range =
-                powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
-        if (!range.isPresent()) {
-            log.warn("No target power range found for port {} on device {}",
-                     portNumber, deviceId);
+        Map<ConnectPoint, ProtectedTransportEndpointState> map = getProtectionSwitchStates(behaviour);
+        if (map == null) {
+            log.warn("Failed to get protected transport endpoint state in device {}", deviceId);
             return;
         }
-
-        Long power = roadmStore.getTargetPower(deviceId, portNumber);
-        if (power == null) {
-            // Set default to middle of the range
-            power = (range.get().lowerEndpoint() + range.get().upperEndpoint()) / 2;
-            roadmStore.setTargetPower(deviceId, portNumber, power);
+        if (map.isEmpty()) {
+            log.warn("No protected transport endpoint state found in device {}", deviceId);
+            return;
         }
-        powerConfig.setTargetPower(portNumber, Direction.ALL, power);
+        behaviour.switchWorkingPath(map.keySet().toArray(new ConnectPoint[0])[0], index);
     }
 
-    // Sets the target port powers for each each port on a device
-    // Attempts to read target powers from store. If no value is found then
-    // default value is used instead
-    private void setAllInitialTargetPortPowers(DeviceId deviceId) {
-        PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
-        if (powerConfig == null) {
-            log.warn("Unable to set default initial powers for device {}",
-                     deviceId);
-            return;
+    @Override
+    public String getProtectionSwitchPortState(DeviceId deviceId, PortNumber portNumber) {
+        checkNotNull(deviceId);
+        ProtectionConfigBehaviour behaviour = getProtectionConfig(deviceId);
+        if (behaviour == null) {
+            return null;
         }
-
-        List<Port> ports = deviceService.getPorts(deviceId);
-        for (Port port : ports) {
-            Optional<Range<Long>> range =
-                    powerConfig.getTargetPowerRange(port.number(), Direction.ALL);
-            if (range.isPresent()) {
-                Long power = roadmStore.getTargetPower(deviceId, port.number());
-                if (power == null) {
-                    // Set default to middle of the range
-                    power = (range.get().lowerEndpoint() + range.get().upperEndpoint()) / 2;
-                    roadmStore.setTargetPower(deviceId, port.number(), power);
+        Map<ConnectPoint, ProtectedTransportEndpointState> map = getProtectionSwitchStates(behaviour);
+        if (map == null) {
+            log.warn("Failed to get protected transport endpoint state in device {}", deviceId);
+            return null;
+        }
+        for (ProtectedTransportEndpointState state : map.values()) {
+            for (TransportEndpointState element : state.pathStates()) {
+                if (element.description().output().connectPoint().port().equals(portNumber)) {
+                    return element.attributes().get(OpticalAnnotations.INPUT_PORT_STATUS);
                 }
-                powerConfig.setTargetPower(port.number(), Direction.ALL, power);
-            } else {
-                log.warn("No target power range found for port {} on device {}",
-                         port.number(), deviceId);
             }
         }
+        log.warn("Unable to get port status, device: {}, port: {}", deviceId, portNumber);
+        return null;
     }
 
     @Override
@@ -208,8 +170,7 @@
     public Long getTargetPortPower(DeviceId deviceId, PortNumber portNumber) {
         checkNotNull(deviceId);
         checkNotNull(portNumber);
-        // getTargetPortPower is not yet implemented in PowerConfig so we
-        // access store instead
+        // getTargetPortPower is not yet implemented in PowerConfig so we access store instead
         return roadmStore.getTargetPower(deviceId, portNumber);
     }
 
@@ -229,15 +190,13 @@
     }
 
     @Override
-    public Long getAttenuation(DeviceId deviceId, PortNumber portNumber,
-                               OchSignal ochSignal) {
+    public Long getAttenuation(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal) {
         checkNotNull(deviceId);
         checkNotNull(portNumber);
         checkNotNull(ochSignal);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Long> attenuation =
-                    powerConfig.getTargetPower(portNumber, ochSignal);
+            Optional<Long> attenuation = powerConfig.getTargetPower(portNumber, ochSignal);
             if (attenuation.isPresent()) {
                 return attenuation.get();
             }
@@ -251,8 +210,7 @@
         checkNotNull(portNumber);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Long> currentPower =
-                    powerConfig.currentPower(portNumber, Direction.ALL);
+            Optional<Long> currentPower = powerConfig.currentPower(portNumber, Direction.ALL);
             if (currentPower.isPresent()) {
                 return currentPower.get();
             }
@@ -261,15 +219,13 @@
     }
 
     @Override
-    public Long getCurrentChannelPower(DeviceId deviceId, PortNumber portNumber,
-                                       OchSignal ochSignal) {
+    public Long getCurrentChannelPower(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal) {
         checkNotNull(deviceId);
         checkNotNull(portNumber);
         checkNotNull(ochSignal);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Long> currentPower =
-                    powerConfig.currentPower(portNumber, ochSignal);
+            Optional<Long> currentPower = powerConfig.currentPower(portNumber, ochSignal);
             if (currentPower.isPresent()) {
                 return currentPower.get();
             }
@@ -290,31 +246,31 @@
 
     @Override
     public FlowId createConnection(DeviceId deviceId, int priority, boolean isPermanent,
-                                 int timeout, PortNumber inPort, PortNumber outPort,
-                                 OchSignal ochSignal) {
+                                 int timeout, PortNumber inPort, PortNumber outPort, OchSignal ochSignal) {
         checkNotNull(deviceId);
         checkNotNull(inPort);
         checkNotNull(outPort);
 
-        FlowRule.Builder flowBuilder = new DefaultFlowRule.Builder();
-        flowBuilder.fromApp(appId);
-        flowBuilder.withPriority(priority);
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .add(Criteria.matchInPort(inPort))
+                .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
+                .add(Criteria.matchLambda(ochSignal))
+                .build();
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .add(Instructions.createOutput(outPort))
+                .build();
+
+        FlowRule.Builder flowBuilder = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .fromApp(appId)
+                .withPriority(priority)
+                .withSelector(selector)
+                .withTreatment(treatment);
         if (isPermanent) {
             flowBuilder.makePermanent();
         } else {
             flowBuilder.makeTemporary(timeout);
         }
-        flowBuilder.forDevice(deviceId);
-
-        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
-        selectorBuilder.add(Criteria.matchInPort(inPort));
-        selectorBuilder.add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID));
-        selectorBuilder.add(Criteria.matchLambda(ochSignal));
-        flowBuilder.withSelector(selectorBuilder.build());
-
-        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
-        treatmentBuilder.add(Instructions.createOutput(outPort));
-        flowBuilder.withTreatment(treatmentBuilder.build());
 
         FlowRule flowRule = flowBuilder.build();
         flowRuleService.applyFlowRules(flowRule);
@@ -332,26 +288,11 @@
         checkNotNull(deviceId);
         checkNotNull(inPort);
         checkNotNull(outPort);
-        FlowId flowId = createConnection(deviceId, priority, isPermanent,
-                                         timeout, inPort, outPort, ochSignal);
+        FlowId flowId = createConnection(deviceId, priority, isPermanent, timeout, inPort, outPort, ochSignal);
         delayedSetAttenuation(deviceId, outPort, ochSignal, attenuation);
         return flowId;
     }
 
-    // Delay the call to setTargetPower because the flow may not be in the store yet
-    private void delayedSetAttenuation(DeviceId deviceId, PortNumber outPort,
-                                       OchSignal ochSignal, long attenuation) {
-        Runnable setAtt = () -> {
-            try {
-                TimeUnit.SECONDS.sleep(1);
-            } catch (InterruptedException e) {
-                log.warn("Thread interrupted. Setting attenuation early.");
-            }
-            setAttenuation(deviceId, outPort, ochSignal, attenuation);
-        };
-        new Thread(setAtt).start();
-    }
-
     @Override
     public void removeConnection(DeviceId deviceId, FlowId flowId) {
         checkNotNull(deviceId);
@@ -371,37 +312,32 @@
         checkNotNull(portNumber);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Range<Long>> range =
-                    powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
+            Optional<Range<Long>> range = powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
             return range.isPresent();
         }
         return false;
     }
 
     @Override
-    public boolean portTargetPowerInRange(DeviceId deviceId, PortNumber portNumber,
-                                          long power) {
+    public boolean portTargetPowerInRange(DeviceId deviceId, PortNumber portNumber, long power) {
         checkNotNull(deviceId);
         checkNotNull(portNumber);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Range<Long>> range =
-                    powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
+            Optional<Range<Long>> range = powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
             return range.isPresent() && range.get().contains(power);
         }
         return false;
     }
 
     @Override
-    public boolean attenuationInRange(DeviceId deviceId, PortNumber outPort,
-                                      long att) {
+    public boolean attenuationInRange(DeviceId deviceId, PortNumber outPort, long att) {
         checkNotNull(deviceId);
         checkNotNull(outPort);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
             OchSignal stubOch = OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, 0);
-            Optional<Range<Long>> range =
-                    powerConfig.getTargetPowerRange(outPort, stubOch);
+            Optional<Range<Long>> range = powerConfig.getTargetPowerRange(outPort, stubOch);
             return range.isPresent() && range.get().contains(att);
         }
         return false;
@@ -413,8 +349,7 @@
         checkNotNull(portNumber);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Range<Long>> range =
-                    powerConfig.getInputPowerRange(portNumber, Direction.ALL);
+            Optional<Range<Long>> range = powerConfig.getInputPowerRange(portNumber, Direction.ALL);
             return range.isPresent();
         }
         return false;
@@ -426,10 +361,10 @@
     }
 
     @Override
-    public boolean validChannel(DeviceId deviceId, PortNumber portNumber,
-                                OchSignal ochSignal) {
+    public boolean validChannel(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal) {
         checkNotNull(deviceId);
         checkNotNull(portNumber);
+        checkNotNull(ochSignal);
         LambdaQuery lambdaQuery = getLambdaQuery(deviceId);
         if (lambdaQuery != null) {
             Set<OchSignal> channels = lambdaQuery.queryLambdas(portNumber);
@@ -451,8 +386,7 @@
     }
 
     @Override
-    public boolean validConnection(DeviceId deviceId, PortNumber inPort,
-                                   PortNumber outPort) {
+    public boolean validConnection(DeviceId deviceId, PortNumber inPort, PortNumber outPort) {
         checkNotNull(deviceId);
         checkNotNull(inPort);
         checkNotNull(outPort);
@@ -465,8 +399,7 @@
         checkNotNull(portNumber);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Range<Long>> range =
-                    powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
+            Optional<Range<Long>> range = powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
             if (range.isPresent()) {
                 return range.get();
             }
@@ -475,15 +408,13 @@
     }
 
     @Override
-    public Range<Long> attenuationRange(DeviceId deviceId, PortNumber portNumber,
-                                        OchSignal ochSignal) {
+    public Range<Long> attenuationRange(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal) {
         checkNotNull(deviceId);
         checkNotNull(portNumber);
         checkNotNull(ochSignal);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Range<Long>> range =
-                    powerConfig.getTargetPowerRange(portNumber, ochSignal);
+            Optional<Range<Long>> range = powerConfig.getTargetPowerRange(portNumber, ochSignal);
             if (range.isPresent()) {
                 return range.get();
             }
@@ -497,8 +428,7 @@
         checkNotNull(portNumber);
         PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
         if (powerConfig != null) {
-            Optional<Range<Long>> range =
-                    powerConfig.getInputPowerRange(portNumber, Direction.ALL);
+            Optional<Range<Long>> range = powerConfig.getInputPowerRange(portNumber, Direction.ALL);
             if (range.isPresent()) {
                 return range.get();
             }
@@ -506,6 +436,137 @@
         return null;
     }
 
+    private PowerConfig<Object> getPowerConfig(DeviceId deviceId) {
+        Device device = deviceService.getDevice(deviceId);
+        if (device != null && device.is(PowerConfig.class)) {
+            return device.as(PowerConfig.class);
+        }
+        log.warn("Unable to load PowerConfig for {}", deviceId);
+        return null;
+    }
+
+    private LambdaQuery getLambdaQuery(DeviceId deviceId) {
+        Device device = deviceService.getDevice(deviceId);
+        if (device != null && device.is(LambdaQuery.class)) {
+            return device.as(LambdaQuery.class);
+        }
+        log.warn("Unable to load LambdaQuery for {}", deviceId);
+        return null;
+    }
+
+    private ProtectionConfigBehaviour getProtectionConfig(DeviceId deviceId) {
+        Device device = deviceService.getDevice(deviceId);
+        if (device != null && device.is(ProtectionConfigBehaviour.class)) {
+            return device.as(ProtectionConfigBehaviour.class);
+        }
+        log.warn("Unable to load ProtectionConfigBehaviour for {}", deviceId);
+        return null;
+    }
+
+    // Initialize all devices
+    private void initDevices() {
+        for (Device device : deviceService.getDevices(Device.Type.ROADM)) {
+            initDevice(device.id());
+            //FIXME
+            // As roadm application is a optional tool for now.
+            // The target power initialization will be enhanced later,
+            // hopefully using an formal optical subsystem.
+            // setAllInitialTargetPortPowers(device.id());
+        }
+    }
+
+    // Initialize RoadmStore for a device to support target power
+    private void initDevice(DeviceId deviceId) {
+        if (!roadmStore.deviceAvailable(deviceId)) {
+            roadmStore.addDevice(deviceId);
+        }
+        log.info("Initialized device {}", deviceId);
+    }
+
+    // Sets the target port powers for a port on a device
+    // Attempts to read target powers from store. If no value is found then
+    // default value is used instead.
+    private void setInitialTargetPortPower(DeviceId deviceId, PortNumber portNumber) {
+        PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
+        if (powerConfig == null) {
+            log.warn("Unable to set default initial powers for port {} on device {}", portNumber, deviceId);
+            return;
+        }
+
+        Optional<Range<Long>> range = powerConfig.getTargetPowerRange(portNumber, Direction.ALL);
+        if (!range.isPresent()) {
+            log.warn("No target power range found for port {} on device {}", portNumber, deviceId);
+            return;
+        }
+
+        Long power = roadmStore.getTargetPower(deviceId, portNumber);
+        if (power == null) {
+            // Set default to middle of the range
+            power = (range.get().lowerEndpoint() + range.get().upperEndpoint()) / 2;
+            roadmStore.setTargetPower(deviceId, portNumber, power);
+        }
+        powerConfig.setTargetPower(portNumber, Direction.ALL, power);
+    }
+
+    // Sets the target port powers for each each port on a device
+    // Attempts to read target powers from store. If no value is found then
+    // default value is used instead
+    private void setAllInitialTargetPortPowers(DeviceId deviceId) {
+        PowerConfig<Object> powerConfig = getPowerConfig(deviceId);
+        if (powerConfig == null) {
+            log.warn("Unable to set default initial powers for device {}", deviceId);
+            return;
+        }
+
+        List<Port> ports = deviceService.getPorts(deviceId);
+        for (Port port : ports) {
+            Optional<Range<Long>> range = powerConfig.getTargetPowerRange(port.number(), Direction.ALL);
+            if (range.isPresent()) {
+                Long power = roadmStore.getTargetPower(deviceId, port.number());
+                if (power == null) {
+                    // Set default to middle of the range
+                    power = (range.get().lowerEndpoint() + range.get().upperEndpoint()) / 2;
+                    roadmStore.setTargetPower(deviceId, port.number(), power);
+                }
+                powerConfig.setTargetPower(port.number(), Direction.ALL, power);
+            } else {
+                log.warn("No target power range found for port {} on device {}", port.number(), deviceId);
+            }
+        }
+    }
+
+    // Delay the call to setTargetPower because the flow may not be in the store yet
+    private void delayedSetAttenuation(DeviceId deviceId, PortNumber outPort,
+                                       OchSignal ochSignal, long attenuation) {
+        Runnable setAtt = () -> {
+            try {
+                TimeUnit.SECONDS.sleep(1);
+            } catch (InterruptedException e) {
+                log.warn("Thread interrupted. Setting attenuation early.");
+            }
+            setAttenuation(deviceId, outPort, ochSignal, attenuation);
+        };
+        new Thread(setAtt).start();
+    }
+
+    // get protection endpoint states
+    private Map<ConnectPoint, ProtectedTransportEndpointState> getProtectionSwitchStates(
+            ProtectionConfigBehaviour behaviour) {
+        CompletableFuture<Map<ConnectPoint, ProtectedTransportEndpointState>>
+                states = behaviour.getProtectionEndpointStates();
+        Map<ConnectPoint, ProtectedTransportEndpointState> map;
+        try {
+            map = states.get();
+        } catch (InterruptedException e1) {
+            log.error("Interrupted.", e1);
+            return null;
+        } catch (ExecutionException e1) {
+            log.error("Exception caught.", e1);
+            return null;
+        }
+        return map;
+    }
+
     // Listens to device events.
     private class InternalDeviceListener implements DeviceListener {
         @Override
@@ -519,7 +580,11 @@
                     break;
                 case PORT_ADDED:
                 case PORT_UPDATED:
-                    setInitialTargetPortPower(device.id(), deviceEvent.port().number());
+                    //FIXME
+                    // As roadm application is a optional tool for now.
+                    // The target power initialization will be enhanced later,
+                    // hopefully using an formal optical subsystem.
+                    // setInitialTargetPortPower(device.id(), deviceEvent.port().number());
                     break;
                 default:
                     break;
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
index 81fb581..2aa80f7 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
@@ -19,12 +19,13 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Range;
 import org.onlab.osgi.ServiceDirectory;
+import org.onlab.util.Frequency;
 import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.optical.OpticalAnnotations;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.OchSignal;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiConnection;
 import org.onosproject.ui.UiMessageHandler;
@@ -34,7 +35,12 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
+import java.util.Set;
+
+import static org.onosproject.net.Device.Type;
 
 /**
  * Table-View message handler for ROADM port view.
@@ -44,17 +50,16 @@
     private static final String ROADM_PORT_DATA_REQ = "roadmPortDataRequest";
     private static final String ROADM_PORT_DATA_RESP = "roadmPortDataResponse";
     private static final String ROADM_PORTS = "roadmPorts";
-
     private static final String ROADM_SET_TARGET_POWER_REQ = "roadmSetTargetPowerRequest";
     private static final String ROADM_SET_TARGET_POWER_RESP = "roadmSetTargetPowerResponse";
-
-    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 = "roadmShowPortItemsRequest";
+    private static final String ROADM_SHOW_ITEMS_RESP = "roadmShowPortItemsResponse";
+    private static final String ROADM_SET_OPS_MODE_REQ = "roadmSetOpsModeRequest";
+    private static final String ROADM_SET_OPS_MODE_RESP = "roadmSetOpsModeResponse";
 
     private static final String ID = "id";
-    private static final String TYPE = "type";
     private static final String NAME = "name";
+    private static final String TYPE = "type";
     private static final String ENABLED = "enabled";
     private static final String MIN_FREQ = "minFreq";
     private static final String MAX_FREQ = "maxFreq";
@@ -63,53 +68,32 @@
     private static final String CURRENT_POWER = "currentPower";
     private static final String TARGET_POWER = "targetPower";
     private static final String HAS_TARGET_POWER = "hasTargetPower";
+    private static final String SERVICE_STATE = "serviceState";
 
     private static final String[] COLUMN_IDS = {
             ID, TYPE, NAME, ENABLED, MIN_FREQ, MAX_FREQ, GRID, INPUT_POWER_RANGE,
-            CURRENT_POWER, TARGET_POWER, HAS_TARGET_POWER,
+            CURRENT_POWER, SERVICE_STATE, TARGET_POWER, HAS_TARGET_POWER
     };
 
-    private static final String NA = "N/A";
-    private static final String UNKNOWN = "Unknown";
-
-    private static final long GHZ = 1_000_000_000L;
-    private static final long THZ = 1_000_000_000_000L;
-
-    private DeviceService deviceService;
     private RoadmService roadmService;
+    private DeviceService deviceService;
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     @Override
     public void init(UiConnection connection, ServiceDirectory directory) {
         super.init(connection, directory);
-        deviceService = get(DeviceService.class);
         roadmService = get(RoadmService.class);
+        deviceService = get(DeviceService.class);
     }
 
     @Override
     protected Collection<RequestHandler> createRequestHandlers() {
-        return ImmutableSet.of(
-                new PortTableDataRequestHandler(),
-                new SetTargetPowerRequestHandler()
-        );
-    }
-
-    private String asGHz(String value) {
-        return String.valueOf(Double.valueOf(value) / GHZ);
-    }
-
-    private String asTHz(String value) {
-        return String.valueOf(Double.valueOf(value) / THZ);
-    }
-
-    private String annotation(Port port, String key, String defaultValue) {
-        String value = port.annotations().value(key);
-        return value != null ? value : defaultValue;
-    }
-
-    private String annotation(Port port, String key) {
-        return annotation(port, key, NA);
+        return ImmutableSet.of(new PortTableDataRequestHandler(),
+                new SetTargetPowerRequestHandler(),
+                new CreateShowItemsRequestHandler(),
+                new CreateOpsModeSetRequestHandler()
+                );
     }
 
     // Handler for sample table requests
@@ -126,13 +110,12 @@
 
         @Override
         protected String noRowsMessage(ObjectNode payload) {
-            return NO_ROWS_MESSAGE;
+            return RoadmUtil.NO_ROWS_MESSAGE;
         }
 
         @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));
             if (deviceService.isAvailable(deviceId)) {
                 List<Port> ports = deviceService.getPorts(deviceId);
                 for (Port port : ports) {
@@ -142,53 +125,65 @@
         }
 
         private void populateRow(TableModel.Row row, Port port, DeviceId deviceId) {
-            row.cell(ID, port.number().toLong())
+            PortNumber portNum = port.number();
+            getFrequencyLimit(deviceId, portNum);
+            row.cell(ID, portNum.toLong())
                     .cell(TYPE, port.type())
                     .cell(ENABLED, port.isEnabled())
-                    .cell(NAME, annotation(port, AnnotationKeys.PORT_NAME))
-                    .cell(MIN_FREQ, asTHz(annotation(port, OpticalAnnotations.MIN_FREQ_HZ)))
-                    .cell(MAX_FREQ, asTHz(annotation(port, OpticalAnnotations.MAX_FREQ_HZ)))
-                    .cell(GRID, asGHz(annotation(port, OpticalAnnotations.GRID_HZ)))
-                    .cell(INPUT_POWER_RANGE, getInputPowerRange(deviceId, port.number()))
-                    .cell(CURRENT_POWER, getCurrentPower(deviceId, port.number()))
-                    .cell(TARGET_POWER, getTargetPower(deviceId, port.number()))
-                    .cell(HAS_TARGET_POWER, roadmService.hasPortTargetPower(deviceId, port.number()));
+                    .cell(NAME, RoadmUtil.getAnnotation(port.annotations(), AnnotationKeys.PORT_NAME))
+                    .cell(MIN_FREQ, RoadmUtil.asTHz(minFreq))
+                    .cell(MAX_FREQ, RoadmUtil.asTHz(maxFreq))
+                    .cell(GRID, RoadmUtil.asGHz(channelSpacing))
+                    .cell(INPUT_POWER_RANGE, getInputPowerRange(deviceId, portNum))
+                    .cell(CURRENT_POWER, getCurrentPower(deviceId, portNum))
+                    .cell(SERVICE_STATE, getPortServiceState(deviceId, portNum))
+                    .cell(TARGET_POWER, getTargetPower(deviceId, portNum))
+                    .cell(HAS_TARGET_POWER, roadmService.hasPortTargetPower(deviceId, portNum));
+        }
+
+        private String getPortServiceState(DeviceId deviceId, PortNumber portNumber) {
+            return RoadmUtil.defaultString(
+                    roadmService.getProtectionSwitchPortState(deviceId, portNumber),
+                    RoadmUtil.UNKNOWN);
+        }
+
+        private Frequency minFreq = null, maxFreq = null, channelSpacing = null;
+        // Gets min frequency, max frequency, channel spacing
+        private void getFrequencyLimit(DeviceId deviceId, PortNumber portNumber) {
+            Set<OchSignal> signals = roadmService.queryLambdas(deviceId, portNumber);
+            if (signals.isEmpty()) {
+                return;
+            }
+            Comparator<OchSignal> compare =
+                    (OchSignal a, OchSignal b) -> a.spacingMultiplier() - b.spacingMultiplier();
+            OchSignal minOch = Collections.min(signals, compare);
+            OchSignal maxOch = Collections.max(signals, compare);
+            minFreq = minOch.centralFrequency();
+            maxFreq = maxOch.centralFrequency();
+            channelSpacing = minOch.channelSpacing().frequency();
         }
 
         // Returns the input power range as a string, N/A if the port is not an
         // input port
         private String getInputPowerRange(DeviceId deviceId, PortNumber portNumber) {
-            Range<Long> range =
-                    roadmService.inputPortPowerRange(deviceId, portNumber);
-            if (range != null) {
-                return range.toString();
-            }
-            return NA;
+            Range<Long> range = roadmService.inputPortPowerRange(deviceId, portNumber);
+            return RoadmUtil.objectToString(range, RoadmUtil.NA);
         }
 
         // Returns the current power as a string, Unknown if no value can be found.
         private String getCurrentPower(DeviceId deviceId, PortNumber portNumber) {
-            Long currentPower =
-                    roadmService.getCurrentPortPower(deviceId, portNumber);
-            if (currentPower != null) {
-                return String.valueOf(currentPower);
-            }
-            return UNKNOWN;
+            Long currentPower = roadmService.getCurrentPortPower(deviceId, portNumber);
+            return RoadmUtil.objectToString(currentPower, RoadmUtil.UNKNOWN);
         }
 
         // Returns target power as a string, Unknown if target power is expected but
         // cannot be found, N/A if port does not have configurable target power
         private String getTargetPower(DeviceId deviceId, PortNumber portNumber) {
-            if (roadmService.hasPortTargetPower(deviceId, portNumber)) {
-                Long targetPower =
-                        roadmService.getTargetPortPower(deviceId, portNumber);
-                if (targetPower != null) {
-                    return String.valueOf(targetPower);
-                } else {
-                    return UNKNOWN;
-                }
+            if (!roadmService.hasPortTargetPower(deviceId, portNumber)) {
+                return RoadmUtil.NA;
             }
-            return NA;
+            Long targetPower = roadmService.getTargetPortPower(deviceId, portNumber);
+            return RoadmUtil.objectToString(targetPower, RoadmUtil.UNKNOWN);
         }
     }
 
@@ -196,9 +191,6 @@
     // Handler for setting port target power
     private final class SetTargetPowerRequestHandler extends RequestHandler {
 
-        private static final String VALID = "valid";
-        private static final String MESSAGE = "message";
-
         private static final String TARGET_POWER_ERR_MSG = "Target power range is %s.";
 
         private SetTargetPowerRequestHandler() {
@@ -207,29 +199,78 @@
 
         @Override
         public void process(ObjectNode payload) {
-            DeviceId deviceId = DeviceId.deviceId(string(payload, DEV_ID, "(none)"));
+            DeviceId deviceId = DeviceId.deviceId(string(payload, RoadmUtil.DEV_ID));
             PortNumber portNumber = PortNumber.portNumber(payload.get(ID).asLong());
-            long targetPower = payload.get(TARGET_POWER).asLong();
-            boolean validTargetPower;
-
-            Range<Long> range =
-                    roadmService.targetPortPowerRange(deviceId, portNumber);
-            if (range != null) {
-                validTargetPower = range.contains(targetPower);
-
-                if (validTargetPower) {
-                    roadmService.setTargetPortPower(deviceId, portNumber, targetPower);
-                }
-
-                ObjectNode rootNode = objectNode();
-                rootNode.put(ID, payload.get(ID).asText());
-                rootNode.put(VALID, validTargetPower);
-                rootNode.put(MESSAGE, String.format(TARGET_POWER_ERR_MSG, range.toString()));
-                sendMessage(ROADM_SET_TARGET_POWER_RESP, rootNode);
-
-            } else {
+            Range<Long> range = roadmService.targetPortPowerRange(deviceId, portNumber);
+            if (range == null) {
                 log.warn("Unable to determine target power range for device {}", deviceId);
+                return;
             }
+            Long targetPower = payload.get(TARGET_POWER).asLong();
+            boolean validTargetPower = range.contains(targetPower);
+            if (validTargetPower) {
+                roadmService.setTargetPortPower(deviceId, portNumber, targetPower);
+            }
+            ObjectNode rootNode = objectNode();
+            rootNode.put(ID, payload.get(ID).asText());
+            rootNode.put(RoadmUtil.VALID, validTargetPower);
+            rootNode.put(RoadmUtil.MESSAGE, String.format(TARGET_POWER_ERR_MSG, range.toString()));
+            sendMessage(ROADM_SET_TARGET_POWER_RESP, rootNode);
+        }
+    }
+
+    private final class CreateShowItemsRequestHandler extends RequestHandler {
+        private static final String SHOW_TARGET_POWER = "showTargetPower";
+        private static final String SHOW_SERVICE_STATE = "showServiceState";
+        private static final String SHOW_FLOW_ICON = "showFlowIcon";
+
+        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_TARGET_POWER, devType != Type.FIBER_SWITCH);
+            node.put(SHOW_SERVICE_STATE, devType == Type.FIBER_SWITCH);
+            node.put(SHOW_FLOW_ICON, devType == Type.ROADM);
+            sendMessage(ROADM_SHOW_ITEMS_RESP, node);
+        }
+    }
+
+    private final class CreateOpsModeSetRequestHandler extends RequestHandler {
+        private static final String OPS_SWITCH_INDEX = "index";
+        private static final String DEVICE_INVALID_ERR_MSG = "Apply failed: device is offline or unavailable.";
+        private static final String TYPE_INVALID_ERR_MSG = "Apply failed: invalid device type.";
+
+        private CreateOpsModeSetRequestHandler() {
+            super(ROADM_SET_OPS_MODE_REQ);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            DeviceId did = DeviceId.deviceId(string(payload, RoadmUtil.DEV_ID));
+            ObjectNode node = objectNode();
+            if (!deviceService.isAvailable(did)) {
+                node.put(RoadmUtil.VALID, false);
+                node.put(RoadmUtil.MESSAGE, DEVICE_INVALID_ERR_MSG);
+                sendMessage(ROADM_SET_OPS_MODE_RESP, node);
+                return;
+            }
+            Type devType = deviceService.getDevice(did).type();
+            if (devType != Type.FIBER_SWITCH) {
+                node.put(RoadmUtil.VALID, false);
+                node.put(RoadmUtil.MESSAGE, TYPE_INVALID_ERR_MSG);
+                sendMessage(ROADM_SET_OPS_MODE_RESP, node);
+                return;
+            }
+            // get virtual port and switch port from payload
+            roadmService.setProtectionSwitchWorkingPath(did, (int) number(payload, OPS_SWITCH_INDEX));
+            node.put(RoadmUtil.VALID, true);
+            sendMessage(ROADM_SET_OPS_MODE_RESP, node);
         }
     }
 }
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmService.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmService.java
index 19334ae..792484f 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmService.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmService.java
@@ -43,6 +43,23 @@
 public interface RoadmService {
 
     /**
+     * Attempts to manually switch working path to the one specified by {@code index}.
+     *
+     * @param deviceId DeviceId of the device to configure
+     * @param index working path index to switch to
+     */
+    void setProtectionSwitchWorkingPath(DeviceId deviceId, int index);
+
+    /**
+     * Retrieves protection switch specified port's service status.
+     *
+     * @param deviceId DeviceId of the device to configure
+     * @param portNumber the port
+     * @return port service status
+     */
+    String getProtectionSwitchPortState(DeviceId deviceId, PortNumber portNumber);
+
+    /**
      * Set target power for a port if the port has configurable target power.
      *
      * @param deviceId DeviceId of the device to configure
@@ -69,8 +86,7 @@
      * @param ochSignal channel to set attenuation for
      * @param attenuation attenuation value to set to
      */
-    void setAttenuation(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal,
-                        long attenuation);
+    void setAttenuation(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal, long attenuation);
 
     /**
      * Returns the attenuation of a connection.
@@ -99,8 +115,7 @@
      * @param ochSignal channel to search for
      * @return channel power if found, null otherwise
      */
-    Long getCurrentChannelPower(DeviceId deviceId, PortNumber portNumber,
-                                OchSignal ochSignal);
+    Long getCurrentChannelPower(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal);
 
     /**
      * Returns the channels supported by a port.
@@ -117,7 +132,7 @@
      * output port).
      *
      * Connections are represented as flows with an input port, output port, and
-     * channel. Implementation of attenuation is up to the vendor.
+     * channel.
      *
      * @param deviceId DeviceId of the device to create this connection for
      * @param priority priority of the flow
@@ -129,8 +144,7 @@
      * @return FlowId of the FlowRule representing the connection
      */
     FlowId createConnection(DeviceId deviceId, int priority, boolean isPermanent,
-                          int timeout, PortNumber inPort, PortNumber outPort,
-                          OchSignal ochSignal);
+                          int timeout, PortNumber inPort, PortNumber outPort, OchSignal ochSignal);
 
     /**
      * Creates a new internal connection on a device with attenuation. This does
@@ -269,8 +283,7 @@
      * @param ochSignal channel to check
      * @return range if found, null otherwise
      */
-    Range<Long> attenuationRange(DeviceId deviceId, PortNumber portNumber,
-                                 OchSignal ochSignal);
+    Range<Long> attenuationRange(DeviceId deviceId, PortNumber portNumber, OchSignal ochSignal);
 
     /**
      * Returns the expected input power range for an input port.
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmUtil.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmUtil.java
new file mode 100644
index 0000000..d125c77
--- /dev/null
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmUtil.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.roadm;
+
+import org.onlab.util.Frequency;
+import org.onosproject.net.Annotations;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+
+/**
+ * Roadm utilities.
+ */
+public final class RoadmUtil {
+
+    public static final String DEV_ID = "devId";
+    public static final String VALID = "valid";
+    public static final String MESSAGE = "message";
+    public static final String NA = "N/A";
+    public static final String UNKNOWN = "Unknown";
+    public static final String NONE = "(none)";
+    public static final String NO_ROWS_MESSAGE = "No items found";
+
+    public static final long GHZ = 1_000_000_000L;
+    public static final long THZ = 1_000_000_000_000L;
+
+    private RoadmUtil() {
+    }
+
+    /**
+     * Formats Hz to GHz.
+     *
+     * @param value Hz in string format
+     * @return GHz in string format
+     */
+    public static String asGHz(Frequency value) {
+        return value == null ? UNKNOWN : String.valueOf((double) value.asHz() / GHZ);
+    }
+
+    /**
+     * Formats Hz to THz.
+     *
+     * @param value Hz in string format
+     * @return THz in string format
+     */
+    public static String asTHz(Frequency value) {
+        return value == null ? UNKNOWN : String.valueOf((double) value.asHz() / THZ);
+    }
+
+    /**
+     * Gives a default value if the string is null or empty.
+     *
+     * @param value the string value
+     * @param defaultValue default value if null or empty
+     * @return processed string
+     */
+    public static String defaultString(String value, String defaultValue) {
+        return isNullOrEmpty(value) ? defaultValue : value;
+    }
+
+    /**
+     * Gives a default value if the object is null.
+     *
+     * @param object the object
+     * @param defaultValue default value if null
+     * @return processed string
+     */
+    public static String objectToString(Object object, String defaultValue) {
+        return object == null ? defaultValue : String.valueOf(object);
+    }
+
+    /**
+     * Gets value from annotations, if not exists, return default value.
+     *
+     * @param annotations the annotations
+     * @param key key value
+     * @param defaultValue default value
+     * @return value in string format
+     */
+    public static String getAnnotation(Annotations annotations, String key, String defaultValue) {
+        return defaultString(annotations.value(key), defaultValue);
+    }
+
+    /**
+     * Gets value from annotations, default value is NA.
+     *
+     * @param annotations the annotations
+     * @param key key value
+     * @return value in string format
+     */
+    public static String getAnnotation(Annotations annotations, String key) {
+        return getAnnotation(annotations, key, NA);
+    }
+}
\ No newline at end of file
diff --git a/apps/roadm/src/main/resources/app/view/roadmDevice/roadmDevice.html b/apps/roadm/src/main/resources/app/view/roadmDevice/roadmDevice.html
index a9a70e8..d2d32d2 100644
--- a/apps/roadm/src/main/resources/app/view/roadmDevice/roadmDevice.html
+++ b/apps/roadm/src/main/resources/app/view/roadmDevice/roadmDevice.html
@@ -16,7 +16,8 @@
             <div ng-class="{active: !!selId}"
                  icon icon-id="flowTable" icon-size="42"
                  tooltip tt-msg="flowTip"
-                 ng-click="nav('roadmFlow')"></div>
+                 ng-click="nav('roadmFlow')"
+                 ng-show="showFlowIcon"></div>
 
 
             <div ng-class="{active: !!selId}"
@@ -34,6 +35,7 @@
                     <td colId="name"sortable>Friendly Name</td>
                     <td colId="id" sortable>Device ID </td>
                     <td colId="master" sortable col-width="120px">Master </td>
+                    <td colId="type" sortable>Device Type </td>
                     <td colId="ports" sortable col-width="70px">Ports </td>
                     <td colId="vendor" sortable>Vendor </td>
                     <td colId="hwVersion" sortable>H/W Version </td>
@@ -53,10 +55,11 @@
 
                 <tr ng-repeat="item in tableData track by $index"
                     ng-class="{selected: item.id === selId}"
-                    ng-click="selectCallback($event, item)">
+                    ng-click="selectCallback($event, item);queryShowItems(item)">
                     <td>{{item.name}}</td>
                     <td>{{item.id}}</td>
                     <td>{{item.master}}</td>
+                    <td>{{item.type}}</td>
                     <td>{{item.ports}}</td>
                     <td>{{item.vendor}}</td>
                     <td>{{item.hwVersion}}</td>
diff --git a/apps/roadm/src/main/resources/app/view/roadmDevice/roadmDevice.js b/apps/roadm/src/main/resources/app/view/roadmDevice/roadmDevice.js
index 2537ed8..9080b12 100644
--- a/apps/roadm/src/main/resources/app/view/roadmDevice/roadmDevice.js
+++ b/apps/roadm/src/main/resources/app/view/roadmDevice/roadmDevice.js
@@ -7,7 +7,6 @@
 
     // constants
     var detailsReq = 'roadmDeviceDetailsRequest';
-
     angular.module('ovRoadmDevice', [])
         .controller('OvRoadmDeviceCtrl',
         ['$log', '$scope', '$location', 'TableBuilderService', 'WebSocketService',
@@ -19,6 +18,8 @@
                 $loc = _$loc_;
                 wss = _wss_;
                 ns = _ns_;
+                
+                $scope.showFlowIcon = true;
 
                 // query for if a certain device needs to be highlighted
                 var params = $loc.search();
@@ -32,6 +33,16 @@
                     tag: 'roadmDevice'
                 });
 
+                $scope.queryShowItems = function (tabRow) {
+                    // hide:OPTICAL_AMPLIFIER,FIBER_SWITCH, show:ROADM
+                    if (tabRow['type'] == 'ROADM') {
+                        $scope.showFlowIcon = true;
+                    } else {
+                        $scope.showFlowIcon = false;
+                    }
+                    $scope.$apply();
+                 }
+
                 $scope.nav = function (path) {
                     if ($scope.selId) {
                         ns.navTo(path, { devId: $scope.selId });
diff --git a/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.css b/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.css
index 81e5c16..885b1a3 100644
--- a/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.css
+++ b/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.css
@@ -84,7 +84,7 @@
     background-color: #ffffff;
     border: 1px solid #888888;
     width: 720px;
-    height: 270px;
+    height: 360px;
     padding: 20px;
     position: absolute;
     right: 15px;
diff --git a/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.html b/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.html
index 323eec5..95979f3 100644
--- a/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.html
+++ b/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.html
@@ -1,5 +1,5 @@
 <!-- partial HTML -->
-<div id="ov-roadm-flow" class="less-gap">
+<div id="ov-roadm-flow" class="less-gap" ng-init="queryShowItems()">
 
     <div class="tabular-header">
         <h2>Connections for Optical Device {{devId}} ({{tableData.length}} total)</h2>
@@ -45,10 +45,10 @@
                     <td colId="state" sortable>State </td>
                     <td colId="inPort" sortable>In Port </td>
                     <td colId="outPort" sortable>Out Port </td>
-                    <td colId="multiplier" sortable>Channel </td>
-                    <td colId="spacing">Spacing <span class="units">(GHz)</span> </td>
+                    <td colId="multiplier" ng-show="showChannel" sortable>Channel </td>
+                    <td colId="spacing" ng-show="showChannel">Spacing <span class="units">(GHz)</span> </td>
                     <td colId="currentPower" col-width="180px">Current Power <span class="units">(0.01dBm)</span></td>
-                    <td colId="attenuation" col-width="200px">Attenuation <span class="units">(0.01dB)</span></td>
+                    <td colId="attenuation" col-width="200px" ng-show="showAttenuation">Attenuation <span class="units">(0.01dB)</span></td>
                 </tr>
             </table>
         </div>
@@ -72,10 +72,10 @@
                     <td>{{flow.state}}</td>
                     <td>{{flow.inPort}}</td>
                     <td>{{flow.outPort}}</td>
-                    <td>{{flow.multiplier}} ({{flow.multiplier * 0.05 + 193.1 | number:2}} THz)</td>
-                    <td>{{flow.spacing}}</td>
+                    <td ng-show="showChannel">{{flow.multiplier}}{{flow.channdlFrequency}}</td>
+                    <td ng-show="showChannel">{{flow.spacing}}</td>
                     <td>{{flow.currentPower}}</td>
-                    <td class="editable" roadm-att="flow" roadm-set-att="setAttenuation(flow, targetVal, cb)"></td>
+                    <td class="editable" roadm-att="flow" roadm-set-att="setAttenuation(flow, targetVal, cb)" ng-show="showAttenuation"></td>
                 </tr>
             </table>
         </div>
@@ -100,16 +100,18 @@
             <span class="form-error" ng-show="form.outPortError">{{form.outPortMessage}}</span>
             <span class="form-error" ng-show="form.connectionError">{{form.connectionMessage}}</span><br />
 
-            <label>Channel Spacing</label><select ng-model="form.flow.spacing" ng-options="x.freq for x in form.spacings"></select>
+            <label>Include Channel</label><input type="checkbox" ng-model="showChannel" /><br />
+
+            <label>Channel Spacing</label><select ng-model="form.flow.spacing" ng-disabled="!showChannel" ng-options="x.freq for x in form.spacings"></select>
             <span class="form-error" ng-show="form.spacingError">{{form.spacingMessage}}</span><br />
 
-            <label>Spacing Multiplier</label><input type="number" ng-model="form.flow.multiplier" />
+            <label>Spacing Multiplier</label><input type="number" ng-model="form.flow.multiplier" ng-disabled="!showChannel"/>
             <span class="form-error" ng-show="form.multiplierError">{{form.multiplierMessage}}</span>
             <span class="form-error" ng-show="form.channelError">{{form.channelMessage}}</span><br />
 
-            <label>Include Attenuation</label><input type="checkbox" ng-model="form.flow.includeAttenuation" /><br />
+            <label>Include Attenuation</label><input type="checkbox" ng-model="showAttenuation" /><br />
 
-            <label>Attenuation</label><input type="number" ng-model="form.flow.attenuation" ng-disabled="!form.flow.includeAttenuation"/>
+            <label>Attenuation</label><input type="number" ng-model="form.flow.attenuation" ng-disabled="!showAttenuation"/>
             <span class="form-error" ng-show="form.attenuationError">{{form.attenuationMessage}}</span><br />
         </form>
         <button type="submit" class="submit" ng-click="form.createFlow(form.flow)">Create Connection</button>
diff --git a/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.js b/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.js
index ad9bf76e..b5d8baf 100644
--- a/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.js
+++ b/apps/roadm/src/main/resources/app/view/roadmFlow/roadmFlow.js
@@ -7,6 +7,8 @@
     var DELETE_FLOW_REQ = "roadmDeleteFlowRequest";
     var CREATE_FLOW_REQ = "roadmCreateFlowRequest";
     var CREATE_FLOW_RESP = "roadmCreateFlowResponse";
+    var SHOW_ITEMS_REQ = "roadmShowFlowItemsRequest";
+    var SHOW_ITEMS_RESP = "roadmShowFlowItemsResponse";
 
     // injected references
     var $log, $scope, $location, fs, tbs, wss, ns;
@@ -24,6 +26,19 @@
             });
     }
 
+    function queryShowItems() {
+        wss.sendEvent(SHOW_ITEMS_REQ,
+            {
+                devId: $scope.devId,
+            });
+    }
+    
+    function showItemsCb(data) {
+        $scope.showChannel = data.showChannel;
+        $scope.showAttenuation = data.showAttenuation;
+        $scope.$apply();
+    }
+
     function attenuationCb(data) {
         flowCbTable[data.flowId](data.valid, data.message);
     }
@@ -55,13 +70,13 @@
             $scope.addFlowTip = 'Create a flow';
             $scope.deviceTip = 'Show device table';
             $scope.flowTip = 'Show flow view for this device';
-            $scope.groupTip = 'Show group view for this device';
-            $scope.meterTip = 'Show meter view for selected device';
+            $scope.portTip = 'Show port view for this device';
 
             $scope.showFlowForm = false;
 
             var handlers = {};
             handlers[SET_ATT_RESP] = attenuationCb;
+            handlers[SHOW_ITEMS_RESP] = showItemsCb;
             wss.bindHandlers(handlers);
 
             params = $location.search();
@@ -83,6 +98,8 @@
                 $scope.showFlowForm = false;
             }
 
+            $scope.queryShowItems = queryShowItems;
+
             $scope.setAttenuation = setAttenuation;
 
             $scope.deleteFlow = function ($event, row) {
@@ -149,7 +166,7 @@
             controller: function($scope, $timeout) {
                 $scope.enableEdit = function() {
                     // connection must support attenuation to be editable
-                    if ($scope.editMode === false) {
+                    if ($scope.currItem.hasAttenuation === 'true' && $scope.editMode === false) {
                         // Ensure that the entry being edited remains the same even
                         // if the table entries are shifted around.
                         $scope.targetItem = $scope.currItem;
@@ -228,7 +245,7 @@
         //this.flow.outPort = 2;
         this.flow.spacing = this.spacings[1];
         //this.flow.multiplier = 0;
-        this.flow.includeAttenuation = true;
+        this.flow.channelFrequency = "";
         this.flow.attenuation = 0;
 
         var parent = this;
@@ -250,15 +267,18 @@
                 parent.spacingMessage = data.spacing.message;
                 parent.spacingError = true;
             }
-            if (!data.multiplier.valid) {
-                parent.multiplierMessage = data.multiplier.message;
-                parent.multiplierError = true;
+            if ($scope.includeChannel)
+            {
+                if (!data.multiplier.valid) {
+                    parent.multiplierMessage = data.multiplier.message;
+                    parent.multiplierError = true;
+                }
+                if (!data.channelAvailable.valid) {
+                    parent.channelMessage = data.channelAvailable.message;
+                    parent.channelError = true;
+                }
             }
-            if (!data.channelAvailable.valid) {
-                parent.channelMessage = data.channelAvailable.message;
-                parent.channelError = true;
-            }
-            if (data.includeAttenuation && !data.attenuation.valid) {
+            if ($scope.includeAttenuation && !data.attenuation.valid) {
                 parent.attenuationMessage = data.attenuation.message;
                 parent.attenuationError = true;
             }
@@ -270,7 +290,6 @@
         var handlers = {}
         handlers[CREATE_FLOW_RESP] = createFlowCb;
         wss.bindHandlers(handlers);
-
         this.createFlow = function(connection) {
             this.clearErrors();
 
@@ -295,12 +314,12 @@
                 this.outPortError = true;
                 error = true;
             }
-            if (!isInteger(connection.multiplier)) {
+            if ($scope.includeChannel && !isInteger(connection.multiplier)) {
                 this.multiplierMessage = notIntegerError;
                 this.multiplierError = true;
                 error = true;
             }
-            if (connection.includeAttenuation && !isInteger(connection.attenuation)) {
+            if ($scope.includeAttenuation && !isInteger(connection.attenuation)) {
                 this.attenuationMessage = notIntegerError;
                 this.attenuationError = true;
                 error = true;
diff --git a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.css b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.css
index 511b2fc..6be2601 100644
--- a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.css
+++ b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.css
@@ -67,3 +67,22 @@
     font-size: 10px;
     width: 180px;
 }
+
+#ov-roadm-port .mode-select .mode-title {
+    margin: 0;
+    font-weight:bold;
+    font-size: 16px;
+}
+
+#ov-roadm-port .mode-select .mode-fail {
+    color: red;
+    font-size: 10px;
+}
+
+#ov-roadm-port .mode-select select {
+    width: 150px;
+}
+
+#ov-roadm-port .mode-select button {
+    width: 100px;
+}
\ No newline at end of file
diff --git a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html
index 1e2affd..9ef475b 100644
--- a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html
+++ b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html
@@ -1,5 +1,5 @@
 <!-- partial HTML -->
-<div id="ov-roadm-port" class="less-gap">
+<div id="ov-roadm-port" class="less-gap" ng-init="queryShowItems()">
 
     <div class="tabular-header">
         <h2>Ports for Optical Device {{devId}} ({{tableData.length}} total)</h2>
@@ -18,7 +18,7 @@
             <div class="active"
                  icon icon-id="flowTable" icon-size="42"
                  tooltip tt-msg="flowTip"
-                 ng-click="nav('roadmFlow')"></div>
+                 ng-click="nav('roadmFlow')" ng-show="showFlowIcon"></div>
 
             <div class="current-view"
                  icon icon-id="portTable" icon-size="42"
@@ -27,7 +27,6 @@
     </div>
 
     <div class="summary-list" onos-table-resize>
-
         <div class="table-header" onos-sortable-header>
             <table>
                 <tr>
@@ -40,7 +39,8 @@
                     <td colId="grid" sortable>Grid <span class="units">(GHz)</span> </td>
                     <td colId="portMac" sortable>Input Power Range </td>
                     <td colId="currentPower">Current Power <span class="units">(0.01dBm)</span> </td>
-                    <td colId="targetPower" col-width="200px">Target Power <span class="units">(0.01dBm)</span> </td>
+                    <td colId="serviceState" ng-show="showServiceState">Protection Status </td>
+                    <td colId="targetPower" col-width="200px" ng-show="showTargetPower">Target Power <span class="units">(0.01dBm)</span> </td>
                 </tr>
             </table>
         </div>
@@ -48,7 +48,7 @@
         <div class="table-body">
             <table>
                 <tr ng-if="!tableData.length" class="no-data">
-                    <td colspan="10">
+                    <td colspan="11">
                         {{annots.no_rows_msg}}
                     </td>
                 </tr>
@@ -64,10 +64,17 @@
                     <td>{{item.grid}}</td>
                     <td>{{item.inputPowerRange}}</td>
                     <td>{{item.currentPower}}</td>
-                    <td class="editable" roadm-power="item" roadm-set-power="setPortPower(port, targetVal, cb)"></td>
+                    <td ng-show="showServiceState">{{item.serviceState}}</td>
+                    <td ng-show="showTargetPower" class="editable" roadm-power="item" roadm-set-power="setPortPower(port, targetVal, cb)"></td>
                 </tr>
             </table>
+            <div class="mode-select" ng-show="showServiceState">
+                <hr/>
+                <label class="mode-title">Protection Mode:</label>
+                <select ng-model="opsModeType" ng-options="mode.type for mode in opsModeTypes"></select>
+                <button ng-click="changeOpsMode()" title="Click to apply selected protection mode">Apply</button>
+                <label class="mode-fail" ng-if="changeModeFail">{{changeModeFailMsg}}</label>
+            </div>
         </div>
-
     </div>
 </div>
diff --git a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.js b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.js
index 447ac7d..128b72c 100644
--- a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.js
+++ b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.js
@@ -4,6 +4,10 @@
 
     var SET_TARGET_POWER_REQ = "roadmSetTargetPowerRequest";
     var SET_TARGET_POWER_RESP = "roadmSetTargetPowerResponse";
+    var SHOW_ITEMS_REQ = "roadmShowPortItemsRequest";
+    var SHOW_ITEMS_RESP = "roadmShowPortItemsResponse";
+    var SET_OPS_MODE_REQ = "roadmSetOpsModeRequest";
+    var SET_OPS_MODE_RESP = "roadmSetOpsModeResponse";
 
     // injected references
     var $log, $scope, $location, fs, tbs, wss, ns;
@@ -13,7 +17,7 @@
     function setPortPower(port, targetVal, cb) {
         var id = port.id;
         portCbTable[id] = cb;
-        wss.sendEvent("roadmSetTargetPowerRequest",
+        wss.sendEvent(SET_TARGET_POWER_REQ,
             {
                 devId: $scope.devId,
                 id: port.id,
@@ -25,6 +29,36 @@
         portCbTable[data.id](data.valid, data.message);
     }
 
+    function queryShowItems() {
+        wss.sendEvent(SHOW_ITEMS_REQ,
+            {
+                devId: $scope.devId,
+            });
+    }
+
+    function showItemsCb(data) {
+        $scope.showTargetPower = data.showTargetPower;
+        $scope.showServiceState = data.showServiceState;
+        $scope.showFlowIcon = data.showFlowIcon;
+        $scope.$apply();
+    }
+
+    function changeOpsMode() {
+        wss.sendEvent(SET_OPS_MODE_REQ,
+            {
+                devId: $scope.devId,
+                index: $scope.opsModeType.index
+            });
+    }
+
+    function changeOpsModeCb(data) {
+        $scope.changeModeFail = !data.valid;
+        if ($scope.changeModeFail) {
+            $scope.changeModeFailMsg = data.message;
+        }
+        $scope.$apply();
+    }
+
     // check if value is an integer
     function isInteger(val) {
         var INTEGER_REGEXP = /^\-?\d+$/;
@@ -51,11 +85,18 @@
 
             $scope.deviceTip = 'Show device table';
             $scope.flowTip = 'Show flow view for this device';
-            $scope.groupTip = 'Show group view for this device';
-            $scope.meterTip = 'Show meter view for selected device';
+            $scope.portTip = 'Show port view for this device';
+            $scope.opsModeTypes = [
+                {index: 0, type: "Auto"},
+                {index: 1, type: "Primary"},
+                {index: 2, type: "Secondary"}
+            ];
+            $scope.opsModeType = $scope.opsModeTypes[0];//auto mode
 
             var handlers = {};
             handlers[SET_TARGET_POWER_RESP] = portPowerCb;
+            handlers[SHOW_ITEMS_RESP] = showItemsCb;
+            handlers[SET_OPS_MODE_RESP] = changeOpsModeCb;
             wss.bindHandlers(handlers);
 
             params = $location.search();
@@ -70,9 +111,11 @@
             });
 
             $scope.setPortPower = setPortPower;
+            $scope.queryShowItems = queryShowItems;
+            $scope.changeOpsMode = changeOpsMode;
 
             $scope.setTargetPower = function (port, targetVal) {
-                wss.sendEvent("roadmSetTargetPowerRequest",
+                wss.sendEvent(SET_TARGET_POWER_REQ,
                     {
                         devId: $scope.devId,
                         id: port.id,
diff --git a/core/api/src/main/java/org/onosproject/ui/RequestHandler.java b/core/api/src/main/java/org/onosproject/ui/RequestHandler.java
index d18f633..aa53f00 100644
--- a/core/api/src/main/java/org/onosproject/ui/RequestHandler.java
+++ b/core/api/src/main/java/org/onosproject/ui/RequestHandler.java
@@ -150,4 +150,15 @@
     protected boolean bool(ObjectNode node, String key) {
         return JsonUtils.bool(node, key);
     }
+
+    /**
+     * Returns the specified node property as a number.
+     *
+     * @param node object node
+     * @param key  property name
+     * @return property as a number
+     */
+    protected long number(ObjectNode node, String key) {
+        return JsonUtils.number(node, key);
+    }
 }
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkPowerConfigUtil.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkPowerConfigUtil.java
index a24df09..45e2c58 100644
--- a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkPowerConfigUtil.java
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkPowerConfigUtil.java
@@ -114,6 +114,9 @@
     private static final long ROADM_POWER_OTHER_OUT_HIGH_THRES = 1500L;
     private static final long ROADM_MIN_ATTENUATION = 0L;
     private static final long ROADM_MAX_ATTENUATION = 2500L;
+    // SWITCH
+    private static final long SWITCH_POWER_LOW_THRES = -6000L;
+    private static final long SWITCH_POWER_HIGH_THRES = 6000L;
 
     /**
      * Create a new OplinkPowerConfigUtil.
@@ -296,6 +299,8 @@
                     return Range.closed(ROADM_POWER_OTHER_OUT_LOW_THRES, ROADM_POWER_OTHER_OUT_HIGH_THRES);
                 }
                 break;
+            case FIBER_SWITCH:
+                return Range.closed(SWITCH_POWER_LOW_THRES, SWITCH_POWER_HIGH_THRES);
             default:
                 log.warn("Unexpected device type: {}", devType);
                 break;
@@ -335,6 +340,8 @@
                     return Range.closed(ROADM_POWER_OTHER_IN_LOW_THRES, ROADM_POWER_OTHER_IN_HIGH_THRES);
                 }
                 break;
+            case FIBER_SWITCH:
+                return Range.closed(SWITCH_POWER_LOW_THRES, SWITCH_POWER_HIGH_THRES);
             default:
                 log.warn("Unexpected device type: {}", devType);
                 break;
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkSwitchPowerConfig.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkSwitchPowerConfig.java
new file mode 100644
index 0000000..5be34a4
--- /dev/null
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/power/OplinkSwitchPowerConfig.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.driver.optical.power;
+
+import java.util.Optional;
+
+import com.google.common.collect.Range;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PowerConfig;
+
+/**
+ * Port Power implementation for Oplink protection switch device.
+ *
+ * Intruduction:
+ *                                       _____
+ *             _____________________    |     | 0  VIRTUAL
+ *            |                     |   |  1  |
+ *   CLIENT   |                     |---|-----|--- PRIMARY
+ * -----------|         OPS         |   |     |                NETWORK
+ *      3     |                     |---|-----|--- SECONDARY
+ *            |_____________________|   |  2  |
+ *                                      |_____|
+ * Uses flow to set switch path.
+ * network port = 0, client port = 3, AUTO mode.
+ * network port = 1 or 2, client port = 3, MANUAL mode.
+ *
+ */
+
+public class OplinkSwitchPowerConfig extends AbstractHandlerBehaviour
+                                    implements PowerConfig<Object> {
+
+    // oplink power config utility
+    private OplinkPowerConfigUtil oplinkUtil = new OplinkPowerConfigUtil(this);
+
+    @Override
+    public Optional<Long> getTargetPower(PortNumber port, Object component) {
+        return Optional.ofNullable(null);
+    }
+
+    @Override
+    public Optional<Long> currentPower(PortNumber port, Object component) {
+        return Optional.ofNullable(oplinkUtil.getCurrentPower(port, component));
+    }
+
+    @Override
+    public void setTargetPower(PortNumber port, Object component, long power) {
+        return;
+    }
+
+    @Override
+    public Optional<Range<Long>> getTargetPowerRange(PortNumber port, Object component) {
+        return Optional.ofNullable(oplinkUtil.getTargetPowerRange(port, component));
+    }
+
+    @Override
+    public Optional<Range<Long>> getInputPowerRange(PortNumber port, Object component) {
+        return Optional.ofNullable(oplinkUtil.getInputPowerRange(port, component));
+    }
+}
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/protection/OplinkSwitchProtection.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/protection/OplinkSwitchProtection.java
index f512b9c..16009f7 100644
--- a/drivers/optical/src/main/java/org/onosproject/driver/optical/protection/OplinkSwitchProtection.java
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/protection/OplinkSwitchProtection.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.driver.optical.protection;
 
+import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.FilteredConnectPoint;
@@ -73,16 +74,14 @@
     private static final int PRIMARY_PORT = 1;
     private static final int SECONDARY_PORT = 2;
     private static final int CLIENT_PORT = 3;
+    private static final int FLOWRULE_PRIORITY = 88;
     private static final String PRIMARY_ID = "primary_port";
     private static final String SECONDARY_ID = "secondary_port";
     private static final String OPLINK_FINGERPRINT = "OplinkOPS";
+    private static final String APP_ID = "org.onosproject.drivers.optical";
 
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
-    private FlowRuleService flowRuleService = this.handler().get(FlowRuleService.class);
-    private DeviceService deviceService = this.handler().get(DeviceService.class);
-    private NetworkConfigService netCfgService = this.handler().get(NetworkConfigService.class);
-
     @Override
     public CompletableFuture<ConnectPoint> createProtectionEndpoint(
             ProtectedTransportEndpointDescription configuration) {
@@ -96,7 +95,7 @@
         //add a virtual link bewteen two virtual ports of this device and peer
         addLinkToPeer(configuration.peer());
 
-        result.complete(new ConnectPoint(this.data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT)));
+        result.complete(new ConnectPoint(data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT)));
 
         return result;
     }
@@ -108,7 +107,7 @@
         log.warn("Update protection configuration is not supported by this device");
 
         CompletableFuture result = new CompletableFuture<ConnectPoint>();
-        result.complete(new ConnectPoint(this.data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT)));
+        result.complete(new ConnectPoint(data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT)));
 
         return result;
     }
@@ -132,7 +131,7 @@
 
     @Override
     public CompletableFuture<Map<ConnectPoint, ProtectedTransportEndpointDescription>> getProtectionEndpointConfigs() {
-        ConnectPoint cp = new ConnectPoint(this.data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT));
+        ConnectPoint cp = new ConnectPoint(data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT));
 
         Map<ConnectPoint, ProtectedTransportEndpointDescription> protectedGroups = new HashMap<>();
         CompletableFuture result = new CompletableFuture<Map<ConnectPoint, ProtectedTransportEndpointDescription>>();
@@ -145,7 +144,7 @@
 
     @Override
     public CompletableFuture<Map<ConnectPoint, ProtectedTransportEndpointState>> getProtectionEndpointStates() {
-        ConnectPoint cp = new ConnectPoint(this.data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT));
+        ConnectPoint cp = new ConnectPoint(data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT));
 
         Map<ConnectPoint, ProtectedTransportEndpointState> protectedGroups = new HashMap<>();
         CompletableFuture result = new CompletableFuture<Map<ConnectPoint, ProtectedTransportEndpointState>>();
@@ -193,9 +192,7 @@
 
         teds.add(tedPrimary);
         teds.add(tedSecondary);
-        return ProtectedTransportEndpointDescription.of(teds,
-                                                        getPeerId(),
-                                                        OPLINK_FINGERPRINT);
+        return ProtectedTransportEndpointDescription.of(teds, getPeerId(), OPLINK_FINGERPRINT);
     }
 
     /*
@@ -205,7 +202,7 @@
         Map<String, String> attributes = new HashMap<>();
 
         //get status form port annotations, the status is update by hand shaker driver periodically
-        Port port = deviceService.getPort(this.data().deviceId(), portNumber);
+        Port port = handler().get(DeviceService.class).getPort(data().deviceId(), portNumber);
         if (port != null) {
             String portStatus = port.annotations().value(OpticalAnnotations.INPUT_PORT_STATUS);
             attributes.put(OpticalAnnotations.INPUT_PORT_STATUS, portStatus);
@@ -214,7 +211,8 @@
     }
 
     private int getActiveIndex() {
-        Port port = deviceService.getPort(this.data().deviceId(), PortNumber.portNumber(PRIMARY_PORT));
+        Port port = handler().get(DeviceService.class)
+                .getPort(data().deviceId(), PortNumber.portNumber(PRIMARY_PORT));
         if (port != null) {
             if (port.annotations().value(OpticalAnnotations.INPUT_PORT_STATUS)
                     .equals(OpticalAnnotations.STATUS_IN_SERVICE)) {
@@ -229,10 +227,12 @@
      */
     private ProtectedTransportEndpointState getProtectedTransportEndpointState() {
         List<TransportEndpointState> tess = new ArrayList<>();
+        PortNumber portPrimary = PortNumber.portNumber(PRIMARY_PORT);
+        PortNumber portSecondary = PortNumber.portNumber(SECONDARY_PORT);
         FilteredConnectPoint fcpPrimary = new FilteredConnectPoint(
-                new ConnectPoint(data().deviceId(), PortNumber.portNumber(PRIMARY_PORT)));
+                new ConnectPoint(data().deviceId(), portPrimary));
         FilteredConnectPoint fcpSecondary = new FilteredConnectPoint(
-                new ConnectPoint(data().deviceId(), PortNumber.portNumber(SECONDARY_PORT)));
+                new ConnectPoint(data().deviceId(), portSecondary));
         TransportEndpointDescription tedPrimary = TransportEndpointDescription.builder()
                 .withOutput(fcpPrimary).build();
         TransportEndpointDescription tedSecondary = TransportEndpointDescription.builder()
@@ -241,12 +241,12 @@
         TransportEndpointState tesPrimary = TransportEndpointState.builder()
                 .withDescription(tedPrimary)
                 .withId(TransportEndpointId.of(PRIMARY_ID))
-                .addAttributes(getProtectionStateAttributes(PortNumber.portNumber(PRIMARY_PORT)))
+                .addAttributes(getProtectionStateAttributes(portPrimary))
                 .build();
         TransportEndpointState tesSecondary = TransportEndpointState.builder()
                 .withDescription(tedSecondary)
                 .withId(TransportEndpointId.of(SECONDARY_ID))
-                .addAttributes(getProtectionStateAttributes((PortNumber.portNumber(SECONDARY_PORT))))
+                .addAttributes(getProtectionStateAttributes((portSecondary)))
                 .build();
 
         tess.add(tesPrimary);
@@ -266,30 +266,32 @@
      *      - A flow from secondary port to client port indicates the device is manually switched to secondary
      */
     private void addFlow(PortNumber workingPort) {
-        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-        FlowRule.Builder flowRule = DefaultFlowRule.builder();
-
-        //set working port as flow's input port
-        selectorBuilder.matchInPort(workingPort);
-
-        //the flow's  output port is always the clinet port
-        treatment.setOutput(PortNumber.portNumber(CLIENT_PORT));
-
-        flowRule.forDevice(this.data().deviceId())
-                .withSelector(selectorBuilder.build())
-                .withTreatment(treatment.build())
-                .makePermanent();
+        // set working port as flow's input port
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchInPort(workingPort)
+                .build();
+        // the flow's  output port is always the clinet port
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(PortNumber.portNumber(CLIENT_PORT))
+                .build();
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .forDevice(data().deviceId())
+                .fromApp(handler().get(CoreService.class).getAppId(APP_ID))
+                .withPriority(FLOWRULE_PRIORITY)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .makePermanent()
+                .build();
 
         // install flow rule
-        flowRuleService.applyFlowRules(flowRule.build());
+        handler().get(FlowRuleService.class).applyFlowRules(flowRule);
     }
     /*
         Delete all the flows to put device in default mode.
      */
     private void deleteFlow() {
         // remove all the flows.
-        flowRuleService.purgeFlowRules(this.data().deviceId());
+        handler().get(FlowRuleService.class).purgeFlowRules(data().deviceId());
     }
 
     private void addLinkToPeer(DeviceId peerId) {
@@ -301,7 +303,8 @@
         ConnectPoint dstCp = new ConnectPoint(data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT));
         ConnectPoint srcCp = new ConnectPoint(peerId, PortNumber.portNumber(VIRTUAL_PORT));
         LinkKey link = linkKey(srcCp, dstCp);
-        BasicLinkConfig cfg = netCfgService.addConfig(link, BasicLinkConfig.class);
+        BasicLinkConfig cfg = handler().get(NetworkConfigService.class)
+                .addConfig(link, BasicLinkConfig.class);
         cfg.type(Link.Type.VIRTUAL);
         cfg.apply();
     }
@@ -314,12 +317,12 @@
         ConnectPoint dstCp = new ConnectPoint(data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT));
         ConnectPoint srcCp = new ConnectPoint(peerId, PortNumber.portNumber(VIRTUAL_PORT));
         LinkKey link = linkKey(srcCp, dstCp);
-        netCfgService.removeConfig(link, BasicLinkConfig.class);
+        handler().get(NetworkConfigService.class).removeConfig(link, BasicLinkConfig.class);
     }
 
     private DeviceId getPeerId() {
         ConnectPoint dstCp = new ConnectPoint(data().deviceId(), PortNumber.portNumber(VIRTUAL_PORT));
-        Set<Link> links = this.handler().get(LinkService.class).getIngressLinks(dstCp);
+        Set<Link> links = handler().get(LinkService.class).getIngressLinks(dstCp);
 
         for (Link l : links) {
             if (l.type() == Link.Type.VIRTUAL) {
@@ -328,6 +331,6 @@
             }
         }
 
-        return null;
+        return data().deviceId();
     }
 }
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkEdfaLambdaQuery.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkEdfaLambdaQuery.java
index b37dfbf..c8bf529 100644
--- a/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkEdfaLambdaQuery.java
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkEdfaLambdaQuery.java
@@ -19,7 +19,9 @@
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
+import org.onlab.util.Spectrum;
 import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.behaviour.LambdaQuery;
@@ -28,21 +30,22 @@
 /**
  * Lambda query implementation for Oplink EDFA.
  *
- * An Oplink EDFA port exposes OMSn resources: 88 lambdas with 50GHz width (fixed grid).
- *
- * Channel id: Nominal central frequency = 193.1 THz + spacingMultiplier * channelSpacing).
- * Channel (-28 to 59): starting from 191.7 THz to 196.05 THz, Increment by 50GHz.
+ * An Oplink EDFA port supports min bandwidth 6.25Hz (flex grid).
+ * Usable optical spectrum range is C band, see {@link Spectrum} for spectrum definitions.
  */
 
 public class OplinkEdfaLambdaQuery extends AbstractHandlerBehaviour implements LambdaQuery {
 
-    private static final int MIN_CHANNEL = -28;
-    private static final int MAX_CHANNEL = 59;
+    // Wavelength range: 1528 - 1567 nm
+    private long startSpacingMultiplier = Spectrum.C_BAND_MIN.subtract(Spectrum.CENTER_FREQUENCY).asHz() /
+            ChannelSpacing.CHL_6P25GHZ.frequency().asHz();
+    private long stopSpacingMultiplier = Spectrum.C_BAND_MAX.subtract(Spectrum.CENTER_FREQUENCY).asHz() /
+            ChannelSpacing.CHL_6P25GHZ.frequency().asHz();
 
     @Override
     public Set<OchSignal> queryLambdas(PortNumber port) {
-        return IntStream.rangeClosed(MIN_CHANNEL, MAX_CHANNEL)
-                .mapToObj(x -> OchSignal.newDwdmSlot(ChannelSpacing.CHL_50GHZ, x))
+        return IntStream.rangeClosed((int) startSpacingMultiplier, (int) stopSpacingMultiplier)
+                .mapToObj(x -> new OchSignal(GridType.FLEX, ChannelSpacing.CHL_6P25GHZ, x, 1))
                 .collect(Collectors.toSet());
     }
 }
diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkSwitchLambdaQuery.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkSwitchLambdaQuery.java
new file mode 100644
index 0000000..21c41bf
--- /dev/null
+++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/query/OplinkSwitchLambdaQuery.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.driver.optical.query;
+
+import org.onlab.util.Spectrum;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.LambdaQuery;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * Lambda query implementation for Oplink optical protection switch.
+ *
+ * An Oplink optical protection switch supports min bandwidth 6.25Hz (flex grid).
+ * Usable optical spectrum range is U to O band, see {@link Spectrum} for spectrum definitions.
+ */
+public class OplinkSwitchLambdaQuery extends AbstractHandlerBehaviour implements LambdaQuery {
+
+    @Override
+    public Set<OchSignal> queryLambdas(PortNumber port) {
+        // Wavelength range: 1260 - 1675 nm
+        long startSpacingMultiplier = Spectrum.U_BAND_MIN.subtract(Spectrum.CENTER_FREQUENCY).asHz() /
+                ChannelSpacing.CHL_6P25GHZ.frequency().asHz();
+        long stopSpacingMultiplier = Spectrum.O_BAND_MAX.subtract(Spectrum.CENTER_FREQUENCY).asHz() /
+                ChannelSpacing.CHL_6P25GHZ.frequency().asHz();
+
+        // Only consider odd values for the multiplier (for easy mapping to fixed grid)
+        return IntStream.rangeClosed((int) startSpacingMultiplier, (int) stopSpacingMultiplier)
+                .mapToObj(x -> new OchSignal(GridType.FLEX, ChannelSpacing.CHL_6P25GHZ, x, 1))
+                .collect(Collectors.toSet());
+    }
+}
diff --git a/drivers/optical/src/main/resources/optical-drivers.xml b/drivers/optical/src/main/resources/optical-drivers.xml
index 21829a1..4d01858 100644
--- a/drivers/optical/src/main/resources/optical-drivers.xml
+++ b/drivers/optical/src/main/resources/optical-drivers.xml
@@ -68,9 +68,12 @@
             manufacturer="Oplink a Molex company" hwVersion="protection-switch" swVersion="of-agent-1.0">
         <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver"
             impl="org.onosproject.driver.optical.handshaker.OplinkSwitchHandshaker"/>
-
         <behaviour api="org.onosproject.net.behaviour.protection.ProtectionConfigBehaviour"
                    impl="org.onosproject.driver.optical.protection.OplinkSwitchProtection"/>
+        <behaviour api="org.onosproject.net.behaviour.LambdaQuery"
+                   impl="org.onosproject.driver.optical.query.OplinkSwitchLambdaQuery"/>
+        <behaviour api="org.onosproject.net.behaviour.PowerConfig"
+                   impl="org.onosproject.driver.optical.power.OplinkSwitchPowerConfig"/>
     </driver>
 
     <driver name="oplk-edfa" extends="default"