blob: 29ce056438b49716e98c5e1ef7e2a0c2cbe523c0 [file] [log] [blame]
// This file provides an index of all standard plot configs.
// In the future, this may be split into more pieces (e.g., to have
// year-specific indices). For now, the pattern for creating a new plot
// is that you provide an exported function (a la the plot*() functions imported
// below) which, when called, will generate the plot in the provided div.
// This file handles providing a master list of all known plots so that
// the user can just open a single web-page and select the plot that they want
// from a drop-down. A given plot will not be loaded until it has been selected
// once, at which point it will stay loaded. This means that if the user wants
// to switch between several plot configs, they don't incur any performance
// penalty associated with swapping.
// By default, no plot is selected, but the plot= URL parameter may be used
// to specify a specific plot, so that people can create links to a specific
// plot.
// The plot*() functions are called *after* we have already received a valid
// config from the web server, so config handlers do not need to be used.
//
// The exact setup of this will be in flux as we add more configs and figure out
// what setups work best--we will likely end up with separate index files for
// each robot year, and may even end up allowing plots to be specified solely
// using JSON rather than requiring people to write a script just to create
// a plot.
import {Configuration} from '../../aos/configuration_generated';
import {plotDemo} from '../../aos/network/www/demo_plot';
import {Connection} from '../../aos/network/www/proxy';
import {plotLocalizer as plot2020Localizer} from '../../y2020/control_loops/drivetrain/localizer_plotter'
import {plotAccelerator as plot2020Accelerator} from '../../y2020/control_loops/superstructure/accelerator_plotter'
import {plotFinisher as plot2020Finisher} from '../../y2020/control_loops/superstructure/finisher_plotter'
import {plotHood as plot2020Hood} from '../../y2020/control_loops/superstructure/hood_plotter'
import {plotTurret as plot2020Turret} from '../../y2020/control_loops/superstructure/turret_plotter'
import {plotSuperstructure as plot2021Superstructure} from '../../y2021_bot3/control_loops/superstructure/superstructure_plotter';
import {plotCatapult as plot2022Catapult} from '../../y2022/control_loops/superstructure/catapult_plotter'
import {plotClimber as plot2022Climber} from '../../y2022/control_loops/superstructure/climber_plotter'
import {plotIntakeBack as plot2022IntakeBack, plotIntakeFront as plot2022IntakeFront} from '../../y2022/control_loops/superstructure/intake_plotter'
import {plotSuperstructure as plot2022Superstructure} from '../../y2022/control_loops/superstructure/superstructure_plotter'
import {plotTurret as plot2022Turret} from '../../y2022/control_loops/superstructure/turret_plotter'
import {plotLocalizer as plot2022Localizer} from '../../y2022/localizer/localizer_plotter'
import {plotVision as plot2022Vision} from '../../y2022/vision/vision_plotter'
import {plotSuperstructure as plot2023Superstructure} from '../../y2023/control_loops/superstructure/superstructure_plotter'
import {plotVision as plot2023Corrections} from '../../y2023/localizer/corrections_plotter'
import {plotLocalizer as plot2023Localizer} from '../../y2023/localizer/localizer_plotter'
import {plotClimber as plot2024Climber, plotIntake as plot2024Intake, plotSuperstructure as plot2024Superstructure} from '../../y2024/control_loops/superstructure/superstructure_plotter'
import {plotSwerve} from '../../y2024_swerve/control_loops/swerve_plotter'
import {plotVision as plot2024Corrections} from '../../y2024/localizer/corrections_plotter'
import {plotLocalizer as plot2024Localizer} from '../../y2024/localizer/localizer_plotter'
import {plotDownEstimator} from '../control_loops/drivetrain/down_estimator_plotter';
import {plotDrivetrain} from '../control_loops/drivetrain/drivetrain_plotter';
import {plotRobotState} from '../control_loops/drivetrain/robot_state_plotter'
import {plotSpline} from '../control_loops/drivetrain/spline_plotter';
import {plotImu} from '../wpilib/imu_plotter';
const rootDiv = document.createElement('div');
rootDiv.style.width = '100%';
document.body.appendChild(rootDiv);
const helpDiv = document.createElement('div');
rootDiv.appendChild(helpDiv);
helpDiv.innerHTML =
'Help: click + drag to pan, double click to reset, scroll to zoom, ' +
'right-click + drag to zoom to rectangle, Esc to cancel. ' +
'Hold the x/y keys to only pan/zoom along the x/y axes.';
class PlotState {
public readonly div: HTMLElement;
private initialized = false;
constructor(
parentDiv: HTMLElement,
private readonly initializer:
(conn: Connection, element: Element) => void) {
this.div = document.createElement('div');
parentDiv.appendChild(this.div);
this.hide();
}
initialize(conn: Connection): void {
if (!this.initialized) {
this.initializer(conn, this.div);
this.initialized = true;
}
}
hide(): void {
this.div.style.display = "none";
}
show(): void {
this.div.style.display = "block";
}
}
const plotSelect = document.createElement('select');
rootDiv.appendChild(plotSelect);
const plotDiv = document.createElement('div');
plotDiv.style.marginTop = '10px';
plotDiv.style.left = '0';
plotDiv.style.position = 'absolute';
plotDiv.style.width = '100%';
rootDiv.appendChild(plotDiv);
// The master list of all the plots that we provide. For a given config, it
// is possible that not all of these plots will be usable depending on the
// presence of certain channels.
const plotIndex = new Map<string, PlotState>([
['Demo', new PlotState(plotDiv, plotDemo)],
['IMU', new PlotState(plotDiv, plotImu)],
['Drivetrain', new PlotState(plotDiv, plotDrivetrain)],
['Spline Debug', new PlotState(plotDiv, plotSpline)],
['Down Estimator', new PlotState(plotDiv, plotDownEstimator)],
['Robot State', new PlotState(plotDiv, plotRobotState)],
['2024 Vision', new PlotState(plotDiv, plot2024Corrections)],
['2024 Localizer', new PlotState(plotDiv, plot2024Localizer)],
['2024 Superstructure', new PlotState(plotDiv, plot2024Superstructure)],
['2024 Swerve', new PlotState(plotDiv, plotSwerve)],
['2024 Climber', new PlotState(plotDiv, plot2024Climber)],
['2024 Intake', new PlotState(plotDiv, plot2024Intake)],
['2023 Vision', new PlotState(plotDiv, plot2023Corrections)],
['2023 Localizer', new PlotState(plotDiv, plot2023Localizer)],
['2023 Superstructure', new PlotState(plotDiv, plot2023Superstructure)],
['2020 Finisher', new PlotState(plotDiv, plot2020Finisher)],
['2020 Accelerator', new PlotState(plotDiv, plot2020Accelerator)],
['2020 Hood', new PlotState(plotDiv, plot2020Hood)],
['2020 Turret', new PlotState(plotDiv, plot2020Turret)],
['2020 Localizer', new PlotState(plotDiv, plot2020Localizer)],
['2022 Localizer', new PlotState(plotDiv, plot2022Localizer)],
['2022 Vision', new PlotState(plotDiv, plot2022Vision)],
['2022 Superstructure', new PlotState(plotDiv, plot2022Superstructure)],
['2022 Catapult', new PlotState(plotDiv, plot2022Catapult)],
['2022 Intake Front', new PlotState(plotDiv, plot2022IntakeFront)],
['2022 Intake Back', new PlotState(plotDiv, plot2022IntakeBack)],
['2022 Climber', new PlotState(plotDiv, plot2022Climber)],
['2022 Turret', new PlotState(plotDiv, plot2022Turret)],
['Y2021 3rd Robot Superstructure', new PlotState(plotDiv, plot2021Superstructure)],
]);
const invalidSelectValue = 'null';
function getDefaultPlot(): string {
const urlParams = (new URL(document.URL)).searchParams;
const urlParamKey = 'plot';
if (!urlParams.has(urlParamKey)) {
return invalidSelectValue;
}
const desiredPlot = urlParams.get(urlParamKey);
if (!plotIndex.has(desiredPlot)) {
return invalidSelectValue;
}
return desiredPlot;
}
const conn = new Connection();
let reloadOnChange = false;
conn.connect();
conn.addConfigHandler((config: Configuration) => {
plotSelect.add(new Option("Select Plot", invalidSelectValue));
for (const name of plotIndex.keys()) {
plotSelect.add(new Option(name, name));
}
plotSelect.addEventListener('input', () => {
for (const plot of plotIndex.values()) {
plot.hide();
}
if (plotSelect.value == invalidSelectValue) {
return;
}
plotIndex.get(plotSelect.value).initialize(conn);
plotIndex.get(plotSelect.value).show();
// Set the URL so that if you reload you get back to this plot.
window.history.replaceState(
null, null, '?plot=' + encodeURIComponent(plotSelect.value));
if (reloadOnChange) {
window.location.reload();
}
reloadOnChange = true;
});
plotSelect.value = getDefaultPlot();
// Force the event to occur once at the start.
plotSelect.dispatchEvent(new Event('input'));
});