blob: 9135cf4a20cbb47e126737fffbe848da1a79d2de [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;
124
Philipp Schradere625ba22020-11-16 20:11:37 -0800125conn.addConfigHandler((config: Configuration) => {
James Kuszmaula8f2c452020-07-05 21:17:56 -0700126 // Locate the timing report schema so that we can read the received messages.
127 let reportSchema = null;
128 for (let ii = 0; ii < config.channelsLength(); ++ii) {
129 if (config.channels(ii).type() === timingReport.type) {
130 reportSchema = config.channels(ii).schema();
131 }
132 }
133 if (reportSchema === null) {
134 throw new Error('Couldn\'t find timing report schema in config.');
135 }
136 reportParser = new Parser(reportSchema);
137
138 // Subscribe to the timing report message.
Philipp Schradere625ba22020-11-16 20:11:37 -0800139 const builder =
140 new flatbuffers_builder.Builder(512) as unknown as flatbuffers.Builder;
James Kuszmaula8f2c452020-07-05 21:17:56 -0700141 const channels: flatbuffers.Offset[] = [];
142 for (const channel of [timingReport]) {
143 const nameFb = builder.createString(channel.name);
144 const typeFb = builder.createString(channel.type);
Philipp Schradere625ba22020-11-16 20:11:37 -0800145 Channel.startChannel(builder);
146 Channel.addName(builder, nameFb);
147 Channel.addType(builder, typeFb);
148 const channelFb = Channel.endChannel(builder);
James Kuszmaul71a81932020-12-15 21:08:01 -0800149 ChannelRequest.startChannelRequest(builder);
150 ChannelRequest.addChannel(builder, channelFb);
151 ChannelRequest.addMethod(builder, TransferMethod.EVERYTHING_WITH_HISTORY);
152 channels.push(ChannelRequest.endChannelRequest(builder));
James Kuszmaula8f2c452020-07-05 21:17:56 -0700153 }
154
James Kuszmaul71a81932020-12-15 21:08:01 -0800155 const channelsFb = SubscriberRequest.createChannelsToTransferVector(builder, channels);
156 SubscriberRequest.startSubscriberRequest(builder);
157 SubscriberRequest.addChannelsToTransfer(builder, channelsFb);
158 const connect = SubscriberRequest.endSubscriberRequest(builder);
James Kuszmaula8f2c452020-07-05 21:17:56 -0700159 builder.finish(connect);
160 conn.sendConnectMessage(builder);
161});
162
Philipp Schradere625ba22020-11-16 20:11:37 -0800163let startTime = null;
James Kuszmaula8f2c452020-07-05 21:17:56 -0700164conn.addHandler(timingReport.type, (data: Uint8Array, time: number) => {
165 if (startTime === null) {
166 startTime = time;
167 }
168 time = time - startTime;
Philipp Schradere625ba22020-11-16 20:11:37 -0800169 const table = Table.getRootTable(new ByteBuffer(data));
James Kuszmaula8f2c452020-07-05 21:17:56 -0700170
171 const timer = reportParser.readVectorOfTables(table, "timers")[0];
172 handlerLines.addPoints(
173 reportParser, reportParser.readTable(timer, 'handler_time'), time);
174 latencyLines.addPoints(
175 reportParser, reportParser.readTable(timer, 'wakeup_latency'), time);
176});
177
178// Set up and draw the benchmarking plot
179benchmarkPlot.getAxisLabels().setTitle(
180 'Benchmarkping plot (1M points per line)');
181const line1 = benchmarkPlot.getDrawer().addLine();
182// For demonstration purposes, make line1 only have points and line2 only have
183// lines.
184line1.setDrawLine(false);
185line1.setLabel('LINE ONE');
186const line2 = benchmarkPlot.getDrawer().addLine();
187line2.setPointSize(0);
188line2.setLabel('LINE TWO');
189const NUM_POINTS = 1000000;
190const points1 = [];
191const points2 = [];
192for (let ii = 0; ii < NUM_POINTS; ++ii) {
193 points1.push(ii);
194 points2.push(ii);
Philipp Schradere625ba22020-11-16 20:11:37 -0800195 points1.push(Math.sin(ii * 10 / NUM_POINTS));
196 points2.push(Math.cos(ii * 10 / NUM_POINTS));
James Kuszmaula8f2c452020-07-05 21:17:56 -0700197}
198line1.setPoints(new Float32Array(points1));
199line2.setPoints(new Float32Array(points2));