blob: 44a7c6f45ea4a450ede33b5f3b582e70f071ea5e [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
Austin Schuha966a372019-04-14 17:15:28 -070010#include "aos/containers/ring_buffer.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070011#include "aos/init.h"
12#include "aos/logging/logging.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070013#include "aos/seasocks/seasocks_logger.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070014#include "aos/time/time.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070015#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Alex Perryd13750f2019-04-10 21:15:28 -070016#include "frc971/control_loops/pose.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070017#include "google/protobuf/util/json_util.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070018#include "internal/Embedded.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070019#include "seasocks/Server.h"
20#include "seasocks/StringUtil.h"
21#include "seasocks/WebSocket.h"
Alex Perryd13750f2019-04-10 21:15:28 -070022#include "y2019/constants.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070023#include "y2019/control_loops/drivetrain/camera.q.h"
Austin Schuh71ae7952019-04-14 15:12:52 -070024#include "y2019/control_loops/superstructure/superstructure.q.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070025#include "y2019/vision/server/server_data.pb.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070026
27namespace y2019 {
28namespace vision {
29
Austin Schuha966a372019-04-14 17:15:28 -070030namespace chrono = ::std::chrono;
31
Brian Silvermanacdabeb2019-03-23 14:04:36 -070032class WebsocketHandler : public seasocks::WebSocket::Handler {
33 public:
34 WebsocketHandler();
35 void onConnect(seasocks::WebSocket* connection) override;
36 void onData(seasocks::WebSocket* connection, const char* data) override;
37 void onDisconnect(seasocks::WebSocket* connection) override;
38
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070039 void SendData(const std::string &data);
40
Brian Silvermanacdabeb2019-03-23 14:04:36 -070041 private:
42 std::set<seasocks::WebSocket *> connections_;
43};
44
45WebsocketHandler::WebsocketHandler() {
46}
47
48void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
49 connections_.insert(connection);
50 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
51 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
52}
53
54void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
55 const char *data) {
56 LOG(INFO, "Got data: %s\n", data);
57}
58
59void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
60 connections_.erase(connection);
61 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
62 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
63}
64
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070065void WebsocketHandler::SendData(const std::string &data) {
66 for (seasocks::WebSocket *websocket : connections_) {
Austin Schuh89a95052019-04-14 14:51:14 -070067 websocket->send(data.c_str());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070068 }
69}
70
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070071struct LocalCameraTarget {
72 double x, y, theta;
73};
74
75struct LocalCameraFrame {
76 aos::monotonic_clock::time_point capture_time =
77 aos::monotonic_clock::min_time;
78 std::vector<LocalCameraTarget> targets;
79
80 bool IsValid(aos::monotonic_clock::time_point now) {
Austin Schuha966a372019-04-14 17:15:28 -070081 return capture_time + chrono::seconds(1) > now;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070082 }
83};
84
85// Sends a new chunk of data to all the websocket clients.
86class UpdateData : public seasocks::Server::Runnable {
87 public:
88 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
89 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
90 ~UpdateData() override = default;
91 UpdateData(const UpdateData &) = delete;
92 UpdateData &operator=(const UpdateData &) = delete;
93
94 void run() override { websocket_handler_->SendData(data_); }
95
96 private:
97 WebsocketHandler *const websocket_handler_;
98 const std::string data_;
99};
100
Austin Schuha966a372019-04-14 17:15:28 -0700101struct DrivetrainPosition {
102 ::aos::monotonic_clock::time_point time;
103 double x;
104 double y;
105 double theta;
106};
107
108DrivetrainPosition ComputePosition(
109 const ::aos::RingBuffer<DrivetrainPosition, 200> &data,
110 ::aos::monotonic_clock::time_point time) {
111 DrivetrainPosition drivetrain_now{time, 0.0f, 0.0f, 0.0f};
112
113 const size_t after_index = ::std::max(
114 static_cast<size_t>(1),
115 static_cast<size_t>(::std::distance(
116 data.begin(),
117 ::std::lower_bound(
118 data.begin(), data.end(), drivetrain_now,
119 [](const DrivetrainPosition &a, const DrivetrainPosition &b) {
120 return a.time < b.time;
121 }))));
122 const size_t before_index = after_index - 1;
123
124 const DrivetrainPosition &before = data[before_index];
125 const DrivetrainPosition &after = data[after_index];
126
127 double alpha = static_cast<double>((time - before.time).count()) /
128 static_cast<double>((after.time - before.time).count());
129 drivetrain_now.x = (1.0 - alpha) * before.x + alpha * after.x;
130 drivetrain_now.y = (1.0 - alpha) * before.y + alpha * after.y;
131 drivetrain_now.theta = (1.0 - alpha) * before.theta + alpha * after.theta;
132
133 return drivetrain_now;
134}
135
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700136void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
Austin Schuh8a633d52019-05-12 15:04:01 -0700137 ::aos::ShmEventLoop event_loop;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700138
Austin Schuh8a633d52019-05-12 15:04:01 -0700139 ::aos::Fetcher<::frc971::control_loops::DrivetrainQueue::Status>
140 drivetrain_status_fetcher =
141 event_loop
142 .MakeFetcher<::frc971::control_loops::DrivetrainQueue::Status>(
143 ".frc971.control_loops.drivetrain_queue.status");
144
145 ::aos::Fetcher<
146 ::y2019::control_loops::superstructure::SuperstructureQueue::Status>
147 superstructure_status_fetcher = event_loop.MakeFetcher<
148 ::y2019::control_loops::superstructure::SuperstructureQueue::Status>(
149 ".y2019.control_loops.superstructure.superstructure_queue.status");
150
151 ::std::array<LocalCameraFrame, 5> latest_frames;
152 ::std::array<aos::monotonic_clock::time_point, 5> last_target_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700153 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700154 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
Austin Schuh1445d822019-04-14 15:35:20 -0700155 DebugData debug_data;
Austin Schuha966a372019-04-14 17:15:28 -0700156 ::aos::RingBuffer<DrivetrainPosition, 200> drivetrain_log;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700157
Austin Schuh8a633d52019-05-12 15:04:01 -0700158 event_loop.MakeWatcher(
159 ".y2019.control_loops.drivetrain.camera_frames",
160 [websocket_handler, server, &latest_frames, &last_target_time,
161 &drivetrain_status_fetcher, &superstructure_status_fetcher,
162 &last_send_time, &drivetrain_log,
163 &debug_data](const ::y2019::control_loops::drivetrain::CameraFrame
164 &camera_frames) {
165 while (drivetrain_status_fetcher.FetchNext()) {
166 DrivetrainPosition drivetrain_position{
167 drivetrain_status_fetcher->sent_time,
168 drivetrain_status_fetcher->x, drivetrain_status_fetcher->y,
169 drivetrain_status_fetcher->theta};
Austin Schuha966a372019-04-14 17:15:28 -0700170
Austin Schuh8a633d52019-05-12 15:04:01 -0700171 drivetrain_log.Push(drivetrain_position);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700172 }
Austin Schuh8a633d52019-05-12 15:04:01 -0700173 superstructure_status_fetcher.Fetch();
174 if (!drivetrain_status_fetcher.get() ||
175 !superstructure_status_fetcher.get()) {
176 // Try again if we don't have any drivetrain statuses.
177 return;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700178 }
Austin Schuh8a633d52019-05-12 15:04:01 -0700179 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700180
Austin Schuh8a633d52019-05-12 15:04:01 -0700181 {
182 const auto &new_frame = camera_frames;
183 // TODO(james): Maybe we only want to fill out a new frame if it has
184 // targets or the saved target is > 0.1 sec old? Not sure, but for now
185 if (new_frame.camera < latest_frames.size()) {
186 latest_frames[new_frame.camera].capture_time =
187 aos::monotonic_clock::time_point(
188 chrono::nanoseconds(new_frame.timestamp));
189 latest_frames[new_frame.camera].targets.clear();
190 if (new_frame.num_targets > 0) {
191 last_target_time[new_frame.camera] =
192 latest_frames[new_frame.camera].capture_time;
193 }
194 for (int target = 0; target < new_frame.num_targets; ++target) {
195 latest_frames[new_frame.camera].targets.emplace_back();
196 const float heading = new_frame.targets[target].heading;
197 const float distance = new_frame.targets[target].distance;
198 latest_frames[new_frame.camera].targets.back().x =
199 ::std::cos(heading) * distance;
200 latest_frames[new_frame.camera].targets.back().y =
201 ::std::sin(heading) * distance;
202 latest_frames[new_frame.camera].targets.back().theta =
203 new_frame.targets[target].skew;
204 }
205 }
206 }
Austin Schuha966a372019-04-14 17:15:28 -0700207
Austin Schuh8a633d52019-05-12 15:04:01 -0700208 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
209 CameraDebug *camera_debug = debug_data.add_camera_debug();
210 LocalCameraFrame cur_frame = latest_frames[ii];
211 constants::Values::CameraCalibration camera_info =
212 constants::GetValues().cameras[ii];
213 frc971::control_loops::TypedPose<double> camera_pose =
214 camera_info.pose;
Austin Schuha966a372019-04-14 17:15:28 -0700215
Austin Schuh8a633d52019-05-12 15:04:01 -0700216 const DrivetrainPosition robot_position =
217 ComputePosition(drivetrain_log, cur_frame.capture_time);
218 const ::frc971::control_loops::TypedPose<double> robot_pose(
219 {robot_position.x, robot_position.y, 0}, robot_position.theta);
Austin Schuh1445d822019-04-14 15:35:20 -0700220
Austin Schuh8a633d52019-05-12 15:04:01 -0700221 camera_pose.set_base(&robot_pose);
Austin Schuh1445d822019-04-14 15:35:20 -0700222
Austin Schuh8a633d52019-05-12 15:04:01 -0700223 camera_debug->set_current_frame_age(
224 chrono::duration_cast<chrono::duration<double>>(
225 now - cur_frame.capture_time)
226 .count());
227 camera_debug->set_time_since_last_target(
228 chrono::duration_cast<chrono::duration<double>>(
229 now - last_target_time[ii])
230 .count());
231 for (const auto &target : cur_frame.targets) {
232 frc971::control_loops::TypedPose<double> target_pose(
233 &camera_pose, {target.x, target.y, 0}, target.theta);
234 Pose *pose = camera_debug->add_targets();
235 pose->set_x(target_pose.abs_pos().x());
236 pose->set_y(target_pose.abs_pos().y());
237 pose->set_theta(target_pose.abs_theta());
238 }
239 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700240
Austin Schuh8a633d52019-05-12 15:04:01 -0700241 if (now > last_send_time + chrono::milliseconds(100)) {
242 last_send_time = now;
243 debug_data.mutable_robot_pose()->set_x(drivetrain_status_fetcher->x);
244 debug_data.mutable_robot_pose()->set_y(drivetrain_status_fetcher->y);
245 debug_data.mutable_robot_pose()->set_theta(
246 drivetrain_status_fetcher->theta);
247 {
248 LineFollowDebug *line_debug =
249 debug_data.mutable_line_follow_debug();
250 line_debug->set_frozen(
251 drivetrain_status_fetcher->line_follow_logging.frozen);
252 line_debug->set_have_target(
253 drivetrain_status_fetcher->line_follow_logging.have_target);
254 line_debug->mutable_goal_target()->set_x(
255 drivetrain_status_fetcher->line_follow_logging.x);
256 line_debug->mutable_goal_target()->set_y(
257 drivetrain_status_fetcher->line_follow_logging.y);
258 line_debug->mutable_goal_target()->set_theta(
259 drivetrain_status_fetcher->line_follow_logging.theta);
260 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700261
Austin Schuh8a633d52019-05-12 15:04:01 -0700262 Sensors *sensors = debug_data.mutable_sensors();
263 sensors->set_wrist(superstructure_status_fetcher->wrist.position);
264 sensors->set_elevator(
265 superstructure_status_fetcher->elevator.position);
266 sensors->set_intake(superstructure_status_fetcher->intake.position);
267 sensors->set_stilts(superstructure_status_fetcher->stilts.position);
268 sensors->set_has_piece(superstructure_status_fetcher->has_piece);
Austin Schuh1445d822019-04-14 15:35:20 -0700269
Austin Schuh8a633d52019-05-12 15:04:01 -0700270 ::std::string json;
271 google::protobuf::util::MessageToJsonString(debug_data, &json);
272 server->execute(std::make_shared<UpdateData>(websocket_handler,
273 ::std::move(json)));
274
275 debug_data.Clear();
276 }
277 });
278 event_loop.Run();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700279}
280
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700281} // namespace vision
282} // namespace y2019
283
284int main(int, char *[]) {
285 // Make sure to reference this to force the linker to include it.
286 findEmbeddedContent("");
287
288 aos::InitNRT();
289
290 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700291 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700292
293 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700294 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700295
296 std::thread data_thread{[&server, websocket_handler]() {
297 y2019::vision::DataThread(&server, websocket_handler.get());
298 }};
299
Austin Schuh53596192019-03-23 18:53:33 -0700300 // See if we are on a robot. If so, serve the robot www folder.
301 bool serve_www = false;
302 {
303 struct stat result;
304 if (stat("/home/admin/robot_code/www", &result) == 0) {
305 serve_www = true;
306 }
307 }
308
309 server.serve(
310 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
311 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700312
313 return 0;
314}