Program Listing for File lift_common.hpp

Return to documentation for file (/tmp/ws/src/rmf_simulation/rmf_building_sim_common/include/rmf_building_sim_common/lift_common.hpp)

/*
 * Copyright (C) 2020 Open Source Robotics Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

#ifndef RMF_BUILDING_SIM_COMMON__LIFT_COMMON_HPP
#define RMF_BUILDING_SIM_COMMON__LIFT_COMMON_HPP

#include <rclcpp/rclcpp.hpp>
#include <rclcpp/logger.hpp>

#include <rmf_lift_msgs/msg/lift_state.hpp>
#include <rmf_lift_msgs/msg/lift_request.hpp>
#include <rmf_door_msgs/msg/door_mode.hpp>
#include <rmf_door_msgs/msg/door_state.hpp>
#include <rmf_door_msgs/msg/door_request.hpp>

#include "utils.hpp"

#include <vector>
#include <utility>
#include <unordered_map>

namespace rmf_building_sim_common {

using LiftState = rmf_lift_msgs::msg::LiftState;
using LiftRequest = rmf_lift_msgs::msg::LiftRequest;
using DoorRequest = rmf_door_msgs::msg::DoorRequest;
using DoorState = rmf_door_msgs::msg::DoorState;
using DoorMode = rmf_door_msgs::msg::DoorMode;

//==============================================================================
class LiftCommon
{

public:

  struct LiftUpdateResult
  {
    double velocity;
    double fmax;
  };

  template<typename SdfPtrT>
  static std::unique_ptr<LiftCommon> make(
    const std::string& lift_name,
    rclcpp::Node::SharedPtr node,
    SdfPtrT& sdf);

  rclcpp::Logger logger() const;

  LiftUpdateResult update(const double time, const double position,
    const double velocity);

  std::string get_joint_name() const;

  double get_elevation() const;

  bool motion_state_changed();

private:

  rclcpp::Node::SharedPtr _ros_node;
  rclcpp::Publisher<LiftState>::SharedPtr _lift_state_pub;
  rclcpp::Publisher<DoorRequest>::SharedPtr _door_request_pub;
  rclcpp::Subscription<LiftRequest>::SharedPtr _lift_request_sub;
  rclcpp::Subscription<DoorState>::SharedPtr _door_state_sub;

  std::string _lift_name;
  std::string _cabin_joint_name;

  MotionParams _cabin_motion_params;
  LiftState::_motion_state_type _old_motion_state;

  std::vector<std::string> _floor_names;
  std::unordered_map<std::string, double> _floor_name_to_elevation;
  std::unordered_map<std::string,
    std::vector<std::string>> _floor_name_to_shaft_door_name;
  std::unordered_map<std::string,
    std::vector<std::string>> _floor_name_to_cabin_door_name;
  std::unordered_map<std::string, DoorState::SharedPtr> _shaft_door_states;
  std::unordered_map<std::string, DoorState::SharedPtr> _cabin_door_states;

  LiftState _lift_state;
  LiftRequest::UniquePtr _lift_request;

  double _last_update_time = 0.0;
  // random start time offset to prevent state message crossfire
  double _last_pub_time = ((double) std::rand()) / ((double) (RAND_MAX));

  void publish_door_request(const double time, std::string door_name,
    uint32_t door_state);

  LiftCommon(rclcpp::Node::SharedPtr node,
    const std::string& lift_name,
    const std::string& joint_name,
    const MotionParams& cabin_motion_params,
    const std::vector<std::string>& floor_names,
    const std::unordered_map<std::string, double>& floor_name_to_elevation,
    std::unordered_map<
      std::string, std::vector<std::string>> floor_name_to_shaft_door_name,
    std::unordered_map<
      std::string, std::vector<std::string>> floor_name_to_cabin_door_name,
    std::unordered_map<std::string, DoorState::SharedPtr> shaft_door_states,
    std::unordered_map<std::string, DoorState::SharedPtr> cabin_door_states,
    std::string initial_floor_name);

  double get_step_velocity(const double dt, const double position,
    const double velocity);

  void update_cabin_state(const double position, const double velocity);

  void move_doors(const double time, uint32_t door_mode);

  void open_doors(const double time);

  void close_doors(const double time);

  uint32_t get_door_state(
    const std::unordered_map<std::string,
    std::vector<std::string>>& floor_to_door_map,
    const std::unordered_map<std::string, DoorState::SharedPtr>& door_states);

  void pub_lift_state(const double time);

  void update_lift_door_state();

};

template<typename SdfPtrT>
std::unique_ptr<LiftCommon> LiftCommon::make(
  const std::string& lift_name,
  rclcpp::Node::SharedPtr node,
  SdfPtrT& sdf)
{
  MotionParams cabin_motion_params;
  std::string joint_name;
  std::vector<std::string> floor_names;
  std::unordered_map<std::string, double> floor_name_to_elevation;
  std::unordered_map<std::string,
    std::vector<std::string>> floor_name_to_shaft_door_name;
  std::unordered_map<std::string,
    std::vector<std::string>> floor_name_to_cabin_door_name;
  std::unordered_map<std::string, DoorState::SharedPtr> shaft_door_states;
  std::unordered_map<std::string, DoorState::SharedPtr> cabin_door_states;


  auto sdf_clone = sdf->Clone();

  // load lift cabin motion parameters
  get_sdf_param_if_available<double>(sdf_clone, "v_max_cabin",
    cabin_motion_params.v_max);
  get_sdf_param_if_available<double>(sdf_clone, "a_max_cabin",
    cabin_motion_params.a_max);
  get_sdf_param_if_available<double>(sdf_clone, "a_nom_cabin",
    cabin_motion_params.a_nom);
  get_sdf_param_if_available<double>(sdf_clone, "dx_min_cabin",
    cabin_motion_params.dx_min);
  get_sdf_param_if_available<double>(sdf_clone, "f_max_cabin",
    cabin_motion_params.f_max);
  if (!get_sdf_param_required(sdf_clone, "cabin_joint_name",
    joint_name))
    return nullptr;

  // load the floor name and elevation for each floor
  auto floor_element = sdf_clone;
  if (!get_element_required(sdf, "floor", floor_element))
  {
    RCLCPP_ERROR(node->get_logger(),
      " -- Missing required floor element for [%s] plugin",
      lift_name.c_str());
    return nullptr;
  }

  while (floor_element)
  {
    std::string floor_name;
    double floor_elevation;
    if (!get_sdf_attribute_required<std::string>(floor_element, "name",
      floor_name) ||
      !get_sdf_attribute_required<double>(floor_element, "elevation",
      floor_elevation))
    {
      RCLCPP_ERROR(
        node->get_logger(),
        " -- Missing required floor name or elevation attributes for [%s] plugin",
        lift_name.c_str());
      return nullptr;
    }
    floor_names.push_back(floor_name);
    floor_name_to_elevation.insert({floor_name, floor_elevation});

    auto door_pair_element = floor_element;
    if (get_element_required(floor_element, "door_pair", door_pair_element))
    {
      while (door_pair_element)
      {
        std::string shaft_door_name;
        std::string cabin_door_name;
        if (!get_sdf_attribute_required<std::string>(door_pair_element,
          "cabin_door", cabin_door_name) ||
          !get_sdf_attribute_required<std::string>(door_pair_element,
          "shaft_door", shaft_door_name))
        {
          RCLCPP_ERROR(node->get_logger(),
            " -- Missing required lift door attributes for [%s] plugin",
            lift_name.c_str());
          return nullptr;
        }
        floor_name_to_cabin_door_name[floor_name].push_back(cabin_door_name);
        floor_name_to_shaft_door_name[floor_name].push_back(shaft_door_name);
        shaft_door_states.insert({shaft_door_name, nullptr});
        cabin_door_states.insert({cabin_door_name, nullptr});

        door_pair_element = door_pair_element->GetNextElement("door_pair");
      }
    }
    floor_element = floor_element->GetNextElement("floor");
  }

  assert(!floor_names.empty());
  std::string initial_floor_name = floor_names[0];
  get_sdf_param_if_available<std::string>(sdf_clone, "initial_floor",
    initial_floor_name);

  if (std::find(floor_names.begin(), floor_names.end(), initial_floor_name) ==
    floor_names.end())
  {
    RCLCPP_WARN(
      node->get_logger(),
      "Initial floor [%s] is not available, changing to deafult",
      initial_floor_name.c_str());
    initial_floor_name = floor_names[0];
  }

  std::unique_ptr<LiftCommon> lift(new LiftCommon(
      node,
      lift_name,
      joint_name,
      cabin_motion_params,
      floor_names,
      floor_name_to_elevation,
      floor_name_to_shaft_door_name,
      floor_name_to_cabin_door_name,
      shaft_door_states,
      cabin_door_states,
      initial_floor_name));

  return lift;
}

} // namespace rmf_building_sim_common

#endif // RMF_BUILDING_SIM_COMMON__LIFT_COMMON_HPP