Merge "Make downloader work a bit better on first-use"
diff --git a/aos/network/www/config_handler.ts b/aos/network/www/config_handler.ts
index 72b496e..ae9896c 100644
--- a/aos/network/www/config_handler.ts
+++ b/aos/network/www/config_handler.ts
@@ -3,16 +3,26 @@
export class ConfigHandler {
private readonly root_div = document.getElementById('config');
+ private readonly tree_div;
constructor(
private readonly config: Configuration,
- private readonly dataChannel: RTCDataChannel) {}
+ private readonly dataChannel: RTCDataChannel) {
+ const show_button = document.createElement('button');
+ show_button.addEventListener('click', () => this.toggleConfig());
+ const show_text = document.createTextNode('Show/Hide Config');
+ show_button.appendChild(show_text);
+ this.tree_div = document.createElement('div');
+ this.tree_div.hidden = true;
+ this.root_div.appendChild(show_button);
+ this.root_div.appendChild(this.tree_div);
+ }
printConfig() {
for (const i = 0; i < this.config.channelsLength(); i++) {
const channel_div = document.createElement('div');
channel_div.classList.add('channel');
- this.root_div.appendChild(channel_div);
+ this.tree_div.appendChild(channel_div);
const input_el = document.createElement('input');
input_el.setAttribute('data-index', i);
@@ -64,4 +74,8 @@
console.log('connect', array);
this.dataChannel.send(array.buffer.slice(array.byteOffset));
}
+
+ toggleConfig() {
+ this.tree_div.hidden = !this.tree_div.hidden;
+ }
}
diff --git a/y2020/BUILD b/y2020/BUILD
index f3ea4d6..4aa851f 100644
--- a/y2020/BUILD
+++ b/y2020/BUILD
@@ -166,6 +166,7 @@
srcs = ["web_proxy.sh"],
data = [
":config.json",
+ "//y2020/www:field_main_bundle",
"//aos/network:web_proxy_main",
"//y2020/www:files",
"//y2020/www:flatbuffers",
diff --git a/y2020/control_loops/drivetrain/localizer.cc b/y2020/control_loops/drivetrain/localizer.cc
index 4b164b5..8330289 100644
--- a/y2020/control_loops/drivetrain/localizer.cc
+++ b/y2020/control_loops/drivetrain/localizer.cc
@@ -23,6 +23,9 @@
return result;
}
+// Indices of the pis to use.
+const std::array<std::string, 3> kPisToUse{"pi1", "pi2", "pi3"};
+
} // namespace
Localizer::Localizer(
@@ -51,9 +54,11 @@
ekf_.P());
});
- image_fetchers_.emplace_back(
- event_loop_->MakeFetcher<frc971::vision::sift::ImageMatchResult>(
- "/pi1/camera"));
+ for (const auto &pi : kPisToUse) {
+ image_fetchers_.emplace_back(
+ event_loop_->MakeFetcher<frc971::vision::sift::ImageMatchResult>(
+ "/" + pi + "/camera"));
+ }
target_selector_.set_has_target(false);
}
@@ -89,9 +94,10 @@
aos::monotonic_clock::time_point now,
double left_encoder, double right_encoder,
double gyro_rate, const Eigen::Vector3d &accel) {
- for (auto &image_fetcher : image_fetchers_) {
+ for (size_t ii = 0; ii < kPisToUse.size(); ++ii) {
+ auto &image_fetcher = image_fetchers_[ii];
while (image_fetcher.FetchNext()) {
- HandleImageMatch(*image_fetcher);
+ HandleImageMatch(kPisToUse[ii], *image_fetcher);
}
}
ekf_.UpdateEncodersAndGyro(left_encoder, right_encoder, gyro_rate, U, accel,
@@ -99,13 +105,13 @@
}
void Localizer::HandleImageMatch(
- const frc971::vision::sift::ImageMatchResult &result) {
+ std::string_view pi, const frc971::vision::sift::ImageMatchResult &result) {
std::chrono::nanoseconds monotonic_offset(0);
clock_offset_fetcher_.Fetch();
if (clock_offset_fetcher_.get() != nullptr) {
for (const auto connection : *clock_offset_fetcher_->connections()) {
if (connection->has_node() && connection->node()->has_name() &&
- connection->node()->name()->string_view() == "pi1") {
+ connection->node()->name()->string_view() == pi) {
monotonic_offset =
std::chrono::nanoseconds(connection->monotonic_offset());
break;
diff --git a/y2020/control_loops/drivetrain/localizer.h b/y2020/control_loops/drivetrain/localizer.h
index 8636a7a..1d6f816 100644
--- a/y2020/control_loops/drivetrain/localizer.h
+++ b/y2020/control_loops/drivetrain/localizer.h
@@ -1,6 +1,8 @@
#ifndef Y2020_CONTROL_LOOPS_DRIVETRAIN_LOCALIZER_H_
#define Y2020_CONTROL_LOOPS_DRIVETRAIN_LOCALIZER_H_
+#include <string_view>
+
#include "aos/containers/ring_buffer.h"
#include "aos/events/event_loop.h"
#include "aos/network/message_bridge_server_generated.h"
@@ -69,8 +71,9 @@
double velocity = 0.0; // rad/sec
};
- // Processes new image data and updates the EKF.
- void HandleImageMatch(const frc971::vision::sift::ImageMatchResult &result);
+ // Processes new image data from the given pi and updates the EKF.
+ void HandleImageMatch(std::string_view pi,
+ const frc971::vision::sift::ImageMatchResult &result);
// Processes the most recent turret position and stores it in the turret_data_
// buffer.
diff --git a/y2020/www/BUILD b/y2020/www/BUILD
index 292618b..e8c227d 100644
--- a/y2020/www/BUILD
+++ b/y2020/www/BUILD
@@ -16,6 +16,18 @@
],
)
+ts_library(
+ name = "field_main",
+ srcs = [
+ "field_main.ts",
+ "field_handler.ts",
+ "constants.ts",
+ ],
+ deps = [
+ "//aos/network/www:proxy",
+ ],
+)
+
rollup_bundle(
name = "main_bundle",
entry_point = "y2020/www/main",
@@ -25,6 +37,15 @@
],
)
+rollup_bundle(
+ name = "field_main_bundle",
+ entry_point = "y2020/www/field_main",
+ deps = [
+ "field_main",
+ ],
+ visibility = ["//y2020:__subpackages__"],
+)
+
filegroup(
name = "files",
srcs = glob([
diff --git a/y2020/www/constants.ts b/y2020/www/constants.ts
new file mode 100644
index 0000000..b94d7a7
--- /dev/null
+++ b/y2020/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/y2020/www/field.html b/y2020/www/field.html
new file mode 100644
index 0000000..37452a3
--- /dev/null
+++ b/y2020/www/field.html
@@ -0,0 +1,12 @@
+<html>
+ <head>
+ <script src="flatbuffers.js"></script>
+ <script src="field_main_bundle.min.js" defer></script>
+ <link rel="stylesheet" href="styles.css">
+ </head>
+ <body>
+ <div id="config">
+ </div>
+ </body>
+</html>
+
diff --git a/y2020/www/field_handler.ts b/y2020/www/field_handler.ts
new file mode 100644
index 0000000..a960a63
--- /dev/null
+++ b/y2020/www/field_handler.ts
@@ -0,0 +1,167 @@
+import {FIELD_LENGTH, FIELD_WIDTH, FT_TO_M, IN_TO_M} from './constants';
+
+const FIELD_SIDE_Y = FIELD_WIDTH / 2;
+const FIELD_CENTER_X = (198.75 + 116) * IN_TO_M;
+
+const DS_WIDTH = 69 * IN_TO_M;
+const DS_ANGLE = 20 * Math.PI / 180;
+const DS_END_X = DS_WIDTH * Math.sin(DS_ANGLE);
+const OTHER_DS_X = FIELD_LENGTH - DS_END_X;
+const DS_INSIDE_Y = FIELD_SIDE_Y - DS_WIDTH * Math.cos(DS_ANGLE);
+
+const TRENCH_START_X = 206.57 * IN_TO_M;
+const TRENCH_END_X = FIELD_LENGTH - TRENCH_START_X;
+const TRENCH_WIDTH = 55.5 * IN_TO_M;
+const TRENCH_INSIDE = FIELD_SIDE_Y - TRENCH_WIDTH;
+
+const SPINNER_LENGTH = 30 * IN_TO_M;
+const SPINNER_TOP_X = 374.59 * IN_TO_M;
+const SPINNER_BOTTOM_X = SPINNER_TOP_X - SPINNER_LENGTH;
+
+const SHIELD_BOTTOM_X = FIELD_CENTER_X - 116 * IN_TO_M;
+const SHIELD_BOTTOM_Y = 43.75 * IN_TO_M;
+
+const SHIELD_TOP_X = FIELD_CENTER_X + 116 * IN_TO_M;
+const SHIELD_TOP_Y = -43.75 * IN_TO_M;
+
+const SHIELD_RIGHT_X = FIELD_CENTER_X - 51.06 * IN_TO_M;
+const SHIELD_RIGHT_Y = -112.88 * IN_TO_M;
+
+const SHIELD_LEFT_X = FIELD_CENTER_X + 51.06 * IN_TO_M;
+const SHIELD_LEFT_Y = 112.88 * IN_TO_M;
+
+const SHIELD_CENTER_TOP_X = (SHIELD_TOP_X + SHIELD_LEFT_X) / 2
+const SHIELD_CENTER_TOP_Y = (SHIELD_TOP_Y + SHIELD_LEFT_Y) / 2
+
+const SHIELD_CENTER_BOTTOM_X = (SHIELD_BOTTOM_X + SHIELD_RIGHT_X) / 2
+const SHIELD_CENTER_BOTTOM_Y = (SHIELD_BOTTOM_Y + SHIELD_RIGHT_Y) / 2
+
+const INITIATION_X = 120 * IN_TO_M;
+const FAR_INITIATION_X = FIELD_LENGTH - 120 * IN_TO_M;
+
+const TARGET_ZONE_TIP_X = 30 * IN_TO_M;
+const TARGET_ZONE_WIDTH = 48 * IN_TO_M;
+const LOADING_ZONE_WIDTH = 60 * IN_TO_M;
+
+export class FieldHandler {
+ private canvas = document.createElement('canvas');
+
+ constructor() {
+ document.body.appendChild(this.canvas);
+ }
+
+ drawField(): void {
+ const MY_COLOR = 'red';
+ const OTHER_COLOR = 'blue';
+ const ctx = this.canvas.getContext('2d');
+ // draw perimiter
+ ctx.beginPath();
+ ctx.moveTo(0, DS_INSIDE_Y);
+ ctx.lineTo(DS_END_X, FIELD_SIDE_Y);
+ ctx.lineTo(OTHER_DS_X, FIELD_SIDE_Y);
+ ctx.lineTo(FIELD_LENGTH, DS_INSIDE_Y);
+ ctx.lineTo(FIELD_LENGTH, -DS_INSIDE_Y);
+ ctx.lineTo(OTHER_DS_X, -FIELD_SIDE_Y);
+ ctx.lineTo(DS_END_X, -FIELD_SIDE_Y);
+ ctx.lineTo(0, -DS_INSIDE_Y);
+ ctx.lineTo(0, DS_INSIDE_Y);
+ ctx.stroke();
+
+ // draw shield generator
+ ctx.beginPath();
+ ctx.moveTo(SHIELD_BOTTOM_X, SHIELD_BOTTOM_Y);
+ ctx.lineTo(SHIELD_RIGHT_X, SHIELD_RIGHT_Y);
+ ctx.lineTo(SHIELD_TOP_X, SHIELD_TOP_Y);
+ ctx.lineTo(SHIELD_LEFT_X, SHIELD_LEFT_Y);
+ ctx.lineTo(SHIELD_BOTTOM_X, SHIELD_BOTTOM_Y);
+ ctx.moveTo(SHIELD_CENTER_TOP_X, SHIELD_CENTER_TOP_Y);
+ ctx.lineTo(SHIELD_CENTER_BOTTOM_X, SHIELD_CENTER_BOTTOM_Y);
+ ctx.stroke();
+
+ // draw trenches
+ ctx.strokeStyle = MY_COLOR;
+ ctx.beginPath();
+ ctx.moveTo(TRENCH_START_X, FIELD_SIDE_Y);
+ ctx.lineTo(TRENCH_START_X, TRENCH_INSIDE);
+ ctx.lineTo(TRENCH_END_X, TRENCH_INSIDE);
+ ctx.lineTo(TRENCH_END_X, FIELD_SIDE_Y);
+ ctx.stroke();
+
+ ctx.strokeStyle = OTHER_COLOR;
+ ctx.beginPath();
+ ctx.moveTo(TRENCH_START_X, -FIELD_SIDE_Y);
+ ctx.lineTo(TRENCH_START_X, -TRENCH_INSIDE);
+ ctx.lineTo(TRENCH_END_X, -TRENCH_INSIDE);
+ ctx.lineTo(TRENCH_END_X, -FIELD_SIDE_Y);
+ ctx.stroke();
+
+ ctx.strokeStyle = 'black';
+ ctx.beginPath();
+ ctx.moveTo(SPINNER_TOP_X, FIELD_SIDE_Y);
+ ctx.lineTo(SPINNER_TOP_X, TRENCH_INSIDE);
+ ctx.lineTo(SPINNER_BOTTOM_X, TRENCH_INSIDE);
+ ctx.lineTo(SPINNER_BOTTOM_X, FIELD_SIDE_Y);
+ ctx.moveTo(FIELD_LENGTH - SPINNER_TOP_X, -FIELD_SIDE_Y);
+ ctx.lineTo(FIELD_LENGTH - SPINNER_TOP_X, -TRENCH_INSIDE);
+ ctx.lineTo(FIELD_LENGTH - SPINNER_BOTTOM_X, -TRENCH_INSIDE);
+ ctx.lineTo(FIELD_LENGTH - SPINNER_BOTTOM_X, -FIELD_SIDE_Y);
+ ctx.stroke();
+
+ // draw initiation lines
+ ctx.beginPath();
+ ctx.moveTo(INITIATION_X, FIELD_SIDE_Y);
+ ctx.lineTo(INITIATION_X, -FIELD_SIDE_Y);
+ ctx.moveTo(FAR_INITIATION_X, FIELD_SIDE_Y);
+ ctx.lineTo(FAR_INITIATION_X, -FIELD_SIDE_Y);
+ ctx.stroke();
+
+ // draw target/loading zones
+ ctx.strokeStyle = MY_COLOR;
+ ctx.beginPath();
+ ctx.moveTo(0, DS_INSIDE_Y);
+ ctx.lineTo(TARGET_ZONE_TIP_X, DS_INSIDE_Y - 0.5 * TARGET_ZONE_WIDTH);
+ ctx.lineTo(0, DS_INSIDE_Y - TARGET_ZONE_WIDTH);
+
+ ctx.moveTo(FIELD_LENGTH, DS_INSIDE_Y);
+ ctx.lineTo(
+ FIELD_LENGTH - TARGET_ZONE_TIP_X,
+ DS_INSIDE_Y - 0.5 * LOADING_ZONE_WIDTH);
+ ctx.lineTo(FIELD_LENGTH, DS_INSIDE_Y - LOADING_ZONE_WIDTH);
+ ctx.stroke();
+
+ ctx.strokeStyle = OTHER_COLOR;
+ ctx.beginPath();
+ ctx.moveTo(0, -DS_INSIDE_Y);
+ ctx.lineTo(TARGET_ZONE_TIP_X, -(DS_INSIDE_Y - 0.5 * LOADING_ZONE_WIDTH));
+ ctx.lineTo(0, -(DS_INSIDE_Y - LOADING_ZONE_WIDTH));
+
+ ctx.moveTo(FIELD_LENGTH, -DS_INSIDE_Y);
+ ctx.lineTo(
+ FIELD_LENGTH - TARGET_ZONE_TIP_X,
+ -(DS_INSIDE_Y - 0.5 * TARGET_ZONE_WIDTH));
+ ctx.lineTo(FIELD_LENGTH, -(DS_INSIDE_Y - TARGET_ZONE_WIDTH));
+ ctx.stroke();
+ }
+
+ 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;
+ ctx.canvas.width = size / 2 + 10;
+ ctx.clearRect(0, 0, size, size / 2 + 10);
+
+ // Translate to center of bottom of display.
+ ctx.translate(size / 4, size);
+ // Coordinate system is:
+ // x -> forward.
+ // y -> to the left.
+ ctx.rotate(-Math.PI / 2);
+ ctx.scale(1, -1);
+ ctx.translate(5, 0);
+
+ 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/y2020/www/field_main.ts b/y2020/www/field_main.ts
new file mode 100644
index 0000000..adcaa27
--- /dev/null
+++ b/y2020/www/field_main.ts
@@ -0,0 +1,13 @@
+import {Connection} from 'aos/network/www/proxy';
+
+import {FieldHandler} from './field_handler';
+
+const conn = new Connection();
+
+conn.connect();
+
+const fieldHandler = new FieldHandler();
+
+fieldHandler.reset();
+fieldHandler.drawField();
+
diff --git a/y2020/www/main.ts b/y2020/www/main.ts
index d414eac..e4c0f10 100644
--- a/y2020/www/main.ts
+++ b/y2020/www/main.ts
@@ -1,6 +1,7 @@
import {Connection} from 'aos/network/www/proxy';
import {ImageHandler} from './image_handler';
+import {FieldHandler} from './field_handler';
const conn = new Connection();
diff --git a/y2020/y2020.json b/y2020/y2020.json
index 0e1a6ba..3d966b8 100644
--- a/y2020/y2020.json
+++ b/y2020/y2020.json
@@ -380,6 +380,42 @@
"max_size": 2000000
},
{
+ "name": "/pi3/camera",
+ "type": "frc971.vision.CameraImage",
+ "source_node": "pi3",
+ "frequency": 25,
+ "max_size": 620000,
+ "num_senders": 18
+ },
+ {
+ "name": "/pi3/camera",
+ "type": "frc971.vision.sift.ImageMatchResult",
+ "source_node": "pi3",
+ "frequency": 25,
+ "max_size": 10000,
+ "destination_nodes": [
+ {
+ "name": "roborio",
+ "priority": 1,
+ "time_to_live": 5000000
+ }
+ ]
+ },
+ {
+ "name": "/pi3/camera/detailed",
+ "type": "frc971.vision.sift.ImageMatchResult",
+ "source_node": "pi3",
+ "frequency": 25,
+ "max_size": 300000
+ },
+ {
+ "name": "/pi3/camera",
+ "type": "frc971.vision.sift.TrainingData",
+ "source_node": "pi3",
+ "frequency": 2,
+ "max_size": 2000000
+ },
+ {
"name": "/autonomous",
"type": "aos.common.actions.Status",
"source_node": "roborio"
@@ -491,6 +527,24 @@
},
{
"match": {
+ "name": "/camera",
+ "source_node": "pi3"
+ },
+ "rename": {
+ "name": "/pi3/camera"
+ }
+ },
+ {
+ "match": {
+ "name": "/camera/detailed",
+ "source_node": "pi3"
+ },
+ "rename": {
+ "name": "/pi3/camera/detailed"
+ }
+ },
+ {
+ "match": {
"name": "/aos",
"type": "aos.RobotState"
},