blob: b2286d1e3f8f035917533252da733f929f204274 [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';
12import * as connect from 'org_frc971/aos/network/connect_generated';
13import {Line, Plot} from 'org_frc971/aos/network/www/plotter';
14import * as proxy from 'org_frc971/aos/network/www/proxy';
15import * 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;
24import Connect = connect.aos.message_bridge.Connect;
James Kuszmaula8f2c452020-07-05 21:17:56 -070025
26const width = 900;
27const height = 400;
28
29const helpDiv = document.createElement('div');
Philipp Schradere625ba22020-11-16 20:11:37 -080030helpDiv.style.top = '0';
31helpDiv.style.left = '0';
James Kuszmaula8f2c452020-07-05 21:17:56 -070032helpDiv.style.position = 'absolute';
33document.body.appendChild(helpDiv);
34helpDiv.innerHTML =
35 'Help: click + drag to pan, double click to reset, scroll to zoom. ' +
36 'Hold the x/y keys to only pan/zoom along the x/y axes.<br>' +
37 'X-axes of the top two plots are linked together.';
38
39const div = document.createElement('div');
Philipp Schradere625ba22020-11-16 20:11:37 -080040div.style.top = '40';
41div.style.left = '0';
James Kuszmaula8f2c452020-07-05 21:17:56 -070042div.style.position = 'absolute';
43document.body.appendChild(div);
44
45const div2 = document.createElement('div');
Philipp Schradere625ba22020-11-16 20:11:37 -080046div2.style.top = (parseFloat(div.style.top) + height).toString();
47div2.style.left = '0';
James Kuszmaula8f2c452020-07-05 21:17:56 -070048div2.style.position = 'absolute';
49document.body.appendChild(div2);
50
51const benchmarkDiv = document.createElement('div');
Philipp Schradere625ba22020-11-16 20:11:37 -080052benchmarkDiv.style.top = (parseFloat(div2.style.top) + height).toString();
53benchmarkDiv.style.left = '0';
James Kuszmaula8f2c452020-07-05 21:17:56 -070054benchmarkDiv.style.position = 'absolute';
55document.body.appendChild(benchmarkDiv);
56
57const handlerTimePlot = new Plot(div, width, height);
58const latencyPlot = new Plot(div2, width, height);
59const benchmarkPlot = new Plot(benchmarkDiv, width, height);
60
61// Class to store the lines for plotting a Statistics message.
62class StatLines {
63 private max: Line;
64 private min: Line;
65 private average: Line;
66 private maxPoints: number[] = [];
67 private minPoints: number[] = [];
68 private averagePoints: number[] = [];
69 constructor(plotter: Plot) {
70 this.max = plotter.getDrawer().addLine();
71 this.min = plotter.getDrawer().addLine();
72 this.average = plotter.getDrawer().addLine();
73
74 this.max.setLabel("Max");
75 this.min.setLabel("Min");
76 this.average.setLabel("Average");
77
78 this.max.setColor([1, 0, 0]);
79 this.min.setColor([0, 1, 0]);
80 this.average.setColor([0, 0, 1]);
81 }
82 addPoints(parser: Parser, statsTable: Table, time: number) {
83 this.maxPoints.push(time);
84 this.minPoints.push(time);
85 this.averagePoints.push(time);
86 this.maxPoints.push(parser.readScalar(statsTable, "max") * 1000);
87 this.minPoints.push(parser.readScalar(statsTable, "min") * 1000);
88 this.averagePoints.push(parser.readScalar(statsTable, "average") * 1000);
89
90
91 this.max.setPoints(new Float32Array(this.maxPoints));
92 this.min.setPoints(new Float32Array(this.minPoints));
93 this.average.setPoints(new Float32Array(this.averagePoints));
94 }
95}
96
97function setupPlot(plotter: Plot, title: string): StatLines {
98 plotter.getAxisLabels().setXLabel("Monotonic send time since start (sec)");
99 plotter.getAxisLabels().setYLabel("Time (msec)");
100 plotter.getAxisLabels().setTitle(title);
101 return new StatLines(plotter);
102}
103
104// Sets of the two x-axes to be shared; remove this to be able to zoom/pan the
105// x-axes independently.
106handlerTimePlot.linkXAxis(latencyPlot);
107
108const handlerLines = setupPlot(handlerTimePlot, "Handler Run Times");
109const latencyLines = setupPlot(latencyPlot, "Handler Latencies");
110
111const conn = new Connection();
112
113conn.connect();
114
115const timingReport = {
116 name: '/aos',
117 type: 'aos.timing.Report',
118};
119
120let reportParser = null;
121
Philipp Schradere625ba22020-11-16 20:11:37 -0800122conn.addConfigHandler((config: Configuration) => {
James Kuszmaula8f2c452020-07-05 21:17:56 -0700123 // Locate the timing report schema so that we can read the received messages.
124 let reportSchema = null;
125 for (let ii = 0; ii < config.channelsLength(); ++ii) {
126 if (config.channels(ii).type() === timingReport.type) {
127 reportSchema = config.channels(ii).schema();
128 }
129 }
130 if (reportSchema === null) {
131 throw new Error('Couldn\'t find timing report schema in config.');
132 }
133 reportParser = new Parser(reportSchema);
134
135 // Subscribe to the timing report message.
Philipp Schradere625ba22020-11-16 20:11:37 -0800136 const builder =
137 new flatbuffers_builder.Builder(512) as unknown as flatbuffers.Builder;
James Kuszmaula8f2c452020-07-05 21:17:56 -0700138 const channels: flatbuffers.Offset[] = [];
139 for (const channel of [timingReport]) {
140 const nameFb = builder.createString(channel.name);
141 const typeFb = builder.createString(channel.type);
Philipp Schradere625ba22020-11-16 20:11:37 -0800142 Channel.startChannel(builder);
143 Channel.addName(builder, nameFb);
144 Channel.addType(builder, typeFb);
145 const channelFb = Channel.endChannel(builder);
James Kuszmaula8f2c452020-07-05 21:17:56 -0700146 channels.push(channelFb);
147 }
148
Philipp Schradere625ba22020-11-16 20:11:37 -0800149 const channelsFb = Connect.createChannelsToTransferVector(builder, channels);
150 Connect.startConnect(builder);
151 Connect.addChannelsToTransfer(builder, channelsFb);
152 const connect = Connect.endConnect(builder);
James Kuszmaula8f2c452020-07-05 21:17:56 -0700153 builder.finish(connect);
154 conn.sendConnectMessage(builder);
155});
156
Philipp Schradere625ba22020-11-16 20:11:37 -0800157let startTime = null;
James Kuszmaula8f2c452020-07-05 21:17:56 -0700158conn.addHandler(timingReport.type, (data: Uint8Array, time: number) => {
159 if (startTime === null) {
160 startTime = time;
161 }
162 time = time - startTime;
Philipp Schradere625ba22020-11-16 20:11:37 -0800163 const table = Table.getRootTable(new ByteBuffer(data));
James Kuszmaula8f2c452020-07-05 21:17:56 -0700164
165 const timer = reportParser.readVectorOfTables(table, "timers")[0];
166 handlerLines.addPoints(
167 reportParser, reportParser.readTable(timer, 'handler_time'), time);
168 latencyLines.addPoints(
169 reportParser, reportParser.readTable(timer, 'wakeup_latency'), time);
170});
171
172// Set up and draw the benchmarking plot
173benchmarkPlot.getAxisLabels().setTitle(
174 'Benchmarkping plot (1M points per line)');
175const line1 = benchmarkPlot.getDrawer().addLine();
176// For demonstration purposes, make line1 only have points and line2 only have
177// lines.
178line1.setDrawLine(false);
179line1.setLabel('LINE ONE');
180const line2 = benchmarkPlot.getDrawer().addLine();
181line2.setPointSize(0);
182line2.setLabel('LINE TWO');
183const NUM_POINTS = 1000000;
184const points1 = [];
185const points2 = [];
186for (let ii = 0; ii < NUM_POINTS; ++ii) {
187 points1.push(ii);
188 points2.push(ii);
Philipp Schradere625ba22020-11-16 20:11:37 -0800189 points1.push(Math.sin(ii * 10 / NUM_POINTS));
190 points2.push(Math.cos(ii * 10 / NUM_POINTS));
James Kuszmaula8f2c452020-07-05 21:17:56 -0700191}
192line1.setPoints(new Float32Array(points1));
193line2.setPoints(new Float32Array(points2));