Add robot status feedback to field webpage

Helps a lot in debugging what the robot is doing.

Change-Id: If3aa88e004952026f4d4b4b33ecb5622fc311eec
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/y2020/www/field.html b/y2020/www/field.html
index c75e7bf..14ebfb0 100644
--- a/y2020/www/field.html
+++ b/y2020/www/field.html
@@ -4,6 +4,53 @@
     <link rel="stylesheet" href="styles.css">
   </head>
   <body>
+    <div id="field"> </div>
+    <div id="readouts">
+      <div>
+        <div> X </div>
+        <div id="x"> NA </div>
+      </div>
+      <div>
+        <div> Y </div>
+        <div id="y"> NA </div>
+      </div>
+      <div>
+        <div> Theta </div>
+        <div id="theta"> NA </div>
+      </div>
+      <div>
+        <div> Shot Distance </div>
+        <div id="shot_distance"> NA </div>
+      </div>
+      <div>
+        <div> Finisher </div>
+        <div id="finisher"> NA </div>
+      </div>
+      <div>
+        <div> Left Accelerator </div>
+        <div id="left_accelerator"> NA </div>
+      </div>
+      <div>
+        <div> Right Accelerator </div>
+        <div id="right_accelerator"> NA </div>
+      </div>
+      <div>
+        <div> Inner Port </div>
+        <div id="inner_port"> NA </div>
+      </div>
+      <div>
+        <div> Hood </div>
+        <div id="hood"> NA </div>
+      </div>
+      <div>
+        <div> Turret </div>
+        <div id="turret"> NA </div>
+      </div>
+      <div>
+        <div> Intake </div>
+        <div id="intake"> NA </div>
+      </div>
+    </div>
   </body>
 </html>
 
diff --git a/y2020/www/field_handler.ts b/y2020/www/field_handler.ts
index 1a0b56a..1cba7eb 100644
--- a/y2020/www/field_handler.ts
+++ b/y2020/www/field_handler.ts
@@ -67,9 +67,20 @@
   private imageMatchResult =  new Map<string, ImageMatchResult>();
   private drivetrainStatus: DrivetrainStatus|null = null;
   private superstructureStatus: SuperstructureStatus|null = null;
+  private x: HTMLDivElement = (document.getElementById('x') as HTMLDivElement);
+  private y: HTMLDivElement = (document.getElementById('y') as HTMLDivElement);
+  private theta: HTMLDivElement = (document.getElementById('theta') as HTMLDivElement);
+  private shotDistance: HTMLDivElement = (document.getElementById('shot_distance') as HTMLDivElement);
+  private finisher: HTMLDivElement = (document.getElementById('finisher') as HTMLDivElement);
+  private leftAccelerator: HTMLDivElement = (document.getElementById('left_accelerator') as HTMLDivElement);
+  private rightAccelerator: HTMLDivElement = (document.getElementById('right_accelerator') as HTMLDivElement);
+  private innerPort: HTMLDivElement = (document.getElementById('inner_port') as HTMLDivElement);
+  private hood: HTMLDivElement = (document.getElementById('hood') as HTMLDivElement);
+  private turret: HTMLDivElement = (document.getElementById('turret') as HTMLDivElement);
+  private intake: HTMLDivElement = (document.getElementById('intake') as HTMLDivElement);
 
   constructor(private readonly connection: Connection) {
-    document.body.appendChild(this.canvas);
+    (document.getElementById('field') as HTMLElement).appendChild(this.canvas);
 
     this.connection.addConfigHandler(() => {
       // Go through and register handlers for both all the individual pis as
@@ -231,6 +242,22 @@
     ctx.restore();
   }
 
+  setZeroing(div: HTMLDivElement): void {
+        div.innerHTML = "zeroing";
+        div.classList.remove("faulted");
+        div.classList.add("zeroing");
+  }
+  setEstopped(div: HTMLDivElement): void {
+        div.innerHTML = "estopped";
+        div.classList.add("faulted");
+        div.classList.remove("zeroing");
+  }
+  setValue(div: HTMLDivElement, val: Number): void {
+        div.innerHTML = val.toFixed(4);
+        div.classList.remove("faulted");
+        div.classList.remove("zeroing");
+  }
+
   draw(): void {
     this.reset();
     this.drawField();
@@ -253,6 +280,50 @@
     }
 
     if (this.drivetrainStatus) {
+      if (!this.drivetrainStatus.zeroing().zeroed()) {
+        this.setZeroing(this.x);
+        this.setZeroing(this.y);
+        this.setZeroing(this.theta);
+      } else if (this.drivetrainStatus.zeroing().faulted()) {
+        this.setEstopped(this.x);
+        this.setEstopped(this.y);
+        this.setEstopped(this.theta);
+      } else {
+        this.setValue(this.x, this.drivetrainStatus.x());
+        this.setValue(this.y, this.drivetrainStatus.y());
+        this.setValue(this.theta, this.drivetrainStatus.theta());
+      }
+
+      this.shotDistance.innerHTML = this.superstructureStatus.aimer().shotDistance().toFixed(2);
+      this.finisher.innerHTML = this.superstructureStatus.shooter().finisher().angularVelocity().toFixed(2);
+      this.leftAccelerator.innerHTML = this.superstructureStatus.shooter().acceleratorLeft().angularVelocity().toFixed(2);
+      this.rightAccelerator.innerHTML = this.superstructureStatus.shooter().acceleratorRight().angularVelocity().toFixed(2);
+      if (this.superstructureStatus.aimer().aimingForInnerPort()) {
+        this.innerPort.innerHTML = "true";
+      } else {
+        this.innerPort.innerHTML = "false";
+      }
+      if (!this.superstructureStatus.hood().zeroed()) {
+        this.setZeroing(this.hood);
+      } else if (this.superstructureStatus.hood().estopped()) {
+        this.setEstopped(this.hood);
+      } else {
+        this.setValue(this.hood, this.superstructureStatus.hood().estimatorState().position());
+      }
+      if (!this.superstructureStatus.turret().zeroed()) {
+        this.setZeroing(this.turret);
+      } else if (this.superstructureStatus.turret().estopped()) {
+        this.setEstopped(this.turret);
+      } else {
+        this.setValue(this.turret, this.superstructureStatus.turret().estimatorState().position());
+      }
+      if (!this.superstructureStatus.intake().zeroed()) {
+        this.setZeroing(this.intake);
+      } else if (this.superstructureStatus.intake().estopped()) {
+        this.setEstopped(this.intake);
+      } else {
+        this.setValue(this.intake, this.superstructureStatus.intake().estimatorState().position());
+      }
       this.drawRobot(
           this.drivetrainStatus.x(), this.drivetrainStatus.y(),
           this.drivetrainStatus.theta(),
diff --git a/y2020/www/styles.css b/y2020/www/styles.css
index 23ceb21..21bd239 100644
--- a/y2020/www/styles.css
+++ b/y2020/www/styles.css
@@ -3,3 +3,31 @@
   border-bottom: 1px solid;
   font-size: 24px;
 }
+#field {
+  display: inline-block
+}
+#readouts {
+  display: inline-block;
+  vertical-align: top;
+  float: right;
+}
+
+#readouts > div {
+  display: table-row;
+  padding: 5px;
+}
+#readouts > div > div {
+  display: table-cell;
+  padding: 5px;
+  text-align: right;
+}
+
+.zeroing {
+  background-color: yellow;
+  border-radius: 10px;
+}
+
+.faulted {
+  background-color: red;
+  border-radius: 10px;
+}