blob: 6f2e26ac6b0be087ddde5216fdeed9bd0d913e75 [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"
12#include "aos/time/time.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070013#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Alex Perryd13750f2019-04-10 21:15:28 -070014#include "frc971/control_loops/pose.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070015#include "internal/Embedded.h"
James Kuszmauld6d37d12019-03-30 13:04:54 -070016#include "google/protobuf/util/json_util.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070017#include "seasocks/PrintfLogger.h"
18#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"
James Kuszmauld6d37d12019-03-30 13:04:54 -070023#include "y2019/vision/server/server_data.pb.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070024
25namespace y2019 {
26namespace vision {
27
28class WebsocketHandler : public seasocks::WebSocket::Handler {
29 public:
30 WebsocketHandler();
31 void onConnect(seasocks::WebSocket* connection) override;
32 void onData(seasocks::WebSocket* connection, const char* data) override;
33 void onDisconnect(seasocks::WebSocket* connection) override;
34
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070035 void SendData(const std::string &data);
36
Brian Silvermanacdabeb2019-03-23 14:04:36 -070037 private:
38 std::set<seasocks::WebSocket *> connections_;
39};
40
41WebsocketHandler::WebsocketHandler() {
42}
43
44void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
45 connections_.insert(connection);
46 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
47 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
48}
49
50void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
51 const char *data) {
52 LOG(INFO, "Got data: %s\n", data);
53}
54
55void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
56 connections_.erase(connection);
57 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
58 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
59}
60
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070061void WebsocketHandler::SendData(const std::string &data) {
62 for (seasocks::WebSocket *websocket : connections_) {
63 websocket->send(reinterpret_cast<const uint8_t *>(data.data()),
64 data.size());
65 }
66}
67
Brian Silvermanacdabeb2019-03-23 14:04:36 -070068// TODO(Brian): Put this somewhere shared.
69class SeasocksLogger : public seasocks::PrintfLogger {
70 public:
71 SeasocksLogger(Level min_level_to_log) : PrintfLogger(min_level_to_log) {}
72 void log(Level level, const char* message) override;
73};
74
75void SeasocksLogger::log(Level level, const char *message) {
76 // Convert Seasocks error codes to AOS.
77 log_level aos_level;
78 switch (level) {
79 case seasocks::Logger::INFO:
80 aos_level = INFO;
81 break;
82 case seasocks::Logger::WARNING:
83 aos_level = WARNING;
84 break;
85 case seasocks::Logger::ERROR:
86 case seasocks::Logger::SEVERE:
87 aos_level = ERROR;
88 break;
89 case seasocks::Logger::DEBUG:
90 case seasocks::Logger::ACCESS:
91 default:
92 aos_level = DEBUG;
93 break;
94 }
95 LOG(aos_level, "Seasocks: %s\n", message);
96}
97
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070098struct LocalCameraTarget {
99 double x, y, theta;
100};
101
102struct LocalCameraFrame {
103 aos::monotonic_clock::time_point capture_time =
104 aos::monotonic_clock::min_time;
105 std::vector<LocalCameraTarget> targets;
106
107 bool IsValid(aos::monotonic_clock::time_point now) {
108 return capture_time + std::chrono::seconds(1) > now;
109 }
110};
111
112// Sends a new chunk of data to all the websocket clients.
113class UpdateData : public seasocks::Server::Runnable {
114 public:
115 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
116 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
117 ~UpdateData() override = default;
118 UpdateData(const UpdateData &) = delete;
119 UpdateData &operator=(const UpdateData &) = delete;
120
121 void run() override { websocket_handler_->SendData(data_); }
122
123 private:
124 WebsocketHandler *const websocket_handler_;
125 const std::string data_;
126};
127
128void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
129 auto &camera_frames = y2019::control_loops::drivetrain::camera_frames;
130 auto &drivetrain_status = frc971::control_loops::drivetrain_queue.status;
131
132 std::array<LocalCameraFrame, 5> latest_frames;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700133 std::array<aos::monotonic_clock::time_point, 5> last_target_time;
134 last_target_time.fill(aos::monotonic_clock::epoch());
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700135 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
136
137 while (true) {
138 camera_frames.FetchNextBlocking();
139 drivetrain_status.FetchLatest();
140 if (!drivetrain_status.get()) {
141 // Try again if we don't have any drivetrain statuses.
142 continue;
143 }
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700144 const auto now = aos::monotonic_clock::now();
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700145
146 {
147 const auto &new_frame = *camera_frames;
Alex Perryd13750f2019-04-10 21:15:28 -0700148 // TODO(james): Maybe we only want to fill out a new frame if it has
149 // targets or the saved target is > 0.1 sec old? Not sure, but for now
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700150 if (new_frame.camera < latest_frames.size()) {
151 latest_frames[new_frame.camera].capture_time =
152 aos::monotonic_clock::time_point(
153 std::chrono::nanoseconds(new_frame.timestamp));
154 latest_frames[new_frame.camera].targets.clear();
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700155 if (new_frame.num_targets > 0) {
James Kuszmauld6d37d12019-03-30 13:04:54 -0700156 last_target_time[new_frame.camera] =
157 latest_frames[new_frame.camera].capture_time;
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700158 }
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700159 for (int target = 0; target < new_frame.num_targets; ++target) {
160 latest_frames[new_frame.camera].targets.emplace_back();
Alex Perryd13750f2019-04-10 21:15:28 -0700161 const float heading = new_frame.targets[target].heading;
162 const float distance = new_frame.targets[target].distance;
163 latest_frames[new_frame.camera].targets.back().x =
164 ::std::cos(heading) * distance;
165 latest_frames[new_frame.camera].targets.back().y =
166 ::std::sin(heading) * distance;
167 latest_frames[new_frame.camera].targets.back().theta =
168 new_frame.targets[target].skew;
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700169 }
170 }
171 }
172
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700173 if (now > last_send_time + std::chrono::milliseconds(100)) {
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700174 // TODO(james): Use protobuf or the such to generate JSON rather than
175 // doing so manually.
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700176 last_send_time = now;
James Kuszmauld6d37d12019-03-30 13:04:54 -0700177 DebugData debug_data;
178 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);
Alex Perryd13750f2019-04-10 21:15:28 -0700181 frc971::control_loops::TypedPose<double> robot_pose(
182 {drivetrain_status->x, drivetrain_status->y, 0},
183 drivetrain_status->theta);
James Kuszmauld6d37d12019-03-30 13:04:54 -0700184 {
185 LineFollowDebug *line_debug = debug_data.mutable_line_follow_debug();
186 line_debug->set_frozen(drivetrain_status->line_follow_logging.frozen);
187 line_debug->set_have_target(
188 drivetrain_status->line_follow_logging.have_target);
189 line_debug->mutable_goal_target()->set_x(
190 drivetrain_status->line_follow_logging.x);
191 line_debug->mutable_goal_target()->set_y(
192 drivetrain_status->line_follow_logging.y);
193 line_debug->mutable_goal_target()->set_theta(
194 drivetrain_status->line_follow_logging.theta);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700195 }
James Kuszmauld6d37d12019-03-30 13:04:54 -0700196 for (size_t ii = 0; ii < latest_frames.size(); ++ii) {
197 CameraDebug *camera_debug = debug_data.add_camera_debug();
198 LocalCameraFrame cur_frame = latest_frames[ii];
Alex Perryd13750f2019-04-10 21:15:28 -0700199 constants::Values::CameraCalibration camera_info =
200 constants::GetValues().cameras[ii];
201 frc971::control_loops::TypedPose<double> camera_pose =
202 camera_info.pose.Rebase(&robot_pose);
James Kuszmaul92ba0e52019-03-29 17:19:30 -0700203
James Kuszmauld6d37d12019-03-30 13:04:54 -0700204 camera_debug->set_current_frame_age(
205 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
206 now - cur_frame.capture_time).count());
207 camera_debug->set_time_since_last_target(
208 ::std::chrono::duration_cast<::std::chrono::duration<double>>(
209 now - last_target_time[ii]).count());
210 for (const auto &target : cur_frame.targets) {
Alex Perryd13750f2019-04-10 21:15:28 -0700211 frc971::control_loops::TypedPose<double> target_pose(
212 &camera_pose, {target.x, target.y, 0}, target.theta);
James Kuszmauld6d37d12019-03-30 13:04:54 -0700213 Pose *pose = camera_debug->add_targets();
Alex Perryd13750f2019-04-10 21:15:28 -0700214 pose->set_x(target_pose.abs_pos().x());
215 pose->set_y(target_pose.abs_pos().y());
216 pose->set_theta(target_pose.abs_theta());
James Kuszmauld6d37d12019-03-30 13:04:54 -0700217 }
218 }
219 ::std::string json;
220 google::protobuf::util::MessageToJsonString(debug_data, &json);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700221 server->execute(
James Kuszmauld6d37d12019-03-30 13:04:54 -0700222 std::make_shared<UpdateData>(websocket_handler, ::std::move(json)));
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700223 }
224 }
225}
226
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700227} // namespace vision
228} // namespace y2019
229
230int main(int, char *[]) {
231 // Make sure to reference this to force the linker to include it.
232 findEmbeddedContent("");
233
234 aos::InitNRT();
235
236 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
237 new y2019::vision::SeasocksLogger(seasocks::Logger::INFO)));
238
239 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700240 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700241
242 std::thread data_thread{[&server, websocket_handler]() {
243 y2019::vision::DataThread(&server, websocket_handler.get());
244 }};
245
Austin Schuh53596192019-03-23 18:53:33 -0700246 // See if we are on a robot. If so, serve the robot www folder.
247 bool serve_www = false;
248 {
249 struct stat result;
250 if (stat("/home/admin/robot_code/www", &result) == 0) {
251 serve_www = true;
252 }
253 }
254
255 server.serve(
256 serve_www ? "/home/admin/robot_code/www" : "y2019/vision/server/www",
257 1180);
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700258
259 return 0;
260}