blob: e531f515fdb1e9150e6eaa18d71477e18a195dc2 [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_) {
Austin Schuh89a95052019-04-14 14:51:14 -070063 websocket->send(data.c_str());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070064 }
65}
66
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070067struct LocalCameraTarget {
68 double x, y, theta;
69};
70
71struct LocalCameraFrame {
72 aos::monotonic_clock::time_point capture_time =
73 aos::monotonic_clock::min_time;
74 std::vector<LocalCameraTarget> targets;
75
76 bool IsValid(aos::monotonic_clock::time_point now) {
77 return capture_time + std::chrono::seconds(1) > now;
78 }
79};
80
81// Sends a new chunk of data to all the websocket clients.
82class UpdateData : public seasocks::Server::Runnable {
83 public:
84 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
85 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
86 ~UpdateData() override = default;
87 UpdateData(const UpdateData &) = delete;
88 UpdateData &operator=(const UpdateData &) = delete;
89
90 void run() override { websocket_handler_->SendData(data_); }
91
92 private:
93 WebsocketHandler *const websocket_handler_;
94 const std::string data_;
95};
96
97void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
98 auto &camera_frames = y2019::control_loops::drivetrain::camera_frames;
99 auto &drivetrain_status = frc971::control_loops::drivetrain_queue.status;
100
101 std::array<LocalCameraFrame, 5> latest_frames;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700102 std::array<aos::monotonic_clock::time_point, 5> last_target_time;
103 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700104 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
105
106 while (true) {
107 camera_frames.FetchNextBlocking();
108 drivetrain_status.FetchLatest();
109 if (!drivetrain_status.get()) {
110 // Try again if we don't have any drivetrain statuses.
111 continue;
112 }
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700113 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700114
115 {
116 const auto &new_frame = *camera_frames;
Alex Perryd13750f2019-04-10 21:15:28 -0700117 // TODO(james): Maybe we only want to fill out a new frame if it has
118 // targets or the saved target is > 0.1 sec old? Not sure, but for now
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700119 if (new_frame.camera < latest_frames.size()) {
120 latest_frames[new_frame.camera].capture_time =
121 aos::monotonic_clock::time_point(
122 std::chrono::nanoseconds(new_frame.timestamp));
123 latest_frames[new_frame.camera].targets.clear();
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700124 if (new_frame.num_targets > 0) {
James Kuszmauld6d37d12019-03-30 13:04:54 -0700125 last_target_time[new_frame.camera] =
126 latest_frames[new_frame.camera].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700127 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700128 for (int target = 0; target < new_frame.num_targets; ++target) {
129 latest_frames[new_frame.camera].targets.emplace_back();
Alex Perryd13750f2019-04-10 21:15:28 -0700130 const float heading = new_frame.targets[target].heading;
131 const float distance = new_frame.targets[target].distance;
132 latest_frames[new_frame.camera].targets.back().x =
133 ::std::cos(heading) * distance;
134 latest_frames[new_frame.camera].targets.back().y =
135 ::std::sin(heading) * distance;
136 latest_frames[new_frame.camera].targets.back().theta =
137 new_frame.targets[target].skew;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700138 }
139 }
140 }
141
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700142 if (now > last_send_time + std::chrono::milliseconds(100)) {
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700143 // TODO(james): Use protobuf or the such to generate JSON rather than
144 // doing so manually.
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700145 last_send_time = now;
James Kuszmauld6d37d12019-03-30 13:04:54 -0700146 DebugData debug_data;
147 debug_data.mutable_robot_pose()->set_x(drivetrain_status->x);
148 debug_data.mutable_robot_pose()->set_y(drivetrain_status->y);
149 debug_data.mutable_robot_pose()->set_theta(drivetrain_status->theta);
Alex Perryd13750f2019-04-10 21:15:28 -0700150 frc971::control_loops::TypedPose<double> robot_pose(
151 {drivetrain_status->x, drivetrain_status->y, 0},
152 drivetrain_status->theta);
James Kuszmauld6d37d12019-03-30 13:04:54 -0700153 {
154 LineFollowDebug *line_debug = debug_data.mutable_line_follow_debug();
155 line_debug->set_frozen(drivetrain_status->line_follow_logging.frozen);
156 line_debug->set_have_target(
157 drivetrain_status->line_follow_logging.have_target);
158 line_debug->mutable_goal_target()->set_x(
159 drivetrain_status->line_follow_logging.x);
160 line_debug->mutable_goal_target()->set_y(
161 drivetrain_status->line_follow_logging.y);
162 line_debug->mutable_goal_target()->set_theta(
163 drivetrain_status->line_follow_logging.theta);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700164 }
James Kuszmauld6d37d12019-03-30 13:04:54 -0700165 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
166 CameraDebug *camera_debug = debug_data.add_camera_debug();
167 LocalCameraFrame cur_frame = latest_frames[ii];
Alex Perryd13750f2019-04-10 21:15:28 -0700168 constants::Values::CameraCalibration camera_info =
169 constants::GetValues().cameras[ii];
James Kuszmaul4374fd12019-04-13 08:28:58 -0700170 frc971::control_loops::TypedPose<double> camera_pose = camera_info.pose;
171 camera_pose.set_base(&robot_pose);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700172
James Kuszmauld6d37d12019-03-30 13:04:54 -0700173 camera_debug->set_current_frame_age(
174 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
175 now - cur_frame.capture_time).count());
176 camera_debug->set_time_since_last_target(
177 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
178 now - last_target_time[ii]).count());
179 for (const auto &target : cur_frame.targets) {
Alex Perryd13750f2019-04-10 21:15:28 -0700180 frc971::control_loops::TypedPose<double> target_pose(
181 &camera_pose, {target.x, target.y, 0}, target.theta);
James Kuszmauld6d37d12019-03-30 13:04:54 -0700182 Pose *pose = camera_debug->add_targets();
Alex Perryd13750f2019-04-10 21:15:28 -0700183 pose->set_x(target_pose.abs_pos().x());
184 pose->set_y(target_pose.abs_pos().y());
185 pose->set_theta(target_pose.abs_theta());
James Kuszmauld6d37d12019-03-30 13:04:54 -0700186 }
187 }
188 ::std::string json;
189 google::protobuf::util::MessageToJsonString(debug_data, &json);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700190 server->execute(
James Kuszmauld6d37d12019-03-30 13:04:54 -0700191 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700192 }
193 }
194}
195
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700196} // namespace vision
197} // namespace y2019
198
199int main(int, char *[]) {
200 // Make sure to reference this to force the linker to include it.
201 findEmbeddedContent("");
202
203 aos::InitNRT();
204
205 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700206 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700207
208 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700209 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700210
211 std::thread data_thread{[&server, websocket_handler]() {
212 y2019::vision::DataThread(&server, websocket_handler.get());
213 }};
214
Austin Schuh53596192019-03-23 18:53:33 -0700215 // See if we are on a robot. If so, serve the robot www folder.
216 bool serve_www = false;
217 {
218 struct stat result;
219 if (stat("/home/admin/robot_code/www", &result) == 0) {
220 serve_www = true;
221 }
222 }
223
224 server.serve(
225 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
226 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700227
228 return 0;
229}