blob: 6c4538935400a301e3427596e38ab082c1ff452e [file] [log] [blame]
Stephan Pleines85b295c2024-02-04 17:50:26 -08001#include "aos/analysis/in_process_plotter.h"
James Kuszmaul48671362020-12-24 13:54:16 -08002
Stephan Pleines31f98da2024-05-22 17:31:23 -07003#include <algorithm>
4#include <ostream>
5
6#include "flatbuffers/flatbuffer_builder.h"
7#include "flatbuffers/vector.h"
8#include "glog/logging.h"
9
James Kuszmaul48671362020-12-24 13:54:16 -080010#include "aos/configuration.h"
11
Stephan Pleines9e40c8e2024-02-07 20:58:28 -080012namespace aos::analysis {
James Kuszmaul48671362020-12-24 13:54:16 -080013
14namespace {
James Kuszmaulac0912d2024-05-21 15:56:59 -070015const char *kDataPath = "../" AOS_REPO_NAME "/aos/analysis/cpp_plot";
16const char *kConfigPath = "../" AOS_REPO_NAME "/aos/analysis/plotter.json";
James Kuszmaul48671362020-12-24 13:54:16 -080017} // namespace
18
19Plotter::Plotter()
20 : config_(aos::configuration::ReadConfig(kConfigPath)),
21 event_loop_factory_(&config_.message()),
22 event_loop_(event_loop_factory_.MakeEventLoop("plotter")),
23 plot_sender_(event_loop_->MakeSender<Plot>("/analysis")),
James Kuszmaulb67409b2022-06-20 16:25:03 -070024 web_proxy_(event_loop_.get(), event_loop_factory_.scheduler_epoll(),
25 aos::web_proxy::StoreHistory::kYes, -1),
James Kuszmaul48671362020-12-24 13:54:16 -080026 builder_(plot_sender_.MakeBuilder()) {
27 web_proxy_.SetDataPath(kDataPath);
28 event_loop_->SkipTimingReport();
Austin Schuhea62f602022-07-18 16:53:04 -070029
30 color_wheel_.emplace_back(ColorWheelColor{.name = "red", .color = {1, 0, 0}});
31 color_wheel_.emplace_back(
32 ColorWheelColor{.name = "green", .color = {0, 1, 0}});
33 color_wheel_.emplace_back(
34 ColorWheelColor{.name = "purple", .color = {0.54, 0.3, 0.75}});
35 color_wheel_.emplace_back(
36 ColorWheelColor{.name = "blue", .color = {0, 0, 1}});
37 color_wheel_.emplace_back(
38 ColorWheelColor{.name = "yellow", .color = {1, 1, 0}});
39 color_wheel_.emplace_back(
40 ColorWheelColor{.name = "teal", .color = {0, 1, 1}});
41 color_wheel_.emplace_back(
42 ColorWheelColor{.name = "pink", .color = {1, 0, 1}});
43 color_wheel_.emplace_back(
44 ColorWheelColor{.name = "orange", .color = {1, 0.6, 0}});
45 color_wheel_.emplace_back(
46 ColorWheelColor{.name = "brown", .color = {0.6, 0.3, 0}});
47 color_wheel_.emplace_back(
48 ColorWheelColor{.name = "white", .color = {1, 1, 1}});
James Kuszmaul48671362020-12-24 13:54:16 -080049}
50
James Kuszmaulb67409b2022-06-20 16:25:03 -070051void Plotter::Spin() {
52 // Set non-infinite replay rate to avoid pegging a full CPU.
53 event_loop_factory_.SetRealtimeReplayRate(1.0);
54 event_loop_factory_.Run();
55}
James Kuszmaul48671362020-12-24 13:54:16 -080056
57void Plotter::Title(std::string_view title) {
58 title_ = builder_.fbb()->CreateString(title);
59}
60
61void Plotter::AddFigure(std::string_view title, double width, double height) {
62 MaybeFinishFigure();
63
64 if (!title.empty()) {
65 figure_title_ = builder_.fbb()->CreateString(title);
66 }
67
68 // For positioning, just stack figures vertically.
69 auto position_builder = builder_.MakeBuilder<Position>();
70 position_builder.add_top(next_top_);
71 position_builder.add_left(0);
72 position_builder.add_width(width);
73 position_builder.add_height(height);
74 position_ = position_builder.Finish();
75
76 next_top_ += height;
77}
78
79void Plotter::XLabel(std::string_view label) {
80 xlabel_ = builder_.fbb()->CreateString(label);
81}
82
83void Plotter::YLabel(std::string_view label) {
84 ylabel_ = builder_.fbb()->CreateString(label);
85}
86
87void Plotter::AddLine(const std::vector<double> &x,
Austin Schuhea62f602022-07-18 16:53:04 -070088 const std::vector<double> &y, LineOptions options) {
Austin Schuh0318e5e2023-02-20 17:39:34 -080089 CHECK_EQ(x.size(), y.size()) << ": " << options.label;
James Kuszmaul48671362020-12-24 13:54:16 -080090 CHECK(!position_.IsNull())
91 << "You must call AddFigure() before calling AddLine().";
92
93 flatbuffers::Offset<flatbuffers::String> label_offset;
Austin Schuhea62f602022-07-18 16:53:04 -070094 if (!options.label.empty()) {
95 label_offset = builder_.fbb()->CreateString(options.label);
James Kuszmaul48671362020-12-24 13:54:16 -080096 }
97
98 std::vector<Point> points;
99 for (size_t ii = 0; ii < x.size(); ++ii) {
100 points.emplace_back(x[ii], y[ii]);
101 }
milind1f1dca32021-07-03 13:50:07 -0700102 const flatbuffers::Offset<flatbuffers::Vector<const Point *>> points_offset =
103 builder_.fbb()->CreateVectorOfStructs(points);
James Kuszmaul48671362020-12-24 13:54:16 -0800104
Austin Schuhea62f602022-07-18 16:53:04 -0700105 const Color *color;
106 if (options.color.empty()) {
107 color = &color_wheel_.at(color_wheel_position_).color;
108 color_wheel_position_ = (color_wheel_position_ + 1) % color_wheel_.size();
109 } else {
110 auto it = std::find_if(
111 color_wheel_.begin(), color_wheel_.end(),
112 [options_color = options.color](const ColorWheelColor &color) {
113 return color.name == options_color;
114 });
115 CHECK(it != color_wheel_.end()) << ": Failed to find " << options.color;
116 color = &(it->color);
117 }
James Kuszmaul48671362020-12-24 13:54:16 -0800118
James Kuszmaul19217a42022-06-17 10:54:29 -0700119 LineStyle::Builder style_builder = builder_.MakeBuilder<LineStyle>();
Austin Schuhea62f602022-07-18 16:53:04 -0700120 if (options.line_style.find('*') != options.line_style.npos) {
Austin Schuh69d0b732022-07-20 21:19:32 -0700121 style_builder.add_point_size(options.point_size);
James Kuszmaul19217a42022-06-17 10:54:29 -0700122 } else {
123 style_builder.add_point_size(0.0);
124 }
Austin Schuhea62f602022-07-18 16:53:04 -0700125 style_builder.add_draw_line(options.line_style.find('-') !=
126 options.line_style.npos);
James Kuszmaul19217a42022-06-17 10:54:29 -0700127 const flatbuffers::Offset<LineStyle> style_offset = style_builder.Finish();
128
James Kuszmaul48671362020-12-24 13:54:16 -0800129 auto line_builder = builder_.MakeBuilder<Line>();
130 line_builder.add_label(label_offset);
131 line_builder.add_points(points_offset);
132 line_builder.add_color(color);
James Kuszmaul19217a42022-06-17 10:54:29 -0700133 line_builder.add_style(style_offset);
James Kuszmaul48671362020-12-24 13:54:16 -0800134 lines_.push_back(line_builder.Finish());
135}
136
137void Plotter::MaybeFinishFigure() {
138 if (!lines_.empty()) {
139 const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Line>>>
140 lines_offset = builder_.fbb()->CreateVector(lines_);
141 auto figure_builder = builder_.MakeBuilder<Figure>();
142 figure_builder.add_title(figure_title_);
143 figure_builder.add_position(position_);
144 figure_builder.add_lines(lines_offset);
145 figure_builder.add_xlabel(xlabel_);
146 figure_builder.add_share_x_axis(share_x_axis_);
147 figure_builder.add_ylabel(ylabel_);
148 figures_.push_back(figure_builder.Finish());
149 }
150 lines_.clear();
151 figure_title_.o = 0;
152 xlabel_.o = 0;
153 ylabel_.o = 0;
154 position_.o = 0;
155 share_x_axis_ = false;
156 color_wheel_position_ = 0;
157}
158
159void Plotter::Publish() {
160 MaybeFinishFigure();
161 const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Figure>>>
162 figures_offset = builder_.fbb()->CreateVector(figures_);
163
164 auto plot_builder = builder_.MakeBuilder<Plot>();
165 plot_builder.add_title(title_);
166 plot_builder.add_figures(figures_offset);
167
Austin Schuhea62f602022-07-18 16:53:04 -0700168 CHECK_EQ(builder_.Send(plot_builder.Finish()), aos::RawSender::Error::kOk);
James Kuszmaul48671362020-12-24 13:54:16 -0800169
170 builder_ = plot_sender_.MakeBuilder();
171
172 title_.o = 0;
173 figures_.clear();
174 next_top_ = 0;
175}
176
Stephan Pleines9e40c8e2024-02-07 20:58:28 -0800177} // namespace aos::analysis