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