blob: 759202a78bc6ef45e7885b31bf5f13f52aa6f3e1 [file] [log] [blame]
James Kuszmaul5f5e1232020-12-22 20:58:00 -08001// This file provides an index of all standard plot configs.
2// In the future, this may be split into more pieces (e.g., to have
3// year-specific indices). For now, the pattern for creating a new plot
4// is that you provide an exported function (a la the plot*() functions imported
5// below) which, when called, will generate the plot in the provided div.
6// This file handles providing a master list of all known plots so that
7// the user can just open a single web-page and select the plot that they want
8// from a drop-down. A given plot will not be loaded until it has been selected
9// once, at which point it will stay loaded. This means that if the user wants
10// to switch between several plot configs, they don't incur any performance
11// penalty associated with swapping.
12// By default, no plot is selected, but the plot= URL parameter may be used
13// to specify a specific plot, so that people can create links to a specific
14// plot.
15// The plot*() functions are called *after* we have already received a valid
16// config from the web server, so config handlers do not need to be used.
17//
18// The exact setup of this will be in flux as we add more configs and figure out
19// what setups work best--we will likely end up with separate index files for
20// each robot year, and may even end up allowing plots to be specified solely
21// using JSON rather than requiring people to write a script just to create
22// a plot.
23import * as configuration from 'org_frc971/aos/configuration_generated';
24import * as proxy from 'org_frc971/aos/network/www/proxy';
25import {plotImu} from 'org_frc971/frc971/wpilib/imu_plotter';
26import {plotDemo} from 'org_frc971/aos/network/www/demo_plot';
27
28import Connection = proxy.Connection;
29import Configuration = configuration.aos.Configuration;
30
31const rootDiv = document.createElement('div');
32document.body.appendChild(rootDiv);
33
34const helpDiv = document.createElement('div');
35rootDiv.appendChild(helpDiv);
36helpDiv.innerHTML =
37 'Help: click + drag to pan, double click to reset, scroll to zoom. ' +
38 'Hold the x/y keys to only pan/zoom along the x/y axes.';
39
40class PlotState {
41 public readonly div: HTMLElement;
42 private initialized = false;
43 constructor(
44 parentDiv: HTMLElement,
45 private readonly initializer:
46 (conn: Connection, element: Element) => void) {
47 this.div = document.createElement('div');
48 parentDiv.appendChild(this.div);
49 this.hide();
50 }
51 initialize(conn: Connection): void {
52 if (!this.initialized) {
53 this.initializer(conn, this.div);
54 this.initialized = true;
55 }
56 }
57 hide(): void {
58 this.div.style.display = "none";
59 }
60 show(): void {
61 this.div.style.display = "block";
62 }
63}
64
65const plotSelect = document.createElement('select');
66rootDiv.appendChild(plotSelect);
67
68const plotDiv = document.createElement('div');
69plotDiv.style.top = (plotSelect.getBoundingClientRect().bottom + 10).toString();
70plotDiv.style.left = '0';
71plotDiv.style.position = 'absolute';
72rootDiv.appendChild(plotDiv);
73
74// The master list of all the plots that we provide. For a given config, it
75// is possible that not all of these plots will be usable depending on the
76// presence of certain channels.
77const plotIndex = new Map<string, PlotState>([
78 ['Demo', new PlotState(plotDiv, plotDemo)],
79 ['IMU', new PlotState(plotDiv, plotImu)]
80]);
81
82const invalidSelectValue = 'null';
83function getDefaultPlot(): string {
84 const urlParams = (new URL(document.URL)).searchParams;
85 const urlParamKey = 'plot';
86 if (!urlParams.has(urlParamKey)) {
87 return invalidSelectValue;
88 }
89 const desiredPlot = urlParams.get(urlParamKey);
90 if (!plotIndex.has(desiredPlot)) {
91 return invalidSelectValue;
92 }
93 return desiredPlot;
94}
95
96const conn = new Connection();
97
98conn.connect();
99
100conn.addConfigHandler((config: Configuration) => {
101 plotSelect.add(new Option("Select Plot", invalidSelectValue));
102 for (const name of plotIndex.keys()) {
103 plotSelect.add(new Option(name, name));
104 }
105 plotSelect.addEventListener('input', () => {
106 for (const plot of plotIndex.values()) {
107 plot.hide();
108 }
109 if (plotSelect.value == invalidSelectValue) {
110 return;
111 }
112 plotIndex.get(plotSelect.value).initialize(conn);
113 plotIndex.get(plotSelect.value).show();
114 });
115 plotSelect.value = getDefaultPlot();
116 // Force the event to occur once at the start.
117 plotSelect.dispatchEvent(new Event('input'));
118});