blob: 95ecd8de46cc39aea1c325e13b377b57885419e1 [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"
James Kuszmauld6d37d12019-03-30 13:04:54 -070014#include "google/protobuf/util/json_util.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070015#include "seasocks/PrintfLogger.h"
16#include "seasocks/Server.h"
17#include "seasocks/StringUtil.h"
18#include "seasocks/WebSocket.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070019#include "y2019/control_loops/drivetrain/camera.q.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070020#include "y2019/vision/server/server_data.pb.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070021
22namespace y2019 {
23namespace vision {
24
25class WebsocketHandler : public seasocks::WebSocket::Handler {
26 public:
27 WebsocketHandler();
28 void onConnect(seasocks::WebSocket* connection) override;
29 void onData(seasocks::WebSocket* connection, const char* data) override;
30 void onDisconnect(seasocks::WebSocket* connection) override;
31
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070032 void SendData(const std::string &data);
33
Brian Silvermanacdabeb2019-03-23 14:04:36 -070034 private:
35 std::set<seasocks::WebSocket *> connections_;
36};
37
38WebsocketHandler::WebsocketHandler() {
39}
40
41void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
42 connections_.insert(connection);
43 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
44 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
45}
46
47void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
48 const char *data) {
49 LOG(INFO, "Got data: %s\n", data);
50}
51
52void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
53 connections_.erase(connection);
54 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
55 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
56}
57
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070058void WebsocketHandler::SendData(const std::string &data) {
59 for (seasocks::WebSocket *websocket : connections_) {
60 websocket->send(reinterpret_cast<const uint8_t *>(data.data()),
61 data.size());
62 }
63}
64
Brian Silvermanacdabeb2019-03-23 14:04:36 -070065// TODO(Brian): Put this somewhere shared.
66class SeasocksLogger : public seasocks::PrintfLogger {
67 public:
68 SeasocksLogger(Level min_level_to_log) : PrintfLogger(min_level_to_log) {}
69 void log(Level level, const char* message) override;
70};
71
72void SeasocksLogger::log(Level level, const char *message) {
73 // Convert Seasocks error codes to AOS.
74 log_level aos_level;
75 switch (level) {
76 case seasocks::Logger::INFO:
77 aos_level = INFO;
78 break;
79 case seasocks::Logger::WARNING:
80 aos_level = WARNING;
81 break;
82 case seasocks::Logger::ERROR:
83 case seasocks::Logger::SEVERE:
84 aos_level = ERROR;
85 break;
86 case seasocks::Logger::DEBUG:
87 case seasocks::Logger::ACCESS:
88 default:
89 aos_level = DEBUG;
90 break;
91 }
92 LOG(aos_level, "Seasocks: %s\n", message);
93}
94
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070095struct LocalCameraTarget {
96 double x, y, theta;
97};
98
99struct LocalCameraFrame {
100 aos::monotonic_clock::time_point capture_time =
101 aos::monotonic_clock::min_time;
102 std::vector<LocalCameraTarget> targets;
103
104 bool IsValid(aos::monotonic_clock::time_point now) {
105 return capture_time + std::chrono::seconds(1) > now;
106 }
107};
108
109// Sends a new chunk of data to all the websocket clients.
110class UpdateData : public seasocks::Server::Runnable {
111 public:
112 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
113 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
114 ~UpdateData() override = default;
115 UpdateData(const UpdateData &) = delete;
116 UpdateData &operator=(const UpdateData &) = delete;
117
118 void run() override { websocket_handler_->SendData(data_); }
119
120 private:
121 WebsocketHandler *const websocket_handler_;
122 const std::string data_;
123};
124
125void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
126 auto &camera_frames = y2019::control_loops::drivetrain::camera_frames;
127 auto &drivetrain_status = frc971::control_loops::drivetrain_queue.status;
128
129 std::array<LocalCameraFrame, 5> latest_frames;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700130 std::array<aos::monotonic_clock::time_point, 5> last_target_time;
131 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700132 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
133
134 while (true) {
135 camera_frames.FetchNextBlocking();
136 drivetrain_status.FetchLatest();
137 if (!drivetrain_status.get()) {
138 // Try again if we don't have any drivetrain statuses.
139 continue;
140 }
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700141 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700142
143 {
144 const auto &new_frame = *camera_frames;
145 if (new_frame.camera < latest_frames.size()) {
146 latest_frames[new_frame.camera].capture_time =
147 aos::monotonic_clock::time_point(
148 std::chrono::nanoseconds(new_frame.timestamp));
149 latest_frames[new_frame.camera].targets.clear();
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700150 if (new_frame.num_targets > 0) {
James Kuszmauld6d37d12019-03-30 13:04:54 -0700151 last_target_time[new_frame.camera] =
152 latest_frames[new_frame.camera].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700153 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700154 for (int target = 0; target < new_frame.num_targets; ++target) {
155 latest_frames[new_frame.camera].targets.emplace_back();
156 // TODO: Do something useful.
157 }
158 }
159 }
160
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700161 if (now > last_send_time + std::chrono::milliseconds(100)) {
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700162 // TODO(james): Use protobuf or the such to generate JSON rather than
163 // doing so manually.
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700164 last_send_time = now;
James Kuszmauld6d37d12019-03-30 13:04:54 -0700165 DebugData debug_data;
166 debug_data.mutable_robot_pose()->set_x(drivetrain_status->x);
167 debug_data.mutable_robot_pose()->set_y(drivetrain_status->y);
168 debug_data.mutable_robot_pose()->set_theta(drivetrain_status->theta);
169 {
170 LineFollowDebug *line_debug = debug_data.mutable_line_follow_debug();
171 line_debug->set_frozen(drivetrain_status->line_follow_logging.frozen);
172 line_debug->set_have_target(
173 drivetrain_status->line_follow_logging.have_target);
174 line_debug->mutable_goal_target()->set_x(
175 drivetrain_status->line_follow_logging.x);
176 line_debug->mutable_goal_target()->set_y(
177 drivetrain_status->line_follow_logging.y);
178 line_debug->mutable_goal_target()->set_theta(
179 drivetrain_status->line_follow_logging.theta);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700180 }
James Kuszmauld6d37d12019-03-30 13:04:54 -0700181 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
182 CameraDebug *camera_debug = debug_data.add_camera_debug();
183 LocalCameraFrame cur_frame = latest_frames[ii];
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700184
James Kuszmauld6d37d12019-03-30 13:04:54 -0700185 camera_debug->set_current_frame_age(
186 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
187 now - cur_frame.capture_time).count());
188 camera_debug->set_time_since_last_target(
189 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
190 now - last_target_time[ii]).count());
191 for (const auto &target : cur_frame.targets) {
192 Pose *pose = camera_debug->add_targets();
193 pose->set_x(target.x);
194 pose->set_y(target.y);
195 pose->set_theta(target.theta);
196 }
197 }
198 ::std::string json;
199 google::protobuf::util::MessageToJsonString(debug_data, &json);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700200 server->execute(
James Kuszmauld6d37d12019-03-30 13:04:54 -0700201 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700202 }
203 }
204}
205
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700206} // namespace vision
207} // namespace y2019
208
209int main(int, char *[]) {
210 // Make sure to reference this to force the linker to include it.
211 findEmbeddedContent("");
212
213 aos::InitNRT();
214
215 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
216 new y2019::vision::SeasocksLogger(seasocks::Logger::INFO)));
217
218 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700219 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700220
221 std::thread data_thread{[&server, websocket_handler]() {
222 y2019::vision::DataThread(&server, websocket_handler.get());
223 }};
224
Austin Schuh53596192019-03-23 18:53:33 -0700225 // See if we are on a robot. If so, serve the robot www folder.
226 bool serve_www = false;
227 {
228 struct stat result;
229 if (stat("/home/admin/robot_code/www", &result) == 0) {
230 serve_www = true;
231 }
232 }
233
234 server.serve(
235 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
236 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700237
238 return 0;
239}