blob: 5f0a33f90774c6e387841df8fb451df6e03edab4 [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
John Park398c74a2018-10-20 21:17:39 -070014#include "aos/init.h"
John Park33858a32018-09-28 23:05:48 -070015#include "aos/logging/logging.h"
16#include "aos/time/time.h"
17#include "aos/util/phased_loop.h"
18#include "aos/mutex/mutex.h"
Comran Morsheddaf69232016-04-20 22:25:37 -070019
Philipp Schrader4bd29b12017-02-22 04:42:27 +000020#include "frc971/autonomous/auto.q.h"
21
Comran Morsheddaf69232016-04-20 22:25:37 -070022#include "y2016/vision/vision.q.h"
23#include "y2016/control_loops/superstructure/superstructure.q.h"
24#include "y2016/queues/ball_detector.q.h"
25
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
51DataCollector::DataCollector()
52 : cur_raw_data_("no data"),
53 sample_id_(0),
54 measure_index_(0),
Comran Morshed40a18002016-04-28 13:40:26 -070055 overflow_id_(1) {}
Comran Morsheddaf69232016-04-20 22:25:37 -070056
57void DataCollector::RunIteration() {
58 ::aos::MutexLocker locker(&mutex_);
59 measure_index_ = 0;
60
61// Add recorded data here. /////
62#ifdef DASHBOARD_TESTING
63 // The following feeds data into the webserver when we do not have a process
64 // feeding data to the queues.
65 // To test, we are sending three streams holding randomly generated numbers.
66 AddPoint("test", ::std::rand() % 4);
67 AddPoint("test2", ::std::rand() % 3);
68 AddPoint("test3", ::std::rand() % 3 - 1);
69 (void)big_indicator::kBlack;
70 (void)big_indicator::kBallIntaked;
71 (void)big_indicator::kAiming;
72 (void)big_indicator::kLockedOn;
73 (void)superstructure_indicator::kBlack;
74 (void)superstructure_indicator::kNotZeroed;
75 (void)superstructure_indicator::kEstopped;
76#else
77 int big_indicator = big_indicator::kBlack;
78 int superstructure_state_indicator = superstructure_indicator::kBlack;
79 // We should never have a -1 here, so this is an indicator that somethings
80 // gone wrong with reading the auto queue.
81 int auto_mode_indicator = -1;
82
Philipp Schrader4bd29b12017-02-22 04:42:27 +000083 ::frc971::autonomous::auto_mode.FetchLatest();
Comran Morsheddaf69232016-04-20 22:25:37 -070084 ::y2016::control_loops::superstructure_queue.status.FetchLatest();
85 ::y2016::sensors::ball_detector.FetchLatest();
Comran Morshed38967332016-04-23 19:26:48 -070086 ::y2016::vision::vision_status.FetchLatest();
Comran Morsheddaf69232016-04-20 22:25:37 -070087
Comran Morshed38967332016-04-23 19:26:48 -070088// Caused glitching with auto-aim at NASA, so be cautious with this until
89// we find a good fix.
90#ifdef DASHBOARD_READ_VISION_QUEUE
91 if (::y2016::vision::vision_status.get() &&
92 (::y2016::vision::vision_status->left_image_valid ||
93 ::y2016::vision::vision_status->right_image_valid)) {
94 big_indicator = big_indicator::kAiming;
95 if (::std::abs(::y2016::vision::vision_status->horizontal_angle) < 0.002) {
96 big_indicator = big_indicator::kLockedOn;
97 }
98 }
99#else
100 (void)big_indicator::kAiming;
101 (void)big_indicator::kLockedOn;
102#endif
103
104 // Ball detector comes after vision because we want to prioritize that
105 // indication.
Comran Morsheddaf69232016-04-20 22:25:37 -0700106 if (::y2016::sensors::ball_detector.get()) {
107 // TODO(comran): Grab detected voltage from joystick_reader. Except this
108 // value may not change, so it may be worth it to keep it as it is right
109 // now.
Comran Morshed38967332016-04-23 19:26:48 -0700110 if (::y2016::sensors::ball_detector->voltage > 2.5) {
111 big_indicator = big_indicator::kBallIntaked;
Comran Morsheddaf69232016-04-20 22:25:37 -0700112 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700113 }
114
115 if (::y2016::control_loops::superstructure_queue.status.get()) {
116 if (!::y2016::control_loops::superstructure_queue.status->zeroed) {
117 superstructure_state_indicator = superstructure_indicator::kNotZeroed;
118 }
119 if (::y2016::control_loops::superstructure_queue.status->estopped) {
120 superstructure_state_indicator = superstructure_indicator::kEstopped;
121 }
122 }
123
Philipp Schrader4bd29b12017-02-22 04:42:27 +0000124 if (::frc971::autonomous::auto_mode.get()) {
125 auto_mode_indicator = ::frc971::autonomous::auto_mode->mode;
Comran Morsheddaf69232016-04-20 22:25:37 -0700126 }
127
128 AddPoint("big indicator", big_indicator);
129 AddPoint("superstructure state indicator", superstructure_state_indicator);
Campbell Crowley9ed61a52016-11-05 17:13:07 -0700130 if(auto_mode_indicator != 15) {
131 AddPoint("auto mode indicator", auto_mode_indicator);
132 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700133#endif
134
135 // Get ready for next iteration. /////
136 sample_id_++;
137}
138
139void DataCollector::AddPoint(const ::std::string &name, double value) {
140 // Mutex should be locked when this method is called to synchronize packets.
141 CHECK(mutex_.OwnedBySelf());
142
143 size_t index = GetIndex(sample_id_);
144
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800145 ItemDatapoint datapoint{value, ::aos::monotonic_clock::now()};
Comran Morsheddaf69232016-04-20 22:25:37 -0700146 if (measure_index_ >= sample_items_.size()) {
147 // New item in our data table.
148 ::std::vector<ItemDatapoint> datapoints;
149 SampleItem item{name, datapoints};
150 sample_items_.emplace_back(item);
151 } else if (index >= sample_items_.at(measure_index_).datapoints.size()) {
152 // New data point for an already existing item.
153 sample_items_.at(measure_index_).datapoints.emplace_back(datapoint);
154 } else {
155 // Overwrite an already existing data point for an already existing item.
156 sample_items_.at(measure_index_).datapoints.at(index) = datapoint;
157 }
158
159 measure_index_++;
160}
161
162::std::string DataCollector::Fetch(int32_t from_sample) {
163 ::aos::MutexLocker locker(&mutex_);
164
165 ::std::stringstream message;
166 message.precision(10);
167
168 // Send out the names of each item when requested by the client.
169 // Example: *item_one_name,item_two_name,item_three_name
170 if (from_sample == 0) {
171 message << "*"; // Begin name packet.
172
173 // Add comma-separated list of names.
174 for (size_t cur_data_name = 0; cur_data_name < sample_items_.size();
175 cur_data_name++) {
176 if (cur_data_name > 0) {
177 message << ",";
178 }
179 message << sample_items_.at(cur_data_name).name;
180 }
181 return message.str();
182 }
183
184 // Send out one sample containing the data.
185 // Samples are split with dollar signs, info with percent signs, and
186 // measurements with commas.
187 // Example of data with two samples: $289%2803.13%10,67$290%2803.14%12,68
188
189 // Note that we are ignoring the from_sample being sent to keep up with the
190 // live data without worrying about client lag.
Comran Morshed40a18002016-04-28 13:40:26 -0700191 int32_t cur_sample = sample_id_;
Comran Morsheddaf69232016-04-20 22:25:37 -0700192 int32_t adjusted_index = GetIndex(cur_sample);
193 message << "$"; // Begin data packet.
194
195 // Make sure we are not out of range.
Comran Morshed40a18002016-04-28 13:40:26 -0700196 if (sample_items_.size() > 0) {
197 if (static_cast<size_t>(adjusted_index) <
198 sample_items_.at(0).datapoints.size()) {
199 message << cur_sample << "%"
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800200 << chrono::duration_cast<chrono::duration<double>>(
201 sample_items_.at(0)
202 .datapoints.at(adjusted_index)
203 .time.time_since_epoch())
204 .count()
205 << "%"; // Send time.
Comran Morshed40a18002016-04-28 13:40:26 -0700206 // Add comma-separated list of data points.
207 for (size_t cur_measure = 0; cur_measure < sample_items_.size();
208 cur_measure++) {
209 if (cur_measure > 0) {
210 message << ",";
211 }
212 message << sample_items_.at(cur_measure)
213 .datapoints.at(adjusted_index)
214 .value;
215 }
Comran Morsheddaf69232016-04-20 22:25:37 -0700216 }
217 }
218
219 return message.str();
220}
221
222size_t DataCollector::GetIndex(size_t sample_id) {
223 return sample_id % overflow_id_;
224}
225
226void DataCollector::operator()() {
227 ::aos::SetCurrentThreadName("DashboardData");
228
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800229 ::aos::time::PhasedLoop phased_loop(chrono::milliseconds(100),
230 chrono::seconds(0));
Comran Morsheddaf69232016-04-20 22:25:37 -0700231 while (run_) {
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800232 phased_loop.SleepUntilNext();
Comran Morsheddaf69232016-04-20 22:25:37 -0700233 RunIteration();
234 }
235}
236
237SocketHandler::SocketHandler()
238 : data_collector_thread_(::std::ref(data_collector_)) {}
239
240void SocketHandler::onConnect(seasocks::WebSocket *connection) {
241 connections_.insert(connection);
242 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
243 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
244}
245
246void SocketHandler::onData(seasocks::WebSocket *connection, const char *data) {
247 int32_t from_sample = atoi(data);
248
249 ::std::string send_data = data_collector_.Fetch(from_sample);
250 connection->send(send_data.c_str());
251}
252
253void SocketHandler::onDisconnect(seasocks::WebSocket *connection) {
254 connections_.erase(connection);
255 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
256 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
257}
258
259void SocketHandler::Quit() {
260 data_collector_.Quit();
261 data_collector_thread_.join();
262}
263
264SeasocksLogger::SeasocksLogger(Level min_level_to_log)
265 : PrintfLogger(min_level_to_log) {}
266
267void SeasocksLogger::log(Level level, const char *message) {
268 // Convert Seasocks error codes to AOS.
269 log_level aos_level;
270 switch (level) {
271 case seasocks::Logger::INFO:
272 aos_level = INFO;
273 break;
274 case seasocks::Logger::WARNING:
275 aos_level = WARNING;
276 break;
277 case seasocks::Logger::ERROR:
278 case seasocks::Logger::SEVERE:
279 aos_level = ERROR;
280 break;
281 case seasocks::Logger::DEBUG:
282 case seasocks::Logger::ACCESS:
283 default:
284 aos_level = DEBUG;
285 break;
286 }
287 LOG(aos_level, "Seasocks: %s\n", message);
288}
289
290} // namespace dashboard
291} // namespace y2016
292
293int main(int, char *[]) {
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700294 // Make sure to reference this to force the linker to include it.
295 findEmbeddedContent("");
296
Comran Morsheddaf69232016-04-20 22:25:37 -0700297 ::aos::InitNRT();
298
299 ::seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
300 new ::y2016::dashboard::SeasocksLogger(seasocks::Logger::INFO)));
301 ::y2016::dashboard::SocketHandler socket_handler;
302
303 server.addWebSocketHandler(
304 "/ws",
305 ::std::shared_ptr<::y2016::dashboard::SocketHandler>(&socket_handler));
306#ifdef DASHBOARD_TESTING
307 server.serve("www", 1180);
308#else
309 // Absolute directory of www folder on the robot.
310 server.serve("/home/admin/robot_code/www", 1180);
311#endif
312
313 socket_handler.Quit();
314
315 ::aos::Cleanup();
316 return 0;
317}