smach_bridge.py
Go to the documentation of this file.
00001 # Copyright (c) 2013, Felix Kolbe
00002 # All rights reserved. BSD License
00003 #
00004 # Redistribution and use in source and binary forms, with or without
00005 # modification, are permitted provided that the following conditions
00006 # are met:
00007 #
00008 # * Redistributions of source code must retain the above copyright
00009 #   notice, this list of conditions and the following disclaimer.
00010 #
00011 # * Redistributions in binary form must reproduce the above copyright
00012 #   notice, this list of conditions and the following disclaimer in the
00013 #   documentation and/or other materials provided with the distribution.
00014 #
00015 # * Neither the name of the {organization} nor the names of its
00016 #   contributors may be used to endorse or promote products derived
00017 #   from this software without specific prior written permission.
00018 #
00019 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00022 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00023 # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00024 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00025 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00026 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00027 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00028 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00029 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 #        if not self.node.action.is_valid(current_worldstate): TODO: current worldstate not known
00051 #            print "Action isn't valid to worldstate! Aborting executor"
00052 #            print ' action: %r' % self.node.action
00053 #            print ' worldstate:', self.node.worldstate
00054 #            return 'aborted'
00055 #        print "Action %s valid to worldstate" % self.node.action
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     # TODO: Maybe this wrapper can be improved to be more self-reliant
00071     # Atm. the method rgoap_path_to_smach_container takes the state from this
00072     # container, and calls this wrapper's translation method. If this wrapper
00073     # itself would be a State, its execute() method could call its action's
00074     # check_freeform_context() (which currently cannot be called at the right
00075     # time), call the translation and the wrapped states and the state's
00076     # execute() method all at once and capsuled.
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         # FIXME: translation from userdata does not work
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     # TODO: maybe make this class a smach.Container and add states dynamically?
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             # else cases are needed to not catch other NotImplementedErrors
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(): # skipping the goal node at the end
00160             next_node = node.parent_node()
00161 
00162             if isinstance(node.action, SMACHStateWrapperAction):
00163                 # TODO: when smach executes SMACHStateWrapperActions, their action.check_freeform_context() is never called!
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


rgoap_smach
Author(s): Felix Kolbe
autogenerated on Sun Oct 5 2014 23:53:05