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,
bool deactivate_before_completion =
true);
170 rclcpp::node_interfaces::NodeBaseInterface::SharedPtr get_node_base_interface();
173 rclcpp::Node::SharedPtr node_ptr_;
174 std::unique_ptr<ModeBase<ActionT>> mode_ptr_;
175 std::shared_ptr<ModeExecutor<ActionT>> mode_executor_ptr_;
182template <
class ActionT>
183ModeExecutor<ActionT>::ModeExecutor(
184 const std::string & action_name, rclcpp::Node::SharedPtr node_ptr,
185 std::shared_ptr<ActionContextType> action_context_ptr, uint8_t mode_id,
bool deactivate_before_completion)
186:
auto_apms_util::ActionWrapper<ActionT>(action_name, node_ptr, action_context_ptr),
187 vehicle_command_client_(*node_ptr),
189 deactivate_before_completion_(deactivate_before_completion)
194template <
class ActionT>
195ModeExecutor<ActionT>::ModeExecutor(
196 const std::string & action_name,
const rclcpp::NodeOptions & options, uint8_t mode_id,
197 bool deactivate_before_completion)
199 vehicle_command_client_(*this->node_ptr_),
201 deactivate_before_completion_(deactivate_before_completion)
206template <
class ActionT>
207ModeExecutor<ActionT>::ModeExecutor(
208 const std::string & action_name,
const rclcpp::NodeOptions & options, FlightMode flight_mode,
209 bool deactivate_before_completion)
210:
ModeExecutor<ActionT>(action_name, options, static_cast<uint8_t>(flight_mode), deactivate_before_completion)
214template <
class ActionT>
215void ModeExecutor<ActionT>::setUp()
217 vehicle_status_sub_ptr_ = this->node_ptr_->template create_subscription<px4_msgs::msg::VehicleStatus>(
218 "fmu/out/vehicle_status" + px4_ros2::getMessageNameVersion<px4_msgs::msg::VehicleStatus>(),
219 rclcpp::QoS(1).best_effort(),
220 [
this](px4_msgs::msg::VehicleStatus::UniquePtr msg) { last_vehicle_status_ptr_ = std::move(msg); });
222 mode_completed_sub_ptr_ = this->node_ptr_->template create_subscription<px4_msgs::msg::ModeCompleted>(
223 "fmu/out/mode_completed" + px4_ros2::getMessageNameVersion<px4_msgs::msg::ModeCompleted>(),
224 rclcpp::QoS(1).best_effort(), [
this](px4_msgs::msg::ModeCompleted::UniquePtr msg) {
225 if (msg->nav_state == mode_id_) {
226 if (msg->result == px4_msgs::msg::ModeCompleted::RESULT_SUCCESS) {
227 this->mode_completed_ =
true;
229 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Flight mode %i failed to execute. Aborting...", this->mode_id_);
230 this->action_context_ptr_->abort();
237template <
class ActionT>
243 bool is_holding = isCurrentNavState(
static_cast<uint8_t
>(FlightMode::Hold));
244 if (state_ == State::WAIT_FOR_ACTIVATION) {
246 auto & clock = *this->node_ptr_->get_clock();
247 RCLCPP_DEBUG_THROTTLE(
248 this->node_ptr_->get_logger(), clock, 250,
"Waiting for flight mode %i to become active before deactivating...",
250 return ActionStatus::RUNNING;
252 state_ = State::COMPLETE;
257 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Deactivated flight mode successfully (HOLD is active)");
258 return ActionStatus::SUCCESS;
261 if (!deactivation_command_sent_) {
262 if (!vehicle_command_client_.syncActivateFlightMode(FlightMode::Hold)) {
263 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Failed to send command to activate HOLD");
264 return ActionStatus::FAILURE;
267 last_vehicle_status_ptr_ =
nullptr;
268 deactivation_command_sent_ =
true;
272 return ActionStatus::RUNNING;
275template <
class ActionT>
276bool ModeExecutor<ActionT>::onGoalRequest(
const std::shared_ptr<const Goal> )
278 state_ = State::REQUEST_ACTIVATION;
279 deactivation_command_sent_ =
false;
280 mode_completed_ =
false;
281 activation_timeout_ = rclcpp::Duration::from_seconds(fmin(this->param_listener_.get_params().loop_rate * 15, 1.5));
285template <
class ActionT>
286bool ModeExecutor<ActionT>::onCancelRequest(
287 std::shared_ptr<const Goal> , std::shared_ptr<Result> )
289 if (deactivate_before_completion_) {
292 this->node_ptr_->get_logger(),
"Cancellation requested! Will deactivate before termination (Enter HOLD)...");
294 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Cancellation requested!");
299template <
class ActionT>
301 std::shared_ptr<const Goal> , std::shared_ptr<Result> )
303 if (deactivate_before_completion_) {
304 return asyncDeactivateFlightMode();
306 return ActionStatus::SUCCESS;
309template <
class ActionT>
310bool ModeExecutor<ActionT>::isCurrentNavState(uint8_t nav_state)
312 if (last_vehicle_status_ptr_ && last_vehicle_status_ptr_->nav_state == nav_state) {
318template <
class ActionT>
320 std::shared_ptr<const Goal> goal_ptr, std::shared_ptr<Feedback> feedback_ptr, std::shared_ptr<Result> )
323 case State::REQUEST_ACTIVATION:
324 if (!sendActivationCommand(vehicle_command_client_, goal_ptr)) {
326 this->node_ptr_->get_logger(),
"Failed to send activation command for flight mode %i. Aborting...", mode_id_);
327 return ActionStatus::FAILURE;
330 last_vehicle_status_ptr_ =
nullptr;
331 state_ = State::WAIT_FOR_ACTIVATION;
332 activation_command_sent_time_ = this->node_ptr_->now();
334 this->node_ptr_->get_logger(),
"Activation command for flight mode %i was sent successfully", mode_id_);
335 return ActionStatus::RUNNING;
336 case State::WAIT_FOR_ACTIVATION:
337 if (isCurrentNavState(mode_id_)) {
338 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Flight mode %i is active", mode_id_);
339 state_ = State::WAIT_FOR_COMPLETION_SIGNAL;
340 }
else if (this->node_ptr_->now() - activation_command_sent_time_ > activation_timeout_) {
341 RCLCPP_ERROR(this->node_ptr_->get_logger(),
"Timeout activating flight mode %i. Aborting...", mode_id_);
342 return ActionStatus::FAILURE;
344 return ActionStatus::RUNNING;
345 case State::WAIT_FOR_COMPLETION_SIGNAL:
347 setFeedback(feedback_ptr, *last_vehicle_status_ptr_);
350 if (isCompleted(goal_ptr, *last_vehicle_status_ptr_)) {
351 state_ = State::COMPLETE;
352 if (deactivate_before_completion_) {
355 this->node_ptr_->get_logger(),
356 "Flight mode %i complete! Will deactivate before termination (Enter HOLD)...", mode_id_);
359 this->node_ptr_->get_logger(),
360 "Flight mode %i complete! Will leave current navigation state as is. User is "
361 "responsible for initiating the next flight mode...",
369 if (!isCurrentNavState(mode_id_)) {
370 RCLCPP_WARN(this->node_ptr_->get_logger(),
"Flight mode %i was deactivated externally. Aborting...", mode_id_);
371 return ActionStatus::FAILURE;
373 return ActionStatus::RUNNING;
374 case State::COMPLETE:
378 if (deactivate_before_completion_) {
379 const auto deactivation_state = asyncDeactivateFlightMode();
380 if (deactivation_state != ActionStatus::SUCCESS) {
381 return deactivation_state;
386 RCLCPP_DEBUG(this->node_ptr_->get_logger(),
"Flight mode %i execution termination", mode_id_);
387 return ActionStatus::SUCCESS;
390template <
class ActionT>
391bool ModeExecutor<ActionT>::sendActivationCommand(
394 return client.syncActivateFlightMode(mode_id_);
397template <
class ActionT>
398bool ModeExecutor<ActionT>::isCompleted(
399 std::shared_ptr<const Goal> ,
const px4_msgs::msg::VehicleStatus & )
401 return mode_completed_;
404template <
class ActionT>
405void ModeExecutor<ActionT>::setFeedback(
406 std::shared_ptr<Feedback> ,
const px4_msgs::msg::VehicleStatus & )
411template <
class ActionT>
412uint8_t ModeExecutor<ActionT>::getModeID()
const
417template <
class ActionT,
class ModeT>
418ModeExecutorFactory<ActionT, ModeT>::ModeExecutorFactory(
419 const std::string & action_name,
const rclcpp::NodeOptions & options,
bool deactivate_before_completion)
420: node_ptr_(std::make_shared<rclcpp::Node>(action_name +
"_node", options))
423 std::is_base_of<ModeBase<ActionT>, ModeT>::value,
424 "Template argument ModeT must inherit auto_apms::ModeBase<ActionT> as public and with same type "
425 "ActionT as auto_apms::ActionWrapper<ActionT>");
427 const auto action_context_ptr = std::make_shared<auto_apms_util::ActionContext<ActionT>>(node_ptr_->get_logger());
429 mode_ptr_ = std::make_unique<ModeT>(*node_ptr_, px4_ros2::ModeBase::Settings(action_name), action_context_ptr);
431 constexpr int max_retries = 5;
432 bool fmu_available =
false;
433 for (
int attempt = 0; attempt < max_retries; ++attempt) {
434 if (px4_ros2::waitForFMU(*node_ptr_, std::chrono::seconds(3))) {
435 fmu_available =
true;
436 RCLCPP_DEBUG(node_ptr_->get_logger(),
"FMU availability test successful (attempt %d).", attempt + 1);
439 RCLCPP_WARN(node_ptr_->get_logger(),
"No message from FMU (attempt %d/%d). Retrying...", attempt + 1, max_retries);
441 if (!fmu_available) {
442 throw std::runtime_error(
"No message from FMU after multiple attempts");
445 if (!mode_ptr_->doRegister()) {
446 RCLCPP_FATAL(node_ptr_->get_logger(),
"Registration of mode with action_name: '%s' failed.", action_name.c_str());
447 throw std::runtime_error(
"Mode registration failed");
451 mode_executor_ptr_ = std::make_shared<ModeExecutor<ActionT>>(
452 action_name, node_ptr_, action_context_ptr, mode_ptr_->id(), deactivate_before_completion);
455template <
class ActionT,
class ModeT>
456rclcpp::node_interfaces::NodeBaseInterface::SharedPtr ModeExecutorFactory<ActionT, ModeT>::get_node_base_interface()
458 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.