blob: 309a90cd801417a9179038b27b7ed73d44df1a0e [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
Stephan Pleinesf63bde82024-01-13 15:59:33 -080031namespace y2019::vision {
Brian Silvermanacdabeb2019-03-23 14:04:36 -070032
Austin Schuha966a372019-04-14 17:15:28 -070033namespace chrono = ::std::chrono;
34
Brian Silvermanacdabeb2019-03-23 14:04:36 -070035class WebsocketHandler : public seasocks::WebSocket::Handler {
36 public:
37 WebsocketHandler();
James Kuszmaul651fc3f2019-05-15 21:14:25 -070038 void onConnect(seasocks::WebSocket *connection) override;
39 void onData(seasocks::WebSocket *connection, const char *data) override;
40 void onDisconnect(seasocks::WebSocket *connection) override;
Brian Silvermanacdabeb2019-03-23 14:04:36 -070041
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070042 void SendData(const std::string &data);
43
Brian Silvermanacdabeb2019-03-23 14:04:36 -070044 private:
45 std::set<seasocks::WebSocket *> connections_;
46};
47
James Kuszmaul651fc3f2019-05-15 21:14:25 -070048WebsocketHandler::WebsocketHandler() {}
Brian Silvermanacdabeb2019-03-23 14:04:36 -070049
50void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
51 connections_.insert(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -070052 AOS_LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
53 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Brian Silvermanacdabeb2019-03-23 14:04:36 -070054}
55
56void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
57 const char *data) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070058 AOS_LOG(INFO, "Got data: %s\n", data);
Brian Silvermanacdabeb2019-03-23 14:04:36 -070059}
60
61void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
62 connections_.erase(connection);
Austin Schuhf257f3c2019-10-27 21:00:43 -070063 AOS_LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
64 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
Brian Silvermanacdabeb2019-03-23 14:04:36 -070065}
66
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070067void WebsocketHandler::SendData(const std::string &data) {
68 for (seasocks::WebSocket *websocket : connections_) {
Austin Schuh89a95052019-04-14 14:51:14 -070069 websocket->send(data.c_str());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070070 }
71}
72
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070073struct LocalCameraTarget {
74 double x, y, theta;
75};
76
77struct LocalCameraFrame {
78 aos::monotonic_clock::time_point capture_time =
79 aos::monotonic_clock::min_time;
80 std::vector<LocalCameraTarget> targets;
81
82 bool IsValid(aos::monotonic_clock::time_point now) {
Austin Schuha966a372019-04-14 17:15:28 -070083 return capture_time + chrono::seconds(1) > now;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070084 }
85};
86
87// Sends a new chunk of data to all the websocket clients.
88class UpdateData : public seasocks::Server::Runnable {
89 public:
90 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
91 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
92 ~UpdateData() override = default;
93 UpdateData(const UpdateData &) = delete;
94 UpdateData &operator=(const UpdateData &) = delete;
95
96 void run() override { websocket_handler_->SendData(data_); }
97
98 private:
99 WebsocketHandler *const websocket_handler_;
100 const std::string data_;
101};
102
Austin Schuha966a372019-04-14 17:15:28 -0700103struct DrivetrainPosition {
104 ::aos::monotonic_clock::time_point time;
105 double x;
106 double y;
107 double theta;
108};
109
110DrivetrainPosition ComputePosition(
111 const ::aos::RingBuffer<DrivetrainPosition, 200> &data,
112 ::aos::monotonic_clock::time_point time) {
113 DrivetrainPosition drivetrain_now{time, 0.0f, 0.0f, 0.0f};
114
115 const size_t after_index = ::std::max(
116 static_cast<size_t>(1),
117 static_cast<size_t>(::std::distance(
118 data.begin(),
119 ::std::lower_bound(
120 data.begin(), data.end(), drivetrain_now,
121 [](const DrivetrainPosition &a, const DrivetrainPosition &b) {
122 return a.time < b.time;
123 }))));
124 const size_t before_index = after_index - 1;
125
126 const DrivetrainPosition &before = data[before_index];
127 const DrivetrainPosition &after = data[after_index];
128
129 double alpha = static_cast<double>((time - before.time).count()) /
130 static_cast<double>((after.time - before.time).count());
131 drivetrain_now.x = (1.0 - alpha) * before.x + alpha * after.x;
132 drivetrain_now.y = (1.0 - alpha) * before.y + alpha * after.y;
133 drivetrain_now.theta = (1.0 - alpha) * before.theta + alpha * after.theta;
134
135 return drivetrain_now;
136}
137
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700138void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700139 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
Austin Schuhc5fa6d92022-02-25 14:36:28 -0800140 aos::configuration::ReadConfig("aos_config.json");
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700141
Alex Perrycb7da4b2019-08-28 19:35:56 -0700142 ::aos::ShmEventLoop event_loop(&config.message());
143
144 ::aos::Fetcher<::frc971::control_loops::drivetrain::Status>
Austin Schuh8a633d52019-05-12 15:04:01 -0700145 drivetrain_status_fetcher =
Alex Perrycb7da4b2019-08-28 19:35:56 -0700146 event_loop.MakeFetcher<::frc971::control_loops::drivetrain::Status>(
147 "/drivetrain");
Austin Schuh8a633d52019-05-12 15:04:01 -0700148
Alex Perrycb7da4b2019-08-28 19:35:56 -0700149 ::aos::Fetcher<::y2019::control_loops::superstructure::Status>
150 superstructure_status_fetcher =
151 event_loop
152 .MakeFetcher<::y2019::control_loops::superstructure::Status>(
153 "/superstructure");
Austin Schuh8a633d52019-05-12 15:04:01 -0700154
155 ::std::array<LocalCameraFrame, 5> latest_frames;
156 ::std::array<aos::monotonic_clock::time_point, 5> last_target_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700157 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700158 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
Austin Schuh1445d822019-04-14 15:35:20 -0700159 DebugData debug_data;
Austin Schuha966a372019-04-14 17:15:28 -0700160 ::aos::RingBuffer<DrivetrainPosition, 200> drivetrain_log;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700161
Philipp Schrader790cb542023-07-05 21:06:52 -0700162 event_loop.MakeWatcher("/camera", [websocket_handler, server, &latest_frames,
163 &last_target_time,
164 &drivetrain_status_fetcher,
165 &superstructure_status_fetcher,
166 &last_send_time, &drivetrain_log,
167 &debug_data](const ::y2019::control_loops::
168 drivetrain::CameraFrame
169 &camera_frames) {
170 while (drivetrain_status_fetcher.FetchNext()) {
171 DrivetrainPosition drivetrain_position{
172 drivetrain_status_fetcher.context().monotonic_event_time,
173 drivetrain_status_fetcher->x(), drivetrain_status_fetcher->y(),
174 drivetrain_status_fetcher->theta()};
Austin Schuha966a372019-04-14 17:15:28 -0700175
Philipp Schrader790cb542023-07-05 21:06:52 -0700176 drivetrain_log.Push(drivetrain_position);
177 }
178 superstructure_status_fetcher.Fetch();
179 if (!drivetrain_status_fetcher.get() ||
180 !superstructure_status_fetcher.get()) {
181 // Try again if we don't have any drivetrain statuses.
182 return;
183 }
184 const auto now = aos::monotonic_clock::now();
185
186 {
187 const auto &new_frame = camera_frames;
188 // TODO(james): Maybe we only want to fill out a new frame if it has
189 // targets or the saved target is > 0.1 sec old? Not sure, but for now
190 if (new_frame.camera() < latest_frames.size()) {
191 latest_frames[new_frame.camera()].capture_time =
192 aos::monotonic_clock::time_point(
193 chrono::nanoseconds(new_frame.timestamp()));
194 latest_frames[new_frame.camera()].targets.clear();
195 if (new_frame.has_targets() && new_frame.targets()->size() > 0) {
196 last_target_time[new_frame.camera()] =
197 latest_frames[new_frame.camera()].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700198 }
Philipp Schrader790cb542023-07-05 21:06:52 -0700199 for (const control_loops::drivetrain::CameraTarget *target :
200 *new_frame.targets()) {
201 latest_frames[new_frame.camera()].targets.emplace_back();
202 const float heading = target->heading();
203 const float distance = target->distance();
204 latest_frames[new_frame.camera()].targets.back().x =
205 ::std::cos(heading) * distance;
206 latest_frames[new_frame.camera()].targets.back().y =
207 ::std::sin(heading) * distance;
208 latest_frames[new_frame.camera()].targets.back().theta =
209 target->skew();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700210 }
Philipp Schrader790cb542023-07-05 21:06:52 -0700211 }
212 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700213
Philipp Schrader790cb542023-07-05 21:06:52 -0700214 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
215 CameraDebug *camera_debug = debug_data.add_camera_debug();
216 LocalCameraFrame cur_frame = latest_frames[ii];
217 constants::Values::CameraCalibration camera_info =
218 constants::GetValues().cameras[ii];
219 frc971::control_loops::TypedPose<double> camera_pose = camera_info.pose;
Austin Schuha966a372019-04-14 17:15:28 -0700220
Philipp Schrader790cb542023-07-05 21:06:52 -0700221 const DrivetrainPosition robot_position =
222 ComputePosition(drivetrain_log, cur_frame.capture_time);
223 const ::frc971::control_loops::TypedPose<double> robot_pose(
224 {robot_position.x, robot_position.y, 0}, robot_position.theta);
Austin Schuha966a372019-04-14 17:15:28 -0700225
Philipp Schrader790cb542023-07-05 21:06:52 -0700226 camera_pose.set_base(&robot_pose);
Austin Schuh1445d822019-04-14 15:35:20 -0700227
Philipp Schrader790cb542023-07-05 21:06:52 -0700228 camera_debug->set_current_frame_age(
229 ::aos::time::DurationInSeconds(now - cur_frame.capture_time));
230 camera_debug->set_time_since_last_target(
231 ::aos::time::DurationInSeconds(now - last_target_time[ii]));
232 for (const auto &target : cur_frame.targets) {
233 frc971::control_loops::TypedPose<double> target_pose(
234 &camera_pose, {target.x, target.y, 0}, target.theta);
235 Pose *pose = camera_debug->add_targets();
236 pose->set_x(target_pose.abs_pos().x());
237 pose->set_y(target_pose.abs_pos().y());
238 pose->set_theta(target_pose.abs_theta());
239 }
240 }
Austin Schuh1445d822019-04-14 15:35:20 -0700241
Philipp Schrader790cb542023-07-05 21:06:52 -0700242 if (now > last_send_time + chrono::milliseconds(100)) {
243 last_send_time = now;
244 debug_data.mutable_robot_pose()->set_x(drivetrain_status_fetcher->x());
245 debug_data.mutable_robot_pose()->set_y(drivetrain_status_fetcher->y());
246 debug_data.mutable_robot_pose()->set_theta(
247 drivetrain_status_fetcher->theta());
248 {
249 LineFollowDebug *line_debug = 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
Philipp Schrader790cb542023-07-05 21:06:52 -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 Schuh71ae7952019-04-14 15:12:52 -0700269
Philipp Schrader790cb542023-07-05 21:06:52 -0700270 ::std::string json;
Adam Snaider13d48d92023-08-03 12:20:15 -0700271 (void)google::protobuf::util::MessageToJsonString(debug_data, &json);
Philipp Schrader790cb542023-07-05 21:06:52 -0700272 server->execute(
273 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Austin Schuh1445d822019-04-14 15:35:20 -0700274
Philipp Schrader790cb542023-07-05 21:06:52 -0700275 debug_data.Clear();
276 }
277 });
Austin Schuh8a633d52019-05-12 15:04:01 -0700278 event_loop.Run();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700279}
280
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800281} // namespace y2019::vision
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700282
Austin Schuh094d09b2020-11-20 23:26:52 -0800283int main(int argc, char **argv) {
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700284 // Make sure to reference this to force the linker to include it.
285 findEmbeddedContent("");
286
Austin Schuh094d09b2020-11-20 23:26:52 -0800287 ::aos::InitGoogle(&argc, &argv);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700288
289 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700290 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700291
292 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700293 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700294
295 std::thread data_thread{[&server, websocket_handler]() {
296 y2019::vision::DataThread(&server, websocket_handler.get());
297 }};
298
Austin Schuh53596192019-03-23 18:53:33 -0700299 // See if we are on a robot. If so, serve the robot www folder.
300 bool serve_www = false;
301 {
302 struct stat result;
Austin Schuhc0ec2a82022-02-24 17:26:29 -0800303 if (stat("/home/admin/bin/www", &result) == 0) {
Austin Schuh53596192019-03-23 18:53:33 -0700304 serve_www = true;
305 }
306 }
307
Philipp Schrader790cb542023-07-05 21:06:52 -0700308 server.serve(serve_www ? "/home/admin/bin/www" : "y2019/vision/server/www",
309 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700310
311 return 0;
312}