blob: e66a70b281dd84f4bcd1c4e931f693f6efd174fa [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();
James Kuszmaul651fc3f2019-05-15 21:14:25 -070035 void onConnect(seasocks::WebSocket *connection) override;
36 void onData(seasocks::WebSocket *connection, const char *data) override;
37 void onDisconnect(seasocks::WebSocket *connection) override;
Brian Silvermanacdabeb2019-03-23 14:04:36 -070038
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
James Kuszmaul651fc3f2019-05-15 21:14:25 -070045WebsocketHandler::WebsocketHandler() {}
Brian Silvermanacdabeb2019-03-23 14:04:36 -070046
47void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
48 connections_.insert(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -070049 AOS_LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
50 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Brian Silvermanacdabeb2019-03-23 14:04:36 -070051}
52
53void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
54 const char *data) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070055 AOS_LOG(INFO, "Got data: %s\n", data);
Brian Silvermanacdabeb2019-03-23 14:04:36 -070056}
57
58void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
59 connections_.erase(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -070060 AOS_LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
61 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Brian Silvermanacdabeb2019-03-23 14:04:36 -070062}
63
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070064void WebsocketHandler::SendData(const std::string &data) {
65 for (seasocks::WebSocket *websocket : connections_) {
Austin Schuh89a95052019-04-14 14:51:14 -070066 websocket->send(data.c_str());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070067 }
68}
69
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070070struct LocalCameraTarget {
71 double x, y, theta;
72};
73
74struct LocalCameraFrame {
75 aos::monotonic_clock::time_point capture_time =
76 aos::monotonic_clock::min_time;
77 std::vector<LocalCameraTarget> targets;
78
79 bool IsValid(aos::monotonic_clock::time_point now) {
Austin Schuha966a372019-04-14 17:15:28 -070080 return capture_time + chrono::seconds(1) > now;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070081 }
82};
83
84// Sends a new chunk of data to all the websocket clients.
85class UpdateData : public seasocks::Server::Runnable {
86 public:
87 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
88 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
89 ~UpdateData() override = default;
90 UpdateData(const UpdateData &) = delete;
91 UpdateData &operator=(const UpdateData &) = delete;
92
93 void run() override { websocket_handler_->SendData(data_); }
94
95 private:
96 WebsocketHandler *const websocket_handler_;
97 const std::string data_;
98};
99
Austin Schuha966a372019-04-14 17:15:28 -0700100struct DrivetrainPosition {
101 ::aos::monotonic_clock::time_point time;
102 double x;
103 double y;
104 double theta;
105};
106
107DrivetrainPosition ComputePosition(
108 const ::aos::RingBuffer<DrivetrainPosition, 200> &data,
109 ::aos::monotonic_clock::time_point time) {
110 DrivetrainPosition drivetrain_now{time, 0.0f, 0.0f, 0.0f};
111
112 const size_t after_index = ::std::max(
113 static_cast<size_t>(1),
114 static_cast<size_t>(::std::distance(
115 data.begin(),
116 ::std::lower_bound(
117 data.begin(), data.end(), drivetrain_now,
118 [](const DrivetrainPosition &a, const DrivetrainPosition &b) {
119 return a.time < b.time;
120 }))));
121 const size_t before_index = after_index - 1;
122
123 const DrivetrainPosition &before = data[before_index];
124 const DrivetrainPosition &after = data[after_index];
125
126 double alpha = static_cast<double>((time - before.time).count()) /
127 static_cast<double>((after.time - before.time).count());
128 drivetrain_now.x = (1.0 - alpha) * before.x + alpha * after.x;
129 drivetrain_now.y = (1.0 - alpha) * before.y + alpha * after.y;
130 drivetrain_now.theta = (1.0 - alpha) * before.theta + alpha * after.theta;
131
132 return drivetrain_now;
133}
134
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700135void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
Austin Schuh8a633d52019-05-12 15:04:01 -0700136 ::aos::ShmEventLoop event_loop;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700137
Austin Schuh8a633d52019-05-12 15:04:01 -0700138 ::aos::Fetcher<::frc971::control_loops::DrivetrainQueue::Status>
139 drivetrain_status_fetcher =
140 event_loop
141 .MakeFetcher<::frc971::control_loops::DrivetrainQueue::Status>(
142 ".frc971.control_loops.drivetrain_queue.status");
143
144 ::aos::Fetcher<
145 ::y2019::control_loops::superstructure::SuperstructureQueue::Status>
146 superstructure_status_fetcher = event_loop.MakeFetcher<
147 ::y2019::control_loops::superstructure::SuperstructureQueue::Status>(
148 ".y2019.control_loops.superstructure.superstructure_queue.status");
149
150 ::std::array<LocalCameraFrame, 5> latest_frames;
151 ::std::array<aos::monotonic_clock::time_point, 5> last_target_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700152 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700153 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
Austin Schuh1445d822019-04-14 15:35:20 -0700154 DebugData debug_data;
Austin Schuha966a372019-04-14 17:15:28 -0700155 ::aos::RingBuffer<DrivetrainPosition, 200> drivetrain_log;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700156
Austin Schuh8a633d52019-05-12 15:04:01 -0700157 event_loop.MakeWatcher(
158 ".y2019.control_loops.drivetrain.camera_frames",
159 [websocket_handler, server, &latest_frames, &last_target_time,
160 &drivetrain_status_fetcher, &superstructure_status_fetcher,
161 &last_send_time, &drivetrain_log,
162 &debug_data](const ::y2019::control_loops::drivetrain::CameraFrame
163 &camera_frames) {
164 while (drivetrain_status_fetcher.FetchNext()) {
165 DrivetrainPosition drivetrain_position{
166 drivetrain_status_fetcher->sent_time,
167 drivetrain_status_fetcher->x, drivetrain_status_fetcher->y,
168 drivetrain_status_fetcher->theta};
Austin Schuha966a372019-04-14 17:15:28 -0700169
Austin Schuh8a633d52019-05-12 15:04:01 -0700170 drivetrain_log.Push(drivetrain_position);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700171 }
Austin Schuh8a633d52019-05-12 15:04:01 -0700172 superstructure_status_fetcher.Fetch();
173 if (!drivetrain_status_fetcher.get() ||
174 !superstructure_status_fetcher.get()) {
175 // Try again if we don't have any drivetrain statuses.
176 return;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700177 }
Austin Schuh8a633d52019-05-12 15:04:01 -0700178 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700179
Austin Schuh8a633d52019-05-12 15:04:01 -0700180 {
181 const auto &new_frame = camera_frames;
182 // TODO(james): Maybe we only want to fill out a new frame if it has
183 // targets or the saved target is > 0.1 sec old? Not sure, but for now
184 if (new_frame.camera < latest_frames.size()) {
185 latest_frames[new_frame.camera].capture_time =
186 aos::monotonic_clock::time_point(
187 chrono::nanoseconds(new_frame.timestamp));
188 latest_frames[new_frame.camera].targets.clear();
189 if (new_frame.num_targets > 0) {
190 last_target_time[new_frame.camera] =
191 latest_frames[new_frame.camera].capture_time;
192 }
193 for (int target = 0; target < new_frame.num_targets; ++target) {
194 latest_frames[new_frame.camera].targets.emplace_back();
195 const float heading = new_frame.targets[target].heading;
196 const float distance = new_frame.targets[target].distance;
197 latest_frames[new_frame.camera].targets.back().x =
198 ::std::cos(heading) * distance;
199 latest_frames[new_frame.camera].targets.back().y =
200 ::std::sin(heading) * distance;
201 latest_frames[new_frame.camera].targets.back().theta =
202 new_frame.targets[target].skew;
203 }
204 }
205 }
Austin Schuha966a372019-04-14 17:15:28 -0700206
Austin Schuh8a633d52019-05-12 15:04:01 -0700207 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
208 CameraDebug *camera_debug = debug_data.add_camera_debug();
209 LocalCameraFrame cur_frame = latest_frames[ii];
210 constants::Values::CameraCalibration camera_info =
211 constants::GetValues().cameras[ii];
212 frc971::control_loops::TypedPose<double> camera_pose =
213 camera_info.pose;
Austin Schuha966a372019-04-14 17:15:28 -0700214
Austin Schuh8a633d52019-05-12 15:04:01 -0700215 const DrivetrainPosition robot_position =
216 ComputePosition(drivetrain_log, cur_frame.capture_time);
217 const ::frc971::control_loops::TypedPose<double> robot_pose(
218 {robot_position.x, robot_position.y, 0}, robot_position.theta);
Austin Schuh1445d822019-04-14 15:35:20 -0700219
Austin Schuh8a633d52019-05-12 15:04:01 -0700220 camera_pose.set_base(&robot_pose);
Austin Schuh1445d822019-04-14 15:35:20 -0700221
Austin Schuh8a633d52019-05-12 15:04:01 -0700222 camera_debug->set_current_frame_age(
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700223 ::aos::time::DurationInSeconds(now - cur_frame.capture_time));
Austin Schuh8a633d52019-05-12 15:04:01 -0700224 camera_debug->set_time_since_last_target(
James Kuszmaul651fc3f2019-05-15 21:14:25 -0700225 ::aos::time::DurationInSeconds(now - last_target_time[ii]));
Austin Schuh8a633d52019-05-12 15:04:01 -0700226 for (const auto &target : cur_frame.targets) {
227 frc971::control_loops::TypedPose<double> target_pose(
228 &camera_pose, {target.x, target.y, 0}, target.theta);
229 Pose *pose = camera_debug->add_targets();
230 pose->set_x(target_pose.abs_pos().x());
231 pose->set_y(target_pose.abs_pos().y());
232 pose->set_theta(target_pose.abs_theta());
233 }
234 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700235
Austin Schuh8a633d52019-05-12 15:04:01 -0700236 if (now > last_send_time + chrono::milliseconds(100)) {
237 last_send_time = now;
238 debug_data.mutable_robot_pose()->set_x(drivetrain_status_fetcher->x);
239 debug_data.mutable_robot_pose()->set_y(drivetrain_status_fetcher->y);
240 debug_data.mutable_robot_pose()->set_theta(
241 drivetrain_status_fetcher->theta);
242 {
243 LineFollowDebug *line_debug =
244 debug_data.mutable_line_follow_debug();
245 line_debug->set_frozen(
246 drivetrain_status_fetcher->line_follow_logging.frozen);
247 line_debug->set_have_target(
248 drivetrain_status_fetcher->line_follow_logging.have_target);
249 line_debug->mutable_goal_target()->set_x(
250 drivetrain_status_fetcher->line_follow_logging.x);
251 line_debug->mutable_goal_target()->set_y(
252 drivetrain_status_fetcher->line_follow_logging.y);
253 line_debug->mutable_goal_target()->set_theta(
254 drivetrain_status_fetcher->line_follow_logging.theta);
255 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700256
Austin Schuh8a633d52019-05-12 15:04:01 -0700257 Sensors *sensors = debug_data.mutable_sensors();
258 sensors->set_wrist(superstructure_status_fetcher->wrist.position);
259 sensors->set_elevator(
260 superstructure_status_fetcher->elevator.position);
261 sensors->set_intake(superstructure_status_fetcher->intake.position);
262 sensors->set_stilts(superstructure_status_fetcher->stilts.position);
263 sensors->set_has_piece(superstructure_status_fetcher->has_piece);
Austin Schuh1445d822019-04-14 15:35:20 -0700264
Austin Schuh8a633d52019-05-12 15:04:01 -0700265 ::std::string json;
266 google::protobuf::util::MessageToJsonString(debug_data, &json);
267 server->execute(std::make_shared<UpdateData>(websocket_handler,
268 ::std::move(json)));
269
270 debug_data.Clear();
271 }
272 });
273 event_loop.Run();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700274}
275
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700276} // namespace vision
277} // namespace y2019
278
279int main(int, char *[]) {
280 // Make sure to reference this to force the linker to include it.
281 findEmbeddedContent("");
282
283 aos::InitNRT();
284
285 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700286 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700287
288 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700289 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700290
291 std::thread data_thread{[&server, websocket_handler]() {
292 y2019::vision::DataThread(&server, websocket_handler.get());
293 }};
294
Austin Schuh53596192019-03-23 18:53:33 -0700295 // See if we are on a robot. If so, serve the robot www folder.
296 bool serve_www = false;
297 {
298 struct stat result;
299 if (stat("/home/admin/robot_code/www", &result) == 0) {
300 serve_www = true;
301 }
302 }
303
304 server.serve(
305 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
306 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700307
308 return 0;
309}