mission.py
Go to the documentation of this file.
1 import rospy
2 import actionlib
3 import clearpath_navigation_msgs.msg
6 
7 
8 # The name of the action server to which missions are sent.
9 # This needs to match the CPR OutdoorNav API.
10 MISSION_ACTION_NAME = 'mission'
11 
12 
13 class Mission:
14  """A sequence of waypoints with optional tasks."""
15 
16  def __init__(self, name, uuid, waypoints, on_start_tasks, on_stop_tasks,
17  from_start=True, start_waypoint_num=0, onav_config="",
18  progress_callback=None, done_callback=None):
19  """Builds up the data structure containing the details of the mission.
20 
21  Parameters
22  ----------
23  name : str
24  The name of the mission
25 
26  uuid : str
27  UUID string used to uniquely identify this mission
28 
29  waypoints : Waypoint[]
30  A list of Waypoint objects, to be executed in sequence
31 
32  on_start : Task[]
33  An array of Tasks to execute on Mission start
34 
35  on_stop : Task[]
36  An array of Tasks to execute on Mission stop. These
37  Tasks will execute regardless of mission success or failure
38 
39  from_start: bool
40  Whether or not the mission should start from the first Waypoint
41 
42  start_waypoint_num: int
43  The index representing the Waypoint in the list of Waypoint, from which the
44  mission should start.
45 
46  onav_config: str
47  Configuration parameters for the mission
48 
49  progress_callback : fn(feedback_str), optional
50  A callback function that is called at regular intervals while the mission
51  is being executed to provide progress updates. The function should take
52  the "feedback_str" string parameter. If set to None, no feedback will be provided
53  and the mission will execute to completion.
54 
55  done_callback : fn(bool), optional
56  A callback function that is called when the mission is complete. The function
57  should take one parameter: a bool to indicate if the mission was completed
58  successfully.
59  """
60 
61  self._name = name
62  self._uuid = uuid
63  self._waypoints = waypoints
64  self._on_start_tasks = on_start_tasks
65  self._on_stop_tasks = on_stop_tasks
66  self._from_start = from_start
67  self._start_waypoint_num = start_waypoint_num
68  self._onav_config = onav_config
69  self._mission_progress_callback = progress_callback
70  self._mission_done_callback = done_callback
71  self._mission_success = False
72  self._mission_complete = True
73  self._client = None
74 
75  def _toMissionMsg(self):
76  """Gets the mission message.
77 
78  Returns
79  -------
80  clearpath_navigation_msgs.msg.Mission
81  the mission message
82  """
83 
84  mission_msg = clearpath_navigation_msgs.msg.Mission()
85  mission_msg.name = self._name
86  mission_msg.uuid = self._uuid
87  mission_msg.onav_config = self._onav_config
88  waypoint_msgs = []
89  for waypoint in self._waypoints:
90  waypoint_msgs.append(waypoint.getWaypointMsg())
91  mission_msg.waypoints = waypoint_msgs
92  on_start_tasks = []
93  for task in self._on_start_tasks:
94  on_start_tasks.append(task.getTaskMsg())
95  mission_msg.on_start = on_start_tasks
96  on_stop_tasks = []
97  for task in self._on_stop_tasks:
98  on_stop_tasks.append(task.getTaskMsg())
99  mission_msg.on_stop = on_stop_tasks
100  return mission_msg
101 
102  def _missionFeedbackCallback(self, feedback):
103  """Executes when 'feedback' is received from the mission execution.
104 
105  Forwards the callback to the user-provided mission feedback callback.
106  """
107 
108  rospy.logdebug("Mission %s: %s" % (self._name, feedback.state))
109  if self._mission_progress_callback is not None:
110  self._mission_progress_callback(feedback)
111 
112  def _missionDoneCallback(self, mission_state, result):
113  """Executes when 'done' is received from the mission execution.
114 
115  Forwards the callback to the user-provided mission done callback.
116  """
117 
118  # Due to a bug, the result field is not getting filled in properly at
119  # present. Once the bug is fixed, should check "result.success" rather
120  # than just "result".
121  self._mission_complete = True
122  if mission_state != actionlib.GoalStatus.SUCCEEDED or not result:
123  mission_result_str = "Failed"
124  self._mission_success = False
125  rospy.logdebug("Mission complete, but not successful.")
126  else:
127  mission_result_str = "Succeeded"
128  self._mission_success = True
129  mission_feedback = "Mission %s: %s" % (self._name, mission_result_str)
130  rospy.logdebug(mission_feedback)
131  if self._mission_done_callback is not None:
133 
134  def _sendMission(self):
135  """Sends the mission to the action server.
136 
137  Returns
138  -------
139  bool
140  True on successful dispatch, else False
141  """
142 
143  if self._client is None:
144  rospy.logerr('Action client not started. Cannot send mission.')
145  return False
146 
147  self._mission_complete = False
148  mission_goal_msg = clearpath_navigation_msgs.msg.MissionGoal()
149  mission_goal_msg.mission = self._toMissionMsg()
150  mission_goal_msg.from_start = self._from_start
151  if not mission_goal_msg.from_start:
152  mission_goal_msg.start_waypoint = self._waypoints[self._start_waypoint_num]
153  try:
154  # Waits until the action server has started up and started
155  # listening for goals, then sends the goal
156  print('Trying to connect to mission server...')
157  if not self._client.wait_for_server(timeout=rospy.Duration()):
158  rospy.logerr("Failed to connect to server")
159  return False
160  print('Connected to mission server')
161  self._client.send_goal(mission_goal_msg,
162  feedback_cb=self._missionFeedbackCallback,
163  done_cb=self._missionDoneCallback)
164  print('Mission executing...')
165  except rospy.ROSInterruptException:
166  rospy.logerr("mission server is not available; exception detected")
167  return False
168  return True
169 
170  def getName(self):
171  """Gets the name of the mission
172 
173  Returns
174  -------
175  str
176  The name of the mission
177  """
178 
179  return self._name
180 
181  def setCallbacks(self, progress_callback=None, done_callback=None):
182  """Set the callbacks for the mission.
183 
184  Provides a way to set the callbacks if not done during object creation,
185  which is the case when creating the object from a YAML file.
186 
187  Parameters
188  ----------
189  progress_callback : fn(feedback_str), optional
190  A callback function that is called at regular intervals while the mission
191  is being executed to provide progress updates. The function should take
192  the "feedback_str" string parameter. If set to None, no feedback will be provided
193  and the mission will execute to completion.
194 
195  done_callback : fn(bool), optional
196  A callback function that is called when the mission is complete. The function
197  should take one parameters: a bool to indicate if the mission was completed
198  successfully.
199  """
200 
201  self._mission_progress_callback = progress_callback
202  self._mission_done_callback = done_callback
203 
204  def getMissionSuccess(self):
205  """Gets the current status of the mission.
206 
207  If the mission is still in progress, reports the current status. If the
208  mission is complete, reports the overall mission status.
209 
210  See also isMissionComplete().
211 
212  Returns
213  -------
214  bool
215  True if the mission is running successfully so far, else False
216  """
217 
218  return self._mission_success
219 
220  def isMissionComplete(self):
221  """Determines if the mission is complete.
222 
223  See also getMissionSuccess().
224 
225  Returns
226  -------
227  bool
228  True if the mission is complete, else False if mission is still running
229  """
230 
231  return self._mission_complete
232 
233  def startMission(self):
234  """Starts the execution of the mission.
235 
236  This function returns as soon as the mission is started. See isMissionComplete()
237  and getMissionSuccess() to determine mission status. If there is already an
238  active mission, a new one cannot be started until the active mission completes
239  or has been stopped.
240 
241  See also stopMission().
242 
243  Returns
244  -------
245  bool
246  True on successful mission start, else False
247  """
248 
249  # Connect to the action server, if it is not already started
250  if self._client is None:
251  print('Creating mission client')
252  self._client = actionlib.SimpleActionClient(MISSION_ACTION_NAME,
253  clearpath_navigation_msgs.msg.MissionAction)
254  # Do not start a new mission if one is already active
255  if not self.isMissionComplete():
256  rospy.logwarn("Existing mission is still active. Cannot start new mission.")
257  return False
258 
259  success = self._sendMission()
260  if not success:
261  rospy.logerr("Cannot send mission. Failed to start mission.")
262  # Initialize the mission success, for final reporting
263  self._mission_success = success
264  return success
265 
266  def stopMission(self):
267  """Stops the mission that is currently executing.
268 
269  Returns
270  -------
271  bool
272  True if the mission was stopped successfully, else False"""
273 
274  if self._client is not None:
275  self._client.cancel_all_goals()
276  rospy.logdebug("Stopping mission. All goals cancelled. Setting success to False.")
277  self._mission_success = False
278  self._mission_complete = True
279 
280  def toYaml(self):
281  """Converts the mission to YAML format, for writing to disk.
282 
283  Returns
284  -------
285  dict
286  A YAML-compatible dictionary representation of the object.
287  """
288 
289  waypoints = self._waypoints
290  waypoints_yaml = []
291  for waypoint in waypoints:
292  waypoint_yaml = waypoint.toYaml()
293  waypoints_yaml.append(waypoint_yaml)
294 
295  on_start_tasks = self._on_start_tasks
296  on_start_tasks_yaml = []
297  for task in on_start_tasks:
298  on_start_task_yaml = task.toYaml()
299  on_start_tasks_yaml.append(on_start_task_yaml)
300 
301  on_stop_tasks = self._on_stop_tasks
302  on_stop_tasks_yaml = []
303  for task in on_stop_tasks:
304  on_stop_task_yaml = task.toYaml()
305  on_stop_tasks_yaml.append(on_stop_task_yaml)
306 
307  mission_yaml = {
308  'name': self._name,
309  'uuid': self._uuid,
310  'waypoints': waypoints_yaml,
311  'on_start_tasks': on_start_tasks_yaml,
312  'on_stop_tasks': on_stop_tasks_yaml,
313  'onav_config': self._onav_config
314  }
315  return mission_yaml
316 
317  @staticmethod
318  def fromYaml(mission_yaml):
319  """Creates a Mission object from a YAML-compatible dictionary representation.
320 
321  Parameters
322  ----------
323  mission_yaml : dict
324  The YAML-compatible dictionary representation of a Mission object
325  """
326 
327  if 'name' not in mission_yaml:
328  rospy.logerr("yamlFileToMission: missing 'name' field")
329  return None
330  if 'uuid' not in mission_yaml:
331  rospy.logerr("yamlFileToMission: missing 'uuid' field")
332  return None
333  if 'waypoints' not in mission_yaml:
334  rospy.logerr("yamlFileToMission: missing 'waypoints' field")
335  return None
336  if 'on_start_tasks' not in mission_yaml:
337  rospy.logerr("yamlFileToMission: missing 'on_start_tasks' field")
338  return None
339  if 'on_stop_tasks' not in mission_yaml:
340  rospy.logerr("yamlFileToMission: missing 'on_stop_tasks' field")
341  return None
342  if 'onav_config' not in mission_yaml:
343  rospy.logerr("yamlFileToMission: missing 'onav_config' field")
344  return None
345  name = mission_yaml['name']
346  uuid = mission_yaml['uuid']
347  onav_config = mission_yaml['onav_config']
348  waypoints = []
349  waypoints_yaml = mission_yaml['waypoints']
350  for waypoint_yaml in waypoints_yaml:
351  waypoint = Waypoint.fromYaml(waypoint_yaml)
352  if waypoint is None:
353  rospy.logerr("yamlFileToMission: could not parse 'waypoints'")
354  return None
355  waypoints.append(waypoint)
356 
357  on_start_tasks = []
358  on_start_tasks_yaml = mission_yaml['on_start_tasks']
359  for on_start_task_yaml in on_start_tasks_yaml:
360  task = Task.fromYaml(on_start_task_yaml)
361  if task is None:
362  rospy.logerr("yamlFileToMission: could not parse 'on_start_tasks'")
363  return None
364  on_start_tasks.append(task)
365 
366  on_stop_tasks = []
367  on_stop_tasks_yaml = mission_yaml['on_stop_tasks']
368  for on_stop_task_yaml in on_stop_tasks_yaml:
369  task = Task.fromYaml(on_stop_task_yaml)
370  if task is None:
371  rospy.logerr("yamlFileToMission: could not parse 'on_stop_tasks'")
372  return None
373  on_stop_tasks.append(task)
374 
375  return Mission(name, uuid, waypoints, on_start_tasks, on_stop_tasks, onav_config)
clearpath_onav_api_examples_lib.mission.Mission.__init__
def __init__(self, name, uuid, waypoints, on_start_tasks, on_stop_tasks, from_start=True, start_waypoint_num=0, onav_config="", progress_callback=None, done_callback=None)
Definition: mission.py:16
clearpath_onav_api_examples_lib.mission.Mission._on_stop_tasks
_on_stop_tasks
Definition: mission.py:63
clearpath_onav_api_examples_lib.mission.Mission._uuid
_uuid
Definition: mission.py:60
clearpath_onav_api_examples_lib.mission.Mission._start_waypoint_num
_start_waypoint_num
Definition: mission.py:65
clearpath_onav_api_examples_lib.mission.Mission.toYaml
def toYaml(self)
Definition: mission.py:280
clearpath_onav_api_examples_lib.mission.Mission._mission_success
_mission_success
Definition: mission.py:69
clearpath_onav_api_examples_lib.mission.Mission.getName
def getName(self)
Definition: mission.py:170
clearpath_onav_api_examples_lib.mission.Mission._toMissionMsg
def _toMissionMsg(self)
Definition: mission.py:75
clearpath_onav_api_examples_lib.mission.Mission.getMissionSuccess
def getMissionSuccess(self)
Definition: mission.py:204
clearpath_onav_api_examples_lib.mission.Mission._mission_done_callback
_mission_done_callback
Definition: mission.py:68
clearpath_onav_api_examples_lib.mission.Mission._on_start_tasks
_on_start_tasks
Definition: mission.py:62
clearpath_onav_api_examples_lib.mission.Mission.stopMission
def stopMission(self)
Definition: mission.py:266
actionlib::SimpleActionClient
clearpath_onav_api_examples_lib.mission.Mission._client
_client
Definition: mission.py:71
clearpath_onav_api_examples_lib.mission.Mission._onav_config
_onav_config
Definition: mission.py:66
clearpath_onav_api_examples_lib.mission.Mission
Definition: mission.py:13
clearpath_onav_api_examples_lib.mission.Mission._waypoints
_waypoints
Definition: mission.py:61
clearpath_onav_api_examples_lib.mission.Mission._from_start
_from_start
Definition: mission.py:64
clearpath_onav_api_examples_lib.mission.Mission._missionDoneCallback
def _missionDoneCallback(self, mission_state, result)
Definition: mission.py:112
clearpath_onav_api_examples_lib.mission.Mission.isMissionComplete
def isMissionComplete(self)
Definition: mission.py:220
clearpath_onav_api_examples_lib.mission.Mission.startMission
def startMission(self)
Definition: mission.py:233
clearpath_onav_api_examples_lib.task
Definition: task.py:1
clearpath_onav_api_examples_lib.waypoint
Definition: waypoint.py:1
clearpath_onav_api_examples_lib.mission.Mission._sendMission
def _sendMission(self)
Definition: mission.py:134
clearpath_onav_api_examples_lib.mission.Mission._mission_progress_callback
_mission_progress_callback
Definition: mission.py:67
clearpath_onav_api_examples_lib.mission.Mission.fromYaml
def fromYaml(mission_yaml)
Definition: mission.py:318
clearpath_onav_api_examples_lib.mission.Mission._mission_complete
_mission_complete
Definition: mission.py:70
clearpath_onav_api_examples_lib.mission.Mission._name
_name
Definition: mission.py:59
clearpath_onav_api_examples_lib.mission.Mission.setCallbacks
def setCallbacks(self, progress_callback=None, done_callback=None)
Definition: mission.py:181
clearpath_onav_api_examples_lib.mission.Mission._missionFeedbackCallback
def _missionFeedbackCallback(self, feedback)
Definition: mission.py:102


clearpath_onav_api_examples_lib
Author(s):
autogenerated on Sun Nov 12 2023 03:29:19