blob: 3ef8983ff6e51f21615888847ccb8f09bfa8ad86 [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) {
137 auto &camera_frames = y2019::control_loops::drivetrain::camera_frames;
138 auto &drivetrain_status = frc971::control_loops::drivetrain_queue.status;
Austin Schuh71ae7952019-04-14 15:12:52 -0700139 auto &superstructure_status =
140 ::y2019::control_loops::superstructure::superstructure_queue.status;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700141
142 std::array<LocalCameraFrame, 5> latest_frames;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700143 std::array<aos::monotonic_clock::time_point, 5> last_target_time;
144 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700145 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
Austin Schuh1445d822019-04-14 15:35:20 -0700146 DebugData debug_data;
Austin Schuha966a372019-04-14 17:15:28 -0700147 ::aos::RingBuffer<DrivetrainPosition, 200> drivetrain_log;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700148
149 while (true) {
150 camera_frames.FetchNextBlocking();
Austin Schuha966a372019-04-14 17:15:28 -0700151 while (drivetrain_status.FetchNext()) {
152 DrivetrainPosition drivetrain_position{
153 drivetrain_status->sent_time, drivetrain_status->x,
154 drivetrain_status->y, drivetrain_status->theta};
155
156 drivetrain_log.Push(drivetrain_position);
157 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700158 superstructure_status.FetchLatest();
159 if (!drivetrain_status.get() || !superstructure_status.get()) {
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700160 // Try again if we don't have any drivetrain statuses.
161 continue;
162 }
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700163 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700164
165 {
166 const auto &new_frame = *camera_frames;
Alex Perryd13750f2019-04-10 21:15:28 -0700167 // TODO(james): Maybe we only want to fill out a new frame if it has
168 // targets or the saved target is > 0.1 sec old? Not sure, but for now
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700169 if (new_frame.camera < latest_frames.size()) {
170 latest_frames[new_frame.camera].capture_time =
171 aos::monotonic_clock::time_point(
Austin Schuha966a372019-04-14 17:15:28 -0700172 chrono::nanoseconds(new_frame.timestamp));
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700173 latest_frames[new_frame.camera].targets.clear();
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700174 if (new_frame.num_targets > 0) {
James Kuszmauld6d37d12019-03-30 13:04:54 -0700175 last_target_time[new_frame.camera] =
176 latest_frames[new_frame.camera].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700177 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700178 for (int target = 0; target < new_frame.num_targets; ++target) {
179 latest_frames[new_frame.camera].targets.emplace_back();
Alex Perryd13750f2019-04-10 21:15:28 -0700180 const float heading = new_frame.targets[target].heading;
181 const float distance = new_frame.targets[target].distance;
182 latest_frames[new_frame.camera].targets.back().x =
183 ::std::cos(heading) * distance;
184 latest_frames[new_frame.camera].targets.back().y =
185 ::std::sin(heading) * distance;
186 latest_frames[new_frame.camera].targets.back().theta =
187 new_frame.targets[target].skew;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700188 }
189 }
190 }
191
Austin Schuh1445d822019-04-14 15:35:20 -0700192 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
193 CameraDebug *camera_debug = debug_data.add_camera_debug();
194 LocalCameraFrame cur_frame = latest_frames[ii];
195 constants::Values::CameraCalibration camera_info =
196 constants::GetValues().cameras[ii];
197 frc971::control_loops::TypedPose<double> camera_pose = camera_info.pose;
Austin Schuha966a372019-04-14 17:15:28 -0700198
199 const DrivetrainPosition robot_position = ComputePosition(
200 drivetrain_log, cur_frame.capture_time);
201 const ::frc971::control_loops::TypedPose<double> robot_pose(
202 {robot_position.x, robot_position.y, 0}, robot_position.theta);
203
Austin Schuh1445d822019-04-14 15:35:20 -0700204 camera_pose.set_base(&robot_pose);
205
206 camera_debug->set_current_frame_age(
Austin Schuha966a372019-04-14 17:15:28 -0700207 chrono::duration_cast<chrono::duration<double>>(
Austin Schuh1445d822019-04-14 15:35:20 -0700208 now - cur_frame.capture_time)
209 .count());
210 camera_debug->set_time_since_last_target(
Austin Schuha966a372019-04-14 17:15:28 -0700211 chrono::duration_cast<chrono::duration<double>>(now -
212 last_target_time[ii])
Austin Schuh1445d822019-04-14 15:35:20 -0700213 .count());
214 for (const auto &target : cur_frame.targets) {
215 frc971::control_loops::TypedPose<double> target_pose(
216 &camera_pose, {target.x, target.y, 0}, target.theta);
217 Pose *pose = camera_debug->add_targets();
218 pose->set_x(target_pose.abs_pos().x());
219 pose->set_y(target_pose.abs_pos().y());
220 pose->set_theta(target_pose.abs_theta());
221 }
222 }
223
Austin Schuha966a372019-04-14 17:15:28 -0700224 if (now > last_send_time + chrono::milliseconds(100)) {
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700225 last_send_time = now;
James Kuszmauld6d37d12019-03-30 13:04:54 -0700226 debug_data.mutable_robot_pose()->set_x(drivetrain_status->x);
227 debug_data.mutable_robot_pose()->set_y(drivetrain_status->y);
228 debug_data.mutable_robot_pose()->set_theta(drivetrain_status->theta);
229 {
230 LineFollowDebug *line_debug = debug_data.mutable_line_follow_debug();
231 line_debug->set_frozen(drivetrain_status->line_follow_logging.frozen);
232 line_debug->set_have_target(
233 drivetrain_status->line_follow_logging.have_target);
234 line_debug->mutable_goal_target()->set_x(
235 drivetrain_status->line_follow_logging.x);
236 line_debug->mutable_goal_target()->set_y(
237 drivetrain_status->line_follow_logging.y);
238 line_debug->mutable_goal_target()->set_theta(
239 drivetrain_status->line_follow_logging.theta);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700240 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700241
242 Sensors *sensors = debug_data.mutable_sensors();
243 sensors->set_wrist(superstructure_status->wrist.position);
244 sensors->set_elevator(superstructure_status->elevator.position);
245 sensors->set_intake(superstructure_status->intake.position);
246 sensors->set_stilts(superstructure_status->stilts.position);
247
James Kuszmauld6d37d12019-03-30 13:04:54 -0700248 ::std::string json;
249 google::protobuf::util::MessageToJsonString(debug_data, &json);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700250 server->execute(
James Kuszmauld6d37d12019-03-30 13:04:54 -0700251 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Austin Schuh1445d822019-04-14 15:35:20 -0700252
253 debug_data.Clear();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700254 }
255 }
256}
257
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700258} // namespace vision
259} // namespace y2019
260
261int main(int, char *[]) {
262 // Make sure to reference this to force the linker to include it.
263 findEmbeddedContent("");
264
265 aos::InitNRT();
266
267 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700268 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700269
270 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700271 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700272
273 std::thread data_thread{[&server, websocket_handler]() {
274 y2019::vision::DataThread(&server, websocket_handler.get());
275 }};
276
Austin Schuh53596192019-03-23 18:53:33 -0700277 // See if we are on a robot. If so, serve the robot www folder.
278 bool serve_www = false;
279 {
280 struct stat result;
281 if (stat("/home/admin/robot_code/www", &result) == 0) {
282 serve_www = true;
283 }
284 }
285
286 server.serve(
287 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
288 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700289
290 return 0;
291}