Go to the documentation of this file.00001
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
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
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
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
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
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