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