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
55 @staticmethod
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
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
112 self = Iterator._currently_opened_container()
113
114 self._state_label = label
115 self._state = state
116
117
118 state_outcomes = state.get_registered_outcomes()
119
120
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
139 self._is_running = True
140
141
142 self._copy_input_keys(parent_ud, self.userdata)
143
144 self.call_start_cbs()
145
146
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 = it.next()
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
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
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
182 if outcome in self._final_outcome_map:
183 outcome = self._final_outcome_map[outcome]
184
185
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
195 self._preempt_requested = True
196 if self._is_running:
197 self._state.request_preempt()
198
199
201 return {self._state_label: self._state}
202
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
210 return [self._state_label]
211
213
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
223 self.userdata.update(userdata)
224
226 if self._is_running:
227 return [self._state_label]
228 return []
229
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
249