Add ball detector indicator webserver.

Change-Id: Iddc5d76b909cadccbc27221af58758708199e5c5
diff --git a/y2016/dashboard/www/index.html b/y2016/dashboard/www/index.html
new file mode 100644
index 0000000..9b08d13
--- /dev/null
+++ b/y2016/dashboard/www/index.html
@@ -0,0 +1,246 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Spartan Dashboard</title>
+
+<script type="text/javascript">
+var escapable =
+  /[\x00-\x1f\ud800-\udfff\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufff0-\uffff]/g;
+var ws;
+var intervalTime = 50;
+var selected = 0;
+var reconnecting = false;
+var lastSampleID = 0;
+var resetTimeout;
+
+// Filter out junky JSON packets that will otherwise cause nasty decoding
+// errors. These errors come as a result of incomplete packets, corrupted data,
+// or any other artifacts that find themselves into the socket stream.
+
+function filterUnicode(quoted) {
+  escapable.lastIndex = 0;
+  if (!escapable.test(quoted)) return quoted;
+
+  return quoted.replace(escapable, '');
+}
+
+// Change the current data index to plot on the graph.
+// Get a new JSON packet from the websocket on the robot.
+function refresh() {
+  if (!reconnecting) ws.send(lastSampleID);
+}
+
+function initSocketLoop() {
+  ws = new WebSocket('ws://' + document.location.host + '/ws');
+
+  var numDatas = 0;
+
+  $(function() {
+
+    // Socket created & first opened.
+    ws.onopen = function() {
+      reconnecting = false;
+      refresh();
+      $('#message').text('');
+    };
+
+    // Socket disconnected.
+    ws.onclose = function() {
+      console.log('onclose');
+      reconnect();
+    };
+
+    ws.onmessage = function(message) {
+      message = message.data;
+      if(message.charAt(0) == "*"){
+        message = message.substring(1);
+        var names = message.split(",");
+        for (var i = numDatas; i < names.length; i++) {
+          $('#dataTable').append('<tr><td>' + names[i] + '</td><td></td></tr>');
+          numDatas++;
+        }
+        lastSampleID = 1;
+      }else{
+        var samples = message.split("$");
+        for(var x = 0;x < samples.length;x++){
+          var info = samples[x].split("%");
+          lastSampleID = info[0];
+
+          if(!(typeof info[2] === "undefined")){
+            var values = info[2].split(",");
+
+            // For the big indicator.
+            switch(parseInt(values[0])){
+              case 1:
+                $("#bigIndicator").css("background", "#00FF00");
+                $("#bigIndicator").css("color", "#000000");
+                $("#bigIndicator").text("Ball detected");
+                break;
+              case 2:
+                $("#bigIndicator").css("background", "#FFFF00");
+                $("#bigIndicator").css("color", "#000000");
+                $("#bigIndicator").text("Target seen");
+                break;
+              case 3:
+                $("#bigIndicator").css("background", "#0000FF");
+                $("#bigIndicator").css("color", "#000000");
+                $("#bigIndicator").text("Target aligned");
+                break;
+              case 0:
+              default:
+                $("#bigIndicator").css("background", "#000000");
+                $("#bigIndicator").css("color", "#444444");
+                $("#bigIndicator").text("No ball, no target");
+                break;
+            }
+
+            // Superstructure state indicator.
+            switch(parseInt(values[1])){
+              case 1:
+                $("#superstructureStateIndicator").css("background", "#FF0000");
+                $("#superstructureStateIndicator").css("color", "#000000");
+                $("#superstructureStateIndicator").text("Not zeroed");
+                break;
+              case 2:
+                $("#superstructureStateIndicator").css("background", "#FF8C00");
+                $("#superstructureStateIndicator").css("color", "#000000");
+                $("#superstructureStateIndicator").text("Estopped");
+                break;
+              case 0:
+              default:
+                $("#superstructureStateIndicator").css("background", "#000000");
+                $("#superstructureStateIndicator").css("color", "#444444");
+                $("#superstructureStateIndicator").text("Superstructure OK");
+                break;
+            }
+
+            // Auto mode indicator.
+            $("#autoModeIndicator").text("Mode: " + values[2]);
+            if (parseInt(values[2]) < 0){
+              $("#autoModeIndicator").css("visibility", "hidden");
+            } else {
+              $("#autoModeIndicator").css("visibility", "visible");
+            }
+
+            // Populate data table.
+            for(var y = 0;y < values.length;y++){
+              if(!(typeof info[1] === "undefined"
+                    || typeof values[y] === "undefined")){
+                $('#dataTable').find('tr').eq(y).find('td').eq(1)
+                  .text(values[y]);
+              }
+            }
+          }
+        }
+      }
+
+      setTimeout(refresh, intervalTime);
+    };
+
+    // Socket error, most likely due to a server-side error.
+    ws.onerror = function(error) {
+      console.log('onerror ' + error);
+    };
+  });
+}
+
+function reconnect() {
+  $('#message').text('Reconnecting...');
+  $('#dataTable').empty();
+  lastSampleID = 0;
+  reconnecting = true;
+
+  setTimeout(function(){
+    initSocketLoop()
+  }, 500);
+}
+
+window.onload = function() {
+  initSocketLoop();
+}
+</script>
+<script type="text/javascript" src='/lib/jquery-2.2.3.min.js'></script>
+<script type="text/javascript" src='/lib/canvasjs.min.js'></script>
+<style>
+body {
+  width: 100%;
+  margin-left: auto;
+  margin-right:auto;
+}
+
+#dataTable {
+  position: absolute;
+  top: 0;
+  background: #FFFFFF;
+  left: 0;
+  width: 200px;
+  cell-spacing:0;
+  cell-padding:0;
+  text-align: left;
+  z-index: 99999;
+}
+
+#headsUpDisplay {
+  width: 1000px;
+  position: absolute;
+  margin-left: auto;
+  margin-right: auto;
+  left: 0;
+  right: 0;
+  z-index: 10000000;
+}
+
+#message {
+  color: #FF0000;
+  text-align: center;
+  background: #000000;
+  font-size: 100px;
+}
+
+#indicatorContainer {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  background: #000000;
+  text-align: center;
+  font-size: 100px;
+}
+
+#bigIndicator {
+  background: #000000;
+}
+
+#superstructureStateIndicator {
+  background: #000000;
+  width: 60%;
+}
+
+#autoModeIndicator {
+  background: #FF0000;
+}
+</style>
+</head>
+<body>
+<!-- Data -->
+<table id="dataTable">
+</table>
+
+<div id="headsUpDisplay">
+  <p id="message"></p>
+  <p id="data"></p>
+</div>
+
+<table id="indicatorContainer">
+  <tr>
+    <td id="bigIndicator" colspan="2">Ball detected</td>
+  </tr>
+  <tr style="height:10%">
+    <td id="superstructureStateIndicator">Superstructure state</td>
+    <td id="autoModeIndicator">Auto mode</td>
+  </tr>
+</table>
+
+</body>
+</html>