PX4 ROS 2 Interface Library
Library to interface with PX4 from a companion computer using ROS 2
mission_executor.hpp
1 /****************************************************************************
2  * Copyright (c) 2024 PX4 Development Team.
3  * SPDX-License-Identifier: BSD-3-Clause
4  ****************************************************************************/
5 
6 #pragma once
7 #include <memory>
8 #include <vector>
9 
10 #include <px4_ros2/components/mode.hpp>
11 #include <px4_ros2/components/mode_executor.hpp>
12 #include <px4_ros2/mission/mission.hpp>
13 #include <px4_ros2/mission/actions/action.hpp>
14 #include <px4_ros2/mission/trajectory/trajectory_executor.hpp>
15 #include <px4_ros2/vehicle_state/land_detected.hpp>
16 
17 namespace px4_ros2
18 {
23 class AsyncFunctionCalls;
24 class ActionStateKeeper;
25 
31 {
32 public:
34  {
35  std::vector<std::function<std::shared_ptr<ActionInterface>(ModeBase & mode)>>
36  custom_actions_factory;
37  std::set<std::string> default_actions{"changeSettings", "onResume", "onFailure", "takeoff",
38  "rtl", "land", "hold"};
39  std::function<std::shared_ptr<TrajectoryExecutorInterface>(ModeBase & mode)>
40  trajectory_executor_factory;
41  std::string persistence_filename;
42 
43  template<class ActionType, typename ... Args>
44  Configuration & addCustomAction(Args &&... args)
45  {
46  custom_actions_factory.push_back(
47  [args = std::tie(std::forward<Args>(args) ...)](ModeBase & mode)mutable {
48  return std::apply(
49  [&mode](auto &&... args)
50  {
51  return std::make_shared<ActionType>(mode, std::forward<Args>(args)...);
52  }, std::move(args));
53  });
54  return *this;
55  }
56  template<typename TrajectoryExecutorType, typename ... Args>
57  Configuration & withTrajectoryExecutor(Args &&... args)
58  {
59  trajectory_executor_factory =
60  [args = std::tie(std::forward<Args>(args) ...)](ModeBase & mode)mutable {
61  return std::apply(
62  [&mode](auto &&... args)
63  {
64  return std::make_shared<TrajectoryExecutorType>(mode, std::forward<Args>(args)...);
65  }, std::move(args));
66  };
67  return *this;
68  }
69  Configuration & withPersistenceFile(const std::string & filename)
70  {
71  persistence_filename = filename;
72  return *this;
73  }
74  };
75 
77  const std::string & mode_name, const Configuration & configuration,
78  rclcpp::Node & node, const std::string & topic_namespace_prefix = "");
79 
80  virtual ~MissionExecutor();
81 
82  bool doRegister();
83 
84  void setMission(const Mission & mission);
85  void resetMission();
86 
87  const Mission & mission() const {return *_mission;}
88 
89  void onActivated(const std::function<void()> & callback) {_on_activated = callback;}
90  void onDeactivated(const std::function<void()> & callback) {_on_deactivated = callback;}
95  void onProgressUpdate(const std::function<void(int)> & callback) {_on_progress_update = callback;}
96  void onCompleted(const std::function<void()> & callback) {_on_completed = callback;}
97  void onFailsafeDeferred(const std::function<void()> & callback);
98  void onReadynessUpdate(
99  const std::function<void(bool ready,
100  const std::vector<std::string> & errors)> & callback);
101 
109  const std::function<void(const std::optional<std::string> &)> & callback)
110  {
111  _on_activity_info_change = callback;
112  }
113 
123  void setActivityInfo(const std::optional<std::string> & activity_info);
134  bool deferFailsafes(bool enabled, int timeout_s = 0);
135 
136  bool controlAutoSetHome(bool enabled);
137 
142  void abort();
143 
144 protected:
145  class MissionMode : public ModeBase
146  {
147 public:
148  MissionMode(
149  rclcpp::Node & node, const Settings & settings, const std::string & topic_namespace_prefix,
150  MissionExecutor & mission_executor)
151  : ModeBase(node, settings, topic_namespace_prefix), _mission_executor(mission_executor)
152  {
153  }
154 
155  explicit MissionMode(const ModeBase & mode_base) = delete;
156 
157  ~MissionMode() override = default;
159  {
160  _mission_executor.checkArmingAndRunConditions(reporter);
161  }
162  void onActivate() override {}
163  void onDeactivate() override {}
164  void updateSetpoint(float dt_s) override {_mission_executor.updateSetpoint();}
165 
166  void disableWatchdogTimer() // NOLINT we just want to change the methods visibility
167  {
168  ModeBase::disableWatchdogTimer();
169  }
170 
171 private:
172  MissionExecutor & _mission_executor;
173  };
174 
176  {
177 public:
179  rclcpp::Node & node, const Settings & settings, ModeBase & owned_mode,
180  const std::string & topic_namespace_prefix, MissionExecutor & mission_executor)
181  : ModeExecutorBase(settings, owned_mode), _mission_executor(
182  mission_executor)
183  {
184  }
185 
186  explicit MissionModeExecutor(const ModeExecutorBase & mode_executor_base) = delete;
187 
188  ~MissionModeExecutor() override = default;
189  void onActivate() override {_mission_executor.onActivate();}
190  void onDeactivate(DeactivateReason reason) override {_mission_executor.onDeactivate(reason);}
191  void onFailsafeDeferred() override
192  {
193  if (on_failsafe_deferred) {
194  on_failsafe_deferred();
195  }
196  }
197 
199  uint32_t command, float param1, float param2, float param3,
200  float param4, float param5, float param6, float param7) override
201  {
202  if (command_handler) {
203  return command_handler(
204  command,
205  param1) ? Result::Success : Result::Rejected;
206  }
208  command, param1, param2, param3, param4, param5,
209  param6, param7);
210  }
211 
212  void setRegistration(const std::shared_ptr<Registration> & registration)
213  {
214  setSkipMessageCompatibilityCheck();
215  overrideRegistration(registration);
216  }
217  std::function<bool(uint32_t, float)> command_handler{nullptr};
218  std::function<void()> on_failsafe_deferred{nullptr};
219 
220 private:
221  MissionExecutor & _mission_executor;
222  };
223 
224  virtual bool doRegisterImpl(MissionMode & mode, MissionModeExecutor & executor_base);
225 
226  void setCommandHandler(const std::function<bool(uint32_t, float)> & command_handler)
227  {
228  _mode_executor->command_handler = command_handler;
229  }
230 
231  ModeBase::ModeID modeId() const {return _mode->id();}
232 
233  ModeExecutorBase & modeExecutor() {return *_mode_executor;}
234 
235  std::shared_ptr<LandDetected> _land_detected;
236 
237 private:
238  using ActionID = int;
239  enum class AbortReason
240  {
241  Other,
242  ModeFailure,
243  TrajectoryFailure,
244  NoValidMission,
245  ActionDoesNotExist,
246  };
247  static std::string abortReasonStr(AbortReason reason);
248 
249  void checkArmingAndRunConditions(HealthAndArmingCheckReporter & reporter);
250  void checkReadynessAndReport();
251  void updateSetpoint();
252  void onActivate();
253  void onDeactivate(ModeExecutorBase::DeactivateReason reason);
254 
255  bool isReady() const {return _has_valid_mission && _actions_ready;}
256 
257  void runMode(
258  ModeBase::ModeID mode_id,
259  const std::function<void()> & on_completed, const std::function<void()> & on_failure = nullptr);
267  void runModeTakeoff(
268  float altitude, float heading,
269  const std::function<void()> & on_completed, const std::function<void()> & on_failure = nullptr);
270  void runAction(
271  const std::string & action_name, const ActionArguments & arguments,
272  const std::function<void()> & on_completed);
273  void runTrajectory(
274  const std::shared_ptr<Mission> & trajectory, int start_index, int end_index,
275  const std::function<void(int)> & on_index_reached, bool stop_at_last_item);
276 
277  void runNextMissionItem();
278  void runCurrentMissionItem(bool resuming);
279  void resetMissionState();
280  std::pair<int, bool> getNextTrajectorySegment(int start_index) const;
281  bool currentActionSupportsResumeFromLanded() const;
282 
283  void setTrajectoryOptions(const TrajectoryOptions & options);
284  void clearTrajectoryOptions();
285 
286  void setCurrentMissionIndex(int index);
287 
288  void abort(AbortReason reason);
289  void invalidateActionHandler();
290 
291  void savePersistentState();
292  void clearPersistentState() const;
293  bool tryLoadPersistentState();
294  nlohmann::json getOnResumeStateAndClear();
295  void runOnResumeStoreState();
296 
297  struct ActionState
298  {
299  std::string name;
300  ActionArguments arguments;
301  };
302  std::unique_ptr<ActionStateKeeper> addContinousAction(const ActionState & state);
303  void removeContinousAction(ActionID id);
304  void runStoredActions();
305  void deactivateAllActions();
306 
307  struct PersistentState
308  {
309  std::optional<int> current_index;
310  std::string mission_checksum;
311 
312  std::map<ActionID, ActionState> continuous_actions;
313 
314  void toJson(nlohmann::json & j) const;
315  void fromJson(const nlohmann::json & j);
316  };
317 
318  PersistentState _state;
319  int _next_continuous_action_id{0};
320  const std::string _persistence_filename;
321 
322  enum class MissionItemState
323  {
324  Trajectory,
325  Other,
326  };
327  MissionItemState _mission_item_state{MissionItemState::Other};
328  ModeBase::ModeID _active_mode{};
329  bool _is_active{false};
330 
331  std::optional<rclcpp::Time> _trajectory_update_warn;
332 
333  std::shared_ptr<TrajectoryExecutorInterface> _trajectory_executor;
334  std::map<std::string, std::shared_ptr<ActionInterface>> _actions;
335  std::shared_ptr<Mission> _mission;
336  TrajectoryOptions _trajectory_options;
337  bool _has_valid_mission{false};
338  static const std::vector<std::string> kNoMissionErrors;
339  std::vector<std::string> _mission_errors{kNoMissionErrors};
340  bool _actions_ready{false};
341  rclcpp::TimerBase::SharedPtr _readyness_timer;
342  int _abort_recursion_level{0};
343 
344  rclcpp::Node & _node;
345  std::unique_ptr<MissionMode> _mode;
346  std::unique_ptr<MissionModeExecutor> _mode_executor;
347  std::shared_ptr<ActionHandler> _action_handler;
348 
349  std::unique_ptr<AsyncFunctionCalls> _reporting; // Report asynchronously to avoid potential recursive calls and state changes in between
350  std::function<void()> _on_activated;
351  std::function<void()> _on_deactivated;
352  std::function<void(int)> _on_progress_update;
353  std::function<void()> _on_completed;
354  std::function<void(const std::optional<std::string> &)> _on_activity_info_change;
355  std::function<void(bool ready, const std::vector<std::string> & errors)> _on_readyness_update;
356 
357  friend class ActionHandler;
358  friend class ActionStateKeeper;
359 };
360 
362 {
363 public:
364  ActionStateKeeper(int id, MissionExecutor & mission_executor)
365  : _id(id), _mission_executor(mission_executor) {}
366 
368  {
369  _mission_executor.removeContinousAction(_id);
370  }
371 
372 private:
373  const int _id;
374  MissionExecutor & _mission_executor;
375 };
376 
382 {
383 public:
384  explicit ActionHandler(MissionExecutor & mission_executor)
385  : _mission_executor(mission_executor) {}
386 
387  void runMode(
388  ModeBase::ModeID mode_id,
389  const std::function<void()> & on_completed, const std::function<void()> & on_failure = nullptr)
390  {
391  if (!_valid) {
392  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
393  return;
394  }
395  _mission_executor.runMode(mode_id, on_completed, on_failure);
396  }
405  float altitude, float heading,
406  const std::function<void()> & on_completed, const std::function<void()> & on_failure = nullptr)
407  {
408  if (!_valid) {
409  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
410  return;
411  }
412  _mission_executor.runModeTakeoff(altitude, heading, on_completed, on_failure);
413  }
414  void runAction(
415  const std::string & action_name, const ActionArguments & arguments,
416  const std::function<void()> & on_completed)
417  {
418  if (!_valid) {
419  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
420  return;
421  }
422  _mission_executor.runAction(action_name, arguments, on_completed);
423  }
424  void runTrajectory(
425  const std::shared_ptr<Mission> & trajectory,
426  const std::function<void()> & on_completed, bool stop_at_last_item = true);
427 
431  TrajectoryOptions getTrajectoryOptions() const {return _mission_executor._trajectory_options;}
436  {
437  if (!_valid) {
438  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
439  return;
440  }
441  _mission_executor.clearTrajectoryOptions();
442  }
449  {
450  if (!_valid) {
451  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
452  return;
453  }
454  _mission_executor.setTrajectoryOptions(options);
455  }
456 
464  void setActvityInfo(const std::string & activity_info)
465  {
466  if (!_valid) {
467  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
468  return;
469  }
470  _mission_executor.setActivityInfo(activity_info);
471  }
472 
479  {
480  if (!_valid) {
481  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
482  return;
483  }
484  _mission_executor.setActivityInfo(std::nullopt);
485  }
486 
487  std::optional<int> getCurrentMissionIndex() const;
495  void setCurrentMissionIndex(int index);
496 
497  bool currentActionSupportsResumeFromLanded() const;
498 
510  std::unique_ptr<ActionStateKeeper> storeState(
511  const std::string & action_name,
512  const ActionArguments & arguments)
513  {
514  if (!_valid) {
515  return nullptr;
516  }
517  return _mission_executor.addContinousAction(
518  MissionExecutor::ActionState{action_name,
519  arguments});
520  }
521 
522  const Mission & mission() const {return _mission_executor.mission();}
523 
531  bool deferFailsafes(bool enabled, int timeout_s = 0)
532  {
533  if (!_valid) {
534  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
535  return false;
536  }
537  return _mission_executor.deferFailsafes(enabled, timeout_s);
538  }
539 
540  bool controlAutoSetHome(bool enabled)
541  {
542  if (!_valid) {
543  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
544  return false;
545  }
546  return _mission_executor.controlAutoSetHome(enabled);
547  }
548 
554  void onFailsafeDeferred(const std::function<void()> & callback)
555  {
556  if (!_valid) {
557  RCLCPP_WARN(_mission_executor._node.get_logger(), "ActionHandler is not valid anymore");
558  return;
559  }
560  _mission_executor.onFailsafeDeferred(callback);
561  }
562 
570  bool isValid() const {return _valid;}
571 
572 private:
573  friend class MissionExecutor;
574  void setInvalid() {_valid = false;}
575 
576  bool _valid{true};
577  MissionExecutor & _mission_executor;
578 };
579 
581 } /* namespace px4_ros2 */
Arguments passed to an action from the mission JSON definition.
Definition: mission.hpp:29
Handler passed to custom actions to run modes, actions and trajectories.
Definition: mission_executor.hpp:382
void setTrajectoryOptions(const TrajectoryOptions &options)
Override the trajectory config options.
Definition: mission_executor.hpp:448
TrajectoryOptions getTrajectoryOptions() const
get the current trajectory config options
Definition: mission_executor.hpp:431
void setActvityInfo(const std::string &activity_info)
Sets the activity info for the mission executor. This method forwards the provided activity string to...
Definition: mission_executor.hpp:464
bool isValid() const
Check if the handler is still valid.
Definition: mission_executor.hpp:570
void clearActivityInfo()
Clears the activity information. This method sets the activity info of the mission executor to std::n...
Definition: mission_executor.hpp:478
bool deferFailsafes(bool enabled, int timeout_s=0)
enable/disable deferral of failsafes
Definition: mission_executor.hpp:531
void clearTrajectoryOptions()
Reset the current trajectory config options to the mission defaults.
Definition: mission_executor.hpp:435
void onFailsafeDeferred(const std::function< void()> &callback)
register callback for failsafe notification
Definition: mission_executor.hpp:554
void runModeTakeoff(float altitude, float heading, const std::function< void()> &on_completed, const std::function< void()> &on_failure=nullptr)
Trigger a takeoff.
Definition: mission_executor.hpp:404
std::unique_ptr< ActionStateKeeper > storeState(const std::string &action_name, const ActionArguments &arguments)
store the state of a continuous action
Definition: mission_executor.hpp:510
void setCurrentMissionIndex(int index)
Set the current mission index.
Definition: mission_executor.hpp:362
Definition: health_and_arming_checks.hpp:24
Definition: mission_executor.hpp:176
void onActivate() override
Definition: mission_executor.hpp:189
Result sendCommandSync(uint32_t command, float param1, float param2, float param3, float param4, float param5, float param6, float param7) override
Definition: mission_executor.hpp:198
void onFailsafeDeferred() override
Definition: mission_executor.hpp:191
void onDeactivate(DeactivateReason reason) override
Definition: mission_executor.hpp:190
Definition: mission_executor.hpp:146
void onDeactivate() override
Definition: mission_executor.hpp:163
void checkArmingAndRunConditions(HealthAndArmingCheckReporter &reporter) override
Definition: mission_executor.hpp:158
void onActivate() override
Definition: mission_executor.hpp:162
Mission execution state machine.
Definition: mission_executor.hpp:31
void onActivityInfoChange(const std::function< void(const std::optional< std::string > &)> &callback)
Registers a callback to be invoked when the activity information changes. This callback provides a wa...
Definition: mission_executor.hpp:108
bool deferFailsafes(bool enabled, int timeout_s=0)
void onProgressUpdate(const std::function< void(int)> &callback)
Definition: mission_executor.hpp:95
void setActivityInfo(const std::optional< std::string > &activity_info)
Sets a string with extra information about the current activity. This provides more specific context ...
Mission definition.
Definition: mission.hpp:158
Base class for a mode.
Definition: mode.hpp:71
uint8_t ModeID
Mode ID, corresponds to nav_state.
Definition: mode.hpp:73
Base class for a mode executor.
Definition: mode_executor.hpp:32
virtual Result sendCommandSync(uint32_t command, float param1=NAN, float param2=NAN, float param3=NAN, float param4=NAN, float param5=NAN, float param6=NAN, float param7=NAN)
Result
Definition: mode.hpp:31
@ Rejected
The request was rejected.
Definition: mission_executor.hpp:34
Definition: mode.hpp:92
Definition: mode_executor.hpp:37
Definition: mission.hpp:121