blob: 33d999ea158bc8e018349b48a4cd68c9da37d81b [file] [log] [blame]
James Kuszmaul48671362020-12-24 13:54:16 -08001#include "frc971/analysis/in_process_plotter.h"
2
3#include "aos/configuration.h"
4
5namespace frc971 {
6namespace analysis {
7
8namespace {
Austin Schuhaa3b0862022-07-15 14:38:41 -07009const char *kDataPath = "frc971/analysis/cpp_plot";
James Kuszmaul48671362020-12-24 13:54:16 -080010const char *kConfigPath = "frc971/analysis/plotter.json";
11} // namespace
12
13Plotter::Plotter()
14 : config_(aos::configuration::ReadConfig(kConfigPath)),
15 event_loop_factory_(&config_.message()),
16 event_loop_(event_loop_factory_.MakeEventLoop("plotter")),
17 plot_sender_(event_loop_->MakeSender<Plot>("/analysis")),
James Kuszmaulb67409b2022-06-20 16:25:03 -070018 web_proxy_(event_loop_.get(), event_loop_factory_.scheduler_epoll(),
19 aos::web_proxy::StoreHistory::kYes, -1),
James Kuszmaul48671362020-12-24 13:54:16 -080020 builder_(plot_sender_.MakeBuilder()) {
21 web_proxy_.SetDataPath(kDataPath);
22 event_loop_->SkipTimingReport();
Austin Schuhea62f602022-07-18 16:53:04 -070023
24 color_wheel_.emplace_back(ColorWheelColor{.name = "red", .color = {1, 0, 0}});
25 color_wheel_.emplace_back(
26 ColorWheelColor{.name = "green", .color = {0, 1, 0}});
27 color_wheel_.emplace_back(
28 ColorWheelColor{.name = "purple", .color = {0.54, 0.3, 0.75}});
29 color_wheel_.emplace_back(
30 ColorWheelColor{.name = "blue", .color = {0, 0, 1}});
31 color_wheel_.emplace_back(
32 ColorWheelColor{.name = "yellow", .color = {1, 1, 0}});
33 color_wheel_.emplace_back(
34 ColorWheelColor{.name = "teal", .color = {0, 1, 1}});
35 color_wheel_.emplace_back(
36 ColorWheelColor{.name = "pink", .color = {1, 0, 1}});
37 color_wheel_.emplace_back(
38 ColorWheelColor{.name = "orange", .color = {1, 0.6, 0}});
39 color_wheel_.emplace_back(
40 ColorWheelColor{.name = "brown", .color = {0.6, 0.3, 0}});
41 color_wheel_.emplace_back(
42 ColorWheelColor{.name = "white", .color = {1, 1, 1}});
James Kuszmaul48671362020-12-24 13:54:16 -080043}
44
James Kuszmaulb67409b2022-06-20 16:25:03 -070045void Plotter::Spin() {
46 // Set non-infinite replay rate to avoid pegging a full CPU.
47 event_loop_factory_.SetRealtimeReplayRate(1.0);
48 event_loop_factory_.Run();
49}
James Kuszmaul48671362020-12-24 13:54:16 -080050
51void Plotter::Title(std::string_view title) {
52 title_ = builder_.fbb()->CreateString(title);
53}
54
55void Plotter::AddFigure(std::string_view title, double width, double height) {
56 MaybeFinishFigure();
57
58 if (!title.empty()) {
59 figure_title_ = builder_.fbb()->CreateString(title);
60 }
61
62 // For positioning, just stack figures vertically.
63 auto position_builder = builder_.MakeBuilder<Position>();
64 position_builder.add_top(next_top_);
65 position_builder.add_left(0);
66 position_builder.add_width(width);
67 position_builder.add_height(height);
68 position_ = position_builder.Finish();
69
70 next_top_ += height;
71}
72
73void Plotter::XLabel(std::string_view label) {
74 xlabel_ = builder_.fbb()->CreateString(label);
75}
76
77void Plotter::YLabel(std::string_view label) {
78 ylabel_ = builder_.fbb()->CreateString(label);
79}
80
81void Plotter::AddLine(const std::vector<double> &x,
Austin Schuhea62f602022-07-18 16:53:04 -070082 const std::vector<double> &y, LineOptions options) {
James Kuszmaul48671362020-12-24 13:54:16 -080083 CHECK_EQ(x.size(), y.size());
84 CHECK(!position_.IsNull())
85 << "You must call AddFigure() before calling AddLine().";
86
87 flatbuffers::Offset<flatbuffers::String> label_offset;
Austin Schuhea62f602022-07-18 16:53:04 -070088 if (!options.label.empty()) {
89 label_offset = builder_.fbb()->CreateString(options.label);
James Kuszmaul48671362020-12-24 13:54:16 -080090 }
91
92 std::vector<Point> points;
93 for (size_t ii = 0; ii < x.size(); ++ii) {
94 points.emplace_back(x[ii], y[ii]);
95 }
milind1f1dca32021-07-03 13:50:07 -070096 const flatbuffers::Offset<flatbuffers::Vector<const Point *>> points_offset =
97 builder_.fbb()->CreateVectorOfStructs(points);
James Kuszmaul48671362020-12-24 13:54:16 -080098
Austin Schuhea62f602022-07-18 16:53:04 -070099 const Color *color;
100 if (options.color.empty()) {
101 color = &color_wheel_.at(color_wheel_position_).color;
102 color_wheel_position_ = (color_wheel_position_ + 1) % color_wheel_.size();
103 } else {
104 auto it = std::find_if(
105 color_wheel_.begin(), color_wheel_.end(),
106 [options_color = options.color](const ColorWheelColor &color) {
107 return color.name == options_color;
108 });
109 CHECK(it != color_wheel_.end()) << ": Failed to find " << options.color;
110 color = &(it->color);
111 }
James Kuszmaul48671362020-12-24 13:54:16 -0800112
James Kuszmaul19217a42022-06-17 10:54:29 -0700113 LineStyle::Builder style_builder = builder_.MakeBuilder<LineStyle>();
Austin Schuhea62f602022-07-18 16:53:04 -0700114 if (options.line_style.find('*') != options.line_style.npos) {
Austin Schuh69d0b732022-07-20 21:19:32 -0700115 style_builder.add_point_size(options.point_size);
James Kuszmaul19217a42022-06-17 10:54:29 -0700116 } else {
117 style_builder.add_point_size(0.0);
118 }
Austin Schuhea62f602022-07-18 16:53:04 -0700119 style_builder.add_draw_line(options.line_style.find('-') !=
120 options.line_style.npos);
James Kuszmaul19217a42022-06-17 10:54:29 -0700121 const flatbuffers::Offset<LineStyle> style_offset = style_builder.Finish();
122
James Kuszmaul48671362020-12-24 13:54:16 -0800123 auto line_builder = builder_.MakeBuilder<Line>();
124 line_builder.add_label(label_offset);
125 line_builder.add_points(points_offset);
126 line_builder.add_color(color);
James Kuszmaul19217a42022-06-17 10:54:29 -0700127 line_builder.add_style(style_offset);
James Kuszmaul48671362020-12-24 13:54:16 -0800128 lines_.push_back(line_builder.Finish());
129}
130
131void Plotter::MaybeFinishFigure() {
132 if (!lines_.empty()) {
133 const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Line>>>
134 lines_offset = builder_.fbb()->CreateVector(lines_);
135 auto figure_builder = builder_.MakeBuilder<Figure>();
136 figure_builder.add_title(figure_title_);
137 figure_builder.add_position(position_);
138 figure_builder.add_lines(lines_offset);
139 figure_builder.add_xlabel(xlabel_);
140 figure_builder.add_share_x_axis(share_x_axis_);
141 figure_builder.add_ylabel(ylabel_);
142 figures_.push_back(figure_builder.Finish());
143 }
144 lines_.clear();
145 figure_title_.o = 0;
146 xlabel_.o = 0;
147 ylabel_.o = 0;
148 position_.o = 0;
149 share_x_axis_ = false;
150 color_wheel_position_ = 0;
151}
152
153void Plotter::Publish() {
154 MaybeFinishFigure();
155 const flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Figure>>>
156 figures_offset = builder_.fbb()->CreateVector(figures_);
157
158 auto plot_builder = builder_.MakeBuilder<Plot>();
159 plot_builder.add_title(title_);
160 plot_builder.add_figures(figures_offset);
161
Austin Schuhea62f602022-07-18 16:53:04 -0700162 CHECK_EQ(builder_.Send(plot_builder.Finish()), aos::RawSender::Error::kOk);
James Kuszmaul48671362020-12-24 13:54:16 -0800163
164 builder_ = plot_sender_.MakeBuilder();
165
166 title_.o = 0;
167 figures_.clear();
168 next_top_ = 0;
169}
170
171} // namespace analysis
172} // namespace frc971