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