mission_protocol_base.h
Go to the documentation of this file.
1 
10 /*
11  * Copyright 2014,2015,2016,2017,2018 Vladimir Ermakov.
12  * Copyright 2021 Charlie Burge.
13  *
14  * This file is part of the mavros package and subject to the license terms
15  * in the top-level LICENSE file of the mavros repository.
16  * https://github.com/mavlink/mavros/tree/master/LICENSE.md
17  */
18 
19 #pragma once
20 
21 #include <chrono>
22 #include <sstream>
23 #include <iomanip>
24 #include <string>
25 
26 #include <condition_variable>
27 #include <mavros/mavros_plugin.h>
28 #include <mavros/mavros_uas.h>
29 
30 #include <mavros_msgs/WaypointList.h>
31 #include <mavros_msgs/WaypointClear.h>
32 #include <mavros_msgs/WaypointPull.h>
33 #include <mavros_msgs/WaypointPush.h>
34 
35 namespace mavros {
36 namespace plugin {
37 using mavlink::common::MAV_CMD;
38 using mavlink::common::MAV_FRAME;
39 using MRES = mavlink::common::MAV_MISSION_RESULT;
40 using utils::enum_value;
41 using mavlink::common::MAV_FRAME;
42 using WP_ITEM = mavlink::common::msg::MISSION_ITEM;
43 using WP_ITEM_INT = mavlink::common::msg::MISSION_ITEM_INT;
44 using WP_TYPE = mavlink::common::MAV_MISSION_TYPE;
45 
46 static double waypoint_encode_factor( const uint8_t &frame ){
47  switch (frame) {
48  // [[[cog:
49  // from pymavlink.dialects.v20 import common
50  // e = common.enums['MAV_FRAME']
51  // all_names = [ee.name[len('MAV_FRAME_'):] for ee in e.values()]
52  // all_names.pop() # remove ENUM_END
53  // global_names = [v for v in all_names if v.startswith('GLOBAL')]
54  // local_names = [v for v in all_names if v.startswith(('LOCAL', 'BODY', 'MOCAP', 'VISION', 'ESTIM'))]
55  // other_names = ['MISSION']
56  //
57  // for names, factor in [(global_names, 10000000), (local_names, 10000), (other_names, 1)]:
58  // for name in names:
59  // cog.outl(f"case enum_value(MAV_FRAME::{name}):")
60  // cog.outl(f"\treturn {factor};")
61  //
62  // cog.outl("default:\n\treturn 1;")
63  // ]]]
64  case enum_value(MAV_FRAME::GLOBAL):
65  case enum_value(MAV_FRAME::GLOBAL_RELATIVE_ALT):
66  case enum_value(MAV_FRAME::GLOBAL_INT):
67  case enum_value(MAV_FRAME::GLOBAL_RELATIVE_ALT_INT):
68  case enum_value(MAV_FRAME::GLOBAL_TERRAIN_ALT):
69  case enum_value(MAV_FRAME::GLOBAL_TERRAIN_ALT_INT):
70  return 10000000;
71  case enum_value(MAV_FRAME::LOCAL_NED):
72  case enum_value(MAV_FRAME::LOCAL_ENU):
73  case enum_value(MAV_FRAME::LOCAL_OFFSET_NED):
74  case enum_value(MAV_FRAME::BODY_NED):
75  case enum_value(MAV_FRAME::BODY_OFFSET_NED):
76  case enum_value(MAV_FRAME::BODY_FRD):
77  case enum_value(MAV_FRAME::LOCAL_FRD):
78  case enum_value(MAV_FRAME::LOCAL_FLU):
79  return 10000;
80  case enum_value(MAV_FRAME::MISSION):
81  return 1;
82  default:
83  return 1;
84  // [[[end]]] (checksum: 152fba25e8f422b878caf990a551a6fd)
85  }
86 }
87 
88 template <class ITEM>
89 mavros_msgs::Waypoint mav_to_msg(const ITEM &mav_msg)
90 {
91  mavros_msgs::Waypoint ret;
92 
93  // [[[cog:
94  // waypoint_item_msg = [(v, v) if isinstance(v, str) else v for v in (
95  // 'frame',
96  // 'command',
97  // ('is_current', 'current'),
98  // 'autocontinue',
99  // 'param1',
100  // 'param2',
101  // 'param3',
102  // 'param4',
103  // )]
104  // waypoint_coords = [
105  // ('x_lat', 'x'),
106  // ('y_long', 'y'),
107  // ('z_alt', 'z'),
108  // ]
109  // for a, b in waypoint_item_msg + waypoint_coords:
110  // cog.outl(f"ret.{a} = mav_msg.{b};")
111  // ]]]
112  ret.frame = mav_msg.frame;
113  ret.command = mav_msg.command;
114  ret.is_current = mav_msg.current;
115  ret.autocontinue = mav_msg.autocontinue;
116  ret.param1 = mav_msg.param1;
117  ret.param2 = mav_msg.param2;
118  ret.param3 = mav_msg.param3;
119  ret.param4 = mav_msg.param4;
120  ret.x_lat = mav_msg.x;
121  ret.y_long = mav_msg.y;
122  ret.z_alt = mav_msg.z;
123  // [[[end]]] (checksum: 27badd1a5facc63f38cdd7aad3be9816)
124 
125  return ret;
126 }
127 
128 
129 template<>
130 inline mavros_msgs::Waypoint mav_to_msg(const WP_ITEM_INT &mav_msg){
131  mavros_msgs::Waypoint ret;
132 
133  // [[[cog:
134  // for a, b in waypoint_item_msg + waypoint_coords:
135  // if a.startswith(('x', 'y')):
136  // cog.outl(f"ret.{a} = mav_msg.{b} / waypoint_encode_factor(mav_msg.frame);")
137  // else:
138  // cog.outl(f"ret.{a} = mav_msg.{b};")
139  // ]]]
140  ret.frame = mav_msg.frame;
141  ret.command = mav_msg.command;
142  ret.is_current = mav_msg.current;
143  ret.autocontinue = mav_msg.autocontinue;
144  ret.param1 = mav_msg.param1;
145  ret.param2 = mav_msg.param2;
146  ret.param3 = mav_msg.param3;
147  ret.param4 = mav_msg.param4;
148  ret.x_lat = mav_msg.x / waypoint_encode_factor(mav_msg.frame);
149  ret.y_long = mav_msg.y / waypoint_encode_factor(mav_msg.frame);
150  ret.z_alt = mav_msg.z;
151  // [[[end]]] (checksum: 6c82a18990af7aeeb1db9211e9b1bbf1)
152 
153  return ret;
154 }
155 
156 
157 template <class ITEM>
158 ITEM mav_from_msg(const mavros_msgs::Waypoint &wp, const uint16_t seq, WP_TYPE type){
159  ITEM ret{};
160 
161  // [[[cog:
162  // for a, b in waypoint_item_msg + waypoint_coords:
163  // cog.outl(f"ret.{b} = wp.{a};")
164  // ]]]
165  ret.frame = wp.frame;
166  ret.command = wp.command;
167  ret.current = wp.is_current;
168  ret.autocontinue = wp.autocontinue;
169  ret.param1 = wp.param1;
170  ret.param2 = wp.param2;
171  ret.param3 = wp.param3;
172  ret.param4 = wp.param4;
173  ret.x = wp.x_lat;
174  ret.y = wp.y_long;
175  ret.z = wp.z_alt;
176  // [[[end]]] (checksum: c1b08cda34f1c4dc94129bda4743aaec)
177 
178  ret.seq = seq;
179 
180  // defaults to 0 -> MAV_MISSION_TYPE_MISSION
181  ret.mission_type = enum_value(type);
182 
183  return ret;
184 }
185 
186 template <>
187 inline WP_ITEM_INT mav_from_msg(const mavros_msgs::Waypoint &wp, const uint16_t seq, WP_TYPE type){
188  WP_ITEM_INT ret{};
189 
190  // [[[cog:
191  // for a, b in waypoint_item_msg + waypoint_coords:
192  // if b.startswith(('x', 'y')):
193  // cog.outl(f"ret.{b} = int32_t(wp.{a} * waypoint_encode_factor(wp.frame));")
194  // else:
195  // cog.outl(f"ret.{b} = wp.{a};")
196  // ]]]
197  ret.frame = wp.frame;
198  ret.command = wp.command;
199  ret.current = wp.is_current;
200  ret.autocontinue = wp.autocontinue;
201  ret.param1 = wp.param1;
202  ret.param2 = wp.param2;
203  ret.param3 = wp.param3;
204  ret.param4 = wp.param4;
205  ret.x = int32_t(wp.x_lat * waypoint_encode_factor(wp.frame));
206  ret.y = int32_t(wp.y_long * waypoint_encode_factor(wp.frame));
207  ret.z = wp.z_alt;
208  // [[[end]]] (checksum: 6315c451fe834dbf20a43ee112b8b5fe)
209 
210  ret.seq = seq;
211 
212  // defaults to 0 -> MAV_MISSION_TYPE_MISSION
213  ret.mission_type = enum_value(type);
214 
215  return ret;
216 }
217 
218 template <class ITEM>
219 std::string waypoint_to_string(const ITEM &wp)
220 {
221  std::stringstream ss;
222  ss.precision(7);
223  ss << '#' << wp.seq << (wp.current ? '*' : ' ') << " F:" << wp.frame << " C:" << std::setw(3) << wp.command;
224  ss << " p: " << wp.param1 << ' ' << wp.param2 << ' ' << wp.param3 << ' ' << wp.param4 << " x: " << wp.x << " y: " << wp.y << " z: " << wp.z;
225  return ss.str();
226 }
227 
228 
233 public:
234  MissionBase(std::string _name) :
235  PluginBase(),
236  log_ns(_name),
237  wp_state(WP::IDLE),
238  wp_type(WP_TYPE::MISSION),
239  wp_count(0),
241  wp_cur_id(0),
242  wp_cur_active(0),
243  wp_set_active(0),
244  is_timedout(false),
245  do_pull_after_gcs(false),
246  enable_partial_push(false),
247  reschedule_pull(false),
248  BOOTUP_TIME_DT(BOOTUP_TIME_MS / 1000.0),
250  WP_TIMEOUT_DT(WP_TIMEOUT_MS / 1000.0),
251  RESCHEDULE_DT(RESCHEDULE_MS / 1000.0)
252  { }
253 
254  virtual void initialize(ros::NodeHandle *_wp_nh)
255  {
257  wp_timer.stop();
258 
261  }
262 
263 protected:
264  using unique_lock = std::unique_lock<std::recursive_mutex>;
265  using lock_guard = std::lock_guard<std::recursive_mutex>;
266 
267  std::string log_ns;
268 
269  std::recursive_mutex mutex;
270 
271  std::vector<mavros_msgs::Waypoint> waypoints;
272  std::vector<mavros_msgs::Waypoint> send_waypoints;
273 
274  enum class WP {
275  IDLE,
276  RXLIST,
277  RXWP,
278  RXWPINT,
279  TXLIST,
280  TXPARTIAL,
281  TXWP,
282  TXWPINT,
283  CLEAR,
284  SET_CUR
285  };
287 
289  size_t wp_count;
290  size_t wp_start_id;
291  size_t wp_end_id;
292  size_t wp_cur_id;
295  size_t wp_retries;
299  std::condition_variable list_receiving;
300  std::condition_variable list_sending;
301 
306 
308 
311 
312  static constexpr int BOOTUP_TIME_MS = 15000;
313  static constexpr int LIST_TIMEOUT_MS = 30000;
314  static constexpr int WP_TIMEOUT_MS = 1000;
315  static constexpr int RESCHEDULE_MS = 5000;
316  static constexpr int RETRIES_COUNT = 3;
317  static constexpr unsigned int MAV_PROTOCOL_CAPABILITY_MISSION_INT = 4;
318 
323 
324  /* -*- rx handlers -*- */
325 
332  void handle_mission_item_int(const mavlink::mavlink_message_t *msg, WP_ITEM_INT &wpi);
333 
334 
341  void handle_mission_item(const mavlink::mavlink_message_t *msg, WP_ITEM &wpi);
342 
350  bool sequence_mismatch(const uint16_t &seq);
351 
358  void handle_mission_request(const mavlink::mavlink_message_t *msg, mavlink::common::msg::MISSION_REQUEST &mreq);
359 
366  void handle_mission_request_int(const mavlink::mavlink_message_t *msg, mavlink::common::msg::MISSION_REQUEST_INT &mreq);
367 
375  void handle_mission_count(const mavlink::mavlink_message_t *msg, mavlink::common::msg::MISSION_COUNT &mcnt);
376 
383  void handle_mission_ack(const mavlink::mavlink_message_t *msg, mavlink::common::msg::MISSION_ACK &mack);
384 
385  /* -*- mid-level helpers -*- */
386 
391  void timeout_cb(const ros::TimerEvent &event);
392 
395  {
396  lock_guard lock(mutex);
397  if (wp_state != WP::IDLE) {
398  /* try later */
399  ROS_DEBUG_NAMED(log_ns, "%s: busy, reschedule pull", log_ns.c_str());
400  schedule_pull(RESCHEDULE_DT);
401  return;
402  }
403 
404  ROS_DEBUG_NAMED(log_ns, "%s: start scheduled pull", log_ns.c_str());
405  wp_state = WP::RXLIST;
406  wp_count = 0;
409  }
410 
413  {
414  /* possibly not needed if count == 0 (QGC impl) */
415  mission_ack(MRES::ACCEPTED);
416 
417  go_idle();
418  list_receiving.notify_all();
419  ROS_INFO_NAMED(log_ns, "%s: mission received", log_ns.c_str());
420  }
421 
422  void go_idle(void)
423  {
424  reschedule_pull = false;
425  wp_state = WP::IDLE;
426  wp_timer.stop();
427  }
428 
430  {
431  wp_retries = RETRIES_COUNT;
433  }
434 
436  {
437  is_timedout = false;
438  wp_timer.stop();
439  wp_timer.start();
440  }
441 
442  void schedule_pull(const ros::Duration &dt)
443  {
444  schedule_timer.stop();
445  schedule_timer.setPeriod(dt);
446  schedule_timer.start();
447  }
448 
450  template <class ITEM>
451  void send_waypoint(size_t seq){
452  if (seq < send_waypoints.size()) {
453  auto wp_msg = send_waypoints.at(seq);
454  auto wpi = mav_from_msg<ITEM>(wp_msg, seq, wp_type);
455  mission_send(wpi);
456  ROS_DEBUG_STREAM_NAMED(log_ns, log_ns << ": send item " << waypoint_to_string<ITEM>(wpi));
457  }
458  }
459 
465  {
466  std::unique_lock<std::mutex> lock(recv_cond_mutex);
467  return list_receiving.wait_for(lock, std::chrono::nanoseconds(LIST_TIMEOUT_DT.toNSec()))
468  == std::cv_status::no_timeout
469  && !is_timedout;
470  }
471 
477  {
478  std::unique_lock<std::mutex> lock(send_cond_mutex);
479 
480  return list_sending.wait_for(lock, std::chrono::nanoseconds(LIST_TIMEOUT_DT.toNSec()))
481  == std::cv_status::no_timeout
482  && !is_timedout;
483  }
484 
486  void set_current_waypoint(size_t seq)
487  {
488  auto i = 0;
489  for (auto &it : waypoints) {
490  it.is_current = (i == seq) ? true : false;
491  i++;
492  }
493  }
494 
496  virtual void publish_waypoints() = 0;
497 
498  /* -*- low-level send functions -*- */
499 
500  template<class ITEM>
501  void mission_send(ITEM &wp)
502  {
503  auto wpi = wp;
504  m_uas->msg_set_target(wpi);
505  UAS_FCU(m_uas)->send_message_ignore_drop(wpi);
506  }
507 
508  void mission_request(uint16_t seq)
509  {
510  ROS_DEBUG_NAMED(log_ns, "%s:m: request #%u", log_ns.c_str(), seq);
511 
512  mavlink::common::msg::MISSION_REQUEST mrq {};
513  mrq.mission_type = enum_value(wp_type);
514  m_uas->msg_set_target(mrq);
515  mrq.seq = seq;
516 
517  UAS_FCU(m_uas)->send_message_ignore_drop(mrq);
518  }
519 
520  void mission_request_int(uint16_t seq)
521  {
522  ROS_DEBUG_NAMED(log_ns, "%s:m: request_int #%u", log_ns.c_str(), seq);
523 
524  mavlink::common::msg::MISSION_REQUEST_INT mrq {};
525  mrq.mission_type = enum_value(wp_type);
526  m_uas->msg_set_target(mrq);
527  mrq.seq = seq;
528 
529  UAS_FCU(m_uas)->send_message_ignore_drop(mrq);
530  }
531 
532  void mission_set_current(uint16_t seq)
533  {
534  ROS_DEBUG_NAMED(log_ns, "%s:m: set current #%u", log_ns.c_str(), seq);
535 
536  mavlink::common::msg::MISSION_SET_CURRENT msc {};
537  m_uas->msg_set_target(msc);
538  msc.seq = seq;
539 
540  UAS_FCU(m_uas)->send_message_ignore_drop(msc);
541  }
542 
544  {
545  ROS_DEBUG_NAMED(log_ns, "%s:m: request list", log_ns.c_str());
546 
547  mavlink::common::msg::MISSION_REQUEST_LIST mrl {};
548  mrl.mission_type = enum_value(wp_type);
549  m_uas->msg_set_target(mrl);
550 
551  UAS_FCU(m_uas)->send_message_ignore_drop(mrl);
552  }
553 
554  void mission_count(uint16_t cnt)
555  {
556  ROS_INFO_NAMED(log_ns, "%s:m: count %u", log_ns.c_str(), cnt);
557 
558  mavlink::common::msg::MISSION_COUNT mcnt {};
559  mcnt.mission_type = enum_value(wp_type);
560  mcnt.count = cnt;
561  m_uas->msg_set_target(mcnt);
562 
563  UAS_FCU(m_uas)->send_message_ignore_drop(mcnt);
564  }
565 
566  void mission_write_partial_list(uint16_t start_index, uint16_t end_index)
567  {
568  ROS_DEBUG_NAMED(log_ns, "%s:m: write partial list %u - %u",
569  log_ns.c_str(),start_index, end_index);
570 
571  mavlink::common::msg::MISSION_WRITE_PARTIAL_LIST mwpl {};
572  mwpl.start_index = start_index;
573  mwpl.end_index = end_index;
574  mwpl.mission_type = enum_value(wp_type);
575  m_uas->msg_set_target(mwpl);
576 
577  UAS_FCU(m_uas)->send_message_ignore_drop(mwpl);
578  }
579 
581  {
582  ROS_DEBUG_NAMED(log_ns, "%s:m: clear all", log_ns.c_str());
583 
584  mavlink::common::msg::MISSION_CLEAR_ALL mclr {};
585  mclr.mission_type = enum_value(wp_type);
586  m_uas->msg_set_target(mclr);
587 
588  UAS_FCU(m_uas)->send_message_ignore_drop(mclr);
589  }
590 
591  void mission_ack(MRES type) {
592  ROS_DEBUG_NAMED(log_ns, "%s:m: ACK %u", log_ns.c_str(), enum_value(type));
593 
594  mavlink::common::msg::MISSION_ACK mack {};
595  mack.type = enum_value(type);
596  mack.mission_type = enum_value(wp_type);
597  m_uas->msg_set_target(mack);
598 
599  UAS_FCU(m_uas)->send_message_ignore_drop(mack);
600  }
601 };
602 } // namespace plugin
603 } // namespace mavros
void timeout_cb(const ros::TimerEvent &event)
Act on a timeout Resend the message that may have been lost.
std::string waypoint_to_string(const ITEM &wp)
MAVROS Plugin base class.
Definition: mavros_plugin.h:36
void mission_request_int(uint16_t seq)
bool sequence_mismatch(const uint16_t &seq)
checks for a sequence mismatch between a MISSION_REQUEST(_INT) sequence and the current waypoint that...
#define ROS_INFO_NAMED(name,...)
static double waypoint_encode_factor(const uint8_t &frame)
static constexpr int LIST_TIMEOUT_MS
system startup delay before start pull
#define ROS_DEBUG_STREAM_NAMED(name, args)
void scheduled_pull_cb(const ros::TimerEvent &event)
Callback for scheduled waypoint pull.
mavlink::common::MAV_MISSION_TYPE WP_TYPE
mavros_msgs::Waypoint mav_to_msg(const ITEM &mav_msg)
MAVROS Plugin base.
void stop()
void setPeriod(const Duration &period, bool reset=true)
void handle_mission_item_int(const mavlink::mavlink_message_t *msg, WP_ITEM_INT &wpi)
handle MISSION_ITEM_INT mavlink msg handles and stores mission items when pulling waypoints ...
void start()
void set_current_waypoint(size_t seq)
set the FCU current waypoint
void send_waypoint(size_t seq)
send a single waypoint to FCU
void msg_set_target(_T &msg)
Definition: mavros_uas.h:388
virtual void publish_waypoints()=0
publish the updated waypoint list after operation
void handle_mission_count(const mavlink::mavlink_message_t *msg, mavlink::common::msg::MISSION_COUNT &mcnt)
handle MISSION_COUNT mavlink msg Handles a mission count from FCU in a Waypoint Pull Triggers a pull ...
void mission_set_current(uint16_t seq)
void request_mission_done(void)
Send ACK back to FCU after pull.
#define ROS_DEBUG_NAMED(name,...)
mavlink::common::msg::MISSION_ITEM WP_ITEM
std::lock_guard< std::recursive_mutex > lock_guard
mavlink::common::msg::MISSION_ITEM_INT WP_ITEM_INT
int64_t toNSec() const
#define UAS_FCU(uasobjptr)
helper accessor to FCU link interface
Definition: mavros_uas.h:42
virtual void initialize(ros::NodeHandle *_wp_nh)
Timer createTimer(Rate r, Handler h, Obj o, bool oneshot=false, bool autostart=true) const
bool wait_fetch_all()
wait until a waypoint pull is complete. Pull happens asynchronously, this function blocks until it is...
static constexpr int BOOTUP_TIME_MS
std::vector< mavros_msgs::Waypoint > send_waypoints
bool wait_push_all()
wait until a waypoint push is complete. Push happens asynchronously, this function blocks until it is...
void mission_write_partial_list(uint16_t start_index, uint16_t end_index)
static constexpr int WP_TIMEOUT_MS
Timeout for pull/push operations.
static constexpr int RESCHEDULE_MS
std::unique_lock< std::recursive_mutex > unique_lock
std::condition_variable list_receiving
std::condition_variable list_sending
MAVROS Plugin context.
void handle_mission_request_int(const mavlink::mavlink_message_t *msg, mavlink::common::msg::MISSION_REQUEST_INT &mreq)
handle MISSION_REQUEST_INT mavlink msg handles and acts on misison request from FCU ...
mavlink::common::MAV_MISSION_RESULT MRES
void handle_mission_item(const mavlink::mavlink_message_t *msg, WP_ITEM &wpi)
handle MISSION_ITEM mavlink msg handles and stores mission items when pulling waypoints ...
ITEM mav_from_msg(const mavros_msgs::Waypoint &wp, const uint16_t seq, WP_TYPE type)
static constexpr int RETRIES_COUNT
void handle_mission_ack(const mavlink::mavlink_message_t *msg, mavlink::common::msg::MISSION_ACK &mack)
handle MISSION_ACK mavlink msg Handles a MISSION_ACK which marks the end of a push, or a failure
void schedule_pull(const ros::Duration &dt)
void handle_mission_request(const mavlink::mavlink_message_t *msg, mavlink::common::msg::MISSION_REQUEST &mreq)
handle MISSION_REQUEST mavlink msg handles and acts on misison request from FCU
std::vector< mavros_msgs::Waypoint > waypoints
static constexpr unsigned int MAV_PROTOCOL_CAPABILITY_MISSION_INT
constexpr std::underlying_type< _T >::type enum_value(_T e)
Definition: utils.h:55
std::recursive_mutex mutex


mavros
Author(s): Vladimir Ermakov
autogenerated on Tue Jun 1 2021 02:36:26