behavior.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 import rospy
00003 import smach_ros
00004 import smach
00005 import importlib
00006 import sys
00007 import string
00008 
00009 from flexbe_core import OperatableStateMachine, LockableStateMachine
00010 from flexbe_core.core import PreemptableState
00011 
00012 '''
00013 Created on 20.05.2013
00014 
00015 @author: Philipp Schillinger
00016 '''
00017 
00018 class Behavior(object):
00019     '''
00020     This is the superclass for all implemented behaviors.
00021     '''
00022 
00023     def __init__(self):
00024         '''
00025         Please call this superclass constructor first when overriding it with your behavior.
00026         '''
00027         self._state_machine = None
00028         self.name = "unnamed behavior"
00029         self.id = 0
00030         
00031         self.contains = {}
00032         self._behaviors = {}
00033         
00034         self._autonomy_level = 3
00035         self._debug = False
00036 
00037         self.requested_state_path = None
00038         
00039     
00040     # Please implement those:
00041     
00042     def create(self):
00043         """
00044         Should create the state machine for this behavior and return it.
00045         It is called immediately before executing the behavior, so used parameters will have their final value when called.
00046         
00047         @return The complete state machine for this behavior.
00048         """
00049         pass
00050     
00051     
00052     # Use those if you need them:
00053     
00054     def add_parameter(self, name, default):
00055         """
00056         Adds a parameter to this behavior.
00057         The parameter should be declared in the behavior manifest.
00058         
00059         @type name: string
00060         @param name: The name of the parameter.
00061         
00062         @type default: object
00063         @param default: The default value of this parameter. Be sure to set it to the right type.
00064         """
00065         setattr(self, name, default)
00066 
00067     def add_behavior(self, behavior_class, behavior_id):
00068         """
00069         Adds another behavior as part of this behavior.
00070         This other behavior should be declared as contained in the behavior manifest.
00071         
00072         @type behavior_class: class
00073         @param behavior_class: The class implementing the other behavior.
00074         
00075         @type behavior_id: string
00076         @param behavior_id: Unique identifier for this behavior instance.
00077         """
00078         if not hasattr(self, 'contains'):
00079             rospy.logerr('Behavior was not initialized! Please call superclass constructor.')
00080 
00081         instance = behavior_class()
00082         self.contains[behavior_id] = instance
00083     
00084     def use_behavior(self, behavior_class, behavior_id, default_keys=None, parameters=None):
00085         """
00086         Creates a state machine implementing the given behavior to use it in the behavior state machine.
00087         Behavior has to be added first.
00088         
00089         @type behavior_class: class
00090         @param behavior_class: The class implementing the other behavior.
00091         
00092         @type behavior_id: string
00093         @param behavior_id: Same identifier as used for adding.
00094         
00095         @type default_keys: list
00096         @param default_keys: List of input keys of the behavior which should be ignored and instead use the default values as given by the behavior.
00097         
00098         @type parameters: dict
00099         @param parameters: Optional assignment of values to behavior parameters. Any assigned parameter will be ignored for runtime customization, i.e., cannot be overwritten by a user who runs the behavior.
00100         """
00101         if not self.contains.has_key(behavior_id):
00102             rospy.logerr('Tried to use not added behavior!')
00103             return None
00104 
00105         if parameters is not None:
00106             for parameter, value in parameters.items():
00107                 setattr(self.contains[behavior_id], parameter, value)
00108         
00109         state_machine = self.contains[behavior_id]._get_state_machine()
00110 
00111         if default_keys is not None:
00112             state_machine._input_keys = list(set(state_machine._input_keys) - set(default_keys))
00113         
00114         return state_machine
00115 
00116 
00117     # Lifecycle
00118 
00119     def prepare_for_execution(self, input_data = {}):
00120         """
00121         Prepares this behavior for execution by building its state machine.
00122         """
00123         OperatableStateMachine.autonomy_level = self._autonomy_level
00124 
00125         self._state_machine = self.create()
00126         self._state_machine._input_keys = {}
00127         self._state_machine._output_keys = {}
00128 
00129         for k, v in input_data.items():
00130            if k in self._state_machine.userdata:
00131                 self._state_machine.userdata[k] = v
00132 
00133 
00134     def confirm(self):
00135         """
00136         Confirms that this behavior is ready for execution.
00137         """
00138         LockableStateMachine.path_for_switch = self.requested_state_path
00139 
00140         self._state_machine.confirm(self.name, self.id)
00141         
00142         
00143     def execute(self):
00144         """
00145         Called when the behavior is executed.
00146         Need to call self.execute_behavior when ready to start the state machine and return its result.
00147         
00148         @return: A string containing the execution result such as finished or failed.
00149         """
00150         PreemptableState.switching = False
00151         result = self._state_machine.execute()
00152 
00153         self._state_machine.destroy()
00154 
00155         return result
00156 
00157 
00158     def prepare_for_switch(self, state):
00159         """
00160         Prepares the behavior for being executed after a behavior switch.
00161         
00162         @type name: string
00163         @param name: The name of this behavior.
00164         """
00165         states = self._get_states_of_path(state._get_path(), self._state_machine)
00166         if states is None:
00167             raise smach.InvalidConstructionError("Did not find locked state in new behavior!")
00168         state_container = state._parent
00169         for sm in states[1:]:
00170             sm.set_initial_state([sm._initial_state_label], state_container.userdata)
00171             #rospy.loginfo("Set userdata for %s from %s; (keys: %d): %s", str(sm.name), str(state_container.name), len(state_container.userdata._data), str(state_container.userdata._data))
00172             state_container = state_container._parent
00173         states[1].replace(state)
00174 
00175         self.requested_state_path = state._get_path()
00176 
00177 
00178     def get_current_state(self):
00179         return self._state_machine._get_deep_state()
00180 
00181     def get_locked_state(self):
00182         state = self._state_machine._get_deep_state()
00183         while not state is None:
00184             if state.is_locked():
00185                 return state
00186             else:
00187                 state = state._parent
00188         return None
00189 
00190     def preempt(self):
00191         PreemptableState.preempt = True
00192 
00193     def preempt_for_switch(self):
00194         PreemptableState.switching = True
00195         PreemptableState.preempt = True
00196     
00197     
00198     # For internal use only
00199     
00200     def _get_state_machine(self):
00201         if self._state_machine is None:
00202             self._state_machine = self.create()
00203         return self._state_machine
00204     
00205     def _get_muted_state_machine(self):
00206         if self._state_machine is None:
00207             self._state_machine = self.create()
00208         self._mute_state_machine(self._state_machine)
00209         return self._state_machine
00210 
00211     def _mute_state_machine(self, sm):
00212         for state in sm._ordered_states:
00213             if isinstance(state, OperatableStateMachine):
00214                 self._mute_state_machine(state)
00215             else:
00216                 state._is_controlled = False
00217 
00218     
00219     def set_up(self, id, autonomy_level, debug):
00220         self.id = id
00221         self._autonomy_level = autonomy_level
00222         self._debug = debug
00223 
00224     def _get_states_of_path(self, path, container):
00225         path_elements = path.split('/')
00226         if len(path_elements) < 2:
00227             return [container]
00228         state_label = path_elements[1]
00229         new_path = "/".join(path_elements[1:])
00230 
00231         if container.get_children().has_key(state_label):
00232             childlist = self._get_states_of_path(new_path, container.get_children()[state_label])
00233             if childlist is None:
00234                 return None
00235             childlist.append(container)
00236             return childlist
00237         else:
00238             return None
00239 
00240 
00241 
00242 
00243 
00244 
00245 
00246 
00247 
00248 
00249 
00250 
00251 
00252 
00253 
00254 
00255 
00256 
00257 
00258 
00259 
00260 
00261 
00262 
00263 
00264 
00265 
00266 
00267 


flexbe_core
Author(s): Philipp Schillinger
autogenerated on Thu Jun 6 2019 19:32:27