Merge "Use the host libcuda and libcudart" into main
diff --git a/.bazelignore b/.bazelignore
index 7fa8e45..cce9d62 100644
--- a/.bazelignore
+++ b/.bazelignore
@@ -1,5 +1,6 @@
external
node_modules
+frc971/control_loops/swerve/spline_ui/www/node_modules
scouting/webserver/requests/messages/node_modules
scouting/www/node_modules
scouting/www/driver_ranking/node_modules
diff --git a/WORKSPACE b/WORKSPACE
index 6be969d..ad59314 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -944,6 +944,7 @@
name = "npm",
data = [
"//aos/analysis/foxglove_extension:package.json",
+ "//control_loops/swerve/spline_ui/www:package.json",
"@//:package.json",
"@//:pnpm-workspace.yaml",
"@//scouting/webserver/requests/messages:package.json",
diff --git a/frc971/control_loops/swerve/BUILD b/frc971/control_loops/swerve/BUILD
index fcd60a1..5adef05 100644
--- a/frc971/control_loops/swerve/BUILD
+++ b/frc971/control_loops/swerve/BUILD
@@ -208,6 +208,7 @@
":physics_test_utils",
"@pip//casadi",
"@pip//numpy",
+ "@pip//scipy",
],
)
@@ -250,3 +251,17 @@
"@pip//scipy",
],
)
+
+py_binary(
+ name = "debug_fatrop",
+ srcs = [
+ "debug_fatrop.py",
+ ],
+ deps = [
+ "@pip//casadi",
+ "@pip//matplotlib",
+ "@pip//numpy",
+ "@pip//pygobject",
+ "@pip//scipy",
+ ],
+)
diff --git a/frc971/control_loops/swerve/casadi_velocity_mpc.py b/frc971/control_loops/swerve/casadi_velocity_mpc.py
index 698cce7..0d3e177 100644
--- a/frc971/control_loops/swerve/casadi_velocity_mpc.py
+++ b/frc971/control_loops/swerve/casadi_velocity_mpc.py
@@ -15,7 +15,7 @@
class MPC(object):
- def __init__(self):
+ def __init__(self, solver='fatrop'):
self.fdot = dynamics.swerve_full_dynamics(
casadi.SX.sym("X", dynamics.NUM_STATES, 1),
casadi.SX.sym("U", 8, 1))
@@ -41,9 +41,7 @@
casadi.SX.sym("U", 8, 1)) for i in range(4)
]
- self.N = 25
-
- # TODO(austin): Can we approximate sin/cos/atan for the initial operating point to solve faster if we need it?
+ self.N = 50
# Start with an empty nonlinear program.
self.w = []
@@ -59,33 +57,42 @@
# We care about the linear and angular velocities only.
self.R = casadi.MX.sym('R', 3)
- # Instead of an equality constraint on the goal, what about:
- # self.w += [Xk]
- # lbw += [0, 1]
- # ubw += [0, 1]
- # w0 += [0, 1]
+ # Make Xn and U for each step. fatrop wants us to interleave the control variables and
+ # states so that it can produce a banded/structured problem which it can solve a lot
+ # faster.
+ Xn_variables = []
+ U_variables = []
+ for i in range(self.N):
+ U_variables.append(casadi.MX.sym(f'U{i}', 8))
- Xn = casadi.MX.sym('Xn', dynamics.NUM_VELOCITY_STATES, self.N - 1)
- U = casadi.MX.sym('U', 8, self.N)
+ if i == 0:
+ continue
+
+ Xn_variables.append(
+ casadi.MX.sym(f'X{i}', dynamics.NUM_VELOCITY_STATES))
+
+ Xn = casadi.horzcat(*Xn_variables)
+ U = casadi.horzcat(*U_variables)
Xk_begin = casadi.horzcat(self.X0, Xn)
Xk_end = self.next_X.map(self.N, "thread")(Xk_begin, U)
J = casadi.sum2(self.cost.map(self.N, "thread")(Xk_end, U, self.R))
- self.w += [casadi.reshape(U, 8 * self.N, 1)]
- self.lbw += [-100] * (8 * self.N)
- self.ubw += [100] * (8 * self.N)
+ # Put U and Xn interleaved into w to go fast.
+ for i in range(self.N):
+ self.w += [U_variables[i]]
+ self.ubw += [100] * 8
+ self.lbw += [-100] * 8
- self.w += [
- casadi.reshape(Xn, dynamics.NUM_VELOCITY_STATES * (self.N - 1), 1)
- ]
- self.ubw += [casadi.inf] * (dynamics.NUM_VELOCITY_STATES *
- (self.N - 1))
- self.lbw += [-casadi.inf] * (dynamics.NUM_VELOCITY_STATES *
- (self.N - 1))
+ if i == self.N - 1:
+ continue
+
+ self.w += [Xn_variables[i]]
+ self.ubw += [casadi.inf] * dynamics.NUM_VELOCITY_STATES
+ self.lbw += [-casadi.inf] * dynamics.NUM_VELOCITY_STATES
self.g += [
- casadi.reshape(Xk_end[:, 0:(self.N - 1)] - Xn,
+ casadi.reshape(Xn - Xk_end[:, 0:(self.N - 1)],
dynamics.NUM_VELOCITY_STATES * (self.N - 1), 1)
]
@@ -102,16 +109,43 @@
'p': casadi.vertcat(self.X0, self.R),
}
- compiler = "clang"
- flags = ["-O1"]
- jit_options = {"flags": flags, "verbose": True, "compiler": compiler}
- options = {
- "jit": False,
- "compiler": "shell",
- "jit_options": jit_options,
- "ipopt.linear_solver": "spral",
+ compiler = "ccache clang"
+ flags = ["-O3"]
+ jit_options = {
+ "flags": flags,
+ "verbose": False,
+ "compiler": compiler,
+ "temp_suffix": False,
}
- self.solver = casadi.nlpsol('solver', 'ipopt', prob, options)
+
+ if solver == 'fatrop':
+ equality = [
+ True
+ for _ in range(dynamics.NUM_VELOCITY_STATES * (self.N - 1))
+ ]
+ options = {
+ "jit": True,
+ "jit_cleanup": False,
+ "jit_temp_suffix": False,
+ "compiler": "shell",
+ "jit_options": jit_options,
+ "structure_detection": "auto",
+ "fatrop": {
+ "tol": 1e-7
+ },
+ "debug": True,
+ "equality": equality,
+ }
+ self.solver = casadi.nlpsol('solver', 'fatrop', prob, options)
+ else:
+ options = {
+ "jit": True,
+ "jit_cleanup": False,
+ "jit_temp_suffix": False,
+ "compiler": "shell",
+ "jit_options": jit_options,
+ }
+ self.solver = casadi.nlpsol('solver', 'ipopt', prob, options)
def make_physics(self):
X0 = casadi.MX.sym('X0', dynamics.NUM_VELOCITY_STATES)
@@ -148,8 +182,8 @@
vperpx = -vnormy
vperpy = vnormx
- J += 100 * ((R[0] - X[dynamics.VELOCITY_STATE_VX]) * vnormx +
- (R[1] - X[dynamics.VELOCITY_STATE_VY]) * vnormy)**2.0
+ J += 75 * ((R[0] - X[dynamics.VELOCITY_STATE_VX]) * vnormx +
+ (R[1] - X[dynamics.VELOCITY_STATE_VY]) * vnormy)**2.0
J += 1500 * ((R[0] - X[dynamics.VELOCITY_STATE_VX]) * vperpx +
(R[1] - X[dynamics.VELOCITY_STATE_VY]) * vperpy)**2.0
@@ -183,8 +217,10 @@
if seed is None:
seed = []
- seed += [0, 0] * 4 * self.N
- seed += list(p[:dynamics.NUM_VELOCITY_STATES, 0]) * (self.N - 1)
+ for i in range(self.N):
+ seed += [0, 0] * 4
+ if i < self.N - 1:
+ seed += list(p[:dynamics.NUM_VELOCITY_STATES, 0])
return self.solver(x0=seed,
lbx=self.lbw,
@@ -194,33 +230,44 @@
p=casadi.DM(p))
def unpack_u(self, sol, i):
- return sol['x'].full().flatten()[8 * i:8 * (i + 1)]
+ return sol['x'].full().flatten()[
+ (8 + dynamics.NUM_VELOCITY_STATES) *
+ i:((8 + dynamics.NUM_VELOCITY_STATES) * i + 8)]
def unpack_x(self, sol, i):
- return sol['x'].full().flatten()[8 * self.N +
- dynamics.NUM_VELOCITY_STATES *
- (i - 1):8 * self.N +
- dynamics.NUM_VELOCITY_STATES * (i)]
+ return sol['x'].full().flatten(
+ )[8 + (8 + dynamics.NUM_VELOCITY_STATES) *
+ (i - 1):(8 + dynamics.NUM_VELOCITY_STATES) * i]
-mpc = MPC()
+mpc = MPC(solver='fatrop')
R_goal = numpy.zeros((3, 1))
R_goal[0, 0] = 1.0
R_goal[1, 0] = 1.0
+R_goal[2, 0] = 0.0
+
+module_velocity = 0.0
X_initial = numpy.zeros((25, 1))
# All the wheels are spinning at the speed needed to hit 1 m/s
-X_initial[3, 0] = 0.0
-X_initial[7, 0] = 0.0
-X_initial[11, 0] = 0.0
-X_initial[15, 0] = 0.0
+X_initial[dynamics.STATE_THETAS0, 0] = 0.0
+X_initial[dynamics.STATE_OMEGAS0, 0] = module_velocity
+
+X_initial[dynamics.STATE_THETAS1, 0] = 0.0
+X_initial[dynamics.STATE_OMEGAS1, 0] = module_velocity
+
+X_initial[dynamics.STATE_THETAS2, 0] = 0.0
+X_initial[dynamics.STATE_OMEGAS2, 0] = module_velocity
+
+X_initial[dynamics.STATE_THETAS3, 0] = 0.0
+X_initial[dynamics.STATE_OMEGAS3, 0] = module_velocity
# Robot is moving at 0 m/s
-X_initial[19, 0] = 0.01
-X_initial[20, 0] = 0.0
+X_initial[dynamics.STATE_VX, 0] = 0.0
+X_initial[dynamics.STATE_VY, 0] = 0.0
# No angular velocity
-X_initial[21, 0] = 0.0
+X_initial[dynamics.STATE_OMEGA, 0] = 0.0
iterations = 100
@@ -236,23 +283,29 @@
fig1, axs1 = pylab.subplots(2)
last_time = time.time()
-seed = [0, 0] * 4 * mpc.N + list(dynamics.to_velocity_state(X)) * (mpc.N - 1)
+seed = ([0, 0] * 4 + list(dynamics.to_velocity_state(X))) * (mpc.N -
+ 1) + [0, 0] * 4
+overall_time = 0
for i in range(iterations):
t.append(i * mpc.dt)
print("Current X at", i * mpc.dt, X.transpose())
print("Goal R at", i * mpc.dt, R_goal)
+ start_time = time.time()
sol = mpc.solve(
# TODO(austin): Is this better or worse than constraints on the initial state for convergence?
p=numpy.vstack((dynamics.to_velocity_state(X), R_goal)),
seed=seed)
+ end_time = time.time()
+ print(f"Took {end_time - start_time} seconds to solve.")
+ overall_time += end_time - start_time
+
X_plot[:, i] = X[:, 0]
U = mpc.unpack_u(sol, 0)
- seed = (list(sol['x'].full().flatten()[8:8 * mpc.N]) +
- list(sol['x'].full().flatten()[8 * (mpc.N - 1) +
- dynamics.NUM_VELOCITY_STATES:]) +
- list(sol['x'].full().flatten()[-dynamics.NUM_VELOCITY_STATES:]))
+ seed = (
+ list(sol['x'].full().flatten()[8 + dynamics.NUM_VELOCITY_STATES:]) +
+ list(sol['x'].full().flatten()[-(8 + dynamics.NUM_VELOCITY_STATES):]))
U_plot[:, i] = U
print('x(0):', X.transpose())
@@ -293,4 +346,6 @@
pyplot.pause(0.0001)
last_time = time.time()
+print(f"Tool {overall_time} seconds overall to solve.")
+
pyplot.pause(-1)
diff --git a/frc971/control_loops/swerve/debug_fatrop.py b/frc971/control_loops/swerve/debug_fatrop.py
new file mode 100755
index 0000000..2f7fa92
--- /dev/null
+++ b/frc971/control_loops/swerve/debug_fatrop.py
@@ -0,0 +1,61 @@
+import casadi
+import pylab
+
+# From https://gist.github.com/jgillis/dec56fa16c90a8e4a69465e8422c5459
+
+# Point this to where the files generated by running casadi with solver options of
+# {"debug": True}
+root = "./"
+
+actual = casadi.Sparsity.from_file(root + "debug_fatrop_actual.mtx")
+
+A = casadi.Sparsity.from_file(root + "debug_fatrop_A.mtx")
+B = casadi.Sparsity.from_file(root + "debug_fatrop_B.mtx")
+C = casadi.Sparsity.from_file(root + "debug_fatrop_C.mtx")
+D = casadi.Sparsity.from_file(root + "debug_fatrop_D.mtx")
+I = casadi.Sparsity.from_file(root + "debug_fatrop_I.mtx")
+errors = casadi.Sparsity.from_file(root + "debug_fatrop_errors.mtx").row()
+
+pylab.figure()
+pylab.spy(A,
+ marker='o',
+ color='r',
+ markersize=5,
+ label="expected A",
+ markerfacecolor="white")
+pylab.spy(B,
+ marker='o',
+ color='b',
+ markersize=5,
+ label="expected B",
+ markerfacecolor="white")
+pylab.spy(C,
+ marker='o',
+ color='g',
+ markersize=5,
+ label="expected C",
+ markerfacecolor="white")
+pylab.spy(D,
+ marker='o',
+ color='y',
+ markersize=5,
+ label="expected D",
+ markerfacecolor="white")
+pylab.spy(I,
+ marker='o',
+ color='k',
+ markersize=5,
+ label="expected I",
+ markerfacecolor="white")
+pylab.spy(actual, marker='o', color='k', markersize=2, label="actual")
+
+pylab.hlines(errors,
+ 0,
+ A.shape[1],
+ color='gray',
+ linestyle='-',
+ label="offending rows")
+
+pylab.title("Debug view of fatrop interface structure detection")
+pylab.legend()
+pylab.show()
diff --git a/frc971/control_loops/swerve/generate_physics.cc b/frc971/control_loops/swerve/generate_physics.cc
index 2e3fe73..e712a39 100644
--- a/frc971/control_loops/swerve/generate_physics.cc
+++ b/frc971/control_loops/swerve/generate_physics.cc
@@ -44,6 +44,7 @@
using SymEngine::cos;
using SymEngine::DenseMatrix;
using SymEngine::div;
+using SymEngine::exp;
using SymEngine::Inf;
using SymEngine::integer;
using SymEngine::map_basic_basic;
@@ -55,7 +56,6 @@
using SymEngine::real_double;
using SymEngine::RealDouble;
using SymEngine::Set;
-using SymEngine::sign;
using SymEngine::simplify;
using SymEngine::sin;
using SymEngine::solve;
@@ -306,14 +306,6 @@
result_cc.emplace_back("#include <cmath>");
result_cc.emplace_back("");
result_cc.emplace_back("namespace frc971::control_loops::swerve {");
- result_cc.emplace_back("namespace {");
- result_cc.emplace_back("");
- result_cc.emplace_back("double sign(double x) {");
- result_cc.emplace_back(" return (x > 0) - (x < 0);");
- result_cc.emplace_back("}");
-
- result_cc.emplace_back("");
- result_cc.emplace_back("} // namespace");
result_cc.emplace_back("");
result_cc.emplace_back("Eigen::Matrix<double, 25, 1> SwervePhysics(");
result_cc.emplace_back(
@@ -410,9 +402,9 @@
void WriteCasadiVariables(std::vector<std::string> *result_py) {
result_py->emplace_back(" sin = casadi.sin");
- result_py->emplace_back(" sign = casadi.sign");
result_py->emplace_back(" cos = casadi.cos");
- result_py->emplace_back(" atan2 = casadi.atan2");
+ result_py->emplace_back(" exp = casadi.exp");
+ result_py->emplace_back(" atan2 = soft_atan2");
result_py->emplace_back(" fmax = casadi.fmax");
result_py->emplace_back(" fabs = casadi.fabs");
@@ -454,9 +446,9 @@
void WriteCasadiVelocityVariables(std::vector<std::string> *result_py) {
result_py->emplace_back(" sin = casadi.sin");
- result_py->emplace_back(" sign = casadi.sign");
+ result_py->emplace_back(" exp = casadi.exp");
result_py->emplace_back(" cos = casadi.cos");
- result_py->emplace_back(" atan2 = casadi.atan2");
+ result_py->emplace_back(" atan2 = soft_atan2");
result_py->emplace_back(" fmax = casadi.fmax");
result_py->emplace_back(" fabs = casadi.fabs");
@@ -574,6 +566,14 @@
// result_py.emplace_back(" [X[STATE_MOMENT, 0]],");
result_py.emplace_back(" ])");
result_py.emplace_back("");
+ constexpr double kLogGain = 1.0 / 0.05;
+ result_py.emplace_back("def soft_atan2(y, x):");
+ result_py.emplace_back(" return casadi.arctan2(");
+ result_py.emplace_back(" y,");
+ result_py.emplace_back(" casadi.logsumexp(casadi.SX(numpy.array(");
+ result_py.emplace_back(absl::Substitute(
+ " [1.0, casadi.fabs(x) * $0.0]))) / $0.0)", kLogGain));
+ result_py.emplace_back("");
result_py.emplace_back("# Returns the derivative of our state vector");
result_py.emplace_back("# [thetas0, thetad0, omegas0, omegad0,");
@@ -839,15 +839,17 @@
// Velocity of the contact patch in field coordinates
DenseMatrix temp_matrix = DenseMatrix(2, 1);
DenseMatrix temp_matrix2 = DenseMatrix(2, 1);
+ DenseMatrix temp_matrix3 = DenseMatrix(2, 1);
result.contact_patch_velocity = DenseMatrix(2, 1);
mul_dense_dense(R(theta_), result.mounting_location, temp_matrix);
add_dense_dense(angle_cross(temp_matrix, omega_), robot_velocity,
temp_matrix2);
mul_dense_dense(R(add(theta_, result.thetas)),
- DenseMatrix(2, 1, {neg(caster_), integer(0)}), temp_matrix);
+ DenseMatrix(2, 1, {neg(caster_), integer(0)}),
+ temp_matrix3);
add_dense_dense(temp_matrix2,
- angle_cross(temp_matrix, add(omega_, result.omegas)),
+ angle_cross(temp_matrix3, add(omega_, result.omegas)),
result.contact_patch_velocity);
VLOG(1);
@@ -894,11 +896,15 @@
result.Fwy = simplify(mul(Cy_, result.slip_angle));
// The self-aligning moment needs to flip when the module flips direction.
- result.Ms = mul(neg(result.Fwy),
- add(div(mul(sign(result.wheel_ground_velocity.get(0, 0)),
- contact_patch_length_),
- integer(3)),
- caster_));
+ RCP<const Basic> softsign_velocity = add(
+ div(integer(-2),
+ add(integer(1), exp(mul(integer(100),
+ result.wheel_ground_velocity.get(0, 0))))),
+ integer(1));
+ result.Ms =
+ mul(neg(result.Fwy),
+ add(div(mul(softsign_velocity, contact_patch_length_), integer(3)),
+ caster_));
VLOG(1);
VLOG(1) << "Ms " << result.Ms->__str__();
VLOG(1);
diff --git a/frc971/control_loops/swerve/physics_test.py b/frc971/control_loops/swerve/physics_test.py
index 2ce82ef..9aa6457 100644
--- a/frc971/control_loops/swerve/physics_test.py
+++ b/frc971/control_loops/swerve/physics_test.py
@@ -3,6 +3,7 @@
import numpy
import sys, os
import casadi
+import scipy
from numpy.testing import assert_array_equal, assert_array_almost_equal
import unittest
@@ -173,17 +174,23 @@
for theta in [0.0, 0.6, -0.4]:
module_angle = numpy.pi * wrap + theta
- # We have redefined the angle to be the sin of the angle.
+ # We have redefined the angle to be the softened sin of the angle.
# That way, when the module flips directions, the slip angle also flips
# directions to keep it stable.
computed_angle = self.slip_angle[i](
utils.state_vector(velocity=velocity,
- module_angle=numpy.pi * wrap +
- theta),
+ module_angle=module_angle),
self.I,
)[0, 0]
- expected = numpy.sin(numpy.pi * wrap + theta)
+ # Compute out the expected value directly to confirm we got it right.
+ loggain = 20.0
+ vy = 1.5 * numpy.sin(-module_angle)
+ vx = 1.5 * numpy.cos(-module_angle)
+ expected = numpy.sin(-numpy.arctan2(
+ vy,
+ scipy.special.logsumexp([1.0, abs(vx) * loggain]) /
+ loggain))
self.assertAlmostEqual(
expected,
diff --git a/frc971/control_loops/swerve/spline_ui/www/BUILD b/frc971/control_loops/swerve/spline_ui/www/BUILD
new file mode 100644
index 0000000..1ecd17c
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/BUILD
@@ -0,0 +1,46 @@
+load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file")
+load("@npm//:defs.bzl", "npm_link_all_packages")
+load("//tools/build_rules:js.bzl", "ng_application")
+load("//tools/build_rules/js:static.bzl", "assemble_static_files")
+
+npm_link_all_packages(name = "node_modules")
+
+ng_application(
+ name = "app",
+ assets = [
+ # Explicitly blank to avoid the default.
+ ],
+ extra_srcs = [
+ "app/common.css",
+ ],
+ html_assets = [
+ "favicon.ico",
+ ],
+ deps = [
+ ":node_modules",
+ ],
+)
+
+assemble_static_files(
+ name = "static_files",
+ srcs = [
+ "//third_party/y2024/field:pictures",
+ ],
+ app_files = ":app",
+ replace_prefixes = {
+ "prod": "",
+ "dev": "",
+ "third_party/y2024": "pictures",
+ },
+ tags = [
+ "no-remote-cache",
+ ],
+ visibility = ["//visibility:public"],
+)
+
+copy_file(
+ name = "app_common_css",
+ src = "common.css",
+ out = "app/common.css",
+ visibility = [":__subpackages__"],
+)
diff --git a/frc971/control_loops/swerve/spline_ui/www/README.md b/frc971/control_loops/swerve/spline_ui/www/README.md
new file mode 100644
index 0000000..17960c7
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/README.md
@@ -0,0 +1,10 @@
+Scouting App Frontend
+================================================================================
+
+Run using:
+```console
+$ bazel build //frc971/control_loops/swerve/spline_ui/www:all
+$ (cd bazel-bin/frc971/control_loops/swerve/spline_ui/www/static_files/ && python3 -m http.server)
+```
+
+Then you can navigate to <http://localhost:8000> to look at the web page.
diff --git a/frc971/control_loops/swerve/spline_ui/www/app/app.module.ts b/frc971/control_loops/swerve/spline_ui/www/app/app.module.ts
new file mode 100644
index 0000000..6948041
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/app/app.module.ts
@@ -0,0 +1,16 @@
+import {NgModule} from '@angular/core';
+import {BrowserModule} from '@angular/platform-browser';
+import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+
+import {App} from './app';
+
+@NgModule({
+ declarations: [App],
+ imports: [
+ BrowserModule,
+ BrowserAnimationsModule,
+ ],
+ exports: [App],
+ bootstrap: [App],
+})
+export class AppModule {}
diff --git a/frc971/control_loops/swerve/spline_ui/www/app/app.ng.html b/frc971/control_loops/swerve/spline_ui/www/app/app.ng.html
new file mode 100644
index 0000000..6e43eac
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/app/app.ng.html
@@ -0,0 +1 @@
+<h1>Spline UI</h1>
diff --git a/frc971/control_loops/swerve/spline_ui/www/app/app.ts b/frc971/control_loops/swerve/spline_ui/www/app/app.ts
new file mode 100644
index 0000000..dc0cb7c
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/app/app.ts
@@ -0,0 +1,9 @@
+import {Component} from '@angular/core';
+
+@Component({
+ selector: 'spline-ui-app',
+ templateUrl: './app.ng.html',
+ styleUrls: ['../app/common.css'],
+})
+export class App {
+}
diff --git a/frc971/control_loops/swerve/spline_ui/www/common.css b/frc971/control_loops/swerve/spline_ui/www/common.css
new file mode 100644
index 0000000..cf58bba
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/common.css
@@ -0,0 +1 @@
+/* This CSS is shared between all spline UI pages. */
diff --git a/frc971/control_loops/swerve/spline_ui/www/favicon.ico b/frc971/control_loops/swerve/spline_ui/www/favicon.ico
new file mode 100644
index 0000000..2bf69ea
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/favicon.ico
Binary files differ
diff --git a/frc971/control_loops/swerve/spline_ui/www/index.html b/frc971/control_loops/swerve/spline_ui/www/index.html
new file mode 100644
index 0000000..c36936d
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/index.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <title>FRC971 Spline UI</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="theme-color" content="#000000" />
+ <link
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
+ rel="stylesheet"
+ integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
+ crossorigin="anonymous"
+ />
+ <link
+ rel="stylesheet"
+ href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"
+ />
+ <script
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
+ integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
+ crossorigin="anonymous"
+ ></script>
+ </head>
+ <body>
+ <spline-ui-app></spline-ui-app>
+ <noscript>
+ Please enable JavaScript to continue using this application.
+ </noscript>
+ </body>
+</html>
diff --git a/frc971/control_loops/swerve/spline_ui/www/main.ts b/frc971/control_loops/swerve/spline_ui/www/main.ts
new file mode 100644
index 0000000..1f8eb00
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/main.ts
@@ -0,0 +1,4 @@
+import {platformBrowser} from '@angular/platform-browser';
+import {AppModule} from './app/app.module';
+
+platformBrowser().bootstrapModule(AppModule);
diff --git a/frc971/control_loops/swerve/spline_ui/www/package.json b/frc971/control_loops/swerve/spline_ui/www/package.json
new file mode 100644
index 0000000..54a7f09
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/package.json
@@ -0,0 +1,6 @@
+{
+ "private": true,
+ "dependencies": {
+ "@angular/animations": "v16-lts"
+ }
+}
diff --git a/frc971/control_loops/swerve/spline_ui/www/polyfills.ts b/frc971/control_loops/swerve/spline_ui/www/polyfills.ts
new file mode 100644
index 0000000..e4555ed
--- /dev/null
+++ b/frc971/control_loops/swerve/spline_ui/www/polyfills.ts
@@ -0,0 +1,52 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes recent versions of Safari, Chrome (including
+ * Opera), Edge on the desktop, and iOS and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ * (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d8164ab..c8e5425 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -170,6 +170,12 @@
specifier: ^5
version: 5.1.6
+ frc971/control_loops/swerve/spline_ui/www:
+ dependencies:
+ '@angular/animations':
+ specifier: v16-lts
+ version: 16.2.12(@angular/core@16.2.12)
+
scouting/webserver/requests/messages: {}
scouting/www:
@@ -390,7 +396,7 @@
'@angular/core': 16.2.12
dependencies:
'@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
- tslib: 2.6.0
+ tslib: 2.6.2
/@angular/cli@16.2.12:
resolution: {integrity: sha512-Pcbiraoqdw4rR2Ey5Ooy0ESLS1Ffbjkb6sPfinKRkHmAvyqsmlvkfbB/qK8GrzDSFSWvAKMMXRw9l8nbjvQEXg==}
@@ -2200,7 +2206,6 @@
function-bind: 1.1.2
get-intrinsic: 1.2.4
set-function-length: 1.2.2
- dev: false
/callsites@3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
@@ -2639,7 +2644,6 @@
es-define-property: 1.0.0
es-errors: 1.3.0
gopd: 1.0.1
- dev: false
/define-lazy-prop@2.0.0:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
@@ -2878,12 +2882,10 @@
engines: {node: '>= 0.4'}
dependencies:
get-intrinsic: 1.2.4
- dev: false
/es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
- dev: false
/es-iterator-helpers@1.0.19:
resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==}
@@ -3567,7 +3569,6 @@
has-proto: 1.0.1
has-symbols: 1.0.3
hasown: 2.0.1
- dev: false
/get-stream@5.2.0:
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
@@ -3708,7 +3709,6 @@
resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
dependencies:
get-intrinsic: 1.2.4
- dev: false
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@@ -3734,7 +3734,6 @@
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
dependencies:
es-define-property: 1.0.0
- dev: false
/has-proto@1.0.1:
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
@@ -4977,7 +4976,6 @@
/object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
- dev: false
/object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
@@ -5385,7 +5383,7 @@
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
engines: {node: '>=0.6'}
dependencies:
- side-channel: 1.0.4
+ side-channel: 1.0.6
/querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
@@ -5652,7 +5650,7 @@
/rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
dependencies:
- tslib: 2.6.0
+ tslib: 2.6.2
dev: true
/safe-array-concat@1.1.2:
@@ -5756,7 +5754,6 @@
get-intrinsic: 1.2.4
gopd: 1.0.1
has-property-descriptors: 1.0.2
- dev: false
/set-function-name@2.0.2:
resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
@@ -5799,7 +5796,6 @@
es-errors: 1.3.0
get-intrinsic: 1.2.4
object-inspect: 1.13.1
- dev: false
/signal-exit@3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
@@ -6293,7 +6289,6 @@
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
- dev: false
/tsutils@3.21.0(typescript@5.1.6):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index f18844b..6675e2f 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,4 +1,5 @@
packages:
+- frc971/control_loops/swerve/spline_ui/www
- scouting/webserver/requests/messages
- scouting/www
- scouting/www/*
diff --git a/scouting/www/BUILD b/scouting/www/BUILD
index c73f93a..2a34fdf 100644
--- a/scouting/www/BUILD
+++ b/scouting/www/BUILD
@@ -3,7 +3,8 @@
load("@npm//:@angular/service-worker/package_json.bzl", angular_service_worker = "bin")
load("@npm//:defs.bzl", "npm_link_all_packages")
load("//tools/build_rules:js.bzl", "ng_application")
-load(":defs.bzl", "assemble_service_worker_files", "assemble_static_files")
+load("//tools/build_rules/js:static.bzl", "assemble_static_files")
+load(":defs.bzl", "assemble_service_worker_files")
npm_link_all_packages(name = "node_modules")
diff --git a/scouting/www/defs.bzl b/scouting/www/defs.bzl
index 86095d7..a8bc775 100644
--- a/scouting/www/defs.bzl
+++ b/scouting/www/defs.bzl
@@ -1,41 +1,3 @@
-load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory_bin_action")
-
-def _assemble_static_files_impl(ctx):
- out_dir = ctx.actions.declare_directory(ctx.label.name)
-
- copy_to_directory_bin = ctx.toolchains["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"].copy_to_directory_info.bin
-
- copy_to_directory_bin_action(
- ctx,
- dst = out_dir,
- name = ctx.label.name,
- copy_to_directory_bin = copy_to_directory_bin,
- files = ctx.files.srcs + ctx.attr.app_files.files.to_list(),
- replace_prefixes = ctx.attr.replace_prefixes,
- )
-
- return [DefaultInfo(
- files = depset([out_dir]),
- runfiles = ctx.runfiles([out_dir]),
- )]
-
-assemble_static_files = rule(
- implementation = _assemble_static_files_impl,
- attrs = {
- "app_files": attr.label(
- mandatory = True,
- ),
- "srcs": attr.label_list(
- mandatory = True,
- allow_files = True,
- ),
- "replace_prefixes": attr.string_dict(
- mandatory = True,
- ),
- },
- toolchains = ["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"],
-)
-
def _assemble_service_worker_files_impl(ctx):
args = ctx.actions.args()
args.add_all(ctx.attr._package.files, before_each = "--input_dir", expand_directories = False)
diff --git a/scouting/www/test/authorize/BUILD b/scouting/www/test/authorize/BUILD
index af8bc70..e982298 100644
--- a/scouting/www/test/authorize/BUILD
+++ b/scouting/www/test/authorize/BUILD
@@ -1,8 +1,8 @@
load("@aspect_rules_js//js:defs.bzl", "js_test")
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load("@npm//:defs.bzl", "npm_link_all_packages")
-load("//scouting/www:defs.bzl", "assemble_static_files")
load("//tools/build_rules:js.bzl", "ng_application")
+load("//tools/build_rules/js:static.bzl", "assemble_static_files")
npm_link_all_packages(name = "node_modules")
diff --git a/tools/build_rules/js.bzl b/tools/build_rules/js.bzl
index d7096a9..a95d5f2 100644
--- a/tools/build_rules/js.bzl
+++ b/tools/build_rules/js.bzl
@@ -84,8 +84,8 @@
assets: assets to include in the file bundle
visibility: visibility of the primary targets ({name}, 'test', 'serve')
"""
- assets = assets if assets else native.glob(["assets/**/*"])
- html_assets = html_assets if html_assets else []
+ assets = assets if assets != None else native.glob(["assets/**/*"])
+ html_assets = html_assets or []
test_spec_srcs = native.glob(["app/**/*.spec.ts"])
diff --git a/tools/build_rules/js/static.bzl b/tools/build_rules/js/static.bzl
new file mode 100644
index 0000000..a2fe2ee
--- /dev/null
+++ b/tools/build_rules/js/static.bzl
@@ -0,0 +1,37 @@
+load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory_bin_action")
+
+def _assemble_static_files_impl(ctx):
+ out_dir = ctx.actions.declare_directory(ctx.label.name)
+
+ copy_to_directory_bin = ctx.toolchains["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"].copy_to_directory_info.bin
+
+ copy_to_directory_bin_action(
+ ctx,
+ dst = out_dir,
+ name = ctx.label.name,
+ copy_to_directory_bin = copy_to_directory_bin,
+ files = ctx.files.srcs + ctx.attr.app_files.files.to_list(),
+ replace_prefixes = ctx.attr.replace_prefixes,
+ )
+
+ return [DefaultInfo(
+ files = depset([out_dir]),
+ runfiles = ctx.runfiles([out_dir]),
+ )]
+
+assemble_static_files = rule(
+ implementation = _assemble_static_files_impl,
+ attrs = {
+ "app_files": attr.label(
+ mandatory = True,
+ ),
+ "srcs": attr.label_list(
+ mandatory = True,
+ allow_files = True,
+ ),
+ "replace_prefixes": attr.string_dict(
+ mandatory = True,
+ ),
+ },
+ toolchains = ["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"],
+)
diff --git a/tools/python/requirements.lock.txt b/tools/python/requirements.lock.txt
index 61a4d62..d991d66 100644
--- a/tools/python/requirements.lock.txt
+++ b/tools/python/requirements.lock.txt
@@ -8,55 +8,56 @@
--hash=sha256:1e3c502a0a8205338fc74dadbfa321f8a0965441b39501e36796a47b4017b642 \
--hash=sha256:d824961e4265367b0750ce58b07e564ad0b83ca64b335521cd3421e9b9f10d89
# via -r tools/python/requirements.txt
-casadi==3.6.5 \
- --hash=sha256:0118637823e292a9270133e02c9c6d3f3c7f75e8c91a6f6dc5275ade82dd1d9d \
- --hash=sha256:02d6fb63c460abd99a450e861034d97568a8aec621fc0a4fed22f7494989c682 \
- --hash=sha256:092e448e05feaed8958d684e896d909e756d199b84d3b9d0182da38cd3deebf6 \
- --hash=sha256:0a38bf808bf51368607c64307dd77a7363fbe8e5c910cd5c605546be60edfaff \
- --hash=sha256:0d6ee0558b4ecdd8aa4aa70fd31528b135801f1086c28a9cb78d8e8242b7aedd \
- --hash=sha256:0e4a4ec2e26ebeb22b0c129f2db3cf90f730cf9fbe98adb9a12720ff6ca1834a \
- --hash=sha256:1ce199a4ea1d376edbe5399cd622a4564040c83f50c50114fe50a69a8ea81d92 \
- --hash=sha256:1ddb6e4afdd1da95d7d9d652ed973c1b7f50ef1454965a9170b657e223a2c73e \
- --hash=sha256:314886ef44bd01f1a98579e7784a3bed6e0584e88f9465cf9596af2523efb0dd \
- --hash=sha256:32644c47fbfb643d5cf9769c7bbc94c6bdb9a40ea9c12c54af5e2754599c3186 \
- --hash=sha256:33afd1a4da0c86b4316953fe541635a8a7dc51703282e24a870ada13a46adb53 \
- --hash=sha256:35b2ff6098e386a4d5e8bc681744e52bcd2f2f15cfa44c09814a8979b51a6794 \
- --hash=sha256:3a3fb8af868f83d4a4f26d878c49f4acc4ed7ee92e731c73e650e5893418a634 \
- --hash=sha256:3bdd645151beda013af5fd019fb554756e7dac37541b9f120cdfba90405b2671 \
- --hash=sha256:409a5f6725eadea40fddfb8ba2321139b5252edac8bc115a72f68e648631d56a \
- --hash=sha256:5266fc82e39352e26cb1a4e0a5c3deb32d09e6333be637bd78c273fa50f9012b \
- --hash=sha256:5e8adffe2015cde370fc545b2d0fe731e96e583e4ea4c5f3044e818fea975cfc \
- --hash=sha256:601b76b7afcb27b11563999f6ad1d9d2a2510ab3d00a6f4ce86a0bee97c9d17a \
- --hash=sha256:6039081fdd1daf4ef7fa2b52814a954d75bfc03eb0dc62414e02af5d25746e8f \
- --hash=sha256:7ea8545579872b6f5412985dafec26b906b67bd4639a6c718b7e07f802af4e42 \
- --hash=sha256:83e3404de4449cb7382e49d811eec79cd370e64b97b5c94b155c604d7c523a40 \
- --hash=sha256:8bbfb2eb8cb6b9e2384814d6427e48bcf6df049bf7ed05b0a58bb311a1fbf18c \
- --hash=sha256:a1ae36449adec534125d4af5be912b6fb9dafe74d1fee39f6c82263695e21ca5 \
- --hash=sha256:af95de5aa5942d627d43312834791623384c2ad6ba87928bf0e3cacc8a6698e8 \
- --hash=sha256:b5192dfabf6f5266b168b984d124dd3086c1c5a408c0743ff3a82290a8ccf3b5 \
- --hash=sha256:bceb69bf9f04fded8a564eb64e298d19e945eaf4734f7145a5ee61cf9ac693e7 \
- --hash=sha256:be40e9897d80fb72a97e750b2143c32f63f8800cfb78f9b396d8ce7a913fca39 \
- --hash=sha256:bebd3909db24ba711e094aacc0a2329b9903d422d73f61be851873731244b7d1 \
- --hash=sha256:c661fe88a93b7cc7ea42802aac76a674135cd65e3e564a6f08570dd3bea05201 \
- --hash=sha256:c6789c8060a99b329bb584d97c1eab6a5e4f3e2d2db391e6c2001c6323774990 \
- --hash=sha256:c951031e26d987986dbc334492b2e6ef108077f11c00e178ff4007e4a9bf91d8 \
- --hash=sha256:c98e68023c9e5905d9d6b99ae1fbbfe4b85ba9846b3685408bb498b20509f99a \
- --hash=sha256:caf395d1e36bfb215b154e8df61583d534a07ddabb18cbe50f259b7692a41ac8 \
- --hash=sha256:ccb962ea02b7d6d245d5cd40fb52c29e812040a45273c6eed32cb8fcff673dda \
- --hash=sha256:d12b67d467a5b2b0a909378ef7231fbc9af0da923baa13b1d5464d8471601ac3 \
- --hash=sha256:dbeb50726603454a1f85323cba7caf72524cd43ca0aeb1f286d07005a967ece9 \
- --hash=sha256:deb2cb2bee8aba0c2cad03c832965b51ca305d0f8eb15de8b857ba86a76f0db0 \
- --hash=sha256:e40afb3c062817dd6ce2497cd001f00f107ee1ea41ec4d6ee9f9a5056d219e83 \
- --hash=sha256:e44af450ce944649932f9ef63ff00d2d21f642b506444418b4b20e69dba3adaf \
- --hash=sha256:e96ca81b00b9621007d45db1254fcf232d518ebcc802f42853f57b4df977c567 \
- --hash=sha256:eb311088dca5359acc05aa4d8895bf99afaa16c7c04b27bf640ce4c2361b8cde \
- --hash=sha256:ee5a4ed50d2becd0bd6d203c7a60ffad27c14a3e0ae357480de11c846a8dd928 \
- --hash=sha256:f62f779481b30e5ea88392bdb8225e9545a21c4460dc3e96c2b782405b938d04 \
- --hash=sha256:f6e10b66d6ae8216dab01532f7ad75cc9d66a95125d421b33d078a51ea0fc2a0 \
- --hash=sha256:f9c1de9a798767c00f89c27677b74059df4c9601d69270967b06d7fcff204b4d \
- --hash=sha256:f9e82658c910e3317535d769334260e0a24d97bbce68cadb72f592e9fcbafd61 \
- --hash=sha256:fe2b64d777e36cc3f101220dd1e219a0e11c3e4ee2b5e708b30fea9a27107e41 \
- --hash=sha256:febc645bcc0aed6d7a2bdb6e58b9a89cb8f74b19bc028c41cc807d75a5d54058
+casadi==3.6.6 \
+ --hash=sha256:0870df9ac7040c14b35fdc82b74578ccfe8f1d9d8615eb79693a560fefb42307 \
+ --hash=sha256:0fd493876c673ad149b03513c4f72275611643f2225f4f5d7c7ff828f75805a1 \
+ --hash=sha256:104601d37ab7ebf897bce7e097823bb090dd7629a7cc4c2e76780f46fc0e59f6 \
+ --hash=sha256:1111aed9afee22c52ba824d60e189a93b8db379d3749ea6d51434c796c7db74f \
+ --hash=sha256:13cdb06c702e1dc666087832516863516a80219fb97923b4da57561454225f0f \
+ --hash=sha256:17c9d369aa55c5410002e038f2d9e010595877e6e7a5671413c468e0f6d0ef54 \
+ --hash=sha256:17cdd6c7beb49f335f554938a39a7d96d329545ac786da277bedf3fa1472bde5 \
+ --hash=sha256:1af055099cf650d5be35dc19fb093b1d6a7e9b11c6ab47746044a3783e268bdf \
+ --hash=sha256:2f6234b9ed2718a7e07b65cfeff9740112664e1d833c867971913be18ebb7a89 \
+ --hash=sha256:3b1fc585faf0d41aa7fd41b0ac1079b8eda5ad8cc8d0cc042cfceb44352827f5 \
+ --hash=sha256:3bab3f398b4b4ea39d861c19a99d95ae3210fa1cb8acee2903538dcdb415b253 \
+ --hash=sha256:45ede033cb063751ffd7c66b29122e6a36b43e9b67c72d4c6f87e8c59506feb4 \
+ --hash=sha256:473f553ac8b94c80e4a88c3373e50910769fc7838f8cd5e2dfb63964657bf585 \
+ --hash=sha256:4a8e0660f19a18bc9c2ebe2da6e76589e464c613045dfd623e373d9638ace507 \
+ --hash=sha256:4bc3d0f461d131407444386e63b2752ae60745f705fb7a587341a84103ac774a \
+ --hash=sha256:4eba456c01575d0f649005902be9a62db71154674caf8f5bd68f7eaa91870bbe \
+ --hash=sha256:55b3b4b1c548f6af8fa0703b6bf848540da26becd8cd5fa1c73887b16240ea5f \
+ --hash=sha256:583f9f7044f7bb235fab5ffff0d2a7ad1e57a1128943e920058211cb213ba68a \
+ --hash=sha256:5d3fb9358a0570b539893b1bd85219ba5398d03cf2f6a7d07ccbc1a053e424c7 \
+ --hash=sha256:6c91ed045236475ceba515a6f9fc37dbb9460647cdbd8f9e94ce79ec1d91c852 \
+ --hash=sha256:6cd3e42e29c1011eb644b5686df309935febec80c2a5893f12f226b3dc710f21 \
+ --hash=sha256:7197e0ba22472227e2e85f4ba6f7f1a8624fb2675c9a0e92da1ac4f3927b603d \
+ --hash=sha256:7ce6dc242809484f36c45685e624919641dbebce54b7437a639d532d323163ee \
+ --hash=sha256:81ef7b8764e9deff7da59ba7d19a107ae3da33e9c4e6420f9a02f290448f9bab \
+ --hash=sha256:826428d2c84b2fffcddaa720ba176d3dba294fe337045a997a7f4febb1030231 \
+ --hash=sha256:84ae0d7416dbcd7167551171aa915346153f0b9a4b15ca52aba63204761721b0 \
+ --hash=sha256:86b23093f3423120e2635b965b8eacae6c45dd22923cb1eaf3c05bfb21db5a32 \
+ --hash=sha256:878ca90794f24467955979a7eec7802f7e11adf40ef390878d211449d7d299a8 \
+ --hash=sha256:90c6cc0afa9ee4c6d9d4f8b3ce72b1bc8d873503227627dd8d645cddc9877fe9 \
+ --hash=sha256:91209bc70764a50ddd1d8fb3f38974baed1419ead64b241541aff47a3dddbeda \
+ --hash=sha256:95c23a2b81399a711ffbf1997856f35f270bbf0ad6f265fe7499fe1e76074d0f \
+ --hash=sha256:9b7c610d67f5452b61e194e224fee679c0e708027821bc90f93548cc805a3744 \
+ --hash=sha256:a63a7d7a74f3242f1cf6e7b51aab01fcf26124809894db9b52ba32e7b8b408a3 \
+ --hash=sha256:aa4840aeb5457728ec70cf6b30142bfb9215378b1b8e622c8d799c03f9c1a0c2 \
+ --hash=sha256:bbfa84129c534e17f4abfeb42b3f6d8d86b8602f93b756b73a9e43febfb9f1f0 \
+ --hash=sha256:bc9a9376ff412eecff0499c521486b7245d98e1022124c664ba72df8df88bde6 \
+ --hash=sha256:cc3594b348f306018b142638d0a8c4026f80e996b4e9798fc504899256a7b029 \
+ --hash=sha256:d1a9d2d7bbd495592d2148031b8a76eaeb096cd49ee6638684dbe3ea59bf1777 \
+ --hash=sha256:da7c8be781a457852810f69167897819ceee96afe061df9f9270b709ddc09e78 \
+ --hash=sha256:dc77c9093cdd642bea58f4e8bd4413e19397d77270b2659f4b0e06f23e575364 \
+ --hash=sha256:dd63609c54f3aaabfbfc4d51948d863ad3dfef55c66f689e9a2b96eef2a0bc96 \
+ --hash=sha256:e2d76092d04847ee6b06adc25cfc8227590cde403cd1fa7a2a621e1accbf5d1a \
+ --hash=sha256:e969f8d4fe41f00474dc7fdc250007a1bb97f6872ccc202cca5211214340ef58 \
+ --hash=sha256:eae6395efafb03509f202ce5f0ff6a35382cf61912bdd65d72f5c246aa1922d9 \
+ --hash=sha256:ed024102d03713db772cb98fe5147c3ff59e0aea3113c2e80b86b4e76dfeb163 \
+ --hash=sha256:ed44f4976f9fd940ca28ccf7e268da44b5e6bfd0ba12734dcd7d49cc749f4325 \
+ --hash=sha256:f0c5593b636980d2c434beecdd2a15de7ae69b1c42bd1fbdb46d1599d40c04b9 \
+ --hash=sha256:f5c0e9312e58f4a35f7b7a009b423bfddd1adc065447cae248dce686cec5b08d \
+ --hash=sha256:ffcba96fe3695223c9f88b9d870cbeb08b7adb92929bfe15bad0e2143e62790d
# via -r tools/python/requirements.txt
certifi==2022.9.24 \
--hash=sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14 \
diff --git a/tools/python/requirements.txt b/tools/python/requirements.txt
index 086b138..48b58ff 100644
--- a/tools/python/requirements.txt
+++ b/tools/python/requirements.txt
@@ -24,4 +24,4 @@
bokeh
tabulate
-casadi
+casadi>=3.6.6
diff --git a/tools/python/whl_overrides.json b/tools/python/whl_overrides.json
index f19d373..ccb50a1 100644
--- a/tools/python/whl_overrides.json
+++ b/tools/python/whl_overrides.json
@@ -3,9 +3,9 @@
"sha256": "1e3c502a0a8205338fc74dadbfa321f8a0965441b39501e36796a47b4017b642",
"url": "https://software.frc971.org/Build-Dependencies/wheelhouse/bokeh-3.4.1-py3-none-any.whl"
},
- "casadi==3.6.5": {
- "sha256": "8bbfb2eb8cb6b9e2384814d6427e48bcf6df049bf7ed05b0a58bb311a1fbf18c",
- "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/casadi-3.6.5-cp39-none-manylinux2014_x86_64.whl"
+ "casadi==3.6.6": {
+ "sha256": "cc3594b348f306018b142638d0a8c4026f80e996b4e9798fc504899256a7b029",
+ "url": "https://software.frc971.org/Build-Dependencies/wheelhouse/casadi-3.6.6-cp39-none-manylinux2014_x86_64.whl"
},
"certifi==2022.9.24": {
"sha256": "90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382",