17#include "auto_apms_px4/mode.hpp"
18#include "auto_apms_px4/vehicle_command_client.hpp"
19#include "auto_apms_util/action_wrapper.hpp"
20#include "px4_msgs/msg/mode_completed.hpp"
21#include "px4_msgs/msg/vehicle_status.hpp"
22#include "px4_ros2/components/wait_for_fmu.hpp"
88template <
class ActionT>
91 enum class State : uint8_t
95 WAIT_FOR_COMPLETION_SIGNAL,
101 using FlightMode = VehicleCommandClient::FlightMode;
102 using typename auto_apms_util::ActionWrapper<ActionT>::ActionContextType;
103 using typename auto_apms_util::ActionWrapper<ActionT>::Goal;
104 using typename auto_apms_util::ActionWrapper<ActionT>::Feedback;
105 using typename auto_apms_util::ActionWrapper<ActionT>::Result;
108 explicit ModeExecutor(
109 const std::string & action_name, rclcpp::Node::SharedPtr node_ptr,
110 std::shared_ptr<ActionContextType> action_context_ptr, uint8_t mode_id,
bool deactivate_before_completion =
true);
111 explicit ModeExecutor(
112 const std::string & action_name,
const rclcpp::NodeOptions & options, uint8_t mode_id,
113 bool deactivate_before_completion =
true);
114 explicit ModeExecutor(
115 const std::string & action_name,
const rclcpp::NodeOptions & options, FlightMode flight_mode,
116 bool deactivate_before_completion =
true);
121 bool onGoalRequest(std::shared_ptr<const Goal> goal_ptr)
override final;
122 bool onCancelRequest(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr)
override final;
124 std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr)
override final;
126 std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Feedback> feedback_ptr,
127 std::shared_ptr<Result> result_ptr)
override final;
130 bool isCurrentNavState(uint8_t nav_state);
131 virtual bool sendActivationCommand(
const VehicleCommandClient & client, std::shared_ptr<const Goal> goal_ptr);
132 virtual bool isCompleted(std::shared_ptr<const Goal> goal_ptr,
const px4_msgs::msg::VehicleStatus & vehicle_status);
133 virtual void setFeedback(std::shared_ptr<Feedback> feedback_ptr,
const px4_msgs::msg::VehicleStatus & vehicle_status);
135 uint8_t getModeID()
const;
138 const VehicleCommandClient vehicle_command_client_;
139 const uint8_t mode_id_;
140 bool deactivate_before_completion_;
141 rclcpp::Subscription<px4_msgs::msg::VehicleStatus>::SharedPtr vehicle_status_sub_ptr_;
142 rclcpp::Subscription<px4_msgs::msg::ModeCompleted>::SharedPtr mode_completed_sub_ptr_;
143 px4_msgs::msg::VehicleStatus::SharedPtr last_vehicle_status_ptr_;
144 bool mode_completed_{
false};
145 bool deactivation_command_sent_{
false};
146 State state_{State::REQUEST_ACTIVATION};
147 rclcpp::Time activation_command_sent_time_;
148 rclcpp::Duration activation_timeout_{0, 0};
158template <
class ActionT,
class ModeT>
159class ModeExecutorFactory
163 const std::string & action_name,
const rclcpp::NodeOptions & options,
164 const std::string & topic_namespace_prefix =
"",
bool deactivate_before_completion =
true);
166 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface();
169 rclcpp::Node::SharedPtr node_ptr_;
170 std::unique_ptr<ModeBase<ActionT>> mode_ptr_;
171 std::shared_ptr<ModeExecutor<ActionT>> mode_executor_ptr_;
178template <
class ActionT>
179ModeExecutor<ActionT>::ModeExecutor(
180 const std::string & action_name, rclcpp::Node::SharedPtr node_ptr,
181 std::shared_ptr<ActionContextType> action_context_ptr, uint8_t mode_id,
bool deactivate_before_completion)
182:
auto_apms_util::ActionWrapper<ActionT>(action_name, node_ptr, action_context_ptr),
183 vehicle_command_client_(*node_ptr),
185 deactivate_before_completion_(deactivate_before_completion)
190template <
class ActionT>
191ModeExecutor<ActionT>::ModeExecutor(
192 const std::string & action_name,
const rclcpp::NodeOptions & options, uint8_t mode_id,
193 bool deactivate_before_completion)
195 vehicle_command_client_(*this->node_ptr_),
197 deactivate_before_completion_(deactivate_before_completion)
202template <
class ActionT>
203ModeExecutor<ActionT>::ModeExecutor(
204 const std::string & action_name,
const rclcpp::NodeOptions & options, FlightMode flight_mode,
205 bool deactivate_before_completion)
206:
ModeExecutor<ActionT>(action_name, options, static_cast<uint8_t>(flight_mode), deactivate_before_completion)
210template <
class ActionT>
211void ModeExecutor<ActionT>::setUp()
213 vehicle_status_sub_ptr_ = this->node_ptr_->template create_subscription<px4_msgs::msg::VehicleStatus>(
214 "/fmu/out/vehicle_status", rclcpp::QoS(1).best_effort(),
215 [
this](px4_msgs::msg::VehicleStatus::UniquePtr msg) { last_vehicle_status_ptr_ = std::move(msg); });
217 mode_completed_sub_ptr_ = this->node_ptr_->template create_subscription<px4_msgs::msg::ModeCompleted>(
218 "/fmu/out/mode_completed", rclcpp::QoS(1).best_effort(), [
this](px4_msgs::msg::ModeCompleted::UniquePtr msg) {
219 if (msg->nav_state == mode_id_) {
220 if (msg->result == px4_msgs::msg::ModeCompleted::RESULT_SUCCESS) {
221 this->mode_completed_ =
true;
223 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Flight mode %i failed to execute. Aborting...", this->mode_id_);
224 this->action_context_ptr_->abort();
231template <
class ActionT>
237 bool is_holding = isCurrentNavState(
static_cast<uint8_t
>(FlightMode::Hold));
238 if (state_ == State::WAIT_FOR_ACTIVATION) {
240 auto & clock = *this->node_ptr_->get_clock();
241 RCLCPP_DEBUG_THROTTLE(
242 this->node_ptr_->get_logger(), clock, 250,
"Waiting for flight mode %i to become active before deactivating...",
244 return ActionStatus::RUNNING;
246 state_ = State::COMPLETE;
251 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Deactivated flight mode successfully (HOLD is active)");
252 return ActionStatus::SUCCESS;
255 if (!deactivation_command_sent_) {
256 if (!vehicle_command_client_.syncActivateFlightMode(FlightMode::Hold)) {
257 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Failed to send command to activate HOLD");
258 return ActionStatus::FAILURE;
261 last_vehicle_status_ptr_ =
nullptr;
262 deactivation_command_sent_ =
true;
266 return ActionStatus::RUNNING;
269template <
class ActionT>
270bool ModeExecutor<ActionT>::onGoalRequest(
const std::shared_ptr<const Goal> )
272 state_ = State::REQUEST_ACTIVATION;
273 deactivation_command_sent_ =
false;
274 mode_completed_ =
false;
275 activation_timeout_ = rclcpp::Duration::from_seconds(fmin(this->param_listener_.get_params().loop_rate * 15, 1.5));
279template <
class ActionT>
280bool ModeExecutor<ActionT>::onCancelRequest(
281 std::shared_ptr<const Goal> , std::shared_ptr<Result> )
283 if (deactivate_before_completion_) {
286 this->node_ptr_->get_logger(),
"Cancellation requested! Will deactivate before termination (Enter HOLD)...");
288 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Cancellation requested!");
293template <
class ActionT>
295 std::shared_ptr<const Goal> , std::shared_ptr<Result> )
297 if (deactivate_before_completion_) {
298 return asyncDeactivateFlightMode();
300 return ActionStatus::SUCCESS;
303template <
class ActionT>
304bool ModeExecutor<ActionT>::isCurrentNavState(uint8_t nav_state)
306 if (last_vehicle_status_ptr_ && last_vehicle_status_ptr_->nav_state == nav_state) {
312template <
class ActionT>
314 std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Feedback> feedback_ptr, std::shared_ptr<Result> )
317 case State::REQUEST_ACTIVATION:
318 if (!sendActivationCommand(vehicle_command_client_, goal_ptr)) {
320 this->node_ptr_->get_logger(),
"Failed to send activation command for flight mode %i. Aborting...", mode_id_);
321 return ActionStatus::FAILURE;
324 last_vehicle_status_ptr_ =
nullptr;
325 state_ = State::WAIT_FOR_ACTIVATION;
326 activation_command_sent_time_ = this->node_ptr_->now();
328 this->node_ptr_->get_logger(),
"Activation command for flight mode %i was sent successfully", mode_id_);
329 return ActionStatus::RUNNING;
330 case State::WAIT_FOR_ACTIVATION:
331 if (isCurrentNavState(mode_id_)) {
332 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Flight mode %i is active", mode_id_);
333 state_ = State::WAIT_FOR_COMPLETION_SIGNAL;
334 }
else if (this->node_ptr_->now() - activation_command_sent_time_ > activation_timeout_) {
335 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Timeout activating flight mode %i. Aborting...", mode_id_);
336 return ActionStatus::FAILURE;
338 return ActionStatus::RUNNING;
339 case State::WAIT_FOR_COMPLETION_SIGNAL:
341 setFeedback(feedback_ptr, *last_vehicle_status_ptr_);
344 if (isCompleted(goal_ptr, *last_vehicle_status_ptr_)) {
345 state_ = State::COMPLETE;
346 if (deactivate_before_completion_) {
349 this->node_ptr_->get_logger(),
350 "Flight mode %i complete! Will deactivate before termination (Enter HOLD)...", mode_id_);
353 this->node_ptr_->get_logger(),
354 "Flight mode %i complete! Will leave current navigation state as is. User is "
355 "responsible for initiating the next flight mode...",
363 if (!isCurrentNavState(mode_id_)) {
364 RCLCPP_WARN(this->node_ptr_->get_logger(),
"Flight mode %i was deactivated externally. Aborting...", mode_id_);
365 return ActionStatus::FAILURE;
367 return ActionStatus::RUNNING;
368 case State::COMPLETE:
372 if (deactivate_before_completion_) {
373 const auto deactivation_state = asyncDeactivateFlightMode();
374 if (deactivation_state != ActionStatus::SUCCESS) {
375 return deactivation_state;
380 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Flight mode %i execution termination", mode_id_);
381 return ActionStatus::SUCCESS;
384template <
class ActionT>
385bool ModeExecutor<ActionT>::sendActivationCommand(
388 return client.syncActivateFlightMode(mode_id_);
391template <
class ActionT>
392bool ModeExecutor<ActionT>::isCompleted(
393 std::shared_ptr<const Goal> ,
const px4_msgs::msg::VehicleStatus & )
395 return mode_completed_;
398template <
class ActionT>
399void ModeExecutor<ActionT>::setFeedback(
400 std::shared_ptr<Feedback> ,
const px4_msgs::msg::VehicleStatus & )
405template <
class ActionT>
406uint8_t ModeExecutor<ActionT>::getModeID()
const
411template <
class ActionT,
class ModeT>
412ModeExecutorFactory<ActionT, ModeT>::ModeExecutorFactory(
413 const std::string & action_name,
const rclcpp::NodeOptions & options,
const std::string & topic_namespace_prefix,
414 bool deactivate_before_completion)
415: node_ptr_(std::make_shared<rclcpp::Node>(action_name +
"_node", options))
418 std::is_base_of<ModeBase<ActionT>, ModeT>::value,
419 "Template argument ModeT must inherit auto_apms::ModeBase<ActionT> as public and with same type "
420 "ActionT as auto_apms::ActionWrapper<ActionT>");
422 const auto action_context_ptr = std::make_shared<auto_apms_util::ActionContext<ActionT>>(node_ptr_->get_logger());
424 mode_ptr_ = std::make_unique<ModeT>(
425 *node_ptr_, px4_ros2::ModeBase::Settings(
"mode_" + action_name), topic_namespace_prefix, action_context_ptr);
427 if (!px4_ros2::waitForFMU(*node_ptr_, std::chrono::seconds(3))) {
428 throw std::runtime_error(
"No message from FMU");
430 RCLCPP_DEBUG(node_ptr_->get_logger(),
"FMU availability test successful.");
433 if (!mode_ptr_->doRegister()) {
434 RCLCPP_FATAL(node_ptr_->get_logger(),
"Registration of mode with action_name: '%s' failed.", action_name.c_str());
435 throw std::runtime_error(
"Mode registration failed");
439 mode_executor_ptr_ = std::make_shared<ModeExecutor<ActionT>>(
440 action_name, node_ptr_, action_context_ptr, mode_ptr_->id(), deactivate_before_completion);
443template <
class ActionT,
class ModeT>
444rclcpp::node_interfaces::NodeBaseInterface::SharedPtr ModeExecutorFactory<ActionT, ModeT>::get_node_base_interface()
446 return node_ptr_->get_node_base_interface();
Generic template class for executing a PX4 mode implementing the interface of a standard ROS 2 action...
Client for sending requests to the PX4 autopilot using the VehicleCommand topic.
Generic base class for implementing robot skills using the ROS 2 action concept.
ActionStatus
Status of the auto_apms_util::ActionWrapper execution process.
Implementation of PX4 mode peers offered by px4_ros2_cpp enabling integration with AutoAPMS.
Fundamental helper classes and utility functions.