00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039 import roslib; roslib.load_manifest('continuous_ops_executive')
00040 import rospy
00041 import actionlib
00042 import smach
00043 import smach_ros
00044 from pr2_msgs.msg import PowerState
00045 from std_msgs.msg import String, Float64, UInt8
00046 from actionlib_msgs.msg import GoalStatus
00047 from pr2_plugs_msgs.msg import RechargeAction, RechargeGoal, RechargeCommand
00048 from continuous_ops_msgs.msg import TaskAction, TaskGoal, ChargeStatus
00049 from continuous_ops_msgs.srv import *
00050 from random import choice
00051 from threading import Thread
00052
00053
00054 class HighBatteryMonitor(smach.State):
00055 def __init__(self):
00056 smach.State.__init__(self, outcomes=['stuffed', 'not_plugged_in', 'preempted'])
00057 self._charge_data = ChargeStatus()
00058 self.sub = rospy.Subscriber('charge_status', ChargeStatus, self.charge_cb)
00059 self.srv = rospy.ServiceProxy('task_manager/get_scheduled_tasks', GetTaskList)
00060
00061 def execute(self, userdata):
00062 rospy.loginfo('Monitoring battery level and task schedule')
00063 while not rospy.is_shutdown():
00064
00065 if self._charge_data.charge_status == ChargeStatus.UNPLUGGED:
00066 rospy.logerr('Robot is not plugged in correctly!')
00067 return 'not_plugged_in'
00068
00069
00070 task_list = self.srv().tasks
00071 if (self._charge_data.battery_status == ChargeStatus.STUFFED) and len(task_list) > 0:
00072 return 'stuffed'
00073
00074 if self.preempt_requested():
00075 self.service_preempt()
00076 return 'preempted'
00077 rospy.sleep(0.1)
00078
00079 def charge_cb(self, msg):
00080 self._charge_data = msg
00081
00082
00083 class LowBatteryMonitor(smach.State):
00084 def __init__(self):
00085 smach.State.__init__(self, outcomes=['bored', 'hungry','preempted'])
00086 self._charge_data = ChargeStatus()
00087 self.sub = rospy.Subscriber('charge_status', ChargeStatus, self.charge_cb)
00088 self.srv = rospy.ServiceProxy('task_manager/get_scheduled_tasks', GetTaskList)
00089
00090 def execute(self, userdata):
00091 rospy.loginfo('Monitoring battery level and task schedule')
00092 while not rospy.is_shutdown():
00093
00094 if self._charge_data.battery_status == ChargeStatus.HUNGRY:
00095 rospy.loginfo("hungry")
00096 return 'hungry'
00097
00098 resp = self.srv()
00099 if (not self._charge_data.battery_status == ChargeStatus.HUNGRY) and (self._charge_data.charge_status == ChargeStatus.UNPLUGGED) and len(resp.tasks) == 0:
00100 rospy.loginfo("Continuous ops bored")
00101 return 'bored'
00102
00103 if self.preempt_requested():
00104 self.service_preempt()
00105 rospy.loginfo("Continuous ops was preempted")
00106 return 'preempted'
00107 rospy.sleep(0.1)
00108
00109 def charge_cb(self, msg):
00110 self._charge_data = msg
00111
00112
00113
00114
00115 class TaskLaunchState(smach.State):
00116 def __init__(self):
00117 smach.State.__init__(self,
00118 outcomes=['succeeded', 'aborted', 'preempted'],
00119 output_keys=['task'])
00120 self.list_srv = rospy.ServiceProxy('task_manager/get_scheduled_tasks', GetTaskList)
00121 self.start_srv = rospy.ServiceProxy('task_manager/start_task', ChangeTask)
00122
00123
00124 def execute(self, userdata):
00125
00126 task_list = self.list_srv().tasks
00127 if self.preempt_requested():
00128 self.service_preempt()
00129 return 'preempted'
00130
00131 if len(task_list) == 0:
00132 rospy.logerr("No scheduled tasks to run")
00133 rospy.sleep(5)
00134 return 'aborted'
00135
00136 task_list = self.start_srv(ChangeTaskRequest(task_list[0].run_id)).tasks
00137 if len(task_list) == 0:
00138 rospy.logerr("No scheduled tasks to run")
00139 rospy.sleep(5)
00140 return 'aborted'
00141
00142 if task_list[0].state == 'Running':
00143 userdata.task = task_list[0]
00144 return 'succeeded'
00145 return 'aborted'
00146
00147
00148
00149
00150 class TaskKillState(smach.State):
00151 def __init__(self):
00152 smach.State.__init__(self,
00153 outcomes=['succeeded', 'preempted'],
00154 input_keys=['task'])
00155 self.stop_srv = rospy.ServiceProxy('task_manager/stop_task', ChangeTask)
00156 self.remove_srv = rospy.ServiceProxy('task_manager/remove_task', ChangeTask)
00157
00158 def execute(self, userdata):
00159 self.stop_srv(ChangeTaskRequest(userdata.task.run_id))
00160
00161 if not self.preempt_requested():
00162 self.remove_srv(ChangeTaskRequest(userdata.task.run_id))
00163 return 'succeeded'
00164 else:
00165 self.service_preempt()
00166 return 'preempted'
00167
00168
00169
00170
00171
00172
00173 class TaskRunState(smach.State):
00174 def __init__(self):
00175 smach.State.__init__(self,
00176 outcomes=['succeeded', 'aborted', 'preempted'],
00177 input_keys=['task'],
00178 output_keys=['task'])
00179 self.connect_timeout = rospy.Duration(30)
00180 self.preempt_timeout = rospy.Duration(30)
00181 self.succeeded = []
00182 self.aborted = []
00183 self.preempted = []
00184 self.srv = rospy.Service('continuous_ops_executive/cancel_running_task', ChangeTask, self.cancel_srv)
00185
00186 def execute(self, userdata):
00187 self.cancel_run_id = ''
00188 print "service should be up"
00189
00190
00191 rospy.loginfo('Connecting to action server %s'%userdata.task.action_ns)
00192 self.ac = actionlib.SimpleActionClient(userdata.task.action_ns, TaskAction)
00193 start_time = rospy.Time.now()
00194 while not rospy.is_shutdown() and not self.ac.wait_for_server(rospy.Duration(0.2)):
00195 if self.cancel_run_id == userdata.task.run_id:
00196 self.cancel_run_id = ''
00197 self.aborted.append(userdata.task.name)
00198 rospy.logerr('Canceling task before it is connected to action server')
00199 return 'aborted'
00200 if self.preempt_requested():
00201 self.preempted.append(userdata.task.name)
00202 return 'preempted'
00203 if rospy.Time.now() > start_time + self.connect_timeout:
00204 self.aborted.append(userdata.task.name)
00205 rospy.logerr('Timed out waiting for action server of task')
00206 return 'aborted'
00207
00208
00209 rospy.loginfo('Sending goal to action server %s'%userdata.task.action_ns)
00210 self.preempting = False
00211 self.ac.send_goal(TaskGoal())
00212 start_time = rospy.Time.now()
00213 while not rospy.is_shutdown():
00214
00215 if self.cancel_run_id == userdata.task.run_id:
00216 self.preempt_task('Current task canceled from external source')
00217 if self.preempt_requested():
00218 self.preempt_task('Current task being preempted by state machine')
00219 if userdata.task.timeout != rospy.Duration(0) and rospy.Time.now() > start_time + userdata.task.timeout:
00220 self.preempt_task('Current task exceeded its allocated time slot')
00221
00222
00223 if self.preempting and rospy.Time.now() > self.preempting_start + self.preempt_timeout:
00224 rospy.logerr('Task did not preempt within timeout. Aborting')
00225 return 'aborted'
00226
00227
00228 state = self.ac.get_state()
00229 if state == GoalStatus.SUCCEEDED:
00230 self.succeeded.append(userdata.task.name)
00231 rospy.loginfo('Task succeeded')
00232 return 'succeeded'
00233 elif state == GoalStatus.PREEMPTED:
00234 self.preempted.append(userdata.task.name)
00235 rospy.logwarn('Task preempted')
00236 return 'preempted'
00237 elif state in [GoalStatus.RECALLED,
00238 GoalStatus.REJECTED,
00239 GoalStatus.ABORTED,
00240 GoalStatus.LOST]:
00241 self.aborted.append(userdata.task.name)
00242 rospy.logerr('Task aborted')
00243 return 'aborted'
00244 rospy.sleep(0.2)
00245
00246 def preempt_task(self, msg):
00247 if not self.preempting:
00248 self.ac.cancel_all_goals()
00249 self.preempting = True
00250 self.preempting_start = rospy.Time.now()
00251 rospy.logwarn('%s'%msg)
00252
00253
00254 def cancel_srv(self, req):
00255 self.cancel_run_id = req.run_id
00256 return ChangeTaskResponse()
00257
00258
00259
00260
00261 class TimeLogger(Thread):
00262 def __init__(self):
00263 Thread.__init__(self)
00264 self.start_time = rospy.Time.now()
00265
00266 def run(self):
00267 print 'Running'
00268 pub = rospy.Publisher('continuous_ops_duration', Float64)
00269 while not rospy.is_shutdown():
00270 rospy.sleep(2.0)
00271 pub.publish((rospy.Time.now() - self.start_time).to_sec())
00272
00273
00274 def main():
00275 rospy.init_node('continuous_ops_executive')
00276
00277 time_logger = TimeLogger()
00278 time_logger.start()
00279
00280
00281 rospy.loginfo('Detecting task manager...')
00282 rospy.wait_for_service('task_manager/get_scheduled_tasks')
00283 rospy.wait_for_service('task_manager/start_task')
00284 rospy.wait_for_service('task_manager/stop_task')
00285 rospy.wait_for_service('task_manager/remove_task')
00286 rospy.loginfo('Connected to task manager')
00287
00288
00289 sm_top = smach.StateMachine(outcomes=['aborted', 'preempted'])
00290 with sm_top:
00291
00292
00293
00294
00295 def child_term_cb(outcome_map):
00296
00297 rospy.loginfo('One concurrent state finished, preempting other state')
00298 return True
00299 def out_cb(outcome_map):
00300 if outcome_map['LOW_BATTERY_MONITOR'] == 'hungry':
00301 return 'hungry'
00302 if outcome_map['LOW_BATTERY_MONITOR'] == 'bored':
00303 return 'bored'
00304 return 'continue'
00305 sm_task_monitor = smach.Concurrence(outcomes=['continue', 'hungry', 'bored'],
00306 default_outcome='continue',
00307 child_termination_cb = child_term_cb,
00308 outcome_cb = out_cb)
00309 with sm_task_monitor:
00310 sm_task = smach.StateMachine(outcomes=['succeeded', 'aborted', 'preempted'])
00311 with sm_task:
00312
00313 smach.StateMachine.add('LAUNCH_TASK', TaskLaunchState(),
00314 transitions={'succeeded':'RUN_TASK',
00315 'aborted':'LAUNCH_TASK',
00316 'preempted':'preempted'})
00317
00318 smach.StateMachine.add('RUN_TASK', TaskRunState(),
00319 transitions={'succeeded':'KILL_TASK',
00320 'aborted':'KILL_TASK',
00321 'preempted':'KILL_TASK'})
00322
00323 smach.StateMachine.add('KILL_TASK', TaskKillState(),
00324 transitions={'succeeded':'LAUNCH_TASK',
00325 'preempted':'preempted'})
00326
00327
00328 smach.Concurrence.add('TASK', sm_task)
00329
00330
00331 smach.Concurrence.add('LOW_BATTERY_MONITOR', LowBatteryMonitor())
00332
00333
00334 smach.StateMachine.add('TASK_WITH_MONITOR', sm_task_monitor,
00335 transitions={'continue':'TASK_WITH_MONITOR',
00336 'bored':'RECHARGE',
00337 'hungry':'RECHARGE'})
00338
00339
00340
00341
00342
00343
00344 sm_recharge = smach.StateMachine(outcomes=['aborted', 'preempted', 'stuffed'])
00345
00346 with sm_recharge:
00347
00348 def location_cb(userdata, goal):
00349 goal.command.plug_id = choice(['wim', 'james'])
00350 rospy.loginfo("Recharging at location %s"%goal.command.plug_id)
00351 return goal
00352 plugin = RechargeGoal(RechargeCommand(RechargeCommand.PLUG_IN, ''))
00353 smach.StateMachine.add('PLUG_IN', smach_ros.SimpleActionState('recharge',
00354 RechargeAction,
00355 goal=plugin,
00356 goal_cb=location_cb),
00357 transitions={'aborted':'PLUG_IN',
00358 'succeeded':'HIGH_BATTERY_MONITOR',
00359 'preempted':'preempted'})
00360
00361
00362 smach.StateMachine.add('HIGH_BATTERY_MONITOR', HighBatteryMonitor(),
00363 transitions={'preempted':'preempted',
00364 'stuffed':'UNPLUG',
00365 'not_plugged_in':'UNPLUG_RECOVER'})
00366
00367
00368 unplug = RechargeGoal(RechargeCommand(RechargeCommand.UNPLUG, ''))
00369 smach.StateMachine.add('UNPLUG', smach_ros.SimpleActionState('recharge',
00370 RechargeAction,
00371 goal=unplug),
00372 transitions={'succeeded':'stuffed',
00373 'aborted':'UNPLUG',
00374 'preempted':'preempted'})
00375
00376
00377 unplug = RechargeGoal(RechargeCommand(RechargeCommand.UNPLUG, ''))
00378 smach.StateMachine.add('UNPLUG_RECOVER', smach_ros.SimpleActionState('recharge',
00379 RechargeAction,
00380 goal=unplug),
00381 transitions={'succeeded':'PLUG_IN',
00382 'aborted':'UNPLUG_RECOVER',
00383 'preempted':'preempted'})
00384
00385 smach.StateMachine.add('RECHARGE', sm_recharge,
00386 transitions={'aborted':'aborted',
00387 'stuffed':'TASK_WITH_MONITOR'})
00388
00389
00390
00391 sis = smach_ros.IntrospectionServer('continuous_ops_executive_server', sm_top, '/CONTINUOUS_OPERATION_EXECUTIVE')
00392 sis.start()
00393
00394
00395 rospy.sleep(4)
00396 outcome = sm_top.execute()
00397
00398
00399 rospy.spin()
00400 sis.stop()
00401
00402
00403 if __name__ == '__main__':
00404 main()