blob: bdfedb8096fa5b6dbf6f1be6cac26706e2026a50 [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),
52 overflow_id_(20) {}
53
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);
127 AddPoint("auto mode indicator", auto_mode_indicator);
128#endif
129
130 // Get ready for next iteration. /////
131 sample_id_++;
132}
133
134void DataCollector::AddPoint(const ::std::string &name, double value) {
135 // Mutex should be locked when this method is called to synchronize packets.
136 CHECK(mutex_.OwnedBySelf());
137
138 size_t index = GetIndex(sample_id_);
139
140 ItemDatapoint datapoint{value, ::aos::time::Time::Now()};
141 if (measure_index_ >= sample_items_.size()) {
142 // New item in our data table.
143 ::std::vector<ItemDatapoint> datapoints;
144 SampleItem item{name, datapoints};
145 sample_items_.emplace_back(item);
146 } else if (index >= sample_items_.at(measure_index_).datapoints.size()) {
147 // New data point for an already existing item.
148 sample_items_.at(measure_index_).datapoints.emplace_back(datapoint);
149 } else {
150 // Overwrite an already existing data point for an already existing item.
151 sample_items_.at(measure_index_).datapoints.at(index) = datapoint;
152 }
153
154 measure_index_++;
155}
156
157::std::string DataCollector::Fetch(int32_t from_sample) {
158 ::aos::MutexLocker locker(&mutex_);
159
160 ::std::stringstream message;
161 message.precision(10);
162
163 // Send out the names of each item when requested by the client.
164 // Example: *item_one_name,item_two_name,item_three_name
165 if (from_sample == 0) {
166 message << "*"; // Begin name packet.
167
168 // Add comma-separated list of names.
169 for (size_t cur_data_name = 0; cur_data_name < sample_items_.size();
170 cur_data_name++) {
171 if (cur_data_name > 0) {
172 message << ",";
173 }
174 message << sample_items_.at(cur_data_name).name;
175 }
176 return message.str();
177 }
178
179 // Send out one sample containing the data.
180 // Samples are split with dollar signs, info with percent signs, and
181 // measurements with commas.
182 // Example of data with two samples: $289%2803.13%10,67$290%2803.14%12,68
183
184 // Note that we are ignoring the from_sample being sent to keep up with the
185 // live data without worrying about client lag.
186 int32_t cur_sample = sample_id_ - 2;
187 int32_t adjusted_index = GetIndex(cur_sample);
188 message << "$"; // Begin data packet.
189
190 // Make sure we are not out of range.
Comran Morshed38967332016-04-23 19:26:48 -0700191 if (static_cast<size_t>(adjusted_index) <
192 sample_items_.at(0).datapoints.size()) {
Comran Morsheddaf69232016-04-20 22:25:37 -0700193 message << cur_sample << "%"
Comran Morshed38967332016-04-23 19:26:48 -0700194 << sample_items_.at(0)
195 .datapoints.at(adjusted_index)
196 .time.ToSeconds() << "%"; // Send time.
Comran Morsheddaf69232016-04-20 22:25:37 -0700197 // Add comma-separated list of data points.
198 for (size_t cur_measure = 0; cur_measure < sample_items_.size();
199 cur_measure++) {
200 if (cur_measure > 0) {
201 message << ",";
202 }
203 message << sample_items_.at(cur_measure)
204 .datapoints.at(adjusted_index)
205 .value;
206 }
207 }
208
209 return message.str();
210}
211
212size_t DataCollector::GetIndex(size_t sample_id) {
213 return sample_id % overflow_id_;
214}
215
216void DataCollector::operator()() {
217 ::aos::SetCurrentThreadName("DashboardData");
218
219 while (run_) {
220 ::aos::time::PhasedLoopXMS(100, 0);
221 RunIteration();
222 }
223}
224
225SocketHandler::SocketHandler()
226 : data_collector_thread_(::std::ref(data_collector_)) {}
227
228void SocketHandler::onConnect(seasocks::WebSocket *connection) {
229 connections_.insert(connection);
230 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
231 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
232}
233
234void SocketHandler::onData(seasocks::WebSocket *connection, const char *data) {
235 int32_t from_sample = atoi(data);
236
237 ::std::string send_data = data_collector_.Fetch(from_sample);
238 connection->send(send_data.c_str());
239}
240
241void SocketHandler::onDisconnect(seasocks::WebSocket *connection) {
242 connections_.erase(connection);
243 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
244 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
245}
246
247void SocketHandler::Quit() {
248 data_collector_.Quit();
249 data_collector_thread_.join();
250}
251
252SeasocksLogger::SeasocksLogger(Level min_level_to_log)
253 : PrintfLogger(min_level_to_log) {}
254
255void SeasocksLogger::log(Level level, const char *message) {
256 // Convert Seasocks error codes to AOS.
257 log_level aos_level;
258 switch (level) {
259 case seasocks::Logger::INFO:
260 aos_level = INFO;
261 break;
262 case seasocks::Logger::WARNING:
263 aos_level = WARNING;
264 break;
265 case seasocks::Logger::ERROR:
266 case seasocks::Logger::SEVERE:
267 aos_level = ERROR;
268 break;
269 case seasocks::Logger::DEBUG:
270 case seasocks::Logger::ACCESS:
271 default:
272 aos_level = DEBUG;
273 break;
274 }
275 LOG(aos_level, "Seasocks: %s\n", message);
276}
277
278} // namespace dashboard
279} // namespace y2016
280
281int main(int, char *[]) {
282 ::aos::InitNRT();
283
284 ::seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
285 new ::y2016::dashboard::SeasocksLogger(seasocks::Logger::INFO)));
286 ::y2016::dashboard::SocketHandler socket_handler;
287
288 server.addWebSocketHandler(
289 "/ws",
290 ::std::shared_ptr<::y2016::dashboard::SocketHandler>(&socket_handler));
291#ifdef DASHBOARD_TESTING
292 server.serve("www", 1180);
293#else
294 // Absolute directory of www folder on the robot.
295 server.serve("/home/admin/robot_code/www", 1180);
296#endif
297
298 socket_handler.Quit();
299
300 ::aos::Cleanup();
301 return 0;
302}