Improvements of roadm application:
  1.Fix the bug of Treating path index as port number for APS;
  2.Creating APS switching operations dynamically, instead of hard code;
  3.Change APS interface for general using;
  4.Compatible test base on the devices of OPENFLOW and NETCONF protocol.

Change-Id: Ib750d40ed28fc184a96b58e97715beab3d80ff17
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 7f44e23..081dedf 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmManager.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmManager.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.roadm;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Range;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -52,7 +53,6 @@
 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;
@@ -62,12 +62,14 @@
 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;
+import static org.onosproject.net.optical.OpticalAnnotations.INPUT_PORT_STATUS;
+import static org.onosproject.roadm.RoadmUtil.OPS_OPT_AUTO;
+import static org.onosproject.roadm.RoadmUtil.OPS_OPT_FORCE;
+import static org.onosproject.roadm.RoadmUtil.OPS_OPT_MANUAL;
 
 /**
  * Application for monitoring and configuring ROADM devices.
@@ -111,6 +113,7 @@
         log.info("Stopped");
     }
 
+    @Deprecated
     @Override
     public void setProtectionSwitchWorkingPath(DeviceId deviceId, int index) {
         checkNotNull(deviceId);
@@ -130,6 +133,7 @@
         behaviour.switchWorkingPath(map.keySet().toArray(new ConnectPoint[0])[0], index);
     }
 
+    @Deprecated
     @Override
     public String getProtectionSwitchPortState(DeviceId deviceId, PortNumber portNumber) {
         checkNotNull(deviceId);
@@ -145,7 +149,7 @@
         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);
+                    return element.attributes().get(INPUT_PORT_STATUS);
                 }
             }
         }
@@ -155,6 +159,37 @@
     }
 
     @Override
+    public void configProtectionSwitch(DeviceId deviceId, String operation, ConnectPoint identifier, int index) {
+        checkNotNull(deviceId);
+        ProtectionConfigBehaviour behaviour = getProtectionConfig(deviceId);
+        if (behaviour == null) {
+            return;
+        }
+        // automatic operation
+        if (OPS_OPT_AUTO.equals(operation)) {
+            behaviour.switchToAutomatic(identifier);
+            return;
+        }
+        // force or manual operation
+        if (OPS_OPT_MANUAL.equals(operation)) {
+            behaviour.switchToManual(identifier, index);
+        } else if (OPS_OPT_FORCE.equals(operation)) {
+            behaviour.switchToForce(identifier, index);
+        }
+    }
+
+    @Override
+    public Map<ConnectPoint, ProtectedTransportEndpointState> getProtectionSwitchStates(DeviceId deviceId) {
+        checkNotNull(deviceId);
+        ProtectionConfigBehaviour behaviour = getProtectionConfig(deviceId);
+        if (behaviour == null) {
+            return ImmutableMap.of();
+        }
+        return getProtectionSwitchStates(behaviour);
+    }
+
+
+    @Override
     public void setTargetPortPower(DeviceId deviceId, PortNumber portNumber, long power) {
         checkNotNull(deviceId);
         checkNotNull(portNumber);
@@ -547,6 +582,7 @@
                 TimeUnit.SECONDS.sleep(1);
             } catch (InterruptedException e) {
                 log.warn("Thread interrupted. Setting attenuation early.");
+                Thread.currentThread().interrupt();
             }
             setAttenuation(deviceId, outPort, ochSignal, attenuation);
         };
@@ -556,17 +592,16 @@
     // 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();
+            map = behaviour.getProtectionEndpointStates().get();
         } catch (InterruptedException e1) {
             log.error("Interrupted.", e1);
-            return null;
+            Thread.currentThread().interrupt();
+            return ImmutableMap.of();
         } catch (ExecutionException e1) {
             log.error("Exception caught.", e1);
-            return null;
+            return ImmutableMap.of();
         }
         return map;
     }
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 97e2fee..84297f8 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmPortViewMessageHandler.java
@@ -15,17 +15,23 @@
  */
 package org.onosproject.roadm;
 
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 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.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointState;
+import org.onosproject.net.behaviour.protection.TransportEndpointState;
 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.optical.OpticalAnnotations;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiConnection;
 import org.onosproject.ui.UiMessageHandler;
@@ -38,9 +44,14 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import static org.onosproject.net.Device.Type;
+import static org.onosproject.net.behaviour.protection.ProtectedTransportEndpointState.ACTIVE_UNKNOWN;
+import static org.onosproject.roadm.RoadmUtil.OPS_OPT_AUTO;
+import static org.onosproject.roadm.RoadmUtil.OPS_OPT_FORCE;
+import static org.onosproject.roadm.RoadmUtil.OPS_OPT_MANUAL;
 
 /**
  * Table-View message handler for ROADM port view.
@@ -142,9 +153,20 @@
         }
 
         private String getPortServiceState(DeviceId deviceId, PortNumber portNumber) {
-            return RoadmUtil.defaultString(
-                    roadmService.getProtectionSwitchPortState(deviceId, portNumber),
-                    RoadmUtil.UNKNOWN);
+            if (deviceService.getDevice(deviceId).type() != Type.FIBER_SWITCH) {
+                return RoadmUtil.NA;
+            }
+            Map<ConnectPoint, ProtectedTransportEndpointState> map =
+                    roadmService.getProtectionSwitchStates(deviceId);
+            for (ProtectedTransportEndpointState state : map.values()) {
+                for (TransportEndpointState element : state.pathStates()) {
+                    if (element.description().output().connectPoint().port().equals(portNumber)) {
+                        return RoadmUtil.defaultString(element.attributes()
+                            .get(OpticalAnnotations.INPUT_PORT_STATUS), RoadmUtil.UNKNOWN);
+                    }
+                }
+            }
+            return RoadmUtil.UNKNOWN;
         }
 
         private Frequency minFreq = null, maxFreq = null, channelSpacing = null;
@@ -224,10 +246,18 @@
         }
     }
 
+    // Protection switch operation type and path index
+    private static final String OPS_ARRAY_INDEX = "index";
+    private static final String OPS_ARRAY_OPERATION = "operation";
+    private static final String[] OPS_NON_AUTO_OPTS = {OPS_OPT_FORCE, OPS_OPT_MANUAL};
+
     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 static final String OPS_PATHS = "opsOperations";
+        private static final String OPS_ARRAY_NAME = "name";
+        private static final String OPS_GROUP_FMT = "GROUP%d ";
 
         private CreateShowItemsRequestHandler() {
             super(ROADM_SHOW_ITEMS_REQ);
@@ -239,15 +269,51 @@
             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);
+            if (devType == Type.FIBER_SWITCH) {
+                node.put(SHOW_TARGET_POWER, false);
+                node.put(SHOW_SERVICE_STATE, true);
+                // add protection switch paths
+                putProtectionSwitchPaths(did, node);
+            } else {
+                node.put(SHOW_TARGET_POWER, true);
+                node.put(SHOW_SERVICE_STATE, false);
+            }
             sendMessage(ROADM_SHOW_ITEMS_RESP, node);
         }
+
+        private void putProtectionSwitchPaths(DeviceId deviceId, ObjectNode node) {
+            Map<ConnectPoint, ProtectedTransportEndpointState> states =
+                    roadmService.getProtectionSwitchStates(deviceId);
+            ArrayNode nodes = node.putArray(OPS_PATHS);
+            // Add path names for every identifier.
+            int groupIndex = 0;
+            for (ConnectPoint identifier : states.keySet()) {
+                // No group name needed if there is only one connection point identifier.
+                String groupName = states.keySet().size() == 1 ? "" : String.format(OPS_GROUP_FMT, ++groupIndex);
+                // Add AUTOMATIC operation.
+                nodes.add(new ObjectNode(JsonNodeFactory.instance)
+                          .put(OPS_ARRAY_INDEX, ACTIVE_UNKNOWN)
+                          .put(OPS_ARRAY_OPERATION, OPS_OPT_AUTO)
+                          .put(OPS_ARRAY_NAME, String.format("%s%s", groupName, OPS_OPT_AUTO)));
+                // Add FORCE and MANUAL operations for every path.
+                for (String opt : OPS_NON_AUTO_OPTS) {
+                    int pathIndex = 0;
+                    for (TransportEndpointState state : states.get(identifier).pathStates()) {
+                        nodes.add(new ObjectNode(JsonNodeFactory.instance)
+                                  .put(OPS_ARRAY_INDEX, pathIndex++)
+                                  .put(OPS_ARRAY_OPERATION, opt)
+                                  .put(OPS_ARRAY_NAME,
+                                       String.format("%s%s %s", groupName, opt, state.id().id().toUpperCase())));
+                    }
+                }
+            }
+
+
+        }
     }
 
     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.";
 
@@ -272,8 +338,10 @@
                 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));
+            // get switch configuration from payload, and then switch the device.
+            roadmService.configProtectionSwitch(did, string(payload, OPS_ARRAY_OPERATION),
+                    roadmService.getProtectionSwitchStates(did).keySet().toArray(new ConnectPoint[0])[0],
+                    (int) number(payload, OPS_ARRAY_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 792484f..377f600 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmService.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmService.java
@@ -16,11 +16,14 @@
 package org.onosproject.roadm;
 
 import com.google.common.collect.Range;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.OchSignal;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.protection.ProtectedTransportEndpointState;
 import org.onosproject.net.flow.FlowId;
 
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -47,7 +50,9 @@
      *
      * @param deviceId DeviceId of the device to configure
      * @param index working path index to switch to
+     * @deprecated 1.11.0
      */
+    @Deprecated
     void setProtectionSwitchWorkingPath(DeviceId deviceId, int index);
 
     /**
@@ -56,10 +61,29 @@
      * @param deviceId DeviceId of the device to configure
      * @param portNumber the port
      * @return port service status
+     * @deprecated 1.11.0
      */
+    @Deprecated
     String getProtectionSwitchPortState(DeviceId deviceId, PortNumber portNumber);
 
     /**
+     * Attempts to config protection switch by specified {@code operation} and {@code index}.
+     *
+     * @param deviceId DeviceId of the device to configure
+     * @param operation switch configuration, automatic, force or manual
+     * @param identifier {@link ConnectPoint} for the virtual Port representing protected path endpoint
+     * @param index working path index to switch to
+     */
+    void configProtectionSwitch(DeviceId deviceId, String operation, ConnectPoint identifier, int index);
+
+    /**
+     * Retrieves protection switch endpoint states.
+     * @param deviceId DeviceId of the device to configure
+     * @return map groups of underlying paths
+     */
+    Map<ConnectPoint, ProtectedTransportEndpointState> getProtectionSwitchStates(DeviceId deviceId);
+
+    /**
      * Set target power for a port if the port has configurable target power.
      *
      * @param deviceId DeviceId of the device to configure
diff --git a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmUtil.java b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmUtil.java
index 22e1791..3aef552 100644
--- a/apps/roadm/src/main/java/org/onosproject/roadm/RoadmUtil.java
+++ b/apps/roadm/src/main/java/org/onosproject/roadm/RoadmUtil.java
@@ -31,6 +31,11 @@
     public static final String NA = "N/A";
     public static final String UNKNOWN = "Unknown";
     public static final String NO_ROWS_MESSAGE = "No items found";
+    // Optical protection switch operations.
+    // There are 3 operations for protection switch now: AUTOMATIC, FORCE, MANUAL.
+    public static final String OPS_OPT_AUTO = "AUTOMATIC";
+    public static final String OPS_OPT_FORCE = "FORCE";
+    public static final String OPS_OPT_MANUAL = "MANUAL";
 
     private RoadmUtil() {
     }
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 6be2601..00e195a 100644
--- a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.css
+++ b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.css
@@ -80,7 +80,7 @@
 }
 
 #ov-roadm-port .mode-select select {
-    width: 150px;
+    width: 250px;
 }
 
 #ov-roadm-port .mode-select button {
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 43aeb93..f896ebd 100644
--- a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html
+++ b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.html
@@ -70,8 +70,8 @@
             </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>
+                <label class="mode-title">Protection Switch:</label>
+                <select ng-model="opsModeType" ng-options="mode.name 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>
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 128b72c..05f05dd 100644
--- a/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.js
+++ b/apps/roadm/src/main/resources/app/view/roadmPort/roadmPort.js
@@ -40,6 +40,10 @@
         $scope.showTargetPower = data.showTargetPower;
         $scope.showServiceState = data.showServiceState;
         $scope.showFlowIcon = data.showFlowIcon;
+        $scope.opsModeTypes = data.opsOperations;
+        if ($scope.opsModeType == null) {
+            $scope.opsModeType = $scope.opsModeTypes[0];
+        }
         $scope.$apply();
     }
 
@@ -47,7 +51,8 @@
         wss.sendEvent(SET_OPS_MODE_REQ,
             {
                 devId: $scope.devId,
-                index: $scope.opsModeType.index
+                index: $scope.opsModeType.index,
+                operation: $scope.opsModeType.operation
             });
     }
 
@@ -86,12 +91,7 @@
             $scope.deviceTip = 'Show device table';
             $scope.flowTip = 'Show flow view for this 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
+            $scope.opsModeType = null;
 
             var handlers = {};
             handlers[SET_TARGET_POWER_RESP] = portPowerCb;