6 from rospy.exceptions
import ROSInterruptException
11 from smach.state_machine
import StateMachine
14 from flexbe_msgs.msg
import Container, ContainerStructure, OutcomeRequest, BehaviorSync, CommandFeedback, BehaviorLog
15 from std_msgs.msg
import Empty, UInt8, Int32
23 A state machine that can be operated. 24 It synchronizes its current state with the mirror and supports some control mechanisms. 31 super(OperatableStateMachine, self).
__init__(*args, **kwargs)
42 self.
_pub = ProxyPublisher()
44 self.
_sub = ProxySubscriberCached()
47 def execute(self, parent_ud = smach.UserData()):
51 smach.loginfo(
"State machine starting in initial state '%s' with userdata: \n\t%s" %
52 (self._current_label, list(self.userdata.keys())))
54 self._set_current_state(self._initial_state_label)
64 self._copy_input_keys(parent_ud, self.userdata)
67 if self.
id is not None:
69 while container_outcome == self.
_loopback_name and not smach.is_shutdown():
75 if smach.is_shutdown():
76 container_outcome =
'preempted' 78 return container_outcome
82 """Run the state machine on entry to this state. 83 This will set the "closed" flag and spin up the execute thread. Once 84 this flag has been set, it will prevent more states from being added to 89 with self._state_transitioning_lock:
92 self.check_consistency()
93 except (smach.InvalidStateError, smach.InvalidTransitionError):
94 smach.logerr(
"Container consistency check failed.")
101 container_outcome =
None 109 self._current_state._rate.sleep()
110 except ROSInterruptException:
111 rospy.logwarn(
'Interrupted rate sleep.')
113 if container_outcome
is not None and container_outcome != self.
_loopback_name:
115 self._copy_output_keys(self.userdata, parent_ud)
125 self._parent._inner_sync_request =
True 128 msg.behavior_id = self.
id 130 self._pub.publish(
'flexbe/mirror/sync', msg)
135 return container_outcome
139 def add(label, state, transitions = None, autonomy = None, remapping = None):
141 Add a state to the opened state machine. 144 @param label: The label of the state being added. 146 @param state: An instance of a class implementing the L{State} interface. 148 @param transitions: A dictionary mapping state outcomes to other state 149 labels or container outcomes. 151 @param autonomy: A dictionary mapping state outcomes to their required 154 @param remapping: A dictionary mapping local userdata keys to userdata 155 keys in the container. 157 self = StateMachine._currently_opened_container()
160 if isinstance(state, LoopbackState):
161 transitions[LoopbackState._loopback_name] = label
162 autonomy[LoopbackState._loopback_name] = -1
163 if isinstance(state, OperatableStateMachine):
164 transitions[OperatableStateMachine._loopback_name] = label
165 autonomy[OperatableStateMachine._loopback_name] = -1
167 self._ordered_states.append(state)
169 state.transitions = transitions
170 state.autonomy = autonomy
173 StateMachine.add(label, state, transitions, remapping)
177 old_state = self._states[new_state.name]
178 new_state.transitions = old_state.transitions
179 new_state.autonomy = old_state.autonomy
180 new_state._parent = old_state._parent
182 self.
_ordered_states[self._ordered_states.index(old_state)] = new_state
183 self._states[new_state.name] = new_state
189 self._sub.unsubscribe_topic(
'flexbe/command/autonomy')
190 self._sub.unsubscribe_topic(
'flexbe/command/sync')
191 self._sub.unsubscribe_topic(
'flexbe/command/attach')
192 self._sub.unsubscribe_topic(
'flexbe/request_mirror_structure')
193 StateLogger.shutdown()
198 Confirms the state machine and triggers the creation of the structural message. 199 It is mandatory to call this function at the top-level state machine 200 between building it and starting its execution. 203 @param name: The name of this state machine to identify it. 208 self._pub.createPublisher(
'flexbe/mirror/sync', BehaviorSync, _latch =
True)
209 self._pub.createPublisher(
'flexbe/mirror/preempt', Empty, _latch =
True)
210 self._pub.createPublisher(
'flexbe/mirror/structure', ContainerStructure)
211 self._pub.createPublisher(
'flexbe/log', BehaviorLog)
212 self._pub.createPublisher(
'flexbe/command_feedback', CommandFeedback)
215 self._sub.subscribe(
'flexbe/command/sync', Empty, self.
_sync_callback)
219 StateLogger.initialize(name)
220 if OperatableStateMachine.autonomy_level != 255:
228 """ Sets the current autonomy level. """ 229 if OperatableStateMachine.autonomy_level != msg.data:
230 rospy.loginfo(
'--> Autonomy changed to %d', msg.data)
235 OperatableStateMachine.autonomy_level = msg.data
237 self._pub.publish(
'flexbe/command_feedback', CommandFeedback(command=
"autonomy", args=[]))
241 rospy.loginfo(
"--> Synchronization requested...")
243 msg.behavior_id = self.
id 247 self._pub.publish(
'flexbe/mirror/sync', msg)
248 self._pub.publish(
'flexbe/command_feedback', CommandFeedback(command=
"sync", args=[]))
249 rospy.loginfo(
"<-- Sent synchronization message for mirror.")
253 rospy.loginfo(
"--> Enabling control...")
255 OperatableStateMachine.autonomy_level = msg.data
260 cfb = CommandFeedback(command=
"attach")
261 cfb.args.append(self.
name)
262 self._pub.publish(
'flexbe/command_feedback', cfb)
263 rospy.loginfo(
"<-- Sent attach confirm.")
267 rospy.loginfo(
"--> Creating behavior structure for mirror...")
269 msg.behavior_id = self.
id 270 self._pub.publish(
'flexbe/mirror/structure', msg)
271 rospy.loginfo(
"<-- Sent behavior structure for mirror.")
275 return self.
_autonomy[label][outcome] < OperatableStateMachine.autonomy_level
280 Adds this state machine to the initial structure message. 283 @param prefix: A path consisting of the container hierarchy containing this state. 285 @type msg: ContainerStructure 286 @param msg: The message that will finally contain the structure message. 291 children.append(str(state.name))
294 name = prefix + (self.
name if self.
id is None else '')
298 self.
_message = ContainerStructure()
299 outcomes = list(self._outcomes)
310 outcome = outcomes[i]
311 if outcome ==
'preempted':
312 transitions.append(
'preempted')
316 autonomy.append(self.
autonomy[outcome])
319 self._message.containers.append(Container(name, children, outcomes, transitions, autonomy))
323 state._build_msg(name+
'/', self.
_message)
332 if isinstance(state, LoopbackState):
334 if isinstance(state, OperatableStateMachine):
335 state._notify_start()
340 if isinstance(state, LoopbackState):
341 state._enable_ros_control()
342 if isinstance(state, OperatableStateMachine):
343 state._enable_ros_control()
347 if isinstance(state, LoopbackState):
349 state._disable_ros_control()
350 if isinstance(state, OperatableStateMachine):
356 if isinstance(state, LoopbackState):
357 state._disable_ros_control()
358 if isinstance(state, OperatableStateMachine):
359 state._disable_ros_control()
365 self._current_state.get_registered_input_keys(),
366 self._current_state.get_registered_output_keys(),
367 self._remappings[self._current_state.name]
369 self._current_state._entering =
True 370 self._current_state.on_exit(ud)
def _sync_callback(self, msg)
def _attach_callback(self, msg)
def on_exit(self, userdata)
def _build_msg(self, prefix, msg=None)
def add(label, state, transitions=None, autonomy=None, remapping=None)
def _mirror_structure_callback(self, msg)
def __init__(self, args, kwargs)
def _get_deep_state(self)
def replace(self, new_state)
def confirm(self, name, id)
def execute(self, parent_ud=smach.UserData())
def _disable_ros_control(self)
def _transition_allowed(self, label, outcome)
def _async_execute(self, parent_ud=smach.UserData())
def _set_autonomy_level(self, msg)
def _enable_ros_control(self)