Program Listing for File mavros_uas.hpp

Return to documentation for file (include/mavros/mavros_uas.hpp)

/*
 * Copyright 2014,2015,2016,2017,2021 Vladimir Ermakov.
 *
 * This file is part of the mavros package and subject to the license terms
 * in the top-level LICENSE file of the mavros repository.
 * https://github.com/mavlink/mavros/tree/master/LICENSE.md
 */
#pragma once

#ifndef MAVROS__MAVROS_UAS_HPP_
#define MAVROS__MAVROS_UAS_HPP_

#include <tf2_ros/buffer.h>                         // NOLINT
#include <tf2_ros/transform_listener.h>             // NOLINT
#include <tf2_ros/transform_broadcaster.h>          // NOLINT
#include <tf2_ros/static_transform_broadcaster.h>   // NOLINT

#include <array>
#include <atomic>
#include <memory>
#include <type_traits>
#include <string>
#include <vector>
#include <unordered_map>

#include "diagnostic_updater/diagnostic_updater.hpp"
#include "mavconn/interface.hpp"
#include "pluginlib/class_loader.hpp"
#include "rclcpp/rclcpp.hpp"
#include "GeographicLib/Geoid.hpp"
#include "sensor_msgs/msg/imu.hpp"
#include "sensor_msgs/msg/nav_sat_fix.hpp"

#include "mavros/utils.hpp"
#include "mavros/plugin.hpp"
#include "mavros/frame_tf.hpp"
#include "mavros/uas_executor.hpp"

namespace mavros
{
namespace uas
{

static constexpr auto MAV_COMP_ID_ONBOARD_COMPUTER = 191;

using s_unique_lock = std::unique_lock<std::shared_timed_mutex>;
using s_shared_lock = std::shared_lock<std::shared_timed_mutex>;

// common enums used by UAS
using MAV_TYPE = mavlink::minimal::MAV_TYPE;
using MAV_AUTOPILOT = mavlink::minimal::MAV_AUTOPILOT;
using MAV_MODE_FLAG = mavlink::minimal::MAV_MODE_FLAG;
using MAV_STATE = mavlink::minimal::MAV_STATE;
using MAV_CAP = mavlink::common::MAV_PROTOCOL_CAPABILITY;
using timesync_mode = utils::timesync_mode;


class Data
{
public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  Data();
  ~Data() = default;

  /* -*- IMU data -*- */

  void update_attitude_imu_enu(const sensor_msgs::msg::Imu & imu);

  void update_attitude_imu_ned(const sensor_msgs::msg::Imu & imu);

  sensor_msgs::msg::Imu get_attitude_imu_enu();

  sensor_msgs::msg::Imu get_attitude_imu_ned();

  geometry_msgs::msg::Quaternion get_attitude_orientation_enu();

  geometry_msgs::msg::Quaternion get_attitude_orientation_ned();

  geometry_msgs::msg::Vector3 get_attitude_angular_velocity_enu();

  geometry_msgs::msg::Vector3 get_attitude_angular_velocity_ned();


  /* -*- GPS data -*- */

  void update_gps_fix_epts(
    const sensor_msgs::msg::NavSatFix & fix,
    float eph, float epv,
    int fix_type, int satellites_visible);

  void get_gps_epts(float & eph, float & epv, int & fix_type, int & satellites_visible);

  sensor_msgs::msg::NavSatFix get_gps_fix();

  /* -*- GograpticLib utils -*- */

  static std::shared_ptr<GeographicLib::Geoid> egm96_5;

  template<class T, std::enable_if_t<std::is_pointer<T>::value, bool> = true>
  inline double geoid_to_ellipsoid_height(const T lla)
  {
    if (egm96_5) {
      return egm96_5->ConvertHeight(lla->latitude, lla->longitude, 0.0,
            GeographicLib::Geoid::GEOIDTOELLIPSOID);
    } else {
      return 0.0;
    }
  }

  template<class T, std::enable_if_t<std::is_class<T>::value, bool> = true>
  inline double geoid_to_ellipsoid_height(const T & lla)
  {
    return geoid_to_ellipsoid_height(&lla);
  }

  template<class T, std::enable_if_t<std::is_pointer<T>::value, bool> = true>
  inline double ellipsoid_to_geoid_height(const T lla)
  {
    if (egm96_5) {
      return egm96_5->ConvertHeight(lla->latitude, lla->longitude, 0.0,
            GeographicLib::Geoid::ELLIPSOIDTOGEOID);
    } else {
      return 0.0;
    }
  }

  template<class T, std::enable_if_t<std::is_class<T>::value, bool> = true>
  inline double ellipsoid_to_geoid_height(const T & lla)
  {
    return ellipsoid_to_geoid_height(&lla);
  }

private:
  std::shared_timed_mutex mu;

  sensor_msgs::msg::Imu imu_enu_data;
  sensor_msgs::msg::Imu imu_ned_data;

  sensor_msgs::msg::NavSatFix gps_fix;
  float gps_eph;
  float gps_epv;
  int gps_fix_type;
  int gps_satellites_visible;

  static std::once_flag init_flag;

  static void init_geographiclib();
};

class UAS : public rclcpp::Node
{
public:
  RCLCPP_SMART_PTR_DEFINITIONS(UAS)
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  // other UAS aliases
  using ConnectionCb = std::function<void (bool)>;
  using CapabilitiesCb = std::function<void (MAV_CAP)>;

  using StrV = std::vector<std::string>;

  explicit UAS(const std::string & name_ = "mavros")
  : UAS(rclcpp::NodeOptions(), name_) {}

  explicit UAS(
    const rclcpp::NodeOptions & options_ = rclcpp::NodeOptions(),
    const std::string & name_ = "mavros",
    const std::string & uas_url_ = "/uas1", uint8_t target_system_ = 1,
    uint8_t target_component_ = 1);

  ~UAS() = default;

  diagnostic_updater::Updater diagnostic_updater;

  Data data;

  inline bool is_connected()
  {
    return connected;
  }

  /* -*- HEARTBEAT data -*- */

  void update_heartbeat(uint8_t type_, uint8_t autopilot_, uint8_t base_mode_);

  void update_connection_status(bool conn_);

  void add_connection_change_handler(ConnectionCb cb);

  inline MAV_TYPE get_type()
  {
    std::underlying_type<MAV_TYPE>::type type_ = type;
    return static_cast<MAV_TYPE>(type_);
  }

  inline MAV_AUTOPILOT get_autopilot()
  {
    std::underlying_type<MAV_AUTOPILOT>::type autopilot_ = autopilot;
    return static_cast<MAV_AUTOPILOT>(autopilot_);
  }

  inline bool get_armed()
  {
    uint8_t base_mode_ = base_mode;
    return base_mode_ & utils::enum_value(MAV_MODE_FLAG::SAFETY_ARMED);
  }

  inline bool get_hil_state()
  {
    uint8_t base_mode_ = base_mode;
    return base_mode_ & utils::enum_value(MAV_MODE_FLAG::HIL_ENABLED);
  }

  /* -*- FCU target id pair -*- */

  inline uint8_t get_tgt_system()
  {
    return target_system;
  }

  inline uint8_t get_tgt_component()
  {
    return target_component;
  }

  inline void set_tgt(uint8_t sys, uint8_t comp)
  {
    target_system = sys;
    target_component = comp;
  }

  /* -*- transform -*- */

  tf2_ros::Buffer tf2_buffer;
  tf2_ros::TransformListener tf2_listener;
  tf2_ros::TransformBroadcaster tf2_broadcaster;
  tf2_ros::StaticTransformBroadcaster tf2_static_broadcaster;

  void add_static_transform(
    const std::string & frame_id, const std::string & child_id,
    const Eigen::Affine3d & tr,
    std::vector<geometry_msgs::msg::TransformStamped> & vector);

  void publish_static_transform(
    const std::string & frame_id, const std::string & child_id,
    const Eigen::Affine3d & tr);

  /* -*- time sync -*- */

  inline void set_time_offset(uint64_t offset_ns)
  {
    time_offset = offset_ns;
  }

  inline uint64_t get_time_offset(void)
  {
    return time_offset;
  }

  inline void set_timesync_mode(timesync_mode mode)
  {
    tsync_mode = mode;
  }

  inline timesync_mode get_timesync_mode(void)
  {
    return tsync_mode;
  }

  rclcpp::Time synchronise_stamp(uint32_t time_boot_ms);
  rclcpp::Time synchronise_stamp(uint64_t time_usec);

  template<typename T>
  inline std_msgs::msg::Header synchronized_header(const std::string & frame_id, const T time_stamp)
  {
    std_msgs::msg::Header out;
    out.frame_id = frame_id;
    out.stamp = synchronise_stamp(time_stamp);
    return out;
  }

  /* -*- autopilot version -*- */

  uint64_t get_capabilities();

  template<typename T>
  bool has_capability(T capability)
  {
    static_assert(
      std::is_enum<T>::value,
      "Only query capabilities using the UAS::MAV_CAP enum.");
    return get_capabilities() & utils::enum_value(capability);
  }

  template<typename ... Ts>
  bool has_capabilities(Ts ... capabilities)
  {
    bool ret = true;
    std::initializer_list<bool> capabilities_list {has_capability<Ts>(capabilities) ...};
    for (auto has_cap : capabilities_list) {
      ret &= has_cap;
    }
    return ret;
  }

  void update_capabilities(bool known, uint64_t caps = 0);

  void add_capabilities_change_handler(CapabilitiesCb cb);

  /* -*- utils -*- */

  template<typename _T>
  inline void msg_set_target(_T & msg)
  {
    msg.target_system = get_tgt_system();
    msg.target_component = get_tgt_component();
  }

  inline bool is_my_target(uint8_t sysid, uint8_t compid)
  {
    return sysid == get_tgt_system() && compid == get_tgt_component();
  }

  inline bool is_my_target(uint8_t sysid)
  {
    return sysid == get_tgt_system();
  }

  inline bool is_ardupilotmega()
  {
    return MAV_AUTOPILOT::ARDUPILOTMEGA == get_autopilot();
  }

  inline bool is_px4()
  {
    return MAV_AUTOPILOT::PX4 == get_autopilot();
  }

  std::string str_mode_v10(uint8_t base_mode, uint32_t custom_mode);

  bool cmode_from_str(std::string cmode_str, uint32_t & custom_mode);

  inline void send_message(const mavlink::Message & msg)
  {
    send_message(msg, source_component);
  }

  void send_message(const mavlink::Message & msg, const uint8_t src_compid);

  void set_protocol_version(mavconn::Protocol ver);

private:
  friend class TestUAS;

  // params
  uint8_t source_system;
  uint8_t source_component;
  uint8_t target_system;
  uint8_t target_component;

  std::string uas_url;

  std::string base_link_frame_id;
  std::string odom_frame_id;
  std::string map_frame_id;

  StrV plugin_allowlist;
  StrV plugin_denylist;

  rclcpp::Node::OnSetParametersCallbackHandle::SharedPtr set_parameters_handle_ptr;
  rclcpp::TimerBase::SharedPtr startup_delay_timer;

  // XXX(vooon): we have to use own executor because Node::create_sub_node() doesn't work for us.
  using thread_ptr = std::unique_ptr<std::thread, std::function<void (std::thread *)>>;
  thread_ptr exec_spin_thd;
  // rclcpp::executors::MultiThreadedExecutor exec;
  UASExecutor exec;

  // plugins
  pluginlib::ClassLoader<plugin::PluginFactory> plugin_factory_loader;
  std::vector<plugin::Plugin::SharedPtr> loaded_plugins;

  std::unordered_map<mavlink::msgid_t, plugin::Plugin::Subscriptions> plugin_subscriptions;

  std::shared_timed_mutex mu;

  // essential data
  std::atomic<uint8_t> type;
  std::atomic<uint8_t> autopilot;
  std::atomic<uint8_t> base_mode;
  std::atomic<bool> fcu_caps_known;
  std::atomic<uint64_t> fcu_capabilities;

  std::atomic<bool> connected;
  std::vector<ConnectionCb> connection_cb_vec;
  std::vector<CapabilitiesCb> capabilities_cb_vec;

  std::atomic<uint64_t> time_offset;
  timesync_mode tsync_mode;

  // UAS -> Router connection
  mavlink::mavlink_status_t mavlink_status;
  rclcpp::Subscription<mavros_msgs::msg::Mavlink>::SharedPtr source;    // FCU -> UAS
  rclcpp::Publisher<mavros_msgs::msg::Mavlink>::SharedPtr sink;         // UAS -> FCU

  void connect_to_router();

  void recv_message(const mavros_msgs::msg::Mavlink::SharedPtr rmsg);

  void plugin_route(const mavlink::mavlink_message_t * mmsg, const mavconn::Framing framing);

  bool is_plugin_allowed(const std::string & pl_name);

  virtual plugin::Plugin::SharedPtr create_plugin_instance(const std::string & pl_name);

  void add_plugin(const std::string & pl_name);

  rcl_interfaces::msg::SetParametersResult on_set_parameters_cb(
    const std::vector<rclcpp::Parameter> & parameters);

  void log_connect_change(bool connected);

  void diag_run(diagnostic_updater::DiagnosticStatusWrapper & stat);
};
}              // namespace uas
}       // namespace mavros

#endif  // MAVROS__MAVROS_UAS_HPP_