Added timeline computation to admin console

git-svn-id: https://hyracks.googlecode.com/svn/branches/hyracks_dev_next@799 123451ca-8445-de46-9d55-352943316053
diff --git a/hyracks-control-cc/src/main/java/edu/uci/ics/hyracks/control/cc/adminconsole/pages/IndexPage.java b/hyracks-control-cc/src/main/java/edu/uci/ics/hyracks/control/cc/adminconsole/pages/IndexPage.java
index 0c24ca4..3b9be36 100644
--- a/hyracks-control-cc/src/main/java/edu/uci/ics/hyracks/control/cc/adminconsole/pages/IndexPage.java
+++ b/hyracks-control-cc/src/main/java/edu/uci/ics/hyracks/control/cc/adminconsole/pages/IndexPage.java
@@ -75,7 +75,7 @@
                     item.add(new Label("start-time", o.getString("start-time")));
                     item.add(new Label("end-time", o.getString("end-time")));
                     PageParameters params = new PageParameters();
-                    params.add("job-id", o.getString("node-id"));
+                    params.add("job-id", o.getString("job-id"));
                     item.add(new BookmarkablePageLink<Object>("job-details", JobDetailsPage.class, params));
                 } catch (JSONException e) {
                     throw new RuntimeException(e);
diff --git a/hyracks-control-cc/src/main/java/edu/uci/ics/hyracks/control/cc/adminconsole/pages/JobDetailsPage.java b/hyracks-control-cc/src/main/java/edu/uci/ics/hyracks/control/cc/adminconsole/pages/JobDetailsPage.java
index 5f05ce0..6b7e143 100644
--- a/hyracks-control-cc/src/main/java/edu/uci/ics/hyracks/control/cc/adminconsole/pages/JobDetailsPage.java
+++ b/hyracks-control-cc/src/main/java/edu/uci/ics/hyracks/control/cc/adminconsole/pages/JobDetailsPage.java
@@ -14,9 +14,17 @@
  */
 package edu.uci.ics.hyracks.control.cc.adminconsole.pages;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
 import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.panel.EmptyPanel;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.util.string.StringValue;
+import org.json.JSONArray;
+import org.json.JSONObject;
 
 import edu.uci.ics.hyracks.api.job.JobId;
 import edu.uci.ics.hyracks.control.cc.ClusterControllerService;
@@ -27,6 +35,9 @@
 public class JobDetailsPage extends AbstractPage {
     private static final long serialVersionUID = 1L;
 
+    private static final int HEIGHT = 9;
+    private static final int WIDTH = 9;
+
     public JobDetailsPage(PageParameters params) throws Exception {
         ClusterControllerService ccs = getAdminConsoleApplication().getClusterControllerService();
 
@@ -45,5 +56,105 @@
         GetJobRunJSONWork gjrw = new GetJobRunJSONWork(ccs, jobId);
         ccs.getWorkQueue().scheduleAndSync(gjrw);
         add(new Label("job-run", gjrw.getJSON().toString()));
+
+        JSONObject jrO = gjrw.getJSON();
+
+        List<TaskProfile> taskProfiles = new ArrayList<TaskProfile>();
+        if (jrO.has("profile")) {
+            JSONObject pO = jrO.getJSONObject("profile");
+            if (pO.has("joblets")) {
+                JSONArray jobletsA = pO.getJSONArray("joblets");
+                for (int i = 0; i < jobletsA.length(); ++i) {
+                    JSONObject jobletO = jobletsA.getJSONObject(i);
+                    if (jobletO.has("tasks")) {
+                        JSONArray tasksA = jobletO.getJSONArray("tasks");
+                        for (int j = 0; j < tasksA.length(); ++j) {
+                            JSONObject taskO = tasksA.getJSONObject(j);
+                            String activityId = taskO.getString("activity-id");
+                            int partition = taskO.getInt("partition");
+                            int attempt = taskO.getInt("attempt");
+                            if (taskO.has("partition-send-profile")) {
+                                JSONArray taskProfilesA = taskO.getJSONArray("partition-send-profile");
+                                for (int k = 0; k < taskProfilesA.length(); ++k) {
+                                    JSONObject ppO = taskProfilesA.getJSONObject(k);
+                                    long openTime = ppO.getLong("open-time");
+                                    long closeTime = ppO.getLong("close-time");
+                                    JSONArray frameTimesA = ppO.getJSONArray("frame-times");
+                                    long[] frameTimes = new long[frameTimesA.length()];
+                                    for (int l = 0; l < frameTimes.length; ++l) {
+                                        frameTimes[l] = frameTimesA.getLong(l);
+                                    }
+                                    taskProfiles.add(new TaskProfile(activityId, partition, attempt, openTime,
+                                            closeTime, frameTimes));
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (!taskProfiles.isEmpty()) {
+            Collections.sort(taskProfiles, new Comparator<TaskProfile>() {
+                @Override
+                public int compare(TaskProfile o1, TaskProfile o2) {
+                    return o1.openTime < o2.openTime ? -1 : (o1.openTime > o2.openTime ? 1 : 0);
+                }
+            });
+            long startTime = taskProfiles.get(0).openTime;
+            long timeRange = taskProfiles.get(taskProfiles.size() - 1).closeTime - startTime;
+            int n = taskProfiles.size();
+            StringBuilder buffer = new StringBuilder();
+            buffer.append("<svg viewBox=\"0 0 ").append((timeRange + 1) * (WIDTH + 1))
+                    .append(' ').append((n + 1) * (HEIGHT + 1)).append("\" version=\"1.1\"\n");
+            buffer.append("xmlns=\"http://www.w3.org/2000/svg\">\n");
+            for (int i = 0; i < n; ++i) {
+                TaskProfile tp = taskProfiles.get(i);
+                open(buffer, i, tp.openTime - startTime);
+                for (long ft : tp.frameTimes) {
+                    nextFrame(buffer, i, ft - startTime);
+                }
+                close(buffer, i, tp.closeTime - startTime);
+            }
+            buffer.append("</svg>");
+            Label markup = new Label("job-timeline", buffer.toString());
+            markup.setEscapeModelStrings(false);
+            add(markup);
+        } else {
+            add(new EmptyPanel("job-timeline"));
+        }
+    }
+
+    private void open(StringBuilder buffer, int i, long openTime) {
+        buffer.append("<rect x=\"").append(openTime * (WIDTH + 1)).append("\" y=\"").append(i * (HEIGHT + 1))
+                .append("\" width=\"").append(WIDTH).append("\" height=\"").append(HEIGHT).append("\"/>\n");
+    }
+
+    private void close(StringBuilder buffer, int i, long closeTime) {
+        buffer.append("<rect x=\"").append(closeTime * (WIDTH + 1)).append("\" y=\"").append(i * (HEIGHT + 1))
+                .append("\" width=\"").append(WIDTH).append("\" height=\"").append(HEIGHT).append("\"/>\n");
+    }
+
+    private void nextFrame(StringBuilder buffer, int i, long frameTime) {
+        buffer.append("<rect x=\"").append(frameTime * (WIDTH + 1)).append("\" y=\"").append(i * (HEIGHT + 1))
+                .append("\" width=\"").append(WIDTH).append("\" height=\"").append(HEIGHT).append("\"/>\n");
+    }
+
+    private static class TaskProfile {
+        private String activityId;
+        private int partition;
+        private int attempt;
+        private long openTime;
+        private long closeTime;
+        private long[] frameTimes;
+
+        public TaskProfile(String activityId, int partition, int attempt, long openTime, long closeTime,
+                long[] frameTimes) {
+            this.activityId = activityId;
+            this.partition = partition;
+            this.activityId = activityId;
+            this.openTime = openTime;
+            this.closeTime = closeTime;
+            this.frameTimes = frameTimes;
+        }
     }
 }
\ No newline at end of file
diff --git a/hyracks-control-cc/src/main/resources/edu/uci/ics/hyracks/control/cc/adminconsole/pages/JobDetailsPage.html b/hyracks-control-cc/src/main/resources/edu/uci/ics/hyracks/control/cc/adminconsole/pages/JobDetailsPage.html
index da6d8a6..f11e480 100644
--- a/hyracks-control-cc/src/main/resources/edu/uci/ics/hyracks/control/cc/adminconsole/pages/JobDetailsPage.html
+++ b/hyracks-control-cc/src/main/resources/edu/uci/ics/hyracks/control/cc/adminconsole/pages/JobDetailsPage.html
@@ -5,4 +5,5 @@
     </div>
     <div id="job-run" wicket:id="job-run" style="display: none;">
     </div>
+    <div wicket:id="job-timeline"></div>
 </wicket:extend>
\ No newline at end of file