blob: 0263891b9573e7d157e227fda68fa84f48039299 [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
10#include "aos/init.h"
11#include "aos/logging/logging.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070012#include "aos/seasocks/seasocks_logger.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070013#include "aos/time/time.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070014#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Alex Perryd13750f2019-04-10 21:15:28 -070015#include "frc971/control_loops/pose.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070016#include "google/protobuf/util/json_util.h"
Austin Schuh86cd5722019-04-14 13:34:20 -070017#include "internal/Embedded.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070018#include "seasocks/Server.h"
19#include "seasocks/StringUtil.h"
20#include "seasocks/WebSocket.h"
Alex Perryd13750f2019-04-10 21:15:28 -070021#include "y2019/constants.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070022#include "y2019/control_loops/drivetrain/camera.q.h"
Austin Schuh71ae7952019-04-14 15:12:52 -070023#include "y2019/control_loops/superstructure/superstructure.q.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070024#include "y2019/vision/server/server_data.pb.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070025
26namespace y2019 {
27namespace vision {
28
29class WebsocketHandler : public seasocks::WebSocket::Handler {
30 public:
31 WebsocketHandler();
32 void onConnect(seasocks::WebSocket* connection) override;
33 void onData(seasocks::WebSocket* connection, const char* data) override;
34 void onDisconnect(seasocks::WebSocket* connection) override;
35
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070036 void SendData(const std::string &data);
37
Brian Silvermanacdabeb2019-03-23 14:04:36 -070038 private:
39 std::set<seasocks::WebSocket *> connections_;
40};
41
42WebsocketHandler::WebsocketHandler() {
43}
44
45void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
46 connections_.insert(connection);
47 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
48 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
49}
50
51void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
52 const char *data) {
53 LOG(INFO, "Got data: %s\n", data);
54}
55
56void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
57 connections_.erase(connection);
58 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
59 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
60}
61
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070062void WebsocketHandler::SendData(const std::string &data) {
63 for (seasocks::WebSocket *websocket : connections_) {
Austin Schuh89a95052019-04-14 14:51:14 -070064 websocket->send(data.c_str());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070065 }
66}
67
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070068struct LocalCameraTarget {
69 double x, y, theta;
70};
71
72struct LocalCameraFrame {
73 aos::monotonic_clock::time_point capture_time =
74 aos::monotonic_clock::min_time;
75 std::vector<LocalCameraTarget> targets;
76
77 bool IsValid(aos::monotonic_clock::time_point now) {
78 return capture_time + std::chrono::seconds(1) > now;
79 }
80};
81
82// Sends a new chunk of data to all the websocket clients.
83class UpdateData : public seasocks::Server::Runnable {
84 public:
85 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
86 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
87 ~UpdateData() override = default;
88 UpdateData(const UpdateData &) = delete;
89 UpdateData &operator=(const UpdateData &) = delete;
90
91 void run() override { websocket_handler_->SendData(data_); }
92
93 private:
94 WebsocketHandler *const websocket_handler_;
95 const std::string data_;
96};
97
98void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
99 auto &camera_frames = y2019::control_loops::drivetrain::camera_frames;
100 auto &drivetrain_status = frc971::control_loops::drivetrain_queue.status;
Austin Schuh71ae7952019-04-14 15:12:52 -0700101 auto &superstructure_status =
102 ::y2019::control_loops::superstructure::superstructure_queue.status;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700103
104 std::array<LocalCameraFrame, 5> latest_frames;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700105 std::array<aos::monotonic_clock::time_point, 5> last_target_time;
106 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700107 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
Austin Schuh1445d822019-04-14 15:35:20 -0700108 DebugData debug_data;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700109
110 while (true) {
111 camera_frames.FetchNextBlocking();
112 drivetrain_status.FetchLatest();
Austin Schuh71ae7952019-04-14 15:12:52 -0700113 superstructure_status.FetchLatest();
114 if (!drivetrain_status.get() || !superstructure_status.get()) {
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700115 // Try again if we don't have any drivetrain statuses.
116 continue;
117 }
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700118 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700119
120 {
121 const auto &new_frame = *camera_frames;
Alex Perryd13750f2019-04-10 21:15:28 -0700122 // TODO(james): Maybe we only want to fill out a new frame if it has
123 // targets or the saved target is > 0.1 sec old? Not sure, but for now
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700124 if (new_frame.camera < latest_frames.size()) {
125 latest_frames[new_frame.camera].capture_time =
126 aos::monotonic_clock::time_point(
127 std::chrono::nanoseconds(new_frame.timestamp));
128 latest_frames[new_frame.camera].targets.clear();
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700129 if (new_frame.num_targets > 0) {
James Kuszmauld6d37d12019-03-30 13:04:54 -0700130 last_target_time[new_frame.camera] =
131 latest_frames[new_frame.camera].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700132 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700133 for (int target = 0; target < new_frame.num_targets; ++target) {
134 latest_frames[new_frame.camera].targets.emplace_back();
Alex Perryd13750f2019-04-10 21:15:28 -0700135 const float heading = new_frame.targets[target].heading;
136 const float distance = new_frame.targets[target].distance;
137 latest_frames[new_frame.camera].targets.back().x =
138 ::std::cos(heading) * distance;
139 latest_frames[new_frame.camera].targets.back().y =
140 ::std::sin(heading) * distance;
141 latest_frames[new_frame.camera].targets.back().theta =
142 new_frame.targets[target].skew;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700143 }
144 }
145 }
146
Austin Schuh1445d822019-04-14 15:35:20 -0700147 frc971::control_loops::TypedPose<double> robot_pose(
148 {drivetrain_status->x, drivetrain_status->y, 0},
149 drivetrain_status->theta);
150 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
151 CameraDebug *camera_debug = debug_data.add_camera_debug();
152 LocalCameraFrame cur_frame = latest_frames[ii];
153 constants::Values::CameraCalibration camera_info =
154 constants::GetValues().cameras[ii];
155 frc971::control_loops::TypedPose<double> camera_pose = camera_info.pose;
156 camera_pose.set_base(&robot_pose);
157
158 camera_debug->set_current_frame_age(
159 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
160 now - cur_frame.capture_time)
161 .count());
162 camera_debug->set_time_since_last_target(
163 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
164 now - last_target_time[ii])
165 .count());
166 for (const auto &target : cur_frame.targets) {
167 frc971::control_loops::TypedPose<double> target_pose(
168 &camera_pose, {target.x, target.y, 0}, target.theta);
169 Pose *pose = camera_debug->add_targets();
170 pose->set_x(target_pose.abs_pos().x());
171 pose->set_y(target_pose.abs_pos().y());
172 pose->set_theta(target_pose.abs_theta());
173 }
174 }
175
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700176 if (now > last_send_time + std::chrono::milliseconds(100)) {
177 last_send_time = now;
James Kuszmauld6d37d12019-03-30 13:04:54 -0700178 debug_data.mutable_robot_pose()->set_x(drivetrain_status->x);
179 debug_data.mutable_robot_pose()->set_y(drivetrain_status->y);
180 debug_data.mutable_robot_pose()->set_theta(drivetrain_status->theta);
181 {
182 LineFollowDebug *line_debug = debug_data.mutable_line_follow_debug();
183 line_debug->set_frozen(drivetrain_status->line_follow_logging.frozen);
184 line_debug->set_have_target(
185 drivetrain_status->line_follow_logging.have_target);
186 line_debug->mutable_goal_target()->set_x(
187 drivetrain_status->line_follow_logging.x);
188 line_debug->mutable_goal_target()->set_y(
189 drivetrain_status->line_follow_logging.y);
190 line_debug->mutable_goal_target()->set_theta(
191 drivetrain_status->line_follow_logging.theta);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700192 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700193
194 Sensors *sensors = debug_data.mutable_sensors();
195 sensors->set_wrist(superstructure_status->wrist.position);
196 sensors->set_elevator(superstructure_status->elevator.position);
197 sensors->set_intake(superstructure_status->intake.position);
198 sensors->set_stilts(superstructure_status->stilts.position);
199
James Kuszmauld6d37d12019-03-30 13:04:54 -0700200 ::std::string json;
201 google::protobuf::util::MessageToJsonString(debug_data, &json);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700202 server->execute(
James Kuszmauld6d37d12019-03-30 13:04:54 -0700203 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Austin Schuh1445d822019-04-14 15:35:20 -0700204
205 debug_data.Clear();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700206 }
207 }
208}
209
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700210} // namespace vision
211} // namespace y2019
212
213int main(int, char *[]) {
214 // Make sure to reference this to force the linker to include it.
215 findEmbeddedContent("");
216
217 aos::InitNRT();
218
219 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700220 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700221
222 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700223 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700224
225 std::thread data_thread{[&server, websocket_handler]() {
226 y2019::vision::DataThread(&server, websocket_handler.get());
227 }};
228
Austin Schuh53596192019-03-23 18:53:33 -0700229 // See if we are on a robot. If so, serve the robot www folder.
230 bool serve_www = false;
231 {
232 struct stat result;
233 if (stat("/home/admin/robot_code/www", &result) == 0) {
234 serve_www = true;
235 }
236 }
237
238 server.serve(
239 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
240 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700241
242 return 0;
243}