blob: bbd24fca2e4426d2e8d3b83ce632a94c60e6e455 [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>
Alex Perryd13750f2019-04-10 21:15:28 -07005#include <cmath>
Brian Silvermanacdabeb2019-03-23 14:04:36 -07006#include <memory>
7#include <set>
Brian Silvermanb42ff2d2019-03-23 15:36:39 -07008#include <sstream>
Brian Silvermanacdabeb2019-03-23 14:04:36 -07009
10#include "aos/init.h"
11#include "aos/logging/logging.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070012#include "aos/seasocks/seasocks_logger.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070013#include "aos/time/time.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070014#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Alex Perryd13750f2019-04-10 21:15:28 -070015#include "frc971/control_loops/pose.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070016#include "google/protobuf/util/json_util.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070017#include "internal/Embedded.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070018#include "seasocks/Server.h"
19#include "seasocks/StringUtil.h"
20#include "seasocks/WebSocket.h"
Alex Perryd13750f2019-04-10 21:15:28 -070021#include "y2019/constants.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070022#include "y2019/control_loops/drivetrain/camera.q.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070023#include "y2019/vision/server/server_data.pb.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070024
25namespace y2019 {
26namespace vision {
27
28class WebsocketHandler : public seasocks::WebSocket::Handler {
29 public:
30 WebsocketHandler();
31 void onConnect(seasocks::WebSocket* connection) override;
32 void onData(seasocks::WebSocket* connection, const char* data) override;
33 void onDisconnect(seasocks::WebSocket* connection) override;
34
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070035 void SendData(const std::string &data);
36
Brian Silvermanacdabeb2019-03-23 14:04:36 -070037 private:
38 std::set<seasocks::WebSocket *> connections_;
39};
40
41WebsocketHandler::WebsocketHandler() {
42}
43
44void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
45 connections_.insert(connection);
46 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
47 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
48}
49
50void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
51 const char *data) {
52 LOG(INFO, "Got data: %s\n", data);
53}
54
55void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
56 connections_.erase(connection);
57 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
58 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
59}
60
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070061void WebsocketHandler::SendData(const std::string &data) {
62 for (seasocks::WebSocket *websocket : connections_) {
63 websocket->send(reinterpret_cast<const uint8_t *>(data.data()),
64 data.size());
65 }
66}
67
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070068struct LocalCameraTarget {
69 double x, y, theta;
70};
71
72struct LocalCameraFrame {
73 aos::monotonic_clock::time_point capture_time =
74 aos::monotonic_clock::min_time;
75 std::vector<LocalCameraTarget> targets;
76
77 bool IsValid(aos::monotonic_clock::time_point now) {
78 return capture_time + std::chrono::seconds(1) > now;
79 }
80};
81
82// Sends a new chunk of data to all the websocket clients.
83class UpdateData : public seasocks::Server::Runnable {
84 public:
85 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
86 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
87 ~UpdateData() override = default;
88 UpdateData(const UpdateData &) = delete;
89 UpdateData &operator=(const UpdateData &) = delete;
90
91 void run() override { websocket_handler_->SendData(data_); }
92
93 private:
94 WebsocketHandler *const websocket_handler_;
95 const std::string data_;
96};
97
98void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
99 auto &camera_frames = y2019::control_loops::drivetrain::camera_frames;
100 auto &drivetrain_status = frc971::control_loops::drivetrain_queue.status;
101
102 std::array<LocalCameraFrame, 5> latest_frames;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700103 std::array<aos::monotonic_clock::time_point, 5> last_target_time;
104 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700105 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
106
107 while (true) {
108 camera_frames.FetchNextBlocking();
109 drivetrain_status.FetchLatest();
110 if (!drivetrain_status.get()) {
111 // Try again if we don't have any drivetrain statuses.
112 continue;
113 }
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700114 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700115
116 {
117 const auto &new_frame = *camera_frames;
Alex Perryd13750f2019-04-10 21:15:28 -0700118 // TODO(james): Maybe we only want to fill out a new frame if it has
119 // targets or the saved target is > 0.1 sec old? Not sure, but for now
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700120 if (new_frame.camera < latest_frames.size()) {
121 latest_frames[new_frame.camera].capture_time =
122 aos::monotonic_clock::time_point(
123 std::chrono::nanoseconds(new_frame.timestamp));
124 latest_frames[new_frame.camera].targets.clear();
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700125 if (new_frame.num_targets > 0) {
James Kuszmauld6d37d12019-03-30 13:04:54 -0700126 last_target_time[new_frame.camera] =
127 latest_frames[new_frame.camera].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700128 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700129 for (int target = 0; target < new_frame.num_targets; ++target) {
130 latest_frames[new_frame.camera].targets.emplace_back();
Alex Perryd13750f2019-04-10 21:15:28 -0700131 const float heading = new_frame.targets[target].heading;
132 const float distance = new_frame.targets[target].distance;
133 latest_frames[new_frame.camera].targets.back().x =
134 ::std::cos(heading) * distance;
135 latest_frames[new_frame.camera].targets.back().y =
136 ::std::sin(heading) * distance;
137 latest_frames[new_frame.camera].targets.back().theta =
138 new_frame.targets[target].skew;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700139 }
140 }
141 }
142
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700143 if (now > last_send_time + std::chrono::milliseconds(100)) {
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700144 // TODO(james): Use protobuf or the such to generate JSON rather than
145 // doing so manually.
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700146 last_send_time = now;
James Kuszmauld6d37d12019-03-30 13:04:54 -0700147 DebugData debug_data;
148 debug_data.mutable_robot_pose()->set_x(drivetrain_status->x);
149 debug_data.mutable_robot_pose()->set_y(drivetrain_status->y);
150 debug_data.mutable_robot_pose()->set_theta(drivetrain_status->theta);
Alex Perryd13750f2019-04-10 21:15:28 -0700151 frc971::control_loops::TypedPose<double> robot_pose(
152 {drivetrain_status->x, drivetrain_status->y, 0},
153 drivetrain_status->theta);
James Kuszmauld6d37d12019-03-30 13:04:54 -0700154 {
155 LineFollowDebug *line_debug = debug_data.mutable_line_follow_debug();
156 line_debug->set_frozen(drivetrain_status->line_follow_logging.frozen);
157 line_debug->set_have_target(
158 drivetrain_status->line_follow_logging.have_target);
159 line_debug->mutable_goal_target()->set_x(
160 drivetrain_status->line_follow_logging.x);
161 line_debug->mutable_goal_target()->set_y(
162 drivetrain_status->line_follow_logging.y);
163 line_debug->mutable_goal_target()->set_theta(
164 drivetrain_status->line_follow_logging.theta);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700165 }
James Kuszmauld6d37d12019-03-30 13:04:54 -0700166 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
167 CameraDebug *camera_debug = debug_data.add_camera_debug();
168 LocalCameraFrame cur_frame = latest_frames[ii];
Alex Perryd13750f2019-04-10 21:15:28 -0700169 constants::Values::CameraCalibration camera_info =
170 constants::GetValues().cameras[ii];
James Kuszmaul4374fd12019-04-13 08:28:58 -0700171 frc971::control_loops::TypedPose<double> camera_pose = camera_info.pose;
172 camera_pose.set_base(&robot_pose);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700173
James Kuszmauld6d37d12019-03-30 13:04:54 -0700174 camera_debug->set_current_frame_age(
175 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
176 now - cur_frame.capture_time).count());
177 camera_debug->set_time_since_last_target(
178 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
179 now - last_target_time[ii]).count());
180 for (const auto &target : cur_frame.targets) {
Alex Perryd13750f2019-04-10 21:15:28 -0700181 frc971::control_loops::TypedPose<double> target_pose(
182 &camera_pose, {target.x, target.y, 0}, target.theta);
James Kuszmauld6d37d12019-03-30 13:04:54 -0700183 Pose *pose = camera_debug->add_targets();
Alex Perryd13750f2019-04-10 21:15:28 -0700184 pose->set_x(target_pose.abs_pos().x());
185 pose->set_y(target_pose.abs_pos().y());
186 pose->set_theta(target_pose.abs_theta());
James Kuszmauld6d37d12019-03-30 13:04:54 -0700187 }
188 }
189 ::std::string json;
190 google::protobuf::util::MessageToJsonString(debug_data, &json);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700191 server->execute(
James Kuszmauld6d37d12019-03-30 13:04:54 -0700192 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700193 }
194 }
195}
196
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700197} // namespace vision
198} // namespace y2019
199
200int main(int, char *[]) {
201 // Make sure to reference this to force the linker to include it.
202 findEmbeddedContent("");
203
204 aos::InitNRT();
205
206 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700207 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700208
209 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700210 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700211
212 std::thread data_thread{[&server, websocket_handler]() {
213 y2019::vision::DataThread(&server, websocket_handler.get());
214 }};
215
Austin Schuh53596192019-03-23 18:53:33 -0700216 // See if we are on a robot. If so, serve the robot www folder.
217 bool serve_www = false;
218 {
219 struct stat result;
220 if (stat("/home/admin/robot_code/www", &result) == 0) {
221 serve_www = true;
222 }
223 }
224
225 server.serve(
226 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
227 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700228
229 return 0;
230}