11 from ast
import literal_eval
as cast
13 from flexbe_core
import Logger, BehaviorLibrary
17 from flexbe_msgs.msg
import BehaviorSelection, BEStatus, CommandFeedback, UserdataInfo
18 from flexbe_msgs.srv
import GetUserdata, GetUserdataRequest, GetUserdataResponse
19 from std_msgs.msg
import Empty
24 Controls the execution of robot behaviors.
42 self.
_pub = ProxyPublisher({
44 'flexbe/heartbeat': Empty
55 self.
_sub = ProxySubscriberCached()
62 rospy.loginfo(
'\033[92m--- Behavior Engine ready! ---\033[0m')
76 Logger.loginfo(
'--> Initiating behavior switch...')
77 self.
_pub.publish(self.
feedback_topic, CommandFeedback(command=
"switch", args=[
'received']))
79 Logger.loginfo(
'--> Starting new behavior...')
84 Logger.logerr(
'Dropped behavior start request because preparation failed.')
86 self.
_pub.publish(self.
feedback_topic, CommandFeedback(command=
"switch", args=[
'failed']))
88 rospy.loginfo(
'\033[92m--- Behavior Engine ready! ---\033[0m')
95 self.
_pub.publish(self.
feedback_topic, CommandFeedback(command=
"switch", args=[
'start']))
98 Logger.logerr(
'Dropped behavior start request because switching is not possible.')
99 self.
_pub.publish(self.
feedback_topic, CommandFeedback(command=
"switch", args=[
'not_switchable']))
102 rate = rospy.Rate(100)
103 while not rospy.is_shutdown():
104 active_state = self.
be.get_current_state()
105 if active_state
is not None or not self.
_running:
109 if active_state
is not None:
110 rospy.loginfo(
"Current state %s is kept active.", active_state.name)
112 be.prepare_for_switch(active_state)
113 self.
_pub.publish(self.
feedback_topic, CommandFeedback(command=
"switch", args=[
'prepared']))
114 except Exception
as e:
115 Logger.logerr(
'Failed to prepare behavior switch:\n%s' % str(e))
116 self.
_pub.publish(self.
feedback_topic, CommandFeedback(command=
"switch", args=[
'failed']))
119 rospy.loginfo(
'Preempting current behavior version...')
130 rospy.loginfo(
'Behavior ready, execution starts now.')
131 rospy.loginfo(
'[%s : %s]', be.name, msg.behavior_checksum)
133 args = [self.
be.requested_state_path]
if self.
be.requested_state_path
is not None else []
135 BEStatus(behavior_id=self.
be.id, code=BEStatus.STARTED, args=args))
136 result = self.
be.execute()
139 BEStatus(behavior_id=self.
be.id, code=BEStatus.SWITCHING))
142 BEStatus(behavior_id=self.
be.id, code=BEStatus.FINISHED, args=[str(result)]))
143 except Exception
as e:
144 self.
_pub.publish(self.
status_topic, BEStatus(behavior_id=msg.behavior_checksum, code=BEStatus.FAILED))
145 Logger.logerr(
'Behavior execution failed!\n%s' % str(e))
147 Logger.loginfo(traceback.format_exc())
148 result = result
or "exception"
157 except Exception
as e:
158 rospy.logerr(
'Failed to clean up behavior:\n%s' % str(e))
161 Logger.loginfo(
'Behavior execution finished with result %s.', str(result))
162 rospy.loginfo(
'\033[92m--- Behavior Engine ready! ---\033[0m')
168 response = GetUserdataResponse()
170 if self.
be and self.
be._state_machine:
172 if self.
be._state_machine._userdata:
173 for key, data
in self.
be._state_machine._userdata._data.items():
175 if (request.userdata_key ==
"" or request.userdata_key == key):
176 userdata.append(UserdataInfo(state=self.
be._state_machine._name,
178 type=type(data).__name__,
183 if (len(userdata) > 0):
185 printable_userdata =
""
187 printable_userdata +=
"\t{}:{}\n".format(ud.key, ud.data)
188 Logger.loginfo(
"Current userdata: \n{}".format(printable_userdata))
189 response.success =
True
191 response.success =
False
192 response.message =
"Found {} occurences of '{}'".format(len(userdata), request.userdata_key)
193 response.userdata = userdata
195 response.success =
False
196 response.message =
"no state_machine running"
208 raise ValueError(msg.behavior_id)
209 be_filepath = self.
_behavior_lib.get_sourcecode_filepath(msg.behavior_id, add_tmp=
True)
210 if os.path.isfile(be_filepath):
211 be_file = open(be_filepath,
"r")
212 rospy.logwarn(
"Found a tmp version of the referred behavior! Assuming local test run.")
214 be_filepath = self.
_behavior_lib.get_sourcecode_filepath(msg.behavior_id)
215 be_file = open(be_filepath,
"r")
217 be_content = be_file.read()
220 except Exception
as e:
221 Logger.logerr(
'Failed to retrieve behavior from library:\n%s' % str(e))
222 self.
_pub.publish(self.
status_topic, BEStatus(behavior_id=msg.behavior_checksum, code=BEStatus.ERROR))
229 for mod
in msg.modifications:
230 file_content += be_content[last_index:mod.index_begin] + mod.new_content
231 last_index = mod.index_end
232 file_content += be_content[last_index:]
233 if zlib.adler32(file_content.encode()) & 0x7fffffff != msg.behavior_checksum:
234 mismatch_msg = (
"Checksum mismatch of behavior versions! \n"
235 "Attempted to load behavior: %s\n"
236 "Make sure that all computers are on the same version a.\n"
237 "Also try: rosrun flexbe_widget clear_cache" % str(be_filepath))
238 raise Exception(mismatch_msg)
240 rospy.loginfo(
"Successfully applied %d modifications." % len(msg.modifications))
241 except Exception
as e:
242 Logger.logerr(
'Failed to apply behavior modifications:\n%s' % str(e))
243 self.
_pub.publish(self.
status_topic, BEStatus(behavior_id=msg.behavior_checksum, code=BEStatus.ERROR))
248 file_path = os.path.join(self.
_tmp_folder,
'tmp_%d.py' % msg.behavior_checksum)
249 with open(file_path,
"w")
as sc_file:
250 sc_file.write(file_content)
251 except Exception
as e:
252 Logger.logerr(
'Failed to create temporary file for behavior class:\n%s' % str(e))
253 self.
_pub.publish(self.
status_topic, BEStatus(behavior_id=msg.behavior_checksum, code=BEStatus.ERROR))
259 package = __import__(
"tmp_%d" % msg.behavior_checksum, fromlist=[
"tmp_%d" % msg.behavior_checksum])
260 clsmembers = inspect.getmembers(package,
lambda member: (inspect.isclass(member)
and
261 member.__module__ == package.__name__))
262 beclass = clsmembers[0][1]
264 rospy.loginfo(
'Behavior ' + be.name +
' created.')
265 except Exception
as e:
266 Logger.logerr(
'Exception caught in behavior definition:\n%s\n'
267 'See onboard terminal for more information.' % str(e))
269 traceback.print_exc()
270 self.
_pub.publish(self.
status_topic, BEStatus(behavior_id=msg.behavior_checksum, code=BEStatus.ERROR))
276 if len(msg.arg_keys) > 0:
277 rospy.loginfo(
'The following parameters will be used:')
279 for i
in range(len(msg.arg_keys)):
281 if msg.arg_keys[i] ==
'':
283 found = be.set_parameter(msg.arg_keys[i], msg.arg_values[i])
285 name_split = msg.arg_keys[i].rsplit(
'/', 1)
286 behavior = name_split[0]
if len(name_split) == 2
else ''
288 suffix =
' (' + behavior +
')' if behavior !=
'' else ''
289 rospy.loginfo(key +
' = ' + msg.arg_values[i] + suffix)
291 rospy.logwarn(
'Parameter ' + msg.arg_keys[i] +
' (set to ' + msg.arg_values[i] +
') not defined')
292 except Exception
as e:
293 Logger.logerr(
'Failed to initialize parameters:\n%s' % str(e))
294 self.
_pub.publish(self.
status_topic, BEStatus(behavior_id=msg.behavior_checksum, code=BEStatus.ERROR))
299 be.set_up(id=msg.behavior_checksum, autonomy_level=msg.autonomy_level, debug=
False)
301 rospy.loginfo(
'State machine built.')
302 except Exception
as e:
303 Logger.logerr(
'Behavior construction failed!\n%s\n'
304 'See onboard terminal for more information.' % str(e))
306 traceback.print_exc()
307 self.
_pub.publish(self.
status_topic, BEStatus(behavior_id=msg.behavior_checksum, code=BEStatus.ERROR))
319 if self.
be.name != be.name:
320 Logger.logerr(
'Unable to switch behavior, names do not match:\ncurrent: %s <--> new: %s' %
321 (self.
be.name, be.name))
329 file_path = os.path.join(self.
_tmp_folder,
'tmp_%d.pyc' % behavior_checksum)
335 os.remove(file_path +
'c')
341 if module
in sys.modules:
342 del sys.modules[module]
353 for k, v
in zip(keys, values):
362 except SyntaxError
as se:
363 Logger.loginfo(
'Unable to parse input value for key "%s", assuming string:\n%s\n%s' %
364 (k, str(v), str(se)))
375 self.
_pub.publish(
'flexbe/heartbeat', Empty())
379 if isinstance(o, list):
381 elif isinstance(o, dict):
388 for subbehavior
in state_machine._states:
390 if isinstance(subbehavior, StateMachine):
391 if subbehavior._userdata:
392 for key, data
in subbehavior._userdata._data.items():
394 if (userdata_key ==
"" or userdata_key == key):
395 userdata.append(UserdataInfo(state=path + subbehavior.name +
"/",
397 type=type(data).__name__,
403 __getattr__ = dict.__getitem__
405 @contextlib.contextmanager
407 previous_modules = set(sys.modules.keys())