Package smach :: Module iterator

Source Code for Module smach.iterator

  1   
  2  import threading 
  3  import traceback 
  4  from contextlib import contextmanager 
  5   
  6  import smach 
  7   
  8  __all__ = ['Iterator'] 
9 10 -class Iterator(smach.container.Container):
11 """Sequence Container 12 13 This container inherits functionality from L{smach.StateMachine} and adds 14 some auto-generated transitions that create a sequence of states from the 15 order in which said states are added to the container. 16 """
17 - def __init__(self, 18 outcomes, 19 input_keys, 20 output_keys, 21 it = [], 22 it_label = 'it_data', 23 exhausted_outcome = 'exhausted'):
24 """Constructor. 25 26 @type outcomes: list of string 27 @param outcomes: The potential outcomes of this container. 28 29 @type it: iterable 30 @param iteritems: Items to iterate over on each cycle 31 32 @type it_label: string 33 @param iteritems_label: The label that the item in the current 34 iteration will be given when it is put into the container's local 35 userdata. 36 """ 37 if exhausted_outcome not in outcomes: 38 outcomes.append(exhausted_outcome) 39 smach.container.Container.__init__(self, outcomes, input_keys, output_keys) 40 41 self._items = it 42 self._items_label = it_label 43 44 self._is_running = False 45 46 self._state_label = '' 47 self._state = None 48 self._loop_outcomes = [] 49 self._break_outcomes = [] 50 self._final_outcome_map = {} 51 self._exhausted_outcome = exhausted_outcome
52 53 54 ### Construction Methods 55 @staticmethod
56 - def set_iteritems(it, it_label='it_data'):
57 """Set the list or generator for the iterator to iterate over. 58 59 @type it: iterable 60 @param iteritems: Items to iterate over on each cycle 61 62 @type it_label: string 63 @param iteritems_label: The label that the item in the current 64 iteration will be given when it is put into the container's local 65 userdata. 66 67 @type exhausted_outcome: string 68 @param exhausted_outcome: If the iterable is exhausted without a break 69 condition this outcome is emitted by the container. 70 """ 71 # Get currently opened container 72 self = Iterator._currently_opened_container() 73 self._items = it 74 self._items_label = it_label
75 76 @staticmethod
77 - def set_contained_state( 78 label, 79 state, 80 loop_outcomes = [], 81 break_outcomes = [], 82 final_outcome_map = {}):
83 """Set the contained state 84 85 @type label: string 86 @param label: The label of the state being added. 87 88 @type state: L{smach.State} 89 @param state: An instance of a class implementing the L{smach.State} interface. 90 91 @param loop_outcomes: List of contained state outcomes that should cause 92 the iterator to continue. If this is empty, all outcomes that are not 93 in the break_outcomes list will cause the iterator to continue to 94 iterate. NOTE: loop_outcomes will be overriden by break_outcomes if both 95 parameters are used. 96 97 @param break_outcomes: List of contained state outcomes that should 98 cause the iterator to break. When the contained state emits an outcome 99 in this list, the container will terminate and return either that 100 outcome or the outcome it is mapped to in final_outcome_map. NOTE: 101 loop_outcomes will be overriden by break_outcomes if both 102 parameters are used. 103 104 @param final_outcome_map: A map from contained state outcomes to container 105 outcomes. On termination of the iterator (either from finishing or from 106 a break) this map will be used to translate contained state outcomes to 107 container outcomes. 108 Unspecified contained state outcomes will fall through as 109 container outcomes. 110 """ 111 # Get currently opened container 112 self = Iterator._currently_opened_container() 113 114 self._state_label = label 115 self._state = state 116 117 # Get potential state outcomes 118 state_outcomes = state.get_registered_outcomes() 119 120 # Check for loop and break outcomes 121 if loop_outcomes and break_outcomes: 122 smach.logwarn('Both loop_outcomes and break_outcomes were specified when constructing SMACH iterator container.') 123 124 if break_outcomes: 125 self._break_outcomes = break_outcomes 126 for outcome in state_outcomes: 127 if outcome not in break_outcomes: 128 self._loop_outcomes.append(outcome) 129 else: 130 self._loop_outcomes = loop_outcomes 131 for outcome in state_outcomes: 132 if outcome not in loop_outcomes: 133 self._break_outcomes.append(outcome) 134 135 self._final_outcome_map = final_outcome_map
136 137 ### State interface
138 - def execute(self, parent_ud):
139 self._is_running = True 140 141 # Copy input keys 142 self._copy_input_keys(parent_ud, self.userdata) 143 144 self.call_start_cbs() 145 146 # Iterate over items 147 outcome = self._exhausted_outcome 148 149 if hasattr(self._items,'__call__'): 150 it = self._items().__iter__() 151 else: 152 it = self._items.__iter__() 153 154 while not smach.is_shutdown(): 155 try: 156 item = next(it) 157 except: 158 outcome = self._exhausted_outcome 159 break 160 smach.loginfo("Iterating %s of %s" % (str(item), str(self._items))) 161 self.userdata[self._items_label] = item 162 # Enter the contained state 163 try: 164 outcome = self._state.execute(self.userdata) 165 except smach.InvalidUserCodeError as ex: 166 smach.logerr("Could not execute Iterator state '%s'" % self._state_label) 167 raise ex 168 except: 169 raise smach.InvalidUserCodeError("Could not execute iterator state '%s' of type '%s': " % ( self._state_label, self._state) + traceback.format_exc()) 170 171 172 173 # Check if we should stop preemptively 174 if self._preempt_requested\ 175 or outcome in self._break_outcomes\ 176 or (len(self._loop_outcomes) > 0 and outcome not in self._loop_outcomes): 177 self._preempt_requested = False 178 break 179 self.call_transition_cbs() 180 181 # Remap the outcome if necessary 182 if outcome in self._final_outcome_map: 183 outcome = self._final_outcome_map[outcome] 184 185 # Copy output keys 186 self._copy_output_keys(self.userdata, parent_ud) 187 188 self._is_running = False 189 190 self.call_termination_cbs(self._state_label,outcome) 191 192 return outcome
193
194 - def request_preempt(self):
195 self._preempt_requested = True 196 if self._is_running: 197 self._state.request_preempt()
198 199 ### Container interface
200 - def get_children(self):
201 return {self._state_label: self._state}
202
203 - def __getitem__(self,key):
204 if key != self._state_label: 205 smach.logerr("Attempting to get state '%s' from Iterator container. The only available state is '%s'." % (key, self._state_label)) 206 raise KeyError() 207 return self._state
208
209 - def get_initial_states(self):
210 return [self._state_label]
211
212 - def set_initial_state(self, initial_states, userdata):
213 # Check initial state 214 if len(initial_states) > 1: 215 smach.logwarn("Attempting to set initial state to include more than one state, but Iterator container can only have one initial state." % (self._state_label)) 216 217 if len(initial_states) > 0: 218 if initial_states[0] != self._state_label: 219 smach.logwarn("Attempting to set state '%s' as initial state in Iterator container. The only available state is '%s'." % (initial_states[0], self._state_label)) 220 raise KeyError() 221 222 # Set local userdata 223 self.userdata.update(userdata)
224
225 - def get_active_states(self):
226 if self._is_running: 227 return [self._state_label] 228 return []
229
230 - def get_internal_edges(self):
231 int_edges = [] 232 233 for outcome in self._loop_outcomes: 234 int_edges.append([outcome, self._state_label, self._state_label]) 235 236 for outcome in self._break_outcomes: 237 container_outcome = outcome 238 if outcome in self._final_outcome_map: 239 container_outcome = self._final_outcome_map[outcome] 240 if outcome == container_outcome: 241 int_edges.append((outcome, self._state_label, None)) 242 else: 243 int_edges.append((outcome, self._state_label, container_outcome)) 244 245 return int_edges
246
247 - def check_consistency(self):
248 pass
249