blob: 80e5fb41aafbf942e2a32230d27958ca18911943 [file] [log] [blame]
Austin Schuh53596192019-03-23 18:53:33 -07001#include <sys/stat.h>
2#include <sys/types.h>
3#include <unistd.h>
Brian Silvermanb42ff2d2019-03-23 15:36:39 -07004#include <array>
Brian Silvermanacdabeb2019-03-23 14:04:36 -07005#include <memory>
6#include <set>
Brian Silvermanb42ff2d2019-03-23 15:36:39 -07007#include <sstream>
Brian Silvermanacdabeb2019-03-23 14:04:36 -07008
9#include "aos/init.h"
10#include "aos/logging/logging.h"
11#include "aos/time/time.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070012#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070013#include "internal/Embedded.h"
14#include "seasocks/PrintfLogger.h"
15#include "seasocks/Server.h"
16#include "seasocks/StringUtil.h"
17#include "seasocks/WebSocket.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070018#include "y2019/control_loops/drivetrain/camera.q.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070019
20namespace y2019 {
21namespace vision {
22
23class WebsocketHandler : public seasocks::WebSocket::Handler {
24 public:
25 WebsocketHandler();
26 void onConnect(seasocks::WebSocket* connection) override;
27 void onData(seasocks::WebSocket* connection, const char* data) override;
28 void onDisconnect(seasocks::WebSocket* connection) override;
29
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070030 void SendData(const std::string &data);
31
Brian Silvermanacdabeb2019-03-23 14:04:36 -070032 private:
33 std::set<seasocks::WebSocket *> connections_;
34};
35
36WebsocketHandler::WebsocketHandler() {
37}
38
39void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
40 connections_.insert(connection);
41 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
42 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
43}
44
45void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
46 const char *data) {
47 LOG(INFO, "Got data: %s\n", data);
48}
49
50void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
51 connections_.erase(connection);
52 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
53 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
54}
55
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070056void WebsocketHandler::SendData(const std::string &data) {
57 for (seasocks::WebSocket *websocket : connections_) {
58 websocket->send(reinterpret_cast<const uint8_t *>(data.data()),
59 data.size());
60 }
61}
62
Brian Silvermanacdabeb2019-03-23 14:04:36 -070063// TODO(Brian): Put this somewhere shared.
64class SeasocksLogger : public seasocks::PrintfLogger {
65 public:
66 SeasocksLogger(Level min_level_to_log) : PrintfLogger(min_level_to_log) {}
67 void log(Level level, const char* message) override;
68};
69
70void SeasocksLogger::log(Level level, const char *message) {
71 // Convert Seasocks error codes to AOS.
72 log_level aos_level;
73 switch (level) {
74 case seasocks::Logger::INFO:
75 aos_level = INFO;
76 break;
77 case seasocks::Logger::WARNING:
78 aos_level = WARNING;
79 break;
80 case seasocks::Logger::ERROR:
81 case seasocks::Logger::SEVERE:
82 aos_level = ERROR;
83 break;
84 case seasocks::Logger::DEBUG:
85 case seasocks::Logger::ACCESS:
86 default:
87 aos_level = DEBUG;
88 break;
89 }
90 LOG(aos_level, "Seasocks: %s\n", message);
91}
92
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070093struct LocalCameraTarget {
94 double x, y, theta;
95};
96
97struct LocalCameraFrame {
98 aos::monotonic_clock::time_point capture_time =
99 aos::monotonic_clock::min_time;
100 std::vector<LocalCameraTarget> targets;
101
102 bool IsValid(aos::monotonic_clock::time_point now) {
103 return capture_time + std::chrono::seconds(1) > now;
104 }
105};
106
107// Sends a new chunk of data to all the websocket clients.
108class UpdateData : public seasocks::Server::Runnable {
109 public:
110 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
111 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
112 ~UpdateData() override = default;
113 UpdateData(const UpdateData &) = delete;
114 UpdateData &operator=(const UpdateData &) = delete;
115
116 void run() override { websocket_handler_->SendData(data_); }
117
118 private:
119 WebsocketHandler *const websocket_handler_;
120 const std::string data_;
121};
122
123void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
124 auto &camera_frames = y2019::control_loops::drivetrain::camera_frames;
125 auto &drivetrain_status = frc971::control_loops::drivetrain_queue.status;
126
127 std::array<LocalCameraFrame, 5> latest_frames;
128 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
129
130 while (true) {
131 camera_frames.FetchNextBlocking();
132 drivetrain_status.FetchLatest();
133 if (!drivetrain_status.get()) {
134 // Try again if we don't have any drivetrain statuses.
135 continue;
136 }
137
138 {
139 const auto &new_frame = *camera_frames;
140 if (new_frame.camera < latest_frames.size()) {
141 latest_frames[new_frame.camera].capture_time =
142 aos::monotonic_clock::time_point(
143 std::chrono::nanoseconds(new_frame.timestamp));
144 latest_frames[new_frame.camera].targets.clear();
145 for (int target = 0; target < new_frame.num_targets; ++target) {
146 latest_frames[new_frame.camera].targets.emplace_back();
147 // TODO: Do something useful.
148 }
149 }
150 }
151
152 const auto now = aos::monotonic_clock::now();
153 if (now > last_send_time + std::chrono::milliseconds(100)) {
154 last_send_time = now;
155 std::ostringstream stream;
156 stream << "{\n";
157
158 stream << "'robot': {";
159 stream << "'x': " << drivetrain_status->x << ",";
160 stream << "'y': " << drivetrain_status->y << ",";
161 stream << "'theta': " << drivetrain_status->theta;
162 stream << "}\n";
163
164 stream << "}";
165 server->execute(
166 std::make_shared<UpdateData>(websocket_handler, stream.str()));
167 }
168 }
169}
170
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700171} // namespace vision
172} // namespace y2019
173
174int main(int, char *[]) {
175 // Make sure to reference this to force the linker to include it.
176 findEmbeddedContent("");
177
178 aos::InitNRT();
179
180 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
181 new y2019::vision::SeasocksLogger(seasocks::Logger::INFO)));
182
183 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700184 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700185
186 std::thread data_thread{[&server, websocket_handler]() {
187 y2019::vision::DataThread(&server, websocket_handler.get());
188 }};
189
Austin Schuh53596192019-03-23 18:53:33 -0700190 // See if we are on a robot. If so, serve the robot www folder.
191 bool serve_www = false;
192 {
193 struct stat result;
194 if (stat("/home/admin/robot_code/www", &result) == 0) {
195 serve_www = true;
196 }
197 }
198
199 server.serve(
200 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
201 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700202
203 return 0;
204}