00001
00002
00003 import rospy
00004 import actionlib
00005 from actionlib_msgs.msg import GoalStatus
00006 from pi_trees_lib.pi_trees_lib import *
00007 import sys
00008
00009 class MonitorTask(Task):
00010 """
00011 Turn a ROS subscriber into a Task.
00012 """
00013 def __init__(self, name, topic, msg_type, msg_cb, wait_for_message=True, timeout=5):
00014 super(MonitorTask, self).__init__(name)
00015
00016 self.topic = topic
00017 self.msg_type = msg_type
00018 self.timeout = timeout
00019 self.msg_cb = msg_cb
00020
00021 rospy.loginfo("Subscribing to topic " + topic)
00022
00023 if wait_for_message:
00024 try:
00025 rospy.wait_for_message(topic, msg_type, timeout=self.timeout)
00026 rospy.loginfo("Connected.")
00027 except:
00028 rospy.loginfo("Timed out waiting for " + topic)
00029
00030
00031 rospy.Subscriber(self.topic, self.msg_type, self._msg_cb)
00032
00033 def _msg_cb(self, msg):
00034 self.set_status(self.msg_cb(msg))
00035
00036 def run(self):
00037 return self.status
00038
00039 class ServiceTask(Task):
00040 """
00041 Turn a ROS service into a Task.
00042 """
00043 def __init__(self, name, service, service_type, request, result_cb=None, wait_for_service=True, timeout=5):
00044 super(ServiceTask, self).__init__(name)
00045
00046 self.result = None
00047 self.request = request
00048 self.timeout = timeout
00049 self.result_cb = result_cb
00050
00051 rospy.loginfo("Connecting to service " + service)
00052
00053 if wait_for_service:
00054 rospy.loginfo("Waiting for service")
00055 rospy.wait_for_service(service, timeout=self.timeout)
00056 rospy.loginfo("Connected.")
00057
00058
00059 self.service_proxy = rospy.ServiceProxy(service, service_type)
00060
00061 def run(self):
00062 try:
00063 result = self.service_proxy(self.request)
00064 if self.result_cb is not None:
00065 self.result_cb(result)
00066 return TaskStatus.SUCCESS
00067 except:
00068 rospy.logerr(sys.exc_info())
00069 return TaskStatus.FAILURE
00070
00071 def reset(self):
00072 self.status = None
00073
00074 class SimpleActionTask(Task):
00075 """
00076 Turn a ROS action into a Task.
00077 """
00078 def __init__(self, name, action, action_type, goal, rate=5, connect_timeout=10, result_timeout=30, reset_after=False, active_cb=None, done_cb=None, feedback_cb=None):
00079 super(SimpleActionTask, self).__init__(name)
00080
00081 self.action = action
00082 self.goal = goal
00083 self.tick = 1.0 / rate
00084 self.rate = rospy.Rate(rate)
00085
00086 self.result = None
00087 self.connect_timeout = connect_timeout
00088 self.result_timeout = result_timeout
00089 self.reset_after = reset_after
00090
00091 self.final_status = None
00092
00093 if done_cb:
00094 self.user_done_cb = done_cb
00095 else:
00096 self.user_done_cb = None
00097
00098 self.done_cb = self.default_done_cb
00099
00100 if active_cb == None:
00101 active_cb = self.default_active_cb
00102 self.active_cb = active_cb
00103
00104 if feedback_cb == None:
00105 feedback_cb = self.default_feedback_cb
00106 self.feedback_cb = feedback_cb
00107
00108 self.action_started = False
00109 self.action_finished = False
00110 self.goal_status_reported = False
00111 self.time_so_far = 0.0
00112
00113
00114 self.goal_states = ['PENDING', 'ACTIVE', 'PREEMPTED',
00115 'SUCCEEDED', 'ABORTED', 'REJECTED',
00116 'PREEMPTING', 'RECALLING', 'RECALLED',
00117 'LOST']
00118
00119 self.retry_goal_states = [GoalStatus.PREEMPTED]
00120
00121 rospy.loginfo("Connecting to action " + action)
00122
00123
00124 self.action_client = actionlib.SimpleActionClient(action, action_type)
00125
00126 rospy.loginfo("Waiting for action server...")
00127
00128
00129 try:
00130 self.action_client.wait_for_server(rospy.Duration(self.connect_timeout))
00131 except:
00132 rospy.loginfo("Timed out connecting to the action server " + action)
00133
00134 rospy.loginfo("Connected to action server")
00135
00136 def run(self):
00137
00138 if not self.action_started:
00139 rospy.loginfo("Sending " + str(self.name) + " goal to action server...")
00140 self.action_client.send_goal(self.goal, done_cb=self.done_cb, active_cb=self.active_cb, feedback_cb=self.feedback_cb)
00141 self.action_started = True
00142 self.activate_time = rospy.Time.now()
00143
00144 ''' We cannot use the wait_for_result() method here as it will block the entire
00145 tree so we break it down in time slices of duration 1 / rate.
00146 '''
00147 if not self.action_finished:
00148 self.time_so_far += self.tick
00149 self.rate.sleep()
00150 if self.time_so_far > self.result_timeout:
00151 self.action_client.cancel_goal()
00152 rospy.loginfo("Timed out achieving goal")
00153 self.action_finished = True
00154 return TaskStatus.FAILURE
00155 else:
00156 return TaskStatus.RUNNING
00157 else:
00158
00159 if self.goal_status == GoalStatus.SUCCEEDED:
00160 self.status = TaskStatus.SUCCESS
00161
00162
00163 elif self.goal_status in self.retry_goal_states:
00164 self.status = TaskStatus.RUNNING
00165 self.action_started = False
00166 self.action_finished = False
00167 self.time_so_far = 0
00168
00169
00170 else:
00171 self.status = TaskStatus.FAILURE
00172
00173
00174 self.final_status = self.status
00175
00176
00177 if self.reset_after:
00178 self.reset()
00179
00180 self.action_client.wait_for_result(rospy.Duration(10))
00181 return self.final_status
00182
00183 def default_done_cb(self, result_state, result):
00184 """Goal Done Callback
00185 This callback resets the active flags and reports the duration of the action.
00186 Also, if the user has defined a result_cb, it is called here before the
00187 method returns.
00188 """
00189 self.goal_status = result_state
00190 self.action_finished = True
00191
00192 if not self.goal_status_reported:
00193 self._duration = rospy.Time.now() - self.activate_time
00194
00195 rospy.loginfo("Action " + self.name + " terminated after "\
00196 + str(self._duration.to_sec()) + " seconds with result "\
00197 + self.goal_states[self.action_client.get_state()] + ".")
00198
00199 self.goal_status_reported = True
00200
00201 if self.user_done_cb:
00202 self.user_done_cb(result_state, result)
00203
00204 def default_active_cb(self):
00205 pass
00206
00207 def default_feedback_cb(self, msg):
00208 pass
00209
00210 def reset(self):
00211 rospy.logdebug("RESETTING " + str(self.name))
00212 self.action_started = False
00213 self.action_finished = False
00214 self.goal_status_reported = False
00215 self.status = self.final_status
00216 self.time_so_far = 0.0
00217 super(SimpleActionTask, self).reset()