00001
00002
00003 """
00004 pi_trees_ros.py - Version 0.1 2013-08-28
00005
00006 ROS wrappers for the pi_trees_lib.py library
00007
00008 Created for the Pi Robot Project: http://www.pirobot.org
00009 Copyright (c) 2013 Patrick Goebel. All rights reserved.
00010
00011 This program is free software; you can redistribute it and/or modify
00012 it under the terms of the GNU General Public License as published by
00013 the Free Software Foundation; either version 2 of the License, or
00014 (at your option) any later version.
00015
00016 This program is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00019 GNU General Public License for more details at:
00020
00021 http://www.gnu.org/licenses/gpl.html
00022 """
00023
00024 import rospy
00025 import actionlib
00026 from actionlib_msgs.msg import GoalStatus
00027 from pi_trees_lib.pi_trees_lib import *
00028
00029 class MonitorTask(Task):
00030 """
00031 Turn a ROS subscriber into a Task.
00032 """
00033 def __init__(self, name, topic, msg_type, msg_cb, wait_for_message=True, timeout=5):
00034 super(MonitorTask, self).__init__(name)
00035
00036 self.topic = topic
00037 self.msg_type = msg_type
00038 self.timeout = timeout
00039 self.msg_cb = msg_cb
00040
00041 rospy.loginfo("Subscribing to topic " + topic)
00042
00043 if wait_for_message:
00044 try:
00045 rospy.wait_for_message(topic, msg_type, timeout=self.timeout)
00046 rospy.loginfo("Connected.")
00047 except:
00048 rospy.loginfo("Timed out waiting for " + topic)
00049
00050
00051 rospy.Subscriber(self.topic, self.msg_type, self._msg_cb)
00052
00053 def _msg_cb(self, msg):
00054 self.set_status(self.msg_cb(msg))
00055
00056 def run(self):
00057 return self.status
00058
00059 def reset(self):
00060 pass
00061
00062 class ServiceTask(Task):
00063 """
00064 Turn a ROS service into a Task.
00065 """
00066 def __init__(self, name, service, service_type, request, result_cb=None, wait_for_service=True, timeout=5):
00067 super(ServiceTask, self).__init__(name)
00068
00069 self.result = None
00070 self.request = request
00071 self.timeout = timeout
00072 self.result_cb = result_cb
00073
00074 rospy.loginfo("Connecting to service " + service)
00075
00076 if wait_for_service:
00077 rospy.loginfo("Waiting for service")
00078 rospy.wait_for_service(service, timeout=self.timeout)
00079 rospy.loginfo("Connected.")
00080
00081
00082 self.service_proxy = rospy.ServiceProxy(service, service_type)
00083
00084 def run(self):
00085 try:
00086 result = self.service_proxy(self.request)
00087 if self.result_cb is not None:
00088 self.result_cb(result)
00089 return TaskStatus.SUCCESS
00090 except:
00091 rospy.logerr(sys.exc_info())
00092 return TaskStatus.FAILURE
00093
00094 def reset(self):
00095 pass
00096
00097 class SimpleActionTask(Task):
00098 """
00099 Turn a ROS action into a Task.
00100 """
00101 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):
00102 super(SimpleActionTask, self).__init__(name)
00103
00104 self.action = action
00105 self.goal = goal
00106 self.tick = 1.0 / rate
00107 self.rate = rospy.Rate(rate)
00108
00109 self.result = None
00110 self.connect_timeout = connect_timeout
00111 self.result_timeout = result_timeout
00112 self.reset_after = reset_after
00113
00114 if done_cb == None:
00115 done_cb = self.default_done_cb
00116 self.done_cb = done_cb
00117
00118 if active_cb == None:
00119 active_cb = self.default_active_cb
00120 self.active_cb = active_cb
00121
00122 if feedback_cb == None:
00123 feedback_cb = self.default_feedback_cb
00124 self.feedback_cb = feedback_cb
00125
00126 self.action_started = False
00127 self.action_finished = False
00128 self.goal_status_reported = False
00129 self.time_so_far = 0.0
00130
00131
00132 self.goal_states = ['PENDING', 'ACTIVE', 'PREEMPTED',
00133 'SUCCEEDED', 'ABORTED', 'REJECTED',
00134 'PREEMPTING', 'RECALLING', 'RECALLED',
00135 'LOST']
00136
00137 rospy.loginfo("Connecting to action " + action)
00138
00139
00140 self.action_client = actionlib.SimpleActionClient(action, action_type)
00141
00142 rospy.loginfo("Waiting for move_base action server...")
00143
00144
00145 try:
00146 self.action_client.wait_for_server(rospy.Duration(self.connect_timeout))
00147 except:
00148 rospy.loginfo("Timed out connecting to the action server " + action)
00149
00150 rospy.loginfo("Connected to action server")
00151
00152 def run(self):
00153
00154 if not self.action_started:
00155 rospy.loginfo("Sending " + str(self.name) + " goal to action server...")
00156 self.action_client.send_goal(self.goal, done_cb=self.done_cb, active_cb=self.active_cb, feedback_cb=self.feedback_cb)
00157 self.action_started = True
00158
00159 ''' We cannot use the wait_for_result() method here as it will block the entire
00160 tree so we break it down in time slices of duration 1 / rate.
00161 '''
00162 if not self.action_finished:
00163 self.time_so_far += self.tick
00164 self.rate.sleep()
00165 if self.time_so_far > self.result_timeout:
00166 self.action_client.cancel_goal()
00167 rospy.loginfo("Timed out achieving goal")
00168 return TaskStatus.FAILURE
00169 else:
00170 return TaskStatus.RUNNING
00171 else:
00172
00173 if self.goal_status == GoalStatus.SUCCEEDED:
00174 self.action_finished = True
00175 if self.reset_after:
00176 self.reset()
00177 return TaskStatus.SUCCESS
00178 elif self.goal_status == GoalStatus.ABORTED:
00179 self.action_started = False
00180 self.action_finished = False
00181 return TaskStatus.FAILURE
00182 else:
00183 self.action_started = False
00184 self.action_finished = False
00185 self.goal_status_reported = False
00186 return TaskStatus.RUNNING
00187
00188 def default_done_cb(self, status, result):
00189
00190 self.goal_status = status
00191 self.action_finished = True
00192
00193 if not self.goal_status_reported:
00194 rospy.loginfo(str(self.name) + " ended with status " + str(self.goal_states[status]))
00195 self.goal_status_reported = True
00196
00197 def default_active_cb(self):
00198 pass
00199
00200 def default_feedback_cb(self, msg):
00201 pass
00202
00203 def reset(self):
00204 self.action_started = False
00205 self.action_finished = False
00206 self.goal_status_reported = False
00207 self.time_so_far = 0.0