Program Listing for File go_to_behavior.cpp

Return to documentation for file (/tmp/ws/src/aerostack2/as2_behaviors/as2_behaviors_motion/go_to_behavior/src/go_to_behavior.cpp)

// Copyright 2024 Universidad Politécnica de Madrid
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//    * Redistributions of source code must retain the above copyright
//      notice, this list of conditions and the following disclaimer.
//
//    * Redistributions in binary form must reproduce the above copyright
//      notice, this list of conditions and the following disclaimer in the
//      documentation and/or other materials provided with the distribution.
//
//    * Neither the name of the Universidad Politécnica de Madrid nor the names of its
//      contributors may be used to endorse or promote products derived from
//      this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include "go_to_behavior/go_to_behavior.hpp"

GoToBehavior::GoToBehavior(const rclcpp::NodeOptions & options)
: as2_behavior::BehaviorServer<as2_msgs::action::GoToWaypoint>(
    as2_names::actions::behaviors::gotowaypoint,
    options)
{
  try {
    this->declare_parameter<std::string>("plugin_name");
  } catch (const rclcpp::ParameterTypeException & e) {
    RCLCPP_FATAL(
      this->get_logger(), "Launch argument <plugin_name> not defined or malformed: %s",
      e.what());
    this->~GoToBehavior();
  }
  try {
    this->declare_parameter<double>("go_to_speed");
  } catch (const rclcpp::ParameterTypeException & e) {
    RCLCPP_FATAL(
      this->get_logger(),
      "Launch argument <go_to_speed> not defined or "
      "malformed: %s",
      e.what());
    this->~GoToBehavior();
  }
  try {
    this->declare_parameter<double>("go_to_threshold");
  } catch (const rclcpp::ParameterTypeException & e) {
    RCLCPP_FATAL(
      this->get_logger(),
      "Launch argument <go_to_threshold> not defined or malformed: %s", e.what());
    this->~GoToBehavior();
  }
  try {
    this->declare_parameter<double>("tf_timeout_threshold");
  } catch (const rclcpp::ParameterTypeException & e) {
    RCLCPP_FATAL(
      this->get_logger(),
      "Launch argument <tf_timeout_threshold> not defined or malformed: %s", e.what());
    this->~GoToBehavior();
  }

  loader_ = std::make_shared<pluginlib::ClassLoader<go_to_base::GoToBase>>(
    "as2_behaviors_motion",
    "go_to_base::GoToBase");

  tf_handler_ = std::make_shared<as2::tf::TfHandler>(this);

  try {
    std::string plugin_name = this->get_parameter("plugin_name").as_string();
    plugin_name += "::Plugin";
    go_to_plugin_ = loader_->createSharedInstance(plugin_name);

    go_to_base::go_to_plugin_params params;
    params.go_to_speed = this->get_parameter("go_to_speed").as_double();
    params.go_to_threshold = this->get_parameter("go_to_threshold").as_double();
    params.tf_timeout_threshold = this->get_parameter("tf_timeout_threshold").as_double();
    tf_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(
      std::chrono::duration<double>(params.tf_timeout_threshold));
    go_to_plugin_->initialize(this, tf_handler_, params);

    RCLCPP_INFO(this->get_logger(), "GO TO BEHAVIOR PLUGIN LOADED: %s", plugin_name.c_str());
  } catch (pluginlib::PluginlibException & ex) {
    RCLCPP_ERROR(
      this->get_logger(), "The plugin failed to load for some reason. Error: %s\n",
      ex.what());
    this->~GoToBehavior();
  }

  base_link_frame_id_ = as2::tf::generateTfName(this, "base_link");

  platform_info_sub_ = this->create_subscription<as2_msgs::msg::PlatformInfo>(
    as2_names::topics::platform::info, as2_names::topics::platform::qos,
    std::bind(&GoToBehavior::platform_info_callback, this, std::placeholders::_1));

  twist_sub_ = this->create_subscription<geometry_msgs::msg::TwistStamped>(
    as2_names::topics::self_localization::twist, as2_names::topics::self_localization::qos,
    std::bind(&GoToBehavior::state_callback, this, std::placeholders::_1));

  RCLCPP_DEBUG(this->get_logger(), "GoToWaypoint Behavior ready!");
}

GoToBehavior::~GoToBehavior() {}

void GoToBehavior::state_callback(const geometry_msgs::msg::TwistStamped::SharedPtr _twist_msg)
{
  try {
    auto [pose_msg, twist_msg] =
      tf_handler_->getState(*_twist_msg, "earth", "earth", base_link_frame_id_, tf_timeout);
    go_to_plugin_->state_callback(pose_msg, twist_msg);
  } catch (tf2::TransformException & ex) {
    RCLCPP_WARN(this->get_logger(), "Could not get transform: %s", ex.what());
  }
  return;
}

void GoToBehavior::platform_info_callback(const as2_msgs::msg::PlatformInfo::SharedPtr msg)
{
  go_to_plugin_->platform_info_callback(msg);
  return;
}

bool GoToBehavior::process_goal(
  std::shared_ptr<const as2_msgs::action::GoToWaypoint::Goal> goal,
  as2_msgs::action::GoToWaypoint::Goal & new_goal)
{
  if (goal->target_pose.header.frame_id == "") {
    RCLCPP_ERROR(this->get_logger(), "Target pose frame_id is empty");
    return false;
  }

  if ((fabs(new_goal.target_pose.point.x) + fabs(new_goal.target_pose.point.y) +
    fabs(new_goal.target_pose.point.z)) == 0.0f)
  {
    RCLCPP_WARN(this->get_logger(), "GoToBehavior: Target point is zero");
  } else if (new_goal.target_pose.point.z <= 0.0f) {
    RCLCPP_WARN(this->get_logger(), "GoToBehavior: Target height is below 0.0");
  }

  if (!tf_handler_->tryConvert(new_goal.target_pose, "earth", tf_timeout)) {
    RCLCPP_ERROR(this->get_logger(), "GoToBehavior: can not get target position in earth frame");
    return false;
  }

  geometry_msgs::msg::QuaternionStamped q;
  q.header = goal->target_pose.header;
  as2::frame::eulerToQuaternion(0.0f, 0.0f, new_goal.yaw.angle, q.quaternion);

  if (!tf_handler_->tryConvert(q, "earth", tf_timeout)) {
    RCLCPP_ERROR(this->get_logger(), "GoToBehavior: can not get target orientation in earth frame");
    return false;
  }

  new_goal.yaw.angle = as2::frame::getYawFromQuaternion(q.quaternion);

  new_goal.max_speed =
    (goal->max_speed != 0.0f) ? goal->max_speed : this->get_parameter("go_to_speed").as_double();

  return true;
}

bool GoToBehavior::on_activate(std::shared_ptr<const as2_msgs::action::GoToWaypoint::Goal> goal)
{
  as2_msgs::action::GoToWaypoint::Goal new_goal = *goal;
  if (!process_goal(goal, new_goal)) {
    return false;
  }
  return go_to_plugin_->on_activate(
    std::make_shared<const as2_msgs::action::GoToWaypoint::Goal>(new_goal));
}

bool GoToBehavior::on_modify(std::shared_ptr<const as2_msgs::action::GoToWaypoint::Goal> goal)
{
  as2_msgs::action::GoToWaypoint::Goal new_goal = *goal;
  if (!process_goal(goal, new_goal)) {
    return false;
  }
  return go_to_plugin_->on_modify(
    std::make_shared<const as2_msgs::action::GoToWaypoint::Goal>(new_goal));
}

bool GoToBehavior::on_deactivate(const std::shared_ptr<std::string> & message)
{
  return go_to_plugin_->on_deactivate(message);
}

bool GoToBehavior::on_pause(const std::shared_ptr<std::string> & message)
{
  return go_to_plugin_->on_pause(message);
}

bool GoToBehavior::on_resume(const std::shared_ptr<std::string> & message)
{
  return go_to_plugin_->on_resume(message);
}

as2_behavior::ExecutionStatus GoToBehavior::on_run(
  const std::shared_ptr<const as2_msgs::action::GoToWaypoint::Goal> & goal,
  std::shared_ptr<as2_msgs::action::GoToWaypoint::Feedback> & feedback_msg,
  std::shared_ptr<as2_msgs::action::GoToWaypoint::Result> & result_msg)
{
  return go_to_plugin_->on_run(goal, feedback_msg, result_msg);
}

void GoToBehavior::on_execution_end(const as2_behavior::ExecutionStatus & state)
{
  return go_to_plugin_->on_execution_end(state);
}

#include "rclcpp_components/register_node_macro.hpp"

// Register the component with class_loader.
// This acts as a sort of entry point, allowing the component to be discoverable when its library
// is being loaded into a running process.
RCLCPP_COMPONENTS_REGISTER_NODE(GoToBehavior)