blob: 1b79555c7c175e73c08414ed5477fa2efaf82975 [file] [log] [blame]
Brian Silvermanb691f5e2015-08-02 11:37:55 -07001#include "y2015/http_status/http_status.h"
Comran Morshedfe7f9ea2015-02-19 23:52:57 +00002
Austin Schuhf2a50ba2016-12-24 16:16:26 -08003#include <chrono>
Comran Morshedfe7f9ea2015-02-19 23:52:57 +00004#include <iostream>
Comran Morshedfe7f9ea2015-02-19 23:52:57 +00005#include <sstream>
6#include <string>
Comran Morshedc4ce9512015-03-08 11:51:09 +00007#include <thread>
8#include <vector>
Comran Morshedfe7f9ea2015-02-19 23:52:57 +00009
Brian Silverman9be949c2015-05-14 00:15:21 -040010#include "seasocks/Server.h"
11
Comran Morshedc4ce9512015-03-08 11:51:09 +000012#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"
Comran Morshedfe7f9ea2015-02-19 23:52:57 +000017
Brian Silvermanb691f5e2015-08-02 11:37:55 -070018#include "y2015/control_loops/claw/claw.q.h"
19#include "y2015/control_loops/fridge/fridge.q.h"
Comran Morshedfe7f9ea2015-02-19 23:52:57 +000020
Brian Silvermanb691f5e2015-08-02 11:37:55 -070021#include "y2015/http_status/embedded.h"
Comran Morshedc4ce9512015-03-08 11:51:09 +000022
Austin Schuh88af0852016-12-04 20:31:32 -080023namespace y2015 {
Comran Morshedc4ce9512015-03-08 11:51:09 +000024namespace http_status {
25
Austin Schuhf2a50ba2016-12-24 16:16:26 -080026using ::aos::monotonic_clock;
27namespace chrono = ::std::chrono;
28
Comran Morshedc4ce9512015-03-08 11:51:09 +000029// TODO(comran): Make some of these separate libraries & document them better.
30
31HTTPStatusMessage::HTTPStatusMessage()
32 : sample_id_(0),
33 measure_index_(0),
34 overflow_id_(200),
35 num_samples_per_packet_(50) {}
36
37void HTTPStatusMessage::NextSample() {
38 int32_t adjusted_index = GetIndex(sample_id_);
39
Austin Schuhf2a50ba2016-12-24 16:16:26 -080040 const monotonic_clock::time_point monotonic_now = monotonic_clock::now();
Comran Morshedc4ce9512015-03-08 11:51:09 +000041
42 if (sample_id_ < overflow_id_) {
Austin Schuhf2a50ba2016-12-24 16:16:26 -080043 sample_times_.emplace_back(monotonic_now);
Comran Morshedc4ce9512015-03-08 11:51:09 +000044 data_values_.emplace_back(::std::vector<double>());
45 } else {
Austin Schuhf2a50ba2016-12-24 16:16:26 -080046 sample_times_[adjusted_index] = monotonic_now;
Comran Morshedc4ce9512015-03-08 11:51:09 +000047 }
48
49 sample_id_++;
50 measure_index_ = 0;
51
52 CHECK(!mutex_.Lock()); // Lock the mutex so measures can be added.
53}
54
55void HTTPStatusMessage::EndSample() { mutex_.Unlock(); }
56
57int32_t HTTPStatusMessage::GetIndex(int32_t sample_id) {
58 return sample_id % overflow_id_;
59}
60
61void HTTPStatusMessage::AddMeasure(::std::string name, double value) {
62 // Mutex should be locked when this method is called to synchronize packets.
63 assert(mutex_.OwnedBySelf());
64
65 int32_t index = GetIndex(sample_id_ - 1);
66
67 if (measure_index_ >= static_cast<int32_t>(data_names_.size())) {
68 data_names_.emplace_back(name);
69 }
70
71 if (measure_index_ >= static_cast<int32_t>(data_values_.at(index).size())) {
72 data_values_.at(index).emplace_back(value);
73 } else {
74 data_values_.at(index).at(measure_index_) = value;
75 }
76 measure_index_++;
77}
78
79::std::string HTTPStatusMessage::Fetch(size_t from_sample) {
80 ::aos::MutexLocker locker(&mutex_);
81
82 ::std::stringstream message;
83 message.precision(10); // Cap how precise the time/measurement data is.
84
85 // To save space, data is being sent with a custom protocol over to the
86 // client.
87 // Initially, a message containing all the names of the measurements is sent
88 // and is preceeded with a *.
89 // Names begin with a star and are split with commas.
90
91 // Example: *test,test2
92 if (static_cast<int32_t>(from_sample) == -1) {
93 message << "*";
94 for (int32_t cur_data_name = 0;
95 cur_data_name < static_cast<int32_t>(data_names_.size());
96 cur_data_name++) {
97 if (cur_data_name > 0) {
98 message << ",";
99 }
100 message << data_names_.at(cur_data_name);
101 }
102 return message.str();
103 }
104
105 // TODO(comran): Use from_sample to determine the speed packets should be sent
106 // out to avoid skipping packets.
107 from_sample = sample_id_ - num_samples_per_packet_;
108
109 // Data packets are sent, with raw data being placed at the same index as the
110 // original index of the measurement name sent in the initial packet.
111 // Samples are split with dollar signs, info with percent signs, and
112 // measurements with commas.
113 // This special format system is helpful for debugging issues and looping
114 // through the data on the client side.
115
116 // Example of two samples that correspond with the initialized example:
117 // 289%2803.135127%10,67$290%2803.140109%12,68
118 for (int32_t cur_sample = from_sample;
119 cur_sample <
120 static_cast<int32_t>(from_sample + num_samples_per_packet_) &&
121 GetIndex(cur_sample) < static_cast<int32_t>(data_values_.size());
122 cur_sample++) {
123 if (cur_sample != static_cast<int32_t>(from_sample)) {
124 message << "$";
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000125 }
126
Comran Morshedc4ce9512015-03-08 11:51:09 +0000127 int32_t adjusted_index = GetIndex(cur_sample);
128
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800129 message << cur_sample << "%"
130 << chrono::duration_cast<chrono::duration<double>>(
131 sample_times_.at(adjusted_index).time_since_epoch())
132 .count()
Comran Morshedc4ce9512015-03-08 11:51:09 +0000133 << "%";
134 for (int32_t cur_measure = 0;
135 cur_measure < static_cast<int32_t>(data_names_.size());
136 cur_measure++) {
137 if (cur_measure > 0) {
138 message << ",";
139 }
140 message << data_values_.at(adjusted_index).at(cur_measure);
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000141 }
Comran Morshedc4ce9512015-03-08 11:51:09 +0000142 }
143 return message.str();
144}
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000145
Comran Morshedc4ce9512015-03-08 11:51:09 +0000146DataCollector::DataCollector() : cur_raw_data_("no data") {}
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000147
Comran Morshedc4ce9512015-03-08 11:51:09 +0000148void DataCollector::RunIteration() {
Austin Schuh88af0852016-12-04 20:31:32 -0800149 auto& fridge_queue = control_loops::fridge::fridge_queue;
Comran Morshedc4ce9512015-03-08 11:51:09 +0000150 auto& claw_queue = control_loops::claw_queue;
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000151
Comran Morshedc4ce9512015-03-08 11:51:09 +0000152 fridge_queue.status.FetchAnother();
153 claw_queue.status.FetchAnother();
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000154
Comran Morshedc4ce9512015-03-08 11:51:09 +0000155 message_.NextSample();
156 // Add recorded data here. /////
157 // NOTE: Try to use fewer than 30 measures, or the whole thing will lag.
158 // Abbreviate names if long, otherwise just use the command to get the value
159 // from the queue.
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000160
Comran Morshedc4ce9512015-03-08 11:51:09 +0000161 // TODO(comran): Make it so that the name doesn't have to be copied as a
162 // string.
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000163
Comran Morshedc4ce9512015-03-08 11:51:09 +0000164 // //// Fridge
165 // Positions
166 message_.AddMeasure("(fridge position left arm encoder)",
167 fridge_queue.position->arm.left.encoder);
168 message_.AddMeasure("(fridge position right arm encoder)",
169 fridge_queue.position->arm.right.encoder);
170 message_.AddMeasure("(fridge position left elev encoder)",
171 fridge_queue.position->elevator.left.encoder);
172 message_.AddMeasure("(fridge position right elev encoder)",
173 fridge_queue.position->elevator.right.encoder);
174 // Goals
175 message_.AddMeasure("fridge_queue.goal->profiling_type",
176 fridge_queue.goal->profiling_type);
177 message_.AddMeasure("fridge_queue.goal->angle", fridge_queue.goal->angle);
178 message_.AddMeasure("fridge_queue.goal->angular_velocity",
179 fridge_queue.goal->angular_velocity);
180 message_.AddMeasure("fridge_queue.goal->height", fridge_queue.goal->height);
181 message_.AddMeasure("fridge_queue.goal->velocity",
182 fridge_queue.goal->velocity);
183 message_.AddMeasure("fridge_queue.x", fridge_queue.goal->x);
184 message_.AddMeasure("fridge_queue.x_velocity", fridge_queue.goal->x_velocity);
185 message_.AddMeasure("fridge_queue.y", fridge_queue.goal->y);
186 message_.AddMeasure("fridge_queue.y_velocity", fridge_queue.goal->y_velocity);
187 // Statuses
188 message_.AddMeasure("fridge_queue.status->height",
189 fridge_queue.status->height);
190 message_.AddMeasure("fridge_queue.status->velocity",
191 fridge_queue.status->velocity);
192 message_.AddMeasure("fridge_queue.status->angle", fridge_queue.status->angle);
193 message_.AddMeasure("fridge_queue.status->angular_velocity",
194 fridge_queue.status->angular_velocity);
195 message_.AddMeasure("fridge_queue.status->x", fridge_queue.status->x);
196 message_.AddMeasure("fridge_queue.status->x_velocity",
197 fridge_queue.status->x_velocity);
198 message_.AddMeasure("fridge_queue.status->y", fridge_queue.status->y);
199 message_.AddMeasure("fridge_queue.status->y_velocity",
200 fridge_queue.status->y_velocity);
201 message_.AddMeasure("fridge_queue.status->state", fridge_queue.status->state);
202 message_.AddMeasure("fridge_queue.status->zeroed",
203 fridge_queue.status->zeroed);
204 message_.AddMeasure("fridge_queue.status->estopped",
205 fridge_queue.status->estopped);
206 // Outputs
207 message_.AddMeasure("fridge_queue.output->left_arm",
208 fridge_queue.output->left_arm);
209 message_.AddMeasure("fridge_queue.output->right_arm",
210 fridge_queue.output->right_arm);
211 message_.AddMeasure("fridge_queue.output->left_elevator",
212 fridge_queue.output->left_elevator);
213 message_.AddMeasure("fridge_queue.output->right_elevator",
214 fridge_queue.output->right_elevator);
215 // End recorded data. /////
216 message_.EndSample();
217}
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000218
Comran Morshedc4ce9512015-03-08 11:51:09 +0000219::std::string DataCollector::GetData(int32_t from_sample) {
220 return message_.Fetch(from_sample);
221}
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000222
Comran Morshedc4ce9512015-03-08 11:51:09 +0000223void DataCollector::operator()() {
224 ::aos::SetCurrentThreadName("HTTPStatusData");
225
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800226 ::aos::time::PhasedLoop phased_loop(chrono::milliseconds(5),
227 chrono::microseconds(0));
Comran Morshedc4ce9512015-03-08 11:51:09 +0000228 while (run_) {
Austin Schuhf2a50ba2016-12-24 16:16:26 -0800229 phased_loop.SleepUntilNext();
Comran Morshedc4ce9512015-03-08 11:51:09 +0000230 RunIteration();
231 }
232}
233
234SocketHandler::SocketHandler()
235 : data_collector_thread_(::std::ref(data_collector_)) {}
236
237void SocketHandler::onConnect(seasocks::WebSocket* connection) {
238 connections_.insert(connection);
239 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
240 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
241}
242
243void SocketHandler::onData(seasocks::WebSocket* connection, const char* data) {
244 int32_t from_sample = atoi(data);
245
246 ::std::string send_data = data_collector_.GetData(from_sample);
247 connection->send(send_data.c_str());
248}
249
250void SocketHandler::onDisconnect(seasocks::WebSocket* connection) {
251 connections_.erase(connection);
252 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
253 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
254}
255
256void SocketHandler::Quit() {
257 data_collector_.Quit();
258 data_collector_thread_.join();
259}
260
261SeasocksLogger::SeasocksLogger(Level min_level_to_log)
262 : PrintfLogger(min_level_to_log) {}
263
264void SeasocksLogger::log(Level level, const char* message) {
265 log_level aos_level;
266 switch (level) {
267 case seasocks::Logger::INFO:
268 aos_level = INFO;
269 break;
270 case seasocks::Logger::WARNING:
271 aos_level = WARNING;
272 break;
273 case seasocks::Logger::ERROR:
274 case seasocks::Logger::SEVERE:
275 aos_level = ERROR;
276 break;
277 case seasocks::Logger::DEBUG:
278 case seasocks::Logger::ACCESS:
279 default:
280 aos_level = DEBUG;
281 break;
282 }
283 LOG(aos_level, "Seasocks: %s\n", message);
284}
285
286} // namespace http_status
Austin Schuh88af0852016-12-04 20:31:32 -0800287} // namespace y2015
Comran Morshedc4ce9512015-03-08 11:51:09 +0000288
289int main(int, char* []) {
290 ::aos::InitNRT();
291
292 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh88af0852016-12-04 20:31:32 -0800293 new ::y2015::http_status::SeasocksLogger(seasocks::Logger::INFO)));
294 ::y2015::http_status::SocketHandler socket_handler;
Comran Morshedc4ce9512015-03-08 11:51:09 +0000295
296 server.addWebSocketHandler(
297 "/ws",
Austin Schuh88af0852016-12-04 20:31:32 -0800298 ::std::shared_ptr<::y2015::http_status::SocketHandler>(&socket_handler));
Comran Morshedc4ce9512015-03-08 11:51:09 +0000299 server.serve("www", 8080);
300
301 socket_handler.Quit();
302
303 ::aos::Cleanup();
304 return 0;
Comran Morshedfe7f9ea2015-02-19 23:52:57 +0000305}