behavior_launcher.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 import rospy
4 from flexbe_msgs.msg import *
5 from rospkg import RosPack, ResourceNotFound
6 
7 from flexbe_core import Logger, BehaviorLibrary
8 from std_msgs.msg import String
9 
10 import pickle
11 import zlib
12 import difflib
13 import os
14 import yaml
15 import xml.etree.ElementTree as ET
16 import threading
17 
18 class BehaviorLauncher(object):
19 
20  MIN_VERSION = '2.2.0'
21 
22  def __init__(self):
23  Logger.initialize()
24 
25  self._ready_event = threading.Event()
26 
27  self._sub = rospy.Subscriber("flexbe/request_behavior", BehaviorRequest, self._callback)
28  self._version_sub = rospy.Subscriber("flexbe/ui_version", String, self._version_callback)
29 
30  self._pub = rospy.Publisher("flexbe/start_behavior", BehaviorSelection, queue_size=100)
31  self._status_pub = rospy.Publisher("flexbe/status", BEStatus, queue_size=100)
32  self._status_sub = rospy.Subscriber("flexbe/status", BEStatus, self._status_callback)
33  self._mirror_pub = rospy.Publisher("flexbe/mirror/structure", ContainerStructure, queue_size=100)
34 
35  self._rp = RosPack()
36  self._behavior_lib = BehaviorLibrary()
37 
38  rospy.loginfo("%d behaviors available, ready for start request." % self._behavior_lib.count_behaviors())
39 
40  def _status_callback(self, msg):
41  if msg.code in [BEStatus.READY, BEStatus.FINISHED, BEStatus.FAILED, BEStatus.ERROR]:
42  self._ready_event.set()
43 
44  def _callback(self, msg):
45  be_id, behavior = self._behavior_lib.find_behavior(msg.behavior_name)
46  if be_id is None:
47  Logger.logerr("Did not find behavior with requested name: %s" % msg.behavior_name)
48  self._status_pub.publish(BEStatus(code=BEStatus.ERROR))
49  return
50 
51  rospy.loginfo("Request for behavior " + behavior["name"])
52 
53  be_selection = BehaviorSelection()
54  be_selection.behavior_id = be_id
55  be_selection.autonomy_level = msg.autonomy_level
56  try:
57  for k, v in zip(msg.arg_keys, msg.arg_values):
58  if k.startswith('/YAML:'):
59  key = k.replace('/YAML:', '/', 1)
60  path = v.split(':')[0]
61  ns = v.split(':')[1]
62  if path.startswith('~') or path.startswith('/'):
63  yamlpath = os.path.expanduser(path)
64  else:
65  yamlpath = os.path.join(self._rp.get_path(path.split('/')[0]), '/'.join(path.split('/')[1:]))
66  with open(yamlpath, 'r') as f:
67  content = getattr(yaml, 'full_load', yaml.load)(f)
68  if ns != '' and ns in content:
69  content = content[ns]
70  be_selection.arg_keys.append(key)
71  be_selection.arg_values.append(yaml.dump(content))
72  else:
73  be_selection.arg_keys.append(k)
74  be_selection.arg_values.append(v)
75  except Exception as e:
76  rospy.logwarn('Failed to parse and substitute behavior arguments, will use direct input.\n%s' % str(e))
77  be_selection.arg_keys = msg.arg_keys
78  be_selection.arg_values = msg.arg_values
79 
80  # wait until Behavior Engine status is BEStatus.READY
81  self._ready_event.wait()
82 
83  be_structure = ContainerStructure()
84  be_structure.containers = msg.structure
85 
86  try:
87  be_filepath_new = self._behavior_lib.get_sourcecode_filepath(be_id)
88  except ResourceNotFound:
89  rospy.logerr("Could not find behavior package '%s'" % (behavior["package"]))
90  rospy.loginfo("Have you updated your ROS_PACKAGE_PATH after creating the behavior?")
91  return
92 
93  with open(be_filepath_new, "r") as f:
94  be_content_new = f.read()
95 
96  be_filepath_old = self._behavior_lib.get_sourcecode_filepath(be_id, add_tmp=True)
97  if not os.path.isfile(be_filepath_old):
98  be_selection.behavior_checksum = zlib.adler32(be_content_new.encode()) & 0x7fffffff
99  if msg.autonomy_level != 255:
100  be_structure.behavior_id = be_selection.behavior_checksum
101  self._mirror_pub.publish(be_structure)
102  self._pub.publish(be_selection)
103  rospy.loginfo("No changes to behavior version.")
104  return
105 
106  with open(be_filepath_old, "r") as f:
107  be_content_old = f.read()
108 
109  sqm = difflib.SequenceMatcher(a=be_content_old, b=be_content_new)
110  diffs = [x[1] for x in sqm.get_grouped_opcodes(0)]
111  for opcode, a0, a1, b0, b1 in diffs:
112  content = be_content_new[b0:b1]
113  be_selection.modifications.append(BehaviorModification(a0, a1, content))
114 
115  be_selection.behavior_checksum = zlib.adler32(be_content_new.encode()) & 0x7fffffff
116  if msg.autonomy_level != 255:
117  be_structure.behavior_id = be_selection.behavior_checksum
118  self._mirror_pub.publish(be_structure)
119 
120  self._pub.publish(be_selection)
121 
122  def _version_callback(self, msg):
123  vui = self._parse_version(msg.data)
124  vex = self._parse_version(BehaviorLauncher.MIN_VERSION)
125  if vui < vex:
126  Logger.logwarn('FlexBE App needs to be updated!\n' \
127  + 'Require at least version %s, but have %s\n' % (BehaviorLauncher.MIN_VERSION, msg.data) \
128  + 'Please run a "git pull" in "roscd flexbe_app".')
129 
130  def _parse_version(self, v):
131  result = 0
132  offset = 1
133  for n in reversed(v.split('.')):
134  result += int(n) * offset
135  offset *= 100
136  return result


flexbe_widget
Author(s): Philipp Schillinger
autogenerated on Sun Dec 13 2020 04:01:47