[ONOS-3851] Initial implementation of Web GUI of CPMan

- Revise chart model to provide default label
- Visualize control message stats per device

Change-Id: I88b8e63ce92114907bba185b1906569fa8cc0b83
diff --git a/apps/cpman/app/src/main/java/org/onosproject/cpman/gui/CpmanViewMessageHandler.java b/apps/cpman/app/src/main/java/org/onosproject/cpman/gui/CpmanViewMessageHandler.java
index 7d0785f3..2d4e28f 100644
--- a/apps/cpman/app/src/main/java/org/onosproject/cpman/gui/CpmanViewMessageHandler.java
+++ b/apps/cpman/app/src/main/java/org/onosproject/cpman/gui/CpmanViewMessageHandler.java
@@ -16,44 +16,122 @@
 package org.onosproject.cpman.gui;
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.LocalDateTime;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cpman.ControlLoadSnapshot;
+import org.onosproject.cpman.ControlMetricType;
+import org.onosproject.cpman.ControlPlaneMonitorService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
 import org.onosproject.ui.RequestHandler;
 import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.chart.ChartModel;
+import org.onosproject.ui.chart.ChartRequestHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.util.Collection;
-import java.util.Random;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import static org.onosproject.cpman.ControlResource.CONTROL_MESSAGE_METRICS;
+import static org.onosproject.cpman.ControlResource.Type.CONTROL_MESSAGE;
 
 /**
  * CpmanViewMessageHandler class implementation.
  */
 public class CpmanViewMessageHandler extends UiMessageHandler {
 
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
     private static final String CPMAN_DATA_REQ = "cpmanDataRequest";
     private static final String CPMAN_DATA_RESP = "cpmanDataResponse";
+    private static final String CPMANS = "cpmans";
 
-    private static final String RANDOM = "random";
+    // TODO: we assume that server side always returns 60 data points
+    // to feed 1 hour time slots, later this should make to be configurable
+    private static final int NUM_OF_DATA_POINTS = 60;
+
+    private static final int MILLI_CONV_UNIT = 1000;
 
     @Override
     protected Collection<RequestHandler> createRequestHandlers() {
         return ImmutableSet.of(
-                new CpmanDataRequestHandler()
+                new ControlMessageRequest()
         );
     }
 
-    // handler for sample data requests
-    private final class CpmanDataRequestHandler extends RequestHandler {
+    private final class ControlMessageRequest extends ChartRequestHandler {
 
-        private CpmanDataRequestHandler() {
-            super(CPMAN_DATA_REQ);
+        private ControlMessageRequest() {
+            super(CPMAN_DATA_REQ, CPMAN_DATA_RESP, CPMANS);
         }
 
         @Override
-        public void process(long sid, ObjectNode payload) {
-            ObjectNode result = objectNode();
-            Random random = new Random();
-            result.put(RANDOM, random.nextInt(50) + 1);
+        protected String[] getSeries() {
+            return CONTROL_MESSAGE_METRICS.stream().map(type ->
+                    StringUtils.lowerCase(type.name())).toArray(String[]::new);
+        }
 
-            sendMessage(CPMAN_DATA_RESP, 0, result);
+        @Override
+        protected void populateChart(ChartModel cm, ObjectNode payload) {
+            String uri = string(payload, "devId");
+            if (!Strings.isNullOrEmpty(uri)) {
+                Map<ControlMetricType, Long[]> data = Maps.newHashMap();
+                DeviceId deviceId = DeviceId.deviceId(uri);
+                ClusterService cs = get(ClusterService.class);
+                ControlPlaneMonitorService cpms = get(ControlPlaneMonitorService.class);
+
+                if (cpms.availableResources(CONTROL_MESSAGE).contains(deviceId.toString())) {
+                    LocalDateTime ldt = null;
+
+                    try {
+                        for (ControlMetricType cmt : CONTROL_MESSAGE_METRICS) {
+                            ControlLoadSnapshot cls = cpms.getLoad(cs.getLocalNode().id(),
+                                    cmt, NUM_OF_DATA_POINTS, TimeUnit.MINUTES,
+                                    Optional.of(deviceId)).get();
+                            data.put(cmt, ArrayUtils.toObject(cls.recent()));
+                            if (ldt == null) {
+                                ldt = new LocalDateTime(cls.time() * MILLI_CONV_UNIT);
+                            }
+                        }
+
+                        for (int i = 0; i < NUM_OF_DATA_POINTS; i++) {
+                            Map<String, Long> local = Maps.newHashMap();
+                            for (ControlMetricType cmt : CONTROL_MESSAGE_METRICS) {
+                                local.put(StringUtils.lowerCase(cmt.name()), data.get(cmt)[i]);
+                            }
+
+                            local.put(LABEL, ldt.minusMinutes(NUM_OF_DATA_POINTS - i).toDateTime().getMillis());
+
+                            populateMetric(cm.addDataPoint(ldt.minusMinutes(NUM_OF_DATA_POINTS - i)
+                                    .toDateTime().getMillis()), local);
+                        }
+
+                    } catch (InterruptedException | ExecutionException e) {
+                        log.warn(e.getMessage());
+                    }
+                }
+            } else {
+                DeviceService ds = get(DeviceService.class);
+                ds.getAvailableDevices();
+            }
+        }
+
+        private void populateAllDevs(ChartModel.DataPoint dataPoint, Map<String, Long> data) {
+
+        }
+
+        private void populateMetric(ChartModel.DataPoint dataPoint,
+                                    Map<String, Long> data) {
+            data.forEach((k, v) -> dataPoint.data(k, v.doubleValue()));
         }
     }
 }