Program Listing for File docking_panel.hpp

Return to documentation for file (include/nav2_rviz_plugins/docking_panel.hpp)

// Copyright (c) 2024 Alberto J. Tudela Roldán
//
// 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 NAV2_RVIZ_PLUGINS__DOCKING_PANEL_HPP_
#define NAV2_RVIZ_PLUGINS__DOCKING_PANEL_HPP_

// QT
#include <QtWidgets>
#include <QBasicTimer>

#include <string>

// ROS
#include "nav2_lifecycle_manager/lifecycle_manager_client.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp_action/rclcpp_action.hpp"
#include "rviz_common/panel.hpp"
#include "sensor_msgs/msg/battery_state.hpp"
#include "nav2_msgs/action/dock_robot.hpp"
#include "nav2_msgs/action/undock_robot.hpp"

class QLineEdit;
class QPushButton;

namespace nav2_rviz_plugins
{

class InitialDockThread;

class DockingPanel : public rviz_common::Panel
{
  Q_OBJECT

public:
  explicit DockingPanel(QWidget * parent = 0);
  virtual ~DockingPanel();

  void onInitialize() override;

  void load(const rviz_common::Config & config) override;
  void save(rviz_common::Config config) const override;

private Q_SLOTS:
  void startThread();
  void onStartup();
  void onDockingButtonPressed();
  void onUndockingButtonPressed();
  void onCancelDocking();
  void onCancelUndocking();
  void dockIdCheckbox();

private:
  using Dock = nav2_msgs::action::DockRobot;
  using Undock = nav2_msgs::action::UndockRobot;
  using DockGoalHandle = rclcpp_action::ClientGoalHandle<Dock>;
  using UndockGoalHandle = rclcpp_action::ClientGoalHandle<Undock>;

  // The (non-spinning) client node used to invoke the action client
  void timerEvent(QTimerEvent * event) override;

  // Create label string from feedback msg
  static inline QString getDockFeedbackLabel(Dock::Feedback msg = Dock::Feedback());

  // Create label string from status msg
  template<typename T>
  static inline std::string toLabel(T & msg);

  // Round off double to the specified precision and convert to string
  static inline std::string toString(double val, int precision = 0);

  // Convert the dock state and error code to string
  static inline std::string dockStateToString(int16_t state);
  static inline std::string dockErrorToString(int16_t error_code);

  // The (non-spinning) client node used to invoke the action client
  rclcpp::Node::SharedPtr client_node_;

  // Timeout value when waiting for action servers to respond
  std::chrono::milliseconds server_timeout_;

  // A timer used to check on the completion status of the action
  QBasicTimer action_timer_;

  // The Dock and Undock action client
  rclcpp_action::Client<Dock>::SharedPtr dock_client_;
  rclcpp_action::Client<Undock>::SharedPtr undock_client_;

  // Docking / Undocking action feedback subscribers
  rclcpp::Subscription<Dock::Impl::FeedbackMessage>::SharedPtr docking_feedback_sub_;
  rclcpp::Subscription<Undock::Impl::FeedbackMessage>::SharedPtr undocking_feedback_sub_;
  rclcpp::Subscription<Dock::Impl::GoalStatusMessage>::SharedPtr docking_goal_status_sub_;
  rclcpp::Subscription<Undock::Impl::GoalStatusMessage>::SharedPtr undocking_goal_status_sub_;

  // Goal related state
  DockGoalHandle::SharedPtr dock_goal_handle_;
  UndockGoalHandle::SharedPtr undock_goal_handle_;

  // Flags to indicate if the plugins have been loaded
  bool plugins_loaded_ = false;
  bool server_failed_ = false;

  QVBoxLayout * main_layout_{nullptr};
  QHBoxLayout * info_layout_{nullptr};
  QVBoxLayout * feedback_layout_{nullptr};
  QHBoxLayout * dock_id_layout_{nullptr};
  QHBoxLayout * dock_type_layout_{nullptr};
  QHBoxLayout * dock_pose_layout_{nullptr};
  QHBoxLayout * nav_stage_layout_{nullptr};

  QComboBox * dock_type_{nullptr};
  QPushButton * docking_button_{nullptr};
  QPushButton * undocking_button_{nullptr};
  QCheckBox * use_dock_id_checkbox_{nullptr};
  QCheckBox * nav_to_staging_checkbox_{nullptr};

  QLabel * docking_goal_status_indicator_{nullptr};
  QLabel * docking_feedback_indicator_{nullptr};
  QLabel * docking_result_indicator_{nullptr};

  QLineEdit * dock_id_{nullptr};
  QLineEdit * dock_pose_x_{nullptr};
  QLineEdit * dock_pose_y_{nullptr};
  QLineEdit * dock_pose_yaw_{nullptr};

  // The current state of the docking and undocking actions
  bool docking_in_progress_ = false;
  bool undocking_in_progress_ = false;
  bool use_dock_id_ = false;

  QStateMachine state_machine_;
  InitialDockThread * initial_thread_;

  QState * pre_initial_{nullptr};
  QState * idle_{nullptr};
  QState * docking_{nullptr};
  QState * undocking_{nullptr};
  QState * canceled_docking_{nullptr};
  QState * canceled_undocking_{nullptr};
};

class InitialDockThread : public QThread
{
  Q_OBJECT

public:
  explicit InitialDockThread(
    rclcpp_action::Client<nav2_msgs::action::DockRobot>::SharedPtr & dock_client,
    rclcpp_action::Client<nav2_msgs::action::UndockRobot>::SharedPtr & undock_client)
  : dock_client_(dock_client), undock_client_(undock_client)
  {}

  void run() override
  {
    while (!dock_active_) {
      dock_active_ = dock_client_->wait_for_action_server(std::chrono::seconds(1));
    }

    while (!undock_active_) {
      undock_active_ = undock_client_->wait_for_action_server(std::chrono::seconds(1));
    }

    if (dock_active_) {
      emit dockingActive();
    } else {
      emit dockingInactive();
    }

    if (undock_active_) {
      emit undockingActive();
    } else {
      emit undockingInactive();
    }
  }

signals:
  void dockingActive();
  void dockingInactive();
  void undockingActive();
  void undockingInactive();

private:
  rclcpp_action::Client<nav2_msgs::action::DockRobot>::SharedPtr dock_client_;
  rclcpp_action::Client<nav2_msgs::action::UndockRobot>::SharedPtr undock_client_;
  bool dock_active_ = false;
  bool undock_active_ = false;
};

}  // namespace nav2_rviz_plugins

#endif  // NAV2_RVIZ_PLUGINS__DOCKING_PANEL_HPP_