blob: 0b12b143ca88772be54a9f1115109c7bff87dc3e [file] [log] [blame]
Comran Morsheddaf69232016-04-20 22:25:37 -07001#include "y2016/dashboard/dashboard.h"
2
Austin Schuhf2a50ba2016-12-24 16:16:26 -08003#include <chrono>
4#include <complex>
Comran Morsheddaf69232016-04-20 22:25:37 -07005#include <iostream>
6#include <sstream>
7#include <string>
8#include <thread>
9#include <vector>
Comran Morsheddaf69232016-04-20 22:25:37 -070010
Alex Perrycb7da4b2019-08-28 19:35:56 -070011#include "aos/events/shm_event_loop.h"
John Park398c74a2018-10-20 21:17:39 -070012#include "aos/init.h"
John Park33858a32018-09-28 23:05:48 -070013#include "aos/logging/logging.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070014#include "aos/realtime.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070015#include "aos/seasocks/seasocks_logger.h"
Brian Silverman1463c092020-10-30 17:28:24 -070016#include "aos/stl_mutex/stl_mutex.h"
John Park33858a32018-09-28 23:05:48 -070017#include "aos/time/time.h"
18#include "aos/util/phased_loop.h"
Austin Schuhed5b26d2019-12-05 20:51:59 -080019#include "frc971/autonomous/auto_mode_generated.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070020#include "internal/Embedded.h"
21#include "seasocks/Server.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070022#include "y2016/control_loops/superstructure/superstructure_status_generated.h"
23#include "y2016/queues/ball_detector_generated.h"
24#include "y2016/vision/vision_generated.h"
Comran Morsheddaf69232016-04-20 22:25:37 -070025
Austin Schuhf2a50ba2016-12-24 16:16:26 -080026namespace chrono = ::std::chrono;
27
Comran Morsheddaf69232016-04-20 22:25:37 -070028namespace y2016 {
29namespace dashboard {
30namespace big_indicator {
31constexpr int kBlack = 0;
32constexpr int kBallIntaked = 1;
33constexpr int kAiming = 2;
34constexpr int kLockedOn = 3;
35} // namespace big_indicator
36
37namespace superstructure_indicator {
38constexpr int kBlack = 0;
39constexpr int kNotZeroed = 1;
40constexpr int kEstopped = 2;
41} // namespace superstructure_indicator
42
43// Define the following if we want to use a local www directory and feed in
44// dummy data.
Austin Schuh50e3dca2023-07-23 14:34:27 -070045// #define DASHBOARD_TESTING
Comran Morsheddaf69232016-04-20 22:25:37 -070046
47// Define the following if we want to read from the vision queue, which has
48// caused problems in the past when auto aiming that still need to be addressed.
Austin Schuh50e3dca2023-07-23 14:34:27 -070049// #define DASHBOARD_READ_VISION_QUEUE
Comran Morsheddaf69232016-04-20 22:25:37 -070050
Austin Schuh1bf8a212019-05-26 22:13:14 -070051DataCollector::DataCollector(::aos::EventLoop *event_loop)
Austin Schuhd32b3622019-06-23 18:49:06 -070052 : event_loop_(event_loop),
53 vision_status_fetcher_(
Brian Silverman1463c092020-10-30 17:28:24 -070054 event_loop->MakeFetcher<::y2016::vision::VisionStatus>("/vision")),
Austin Schuh4b652c92019-05-27 13:22:27 -070055 ball_detector_fetcher_(
56 event_loop->MakeFetcher<::y2016::sensors::BallDetector>(
Alex Perrycb7da4b2019-08-28 19:35:56 -070057 "/superstructure")),
Austin Schuha250b2d2019-05-27 16:14:02 -070058 autonomous_mode_fetcher_(
59 event_loop->MakeFetcher<::frc971::autonomous::AutonomousMode>(
Tyler Chatow24b5db12020-01-06 21:16:56 -080060 "/autonomous")),
Austin Schuh9481d0d2019-06-29 21:56:17 -070061 superstructure_status_fetcher_(
Alex Perrycb7da4b2019-08-28 19:35:56 -070062 event_loop
63 ->MakeFetcher<::y2016::control_loops::superstructure::Status>(
64 "/superstructure")),
Austin Schuh1bf8a212019-05-26 22:13:14 -070065 cur_raw_data_("no data"),
Comran Morsheddaf69232016-04-20 22:25:37 -070066 sample_id_(0),
67 measure_index_(0),
Comran Morshed40a18002016-04-28 13:40:26 -070068 overflow_id_(1) {}
Comran Morsheddaf69232016-04-20 22:25:37 -070069
70void DataCollector::RunIteration() {
Brian Silverman1463c092020-10-30 17:28:24 -070071 std::unique_lock<aos::stl_mutex> locker(mutex_);
Comran Morsheddaf69232016-04-20 22:25:37 -070072 measure_index_ = 0;
73
74// Add recorded data here. /////
75#ifdef DASHBOARD_TESTING
76 // The following feeds data into the webserver when we do not have a process
77 // feeding data to the queues.
78 // To test, we are sending three streams holding randomly generated numbers.
79 AddPoint("test", ::std::rand() % 4);
80 AddPoint("test2", ::std::rand() % 3);
81 AddPoint("test3", ::std::rand() % 3 - 1);
82 (void)big_indicator::kBlack;
83 (void)big_indicator::kBallIntaked;
84 (void)big_indicator::kAiming;
85 (void)big_indicator::kLockedOn;
86 (void)superstructure_indicator::kBlack;
87 (void)superstructure_indicator::kNotZeroed;
88 (void)superstructure_indicator::kEstopped;
89#else
90 int big_indicator = big_indicator::kBlack;
91 int superstructure_state_indicator = superstructure_indicator::kBlack;
92 // We should never have a -1 here, so this is an indicator that somethings
93 // gone wrong with reading the auto queue.
94 int auto_mode_indicator = -1;
95
Austin Schuha250b2d2019-05-27 16:14:02 -070096 autonomous_mode_fetcher_.Fetch();
Austin Schuh9481d0d2019-06-29 21:56:17 -070097 superstructure_status_fetcher_.Fetch();
Austin Schuh4b652c92019-05-27 13:22:27 -070098 ball_detector_fetcher_.Fetch();
Austin Schuh1bf8a212019-05-26 22:13:14 -070099 vision_status_fetcher_.Fetch();
Comran Morsheddaf69232016-04-20 22:25:37 -0700100
Comran Morshed38967332016-04-23 19:26:48 -0700101// Caused glitching with auto-aim at NASA, so be cautious with this until
102// we find a good fix.
103#ifdef DASHBOARD_READ_VISION_QUEUE
Austin Schuh1bf8a212019-05-26 22:13:14 -0700104 if (vision_status_fetcher_.get() &&
105 (vision_status_fetcher_->left_image_valid ||
106 vision_status_fetcher_->right_image_valid)) {
Comran Morshed38967332016-04-23 19:26:48 -0700107 big_indicator = big_indicator::kAiming;
Austin Schuh1bf8a212019-05-26 22:13:14 -0700108 if (::std::abs(vision_status_fetcher_->horizontal_angle) < 0.002) {
Comran Morshed38967332016-04-23 19:26:48 -0700109 big_indicator = big_indicator::kLockedOn;
110 }
111 }
112#else
113 (void)big_indicator::kAiming;
114 (void)big_indicator::kLockedOn;
115#endif
116
117 // Ball detector comes after vision because we want to prioritize that
118 // indication.
Austin Schuh4b652c92019-05-27 13:22:27 -0700119 if (ball_detector_fetcher_.get()) {
Comran Morsheddaf69232016-04-20 22:25:37 -0700120 // TODO(comran): Grab detected voltage from joystick_reader. Except this
121 // value may not change, so it may be worth it to keep it as it is right
122 // now.
Alex Perrycb7da4b2019-08-28 19:35:56 -0700123 if (ball_detector_fetcher_->voltage() > 2.5) {
Comran Morshed38967332016-04-23 19:26:48 -0700124 big_indicator = big_indicator::kBallIntaked;
Comran Morsheddaf69232016-04-20 22:25:37 -0700125 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700126 }
127
Austin Schuh9481d0d2019-06-29 21:56:17 -0700128 if (superstructure_status_fetcher_.get()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700129 if (!superstructure_status_fetcher_->zeroed()) {
Comran Morsheddaf69232016-04-20 22:25:37 -0700130 superstructure_state_indicator = superstructure_indicator::kNotZeroed;
131 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700132 if (superstructure_status_fetcher_->estopped()) {
Comran Morsheddaf69232016-04-20 22:25:37 -0700133 superstructure_state_indicator = superstructure_indicator::kEstopped;
134 }
135 }
136
Austin Schuha250b2d2019-05-27 16:14:02 -0700137 if (autonomous_mode_fetcher_.get()) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700138 auto_mode_indicator = autonomous_mode_fetcher_->mode();
Comran Morsheddaf69232016-04-20 22:25:37 -0700139 }
140
141 AddPoint("big indicator", big_indicator);
142 AddPoint("superstructure state indicator", superstructure_state_indicator);
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700143 if (auto_mode_indicator != 15) {
Campbell Crowley9ed61a52016-11-05 17:13:07 -0700144 AddPoint("auto mode indicator", auto_mode_indicator);
145 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700146#endif
147
148 // Get ready for next iteration. /////
149 sample_id_++;
150}
151
152void DataCollector::AddPoint(const ::std::string &name, double value) {
153 // Mutex should be locked when this method is called to synchronize packets.
Brian Silverman1463c092020-10-30 17:28:24 -0700154 AOS_CHECK(mutex_islocked(mutex_.native_handle()));
Comran Morsheddaf69232016-04-20 22:25:37 -0700155
156 size_t index = GetIndex(sample_id_);
157
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800158 ItemDatapoint datapoint{value, ::aos::monotonic_clock::now()};
Comran Morsheddaf69232016-04-20 22:25:37 -0700159 if (measure_index_ >= sample_items_.size()) {
160 // New item in our data table.
161 ::std::vector<ItemDatapoint> datapoints;
162 SampleItem item{name, datapoints};
163 sample_items_.emplace_back(item);
164 } else if (index >= sample_items_.at(measure_index_).datapoints.size()) {
165 // New data point for an already existing item.
166 sample_items_.at(measure_index_).datapoints.emplace_back(datapoint);
167 } else {
168 // Overwrite an already existing data point for an already existing item.
169 sample_items_.at(measure_index_).datapoints.at(index) = datapoint;
170 }
171
172 measure_index_++;
173}
174
175::std::string DataCollector::Fetch(int32_t from_sample) {
Brian Silverman1463c092020-10-30 17:28:24 -0700176 std::unique_lock<aos::stl_mutex> locker(mutex_);
Comran Morsheddaf69232016-04-20 22:25:37 -0700177
178 ::std::stringstream message;
179 message.precision(10);
180
181 // Send out the names of each item when requested by the client.
182 // Example: *item_one_name,item_two_name,item_three_name
183 if (from_sample == 0) {
184 message << "*"; // Begin name packet.
185
186 // Add comma-separated list of names.
187 for (size_t cur_data_name = 0; cur_data_name < sample_items_.size();
188 cur_data_name++) {
189 if (cur_data_name > 0) {
190 message << ",";
191 }
192 message << sample_items_.at(cur_data_name).name;
193 }
194 return message.str();
195 }
196
197 // Send out one sample containing the data.
198 // Samples are split with dollar signs, info with percent signs, and
199 // measurements with commas.
200 // Example of data with two samples: $289%2803.13%10,67$290%2803.14%12,68
201
202 // Note that we are ignoring the from_sample being sent to keep up with the
203 // live data without worrying about client lag.
Comran Morshed40a18002016-04-28 13:40:26 -0700204 int32_t cur_sample = sample_id_;
Comran Morsheddaf69232016-04-20 22:25:37 -0700205 int32_t adjusted_index = GetIndex(cur_sample);
206 message << "$"; // Begin data packet.
207
208 // Make sure we are not out of range.
Comran Morshed40a18002016-04-28 13:40:26 -0700209 if (sample_items_.size() > 0) {
210 if (static_cast<size_t>(adjusted_index) <
211 sample_items_.at(0).datapoints.size()) {
212 message << cur_sample << "%"
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700213 << ::aos::time::DurationInSeconds(
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800214 sample_items_.at(0)
215 .datapoints.at(adjusted_index)
216 .time.time_since_epoch())
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800217 << "%"; // Send time.
Comran Morshed40a18002016-04-28 13:40:26 -0700218 // Add comma-separated list of data points.
219 for (size_t cur_measure = 0; cur_measure < sample_items_.size();
220 cur_measure++) {
221 if (cur_measure > 0) {
222 message << ",";
223 }
224 message << sample_items_.at(cur_measure)
225 .datapoints.at(adjusted_index)
226 .value;
227 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700228 }
229 }
230
231 return message.str();
232}
233
234size_t DataCollector::GetIndex(size_t sample_id) {
235 return sample_id % overflow_id_;
236}
237
238void DataCollector::operator()() {
239 ::aos::SetCurrentThreadName("DashboardData");
240
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800241 ::aos::time::PhasedLoop phased_loop(chrono::milliseconds(100),
Austin Schuhd32b3622019-06-23 18:49:06 -0700242 event_loop_->monotonic_now(),
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800243 chrono::seconds(0));
Comran Morsheddaf69232016-04-20 22:25:37 -0700244 while (run_) {
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800245 phased_loop.SleepUntilNext();
Comran Morsheddaf69232016-04-20 22:25:37 -0700246 RunIteration();
247 }
248}
249
Austin Schuh1bf8a212019-05-26 22:13:14 -0700250SocketHandler::SocketHandler(::aos::EventLoop *event_loop)
251 : data_collector_(event_loop),
252 data_collector_thread_(::std::ref(data_collector_)) {}
Comran Morsheddaf69232016-04-20 22:25:37 -0700253
254void SocketHandler::onConnect(seasocks::WebSocket *connection) {
255 connections_.insert(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700256 AOS_LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
257 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Comran Morsheddaf69232016-04-20 22:25:37 -0700258}
259
260void SocketHandler::onData(seasocks::WebSocket *connection, const char *data) {
261 int32_t from_sample = atoi(data);
262
263 ::std::string send_data = data_collector_.Fetch(from_sample);
264 connection->send(send_data.c_str());
265}
266
267void SocketHandler::onDisconnect(seasocks::WebSocket *connection) {
268 connections_.erase(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -0700269 AOS_LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
270 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Comran Morsheddaf69232016-04-20 22:25:37 -0700271}
272
273void SocketHandler::Quit() {
274 data_collector_.Quit();
275 data_collector_thread_.join();
276}
277
Comran Morsheddaf69232016-04-20 22:25:37 -0700278} // namespace dashboard
279} // namespace y2016
280
Austin Schuh094d09b2020-11-20 23:26:52 -0800281int main(int argc, char **argv) {
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700282 // Make sure to reference this to force the linker to include it.
283 findEmbeddedContent("");
284
Austin Schuh094d09b2020-11-20 23:26:52 -0800285 ::aos::InitGoogle(&argc, &argv);
Comran Morsheddaf69232016-04-20 22:25:37 -0700286
Alex Perrycb7da4b2019-08-28 19:35:56 -0700287 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
Austin Schuhc5fa6d92022-02-25 14:36:28 -0800288 aos::configuration::ReadConfig("aos_config.json");
Alex Perrycb7da4b2019-08-28 19:35:56 -0700289
290 ::aos::ShmEventLoop event_loop(&config.message());
Austin Schuh1bf8a212019-05-26 22:13:14 -0700291
Comran Morsheddaf69232016-04-20 22:25:37 -0700292 ::seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700293 new ::aos::seasocks::SeasocksLogger(::seasocks::Logger::Level::Info)));
Austin Schuh1bf8a212019-05-26 22:13:14 -0700294 ::y2016::dashboard::SocketHandler socket_handler(&event_loop);
Comran Morsheddaf69232016-04-20 22:25:37 -0700295
296 server.addWebSocketHandler(
297 "/ws",
298 ::std::shared_ptr<::y2016::dashboard::SocketHandler>(&socket_handler));
299#ifdef DASHBOARD_TESTING
300 server.serve("www", 1180);
301#else
302 // Absolute directory of www folder on the robot.
Austin Schuhc0ec2a82022-02-24 17:26:29 -0800303 server.serve("/home/admin/bin/www", 1180);
Comran Morsheddaf69232016-04-20 22:25:37 -0700304#endif
305
306 socket_handler.Quit();
307
Comran Morsheddaf69232016-04-20 22:25:37 -0700308 return 0;
309}