Skip to content

File plugin.cpp

File List > offload_vio > server_rx > plugin.cpp

Go to the documentation of this file

#include "plugin.hpp"

#include "illixr/network/net_config.hpp"
#include "video_decoder.hpp"

using namespace ILLIXR;
using namespace ILLIXR::data_format;

// #define USE_COMPRESSION

[[maybe_unused]] server_reader::server_reader(const std::string& name, phonebook* pb)
    : threadloop{name, pb}
    , switchboard_{phonebook_->lookup_impl<switchboard>()}
    , clock_{pb->lookup_impl<relative_clock>()}
    , imu_{switchboard_->get_writer<imu_type>("imu")}
    , cam_{switchboard_->get_writer<binocular_cam_type>("cam")}
    , imu_cam_reader_{switchboard_->get_buffered_reader<switchboard::event_wrapper<std::string>>("compressed_imu_cam")}
    , log_(spdlogger(switchboard_->get_env_char("OFFLOAD_VIO_LOG_LEVEL"))) {
    log_->info("Camera Time,Uplink Time(ms)");
}

ILLIXR::threadloop::skip_option server_reader::_p_should_skip() {
    return skip_option::run;
}

void server_reader::_p_one_iteration() {
    if (imu_cam_reader_.size() > 0) {
        auto                       buffer_ptr   = imu_cam_reader_.dequeue();
        std::string                buffer_str   = **buffer_ptr;
        std::string::size_type     end_position = buffer_str.find(delimiter_);
        vio_input_proto::IMUCamVec vio_input;
        bool                       success = vio_input.ParseFromString(buffer_str.substr(0, end_position));
        if (!success) {
            log_->error("[offload_vio.server_rx]Error parsing the protobuf, vio input size = {}",
                        buffer_str.size() - delimiter_.size());
        } else {
            receive_vio_input(vio_input);
        }
    }
}

void server_reader::start() {
    threadloop::start();

    decoder_ = std::make_unique<vio_video_decoder>([this](cv::Mat&& img0, cv::Mat&& img1) {
        queue_.consume_one([&](uint64_t& timestamp) {
            (void) timestamp;
            uint64_t curr =
                std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch())
                    .count();
            // std::cout << "=== latency: " << (curr - timestamp) / 1000000.0 << std::endl;
        });
        {
            std::lock_guard<std::mutex> lock{mutex_};
            this->img0_dst_ = std::forward<cv::Mat>(img0);
            this->img1_dst_ = std::forward<cv::Mat>(img1);
            img_ready_      = true;
        }
        condition_variable_.notify_one();
    });
    decoder_->init();
}

void server_reader::receive_vio_input(const vio_input_proto::IMUCamVec& vio_input) {
    // Logging the transmitting time
    unsigned long long curr_time =
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
    double msec_to_trans = (curr_time - vio_input.real_timestamp()) / 1e6;

    // Loop through and publish all IMU values first
    for (int i = 0; i < vio_input.imu_data_size() - 1; i++) {
        vio_input_proto::IMUData curr_data = vio_input.imu_data(i);
        imu_.put(imu_.allocate<imu_type>(imu_type{
            time_point{std::chrono::nanoseconds{curr_data.timestamp()}},
            Eigen::Vector3d{curr_data.angular_vel().x(), curr_data.angular_vel().y(), curr_data.angular_vel().z()},
            Eigen::Vector3d{curr_data.linear_accel().x(), curr_data.linear_accel().y(), curr_data.linear_accel().z()}}));
    }
    // Publish the Cam value then
    vio_input_proto::CamData cam_data = vio_input.cam_data();
    log_->info("{},{}", cam_data.timestamp(), msec_to_trans);

    // Must do a deep copy of the received data (in the form of a string of bytes)
    auto img0_copy = std::string(cam_data.img0_data());
    auto img1_copy = std::string(cam_data.img1_data());

#ifdef USE_COMPRESSION
    time_point start_decomp = clock_->now();
    // With compression
    uint64_t curr =
        std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
    queue_.push(curr);
    std::unique_lock<std::mutex> lock{mutex_};
    decoder_->enqueue(img0_copy, img1_copy);
    condition_variable_.wait(lock, [this]() {
        return img_ready_;
    });
    img_ready_ = false;

    cv::Mat img0(img0_dst_.clone());
    cv::Mat img1(img1_dst_.clone());

    lock.unlock();
    log_->warn("{},{}", cam_data.timestamp(), (clock_->now() - start_decomp).count() / 1e6);
    // With compression end
#else
    // Without compression
    cv::Mat img0(cam_data.rows(), cam_data.cols(), CV_8UC1, img0_copy.data());
    cv::Mat img1(cam_data.rows(), cam_data.cols(), CV_8UC1, img1_copy.data());
    // Without compression end
#endif
    cam_.put(cam_.allocate<binocular_cam_type>(binocular_cam_type{
        time_point{std::chrono::nanoseconds{cam_data.timestamp()}},
        img0.clone(),
        img1.clone(),
    }));
    // If we publish all IMU samples before the camera data, the camera data may not be captured in any of the IMU callbacks
    // in the tracking algorithm (e.g. OpenVINS), and has to wait for another camera frame time (until the next packet
    // arrives) to be consumed. Therefore, we publish one (or more) IMU samples after the camera data to make sure that the
    // camera data will be captured.
    vio_input_proto::IMUData last_imu = vio_input.imu_data(vio_input.imu_data_size() - 1);
    imu_.put(imu_.allocate<imu_type>(
        imu_type{time_point{std::chrono::nanoseconds{last_imu.timestamp()}},
                 Eigen::Vector3d{last_imu.angular_vel().x(), last_imu.angular_vel().y(), last_imu.angular_vel().z()},
                 Eigen::Vector3d{last_imu.linear_accel().x(), last_imu.linear_accel().y(), last_imu.linear_accel().z()}}));
}

PLUGIN_MAIN(server_reader)