blob: c322322f7d0b3669ff5200f078a1c3f345a17f27 [file] [log] [blame]
Brian Silvermanb42ff2d2019-03-23 15:36:39 -07001#include <array>
Brian Silvermanacdabeb2019-03-23 14:04:36 -07002#include <memory>
3#include <set>
Brian Silvermanb42ff2d2019-03-23 15:36:39 -07004#include <sstream>
Brian Silvermanacdabeb2019-03-23 14:04:36 -07005
6#include "aos/init.h"
7#include "aos/logging/logging.h"
8#include "aos/time/time.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -07009#include "frc971/control_loops/drivetrain/drivetrain.q.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070010#include "internal/Embedded.h"
11#include "seasocks/PrintfLogger.h"
12#include "seasocks/Server.h"
13#include "seasocks/StringUtil.h"
14#include "seasocks/WebSocket.h"
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070015#include "y2019/control_loops/drivetrain/camera.q.h"
Brian Silvermanacdabeb2019-03-23 14:04:36 -070016
17namespace y2019 {
18namespace vision {
19
20class WebsocketHandler : public seasocks::WebSocket::Handler {
21 public:
22 WebsocketHandler();
23 void onConnect(seasocks::WebSocket* connection) override;
24 void onData(seasocks::WebSocket* connection, const char* data) override;
25 void onDisconnect(seasocks::WebSocket* connection) override;
26
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070027 void SendData(const std::string &data);
28
Brian Silvermanacdabeb2019-03-23 14:04:36 -070029 private:
30 std::set<seasocks::WebSocket *> connections_;
31};
32
33WebsocketHandler::WebsocketHandler() {
34}
35
36void WebsocketHandler::onConnect(seasocks::WebSocket *connection) {
37 connections_.insert(connection);
38 LOG(INFO, "Connected: %s : %s\n", connection->getRequestUri().c_str(),
39 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
40}
41
42void WebsocketHandler::onData(seasocks::WebSocket * /*connection*/,
43 const char *data) {
44 LOG(INFO, "Got data: %s\n", data);
45}
46
47void WebsocketHandler::onDisconnect(seasocks::WebSocket *connection) {
48 connections_.erase(connection);
49 LOG(INFO, "Disconnected: %s : %s\n", connection->getRequestUri().c_str(),
50 seasocks::formatAddress(connection->getRemoteAddress()).c_str());
51}
52
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070053void WebsocketHandler::SendData(const std::string &data) {
54 for (seasocks::WebSocket *websocket : connections_) {
55 websocket->send(reinterpret_cast<const uint8_t *>(data.data()),
56 data.size());
57 }
58}
59
Brian Silvermanacdabeb2019-03-23 14:04:36 -070060// TODO(Brian): Put this somewhere shared.
61class SeasocksLogger : public seasocks::PrintfLogger {
62 public:
63 SeasocksLogger(Level min_level_to_log) : PrintfLogger(min_level_to_log) {}
64 void log(Level level, const char* message) override;
65};
66
67void SeasocksLogger::log(Level level, const char *message) {
68 // Convert Seasocks error codes to AOS.
69 log_level aos_level;
70 switch (level) {
71 case seasocks::Logger::INFO:
72 aos_level = INFO;
73 break;
74 case seasocks::Logger::WARNING:
75 aos_level = WARNING;
76 break;
77 case seasocks::Logger::ERROR:
78 case seasocks::Logger::SEVERE:
79 aos_level = ERROR;
80 break;
81 case seasocks::Logger::DEBUG:
82 case seasocks::Logger::ACCESS:
83 default:
84 aos_level = DEBUG;
85 break;
86 }
87 LOG(aos_level, "Seasocks: %s\n", message);
88}
89
Brian Silvermanb42ff2d2019-03-23 15:36:39 -070090struct LocalCameraTarget {
91 double x, y, theta;
92};
93
94struct LocalCameraFrame {
95 aos::monotonic_clock::time_point capture_time =
96 aos::monotonic_clock::min_time;
97 std::vector<LocalCameraTarget> targets;
98
99 bool IsValid(aos::monotonic_clock::time_point now) {
100 return capture_time + std::chrono::seconds(1) > now;
101 }
102};
103
104// Sends a new chunk of data to all the websocket clients.
105class UpdateData : public seasocks::Server::Runnable {
106 public:
107 UpdateData(WebsocketHandler *websocket_handler, std::string &&data)
108 : websocket_handler_(websocket_handler), data_(std::move(data)) {}
109 ~UpdateData() override = default;
110 UpdateData(const UpdateData &) = delete;
111 UpdateData &operator=(const UpdateData &) = delete;
112
113 void run() override { websocket_handler_->SendData(data_); }
114
115 private:
116 WebsocketHandler *const websocket_handler_;
117 const std::string data_;
118};
119
120void DataThread(seasocks::Server *server, WebsocketHandler *websocket_handler) {
121 auto &camera_frames = y2019::control_loops::drivetrain::camera_frames;
122 auto &drivetrain_status = frc971::control_loops::drivetrain_queue.status;
123
124 std::array<LocalCameraFrame, 5> latest_frames;
125 aos::monotonic_clock::time_point last_send_time = aos::monotonic_clock::now();
126
127 while (true) {
128 camera_frames.FetchNextBlocking();
129 drivetrain_status.FetchLatest();
130 if (!drivetrain_status.get()) {
131 // Try again if we don't have any drivetrain statuses.
132 continue;
133 }
134
135 {
136 const auto &new_frame = *camera_frames;
137 if (new_frame.camera < latest_frames.size()) {
138 latest_frames[new_frame.camera].capture_time =
139 aos::monotonic_clock::time_point(
140 std::chrono::nanoseconds(new_frame.timestamp));
141 latest_frames[new_frame.camera].targets.clear();
142 for (int target = 0; target < new_frame.num_targets; ++target) {
143 latest_frames[new_frame.camera].targets.emplace_back();
144 // TODO: Do something useful.
145 }
146 }
147 }
148
149 const auto now = aos::monotonic_clock::now();
150 if (now > last_send_time + std::chrono::milliseconds(100)) {
151 last_send_time = now;
152 std::ostringstream stream;
153 stream << "{\n";
154
155 stream << "'robot': {";
156 stream << "'x': " << drivetrain_status->x << ",";
157 stream << "'y': " << drivetrain_status->y << ",";
158 stream << "'theta': " << drivetrain_status->theta;
159 stream << "}\n";
160
161 stream << "}";
162 server->execute(
163 std::make_shared<UpdateData>(websocket_handler, stream.str()));
164 }
165 }
166}
167
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700168} // namespace vision
169} // namespace y2019
170
171int main(int, char *[]) {
172 // Make sure to reference this to force the linker to include it.
173 findEmbeddedContent("");
174
175 aos::InitNRT();
176
177 seasocks::Server server(::std::shared_ptr<seasocks::Logger>(
178 new y2019::vision::SeasocksLogger(seasocks::Logger::INFO)));
179
180 auto websocket_handler = std::make_shared<y2019::vision::WebsocketHandler>();
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700181 server.addWebSocketHandler("/ws", websocket_handler);
Brian Silvermanb42ff2d2019-03-23 15:36:39 -0700182
183 std::thread data_thread{[&server, websocket_handler]() {
184 y2019::vision::DataThread(&server, websocket_handler.get());
185 }};
186
Brian Silvermanacdabeb2019-03-23 14:04:36 -0700187 server.serve("/home/admin/robot_code/www", 1180);
188
189 return 0;
190}