Add y2023 folder

2022 Bot specific things were removed

Change-Id: I6563d477a65bf2eae5d39933003742bd372cf82e
Signed-off-by: Maxwell Henderson <mxwhenderson@gmail.com>
Signed-off-by: Xander Yee <xander.yee@gmail.com>
diff --git a/y2023/www/2022.png b/y2023/www/2022.png
new file mode 100644
index 0000000..68087bd
--- /dev/null
+++ b/y2023/www/2022.png
Binary files differ
diff --git a/y2023/www/BUILD b/y2023/www/BUILD
new file mode 100644
index 0000000..5dae3c6
--- /dev/null
+++ b/y2023/www/BUILD
@@ -0,0 +1,53 @@
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
+load("//tools/build_rules:js.bzl", "rollup_bundle")
+load("//frc971/downloader:downloader.bzl", "aos_downloader_dir")
+
+filegroup(
+    name = "files",
+    srcs = glob([
+        "**/*.html",
+        "**/*.css",
+        "**/*.png",
+    ]),
+    visibility = ["//visibility:public"],
+)
+
+ts_library(
+    name = "field_main",
+    srcs = [
+        "constants.ts",
+        "field_handler.ts",
+        "field_main.ts",
+    ],
+    target_compatible_with = ["@platforms//os:linux"],
+    deps = [
+        "//aos/network:connect_ts_fbs",
+        "//aos/network:web_proxy_ts_fbs",
+        "//aos/network/www:proxy",
+        "//frc971/control_loops/drivetrain:drivetrain_status_ts_fbs",
+        "//y2023/control_loops/superstructure:superstructure_status_ts_fbs",
+        "@com_github_google_flatbuffers//ts:flatbuffers_ts",
+    ],
+)
+
+rollup_bundle(
+    name = "field_main_bundle",
+    entry_point = "field_main.ts",
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//y2023:__subpackages__"],
+    deps = [
+        ":field_main",
+    ],
+)
+
+aos_downloader_dir(
+    name = "www_files",
+    srcs = [
+        ":field_main_bundle.min.js",
+        ":files",
+        "//frc971/analysis:plot_index_bundle.min.js",
+    ],
+    dir = "www",
+    target_compatible_with = ["@platforms//os:linux"],
+    visibility = ["//visibility:public"],
+)
diff --git a/y2023/www/constants.ts b/y2023/www/constants.ts
new file mode 100644
index 0000000..b94d7a7
--- /dev/null
+++ b/y2023/www/constants.ts
@@ -0,0 +1,7 @@
+// Conversion constants to meters
+export const IN_TO_M = 0.0254;
+export const FT_TO_M = 0.3048;
+// Dimensions of the field in meters
+export const FIELD_WIDTH = 26 * FT_TO_M + 11.25 * IN_TO_M;
+export const FIELD_LENGTH = 52 * FT_TO_M + 5.25 * IN_TO_M;
+
diff --git a/y2023/www/field.html b/y2023/www/field.html
new file mode 100644
index 0000000..f39c1a4
--- /dev/null
+++ b/y2023/www/field.html
@@ -0,0 +1,126 @@
+<html>
+  <head>
+    <script src="field_main_bundle.min.js" defer></script>
+    <link rel="stylesheet" href="styles.css">
+  </head>
+  <body>
+    <div id="field"> </div>
+    <div id="legend"> </div>
+    <div id="readouts">
+      <table>
+        <tr>
+          <th colspan="2">Robot State</th>
+        </tr>
+        <tr>
+          <td>X</td>
+          <td id="x"> NA </td>
+        </tr>
+        <tr>
+          <td>Y</td>
+          <td id="y"> NA </td>
+        </tr>
+        <tr>
+          <td>Theta</td>
+          <td id="theta"> NA </td>
+        </tr>
+      </table>
+
+      <table>
+        <tr>
+          <th colspan="2">Aiming</th>
+        </tr>
+        <tr>
+          <td>Shot distance</td>
+          <td id="shot_distance"> NA </td>
+        </tr>
+        <tr>
+          <td>Turret</td>
+          <td id="turret"> NA </td>
+        </tr>
+      </table>
+
+      <table>
+        <tr>
+          <th colspan="2">Catapult</th>
+        </tr>
+        <tr>
+          <td>Fire</td>
+          <td id="fire"> NA </td>
+        </tr>
+        <tr>
+          <td>Solve Time</td>
+          <td id="mpc_solve_time"> NA </td>
+        </tr>
+        <tr>
+          <td>MPC Active</td>
+          <td id="mpc_horizon"> NA </td>
+        </tr>
+        <tr>
+          <td>Shot Count</td>
+          <td id="shot_count"> NA </td>
+        </tr>
+        <tr>
+          <td>Position</td>
+          <td id="catapult"> NA </td>
+        </tr>
+      </table>
+
+      <table>
+        <tr>
+          <th colspan="2">Superstructure</th>
+        </tr>
+        <tr>
+          <td>State</td>
+          <td id="superstructure_state"> NA </td>
+        </tr>
+        <tr>
+          <td>Intake State</td>
+          <td id="intake_state"> NA </td>
+        </tr>
+        <tr>
+          <td>Reseating</td>
+          <td id="reseating_in_catapult"> NA </td>
+        </tr>
+        <tr>
+          <td>Flippers Open</td>
+          <td id="flippers_open"> NA </td>
+        </tr>
+        <tr>
+          <td>Climber</td>
+          <td id="climber"> NA </td>
+        </tr>
+      </table>
+
+      <table>
+        <tr>
+          <th colspan="2">Intakes</th>
+        </tr>
+        <tr>
+          <td>Front Intake</td>
+          <td id="front_intake"> NA </td>
+        </tr>
+        <tr>
+          <td>Back Intake</td>
+          <td id="back_intake"> NA </td>
+        </tr>
+      </table>
+
+      <table>
+        <tr>
+          <th colspan="2">Images</th>
+        </tr>
+        <tr>
+          <td> Images Accepted </td>
+          <td id="images_accepted"> NA </td>
+        </tr>
+        <tr>
+          <td> Images Rejected </td>
+          <td id="images_rejected"> NA </td>
+        </tr>
+      </table>
+    </div>
+    <div id="vision_readouts">
+    </div>
+  </body>
+</html>
+
diff --git a/y2023/www/field_handler.ts b/y2023/www/field_handler.ts
new file mode 100644
index 0000000..67566ea
--- /dev/null
+++ b/y2023/www/field_handler.ts
@@ -0,0 +1,210 @@
+import {ByteBuffer} from 'flatbuffers';
+import {Connection} from '../../aos/network/www/proxy';
+import {Status as SuperstructureStatus} from '../control_loops/superstructure/superstructure_status_generated'
+import {Status as DrivetrainStatus} from '../../frc971/control_loops/drivetrain/drivetrain_status_generated';
+
+import {FIELD_LENGTH, FIELD_WIDTH, FT_TO_M, IN_TO_M} from './constants';
+
+// (0,0) is field center, +X is toward red DS
+const FIELD_SIDE_Y = FIELD_WIDTH / 2;
+const FIELD_EDGE_X = FIELD_LENGTH / 2;
+
+const ROBOT_WIDTH = 34 * IN_TO_M;
+const ROBOT_LENGTH = 36 * IN_TO_M;
+
+const PI_COLORS = ['#ff00ff', '#ffff00', '#00ffff', '#ffa500'];
+
+export class FieldHandler {
+  private canvas = document.createElement('canvas');
+  private drivetrainStatus: DrivetrainStatus|null = null;
+  private superstructureStatus: SuperstructureStatus|null = null;
+
+  // Image information indexed by timestamp (seconds since the epoch), so that
+  // we can stop displaying images after a certain amount of time.
+    private x: HTMLElement = (document.getElementById('x') as HTMLElement);
+  private y: HTMLElement = (document.getElementById('y') as HTMLElement);
+  private theta: HTMLElement =
+      (document.getElementById('theta') as HTMLElement);
+  private superstructureState: HTMLElement =
+      (document.getElementById('superstructure_state') as HTMLElement);
+  private imagesAcceptedCounter: HTMLElement =
+      (document.getElementById('images_accepted') as HTMLElement);
+  private imagesRejectedCounter: HTMLElement =
+      (document.getElementById('images_rejected') as HTMLElement);
+  private fieldImage: HTMLImageElement = new Image();
+
+  constructor(private readonly connection: Connection) {
+    (document.getElementById('field') as HTMLElement).appendChild(this.canvas);
+
+    this.fieldImage.src = "2022.png";
+
+    for (let ii = 0; ii < PI_COLORS.length; ++ii) {
+      const legendEntry = document.createElement('div');
+      legendEntry.style.color = PI_COLORS[ii];
+      legendEntry.innerHTML = 'PI' + (ii + 1).toString()
+      document.getElementById('legend').appendChild(legendEntry);
+    }
+
+    this.connection.addConfigHandler(() => {
+      // Visualization message is reliable so that we can see *all* the vision
+      // matches.
+      this.connection.addHandler(
+          '/drivetrain', "frc971.control_loops.drivetrain.Status", (data) => {
+            this.handleDrivetrainStatus(data);
+          });
+      this.connection.addHandler(
+          '/superstructure', "y2023.control_loops.superstructure.Status",
+          (data) => {
+            this.handleSuperstructureStatus(data);
+          });
+    });
+  }
+
+  private handleDrivetrainStatus(data: Uint8Array): void {
+    const fbBuffer = new ByteBuffer(data);
+    this.drivetrainStatus = DrivetrainStatus.getRootAsStatus(fbBuffer);
+  }
+
+  private handleSuperstructureStatus(data: Uint8Array): void {
+    const fbBuffer = new ByteBuffer(data);
+    this.superstructureStatus = SuperstructureStatus.getRootAsStatus(fbBuffer);
+  }
+
+  drawField(): void {
+    const ctx = this.canvas.getContext('2d');
+    ctx.save();
+    ctx.scale(-1.0, 1.0);
+    ctx.drawImage(
+        this.fieldImage, 0, 0, this.fieldImage.width, this.fieldImage.height,
+        -FIELD_EDGE_X, -FIELD_SIDE_Y, FIELD_LENGTH, FIELD_WIDTH);
+    ctx.restore();
+  }
+
+  drawCamera(
+      x: number, y: number, theta: number, color: string = 'blue',
+      extendLines: boolean = true): void {
+    const ctx = this.canvas.getContext('2d');
+    ctx.save();
+    ctx.translate(x, y);
+    ctx.rotate(theta);
+    ctx.strokeStyle = color;
+    ctx.beginPath();
+    ctx.moveTo(0.5, 0.5);
+    ctx.lineTo(0, 0);
+    if (extendLines) {
+      ctx.lineTo(100.0, 0);
+      ctx.lineTo(0, 0);
+    }
+    ctx.lineTo(0.5, -0.5);
+    ctx.stroke();
+    ctx.beginPath();
+    ctx.arc(0, 0, 0.25, -Math.PI / 4, Math.PI / 4);
+    ctx.stroke();
+    ctx.restore();
+  }
+
+  drawRobot(
+      x: number, y: number, theta: number, turret: number|null,
+      color: string = 'blue', dashed: boolean = false,
+      extendLines: boolean = true): void {
+    const ctx = this.canvas.getContext('2d');
+    ctx.save();
+    ctx.translate(x, y);
+    ctx.rotate(theta);
+    ctx.strokeStyle = color;
+    ctx.lineWidth = ROBOT_WIDTH / 10.0;
+    if (dashed) {
+      ctx.setLineDash([0.05, 0.05]);
+    } else {
+      // Empty array = solid line.
+      ctx.setLineDash([]);
+    }
+    ctx.rect(-ROBOT_LENGTH / 2, -ROBOT_WIDTH / 2, ROBOT_LENGTH, ROBOT_WIDTH);
+    ctx.stroke();
+
+    // Draw line indicating which direction is forwards on the robot.
+    ctx.beginPath();
+    ctx.moveTo(0, 0);
+    if (extendLines) {
+      ctx.lineTo(1000.0, 0);
+    } else {
+      ctx.lineTo(ROBOT_LENGTH / 2.0, 0);
+    }
+    ctx.stroke();
+
+    ctx.restore();
+  }
+
+  setZeroing(div: HTMLElement): void {
+    div.innerHTML = 'zeroing';
+    div.classList.remove('faulted');
+    div.classList.add('zeroing');
+    div.classList.remove('near');
+  }
+
+  setEstopped(div: HTMLElement): void {
+    div.innerHTML = 'estopped';
+    div.classList.add('faulted');
+    div.classList.remove('zeroing');
+    div.classList.remove('near');
+  }
+
+  setTargetValue(
+      div: HTMLElement, target: number, val: number, tolerance: number): void {
+    div.innerHTML = val.toFixed(4);
+    div.classList.remove('faulted');
+    div.classList.remove('zeroing');
+    if (Math.abs(target - val) < tolerance) {
+      div.classList.add('near');
+    } else {
+      div.classList.remove('near');
+    }
+  }
+
+  setValue(div: HTMLElement, val: number): void {
+    div.innerHTML = val.toFixed(4);
+    div.classList.remove('faulted');
+    div.classList.remove('zeroing');
+    div.classList.remove('near');
+  }
+
+  draw(): void {
+    this.reset();
+    this.drawField();
+
+    // Draw the matches with debugging information from the localizer.
+    const now = Date.now() / 1000.0;
+    
+    if (this.drivetrainStatus && this.drivetrainStatus.trajectoryLogging()) {
+      this.drawRobot(
+          this.drivetrainStatus.trajectoryLogging().x(),
+          this.drivetrainStatus.trajectoryLogging().y(),
+          this.drivetrainStatus.trajectoryLogging().theta(), null, "#000000FF",
+          false);
+    }
+
+    window.requestAnimationFrame(() => this.draw());
+  }
+
+  reset(): void {
+    const ctx = this.canvas.getContext('2d');
+    ctx.setTransform(1, 0, 0, 1, 0, 0);
+    const size = window.innerHeight * 0.9;
+    ctx.canvas.height = size;
+    const width = size / 2 + 20;
+    ctx.canvas.width = width;
+    ctx.clearRect(0, 0, size, width);
+
+    // Translate to center of display.
+    ctx.translate(width / 2, size / 2);
+    // Coordinate system is:
+    // x -> forward.
+    // y -> to the left.
+    ctx.rotate(-Math.PI / 2);
+    ctx.scale(1, -1);
+
+    const M_TO_PX = (size - 10) / FIELD_LENGTH;
+    ctx.scale(M_TO_PX, M_TO_PX);
+    ctx.lineWidth = 1 / M_TO_PX;
+  }
+}
diff --git a/y2023/www/field_main.ts b/y2023/www/field_main.ts
new file mode 100644
index 0000000..7e2e392
--- /dev/null
+++ b/y2023/www/field_main.ts
@@ -0,0 +1,12 @@
+import {Connection} from 'org_frc971/aos/network/www/proxy';
+
+import {FieldHandler} from './field_handler';
+
+const conn = new Connection();
+
+conn.connect();
+
+const fieldHandler = new FieldHandler(conn);
+
+fieldHandler.draw();
+
diff --git a/y2023/www/index.html b/y2023/www/index.html
new file mode 100644
index 0000000..e4e185e
--- /dev/null
+++ b/y2023/www/index.html
@@ -0,0 +1,6 @@
+<html>
+  <body>
+    <a href="field.html">Field Visualization</a><br>
+    <a href="plotter.html">Plots</a>
+  </body>
+</html>
diff --git a/y2023/www/plotter.html b/y2023/www/plotter.html
new file mode 100644
index 0000000..629ceaa
--- /dev/null
+++ b/y2023/www/plotter.html
@@ -0,0 +1,7 @@
+<html>
+  <head>
+    <script src="plot_index_bundle.min.js" defer></script>
+  </head>
+  <body>
+  </body>
+</html>
diff --git a/y2023/www/styles.css b/y2023/www/styles.css
new file mode 100644
index 0000000..c486115
--- /dev/null
+++ b/y2023/www/styles.css
@@ -0,0 +1,81 @@
+.channel {
+  display: flex;
+  border-bottom: 1px solid;
+  font-size: 24px;
+}
+#field {
+  display: inline-block
+}
+
+#targets,
+#readouts,
+#vision_readouts {
+  display: inline-block;
+  vertical-align: top;
+  float: right;
+}
+
+#legend {
+  display: inline-block;
+}
+
+#outer_target {
+  border: 1px solid black;
+  width: 140px;
+  background-color: white;
+}
+
+#inner_target {
+  width: 60px;
+  height: 60px;
+  margin: 40px;
+  border: 1px solid black;
+  background-color: white;
+}
+
+#outer_target.targetted,
+#inner_target.targetted {
+  background-color: green;
+}
+
+table, th, td {
+  border: 1px solid black;
+  border-collapse: collapse;
+  padding: 5px;
+  margin: 10px;
+}
+
+th, td {
+  text-align: right;
+  width: 70px;
+}
+
+td:first-child {
+  width: 150px;
+}
+
+.near {
+  background-color: LightGreen;
+  border-radius: 10px;
+}
+
+.zeroing {
+  background-color: yellow;
+  border-radius: 10px;
+}
+
+.faulted {
+  background-color: red;
+  border-radius: 10px;
+}
+
+#vision_readouts > div {
+  display: table-row;
+  padding: 5px;
+}
+
+#vision_readouts > div > div {
+  display: table-cell;
+  padding: 5px;
+  text-align: right;
+}