blob: 300986e6f22ba9e2245a25b6062cc692e4a54210 [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
Brian Silvermanacdabeb2019-03-23 14:04:36 -070011#include "internal/Embedded.h"
Comran Morsheddaf69232016-04-20 22:25:37 -070012#include "seasocks/Server.h"
13
Austin Schuh1bf8a212019-05-26 22:13:14 -070014#include "aos/events/shm-event-loop.h"
John Park398c74a2018-10-20 21:17:39 -070015#include "aos/init.h"
John Park33858a32018-09-28 23:05:48 -070016#include "aos/logging/logging.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070017#include "aos/mutex/mutex.h"
18#include "aos/seasocks/seasocks_logger.h"
John Park33858a32018-09-28 23:05:48 -070019#include "aos/time/time.h"
20#include "aos/util/phased_loop.h"
Philipp Schrader4bd29b12017-02-22 04:42:27 +000021#include "frc971/autonomous/auto.q.h"
Comran Morsheddaf69232016-04-20 22:25:37 -070022#include "y2016/control_loops/superstructure/superstructure.q.h"
23#include "y2016/queues/ball_detector.q.h"
James Kuszmaul651fc3f2019-05-15 21:14:25 -070024#include "y2016/vision/vision.q.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.
45//#define DASHBOARD_TESTING
46
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.
49//#define DASHBOARD_READ_VISION_QUEUE
50
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_(
Austin Schuh1bf8a212019-05-26 22:13:14 -070054 event_loop->MakeFetcher<::y2016::vision::VisionStatus>(
55 ".y2016.vision.vision_status")),
Austin Schuh4b652c92019-05-27 13:22:27 -070056 ball_detector_fetcher_(
57 event_loop->MakeFetcher<::y2016::sensors::BallDetector>(
58 ".y2016.sensors.ball_detector")),
Austin Schuha250b2d2019-05-27 16:14:02 -070059 autonomous_mode_fetcher_(
60 event_loop->MakeFetcher<::frc971::autonomous::AutonomousMode>(
61 ".frc971.autonomous.auto_mode")),
Austin Schuh1bf8a212019-05-26 22:13:14 -070062 cur_raw_data_("no data"),
Comran Morsheddaf69232016-04-20 22:25:37 -070063 sample_id_(0),
64 measure_index_(0),
Comran Morshed40a18002016-04-28 13:40:26 -070065 overflow_id_(1) {}
Comran Morsheddaf69232016-04-20 22:25:37 -070066
67void DataCollector::RunIteration() {
68 ::aos::MutexLocker locker(&mutex_);
69 measure_index_ = 0;
70
71// Add recorded data here. /////
72#ifdef DASHBOARD_TESTING
73 // The following feeds data into the webserver when we do not have a process
74 // feeding data to the queues.
75 // To test, we are sending three streams holding randomly generated numbers.
76 AddPoint("test", ::std::rand() % 4);
77 AddPoint("test2", ::std::rand() % 3);
78 AddPoint("test3", ::std::rand() % 3 - 1);
79 (void)big_indicator::kBlack;
80 (void)big_indicator::kBallIntaked;
81 (void)big_indicator::kAiming;
82 (void)big_indicator::kLockedOn;
83 (void)superstructure_indicator::kBlack;
84 (void)superstructure_indicator::kNotZeroed;
85 (void)superstructure_indicator::kEstopped;
86#else
87 int big_indicator = big_indicator::kBlack;
88 int superstructure_state_indicator = superstructure_indicator::kBlack;
89 // We should never have a -1 here, so this is an indicator that somethings
90 // gone wrong with reading the auto queue.
91 int auto_mode_indicator = -1;
92
Austin Schuha250b2d2019-05-27 16:14:02 -070093 autonomous_mode_fetcher_.Fetch();
Comran Morsheddaf69232016-04-20 22:25:37 -070094 ::y2016::control_loops::superstructure_queue.status.FetchLatest();
Austin Schuh4b652c92019-05-27 13:22:27 -070095 ball_detector_fetcher_.Fetch();
Austin Schuh1bf8a212019-05-26 22:13:14 -070096 vision_status_fetcher_.Fetch();
Comran Morsheddaf69232016-04-20 22:25:37 -070097
Comran Morshed38967332016-04-23 19:26:48 -070098// Caused glitching with auto-aim at NASA, so be cautious with this until
99// we find a good fix.
100#ifdef DASHBOARD_READ_VISION_QUEUE
Austin Schuh1bf8a212019-05-26 22:13:14 -0700101 if (vision_status_fetcher_.get() &&
102 (vision_status_fetcher_->left_image_valid ||
103 vision_status_fetcher_->right_image_valid)) {
Comran Morshed38967332016-04-23 19:26:48 -0700104 big_indicator = big_indicator::kAiming;
Austin Schuh1bf8a212019-05-26 22:13:14 -0700105 if (::std::abs(vision_status_fetcher_->horizontal_angle) < 0.002) {
Comran Morshed38967332016-04-23 19:26:48 -0700106 big_indicator = big_indicator::kLockedOn;
107 }
108 }
109#else
110 (void)big_indicator::kAiming;
111 (void)big_indicator::kLockedOn;
112#endif
113
114 // Ball detector comes after vision because we want to prioritize that
115 // indication.
Austin Schuh4b652c92019-05-27 13:22:27 -0700116 if (ball_detector_fetcher_.get()) {
Comran Morsheddaf69232016-04-20 22:25:37 -0700117 // TODO(comran): Grab detected voltage from joystick_reader. Except this
118 // value may not change, so it may be worth it to keep it as it is right
119 // now.
Austin Schuh4b652c92019-05-27 13:22:27 -0700120 if (ball_detector_fetcher_->voltage > 2.5) {
Comran Morshed38967332016-04-23 19:26:48 -0700121 big_indicator = big_indicator::kBallIntaked;
Comran Morsheddaf69232016-04-20 22:25:37 -0700122 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700123 }
124
125 if (::y2016::control_loops::superstructure_queue.status.get()) {
126 if (!::y2016::control_loops::superstructure_queue.status->zeroed) {
127 superstructure_state_indicator = superstructure_indicator::kNotZeroed;
128 }
129 if (::y2016::control_loops::superstructure_queue.status->estopped) {
130 superstructure_state_indicator = superstructure_indicator::kEstopped;
131 }
132 }
133
Austin Schuha250b2d2019-05-27 16:14:02 -0700134 if (autonomous_mode_fetcher_.get()) {
135 auto_mode_indicator = autonomous_mode_fetcher_->mode;
Comran Morsheddaf69232016-04-20 22:25:37 -0700136 }
137
138 AddPoint("big indicator", big_indicator);
139 AddPoint("superstructure state indicator", superstructure_state_indicator);
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700140 if (auto_mode_indicator != 15) {
Campbell Crowley9ed61a52016-11-05 17:13:07 -0700141 AddPoint("auto mode indicator", auto_mode_indicator);
142 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700143#endif
144
145 // Get ready for next iteration. /////
146 sample_id_++;
147}
148
149void DataCollector::AddPoint(const ::std::string &name, double value) {
150 // Mutex should be locked when this method is called to synchronize packets.
151 CHECK(mutex_.OwnedBySelf());
152
153 size_t index = GetIndex(sample_id_);
154
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800155 ItemDatapoint datapoint{value, ::aos::monotonic_clock::now()};
Comran Morsheddaf69232016-04-20 22:25:37 -0700156 if (measure_index_ >= sample_items_.size()) {
157 // New item in our data table.
158 ::std::vector<ItemDatapoint> datapoints;
159 SampleItem item{name, datapoints};
160 sample_items_.emplace_back(item);
161 } else if (index >= sample_items_.at(measure_index_).datapoints.size()) {
162 // New data point for an already existing item.
163 sample_items_.at(measure_index_).datapoints.emplace_back(datapoint);
164 } else {
165 // Overwrite an already existing data point for an already existing item.
166 sample_items_.at(measure_index_).datapoints.at(index) = datapoint;
167 }
168
169 measure_index_++;
170}
171
172::std::string DataCollector::Fetch(int32_t from_sample) {
173 ::aos::MutexLocker locker(&mutex_);
174
175 ::std::stringstream message;
176 message.precision(10);
177
178 // Send out the names of each item when requested by the client.
179 // Example: *item_one_name,item_two_name,item_three_name
180 if (from_sample == 0) {
181 message << "*"; // Begin name packet.
182
183 // Add comma-separated list of names.
184 for (size_t cur_data_name = 0; cur_data_name < sample_items_.size();
185 cur_data_name++) {
186 if (cur_data_name > 0) {
187 message << ",";
188 }
189 message << sample_items_.at(cur_data_name).name;
190 }
191 return message.str();
192 }
193
194 // Send out one sample containing the data.
195 // Samples are split with dollar signs, info with percent signs, and
196 // measurements with commas.
197 // Example of data with two samples: $289%2803.13%10,67$290%2803.14%12,68
198
199 // Note that we are ignoring the from_sample being sent to keep up with the
200 // live data without worrying about client lag.
Comran Morshed40a18002016-04-28 13:40:26 -0700201 int32_t cur_sample = sample_id_;
Comran Morsheddaf69232016-04-20 22:25:37 -0700202 int32_t adjusted_index = GetIndex(cur_sample);
203 message << "$"; // Begin data packet.
204
205 // Make sure we are not out of range.
Comran Morshed40a18002016-04-28 13:40:26 -0700206 if (sample_items_.size() > 0) {
207 if (static_cast<size_t>(adjusted_index) <
208 sample_items_.at(0).datapoints.size()) {
209 message << cur_sample << "%"
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700210 << ::aos::time::DurationInSeconds(
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800211 sample_items_.at(0)
212 .datapoints.at(adjusted_index)
213 .time.time_since_epoch())
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800214 << "%"; // Send time.
Comran Morshed40a18002016-04-28 13:40:26 -0700215 // Add comma-separated list of data points.
216 for (size_t cur_measure = 0; cur_measure < sample_items_.size();
217 cur_measure++) {
218 if (cur_measure > 0) {
219 message << ",";
220 }
221 message << sample_items_.at(cur_measure)
222 .datapoints.at(adjusted_index)
223 .value;
224 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700225 }
226 }
227
228 return message.str();
229}
230
231size_t DataCollector::GetIndex(size_t sample_id) {
232 return sample_id % overflow_id_;
233}
234
235void DataCollector::operator()() {
236 ::aos::SetCurrentThreadName("DashboardData");
237
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800238 ::aos::time::PhasedLoop phased_loop(chrono::milliseconds(100),
Austin Schuhd32b3622019-06-23 18:49:06 -0700239 event_loop_->monotonic_now(),
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800240 chrono::seconds(0));
Comran Morsheddaf69232016-04-20 22:25:37 -0700241 while (run_) {
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800242 phased_loop.SleepUntilNext();
Comran Morsheddaf69232016-04-20 22:25:37 -0700243 RunIteration();
244 }
245}
246
Austin Schuh1bf8a212019-05-26 22:13:14 -0700247SocketHandler::SocketHandler(::aos::EventLoop *event_loop)
248 : data_collector_(event_loop),
249 data_collector_thread_(::std::ref(data_collector_)) {}
Comran Morsheddaf69232016-04-20 22:25:37 -0700250
251void SocketHandler::onConnect(seasocks::WebSocket *connection) {
252 connections_.insert(connection);
253 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
254 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
255}
256
257void SocketHandler::onData(seasocks::WebSocket *connection, const char *data) {
258 int32_t from_sample = atoi(data);
259
260 ::std::string send_data = data_collector_.Fetch(from_sample);
261 connection->send(send_data.c_str());
262}
263
264void SocketHandler::onDisconnect(seasocks::WebSocket *connection) {
265 connections_.erase(connection);
266 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
267 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
268}
269
270void SocketHandler::Quit() {
271 data_collector_.Quit();
272 data_collector_thread_.join();
273}
274
Comran Morsheddaf69232016-04-20 22:25:37 -0700275} // namespace dashboard
276} // namespace y2016
277
278int main(int, char *[]) {
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700279 // Make sure to reference this to force the linker to include it.
280 findEmbeddedContent("");
281
Comran Morsheddaf69232016-04-20 22:25:37 -0700282 ::aos::InitNRT();
283
Austin Schuh1bf8a212019-05-26 22:13:14 -0700284 ::aos::ShmEventLoop event_loop;
285
Comran Morsheddaf69232016-04-20 22:25:37 -0700286 ::seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700287 new ::aos::seasocks::SeasocksLogger(::seasocks::Logger::Level::Info)));
Austin Schuh1bf8a212019-05-26 22:13:14 -0700288 ::y2016::dashboard::SocketHandler socket_handler(&event_loop);
Comran Morsheddaf69232016-04-20 22:25:37 -0700289
290 server.addWebSocketHandler(
291 "/ws",
292 ::std::shared_ptr<::y2016::dashboard::SocketHandler>(&socket_handler));
293#ifdef DASHBOARD_TESTING
294 server.serve("www", 1180);
295#else
296 // Absolute directory of www folder on the robot.
297 server.serve("/home/admin/robot_code/www", 1180);
298#endif
299
300 socket_handler.Quit();
301
302 ::aos::Cleanup();
303 return 0;
304}