blob: 2548e1dbb50f684ae1d9cf39774b0d4a9a0a3982 [file] [log] [blame]
James Kuszmaula8f2c452020-07-05 21:17:56 -07001// This file creates a set of two demonstration plots for testing out the
2// plotting utility.
3// To run this and see a demo, run:
4// bazel run -c opt //aos/network/www:web_proxy_demo
5// And then open localhost:8080/graph.html
6//
7// The plots are just the plots of the handler latency / run times of the first
8// timer in the the /aos timing report message (this message was chosen because
9// it is already being published by the web proxy process, so the demo requires
10// very little setup).
Philipp Schradere625ba22020-11-16 20:11:37 -080011import * as configuration from 'org_frc971/aos/configuration_generated';
Philipp Schradere625ba22020-11-16 20:11:37 -080012import {Line, Plot} from 'org_frc971/aos/network/www/plotter';
13import * as proxy from 'org_frc971/aos/network/www/proxy';
James Kuszmaul71a81932020-12-15 21:08:01 -080014import * as web_proxy from 'org_frc971/aos/network/web_proxy_generated';
Philipp Schradere625ba22020-11-16 20:11:37 -080015import * as reflection from 'org_frc971/aos/network/www/reflection'
16import * as flatbuffers_builder from 'org_frc971/external/com_github_google_flatbuffers/ts/builder';
17import {ByteBuffer} from 'org_frc971/external/com_github_google_flatbuffers/ts/byte-buffer';
18
19import Channel = configuration.aos.Channel;
20import Connection = proxy.Connection;
21import Configuration = configuration.aos.Configuration;
22import Parser = reflection.Parser;
23import Table = reflection.Table;
James Kuszmaul71a81932020-12-15 21:08:01 -080024import SubscriberRequest = web_proxy.aos.web_proxy.SubscriberRequest;
25import ChannelRequest = web_proxy.aos.web_proxy.ChannelRequest;
26import TransferMethod = web_proxy.aos.web_proxy.TransferMethod;
James Kuszmaula8f2c452020-07-05 21:17:56 -070027
28const width = 900;
29const height = 400;
30
31const helpDiv = document.createElement('div');
Philipp Schradere625ba22020-11-16 20:11:37 -080032helpDiv.style.top = '0';
33helpDiv.style.left = '0';
James Kuszmaula8f2c452020-07-05 21:17:56 -070034helpDiv.style.position = 'absolute';
35document.body.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.<br>' +
39 'X-axes of the top two plots are linked together.';
40
41const div = document.createElement('div');
Philipp Schradere625ba22020-11-16 20:11:37 -080042div.style.top = '40';
43div.style.left = '0';
James Kuszmaula8f2c452020-07-05 21:17:56 -070044div.style.position = 'absolute';
45document.body.appendChild(div);
46
47const div2 = document.createElement('div');
Philipp Schradere625ba22020-11-16 20:11:37 -080048div2.style.top = (parseFloat(div.style.top) + height).toString();
49div2.style.left = '0';
James Kuszmaula8f2c452020-07-05 21:17:56 -070050div2.style.position = 'absolute';
51document.body.appendChild(div2);
52
53const benchmarkDiv = document.createElement('div');
Philipp Schradere625ba22020-11-16 20:11:37 -080054benchmarkDiv.style.top = (parseFloat(div2.style.top) + height).toString();
55benchmarkDiv.style.left = '0';
James Kuszmaula8f2c452020-07-05 21:17:56 -070056benchmarkDiv.style.position = 'absolute';
57document.body.appendChild(benchmarkDiv);
58
59const handlerTimePlot = new Plot(div, width, height);
60const latencyPlot = new Plot(div2, width, height);
61const benchmarkPlot = new Plot(benchmarkDiv, width, height);
62
63// Class to store the lines for plotting a Statistics message.
64class StatLines {
65 private max: Line;
66 private min: Line;
67 private average: Line;
68 private maxPoints: number[] = [];
69 private minPoints: number[] = [];
70 private averagePoints: number[] = [];
71 constructor(plotter: Plot) {
72 this.max = plotter.getDrawer().addLine();
73 this.min = plotter.getDrawer().addLine();
74 this.average = plotter.getDrawer().addLine();
75
76 this.max.setLabel("Max");
77 this.min.setLabel("Min");
78 this.average.setLabel("Average");
79
80 this.max.setColor([1, 0, 0]);
81 this.min.setColor([0, 1, 0]);
82 this.average.setColor([0, 0, 1]);
83 }
84 addPoints(parser: Parser, statsTable: Table, time: number) {
85 this.maxPoints.push(time);
86 this.minPoints.push(time);
87 this.averagePoints.push(time);
88 this.maxPoints.push(parser.readScalar(statsTable, "max") * 1000);
89 this.minPoints.push(parser.readScalar(statsTable, "min") * 1000);
90 this.averagePoints.push(parser.readScalar(statsTable, "average") * 1000);
91
92
James Kuszmaul71a81932020-12-15 21:08:01 -080093 // TODO: These memory allocations absolutely kill performance.
James Kuszmaula8f2c452020-07-05 21:17:56 -070094 this.max.setPoints(new Float32Array(this.maxPoints));
95 this.min.setPoints(new Float32Array(this.minPoints));
96 this.average.setPoints(new Float32Array(this.averagePoints));
97 }
98}
99
100function setupPlot(plotter: Plot, title: string): StatLines {
101 plotter.getAxisLabels().setXLabel("Monotonic send time since start (sec)");
102 plotter.getAxisLabels().setYLabel("Time (msec)");
103 plotter.getAxisLabels().setTitle(title);
104 return new StatLines(plotter);
105}
106
107// Sets of the two x-axes to be shared; remove this to be able to zoom/pan the
108// x-axes independently.
109handlerTimePlot.linkXAxis(latencyPlot);
110
111const handlerLines = setupPlot(handlerTimePlot, "Handler Run Times");
112const latencyLines = setupPlot(latencyPlot, "Handler Latencies");
113
114const conn = new Connection();
115
116conn.connect();
117
118const timingReport = {
119 name: '/aos',
120 type: 'aos.timing.Report',
121};
122
123let reportParser = null;
James Kuszmaul527038a2020-12-21 23:40:44 -0800124let startTime = null;
James Kuszmaula8f2c452020-07-05 21:17:56 -0700125
Philipp Schradere625ba22020-11-16 20:11:37 -0800126conn.addConfigHandler((config: Configuration) => {
James Kuszmaula8f2c452020-07-05 21:17:56 -0700127 // Locate the timing report schema so that we can read the received messages.
128 let reportSchema = null;
129 for (let ii = 0; ii < config.channelsLength(); ++ii) {
130 if (config.channels(ii).type() === timingReport.type) {
131 reportSchema = config.channels(ii).schema();
132 }
133 }
134 if (reportSchema === null) {
135 throw new Error('Couldn\'t find timing report schema in config.');
136 }
137 reportParser = new Parser(reportSchema);
138
James Kuszmaul527038a2020-12-21 23:40:44 -0800139 conn.addReliableHandler(
140 timingReport.name, timingReport.type,
141 (data: Uint8Array, time: number) => {
142 if (startTime === null) {
143 startTime = time;
144 }
145 time = time - startTime;
146 const table = Table.getRootTable(new ByteBuffer(data));
James Kuszmaula8f2c452020-07-05 21:17:56 -0700147
James Kuszmaul527038a2020-12-21 23:40:44 -0800148 const timer = reportParser.readVectorOfTables(table, 'timers')[0];
149 handlerLines.addPoints(
150 reportParser, reportParser.readTable(timer, 'handler_time'), time);
151 latencyLines.addPoints(
152 reportParser, reportParser.readTable(timer, 'wakeup_latency'),
153 time);
154 });
James Kuszmaula8f2c452020-07-05 21:17:56 -0700155});
156
157// Set up and draw the benchmarking plot
158benchmarkPlot.getAxisLabels().setTitle(
159 'Benchmarkping plot (1M points per line)');
160const line1 = benchmarkPlot.getDrawer().addLine();
161// For demonstration purposes, make line1 only have points and line2 only have
162// lines.
163line1.setDrawLine(false);
164line1.setLabel('LINE ONE');
165const line2 = benchmarkPlot.getDrawer().addLine();
166line2.setPointSize(0);
167line2.setLabel('LINE TWO');
168const NUM_POINTS = 1000000;
169const points1 = [];
170const points2 = [];
171for (let ii = 0; ii < NUM_POINTS; ++ii) {
172 points1.push(ii);
173 points2.push(ii);
Philipp Schradere625ba22020-11-16 20:11:37 -0800174 points1.push(Math.sin(ii * 10 / NUM_POINTS));
175 points2.push(Math.cos(ii * 10 / NUM_POINTS));
James Kuszmaula8f2c452020-07-05 21:17:56 -0700176}
177line1.setPoints(new Float32Array(points1));
178line2.setPoints(new Float32Array(points2));