blob: 05a53e2fe39f4f725cc86f4f38eb331106a06be3 [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();
108
109 while (true) {
110 camera_frames.FetchNextBlocking();
111 drivetrain_status.FetchLatest();
Austin Schuh71ae7952019-04-14 15:12:52 -0700112 superstructure_status.FetchLatest();
113 if (!drivetrain_status.get() || !superstructure_status.get()) {
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700114 // Try again if we don't have any drivetrain statuses.
115 continue;
116 }
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700117 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700118
119 {
120 const auto &new_frame = *camera_frames;
Alex Perryd13750f2019-04-10 21:15:28 -0700121 // TODO(james): Maybe we only want to fill out a new frame if it has
122 // targets or the saved target is > 0.1 sec old? Not sure, but for now
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700123 if (new_frame.camera < latest_frames.size()) {
124 latest_frames[new_frame.camera].capture_time =
125 aos::monotonic_clock::time_point(
126 std::chrono::nanoseconds(new_frame.timestamp));
127 latest_frames[new_frame.camera].targets.clear();
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700128 if (new_frame.num_targets > 0) {
James Kuszmauld6d37d12019-03-30 13:04:54 -0700129 last_target_time[new_frame.camera] =
130 latest_frames[new_frame.camera].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700131 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700132 for (int target = 0; target < new_frame.num_targets; ++target) {
133 latest_frames[new_frame.camera].targets.emplace_back();
Alex Perryd13750f2019-04-10 21:15:28 -0700134 const float heading = new_frame.targets[target].heading;
135 const float distance = new_frame.targets[target].distance;
136 latest_frames[new_frame.camera].targets.back().x =
137 ::std::cos(heading) * distance;
138 latest_frames[new_frame.camera].targets.back().y =
139 ::std::sin(heading) * distance;
140 latest_frames[new_frame.camera].targets.back().theta =
141 new_frame.targets[target].skew;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700142 }
143 }
144 }
145
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700146 if (now > last_send_time + std::chrono::milliseconds(100)) {
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700147 // TODO(james): Use protobuf or the such to generate JSON rather than
148 // doing so manually.
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700149 last_send_time = now;
James Kuszmauld6d37d12019-03-30 13:04:54 -0700150 DebugData debug_data;
151 debug_data.mutable_robot_pose()->set_x(drivetrain_status->x);
152 debug_data.mutable_robot_pose()->set_y(drivetrain_status->y);
153 debug_data.mutable_robot_pose()->set_theta(drivetrain_status->theta);
Alex Perryd13750f2019-04-10 21:15:28 -0700154 frc971::control_loops::TypedPose<double> robot_pose(
155 {drivetrain_status->x, drivetrain_status->y, 0},
156 drivetrain_status->theta);
James Kuszmauld6d37d12019-03-30 13:04:54 -0700157 {
158 LineFollowDebug *line_debug = debug_data.mutable_line_follow_debug();
159 line_debug->set_frozen(drivetrain_status->line_follow_logging.frozen);
160 line_debug->set_have_target(
161 drivetrain_status->line_follow_logging.have_target);
162 line_debug->mutable_goal_target()->set_x(
163 drivetrain_status->line_follow_logging.x);
164 line_debug->mutable_goal_target()->set_y(
165 drivetrain_status->line_follow_logging.y);
166 line_debug->mutable_goal_target()->set_theta(
167 drivetrain_status->line_follow_logging.theta);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700168 }
James Kuszmauld6d37d12019-03-30 13:04:54 -0700169 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
170 CameraDebug *camera_debug = debug_data.add_camera_debug();
171 LocalCameraFrame cur_frame = latest_frames[ii];
Alex Perryd13750f2019-04-10 21:15:28 -0700172 constants::Values::CameraCalibration camera_info =
173 constants::GetValues().cameras[ii];
James Kuszmaul4374fd12019-04-13 08:28:58 -0700174 frc971::control_loops::TypedPose<double> camera_pose = camera_info.pose;
175 camera_pose.set_base(&robot_pose);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700176
James Kuszmauld6d37d12019-03-30 13:04:54 -0700177 camera_debug->set_current_frame_age(
178 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
179 now - cur_frame.capture_time).count());
180 camera_debug->set_time_since_last_target(
181 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
182 now - last_target_time[ii]).count());
183 for (const auto &target : cur_frame.targets) {
Alex Perryd13750f2019-04-10 21:15:28 -0700184 frc971::control_loops::TypedPose<double> target_pose(
185 &camera_pose, {target.x, target.y, 0}, target.theta);
James Kuszmauld6d37d12019-03-30 13:04:54 -0700186 Pose *pose = camera_debug->add_targets();
Alex Perryd13750f2019-04-10 21:15:28 -0700187 pose->set_x(target_pose.abs_pos().x());
188 pose->set_y(target_pose.abs_pos().y());
189 pose->set_theta(target_pose.abs_theta());
James Kuszmauld6d37d12019-03-30 13:04:54 -0700190 }
191 }
Austin Schuh71ae7952019-04-14 15:12:52 -0700192
193 Sensors *sensors = debug_data.mutable_sensors();
194 sensors->set_wrist(superstructure_status->wrist.position);
195 sensors->set_elevator(superstructure_status->elevator.position);
196 sensors->set_intake(superstructure_status->intake.position);
197 sensors->set_stilts(superstructure_status->stilts.position);
198
James Kuszmauld6d37d12019-03-30 13:04:54 -0700199 ::std::string json;
200 google::protobuf::util::MessageToJsonString(debug_data, &json);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700201 server->execute(
James Kuszmauld6d37d12019-03-30 13:04:54 -0700202 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700203 }
204 }
205}
206
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700207} // namespace vision
208} // namespace y2019
209
210int main(int, char *[]) {
211 // Make sure to reference this to force the linker to include it.
212 findEmbeddedContent("");
213
214 aos::InitNRT();
215
216 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
Austin Schuh86cd5722019-04-14 13:34:20 -0700217 new ::aos::seasocks::SeasocksLogger(seasocks::Logger::Level::Info)));
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700218
219 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700220 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700221
222 std::thread data_thread{[&server, websocket_handler]() {
223 y2019::vision::DataThread(&server, websocket_handler.get());
224 }};
225
Austin Schuh53596192019-03-23 18:53:33 -0700226 // See if we are on a robot. If so, serve the robot www folder.
227 bool serve_www = false;
228 {
229 struct stat result;
230 if (stat("/home/admin/robot_code/www", &result) == 0) {
231 serve_www = true;
232 }
233 }
234
235 server.serve(
236 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
237 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700238
239 return 0;
240}