1use minmax::XMinMax;
28
29use plotters::coord::types::RangedCoordf64;
30use plotters::coord::types::RangedCoordu64;
31use plotters::coord::Shift;
32
33use crate::plots::colors::*;
34use crate::plots::*;
35
36use crate::datastore::Datastore;
37use crate::seriesstore::SeriesStore;
38
39struct XYMinMax {
40 pub x: XMinMax,
41 pub y_min: u64,
42 pub y_max: u64,
43}
44
45impl XYMinMax {
46 fn init(params: &PlotParameters, ss: &SeriesStore, y_max: u64) -> Self {
47 let x = XMinMax::new(
48 ss.sent_x_min,
49 ss.sent_x_max,
50 params.clamp.start,
51 params.clamp.end,
52 );
53
54 Self { x, y_min: 0, y_max }
55 }
56
57 fn y_range(&self) -> std::ops::Range<u64> {
58 self.y_min..self.y_max
59 }
60}
61
62const Y_WIGGLE: f64 = 1.1;
63
64pub fn draw_packet_sent_received_plot<'a, DB: DrawingBackend + 'a>(
65 is_sent: bool, filename: &str, params: &PlotParameters, ss: &SeriesStore,
66 plot: &plotters::drawing::DrawingArea<DB, Shift>,
67) -> ChartContext<'a, DB, Cartesian2d<RangedCoordf64, RangedCoordu64>> {
68 let (caption, y_max) = if is_sent {
69 let y_max = (ss.y_max_onertt_pkt_sent_plot as f64 * Y_WIGGLE) as u64;
70
71 (format!("{} Packet Sent timeline", filename), y_max)
72 } else {
73 let y_max = (ss.y_max_onertt_pkt_received_plot as f64 * Y_WIGGLE) as u64;
74
75 (format!("{} Packet Received timeline", filename), y_max)
76 };
77
78 let axis = XYMinMax::init(params, ss, y_max);
79
80 let mut builder = ChartBuilder::on(plot);
81
82 builder
83 .x_label_area_size(params.area_margin.x)
84 .y_label_area_size(params.area_margin.y);
85
86 if params.display_chart_title {
87 builder.caption(caption, chart_title_style(¶ms.colors.caption));
88 }
89
90 let mut chart = builder
91 .build_cartesian_2d(axis.x.range(), axis.y_range())
92 .unwrap();
93
94 draw_mesh(
95 ¶ms.colors,
96 "Relative time (ms)",
97 "Packet Number",
98 params.display_minor_lines,
99 &mut chart,
100 );
101
102 if is_sent {
103 chart
104 .draw_series(LineSeries::new(ss.onertt_packet_created.clone(), GREEN))
105 .unwrap()
106 .label("packet created")
107 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], GREEN));
108
109 chart
110 .draw_series(LineSeries::new(ss.onertt_packet_sent.clone(), ORANGE))
111 .unwrap()
112 .label("packet sent (packet number)")
113 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], ORANGE));
114
115 let packet_losses = ss
116 .onertt_packet_lost_hacky
117 .iter()
118 .map(|point| Cross::new(*point, 2, PURPLE));
119
120 chart
121 .draw_series(packet_losses)
122 .unwrap()
123 .label("packet lost (packet number)")
124 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], PURPLE));
125 } else {
126 let missing_packets_line = ss
127 .netlog_missing_packets
128 .iter()
129 .map(|t| PathElement::new([(*t, 0), (*t, y_max)], BLUE));
130
131 chart
132 .draw_series(missing_packets_line)
133 .unwrap()
134 .label("missing packets")
135 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE));
136
137 chart
138 .draw_series(LineSeries::new(
139 ss.onertt_packet_received.clone(),
140 ORANGE,
141 ))
142 .unwrap()
143 .label("packet received")
144 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], ORANGE));
145 }
146
147 if params.display_legend {
148 chart
149 .configure_series_labels()
150 .label_font(chart_label_style(¶ms.colors.caption))
151 .background_style(params.colors.fill.mix(0.8))
152 .border_style(params.colors.axis)
153 .position(SeriesLabelPosition::UpperLeft)
154 .draw()
155 .unwrap();
156 }
157
158 chart
159}
160
161pub fn draw_packet_sent_lost_delivered_count_plot<'a, DB: DrawingBackend + 'a>(
162 params: &PlotParameters, ss: &SeriesStore,
163 plot: &plotters::drawing::DrawingArea<DB, Shift>,
164) -> ChartContext<'a, DB, Cartesian2d<RangedCoordf64, RangedCoordu64>> {
165 let caption = "Packet sent/lost/delivered counts";
166
167 let y_max = (ss.y_max_onertt_pkt_sent_plot as f64 * Y_WIGGLE) as u64;
168
169 let axis = XYMinMax::init(params, ss, y_max);
170
171 let mut builder = ChartBuilder::on(plot);
172
173 builder
174 .x_label_area_size(params.area_margin.x)
175 .y_label_area_size(params.area_margin.y);
176
177 if params.display_chart_title {
178 builder.caption(caption, chart_subtitle_style(¶ms.colors.caption));
179 }
180
181 let mut chart = builder
182 .build_cartesian_2d(axis.x.range(), axis.y_range())
183 .unwrap();
184
185 draw_mesh(
186 ¶ms.colors,
187 "Relative time (ms)",
188 "Packet Count",
189 params.display_minor_lines,
190 &mut chart,
191 );
192
193 chart
194 .draw_series(LineSeries::new(
195 ss.onertt_packet_sent_aggregate_count.clone(),
196 TAUPE,
197 ))
198 .unwrap()
199 .label("packet sent count")
200 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], TAUPE));
201
202 chart
203 .draw_series(LineSeries::new(
204 ss.onertt_packet_delivered_aggregate_count.clone(),
205 BLUE,
206 ))
207 .unwrap()
208 .label("packet delivered count")
209 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE));
210
211 chart
212 .draw_series(LineSeries::new(
213 ss.onertt_packet_lost_aggregate_count.clone(),
214 SOFT_PINK,
215 ))
216 .unwrap()
217 .label("packet lost count")
218 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], SOFT_PINK));
219
220 if params.display_legend {
221 chart
222 .configure_series_labels()
223 .label_font(chart_label_style(¶ms.colors.caption))
224 .background_style(params.colors.fill.mix(0.8))
225 .border_style(params.colors.axis)
226 .position(SeriesLabelPosition::UpperLeft)
227 .draw()
228 .unwrap();
229 }
230
231 chart
232}
233
234fn draw_delta_plot<'a, DB: DrawingBackend + 'a>(
235 params: &PlotParameters, ss: &SeriesStore,
236 plot: &plotters::drawing::DrawingArea<DB, Shift>,
237) -> ChartContext<'a, DB, Cartesian2d<RangedCoordu64, RangedCoordf64>> {
238 let y_range = ss.y_min_onertt_packet_created_sent_delta..
239 (ss.y_max_onertt_packet_created_sent_delta * Y_WIGGLE);
240
241 let mut builder = ChartBuilder::on(plot);
242 builder
243 .x_label_area_size(params.area_margin.x)
244 .y_label_area_size(params.area_margin.y);
245
246 if params.display_chart_title {
247 builder.caption(
248 "Packet created/sent Delta timing",
249 chart_subtitle_style(¶ms.colors.caption),
250 );
251 }
252
253 let mut chart = builder
254 .build_cartesian_2d(0..ss.y_max_onertt_pkt_sent_plot, y_range)
255 .unwrap();
256
257 draw_mesh(
258 ¶ms.colors,
259 "Packet Number",
260 "Delta time (ms)",
261 params.display_minor_lines,
262 &mut chart,
263 );
264
265 let lines =
269 LineSeries::new(ss.onertt_packet_created_sent_delta.clone(), BLUE);
270
271 let _crosses = ss
272 .onertt_packet_created_sent_delta
273 .iter()
274 .map(|point| Cross::new(*point, 2, BLUE));
275
276 chart
277 .draw_series(lines)
278 .unwrap()
279 .label("packet created/sent delta")
280 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLACK));
281
282 if params.display_legend {
283 chart
284 .configure_series_labels()
285 .label_font(chart_label_style(¶ms.colors.caption))
286 .background_style(params.colors.fill.mix(0.8))
287 .border_style(params.colors.axis)
288 .position(SeriesLabelPosition::MiddleMiddle)
289 .draw()
290 .unwrap();
291 }
292
293 chart
294}
295
296fn draw_pacing_rate_plot<'a, DB: DrawingBackend + 'a>(
297 params: &PlotParameters, ss: &SeriesStore, _ds: &Datastore,
298 plot: &plotters::drawing::DrawingArea<DB, Shift>,
299) -> ChartContext<'a, DB, Cartesian2d<RangedCoordf64, RangedCoordu64>> {
300 let y_max = ss
301 .max_pacing_rate
302 .max(ss.max_delivery_rate)
303 .max(ss.max_send_rate)
304 .max(ss.max_ack_rate);
305 let y_max = (y_max as f64 * Y_WIGGLE) as u64;
306 let axis = XYMinMax::init(params, ss, y_max);
307 let mut builder = ChartBuilder::on(plot);
308
309 builder
310 .x_label_area_size(params.area_margin.x)
311 .y_label_area_size(params.area_margin.y);
312
313 if params.display_chart_title {
314 builder.caption(
315 "Pacing Rate vs Delivery Rate",
316 chart_subtitle_style(¶ms.colors.caption),
317 );
318 }
319
320 let mut chart = builder
321 .build_cartesian_2d(axis.x.range(), axis.y_range())
322 .unwrap();
323
324 draw_mesh(
325 ¶ms.colors,
326 "Relative time (ms)",
327 "Rate (bytes/sec)",
328 params.display_minor_lines,
329 &mut chart,
330 );
331
332 chart
334 .draw_series(LineSeries::new(ss.local_pacing_rate.clone(), PURPLE))
335 .unwrap()
336 .label("pacing rate")
337 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], PURPLE));
338
339 chart
340 .draw_series(LineSeries::new(ss.local_delivery_rate.clone(), TEAL))
341 .unwrap()
342 .label("delivery rate")
343 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], TEAL));
344
345 chart
346 .draw_series(LineSeries::new(ss.local_send_rate.clone(), ORANGE))
347 .unwrap()
348 .label("send rate")
349 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], ORANGE));
350
351 chart
352 .draw_series(LineSeries::new(ss.local_ack_rate.clone(), BLUE))
353 .unwrap()
354 .label("ack rate")
355 .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE));
356
357 if params.display_legend {
358 chart
359 .configure_series_labels()
360 .label_font(chart_label_style(¶ms.colors.caption))
361 .background_style(params.colors.fill.mix(0.8))
362 .border_style(params.colors.axis)
363 .position(SeriesLabelPosition::UpperLeft)
364 .draw()
365 .unwrap();
366 }
367
368 chart
369}
370
371pub fn plot_packet_sent(
372 params: &PlotParameters, filename: &str, ss: &SeriesStore, ds: &Datastore,
373 ty: &ChartOutputType,
374) {
375 let chart_config = make_chart_config("packet-sent", params, filename, ds, ty);
376
377 chart_config.init_chart_dir();
378
379 #[cfg(not(target_arch = "wasm32"))]
380 let chart_path = chart_config.chart_filepath();
381
382 #[cfg(not(target_arch = "wasm32"))]
383 let root = make_chart_bitmap_area(
384 &chart_path,
385 params.chart_size,
386 params.colors,
387 params.chart_margin,
388 );
389
390 #[cfg(target_arch = "wasm32")]
391 let canvas_id: String = chart_config.canvas_id().unwrap_or_default();
392
393 #[cfg(target_arch = "wasm32")]
394 let root =
395 make_chart_canvas_area(&canvas_id, params.colors, params.chart_margin);
396 let (raw_timings, remainder) = root.split_vertically((33).percent());
397 let (counts, remainder) = remainder.split_vertically((33).percent());
398
399 let (delta_timings, pacing_rate) = remainder.split_vertically((50).percent());
400
401 draw_packet_sent_received_plot(true, filename, params, ss, &raw_timings);
402 draw_packet_sent_lost_delivered_count_plot(params, ss, &counts);
403 draw_delta_plot(params, ss, &delta_timings);
404 draw_pacing_rate_plot(params, ss, ds, &pacing_rate);
405}
406
407#[cfg(target_arch = "wasm32")]
408pub fn plot_packet_sent_plot_canvas<'a>(
409 params: &PlotParameters, filename: &str, ss: &SeriesStore, canvas_id: &str,
410) -> ChartContext<'a, CanvasBackend, Cartesian2d<RangedCoordf64, RangedCoordu64>>
411{
412 let root =
413 make_chart_canvas_area(canvas_id, params.colors, params.chart_margin);
414
415 draw_packet_sent_received_plot(true, filename, params, ss, &root)
416}
417
418#[cfg(target_arch = "wasm32")]
419pub fn plot_packet_sent_lost_delivered_count_plot<'a>(
420 params: &PlotParameters, ss: &SeriesStore, canvas_id: &str,
421) -> ChartContext<'a, CanvasBackend, Cartesian2d<RangedCoordf64, RangedCoordu64>>
422{
423 let root =
424 make_chart_canvas_area(canvas_id, params.colors, params.chart_margin);
425
426 draw_packet_sent_lost_delivered_count_plot(params, ss, &root)
427}
428
429#[cfg(target_arch = "wasm32")]
430pub fn plot_packet_sent_delta_plot_canvas<'a>(
431 params: &PlotParameters, ss: &SeriesStore, canvas_id: &str,
432) -> ChartContext<'a, CanvasBackend, Cartesian2d<RangedCoordu64, RangedCoordf64>>
433{
434 let root =
435 make_chart_canvas_area(canvas_id, params.colors, params.chart_margin);
436
437 draw_delta_plot(params, ss, &root)
438}
439
440#[cfg(target_arch = "wasm32")]
441pub fn plot_packet_sent_pacing_rate_plot_canvas<'a>(
442 params: &PlotParameters, ss: &SeriesStore, ds: &Datastore, canvas_id: &str,
443) -> ChartContext<'a, CanvasBackend, Cartesian2d<RangedCoordf64, RangedCoordu64>>
444{
445 let root =
446 make_chart_canvas_area(canvas_id, params.colors, params.chart_margin);
447
448 draw_pacing_rate_plot(params, ss, ds, &root)
449}