|
|
@@ -0,0 +1,135 @@
|
|
|
+#include "ndi_stray_point_tracker.h"
|
|
|
+
|
|
|
+// from sophiar
|
|
|
+#include "utility/coro_worker.hpp"
|
|
|
+#include "utility/coro_worker_helper_func.hpp"
|
|
|
+
|
|
|
+#include <boost/asio/io_context.hpp>
|
|
|
+#include <boost/asio/ip/tcp.hpp>
|
|
|
+#include <boost/asio/steady_timer.hpp>
|
|
|
+#include <boost/asio/write.hpp>
|
|
|
+#include <boost/asio/read_until.hpp>
|
|
|
+#include <boost/asio/streambuf.hpp>
|
|
|
+#include <boost/asio/use_awaitable.hpp>
|
|
|
+#include <boost/asio/awaitable.hpp>
|
|
|
+#include <boost/asio/co_spawn.hpp>
|
|
|
+#include <boost/asio/detached.hpp>
|
|
|
+
|
|
|
+#include <shared_mutex>
|
|
|
+
|
|
|
+using namespace sophiar;
|
|
|
+
|
|
|
+namespace ba = boost::asio;
|
|
|
+using namespace ba::ip;
|
|
|
+using namespace std::placeholders;
|
|
|
+
|
|
|
+using boost::system::error_code;
|
|
|
+
|
|
|
+namespace {
|
|
|
+ constexpr auto track_interval = std::chrono::milliseconds(17); // 60Hz
|
|
|
+ constexpr const char track_command[] = "BX 1000\r";
|
|
|
+ constexpr auto ndi_endian = boost::endian::order::little;
|
|
|
+ constexpr uint16_t binary_header_magic = 0xA5C4;
|
|
|
+}
|
|
|
+
|
|
|
+struct ndi_stray_point_tracker::impl {
|
|
|
+ create_config conf;
|
|
|
+
|
|
|
+ std::optional<tcp::socket> socket;
|
|
|
+ coro_worker::pointer track_worker;
|
|
|
+
|
|
|
+ points_type last_points;
|
|
|
+ std::shared_mutex mu;
|
|
|
+
|
|
|
+ void establish_connection() {
|
|
|
+ socket.emplace(*global_context);
|
|
|
+ const auto ndi_ep = tcp::endpoint(make_address(conf.ip_addr), conf.port);
|
|
|
+ socket->async_connect(ndi_ep, [this](const error_code &ec) {
|
|
|
+ assert(!ec);
|
|
|
+ track_worker = make_interval_coro_worker(
|
|
|
+ track_interval, [this] { return track_once(); });
|
|
|
+ track_worker->run();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ ba::awaitable<dynamic_memory::pointer> read_reply() {
|
|
|
+ // read header
|
|
|
+ auto header_buf = static_memory<6>{};
|
|
|
+ co_await async_fill_memory_from(*socket, header_buf);
|
|
|
+ auto header_reader = versatile_reader<ndi_endian>(header_buf);
|
|
|
+ uint16_t header_magic, reply_length, header_crc16;
|
|
|
+ header_reader >> header_magic >> reply_length >> header_crc16;
|
|
|
+ assert(header_magic == binary_header_magic);
|
|
|
+
|
|
|
+ // TODO: check header crc
|
|
|
+
|
|
|
+ // read reply content
|
|
|
+ auto reply_buf = dynamic_memory::new_instance(reply_length + 2);
|
|
|
+ co_await async_fill_memory_from(*socket, *reply_buf);
|
|
|
+
|
|
|
+ // TODO: check content crc
|
|
|
+
|
|
|
+ reply_buf->increase_size(-2); // strip the crc
|
|
|
+ co_return std::move(reply_buf);
|
|
|
+ }
|
|
|
+
|
|
|
+ ba::awaitable<bool> track_once() {
|
|
|
+ // send command
|
|
|
+ co_await async_write(*socket, ba::buffer(track_command), ba::use_awaitable);
|
|
|
+ // read reply
|
|
|
+ const auto reply = co_await read_reply();
|
|
|
+
|
|
|
+ // parse reply
|
|
|
+ auto reader = versatile_reader<ndi_endian>(*reply);
|
|
|
+ const auto num_handle = reader.read_value<uint8_t>();
|
|
|
+ reader.manual_offset(2 * sizeof(uint8_t) * num_handle); // skip the tool data
|
|
|
+
|
|
|
+ const auto num_points = reader.read_value<uint8_t>();
|
|
|
+ const auto ov_size = (num_points + 8) / 8;
|
|
|
+ reader.manual_offset(ov_size * sizeof(uint8_t)); // TODO: check point status, P56
|
|
|
+ if (true) {
|
|
|
+ auto lock = std::unique_lock(mu);
|
|
|
+ last_points.resize(num_points);
|
|
|
+ for (auto &point: last_points) {
|
|
|
+ point.out_of_volume = false;
|
|
|
+ reader >> point.pos.x >> point.pos.y >> point.pos.z;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ co_return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ explicit impl(create_config _conf)
|
|
|
+ : conf(std::move(_conf)) {
|
|
|
+ establish_connection();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+ndi_stray_point_tracker::ndi_stray_point_tracker(const create_config &conf)
|
|
|
+ : pimpl(std::make_shared<impl>(conf)) {
|
|
|
+}
|
|
|
+
|
|
|
+ndi_stray_point_tracker::~ndi_stray_point_tracker() {
|
|
|
+ co_spawn(*global_context, [pimpl = pimpl] -> ba::awaitable<void> {
|
|
|
+ SAFE_RESET_WORKER(pimpl->track_worker);
|
|
|
+ co_return;
|
|
|
+ }, ba::detached);
|
|
|
+}
|
|
|
+
|
|
|
+ndi_stray_point_tracker::points_type
|
|
|
+ndi_stray_point_tracker::get_stray_points() const {
|
|
|
+ auto lock = std::shared_lock(pimpl->mu);
|
|
|
+ return pimpl->last_points;
|
|
|
+}
|
|
|
+
|
|
|
+namespace {
|
|
|
+ std::optional<ndi_stray_point_tracker> tracker;
|
|
|
+}
|
|
|
+
|
|
|
+void create_ndi_stray_points_tracker(
|
|
|
+ const ndi_stray_point_tracker::create_config &conf) {
|
|
|
+ tracker.emplace(conf);
|
|
|
+}
|
|
|
+
|
|
|
+ndi_stray_point_tracker::points_type get_ndi_stray_points() {
|
|
|
+ return tracker->get_stray_points();
|
|
|
+}
|