Codebase list ros-actionlib / ff37531
Imported Upstream version 1.11.6 Jochen Sprickerhof 7 years ago
14 changed file(s) with 699 addition(s) and 718 deletion(s). Raw diff Collapse all Expand all
00 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11 Changelog for package actionlib
22 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3
4 1.11.6 (2016-06-22)
5 -------------------
6 * Python code cleanup (`#43 <https://github.com/ros/actionlib/issues/43>`_)
7 * Cleaned up semicolons, indentation, spaces.
8 * Removed unused local var after further confirmation of no risk of side effects.
9 * Contributors: Andrew Blakey
10
11 1.11.5 (2016-03-14)
12 -------------------
13 * update maintainer
14 * Merge pull request `#42 <https://github.com/ros/actionlib/issues/42>`_ from jonbinney/python3-compat
15 Python 3 compatibility changes
16 * More readable iteration in state name lookup
17 * Update syntax for exception handling
18 * Iterate over dictionary in python3 compatible way
19 * Use absolute imports for python3 compatibility
20 * Merge pull request `#39 <https://github.com/ros/actionlib/issues/39>`_ from clearpathrobotics/action-fixup
21 Minor improvements
22 * Enable UI feedback for preempt-requested goal in axserver.py
23 * Clean up axclient.py initialization to allow starting before actionserver, requires action type passed in
24 * Add hashes to ServerGoalHandle and ClientGoalHandles
25 * Contributors: Esteve Fernandez, Jon Binney, Mikael Arguedas, Paul Bovbel
326
427 1.11.4 (2015-04-22)
528 -------------------
00 <package>
11 <name>actionlib</name>
2 <version>1.11.4</version>
2 <version>1.11.6</version>
33 <description>
44 The actionlib stack provides a standardized interface for
55 interfacing with preemptable tasks. Examples of this include moving
66 the base to a target location, performing a laser scan and returning
77 the resulting point cloud, detecting the handle of a door, etc.
88 </description>
9 <maintainer email="esteve@osrfoundation.org">Esteve Fernandez</maintainer>
9 <maintainer email="mikael@osrfoundation.org">Mikael Arguedas</maintainer>
1010 <license>BSD</license>
1111
1212 <url type="website">http://www.ros.org/wiki/actionlib</url>
13 <url type="bugtracker">https://github.com/ros/actionlib/issues</url>
14 <url type="repository">https://github.com/ros/actionlib</url>
1315 <author>Eitan Marder-Eppstein</author>
1416 <author>Vijay Pradeep</author>
1517
00 # Copyright (c) 2009, Willow Garage, Inc.
11 # All rights reserved.
2 #
2 #
33 # Redistribution and use in source and binary forms, with or without
44 # modification, are permitted provided that the following conditions are met:
5 #
5 #
66 # * Redistributions of source code must retain the above copyright
77 # notice, this list of conditions and the following disclaimer.
88 # * Redistributions in binary form must reproduce the above copyright
1111 # * Neither the name of the Willow Garage, Inc. nor the names of its
1212 # contributors may be used to endorse or promote products derived from
1313 # this software without specific prior written permission.
14 #
14 #
1515 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1616 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1717 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2424 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2525 # POSSIBILITY OF SUCH DAMAGE.
2626
27 from action_client import *
28 from simple_action_client import *
29 from action_server import *
30 from simple_action_server import *
31
32
33
27 from actionlib.action_client import *
28 from actionlib.simple_action_client import *
29 from actionlib.action_server import *
30 from actionlib.simple_action_server import *
5959
6060 g_goal_id = 1
6161
62
6263 def get_name_of_constant(C, n):
63 for k,v in C.__dict__.iteritems():
64 for k, v in C.__dict__.items():
6465 if type(v) is int and v == n:
6566 return k
6667 return "NO_SUCH_STATE_%d" % n
7778 DONE = 7
7879 LOST = 8
7980
81
8082 class TerminalState(object):
8183 RECALLED = GoalStatus.RECALLED
8284 REJECTED = GoalStatus.REJECTED
9092 CommState.to_string = classmethod(get_name_of_constant)
9193 TerminalState.to_string = classmethod(get_name_of_constant)
9294
95
9396 def _find_status_by_goal_id(status_array, id):
9497 for s in status_array.status_list:
9598 if s.goal_id.id == id:
9699 return s
97100 return None
101
98102
99103 ## @brief Client side handle to monitor goal progress.
100104 ##
110114 def __init__(self, comm_state_machine):
111115 self.comm_state_machine = comm_state_machine
112116
113 #print "GH created. id = %.3f" % self.comm_state_machine.action_goal.goal_id.stamp.to_sec()
117 # print "GH created. id = %.3f" % self.comm_state_machine.action_goal.goal_id.stamp.to_sec()
114118
115119 ## @brief True iff the two ClientGoalHandle's are tracking the same goal
116120 def __eq__(self, o):
124128 return True
125129 return not (self.comm_state_machine == o.comm_state_machine)
126130
131 ## @brieft Hash function for ClientGoalHandle
132 def __hash__(self):
133 return hash(self.comm_state_machine)
127134
128135 ## @brief Sends a cancel message for this specific goal to the ActionServer.
129136 ##
130137 ## Also transitions the client state to WAITING_FOR_CANCEL_ACK
131138 def cancel(self):
132139 with self.comm_state_machine.mutex:
133 cancel_msg = GoalID(stamp = rospy.Time(0),
134 id = self.comm_state_machine.action_goal.goal_id.id)
140 cancel_msg = GoalID(stamp=rospy.Time(0),
141 id=self.comm_state_machine.action_goal.goal_id.id)
135142 self.comm_state_machine.send_cancel_fn(cancel_msg)
136143 self.comm_state_machine.transition_to(CommState.WAITING_FOR_CANCEL_ACK)
137144
196203 with self.comm_state_machine.mutex:
197204 if self.comm_state_machine.state != CommState.DONE:
198205 rospy.logwarn("Asking for the terminal state when we're in [%s]",
199 CommState.to_string(self.comm_state_machine.state))
206 CommState.to_string(self.comm_state_machine.state))
200207
201208 goal_status = self.comm_state_machine.latest_goal_status.status
202209 if goal_status in [GoalStatus.PREEMPTED, GoalStatus.SUCCEEDED,
206213
207214 rospy.logerr("Asking for a terminal state, but the goal status is %d", goal_status)
208215 return GoalStatus.LOST
209
210
211216
212217
213218 NO_TRANSITION = -1
222227 GoalStatus.PREEMPTED: (CommState.ACTIVE, CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
223228 GoalStatus.SUCCEEDED: (CommState.ACTIVE, CommState.WAITING_FOR_RESULT),
224229 GoalStatus.ABORTED: (CommState.ACTIVE, CommState.WAITING_FOR_RESULT),
225 GoalStatus.PREEMPTING: (CommState.ACTIVE, CommState.PREEMPTING) },
230 GoalStatus.PREEMPTING: (CommState.ACTIVE, CommState.PREEMPTING)},
226231 CommState.PENDING: {
227232 GoalStatus.PENDING: NO_TRANSITION,
228233 GoalStatus.ACTIVE: CommState.ACTIVE,
232237 GoalStatus.PREEMPTED: (CommState.ACTIVE, CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
233238 GoalStatus.SUCCEEDED: (CommState.ACTIVE, CommState.WAITING_FOR_RESULT),
234239 GoalStatus.ABORTED: (CommState.ACTIVE, CommState.WAITING_FOR_RESULT),
235 GoalStatus.PREEMPTING: (CommState.ACTIVE, CommState.PREEMPTING) },
240 GoalStatus.PREEMPTING: (CommState.ACTIVE, CommState.PREEMPTING)},
236241 CommState.ACTIVE: {
237242 GoalStatus.PENDING: INVALID_TRANSITION,
238243 GoalStatus.ACTIVE: NO_TRANSITION,
242247 GoalStatus.PREEMPTED: (CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
243248 GoalStatus.SUCCEEDED: CommState.WAITING_FOR_RESULT,
244249 GoalStatus.ABORTED: CommState.WAITING_FOR_RESULT,
245 GoalStatus.PREEMPTING: CommState.PREEMPTING },
250 GoalStatus.PREEMPTING: CommState.PREEMPTING},
246251 CommState.WAITING_FOR_RESULT: {
247252 GoalStatus.PENDING: INVALID_TRANSITION,
248253 GoalStatus.ACTIVE: NO_TRANSITION,
252257 GoalStatus.PREEMPTED: NO_TRANSITION,
253258 GoalStatus.SUCCEEDED: NO_TRANSITION,
254259 GoalStatus.ABORTED: NO_TRANSITION,
255 GoalStatus.PREEMPTING: INVALID_TRANSITION },
260 GoalStatus.PREEMPTING: INVALID_TRANSITION},
256261 CommState.WAITING_FOR_CANCEL_ACK: {
257262 GoalStatus.PENDING: NO_TRANSITION,
258263 GoalStatus.ACTIVE: NO_TRANSITION,
262267 GoalStatus.PREEMPTED: (CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
263268 GoalStatus.SUCCEEDED: (CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
264269 GoalStatus.ABORTED: (CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
265 GoalStatus.PREEMPTING: CommState.PREEMPTING },
270 GoalStatus.PREEMPTING: CommState.PREEMPTING},
266271 CommState.RECALLING: {
267272 GoalStatus.PENDING: INVALID_TRANSITION,
268273 GoalStatus.ACTIVE: INVALID_TRANSITION,
272277 GoalStatus.PREEMPTED: (CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
273278 GoalStatus.SUCCEEDED: (CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
274279 GoalStatus.ABORTED: (CommState.PREEMPTING, CommState.WAITING_FOR_RESULT),
275 GoalStatus.PREEMPTING: CommState.PREEMPTING },
280 GoalStatus.PREEMPTING: CommState.PREEMPTING},
276281 CommState.PREEMPTING: {
277282 GoalStatus.PENDING: INVALID_TRANSITION,
278283 GoalStatus.ACTIVE: INVALID_TRANSITION,
282287 GoalStatus.PREEMPTED: CommState.WAITING_FOR_RESULT,
283288 GoalStatus.SUCCEEDED: CommState.WAITING_FOR_RESULT,
284289 GoalStatus.ABORTED: CommState.WAITING_FOR_RESULT,
285 GoalStatus.PREEMPTING: NO_TRANSITION },
290 GoalStatus.PREEMPTING: NO_TRANSITION},
286291 CommState.DONE: {
287292 GoalStatus.PENDING: INVALID_TRANSITION,
288293 GoalStatus.ACTIVE: INVALID_TRANSITION,
292297 GoalStatus.PREEMPTED: NO_TRANSITION,
293298 GoalStatus.SUCCEEDED: NO_TRANSITION,
294299 GoalStatus.ABORTED: NO_TRANSITION,
295 GoalStatus.PREEMPTING: INVALID_TRANSITION } }
296
300 GoalStatus.PREEMPTING: INVALID_TRANSITION}}
297301
298302
299303 class CommStateMachine:
306310
307311 self.state = CommState.WAITING_FOR_GOAL_ACK
308312 self.mutex = threading.RLock()
309 self.latest_goal_status = GoalStatus(status = GoalStatus.PENDING)
313 self.latest_goal_status = GoalStatus(status=GoalStatus.PENDING)
310314 self.latest_result = None
311315
312316 def __eq__(self, o):
313317 return self.action_goal.goal_id.id == o.action_goal.goal_id.id
318
319 ## @brieft Hash function for CommStateMachine
320 def __hash__(self):
321 return hash(self.action_goal.goal_id.id)
314322
315323 def set_state(self, state):
316324 rospy.logdebug("Transitioning CommState from %s to %s",
403411 if self.action_goal.goal_id.id != action_feedback.status.goal_id.id:
404412 return
405413
406 #with self.mutex:
414 # with self.mutex:
407415 if self.feedback_cb and self.state != CommState.DONE:
408416 self.feedback_cb(ClientGoalHandle(self), action_feedback.feedback)
409417
431439 global g_goal_id
432440 id, g_goal_id = g_goal_id, g_goal_id + 1
433441 now = rospy.Time.now()
434 return GoalID(id = "%s-%i-%.3f" % \
435 (rospy.get_caller_id(), id, now.to_sec()), stamp = now)
442 return GoalID(id="%s-%i-%.3f" % (rospy.get_caller_id(), id, now.to_sec()), stamp=now)
436443
437444 def register_send_goal_fn(self, fn):
438445 self.send_goal_fn = fn
446
439447 def register_cancel_fn(self, fn):
440448 self.cancel_fn = fn
441449
442450 ## Sends off a goal and starts tracking its status.
443451 ##
444452 ## @return ClientGoalHandle for the sent goal.
445 def init_goal(self, goal, transition_cb = None, feedback_cb = None):
446 action_goal = self.ActionGoal(header = Header(),
447 goal_id = self._generate_id(),
448 goal = goal)
453 def init_goal(self, goal, transition_cb=None, feedback_cb=None):
454 action_goal = self.ActionGoal(header=Header(),
455 goal_id=self._generate_id(),
456 goal=goal)
449457 action_goal.header.stamp = rospy.get_rostime()
450458
451459 csm = CommStateMachine(action_goal, transition_cb, feedback_cb,
457465 self.send_goal_fn(action_goal)
458466
459467 return ClientGoalHandle(csm)
460
461468
462469 # Pulls out the statuses that are still live (creating strong
463470 # references to them)
467474 live_statuses = filter(lambda x: x, live_statuses)
468475 return live_statuses
469476
470
471477 ## Updates the statuses of all goals from the information in status_array.
472478 ##
473479 ## @param status_array (\c actionlib_msgs/GoalStatusArray)
474480 def update_statuses(self, status_array):
475 live_statuses = []
476481
477482 with self.list_mutex:
478483 # Garbage collects dead status objects
481486 for status in self._get_live_statuses():
482487 status.update_status(status_array)
483488
484
485489 def update_results(self, action_result):
486490 for status in self._get_live_statuses():
487491 status.update_result(action_result)
489493 def update_feedbacks(self, action_feedback):
490494 for status in self._get_live_statuses():
491495 status.update_feedback(action_feedback)
496
492497
493498 class ActionClient:
494499 ## @brief Constructs an ActionClient and opens connections to an ActionServer.
537542 ## message.
538543 ##
539544 ## @return ClientGoalHandle for the sent goal.
540 def send_goal(self, goal, transition_cb = None, feedback_cb = None):
545 def send_goal(self, goal, transition_cb=None, feedback_cb=None):
541546 return self.manager.init_goal(goal, transition_cb, feedback_cb)
542547
543548 ## @brief Cancels all goals currently running on the action server.
545550 ## Preempts all goals running on the action server at the point
546551 ## that the cancel message is serviced by the action server.
547552 def cancel_all_goals(self):
548 cancel_msg = GoalID(stamp = rospy.Time.from_sec(0.0),
549 id = "")
553 cancel_msg = GoalID(stamp=rospy.Time.from_sec(0.0), id="")
550554 self.pub_cancel.publish(cancel_msg)
551555
552556 ## @brief Cancels all goals prior to a given timestamp
553557 ##
554 ## This preempts all goals running on the action server for which the
558 ## This preempts all goals running on the action server for which the
555559 ## time stamp is earlier than the specified time stamp
556560 ## this message is serviced by the ActionServer.
557561
558562 def cancel_goals_at_and_before_time(self, time):
559 cancel_msg = GoalID(stamp = time, id = "")
563 cancel_msg = GoalID(stamp=time, id="")
560564 self.pub_cancel.publish(cancel_msg)
561
562
565
566
563567 ## @brief [Deprecated] Use wait_for_server
564 def wait_for_action_server_to_start(self, timeout = rospy.Duration(0.0)):
568 def wait_for_action_server_to_start(self, timeout=rospy.Duration(0.0)):
565569 return self.wait_for_server(timeout)
566570
567571 ## @brief Waits for the ActionServer to connect to this client
569573 ## Often, it can take a second for the action server & client to negotiate
570574 ## a connection, thus, risking the first few goals to be dropped. This call lets
571575 ## the user wait until the network connection to the server is negotiated
572 def wait_for_server(self, timeout = rospy.Duration(0.0)):
576 def wait_for_server(self, timeout=rospy.Duration(0.0)):
573577 started = False
574578 timeout_time = rospy.get_rostime() + timeout
575579 while not rospy.is_shutdown():
578582
579583 if self.pub_goal.impl.has_connection(server_id) and \
580584 self.pub_cancel.impl.has_connection(server_id):
581 #We'll also check that all of the subscribers have at least
582 #one publisher, this isn't a perfect check, but without
583 #publisher callbacks... it'll have to do
585 # We'll also check that all of the subscribers have at least
586 # one publisher, this isn't a perfect check, but without
587 # publisher callbacks... it'll have to do
584588 status_num_pubs = 0
585589 for stat in self.status_sub.impl.get_stats()[1]:
586590 if stat[4]:
616620
617621 def _feedback_cb(self, msg):
618622 self.manager.update_feedbacks(msg)
619
620
2727 #
2828 # Author: Alexander Sorokin.
2929 # Based on C++ action_server.h by Eitan Marder-Eppstein
30
3130 import rospy
32
3331 import threading
3432
3533 from actionlib_msgs.msg import *
3634
37 from goal_id_generator import GoalIDGenerator
38 from status_tracker import StatusTracker
39
40 from handle_tracker_deleter import HandleTrackerDeleter
41 from server_goal_handle import ServerGoalHandle
35 from actionlib.goal_id_generator import GoalIDGenerator
36 from actionlib.status_tracker import StatusTracker
37
38 from actionlib.handle_tracker_deleter import HandleTrackerDeleter
39 from actionlib.server_goal_handle import ServerGoalHandle
4240
4341 from actionlib.exceptions import *
4442
4644 def nop_cb(goal_handle):
4745 pass
4846
49 def ros_timer(callable,frequency):
47
48 def ros_timer(callable, frequency):
5049 rate = rospy.Rate(frequency)
5150
52 rospy.logdebug("Starting timer");
51 rospy.logdebug("Starting timer")
5352 while not rospy.is_shutdown():
5453 try:
5554 rate.sleep()
5655 callable()
5756 except rospy.exceptions.ROSInterruptException:
58 rospy.logdebug("Sleep interrupted");
57 rospy.logdebug("Sleep interrupted")
58
5959
6060 ## @class ActionServer
6161 ## @brief The ActionServer is a helpful tool for managing goal requests to a
7272 ## @param goal_cb A goal callback to be called when the ActionServer receives a new goal over the wire
7373 ## @param cancel_cb A cancel callback to be called when the ActionServer receives a new cancel request over the wire
7474 ## @param auto_start A boolean value that tells the ActionServer wheteher or not to start publishing as soon as it comes up. THIS SHOULD ALWAYS BE SET TO FALSE TO AVOID RACE CONDITIONS and start() should be called after construction of the server.
75 def __init__(self, ns, ActionSpec, goal_cb, cancel_cb = nop_cb, auto_start = True):
76 self.ns=ns;
75 def __init__(self, ns, ActionSpec, goal_cb, cancel_cb=nop_cb, auto_start=True):
76 self.ns = ns
7777
7878 try:
7979 a = ActionSpec()
8686 except AttributeError:
8787 raise ActionException("Type is not an action spec: %s" % str(ActionSpec))
8888
89
9089 self.goal_sub = None
9190 self.cancel_sub = None
9291 self.status_pub = None
9594
9695 self.lock = threading.RLock()
9796
98 self.status_timer = None;
99
100 self.status_list = [];
101
102 self.last_cancel = rospy.Time();
103 self.status_list_timeout = rospy.Duration();
104
105 self.id_generator = GoalIDGenerator();
106
107 self.goal_callback = goal_cb;
108 assert(self.goal_callback);
109
110 self.cancel_callback = cancel_cb;
111 self.auto_start = auto_start;
112
113 self.started = False;
97 self.status_timer = None
98
99 self.status_list = []
100
101 self.last_cancel = rospy.Time()
102 self.status_list_timeout = rospy.Duration()
103
104 self.id_generator = GoalIDGenerator()
105
106 self.goal_callback = goal_cb
107 assert(self.goal_callback)
108
109 self.cancel_callback = cancel_cb
110 self.auto_start = auto_start
111
112 self.started = False
114113
115114 if self.auto_start:
116 rospy.logwarn("You've passed in true for auto_start to the python action server, you should always pass in false to avoid race conditions.")
115 rospy.logwarn("You've passed in true for auto_start to the python action server, you should always pass "
116 "in false to avoid race conditions.")
117117 self.start()
118118
119119
124124
125125 ## @brief Register a callback to be invoked when a new cancel is received, this will replace any previously registered callback
126126 ## @param cb The callback to invoke
127 def register_cancel_callback(self,cancel_cb):
128 self.cancel_callback = cancel_cb
127 def register_cancel_callback(self, cancel_cb):
128 self.cancel_callback = cancel_cb
129129
130130 ## @brief Start the action server
131131 def start(self):
132132 with self.lock:
133 self.initialize();
134 self.started = True;
135 self.publish_status();
133 self.initialize()
134 self.started = True
135 self.publish_status()
136136
137137 ## @brief Initialize all ROS connections and setup timers
138138 def initialize(self):
139 self.status_pub = rospy.Publisher(rospy.remap_name(self.ns)+"/status", GoalStatusArray, latch=True, queue_size=50);
140 self.result_pub = rospy.Publisher(rospy.remap_name(self.ns)+"/result", self.ActionResult, queue_size=50);
141 self.feedback_pub = rospy.Publisher(rospy.remap_name(self.ns)+"/feedback", self.ActionFeedback, queue_size=50);
142
143 self.goal_sub = rospy.Subscriber(rospy.remap_name(self.ns)+"/goal", self.ActionGoal,self.internal_goal_callback);
144
145 self.cancel_sub = rospy.Subscriber(rospy.remap_name(self.ns)+"/cancel", GoalID,self.internal_cancel_callback);
146
147 #read the frequency with which to publish status from the parameter server
148 #if not specified locally explicitly, use search param to find actionlib_status_frequency
149 resolved_status_frequency_name = rospy.remap_name(self.ns)+"/status_frequency"
150 if rospy.has_param(resolved_status_frequency_name):
151 self.status_frequency = rospy.get_param(resolved_status_frequency_name, 5.0);
152 rospy.logwarn("You're using the deprecated status_frequency parameter, please switch to actionlib_status_frequency.")
153 else:
154 search_status_frequency_name = rospy.search_param("actionlib_status_frequency")
155 if search_status_frequency_name is None:
156 self.status_frequency = 5.0
157 else:
158 self.status_frequency = rospy.get_param(search_status_frequency_name, 5.0)
159
160 status_list_timeout = rospy.get_param(rospy.remap_name(self.ns)+"/status_list_timeout", 5.0);
161 self.status_list_timeout = rospy.Duration(status_list_timeout);
162
163
164 self.status_timer = threading.Thread( None, ros_timer, None, (self.publish_status_async,self.status_frequency) );
165 self.status_timer.start();
139 self.status_pub = rospy.Publisher(rospy.remap_name(self.ns)+"/status", GoalStatusArray, latch=True, queue_size=50)
140 self.result_pub = rospy.Publisher(rospy.remap_name(self.ns)+"/result", self.ActionResult, queue_size=50)
141 self.feedback_pub = rospy.Publisher(rospy.remap_name(self.ns)+"/feedback", self.ActionFeedback, queue_size=50)
142
143 self.goal_sub = rospy.Subscriber(rospy.remap_name(self.ns)+"/goal", self.ActionGoal, self.internal_goal_callback)
144
145 self.cancel_sub = rospy.Subscriber(rospy.remap_name(self.ns)+"/cancel", GoalID, self.internal_cancel_callback)
146
147 # read the frequency with which to publish status from the parameter server
148 # if not specified locally explicitly, use search param to find actionlib_status_frequency
149 resolved_status_frequency_name = rospy.remap_name(self.ns)+"/status_frequency"
150 if rospy.has_param(resolved_status_frequency_name):
151 self.status_frequency = rospy.get_param(resolved_status_frequency_name, 5.0)
152 rospy.logwarn("You're using the deprecated status_frequency parameter, please switch to actionlib_status_frequency.")
153 else:
154 search_status_frequency_name = rospy.search_param("actionlib_status_frequency")
155 if search_status_frequency_name is None:
156 self.status_frequency = 5.0
157 else:
158 self.status_frequency = rospy.get_param(search_status_frequency_name, 5.0)
159
160 status_list_timeout = rospy.get_param(rospy.remap_name(self.ns)+"/status_list_timeout", 5.0)
161 self.status_list_timeout = rospy.Duration(status_list_timeout)
162
163 self.status_timer = threading.Thread(None, ros_timer, None, (self.publish_status_async, self.status_frequency))
164 self.status_timer.start()
166165
167166 ## @brief Publishes a result for a given goal
168167 ## @param status The status of the goal with which the result is associated
169168 ## @param result The result to publish
170169 def publish_result(self, status, result):
171170 with self.lock:
172 ar = self.ActionResult();
173 ar.header.stamp = rospy.Time.now();
174 ar.status = status;
175 ar.result = result;
176 self.result_pub.publish(ar);
171 ar = self.ActionResult()
172 ar.header.stamp = rospy.Time.now()
173 ar.status = status
174 ar.result = result
175 self.result_pub.publish(ar)
177176 self.publish_status()
178177
179178
181180 ## @param status The status of the goal with which the feedback is associated
182181 ## @param feedback The feedback to publish
183182 def publish_feedback(self, status, feedback):
184 with self.lock:
185
186 af=self.ActionFeedback();
187 af.header.stamp = rospy.Time.now();
188 af.status = status;
189 af.feedback = feedback;
190 self.feedback_pub.publish(af);
183 with self.lock:
184 af = self.ActionFeedback()
185 af.header.stamp = rospy.Time.now()
186 af.status = status
187 af.feedback = feedback
188 self.feedback_pub.publish(af)
191189
192190
193191 ## @brief The ROS callback for cancel requests coming into the ActionServer
194192 def internal_cancel_callback(self, goal_id):
195 with self.lock:
196
197 #if we're not started... then we're not actually going to do anything
198 if not self.started:
199 return;
200
201 #we need to handle a cancel for the user
202 rospy.logdebug("The action server has received a new cancel request");
203
204 goal_id_found = False;
205 for st in self.status_list[:]:
206 #check if the goal id is zero or if it is equal to the goal id of
207 #the iterator or if the time of the iterator warrants a cancel
208
209 cancel_everything = (goal_id.id == "" and goal_id.stamp == rospy.Time() ) #rospy::Time()) #id and stamp 0 --> cancel everything
210 cancel_this_one = ( goal_id.id == st.status.goal_id.id) #ids match... cancel that goal
211 cancel_before_stamp = (goal_id.stamp != rospy.Time() and st.status.goal_id.stamp <= goal_id.stamp) #//stamp != 0 --> cancel everything before stamp
212
213 if cancel_everything or cancel_this_one or cancel_before_stamp:
214 #we need to check if we need to store this cancel request for later
215 if goal_id.id == st.status.goal_id.id:
216 goal_id_found = True;
217
218 #attempt to get the handle_tracker for the list item if it exists
219 handle_tracker = st.handle_tracker;
220
221 if handle_tracker is None:
222 #if the handle tracker is expired, then we need to create a new one
223 handle_tracker = HandleTrackerDeleter(self, st);
224 st.handle_tracker = handle_tracker;
225
226 #we also need to reset the time that the status is supposed to be removed from the list
227 st.handle_destruction_time = rospy.Time.now()
228
229
230 #set the status of the goal to PREEMPTING or RECALLING as approriate
231 #and check if the request should be passed on to the user
232 gh = ServerGoalHandle(st, self, handle_tracker);
233 if gh.set_cancel_requested():
234 #call the user's cancel callback on the relevant goal
235 self.cancel_callback(gh);
236
237
238 #if the requested goal_id was not found, and it is non-zero, then we need to store the cancel request
239 if goal_id.id != "" and not goal_id_found:
240 tracker= StatusTracker(goal_id, actionlib_msgs.msg.GoalStatus.RECALLING);
241 self.status_list.append(tracker)
242 #start the timer for how long the status will live in the list without a goal handle to it
243 tracker.handle_destruction_time = rospy.Time.now()
244
245 #make sure to set last_cancel_ based on the stamp associated with this cancel request
246 if goal_id.stamp > self.last_cancel:
247 self.last_cancel = goal_id.stamp;
193 with self.lock:
194
195 # if we're not started... then we're not actually going to do anything
196 if not self.started:
197 return
198
199 # we need to handle a cancel for the user
200 rospy.logdebug("The action server has received a new cancel request")
201
202 goal_id_found = False
203 for st in self.status_list[:]:
204 # check if the goal id is zero or if it is equal to the goal id of
205 # the iterator or if the time of the iterator warrants a cancel
206
207 cancel_everything = (goal_id.id == "" and goal_id.stamp == rospy.Time()) # rospy::Time()) #id and stamp 0 --> cancel everything
208 cancel_this_one = (goal_id.id == st.status.goal_id.id) # ids match... cancel that goal
209 cancel_before_stamp = (goal_id.stamp != rospy.Time() and st.status.goal_id.stamp <= goal_id.stamp) # //stamp != 0 --> cancel everything before stamp
210
211 if cancel_everything or cancel_this_one or cancel_before_stamp:
212 # we need to check if we need to store this cancel request for later
213 if goal_id.id == st.status.goal_id.id:
214 goal_id_found = True
215
216 # attempt to get the handle_tracker for the list item if it exists
217 handle_tracker = st.handle_tracker
218
219 if handle_tracker is None:
220 # if the handle tracker is expired, then we need to create a new one
221 handle_tracker = HandleTrackerDeleter(self, st)
222 st.handle_tracker = handle_tracker
223
224 # we also need to reset the time that the status is supposed to be removed from the list
225 st.handle_destruction_time = rospy.Time.now()
226
227 # set the status of the goal to PREEMPTING or RECALLING as approriate
228 # and check if the request should be passed on to the user
229 gh = ServerGoalHandle(st, self, handle_tracker)
230 if gh.set_cancel_requested():
231 # call the user's cancel callback on the relevant goal
232 self.cancel_callback(gh)
233
234 # if the requested goal_id was not found, and it is non-zero, then we need to store the cancel request
235 if goal_id.id != "" and not goal_id_found:
236 tracker = StatusTracker(goal_id, actionlib_msgs.msg.GoalStatus.RECALLING)
237 self.status_list.append(tracker)
238 # start the timer for how long the status will live in the list without a goal handle to it
239 tracker.handle_destruction_time = rospy.Time.now()
240
241 # make sure to set last_cancel_ based on the stamp associated with this cancel request
242 if goal_id.stamp > self.last_cancel:
243 self.last_cancel = goal_id.stamp
248244
249245
250246 ## @brief The ROS callback for goals coming into the ActionServer
251247 def internal_goal_callback(self, goal):
252 with self.lock:
253 #if we're not started... then we're not actually going to do anything
254 if not self.started:
255 return;
256
257 rospy.logdebug("The action server has received a new goal request");
258
259 #we need to check if this goal already lives in the status list
260 for st in self.status_list[:]:
261 if goal.goal_id.id == st.status.goal_id.id:
262 rospy.logdebug("Goal %s was already in the status list with status %i" % (goal.goal_id.id, st.status.status))
263 # Goal could already be in recalling state if a cancel came in before the goal
264 if st.status.status == actionlib_msgs.msg.GoalStatus.RECALLING:
265 st.status.status = actionlib_msgs.msg.GoalStatus.RECALLED
266 self.publish_result(st.status, self.ActionResultType())
267
268 #if this is a request for a goal that has no active handles left,
269 #we'll bump how long it stays in the list
270 if st.handle_tracker is None:
271 st.handle_destruction_time = rospy.Time.now()
272
273 #make sure not to call any user callbacks or add duplicate status onto the list
274 return;
275
276 #if the goal is not in our list, we need to create a StatusTracker associated with this goal and push it on
277 st = StatusTracker(None,None,goal)
278 self.status_list.append(st);
279
280 #we need to create a handle tracker for the incoming goal and update the StatusTracker
281 handle_tracker = HandleTrackerDeleter(self, st);
282
283 st.handle_tracker = handle_tracker;
284
285 #check if this goal has already been canceled based on its timestamp
286 gh= ServerGoalHandle(st, self, handle_tracker);
287 if goal.goal_id.stamp != rospy.Time() and goal.goal_id.stamp <= self.last_cancel:
288 #if it has... just create a GoalHandle for it and setCanceled
289 gh.set_canceled(None, "This goal handle was canceled by the action server because its timestamp is before the timestamp of the last cancel request");
290 else:
291 #now, we need to create a goal handle and call the user's callback
292 self.goal_callback(gh);
293
248 with self.lock:
249 # if we're not started... then we're not actually going to do anything
250 if not self.started:
251 return
252
253 rospy.logdebug("The action server has received a new goal request")
254
255 # we need to check if this goal already lives in the status list
256 for st in self.status_list[:]:
257 if goal.goal_id.id == st.status.goal_id.id:
258 rospy.logdebug("Goal %s was already in the status list with status %i" % (goal.goal_id.id, st.status.status))
259 # Goal could already be in recalling state if a cancel came in before the goal
260 if st.status.status == actionlib_msgs.msg.GoalStatus.RECALLING:
261 st.status.status = actionlib_msgs.msg.GoalStatus.RECALLED
262 self.publish_result(st.status, self.ActionResultType())
263
264 # if this is a request for a goal that has no active handles left,
265 # we'll bump how long it stays in the list
266 if st.handle_tracker is None:
267 st.handle_destruction_time = rospy.Time.now()
268
269 # make sure not to call any user callbacks or add duplicate status onto the list
270 return
271
272 # if the goal is not in our list, we need to create a StatusTracker associated with this goal and push it on
273 st = StatusTracker(None, None, goal)
274 self.status_list.append(st)
275
276 # we need to create a handle tracker for the incoming goal and update the StatusTracker
277 handle_tracker = HandleTrackerDeleter(self, st)
278
279 st.handle_tracker = handle_tracker
280
281 # check if this goal has already been canceled based on its timestamp
282 gh = ServerGoalHandle(st, self, handle_tracker)
283 if goal.goal_id.stamp != rospy.Time() and goal.goal_id.stamp <= self.last_cancel:
284 # if it has... just create a GoalHandle for it and setCanceled
285 gh.set_canceled(None, "This goal handle was canceled by the action server because its timestamp is before the timestamp of the last cancel request")
286 else:
287 # now, we need to create a goal handle and call the user's callback
288 self.goal_callback(gh)
294289
295290 ## @brief Publish status for all goals on a timer event
296291 def publish_status_async(self):
297 rospy.logdebug("Status async");
298 with self.lock:
299 #we won't publish status unless we've been started
300 if not self.started:
301 return
302 self.publish_status();
303
304
292 rospy.logdebug("Status async")
293 with self.lock:
294 # we won't publish status unless we've been started
295 if not self.started:
296 return
297 self.publish_status()
305298
306299 ## @brief Explicitly publish status
307300 def publish_status(self):
308 with self.lock:
309 #build a status array
310 status_array = actionlib_msgs.msg.GoalStatusArray()
311
312 #status_array.set_status_list_size(len(self.status_list));
313
314 i=0;
315 while i<len(self.status_list):
316 st = self.status_list[i]
317 #check if the item is due for deletion from the status list
318 if st.handle_destruction_time != rospy.Time() and st.handle_destruction_time + self.status_list_timeout < rospy.Time.now():
319 rospy.logdebug("Item %s with destruction time of %.3f being removed from list. Now = %.3f" % \
301 with self.lock:
302 # build a status array
303 status_array = actionlib_msgs.msg.GoalStatusArray()
304
305 # status_array.set_status_list_size(len(self.status_list));
306
307 i = 0
308 while i < len(self.status_list):
309 st = self.status_list[i]
310 # check if the item is due for deletion from the status list
311 if st.handle_destruction_time != rospy.Time() and st.handle_destruction_time + self.status_list_timeout < rospy.Time.now():
312 rospy.logdebug("Item %s with destruction time of %.3f being removed from list. Now = %.3f" %
320313 (st.status.goal_id, st.handle_destruction_time.to_sec(), rospy.Time.now().to_sec()))
321 del self.status_list[i]
322 else:
323 status_array.status_list.append(st.status);
324 i+=1
325
326
327 status_array.header.stamp = rospy.Time.now()
328 self.status_pub.publish(status_array)
329
330
314 del self.status_list[i]
315 else:
316 status_array.status_list.append(st.status)
317 i += 1
318
319 status_array.header.stamp = rospy.Time.now()
320 self.status_pub.publish(status_array)
2727
2828 # Author: Jonathan Bohren
2929
30 class ActionException(Exception): pass
30
31 class ActionException(Exception):
32 pass
00 #! /usr/bin/env python
11 # Copyright (c) 2009, Willow Garage, Inc.
22 # All rights reserved.
3 #
3 #
44 # Redistribution and use in source and binary forms, with or without
55 # modification, are permitted provided that the following conditions are met:
6 #
6 #
77 # * Redistributions of source code must retain the above copyright
88 # notice, this list of conditions and the following disclaimer.
99 # * Redistributions in binary form must reproduce the above copyright
1212 # * Neither the name of the Willow Garage, Inc. nor the names of its
1313 # contributors may be used to endorse or promote products derived from
1414 # this software without specific prior written permission.
15 #
15 #
1616 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1717 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1818 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2525 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2626 # POSSIBILITY OF SUCH DAMAGE.
2727
28 # Author: Alexander Sorokin.
28 # Author: Alexander Sorokin.
2929 # Based on C++ goal_id_generator.h/cpp
30
3130 import rospy
32
33 import sys
3431
3532 from actionlib_msgs.msg import GoalID
3633 import threading
3734
3835 global s_goalcount_lock
3936 global s_goalcount
40 s_goalcount_lock = threading.Lock();
37 s_goalcount_lock = threading.Lock()
4138 s_goalcount = 0
4239
4340
4441 class GoalIDGenerator:
4542
46
47 def __init__(self,name=None):
43 def __init__(self, name=None):
4844 """
4945 * Create a generator that prepends the fully qualified node name to the Goal ID
5046 * \param name Unique name to prepend to the goal id. This will
5349 if name is not None:
5450 self.set_name(name)
5551 else:
56 self.set_name(rospy.get_name());
52 self.set_name(rospy.get_name())
5753
58
59 def set_name(self,name):
54 def set_name(self, name):
6055 """
6156 * \param name Set the name to prepend to the goal id. This will
6257 * generally be a fully qualified node name.
6358 """
64 self.name=name;
65
66
59 self.name = name
6760
6861 def generate_ID(self):
6962 """
7063 * \brief Generates a unique ID
7164 * \return A unique GoalID for this action
7265 """
73 id = GoalID();
74 cur_time = rospy.Time.now();
75 ss = self.name + "-";
66 id = GoalID()
67 cur_time = rospy.Time.now()
68 ss = self.name + "-"
7669 global s_goalcount_lock
7770 global s_goalcount
7871 with s_goalcount_lock:
7972 s_goalcount += 1
80 ss += str(s_goalcount) + "-";
81 ss += str(cur_time.secs) + "." + str(cur_time.nsecs);
73 ss += str(s_goalcount) + "-"
74 ss += str(cur_time.secs) + "." + str(cur_time.nsecs)
8275
83 id.id = ss;
84 id.stamp = cur_time;
85 return id;
76 id.id = ss
77 id.stamp = cur_time
78 return id
00 # Copyright (c) 2009, Willow Garage, Inc.
11 # All rights reserved.
2 #
2 #
33 # Redistribution and use in source and binary forms, with or without
44 # modification, are permitted provided that the following conditions are met:
5 #
5 #
66 # * Redistributions of source code must retain the above copyright
77 # notice, this list of conditions and the following disclaimer.
88 # * Redistributions in binary form must reproduce the above copyright
1111 # * Neither the name of the Willow Garage, Inc. nor the names of its
1212 # contributors may be used to endorse or promote products derived from
1313 # this software without specific prior written permission.
14 #
14 #
1515 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1616 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1717 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2424 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2525 # POSSIBILITY OF SUCH DAMAGE.
2626
27 # Author: Alexander Sorokin.
27 # Author: Alexander Sorokin.
2828 # Based on C++ goal_id_generator.h/cpp
29 import rospy
2930
30 import rospy
3131
3232 class HandleTrackerDeleter:
3333 """
4141 """
4242 @brief create deleter
4343 """
44 self.action_server = action_server;
45 self.status_tracker = status_tracker;
44 self.action_server = action_server
45 self.status_tracker = status_tracker
4646
47 def __call__(self,ptr):
47 def __call__(self, ptr):
4848 if self.action_server:
4949 with self.action_server.lock:
50 self.status_tracker.handle_destruction_time = rospy.Time.now();
50 self.status_tracker.handle_destruction_time = rospy.Time.now()
00 # Copyright (c) 2009, Willow Garage, Inc.
11 # All rights reserved.
2 #
2 #
33 # Redistribution and use in source and binary forms, with or without
44 # modification, are permitted provided that the following conditions are met:
5 #
5 #
66 # * Redistributions of source code must retain the above copyright
77 # notice, this list of conditions and the following disclaimer.
88 # * Redistributions in binary form must reproduce the above copyright
1111 # * Neither the name of the Willow Garage, Inc. nor the names of its
1212 # contributors may be used to endorse or promote products derived from
1313 # this software without specific prior written permission.
14 #
14 #
1515 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1616 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1717 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2424 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2525 # POSSIBILITY OF SUCH DAMAGE.
2626
27 # Author: Alexander Sorokin.
27 # Author: Alexander Sorokin.
2828 # Based on C++ goal_id_generator.h/cpp
29
3029 import rospy
3130
3231 import actionlib_msgs.msg
32
3333
3434 class ServerGoalHandle:
3535 """
4545 A private constructor used by the ActionServer to initialize a ServerGoalHandle.
4646 @node The default constructor was not ported.
4747 """
48 self.status_tracker = status_tracker;
48 self.status_tracker = status_tracker
4949 self.action_server = action_server
50 self.handle_tracker = handle_tracker;
50 self.handle_tracker = handle_tracker
5151
5252 if status_tracker:
53 self.goal=status_tracker.goal;
54 else:
55 self.goal=None
56
53 self.goal = status_tracker.goal
54 else:
55 self.goal = None
5756
5857 def get_default_result(self):
59 return self.action_server.ActionResultType();
58 return self.action_server.ActionResultType()
6059
6160 def set_accepted(self, text=""):
62 """
63 Accept the goal referenced by the goal handle. This will
64 transition to the ACTIVE state or the PREEMPTING state depending
65 on whether a cancel request has been received for the goal
66 """
67
68 rospy.logdebug("Accepting goal, id: %s, stamp: %.2f", self.get_goal_id().id, self.get_goal_id().stamp.to_sec());
69 if self.goal:
70 with self.action_server.lock:
71 status = self.status_tracker.status.status;
72
73 #if we were pending before, then we'll go active
74 if(status == actionlib_msgs.msg.GoalStatus.PENDING):
75 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.ACTIVE;
76 self.status_tracker.status.text = text
77 self.action_server.publish_status();
78
79 #if we were recalling before, now we'll go to preempting
80 elif(status == actionlib_msgs.msg.GoalStatus.RECALLING) :
81 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.PREEMPTING;
82 self.status_tracker.status.text = text
83 self.action_server.publish_status();
84
85 else:
86 rospy.logerr("To transition to an active state, the goal must be in a pending or recalling state, it is currently in state: %d", self.status_tracker.status.status);
87
88 else:
89 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle");
90
61 """
62 Accept the goal referenced by the goal handle. This will
63 transition to the ACTIVE state or the PREEMPTING state depending
64 on whether a cancel request has been received for the goal
65 """
66
67 rospy.logdebug("Accepting goal, id: %s, stamp: %.2f", self.get_goal_id().id, self.get_goal_id().stamp.to_sec());
68 if self.goal:
69 with self.action_server.lock:
70 status = self.status_tracker.status.status
71
72 # if we were pending before, then we'll go active
73 if status == actionlib_msgs.msg.GoalStatus.PENDING:
74 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.ACTIVE
75 self.status_tracker.status.text = text
76 self.action_server.publish_status()
77
78 # if we were recalling before, now we'll go to preempting
79 elif status == actionlib_msgs.msg.GoalStatus.RECALLING:
80 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.PREEMPTING
81 self.status_tracker.status.text = text
82 self.action_server.publish_status()
83
84 else:
85 rospy.logerr("To transition to an active state, the goal must be in a pending or recalling state, it is currently in state: %d", self.status_tracker.status.status)
86
87 else:
88 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle")
9189
9290 def set_canceled(self, result=None, text=""):
9391 """
9694 @param result Optionally, the user can pass in a result to be sent to any clients of the goal
9795 """
9896 if not result:
99 result=self.get_default_result();
100
101 rospy.logdebug("Setting status to canceled on goal, id: %s, stamp: %.2f", self.get_goal_id().id, self.get_goal_id().stamp.to_sec());
102
103 if(self.goal):
97 result = self.get_default_result()
98
99 rospy.logdebug("Setting status to canceled on goal, id: %s, stamp: %.2f", self.get_goal_id().id, self.get_goal_id().stamp.to_sec())
100
101 if self.goal:
104102 with self.action_server.lock:
105103 status = self.status_tracker.status.status;
106 if(status == actionlib_msgs.msg.GoalStatus.PENDING or status == actionlib_msgs.msg.GoalStatus.RECALLING):
107 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.RECALLED;
108 self.status_tracker.status.text = text
109 #on transition to a terminal state, we'll also set the handle destruction time
110 self.status_tracker.handle_destruction_time = rospy.Time.now()
111 self.action_server.publish_result(self.status_tracker.status, result);
112 elif(status == actionlib_msgs.msg.GoalStatus.ACTIVE or status == actionlib_msgs.msg.GoalStatus.PREEMPTING):
113 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.PREEMPTED;
114 self.status_tracker.status.text = text
115 #on transition to a terminal state, we'll also set the handle destruction time
116 self.status_tracker.handle_destruction_time = rospy.Time.now()
117 self.action_server.publish_result(self.status_tracker.status, result);
118
104 if status == actionlib_msgs.msg.GoalStatus.PENDING or status == actionlib_msgs.msg.GoalStatus.RECALLING:
105 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.RECALLED
106 self.status_tracker.status.text = text
107 # on transition to a terminal state, we'll also set the handle destruction time
108 self.status_tracker.handle_destruction_time = rospy.Time.now()
109 self.action_server.publish_result(self.status_tracker.status, result)
110 elif status == actionlib_msgs.msg.GoalStatus.ACTIVE or status == actionlib_msgs.msg.GoalStatus.PREEMPTING:
111 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.PREEMPTED
112 self.status_tracker.status.text = text
113 # on transition to a terminal state, we'll also set the handle destruction time
114 self.status_tracker.handle_destruction_time = rospy.Time.now()
115 self.action_server.publish_result(self.status_tracker.status, result)
116
119117 else:
120118 rospy.logerr("To transition to a cancelled state, the goal must be in a pending, recalling, active, or preempting state, it is currently in state: %d",
121 self.status_tracker.status.status);
122
123 else:
124 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle");
125
119 self.status_tracker.status.status)
120
121 else:
122 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle")
126123
127124 def set_rejected(self, result=None, text=""):
128125 """
130127 * @param result Optionally, the user can pass in a result to be sent to any clients of the goal
131128 """
132129 if not result:
133 result=self.get_default_result();
134
135 rospy.logdebug("Setting status to rejected on goal, id: %s, stamp: %.2f", self.get_goal_id().id, self.get_goal_id().stamp.to_sec());
136 if self.goal :
137 with self.action_server.lock:
138 status = self.status_tracker.status.status;
139 if(status == actionlib_msgs.msg.GoalStatus.PENDING or status == actionlib_msgs.msg.GoalStatus.RECALLING):
140 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.REJECTED;
141 self.status_tracker.status.text = text
142 #on transition to a terminal state, we'll also set the handle destruction time
143 self.status_tracker.handle_destruction_time = rospy.Time.now()
144 self.action_server.publish_result(self.status_tracker.status, result);
145
130 result = self.get_default_result()
131
132 rospy.logdebug("Setting status to rejected on goal, id: %s, stamp: %.2f", self.get_goal_id().id, self.get_goal_id().stamp.to_sec())
133 if self.goal:
134 with self.action_server.lock:
135 status = self.status_tracker.status.status
136 if status == actionlib_msgs.msg.GoalStatus.PENDING or status == actionlib_msgs.msg.GoalStatus.RECALLING:
137 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.REJECTED
138 self.status_tracker.status.text = text
139 # on transition to a terminal state, we'll also set the handle destruction time
140 self.status_tracker.handle_destruction_time = rospy.Time.now()
141 self.action_server.publish_result(self.status_tracker.status, result)
142
146143 else:
147144 rospy.logerr("To transition to a rejected state, the goal must be in a pending or recalling state, it is currently in state: %d",
148 self.status_tracker.status.status);
149
150 else:
151 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle");
152
145 self.status_tracker.status.status)
146
147 else:
148 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle")
153149
154150 def set_aborted(self, result=None, text=""):
155151 """
157153 @param result Optionally, the user can pass in a result to be sent to any clients of the goal
158154 """
159155 if not result:
160 result=self.get_default_result();
161
162 rospy.logdebug("Setting status to aborted on goal, id: %s, stamp: %.2f", self.get_goal_id().id, self.get_goal_id().stamp.to_sec());
163 if self.goal:
164 with self.action_server.lock:
165 status = self.status_tracker.status.status;
156 result = self.get_default_result()
157
158 rospy.logdebug("Setting status to aborted on goal, id: %s, stamp: %.2f", self.get_goal_id().id, self.get_goal_id().stamp.to_sec())
159 if self.goal:
160 with self.action_server.lock:
161 status = self.status_tracker.status.status
166162 if status == actionlib_msgs.msg.GoalStatus.PREEMPTING or status == actionlib_msgs.msg.GoalStatus.ACTIVE:
167 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.ABORTED;
168 self.status_tracker.status.text = text
169 #on transition to a terminal state, we'll also set the handle destruction time
170 self.status_tracker.handle_destruction_time = rospy.Time.now()
171 self.action_server.publish_result(self.status_tracker.status, result);
172
163 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.ABORTED
164 self.status_tracker.status.text = text
165 # on transition to a terminal state, we'll also set the handle destruction time
166 self.status_tracker.handle_destruction_time = rospy.Time.now()
167 self.action_server.publish_result(self.status_tracker.status, result)
168
173169 else:
174170 rospy.logerr("To transition to an aborted state, the goal must be in a preempting or active state, it is currently in state: %d",
175 status);
176
177 else:
178 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle");
179
180
181 def set_succeeded(self,result=None, text=""):
171 status)
172
173 else:
174 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle")
175
176 def set_succeeded(self, result=None, text=""):
182177 """
183178 Set the status of the goal associated with the ServerGoalHandle to succeeded
184179 @param result Optionally, the user can pass in a result to be sent to any clients of the goal
185180 """
186181 if not result:
187 result=self.get_default_result();
182 result = self.get_default_result()
188183
189184 rospy.logdebug("Setting status to succeeded on goal, id: %s, stamp: %.2f",
190 self.get_goal_id().id, self.get_goal_id().stamp.to_sec());
191 if self.goal:
192 with self.action_server.lock:
193 status = self.status_tracker.status.status;
194 if status == actionlib_msgs.msg.GoalStatus.PREEMPTING or status == actionlib_msgs.msg.GoalStatus.ACTIVE :
195 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.SUCCEEDED;
196 self.status_tracker.status.text = text
197 #on transition to a terminal state, we'll also set the handle destruction time
198 self.status_tracker.handle_destruction_time = rospy.Time.now()
199 self.action_server.publish_result(self.status_tracker.status, result);
200
185 self.get_goal_id().id, self.get_goal_id().stamp.to_sec())
186 if self.goal:
187 with self.action_server.lock:
188 status = self.status_tracker.status.status
189 if status == actionlib_msgs.msg.GoalStatus.PREEMPTING or status == actionlib_msgs.msg.GoalStatus.ACTIVE:
190 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.SUCCEEDED
191 self.status_tracker.status.text = text
192 # on transition to a terminal state, we'll also set the handle destruction time
193 self.status_tracker.handle_destruction_time = rospy.Time.now()
194 self.action_server.publish_result(self.status_tracker.status, result)
195
201196 else:
202197 rospy.logerr("To transition to a succeeded state, the goal must be in a preempting or active state, it is currently in state: %d",
203 status);
204
205 else:
206 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle");
207
208
209 def publish_feedback(self,feedback):
198 status)
199
200 else:
201 rospy.logerr("Attempt to set status on an uninitialized ServerGoalHandle")
202
203 def publish_feedback(self, feedback):
210204 """
211205 Send feedback to any clients of the goal associated with this ServerGoalHandle
212206 @param feedback The feedback to send to the client
213207 """
214 rospy.logdebug("Publishing feedback for goal, id: %s, stamp: %.2f",
215 self.get_goal_id().id, self.get_goal_id().stamp.to_sec());
216 if self.goal:
217 with self.action_server.lock:
218 self.action_server.publish_feedback(self.status_tracker.status, feedback);
219 else:
220 rospy.logerr("Attempt to publish feedback on an uninitialized ServerGoalHandle");
221
208 rospy.logdebug("Publishing feedback for goal, id: %s, stamp: %.2f",
209 self.get_goal_id().id, self.get_goal_id().stamp.to_sec())
210 if self.goal:
211 with self.action_server.lock:
212 self.action_server.publish_feedback(self.status_tracker.status, feedback)
213 else:
214 rospy.logerr("Attempt to publish feedback on an uninitialized ServerGoalHandle")
222215
223216 def get_goal(self):
224217 """
225218 Accessor for the goal associated with the ServerGoalHandle
226219 @return A shared_ptr to the goal object
227220 """
228 #if we have a goal that is non-null
229 if self.goal:
230 #@todo Test that python reference counting automatically handles this.
231 #create the deleter for our goal subtype
232 #d = EnclosureDeleter(self.goal);
233 #weakref.ref(boost::shared_ptr<const Goal>(&(goal_->goal), d);
221 # if we have a goal that is non-null
222 if self.goal:
223 # @todo Test that python reference counting automatically handles this.
224 # create the deleter for our goal subtype
225 # d = EnclosureDeleter(self.goal);
226 # weakref.ref(boost::shared_ptr<const Goal>(&(goal_->goal), d);
234227 return self.goal.goal
235
228
236229 return None
237
238230
239231 def get_goal_id(self):
240232 """
243235 """
244236 if self.goal:
245237 with self.action_server.lock:
246 return self.status_tracker.status.goal_id;
247 else:
248 rospy.logerr("Attempt to get a goal id on an uninitialized ServerGoalHandle");
249 return actionlib_msgs.msg.GoalID();
250
251
238 return self.status_tracker.status.goal_id
239 else:
240 rospy.logerr("Attempt to get a goal id on an uninitialized ServerGoalHandle")
241 return actionlib_msgs.msg.GoalID()
242
252243 def get_goal_status(self):
253244 """
254245 Accessor for the status associated with the ServerGoalHandle
256247 """
257248 if self.goal:
258249 with self.action_server.lock:
259 return self.status_tracker.status;
260 else:
261 rospy.logerr("Attempt to get goal status on an uninitialized ServerGoalHandle");
262 return actionlib_msgs.msg.GoalStatus();
263
264
250 return self.status_tracker.status
251 else:
252 rospy.logerr("Attempt to get goal status on an uninitialized ServerGoalHandle")
253 return actionlib_msgs.msg.GoalStatus()
254
265255 def __eq__(self, other):
266256 """
267257 Equals operator for ServerGoalHandles
268258 @param other The ServerGoalHandle to compare to
269259 @return True if the ServerGoalHandles refer to the same goal, false otherwise
270260 """
271
272 if( not self.goal or not other.goal):
273 return False;
274 my_id = self.get_goal_id();
275 their_id = other.get_goal_id();
276 return my_id.id == their_id.id;
261
262 if not self.goal or not other.goal:
263 return False
264 my_id = self.get_goal_id()
265 their_id = other.get_goal_id()
266 return my_id.id == their_id.id
277267
278268 def __ne__(self, other):
279269 """
281271 @param other The ServerGoalHandle to compare to
282272 @return True if the ServerGoalHandles refer to different goals, false otherwise
283273 """
284 if( not self.goal or not other.goal):
285 return True;
286 my_id = self.get_goal_id();
287 their_id = other.get_goal_id();
288
289 return my_id.id != their_id.id;
290
274 if not self.goal or not other.goal:
275 return True
276 my_id = self.get_goal_id()
277 their_id = other.get_goal_id()
278
279 return my_id.id != their_id.id
280
281 def __hash__(self):
282 """
283 hash function for ServerGoalHandles
284 @return hash of the goal ID
285 """
286 return hash(self.get_goal_id().id)
291287
292288 def set_cancel_requested(self):
293289 """
294290 A private method to set status to PENDING or RECALLING
295291 @return True if the cancel request should be passed on to the user, false otherwise
296292 """
297 rospy.logdebug("Transisitoning to a cancel requested state on goal id: %s, stamp: %.2f",
298 self.get_goal_id().id, self.get_goal_id().stamp.to_sec());
299 if self.goal:
300 with self.action_server.lock:
301 status = self.status_tracker.status.status;
302 if (status == actionlib_msgs.msg.GoalStatus.PENDING):
303 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.RECALLING;
304 self.action_server.publish_status();
305 return True;
306
307 if(status == actionlib_msgs.msg.GoalStatus.ACTIVE):
308 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.PREEMPTING;
309 self.action_server.publish_status();
310 return True;
311
312 return False;
313
314
315
316
317
318
319
320
321
322
323
293 rospy.logdebug("Transisitoning to a cancel requested state on goal id: %s, stamp: %.2f",
294 self.get_goal_id().id, self.get_goal_id().stamp.to_sec())
295 if self.goal:
296 with self.action_server.lock:
297 status = self.status_tracker.status.status
298 if status == actionlib_msgs.msg.GoalStatus.PENDING:
299 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.RECALLING
300 self.action_server.publish_status()
301 return True
302
303 if status == actionlib_msgs.msg.GoalStatus.ACTIVE:
304 self.status_tracker.status.status = actionlib_msgs.msg.GoalStatus.PREEMPTING
305 self.action_server.publish_status()
306 return True
307
308 return False
2626 # POSSIBILITY OF SUCH DAMAGE.
2727
2828 # Author: Stuart Glaser
29
29 import rospy
3030 import threading
31 import time
32 import rospy
33 from rospy import Header
31
3432 from actionlib_msgs.msg import *
35 from action_client import ActionClient, CommState, get_name_of_constant
33 from actionlib.action_client import ActionClient, CommState, get_name_of_constant
34
3635
3736 class SimpleGoalState:
3837 PENDING = 0
3938 ACTIVE = 1
4039 DONE = 2
40
4141 SimpleGoalState.to_string = classmethod(get_name_of_constant)
4242
4343
6161 ## timeout is interpreted as an infinite timeout.
6262 ##
6363 ## @return True if the server connected in the allocated time. False on timeout
64 def wait_for_server(self, timeout = rospy.Duration()):
64 def wait_for_server(self, timeout=rospy.Duration()):
6565 return self.action_client.wait_for_server(timeout)
66
6766
6867 ## @brief Sends a goal to the ActionServer, and also registers callbacks
6968 ##
7978 ##
8079 ## @param feedback_cb Callback that gets called whenever feedback
8180 ## for this goal is received. Takes one parameter: the feedback.
82 def send_goal(self, goal, done_cb = None, active_cb = None, feedback_cb = None):
81 def send_goal(self, goal, done_cb=None, active_cb=None, feedback_cb=None):
8382 # destroys the old goal handle
8483 self.stop_tracking_goal()
8584
9089 self.simple_state = SimpleGoalState.PENDING
9190 self.gh = self.action_client.send_goal(goal, self._handle_transition, self._handle_feedback)
9291
93
9492 ## @brief Sends a goal to the ActionServer, waits for the goal to complete, and preempts goal is necessary
9593 ##
9694 ## If a previous goal is already active when this is called. We simply forget
106104 ## @param preempt_timeout The time to wait for preemption to complete
107105 ##
108106 ## @return The goal's state.
109 def send_goal_and_wait(self, goal, execute_timeout = rospy.Duration(), preempt_timeout = rospy.Duration()):
107 def send_goal_and_wait(self, goal, execute_timeout=rospy.Duration(), preempt_timeout=rospy.Duration()):
110108 self.send_goal(goal)
111109 if not self.wait_for_result(execute_timeout):
112110 # preempt action
113111 rospy.logdebug("Canceling goal")
114112 self.cancel_goal()
115113 if self.wait_for_result(preempt_timeout):
116 rospy.logdebug("Preempt finished within specified preempt_timeout [%.2f]", preempt_timeout.to_sec());
114 rospy.logdebug("Preempt finished within specified preempt_timeout [%.2f]", preempt_timeout.to_sec())
117115 else:
118 rospy.logdebug("Preempt didn't finish specified preempt_timeout [%.2f]", preempt_timeout.to_sec());
116 rospy.logdebug("Preempt didn't finish specified preempt_timeout [%.2f]", preempt_timeout.to_sec())
119117 return self.get_state()
120118
121119
122120 ## @brief Blocks until this goal transitions to done
123121 ## @param timeout Max time to block before returning. A zero timeout is interpreted as an infinite timeout.
124122 ## @return True if the goal finished. False if the goal didn't finish within the allocated timeout
125 def wait_for_result(self, timeout = rospy.Duration()):
123 def wait_for_result(self, timeout=rospy.Duration()):
126124 if not self.gh:
127125 rospy.logerr("Called wait_for_goal_to_finish when no goal exists")
128126 return False
145143
146144 return self.simple_state == SimpleGoalState.DONE
147145
148
149146 ## @brief Gets the Result of the current goal
150147 def get_result(self):
151148 if not self.gh:
153150 return None
154151
155152 return self.gh.get_result()
156
157153
158154 ## @brief Get the state information for this goal
159155 ##
175171
176172 return status
177173
178
179174 ## @brief Returns the current status text of the goal.
180175 ##
181176 ## The text is sent by the action server. It is designed to
202197
203198 ## @brief Cancels all goals prior to a given timestamp
204199 ##
205 ## This preempts all goals running on the action server for which the
200 ## This preempts all goals running on the action server for which the
206201 ## time stamp is earlier than the specified time stamp
207202 ## this message is serviced by the ActionServer.
208203
258253
259254 def _handle_feedback(self, gh, feedback):
260255 if not self.gh:
261 rospy.logerr("Got a feedback callback when we're not tracking a goal. (id: %s)" % \
262 gh.comm_state_machine.action_goal.goal_id.id)
256 rospy.logerr("Got a feedback callback when we're not tracking a goal. (id: %s)" %
257 gh.comm_state_machine.action_goal.goal_id.id)
263258 return
264259 if gh != self.gh:
265 rospy.logerr("Got a feedback callback on a goal handle that we're not tracking. %s vs %s" % \
266 (self.gh.comm_state_machine.action_goal.goal_id.id,
267 gh.comm_state_machine.action_goal.goal_id.id))
260 rospy.logerr("Got a feedback callback on a goal handle that we're not tracking. %s vs %s" %
261 (self.gh.comm_state_machine.action_goal.goal_id.id, gh.comm_state_machine.action_goal.goal_id.id))
268262 return
269263 if self.feedback_cb:
270264 self.feedback_cb(feedback)
271265
272
273266 def _set_simple_state(self, state):
274267 self.simple_state = state
2727 #
2828 # Author: Alexander Sorokin.
2929 # Based on C++ simple_action_server.h by Eitan Marder-Eppstein
30
3130 import rospy
32
3331 import threading
3432 import traceback
3533
3634 from actionlib_msgs.msg import *
3735
3836 from actionlib import ActionServer
39 from actionlib.server_goal_handle import ServerGoalHandle;
37 from actionlib.server_goal_handle import ServerGoalHandle
38
4039
4140 def nop_cb(goal_handle):
4241 pass
5958 ## a new goal is received, allowing users to have blocking callbacks.
6059 ## Adding an execute callback also deactivates the goalCallback.
6160 ## @param auto_start A boolean value that tells the ActionServer wheteher or not to start publishing as soon as it comes up. THIS SHOULD ALWAYS BE SET TO FALSE TO AVOID RACE CONDITIONS and start() should be called after construction of the server.
62 def __init__(self, name, ActionSpec, execute_cb = None, auto_start = True):
61 def __init__(self, name, ActionSpec, execute_cb=None, auto_start=True):
6362
6463 self.new_goal = False
6564 self.preempt_request = False
6665 self.new_goal_preempt_request = False
6766
68 self.execute_callback = execute_cb;
69 self.goal_callback = None;
70 self.preempt_callback = None;
67 self.execute_callback = execute_cb
68 self.goal_callback = None
69 self.preempt_callback = None
7170
7271 self.need_to_terminate = False
73 self.terminate_mutex = threading.RLock();
72 self.terminate_mutex = threading.RLock()
7473
7574 # since the internal_goal/preempt_callbacks are invoked from the
7675 # ActionServer while holding the self.action_server.lock
7776 # self.lock must always be locked after the action server lock
7877 # to avoid an inconsistent lock acquisition order
79 self.lock = threading.RLock();
80
81 self.execute_condition = threading.Condition(self.lock);
82
83 self.current_goal = ServerGoalHandle();
84 self.next_goal = ServerGoalHandle();
78 self.lock = threading.RLock()
79
80 self.execute_condition = threading.Condition(self.lock)
81
82 self.current_goal = ServerGoalHandle()
83 self.next_goal = ServerGoalHandle()
8584
8685 if self.execute_callback:
87 self.execute_thread = threading.Thread(None, self.executeLoop);
88 self.execute_thread.start();
86 self.execute_thread = threading.Thread(None, self.executeLoop)
87 self.execute_thread.start()
8988 else:
9089 self.execute_thread = None
9190
9291 #create the action server
93 self.action_server = ActionServer(name, ActionSpec, self.internal_goal_callback,self.internal_preempt_callback,auto_start);
94
92 self.action_server = ActionServer(name, ActionSpec, self.internal_goal_callback,self.internal_preempt_callback, auto_start)
9593
9694 def __del__(self):
9795 if hasattr(self, 'execute_callback') and self.execute_callback:
9896 with self.terminate_mutex:
99 self.need_to_terminate = True;
100
101 assert(self.execute_thread);
102 self.execute_thread.join();
103
97 self.need_to_terminate = True
98
99 assert(self.execute_thread)
100 self.execute_thread.join()
104101
105102 ## @brief Accepts a new goal when one is available The status of this
106103 ## goal is set to active upon acceptance, and the status of any
114111 def accept_new_goal(self):
115112 with self.action_server.lock, self.lock:
116113 if not self.new_goal or not self.next_goal.get_goal():
117 rospy.logerr("Attempting to accept the next goal when a new goal is not available");
118 return None;
119
120 #check if we need to send a preempted message for the goal that we're currently pursuing
114 rospy.logerr("Attempting to accept the next goal when a new goal is not available")
115 return None
116
117 # check if we need to send a preempted message for the goal that we're currently pursuing
121118 if self.is_active() and self.current_goal.get_goal() and self.current_goal != self.next_goal:
122 self.current_goal.set_canceled(None, "This goal was canceled because another goal was received by the simple action server");
123
124 rospy.logdebug("Accepting a new goal");
125
126 #accept the next goal
127 self.current_goal = self.next_goal;
128 self.new_goal = False;
129
130 #set preempt to request to equal the preempt state of the new goal
131 self.preempt_request = self.new_goal_preempt_request;
132 self.new_goal_preempt_request = False;
133
134 #set the status of the current goal to be active
135 self.current_goal.set_accepted("This goal has been accepted by the simple action server");
136
137 return self.current_goal.get_goal();
138
119 self.current_goal.set_canceled(None, "This goal was canceled because another goal was received by the simple action server")
120
121 rospy.logdebug("Accepting a new goal")
122
123 # accept the next goal
124 self.current_goal = self.next_goal
125 self.new_goal = False
126
127 # set preempt to request to equal the preempt state of the new goal
128 self.preempt_request = self.new_goal_preempt_request
129 self.new_goal_preempt_request = False
130
131 # set the status of the current goal to be active
132 self.current_goal.set_accepted("This goal has been accepted by the simple action server")
133
134 return self.current_goal.get_goal()
139135
140136 ## @brief Allows polling implementations to query about the availability of a new goal
141137 ## @return True if a new goal is available, false otherwise
142138 def is_new_goal_available(self):
143 return self.new_goal;
144
139 return self.new_goal
145140
146141 ## @brief Allows polling implementations to query about preempt requests
147142 ## @return True if a preempt is requested, false otherwise
148143 def is_preempt_requested(self):
149 return self.preempt_request;
144 return self.preempt_request
150145
151146 ## @brief Allows polling implementations to query about the status of the current goal
152147 ## @return True if a goal is active, false otherwise
153148 def is_active(self):
154 if not self.current_goal.get_goal():
155 return False;
156
157 status = self.current_goal.get_goal_status().status;
158 return status == actionlib_msgs.msg.GoalStatus.ACTIVE or status == actionlib_msgs.msg.GoalStatus.PREEMPTING;
149 if not self.current_goal.get_goal():
150 return False
151
152 status = self.current_goal.get_goal_status().status
153 return status == actionlib_msgs.msg.GoalStatus.ACTIVE or status == actionlib_msgs.msg.GoalStatus.PREEMPTING
159154
160155 ## @brief Sets the status of the active goal to succeeded
161156 ## @param result An optional result to send back to any clients of the goal
162 def set_succeeded(self,result=None, text=""):
163 with self.action_server.lock, self.lock:
164 if not result:
165 result=self.get_default_result();
166 self.current_goal.set_succeeded(result, text);
157 def set_succeeded(self, result=None, text=""):
158 with self.action_server.lock, self.lock:
159 if not result:
160 result = self.get_default_result()
161 self.current_goal.set_succeeded(result, text)
167162
168163 ## @brief Sets the status of the active goal to aborted
169164 ## @param result An optional result to send back to any clients of the goal
170 def set_aborted(self, result = None, text=""):
165 def set_aborted(self, result=None, text=""):
171166 with self.action_server.lock, self.lock:
172167 if not result:
173 result=self.get_default_result();
174 self.current_goal.set_aborted(result, text);
168 result = self.get_default_result()
169 self.current_goal.set_aborted(result, text)
175170
176171 ## @brief Publishes feedback for a given goal
177172 ## @param feedback Shared pointer to the feedback to publish
178 def publish_feedback(self,feedback):
179 self.current_goal.publish_feedback(feedback);
180
173 def publish_feedback(self, feedback):
174 self.current_goal.publish_feedback(feedback)
181175
182176 def get_default_result(self):
183 return self.action_server.ActionResultType();
177 return self.action_server.ActionResultType()
184178
185179 ## @brief Sets the status of the active goal to preempted
186180 ## @param result An optional result to send back to any clients of the goal
187 def set_preempted(self,result=None, text=""):
181 def set_preempted(self, result=None, text=""):
188182 if not result:
189 result=self.get_default_result();
183 result = self.get_default_result()
190184 with self.action_server.lock, self.lock:
191 rospy.logdebug("Setting the current goal as canceled");
192 self.current_goal.set_canceled(result, text);
185 rospy.logdebug("Setting the current goal as canceled")
186 self.current_goal.set_canceled(result, text)
193187
194188 ## @brief Allows users to register a callback to be invoked when a new goal is available
195189 ## @param cb The callback to be invoked
196 def register_goal_callback(self,cb):
190 def register_goal_callback(self, cb):
197191 if self.execute_callback:
198 rospy.logwarn("Cannot call SimpleActionServer.register_goal_callback() because an executeCallback exists. Not going to register it.");
192 rospy.logwarn("Cannot call SimpleActionServer.register_goal_callback() because an executeCallback exists. Not going to register it.")
199193 else:
200 self.goal_callback = cb;
194 self.goal_callback = cb
201195
202196 ## @brief Allows users to register a callback to be invoked when a new preempt request is available
203197 ## @param cb The callback to be invoked
204198 def register_preempt_callback(self, cb):
205 self.preempt_callback = cb;
206
199 self.preempt_callback = cb
207200
208201 ## @brief Explicitly start the action server, used it auto_start is set to false
209202 def start(self):
210 self.action_server.start();
211
203 self.action_server.start()
212204
213205 ## @brief Callback for when the ActionServer receives a new goal and passes it on
214206 def internal_goal_callback(self, goal):
215 self.execute_condition.acquire();
216
217 try:
218 rospy.logdebug("A new goal %shas been recieved by the single goal action server",goal.get_goal_id().id);
219
220 #check that the timestamp is past that of the current goal and the next goal
221 if((not self.current_goal.get_goal() or goal.get_goal_id().stamp >= self.current_goal.get_goal_id().stamp)
222 and (not self.next_goal.get_goal() or goal.get_goal_id().stamp >= self.next_goal.get_goal_id().stamp)):
223 #if next_goal has not been accepted already... its going to get bumped, but we need to let the client know we're preempting
224 if(self.next_goal.get_goal() and (not self.current_goal.get_goal() or self.next_goal != self.current_goal)):
225 self.next_goal.set_canceled(None, "This goal was canceled because another goal was received by the simple action server");
226
227 self.next_goal = goal;
228 self.new_goal = True;
229 self.new_goal_preempt_request = False;
230
231 #if the server is active, we'll want to call the preempt callback for the current goal
232 if(self.is_active()):
233 self.preempt_request = True;
234 #if the user has registered a preempt callback, we'll call it now
235 if(self.preempt_callback):
236 self.preempt_callback();
237
238 #if the user has defined a goal callback, we'll call it now
239 if self.goal_callback:
240 self.goal_callback();
241
242 #Trigger runLoop to call execute()
243 self.execute_condition.notify();
244 self.execute_condition.release();
245 else:
246 #the goal requested has already been preempted by a different goal, so we're not going to execute it
247 goal.set_canceled(None, "This goal was canceled because another goal was received by the simple action server");
248 self.execute_condition.release();
249 except Exception, e:
250 rospy.logerr("SimpleActionServer.internal_goal_callback - exception %s",str(e))
251 self.execute_condition.release();
207 self.execute_condition.acquire()
208
209 try:
210 rospy.logdebug("A new goal %shas been recieved by the single goal action server", goal.get_goal_id().id)
211
212 # check that the timestamp is past that of the current goal and the next goal
213 if((not self.current_goal.get_goal() or goal.get_goal_id().stamp >= self.current_goal.get_goal_id().stamp)
214 and (not self.next_goal.get_goal() or goal.get_goal_id().stamp >= self.next_goal.get_goal_id().stamp)):
215 # if next_goal has not been accepted already... its going to get bumped, but we need to let the client know we're preempting
216 if(self.next_goal.get_goal() and (not self.current_goal.get_goal() or self.next_goal != self.current_goal)):
217 self.next_goal.set_canceled(None, "This goal was canceled because another goal was received by the simple action server")
218
219 self.next_goal = goal
220 self.new_goal = True
221 self.new_goal_preempt_request = False
222
223 # if the server is active, we'll want to call the preempt callback for the current goal
224 if(self.is_active()):
225 self.preempt_request = True
226 # if the user has registered a preempt callback, we'll call it now
227 if(self.preempt_callback):
228 self.preempt_callback()
229
230 # if the user has defined a goal callback, we'll call it now
231 if self.goal_callback:
232 self.goal_callback()
233
234 # Trigger runLoop to call execute()
235 self.execute_condition.notify()
236 self.execute_condition.release()
237 else:
238 # the goal requested has already been preempted by a different goal, so we're not going to execute it
239 goal.set_canceled(None, "This goal was canceled because another goal was received by the simple action server")
240 self.execute_condition.release()
241 except Exception as e:
242 rospy.logerr("SimpleActionServer.internal_goal_callback - exception %s", str(e))
243 self.execute_condition.release()
252244
253245 ## @brief Callback for when the ActionServer receives a new preempt and passes it on
254 def internal_preempt_callback(self,preempt):
255 with self.lock:
256 rospy.logdebug("A preempt has been received by the SimpleActionServer");
257
258 #if the preempt is for the current goal, then we'll set the preemptRequest flag and call the user's preempt callback
259 if(preempt == self.current_goal):
260 rospy.logdebug("Setting preempt_request bit for the current goal to TRUE and invoking callback");
261 self.preempt_request = True;
262
263 #if the user has registered a preempt callback, we'll call it now
264 if(self.preempt_callback):
265 self.preempt_callback();
266 #if the preempt applies to the next goal, we'll set the preempt bit for that
267 elif(preempt == self.next_goal):
268 rospy.logdebug("Setting preempt request bit for the next goal to TRUE");
269 self.new_goal_preempt_request = True;
246 def internal_preempt_callback(self, preempt):
247 with self.lock:
248 rospy.logdebug("A preempt has been received by the SimpleActionServer")
249
250 #if the preempt is for the current goal, then we'll set the preemptRequest flag and call the user's preempt callback
251 if(preempt == self.current_goal):
252 rospy.logdebug("Setting preempt_request bit for the current goal to TRUE and invoking callback")
253 self.preempt_request = True
254
255 #if the user has registered a preempt callback, we'll call it now
256 if(self.preempt_callback):
257 self.preempt_callback()
258 #if the preempt applies to the next goal, we'll set the preempt bit for that
259 elif(preempt == self.next_goal):
260 rospy.logdebug("Setting preempt request bit for the next goal to TRUE")
261 self.new_goal_preempt_request = True
270262
271263 ## @brief Called from a separate thread to call blocking execute calls
272264 def executeLoop(self):
273 loop_duration = rospy.Duration.from_sec(.1);
274
275 while (not rospy.is_shutdown()):
276 rospy.logdebug("SAS: execute");
277
278 with self.terminate_mutex:
279 if (self.need_to_terminate):
280 break;
281
282 # the following checks (is_active, is_new_goal_available)
283 # are performed without locking
284 # the worst thing that might happen in case of a race
285 # condition is a warning/error message on the console
286 if (self.is_active()):
287 rospy.logerr("Should never reach this code with an active goal");
288 return
289
290 if (self.is_new_goal_available()):
291 # accept_new_goal() is performing its own locking
292 goal = self.accept_new_goal();
293 if not self.execute_callback:
294 rospy.logerr("execute_callback_ must exist. This is a bug in SimpleActionServer");
295 return
296
297 try:
298 self.execute_callback(goal)
299
300 if self.is_active():
301 rospy.logwarn("Your executeCallback did not set the goal to a terminal status. " +
302 "This is a bug in your ActionServer implementation. Fix your code! "+
303 "For now, the ActionServer will set this goal to aborted");
304 self.set_aborted(None, "No terminal state was set.");
305 except Exception, ex:
306 rospy.logerr("Exception in your execute callback: %s\n%s", str(ex),
307 traceback.format_exc())
308 self.set_aborted(None, "Exception in execute callback: %s" % str(ex))
309
310 with self.execute_condition:
311 self.execute_condition.wait(loop_duration.to_sec());
312
313
314
265 loop_duration = rospy.Duration.from_sec(.1)
266
267 while (not rospy.is_shutdown()):
268 rospy.logdebug("SAS: execute")
269
270 with self.terminate_mutex:
271 if (self.need_to_terminate):
272 break
273
274 # the following checks (is_active, is_new_goal_available)
275 # are performed without locking
276 # the worst thing that might happen in case of a race
277 # condition is a warning/error message on the console
278 if (self.is_active()):
279 rospy.logerr("Should never reach this code with an active goal")
280 return
281
282 if (self.is_new_goal_available()):
283 # accept_new_goal() is performing its own locking
284 goal = self.accept_new_goal()
285 if not self.execute_callback:
286 rospy.logerr("execute_callback_ must exist. This is a bug in SimpleActionServer")
287 return
288
289 try:
290 self.execute_callback(goal)
291
292 if self.is_active():
293 rospy.logwarn("Your executeCallback did not set the goal to a terminal status. " +
294 "This is a bug in your ActionServer implementation. Fix your code! " +
295 "For now, the ActionServer will set this goal to aborted")
296 self.set_aborted(None, "No terminal state was set.")
297 except Exception as ex:
298 rospy.logerr("Exception in your execute callback: %s\n%s", str(ex),
299 traceback.format_exc())
300 self.set_aborted(None, "Exception in execute callback: %s" % str(ex))
301
302 with self.execute_condition:
303 self.execute_condition.wait(loop_duration.to_sec())
00 # Copyright (c) 2009, Willow Garage, Inc.
11 # All rights reserved.
2 #
2 #
33 # Redistribution and use in source and binary forms, with or without
44 # modification, are permitted provided that the following conditions are met:
5 #
5 #
66 # * Redistributions of source code must retain the above copyright
77 # notice, this list of conditions and the following disclaimer.
88 # * Redistributions in binary form must reproduce the above copyright
1111 # * Neither the name of the Willow Garage, Inc. nor the names of its
1212 # contributors may be used to endorse or promote products derived from
1313 # this software without specific prior written permission.
14 #
14 #
1515 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1616 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1717 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2424 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2525 # POSSIBILITY OF SUCH DAMAGE.
2626
27 # Author: Alexander Sorokin.
27 # Author: Alexander Sorokin.
2828 # Based on C++ goal_id_generator.h/cpp
29
3029 import rospy
3130
32 import actionlib
33 import actionlib_msgs.msg
34 from actionlib import goal_id_generator
31 import actionlib_msgs.msg
32 from actionlib import goal_id_generator
3533
3634
3735 class StatusTracker:
4543 """
4644 @brief create status tracker. Either pass goal_id and status OR goal
4745 """
48 self.goal = None ;
49 self.handle_tracker = None;
50 self.status = actionlib_msgs.msg.GoalStatus();
46 self.goal = None
47 self.handle_tracker = None
48 self.status = actionlib_msgs.msg.GoalStatus()
5149
52 self.handle_destruction_time = rospy.Time();
50 self.handle_destruction_time = rospy.Time()
5351
54 self.id_generator = goal_id_generator.GoalIDGenerator();
52 self.id_generator = goal_id_generator.GoalIDGenerator()
5553
5654 if goal_id:
57 #set the goal id and status appropriately
58 self.status.goal_id = goal_id;
59 self.status.status = status;
55 # set the goal id and status appropriately
56 self.status.goal_id = goal_id
57 self.status.status = status
6058 else:
6159 self.goal = goal
62 self.status.goal_id = goal.goal_id;
60 self.status.goal_id = goal.goal_id
6361
64 #initialize the status of the goal to pending
65 self.status.status = actionlib_msgs.msg.GoalStatus.PENDING;
62 # initialize the status of the goal to pending
63 self.status.status = actionlib_msgs.msg.GoalStatus.PENDING
6664
67 #if the goal id is zero, then we need to make up an id for the goal
65 # if the goal id is zero, then we need to make up an id for the goal
6866 if self.status.goal_id.id == "":
69 self.status.goal_id = self.id_generator.generate_ID();
67 self.status.goal_id = self.id_generator.generate_ID()
7068
71 #if the timestamp of the goal is zero, then we'll set it to now()
69 # if the timestamp of the goal is zero, then we'll set it to now()
7270 if self.status.goal_id.stamp == rospy.Time():
73 self.status.goal_id.stamp = rospy.Time.now();
71 self.status.goal_id.stamp = rospy.Time.now()
3030 #* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
3131 #* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3232 #* POSSIBILITY OF SUCH DAMAGE.
33 #*
33 #*
3434 #* Author: Eitan Marder-Eppstein
3535 #***********************************************************
3636 """
169169 self.goal_st_bx = wx.StaticBox(self.frame, -1, "Goal")
170170 self.goal_st = wx.StaticBoxSizer(self.goal_st_bx, wx.VERTICAL)
171171 self.goal_st.Add(self.goal, 1, wx.EXPAND)
172
172
173173 self.feedback = wx.TextCtrl(self.frame, -1, style=(wx.TE_MULTILINE |
174174 wx.TE_READONLY))
175175 self.feedback_st_bx = wx.StaticBox(self.frame, -1, "Feedback")
228228 # parser.add_option("-t","--test",action="store_true", dest="test",default=False,
229229 # help="A testing flag")
230230 # parser.add_option("-v","--var",action="store",type="string", dest="var",default="blah")
231
231
232232 (options, args) = parser.parse_args(rospy.myargv())
233233
234 if (len(args) != 2):
235 parser.error("You must specify the action topic name Eg: ./axclient.py my_action_topic")
236
237 # get action type from topic
238 topic_type = rostopic._get_topic_type("%s/goal"%args[1])[0]
239 # remove "Goal" string from action type
240 assert("Goal" in topic_type)
241 topic_type = topic_type[0:len(topic_type)-4]
242
234 if (len(args) == 2):
235 # get action type via rostopic
236 topic_type = rostopic._get_topic_type("%s/goal"%args[1])[0]
237 # remove "Goal" string from action type
238 assert("Goal" in topic_type)
239 topic_type = topic_type[0:len(topic_type)-4]
240 elif (len(args) == 3):
241 topic_type = args[2]
242 print(topic_type)
243 assert("Action" in topic_type)
244 else:
245 parser.error("You must specify the action topic name (and optionally type) Eg: ./axclient.py action_topic actionlib/TwoIntsAction ")
246
243247 action = DynamicAction(topic_type)
244
245248 app = AXClientApp(action, args[1])
246249 app.MainLoop()
247250 app.OnQuit()
7171 self.status.SetLabel("Waiting For Goal...")
7272 self.send_feedback.Disable()
7373 self.succeed.Disable()
74 self.abort.Disable()
75 self.preempt.Disable()
74 self.abort.Disable()
75 self.preempt.Disable()
7676
7777 self.goal.SetValue("")
7878
8282 self.send_feedback.Enable()
8383 self.succeed.Enable()
8484 self.abort.Enable()
85 self.preempt.Enable()
85 self.preempt.Enable()
8686
8787 try:
8888 self.goal.SetValue(to_yaml(goal))
8989 except UnicodeDecodeError:
9090 self.goal.SetValue("Cannot display goal due to unprintable characters")
9191
92 def set_preempt_requested(self):
93 self.status_bg.SetBackgroundColour(wx.Colour(0, 200, 200))
94 self.status.SetLabel("Preempt requested...")
95
9296 def execute(self, goal):
9397
9498 wx.CallAfter(self.set_goal, goal)
95
9699 self.condition.acquire()
97100
98101 self.result_msg = None
99102 self.feedback_msg = None
100103 self.execute_type = None
101
104
102105 while self.execute_type is None or self.execute_type == SEND_FEEDBACK:
103
104106 self.result_msg = None
105107 self.feedback_msg = None
106108 self.execute_type = None
107
109
108110 while self.execute_type is None:
109 self.condition.wait()
111 if self.server.is_preempt_requested():
112 wx.CallAfter(self.set_preempt_requested)
113 self.condition.wait(1.0)
110114
111115 if self.execute_type == SEND_FEEDBACK:
112116 if self.feedback_msg is not None:
128132
129133 def on_feedback(self, event):
130134 self.condition.acquire()
131
135
132136 try:
133137 self.feedback_msg = yaml_msg_str(self.action_type.feedback,
134138 self.feedback.GetValue())
135139 buff = StringIO()
136140 self.feedback_msg.serialize(buff)
137
141
138142 self.execute_type = SEND_FEEDBACK
139143 self.condition.notify()
140144 except roslib.message.SerializationError, e:
147151
148152 def on_succeed(self, event):
149153 self.condition.acquire()
150
154
151155 try:
152156 self.result_msg = yaml_msg_str(self.action_type.result, self.result.GetValue())
153157 buff = StringIO()
154158 self.result_msg.serialize(buff)
155
159
156160 self.execute_type = SUCCEED
157161 self.condition.notify()
158162 except roslib.message.SerializationError, e:
190194 self.goal_st_bx = wx.StaticBox(self.frame, -1, "Goal")
191195 self.goal_st = wx.StaticBoxSizer(self.goal_st_bx, wx.VERTICAL)
192196 self.goal_st.Add(self.goal, 1, wx.EXPAND)
193
197
194198 self.feedback = wx.TextCtrl(self.frame, -1, style=wx.TE_MULTILINE)
195199 self.feedback.SetValue(to_yaml(tmp_feedback))
196200 self.feedback_st_bx = wx.StaticBox(self.frame, -1, "Feedback")
244248 # parser.add_option("-t","--test",action="store_true", dest="test",default=False,
245249 # help="A testing flag")
246250 # parser.add_option("-v","--var",action="store",type="string", dest="var",default="blah")
247
251
248252 (options, args) = parser.parse_args(rospy.myargv())
249253
250254 if (len(args) != 3):