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 from smach import State, StateMachine, UserData
00033
00034 from rgoap import Action
00035
00036
00037 import logging
00038 _logger = logging.getLogger('rgoap.smach')
00039
00040
00041
00042 class RGOAPNodeWrapperState(State):
00043 """Used (by the runner) to add RGOAP nodes (aka instances of RGOAP actions)
00044 to a SMACH state machine"""
00045 def __init__(self, node):
00046 State.__init__(self, outcomes=['succeeded', 'aborted'])
00047 self.node = node
00048
00049 def execute(self, userdata):
00050
00051
00052
00053
00054
00055
00056 if not self.node.action.check_freeform_context():
00057 _logger.error("Action's freeform context isn't valid! Aborting"
00058 " wrapping state for %s", self.node.action)
00059 return 'aborted'
00060 next_node = self.node.parent_node()
00061 self.node.action.run(next_node.worldstate)
00062 return 'succeeded'
00063
00064
00065 class SMACHStateWrapperAction(Action):
00066 """A special Action to wrap a SMACH state.
00067
00068 Subclass this class to make a SMACH state available to RGOAP planning.
00069 """
00070
00071
00072
00073
00074
00075
00076
00077 def __init__(self, state, preconditions, effects, **kwargs):
00078 Action.__init__(self, preconditions, effects, **kwargs)
00079 self.state = state
00080
00081 def get_remapping(self):
00082 """Override this to set a remapping.
00083 Actually planned for future use"""
00084 return {}
00085
00086 def translate_worldstate_to_userdata(self, next_worldstate, userdata):
00087 """Override to make worldstate data available to the state."""
00088 pass
00089
00090 def translate_userdata_to_worldstate(self, userdata, next_worldstate):
00091
00092 """Override to make the state's output available to the worldstate."""
00093 pass
00094
00095 def run(self, next_worldstate):
00096 userdata = UserData()
00097 self.translate_worldstate_to_userdata(next_worldstate, userdata)
00098 self.state.execute(userdata)
00099 self.translate_userdata_to_worldstate(userdata, next_worldstate)
00100
00101
00102
00103 class RGOAPRunnerState(State):
00104 """Subclass this state to activate the RGOAP planner from within a
00105 surrounding SMACH state container, e.g. the ActionServerWrapper
00106 """
00107
00108 def __init__(self, runner, **kwargs):
00109 State.__init__(self, ['succeeded', 'aborted', 'preempted'], **kwargs)
00110 self.runner = runner
00111
00112 def execute(self, userdata):
00113 try:
00114 goal = self._build_goal(userdata)
00115 except NotImplementedError:
00116 try:
00117 goals = self._build_goals(userdata)
00118 except NotImplementedError:
00119 raise NotImplementedError("Subclass %s neither implements %s nor %s" % (
00120 self.__class__.__name__,
00121 self._build_goal.__name__,
00122 self._build_goals.__name__))
00123
00124 else:
00125 outcome = self.runner.plan_and_execute_goals(goals)
00126 else:
00127 outcome = self.runner.update_and_plan_and_execute(goal, introspection=True)
00128
00129
00130 _logger.info("Generated RGOAP sub state machine returns: %s", outcome)
00131 if self.preempt_requested():
00132 self.service_preempt()
00133 return 'preempted'
00134 return outcome
00135
00136 def _build_goal(self, userdata):
00137 """Build and return a rgoap.Goal the planner should accomplish"""
00138 raise NotImplementedError
00139
00140 def _build_goals(self, userdata):
00141 """Build and return a rgoap.Goal list the planner should accomplish"""
00142 raise NotImplementedError
00143
00144 def request_preempt(self):
00145 self.runner.request_preempt()
00146 State.request_preempt(self)
00147
00148 def service_preempt(self):
00149 self.runner.service_preempt()
00150 State.service_preempt(self)
00151
00152
00153
00154 def rgoap_path_to_smach_container(start_node):
00155 sm = StateMachine(outcomes=['succeeded', 'aborted', 'preempted'])
00156
00157 node = start_node
00158 with sm:
00159 while not node.is_goal():
00160 next_node = node.parent_node()
00161
00162 if isinstance(node.action, SMACHStateWrapperAction):
00163
00164 StateMachine.add_auto('%s_%X' % (node.action.__class__.__name__, id(node)),
00165 node.action.state,
00166 ['succeeded'],
00167 remapping=node.action.get_remapping())
00168 node.action.translate_worldstate_to_userdata(next_node.worldstate, sm.userdata)
00169 else:
00170 StateMachine.add_auto('%s_%X' % (node.action.__class__.__name__, id(node)),
00171 RGOAPNodeWrapperState(node),
00172 ['succeeded'])
00173
00174 node = next_node
00175
00176 return sm