Program Listing for File controller_manager.cpp

Return to documentation for file (/tmp/ws/src/aerostack2/as2_motion_controller/src/controller_manager.cpp)

// Copyright 2023 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.

/*!*******************************************************************************************
 *  \file       controller_manager.cpp
 *  \brief      Controller manager class implementation
 *  \authors    Miguel Fernández Cortizas
 *              Rafael Pérez Seguí
 ********************************************************************************************/

#include "as2_motion_controller/controller_manager.hpp"

namespace controller_manager
{

ControllerManager::ControllerManager(const rclcpp::NodeOptions & options)
: as2::Node("controller_manager", get_modified_options(options))
{
  try {
    this->get_parameter("plugin_name", plugin_name_);
  } catch (const rclcpp::ParameterTypeException & e) {
    RCLCPP_FATAL(
      this->get_logger(), "Launch argument <plugin_name> not defined or malformed: %s",
      e.what());
    this->~ControllerManager();
  }

  this->get_parameter("cmd_freq", cmd_freq_);
  if (cmd_freq_ <= 0.0f) {
    RCLCPP_ERROR(this->get_logger(), "Param cmd_freq must be greater than 0.0");
    assert(info_freq_ > 0.0f);
    return;
  }

  this->get_parameter("info_freq", info_freq_);
  if (info_freq_ <= 0.0f) {
    RCLCPP_ERROR(this->get_logger(), "Param info_freq must be greater than 0.0");
    assert(info_freq_ > 0.0f);
    return;
  }

  plugin_name_ += "::Plugin";
  // this->get_parameter("plugin_config_file", parameter_string_);
  this->get_parameter("plugin_available_modes_config_file", available_modes_config_file_);

  loader_ =
    std::make_shared<pluginlib::ClassLoader<as2_motion_controller_plugin_base::ControllerBase>>(
    "as2_motion_controller", "as2_motion_controller_plugin_base::ControllerBase");
  try {
    controller_ = loader_->createSharedInstance(plugin_name_);
    controller_->initialize(this);
    controller_->reset();
    auto parameters = this->list_parameters({}, 0);
    std::vector<rclcpp::Parameter> params;
    params.reserve(parameters.names.size());
    for (const auto & param : parameters.names) {
      params.emplace_back(this->get_parameter(param));
    }
    controller_->updateParams(params);
    controller_handler_ =
      std::make_shared<controller_handler::ControllerHandler>(controller_, this);
    RCLCPP_INFO(this->get_logger(), "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());
    return;
  }

  // controller_handler_->initialize(this);
  if (available_modes_config_file_.empty()) {
    // Get the path of the package
    available_modes_config_file_ = loader_->getPluginManifestPath(plugin_name_);

    // Try search if file available_modes.yaml exists in package_folder/config/
    available_modes_config_file_ =
      available_modes_config_file_.parent_path() / "config" / "available_modes.yaml";

    if (!std::filesystem::exists(available_modes_config_file_)) {
      // Try search if file available_modes.yaml exists in
      // package_folder/plugins/plugin_name/config/
      std::string plugin_name;
      this->get_parameter("plugin_name", plugin_name);
      available_modes_config_file_ = loader_->getPluginManifestPath(plugin_name_);
      available_modes_config_file_ = available_modes_config_file_.parent_path() / "plugins" /
        plugin_name / "config" / "available_modes.yaml";

      if (!std::filesystem::exists(available_modes_config_file_)) {
        RCLCPP_ERROR(
          this->get_logger(),
          "Default modes file available_modes.yaml not found in plugin config folder: %s",
          available_modes_config_file_.c_str());
        return;
      }
    }
  }

  RCLCPP_DEBUG(
    this->get_logger(), "MODES FILE LOADED: %s",
    available_modes_config_file_.parent_path().c_str());

  configAvailableControlModes(available_modes_config_file_.parent_path());

  mode_pub_ = this->create_publisher<as2_msgs::msg::ControllerInfo>(
    as2_names::topics::controller::info, as2_names::topics::controller::qos_info);

  mode_timer_ = this->create_timer(
    std::chrono::duration<double>(1.0f / info_freq_),
    std::bind(&ControllerManager::modeTimerCallback, this));
}

ControllerManager::~ControllerManager() {}

void ControllerManager::configAvailableControlModes(const std::filesystem::path project_path)
{
  auto available_input_modes =
    as2::yaml::parse_uint_from_string(
    as2::yaml::find_tag_from_project_exports_path<std::string>(
      project_path, "input_control_modes"));
  RCLCPP_INFO(this->get_logger(), "==========================================================");
  RCLCPP_INFO(this->get_logger(), "AVAILABLE INPUT MODES: ");
  for (auto mode : available_input_modes) {
    RCLCPP_INFO(
      this->get_logger(), "\t - %s",
      as2::control_mode::controlModeToString(mode).c_str());
  }
  auto available_output_modes =
    as2::yaml::parse_uint_from_string(
    as2::yaml::find_tag_from_project_exports_path<std::string>(
      project_path, "output_control_modes"));
  RCLCPP_INFO(this->get_logger(), "AVAILABLE OUTPUT MODES: ");
  for (auto mode : available_output_modes) {
    RCLCPP_INFO(this->get_logger(), "\t -%s", as2::control_mode::controlModeToString(mode).c_str());
  }

  RCLCPP_INFO(this->get_logger(), "==========================================================");

  controller_handler_->setInputControlModesAvailables(available_input_modes);
  controller_handler_->setOutputControlModesAvailables(available_output_modes);
}

void ControllerManager::modeTimerCallback()
{
  as2_msgs::msg::ControllerInfo msg;
  msg.header.stamp = this->now();
  controller_handler_->getMode(msg.input_control_mode, msg.output_control_mode);
  mode_pub_->publish(msg);
}

rclcpp::NodeOptions ControllerManager::get_modified_options(const rclcpp::NodeOptions & options)
{
  // Create a copy of the options and modify it
  rclcpp::NodeOptions modified_options = options;
  modified_options.allow_undeclared_parameters(true);
  modified_options.automatically_declare_parameters_from_overrides(true);
  return modified_options;
}

}  // namespace controller_manager