blob: 8378f7705afd2457b08a1e2dd42620beb1641003 [file] [log] [blame]
Philipp Schrader790cb542023-07-05 21:06:52 -07001#include "seasocks/Server.h"
2
Austin Schuh53596192019-03-23 18:53:33 -07003#include <sys/stat.h>
4#include <sys/types.h>
5#include <unistd.h>
Philipp Schrader790cb542023-07-05 21:06:52 -07006
Brian Silvermanb42ff2d2019-03-23 15:36:39 -07007#include <array>
Alex Perryd13750f2019-04-10 21:15:28 -07008#include <cmath>
Brian Silvermanacdabeb2019-03-23 14:04:36 -07009#include <memory>
10#include <set>
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070011#include <sstream>
Brian Silvermanacdabeb2019-03-23 14:04:36 -070012
Philipp Schrader790cb542023-07-05 21:06:52 -070013#include "google/protobuf/util/json_util.h"
14
Austin Schuha966a372019-04-14 17:15:28 -070015#include "aos/containers/ring_buffer.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070016#include "aos/events/shm_event_loop.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070017#include "aos/init.h"
18#include "aos/logging/logging.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070019#include "aos/seasocks/seasocks_logger.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070020#include "aos/time/time.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070021#include "frc971/control_loops/drivetrain/drivetrain_status_generated.h"
Alex Perryd13750f2019-04-10 21:15:28 -070022#include "frc971/control_loops/pose.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070023#include "internal/Embedded.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070024#include "seasocks/StringUtil.h"
25#include "seasocks/WebSocket.h"
Alex Perryd13750f2019-04-10 21:15:28 -070026#include "y2019/constants.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070027#include "y2019/control_loops/drivetrain/camera_generated.h"
28#include "y2019/control_loops/superstructure/superstructure_status_generated.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070029#include "y2019/vision/server/server_data.pb.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070030
31namespace y2019 {
32namespace vision {
33
Austin Schuha966a372019-04-14 17:15:28 -070034namespace chrono = ::std::chrono;
35
Brian Silvermanacdabeb2019-03-23 14:04:36 -070036class WebsocketHandler : public seasocks::WebSocket::Handler {
37 public:
38 WebsocketHandler();
James Kuszmaul651fc3f2019-05-15 21:14:25 -070039 void onConnect(seasocks::WebSocket *connection) override;
40 void onData(seasocks::WebSocket *connection, const char *data) override;
41 void onDisconnect(seasocks::WebSocket *connection) override;
Brian Silvermanacdabeb2019-03-23 14:04:36 -070042
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070043 void SendData(const std::string &data);
44
Brian Silvermanacdabeb2019-03-23 14:04:36 -070045 private:
46 std::set<seasocks::WebSocket *> connections_;
47};
48
James Kuszmaul651fc3f2019-05-15 21:14:25 -070049WebsocketHandler::WebsocketHandler() {}
Brian Silvermanacdabeb2019-03-23 14:04:36 -070050
51void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
52 connections_.insert(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -070053 AOS_LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
54 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Brian Silvermanacdabeb2019-03-23 14:04:36 -070055}
56
57void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
58 const char *data) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070059 AOS_LOG(INFO, "Got data: %s\n", data);
Brian Silvermanacdabeb2019-03-23 14:04:36 -070060}
61
62void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
63 connections_.erase(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -070064 AOS_LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
65 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Brian Silvermanacdabeb2019-03-23 14:04:36 -070066}
67
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070068void WebsocketHandler::SendData(const std::string &data) {
69 for (seasocks::WebSocket *websocket : connections_) {
Austin Schuh89a95052019-04-14 14:51:14 -070070 websocket->send(data.c_str());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070071 }
72}
73
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070074struct LocalCameraTarget {
75 double x, y, theta;
76};
77
78struct LocalCameraFrame {
79 aos::monotonic_clock::time_point capture_time =
80 aos::monotonic_clock::min_time;
81 std::vector<LocalCameraTarget> targets;
82
83 bool IsValid(aos::monotonic_clock::time_point now) {
Austin Schuha966a372019-04-14 17:15:28 -070084 return capture_time + chrono::seconds(1) > now;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070085 }
86};
87
88// Sends a new chunk of data to all the websocket clients.
89class UpdateData : public seasocks::Server::Runnable {
90 public:
91 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
92 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
93 ~UpdateData() override = default;
94 UpdateData(const UpdateData &) = delete;
95 UpdateData &operator=(const UpdateData &) = delete;
96
97 void run() override { websocket_handler_->SendData(data_); }
98
99 private:
100 WebsocketHandler *const websocket_handler_;
101 const std::string data_;
102};
103
Austin Schuha966a372019-04-14 17:15:28 -0700104struct DrivetrainPosition {
105 ::aos::monotonic_clock::time_point time;
106 double x;
107 double y;
108 double theta;
109};
110
111DrivetrainPosition ComputePosition(
112 const ::aos::RingBuffer<DrivetrainPosition, 200> &data,
113 ::aos::monotonic_clock::time_point time) {
114 DrivetrainPosition drivetrain_now{time, 0.0f, 0.0f, 0.0f};
115
116 const size_t after_index = ::std::max(
117 static_cast<size_t>(1),
118 static_cast<size_t>(::std::distance(
119 data.begin(),
120 ::std::lower_bound(
121 data.begin(), data.end(), drivetrain_now,
122 [](const DrivetrainPosition &a, const DrivetrainPosition &b) {
123 return a.time < b.time;
124 }))));
125 const size_t before_index = after_index - 1;
126
127 const DrivetrainPosition &before = data[before_index];
128 const DrivetrainPosition &after = data[after_index];
129
130 double alpha = static_cast<double>((time - before.time).count()) /
131 static_cast<double>((after.time - before.time).count());
132 drivetrain_now.x = (1.0 - alpha) * before.x + alpha * after.x;
133 drivetrain_now.y = (1.0 - alpha) * before.y + alpha * after.y;
134 drivetrain_now.theta = (1.0 - alpha) * before.theta + alpha * after.theta;
135
136 return drivetrain_now;
137}
138
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700139void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700140 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
Austin Schuhc5fa6d92022-02-25 14:36:28 -0800141 aos::configuration::ReadConfig("aos_config.json");
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700142
Alex Perrycb7da4b2019-08-28 19:35:56 -0700143 ::aos::ShmEventLoop event_loop(&config.message());
144
145 ::aos::Fetcher<::frc971::control_loops::drivetrain::Status>
Austin Schuh8a633d52019-05-12 15:04:01 -0700146 drivetrain_status_fetcher =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700147 event_loop.MakeFetcher<::frc971::control_loops::drivetrain::Status>(
148 "/drivetrain");
Austin Schuh8a633d52019-05-12 15:04:01 -0700149
Alex Perrycb7da4b2019-08-28 19:35:56 -0700150 ::aos::Fetcher<::y2019::control_loops::superstructure::Status>
151 superstructure_status_fetcher =
152 event_loop
153 .MakeFetcher<::y2019::control_loops::superstructure::Status>(
154 "/superstructure");
Austin Schuh8a633d52019-05-12 15:04:01 -0700155
156 ::std::array<LocalCameraFrame, 5> latest_frames;
157 ::std::array<aos::monotonic_clock::time_point, 5> last_target_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700158 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700159 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
Austin Schuh1445d822019-04-14 15:35:20 -0700160 DebugData debug_data;
Austin Schuha966a372019-04-14 17:15:28 -0700161 ::aos::RingBuffer<DrivetrainPosition, 200> drivetrain_log;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700162
Philipp Schrader790cb542023-07-05 21:06:52 -0700163 event_loop.MakeWatcher("/camera", [websocket_handler, server, &latest_frames,
164 &last_target_time,
165 &drivetrain_status_fetcher,
166 &superstructure_status_fetcher,
167 &last_send_time, &drivetrain_log,
168 &debug_data](const ::y2019::control_loops::
169 drivetrain::CameraFrame
170 &camera_frames) {
171 while (drivetrain_status_fetcher.FetchNext()) {
172 DrivetrainPosition drivetrain_position{
173 drivetrain_status_fetcher.context().monotonic_event_time,
174 drivetrain_status_fetcher->x(), drivetrain_status_fetcher->y(),
175 drivetrain_status_fetcher->theta()};
Austin Schuha966a372019-04-14 17:15:28 -0700176
Philipp Schrader790cb542023-07-05 21:06:52 -0700177 drivetrain_log.Push(drivetrain_position);
178 }
179 superstructure_status_fetcher.Fetch();
180 if (!drivetrain_status_fetcher.get() ||
181 !superstructure_status_fetcher.get()) {
182 // Try again if we don't have any drivetrain statuses.
183 return;
184 }
185 const auto now = aos::monotonic_clock::now();
186
187 {
188 const auto &new_frame = camera_frames;
189 // TODO(james): Maybe we only want to fill out a new frame if it has
190 // targets or the saved target is > 0.1 sec old? Not sure, but for now
191 if (new_frame.camera() < latest_frames.size()) {
192 latest_frames[new_frame.camera()].capture_time =
193 aos::monotonic_clock::time_point(
194 chrono::nanoseconds(new_frame.timestamp()));
195 latest_frames[new_frame.camera()].targets.clear();
196 if (new_frame.has_targets() && new_frame.targets()->size() > 0) {
197 last_target_time[new_frame.camera()] =
198 latest_frames[new_frame.camera()].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700199 }
Philipp Schrader790cb542023-07-05 21:06:52 -0700200 for (const control_loops::drivetrain::CameraTarget *target :
201 *new_frame.targets()) {
202 latest_frames[new_frame.camera()].targets.emplace_back();
203 const float heading = target->heading();
204 const float distance = target->distance();
205 latest_frames[new_frame.camera()].targets.back().x =
206 ::std::cos(heading) * distance;
207 latest_frames[new_frame.camera()].targets.back().y =
208 ::std::sin(heading) * distance;
209 latest_frames[new_frame.camera()].targets.back().theta =
210 target->skew();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700211 }
Philipp Schrader790cb542023-07-05 21:06:52 -0700212 }
213 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700214
Philipp Schrader790cb542023-07-05 21:06:52 -0700215 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
216 CameraDebug *camera_debug = debug_data.add_camera_debug();
217 LocalCameraFrame cur_frame = latest_frames[ii];
218 constants::Values::CameraCalibration camera_info =
219 constants::GetValues().cameras[ii];
220 frc971::control_loops::TypedPose<double> camera_pose = camera_info.pose;
Austin Schuha966a372019-04-14 17:15:28 -0700221
Philipp Schrader790cb542023-07-05 21:06:52 -0700222 const DrivetrainPosition robot_position =
223 ComputePosition(drivetrain_log, cur_frame.capture_time);
224 const ::frc971::control_loops::TypedPose<double> robot_pose(
225 {robot_position.x, robot_position.y, 0}, robot_position.theta);
Austin Schuha966a372019-04-14 17:15:28 -0700226
Philipp Schrader790cb542023-07-05 21:06:52 -0700227 camera_pose.set_base(&robot_pose);
Austin Schuh1445d822019-04-14 15:35:20 -0700228
Philipp Schrader790cb542023-07-05 21:06:52 -0700229 camera_debug->set_current_frame_age(
230 ::aos::time::DurationInSeconds(now - cur_frame.capture_time));
231 camera_debug->set_time_since_last_target(
232 ::aos::time::DurationInSeconds(now - last_target_time[ii]));
233 for (const auto &target : cur_frame.targets) {
234 frc971::control_loops::TypedPose<double> target_pose(
235 &camera_pose, {target.x, target.y, 0}, target.theta);
236 Pose *pose = camera_debug->add_targets();
237 pose->set_x(target_pose.abs_pos().x());
238 pose->set_y(target_pose.abs_pos().y());
239 pose->set_theta(target_pose.abs_theta());
240 }
241 }
Austin Schuh1445d822019-04-14 15:35:20 -0700242
Philipp Schrader790cb542023-07-05 21:06:52 -0700243 if (now > last_send_time + chrono::milliseconds(100)) {
244 last_send_time = now;
245 debug_data.mutable_robot_pose()->set_x(drivetrain_status_fetcher->x());
246 debug_data.mutable_robot_pose()->set_y(drivetrain_status_fetcher->y());
247 debug_data.mutable_robot_pose()->set_theta(
248 drivetrain_status_fetcher->theta());
249 {
250 LineFollowDebug *line_debug = debug_data.mutable_line_follow_debug();
251 line_debug->set_frozen(
252 drivetrain_status_fetcher->line_follow_logging()->frozen());
253 line_debug->set_have_target(
254 drivetrain_status_fetcher->line_follow_logging()->have_target());
255 line_debug->mutable_goal_target()->set_x(
256 drivetrain_status_fetcher->line_follow_logging()->x());
257 line_debug->mutable_goal_target()->set_y(
258 drivetrain_status_fetcher->line_follow_logging()->y());
259 line_debug->mutable_goal_target()->set_theta(
260 drivetrain_status_fetcher->line_follow_logging()->theta());
261 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700262
Philipp Schrader790cb542023-07-05 21:06:52 -0700263 Sensors *sensors = debug_data.mutable_sensors();
264 sensors->set_wrist(superstructure_status_fetcher->wrist()->position());
265 sensors->set_elevator(
266 superstructure_status_fetcher->elevator()->position());
267 sensors->set_intake(superstructure_status_fetcher->intake()->position());
268 sensors->set_stilts(superstructure_status_fetcher->stilts()->position());
269 sensors->set_has_piece(superstructure_status_fetcher->has_piece());
Austin Schuh71ae7952019-04-14 15:12:52 -0700270
Philipp Schrader790cb542023-07-05 21:06:52 -0700271 ::std::string json;
272 google::protobuf::util::MessageToJsonString(debug_data, &json);
273 server->execute(
274 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Austin Schuh1445d822019-04-14 15:35:20 -0700275
Philipp Schrader790cb542023-07-05 21:06:52 -0700276 debug_data.Clear();
277 }
278 });
Austin Schuh8a633d52019-05-12 15:04:01 -0700279 event_loop.Run();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700280}
281
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700282} // namespace vision
283} // namespace y2019
284
Austin Schuh094d09b2020-11-20 23:26:52 -0800285int main(int argc, char **argv) {
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700286 // Make sure to reference this to force the linker to include it.
287 findEmbeddedContent("");
288
Austin Schuh094d09b2020-11-20 23:26:52 -0800289 ::aos::InitGoogle(&argc, &argv);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700290
291 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700292 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700293
294 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700295 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700296
297 std::thread data_thread{[&server, websocket_handler]() {
298 y2019::vision::DataThread(&server, websocket_handler.get());
299 }};
300
Austin Schuh53596192019-03-23 18:53:33 -0700301 // See if we are on a robot. If so, serve the robot www folder.
302 bool serve_www = false;
303 {
304 struct stat result;
Austin Schuhc0ec2a82022-02-24 17:26:29 -0800305 if (stat("/home/admin/bin/www", &result) == 0) {
Austin Schuh53596192019-03-23 18:53:33 -0700306 serve_www = true;
307 }
308 }
309
Philipp Schrader790cb542023-07-05 21:06:52 -0700310 server.serve(serve_www ? "/home/admin/bin/www" : "y2019/vision/server/www",
311 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700312
313 return 0;
314}