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"
23#include "px4_ros2/utils/message_version.hpp"
93template <
class ActionT>
96 enum class State : uint8_t
100 WAIT_FOR_COMPLETION_SIGNAL,
106 using FlightMode = VehicleCommandClient::FlightMode;
107 using typename auto_apms_util::ActionWrapper<ActionT>::ActionContextType;
108 using typename auto_apms_util::ActionWrapper<ActionT>::Goal;
109 using typename auto_apms_util::ActionWrapper<ActionT>::Feedback;
110 using typename auto_apms_util::ActionWrapper<ActionT>::Result;
113 explicit ModeExecutor(
114 const std::string & action_name, rclcpp::Node::SharedPtr node_ptr,
115 std::shared_ptr<ActionContextType> action_context_ptr, uint8_t mode_id,
bool deactivate_before_completion =
true);
116 explicit ModeExecutor(
117 const std::string & action_name,
const rclcpp::NodeOptions & options, uint8_t mode_id,
118 bool deactivate_before_completion =
true);
119 explicit ModeExecutor(
120 const std::string & action_name,
const rclcpp::NodeOptions & options, FlightMode flight_mode,
121 bool deactivate_before_completion =
true);
126 bool onGoalRequest(std::shared_ptr<const Goal> goal_ptr)
override final;
127 bool onCancelRequest(std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr)
override final;
129 std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Result> result_ptr)
override final;
131 std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Feedback> feedback_ptr,
132 std::shared_ptr<Result> result_ptr)
override final;
135 bool isCurrentNavState(uint8_t nav_state);
136 virtual bool sendActivationCommand(
const VehicleCommandClient & client, std::shared_ptr<const Goal> goal_ptr);
137 virtual bool isCompleted(std::shared_ptr<const Goal> goal_ptr,
const px4_msgs::msg::VehicleStatus & vehicle_status);
138 virtual void setFeedback(std::shared_ptr<Feedback> feedback_ptr,
const px4_msgs::msg::VehicleStatus & vehicle_status);
140 uint8_t getModeID()
const;
143 const VehicleCommandClient vehicle_command_client_;
144 const uint8_t mode_id_;
145 bool deactivate_before_completion_;
146 rclcpp::Subscription<px4_msgs::msg::VehicleStatus>::SharedPtr vehicle_status_sub_ptr_;
147 rclcpp::Subscription<px4_msgs::msg::ModeCompleted>::SharedPtr mode_completed_sub_ptr_;
148 px4_msgs::msg::VehicleStatus::SharedPtr last_vehicle_status_ptr_;
149 bool mode_completed_{
false};
150 bool deactivation_command_sent_{
false};
151 State state_{State::REQUEST_ACTIVATION};
152 rclcpp::Time activation_command_sent_time_;
153 rclcpp::Duration activation_timeout_{0, 0};
163template <
class ActionT,
class ModeT>
164class ModeExecutorFactory
168 const std::string & action_name,
const rclcpp::NodeOptions & options,
169 const std::string & topic_namespace_prefix =
"",
bool deactivate_before_completion =
true);
171 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface();
174 rclcpp::Node::SharedPtr node_ptr_;
175 std::unique_ptr<ModeBase<ActionT>> mode_ptr_;
176 std::shared_ptr<ModeExecutor<ActionT>> mode_executor_ptr_;
183template <
class ActionT>
184ModeExecutor<ActionT>::ModeExecutor(
185 const std::string & action_name, rclcpp::Node::SharedPtr node_ptr,
186 std::shared_ptr<ActionContextType> action_context_ptr, uint8_t mode_id,
bool deactivate_before_completion)
187:
auto_apms_util::ActionWrapper<ActionT>(action_name, node_ptr, action_context_ptr),
188 vehicle_command_client_(*node_ptr),
190 deactivate_before_completion_(deactivate_before_completion)
195template <
class ActionT>
196ModeExecutor<ActionT>::ModeExecutor(
197 const std::string & action_name,
const rclcpp::NodeOptions & options, uint8_t mode_id,
198 bool deactivate_before_completion)
200 vehicle_command_client_(*this->node_ptr_),
202 deactivate_before_completion_(deactivate_before_completion)
207template <
class ActionT>
208ModeExecutor<ActionT>::ModeExecutor(
209 const std::string & action_name,
const rclcpp::NodeOptions & options, FlightMode flight_mode,
210 bool deactivate_before_completion)
211:
ModeExecutor<ActionT>(action_name, options, static_cast<uint8_t>(flight_mode), deactivate_before_completion)
215template <
class ActionT>
216void ModeExecutor<ActionT>::setUp()
218 vehicle_status_sub_ptr_ = this->node_ptr_->template create_subscription<px4_msgs::msg::VehicleStatus>(
219 "/fmu/out/vehicle_status" + px4_ros2::getMessageNameVersion<px4_msgs::msg::VehicleStatus>(),
220 rclcpp::QoS(1).best_effort(),
221 [
this](px4_msgs::msg::VehicleStatus::UniquePtr msg) { last_vehicle_status_ptr_ = std::move(msg); });
223 mode_completed_sub_ptr_ = this->node_ptr_->template create_subscription<px4_msgs::msg::ModeCompleted>(
224 "/fmu/out/mode_completed" + px4_ros2::getMessageNameVersion<px4_msgs::msg::ModeCompleted>(),
225 rclcpp::QoS(1).best_effort(), [
this](px4_msgs::msg::ModeCompleted::UniquePtr msg) {
226 if (msg->nav_state == mode_id_) {
227 if (msg->result == px4_msgs::msg::ModeCompleted::RESULT_SUCCESS) {
228 this->mode_completed_ =
true;
230 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Flight mode %i failed to execute. Aborting...", this->mode_id_);
231 this->action_context_ptr_->abort();
238template <
class ActionT>
244 bool is_holding = isCurrentNavState(
static_cast<uint8_t
>(FlightMode::Hold));
245 if (state_ == State::WAIT_FOR_ACTIVATION) {
247 auto & clock = *this->node_ptr_->get_clock();
248 RCLCPP_DEBUG_THROTTLE(
249 this->node_ptr_->get_logger(), clock, 250,
"Waiting for flight mode %i to become active before deactivating...",
251 return ActionStatus::RUNNING;
253 state_ = State::COMPLETE;
258 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Deactivated flight mode successfully (HOLD is active)");
259 return ActionStatus::SUCCESS;
262 if (!deactivation_command_sent_) {
263 if (!vehicle_command_client_.syncActivateFlightMode(FlightMode::Hold)) {
264 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Failed to send command to activate HOLD");
265 return ActionStatus::FAILURE;
268 last_vehicle_status_ptr_ =
nullptr;
269 deactivation_command_sent_ =
true;
273 return ActionStatus::RUNNING;
276template <
class ActionT>
277bool ModeExecutor<ActionT>::onGoalRequest(
const std::shared_ptr<const Goal> )
279 state_ = State::REQUEST_ACTIVATION;
280 deactivation_command_sent_ =
false;
281 mode_completed_ =
false;
282 activation_timeout_ = rclcpp::Duration::from_seconds(fmin(this->param_listener_.get_params().loop_rate * 15, 1.5));
286template <
class ActionT>
287bool ModeExecutor<ActionT>::onCancelRequest(
288 std::shared_ptr<const Goal> , std::shared_ptr<Result> )
290 if (deactivate_before_completion_) {
293 this->node_ptr_->get_logger(),
"Cancellation requested! Will deactivate before termination (Enter HOLD)...");
295 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Cancellation requested!");
300template <
class ActionT>
302 std::shared_ptr<const Goal> , std::shared_ptr<Result> )
304 if (deactivate_before_completion_) {
305 return asyncDeactivateFlightMode();
307 return ActionStatus::SUCCESS;
310template <
class ActionT>
311bool ModeExecutor<ActionT>::isCurrentNavState(uint8_t nav_state)
313 if (last_vehicle_status_ptr_ && last_vehicle_status_ptr_->nav_state == nav_state) {
319template <
class ActionT>
321 std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Feedback> feedback_ptr, std::shared_ptr<Result> )
324 case State::REQUEST_ACTIVATION:
325 if (!sendActivationCommand(vehicle_command_client_, goal_ptr)) {
327 this->node_ptr_->get_logger(),
"Failed to send activation command for flight mode %i. Aborting...", mode_id_);
328 return ActionStatus::FAILURE;
331 last_vehicle_status_ptr_ =
nullptr;
332 state_ = State::WAIT_FOR_ACTIVATION;
333 activation_command_sent_time_ = this->node_ptr_->now();
335 this->node_ptr_->get_logger(),
"Activation command for flight mode %i was sent successfully", mode_id_);
336 return ActionStatus::RUNNING;
337 case State::WAIT_FOR_ACTIVATION:
338 if (isCurrentNavState(mode_id_)) {
339 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Flight mode %i is active", mode_id_);
340 state_ = State::WAIT_FOR_COMPLETION_SIGNAL;
341 }
else if (this->node_ptr_->now() - activation_command_sent_time_ > activation_timeout_) {
342 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Timeout activating flight mode %i. Aborting...", mode_id_);
343 return ActionStatus::FAILURE;
345 return ActionStatus::RUNNING;
346 case State::WAIT_FOR_COMPLETION_SIGNAL:
348 setFeedback(feedback_ptr, *last_vehicle_status_ptr_);
351 if (isCompleted(goal_ptr, *last_vehicle_status_ptr_)) {
352 state_ = State::COMPLETE;
353 if (deactivate_before_completion_) {
356 this->node_ptr_->get_logger(),
357 "Flight mode %i complete! Will deactivate before termination (Enter HOLD)...", mode_id_);
360 this->node_ptr_->get_logger(),
361 "Flight mode %i complete! Will leave current navigation state as is. User is "
362 "responsible for initiating the next flight mode...",
370 if (!isCurrentNavState(mode_id_)) {
371 RCLCPP_WARN(this->node_ptr_->get_logger(),
"Flight mode %i was deactivated externally. Aborting...", mode_id_);
372 return ActionStatus::FAILURE;
374 return ActionStatus::RUNNING;
375 case State::COMPLETE:
379 if (deactivate_before_completion_) {
380 const auto deactivation_state = asyncDeactivateFlightMode();
381 if (deactivation_state != ActionStatus::SUCCESS) {
382 return deactivation_state;
387 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Flight mode %i execution termination", mode_id_);
388 return ActionStatus::SUCCESS;
391template <
class ActionT>
392bool ModeExecutor<ActionT>::sendActivationCommand(
395 return client.syncActivateFlightMode(mode_id_);
398template <
class ActionT>
399bool ModeExecutor<ActionT>::isCompleted(
400 std::shared_ptr<const Goal> ,
const px4_msgs::msg::VehicleStatus & )
402 return mode_completed_;
405template <
class ActionT>
406void ModeExecutor<ActionT>::setFeedback(
407 std::shared_ptr<Feedback> ,
const px4_msgs::msg::VehicleStatus & )
412template <
class ActionT>
413uint8_t ModeExecutor<ActionT>::getModeID()
const
418template <
class ActionT,
class ModeT>
419ModeExecutorFactory<ActionT, ModeT>::ModeExecutorFactory(
420 const std::string & action_name,
const rclcpp::NodeOptions & options,
const std::string & topic_namespace_prefix,
421 bool deactivate_before_completion)
422: node_ptr_(std::make_shared<rclcpp::Node>(action_name +
"_node", options))
425 std::is_base_of<ModeBase<ActionT>, ModeT>::value,
426 "Template argument ModeT must inherit auto_apms::ModeBase<ActionT> as public and with same type "
427 "ActionT as auto_apms::ActionWrapper<ActionT>");
429 const auto action_context_ptr = std::make_shared<auto_apms_util::ActionContext<ActionT>>(node_ptr_->get_logger());
431 mode_ptr_ = std::make_unique<ModeT>(
432 *node_ptr_, px4_ros2::ModeBase::Settings(action_name), topic_namespace_prefix, action_context_ptr);
434 constexpr int max_retries = 5;
435 bool fmu_available =
false;
436 for (
int attempt = 0; attempt < max_retries; ++attempt) {
437 if (px4_ros2::waitForFMU(*node_ptr_, std::chrono::seconds(3))) {
438 fmu_available =
true;
439 RCLCPP_DEBUG(node_ptr_->get_logger(),
"FMU availability test successful (attempt %d).", attempt + 1);
442 RCLCPP_WARN(node_ptr_->get_logger(),
"No message from FMU (attempt %d/%d). Retrying...", attempt + 1, max_retries);
444 if (!fmu_available) {
445 throw std::runtime_error(
"No message from FMU after multiple attempts");
448 if (!mode_ptr_->doRegister()) {
449 RCLCPP_FATAL(node_ptr_->get_logger(),
"Registration of mode with action_name: '%s' failed.", action_name.c_str());
450 throw std::runtime_error(
"Mode registration failed");
454 mode_executor_ptr_ = std::make_shared<ModeExecutor<ActionT>>(
455 action_name, node_ptr_, action_context_ptr, mode_ptr_->id(), deactivate_before_completion);
458template <
class ActionT,
class ModeT>
459rclcpp::node_interfaces::NodeBaseInterface::SharedPtr ModeExecutorFactory<ActionT, ModeT>::get_node_base_interface()
461 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.