blob: 91dc61a894905efa7b158b3b7fdc961d3bd47d0c [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"
Alex Perrycb7da4b2019-08-28 19:35:56 -070011#include "aos/events/shm_event_loop.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070012#include "aos/init.h"
13#include "aos/logging/logging.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070014#include "aos/seasocks/seasocks_logger.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070015#include "aos/time/time.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070016#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
Alex Perryd13750f2019-04-10 21:15:28 -070017#include "frc971/control_loops/pose.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070018#include "google/protobuf/util/json_util.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070019#include "internal/Embedded.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070020#include "seasocks/Server.h"
21#include "seasocks/StringUtil.h"
22#include "seasocks/WebSocket.h"
Alex Perryd13750f2019-04-10 21:15:28 -070023#include "y2019/constants.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070024#include "y2019/control_loops/drivetrain/camera_generated.h"
25#include "y2019/control_loops/superstructure/superstructure_status_generated.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070026#include "y2019/vision/server/server_data.pb.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070027
28namespace y2019 {
29namespace vision {
30
Austin Schuha966a372019-04-14 17:15:28 -070031namespace chrono = ::std::chrono;
32
Brian Silvermanacdabeb2019-03-23 14:04:36 -070033class WebsocketHandler : public seasocks::WebSocket::Handler {
34 public:
35 WebsocketHandler();
James Kuszmaul651fc3f2019-05-15 21:14:25 -070036 void onConnect(seasocks::WebSocket *connection) override;
37 void onData(seasocks::WebSocket *connection, const char *data) override;
38 void onDisconnect(seasocks::WebSocket *connection) override;
Brian Silvermanacdabeb2019-03-23 14:04:36 -070039
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070040 void SendData(const std::string &data);
41
Brian Silvermanacdabeb2019-03-23 14:04:36 -070042 private:
43 std::set<seasocks::WebSocket *> connections_;
44};
45
James Kuszmaul651fc3f2019-05-15 21:14:25 -070046WebsocketHandler::WebsocketHandler() {}
Brian Silvermanacdabeb2019-03-23 14:04:36 -070047
48void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
49 connections_.insert(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -070050 AOS_LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
51 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Brian Silvermanacdabeb2019-03-23 14:04:36 -070052}
53
54void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
55 const char *data) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070056 AOS_LOG(INFO, "Got data: %s\n", data);
Brian Silvermanacdabeb2019-03-23 14:04:36 -070057}
58
59void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
60 connections_.erase(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -070061 AOS_LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
62 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Brian Silvermanacdabeb2019-03-23 14:04:36 -070063}
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) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700137 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
138 aos::configuration::ReadConfig("config.json");
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700139
Alex Perrycb7da4b2019-08-28 19:35:56 -0700140 ::aos::ShmEventLoop event_loop(&config.message());
141
142 ::aos::Fetcher<::frc971::control_loops::drivetrain::Status>
Austin Schuh8a633d52019-05-12 15:04:01 -0700143 drivetrain_status_fetcher =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700144 event_loop.MakeFetcher<::frc971::control_loops::drivetrain::Status>(
145 "/drivetrain");
Austin Schuh8a633d52019-05-12 15:04:01 -0700146
Alex Perrycb7da4b2019-08-28 19:35:56 -0700147 ::aos::Fetcher<::y2019::control_loops::superstructure::Status>
148 superstructure_status_fetcher =
149 event_loop
150 .MakeFetcher<::y2019::control_loops::superstructure::Status>(
151 "/superstructure");
Austin Schuh8a633d52019-05-12 15:04:01 -0700152
153 ::std::array<LocalCameraFrame, 5> latest_frames;
154 ::std::array<aos::monotonic_clock::time_point, 5> last_target_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700155 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700156 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
Austin Schuh1445d822019-04-14 15:35:20 -0700157 DebugData debug_data;
Austin Schuha966a372019-04-14 17:15:28 -0700158 ::aos::RingBuffer<DrivetrainPosition, 200> drivetrain_log;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700159
Austin Schuh8a633d52019-05-12 15:04:01 -0700160 event_loop.MakeWatcher(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700161 "/drivetrain",
Austin Schuh8a633d52019-05-12 15:04:01 -0700162 [websocket_handler, server, &latest_frames, &last_target_time,
163 &drivetrain_status_fetcher, &superstructure_status_fetcher,
164 &last_send_time, &drivetrain_log,
165 &debug_data](const ::y2019::control_loops::drivetrain::CameraFrame
166 &camera_frames) {
167 while (drivetrain_status_fetcher.FetchNext()) {
168 DrivetrainPosition drivetrain_position{
Alex Perrycb7da4b2019-08-28 19:35:56 -0700169 drivetrain_status_fetcher.context().monotonic_sent_time,
170 drivetrain_status_fetcher->x(), drivetrain_status_fetcher->y(),
171 drivetrain_status_fetcher->theta()};
Austin Schuha966a372019-04-14 17:15:28 -0700172
Austin Schuh8a633d52019-05-12 15:04:01 -0700173 drivetrain_log.Push(drivetrain_position);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700174 }
Austin Schuh8a633d52019-05-12 15:04:01 -0700175 superstructure_status_fetcher.Fetch();
176 if (!drivetrain_status_fetcher.get() ||
177 !superstructure_status_fetcher.get()) {
178 // Try again if we don't have any drivetrain statuses.
179 return;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700180 }
Austin Schuh8a633d52019-05-12 15:04:01 -0700181 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700182
Austin Schuh8a633d52019-05-12 15:04:01 -0700183 {
184 const auto &new_frame = camera_frames;
185 // TODO(james): Maybe we only want to fill out a new frame if it has
186 // targets or the saved target is > 0.1 sec old? Not sure, but for now
Alex Perrycb7da4b2019-08-28 19:35:56 -0700187 if (new_frame.camera() < latest_frames.size()) {
188 latest_frames[new_frame.camera()].capture_time =
Austin Schuh8a633d52019-05-12 15:04:01 -0700189 aos::monotonic_clock::time_point(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700190 chrono::nanoseconds(new_frame.timestamp()));
191 latest_frames[new_frame.camera()].targets.clear();
192 if (new_frame.has_targets() && new_frame.targets()->size() > 0) {
193 last_target_time[new_frame.camera()] =
194 latest_frames[new_frame.camera()].capture_time;
Austin Schuh8a633d52019-05-12 15:04:01 -0700195 }
Alex Perrycb7da4b2019-08-28 19:35:56 -0700196 for (const control_loops::drivetrain::CameraTarget *target :
197 *new_frame.targets()) {
198 latest_frames[new_frame.camera()].targets.emplace_back();
199 const float heading = target->heading();
200 const float distance = target->distance();
201 latest_frames[new_frame.camera()].targets.back().x =
Austin Schuh8a633d52019-05-12 15:04:01 -0700202 ::std::cos(heading) * distance;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700203 latest_frames[new_frame.camera()].targets.back().y =
Austin Schuh8a633d52019-05-12 15:04:01 -0700204 ::std::sin(heading) * distance;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700205 latest_frames[new_frame.camera()].targets.back().theta =
206 target->skew();
Austin Schuh8a633d52019-05-12 15:04:01 -0700207 }
208 }
209 }
Austin Schuha966a372019-04-14 17:15:28 -0700210
Austin Schuh8a633d52019-05-12 15:04:01 -0700211 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
212 CameraDebug *camera_debug = debug_data.add_camera_debug();
213 LocalCameraFrame cur_frame = latest_frames[ii];
214 constants::Values::CameraCalibration camera_info =
215 constants::GetValues().cameras[ii];
216 frc971::control_loops::TypedPose<double> camera_pose =
217 camera_info.pose;
Austin Schuha966a372019-04-14 17:15:28 -0700218
Austin Schuh8a633d52019-05-12 15:04:01 -0700219 const DrivetrainPosition robot_position =
220 ComputePosition(drivetrain_log, cur_frame.capture_time);
221 const ::frc971::control_loops::TypedPose<double> robot_pose(
222 {robot_position.x, robot_position.y, 0}, robot_position.theta);
Austin Schuh1445d822019-04-14 15:35:20 -0700223
Austin Schuh8a633d52019-05-12 15:04:01 -0700224 camera_pose.set_base(&robot_pose);
Austin Schuh1445d822019-04-14 15:35:20 -0700225
Austin Schuh8a633d52019-05-12 15:04:01 -0700226 camera_debug->set_current_frame_age(
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700227 ::aos::time::DurationInSeconds(now - cur_frame.capture_time));
Austin Schuh8a633d52019-05-12 15:04:01 -0700228 camera_debug->set_time_since_last_target(
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700229 ::aos::time::DurationInSeconds(now - last_target_time[ii]));
Austin Schuh8a633d52019-05-12 15:04:01 -0700230 for (const auto &target : cur_frame.targets) {
231 frc971::control_loops::TypedPose<double> target_pose(
232 &camera_pose, {target.x, target.y, 0}, target.theta);
233 Pose *pose = camera_debug->add_targets();
234 pose->set_x(target_pose.abs_pos().x());
235 pose->set_y(target_pose.abs_pos().y());
236 pose->set_theta(target_pose.abs_theta());
237 }
238 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700239
Austin Schuh8a633d52019-05-12 15:04:01 -0700240 if (now > last_send_time + chrono::milliseconds(100)) {
241 last_send_time = now;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700242 debug_data.mutable_robot_pose()->set_x(drivetrain_status_fetcher->x());
243 debug_data.mutable_robot_pose()->set_y(drivetrain_status_fetcher->y());
Austin Schuh8a633d52019-05-12 15:04:01 -0700244 debug_data.mutable_robot_pose()->set_theta(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700245 drivetrain_status_fetcher->theta());
Austin Schuh8a633d52019-05-12 15:04:01 -0700246 {
247 LineFollowDebug *line_debug =
248 debug_data.mutable_line_follow_debug();
249 line_debug->set_frozen(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700250 drivetrain_status_fetcher->line_follow_logging()->frozen());
Austin Schuh8a633d52019-05-12 15:04:01 -0700251 line_debug->set_have_target(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700252 drivetrain_status_fetcher->line_follow_logging()->have_target());
Austin Schuh8a633d52019-05-12 15:04:01 -0700253 line_debug->mutable_goal_target()->set_x(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700254 drivetrain_status_fetcher->line_follow_logging()->x());
Austin Schuh8a633d52019-05-12 15:04:01 -0700255 line_debug->mutable_goal_target()->set_y(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700256 drivetrain_status_fetcher->line_follow_logging()->y());
Austin Schuh8a633d52019-05-12 15:04:01 -0700257 line_debug->mutable_goal_target()->set_theta(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700258 drivetrain_status_fetcher->line_follow_logging()->theta());
Austin Schuh8a633d52019-05-12 15:04:01 -0700259 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700260
Austin Schuh8a633d52019-05-12 15:04:01 -0700261 Sensors *sensors = debug_data.mutable_sensors();
Alex Perrycb7da4b2019-08-28 19:35:56 -0700262 sensors->set_wrist(
263 superstructure_status_fetcher->wrist()->position());
Austin Schuh8a633d52019-05-12 15:04:01 -0700264 sensors->set_elevator(
Alex Perrycb7da4b2019-08-28 19:35:56 -0700265 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}